diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index 17b5b9c26..fab0ba15b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -159,6 +159,8 @@ class PreferencesHelper(val context: Context) { fun thumbnailRows() = rxPrefs.getString("ex_thumb_rows", "tr_2") + fun migrateLibraryAsked() = rxPrefs.getBoolean("ex_migrate_library", false) + //EH Cookies fun memberIdVal() = rxPrefs.getString("eh_ipb_member_id", null) fun passHashVal() = rxPrefs.getString("eh_ipb_pass_hash", null) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt index b1a99c92d..94617d3d5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt @@ -1,5 +1,7 @@ package eu.kanade.tachiyomi.ui.library +import android.os.Handler +import android.os.Looper import android.view.Gravity import android.view.ViewGroup import android.view.ViewGroup.LayoutParams.MATCH_PARENT @@ -17,6 +19,7 @@ import exh.search.SearchEngine import kotlinx.android.synthetic.main.item_catalogue_grid.view.* import uy.kohesive.injekt.injectLazy import java.util.* +import kotlin.concurrent.thread /** * Adapter storing a list of manga in a certain category. @@ -36,6 +39,8 @@ class LibraryCategoryAdapter(val fragment: LibraryCategoryView) : private val searchEngine = SearchEngine() private val metadataHelper = MetadataHelper() + var asyncSearchText: String? = null + init { setHasStableIds(true) } @@ -69,8 +74,17 @@ class LibraryCategoryAdapter(val fragment: LibraryCategoryView) : * @param param the filter. Not used. */ override fun updateDataSet(param: String?) { - filterItems(mangas) - notifyDataSetChanged() + //Async search filter (EH) + val filtered = asyncSearchText?.let { search -> + mangas.filter { + filterObject(it, search) + } + } ?: mangas + //The rest of the filters run on the main loop + Handler(Looper.getMainLooper()).post { + filterItems(filtered) + notifyDataSetChanged() + } } /** @@ -100,7 +114,7 @@ class LibraryCategoryAdapter(val fragment: LibraryCategoryView) : val metadata = metadataHelper.fetchMetadata(manga.url, exh) metadata?.let { searchEngine.matches(metadata, searchEngine.parseQuery(query)) - } ?: title.toLowerCase().contains(query) //Use regular searching when the metadata is not set up for this gallery + } ?: title.contains(query, ignoreCase = true) //Use regular searching when the metadata is not set up for this gallery } ?: false } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt index 3caece0ef..9cb52bd6e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt @@ -19,7 +19,9 @@ import eu.kanade.tachiyomi.util.toast import eu.kanade.tachiyomi.widget.AutofitRecyclerView import kotlinx.android.synthetic.main.item_library_category.view.* import rx.Subscription +import rx.android.schedulers.AndroidSchedulers import uy.kohesive.injekt.injectLazy +import java.util.concurrent.TimeUnit /** * Fragment containing the library manga for a certain category. @@ -114,8 +116,11 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att val presenter = fragment.presenter - searchSubscription = presenter.searchSubject.subscribe { text -> - adapter.searchText = text + searchSubscription = presenter + .searchSubject + .debounce(100L, TimeUnit.MILLISECONDS) + .subscribe { text -> //Debounce search (EH) + adapter.asyncSearchText = text.trim().toLowerCase() adapter.updateDataSet() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index e372b4fed..b7172f494 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -7,6 +7,7 @@ import android.support.v4.app.TaskStackBuilder import android.support.v4.view.GravityCompat import android.view.MenuItem import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.ui.backup.BackupFragment import eu.kanade.tachiyomi.ui.base.activity.BaseActivity @@ -17,9 +18,12 @@ import eu.kanade.tachiyomi.ui.library.LibraryFragment import eu.kanade.tachiyomi.ui.recent_updates.RecentChaptersFragment import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadFragment import eu.kanade.tachiyomi.ui.setting.SettingsActivity +import exh.ui.MetadataFetchDialog import exh.ui.batchadd.BatchAddFragment import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.toolbar.* +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy class MainActivity : BaseActivity() { @@ -82,6 +86,14 @@ class MainActivity : BaseActivity() { // Show changelog if needed ChangelogDialogFragment.show(this, preferences, supportFragmentManager) + + // Migrate library if needed + Injekt.get().getLibraryMangas().asRxSingle().subscribe { + if(it.size > 0) + runOnUiThread { + MetadataFetchDialog().tryAskMigration(this) + } + } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhFragment.kt index 769ab6b66..23d18e5cb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhFragment.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhFragment.kt @@ -6,7 +6,9 @@ import android.support.v7.preference.XpPreferenceFragment import android.view.View import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.util.plusAssign +import exh.ui.MetadataFetchDialog import exh.ui.login.LoginActivity +import net.xpece.android.support.preference.Preference import net.xpece.android.support.preference.SwitchPreference import uy.kohesive.injekt.injectLazy @@ -29,6 +31,10 @@ class SettingsEhFragment : SettingsFragment() { findPreference("enable_exhentai") as SwitchPreference } + val migrateLibraryPref by lazy { + findPreference("ex_migrate_library") as Preference + } + override fun onViewCreated(view: View, savedState: Bundle?) { super.onViewCreated(view, savedState) @@ -48,5 +54,10 @@ class SettingsEhFragment : SettingsFragment() { false } } + + migrateLibraryPref.setOnPreferenceClickListener { + MetadataFetchDialog().askMigration(activity) + true + } } } diff --git a/app/src/main/java/exh/metadata/MetadataHelper.kt b/app/src/main/java/exh/metadata/MetadataHelper.kt index 735603c15..a8d01c407 100644 --- a/app/src/main/java/exh/metadata/MetadataHelper.kt +++ b/app/src/main/java/exh/metadata/MetadataHelper.kt @@ -9,15 +9,22 @@ class MetadataHelper { = exGalleryBook().write(galleryMetadata.galleryUniqueIdentifier(), galleryMetadata) fun fetchMetadata(url: String, exh: Boolean): ExGalleryMetadata? - = ExGalleryMetadata().apply { - this.url = url - this.exh = exh - return exGalleryBook().read(galleryUniqueIdentifier()) + = ExGalleryMetadata().let { + it.url = url + it.exh = exh + return exGalleryBook().read(it.galleryUniqueIdentifier()) } fun getAllGalleries() = exGalleryBook().allKeys.map { exGalleryBook().read(it) } + fun hasMetadata(url: String, exh: Boolean): Boolean + = ExGalleryMetadata().let { + it.url = url + it.exh = exh + return exGalleryBook().exist(it.galleryUniqueIdentifier()) + } + fun exGalleryBook() = Paper.book("gallery-ex")!! } \ No newline at end of file diff --git a/app/src/main/java/exh/search/SearchEngine.kt b/app/src/main/java/exh/search/SearchEngine.kt index 00aaaa9df..cea46f19d 100644 --- a/app/src/main/java/exh/search/SearchEngine.kt +++ b/app/src/main/java/exh/search/SearchEngine.kt @@ -9,7 +9,7 @@ class SearchEngine { fun matches(metadata: ExGalleryMetadata, query: List): Boolean { - fun matchTagList(tags: List, + fun matchTagList(tags: Sequence, component: Text): Boolean { //Match tags val tagMatcher = if(!component.exact) @@ -28,15 +28,18 @@ class SearchEngine { return true } + val cachedLowercaseTitle = metadata.title?.toLowerCase() + val cachedLowercaseAltTitle = metadata.altTitle?.toLowerCase() + for(component in query) { if(component is Text) { //Match title - if (component.asRegex().test(metadata.title?.toLowerCase()) - || component.asRegex().test(metadata.altTitle?.toLowerCase())) { + if (component.asRegex().test(cachedLowercaseTitle) + || component.asRegex().test(cachedLowercaseAltTitle)) { continue } //Match tags - if(!matchTagList(metadata.tags.entries.flatMap { it.value }, + if(!matchTagList(metadata.tags.entries.asSequence().flatMap { it.value.asSequence() }, component)) return false } else if(component is Namespace) { if(component.namespace == "uploader") { @@ -47,9 +50,9 @@ class SearchEngine { } } else { //Match namespace - val ns = metadata.tags.entries.filter { + val ns = metadata.tags.entries.asSequence().filter { it.key == component.namespace - }.flatMap { it.value } + }.flatMap { it.value.asSequence() } //Match tags if (!matchTagList(ns, component.tag!!)) return false diff --git a/app/src/main/java/exh/ui/MetadataFetchDialog.kt b/app/src/main/java/exh/ui/MetadataFetchDialog.kt new file mode 100644 index 000000000..2ceefe3c7 --- /dev/null +++ b/app/src/main/java/exh/ui/MetadataFetchDialog.kt @@ -0,0 +1,139 @@ +package exh.ui + +import android.app.Activity +import android.content.pm.ActivityInfo +import com.afollestad.materialdialogs.MaterialDialog +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.database.DatabaseHelper +import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import eu.kanade.tachiyomi.data.preference.getOrDefault +import eu.kanade.tachiyomi.data.source.SourceManager +import eu.kanade.tachiyomi.data.source.online.all.EHentai +import exh.metadata.MetadataHelper +import exh.metadata.copyTo +import timber.log.Timber +import uy.kohesive.injekt.injectLazy +import kotlin.concurrent.thread + +class MetadataFetchDialog { + + val metadataHelper by lazy { MetadataHelper() } + + val db: DatabaseHelper by injectLazy() + + val sourceManager: SourceManager by injectLazy() + + val preferenceHelper: PreferencesHelper by injectLazy() + + fun show(context: Activity) { + //Too lazy to actually deal with orientation changes + context.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR + + val progressDialog = MaterialDialog.Builder(context) + .title("Migrating library") + .content("Preparing library") + .progress(false, 0, true) + .cancelable(false) + .canceledOnTouchOutside(false) + .show() + + thread { + db.deleteMangasNotInLibrary().executeAsBlocking() + + val libraryMangas = db.getLibraryMangas() + .executeAsBlocking() + .filter { + it.source <= 2 + && !metadataHelper.hasMetadata(it.url, it.source == 2) + } + + context.runOnUiThread { + progressDialog.maxProgress = libraryMangas.size + } + + //Actual metadata fetch code + libraryMangas.forEachIndexed { i, manga -> + context.runOnUiThread { + progressDialog.setContent("Processing: ${manga.title}") + progressDialog.setProgress(i + 1) + } + try { + val source = sourceManager.get(manga.source) + source?.let { + it as EHentai + manga.copyFrom(it.fetchMangaDetails(manga).toBlocking().first()) + metadataHelper.fetchMetadata(manga.url, it.exh)?.copyTo(manga) + } + } catch(t: Throwable) { + Timber.e(t, "Could not migrate manga!") + } + } + + context.runOnUiThread { + progressDialog.dismiss() + + //Enable orientation changes again + context.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR + + displayMigrationComplete(context) + } + } + } + + fun tryAskMigration(activity: Activity) { + if(preferenceHelper.migrateLibraryAsked().getOrDefault()) return + + MaterialDialog.Builder(activity) + .title("Migrate library") + .content("You need to migrate your library before tag searching in the library will function.\n\n" + + "This migration may take a long time depending on your library size and will also use up a significant amount of internet bandwidth.\n\n" + + "This process can be done later if required.") + .positiveText("Migrate") + .negativeText("Later") + .onPositive { materialDialog, dialogAction -> show(activity) } + .onNegative { materialDialog, dialogAction -> adviseMigrationLater(activity) } + .cancelable(false) + .canceledOnTouchOutside(false) + .dismissListener { + preferenceHelper.migrateLibraryAsked().set(true) + }.show() + } + + fun askMigration(activity: Activity) { + MaterialDialog.Builder(activity) + .title("Migrate library") + .content("You need to migrate your library before tag searching in the library will function.\n\n" + + "This migration may take a long time depending on your library size and will also use up a significant amount of internet bandwidth.\n\n" + + "This process can be done later if required.") + .positiveText("Migrate") + .negativeText("Later") + .onPositive { materialDialog, dialogAction -> show(activity) } + .onNegative { materialDialog, dialogAction -> adviseMigrationLater(activity) } + .cancelable(false) + .canceledOnTouchOutside(false) + .dismissListener { + preferenceHelper.migrateLibraryAsked().set(true) + }.show() + } + + fun adviseMigrationLater(activity: Activity) { + MaterialDialog.Builder(activity) + .title("Migration canceled") + .content("Library migration has been canceled.\n\n" + + "You can run this operation later by going to: Settings > EHentai > Migrate Library") + .positiveText("Ok") + .cancelable(true) + .canceledOnTouchOutside(true) + .show() + } + + fun displayMigrationComplete(activity: Activity) { + MaterialDialog.Builder(activity) + .title("Migration complete") + .content("${activity.getString(R.string.app_name)} is now ready for use!") + .positiveText("Ok") + .cancelable(true) + .canceledOnTouchOutside(true) + .show() + } +} diff --git a/app/src/main/res/xml/eh_pref_eh.xml b/app/src/main/res/xml/eh_pref_eh.xml index c11714106..3c99fe488 100644 --- a/app/src/main/res/xml/eh_pref_eh.xml +++ b/app/src/main/res/xml/eh_pref_eh.xml @@ -63,6 +63,16 @@ android:entries="@array/ehentai_thumbnail_rows" android:entryValues="@array/ehentai_thumbnail_rows_values" /> + + + + \ No newline at end of file