package com.reactnativereadium.utils import android.content.ContentUris import android.content.Context import android.net.Uri import android.provider.DocumentsContract import android.provider.MediaStore import android.text.TextUtils import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import com.reactnativereadium.utils.extensions.toFile import java.io.File import java.io.FileNotFoundException import java.io.InputStream import java.net.URL object ContentResolverUtil { suspend fun getContentInputStream(context: Context, uri: Uri, publicationPath: String) { withContext(Dispatchers.IO) { try { val path = getRealPath(context, uri) if (path != null) { File(path).copyTo(File(publicationPath)) } else { val input = URL(uri.toString()).openStream() input.toFile(publicationPath) } } catch (e: Exception) { val input = getInputStream(context, uri) input?.let { input.toFile(publicationPath) } } } } private fun getInputStream(context: Context, uri: Uri): InputStream? { return try { context.contentResolver.openInputStream(uri) } catch (e: FileNotFoundException) { e.printStackTrace() null } } private fun getRealPath(context: Context, uri: Uri): String? { // DocumentProvider if (DocumentsContract.isDocumentUri(context, uri)) { // ExternalStorageProvider if (isExternalStorageDocument(uri)) { val docId = DocumentsContract.getDocumentId(uri) val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() val type = split[0] if ("primary".equals(type, ignoreCase = true)) { return context.getExternalFilesDir(null).toString() + "/" + split[1] } // TODO handle non-primary volumes } else if (isDownloadsDocument(uri)) { val id = DocumentsContract.getDocumentId(uri) if (!TextUtils.isEmpty(id)) { if (id.startsWith("raw:")) { return id.replaceFirst("raw:".toRegex(), "") } return try { val contentUri = ContentUris.withAppendedId( Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id)) getDataColumn(context, contentUri, null, null) } catch (e: NumberFormatException) { null } } } else if (isMediaDocument(uri)) { val docId = DocumentsContract.getDocumentId(uri) val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() val type = split[0] var contentUri: Uri? = null when (type) { "image" -> contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI "video" -> contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI "audio" -> contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI } val selection = "_id=?" val selectionArgs = arrayOf(split[1]) return getDataColumn(context, contentUri, selection, selectionArgs) } } else if ("content".equals(uri.scheme!!, ignoreCase = true)) { // Return the remote address return getDataColumn(context, uri, null, null) } else if ("file".equals(uri.scheme!!, ignoreCase = true)) { return uri.path } 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. */ private fun getDataColumn(context: Context, uri: Uri?, selection: String?, selectionArgs: Array?): String? { val column = "_data" val projection = arrayOf(column) context.contentResolver.query(uri!!, projection, selection, selectionArgs, null).use { cursor -> cursor?.let { if (cursor.moveToFirst()) { val index = cursor.getColumnIndexOrThrow(column) return cursor.getString(index) } } } return null } /** * @param uri The Uri to check. * @return Whether the Uri authority is ExternalStorageProvider. */ private fun isExternalStorageDocument(uri: Uri): Boolean { return "com.android.externalstorage.documents" == uri.authority } /** * @param uri The Uri to check. * @return Whether the Uri authority is DownloadsProvider. */ private fun isDownloadsDocument(uri: Uri): Boolean { return "com.android.providers.downloads.documents" == uri.authority } /** * @param uri The Uri to check. * @return Whether the Uri authority is MediaProvider. */ private fun isMediaDocument(uri: Uri): Boolean { return "com.android.providers.media.documents" == uri.authority } }