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 da931dbe45..4529697099 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 @@ -161,6 +161,8 @@ class PreferencesHelper(val context: Context) { fun lastUsedCategory() = rxPrefs.getInteger(Keys.lastUsedCategory, 0) + fun lastUsedSources() = flowPrefs.getStringSet("last_used_sources", emptySet()) + fun lastVersionCode() = rxPrefs.getInteger("last_version_code", 0) fun browseAsList() = rxPrefs.getBoolean(Keys.catalogueAsList, false) 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 b7d81f6386..ea765d069d 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 @@ -60,6 +60,8 @@ import eu.kanade.tachiyomi.ui.setting.AboutController import eu.kanade.tachiyomi.ui.setting.SettingsController import eu.kanade.tachiyomi.ui.setting.SettingsMainController import eu.kanade.tachiyomi.ui.source.BrowseController +import eu.kanade.tachiyomi.ui.source.browse.BrowseSourceController +import eu.kanade.tachiyomi.util.manga.MangaShortcutManager import eu.kanade.tachiyomi.util.system.contextCompatDrawable import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.hasSideNavBar @@ -101,6 +103,7 @@ open class MainActivity : BaseActivity(), DownloadServiceLi private var animationSet: AnimatorSet? = null private val downloadManager: DownloadManager by injectLazy() + private val mangaShortcutManager: MangaShortcutManager by injectLazy() private val hideBottomNav get() = router.backstackSize > 1 && router.backstack[1].controller() !is DialogController @@ -417,6 +420,7 @@ open class MainActivity : BaseActivity(), DownloadServiceLi super.onPause() snackBar?.dismiss() setStartingTab() + mangaShortcutManager.updateShortcuts() } private fun getAppUpdates() { @@ -494,6 +498,11 @@ open class MainActivity : BaseActivity(), DownloadServiceLi if (router.backstack.isEmpty()) binding.bottomNav.selectedItemId = R.id.nav_library router.pushController(MangaDetailsController(extras).withFadeTransaction()) } + SHORTCUT_SOURCE -> { + val extras = intent.extras ?: return false + if (router.backstack.isEmpty()) binding.bottomNav.selectedItemId = R.id.nav_library + router.pushController(BrowseSourceController(extras).withFadeTransaction()) + } SHORTCUT_DOWNLOADS -> { binding.bottomNav.selectedItemId = R.id.nav_recents router.popToRoot() @@ -528,6 +537,7 @@ open class MainActivity : BaseActivity(), DownloadServiceLi setStartingTab() } SecureActivityDelegate.locked = this !is SearchActivity + mangaShortcutManager.updateShortcuts() super.onBackPressed() } } @@ -732,6 +742,7 @@ open class MainActivity : BaseActivity(), DownloadServiceLi const val SHORTCUT_BROWSE = "eu.kanade.tachiyomi.SHOW_BROWSE" const val SHORTCUT_DOWNLOADS = "eu.kanade.tachiyomi.SHOW_DOWNLOADS" const val SHORTCUT_MANGA = "eu.kanade.tachiyomi.SHOW_MANGA" + const val SHORTCUT_SOURCE = "eu.kanade.tachiyomi.SHOW_SOURCE" const val SHORTCUT_READER_SETTINGS = "eu.kanade.tachiyomi.READER_SETTINGS" const val SHORTCUT_EXTENSIONS = "eu.kanade.tachiyomi.EXTENSIONS" diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/SearchActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/SearchActivity.kt index 4763cf6cdc..97892cf2f2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/SearchActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/SearchActivity.kt @@ -9,10 +9,13 @@ import com.bluelinelabs.conductor.RouterTransaction import com.bluelinelabs.conductor.changehandler.FadeChangeHandler import com.bluelinelabs.conductor.changehandler.SimpleSwapChangeHandler import eu.kanade.tachiyomi.data.notification.NotificationReceiver +import eu.kanade.tachiyomi.ui.base.controller.BaseController import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.manga.MangaDetailsController import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate +import eu.kanade.tachiyomi.ui.setting.SettingsController import eu.kanade.tachiyomi.ui.setting.SettingsReaderController +import eu.kanade.tachiyomi.ui.source.browse.BrowseSourceController import eu.kanade.tachiyomi.ui.source.global_search.GlobalSearchController import eu.kanade.tachiyomi.util.view.gone import eu.kanade.tachiyomi.util.view.withFadeTransaction @@ -21,10 +24,12 @@ class SearchActivity : MainActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - binding.toolbar?.navigationIcon = drawerArrow - binding.toolbar?.setNavigationOnClickListener { + binding.toolbar.navigationIcon = drawerArrow + binding.toolbar.setNavigationOnClickListener { popToRoot() } + (router.backstack.lastOrNull()?.controller() as? BaseController<*>)?.setTitle() + (router.backstack.lastOrNull()?.controller() as? SettingsController)?.setTitle() } override fun onBackPressed() { @@ -35,7 +40,7 @@ class SearchActivity : MainActivity() { } private fun popToRoot() { - if (intent.action == SHORTCUT_MANGA || intent.action == SHORTCUT_READER_SETTINGS) { + if (intentShouldGoBack()) { onBackPressed() } else if (!router.handleBack()) { val intent = Intent(this, MainActivity::class.java).apply { @@ -46,6 +51,9 @@ class SearchActivity : MainActivity() { } } + private fun intentShouldGoBack() = + intent.action in listOf(SHORTCUT_MANGA, SHORTCUT_READER_SETTINGS, SHORTCUT_BROWSE) + override fun syncActivityViewWithController( to: Controller?, from: Controller?, @@ -98,6 +106,14 @@ class SearchActivity : MainActivity() { .popChangeHandler(FadeChangeHandler()) ) } + SHORTCUT_SOURCE -> { + val extras = intent.extras ?: return false + router.replaceTopController( + RouterTransaction.with(BrowseSourceController(extras)) + .pushChangeHandler(SimpleSwapChangeHandler()) + .popChangeHandler(FadeChangeHandler()) + ) + } SHORTCUT_READER_SETTINGS -> { router.replaceTopController( RouterTransaction.with(SettingsReaderController()) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsPresenter.kt index e55d28f036..e78158aebe 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsPresenter.kt @@ -455,11 +455,11 @@ class RecentsPresenter( const val VIEW_TYPE_ONLY_HISTORY = 2 const val VIEW_TYPE_ONLY_UPDATES = 3 - suspend fun getRecentManga(): List { + suspend fun getRecentManga(): List> { val presenter = RecentsPresenter(null) presenter.viewType = 1 presenter.runRecents(limit = true) - return presenter.recentItems.filter { it.mch.manga.id != null }.map { it.mch.manga } + return presenter.recentItems.filter { it.mch.manga.id != null }.map { it.mch.manga to it.mch.history.last_read } } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/BrowseController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/BrowseController.kt index ebed292c17..6820bb5dd3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/BrowseController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/BrowseController.kt @@ -58,6 +58,7 @@ import eu.kanade.tachiyomi.util.view.withFadeTransaction import kotlinx.android.parcel.Parcelize import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import java.util.Date import kotlin.math.max import kotlin.math.min @@ -439,6 +440,11 @@ class BrowseController : */ private fun openCatalogue(source: CatalogueSource, controller: BrowseSourceController) { preferences.lastUsedCatalogueSource().set(source.id) + val list = preferences.lastUsedSources().get().toMutableSet() + list.removeAll { it.startsWith("${source.id}:") } + list.add("${source.id}:${Date().time}") + val sortedList = list.filter { it.split(":").size == 2 }.sortedByDescending { it.split(":").last().toLong() } + preferences.lastUsedSources().set(sortedList.subList(0, min(sortedList.size, 2)).toSet()) router.pushController(controller.withFadeTransaction()) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceController.kt index 02c7e7d7e9..b56183937c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceController.kt @@ -592,7 +592,7 @@ open class BrowseSourceController(bundle: Bundle) : } } - protected companion object { + companion object { const val SOURCE_ID_KEY = "sourceId" const val SEARCH_QUERY_KEY = "searchQuery" diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/manga/MangaShortcutManager.kt b/app/src/main/java/eu/kanade/tachiyomi/util/manga/MangaShortcutManager.kt index 96d22b5458..c85dcbcea6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/manga/MangaShortcutManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/manga/MangaShortcutManager.kt @@ -6,17 +6,24 @@ import android.content.pm.ShortcutInfo import android.content.pm.ShortcutManager import android.graphics.Bitmap import android.graphics.BitmapFactory +import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Icon import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.database.DatabaseHelper +import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import eu.kanade.tachiyomi.source.Source +import eu.kanade.tachiyomi.source.SourceManager +import eu.kanade.tachiyomi.source.icon import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.main.SearchActivity import eu.kanade.tachiyomi.ui.manga.MangaDetailsController import eu.kanade.tachiyomi.ui.recents.RecentsPresenter +import eu.kanade.tachiyomi.ui.source.browse.BrowseSourceController import eu.kanade.tachiyomi.util.system.launchIO import kotlinx.coroutines.GlobalScope +import timber.log.Timber import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.util.Date @@ -25,7 +32,8 @@ import kotlin.math.min class MangaShortcutManager( val preferences: PreferencesHelper = Injekt.get(), val db: DatabaseHelper = Injekt.get(), - val coverCache: CoverCache = Injekt.get() + val coverCache: CoverCache = Injekt.get(), + val sourceManager: SourceManager = Injekt.get() ) { val context: Context = preferences.context @@ -35,54 +43,105 @@ class MangaShortcutManager( val shortcutManager = context.getSystemService(ShortcutManager::class.java) val recentManga = RecentsPresenter.getRecentManga() - val shortcuts = recentManga.subList( + val recentSources = preferences.lastUsedSources().get().mapNotNull { + val splitS = it.split(":") + splitS.first().toLongOrNull()?.let { id -> + sourceManager.getOrStub(id) to splitS[1].toLong() + } + } + var recents = ( + recentManga.subList( + 0, + min( + recentManga.size, + shortcutManager.maxShortcutCountPerActivity + ) + ) + recentSources + ) + .sortedByDescending { it.second }.map { it.first } + + recents = recents.subList( 0, min( recentManga.size, shortcutManager.maxShortcutCountPerActivity ) - ).map { manga -> - val customCoverFile = coverCache.getCustomCoverFile(manga) - val coverFile = if (customCoverFile.exists()) { - customCoverFile - } else { - val coverFile = coverCache.getCoverFile(manga) - if (coverFile.exists()) { - if (!manga.favorite) { - coverFile.setLastModified(Date().time) + ) + + val shortcuts = recents.mapNotNull { item -> + when (item) { + is Manga -> { + val customCoverFile = coverCache.getCustomCoverFile(item) + val coverFile = if (customCoverFile.exists()) { + customCoverFile + } else { + val coverFile = coverCache.getCoverFile(item) + if (coverFile.exists()) { + if (!item.favorite) { + coverFile.setLastModified(Date().time) + } + coverFile + } else { + null + } } - coverFile - } else { + val bitmap = if (coverFile != null) { + BitmapFactory.decodeFile(coverFile.path) + } else { + null + } + + ShortcutInfo.Builder(context, "Manga-${item.id?.toString() ?: item.title}") + .setShortLabel(item.title) + .setLongLabel(item.title) + .setIcon( + if (bitmap != null) if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + Icon.createWithAdaptiveBitmap(bitmap.toSquare()) + } else { + Icon.createWithBitmap(bitmap) + } + else Icon.createWithResource(context, R.drawable.ic_book_24dp) + ) + .setIntent( + Intent( + context, + SearchActivity::class.java + ).setAction(MainActivity.SHORTCUT_MANGA) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP) + .putExtra(MangaDetailsController.MANGA_EXTRA, item.id) + ) + .build() + } + is Source -> { + val bitmap = (item.icon() as? BitmapDrawable)?.bitmap + + ShortcutInfo.Builder(context, "Source-${item.id}") + .setShortLabel(item.name) + .setLongLabel(item.name) + .setIcon( + if (bitmap != null) if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + Icon.createWithAdaptiveBitmap(bitmap.toSquare()) + } else { + Icon.createWithBitmap(bitmap) + } + else Icon.createWithResource(context, R.drawable.ic_extension_update_24dp) + ) + .setIntent( + Intent( + context, + SearchActivity::class.java + ).setAction(MainActivity.SHORTCUT_SOURCE) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP) + .putExtra(BrowseSourceController.SOURCE_ID_KEY, item.id) + ) + .build() + } + else -> { null } } - val bitmap = if (coverFile != null) { - BitmapFactory.decodeFile(coverFile.path) - } else { - null - } - - ShortcutInfo.Builder(context, "Manga-${manga.id?.toString() ?: manga.title}") - .setShortLabel(manga.title) - .setLongLabel(manga.title) - .setIcon( - if (bitmap != null) if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { - Icon.createWithAdaptiveBitmap(bitmap.toSquare()) - } else { - Icon.createWithBitmap(bitmap) - } - else Icon.createWithResource(context, R.drawable.ic_book_24dp) - ) - .setIntent( - Intent( - context, - SearchActivity::class.java - ).setAction(MainActivity.SHORTCUT_MANGA) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP) - .putExtra(MangaDetailsController.MANGA_EXTRA, manga.id) - ) - .build() } + Timber.d("Shortcuts: ${shortcuts.joinToString(", ") { it.longLabel ?: "n/a" }}") shortcutManager.dynamicShortcuts = shortcuts } }