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 deleted file mode 100644 index 60255cd91..000000000 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreWebViewClient.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Kiwix Android - * Copyright (c) 2019 Kiwix - * 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, see . - * - */ - -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; -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.SharedPreferenceUtil; -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; -import static org.kiwix.kiwixmobile.core.utils.ConstantsKt.TAG_KIWIX; - -public class CoreWebViewClient extends WebViewClient { - private static final HashMap DOCUMENT_TYPES = new HashMap() {{ - put("epub", "application/epub+zip"); - put("pdf", "application/pdf"); - }}; - protected final WebViewCallback callback; - protected final ZimReaderContainer zimReaderContainer; - private final SharedPreferenceUtil sharedPreferenceUtil; - private static String[] LEGACY_CONTENT_PREFIXES = new String[] { - "zim://content/", - Uri.parse("content://" + CoreApp.getInstance().getPackageName() + ".zim.base/").toString() - }; - private String urlWithAnchor; - - public CoreWebViewClient( - WebViewCallback callback, - ZimReaderContainer zimReaderContainer, - SharedPreferenceUtil sharedPreferenceUtil) { - this.callback = callback; - this.zimReaderContainer = zimReaderContainer; - this.sharedPreferenceUtil = sharedPreferenceUtil; - } - - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - callback.webViewUrlLoading(); - url = convertLegacyUrl(url); - urlWithAnchor = url.contains("#") ? url : null; - if (zimReaderContainer.isRedirect(url)) { - if (handleEpubAndPdf(url)) { - return true; - } - view.loadUrl(zimReaderContainer.getRedirect(url)); - return true; - } - if (url.startsWith(CONTENT_PREFIX)) { - return handleEpubAndPdf(url); - } - if (url.startsWith("javascript:")) { - // Allow javascript for HTML functions and code execution (EX: night mode) - return true; - } - if (url.startsWith(UI_URI.toString())) { - Log.e("KiwixWebViewClient", "UI Url " + url + " not supported."); - //TODO: Document this code - what's a UI_URL? - return true; - } - - // Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - callback.openExternalUrl(intent); - return true; - } - - private String convertLegacyUrl(String url) { - for (String legacyContentPrefix : LEGACY_CONTENT_PREFIXES) { - if (url.startsWith(legacyContentPrefix)) { - return url.replace(legacyContentPrefix, CONTENT_PREFIX); - } - } - return url; - } - - private boolean handleEpubAndPdf(String url) { - String extension = MimeTypeMap.getFileExtensionFromUrl(url); - if (DOCUMENT_TYPES.containsKey(extension)) { - File savedFile = - FileUtils.downloadFileFromUrl(url, null, zimReaderContainer, sharedPreferenceUtil); - 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; - } - - @Override - public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { - callback.webViewFailedLoading(failingUrl); - } - - @Override - public void onPageFinished(WebView view, String url) { - boolean invalidUrl = url.equals(CONTENT_PREFIX + "null"); - - Log.d(TAG_KIWIX, "invalidUrl = " + invalidUrl); - - if (invalidUrl) { - return; - } - - jumpToAnchor(view, url); - callback.webViewUrlFinishedLoading(); - } - - /* - * If 2 urls are the same aside from the `#` component then calling load - * does not trigger our loading code and the webview will go to the anchor - * */ - private void jumpToAnchor(WebView view, String loadedUrl) { - if (urlWithAnchor != null && urlWithAnchor.startsWith(loadedUrl)) { - view.loadUrl(urlWithAnchor); - urlWithAnchor = null; - } - } - - @Nullable - @Override - public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { - String url = convertLegacyUrl(request.getUrl().toString()); - if (url.startsWith(CONTENT_PREFIX)) { - return zimReaderContainer.load(url, request.getRequestHeaders()); - } else { - return super.shouldInterceptRequest(view, request); - } - } -} diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreWebViewClient.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreWebViewClient.kt new file mode 100644 index 000000000..901588b57 --- /dev/null +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreWebViewClient.kt @@ -0,0 +1,172 @@ +/* + * Kiwix Android + * Copyright (c) 2019 Kiwix + * 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, see . + * + */ +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 +import android.webkit.WebResourceResponse +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.core.content.FileProvider +import org.kiwix.kiwixmobile.core.CoreApp.Companion.instance +import org.kiwix.kiwixmobile.core.reader.ZimFileReader +import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer +import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil +import org.kiwix.kiwixmobile.core.utils.TAG_KIWIX +import org.kiwix.kiwixmobile.core.utils.files.FileUtils.downloadFileFromUrl + +open class CoreWebViewClient( + protected val callback: WebViewCallback, + protected val zimReaderContainer: ZimReaderContainer, + private val sharedPreferenceUtil: SharedPreferenceUtil +) : WebViewClient() { + private var urlWithAnchor: String? = null + + @Suppress("ReturnCount") + override fun shouldOverrideUrlLoading(view: WebView, loadedUrl: String): Boolean { + var url = loadedUrl + callback.webViewUrlLoading() + url = convertLegacyUrl(url) + urlWithAnchor = if (url.contains("#")) url else null + if (zimReaderContainer.isRedirect(url)) { + if (handleEpubAndPdf(url)) { + return true + } + view.loadUrl(zimReaderContainer.getRedirect(url)) + return true + } + if (url.startsWith(ZimFileReader.CONTENT_PREFIX)) { + return handleEpubAndPdf(url) + } + if (url.startsWith("javascript:")) { + // Allow javascript for HTML functions and code execution (EX: night mode) + return true + } + if (url.startsWith(ZimFileReader.UI_URI.toString())) { + Log.e("KiwixWebViewClient", "UI Url $url not supported.") + // Document this code - what's a UI_URL? + return true + } + + // Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) + callback.openExternalUrl(intent) + return true + } + + private fun convertLegacyUrl(url: String): String { + return LEGACY_CONTENT_PREFIXES + .firstOrNull(url::startsWith) + ?.let { url.replace(it, ZimFileReader.CONTENT_PREFIX) } + ?: url + } + + @Suppress("NestedBlockDepth") + private fun handleEpubAndPdf(url: String): Boolean { + val extension = MimeTypeMap.getFileExtensionFromUrl(url) + if (DOCUMENT_TYPES.containsKey(extension)) { + downloadFileFromUrl( + url, + null, + zimReaderContainer, + sharedPreferenceUtil + )?.let { + if (it.exists()) { + val context: Context = instance + val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + FileProvider.getUriForFile( + context, + context.packageName + ".fileprovider", it + ) else Uri.fromFile(it) + val intent = Intent(Intent.ACTION_VIEW).apply { + setDataAndType(uri, DOCUMENT_TYPES[extension]) + flags = Intent.FLAG_ACTIVITY_NO_HISTORY + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + } + callback.openExternalUrl(intent) + } + } + return true + } + return false + } + + override fun onReceivedError( + view: WebView, + errorCode: Int, + description: String, + failingUrl: String + ) { + callback.webViewFailedLoading(failingUrl) + } + + override fun onPageFinished(view: WebView, url: String) { + val invalidUrl = url == ZimFileReader.CONTENT_PREFIX + "null" + Log.d(TAG_KIWIX, "invalidUrl = $invalidUrl") + if (invalidUrl) { + return + } + jumpToAnchor(view, url) + callback.webViewUrlFinishedLoading() + } + + /* + * If 2 urls are the same aside from the `#` component then calling load + * does not trigger our loading code and the webview will go to the anchor + * */ + private fun jumpToAnchor(view: WebView, loadedUrl: String) { + urlWithAnchor?.let { + if (it.startsWith(loadedUrl)) { + view.loadUrl(it) + urlWithAnchor = null + } + } + } + + override fun shouldInterceptRequest( + view: WebView, + request: WebResourceRequest + ): WebResourceResponse? { + val url = convertLegacyUrl(request.url.toString()) + return if (url.startsWith(ZimFileReader.CONTENT_PREFIX)) { + zimReaderContainer.load(url, request.requestHeaders) + } else { + super.shouldInterceptRequest(view, request) + } + } + + companion object { + private val DOCUMENT_TYPES: HashMap = object : HashMap() { + init { + put("epub", "application/epub+zip") + put("pdf", "application/pdf") + } + } + private val LEGACY_CONTENT_PREFIXES = arrayOf( + "zim://content/", + Uri.parse("content://" + instance.packageName + ".zim.base/").toString() + ) + } +}