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
This commit is contained in:
Jays2Kings 2021-04-07 00:28:14 -04:00
parent 53c04c7084
commit c68053d74c
6 changed files with 280 additions and 155 deletions

View File

@ -13,6 +13,7 @@ import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.util.chapter.ChapterFilter import eu.kanade.tachiyomi.util.chapter.ChapterFilter
import eu.kanade.tachiyomi.util.manga.MangaShortcutManager
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
@ -53,6 +54,8 @@ class AppModule(val app: Application) : InjektModule {
addSingletonFactory { ChapterFilter() } addSingletonFactory { ChapterFilter() }
addSingletonFactory { MangaShortcutManager() }
// Asynchronously init expensive components for a faster cold start // Asynchronously init expensive components for a faster cold start
GlobalScope.launch { get<PreferencesHelper>() } GlobalScope.launch { get<PreferencesHelper>() }

View File

@ -30,6 +30,7 @@ import eu.kanade.tachiyomi.source.model.toSManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.storage.getUriCompat import eu.kanade.tachiyomi.util.storage.getUriCompat
import eu.kanade.tachiyomi.util.manga.MangaShortcutManager
import eu.kanade.tachiyomi.util.system.executeOnIO import eu.kanade.tachiyomi.util.system.executeOnIO
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineExceptionHandler
@ -64,7 +65,8 @@ class LibraryUpdateService(
val sourceManager: SourceManager = Injekt.get(), val sourceManager: SourceManager = Injekt.get(),
val preferences: PreferencesHelper = Injekt.get(), val preferences: PreferencesHelper = Injekt.get(),
val downloadManager: DownloadManager = Injekt.get(), val downloadManager: DownloadManager = Injekt.get(),
val trackManager: TrackManager = Injekt.get() val trackManager: TrackManager = Injekt.get(),
private val mangaShortcutManager: MangaShortcutManager = Injekt.get()
) : Service() { ) : Service() {
/** /**
@ -346,6 +348,7 @@ class LibraryUpdateService(
errorFile.getUriCompat(this) errorFile.getUriCompat(this)
) )
} }
mangaShortcutManager.updateShortcuts()
failedUpdates.clear() failedUpdates.clear()
notifier.cancelProgressNotification() notifier.cancelProgressNotification()
} }

View File

@ -37,6 +37,7 @@ import eu.kanade.tachiyomi.util.lang.trimOrNull
import eu.kanade.tachiyomi.util.shouldDownloadNewChapters import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.system.ImageUtil import eu.kanade.tachiyomi.util.system.ImageUtil
import eu.kanade.tachiyomi.util.manga.MangaShortcutManager
import eu.kanade.tachiyomi.util.system.executeOnIO import eu.kanade.tachiyomi.util.system.executeOnIO
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -68,6 +69,7 @@ class MangaDetailsPresenter(
private var scope = CoroutineScope(Job() + Dispatchers.Default) private var scope = CoroutineScope(Job() + Dispatchers.Default)
private val customMangaManager: CustomMangaManager by injectLazy() private val customMangaManager: CustomMangaManager by injectLazy()
private val mangaShortcutManager: MangaShortcutManager by injectLazy()
var isLockedFromSearch = false var isLockedFromSearch = false
var hasRequested = false var hasRequested = false
@ -357,6 +359,7 @@ class MangaDetailsPresenter(
.map { it.toModel() } .map { it.toModel() }
) )
} }
mangaShortcutManager.updateShortcuts()
} }
if (newChapters.second.isNotEmpty()) { if (newChapters.second.isNotEmpty()) {
val removedChaptersId = newChapters.second.map { it.id } val removedChaptersId = newChapters.second.map { it.id }

View File

@ -31,6 +31,7 @@ import eu.kanade.tachiyomi.util.chapter.ChapterFilter
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.system.ImageUtil import eu.kanade.tachiyomi.util.system.ImageUtil
import eu.kanade.tachiyomi.util.manga.MangaShortcutManager
import eu.kanade.tachiyomi.util.system.executeOnIO import eu.kanade.tachiyomi.util.system.executeOnIO
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
@ -57,7 +58,8 @@ class ReaderPresenter(
private val downloadManager: DownloadManager = Injekt.get(), private val downloadManager: DownloadManager = Injekt.get(),
private val coverCache: CoverCache = Injekt.get(), private val coverCache: CoverCache = Injekt.get(),
private val preferences: PreferencesHelper = 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<ReaderActivity>() { ) : BasePresenter<ReaderActivity>() {
/** /**
@ -153,6 +155,7 @@ class ReaderPresenter(
saveChapterProgress(currentChapters.currChapter) saveChapterProgress(currentChapters.currChapter)
saveChapterHistory(currentChapters.currChapter) saveChapterHistory(currentChapters.currChapter)
} }
mangaShortcutManager.updateShortcuts()
} }
/** /**

View File

@ -32,7 +32,7 @@ import java.util.concurrent.TimeUnit
import kotlin.math.abs import kotlin.math.abs
class RecentsPresenter( class RecentsPresenter(
val controller: RecentsController, val controller: RecentsController?,
val preferences: PreferencesHelper = Injekt.get(), val preferences: PreferencesHelper = Injekt.get(),
val downloadManager: DownloadManager = Injekt.get(), val downloadManager: DownloadManager = Injekt.get(),
private val db: DatabaseHelper = 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) { fun getRecents(updatePageCount: Boolean = false, retryCount: Int = 0, itemCount: Int = 0) {
val oldQuery = query val oldQuery = query
scope.launch { scope.launch {
if (retryCount > 20) { runRecents(oldQuery, updatePageCount, retryCount, itemCount)
finished = true }
setDownloadedChapters(recentItems) }
withContext(Dispatchers.Main) { controller.showLists(recentItems, false) }
}
val showRead = preferences.showReadInAllRecents().get() private suspend fun runRecents(oldQuery: String = "", updatePageCount: Boolean = false, retryCount: Int = 0, itemCount: Int = 0, limit: Boolean = false) {
if (updatePageCount) { if (retryCount > 20) {
page++ finished = true
} setDownloadedChapters(recentItems)
withContext(Dispatchers.Main) { controller?.showLists(recentItems, false) }
}
val isUngrouped = viewType > VIEW_TYPE_GROUP_ALL && query.isEmpty() val showRead = preferences.showReadInAllRecents().get() && !limit
val cal = Calendar.getInstance().apply { if (updatePageCount) {
time = Date() page++
when { }
query.isNotEmpty() -> add(Calendar.YEAR, -50)
isUngrouped -> add(Calendar.MONTH, -(page + 1))
else -> add(Calendar.MONTH, -1)
}
}
val startCal = Calendar.getInstance().apply { val isUngrouped = viewType > VIEW_TYPE_GROUP_ALL && query.isEmpty()
time = Date() val cal = Calendar.getInstance().apply {
when { time = Date()
query.isNotEmpty() -> {} when {
isUngrouped && !updatePageCount -> {} query.isNotEmpty() -> add(Calendar.YEAR, -50)
isUngrouped -> add(Calendar.MONTH, -page) isUngrouped -> add(Calendar.MONTH, -(page + 1))
else -> {} else -> add(Calendar.MONTH, -1)
}
} }
}
val calWeek = Calendar.getInstance().apply { val startCal = Calendar.getInstance().apply {
time = Date() time = Date()
when { when {
query.isNotEmpty() -> add(Calendar.YEAR, -50) query.isNotEmpty() -> {}
isUngrouped -> add(Calendar.MONTH, -(page + 1)) isUngrouped && !updatePageCount -> {}
else -> add(Calendar.WEEK_OF_YEAR, -1) isUngrouped -> add(Calendar.MONTH, -page)
} else -> {}
} }
}
val calDay = Calendar.getInstance().apply { val calWeek = Calendar.getInstance().apply {
time = Date() time = Date()
when { when {
query.isNotEmpty() -> add(Calendar.YEAR, -50) query.isNotEmpty() -> add(Calendar.YEAR, -50)
isUngrouped -> add(Calendar.MONTH, -1) isUngrouped -> add(Calendar.MONTH, -(page + 1))
else -> add(Calendar.DAY_OF_YEAR, -1) else -> add(Calendar.WEEK_OF_YEAR, -1)
}
} }
}
val cReading = if (viewType != VIEW_TYPE_ONLY_UPDATES) { val calDay = Calendar.getInstance().apply {
if (query.isEmpty() && viewType != VIEW_TYPE_ONLY_HISTORY) { time = Date()
if (showRead) { when {
db.getAllRecents(startCal.time, cal.time, query, isUngrouped) query.isNotEmpty() -> add(Calendar.YEAR, -50)
.executeOnIO() isUngrouped -> add(Calendar.MONTH, -1)
} else { else -> add(Calendar.DAY_OF_YEAR, -1)
db.getRecentsWithUnread(startCal.time, cal.time, query, isUngrouped) }
.executeOnIO() }
}
} else db.getRecentMangaLimit( val cReading = if (viewType != VIEW_TYPE_ONLY_UPDATES) {
startCal.time, if (query.isEmpty() && viewType != VIEW_TYPE_ONLY_HISTORY) {
cal.time, if (showRead) {
if (viewType == VIEW_TYPE_ONLY_HISTORY) 200 else 8, db.getAllRecents(startCal.time, cal.time, query, isUngrouped && !limit)
query .executeOnIO()
).executeOnIO() } else {
} else emptyList() db.getRecentsWithUnread(startCal.time, cal.time, query, isUngrouped && !limit)
val rUpdates = when { .executeOnIO()
viewType == VIEW_TYPE_ONLY_UPDATES -> db.getRecentChapters(startCal.time, calWeek.time).executeOnIO().map {
MangaChapterHistory(it.manga, it.chapter, HistoryImpl())
} }
viewType != VIEW_TYPE_ONLY_HISTORY -> db.getUpdatedManga(startCal.time, calWeek.time, query, isUngrouped).executeOnIO() } else db.getRecentMangaLimit(
else -> emptyList() 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 { viewType != VIEW_TYPE_ONLY_HISTORY -> db.getUpdatedManga(startCal.time, calWeek.time, query, isUngrouped && !limit).executeOnIO()
it.history.last_read = it.chapter.date_fetch 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) { if (chapter == null) if ((query.isNotEmpty() || viewType > VIEW_TYPE_UNGROUP_ALL) &&
db.getRecentlyAdded(startCal.time, calDay.time, query, isUngrouped).executeOnIO() it.chapter.id != null
} else emptyList() ) Pair(it, it.chapter)
nAdditions.forEach { else null
it.history.last_read = it.manga.date_added else Pair(it, chapter)
} }
if (query != oldQuery) return@launch val newItems = if (query.isEmpty() && !isUngrouped) {
val mangaList = (cReading + rUpdates + nAdditions).sortedByDescending { val nChaptersItems =
it.history.last_read pairs.filter { it.first.history.id == null && it.first.chapter.id != null }
}.distinctBy { .sortedWith { f1, f2 ->
if (query.isEmpty() && viewType != VIEW_TYPE_ONLY_HISTORY) it.manga.id else it.chapter.id if (abs(f1.second.date_fetch - f2.second.date_fetch) <=
} TimeUnit.HOURS.toMillis(12)
val pairs = mangaList.mapNotNull { ) {
val chapter = when { f2.second.date_upload.compareTo(f1.second.date_upload)
viewType == VIEW_TYPE_ONLY_HISTORY -> it.chapter } else {
it.chapter.read || it.chapter.id == null -> getNextChapter(it.manga) f2.second.date_fetch.compareTo(f1.second.date_fetch)
?: 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)
}
} }
.take(4).map { }
RecentMangaItem( .take(4).map {
it.first,
it.second,
newChaptersHeader
)
} +
RecentMangaItem(header = newChaptersHeader)
val cReadingItems =
pairs.filter { it.first.history.id != null }.take(9 - nChaptersItems.size).map {
RecentMangaItem( RecentMangaItem(
it.first, it.first,
it.second, it.second,
continueReadingHeader newChaptersHeader
) )
} + RecentMangaItem(header = continueReadingHeader) } +
val nAdditionsItems = pairs.filter { it.first.chapter.id == null }.take(4) RecentMangaItem(header = newChaptersHeader)
.map { RecentMangaItem(it.first, it.second, newAdditionsHeader) } val cReadingItems =
listOf(nChaptersItems, cReadingItems, nAdditionsItems).sortedByDescending { pairs.filter { it.first.history.id != null }.take(9 - nChaptersItems.size).map {
it.firstOrNull()?.mch?.history?.last_read ?: 0L RecentMangaItem(
}.flatten() it.first,
} else { it.second,
if (viewType == VIEW_TYPE_ONLY_UPDATES) { continueReadingHeader
val map = TreeMap<Date, MutableList<Pair<MangaChapterHistory, Chapter>>> { )
d1, d2 -> } + RecentMangaItem(header = continueReadingHeader)
d2 val nAdditionsItems = pairs.filter { it.first.chapter.id == null }.take(4)
.compareTo(d1) .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<Date, MutableList<Pair<MangaChapterHistory, Chapter>>> {
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) }) } else pairs.map { RecentMangaItem(it.first, it.second, null) }
byDay.flatMap { }
val dateItem = DateItem(it.key, true) recentItems = if (page == 0) {
it.value.map { item -> newItems
RecentMangaItem(item.first, item.second, dateItem) } else {
} recentItems + newItems
} }
} else pairs.map { RecentMangaItem(it.first, it.second, null) } val newCount = itemCount + newItems.size
} val hasNewItems = newItems.isNotEmpty()
recentItems = if (page == 0) { if (newCount < 25 && viewType != VIEW_TYPE_GROUP_ALL && query.isEmpty() && !limit) {
newItems page++
} else { getRecents(true, retryCount + (if (hasNewItems) 0 else 1), newCount)
recentItems + newItems return
} }
val newCount = itemCount + newItems.size if (!limit) {
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
}
setDownloadedChapters(recentItems) setDownloadedChapters(recentItems)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
controller.showLists(recentItems, hasNewItems, shouldMoveToTop) controller?.showLists(recentItems, hasNewItems, shouldMoveToTop)
shouldMoveToTop = false shouldMoveToTop = false
} }
} }
@ -310,7 +316,7 @@ class RecentsPresenter(
override fun updateDownload(download: Download) { override fun updateDownload(download: Download) {
recentItems.find { it.chapter.id == download.chapter.id }?.download = download recentItems.find { it.chapter.id == download.chapter.id }?.download = download
scope.launch(Dispatchers.Main) { scope.launch(Dispatchers.Main) {
controller.updateChapterDownload(download) controller?.updateChapterDownload(download)
} }
} }
@ -318,16 +324,16 @@ class RecentsPresenter(
scope.launch { scope.launch {
setDownloadedChapters(recentItems) setDownloadedChapters(recentItems)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
controller.showLists(recentItems, true) controller?.showLists(recentItems, true)
} }
} }
} }
override fun onUpdateManga(manga: Manga?) { override fun onUpdateManga(manga: Manga?) {
if (manga == null && !LibraryUpdateService.isRunning()) { if (manga == null && !LibraryUpdateService.isRunning()) {
scope.launch(Dispatchers.Main) { controller.setRefreshing(false) } scope.launch(Dispatchers.Main) { controller?.setRefreshing(false) }
} else if (manga == null) { } else if (manga == null) {
scope.launch(Dispatchers.Main) { controller.setRefreshing(true) } scope.launch(Dispatchers.Main) { controller?.setRefreshing(true) }
} else { } else {
getRecents() getRecents()
} }
@ -348,7 +354,7 @@ class RecentsPresenter(
download = null 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_UNGROUP_ALL = 1
const val VIEW_TYPE_ONLY_HISTORY = 2 const val VIEW_TYPE_ONLY_HISTORY = 2
const val VIEW_TYPE_ONLY_UPDATES = 3 const val VIEW_TYPE_ONLY_UPDATES = 3
suspend fun getRecentManga(): List<Manga> {
val presenter = RecentsPresenter(null)
presenter.viewType = 1
presenter.runRecents(limit = true)
return presenter.recentItems.filter { it.mch.manga.id != null }.map { it.mch.manga }
}
} }
} }

View File

@ -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)
}
}