mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-11-10 05:25:04 +01:00
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:
parent
53c04c7084
commit
c68053d74c
@ -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>() }
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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 }
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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,13 +88,18 @@ 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 {
|
||||||
|
runRecents(oldQuery, updatePageCount, retryCount, itemCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun runRecents(oldQuery: String = "", updatePageCount: Boolean = false, retryCount: Int = 0, itemCount: Int = 0, limit: Boolean = false) {
|
||||||
if (retryCount > 20) {
|
if (retryCount > 20) {
|
||||||
finished = true
|
finished = true
|
||||||
setDownloadedChapters(recentItems)
|
setDownloadedChapters(recentItems)
|
||||||
withContext(Dispatchers.Main) { controller.showLists(recentItems, false) }
|
withContext(Dispatchers.Main) { controller?.showLists(recentItems, false) }
|
||||||
}
|
}
|
||||||
|
|
||||||
val showRead = preferences.showReadInAllRecents().get()
|
val showRead = preferences.showReadInAllRecents().get() && !limit
|
||||||
if (updatePageCount) {
|
if (updatePageCount) {
|
||||||
page++
|
page++
|
||||||
}
|
}
|
||||||
@ -140,10 +145,10 @@ class RecentsPresenter(
|
|||||||
val cReading = if (viewType != VIEW_TYPE_ONLY_UPDATES) {
|
val cReading = if (viewType != VIEW_TYPE_ONLY_UPDATES) {
|
||||||
if (query.isEmpty() && viewType != VIEW_TYPE_ONLY_HISTORY) {
|
if (query.isEmpty() && viewType != VIEW_TYPE_ONLY_HISTORY) {
|
||||||
if (showRead) {
|
if (showRead) {
|
||||||
db.getAllRecents(startCal.time, cal.time, query, isUngrouped)
|
db.getAllRecents(startCal.time, cal.time, query, isUngrouped && !limit)
|
||||||
.executeOnIO()
|
.executeOnIO()
|
||||||
} else {
|
} else {
|
||||||
db.getRecentsWithUnread(startCal.time, cal.time, query, isUngrouped)
|
db.getRecentsWithUnread(startCal.time, cal.time, query, isUngrouped && !limit)
|
||||||
.executeOnIO()
|
.executeOnIO()
|
||||||
}
|
}
|
||||||
} else db.getRecentMangaLimit(
|
} else db.getRecentMangaLimit(
|
||||||
@ -157,19 +162,19 @@ class RecentsPresenter(
|
|||||||
viewType == VIEW_TYPE_ONLY_UPDATES -> db.getRecentChapters(startCal.time, calWeek.time).executeOnIO().map {
|
viewType == VIEW_TYPE_ONLY_UPDATES -> db.getRecentChapters(startCal.time, calWeek.time).executeOnIO().map {
|
||||||
MangaChapterHistory(it.manga, it.chapter, HistoryImpl())
|
MangaChapterHistory(it.manga, it.chapter, HistoryImpl())
|
||||||
}
|
}
|
||||||
viewType != VIEW_TYPE_ONLY_HISTORY -> db.getUpdatedManga(startCal.time, calWeek.time, query, isUngrouped).executeOnIO()
|
viewType != VIEW_TYPE_ONLY_HISTORY -> db.getUpdatedManga(startCal.time, calWeek.time, query, isUngrouped && !limit).executeOnIO()
|
||||||
else -> emptyList()
|
else -> emptyList()
|
||||||
}
|
}
|
||||||
rUpdates.forEach {
|
rUpdates.forEach {
|
||||||
it.history.last_read = it.chapter.date_fetch
|
it.history.last_read = it.chapter.date_fetch
|
||||||
}
|
}
|
||||||
val nAdditions = if (viewType < VIEW_TYPE_ONLY_HISTORY) {
|
val nAdditions = if (viewType < VIEW_TYPE_ONLY_HISTORY) {
|
||||||
db.getRecentlyAdded(startCal.time, calDay.time, query, isUngrouped).executeOnIO()
|
db.getRecentlyAdded(startCal.time, calDay.time, query, isUngrouped && !limit).executeOnIO()
|
||||||
} else emptyList()
|
} else emptyList()
|
||||||
nAdditions.forEach {
|
nAdditions.forEach {
|
||||||
it.history.last_read = it.manga.date_added
|
it.history.last_read = it.manga.date_added
|
||||||
}
|
}
|
||||||
if (query != oldQuery) return@launch
|
if (query != oldQuery) return
|
||||||
val mangaList = (cReading + rUpdates + nAdditions).sortedByDescending {
|
val mangaList = (cReading + rUpdates + nAdditions).sortedByDescending {
|
||||||
it.history.last_read
|
it.history.last_read
|
||||||
}.distinctBy {
|
}.distinctBy {
|
||||||
@ -247,14 +252,15 @@ class RecentsPresenter(
|
|||||||
}
|
}
|
||||||
val newCount = itemCount + newItems.size
|
val newCount = itemCount + newItems.size
|
||||||
val hasNewItems = newItems.isNotEmpty()
|
val hasNewItems = newItems.isNotEmpty()
|
||||||
if (newCount < 25 && viewType != VIEW_TYPE_GROUP_ALL && query.isEmpty()) {
|
if (newCount < 25 && viewType != VIEW_TYPE_GROUP_ALL && query.isEmpty() && !limit) {
|
||||||
page++
|
page++
|
||||||
getRecents(true, retryCount + (if (hasNewItems) 0 else 1), newCount)
|
getRecents(true, retryCount + (if (hasNewItems) 0 else 1), newCount)
|
||||||
return@launch
|
return
|
||||||
}
|
}
|
||||||
|
if (!limit) {
|
||||||
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 }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user