|  | @@ -9,8 +9,15 @@ import android.os.Build;
 | 
	
		
			
				|  |  |  import android.os.Environment;
 | 
	
		
			
				|  |  |  import android.provider.DocumentsContract;
 | 
	
		
			
				|  |  |  import android.provider.MediaStore;
 | 
	
		
			
				|  |  | +import android.text.TextUtils;
 | 
	
		
			
				|  |  | +import android.util.Log;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +import java.io.File;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// The following refs
 | 
	
		
			
				|  |  |  // https://stackoverflow.com/questions/29713587/how-to-get-the-real-path-with-action-open-document-tree-intent
 | 
	
		
			
				|  |  |  // https://gist.github.com/asifmujteba/d89ba9074bc941de1eaa#file-asfurihelper
 | 
	
		
			
				|  |  | +// with bug fixes and patches.
 | 
	
		
			
				|  |  |  public class FileUtil {
 | 
	
		
			
				|  |  |      @TargetApi(Build.VERSION_CODES.KITKAT)
 | 
	
		
			
				|  |  |      public static String getPath(final Context context, final Uri uri) {
 | 
	
	
		
			
				|  | @@ -24,24 +31,64 @@ public class FileUtil {
 | 
	
		
			
				|  |  |                  final String docId = DocumentsContract.getDocumentId(uri);
 | 
	
		
			
				|  |  |                  final String[] split = docId.split(":");
 | 
	
		
			
				|  |  |                  final String type = split[0];
 | 
	
		
			
				|  |  | +                // NOTE: It's not a good idea to use storage root as Graph root.
 | 
	
		
			
				|  |  | +                String remain = "";
 | 
	
		
			
				|  |  | +                if (split.length == 2) {
 | 
	
		
			
				|  |  | +                    remain = split[1];
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                  if ("primary".equalsIgnoreCase(type)) {
 | 
	
		
			
				|  |  | -                    return Environment.getExternalStorageDirectory() + "/" + split[1];
 | 
	
		
			
				|  |  | +                    return Environment.getExternalStorageDirectory() + "/" + remain;
 | 
	
		
			
				|  |  | +                } else if ("home".equalsIgnoreCase(type)) {
 | 
	
		
			
				|  |  | +                    return Environment.getExternalStorageDirectory() + "/Documents/" + remain;
 | 
	
		
			
				|  |  | +                } else if ("downloads".equalsIgnoreCase(type)) {
 | 
	
		
			
				|  |  | +                    return Environment.getExternalStorageDirectory() + "/Download/" + remain; // No 's' here
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                // TODO handle non-primary volumes
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -            // DownloadsProvider
 | 
	
		
			
				|  |  | -            else if (isDownloadsDocument(uri)) {
 | 
	
		
			
				|  |  | +                File dir = null;
 | 
	
		
			
				|  |  | +                File[] mdirs = context.getExternalMediaDirs();
 | 
	
		
			
				|  |  | +                for (File mdir : mdirs) {
 | 
	
		
			
				|  |  | +                    String extPath = mdir.getAbsolutePath();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                    if (extPath.contains("/" + type + "/")) {
 | 
	
		
			
				|  |  | +                        dir = new File(extPath.substring(0, extPath.indexOf("/Android")) + "/" + remain);
 | 
	
		
			
				|  |  | +                        if (dir.exists()) {
 | 
	
		
			
				|  |  | +                            return dir.getAbsolutePath();
 | 
	
		
			
				|  |  | +                        }
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                // FIXME: The following attempt cannot handle same directory name on different devices!
 | 
	
		
			
				|  |  | +                // attempt 1
 | 
	
		
			
				|  |  | +                dir = new File("/storage/" + type + "/" + remain);
 | 
	
		
			
				|  |  | +                if (dir.exists()) {
 | 
	
		
			
				|  |  | +                    return dir.getAbsolutePath();
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                // attempt 3
 | 
	
		
			
				|  |  | +                dir = new File("/mnt/media_rw/" + type + "/" + remain);
 | 
	
		
			
				|  |  | +                if (dir.exists()) {
 | 
	
		
			
				|  |  | +                    return dir.getAbsolutePath();
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                // attempt 3
 | 
	
		
			
				|  |  | +                dir = new File("/mnt/" + type + "/" + remain);
 | 
	
		
			
				|  |  | +                if (dir.exists()) {
 | 
	
		
			
				|  |  | +                    return dir.getAbsolutePath();
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +                // TODO: other cases
 | 
	
		
			
				|  |  | +            } else if (isDownloadsDocument(uri)) {
 | 
	
		
			
				|  |  |                  final String id = DocumentsContract.getDocumentId(uri);
 | 
	
		
			
				|  |  | -                final Uri contentUri = ContentUris.withAppendedId(
 | 
	
		
			
				|  |  | -                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                return getDataColumn(context, contentUri, null, null);
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -            // MediaProvider
 | 
	
		
			
				|  |  | -            else if (isMediaDocument(uri)) {
 | 
	
		
			
				|  |  | +                if (!TextUtils.isEmpty(id)) {
 | 
	
		
			
				|  |  | +                    if (id.startsWith("raw:")) {
 | 
	
		
			
				|  |  | +                        return id.replaceFirst("raw:", "");
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                    try {
 | 
	
		
			
				|  |  | +                        final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
 | 
	
		
			
				|  |  | +                        return getDataColumn(context, contentUri, null, null);
 | 
	
		
			
				|  |  | +                    } catch (NumberFormatException e) {
 | 
	
		
			
				|  |  | +                        return null;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +            } else if (isMediaDocument(uri)) {
 | 
	
		
			
				|  |  |                  final String docId = DocumentsContract.getDocumentId(uri);
 | 
	
		
			
				|  |  |                  final String[] split = docId.split(":");
 | 
	
		
			
				|  |  |                  final String type = split[0];
 | 
	
	
		
			
				|  | @@ -56,24 +103,49 @@ public class FileUtil {
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                  final String selection = "_id=?";
 | 
	
		
			
				|  |  | -                final String[] selectionArgs = new String[] {
 | 
	
		
			
				|  |  | +                final String[] selectionArgs = new String[]{
 | 
	
		
			
				|  |  |                          split[1]
 | 
	
		
			
				|  |  |                  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                  return getDataColumn(context, contentUri, selection, selectionArgs);
 | 
	
		
			
				|  |  | +            } else if (isTermuxDocument(uri)) {
 | 
	
		
			
				|  |  | +                String docId = DocumentsContract.getDocumentId(uri);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                // Ref: https://github.com/termux/termux-app/blob/master/app/src/main/java/com/termux/app/TermuxInstaller.java
 | 
	
		
			
				|  |  | +                if (docId.startsWith("/")) {
 | 
	
		
			
				|  |  | +                    if (docId.contains("/com.termux/files/home/storage/")) {
 | 
	
		
			
				|  |  | +                        String remain = docId.replaceFirst("^.*?com\\.termux/files/home/storage/[^/]+/", "");
 | 
	
		
			
				|  |  | +                        if (docId.contains("/storage/external-1")) { // TODO: Support external-2 or more
 | 
	
		
			
				|  |  | +                            File[] dirs = context.getExternalFilesDirs(remain);
 | 
	
		
			
				|  |  | +                            if (dirs != null && dirs.length >= 2) {
 | 
	
		
			
				|  |  | +                                docId = dirs[1].getAbsolutePath();
 | 
	
		
			
				|  |  | +                            }
 | 
	
		
			
				|  |  | +                        } else if (docId.contains("/storage/media-1")) {
 | 
	
		
			
				|  |  | +                            File[] dirs = context.getExternalMediaDirs();
 | 
	
		
			
				|  |  | +                            if (dirs != null && dirs.length >= 2) {
 | 
	
		
			
				|  |  | +                                docId = dirs[1].getAbsolutePath() + "/" + remain;
 | 
	
		
			
				|  |  | +                            }
 | 
	
		
			
				|  |  | +                        } else if (docId.contains("/storage/downloads")) {
 | 
	
		
			
				|  |  | +                            docId = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/" + remain;
 | 
	
		
			
				|  |  | +                        } else if (docId.contains("/storage/shared")) {
 | 
	
		
			
				|  |  | +                            docId = Environment.getExternalStorageDirectory() + "/" + remain;
 | 
	
		
			
				|  |  | +                        }
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                    File dir = new File(docId);
 | 
	
		
			
				|  |  | +                    if (dir.exists()) {
 | 
	
		
			
				|  |  | +                        return dir.getAbsolutePath();
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                    Log.e("Logseq/FileUtil", "Handle termux content url failed: " + docId);
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                // FIXME: Are there any other cases?
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        // MediaStore (and general)
 | 
	
		
			
				|  |  | -        else if ("content".equalsIgnoreCase(uri.getScheme())) {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +        } else if ("content".equalsIgnoreCase(uri.getScheme())) {
 | 
	
		
			
				|  |  |              // Return the remote address
 | 
	
		
			
				|  |  |              if (isGooglePhotosUri(uri))
 | 
	
		
			
				|  |  |                  return uri.getLastPathSegment();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              return getDataColumn(context, uri, null, null);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        // File
 | 
	
		
			
				|  |  | -        else if ("file".equalsIgnoreCase(uri.getScheme())) {
 | 
	
		
			
				|  |  | +        } else if ("file".equalsIgnoreCase(uri.getScheme())) {
 | 
	
		
			
				|  |  |              return uri.getPath();
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -135,4 +207,8 @@ public class FileUtil {
 | 
	
		
			
				|  |  |      public static boolean isGooglePhotosUri(Uri uri) {
 | 
	
		
			
				|  |  |          return "com.google.android.apps.photos.content".equals(uri.getAuthority());
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    public static boolean isTermuxDocument(Uri uri) {
 | 
	
		
			
				|  |  | +        return "com.termux.documents".equals(uri.getAuthority());
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  }
 |