mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-09-21 03:02:59 -04:00
Refactored the code to support the java-libkiwix 11.0.0
This commit is contained in:
parent
8469e18e23
commit
1476452085
@ -100,6 +100,9 @@ play {
|
||||
|
||||
dependencies {
|
||||
androidTestImplementation(Libs.leakcanary_android_instrumentation)
|
||||
implementation("com.getkeepsafe.relinker:relinker:1.4.5")
|
||||
api(fileTree(mapOf("include" to "*.aar", "dir" to "libs")))
|
||||
implementation(files("/home/hp-pc03/Desktop/lib-release.aar"))
|
||||
}
|
||||
task("generateVersionCodeAndName") {
|
||||
val file = File("VERSION_INFO")
|
||||
|
@ -25,7 +25,7 @@ import androidx.test.uiautomator.UiDevice
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.kiwix.kiwixlib.JNIKiwixReader
|
||||
import org.kiwix.libzim.Archive
|
||||
import org.kiwix.kiwixmobile.BaseActivityTest
|
||||
import org.kiwix.kiwixmobile.core.NightModeConfig
|
||||
import org.kiwix.kiwixmobile.core.reader.ZimFileReader
|
||||
@ -70,12 +70,12 @@ class MimeTypeTest : BaseActivityTest() {
|
||||
}
|
||||
val zimFileReader = ZimFileReader(
|
||||
zimFile,
|
||||
JNIKiwixReader(zimFile.canonicalPath),
|
||||
Archive(zimFile.canonicalPath),
|
||||
NightModeConfig(SharedPreferenceUtil(context), context)
|
||||
)
|
||||
zimFileReader.getRandomArticleUrl()?.let {
|
||||
val mimeType = zimFileReader.readContentAndMimeType(it)
|
||||
if (mimeType.contains("^([^ ]+).*$") || mimeType.contains(";")) {
|
||||
val mimeType = zimFileReader.getMimeTypeFromUrl(it)
|
||||
if (mimeType?.contains("^([^ ]+).*$") == true || mimeType?.contains(";") == true) {
|
||||
Assert.fail(
|
||||
"Unable to get mime type from zim file. File = " +
|
||||
" $zimFile and url of article = $it"
|
||||
|
@ -19,26 +19,31 @@
|
||||
package org.kiwix.kiwixmobile.webserver
|
||||
|
||||
import android.util.Log
|
||||
import org.kiwix.kiwixlib.JNIKiwixException
|
||||
import org.kiwix.kiwixlib.JNIKiwixServer
|
||||
import org.kiwix.kiwixlib.Library
|
||||
import org.kiwix.libkiwix.Book
|
||||
import org.kiwix.libkiwix.JNIKiwixException
|
||||
import org.kiwix.libkiwix.Library
|
||||
import org.kiwix.libkiwix.Server
|
||||
import org.kiwix.libzim.Archive
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val TAG = "KiwixServer"
|
||||
|
||||
class KiwixServer @Inject constructor(private val jniKiwixServer: JNIKiwixServer) {
|
||||
class KiwixServer @Inject constructor(private val jniKiwixServer: Server) {
|
||||
|
||||
class Factory @Inject constructor() {
|
||||
fun createKiwixServer(selectedBooksPath: ArrayList<String>): KiwixServer {
|
||||
val kiwixLibrary = Library()
|
||||
selectedBooksPath.forEach { path ->
|
||||
try {
|
||||
kiwixLibrary.addBook(path)
|
||||
val book = Book().apply {
|
||||
update(Archive(path))
|
||||
}
|
||||
kiwixLibrary.addBook(book)
|
||||
} catch (e: JNIKiwixException) {
|
||||
Log.v(TAG, "Couldn't add book with path:{ $path }")
|
||||
}
|
||||
}
|
||||
return KiwixServer(JNIKiwixServer(kiwixLibrary))
|
||||
return KiwixServer(Server(kiwixLibrary))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,12 +60,16 @@ dependencies {
|
||||
implementation(Libs.threetenabp)
|
||||
|
||||
// Get kiwixlib online if it is not populated locally
|
||||
if (!shouldUseLocalVersion()) {
|
||||
api(Libs.kiwixlib)
|
||||
} else {
|
||||
implementation("com.getkeepsafe.relinker:relinker:1.4.5")
|
||||
api(fileTree(mapOf("include" to "*.aar", "dir" to "libs")))
|
||||
}
|
||||
implementation("com.getkeepsafe.relinker:relinker:1.4.5")
|
||||
api(fileTree(mapOf("include" to "*.aar", "dir" to "libs")))
|
||||
implementation(files("/home/hp-pc03/Desktop/lib-release.aar"))
|
||||
// if (!shouldUseLocalVersion()) {
|
||||
// api(Libs.kiwixlib)
|
||||
// } else {
|
||||
// implementation("com.getkeepsafe.relinker:relinker:1.4.5")
|
||||
// api(fileTree(mapOf("include" to "*.aar", "dir" to "libs")))
|
||||
// implementation(files("/home/hp-pc03/Desktop/lib-release.aar"))
|
||||
// }
|
||||
|
||||
// Document File
|
||||
implementation(Libs.select_folder_document_file)
|
||||
|
@ -19,7 +19,7 @@ package org.kiwix.kiwixmobile.core
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import org.kiwix.kiwixlib.JNIKiwix
|
||||
import org.kiwix.libkiwix.JNIKiwix
|
||||
import org.kiwix.kiwixmobile.core.utils.TAG_KIWIX
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
@ -27,7 +27,7 @@ import javax.inject.Inject
|
||||
|
||||
internal class JNIInitialiser @Inject constructor(context: Context, jniKiwix: JNIKiwix) {
|
||||
init {
|
||||
loadICUData(context)?.let(jniKiwix::setDataDirectory)
|
||||
// loadICUData(context)?.let(jniKiwix::setDataDirectory)
|
||||
}
|
||||
|
||||
private fun loadICUData(context: Context): String? {
|
||||
|
@ -20,20 +20,11 @@ package org.kiwix.kiwixmobile.core.di.modules
|
||||
import android.content.Context
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import org.kiwix.kiwixlib.JNIKiwix
|
||||
import org.kiwix.kiwixlib.JNIKiwixSearcher
|
||||
import org.kiwix.libkiwix.JNIKiwix
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
class JNIModule {
|
||||
@Provides @Singleton
|
||||
fun providesJNIKiwix(context: Context): JNIKiwix = JNIKiwix(context)
|
||||
|
||||
@Provides @Singleton fun providesJNIKiwixSearcher(): JNIKiwixSearcher? {
|
||||
return try {
|
||||
JNIKiwixSearcher()
|
||||
} catch (ignore: UnsatisfiedLinkError) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,15 +21,11 @@ import android.annotation.SuppressLint
|
||||
import android.content.res.AssetFileDescriptor
|
||||
import android.net.Uri
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import androidx.core.net.toUri
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import org.kiwix.kiwixlib.DirectAccessInfo
|
||||
import org.kiwix.kiwixlib.JNIKiwixException
|
||||
import org.kiwix.kiwixlib.JNIKiwixInt
|
||||
import org.kiwix.kiwixlib.JNIKiwixReader
|
||||
import org.kiwix.kiwixlib.JNIKiwixString
|
||||
import org.kiwix.kiwixmobile.core.CoreApp
|
||||
import org.kiwix.kiwixmobile.core.NightModeConfig
|
||||
import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity.Book
|
||||
@ -38,6 +34,13 @@ import org.kiwix.kiwixmobile.core.main.UNINITIALISE_HTML
|
||||
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 org.kiwix.libkiwix.JNIKiwixException
|
||||
import org.kiwix.libzim.Archive
|
||||
import org.kiwix.libzim.DirectAccessInfo
|
||||
import org.kiwix.libzim.EntryNotFoundException
|
||||
import org.kiwix.libzim.Item
|
||||
import org.kiwix.libzim.SuggestionSearch
|
||||
import org.kiwix.libzim.SuggestionSearcher
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.IOException
|
||||
@ -51,8 +54,9 @@ private const val TAG = "ZimFileReader"
|
||||
|
||||
class ZimFileReader constructor(
|
||||
val zimFile: File,
|
||||
private val jniKiwixReader: JNIKiwixReader = JNIKiwixReader(zimFile.canonicalPath),
|
||||
private val nightModeConfig: NightModeConfig
|
||||
private val jniKiwixReader: Archive = Archive(zimFile.canonicalPath),
|
||||
private val nightModeConfig: NightModeConfig,
|
||||
private val suggestionSearcher: SuggestionSearcher = SuggestionSearcher(jniKiwixReader)
|
||||
) {
|
||||
interface Factory {
|
||||
fun create(file: File): ZimFileReader?
|
||||
@ -61,7 +65,9 @@ class ZimFileReader constructor(
|
||||
Factory {
|
||||
override fun create(file: File) =
|
||||
try {
|
||||
ZimFileReader(file, nightModeConfig = nightModeConfig)
|
||||
ZimFileReader(file, nightModeConfig = nightModeConfig).also {
|
||||
Log.e(TAG, "create: ${file.path}")
|
||||
}
|
||||
} catch (ignore: JNIKiwixException) {
|
||||
null
|
||||
}
|
||||
@ -72,17 +78,42 @@ class ZimFileReader constructor(
|
||||
* Note that the value returned is NOT unique for each zim file. Versions of the same wiki
|
||||
* (complete, nopic, novid, etc) may return the same title.
|
||||
*/
|
||||
val title: String get() = jniKiwixReader.title ?: "No Title Found"
|
||||
val mainPage: String get() = jniKiwixReader.mainPage
|
||||
val id: String get() = jniKiwixReader.id
|
||||
val fileSize: Int get() = jniKiwixReader.fileSize
|
||||
val creator: String get() = jniKiwixReader.creator
|
||||
val publisher: String get() = jniKiwixReader.publisher
|
||||
val name: String get() = jniKiwixReader.name?.takeIf(String::isNotEmpty) ?: id
|
||||
val date: String get() = jniKiwixReader.date
|
||||
val description: String get() = jniKiwixReader.description
|
||||
val favicon: String? get() = jniKiwixReader.favicon
|
||||
val language: String get() = jniKiwixReader.language
|
||||
val title: String
|
||||
get() =
|
||||
try {
|
||||
if (jniKiwixReader.mainEntry.isRedirect)
|
||||
jniKiwixReader.mainEntry.getItem(true).title
|
||||
else
|
||||
jniKiwixReader.mainEntry.title
|
||||
} catch (entryNotFound: EntryNotFoundException) {
|
||||
"No Title Found"
|
||||
}
|
||||
val mainPage: String?
|
||||
get() =
|
||||
try {
|
||||
if (jniKiwixReader.mainEntry.isRedirect)
|
||||
jniKiwixReader.mainEntry.getItem(true).path
|
||||
else
|
||||
jniKiwixReader.mainEntry.path
|
||||
} catch (entryNotFound: EntryNotFoundException) {
|
||||
null
|
||||
}
|
||||
val id: String get() = jniKiwixReader.uuid
|
||||
val fileSize: Long get() = jniKiwixReader.filesize
|
||||
val creator: String get() = jniKiwixReader.getMetadata("Creator")
|
||||
val publisher: String get() = jniKiwixReader.getMetadata("Publisher")
|
||||
val name: String get() = jniKiwixReader.getMetadata("Name")?.takeIf(String::isNotEmpty) ?: id
|
||||
val date: String get() = jniKiwixReader.getMetadata("Date")
|
||||
val description: String get() = jniKiwixReader.getMetadata("Description")
|
||||
val favicon: String?
|
||||
get() = if (jniKiwixReader.hasIllustration(48))
|
||||
Base64.encodeToString(
|
||||
jniKiwixReader.getIllustrationItem(48).data.data,
|
||||
Base64.DEFAULT
|
||||
)
|
||||
else
|
||||
null
|
||||
val language: String get() = jniKiwixReader.getMetadata("Language")
|
||||
val tags: String get() = "${getContent("M/Tags")}"
|
||||
private val mediaCount: Int?
|
||||
get() = try {
|
||||
@ -97,23 +128,28 @@ class ZimFileReader constructor(
|
||||
null
|
||||
}
|
||||
|
||||
fun searchSuggestions(prefix: String, count: Int) =
|
||||
jniKiwixReader.searchSuggestions(prefix, count)
|
||||
fun searchSuggestions(prefix: String): SuggestionSearch =
|
||||
suggestionSearcher.suggest(prefix)
|
||||
|
||||
fun getNextSuggestion(): SearchSuggestion? {
|
||||
val title = JNIKiwixString()
|
||||
val url = JNIKiwixString()
|
||||
|
||||
return if (jniKiwixReader.getNextSuggestion(title, url))
|
||||
SearchSuggestion(title.value, url.value)
|
||||
else null
|
||||
fun getNextSuggestion(suggestionSearch: SuggestionSearch?): SearchSuggestion? {
|
||||
val suggestionIterator =
|
||||
suggestionSearch?.getResults(0, suggestionSearch.estimatedMatches.toInt())
|
||||
if (suggestionIterator != null) {
|
||||
while (suggestionIterator.hasNext()) {
|
||||
val suggestionItem = suggestionIterator.next()
|
||||
return SearchSuggestion(suggestionItem.title, suggestionItem.path)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun getPageUrlFrom(title: String): String? =
|
||||
valueOfJniStringAfter { jniKiwixReader.getPageUrlFromTitle(title, it) }
|
||||
if (jniKiwixReader.hasEntryByTitle(title))
|
||||
jniKiwixReader.getEntryByTitle(title).path
|
||||
else
|
||||
null
|
||||
|
||||
fun getRandomArticleUrl(): String? =
|
||||
valueOfJniStringAfter(jniKiwixReader::getRandomPage)
|
||||
fun getRandomArticleUrl(): String? = jniKiwixReader.randomEntry.path
|
||||
|
||||
fun load(uri: String): InputStream? {
|
||||
val extension = uri.substringAfterLast(".")
|
||||
@ -127,8 +163,8 @@ class ZimFileReader constructor(
|
||||
return loadContent(uri)
|
||||
}
|
||||
|
||||
fun readContentAndMimeType(uri: String): String = getContentAndMimeType(uri)
|
||||
.second.truncateMimeType.also {
|
||||
fun getMimeTypeFromUrl(uri: String): String? = getItem(uri)?.mimetype
|
||||
?.truncateMimeType.also {
|
||||
Log.d(TAG, "getting mimetype for $uri = $it")
|
||||
}
|
||||
|
||||
@ -141,9 +177,20 @@ class ZimFileReader constructor(
|
||||
}
|
||||
|
||||
private fun toRedirect(url: String) =
|
||||
"$CONTENT_PREFIX${
|
||||
jniKiwixReader.checkUrl(url.toUri().filePath).replaceWithEncodedString
|
||||
}".toUri()
|
||||
"$CONTENT_PREFIX${getActualUrl(url)}".toUri()
|
||||
|
||||
private fun getActualUrl(url: String): String {
|
||||
val actualPath = url.toUri().filePath
|
||||
val redirectPath = if (jniKiwixReader.hasEntryByPath(actualPath)) {
|
||||
jniKiwixReader.getEntryByPath(actualPath)
|
||||
.getItem(true)
|
||||
.path
|
||||
.replaceWithEncodedString
|
||||
} else {
|
||||
""
|
||||
}
|
||||
return redirectPath
|
||||
}
|
||||
|
||||
private fun loadContent(uri: String) =
|
||||
try {
|
||||
@ -154,14 +201,19 @@ class ZimFileReader constructor(
|
||||
}
|
||||
|
||||
private fun loadAsset(uri: String): InputStream? {
|
||||
val infoPair = jniKiwixReader.getDirectAccessInformation(uri.filePath)
|
||||
val article = if (jniKiwixReader.hasEntryByPath(uri.filePath)) {
|
||||
jniKiwixReader.getEntryByPath(uri.filePath).getItem(true)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val infoPair = article?.directAccessInformation
|
||||
if (infoPair == null || !File(infoPair.filename).exists()) {
|
||||
return loadAssetFromCache(uri)
|
||||
}
|
||||
return AssetFileDescriptor(
|
||||
infoPair.parcelFileDescriptor,
|
||||
infoPair.offset,
|
||||
jniKiwixReader.getArticleSize(uri.filePath)
|
||||
article.size
|
||||
).createInputStream()
|
||||
}
|
||||
|
||||
@ -170,11 +222,11 @@ class ZimFileReader constructor(
|
||||
return File(
|
||||
FileUtils.getFileCacheDir(CoreApp.instance),
|
||||
uri.substringAfterLast("/")
|
||||
).apply { writeBytes(getContent(uri)) }
|
||||
).apply { getContent(uri)?.let(::writeBytes) }
|
||||
.inputStream()
|
||||
}
|
||||
|
||||
private fun getContent(url: String) = getContentAndMimeType(url).let { (content, _) -> content }
|
||||
private fun getContent(url: String) = getItem(url)?.data?.data
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private fun streamZimContentToPipe(uri: String, outputStream: OutputStream) {
|
||||
@ -184,11 +236,11 @@ class ZimFileReader constructor(
|
||||
if (uri.endsWith(UNINITIALISER_ADDRESS)) {
|
||||
it.write(UNINITIALISE_HTML.toByteArray())
|
||||
} else {
|
||||
getContentAndMimeType(uri).let { (content: ByteArray, mimeType: String) ->
|
||||
if ("text/css" == mimeType && nightModeConfig.isNightModeActive()) {
|
||||
getItem(uri)?.let { item ->
|
||||
if ("text/css" == item.mimetype && nightModeConfig.isNightModeActive()) {
|
||||
it.write(INVERT_IMAGES_VIDEO.toByteArray())
|
||||
}
|
||||
it.write(content)
|
||||
it.write(item.data.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -200,21 +252,15 @@ class ZimFileReader constructor(
|
||||
.subscribe({ }, Throwable::printStackTrace)
|
||||
}
|
||||
|
||||
private fun getContentAndMimeType(uri: String) = with(JNIKiwixString()) {
|
||||
getContent(url = JNIKiwixString(uri.filePath), 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).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
|
||||
private fun getItem(url: String): Item? =
|
||||
if (jniKiwixReader.hasEntryByPath(getActualUrl(url))) {
|
||||
jniKiwixReader.getEntryByPath(getActualUrl(url)).getItem(true).also {
|
||||
Log.e(TAG, "getItem: $url")
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "getItem else: $url")
|
||||
null
|
||||
}
|
||||
|
||||
@Suppress("ExplicitThis") // this@ZimFileReader.name is required
|
||||
fun toBook() = Book().apply {
|
||||
|
@ -50,7 +50,7 @@ class ZimReaderContainer @Inject constructor(private val zimFileReaderFactory: F
|
||||
fun load(url: String, requestHeaders: Map<String, String>): WebResourceResponse {
|
||||
val data = zimFileReader?.load(url)
|
||||
return WebResourceResponse(
|
||||
zimFileReader?.readContentAndMimeType(url),
|
||||
zimFileReader?.getMimeTypeFromUrl(url),
|
||||
Charsets.UTF_8.name(),
|
||||
data
|
||||
)
|
||||
@ -80,7 +80,7 @@ class ZimReaderContainer @Inject constructor(private val zimFileReaderFactory: F
|
||||
val zimFileTitle get() = zimFileReader?.title
|
||||
val mainPage get() = zimFileReader?.mainPage
|
||||
val id get() = zimFileReader?.id
|
||||
val fileSize get() = zimFileReader?.fileSize ?: 0
|
||||
val fileSize get() = zimFileReader?.fileSize ?: 0L
|
||||
val creator get() = zimFileReader?.creator
|
||||
val publisher get() = zimFileReader?.publisher
|
||||
val name get() = zimFileReader?.name
|
||||
|
@ -24,6 +24,7 @@ import kotlinx.coroutines.yield
|
||||
import org.kiwix.kiwixmobile.core.reader.ZimFileReader
|
||||
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem
|
||||
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem.ZimSearchResultListItem
|
||||
import org.kiwix.libzim.SuggestionSearch
|
||||
import javax.inject.Inject
|
||||
|
||||
interface SearchResultGenerator {
|
||||
@ -46,13 +47,16 @@ class ZimSearchResultGenerator @Inject constructor() : SearchResultGenerator {
|
||||
reader: ZimFileReader?
|
||||
) =
|
||||
reader.also { yield() }
|
||||
?.searchSuggestions(searchTerm, 200)
|
||||
?.searchSuggestions(searchTerm)
|
||||
.also { yield() }
|
||||
.run { suggestionResults(reader) }
|
||||
.run { suggestionResults(reader, this) }
|
||||
|
||||
private suspend fun suggestionResults(reader: ZimFileReader?) = createList {
|
||||
private suspend fun suggestionResults(
|
||||
reader: ZimFileReader?,
|
||||
suggestionSearch: SuggestionSearch?
|
||||
) = createList {
|
||||
yield()
|
||||
reader?.getNextSuggestion()
|
||||
reader?.getNextSuggestion(suggestionSearch)
|
||||
?.let { ZimSearchResultListItem(it.title) }
|
||||
}
|
||||
.distinct()
|
||||
|
@ -48,14 +48,14 @@ internal class ZimSearchResultGeneratorTest {
|
||||
val validTitle = "title"
|
||||
val searchTerm = " "
|
||||
val item = mockk<SearchSuggestion>()
|
||||
every { zimFileReader.searchSuggestions(" ", 200) } returns true
|
||||
every { zimFileReader.getNextSuggestion() } returnsMany listOf(item, item, null)
|
||||
every { zimFileReader.searchSuggestions(" ") } returns true
|
||||
every { zimFileReader.getNextSuggestion(suggestionSearch) } returnsMany listOf(item, item, null)
|
||||
every { item.title } returns validTitle
|
||||
runBlocking {
|
||||
assertThat(zimSearchResultGenerator.generateSearchResults(searchTerm, zimFileReader))
|
||||
.isEqualTo(listOf(ZimSearchResultListItem(validTitle)))
|
||||
verify {
|
||||
zimFileReader.searchSuggestions(searchTerm, 200)
|
||||
zimFileReader.searchSuggestions(searchTerm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user