From c68053d74c07c113202a02f17021718c4616202a Mon Sep 17 00:00:00 2001 From: Jays2Kings Date: Wed, 7 Apr 2021 00:28:14 -0400 Subject: [PATCH] Adding dynamic shortcuts populated with recent manga using the logic for recents to populate this list To Carlos: I'm sorry I couldn't help it --- .../java/eu/kanade/tachiyomi/AppModule.kt | 3 + .../data/library/LibraryUpdateService.kt | 5 +- .../ui/manga/MangaDetailsPresenter.kt | 3 + .../tachiyomi/ui/reader/ReaderPresenter.kt | 5 +- .../tachiyomi/ui/recents/RecentsPresenter.kt | 319 +++++++++--------- .../util/manga/MangaShortcutManager.kt | 100 ++++++ 6 files changed, 280 insertions(+), 155 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/util/manga/MangaShortcutManager.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/AppModule.kt b/app/src/main/java/eu/kanade/tachiyomi/AppModule.kt index ee842500b7..ddfe633fd7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/AppModule.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/AppModule.kt @@ -13,6 +13,7 @@ import eu.kanade.tachiyomi.extension.ExtensionManager import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.util.chapter.ChapterFilter +import eu.kanade.tachiyomi.util.manga.MangaShortcutManager import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.serialization.json.Json @@ -53,6 +54,8 @@ class AppModule(val app: Application) : InjektModule { addSingletonFactory { ChapterFilter() } + addSingletonFactory { MangaShortcutManager() } + // Asynchronously init expensive components for a faster cold start GlobalScope.launch { get() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt index be02c107ff..d7da69cf8e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt @@ -30,6 +30,7 @@ import eu.kanade.tachiyomi.source.model.toSManga import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import eu.kanade.tachiyomi.util.storage.getUriCompat +import eu.kanade.tachiyomi.util.manga.MangaShortcutManager import eu.kanade.tachiyomi.util.system.executeOnIO import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineExceptionHandler @@ -64,7 +65,8 @@ class LibraryUpdateService( val sourceManager: SourceManager = Injekt.get(), val preferences: PreferencesHelper = Injekt.get(), val downloadManager: DownloadManager = Injekt.get(), - val trackManager: TrackManager = Injekt.get() + val trackManager: TrackManager = Injekt.get(), + private val mangaShortcutManager: MangaShortcutManager = Injekt.get() ) : Service() { /** @@ -346,6 +348,7 @@ class LibraryUpdateService( errorFile.getUriCompat(this) ) } + mangaShortcutManager.updateShortcuts() failedUpdates.clear() notifier.cancelProgressNotification() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt index f68c1fa868..812ae65965 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt @@ -37,6 +37,7 @@ import eu.kanade.tachiyomi.util.lang.trimOrNull import eu.kanade.tachiyomi.util.shouldDownloadNewChapters import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.system.ImageUtil +import eu.kanade.tachiyomi.util.manga.MangaShortcutManager import eu.kanade.tachiyomi.util.system.executeOnIO import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -68,6 +69,7 @@ class MangaDetailsPresenter( private var scope = CoroutineScope(Job() + Dispatchers.Default) private val customMangaManager: CustomMangaManager by injectLazy() + private val mangaShortcutManager: MangaShortcutManager by injectLazy() var isLockedFromSearch = false var hasRequested = false @@ -357,6 +359,7 @@ class MangaDetailsPresenter( .map { it.toModel() } ) } + mangaShortcutManager.updateShortcuts() } if (newChapters.second.isNotEmpty()) { val removedChaptersId = newChapters.second.map { it.id } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt index e9c868c1d0..eafbc4124f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt @@ -31,6 +31,7 @@ import eu.kanade.tachiyomi.util.chapter.ChapterFilter import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.system.ImageUtil +import eu.kanade.tachiyomi.util.manga.MangaShortcutManager import eu.kanade.tachiyomi.util.system.executeOnIO import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope @@ -57,7 +58,8 @@ class ReaderPresenter( private val downloadManager: DownloadManager = Injekt.get(), private val coverCache: CoverCache = Injekt.get(), private val preferences: PreferencesHelper = Injekt.get(), - private val chapterFilter: ChapterFilter = Injekt.get() + private val chapterFilter: ChapterFilter = Injekt.get(), + private val mangaShortcutManager: MangaShortcutManager = Injekt.get() ) : BasePresenter() { /** @@ -153,6 +155,7 @@ class ReaderPresenter( saveChapterProgress(currentChapters.currChapter) saveChapterHistory(currentChapters.currChapter) } + mangaShortcutManager.updateShortcuts() } /** 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 d8658d19ef..b7532d2313 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 @@ -32,7 +32,7 @@ import java.util.concurrent.TimeUnit import kotlin.math.abs class RecentsPresenter( - val controller: RecentsController, + val controller: RecentsController?, val preferences: PreferencesHelper = Injekt.get(), val downloadManager: DownloadManager = Injekt.get(), private val db: DatabaseHelper = Injekt.get() @@ -88,173 +88,179 @@ class RecentsPresenter( fun getRecents(updatePageCount: Boolean = false, retryCount: Int = 0, itemCount: Int = 0) { val oldQuery = query scope.launch { - if (retryCount > 20) { - finished = true - setDownloadedChapters(recentItems) - withContext(Dispatchers.Main) { controller.showLists(recentItems, false) } - } + runRecents(oldQuery, updatePageCount, retryCount, itemCount) + } + } - val showRead = preferences.showReadInAllRecents().get() - if (updatePageCount) { - page++ - } + private suspend fun runRecents(oldQuery: String = "", updatePageCount: Boolean = false, retryCount: Int = 0, itemCount: Int = 0, limit: Boolean = false) { + if (retryCount > 20) { + finished = true + setDownloadedChapters(recentItems) + withContext(Dispatchers.Main) { controller?.showLists(recentItems, false) } + } - val isUngrouped = viewType > VIEW_TYPE_GROUP_ALL && query.isEmpty() - val cal = Calendar.getInstance().apply { - time = Date() - when { - query.isNotEmpty() -> add(Calendar.YEAR, -50) - isUngrouped -> add(Calendar.MONTH, -(page + 1)) - else -> add(Calendar.MONTH, -1) - } - } + val showRead = preferences.showReadInAllRecents().get() && !limit + if (updatePageCount) { + page++ + } - val startCal = Calendar.getInstance().apply { - time = Date() - when { - query.isNotEmpty() -> {} - isUngrouped && !updatePageCount -> {} - isUngrouped -> add(Calendar.MONTH, -page) - else -> {} - } + val isUngrouped = viewType > VIEW_TYPE_GROUP_ALL && query.isEmpty() + val cal = Calendar.getInstance().apply { + time = Date() + when { + query.isNotEmpty() -> add(Calendar.YEAR, -50) + isUngrouped -> add(Calendar.MONTH, -(page + 1)) + else -> add(Calendar.MONTH, -1) } + } - val calWeek = Calendar.getInstance().apply { - time = Date() - when { - query.isNotEmpty() -> add(Calendar.YEAR, -50) - isUngrouped -> add(Calendar.MONTH, -(page + 1)) - else -> add(Calendar.WEEK_OF_YEAR, -1) - } + val startCal = Calendar.getInstance().apply { + time = Date() + when { + query.isNotEmpty() -> {} + isUngrouped && !updatePageCount -> {} + isUngrouped -> add(Calendar.MONTH, -page) + else -> {} } + } - val calDay = Calendar.getInstance().apply { - time = Date() - when { - query.isNotEmpty() -> add(Calendar.YEAR, -50) - isUngrouped -> add(Calendar.MONTH, -1) - else -> add(Calendar.DAY_OF_YEAR, -1) - } + val calWeek = Calendar.getInstance().apply { + time = Date() + when { + query.isNotEmpty() -> add(Calendar.YEAR, -50) + isUngrouped -> add(Calendar.MONTH, -(page + 1)) + else -> add(Calendar.WEEK_OF_YEAR, -1) } + } - val cReading = if (viewType != VIEW_TYPE_ONLY_UPDATES) { - if (query.isEmpty() && viewType != VIEW_TYPE_ONLY_HISTORY) { - if (showRead) { - db.getAllRecents(startCal.time, cal.time, query, isUngrouped) - .executeOnIO() - } else { - db.getRecentsWithUnread(startCal.time, cal.time, query, isUngrouped) - .executeOnIO() - } - } else db.getRecentMangaLimit( - startCal.time, - cal.time, - if (viewType == VIEW_TYPE_ONLY_HISTORY) 200 else 8, - query - ).executeOnIO() - } else emptyList() - val rUpdates = when { - viewType == VIEW_TYPE_ONLY_UPDATES -> db.getRecentChapters(startCal.time, calWeek.time).executeOnIO().map { - MangaChapterHistory(it.manga, it.chapter, HistoryImpl()) + val calDay = Calendar.getInstance().apply { + time = Date() + when { + query.isNotEmpty() -> add(Calendar.YEAR, -50) + isUngrouped -> add(Calendar.MONTH, -1) + else -> add(Calendar.DAY_OF_YEAR, -1) + } + } + + val cReading = if (viewType != VIEW_TYPE_ONLY_UPDATES) { + if (query.isEmpty() && viewType != VIEW_TYPE_ONLY_HISTORY) { + if (showRead) { + db.getAllRecents(startCal.time, cal.time, query, isUngrouped && !limit) + .executeOnIO() + } else { + db.getRecentsWithUnread(startCal.time, cal.time, query, isUngrouped && !limit) + .executeOnIO() } - viewType != VIEW_TYPE_ONLY_HISTORY -> db.getUpdatedManga(startCal.time, calWeek.time, query, isUngrouped).executeOnIO() - else -> emptyList() + } else db.getRecentMangaLimit( + startCal.time, + cal.time, + if (viewType == VIEW_TYPE_ONLY_HISTORY) 200 else 8, + query + ).executeOnIO() + } else emptyList() + val rUpdates = when { + viewType == VIEW_TYPE_ONLY_UPDATES -> db.getRecentChapters(startCal.time, calWeek.time).executeOnIO().map { + MangaChapterHistory(it.manga, it.chapter, HistoryImpl()) } - rUpdates.forEach { - it.history.last_read = it.chapter.date_fetch + viewType != VIEW_TYPE_ONLY_HISTORY -> db.getUpdatedManga(startCal.time, calWeek.time, query, isUngrouped && !limit).executeOnIO() + else -> emptyList() + } + rUpdates.forEach { + it.history.last_read = it.chapter.date_fetch + } + val nAdditions = if (viewType < VIEW_TYPE_ONLY_HISTORY) { + db.getRecentlyAdded(startCal.time, calDay.time, query, isUngrouped && !limit).executeOnIO() + } else emptyList() + nAdditions.forEach { + it.history.last_read = it.manga.date_added + } + if (query != oldQuery) return + val mangaList = (cReading + rUpdates + nAdditions).sortedByDescending { + it.history.last_read + }.distinctBy { + if (query.isEmpty() && viewType != VIEW_TYPE_ONLY_HISTORY) it.manga.id else it.chapter.id + } + val pairs = mangaList.mapNotNull { + val chapter = when { + viewType == VIEW_TYPE_ONLY_HISTORY -> it.chapter + it.chapter.read || it.chapter.id == null -> getNextChapter(it.manga) + ?: if (showRead && it.chapter.id != null) it.chapter else null + it.history.id == null -> getFirstUpdatedChapter(it.manga, it.chapter) + ?: if (showRead && it.chapter.id != null) it.chapter else null + else -> it.chapter } - val nAdditions = if (viewType < VIEW_TYPE_ONLY_HISTORY) { - db.getRecentlyAdded(startCal.time, calDay.time, query, isUngrouped).executeOnIO() - } else emptyList() - nAdditions.forEach { - it.history.last_read = it.manga.date_added - } - if (query != oldQuery) return@launch - val mangaList = (cReading + rUpdates + nAdditions).sortedByDescending { - it.history.last_read - }.distinctBy { - if (query.isEmpty() && viewType != VIEW_TYPE_ONLY_HISTORY) it.manga.id else it.chapter.id - } - val pairs = mangaList.mapNotNull { - val chapter = when { - viewType == VIEW_TYPE_ONLY_HISTORY -> it.chapter - it.chapter.read || it.chapter.id == null -> getNextChapter(it.manga) - ?: if (showRead && it.chapter.id != null) it.chapter else null - it.history.id == null -> getFirstUpdatedChapter(it.manga, it.chapter) - ?: if (showRead && it.chapter.id != null) it.chapter else null - else -> it.chapter - } - if (chapter == null) if ((query.isNotEmpty() || viewType > VIEW_TYPE_UNGROUP_ALL) && - it.chapter.id != null - ) Pair(it, it.chapter) - else null - else Pair(it, chapter) - } - val newItems = if (query.isEmpty() && !isUngrouped) { - val nChaptersItems = - pairs.filter { it.first.history.id == null && it.first.chapter.id != null } - .sortedWith { f1, f2 -> - if (abs(f1.second.date_fetch - f2.second.date_fetch) <= - TimeUnit.HOURS.toMillis(12) - ) { - f2.second.date_upload.compareTo(f1.second.date_upload) - } else { - f2.second.date_fetch.compareTo(f1.second.date_fetch) - } + if (chapter == null) if ((query.isNotEmpty() || viewType > VIEW_TYPE_UNGROUP_ALL) && + it.chapter.id != null + ) Pair(it, it.chapter) + else null + else Pair(it, chapter) + } + val newItems = if (query.isEmpty() && !isUngrouped) { + val nChaptersItems = + pairs.filter { it.first.history.id == null && it.first.chapter.id != null } + .sortedWith { f1, f2 -> + if (abs(f1.second.date_fetch - f2.second.date_fetch) <= + TimeUnit.HOURS.toMillis(12) + ) { + f2.second.date_upload.compareTo(f1.second.date_upload) + } else { + f2.second.date_fetch.compareTo(f1.second.date_fetch) } - .take(4).map { - RecentMangaItem( - it.first, - it.second, - newChaptersHeader - ) - } + - RecentMangaItem(header = newChaptersHeader) - val cReadingItems = - pairs.filter { it.first.history.id != null }.take(9 - nChaptersItems.size).map { + } + .take(4).map { RecentMangaItem( it.first, it.second, - continueReadingHeader + newChaptersHeader ) - } + RecentMangaItem(header = continueReadingHeader) - val nAdditionsItems = pairs.filter { it.first.chapter.id == null }.take(4) - .map { RecentMangaItem(it.first, it.second, newAdditionsHeader) } - listOf(nChaptersItems, cReadingItems, nAdditionsItems).sortedByDescending { - it.firstOrNull()?.mch?.history?.last_read ?: 0L - }.flatten() - } else { - if (viewType == VIEW_TYPE_ONLY_UPDATES) { - val map = TreeMap>> { - d1, d2 -> - d2 - .compareTo(d1) + } + + RecentMangaItem(header = newChaptersHeader) + val cReadingItems = + pairs.filter { it.first.history.id != null }.take(9 - nChaptersItems.size).map { + RecentMangaItem( + it.first, + it.second, + continueReadingHeader + ) + } + RecentMangaItem(header = continueReadingHeader) + val nAdditionsItems = pairs.filter { it.first.chapter.id == null }.take(4) + .map { RecentMangaItem(it.first, it.second, newAdditionsHeader) } + listOf(nChaptersItems, cReadingItems, nAdditionsItems).sortedByDescending { + it.firstOrNull()?.mch?.history?.last_read ?: 0L + }.flatten() + } else { + if (viewType == VIEW_TYPE_ONLY_UPDATES) { + val map = TreeMap>> { + d1, d2 -> + d2 + .compareTo(d1) + } + val byDay = + pairs.groupByTo(map, { getMapKey(it.first.history.last_read) }) + byDay.flatMap { + val dateItem = DateItem(it.key, true) + it.value.map { item -> + RecentMangaItem(item.first, item.second, dateItem) } - val byDay = - pairs.groupByTo(map, { getMapKey(it.first.history.last_read) }) - byDay.flatMap { - val dateItem = DateItem(it.key, true) - it.value.map { item -> - RecentMangaItem(item.first, item.second, dateItem) - } - } - } else pairs.map { RecentMangaItem(it.first, it.second, null) } - } - recentItems = if (page == 0) { - newItems - } else { - recentItems + newItems - } - val newCount = itemCount + newItems.size - val hasNewItems = newItems.isNotEmpty() - if (newCount < 25 && viewType != VIEW_TYPE_GROUP_ALL && query.isEmpty()) { - page++ - getRecents(true, retryCount + (if (hasNewItems) 0 else 1), newCount) - return@launch - } + } + } else pairs.map { RecentMangaItem(it.first, it.second, null) } + } + recentItems = if (page == 0) { + newItems + } else { + recentItems + newItems + } + val newCount = itemCount + newItems.size + val hasNewItems = newItems.isNotEmpty() + if (newCount < 25 && viewType != VIEW_TYPE_GROUP_ALL && query.isEmpty() && !limit) { + page++ + getRecents(true, retryCount + (if (hasNewItems) 0 else 1), newCount) + return + } + if (!limit) { setDownloadedChapters(recentItems) withContext(Dispatchers.Main) { - controller.showLists(recentItems, hasNewItems, shouldMoveToTop) + controller?.showLists(recentItems, hasNewItems, shouldMoveToTop) shouldMoveToTop = false } } @@ -310,7 +316,7 @@ class RecentsPresenter( override fun updateDownload(download: Download) { recentItems.find { it.chapter.id == download.chapter.id }?.download = download scope.launch(Dispatchers.Main) { - controller.updateChapterDownload(download) + controller?.updateChapterDownload(download) } } @@ -318,16 +324,16 @@ class RecentsPresenter( scope.launch { setDownloadedChapters(recentItems) withContext(Dispatchers.Main) { - controller.showLists(recentItems, true) + controller?.showLists(recentItems, true) } } } override fun onUpdateManga(manga: Manga?) { if (manga == null && !LibraryUpdateService.isRunning()) { - scope.launch(Dispatchers.Main) { controller.setRefreshing(false) } + scope.launch(Dispatchers.Main) { controller?.setRefreshing(false) } } else if (manga == null) { - scope.launch(Dispatchers.Main) { controller.setRefreshing(true) } + scope.launch(Dispatchers.Main) { controller?.setRefreshing(true) } } else { getRecents() } @@ -348,7 +354,7 @@ class RecentsPresenter( download = null } - controller.showLists(recentItems, true) + controller?.showLists(recentItems, true) } } @@ -437,5 +443,12 @@ class RecentsPresenter( const val VIEW_TYPE_UNGROUP_ALL = 1 const val VIEW_TYPE_ONLY_HISTORY = 2 const val VIEW_TYPE_ONLY_UPDATES = 3 + + 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 } + } } } 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 new file mode 100644 index 0000000000..96d22b5458 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/util/manga/MangaShortcutManager.kt @@ -0,0 +1,100 @@ +package eu.kanade.tachiyomi.util.manga + +import android.content.Context +import android.content.Intent +import android.content.pm.ShortcutInfo +import android.content.pm.ShortcutManager +import android.graphics.Bitmap +import android.graphics.BitmapFactory +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.preference.PreferencesHelper +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.util.system.launchIO +import kotlinx.coroutines.GlobalScope +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import java.util.Date +import kotlin.math.min + +class MangaShortcutManager( + val preferences: PreferencesHelper = Injekt.get(), + val db: DatabaseHelper = Injekt.get(), + val coverCache: CoverCache = Injekt.get() +) { + + val context: Context = preferences.context + fun updateShortcuts() { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N_MR1) { + GlobalScope.launchIO { + val shortcutManager = context.getSystemService(ShortcutManager::class.java) + + val recentManga = RecentsPresenter.getRecentManga() + val shortcuts = recentManga.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) + } + coverFile + } 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() + } + shortcutManager.dynamicShortcuts = shortcuts + } + } + } + + private fun Bitmap.toSquare(): Bitmap? { + val side = min(width, height) + + val xOffset = (width - side) / 2 + // Slight offset for the y, since a lil bit under the top is usually the focus of covers + val yOffset = ((height - side) / 2 * 0.25).toInt() + + return Bitmap.createBitmap(this, xOffset, yOffset, side, side) + } +}