mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-08-03 18:56:44 -04:00
Fixed: In Samsung tablet of Android 14, downloaded files can not be opened via file picker or deep linking.
* When we open a file from different browsers, they provide a URI through their own file provider, and the content resolver cannot retrieve the file path for these types of URIs. To fix this issue, we have introduced a fallback method that returns the exact path of the file located in the Downloads folder. * Another issue we encountered on tablets is that the URIs are different from those on regular mobile devices, and our `documentProviderContentQuery` method could not return the path for these types of URIs from the Downloads folder. To fix this issue, we used our fallback method to retrieve the file path for these URIs.
This commit is contained in:
parent
eaa28d66df
commit
777c0fff4b
@ -406,6 +406,15 @@ class FileUtilsInstrumentationTest {
|
||||
Uri.parse(
|
||||
"${downloadUriPrefix}0"
|
||||
)
|
||||
),
|
||||
DummyUrlData(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
Uri.parse(
|
||||
"${downloadDocumentUriPrefix}msf%3A1000000057"
|
||||
)
|
||||
)
|
||||
)
|
||||
context?.let { context ->
|
||||
|
@ -21,10 +21,13 @@ import android.annotation.SuppressLint
|
||||
import android.content.ContentUris
|
||||
import android.content.Context
|
||||
import android.content.res.AssetFileDescriptor
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.os.storage.StorageManager
|
||||
import android.provider.DocumentsContract
|
||||
import android.provider.MediaStore
|
||||
import android.webkit.URLUtil
|
||||
import androidx.core.content.ContextCompat
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@ -143,7 +146,7 @@ object FileUtils {
|
||||
}
|
||||
} else if (uri.scheme != null) {
|
||||
if ("content".equals(uri.scheme, ignoreCase = true)) {
|
||||
return contentQuery(context, uri)
|
||||
return getFilePathOfContentUri(context, uri)
|
||||
} else if ("file".equals(uri.scheme, ignoreCase = true)) {
|
||||
return uri.path
|
||||
}
|
||||
@ -154,6 +157,73 @@ object FileUtils {
|
||||
return null
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFilePathFromFileName(context: Context, fileName: String?): String? {
|
||||
var filePath: String? = null
|
||||
getStorageVolumesList(context).forEach { volume ->
|
||||
val file = File("$volume/Download/$fileName")
|
||||
if (file.isFileExist()) {
|
||||
filePath = file.path
|
||||
}
|
||||
}
|
||||
return filePath
|
||||
}
|
||||
|
||||
private fun getStorageVolumesList(context: Context): HashSet<String> {
|
||||
val storageVolumes = context.getSystemService(Context.STORAGE_SERVICE) as StorageManager
|
||||
val storageVolumesList = HashSet<String>()
|
||||
storageVolumes.storageVolumes.filterNotNull().forEach {
|
||||
if (it.isPrimary) {
|
||||
storageVolumesList.add("${Environment.getExternalStorageDirectory()}/")
|
||||
} else {
|
||||
val externalStorageName = it.uuid?.let { uuid ->
|
||||
"/$uuid/"
|
||||
} ?: kotlin.run {
|
||||
"/${it.getDescription(context)}/"
|
||||
}
|
||||
storageVolumesList.add(externalStorageName)
|
||||
}
|
||||
}
|
||||
return storageVolumesList
|
||||
}
|
||||
|
||||
private fun getFileNameFromUri(context: Context, uri: Uri?): String? {
|
||||
if (uri == null) return null
|
||||
var cursor: Cursor? = null
|
||||
val projection = arrayOf(
|
||||
MediaStore.MediaColumns.DISPLAY_NAME
|
||||
)
|
||||
return try {
|
||||
cursor = context.contentResolver.query(
|
||||
uri, projection, null, null,
|
||||
null
|
||||
)
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
val index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME)
|
||||
cursor.getString(index)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} catch (ignore: Exception) {
|
||||
null
|
||||
} finally {
|
||||
cursor?.close()
|
||||
}
|
||||
}
|
||||
|
||||
fun documentProviderContentQuery(
|
||||
context: Context,
|
||||
uri: Uri,
|
||||
@ -183,7 +253,14 @@ object FileUtils {
|
||||
actualDocumentId,
|
||||
contentUriPrefixes,
|
||||
documentsContractWrapper
|
||||
)
|
||||
) ?: kotlin.run {
|
||||
// Fallback method to get the actual path of the URI. This will be called
|
||||
// when queryForActualPath returns null, especially in cases where the user directly opens
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
private fun queryForActualPath(
|
||||
|
Loading…
x
Reference in New Issue
Block a user