File indexing completed on 2025-02-02 04:47:49
0001 /* 0002 * SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <albertvaka@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 package org.kde.kdeconnect.Helpers; 0008 0009 import android.content.Context; 0010 import android.net.Uri; 0011 import android.os.Environment; 0012 import android.provider.DocumentsContract; 0013 import android.util.Log; 0014 0015 import androidx.annotation.NonNull; 0016 0017 import org.apache.commons.io.IOUtils; 0018 import org.apache.commons.lang3.ArrayUtils; 0019 import org.apache.commons.lang3.StringUtils; 0020 0021 import java.io.File; 0022 import java.io.FileReader; 0023 import java.util.ArrayList; 0024 import java.util.HashSet; 0025 import java.util.List; 0026 import java.util.Scanner; 0027 import java.util.StringTokenizer; 0028 import java.util.stream.Collectors; 0029 0030 //Code from http://stackoverflow.com/questions/9340332/how-can-i-get-the-list-of-mounted-external-storage-of-android-device/19982338#19982338 0031 //modified to work on Lollipop and other devices 0032 public class StorageHelper { 0033 0034 public static class StorageInfo { 0035 0036 public final String path; 0037 public final boolean readonly; 0038 public final boolean removable; 0039 public final int number; 0040 0041 public StorageInfo(String path, boolean readonly, boolean removable, int number) { 0042 this.path = path; 0043 this.readonly = readonly; 0044 this.removable = removable; 0045 this.number = number; 0046 } 0047 0048 } 0049 0050 /* 0051 * This function is bullshit because there is no proper way to do this in Android. 0052 * Patch after patch I'm making it even more horrible by trying to make it work for *more* 0053 * devices while trying no to break previously working ones. 0054 * If this function was a living being, it would be begging "please kill me". 0055 */ 0056 public static List<StorageInfo> getStorageList() { 0057 0058 List<StorageInfo> list = new ArrayList<>(); 0059 String def_path = Environment.getExternalStorageDirectory().getPath(); 0060 boolean def_path_removable = Environment.isExternalStorageRemovable(); 0061 String def_path_state = Environment.getExternalStorageState(); 0062 boolean def_path_available = def_path_state.equals(Environment.MEDIA_MOUNTED) 0063 || def_path_state.equals(Environment.MEDIA_MOUNTED_READ_ONLY); 0064 boolean def_path_readonly = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY); 0065 0066 HashSet<String> paths = new HashSet<>(); 0067 int cur_removable_number = 1; 0068 0069 if (def_path_available) { 0070 paths.add(def_path); 0071 list.add(0, new StorageInfo(def_path, def_path_readonly, def_path_removable, def_path_removable ? cur_removable_number++ : -1)); 0072 } 0073 0074 File storage = new File("/storage/"); 0075 if (storage.exists() && storage.isDirectory() && storage.canRead()) { 0076 String mounts = null; 0077 try (Scanner scanner = new Scanner(new File("/proc/mounts"))) { 0078 mounts = scanner.useDelimiter("\\A").next(); 0079 } catch (Exception e) { 0080 Log.e("StorageHelper", "Exception while getting storageList", e); 0081 } 0082 0083 File[] dirs = storage.listFiles(); 0084 for (File dir : dirs) { 0085 //Log.e("getStorageList", "path: "+dir.getAbsolutePath()); 0086 if (dir.isDirectory() && dir.canRead() && dir.canExecute()) { 0087 String path, path2; 0088 path2 = dir.getAbsolutePath(); 0089 try { 0090 //Log.e(dir.getAbsolutePath(), dir.getCanonicalPath()); 0091 path = dir.getCanonicalPath(); 0092 } catch (Exception e) { 0093 path = path2; 0094 } 0095 if (!path.startsWith("/storage/emulated") || dirs.length == 1) { 0096 if (!paths.contains(path) && !paths.contains(path2)) { 0097 if (mounts == null || StringUtils.containsAny(mounts, path, path2)) { 0098 list.add(0, new StorageInfo(path, dir.canWrite(), true, cur_removable_number++)); 0099 paths.add(path); 0100 } 0101 } 0102 } 0103 } 0104 } 0105 } else { 0106 //Legacy code for Android < 4.0 that still didn't have /storage 0107 List<String> entries = new ArrayList<>(); 0108 try (FileReader fileReader = new FileReader("/proc/mounts")) { 0109 // The reader is buffered internally, so buffering it separately is unnecessary. 0110 final List<String> lines = IOUtils.readLines(fileReader).stream() 0111 .filter(line -> StringUtils.containsAny(line, "vfat", "exfat", "ntfs", "/mnt")) 0112 .collect(Collectors.toList()); 0113 for (String line : lines) { 0114 if (line.contains("/storage/sdcard")) 0115 entries.add(0, line); 0116 else 0117 entries.add(line); 0118 } 0119 } catch (Exception e) { 0120 Log.e("StorageHelper", "Exception", e); 0121 } 0122 0123 for (String line : entries) { 0124 StringTokenizer tokens = new StringTokenizer(line, " "); 0125 tokens.nextToken(); //device 0126 String mount_point = tokens.nextToken(); //mount point 0127 if (paths.contains(mount_point)) { 0128 continue; 0129 } 0130 tokens.nextToken(); //file system 0131 String[] flags = tokens.nextToken().split(","); //flags 0132 boolean readonly = ArrayUtils.contains(flags, "ro"); 0133 0134 if (line.contains("/dev/block/vold") && !StringUtils.containsAny(line, "/mnt/secure", 0135 "/mnt/asec", "/mnt/obb", "/dev/mapper", "tmpfs")) { 0136 paths.add(mount_point); 0137 list.add(new StorageInfo(mount_point, readonly, true, cur_removable_number++)); 0138 } 0139 } 0140 } 0141 0142 return list; 0143 } 0144 0145 /* treeUri documentId 0146 * ================================================================================================== 0147 * content://com.android.providers.downloads.documents/tree/downloads => downloads 0148 * content://com.android.externalstorage.documents/tree/1715-1D1F: => 1715-1D1F: 0149 * content://com.android.externalstorage.documents/tree/1715-1D1F:My%20Photos => 1715-1D1F:My Photos 0150 * content://com.android.externalstorage.documents/tree/primary: => primary: 0151 * content://com.android.externalstorage.documents/tree/primary:DCIM => primary:DCIM 0152 * content://com.android.externalstorage.documents/tree/primary:Download/bla => primary:Download/bla 0153 */ 0154 public static String getDisplayName(@NonNull Context context, @NonNull Uri treeUri) { 0155 List<String> pathSegments = treeUri.getPathSegments(); 0156 0157 if (!pathSegments.get(0).equals("tree")) { 0158 throw new IllegalArgumentException("treeUri is not valid"); 0159 } 0160 0161 String documentId = DocumentsContract.getTreeDocumentId(treeUri); 0162 0163 int colonIdx = pathSegments.get(1).indexOf(':'); 0164 0165 if (colonIdx >= 0) { 0166 String tree = pathSegments.get(1).substring(0, colonIdx + 1); 0167 0168 if (!documentId.equals(tree)) { 0169 return documentId.substring(tree.length()); 0170 } else { 0171 return documentId.substring(0, colonIdx); 0172 } 0173 } 0174 0175 return documentId; 0176 } 0177 }