From 99614ed88fb2e13544d8ea99e40e32ece242ffe4 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Thu, 26 Oct 2023 18:33:15 +0530 Subject: [PATCH 1/8] Introducing reading zim file content through `fileDescriptor` instead of creating a file to avoid using storage twice for same zim file. --- .../core/main/CoreReaderFragment.kt | 27 ++++++++++---- .../page/bookmark/adapter/BookmarkItem.kt | 2 +- .../page/history/adapter/HistoryListItem.kt | 2 +- .../core/page/notes/adapter/NoteListItem.kt | 2 +- .../kiwixmobile/core/reader/ZimFileReader.kt | 19 +++++++++- .../core/reader/ZimReaderContainer.kt | 7 ++++ .../core/webserver/ZimHostFragment.kt | 15 +++++--- .../custom/main/CustomFileValidator.kt | 37 +++++-------------- .../custom/main/CustomReaderFragment.kt | 8 +++- 9 files changed, 74 insertions(+), 45 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt index a579544d07..363063a36d 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt @@ -154,6 +154,7 @@ import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog import org.kiwix.kiwixmobile.core.utils.files.FileUtils.deleteCachedFiles import org.kiwix.kiwixmobile.core.utils.files.FileUtils.readFile import java.io.File +import java.io.FileDescriptor import java.io.IOException import java.text.SimpleDateFormat import java.util.Date @@ -1386,13 +1387,20 @@ abstract class CoreReaderFragment : externalLinkOpener?.openExternalUrl(intent) } - protected fun openZimFile(file: File, isCustomApp: Boolean = false) { + protected fun openZimFile( + file: File?, + isCustomApp: Boolean = false, + fileDescriptor: FileDescriptor? = null + ) { if (hasPermission(Manifest.permission.READ_EXTERNAL_STORAGE) || isCustomApp) { - if (file.isFileExist()) { - openAndSetInContainer(file) + if (file?.isFileExist() == true) { + openAndSetInContainer(file = file) + updateTitle() + } else if (fileDescriptor != null) { + openAndSetInContainer(fileDescriptor = fileDescriptor) updateTitle() } else { - Log.w(TAG_KIWIX, "ZIM file doesn't exist at " + file.absolutePath) + Log.w(TAG_KIWIX, "ZIM file doesn't exist at " + file?.absolutePath) requireActivity().toast(R.string.error_file_not_found, Toast.LENGTH_LONG) } } else { @@ -1419,16 +1427,21 @@ abstract class CoreReaderFragment : ) } - private fun openAndSetInContainer(file: File) { + private fun openAndSetInContainer(file: File? = null, fileDescriptor: FileDescriptor? = null) { try { - if (isNotPreviouslyOpenZim(file.canonicalPath)) { + if (isNotPreviouslyOpenZim(file?.canonicalPath)) { webViewList.clear() } } catch (e: IOException) { e.printStackTrace() } zimReaderContainer?.let { zimReaderContainer -> - zimReaderContainer.setZimFile(file) + if (fileDescriptor != null) { + zimReaderContainer.setZimFileDescriptor(fileDescriptor) + } else { + zimReaderContainer.setZimFile(file) + } + val zimFileReader = zimReaderContainer.zimFileReader zimFileReader?.let { zimFileReader -> // uninitialized the service worker to fix https://github.com/kiwix/kiwix-android/issues/2561 diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/BookmarkItem.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/BookmarkItem.kt index 0f5401103b..552028020d 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/BookmarkItem.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/BookmarkItem.kt @@ -51,7 +51,7 @@ data class BookmarkItem( ) : this( zimId = zimFileReader.id, zimName = zimFileReader.name, - zimFilePath = zimFileReader.zimFile.canonicalPath, + zimFilePath = zimFileReader.zimFile?.canonicalPath, bookmarkUrl = url, title = title, favicon = zimFileReader.favicon diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/history/adapter/HistoryListItem.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/history/adapter/HistoryListItem.kt index eee4fca52b..8556e4086e 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/history/adapter/HistoryListItem.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/history/adapter/HistoryListItem.kt @@ -48,7 +48,7 @@ sealed class HistoryListItem : PageRelated { ) : this( zimId = zimFileReader.id, zimName = zimFileReader.name, - zimFilePath = zimFileReader.zimFile.canonicalPath, + zimFilePath = zimFileReader.zimFile?.canonicalPath ?: "", favicon = zimFileReader.favicon, historyUrl = url, title = title, diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/notes/adapter/NoteListItem.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/notes/adapter/NoteListItem.kt index 4f40d24cd8..1741fab2f9 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/notes/adapter/NoteListItem.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/notes/adapter/NoteListItem.kt @@ -35,7 +35,7 @@ data class NoteListItem( ) : this( zimId = zimFileReader.id, title = title, - zimFilePath = zimFileReader.zimFile.canonicalPath, + zimFilePath = zimFileReader.zimFile?.canonicalPath, zimUrl = url, favicon = zimFileReader.favicon, noteFilePath = noteFilePath 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 b8553d56e4..f536a7aac7 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 @@ -40,6 +40,7 @@ import org.kiwix.libzim.Item import org.kiwix.libzim.SuggestionSearch import org.kiwix.libzim.SuggestionSearcher import java.io.File +import java.io.FileDescriptor import java.io.FileInputStream import java.io.IOException import java.io.InputStream @@ -52,13 +53,14 @@ import javax.inject.Inject private const val TAG = "ZimFileReader" class ZimFileReader constructor( - val zimFile: File, + val zimFile: File?, private val jniKiwixReader: Archive, private val nightModeConfig: NightModeConfig, private val searcher: SuggestionSearcher = SuggestionSearcher(jniKiwixReader) ) { interface Factory { fun create(file: File): ZimFileReader? + fun create(fileDescriptor: FileDescriptor): ZimFileReader? class Impl @Inject constructor(private val nightModeConfig: NightModeConfig) : Factory { @@ -76,6 +78,21 @@ class ZimFileReader constructor( } catch (ignore: Exception) { // for handing the error, if any zim file is corrupted null } + + override fun create(fileDescriptor: FileDescriptor): ZimFileReader? = + try { + ZimFileReader( + null, + nightModeConfig = nightModeConfig, + jniKiwixReader = Archive(fileDescriptor) + ).also { + Log.e(TAG, "create: with fileDescriptor") + } + } catch (ignore: JNIKiwixException) { + null + } catch (ignore: Exception) { // for handing the error, if any zim file is corrupted + null + } } } 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 5c0218228a..c354df4877 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 @@ -21,6 +21,7 @@ import android.webkit.WebResourceResponse import org.kiwix.kiwixmobile.core.extensions.isFileExist import org.kiwix.kiwixmobile.core.reader.ZimFileReader.Factory import java.io.File +import java.io.FileDescriptor import java.net.HttpURLConnection import javax.inject.Inject import javax.inject.Singleton @@ -42,6 +43,12 @@ class ZimReaderContainer @Inject constructor(private val zimFileReaderFactory: F else null } + fun setZimFileDescriptor(fileDescriptor: FileDescriptor) { + zimFileReader = + if (fileDescriptor.valid()) zimFileReaderFactory.create(fileDescriptor) + else null + } + fun getPageUrlFromTitle(title: String) = zimFileReader?.getPageUrlFrom(title) fun getRandomArticleUrl() = zimFileReader?.getRandomArticleUrl() diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/webserver/ZimHostFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/webserver/ZimHostFragment.kt index d9f0052ccd..c3bdaf0f33 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/webserver/ZimHostFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/webserver/ZimHostFragment.kt @@ -487,13 +487,16 @@ class ZimHostFragment : BaseFragment(), ZimHostCallbacks, ZimHostContract.View { if (it is BookOnDisk) { zimReaderFactory.create(it.file)?.let { zimFileReader -> val booksOnDiskListItem = - (BookOnDisk(zimFileReader.zimFile, zimFileReader) as BooksOnDiskListItem) - .apply { - isSelected = true - } - updatedBooksList.add(booksOnDiskListItem).also { - zimFileReader.dispose() + zimFileReader.zimFile?.let { file -> + (BookOnDisk(file, zimFileReader) as BooksOnDiskListItem) + .apply { + isSelected = true + } + } + if (booksOnDiskListItem != null) { + updatedBooksList.add(booksOnDiskListItem) } + zimFileReader.dispose() } } else { updatedBooksList.add(it) diff --git a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomFileValidator.kt b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomFileValidator.kt index 73cad95ca8..0cf15ed0e5 100644 --- a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomFileValidator.kt +++ b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomFileValidator.kt @@ -22,13 +22,12 @@ import android.content.Context import android.content.pm.PackageManager import android.util.Log import androidx.core.content.ContextCompat -import org.kiwix.kiwixmobile.core.extensions.isFileExist import org.kiwix.kiwixmobile.custom.BuildConfig import org.kiwix.kiwixmobile.custom.main.ValidationState.HasBothFiles import org.kiwix.kiwixmobile.custom.main.ValidationState.HasFile import org.kiwix.kiwixmobile.custom.main.ValidationState.HasNothing import java.io.File -import java.io.FileOutputStream +import java.io.FileDescriptor import java.io.IOException import javax.inject.Inject @@ -44,10 +43,10 @@ class CustomFileValidator @Inject constructor(private val context: Context) { private fun detectInstallationState( obbFiles: List = obbFiles(), zimFiles: List = zimFiles(), - assetFile: File? = getFileFromPlayAssetDelivery() + assetFileDescriptor: FileDescriptor? = getFileFromPlayAssetDelivery() ): ValidationState { return when { - assetFile != null -> HasFile(assetFile) + assetFileDescriptor != null -> HasFile(null, assetFileDescriptor) obbFiles.isNotEmpty() && zimFiles().isNotEmpty() -> HasBothFiles(obbFiles[0], zimFiles[0]) obbFiles.isNotEmpty() -> HasFile(obbFiles[0]) zimFiles.isNotEmpty() -> HasFile(zimFiles[0]) @@ -56,30 +55,12 @@ class CustomFileValidator @Inject constructor(private val context: Context) { } @Suppress("NestedBlockDepth", "MagicNumber") - private fun getFileFromPlayAssetDelivery(): File? { - var zimFile: File? = null + private fun getFileFromPlayAssetDelivery(): FileDescriptor? { try { val context = context.createPackageContext(context.packageName, 0) val assetManager = context.assets - val inputStream = assetManager.open(BuildConfig.PLAY_ASSET_FILE) - val filePath = ContextCompat.getExternalFilesDirs(context, null)[0] - zimFile = File(filePath, BuildConfig.PLAY_ASSET_FILE) - // Write zim file data if file does not exist or corrupted - if (!zimFile.isFileExist() || zimFile.length() == 0L) { - // Delete previously corrupted file - if (zimFile.isFileExist()) zimFile.delete() - zimFile.createNewFile() - FileOutputStream(zimFile).use { outputSteam -> - inputStream.use { inputStream -> - val buffer = ByteArray(1024) - var length: Int - while (inputStream.read(buffer).also { length = it } > 0) { - outputSteam.write(buffer, 0, length) - } - outputSteam.flush() - } - } - } + val inputStream = assetManager.openFd(BuildConfig.PLAY_ASSET_FILE) + return inputStream.fileDescriptor } catch (packageNameNotFoundException: PackageManager.NameNotFoundException) { Log.w( "ASSET_PACKAGE_DELIVERY", @@ -88,7 +69,7 @@ class CustomFileValidator @Inject constructor(private val context: Context) { } catch (ioException: IOException) { Log.w("ASSET_PACKAGE_DELIVERY", "Unable to copy the content of asset $ioException") } - return zimFile + return null } private fun obbFiles() = scanDirs(ContextCompat.getObbDirs(context), "obb") @@ -124,6 +105,8 @@ class CustomFileValidator @Inject constructor(private val context: Context) { sealed class ValidationState { data class HasBothFiles(val obbFile: File, val zimFile: File) : ValidationState() - data class HasFile(val file: File) : ValidationState() + data class HasFile(val file: File?, val fileDescriptor: FileDescriptor? = null) : + ValidationState() + object HasNothing : ValidationState() } diff --git a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomReaderFragment.kt b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomReaderFragment.kt index 090bac52d5..f9ba5d208f 100644 --- a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomReaderFragment.kt +++ b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomReaderFragment.kt @@ -143,7 +143,13 @@ class CustomReaderFragment : CoreReaderFragment() { customFileValidator.validate( onFilesFound = { when (it) { - is ValidationState.HasFile -> openZimFile(it.file, true) + is ValidationState.HasFile -> { + if (it.fileDescriptor != null) { + openZimFile(null, true, it.fileDescriptor) + } else { + openZimFile(it.file, true) + } + } is ValidationState.HasBothFiles -> { it.zimFile.delete() openZimFile(it.obbFile, true) From c8b98cc5041df46ec184c93d0d4fce34c6645fbd Mon Sep 17 00:00:00 2001 From: MohitMali Date: Tue, 31 Oct 2023 11:55:21 +0530 Subject: [PATCH 2/8] Using `ParcelFileDescriptor` instead of `FileDescriptor`. * Telling android to not compress the `.zim` files in asset folder while building the apk/bundle. --- .../core/main/CoreReaderFragment.kt | 17 ++++++++++------- .../kiwixmobile/core/reader/ZimFileReader.kt | 9 +++++---- .../core/reader/ZimReaderContainer.kt | 7 ++++--- custom/build.gradle.kts | 4 ++++ .../custom/main/CustomFileValidator.kt | 18 +++++++++--------- .../custom/main/CustomReaderFragment.kt | 4 ++-- 6 files changed, 34 insertions(+), 25 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt index 363063a36d..c419baf48f 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt @@ -38,6 +38,7 @@ import android.os.CountDownTimer import android.os.Handler import android.os.IBinder import android.os.Looper +import android.os.ParcelFileDescriptor import android.provider.Settings import android.util.AttributeSet import android.util.Log @@ -154,7 +155,6 @@ import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog import org.kiwix.kiwixmobile.core.utils.files.FileUtils.deleteCachedFiles import org.kiwix.kiwixmobile.core.utils.files.FileUtils.readFile import java.io.File -import java.io.FileDescriptor import java.io.IOException import java.text.SimpleDateFormat import java.util.Date @@ -1390,14 +1390,14 @@ abstract class CoreReaderFragment : protected fun openZimFile( file: File?, isCustomApp: Boolean = false, - fileDescriptor: FileDescriptor? = null + parcelFileDescriptor: ParcelFileDescriptor? = null ) { if (hasPermission(Manifest.permission.READ_EXTERNAL_STORAGE) || isCustomApp) { if (file?.isFileExist() == true) { openAndSetInContainer(file = file) updateTitle() - } else if (fileDescriptor != null) { - openAndSetInContainer(fileDescriptor = fileDescriptor) + } else if (parcelFileDescriptor != null) { + openAndSetInContainer(parcelFileDescriptor = parcelFileDescriptor) updateTitle() } else { Log.w(TAG_KIWIX, "ZIM file doesn't exist at " + file?.absolutePath) @@ -1427,7 +1427,10 @@ abstract class CoreReaderFragment : ) } - private fun openAndSetInContainer(file: File? = null, fileDescriptor: FileDescriptor? = null) { + private fun openAndSetInContainer( + file: File? = null, + parcelFileDescriptor: ParcelFileDescriptor? = null + ) { try { if (isNotPreviouslyOpenZim(file?.canonicalPath)) { webViewList.clear() @@ -1436,8 +1439,8 @@ abstract class CoreReaderFragment : e.printStackTrace() } zimReaderContainer?.let { zimReaderContainer -> - if (fileDescriptor != null) { - zimReaderContainer.setZimFileDescriptor(fileDescriptor) + if (parcelFileDescriptor != null) { + zimReaderContainer.setZimFileDescriptor(parcelFileDescriptor) } else { zimReaderContainer.setZimFile(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 f536a7aac7..438c1fa19d 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 @@ -40,7 +40,6 @@ import org.kiwix.libzim.Item import org.kiwix.libzim.SuggestionSearch import org.kiwix.libzim.SuggestionSearcher import java.io.File -import java.io.FileDescriptor import java.io.FileInputStream import java.io.IOException import java.io.InputStream @@ -54,13 +53,14 @@ private const val TAG = "ZimFileReader" class ZimFileReader constructor( val zimFile: File?, + val parcelFileDescriptor: ParcelFileDescriptor? = null, private val jniKiwixReader: Archive, private val nightModeConfig: NightModeConfig, private val searcher: SuggestionSearcher = SuggestionSearcher(jniKiwixReader) ) { interface Factory { fun create(file: File): ZimFileReader? - fun create(fileDescriptor: FileDescriptor): ZimFileReader? + fun create(parcelFileDescriptor: ParcelFileDescriptor): ZimFileReader? class Impl @Inject constructor(private val nightModeConfig: NightModeConfig) : Factory { @@ -79,12 +79,13 @@ class ZimFileReader constructor( null } - override fun create(fileDescriptor: FileDescriptor): ZimFileReader? = + override fun create(parcelFileDescriptor: ParcelFileDescriptor): ZimFileReader? = try { ZimFileReader( null, + parcelFileDescriptor, nightModeConfig = nightModeConfig, - jniKiwixReader = Archive(fileDescriptor) + jniKiwixReader = Archive(parcelFileDescriptor.fileDescriptor) ).also { Log.e(TAG, "create: with fileDescriptor") } 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 c354df4877..052d900b1c 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,11 +17,11 @@ */ package org.kiwix.kiwixmobile.core.reader +import android.os.ParcelFileDescriptor import android.webkit.WebResourceResponse import org.kiwix.kiwixmobile.core.extensions.isFileExist import org.kiwix.kiwixmobile.core.reader.ZimFileReader.Factory import java.io.File -import java.io.FileDescriptor import java.net.HttpURLConnection import javax.inject.Inject import javax.inject.Singleton @@ -43,9 +43,10 @@ class ZimReaderContainer @Inject constructor(private val zimFileReaderFactory: F else null } - fun setZimFileDescriptor(fileDescriptor: FileDescriptor) { + fun setZimFileDescriptor(parcelFileDescriptor: ParcelFileDescriptor) { zimFileReader = - if (fileDescriptor.valid()) zimFileReaderFactory.create(fileDescriptor) + if (parcelFileDescriptor.fileDescriptor.valid()) + zimFileReaderFactory.create(parcelFileDescriptor) else null } diff --git a/custom/build.gradle.kts b/custom/build.gradle.kts index 7b042594be..e743c213ab 100644 --- a/custom/build.gradle.kts +++ b/custom/build.gradle.kts @@ -49,6 +49,10 @@ android { } } assetPacks += ":install_time_asset" + androidResources { + // to not compress zim file in asset folder + noCompress.add("zim") + } } fun ProductFlavor.createDownloadTask(file: File): Task { diff --git a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomFileValidator.kt b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomFileValidator.kt index 0cf15ed0e5..b0963ea27d 100644 --- a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomFileValidator.kt +++ b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomFileValidator.kt @@ -20,6 +20,7 @@ package org.kiwix.kiwixmobile.custom.main import android.content.Context import android.content.pm.PackageManager +import android.os.ParcelFileDescriptor import android.util.Log import androidx.core.content.ContextCompat import org.kiwix.kiwixmobile.custom.BuildConfig @@ -27,7 +28,6 @@ import org.kiwix.kiwixmobile.custom.main.ValidationState.HasBothFiles import org.kiwix.kiwixmobile.custom.main.ValidationState.HasFile import org.kiwix.kiwixmobile.custom.main.ValidationState.HasNothing import java.io.File -import java.io.FileDescriptor import java.io.IOException import javax.inject.Inject @@ -43,24 +43,24 @@ class CustomFileValidator @Inject constructor(private val context: Context) { private fun detectInstallationState( obbFiles: List = obbFiles(), zimFiles: List = zimFiles(), - assetFileDescriptor: FileDescriptor? = getFileFromPlayAssetDelivery() + assetFileDescriptor: ParcelFileDescriptor? = getParcelFileDescriptorFromPlayAssetDelivery() ): ValidationState { return when { assetFileDescriptor != null -> HasFile(null, assetFileDescriptor) - obbFiles.isNotEmpty() && zimFiles().isNotEmpty() -> HasBothFiles(obbFiles[0], zimFiles[0]) - obbFiles.isNotEmpty() -> HasFile(obbFiles[0]) - zimFiles.isNotEmpty() -> HasFile(zimFiles[0]) + // obbFiles.isNotEmpty() && zimFiles().isNotEmpty() -> HasBothFiles(obbFiles[0], zimFiles[0]) + // obbFiles.isNotEmpty() -> HasFile(obbFiles[0]) + // zimFiles.isNotEmpty() -> HasFile(zimFiles[0]) else -> HasNothing } } @Suppress("NestedBlockDepth", "MagicNumber") - private fun getFileFromPlayAssetDelivery(): FileDescriptor? { + private fun getParcelFileDescriptorFromPlayAssetDelivery(): ParcelFileDescriptor? { try { val context = context.createPackageContext(context.packageName, 0) val assetManager = context.assets - val inputStream = assetManager.openFd(BuildConfig.PLAY_ASSET_FILE) - return inputStream.fileDescriptor + val assetFileDescriptor = assetManager.openFd(BuildConfig.PLAY_ASSET_FILE) + return assetFileDescriptor.parcelFileDescriptor } catch (packageNameNotFoundException: PackageManager.NameNotFoundException) { Log.w( "ASSET_PACKAGE_DELIVERY", @@ -105,7 +105,7 @@ class CustomFileValidator @Inject constructor(private val context: Context) { sealed class ValidationState { data class HasBothFiles(val obbFile: File, val zimFile: File) : ValidationState() - data class HasFile(val file: File?, val fileDescriptor: FileDescriptor? = null) : + data class HasFile(val file: File?, val parcelFileDescriptor: ParcelFileDescriptor? = null) : ValidationState() object HasNothing : ValidationState() diff --git a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomReaderFragment.kt b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomReaderFragment.kt index f9ba5d208f..d0e28ee5a5 100644 --- a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomReaderFragment.kt +++ b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomReaderFragment.kt @@ -144,8 +144,8 @@ class CustomReaderFragment : CoreReaderFragment() { onFilesFound = { when (it) { is ValidationState.HasFile -> { - if (it.fileDescriptor != null) { - openZimFile(null, true, it.fileDescriptor) + if (it.parcelFileDescriptor != null) { + openZimFile(null, true, it.parcelFileDescriptor) } else { openZimFile(it.file, true) } From ae0893095c406c6d59692e02230dd9f3076b8646 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Tue, 31 Oct 2023 12:21:46 +0530 Subject: [PATCH 3/8] Fixed MimeTypeTest compilation error --- .../java/org/kiwix/kiwixmobile/mimetype/MimeTypeTest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/mimetype/MimeTypeTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/mimetype/MimeTypeTest.kt index c2190a20b6..576bf5dbe4 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/mimetype/MimeTypeTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/mimetype/MimeTypeTest.kt @@ -76,6 +76,7 @@ class MimeTypeTest : BaseActivityTest() { } val zimFileReader = ZimFileReader( zimFile, + null, Archive(zimFile.canonicalPath), NightModeConfig(SharedPreferenceUtil(context), context) ) From f31535966bfd02301339a46b4d77986dea07e266 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Tue, 31 Oct 2023 18:22:47 +0530 Subject: [PATCH 4/8] Using `AssetFileDescriptor` instead of `ParcelFileDescriptor`. --- .../core/main/CoreReaderFragment.kt | 14 +++++++------- .../kiwixmobile/core/reader/ZimFileReader.kt | 15 ++++++++++----- .../core/reader/ZimReaderContainer.kt | 8 ++++---- .../custom/main/CustomFileValidator.kt | 19 +++++++++---------- .../custom/main/CustomReaderFragment.kt | 4 ++-- 5 files changed, 32 insertions(+), 28 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt index c419baf48f..18e7236be6 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt @@ -26,6 +26,7 @@ import android.content.Intent import android.content.ServiceConnection import android.content.SharedPreferences import android.content.pm.PackageManager +import android.content.res.AssetFileDescriptor import android.content.res.Configuration import android.graphics.Canvas import android.graphics.Rect @@ -38,7 +39,6 @@ import android.os.CountDownTimer import android.os.Handler import android.os.IBinder import android.os.Looper -import android.os.ParcelFileDescriptor import android.provider.Settings import android.util.AttributeSet import android.util.Log @@ -1390,14 +1390,14 @@ abstract class CoreReaderFragment : protected fun openZimFile( file: File?, isCustomApp: Boolean = false, - parcelFileDescriptor: ParcelFileDescriptor? = null + assetFileDescriptor: AssetFileDescriptor? = null ) { if (hasPermission(Manifest.permission.READ_EXTERNAL_STORAGE) || isCustomApp) { if (file?.isFileExist() == true) { openAndSetInContainer(file = file) updateTitle() - } else if (parcelFileDescriptor != null) { - openAndSetInContainer(parcelFileDescriptor = parcelFileDescriptor) + } else if (assetFileDescriptor != null) { + openAndSetInContainer(assetFileDescriptor = assetFileDescriptor) updateTitle() } else { Log.w(TAG_KIWIX, "ZIM file doesn't exist at " + file?.absolutePath) @@ -1429,7 +1429,7 @@ abstract class CoreReaderFragment : private fun openAndSetInContainer( file: File? = null, - parcelFileDescriptor: ParcelFileDescriptor? = null + assetFileDescriptor: AssetFileDescriptor? = null ) { try { if (isNotPreviouslyOpenZim(file?.canonicalPath)) { @@ -1439,8 +1439,8 @@ abstract class CoreReaderFragment : e.printStackTrace() } zimReaderContainer?.let { zimReaderContainer -> - if (parcelFileDescriptor != null) { - zimReaderContainer.setZimFileDescriptor(parcelFileDescriptor) + if (assetFileDescriptor != null) { + zimReaderContainer.setZimFileDescriptor(assetFileDescriptor) } else { zimReaderContainer.setZimFile(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 438c1fa19d..4b6366ae96 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 @@ -53,14 +53,14 @@ private const val TAG = "ZimFileReader" class ZimFileReader constructor( val zimFile: File?, - val parcelFileDescriptor: ParcelFileDescriptor? = null, + private val assetFileDescriptor: AssetFileDescriptor? = null, private val jniKiwixReader: Archive, private val nightModeConfig: NightModeConfig, private val searcher: SuggestionSearcher = SuggestionSearcher(jniKiwixReader) ) { interface Factory { fun create(file: File): ZimFileReader? - fun create(parcelFileDescriptor: ParcelFileDescriptor): ZimFileReader? + fun create(assetFileDescriptor: AssetFileDescriptor): ZimFileReader? class Impl @Inject constructor(private val nightModeConfig: NightModeConfig) : Factory { @@ -79,13 +79,17 @@ class ZimFileReader constructor( null } - override fun create(parcelFileDescriptor: ParcelFileDescriptor): ZimFileReader? = + override fun create(assetFileDescriptor: AssetFileDescriptor): ZimFileReader? = try { ZimFileReader( null, - parcelFileDescriptor, + assetFileDescriptor, nightModeConfig = nightModeConfig, - jniKiwixReader = Archive(parcelFileDescriptor.fileDescriptor) + jniKiwixReader = Archive( + assetFileDescriptor.parcelFileDescriptor.dup().fileDescriptor, + assetFileDescriptor.startOffset, + assetFileDescriptor.length + ) ).also { Log.e(TAG, "create: with fileDescriptor") } @@ -307,6 +311,7 @@ class ZimFileReader constructor( fun dispose() { jniKiwixReader.dispose() searcher.dispose() + assetFileDescriptor?.parcelFileDescriptor?.detachFd() } @Suppress("TooGenericExceptionCaught") 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 052d900b1c..9c7b769028 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.os.ParcelFileDescriptor +import android.content.res.AssetFileDescriptor import android.webkit.WebResourceResponse import org.kiwix.kiwixmobile.core.extensions.isFileExist import org.kiwix.kiwixmobile.core.reader.ZimFileReader.Factory @@ -43,10 +43,10 @@ class ZimReaderContainer @Inject constructor(private val zimFileReaderFactory: F else null } - fun setZimFileDescriptor(parcelFileDescriptor: ParcelFileDescriptor) { + fun setZimFileDescriptor(assetFileDescriptor: AssetFileDescriptor) { zimFileReader = - if (parcelFileDescriptor.fileDescriptor.valid()) - zimFileReaderFactory.create(parcelFileDescriptor) + if (assetFileDescriptor.parcelFileDescriptor.dup().fileDescriptor.valid()) + zimFileReaderFactory.create(assetFileDescriptor) else null } diff --git a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomFileValidator.kt b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomFileValidator.kt index b0963ea27d..c689872de0 100644 --- a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomFileValidator.kt +++ b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomFileValidator.kt @@ -20,7 +20,7 @@ package org.kiwix.kiwixmobile.custom.main import android.content.Context import android.content.pm.PackageManager -import android.os.ParcelFileDescriptor +import android.content.res.AssetFileDescriptor import android.util.Log import androidx.core.content.ContextCompat import org.kiwix.kiwixmobile.custom.BuildConfig @@ -43,24 +43,23 @@ class CustomFileValidator @Inject constructor(private val context: Context) { private fun detectInstallationState( obbFiles: List = obbFiles(), zimFiles: List = zimFiles(), - assetFileDescriptor: ParcelFileDescriptor? = getParcelFileDescriptorFromPlayAssetDelivery() + assetFileDescriptor: AssetFileDescriptor? = getAssetFileDescriptorFromPlayAssetDelivery() ): ValidationState { return when { assetFileDescriptor != null -> HasFile(null, assetFileDescriptor) - // obbFiles.isNotEmpty() && zimFiles().isNotEmpty() -> HasBothFiles(obbFiles[0], zimFiles[0]) - // obbFiles.isNotEmpty() -> HasFile(obbFiles[0]) - // zimFiles.isNotEmpty() -> HasFile(zimFiles[0]) + obbFiles.isNotEmpty() && zimFiles().isNotEmpty() -> HasBothFiles(obbFiles[0], zimFiles[0]) + obbFiles.isNotEmpty() -> HasFile(obbFiles[0]) + zimFiles.isNotEmpty() -> HasFile(zimFiles[0]) else -> HasNothing } } - @Suppress("NestedBlockDepth", "MagicNumber") - private fun getParcelFileDescriptorFromPlayAssetDelivery(): ParcelFileDescriptor? { + @Suppress("MagicNumber") + private fun getAssetFileDescriptorFromPlayAssetDelivery(): AssetFileDescriptor? { try { val context = context.createPackageContext(context.packageName, 0) val assetManager = context.assets - val assetFileDescriptor = assetManager.openFd(BuildConfig.PLAY_ASSET_FILE) - return assetFileDescriptor.parcelFileDescriptor + return assetManager.openFd(BuildConfig.PLAY_ASSET_FILE) } catch (packageNameNotFoundException: PackageManager.NameNotFoundException) { Log.w( "ASSET_PACKAGE_DELIVERY", @@ -105,7 +104,7 @@ class CustomFileValidator @Inject constructor(private val context: Context) { sealed class ValidationState { data class HasBothFiles(val obbFile: File, val zimFile: File) : ValidationState() - data class HasFile(val file: File?, val parcelFileDescriptor: ParcelFileDescriptor? = null) : + data class HasFile(val file: File?, val assetFileDescriptor: AssetFileDescriptor? = null) : ValidationState() object HasNothing : ValidationState() diff --git a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomReaderFragment.kt b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomReaderFragment.kt index d0e28ee5a5..0dcad4b6bf 100644 --- a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomReaderFragment.kt +++ b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomReaderFragment.kt @@ -144,8 +144,8 @@ class CustomReaderFragment : CoreReaderFragment() { onFilesFound = { when (it) { is ValidationState.HasFile -> { - if (it.parcelFileDescriptor != null) { - openZimFile(null, true, it.parcelFileDescriptor) + if (it.assetFileDescriptor != null) { + openZimFile(null, true, it.assetFileDescriptor) } else { openZimFile(it.file, true) } From b6d718f287df5424f06f396b6ec18c3ae3c20249 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Wed, 1 Nov 2023 15:36:14 +0530 Subject: [PATCH 5/8] Fixed note saving with `FileDescriptor`. * Now we are using fileDescriptor for custom apps to read zim file from asset folder, so we have improved our notes saving functionality according to this change. --- .../org/kiwix/kiwixmobile/core/main/AddNoteDialog.kt | 2 +- .../page/notes/viewmodel/effects/ShowOpenNoteDialog.kt | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/AddNoteDialog.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/AddNoteDialog.kt index e280061a1d..dc52f0ff4a 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/AddNoteDialog.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/AddNoteDialog.kt @@ -117,7 +117,7 @@ class AddNoteDialog : DialogFragment() { .inject(this) // Returns name of the form ".../Kiwix/granbluefantasy_en_all_all_nopic_2018-10.zim" - zimFileName = zimReaderContainer.zimCanonicalPath + zimFileName = zimReaderContainer.zimCanonicalPath ?: zimReaderContainer.name if (zimFileName != null) { // No zim file currently opened zimFileTitle = zimReaderContainer.zimFileTitle zimId = zimReaderContainer.id.orEmpty() diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/notes/viewmodel/effects/ShowOpenNoteDialog.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/notes/viewmodel/effects/ShowOpenNoteDialog.kt index 77873906d4..8adaa9b4ae 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/notes/viewmodel/effects/ShowOpenNoteDialog.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/notes/viewmodel/effects/ShowOpenNoteDialog.kt @@ -45,8 +45,13 @@ data class ShowOpenNoteDialog( { effects.offer(OpenPage(page, zimReaderContainer)) }, { val item = page as NoteListItem - val file = File(item.zimFilePath.orEmpty()) - zimReaderContainer.setZimFile(file) + // Check if zimFilePath is not null, and then set it in zimReaderContainer. + // For custom apps, we are currently using fileDescriptor, and they only have a single file in them, + // which is already set in zimReaderContainer, so there's no need to set it again. + item.zimFilePath?.let { + val file = File(it) + zimReaderContainer.setZimFile(file) + } effects.offer(OpenNote(item.noteFilePath, item.zimUrl, item.title)) } ) From 845f19eab62f20b0d203429eb854ed3ac9e3332d Mon Sep 17 00:00:00 2001 From: MohitMali Date: Wed, 1 Nov 2023 16:51:52 +0530 Subject: [PATCH 6/8] Fixed #3519. * The problem was that we were saving the note name combined with `zimFileTitle` and `articleTitle`. When opening the saved note from `NotesFragment`, it retrieved the `noteTitle` from the database, which already contained the combined name. Subsequently, when saving the note again, it redundantly combined the name. To resolve this issue, we now extract the `articleName` when updating notes in `NotesFragment`, ensuring that it updates existing notes instead of creating new ones. --- .../main/java/org/kiwix/kiwixmobile/core/main/AddNoteDialog.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/AddNoteDialog.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/AddNoteDialog.kt index dc52f0ff4a..5cd95a7f7e 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/AddNoteDialog.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/AddNoteDialog.kt @@ -123,7 +123,7 @@ class AddNoteDialog : DialogFragment() { zimId = zimReaderContainer.id.orEmpty() if (arguments != null) { - articleTitle = arguments?.getString(NOTES_TITLE) + articleTitle = arguments?.getString(NOTES_TITLE)?.substringAfter(": ") zimFileUrl = arguments?.getString(ARTICLE_URL).orEmpty() } else { val webView = (activity as WebViewProvider?)?.getCurrentWebView() From bdb00d7aae666e69a1bf039538c494dab85c453b Mon Sep 17 00:00:00 2001 From: MohitMali Date: Thu, 2 Nov 2023 17:21:29 +0530 Subject: [PATCH 7/8] Fixed the issue where the Zim File was not displaying in the `ZimHostFragment` for custom apps. * In `ZimHostFragment`, we show Zim files that are saved in the database. These files are typically saved when downloading Zim files. In a custom app, where Zim files are already included within the app and not downloaded separately that's why they are not showing on the `ZimHostFragment`, we have addressed this issue by saving the Zim files in the database to ensure they appear in the `ZimHostFragment`. * Regarding the `FileDescriptor`, there are no file objects available because we read Zim files using `FileDescriptor`. To address this, we have created a `demo.zim` file to save it in the database so that it will be displayed in the `ZimHostFragment`. We handle this file within the `KiwixServer`. When the current Zim file is `demo.zim`, we create an `Archive` object with the `FileDescriptor` to host the Zim file on the `KiwixServer`. --- .../core/main/MainRepositoryActions.kt | 8 +++++ .../kiwixmobile/core/reader/ZimFileReader.kt | 2 +- .../kiwixmobile/core/utils/files/FileUtils.kt | 5 ++++ .../kiwixmobile/core/webserver/KiwixServer.kt | 26 +++++++++++++++-- .../core/webserver/ZimHostFragment.kt | 29 +++++++++---------- .../custom/main/CustomReaderFragment.kt | 18 ++++++++++++ 6 files changed, 70 insertions(+), 18 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/MainRepositoryActions.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/MainRepositoryActions.kt index 4f6a440cc9..6acee4c889 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/MainRepositoryActions.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/MainRepositoryActions.kt @@ -24,6 +24,7 @@ import org.kiwix.kiwixmobile.core.di.ActivityScope import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem.HistoryItem import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem +import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk import javax.inject.Inject private const val TAG = "MainPresenter" @@ -33,6 +34,7 @@ class MainRepositoryActions @Inject constructor(private val dataSource: DataSour private var saveHistoryDisposable: Disposable? = null private var saveBookmarkDisposable: Disposable? = null private var saveNoteDisposable: Disposable? = null + private var saveBookDisposable: Disposable? = null private var deleteNoteDisposable: Disposable? = null fun saveHistory(history: HistoryItem) { @@ -61,10 +63,16 @@ class MainRepositoryActions @Inject constructor(private val dataSource: DataSour .subscribe({}, { e -> Log.e(TAG, "Unable to delete note", e) }) } + fun saveBook(book: BookOnDisk) { + saveBookDisposable = dataSource.saveBook(book) + .subscribe({}, { e -> Log.e(TAG, "Unable to save book", e) }) + } + fun dispose() { saveHistoryDisposable?.dispose() saveBookmarkDisposable?.dispose() saveNoteDisposable?.dispose() deleteNoteDisposable?.dispose() + saveBookDisposable?.dispose() } } 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 4b6366ae96..5a85bf35cf 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 @@ -53,7 +53,7 @@ private const val TAG = "ZimFileReader" class ZimFileReader constructor( val zimFile: File?, - private val assetFileDescriptor: AssetFileDescriptor? = null, + val assetFileDescriptor: AssetFileDescriptor? = null, private val jniKiwixReader: Archive, private val nightModeConfig: NightModeConfig, private val searcher: SuggestionSearcher = SuggestionSearcher(jniKiwixReader) 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 67642e208a..942ba5a100 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 @@ -29,6 +29,7 @@ import android.provider.DocumentsContract import android.util.Log import android.webkit.URLUtil import android.widget.Toast +import androidx.core.content.ContextCompat import androidx.documentfile.provider.DocumentFile import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -402,4 +403,8 @@ object FileUtils { null } } + + @JvmStatic + fun getDemoFilePathForCustomApp(context: Context) = + "${ContextCompat.getExternalFilesDirs(context, null)[0]}/demo.zim" } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/webserver/KiwixServer.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/webserver/KiwixServer.kt index b2071029b4..5d2fef13ec 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/webserver/KiwixServer.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/webserver/KiwixServer.kt @@ -18,7 +18,10 @@ package org.kiwix.kiwixmobile.core.webserver +import android.content.Context import android.util.Log +import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer +import org.kiwix.kiwixmobile.core.utils.files.FileUtils.getDemoFilePathForCustomApp import org.kiwix.libkiwix.Book import org.kiwix.libkiwix.JNIKiwixException import org.kiwix.libkiwix.Library @@ -36,13 +39,32 @@ class KiwixServer @Inject constructor( private val jniKiwixServer: Server ) { - class Factory @Inject constructor() { + class Factory @Inject constructor( + private val context: Context, + private val zimReaderContainer: ZimReaderContainer + ) { + @Suppress("NestedBlockDepth") fun createKiwixServer(selectedBooksPath: ArrayList): KiwixServer { val kiwixLibrary = Library() selectedBooksPath.forEach { path -> try { val book = Book().apply { - update(Archive(path)) + // Determine whether to create an Archive from an asset or a file path + val archive = if (path == getDemoFilePathForCustomApp(context)) { + // For custom apps using a demo file, create an Archive with FileDescriptor + val assetFileDescriptor = zimReaderContainer.zimFileReader?.assetFileDescriptor + val startOffset = assetFileDescriptor?.startOffset ?: 0L + val size = assetFileDescriptor?.length ?: 0L + Archive( + assetFileDescriptor?.parcelFileDescriptor?.dup()?.fileDescriptor, + startOffset, + size + ) + } else { + // For regular files, create an Archive from the file path + Archive(path) + } + update(archive) } kiwixLibrary.addBook(book) } catch (e: JNIKiwixException) { diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/webserver/ZimHostFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/webserver/ZimHostFragment.kt index c3bdaf0f33..feadfbda92 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/webserver/ZimHostFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/webserver/ZimHostFragment.kt @@ -53,6 +53,7 @@ import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.requestNotificat import org.kiwix.kiwixmobile.core.extensions.toast import org.kiwix.kiwixmobile.core.navigateToAppSettings import org.kiwix.kiwixmobile.core.reader.ZimFileReader +import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer import org.kiwix.kiwixmobile.core.utils.ConnectivityReporter import org.kiwix.kiwixmobile.core.utils.REQUEST_POST_NOTIFICATION_PERMISSION import org.kiwix.kiwixmobile.core.utils.ServerUtils @@ -60,15 +61,15 @@ import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil import org.kiwix.kiwixmobile.core.utils.dialog.AlertDialogShower import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.StartServer +import org.kiwix.kiwixmobile.core.webserver.wifi_hotspot.HotspotService +import org.kiwix.kiwixmobile.core.webserver.wifi_hotspot.HotspotService.Companion.ACTION_CHECK_IP_ADDRESS +import org.kiwix.kiwixmobile.core.webserver.wifi_hotspot.HotspotService.Companion.ACTION_START_SERVER +import org.kiwix.kiwixmobile.core.webserver.wifi_hotspot.HotspotService.Companion.ACTION_STOP_SERVER import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.SelectionMode import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BookOnDiskDelegate import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskAdapter import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk -import org.kiwix.kiwixmobile.core.webserver.wifi_hotspot.HotspotService -import org.kiwix.kiwixmobile.core.webserver.wifi_hotspot.HotspotService.Companion.ACTION_CHECK_IP_ADDRESS -import org.kiwix.kiwixmobile.core.webserver.wifi_hotspot.HotspotService.Companion.ACTION_START_SERVER -import org.kiwix.kiwixmobile.core.webserver.wifi_hotspot.HotspotService.Companion.ACTION_STOP_SERVER import javax.inject.Inject class ZimHostFragment : BaseFragment(), ZimHostCallbacks, ZimHostContract.View { @@ -87,6 +88,9 @@ class ZimHostFragment : BaseFragment(), ZimHostCallbacks, ZimHostContract.View { @Inject lateinit var zimReaderFactory: ZimFileReader.Factory + @Inject + lateinit var zimReaderContainer: ZimReaderContainer + private lateinit var booksAdapter: BooksOnDiskAdapter private lateinit var bookDelegate: BookOnDiskDelegate.BookDelegate private var hotspotService: HotspotService? = null @@ -485,18 +489,13 @@ class ZimHostFragment : BaseFragment(), ZimHostCallbacks, ZimHostContract.View { val updatedBooksList: MutableList = arrayListOf() books.forEach { if (it is BookOnDisk) { - zimReaderFactory.create(it.file)?.let { zimFileReader -> + zimReaderContainer.zimFileReader?.let { zimFileReader -> val booksOnDiskListItem = - zimFileReader.zimFile?.let { file -> - (BookOnDisk(file, zimFileReader) as BooksOnDiskListItem) - .apply { - isSelected = true - } - } - if (booksOnDiskListItem != null) { - updatedBooksList.add(booksOnDiskListItem) - } - zimFileReader.dispose() + (BookOnDisk(it.file, zimFileReader) as BooksOnDiskListItem) + .apply { + isSelected = true + } + updatedBooksList.add(booksOnDiskListItem) } } else { updatedBooksList.add(it) diff --git a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomReaderFragment.kt b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomReaderFragment.kt index 0dcad4b6bf..20672ff4ee 100644 --- a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomReaderFragment.kt +++ b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomReaderFragment.kt @@ -32,6 +32,7 @@ import org.kiwix.kiwixmobile.core.base.BaseActivity import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.observeNavigationResult import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.setupDrawerToggle +import org.kiwix.kiwixmobile.core.extensions.isFileExist import org.kiwix.kiwixmobile.core.main.CoreReaderFragment import org.kiwix.kiwixmobile.core.main.FIND_IN_PAGE_SEARCH_STRING import org.kiwix.kiwixmobile.core.main.MainMenu @@ -39,11 +40,14 @@ import org.kiwix.kiwixmobile.core.search.viewmodel.effects.SearchItemToOpen import org.kiwix.kiwixmobile.core.utils.LanguageUtils import org.kiwix.kiwixmobile.core.utils.TAG_FILE_SEARCHED import org.kiwix.kiwixmobile.core.utils.dialog.DialogShower +import org.kiwix.kiwixmobile.core.utils.files.FileUtils.getDemoFilePathForCustomApp import org.kiwix.kiwixmobile.core.utils.titleToUrl import org.kiwix.kiwixmobile.core.utils.urlSuffixToParsableUrl +import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk import org.kiwix.kiwixmobile.custom.BuildConfig import org.kiwix.kiwixmobile.custom.R import org.kiwix.kiwixmobile.custom.customActivityComponent +import java.io.File import java.util.Locale import javax.inject.Inject @@ -149,6 +153,15 @@ class CustomReaderFragment : CoreReaderFragment() { } else { openZimFile(it.file, true) } + // Save book in the database to display it in `ZimHostFragment`. + zimReaderContainer?.zimFileReader?.let { zimFileReader -> + // Check if the file is not null. If the file is null, + // it means we have created zimFileReader with a fileDescriptor, + // so we create a demo file to save it in the database for display on the `ZimHostFragment`. + val file = it.file ?: createDemoFile() + val bookOnDisk = BookOnDisk(file, zimFileReader) + repositoryActions?.saveBook(bookOnDisk) + } } is ValidationState.HasBothFiles -> { it.zimFile.delete() @@ -163,6 +176,11 @@ class CustomReaderFragment : CoreReaderFragment() { ) } + private fun createDemoFile() = + File(getDemoFilePathForCustomApp(requireActivity())).also { + if (!it.isFileExist()) it.createNewFile() + } + @Suppress("DEPRECATION") override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { super.onCreateOptionsMenu(menu, inflater) From 8b1cb8bc7e42af940873b64df390467f31484c49 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Mon, 6 Nov 2023 15:58:36 +0530 Subject: [PATCH 8/8] We are temporary hiding the `kiwixServer` feature from `Custom Apps`. * Since we are now using fd (FileDescriptor) to read the zim file from the asset folder. Currently, 'KiwixServer' is unable to host zim files via fd. This feature is temporarily hidden for custom apps. We will re-enable it for custom apps once the issue is resolved. --- .../kiwixmobile/core/webserver/ZimHostFragment.kt | 3 ++- .../kiwixmobile/custom/main/CustomMainActivity.kt | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/webserver/ZimHostFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/webserver/ZimHostFragment.kt index feadfbda92..84f57414ed 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/webserver/ZimHostFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/webserver/ZimHostFragment.kt @@ -49,6 +49,7 @@ import org.kiwix.kiwixmobile.core.base.BaseFragment import org.kiwix.kiwixmobile.core.databinding.ActivityZimHostBinding import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.cachedComponent import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.hasNotificationPermission +import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.isCustomApp import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.requestNotificationPermission import org.kiwix.kiwixmobile.core.extensions.toast import org.kiwix.kiwixmobile.core.navigateToAppSettings @@ -483,7 +484,7 @@ class ZimHostFragment : BaseFragment(), ZimHostCallbacks, ZimHostContract.View { @Suppress("NestedBlockDepth") override fun addBooks(books: List) { // Check if this is the app module, as custom apps may have multiple package names - if (requireActivity().packageName == "org.kiwix.kiwixmobile") { + if (!requireActivity().isCustomApp()) { booksAdapter.items = books } else { val updatedBooksList: MutableList = arrayListOf() diff --git a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomMainActivity.kt b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomMainActivity.kt index 3c0d1fca3d..308506cb93 100644 --- a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomMainActivity.kt +++ b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomMainActivity.kt @@ -95,6 +95,16 @@ class CustomMainActivity : CoreMainActivity() { override fun setupDrawerToggle(toolbar: Toolbar) { super.setupDrawerToggle(toolbar) activityCustomMainBinding.drawerNavView.apply { + /** + * Hide the 'ZimHostFragment' option from the navigation menu + * because we are now using fd (FileDescriptor) + * to read the zim file from the asset folder. Currently, + * 'KiwixServer' is unable to host zim files via fd. + * This feature is temporarily hidden for custom apps. + * We will re-enable it for custom apps once the issue is resolved. + * For more info see https://github.com/kiwix/kiwix-android/pull/3516 + */ + menu.findItem(R.id.menu_host_books)?.isVisible = false setNavigationItemSelectedListener { item -> closeNavigationDrawer() onNavigationItemSelected(item)