diff --git a/.idea/inspectionProfiles/kiwixAndroidInspections.xml b/.idea/inspectionProfiles/kiwixAndroidInspections.xml
index 2f014f407..49dd2a8e3 100644
--- a/.idea/inspectionProfiles/kiwixAndroidInspections.xml
+++ b/.idea/inspectionProfiles/kiwixAndroidInspections.xml
@@ -364,7 +364,9 @@
-
+
+
+
diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/NetworkTest.java b/app/src/androidTest/java/org/kiwix/kiwixmobile/NetworkTest.java
index dc874df72..7beefa3a1 100644
--- a/app/src/androidTest/java/org/kiwix/kiwixmobile/NetworkTest.java
+++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/NetworkTest.java
@@ -39,7 +39,6 @@ import org.junit.runner.RunWith;
import org.kiwix.kiwixmobile.core.CoreApp;
import org.kiwix.kiwixmobile.core.di.components.DaggerTestComponent;
import org.kiwix.kiwixmobile.core.di.components.TestComponent;
-import org.kiwix.kiwixmobile.core.reader.ZimContentProvider;
import org.kiwix.kiwixmobile.main.KiwixMainActivity;
import org.kiwix.kiwixmobile.testutils.TestUtils;
import org.kiwix.kiwixmobile.utils.KiwixIdlingResource;
@@ -92,8 +91,6 @@ public class NetworkTest {
CoreApp.setCoreComponent(component);
- ZimContentProvider zimContentProvider = new ZimContentProvider();
- CoreApp.getCoreComponent().inject(zimContentProvider);
component.inject(this);
InputStream library = NetworkTest.class.getClassLoader().getResourceAsStream("library.xml");
InputStream metalinks =
diff --git a/build.gradle.kts b/build.gradle.kts
index 29d4115f0..56327158c 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -6,9 +6,6 @@ buildscript {
dependencies {
classpath(Libs.com_android_tools_build_gradle)
classpath(Libs.kotlin_gradle_plugin)
- classpath(Libs.ktlint_gradle)
- classpath(Libs.jacoco_android)
- classpath(Libs.detekt_gradle_plugin)
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt
index dab490388..f2a28b042 100644
--- a/buildSrc/src/main/kotlin/Versions.kt
+++ b/buildSrc/src/main/kotlin/Versions.kt
@@ -89,7 +89,7 @@ object Versions {
const val core_ktx: String = "1.2.0"
- const val kiwixlib: String = "9.1.0"
+ const val kiwixlib: String = "9.1.2"
const val material: String = "1.1.0-beta02" // available: "1.1.0"
diff --git a/core/detekt_baseline.xml b/core/detekt_baseline.xml
index ee0f581dd..40be6d414 100644
--- a/core/detekt_baseline.xml
+++ b/core/detekt_baseline.xml
@@ -4,6 +4,7 @@
EmptyFunctionBlock:BooksOnDiskViewHolder.kt$BookOnDiskViewHolder.BookViewHolder${ }
EmptyFunctionBlock:FetchDownloadMonitor.kt$FetchDownloadMonitor.<no name provided>${}
+ ForbiddenComment:JNIInitialiser.kt$JNIInitialiser$// TODO: Consider surfacing to user
LongParameterList:MainMenu.kt$MainMenu.Factory$( menu: Menu, webViews: MutableList<KiwixWebView>, urlIsValid: Boolean, menuClickListener: MenuClickListener, disableReadAloud: Boolean, disableTabs: Boolean )
MagicNumber:ArticleCount.kt$ArticleCount$1000.0
MagicNumber:ArticleCount.kt$ArticleCount$3
@@ -12,6 +13,7 @@
MagicNumber:DownloaderModule.kt$DownloaderModule$5
MagicNumber:FetchDownloadRequester.kt$10
MagicNumber:FileUtils.kt$FileUtils$3
+ MagicNumber:JNIInitialiser.kt$JNIInitialiser$1024
MagicNumber:KiloByte.kt$KiloByte$1024.0
MagicNumber:MainMenu.kt$MainMenu$99
MagicNumber:SearchResultGenerator.kt$ZimSearchResultGenerator$200
@@ -20,6 +22,7 @@
MagicNumber:Seconds.kt$Seconds$60.0
NestedBlockDepth:FileUtils.kt$FileUtils$deleteZimFile
NestedBlockDepth:ImageUtils.kt$ImageUtils$getBitmapFromView
+ NestedBlockDepth:JNIInitialiser.kt$JNIInitialiser$loadICUData
NestedBlockDepth:StorageDeviceUtils.kt$StorageDeviceUtils$canWrite
PackageNaming:ArticleCount.kt$package org.kiwix.kiwixmobile.core.zim_manager.fileselect_view
PackageNaming:BookOnDiskDelegate.kt$package org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter
@@ -36,6 +39,7 @@
ReturnCount:FileUtils.kt$FileUtils$@JvmStatic fun hasPart(file: File): Boolean
ReturnCount:FileUtils.kt$FileUtils$@Synchronized private fun deleteZimFileParts(path: String): Boolean
ReturnCount:ImageUtils.kt$ImageUtils$private fun getBitmapFromView(width: Int, height: Int, viewToDrawFrom: View): Bitmap?
+ TooGenericExceptionCaught:JNIInitialiser.kt$JNIInitialiser$e: Exception
TooGenericExceptionThrown:AbstractContentProvider.kt$AbstractContentProvider$throw RuntimeException("Operation not supported")
TooGenericExceptionThrown:AdapterDelegateManager.kt$AdapterDelegateManager$throw RuntimeException("No delegate registered for $item")
TooGenericExceptionThrown:Bytes.kt$Bytes$throw RuntimeException("impossible value $size")
@@ -51,7 +55,6 @@
TooManyFunctions:NewBookDao.kt$NewBookDao$NewBookDao
TooManyFunctions:Repository.kt$Repository$Repository
TooManyFunctions:ZimFileReader.kt$ZimFileReader$ZimFileReader
- TooManyFunctions:ZimReaderContainer.kt$ZimReaderContainer$ZimReaderContainer
TopLevelPropertyNaming:Bytes.kt$const val Eb = Pb * 1024
TopLevelPropertyNaming:Bytes.kt$const val Gb = Mb * 1024
TopLevelPropertyNaming:Bytes.kt$const val Kb = 1 * 1024L
diff --git a/core/src/main/AndroidManifest.xml b/core/src/main/AndroidManifest.xml
index 389603e6d..84aaeb472 100644
--- a/core/src/main/AndroidManifest.xml
+++ b/core/src/main/AndroidManifest.xml
@@ -29,11 +29,6 @@
-
-
diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/CoreApp.java b/core/src/main/java/org/kiwix/kiwixmobile/core/CoreApp.java
index 020f8c352..15c7473d5 100644
--- a/core/src/main/java/org/kiwix/kiwixmobile/core/CoreApp.java
+++ b/core/src/main/java/org/kiwix/kiwixmobile/core/CoreApp.java
@@ -50,6 +50,14 @@ public abstract class CoreApp extends Application {
@Inject
KiwixDatabase kiwixDatabase;
+ /**
+ * The init of this class does the work of initializing,
+ * simply injecting it is all that there is to be done
+ */
+ @SuppressWarnings("unused")
+ @Inject
+ JNIInitialiser jniInitialiser;
+
public static CoreApp getInstance() {
return app;
}
diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/JNIInitialiser.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/JNIInitialiser.kt
new file mode 100644
index 000000000..c8e37c98f
--- /dev/null
+++ b/core/src/main/java/org/kiwix/kiwixmobile/core/JNIInitialiser.kt
@@ -0,0 +1,57 @@
+/*
+ * Kiwix Android
+ * Copyright (c) 2020 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
+
+import android.content.Context
+import android.util.Log
+import org.kiwix.kiwixlib.JNIKiwix
+import org.kiwix.kiwixmobile.core.utils.TAG_KIWIX
+import java.io.File
+import java.io.FileOutputStream
+import javax.inject.Inject
+
+internal class JNIInitialiser @Inject constructor(context: Context, jniKiwix: JNIKiwix) {
+ init {
+ loadICUData(context)?.let(jniKiwix::setDataDirectory)
+ }
+
+ private fun loadICUData(context: Context): String? {
+ return try {
+ val icuDir = File(context.filesDir, "icu")
+ if (!icuDir.exists()) {
+ icuDir.mkdirs()
+ }
+ val icuFileNames = context.assets.list("icu") ?: emptyArray()
+ for (icuFileName in icuFileNames) {
+ val icuDataFile = File(icuDir, icuFileName)
+ if (!icuDataFile.exists()) {
+ FileOutputStream(icuDataFile).use { outputStream ->
+ context.assets.open("icu/$icuFileName").use { inputStream ->
+ inputStream.copyTo(outputStream, 1024)
+ }
+ }
+ }
+ }
+ icuDir.absolutePath
+ } catch (e: Exception) {
+ Log.w(TAG_KIWIX, "Error copying icu data file", e)
+ // TODO: Consider surfacing to user
+ null
+ }
+ }
+}
diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/data/AbstractContentProvider.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/data/AbstractContentProvider.kt
deleted file mode 100644
index c7ea63549..000000000
--- a/core/src/main/java/org/kiwix/kiwixmobile/core/data/AbstractContentProvider.kt
+++ /dev/null
@@ -1,53 +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.data
-
-import android.content.ContentProvider
-import android.content.ContentValues
-import android.database.Cursor
-import android.net.Uri
-
-internal abstract class AbstractContentProvider : ContentProvider() {
- override fun query(
- url: Uri,
- projection: Array?,
- selection: String?,
- selectionArgs: Array?,
- sort: String?
- ): Cursor? {
- throw RuntimeException("Operation not supported")
- }
-
- override fun insert(uri: Uri, initialValues: ContentValues?): Uri? {
- throw RuntimeException("Operation not supported")
- }
-
- override fun update(
- uri: Uri,
- values: ContentValues?,
- where: String?,
- whereArgs: Array?
- ): Int {
- throw RuntimeException("Operation not supported")
- }
-
- override fun delete(uri: Uri, where: String?, whereArgs: Array?): Int {
- throw RuntimeException("Operation not supported")
- }
-}
diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/di/components/CoreComponent.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/di/components/CoreComponent.kt
index a804fc8b3..5b67faab1 100644
--- a/core/src/main/java/org/kiwix/kiwixmobile/core/di/components/CoreComponent.kt
+++ b/core/src/main/java/org/kiwix/kiwixmobile/core/di/components/CoreComponent.kt
@@ -47,7 +47,6 @@ import org.kiwix.kiwixmobile.core.error.ErrorActivity
import org.kiwix.kiwixmobile.core.help.HelpActivity
import org.kiwix.kiwixmobile.core.history.HistoryModule
import org.kiwix.kiwixmobile.core.main.KiwixWebView
-import org.kiwix.kiwixmobile.core.reader.ZimContentProvider
import org.kiwix.kiwixmobile.core.reader.ZimFileReader
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
import org.kiwix.kiwixmobile.core.search.SearchActivity
@@ -101,7 +100,6 @@ interface CoreComponent {
fun notificationManager(): NotificationManager
fun inject(application: CoreApp)
- fun inject(zimContentProvider: ZimContentProvider)
fun inject(kiwixWebView: KiwixWebView)
fun inject(storageSelectDialog: StorageSelectDialog)
diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.java b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.java
index 95a8fcf27..fa8388e89 100644
--- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.java
+++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.java
@@ -1391,7 +1391,7 @@ public abstract class CoreMainActivity extends BaseActivity
@NotNull
private String contentUrl(String articleUrl) {
- return Uri.parse(ZimFileReader.CONTENT_URI + articleUrl).toString();
+ return Uri.parse(ZimFileReader.CONTENT_PREFIX + articleUrl).toString();
}
@NotNull
@@ -1680,7 +1680,7 @@ public abstract class CoreMainActivity extends BaseActivity
@Override
public void webViewLongClick(final String url) {
boolean handleEvent = false;
- if (url.startsWith(ZimFileReader.CONTENT_URI.toString())) {
+ if (url.startsWith(ZimFileReader.CONTENT_PREFIX)) {
// This is my web site, so do not override; let my WebView load the page
handleEvent = true;
} else if (url.startsWith("file://")) {
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 f00effd23..936cd0ca4 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
@@ -24,16 +24,19 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.webkit.MimeTypeMap;
+import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
+import androidx.annotation.Nullable;
import java.util.HashMap;
import org.kiwix.kiwixmobile.core.CoreApp;
import org.kiwix.kiwixmobile.core.R;
-import org.kiwix.kiwixmobile.core.reader.ZimFileReader;
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer;
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil;
import static org.kiwix.kiwixmobile.core.main.CoreMainActivity.HOME_URL;
+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.EXTRA_EXTERNAL_LINK;
import static org.kiwix.kiwixmobile.core.utils.ConstantsKt.TAG_KIWIX;
@@ -65,7 +68,7 @@ public abstract class CoreWebViewClient extends WebViewClient {
view.loadUrl(zimReaderContainer.getRedirect(url));
return true;
}
- if (url.startsWith(ZimFileReader.CONTENT_URI.toString())) {
+ if (url.startsWith(CONTENT_PREFIX)) {
return handleEpubAndPdf(url);
}
if (url.startsWith("file://")) {
@@ -76,7 +79,7 @@ public abstract class CoreWebViewClient extends WebViewClient {
// Allow javascript for HTML functions and code execution (EX: night mode)
return true;
}
- if (url.startsWith(ZimFileReader.UI_URI.toString())) {
+ 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;
@@ -137,4 +140,14 @@ public abstract class CoreWebViewClient extends WebViewClient {
view.removeAllViews();
view.addView(home);
}
+
+ @Nullable
+ @Override
+ public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
+ if (url.startsWith(CONTENT_PREFIX)) {
+ return zimReaderContainer.load(url);
+ } else {
+ return super.shouldInterceptRequest(view, url);
+ }
+ }
}
diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/reader/ZimContentProvider.java b/core/src/main/java/org/kiwix/kiwixmobile/core/reader/ZimContentProvider.java
deleted file mode 100644
index 7dd4cfc9c..000000000
--- a/core/src/main/java/org/kiwix/kiwixmobile/core/reader/ZimContentProvider.java
+++ /dev/null
@@ -1,120 +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.reader;
-
-import android.content.Context;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
-import javax.inject.Inject;
-import org.kiwix.kiwixlib.JNIKiwix;
-import org.kiwix.kiwixmobile.core.CoreApp;
-import org.kiwix.kiwixmobile.core.data.AbstractContentProvider;
-
-import static org.kiwix.kiwixmobile.core.utils.ConstantsKt.TAG_KIWIX;
-
-public class ZimContentProvider extends AbstractContentProvider {
-
- @Inject
- public JNIKiwix jniKiwix;
- @Inject
- ZimReaderContainer zimReaderContainer;
-
- @Override
- public boolean onCreate() {
- CoreApp.getCoreComponent().inject(this);
- setIcuDataDirectory();
- return true;
- }
-
- @Override
- public String getType(Uri uri) {
- return zimReaderContainer.readMimeType(uri);
- }
-
- @Override
- public ParcelFileDescriptor openFile(Uri uri, String mode) {
- return zimReaderContainer.load(uri);
- }
-
- private void setIcuDataDirectory() {
- String icuDirPath = loadICUData(getContext());
- if (icuDirPath != null) {
- Log.d(TAG_KIWIX, "Setting the ICU directory path to " + icuDirPath);
- jniKiwix.setDataDirectory(icuDirPath);
- }
- }
-
- private String loadICUData(Context context) {
- try {
- File icuDir = new File(context.getFilesDir(), "icu");
- if (!icuDir.exists()) {
- icuDir.mkdirs();
- }
- String[] icuFileNames = context.getAssets().list("icu");
- for (int i = 0; i < icuFileNames.length; i++) {
- String icuFileName = icuFileNames[i];
- File icuDataFile = new File(icuDir, icuFileName);
- if (!icuDataFile.exists()) {
- InputStream in = context.getAssets().open("icu/" + icuFileName);
- OutputStream out = new FileOutputStream(icuDataFile);
- byte[] buf = new byte[1024];
- int len;
- while ((len = in.read(buf)) > 0) {
- out.write(buf, 0, len);
- }
- in.close();
- out.flush();
- out.close();
- }
- }
- return icuDir.getAbsolutePath();
- } catch (Exception e) {
- Log.w(TAG_KIWIX, "Error copying icu data file", e);
- //TODO: Consider surfacing to user
- return null;
- }
- }
-
- private static String getFulltextIndexPath(String file) {
- String[] names = { file, file };
-
- /* File might be a ZIM chunk like foobar.zimaa */
- if (!names[0].substring(names[0].length() - 3).equals("zim")) {
- names[0] = names[0].substring(0, names[0].length() - 2);
- }
-
- /* Try to find a *.idx fulltext file/directory beside the ZIM
- * file. Returns .zim.idx or .zimaa.idx. */
- for (String name : names) {
- File f = new File(name + ".idx");
- if (f.exists() && f.isDirectory()) {
- return f.getPath();
- }
- }
-
- /* If no separate fulltext index file found then returns the ZIM
- * file path itself (embedded fulltext index) */
- return file;
- }
-}
diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/reader/ZimFileReader.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/reader/ZimFileReader.kt
index acb01b2ed..74ec0f29f 100644
--- a/core/src/main/java/org/kiwix/kiwixmobile/core/reader/ZimFileReader.kt
+++ b/core/src/main/java/org/kiwix/kiwixmobile/core/reader/ZimFileReader.kt
@@ -17,14 +17,13 @@
*/
package org.kiwix.kiwixmobile.core.reader
+import android.content.res.AssetFileDescriptor
import android.net.Uri
import android.os.ParcelFileDescriptor
-import android.os.ParcelFileDescriptor.AutoCloseOutputStream
-import android.os.ParcelFileDescriptor.dup
import android.util.Log
import android.webkit.MimeTypeMap
import androidx.core.net.toUri
-import io.reactivex.Single
+import io.reactivex.Completable
import io.reactivex.schedulers.Schedulers
import org.kiwix.kiwixlib.JNIKiwixException
import org.kiwix.kiwixlib.JNIKiwixInt
@@ -34,14 +33,16 @@ import org.kiwix.kiwixlib.Pair
import org.kiwix.kiwixmobile.core.CoreApp
import org.kiwix.kiwixmobile.core.NightModeConfig
import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity.Book
-import org.kiwix.kiwixmobile.core.reader.ZimFileReader.Companion.CONTENT_URI
+import org.kiwix.kiwixmobile.core.reader.ZimFileReader.Companion.CONTENT_PREFIX
import org.kiwix.kiwixmobile.core.search.SearchSuggestion
import org.kiwix.kiwixmobile.core.utils.files.FileUtils
import java.io.File
-import java.io.FileDescriptor
-import java.io.FileOutputStream
+import java.io.FileInputStream
import java.io.IOException
-import java.io.RandomAccessFile
+import java.io.InputStream
+import java.io.OutputStream
+import java.io.PipedInputStream
+import java.io.PipedOutputStream
import javax.inject.Inject
private const val TAG = "ZimFileReader"
@@ -80,7 +81,7 @@ class ZimFileReader constructor(
val description: String get() = jniKiwixReader.description
val favicon: String? get() = jniKiwixReader.favicon
val language: String get() = jniKiwixReader.language
- val tags: String get() = "${getContent(Uri.parse("M/Tags"))}"
+ val tags: String get() = "${getContent("M/Tags")}"
private val mediaCount: Int?
get() = try {
jniKiwixReader.mediaCount
@@ -112,8 +113,9 @@ class ZimFileReader constructor(
fun getRandomArticleUrl(): String? =
valueOfJniStringAfter(jniKiwixReader::getRandomPage)
- fun load(uri: Uri): ParcelFileDescriptor {
- if ("$uri".matches(VIDEO_REGEX)) {
+ fun load(uri: String): InputStream? {
+ val extension = uri.substringAfterLast(".")
+ if (videoExtensions.any { it == extension }) {
try {
return loadVideo(uri)
} catch (ioException: IOException) {
@@ -123,7 +125,7 @@ class ZimFileReader constructor(
return loadContent(uri)
}
- fun readMimeType(uri: Uri) = "$uri".removeArguments().let {
+ fun readMimeType(uri: String) = uri.removeArguments().let {
it.mimeType?.takeIf(String::isNotEmpty) ?: mimeTypeFromReader(it)
}.also { Log.d(TAG, "getting mimetype for $uri = $it") }
@@ -134,78 +136,73 @@ class ZimFileReader constructor(
fun getRedirect(url: String) = "${toRedirect(url)}"
fun isRedirect(url: String) =
- url.startsWith("$CONTENT_URI") && url != getRedirect(url)
+ url.startsWith(CONTENT_PREFIX) && url != getRedirect(url)
private fun toRedirect(url: String) =
- "$CONTENT_URI${jniKiwixReader.checkUrl(url.toUri().filePath)}".toUri()
+ "$CONTENT_PREFIX${jniKiwixReader.checkUrl(url.toUri().filePath)}".toUri()
- private fun loadContent(uri: Uri) =
+ private fun loadContent(uri: String) =
try {
- ParcelFileDescriptor.createPipe().also {
- streamZimContentToPipe(uri, AutoCloseOutputStream(it[1]))
- }[0]
+ val outputStream = PipedOutputStream()
+ PipedInputStream(outputStream).also { streamZimContentToPipe(uri, outputStream) }
} catch (ioException: IOException) {
throw IOException("Could not open pipe for $uri", ioException)
}
- private fun loadVideo(uri: Uri): ParcelFileDescriptor {
+ private fun loadVideo(uri: String): InputStream? {
val infoPair = jniKiwixReader.getDirectAccessInformation(uri.filePath)
if (infoPair == null || !File(infoPair.filename).exists()) {
return loadVideoFromCache(uri)
}
- return dup(infoPair.fileDescriptor)
+ return AssetFileDescriptor(
+ infoPair.parcelFileDescriptor,
+ infoPair.offset,
+ jniKiwixReader.getArticleSize(uri.filePath)
+ ).createInputStream()
}
@Throws(IOException::class)
- private fun loadVideoFromCache(uri: Uri): ParcelFileDescriptor {
- val outputFile = File(
+ private fun loadVideoFromCache(uri: String): FileInputStream {
+ return File(
FileUtils.getFileCacheDir(CoreApp.getInstance()),
- "$uri".substringAfterLast("/")
- )
- FileOutputStream(outputFile).use { it.write(getContent(uri)) }
- return ParcelFileDescriptor.open(outputFile, ParcelFileDescriptor.MODE_READ_ONLY)
+ uri.substringAfterLast("/")
+ ).apply { writeBytes(getContent(uri)) }
+ .inputStream()
}
- private fun streamZimContentToPipe(
- uri: Uri,
- outputStream: AutoCloseOutputStream
- ) {
- Single.just(Unit)
- .subscribeOn(Schedulers.io())
- .observeOn(Schedulers.io())
- .subscribe(
- {
- try {
- outputStream.use {
- val mime = JNIKiwixString()
- val size = JNIKiwixInt()
- val url = JNIKiwixString(uri.filePath.removeArguments())
- val content = getContent(url = url, mime = mime, size = size)
- if ("text/css" == mime.value && nightModeConfig.isNightModeActive()) {
- it.write(INVERT_IMAGES_VIDEO.toByteArray(Charsets.UTF_8))
- }
- it.write(content)
- Log.d(
- TAG,
- "reading ${url.value}(mime: ${mime.value}, size: ${size.value}) finished."
- )
+ private fun getContent(url: String) = getContentAndMimeType(url).let { (content, _) -> content }
+
+ private fun streamZimContentToPipe(uri: String, outputStream: OutputStream) {
+ Completable.fromAction {
+ try {
+ outputStream.use {
+ getContentAndMimeType(uri).let { (content: ByteArray, mimeType: String) ->
+ if ("text/css" == mimeType && nightModeConfig.isNightModeActive()) {
+ it.write(INVERT_IMAGES_VIDEO.toByteArray(Charsets.UTF_8))
}
- } catch (ioException: IOException) {
- Log.e(TAG, "error writing pipe for $uri", ioException)
+ it.write(content)
}
- },
- Throwable::printStackTrace
- )
+ }
+ } catch (ioException: IOException) {
+ Log.e(TAG, "error writing pipe for $uri", ioException)
+ }
+ }
+ .subscribeOn(Schedulers.io())
+ .subscribe({ }, Throwable::printStackTrace)
}
- private fun getContent(uri: Uri) = getContent(JNIKiwixString(uri.filePath.removeArguments()))
+ private fun getContentAndMimeType(uri: String) = with(JNIKiwixString()) {
+ getContent(url = JNIKiwixString(uri.filePath.removeArguments()), mime = this) to value
+ }
private fun getContent(
url: JNIKiwixString = JNIKiwixString(),
jniKiwixString: JNIKiwixString = JNIKiwixString(),
mime: JNIKiwixString = JNIKiwixString(),
size: JNIKiwixInt = JNIKiwixInt()
- ) = jniKiwixReader.getContent(url, jniKiwixString, mime, size)
+ ) = jniKiwixReader.getContent(url, jniKiwixString, mime, size).also {
+ Log.d(TAG, "reading ${url.value}(mime: ${mime.value}, size: ${size.value}) finished.")
+ }
private fun valueOfJniStringAfter(jniStringFunction: (JNIKiwixString) -> Boolean) =
JNIKiwixString().takeIf { jniStringFunction(it) }?.value
@@ -233,9 +230,11 @@ class ZimFileReader constructor(
*/
@JvmField
val UI_URI: Uri? = Uri.parse("content://org.kiwix.ui/")
+
@JvmField
- val CONTENT_URI: Uri? =
- Uri.parse("content://${CoreApp.getInstance().packageName}.zim.base/")
+ val CONTENT_PREFIX =
+ Uri.parse("content://${CoreApp.getInstance().packageName}.zim.base/").toString()
+
private val INVERT_IMAGES_VIDEO =
"""
img, video, div[poster], div#header {
@@ -251,18 +250,18 @@ class ZimFileReader constructor(
filter: invert(0);
}
""".trimIndent()
- private val VIDEO_REGEX = Regex("([^\\s]+(\\.(?i)(3gp|mp4|m4a|webm|mkv|ogg|ogv))\$)")
+ private val videoExtensions = listOf("3gp", "mp4", "m4a", "webm", "mkv", "ogg", "ogv")
}
}
private fun String.removeArguments() = substringBefore("?")
-private val Pair.fileDescriptor: FileDescriptor?
- get() = RandomAccessFile(filename, "r").apply { seek(offset.toLong()) }.fd
private val Uri.filePath: String
get() = toString().filePath
private val String.filePath: String
- get() = substringAfter("$CONTENT_URI").substringBefore("#")
+ get() = substringAfter(CONTENT_PREFIX).substringBefore("#")
private val String.mimeType: String?
get() = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
MimeTypeMap.getFileExtensionFromUrl(this)
)
+private val Pair.parcelFileDescriptor: ParcelFileDescriptor?
+ get() = ParcelFileDescriptor.open(File(filename), ParcelFileDescriptor.MODE_READ_ONLY)
diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/reader/ZimReaderContainer.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/reader/ZimReaderContainer.kt
index 242e08ab1..e26df34cd 100644
--- a/core/src/main/java/org/kiwix/kiwixmobile/core/reader/ZimReaderContainer.kt
+++ b/core/src/main/java/org/kiwix/kiwixmobile/core/reader/ZimReaderContainer.kt
@@ -17,7 +17,7 @@
*/
package org.kiwix.kiwixmobile.core.reader
-import android.net.Uri
+import android.webkit.WebResourceResponse
import org.kiwix.kiwixlib.JNIKiwixSearcher
import org.kiwix.kiwixmobile.core.reader.ZimFileReader.Factory
import java.io.File
@@ -48,10 +48,6 @@ class ZimReaderContainer @Inject constructor(
else null
}
- fun readMimeType(uri: Uri) = zimFileReader?.readMimeType(uri)
-
- fun load(uri: Uri) = zimFileReader?.load(uri)
-
fun searchSuggestions(prefix: String, count: Int) =
zimFileReader?.searchSuggestions(prefix, count) ?: false
@@ -67,8 +63,15 @@ class ZimReaderContainer @Inject constructor(
fun getNextResult() = jniKiwixSearcher?.nextResult?.let { SearchResult(it.title) }
fun isRedirect(url: String): Boolean = zimFileReader?.isRedirect(url) == true
fun getRedirect(url: String): String = zimFileReader?.getRedirect(url) ?: ""
+ fun load(url: String) =
+ WebResourceResponse(
+ zimFileReader?.readMimeType(url),
+ Charsets.UTF_8.name(),
+ zimFileReader?.load(url)
+ )
val zimFile get() = zimFileReader?.zimFile
+
val zimCanonicalPath get() = zimFileReader?.zimFile?.canonicalPath
val zimFileTitle get() = zimFileReader?.title
val mainPage get() = zimFileReader?.mainPage
diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/StorageObserverTest.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/StorageObserverTest.kt
index 8e1a95e04..4c5f4a949 100644
--- a/core/src/test/java/org/kiwix/kiwixmobile/core/StorageObserverTest.kt
+++ b/core/src/test/java/org/kiwix/kiwixmobile/core/StorageObserverTest.kt
@@ -18,6 +18,7 @@
package org.kiwix.kiwixmobile.core
+import android.net.Uri
import io.mockk.clearAllMocks
import io.mockk.every
import io.mockk.mockk
@@ -58,6 +59,8 @@ class StorageObserverTest {
setScheduler(Schedulers.trampoline())
mockkStatic(CoreApp::class)
every { CoreApp.getInstance().packageName } returns "pkg"
+ mockkStatic(Uri::class)
+ every { Uri.parse(any()).toString() } returns "pkg"
zimFileReader = mockk()
}