├── README.md └── javautil └── FileUtils.java /README.md: -------------------------------------------------------------------------------- 1 | # Android Filename Picker using Java 2 | A library for getting filenames in an Android device 3 | -------------------------------------------------------------------------------- /javautil/FileUtils.java: -------------------------------------------------------------------------------- 1 | package javautil; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.ContentUris; 5 | import android.content.Context; 6 | import android.database.Cursor; 7 | import android.net.Uri; 8 | import android.os.Build; 9 | import android.os.Environment; 10 | import android.provider.DocumentsContract; 11 | import android.provider.MediaStore; 12 | import android.provider.OpenableColumns; 13 | import android.text.TextUtils; 14 | import android.util.Log; 15 | 16 | import java.io.File; 17 | import java.io.FileOutputStream; 18 | import java.io.InputStream; 19 | import java.util.UUID; 20 | 21 | public class FileUtils { 22 | public static String FALLBACK_COPY_FOLDER = "upload_part"; 23 | 24 | private static String TAG = "FileUtils"; 25 | 26 | private static Uri contentUri = null; 27 | 28 | Context context; 29 | 30 | public FileUtils(Context context) { 31 | this.context = context; 32 | } 33 | 34 | @SuppressLint("NewApi") 35 | public String getPath(final Uri uri) { 36 | // check here to KITKAT or new version 37 | final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; 38 | String selection = null; 39 | String[] selectionArgs = null; 40 | // DocumentProvider 41 | 42 | if (isKitKat) { 43 | // ExternalStorageProvider 44 | 45 | if (isExternalStorageDocument(uri)) { 46 | final String docId = DocumentsContract.getDocumentId(uri); 47 | final String[] split = docId.split(":"); 48 | final String type = split[0]; 49 | 50 | String fullPath = getPathFromExtSD(split); 51 | 52 | if (fullPath == null || !fileExists(fullPath)) { 53 | Log.d(TAG, "Copy files as a fallback"); 54 | fullPath = copyFileToInternalStorage(uri, FALLBACK_COPY_FOLDER); 55 | } 56 | 57 | if (fullPath != "") { 58 | return fullPath; 59 | } else { 60 | return null; 61 | } 62 | } 63 | 64 | 65 | // DownloadsProvider 66 | 67 | if (isDownloadsDocument(uri)) { 68 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 69 | final String id; 70 | Cursor cursor = null; 71 | try { 72 | cursor = context.getContentResolver().query(uri, new String[] { 73 | MediaStore.MediaColumns.DISPLAY_NAME 74 | }, null, null, null); 75 | if (cursor != null && cursor.moveToFirst()) { 76 | String fileName = cursor.getString(0); 77 | String path = Environment.getExternalStorageDirectory().toString() + "/Download/" + fileName; 78 | if (!TextUtils.isEmpty(path)) { 79 | return path; 80 | } 81 | } 82 | } finally { 83 | if (cursor != null) 84 | cursor.close(); 85 | } 86 | id = DocumentsContract.getDocumentId(uri); 87 | 88 | if (!TextUtils.isEmpty(id)) { 89 | if (id.startsWith("raw:")) { 90 | return id.replaceFirst("raw:", ""); 91 | } 92 | String[] contentUriPrefixesToTry = new String[] { 93 | "content://downloads/public_downloads", 94 | "content://downloads/my_downloads" 95 | }; 96 | 97 | for (String contentUriPrefix: contentUriPrefixesToTry) { 98 | try { 99 | final Uri contentUri = ContentUris.withAppendedId(Uri.parse(contentUriPrefix), Long.valueOf(id)); 100 | 101 | return getDataColumn(context, contentUri, null, null); 102 | } catch (NumberFormatException e) { 103 | //In Android 8 and Android P the id is not a number 104 | return uri.getPath().replaceFirst("^/document/raw:", "").replaceFirst("^raw:", ""); 105 | } 106 | } 107 | } 108 | } else { 109 | final String id = DocumentsContract.getDocumentId(uri); 110 | 111 | if (id.startsWith("raw:")) { 112 | return id.replaceFirst("raw:", ""); 113 | } 114 | try { 115 | contentUri = ContentUris.withAppendedId( 116 | Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); 117 | } catch (NumberFormatException e) { 118 | e.printStackTrace(); 119 | } 120 | 121 | if (contentUri != null) 122 | return getDataColumn(context, contentUri, null, null); 123 | } 124 | } 125 | 126 | 127 | // MediaProvider 128 | if (isMediaDocument(uri)) { 129 | final String docId = DocumentsContract.getDocumentId(uri); 130 | final String[] split = docId.split(":"); 131 | final String type = split[0]; 132 | 133 | Log.d(TAG, "MEDIA DOCUMENT TYPE: " + type); 134 | 135 | Uri contentUri = null; 136 | 137 | if ("image".equals(type)) { 138 | contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; 139 | } else if ("video".equals(type)) { 140 | contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; 141 | } else if ("audio".equals(type)) { 142 | contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; 143 | } else if ("document".equals(type)) { 144 | contentUri = MediaStore.Files.getContentUri(MediaStore.getVolumeName(uri)); 145 | } 146 | 147 | selection = "_id=?"; 148 | selectionArgs = new String[] { 149 | split[1] 150 | }; 151 | 152 | 153 | return getDataColumn(context, contentUri, selection, selectionArgs); 154 | } 155 | 156 | if (isGoogleDriveUri(uri)) { 157 | return getDriveFilePath(uri); 158 | } 159 | 160 | if (isWhatsAppFile(uri)) { 161 | return getFilePathForWhatsApp(uri); 162 | } 163 | 164 | if ("content".equalsIgnoreCase(uri.getScheme())) { 165 | if (isGooglePhotosUri(uri)) { 166 | return uri.getLastPathSegment(); 167 | } 168 | 169 | if (isGoogleDriveUri(uri)) { 170 | return getDriveFilePath(uri); 171 | } 172 | 173 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { 174 | // return getFilePathFromURI(context,uri); 175 | return copyFileToInternalStorage(uri, FALLBACK_COPY_FOLDER); 176 | // return getRealPathFromURI(context,uri); 177 | } else { 178 | return getDataColumn(context, uri, null, null); 179 | } 180 | 181 | } 182 | 183 | if ("file".equalsIgnoreCase(uri.getScheme())) { 184 | return uri.getPath(); 185 | } 186 | } else { 187 | if (isWhatsAppFile(uri)) { 188 | return getFilePathForWhatsApp(uri); 189 | } 190 | 191 | if ("content".equalsIgnoreCase(uri.getScheme())) { 192 | String[] projection = { 193 | MediaStore.Images.Media.DATA 194 | }; 195 | Cursor cursor = null; 196 | 197 | try { 198 | cursor = context.getContentResolver() 199 | .query(uri, projection, selection, selectionArgs, null); 200 | int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); 201 | 202 | if (cursor.moveToFirst()) { 203 | return cursor.getString(column_index); 204 | } 205 | } catch (Exception e) { 206 | e.printStackTrace(); 207 | } 208 | } 209 | } 210 | 211 | return copyFileToInternalStorage(uri, FALLBACK_COPY_FOLDER); 212 | } 213 | 214 | private static boolean fileExists(String filePath) { 215 | File file = new File(filePath); 216 | 217 | return file.exists(); 218 | } 219 | 220 | private static String getPathFromExtSD(String[] pathData) { 221 | final String type = pathData[0]; 222 | final String relativePath = File.separator + pathData[1]; 223 | String fullPath = ""; 224 | 225 | 226 | Log.d(TAG, "MEDIA EXTSD TYPE: " + type); 227 | Log.d(TAG, "Relative path: " + relativePath); 228 | // on my Sony devices (4.4.4 & 5.1.1), `type` is a dynamic string 229 | // something like "71F8-2C0A", some kind of unique id per storage 230 | // don't know any API that can get the root path of that storage based on its id. 231 | // 232 | // so no "primary" type, but let the check here for other devices 233 | if ("primary".equalsIgnoreCase(type)) { 234 | fullPath = Environment.getExternalStorageDirectory() + relativePath; 235 | if (fileExists(fullPath)) { 236 | return fullPath; 237 | } 238 | } 239 | 240 | if ("home".equalsIgnoreCase(type)) { 241 | fullPath = "/storage/emulated/0/Documents" + relativePath; 242 | if (fileExists(fullPath)) { 243 | return fullPath; 244 | } 245 | } 246 | 247 | // Environment.isExternalStorageRemovable() is `true` for external and internal storage 248 | // so we cannot relay on it. 249 | // 250 | // instead, for each possible path, check if file exists 251 | // we'll start with secondary storage as this could be our (physically) removable sd card 252 | fullPath = System.getenv("SECONDARY_STORAGE") + relativePath; 253 | if (fileExists(fullPath)) { 254 | return fullPath; 255 | } 256 | 257 | fullPath = System.getenv("EXTERNAL_STORAGE") + relativePath; 258 | if (fileExists(fullPath)) { 259 | return fullPath; 260 | } 261 | 262 | return null; 263 | } 264 | 265 | private String getDriveFilePath(Uri uri) { 266 | Uri returnUri = uri; 267 | Cursor returnCursor = context.getContentResolver().query(returnUri, null, null, null, null); 268 | /* 269 | * Get the column indexes of the data in the Cursor, 270 | * * move to the first row in the Cursor, get the data, 271 | * * and display it. 272 | * */ 273 | int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); 274 | int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE); 275 | returnCursor.moveToFirst(); 276 | String name = (returnCursor.getString(nameIndex)); 277 | String size = (Long.toString(returnCursor.getLong(sizeIndex))); 278 | File file = new File(context.getCacheDir(), name); 279 | try { 280 | InputStream inputStream = context.getContentResolver().openInputStream(uri); 281 | FileOutputStream outputStream = new FileOutputStream(file); 282 | int read = 0; 283 | int maxBufferSize = 1 * 1024 * 1024; 284 | int bytesAvailable = inputStream.available(); 285 | 286 | //int bufferSize = 1024; 287 | int bufferSize = Math.min(bytesAvailable, maxBufferSize); 288 | 289 | final byte[] buffers = new byte[bufferSize]; 290 | while ((read = inputStream.read(buffers)) != -1) { 291 | outputStream.write(buffers, 0, read); 292 | } 293 | Log.e(TAG, "Size " + file.length()); 294 | inputStream.close(); 295 | outputStream.close(); 296 | Log.e(TAG, "Path " + file.getPath()); 297 | Log.e(TAG, "Size " + file.length()); 298 | } catch (Exception e) { 299 | Log.e(TAG, e.getMessage()); 300 | } 301 | 302 | return file.getPath(); 303 | } 304 | 305 | /*** 306 | * Used for Android Q+ 307 | * @param uri 308 | * @param newDirName if you want to create a directory, you can set this variable 309 | * @return 310 | */ 311 | private String copyFileToInternalStorage(Uri uri, String newDirName) { 312 | Uri returnUri = uri; 313 | 314 | Cursor returnCursor = context.getContentResolver().query(returnUri, new String[] { 315 | OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE 316 | }, null, null, null); 317 | 318 | 319 | /* 320 | * Get the column indexes of the data in the Cursor, 321 | * * move to the first row in the Cursor, get the data, 322 | * * and display it. 323 | * */ 324 | int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); 325 | int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE); 326 | returnCursor.moveToFirst(); 327 | String name = (returnCursor.getString(nameIndex)); 328 | String size = (Long.toString(returnCursor.getLong(sizeIndex))); 329 | 330 | File output; 331 | if (!newDirName.equals("")) { 332 | String random_collision_avoidance = UUID.randomUUID().toString(); 333 | 334 | File dir = new File(context.getFilesDir() + File.separator + newDirName + File.separator + random_collision_avoidance); 335 | if (!dir.exists()) { 336 | dir.mkdirs(); 337 | } 338 | output = new File(context.getFilesDir() + File.separator + newDirName + File.separator + random_collision_avoidance + File.separator + name); 339 | } else { 340 | output = new File(context.getFilesDir() + File.separator + name); 341 | } 342 | 343 | try { 344 | InputStream inputStream = context.getContentResolver().openInputStream(uri); 345 | FileOutputStream outputStream = new FileOutputStream(output); 346 | int read = 0; 347 | int bufferSize = 1024; 348 | final byte[] buffers = new byte[bufferSize]; 349 | 350 | while ((read = inputStream.read(buffers)) != -1) { 351 | outputStream.write(buffers, 0, read); 352 | } 353 | 354 | inputStream.close(); 355 | outputStream.close(); 356 | } catch (Exception e) { 357 | Log.e(TAG, e.getMessage()); 358 | } 359 | 360 | return output.getPath(); 361 | } 362 | 363 | private String getFilePathForWhatsApp(Uri uri) { 364 | return copyFileToInternalStorage(uri, "whatsapp"); 365 | } 366 | 367 | private String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { 368 | Cursor cursor = null; 369 | final String column = "_data"; 370 | final String[] projection = { 371 | column 372 | }; 373 | 374 | try { 375 | cursor = context.getContentResolver().query(uri, projection, 376 | selection, selectionArgs, null); 377 | 378 | if (cursor != null && cursor.moveToFirst()) { 379 | final int index = cursor.getColumnIndexOrThrow(column); 380 | return cursor.getString(index); 381 | } 382 | } finally { 383 | if (cursor != null) 384 | cursor.close(); 385 | } 386 | 387 | return null; 388 | } 389 | 390 | private static boolean isExternalStorageDocument(Uri uri) { 391 | return "com.android.externalstorage.documents".equals(uri.getAuthority()); 392 | } 393 | 394 | private static boolean isDownloadsDocument(Uri uri) { 395 | return "com.android.providers.downloads.documents".equals(uri.getAuthority()); 396 | } 397 | 398 | private boolean isMediaDocument(Uri uri) { 399 | return "com.android.providers.media.documents".equals(uri.getAuthority()); 400 | } 401 | 402 | private boolean isGooglePhotosUri(Uri uri) { 403 | return "com.google.android.apps.photos.content".equals(uri.getAuthority()); 404 | } 405 | 406 | public boolean isWhatsAppFile(Uri uri) { 407 | return "com.whatsapp.provider.media".equals(uri.getAuthority()); 408 | } 409 | 410 | private boolean isGoogleDriveUri(Uri uri) { 411 | return "com.google.android.apps.docs.storage".equals(uri.getAuthority()) || "com.google.android.apps.docs.storage.legacy".equals(uri.getAuthority()); 412 | } 413 | } --------------------------------------------------------------------------------