Fixed the issue where files would not open on Huawei phones when directly accessed from the file manager.

* Improved the method for resolving content type URIs to properly return the file path of the selected URI.
* Refined the code and methods for improved clarity in their operations.
This commit is contained in:
MohitMaliFtechiz 2024-10-04 16:17:00 +05:30 committed by Kelson
parent 777c0fff4b
commit b4bb0f98c8

View File

@ -25,6 +25,7 @@ import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.os.Environment.DIRECTORY_DOWNLOADS
import android.os.storage.StorageManager
import android.provider.DocumentsContract
import android.provider.MediaStore
@ -157,29 +158,39 @@ object FileUtils {
return null
}
/**
* Retrieves the file path from a given content URI. This method first attempts to get the path
* using the content resolver (via `contentQuery`). If that returns null or empty, it falls back
* to a secondary method to resolve the actual path.
*
* This fallback is especially necessary when:
* 1. The user clicks directly on a downloaded file from browsers, where different browsers
* return URIs using their own file providers.
* 2. On devices below Android 11, when files are clicked directly in the file manager, the content
* resolver may not be able to retrieve the path for certain URIs.
*/
private fun getFilePathOfContentUri(context: Context, uri: Uri): String? {
val filePath = contentQuery(context, uri)
return if (!filePath.isNullOrEmpty()) {
filePath
} else {
// Fallback method to get the actual path of the URI. This will be called
// when contentQuery returns null, especially in cases where the user directly clicks
// on the downloaded file within browsers (since different browsers provide URIs with their
// own file providers, the content resolver cannot directly retrieve paths for those URIs).
val fileName = getFileNameFromUri(context, uri)
getFilePathFromFileName(context, fileName)
// Fallback method to get the actual path of the URI
getActualFilePathOfContentUri(context, uri)
}
}
private fun getFilePathFromFileName(context: Context, fileName: String?): String? {
var filePath: String? = null
private fun getFullFilePathFromFilePath(
context: Context,
filePath: String?
): String? {
var actualFilePath: String? = null
getStorageVolumesList(context).forEach { volume ->
val file = File("$volume/Download/$fileName")
val file = File("$volume/$filePath")
if (file.isFileExist()) {
filePath = file.path
actualFilePath = file.path
}
}
return filePath
return actualFilePath
}
private fun getStorageVolumesList(context: Context): HashSet<String> {
@ -194,14 +205,13 @@ object FileUtils {
} ?: kotlin.run {
"/${it.getDescription(context)}/"
}
storageVolumesList.add(externalStorageName)
storageVolumesList.add("/storage$externalStorageName")
}
}
return storageVolumesList
}
private fun getFileNameFromUri(context: Context, uri: Uri?): String? {
if (uri == null) return null
private fun getFileNameFromUri(context: Context, uri: Uri): String? {
var cursor: Cursor? = null
val projection = arrayOf(
MediaStore.MediaColumns.DISPLAY_NAME
@ -224,6 +234,61 @@ object FileUtils {
}
}
/**
* Retrieves the actual file path from a given content URI. This method handles various cases based on
* the type of URI and the source (file managers, browser downloads, etc.).
*
* 1. For file managers that include the full file path in the URI (common in devices below Android 11),
* it triggers when the user clicks directly on the ZIM file in the file manager. The file manager may
* return the path with their own file provider, and we extract the path.
*
* 2. For URIs from the download provider (e.g., when opening files directly from browsers), this method
* constructs the full path using the `DIRECTORY_DOWNLOADS` directory and the file name.
*
* 3. For other URIs, it attempts to resolve the full file path from the provided URI using a custom
* method to retrieve the folder and file path.
*/
private fun getActualFilePathOfContentUri(context: Context, uri: Uri): String? {
return when {
// For file managers that provide the full path in the URI (common on devices below Android 11).
// This triggers when the user clicks directly on a ZIM file in the file manager, and the file
// manager returns the path via its own file provider.
"$uri".contains("root") && "$uri".endsWith("zim") -> {
"$uri".substringAfter("/root")
}
// Handles URIs from the download provider, commonly used when files are opened from browsers.
// Some browsers return URIs with their DownloadProvider.
isDownloadProviderUri(uri) -> {
getFullFilePathFromFilePath(
context,
"$DIRECTORY_DOWNLOADS/${getFileNameFromUri(context, uri)}"
)
}
else -> {
// Attempts to retrieve the full path from the URI using a custom method.
getFullFilePathFromFilePath(
context,
getFilePathWithFolderFromUri(uri)
)
}
}
}
private fun getFilePathWithFolderFromUri(uri: Uri): String? {
val pathSegments = uri.pathSegments
if (pathSegments.isNotEmpty()) {
// Returns the path of the folder containing the file with the specified fileName,
// from which the user selects the file.
return pathSegments.drop(1).joinToString(separator = "/")
}
return null
}
private fun isDownloadProviderUri(uri: Uri): Boolean =
"$uri".contains("DownloadProvider") || "$uri".contains("/downloads")
fun documentProviderContentQuery(
context: Context,
uri: Uri,
@ -259,7 +324,7 @@ object FileUtils {
// the file from the file manager in the downloads folder, and the URI contains a different
// document ID (particularly on tablets). See https://github.com/kiwix/kiwix-android/issues/4008
val fileName = getFileNameFromUri(context, uri)
getFilePathFromFileName(context, fileName)
getFullFilePathFromFilePath(context, "$DIRECTORY_DOWNLOADS/$fileName")
}
}