diff --git a/app/build.gradle.kts b/app/build.gradle.kts index eb7198ef7..c33d800cf 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -84,4 +84,10 @@ dependencies { implementation(Libs.squidb_annotations) implementation(Libs.ink_page_indicator) add("kapt", Libs.squidb_processor) + val pagingVersion = "2.1.1" + + implementation("androidx.paging:paging-runtime-ktx:$pagingVersion") + testImplementation("androidx.paging:paging-common-ktx:$pagingVersion") + implementation("androidx.paging:paging-rxjava2-ktx:$pagingVersion") + implementation ("com.hannesdorfmann:adapterdelegates4-pagination:4.2.0") } diff --git a/app/src/main/java/org/kiwix/kiwixmobile/Aliases.kt b/app/src/main/java/org/kiwix/kiwixmobile/Aliases.kt new file mode 100644 index 000000000..4a0be5b1e --- /dev/null +++ b/app/src/main/java/org/kiwix/kiwixmobile/Aliases.kt @@ -0,0 +1,25 @@ +/* + * 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 + +import org.kiwix.kiwixlib.Book +import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity + +typealias NetworkBook = LibraryNetworkEntity.Book +typealias JniBook = Book diff --git a/app/src/main/java/org/kiwix/kiwixmobile/di/modules/ServiceModule.kt b/app/src/main/java/org/kiwix/kiwixmobile/di/modules/ServiceModule.kt index 8f41eb771..a61810662 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/di/modules/ServiceModule.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/di/modules/ServiceModule.kt @@ -23,8 +23,8 @@ import android.app.Service import android.content.Context import dagger.Module import dagger.Provides -import org.kiwix.kiwixlib.Library import org.kiwix.kiwixlib.JNIKiwixServer +import org.kiwix.kiwixlib.Library import org.kiwix.kiwixmobile.di.ServiceScope import org.kiwix.kiwixmobile.webserver.WebServerHelper import org.kiwix.kiwixmobile.webserver.wifi_hotspot.HotspotNotificationManager diff --git a/app/src/main/java/org/kiwix/kiwixmobile/webserver/WebServerHelper.java b/app/src/main/java/org/kiwix/kiwixmobile/webserver/WebServerHelper.java index 65b5178b3..e7a5e2463 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/webserver/WebServerHelper.java +++ b/app/src/main/java/org/kiwix/kiwixmobile/webserver/WebServerHelper.java @@ -26,8 +26,8 @@ import java.util.ArrayList; import java.util.concurrent.TimeUnit; import javax.inject.Inject; import org.kiwix.kiwixlib.JNIKiwixException; -import org.kiwix.kiwixlib.Library; import org.kiwix.kiwixlib.JNIKiwixServer; +import org.kiwix.kiwixlib.Library; import org.kiwix.kiwixmobile.core.utils.ServerUtils; import org.kiwix.kiwixmobile.webserver.wifi_hotspot.IpAddressCallbacks; diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/NetworkResponseMapper.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/NetworkResponseMapper.kt new file mode 100644 index 000000000..bca2200ca --- /dev/null +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/NetworkResponseMapper.kt @@ -0,0 +1,53 @@ +/* + * 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.zim_manager + +import org.kiwix.kiwixlib.Filter +import org.kiwix.kiwixlib.Library +import org.kiwix.kiwixlib.Manager +import org.kiwix.kiwixmobile.core.di.modules.NetworkModule.KIWIX_DOWNLOAD_URL +import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity.Book +import javax.inject.Inject + +class NetworkResponseMapper @Inject constructor() { + + fun map(rawStream: String): List = + with(Library()) { + Manager(this).readOpds(rawStream, KIWIX_DOWNLOAD_URL) + val filter = filter(Filter().query("wikipedia articles about")) + filter.map(this::getBookById).map(::OpdsBook).map { + Book().apply { + id = it.id + title = it.title + description = it.description + language = it.language + creator = it.creator + publisher = it.publisher + date = it.date + url = it.url + articleCount = it.articleCount.toString() + mediaCount = it.mediaCount.toString() + size = it.size.toString() + bookName = it.name + favicon = it.favicon + tags = it.tags + } + } + } +} diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/OpdsBook.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/OpdsBook.kt new file mode 100644 index 000000000..c98514a7b --- /dev/null +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/OpdsBook.kt @@ -0,0 +1,66 @@ +/* + * 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.zim_manager + +import org.kiwix.kiwixmobile.JniBook + +data class OpdsBook( + val id: String, + val path: String, + val isPathValid: Boolean, + val title: String, + val description: String, + val language: String, + val creator: String, + val publisher: String, + val date: String, + val url: String, + val name: String, + val flavour: String, + val tags: String, + val articleCount: Long, + val mediaCount: Long, + val size: Long, + val favicon: String, + val faviconUrl: String, + val faviconMimeType: String +) { + + constructor(book: JniBook) : this( + book.id, + book.path, + book.isPathValid, + book.title, + book.description, + book.language, + book.creator, + book.publisher, + book.date, + book.url, + book.name, + book.flavour, + book.tags, + book.articleCount, + book.mediaCount, + book.size, + book.favicon, + book.faviconUrl, + book.faviconMimeType + ) +} diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/ZimManageViewModel.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/ZimManageViewModel.kt index bfb42baef..288677c95 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/ZimManageViewModel.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/ZimManageViewModel.kt @@ -30,6 +30,8 @@ import io.reactivex.functions.Function6 import io.reactivex.processors.BehaviorProcessor import io.reactivex.processors.PublishProcessor import io.reactivex.schedulers.Schedulers +import org.kiwix.kiwixmobile.NetworkBook +import org.kiwix.kiwixmobile.core.CoreApp import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.StorageObserver import org.kiwix.kiwixmobile.core.base.SideEffect @@ -39,9 +41,9 @@ import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao import org.kiwix.kiwixmobile.core.data.DataSource import org.kiwix.kiwixmobile.core.data.remote.KiwixService import org.kiwix.kiwixmobile.core.downloader.model.DownloadModel -import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity.Book import org.kiwix.kiwixmobile.core.extensions.calculateSearchMatches +import org.kiwix.kiwixmobile.core.extensions.locale import org.kiwix.kiwixmobile.core.extensions.registerReceiver import org.kiwix.kiwixmobile.core.utils.BookUtils import org.kiwix.kiwixmobile.core.zim_manager.Language @@ -68,12 +70,19 @@ import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.BookItem import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.DividerItem import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.LibraryDownloadItem -import java.util.LinkedList import java.util.Locale +import java.util.MissingResourceException import java.util.concurrent.TimeUnit.MILLISECONDS import java.util.concurrent.TimeUnit.SECONDS import javax.inject.Inject +private val Locale.safeIsO3Language: String + get() = try { + isO3Language + } catch (_: MissingResourceException) { + language + } + class ZimManageViewModel @Inject constructor( private val downloadDao: FetchDownloadDao, private val bookDao: NewBookDao, @@ -85,7 +94,8 @@ class ZimManageViewModel @Inject constructor( private val bookUtils: BookUtils, private val fat32Checker: Fat32Checker, private val defaultLanguageProvider: DefaultLanguageProvider, - private val dataSource: DataSource + private val dataSource: DataSource, + private val networkResponseMapper: NetworkResponseMapper ) : ViewModel() { sealed class FileSelectActions { data class RequestOpen(val bookOnDisk: BookOnDisk) : FileSelectActions() @@ -97,7 +107,7 @@ class ZimManageViewModel @Inject constructor( object RestartActionMode : FileSelectActions() } - val sideEffects = PublishProcessor.create>() + val sideEffects = PublishProcessor.create>() val libraryItems: MutableLiveData> = MutableLiveData() val fileSelectListStates: MutableLiveData = MutableLiveData() val deviceListIsRefreshing = MutableLiveData() @@ -133,7 +143,7 @@ class ZimManageViewModel @Inject constructor( private fun disposables(): Array { val downloads = downloadDao.downloads() val booksFromDao = books() - val networkLibrary = PublishProcessor.create() + val networkLibrary = PublishProcessor.create>() val languages = languageDao.languages() return arrayOf( updateBookItems(), @@ -210,7 +220,7 @@ class ZimManageViewModel @Inject constructor( } private fun requestsAndConnectivtyChangesToLibraryRequests( - library: PublishProcessor + library: PublishProcessor> ) = Flowable.combineLatest( requestDownloadLibrary, @@ -223,14 +233,19 @@ class ZimManageViewModel @Inject constructor( .observeOn(Schedulers.io()) .subscribe( { - kiwixService.library + kiwixService.getOpdsLibrary( + null, + CoreApp.getInstance().locale.safeIsO3Language, + null, + "1000", + null + ) .timeout(60, SECONDS) .retry(5) - .subscribe( - library::onNext - ) { + .map(networkResponseMapper::map) + .subscribe(library::onNext) { it.printStackTrace() - library.onNext(LibraryNetworkEntity().apply { book = LinkedList() }) + library.onNext(emptyList()) } }, Throwable::printStackTrace @@ -244,7 +259,7 @@ class ZimManageViewModel @Inject constructor( private fun updateLibraryItems( booksFromDao: Flowable>, downloads: Flowable>, - library: Flowable, + library: Flowable>, languages: Flowable> ) = Flowable.combineLatest( booksFromDao, @@ -269,11 +284,10 @@ class ZimManageViewModel @Inject constructor( ) private fun updateLanguagesInDao( - library: Flowable, + library: Flowable>, languages: Flowable> ) = library .subscribeOn(Schedulers.io()) - .map { it.books } .withLatestFrom( languages, BiFunction(::combineToLanguageList) @@ -340,7 +354,7 @@ class ZimManageViewModel @Inject constructor( booksOnFileSystem: List, activeDownloads: List, allLanguages: List, - libraryNetworkEntity: LibraryNetworkEntity, + networkBooks: List, filter: String, fileSystemState: FileSystemState ): List { @@ -348,7 +362,7 @@ class ZimManageViewModel @Inject constructor( .map(Language::languageCode) val booksUnfilteredByLanguage = applySearchFilter( - libraryNetworkEntity.books - booksOnFileSystem.map(BookOnDisk::book), + networkBooks - booksOnFileSystem.map(BookOnDisk::book), filter ) diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/LibraryViewHolder.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/LibraryViewHolder.kt index 60eb7c4fe..367233198 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/LibraryViewHolder.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/LibraryViewHolder.kt @@ -23,6 +23,7 @@ import android.view.View import android.view.View.MeasureSpec import android.widget.Toast import androidx.annotation.StringRes +import eu.mhutti1.utils.storage.Bytes import kotlinx.android.synthetic.main.item_download.downloadProgress import kotlinx.android.synthetic.main.item_download.downloadState import kotlinx.android.synthetic.main.item_download.eta @@ -50,7 +51,6 @@ import org.kiwix.kiwixmobile.core.extensions.setBitmap import org.kiwix.kiwixmobile.core.extensions.setTextAndVisibility import org.kiwix.kiwixmobile.core.utils.BookUtils import org.kiwix.kiwixmobile.core.utils.NetworkUtils -import org.kiwix.kiwixmobile.core.zim_manager.KiloByte import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CannotWrite4GbFile import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.Unknown import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.BookItem @@ -71,7 +71,7 @@ sealed class LibraryViewHolder(containerView: View) : libraryBookCreator.setTextAndVisibility(item.book.creator) libraryBookPublisher.setTextAndVisibility(item.book.publisher) libraryBookDate.setTextAndVisibility(item.book.date) - libraryBookSize.setTextAndVisibility(KiloByte(item.book.size).humanReadable) + libraryBookSize.setTextAndVisibility(Bytes(item.book.size.toLong()).humanReadable) libraryBookLanguage.text = bookUtils.getLanguage(item.book.getLanguage()) libraryBookFileName.text = NetworkUtils.parseURL(CoreApp.getInstance(), item.book.url) libraryBookFavicon.setBitmap(Base64String(item.book.favicon)) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 8a4538b1c..0cc88a34d 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -60,4 +60,5 @@ dependencies { implementation(Libs.android_arch_lifecycle_extensions) implementation(Libs.objectbox_kotlin) implementation(Libs.objectbox_rxjava) + implementation("com.squareup.retrofit2:converter-scalars:2.1.0") } diff --git a/core/libs/kiwixLibAndroid-release.aar b/core/libs/kiwixLibAndroid-release.aar new file mode 100644 index 000000000..8c76b58cb Binary files /dev/null and b/core/libs/kiwixLibAndroid-release.aar differ diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/KiwixService.java b/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/KiwixService.java index c9e861622..97250f5c3 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/KiwixService.java +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/KiwixService.java @@ -20,20 +20,28 @@ package org.kiwix.kiwixmobile.core.data.remote; import io.reactivex.Observable; import io.reactivex.Single; import io.reactivex.schedulers.Schedulers; +import java.util.List; import okhttp3.OkHttpClient; -import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity; import org.kiwix.kiwixmobile.core.entity.MetaLinkNetworkEntity; import retrofit2.Retrofit; import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; +import retrofit2.converter.scalars.ScalarsConverterFactory; import retrofit2.converter.simplexml.SimpleXmlConverterFactory; import retrofit2.http.GET; +import retrofit2.http.Query; import retrofit2.http.Url; public interface KiwixService { - String LIBRARY_NETWORK_PATH = "/library/library_zim.xml"; + String LIBRARY_NETWORK_PATH = "/catalog/search"; - @GET(LIBRARY_NETWORK_PATH) Single getLibrary(); + @GET(LIBRARY_NETWORK_PATH) Single getOpdsLibrary( + @Query("q") String searchTerm, + @Query("lang") String language, + @Query("tag") List tags, + @Query("count") String count, + @Query("start") String start + ); @GET Observable getMetaLinks(@Url String url); @@ -44,6 +52,7 @@ public interface KiwixService { Retrofit retrofit = new Retrofit.Builder() .baseUrl(baseUrl) .client(okHttpClient) + .addConverterFactory(ScalarsConverterFactory.create()) .addConverterFactory(SimpleXmlConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io())) .build(); diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/NetworkModule.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/NetworkModule.kt index 606419215..bfef2e69d 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/NetworkModule.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/NetworkModule.kt @@ -23,7 +23,7 @@ import dagger.Module import dagger.Provides import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor -import okhttp3.logging.HttpLoggingInterceptor.Level.BASIC +import okhttp3.logging.HttpLoggingInterceptor.Level.BODY import okhttp3.logging.HttpLoggingInterceptor.Level.NONE import org.kiwix.kiwixmobile.core.BuildConfig import org.kiwix.kiwixmobile.core.data.remote.KiwixService @@ -44,7 +44,7 @@ class NetworkModule { .connectTimeout(CONNECTION_TIMEOUT, SECONDS) .readTimeout(READ_TIMEOUT, SECONDS) .addNetworkInterceptor(HttpLoggingInterceptor().apply { - level = if (BuildConfig.DEBUG) BASIC else NONE + level = if (BuildConfig.DEBUG) BODY else NONE }) .addNetworkInterceptor(UserAgentInterceptor(USER_AGENT)) .build() diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/extensions/ContextExtensions.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/extensions/ContextExtensions.kt index a6fee171f..99d7b3a5e 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/extensions/ContextExtensions.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/extensions/ContextExtensions.kt @@ -21,11 +21,10 @@ package org.kiwix.kiwixmobile.core.extensions import android.content.Context import android.content.Intent import android.content.IntentFilter -import android.os.Build.VERSION -import android.os.Build.VERSION_CODES import android.util.TypedValue import android.widget.Toast import androidx.annotation.AttrRes +import androidx.core.os.ConfigurationCompat import org.kiwix.kiwixmobile.core.base.BaseBroadcastReceiver import java.util.Locale @@ -53,9 +52,7 @@ fun Context.registerReceiver(baseBroadcastReceiver: BaseBroadcastReceiver): Inte registerReceiver(baseBroadcastReceiver, IntentFilter(baseBroadcastReceiver.action)) val Context.locale: Locale - get() = - if (VERSION.SDK_INT >= VERSION_CODES.N) resources.configuration.locales.get(0) - else resources.configuration.locale + get() = ConfigurationCompat.getLocales(resources.configuration).get(0) fun Context.getColorAttribute(@AttrRes attributeRes: Int) = with(TypedValue()) { if (theme.resolveAttribute(attributeRes, this, true)) 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 74ec0f29f..bc0bd0956 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 @@ -219,7 +219,8 @@ class ZimFileReader constructor( language = this@ZimFileReader.language articleCount = this@ZimFileReader.articleCount.toString() mediaCount = this@ZimFileReader.mediaCount.toString() - bookName = name + @Suppress("ExplicitThis") + bookName = this@ZimFileReader.name tags = this@ZimFileReader.tags } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/LanguageUtils.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/LanguageUtils.kt index 609253c85..7c8a1778a 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/LanguageUtils.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/LanguageUtils.kt @@ -30,7 +30,6 @@ import android.view.InflateException import android.view.LayoutInflater import android.view.View import android.widget.TextView -import androidx.core.os.ConfigurationCompat import org.kiwix.kiwixmobile.core.extensions.locale import org.kiwix.kiwixmobile.core.utils.files.FileUtils import java.text.Collator @@ -191,10 +190,8 @@ class LanguageUtils(private val context: Context) { @JvmStatic fun handleLocaleChange(context: Context, language: String) { val locale = - if (language == Locale.ROOT.toString()) - ConfigurationCompat.getLocales(context.applicationContext.resources.configuration)[0] - else - Locale(language) + if (language == Locale.ROOT.toString()) context.applicationContext.locale + else Locale(language) Locale.setDefault(locale) val config = Configuration() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {