Blame | Last modification | View Log | RSS feed
/*
* Copyright (C) 2021 by Andreas Theofilu <andreas@theosys.at>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.qtproject.theosys;
import android.content.ContentUris;
import android.content.Context;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Path;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.util.Log;
import android.webkit.MimeTypeMap;
import java.io.File;
import java.io.FileNotFoundException;
public class UriToPath
{
public static String getFileName(Uri uri, Context context)
{
Log.d("UriToPath", "Converting URI " + uri.toString() + " to a path ...");
String fileName = getRealPathFromURI(context, uri);
if (fileName == null)
{
fileName = "";
}
Log.d("UriToPath", "Found filename: " + fileName);
return fileName;
}
/**
* Get a file path from a Uri. This will get the the path for Storage Access
* Framework Documents, as well as the _data field for the MediaStore and
* other file-based ContentProviders.
*
* @param context The context.
* @param uri The Uri to query.
* @author paulburke
*/
public static String getRealPathFromURI(final Context context, final Uri uri)
{
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri))
{
Log.d("UriToPath", "Found document URI ...");
// ExternalStorageProvider
if (isExternalStorageDocument(uri))
{
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
String storageDefinition;
// content://com.android.externalstorage.documents/document/primary%3ADocuments%2Ftpanel.log
if ("primary".equalsIgnoreCase(type))
return Environment.getExternalStorageDirectory() + "/" + split[1];
// content://com.android.externalstorage.documents/document/17ED-280E%3ADocuments%2Ftpanel.log
if(Environment.isExternalStorageRemovable())
storageDefinition = "EXTERNAL_STORAGE";
else
{
storageDefinition = "SECONDARY_STORAGE";
if (System.getenv(storageDefinition) == null)
storageDefinition = "EXTERNAL_STORAGE";
}
Log.d("UriToPath", "Trying environment " + storageDefinition);
return System.getenv(storageDefinition) + "/" + split[1];
}
// DownloadsProvider
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))
{
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type))
{
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
}
else if ("video".equals(type))
{
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
}
else if ("audio".equals(type))
{
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[]
{
split[1]
};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
}
else if (isKitKat && isExternalStorageDocument(uri))
{
Log.d("UriToPath", "Found external storage document ...");
// ExternalStorageProvider
final String docId = uri.getPath(); //DocumentsContract.getDocumentId(uri);
if (docId == null)
return null;
final String[] split = docId.split(":");
final String type = split[0];
String fullPath = getPathFromExtSD(split);
if (fullPath != "")
return fullPath;
else
return null;
}
else if ("content".equalsIgnoreCase(uri.getScheme())) // MediaStore (and general)
{
Log.d("UriToPath", "Found media store or general ...");
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
else if (isPrimaryUri(uri))
{
try
{
Log.d("UriToPath", "Trying to resolve a primary URI ...");
/* Uri u = uri;
if (!uri.getPath().contains("."))
{
String s = uri.toString() + "/x.txt";
u = uri.parse(s);
Log.d("UriToPath", "Using faked uri " + u.toString());
}
*/
ContentResolver resolver = context.getContentResolver();
Bundle metadata = DocumentsContract.getDocumentMetadata(resolver, uri);
return metadata.toString();
// DocumentsContract.Path path = DocumentsContract.findDocumentPath(resolver, u);
// return path.toString();
}
catch (FileNotFoundException ignore)
{
Log.e("UriToPath", "Error: File not found!");
return null;
}
catch (IllegalArgumentException ignore)
{
Log.e("UriToPath", "Uri \"" + uri.toString() + "\" contains an illegal argument!");
return null;
}
catch (NullPointerException ignore)
{
Log.e("UriToPath", "Null pointer exception!");
return null;
}
}
try
{
return getDataColumn(context, uri, null, null);
}
catch (IllegalArgumentException ign)
{
Log.e("UriToPath", "Uri \"" + uri.toString() + "\" contains an illegal argument!");
return null;
}
}
else if ("file".equalsIgnoreCase(uri.getScheme())) // File
{
return uri.getPath();
}
return null;
}
private static boolean fileExists(String filePath)
{
File file = new File(filePath);
return file.exists();
}
private static String getPathFromExtSD(String[] pathData)
{
final String type = pathData[0];
final String relativePath = "/" + pathData[1];
String fullPath = "";
// on my Sony devices (4.4.4 & 5.1.1), `type` is a dynamic string
// something like "71F8-2C0A", some kind of unique id per storage
// don't know any API that can get the root path of that storage based on its id.
//
// so no "primary" type, but let the check here for other devices
if ("primary".equalsIgnoreCase(type))
{
fullPath = Environment.getExternalStorageDirectory() + relativePath;
if (fileExists(fullPath))
return fullPath;
}
// Environment.isExternalStorageRemovable() is `true` for external and internal storage
// so we cannot relay on it.
//
// instead, for each possible path, check if file exists
// we'll start with secondary storage as this could be our (physically) removable sd card
fullPath = System.getenv("SECONDARY_STORAGE") + relativePath;
if (fileExists(fullPath))
return fullPath;
fullPath = System.getenv("EXTERNAL_STORAGE") + relativePath;
if (fileExists(fullPath))
return fullPath;
return null;
}
/**
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
*
* @param context The context.
* @param uri The Uri to query.
* @param selection (Optional) Filter used in the query.
* @param selectionArgs (Optional) Selection arguments used in the query.
* @return The value of the _data column, which is typically a file path.
*/
public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs)
{
Log.d("UriToPath", "getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs)");
Cursor cursor = null;
final String column = "_data";
final String[] projection = { column };
try
{
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst())
{
final int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
}
catch (RuntimeException ignore)
{
Log.e("UriToPath", "Runtime exception occured!");
ignore.printStackTrace();
}
finally
{
if (cursor != null)
cursor.close();
}
return null;
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
*/
public static boolean isExternalStorageDocument(Uri uri)
{
Log.d("UriToPath", "isExternalStorageDocument(Uri uri)");
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
*/
public static boolean isDownloadsDocument(Uri uri)
{
Log.d("UriToPath", "isDownloadsDocument(Uri uri)");
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is MediaProvider.
*/
public static boolean isMediaDocument(Uri uri)
{
Log.d("UriToPath", "isMediaDocument(Uri uri)");
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
public static boolean isGooglePhotosUri(Uri uri)
{
Log.d("UriToPath", "isGooglePhotosUri(Uri uri)");
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
public static boolean isPrimaryUri(Uri uri)
{
Log.d("UriToPath", "isPrimaryUri(Uri uri) --> " + uri.getPath());
return uri.getPath().contains("tree/primary");
}
}