diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreWebViewClient.java b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreWebViewClient.java index a2ca2a179..093524ad4 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreWebViewClient.java +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreWebViewClient.java @@ -18,8 +18,10 @@ package org.kiwix.kiwixmobile.core.main; +import android.content.Context; import android.content.Intent; import android.net.Uri; +import android.os.Build; import android.util.Log; import android.webkit.MimeTypeMap; import android.webkit.WebResourceRequest; @@ -27,9 +29,12 @@ import android.webkit.WebResourceResponse; import android.webkit.WebView; import android.webkit.WebViewClient; import androidx.annotation.Nullable; +import androidx.core.content.FileProvider; +import java.io.File; import java.util.HashMap; import org.kiwix.kiwixmobile.core.CoreApp; import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer; +import org.kiwix.kiwixmobile.core.utils.files.FileUtils; import static org.kiwix.kiwixmobile.core.reader.ZimFileReader.CONTENT_PREFIX; import static org.kiwix.kiwixmobile.core.reader.ZimFileReader.UI_URI; @@ -97,11 +102,22 @@ public class CoreWebViewClient extends WebViewClient { private boolean handleEpubAndPdf(String url) { String extension = MimeTypeMap.getFileExtensionFromUrl(url); if (DOCUMENT_TYPES.containsKey(extension)) { - Intent intent = new Intent(Intent.ACTION_VIEW); - Uri uri = Uri.parse(url); - intent.setDataAndType(uri, DOCUMENT_TYPES.get(extension)); - intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); - callback.openExternalUrl(intent); + File savedFile = FileUtils.downloadFileFromUrl(url, null, zimReaderContainer); + if (savedFile != null && savedFile.exists()) { + Context context = CoreApp.getInstance(); + Uri uri = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N + ? FileProvider.getUriForFile( + context, + context.getPackageName() + ".fileprovider", savedFile) + : Uri.fromFile(savedFile); + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setDataAndType(uri, DOCUMENT_TYPES.get(extension)); + intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + } + callback.openExternalUrl(intent); + } return true; } return false; diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/KiwixWebView.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/KiwixWebView.kt index 114779636..ec91990ab 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/KiwixWebView.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/KiwixWebView.kt @@ -20,12 +20,9 @@ package org.kiwix.kiwixmobile.core.main import android.annotation.SuppressLint import android.app.Activity import android.content.Context -import android.net.Uri -import android.os.Environment import android.os.Handler import android.os.Message import android.util.AttributeSet -import android.util.Log import android.view.ContextMenu import android.view.View import android.view.ViewGroup @@ -40,8 +37,7 @@ import org.kiwix.kiwixmobile.core.extensions.toast import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer import org.kiwix.kiwixmobile.core.utils.LanguageUtils.Companion.getCurrentLocale import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil -import java.io.File -import java.io.IOException +import org.kiwix.kiwixmobile.core.utils.files.FileUtils import javax.inject.Inject private const val INITIAL_SCALE = 100 @@ -147,39 +143,16 @@ open class KiwixWebView @SuppressLint("SetJavaScriptEnabled") constructor( internal class SaveHandler(private val zimReaderContainer: ZimReaderContainer) : Handler() { - private fun getDecodedFileName(url: String?, src: String?): String = - url?.substringAfterLast("/", "") - ?.takeIf { it.contains(".") } - ?: src?.substringAfterLast("/", "") - ?.substringAfterLast("%3A") ?: "" @SuppressWarnings("NestedBlockDepth") override fun handleMessage(msg: Message) { val url = msg.data["url"] as? String val src = msg.data["src"] as? String if (url != null || src != null) { - val fileName = getDecodedFileName(url, src) - var root = - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) - if (instance.externalMediaDirs.isNotEmpty()) { - root = instance.externalMediaDirs[0] - } - val fileToSave = sequence { - yield(File(root, fileName)) - yieldAll( - generateSequence(1) { it + 1 }.map { - File(root, fileName.replace(".", "_$it.")) - } - ) - }.first { !it.exists() } - val source = Uri.parse(src) - try { - zimReaderContainer.load("$source", emptyMap()).data.use { inputStream -> - fileToSave.outputStream().use { inputStream.copyTo(it) } - } - instance.toast(instance.getString(R.string.save_media_saved, fileToSave.name)) - } catch (e: IOException) { - Log.w("kiwix", "Couldn't save image", e) + val savedFile = FileUtils.downloadFileFromUrl(url, src, zimReaderContainer) + savedFile?.let { + instance.toast(instance.getString(R.string.save_media_saved, it.name)) + } ?: run { instance.toast(R.string.save_media_error) } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/files/FileUtils.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/files/FileUtils.kt index cbf63f836..4a339a89b 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/files/FileUtils.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/files/FileUtils.kt @@ -28,11 +28,13 @@ import android.provider.DocumentsContract import android.util.Log import android.widget.Toast import androidx.documentfile.provider.DocumentFile +import org.kiwix.kiwixmobile.core.CoreApp import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.downloader.ChunkUtils import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity.Book import org.kiwix.kiwixmobile.core.extensions.get import org.kiwix.kiwixmobile.core.extensions.toast +import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer import java.io.BufferedReader import java.io.File import java.io.IOException @@ -288,4 +290,48 @@ object FileUtils { } return null } + + /** + * Returns the file name from the url or src. In url it gets the file name from the last '/' and + * if it contains '.'. If the url is null then it'll get the file name from the last '/'. + * If the url and src doesn't exist it returns the empty string. + */ + fun getDecodedFileName(url: String?, src: String?): String = + url?.substringAfterLast("/", "") + ?.takeIf { it.contains(".") } + ?: src?.substringAfterLast("/", "") + ?.substringAfterLast("%3A") ?: "" + + @JvmStatic fun downloadFileFromUrl( + url: String?, + src: String?, + zimReaderContainer: ZimReaderContainer + ): File? { + val fileName = getDecodedFileName(url, src) + var root = + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + if (CoreApp.instance.externalMediaDirs.isNotEmpty()) { + root = CoreApp.instance.externalMediaDirs[0] + } + val fileToSave = sequence { + yield(File(root, fileName)) + yieldAll( + generateSequence(1) { it + 1 }.map { + File( + root, fileName.replace(".", "_$it.") + ) + } + ) + }.first { !it.exists() } + val source = if (url == null) Uri.parse(src) else Uri.parse(url) + return try { + zimReaderContainer.load("$source", emptyMap()).data.use { inputStream -> + fileToSave.outputStream().use { inputStream.copyTo(it) } + } + fileToSave + } catch (e: IOException) { + Log.w("kiwix", "Couldn't save file", e) + null + } + } } diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/utils/files/FileUtilsTest.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/utils/files/FileUtilsTest.kt index 4058b9ee5..298c738c2 100644 --- a/core/src/test/java/org/kiwix/kiwixmobile/core/utils/files/FileUtilsTest.kt +++ b/core/src/test/java/org/kiwix/kiwixmobile/core/utils/files/FileUtilsTest.kt @@ -88,4 +88,44 @@ class FileUtilsTest { every { mockFile.path } returns "$fileName$extension" every { mockFile.exists() } returns fileExists } + + @Test + fun `test decode file name`() { + val fileName = + FileUtils.getDecodedFileName( + url = "https://kiwix.org/contributors/contributors_list.pdf", + src = null + ) + assertThat(fileName).isEqualTo("contributors_list.pdf") + } + + @Test + fun `test file name if extension doesn't exist`() { + val fileName = FileUtils.getDecodedFileName(url = "https://kiwix.org/contributors/", src = null) + assertThat(fileName).isEqualTo("") + } + + @Test + fun `test file name if the url and src doesn't exist`() { + val fileName = FileUtils.getDecodedFileName(url = null, src = null) + assertThat(fileName).isEqualTo("") + } + + @Test + fun `test file name if only file name exist`() { + val fileName = FileUtils.getDecodedFileName(src = "android_tutorials.pdf", url = null) + assertThat(fileName).isEqualTo("") + } + + @Test + fun `test file name if url doesn't exist`() { + val fileName = FileUtils.getDecodedFileName(url = null, src = "/html/images/test.png") + assertThat(fileName).isEqualTo("test.png") + } + + @Test + fun `test file name if url and src's extension doesn't exist`() { + val fileName = FileUtils.getDecodedFileName(url = null, src = "/html/images/") + assertThat(fileName).isEqualTo("") + } }