Introducing reading zim file content through fileDescriptor instead of creating a file to avoid using storage twice for same zim file.

This commit is contained in:
MohitMali 2023-10-26 18:33:15 +05:30 committed by Kelson
parent ae413559fe
commit 99614ed88f
9 changed files with 74 additions and 45 deletions

View File

@ -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 ->
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

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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
}
}
}

View File

@ -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()

View File

@ -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)
zimFileReader.zimFile?.let { file ->
(BookOnDisk(file, zimFileReader) as BooksOnDiskListItem)
.apply {
isSelected = true
}
updatedBooksList.add(booksOnDiskListItem).also {
zimFileReader.dispose()
}
if (booksOnDiskListItem != null) {
updatedBooksList.add(booksOnDiskListItem)
}
zimFileReader.dispose()
}
} else {
updatedBooksList.add(it)

View File

@ -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<File> = obbFiles(),
zimFiles: List<File> = 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()
}

View File

@ -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)