Cleanup resources and put comments where SY code is different from preview code

This commit is contained in:
Jobobby04 2020-06-19 20:50:16 -04:00
parent 7e5d095f8d
commit 4ef72194bb
114 changed files with 440 additions and 352 deletions

View File

@ -46,9 +46,11 @@ class AppModule(val app: Application) : InjektModule {
addSingletonFactory { Gson() }
// SY -->
addSingletonFactory { EHentaiUpdateHelper(app) }
addSingletonFactory { Markwon.create(app) }
// SY <--
// Asynchronously init expensive components for a faster cold start

View File

@ -151,9 +151,10 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) {
// Backup extension ID/name mapping
backupExtensionInfo(extensionEntries, extensions)
// SY -->
root[SAVEDSEARCHES] =
Injekt.get<PreferencesHelper>().eh_savedSearches().get().joinToString(separator = "***")
// SY <--
}
try {
@ -306,6 +307,7 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) {
* @return [Observable] that contains manga
*/
fun restoreChapterFetchObservable(source: Source, manga: Manga, chapters: List<Chapter>, throttleManager: EHentaiThrottleManager): Observable<Pair<List<Chapter>, List<Chapter>>> {
// SY -->
return (
if (source is EHentai) {
source.fetchChapterList(manga, throttleManager::throttle)
@ -322,6 +324,7 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) {
syncChaptersWithSource(databaseHelper, it, manga, source)
}
}
// SY <--
.doOnNext { pair ->
if (pair.first.isNotEmpty()) {
chapters.forEach { it.manga_id = manga.id }
@ -496,6 +499,7 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) {
return true
}
// SY -->
internal fun restoreSavedSearches(jsonSavedSearches: JsonElement) {
val backupSavedSearches = jsonSavedSearches.asString.split("***").toSet()
backupSavedSearches.forEach {
@ -505,6 +509,7 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) {
}
}
}
// SY <--
/**
* Returns manga

View File

@ -103,7 +103,9 @@ class BackupRestoreService : Service() {
private var job: Job? = null
// SY -->
private val throttleManager = EHentaiThrottleManager()
// SY <--
/**
* The progress of a backup restore
@ -115,9 +117,11 @@ class BackupRestoreService : Service() {
*/
private var restoreAmount = 0
// SY -->
private var skippedAmount = 0
private var totalAmount = 0
// SY <--
/**
* Mapping of source ID to source name from backup data
@ -178,7 +182,9 @@ class BackupRestoreService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val uri = intent?.getParcelableExtra<Uri>(BackupConst.EXTRA_URI) ?: return START_NOT_STICKY
// SY -->
throttleManager.resetThrottle()
// SY <--
// Cancel any previous job if needed.
job?.cancel()
@ -221,6 +227,7 @@ class BackupRestoreService : Service() {
val mangasJson = json.get(MANGAS).asJsonArray
// SY -->
val validManga = mangasJson.filter {
var manga = backupManager.parser.fromJson<MangaImpl>(it.asJsonObject.get(MANGA))
// EXH -->
@ -235,13 +242,16 @@ class BackupRestoreService : Service() {
totalAmount = mangasJson.size()
restoreAmount = validManga.count() + 1 // +1 for categories
skippedAmount = mangasJson.size() - validManga.count()
// SY <--
restoreProgress = 0
errors.clear()
// Restore categories
json.get(CATEGORIES)?.let { restoreCategories(it) }
// SY -->
json.get(SAVEDSEARCHES)?.let { restoreSavedSearches(it) }
// SY <--
// Store source mapping for error messages
sourceMapping = BackupRestoreValidator.getSourceMapping(json)
@ -273,15 +283,19 @@ class BackupRestoreService : Service() {
showRestoreProgress(restoreProgress, restoreAmount, getString(R.string.categories))
}
// SY -->
private fun restoreSavedSearches(savedSearchesJson: JsonElement) {
backupManager.restoreSavedSearches(savedSearchesJson)
restoreProgress += 1
showRestoreProgress(restoreProgress, restoreAmount, getString(R.string.eh_saved_searches))
}
// SY <--
private fun restoreManga(mangaJson: JsonObject) {
// SY -->
var manga = backupManager.parser.fromJson<MangaImpl>(mangaJson.get(MANGA))
// SY <--
val chapters = backupManager.parser.fromJson<List<ChapterImpl>>(
mangaJson.get(CHAPTERS)
?: JsonArray()
@ -437,7 +451,9 @@ class BackupRestoreService : Service() {
* @return [Observable] that contains manga
*/
private fun chapterFetchObservable(source: Source, manga: Manga, chapters: List<Chapter>): Observable<Pair<List<Chapter>, List<Chapter>>> {
// SY -->
return backupManager.restoreChapterFetchObservable(source, manga, chapters, throttleManager)
// SY <--
// If there's any error, return empty update and continue.
.onErrorReturn {
errors.add(Date() to "${manga.title} - ${it.message}")

View File

@ -17,7 +17,9 @@ object Backup {
const val EXTENSIONS = "extensions"
const val HISTORY = "history"
const val VERSION = "version"
// SY -->
const val SAVEDSEARCHES = "savedsearches"
// SY <--
fun getDefaultFilename(): String {
val date = SimpleDateFormat("yyyy-MM-dd_HH-mm", Locale.getDefault()).format(Date())

View File

@ -60,5 +60,7 @@ open class DatabaseHelper(context: Context) :
inline fun inTransaction(block: () -> Unit) = db.inTransaction(block)
// SY -->
fun lowLevel() = db.lowLevel()
// SY <--
}

View File

@ -41,8 +41,10 @@ class CategoryPutResolver : DefaultPutResolver<Category>() {
put(COL_NAME, obj.name)
put(COL_ORDER, obj.order)
put(COL_FLAGS, obj.flags)
// SY -->
val orderString = obj.mangaOrder.joinToString("/")
put(COL_MANGA_ORDER, orderString)
// SY <--
}
}
@ -54,8 +56,10 @@ class CategoryGetResolver : DefaultGetResolver<Category>() {
order = cursor.getInt(cursor.getColumnIndex(COL_ORDER))
flags = cursor.getInt(cursor.getColumnIndex(COL_FLAGS))
// SY -->
val orderString = cursor.getString(cursor.getColumnIndex(COL_MANGA_ORDER))
mangaOrder = orderString?.split("/")?.mapNotNull { it.toLongOrNull() } ?: emptyList()
// SY <--
}
}

View File

@ -12,7 +12,9 @@ interface Category : Serializable {
var flags: Int
// SY -->
var mangaOrder: List<Long>
// SY <--
val nameLower: String
get() = name.toLowerCase()

View File

@ -10,7 +10,9 @@ class CategoryImpl : Category {
override var flags: Int = 0
// SY -->
override var mangaOrder: List<Long> = emptyList()
// SY <--
override fun equals(other: Any?): Boolean {
if (this === other) return true

View File

@ -8,7 +8,9 @@ open class MangaImpl : Manga {
override lateinit var url: String
// SY -->
override var title: String = ""
// SY <--
override var artist: String? = null

View File

@ -14,7 +14,7 @@ import eu.kanade.tachiyomi.data.database.tables.ChapterTable
import java.util.Date
interface ChapterQueries : DbProvider {
// SY -->
fun getChapters(manga: Manga) = getChaptersByMangaId(manga.id)
fun getChaptersByMangaId(mangaId: Long?) = db.get()
@ -36,6 +36,7 @@ interface ChapterQueries : DbProvider {
.build()
)
.prepare()
// SY <--
fun getRecentChapters(date: Date) = db.get()
.listOfObjects(MangaChapter::class.java)
@ -82,6 +83,7 @@ interface ChapterQueries : DbProvider {
)
.prepare()
// SY -->
fun getChapters(url: String) = db.get()
.listOfObjects(Chapter::class.java)
.withQuery(
@ -92,6 +94,7 @@ interface ChapterQueries : DbProvider {
.build()
)
.prepare()
// SY <--
fun insertChapter(chapter: Chapter) = db.put().`object`(chapter).prepare()

View File

@ -18,6 +18,7 @@ interface HistoryQueries : DbProvider {
*/
fun insertHistory(history: History) = db.put().`object`(history).prepare()
// SY -->
/**
* Returns history of recent manga containing last read chapter
* @param date recent date range
@ -50,6 +51,7 @@ interface HistoryQueries : DbProvider {
)
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
.prepare()
// SY <--
fun getHistoryByMangaId(mangaId: Long) = db.get()
.listOfObjects(History::class.java)

View File

@ -26,12 +26,14 @@ interface MangaCategoryQueries : DbProvider {
fun setMangaCategories(mangasCategories: List<MangaCategory>, mangas: List<Manga>) {
db.inTransaction {
// SY -->
mangas.chunked(100) { chunk ->
deleteOldMangasCategories(chunk).executeAsBlocking()
}
mangasCategories.chunked(100) { chunk ->
insertMangasCategories(chunk).executeAsBlocking()
}
// SY <--
}
}
}

View File

@ -75,6 +75,7 @@ interface MangaQueries : DbProvider {
)
.prepare()
// SY -->
fun getMergedMangas(id: Long) = db.get()
.listOfObjects(Manga::class.java)
.withQuery(
@ -83,6 +84,7 @@ interface MangaQueries : DbProvider {
.build()
)
.prepare()
// SY <--
fun insertManga(manga: Manga) = db.put().`object`(manga).prepare()
@ -170,6 +172,7 @@ interface MangaQueries : DbProvider {
)
.prepare()
// SY -->
fun getMangaWithMetadata() = db.get()
.listOfObjects(Manga::class.java)
.withQuery(
@ -219,4 +222,5 @@ interface MangaQueries : DbProvider {
.build()
)
.prepare()
// SY <--
}

View File

@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable as MangaCateg
import eu.kanade.tachiyomi.data.database.tables.MangaTable as Manga
import eu.kanade.tachiyomi.data.database.tables.MergedTable as Merged
// SY -->
/**
* Query to get the manga merged into a merged manga
*/
@ -32,6 +33,7 @@ fun getMergedChaptersQuery(id: Long) =
JOIN ${Chapter.TABLE}
ON ${Chapter.TABLE}.${Chapter.COL_MANGA_ID} = M.${Merged.COL_MANGA_ID}
"""
// SY <--
/**
* Query to get the manga from the library, with their categories and unread count.
@ -76,6 +78,7 @@ fun getRecentsQuery() =
* and are read after the given time period
* @return return limit is 25
*/
// SY -->
fun getRecentMangasQuery(offset: Int = 0, search: String = "") =
"""
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, ${Manga.TABLE}.*, ${Chapter.TABLE}.*, ${History.TABLE}.*
@ -122,6 +125,7 @@ fun getRecentMangasLimitQuery(limit: Int = 25, search: String = "") =
ORDER BY max_last_read.${History.COL_LAST_READ} DESC
LIMIT $limit
"""
// SY <--
fun getHistoryByMangaId() =
"""

View File

@ -1,32 +0,0 @@
package eu.kanade.tachiyomi.data.database.resolvers
import android.content.ContentValues
import com.pushtorefresh.storio.sqlite.StorIOSQLite
import com.pushtorefresh.storio.sqlite.operations.put.PutResolver
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import eu.kanade.tachiyomi.data.database.inTransactionReturn
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.tables.MangaTable
// [EXH]
class MangaUrlPutResolver : PutResolver<Manga>() {
override fun performPut(db: StorIOSQLite, manga: Manga) = db.inTransactionReturn {
val updateQuery = mapToUpdateQuery(manga)
val contentValues = mapToContentValues(manga)
val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues)
PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table())
}
fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder()
.table(MangaTable.TABLE)
.where("${MangaTable.COL_ID} = ?")
.whereArgs(manga.id)
.build()
fun mapToContentValues(manga: Manga) = ContentValues(1).apply {
put(MangaTable.COL_URL, manga.url)
}
}

View File

@ -12,7 +12,9 @@ object CategoryTable {
const val COL_FLAGS = "flags"
// SY -->
const val COL_MANGA_ORDER = "manga_order"
// SY <--
val createTableQuery: String
get() =
@ -21,9 +23,12 @@ object CategoryTable {
$COL_NAME TEXT NOT NULL,
$COL_ORDER INTEGER NOT NULL,
$COL_FLAGS INTEGER NOT NULL,
// SY -->
$COL_MANGA_ORDER TEXT NOT NULL
// SY <--
)"""
// SY -->
val addMangaOrder: String
get() = "ALTER TABLE $TABLE ADD COLUMN $COL_MANGA_ORDER TEXT"
// SY <--
}

View File

@ -6,7 +6,9 @@ abstract class UpdateChecker {
companion object {
fun getUpdateChecker(): UpdateChecker {
// SY -->
return GithubUpdateChecker()
// SY <--
}
}

View File

@ -24,9 +24,11 @@ interface GithubService {
}
}
// SY -->
@GET("/repos/jobobby04/tachiyomiSY/releases/latest")
suspend fun getLatestVersion(): GithubRelease
@GET("/repos/jobobby04/TachiyomiSYPreview/releases/latest")
suspend fun getLatestDebugVersion(): GithubRelease
// SY <--
}

View File

@ -4,7 +4,7 @@ import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.data.updater.UpdateChecker
import eu.kanade.tachiyomi.data.updater.UpdateResult
import exh.syDebugVersion
// SY -->
class GithubUpdateChecker(val debug: Boolean = false) : UpdateChecker() {
private val service: GithubService = GithubService.create()
@ -25,3 +25,4 @@ class GithubUpdateChecker(val debug: Boolean = false) : UpdateChecker() {
}
}
}
// SY <--

View File

@ -79,6 +79,7 @@ class ExtensionManager(
return iconMap[pkgName] ?: iconMap.getOrPut(pkgName) { context.packageManager.getApplicationIcon(pkgName) }
}
// SY -->
return when (source.id) {
EH_SOURCE_ID -> context.getDrawable(R.mipmap.ic_ehentai_source)
EXH_SOURCE_ID -> context.getDrawable(R.mipmap.ic_ehentai_source)
@ -91,6 +92,7 @@ class ExtensionManager(
MERGED_SOURCE_ID -> context.getDrawable(R.mipmap.ic_merged_source)
else -> null
}
// SY <--
}
/**
@ -108,7 +110,9 @@ class ExtensionManager(
updatedInstalledExtensionsStatuses(value)
}
// SY -->
var unalteredAvailableExtensions = emptyList<Extension.Available>()
// SY <--
/**
* Relay used to notify the untrusted extensions.
@ -155,7 +159,9 @@ class ExtensionManager(
untrustedExtensions = extensions
.filterIsInstance<LoadResult.Untrusted>()
.map { it.extension }
// SY -->
.filterNotBlacklisted()
// SY <--
}
// EXH -->
@ -210,8 +216,10 @@ class ExtensionManager(
emptyList()
}
// SY -->
unalteredAvailableExtensions = extensions
availableExtensions = extensions.filterNotBlacklisted()
// SY <--
}
}
@ -231,14 +239,18 @@ class ExtensionManager(
for ((index, installedExt) in mutInstalledExtensions.withIndex()) {
val pkgName = installedExt.pkgName
// SY -->
val availableExt = unalteredAvailableExtensions.find { it.pkgName == pkgName }
// SY <--
if (availableExt == null && !installedExt.isObsolete) {
mutInstalledExtensions[index] = installedExt.copy(isObsolete = true)
changed = true
// SY -->
} else if (installedExt.isBlacklisted() && !installedExt.isRedundant) {
mutInstalledExtensions[index] = installedExt.copy(isRedundant = true)
changed = true
// SY <--
} else if (availableExt != null) {
val hasUpdate = availableExt.versionCode > installedExt.versionCode
if (installedExt.hasUpdate != hasUpdate) {
@ -334,10 +346,12 @@ class ExtensionManager(
* @param extension The extension to be registered.
*/
private fun registerNewExtension(extension: Extension.Installed) {
// SY -->
if (extension.isBlacklisted()) {
XLog.d("[EXH] Removing blacklisted extension: (name: String, pkgName: %s)!", extension.name, extension.pkgName)
return
}
// SY <--
installedExtensions += extension
extension.sources.forEach { sourceManager.registerSource(it) }
@ -350,10 +364,12 @@ class ExtensionManager(
* @param extension The extension to be registered.
*/
private fun registerUpdatedExtension(extension: Extension.Installed) {
// SY -->
if (extension.isBlacklisted()) {
XLog.d("[EXH] Removing blacklisted extension: (name: String, pkgName: %s)!", extension.name, extension.pkgName)
return
}
// SY <--
val mutInstalledExtensions = installedExtensions.toMutableList()
val oldExtension = mutInstalledExtensions.find { it.pkgName == extension.pkgName }

View File

@ -47,12 +47,16 @@ internal class ExtensionGithubApi {
preferences.lastExtCheck().set(Date().time)
// SY -->
val blacklistEnabled = preferences.eh_enableSourceBlacklist().get()
// SY <--
val installedExtensions = ExtensionLoader.loadExtensions(context)
.filterIsInstance<LoadResult.Success>()
.map { it.extension }
// SY -->
.filterNot { it.isBlacklisted(blacklistEnabled) }
// SY <--
val extensionsWithUpdate = mutableListOf<Extension.Installed>()
for (installedExt in installedExtensions) {
@ -96,12 +100,14 @@ internal class ExtensionGithubApi {
return "$REPO_URL/apk/${extension.apkName}"
}
// SY -->
fun Extension.isBlacklisted(
blacklistEnabled: Boolean =
preferences.eh_enableSourceBlacklist().get()
): Boolean {
return pkgName in BlacklistedSources.BLACKLISTED_EXTENSIONS && blacklistEnabled
}
// SY <--
companion object {
private const val REPO_URL = "https://raw.githubusercontent.com/inorichi/tachiyomi-extensions/repo"

View File

@ -20,7 +20,9 @@ sealed class Extension {
val hasUpdate: Boolean = false,
val isObsolete: Boolean = false,
val isUnofficial: Boolean = false,
// SY -->
val isRedundant: Boolean = false
// SY <--
) : Extension()
data class Available(

View File

@ -6,6 +6,7 @@ import java.util.concurrent.TimeUnit
import okhttp3.Cache
import okhttp3.OkHttpClient
// SY -->
open class NetworkHelper(context: Context) {
private val cacheDir = File(context.cacheDir, "network_cache")
@ -26,3 +27,4 @@ open class NetworkHelper(context: Context) {
.addInterceptor(CloudflareInterceptor(context))
.build()
}
// SY <--

View File

@ -47,3 +47,5 @@ interface Source {
}
fun Source.icon(): Drawable? = Injekt.get<ExtensionManager>().getAppIconForSource(this)
fun Source.getPreferenceKey(): String = "source_$id"

View File

@ -37,23 +37,27 @@ import uy.kohesive.injekt.injectLazy
open class SourceManager(private val context: Context) {
private val prefs: PreferencesHelper by injectLazy()
private val sourcesMap = mutableMapOf<Long, Source>()
private val stubSourcesMap = mutableMapOf<Long, StubSource>()
// SY -->
private val prefs: PreferencesHelper by injectLazy()
private val scope = CoroutineScope(Job() + Dispatchers.Main)
// SY <--
init {
createInternalSources().forEach { registerSource(it) }
// SY -->
// Recreate sources when they change
prefs.enableExhentai().asFlow().onEach {
createEHSources().forEach { registerSource(it) }
}.launchIn(scope)
registerSource(MergedSource())
// SY <--
}
open fun get(sourceKey: Long): Source? {
@ -70,9 +74,11 @@ open class SourceManager(private val context: Context) {
fun getCatalogueSources() = sourcesMap.values.filterIsInstance<CatalogueSource>()
// SY -->
fun getVisibleCatalogueSources() = sourcesMap.values.filterIsInstance<CatalogueSource>().filter {
it.id !in BlacklistedSources.HIDDEN_SOURCES
}
// SY <--
internal fun registerSource(source: Source, overwrite: Boolean = false) {
// EXH -->
@ -105,6 +111,7 @@ open class SourceManager(private val context: Context) {
LocalSource(context)
)
// SY -->
private fun createEHSources(): List<Source> {
val exSrcs = mutableListOf<HttpSource>(
EHentai(EH_SOURCE_ID, false, context)
@ -120,6 +127,7 @@ open class SourceManager(private val context: Context) {
exSrcs += HBrowse()
return exSrcs
}
// SY <--
private inner class StubSource(override val id: Long) : Source {
@ -147,6 +155,7 @@ open class SourceManager(private val context: Context) {
}
}
// SY -->
companion object {
val DELEGATED_SOURCES = listOf(
DelegatedSource(
@ -176,6 +185,7 @@ open class SourceManager(private val context: Context) {
val newSourceClass: KClass<out DelegatedHttpSource>
)
}
// SY <--
}
class SourceNotFoundException(message: String, val id: Long) : Exception(message)

View File

@ -6,7 +6,9 @@ import rx.subjects.Subject
open class Page(
val index: Int,
// SY -->
var url: String = "",
// SY <--
var imageUrl: String? = null,
@Transient var uri: Uri? = null // Deprecated but can't be deleted due to extensions
) : ProgressListener {

View File

@ -61,7 +61,9 @@ interface SManga : Serializable {
const val ONGOING = 1
const val COMPLETED = 2
const val LICENSED = 3
// SY -->
const val RECOMMENDS = 69 // nice
// SY <--
fun create(): SManga {
return SMangaImpl()

View File

@ -4,7 +4,9 @@ class SMangaImpl : SManga {
override lateinit var url: String
// SY -->
override var title: String = ""
// SY <--
override var artist: String? = null

View File

@ -34,6 +34,7 @@ abstract class HttpSource : CatalogueSource {
/**
* Network service.
*/
// SY -->
protected val network: NetworkHelper by lazy {
val original = Injekt.get<NetworkHelper>()
object : NetworkHelper(Injekt.get<Application>()) {
@ -53,12 +54,13 @@ abstract class HttpSource : CatalogueSource {
get() = original.cookieManager
}
}
// SY <--
// /**
// * Preferences that a source may need.
// */
// val preferences: SharedPreferences by lazy {
// Injekt.get<Application>().getSharedPreferences("source_$id", Context.MODE_PRIVATE)
// Injekt.get<Application>().getSharedPreferences(source.getPreferenceKey(), Context.MODE_PRIVATE)
// }
/**
@ -92,7 +94,9 @@ abstract class HttpSource : CatalogueSource {
* Default network client for doing requests.
*/
open val client: OkHttpClient
// SY -->
get() = delegate?.baseHttpClient ?: network.client
// SY <--
/**
* Headers builder for requests. Implementations can override this method for custom headers.
@ -323,7 +327,7 @@ abstract class HttpSource : CatalogueSource {
*
* @param page the page whose source image has to be downloaded.
*/
open fun fetchImage(page: Page): Observable<Response> {
/* SY --> */ open /* SY <-- */ fun fetchImage(page: Page): Observable<Response> {
return client.newCallWithProgress(imageRequest(page), page)
.asObservableSuccess()
}

View File

@ -4,7 +4,7 @@ import android.view.View
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.extensions.LayoutContainer
abstract class BaseViewHolder(view: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(view), LayoutContainer {
abstract class BaseViewHolder(view: View) : RecyclerView.ViewHolder(view), LayoutContainer {
override val containerView: View?
get() = itemView

View File

@ -101,7 +101,9 @@ class BrowseController :
activity?.tabs?.apply {
val updates = preferences.extensionUpdatesCount().get()
if (updates > 0) {
// SY -->
val badge: BadgeDrawable? = getTabAt(EXTENSIONS_CONTROLLER)?.orCreateBadge
// SY <--
badge?.isVisible = true
} else {
getTabAt(EXTENSIONS_CONTROLLER)?.removeBadge()
@ -111,6 +113,7 @@ class BrowseController :
private inner class BrowseAdapter : RouterPagerAdapter(this@BrowseController) {
// SY -->
private val tabTitles = (
if (preferences.latestTabInFront().get()) {
listOf(
@ -129,6 +132,7 @@ class BrowseController :
)
}
)
// SY <--
.map { resources!!.getString(it) }
override fun getCount(): Int {
@ -138,8 +142,10 @@ class BrowseController :
override fun configureRouter(router: Router, position: Int) {
if (!router.hasRootController()) {
val controller: Controller = when (position) {
// SY -->
SOURCES_CONTROLLER -> if (preferences.latestTabInFront().get()) LatestController() else SourceController()
LATEST_CONTROLLER -> if (!preferences.latestTabInFront().get()) LatestController() else SourceController()
// SY <--
EXTENSIONS_CONTROLLER -> ExtensionController()
MIGRATION_CONTROLLER -> MigrationSourcesController()
else -> error("Wrong position $position")
@ -157,8 +163,10 @@ class BrowseController :
const val TO_EXTENSIONS_EXTRA = "to_extensions"
const val SOURCES_CONTROLLER = 0
// SY -->
const val LATEST_CONTROLLER = 1
const val EXTENSIONS_CONTROLLER = 2
const val MIGRATION_CONTROLLER = 3
// SY <--
}
}

View File

@ -61,7 +61,7 @@ open class ExtensionPresenter(
val items = mutableListOf<ExtensionItem>()
val updatesSorted = installed.filter { it.hasUpdate }.sortedBy { it.pkgName }
val installedSorted = installed.filter { !it.hasUpdate }.sortedWith(compareBy({ !it.isObsolete && !it.isRedundant }, { it.pkgName }))
val installedSorted = installed.filter { !it.hasUpdate }.sortedWith(compareBy({ !it.isObsolete /* SY --> */ && !it.isRedundant /* SY <-- */ }, { it.pkgName }))
val untrustedSorted = untrusted.sortedBy { it.pkgName }
val availableSorted = available
// Filter out already installed extensions and disabled languages

View File

@ -4,7 +4,7 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.util.DeferredField
import exh.util.DeferredField
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
@ -23,7 +23,7 @@ class MigratingManga(
val migrationJob = parentContext + SupervisorJob() + Dispatchers.Default
var migrationStatus: Int = MigrationStatus.RUNNUNG
var migrationStatus: Int = MigrationStatus.RUNNING
@Volatile
private var manga: Manga? = null
@ -44,8 +44,8 @@ class MigratingManga(
class MigrationStatus {
companion object {
val RUNNUNG = 0
val MANGA_FOUND = 1
val MANGA_NOT_FOUND = 2
const val RUNNING = 0
const val MANGA_FOUND = 1
const val MANGA_NOT_FOUND = 2
}
}

View File

@ -89,7 +89,7 @@ class MigrationListController(bundle: Bundle? = null) :
override fun getTitle(): String? {
return resources?.getString(R.string.migration) + " (${adapter?.items?.count {
it.manga.migrationStatus != MigrationStatus.RUNNUNG
it.manga.migrationStatus != MigrationStatus.RUNNING
}}/${adapter?.itemCount ?: 0})"
}
@ -369,7 +369,7 @@ class MigrationListController(bundle: Bundle? = null) :
fun useMangaForMigration(manga: Manga, source: Source) {
val firstIndex = selectedPosition ?: return
val migratingManga = adapter?.getItem(firstIndex) ?: return
migratingManga.manga.migrationStatus = MigrationStatus.RUNNUNG
migratingManga.manga.migrationStatus = MigrationStatus.RUNNING
adapter?.notifyItemChanged(firstIndex)
launchUI {
val result = CoroutineScope(migratingManga.manga.migrationJob).async {

View File

@ -45,7 +45,7 @@ class MigrationProcessAdapter(
fun allMangasDone() = (
items.all {
it.manga.migrationStatus != MigrationStatus
.RUNNUNG
.RUNNING
} && items.any { it.manga.migrationStatus == MigrationStatus.MANGA_FOUND }
)

View File

@ -122,7 +122,7 @@ class MigrationProcessHolder(
}
withContext(Dispatchers.Main) {
if (item.manga.mangaId != this@MigrationProcessHolder.item?.manga?.mangaId ||
item.manga.migrationStatus == MigrationStatus.RUNNUNG
item.manga.migrationStatus == MigrationStatus.RUNNING
) {
return@withContext
}

View File

@ -18,7 +18,10 @@ class MangaItem(val manga: Manga) : AbstractFlexibleItem<MangaHolder>(), Parcela
}
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): MangaHolder {
return MangaHolder(view, adapter)
return MangaHolder(
view,
adapter
)
}
override fun bindViewHolder(

View File

@ -19,7 +19,9 @@ import uy.kohesive.injekt.api.get
class MigrationMangaController :
NucleusController<MigrationMangaControllerBinding, MigrationMangaPresenter>,
FlexibleAdapter.OnItemClickListener,
// SY -->
MigrationInterface {
// SY <--
private var adapter: FlexibleAdapter<IFlexible<*>>? = null
@ -72,20 +74,23 @@ class MigrationMangaController :
}
override fun onItemClick(view: View, position: Int): Boolean {
val item = adapter?.getItem(position) as? MangaItem
?: return false
val item = adapter?.getItem(position) as? MangaItem ?: return false
// SY -->
PreMigrationController.navigateToMigration(
Injekt.get<PreferencesHelper>().skipPreMigration().get(),
router,
listOf(item.manga.id!!)
)
// SY <--
return false
}
// SY -->
override fun migrateManga(prevManga: Manga, manga: Manga, replace: Boolean): Manga? {
presenter.migrateManga(prevManga, manga, replace)
return null
}
// SY <--
companion object {
const val SOURCE_ID_EXTRA = "source_id_extra"
@ -93,6 +98,8 @@ class MigrationMangaController :
}
}
// SY -->
interface MigrationInterface {
fun migrateManga(prevManga: Manga, manga: Manga, replace: Boolean): Manga?
}
// SY <--

View File

@ -38,6 +38,7 @@ class MigrationMangaPresenter(
.map { MangaItem(it) }
}
// SY -->
fun migrateManga(prevManga: Manga, manga: Manga, replace: Boolean) {
val source = sourceManager.get(manga.source) ?: return
@ -109,4 +110,5 @@ class MigrationMangaPresenter(
db.updateMangaTitle(manga).executeAsBlocking()
}
}
// SY <--
}

View File

@ -26,7 +26,9 @@ import uy.kohesive.injekt.api.get
class MigrationSourcesController :
NucleusController<MigrationSourcesControllerBinding, MigrationSourcesPresenter>(),
FlexibleAdapter.OnItemClickListener,
// SY -->
SourceAdapter.OnAllClickListener {
// SY <--
private var adapter: SourceAdapter? = null
@ -58,6 +60,7 @@ class MigrationSourcesController :
adapter?.updateDataSet(sourcesWithManga)
}
// SY -->
override fun getTitle(): String? {
return resources?.getString(R.string.source_migration)
}
@ -93,4 +96,5 @@ class MigrationSourcesController :
}
}
}
// SY <--
}

View File

@ -28,16 +28,12 @@ class MigrationSourcesPresenter(
}
private fun findSourcesWithManga(library: List<Manga>): List<SourceItem> {
val header =
SelectionHeader()
val header = SelectionHeader()
return library.map { it.source }.toSet()
// SY -->
.mapNotNull { if (it != LocalSource.ID && it != MERGED_SOURCE_ID) sourceManager.getOrStub(it) else null }
.sortedBy { it.name.toLowerCase(Locale.ROOT) }
.map {
SourceItem(
it,
header
)
}
// SY <--
.map { SourceItem(it, header) }
}
}

View File

@ -25,7 +25,10 @@ class SelectionHeader : AbstractHeaderItem<SelectionHeader.Holder>() {
* Creates a new view holder for this item.
*/
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): Holder {
return Holder(view, adapter)
return Holder(
view,
adapter
)
}
/**
@ -35,14 +38,16 @@ class SelectionHeader : AbstractHeaderItem<SelectionHeader.Holder>() {
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
holder: Holder,
position: Int,
// SY -->
payloads: MutableList<Any?>?
// SY <--
) {
// Intentionally empty
}
class Holder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>) : BaseFlexibleViewHolder(view, adapter) {
class Holder(view: View, adapter: FlexibleAdapter</* SY --> */ IFlexible<RecyclerView.ViewHolder> /* SY <-- */ >) : BaseFlexibleViewHolder(view, adapter) {
init {
title.text = view.context.getString(R.string.select_a_source_to_migrate_from)
title.text = view.context.getString(/* SY --> */ R.string.select_a_source_to_migrate_from /* SY <-- */)
}
}

View File

@ -19,6 +19,7 @@ class SourceAdapter(val controller: MigrationSourcesController) :
setDisplayHeadersAtStartUp(true)
}
// SY -->
/**
* Listener for auto item clicks.
*/
@ -30,4 +31,5 @@ class SourceAdapter(val controller: MigrationSourcesController) :
interface OnAllClickListener {
fun onAllClick(position: Int)
}
// SY <--
}

View File

@ -24,11 +24,13 @@ class SourceHolder(view: View, override val adapter: SourceAdapter) :
get() = card
init {
source_browse.gone()
source_latest.text = "All"
source_latest.setOnClickListener {
source_latest.gone()
// SY -->
source_browse.text = "All"
source_browse.setOnClickListener {
adapter.allClickListener?.onAllClick(bindingAdapterPosition)
}
// SY <--
}
fun bind(item: SourceItem) {

View File

@ -28,7 +28,10 @@ data class SourceItem(val source: Source, val header: SelectionHeader) :
* Creates a new view holder for this item.
*/
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): SourceHolder {
return SourceHolder(view, adapter as SourceAdapter)
return SourceHolder(
view,
adapter as SourceAdapter
)
}
/**

View File

@ -70,18 +70,22 @@ class SourceController(bundle: Bundle? = null) :
// EXH <--
init {
// Enable the option menu
// SY -->
setHasOptionsMenu(mode == Mode.CATALOGUE)
// SY <--
}
override fun getTitle(): String? {
// SY -->
return when (mode) {
Mode.CATALOGUE -> applicationContext?.getString(R.string.label_sources)
Mode.SMART_SEARCH -> "Find in another source"
}
// SY <--
}
override fun createPresenter(): SourcePresenter {
return SourcePresenter(controllerMode = mode)
return SourcePresenter(/* SY --> */ controllerMode = mode /* SY <-- */)
}
/**
@ -109,6 +113,7 @@ class SourceController(bundle: Bundle? = null) :
requestPermissionsSafe(arrayOf(WRITE_EXTERNAL_STORAGE), 301)
// SY -->
if (mode == Mode.CATALOGUE) {
// Update list on extension changes (e.g. new installation)
(parentController as BrowseController).extensionListUpdateRelay
@ -116,6 +121,7 @@ class SourceController(bundle: Bundle? = null) :
presenter.updateSources()
}
}
// SY <--
}
override fun onDestroyView(view: View) {
@ -138,6 +144,7 @@ class SourceController(bundle: Bundle? = null) :
private fun onItemClick(position: Int) {
val item = adapter?.getItem(position) as? SourceItem ?: return
val source = item.source
// SY -->
when (mode) {
Mode.CATALOGUE -> {
// Open the catalogue view.
@ -152,6 +159,7 @@ class SourceController(bundle: Bundle? = null) :
).withFadeTransaction()
)
}
// SY <--
}
override fun onItemLongClick(position: Int) {
@ -170,6 +178,7 @@ class SourceController(bundle: Bundle? = null) :
items.add(Pair(activity.getString(R.string.action_hide), { hideCatalogue(item.source) }))
}
// SY -->
val isWatched = preferences.latestTabSources().get().contains(item.source.id.toString())
if (item.source.supportsLatest) {
@ -187,6 +196,7 @@ class SourceController(bundle: Bundle? = null) :
{ addToCategories(item.source) }
)
)
// SY <--
MaterialDialog(activity)
.title(text = item.source.name)
@ -218,6 +228,7 @@ class SourceController(bundle: Bundle? = null) :
presenter.updateSources()
}
// SY -->
private fun watchCatalogue(source: Source, isWatched: Boolean) {
val current = preferences.latestTabSources().get()
@ -278,6 +289,7 @@ class SourceController(bundle: Bundle? = null) :
)
presenter.updateSources()
}
// SY <--
/**
* Called when browse is clicked in [SourceAdapter]
@ -312,10 +324,12 @@ class SourceController(bundle: Bundle? = null) :
// Inflate menu
inflater.inflate(R.menu.source_main, menu)
// SY -->
if (mode == Mode.SMART_SEARCH) {
menu.findItem(R.id.action_search).isVisible = false
menu.findItem(R.id.action_settings).isVisible = false
}
// SY <--
// Initialize search option.
val searchItem = menu.findItem(R.id.action_search)
@ -375,6 +389,7 @@ class SourceController(bundle: Bundle? = null) :
}
}
// SY -->
@Parcelize
data class SmartSearchConfig(val origTitle: String, val origMangaId: Long? = null) : Parcelable
@ -386,4 +401,5 @@ class SourceController(bundle: Bundle? = null) :
companion object {
const val SMART_SEARCH_CONFIG = "SMART_SEARCH_CONFIG"
}
// SY <--
}

View File

@ -15,7 +15,7 @@ import kotlinx.android.synthetic.main.source_main_controller_card_item.source_br
import kotlinx.android.synthetic.main.source_main_controller_card_item.source_latest
import kotlinx.android.synthetic.main.source_main_controller_card_item.title
class SourceHolder(view: View, override val adapter: SourceAdapter, val showButtons: Boolean) :
class SourceHolder(view: View, override val adapter: SourceAdapter /* SY --> */, val showButtons: Boolean /* SY <-- */) :
BaseFlexibleViewHolder(view, adapter),
SlicedHolder {
@ -35,10 +35,12 @@ class SourceHolder(view: View, override val adapter: SourceAdapter, val showButt
adapter.latestClickListener.onLatestClick(bindingAdapterPosition)
}
// SY -->
if (!showButtons) {
source_browse.gone()
source_latest.gone()
}
// SY <--
}
fun bind(item: SourceItem) {
@ -58,7 +60,7 @@ class SourceHolder(view: View, override val adapter: SourceAdapter, val showButt
}
source_browse.setText(R.string.browse)
if (source.supportsLatest && showButtons) {
if (source.supportsLatest /* SY --> */ && showButtons /* SY <-- */) {
source_latest.visible()
} else {
source_latest.gone()

View File

@ -14,7 +14,7 @@ import eu.kanade.tachiyomi.source.CatalogueSource
* @param source Instance of [CatalogueSource] containing source information.
* @param header The header for this item.
*/
data class SourceItem(val source: CatalogueSource, val header: LangItem? = null, val showButtons: Boolean) :
data class SourceItem(val source: CatalogueSource, val header: LangItem? = null /* SY --> */, val showButtons: Boolean /* SY <-- */) :
AbstractSectionableItem<SourceHolder, LangItem>(header) {
/**
@ -28,7 +28,7 @@ data class SourceItem(val source: CatalogueSource, val header: LangItem? = null,
* Creates a new view holder for this item.
*/
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): SourceHolder {
return SourceHolder(view, adapter as SourceAdapter, showButtons)
return SourceHolder(view, adapter as SourceAdapter /* SY --> */, showButtons /* SY <-- */)
}
/**

View File

@ -32,7 +32,9 @@ import uy.kohesive.injekt.api.get
class SourcePresenter(
val sourceManager: SourceManager = Injekt.get(),
private val preferences: PreferencesHelper = Injekt.get(),
// SY -->
private val controllerMode: SourceController.Mode
// SY <--
) : BasePresenter<SourceController>() {
private val scope = CoroutineScope(Job() + Dispatchers.Main)
@ -60,6 +62,8 @@ class SourcePresenter(
val pinnedSources = mutableListOf<SourceItem>()
val pinnedCatalogues = preferences.pinnedCatalogues().get()
// SY -->
val categories = mutableListOf<SourceCategory>()
preferences.sourcesTabCategories().get().sortedByDescending { it.toLowerCase() }.forEach {
@ -73,6 +77,7 @@ class SourcePresenter(
} else null
val sourcesInCategories = sourcesAndCategories?.map { it.first }
// SY <--
val map = TreeMap<String, MutableList<CatalogueSource>> { d1, d2 ->
// Catalogues without a lang defined will be placed at the end
@ -90,6 +95,7 @@ class SourcePresenter(
pinnedSources.add(SourceItem(source, LangItem(PINNED_KEY), controllerMode == SourceController.Mode.CATALOGUE))
}
// SY -->
if (sourcesInCategories != null && source.id.toString() in sourcesInCategories) {
sourcesAndCategories
.filter { SourcesAndCategory -> SourcesAndCategory.first == source.id.toString() }
@ -107,14 +113,17 @@ class SourcePresenter(
}
}
}
// SY <--
SourceItem(source, langItem, controllerMode == SourceController.Mode.CATALOGUE)
}
}
// SY -->
categories.forEach {
sourceItems = it.sources.sortedBy { sourceItem -> sourceItem.source.name.toLowerCase() } + sourceItems
}
// SY <--
if (pinnedSources.isNotEmpty()) {
sourceItems = pinnedSources + sourceItems
@ -170,4 +179,6 @@ class SourcePresenter(
}
}
// SY -->
data class SourceCategory(val category: String, var sources: MutableList<SourceItem> = mutableListOf())
// SY <--

View File

@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.ui.browse.source.browse
import android.content.res.Configuration
import android.os.Bundle
import android.os.Parcelable
import android.util.Log
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
@ -81,7 +80,9 @@ open class BrowseSourceController(bundle: Bundle) :
constructor(
source: CatalogueSource,
searchQuery: String? = null,
// SY -->
smartSearchConfig: SourceController.SmartSearchConfig? = null
// SY <--
) : this(
Bundle().apply {
putLong(SOURCE_ID_KEY, source.id)
@ -90,15 +91,19 @@ open class BrowseSourceController(bundle: Bundle) :
putString(SEARCH_QUERY_KEY, searchQuery)
}
// SY -->
if (smartSearchConfig != null) {
putParcelable(SMART_SEARCH_CONFIG_KEY, smartSearchConfig)
}
// SY <--
}
)
private val preferences: PreferencesHelper by injectLazy()
// SY -->
private val recommendsConfig: RecommendsConfig? = args.getParcelable(RECOMMENDS_CONFIG)
// SY <--
// AZ -->
private val mode = if (recommendsConfig == null) Mode.CATALOGUE else Mode.RECOMMENDS
@ -138,19 +143,23 @@ open class BrowseSourceController(bundle: Bundle) :
}
override fun getTitle(): String? {
// SY -->
return when (mode) {
Mode.CATALOGUE -> presenter.source.name
Mode.RECOMMENDS -> recommendsConfig!!.manga.title
}
// SY <--
}
override fun createPresenter(): BrowseSourcePresenter {
// SY -->
return BrowseSourcePresenter(
args.getLong(SOURCE_ID_KEY),
args.getString(SEARCH_QUERY_KEY),
searchManga = if (mode == Mode.RECOMMENDS) recommendsConfig?.manga else null,
recommends = (mode == Mode.RECOMMENDS)
)
// SY <--
}
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
@ -172,12 +181,16 @@ open class BrowseSourceController(bundle: Bundle) :
}
open fun initFilterSheet() {
// SY -->
if (mode == Mode.RECOMMENDS) {
return
}
// SY <--
if (presenter.sourceFilters.isEmpty()) {
// SY -->
binding.fabFilter.text = activity!!.getString(R.string.eh_saved_searches)
// SY <--
}
filterSheet = SourceFilterSheet(
@ -373,9 +386,11 @@ open class BrowseSourceController(bundle: Bundle) :
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.source_browse, menu)
// SY -->
if (mode == Mode.RECOMMENDS) {
menu.findItem(R.id.action_search).isVisible = false
}
// SY <--
// Initialize search menu
val searchItem = menu.findItem(R.id.action_search)
@ -418,9 +433,11 @@ open class BrowseSourceController(bundle: Bundle) :
menu.findItem(R.id.action_open_in_web_view).isVisible = isHttpSource
val isLocalSource = presenter.source is LocalSource
// SY -->
menu.findItem(R.id.action_local_source_help).isVisible = isLocalSource && mode == Mode.CATALOGUE
menu.findItem(R.id.action_settings).isVisible = presenter.source is ConfigurableSource
// SY <--
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -430,7 +447,9 @@ open class BrowseSourceController(bundle: Bundle) :
R.id.action_comfortable_grid -> setDisplayMode(DisplayMode.COMFORTABLE_GRID)
R.id.action_list -> setDisplayMode(DisplayMode.LIST)
R.id.action_open_in_web_view -> openInWebView()
// SY -->
R.id.action_settings -> openSourceSettings()
// SY <--
R.id.action_local_source_help -> openLocalSourceHelpGuide()
}
return super.onOptionsItemSelected(item)
@ -448,11 +467,13 @@ open class BrowseSourceController(bundle: Bundle) :
activity?.openInBrowser(LocalSource.HELP_URL)
}
// SY -->
private fun openSourceSettings() {
router.pushController(
SourcePreferencesController(presenter.source.id).withFadeTransaction()
)
}
// SY <--
/**
* Restarts the request with a new query.
@ -493,6 +514,7 @@ open class BrowseSourceController(bundle: Bundle) :
* @param error the error received.
*/
fun onAddPageError(error: Throwable) {
// SY -->
XLog.w("> Failed to load next catalogue page!", error)
if (mode == Mode.CATALOGUE) {
@ -504,6 +526,7 @@ open class BrowseSourceController(bundle: Bundle) :
} else {
XLog.w("> Recommendations")
}
// SY <--
val adapter = adapter ?: return
adapter.onLoadMoreComplete(null)
@ -523,10 +546,9 @@ open class BrowseSourceController(bundle: Bundle) :
}
if (adapter.isEmpty) {
Log.d("Adapter", "empty")
val actions = emptyList<EmptyView.Action>().toMutableList()
if (presenter.source is LocalSource && mode == Mode.CATALOGUE) {
if (presenter.source is LocalSource /* SY --> */ && mode == Mode.CATALOGUE /* SY <-- */) {
actions += EmptyView.Action(R.string.local_source_help_guide, View.OnClickListener { openLocalSourceHelpGuide() })
} else {
actions += EmptyView.Action(R.string.action_retry, retryAction)
@ -669,7 +691,7 @@ open class BrowseSourceController(bundle: Bundle) :
*/
override fun onItemClick(view: View, position: Int): Boolean {
val item = adapter?.getItem(position) as? SourceItem ?: return false
// SY -->
when (mode) {
Mode.CATALOGUE -> {
if (preferences.eh_useNewMangaInterface().get()) {
@ -692,6 +714,7 @@ open class BrowseSourceController(bundle: Bundle) :
}
Mode.RECOMMENDS -> openSmartSearch(item.manga.title)
}
// SY <--
return false
}
@ -718,7 +741,9 @@ open class BrowseSourceController(bundle: Bundle) :
* @param position the position of the element clicked.
*/
override fun onItemLongClick(position: Int) {
if (mode == Mode.RECOMMENDS) { return }
// SY -->
if (mode == Mode.RECOMMENDS) return
// SY <--
val activity = activity ?: return
val manga = (adapter?.getItem(position) as? SourceItem?)?.manga ?: return
@ -794,6 +819,7 @@ open class BrowseSourceController(bundle: Bundle) :
activity?.toast(activity?.getString(R.string.manga_added_library))
}
// SY -->
@Parcelize
data class RecommendsConfig(val manga: Manga) : Parcelable
@ -801,13 +827,15 @@ open class BrowseSourceController(bundle: Bundle) :
CATALOGUE,
RECOMMENDS
}
// SY <--
companion object {
const val SOURCE_ID_KEY = "sourceId"
const val SEARCH_QUERY_KEY = "searchQuery"
const val SMART_SEARCH_CONFIG_KEY = "smartSearchConfig"
// SY -->
const val SMART_SEARCH_CONFIG_KEY = "smartSearchConfig"
const val RECOMMENDS_CONFIG = "RECOMMENDS_CONFIG"
// SY <--
}
}

View File

@ -59,7 +59,9 @@ open class BrowseSourcePresenter(
private val db: DatabaseHelper = Injekt.get(),
private val prefs: PreferencesHelper = Injekt.get(),
private val coverCache: CoverCache = Injekt.get(),
// SY -->
private val recommends: Boolean = false
// SY <--
) : BasePresenter<BrowseSourceController>() {
/**
@ -70,7 +72,7 @@ open class BrowseSourcePresenter(
/**
* Query from the view.
*/
var query = if (recommends) "" else searchQuery ?: ""
var query = /* SY --> */ if (recommends) "" else /* SY <-- */ searchQuery ?: ""
private set
/**
@ -146,9 +148,11 @@ open class BrowseSourcePresenter(
subscribeToMangaInitializer()
// Create a new pager.
// SY -->
pager = if (recommends && searchManga != null) RecommendsPager(
searchManga
) else createPager(query, filters)
// SY <--
val sourceId = source.id

View File

@ -45,13 +45,9 @@ class SourceFilterSheet(
// EXH -->
filterNavView.onSaveClicked = onSaveClicked
// EXH <--
// EXH -->
filterNavView.onSavedSearchClicked = onSavedSearchClicked
// EXH <--
// EXH -->
filterNavView.onSavedSearchDeleteClicked = onSavedSearchDeleteClicked
// EXH <--
@ -62,6 +58,7 @@ class SourceFilterSheet(
filterNavView.adapter.updateDataSet(items)
}
// SY -->
fun setSavedSearches(searches: List<EXHSavedSearch>) {
filterNavView.setSavedSearches(searches)
}
@ -69,19 +66,16 @@ class SourceFilterSheet(
fun hideFilterButton() {
filterNavView.hideFilterButton()
}
// SY <--
class FilterNavigationView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
SimpleNavigationView(context, attrs) {
// EXH -->
var onSaveClicked = {}
// EXH <--
// EXH -->
var onSavedSearchClicked: (Int) -> Unit = {}
// EXH <--
// EXH -->
var onSavedSearchDeleteClicked: (Int, String) -> Unit = { _, _ -> }
// EXH <--
@ -96,9 +90,13 @@ class SourceFilterSheet(
recycler.adapter = adapter
recycler.setHasFixedSize(true)
val view = inflate(R.layout.source_filter_sheet)
// SY -->
((view as ViewGroup).findViewById(R.id.source_filter_content) as ViewGroup).addView(recycler)
// SY <--
addView(view)
// SY -->
save_search_btn.setOnClickListener { onSaveClicked() }
// SY <--
filter_btn.setOnClickListener { onFilterClicked() }
reset_btn.setOnClickListener { onResetClicked() }
}

View File

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.browse.source.filter
import android.view.View
import android.widget.CheckBox
import androidx.recyclerview.widget.RecyclerView
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IFlexible
@ -15,11 +16,11 @@ open class CheckboxItem(val filter: Filter.CheckBox) : AbstractFlexibleItem<Chec
return R.layout.navigation_view_checkbox
}
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<androidx.recyclerview.widget.RecyclerView.ViewHolder>>): Holder {
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): Holder {
return Holder(view, adapter)
}
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<androidx.recyclerview.widget.RecyclerView.ViewHolder>>, holder: Holder, position: Int, payloads: List<Any?>?) {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>, holder: Holder, position: Int, payloads: List<Any?>?) {
val view = holder.check
view.text = filter.name
view.isChecked = filter.state

View File

@ -4,6 +4,7 @@ import android.view.View
import android.widget.ArrayAdapter
import android.widget.Spinner
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IFlexible
@ -18,11 +19,11 @@ open class SelectItem(val filter: Filter.Select<*>) : AbstractFlexibleItem<Selec
return R.layout.navigation_view_spinner
}
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<androidx.recyclerview.widget.RecyclerView.ViewHolder>>): Holder {
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): Holder {
return Holder(view, adapter)
}
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<androidx.recyclerview.widget.RecyclerView.ViewHolder>>, holder: Holder, position: Int, payloads: List<Any?>?) {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>, holder: Holder, position: Int, payloads: List<Any?>?) {
holder.text.text = filter.name + ": "
val spinner = holder.spinner

View File

@ -15,10 +15,12 @@ import uy.kohesive.injekt.api.get
class GlobalSearchCardItem(val manga: Manga) : AbstractFlexibleItem<GlobalSearchCardHolder>() {
override fun getLayoutRes(): Int {
// SY -->
return when (Injekt.get<PreferencesHelper>().catalogueDisplayMode().get()) {
PreferenceValues.DisplayMode.COMPACT_GRID -> R.layout.global_search_controller_compact_card_item
else -> R.layout.global_search_controller_comfortable_card_item
}
// SY <--
}
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): GlobalSearchCardHolder {

View File

@ -77,11 +77,13 @@ open class GlobalSearchController(
*/
override fun onMangaClick(manga: Manga) {
// Open MangaController.
// SY -->
if (presenter.preferences.eh_useNewMangaInterface().get()) {
router.pushController(MangaAllInOneController(manga, true).withFadeTransaction())
} else {
router.pushController(MangaController(manga, true).withFadeTransaction())
}
// SY <--
}
/**

View File

@ -49,7 +49,9 @@ class LibraryCategoryAdapter(view: LibraryCategoryView) :
*/
private var mangas: List<LibraryItem> = emptyList()
// SY -->
val onItemReleaseListener: CategoryAdapter.OnItemReleaseListener = view
// SY <--
/**
* Sets a list of manga in the adapter.

View File

@ -47,8 +47,10 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
FrameLayout(context, attrs),
FlexibleAdapter.OnItemClickListener,
FlexibleAdapter.OnItemLongClickListener,
// SY -->
FlexibleAdapter.OnItemMoveListener,
CategoryAdapter.OnItemReleaseListener {
// SY <--
private val scope = CoroutineScope(Job() + Dispatchers.Main)
@ -142,8 +144,10 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
} else {
SelectableAdapter.Mode.SINGLE
}
// SY -->
val sortingMode = preferences.librarySortingMode().get()
adapter.isLongPressDragEnabled = sortingMode == LibrarySort.DRAG_AND_DROP
// SY <--
// EXH -->
scope2 = newScope()
@ -206,6 +210,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
controller.invalidateActionMode()
}
// SY -->
subscriptions += controller.reorganizeRelay
.subscribe {
if (it.first == category.id) {
@ -230,10 +235,13 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
controller.invalidateActionMode()
}
// }
// SY <--
}
fun onRecycle() {
// SY -->
runBlocking { adapter.setItems(this, emptyList()) }
// SY <--
adapter.clearSelection()
unsubscribe()
}
@ -254,7 +262,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
*/
suspend fun onNextLibraryManga(cScope: CoroutineScope, event: LibraryMangaEvent) {
// Get the manga list for this category.
// SY -->
val sortingMode = preferences.librarySortingMode().get()
adapter.isLongPressDragEnabled = sortingMode == LibrarySort.DRAG_AND_DROP
var mangaForCategory = event.getMangaForCategory(category).orEmpty()
@ -270,6 +278,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
)
}
}
// SY <--
// Update the category with its manga.
// EXH -->
adapter.setItems(cScope, mangaForCategory)
@ -297,7 +306,9 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
is LibrarySelectionEvent.Selected -> {
if (adapter.mode != SelectableAdapter.Mode.MULTI) {
adapter.mode = SelectableAdapter.Mode.MULTI
// SY -->
adapter.isLongPressDragEnabled = false
// SY <--
}
findAndToggleSelection(event.manga)
}
@ -306,16 +317,20 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
if (adapter.indexOf(event.manga) != -1) lastClickPosition = -1
if (controller.selectedMangas.isEmpty()) {
adapter.mode = SelectableAdapter.Mode.SINGLE
// SY -->
adapter.isLongPressDragEnabled = preferences.librarySortingMode()
.get() == LibrarySort.DRAG_AND_DROP
// SY <--
}
}
is LibrarySelectionEvent.Cleared -> {
adapter.mode = SelectableAdapter.Mode.SINGLE
adapter.clearSelection()
lastClickPosition = -1
// SY -->
adapter.isLongPressDragEnabled = preferences.librarySortingMode()
.get() == LibrarySort.DRAG_AND_DROP
// SY <--
}
}
}
@ -359,7 +374,9 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
*/
override fun onItemLongClick(position: Int) {
controller.createActionModeIfNeeded()
// SY -->
adapter.isLongPressDragEnabled = false
// SY <--
when {
lastClickPosition == -1 -> setSelection(position)
lastClickPosition > position ->
@ -372,7 +389,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
}
lastClickPosition = position
}
// SY -->
override fun onItemMove(fromPosition: Int, toPosition: Int) {
}
@ -405,6 +422,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
onItemLongClick(position)
}
}
// SY <--
/**
* Opens a manga.

View File

@ -28,7 +28,9 @@ import kotlinx.android.synthetic.main.source_compact_grid_item.unread_text
*/
open class LibraryCompactGridHolder(
private val view: View,
// SY -->
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>
// SY <--
) : LibraryHolder(view, adapter) {
/**

View File

@ -125,10 +125,12 @@ class LibraryController(
*/
val selectInverseRelay: PublishRelay<Int> = PublishRelay.create()
// SY -->
/**
* Relay to notify the library's viewpager to reotagnize all
*/
val reorganizeRelay: PublishRelay<Pair<Int, Int>> = PublishRelay.create()
// SY <--
/**
* Number of manga per row in grid mode.
@ -349,7 +351,9 @@ class LibraryController(
* Called when the sorting mode is changed.
*/
private fun onSortChanged() {
// SY -->
activity?.invalidateOptionsMenu()
// SY <--
presenter.requestSortUpdate()
}
@ -391,8 +395,10 @@ class LibraryController(
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.library, menu)
// SY -->
val reorganizeItem = menu.findItem(R.id.action_reorganize)
reorganizeItem.isVisible = preferences.librarySortingMode().get() == LibrarySort.DRAG_AND_DROP
// SY <--
val searchItem = menu.findItem(R.id.action_search)
val searchView = searchItem.actionView as SearchView
@ -421,7 +427,9 @@ class LibraryController(
// Mutate the filter icon because it needs to be tinted and the resource is shared.
menu.findItem(R.id.action_filter).icon.mutate()
// SY -->
menu.findItem(R.id.action_sync_favorites).isVisible = preferences.eh_isHentaiEnabled().get()
// SY <--
}
fun search(query: String) {
@ -451,10 +459,10 @@ class LibraryController(
}
}
}
// SY -->
R.id.action_source_migration -> {
router.pushController(MigrationSourcesController().withFadeTransaction())
}
// --> EXH
R.id.action_sync_favorites -> {
if (preferences.eh_showSyncIntro().get()) {
activity?.let { FavoritesIntroDialog().show(it) }
@ -462,21 +470,23 @@ class LibraryController(
presenter.favoritesSync.runSync()
}
}
// <-- EXH
R.id.action_alpha_asc -> reOrder(1)
R.id.action_alpha_dsc -> reOrder(2)
R.id.action_update_asc -> reOrder(3)
R.id.action_update_dsc -> reOrder(4)
// SY <--
}
return super.onOptionsItemSelected(item)
}
// SY -->
private fun reOrder(type: Int) {
adapter?.categories?.getOrNull(binding.libraryPager.currentItem)?.id?.let {
reorganizeRelay.call(it to type)
}
}
// SY <--
/**
* Invalidates the action mode, forcing it to refresh its content.
@ -514,11 +524,13 @@ class LibraryController(
R.id.action_delete -> showDeleteMangaDialog()
R.id.action_select_all -> selectAllCategoryManga()
R.id.action_select_inverse -> selectInverseCategoryManga()
// SY -->
R.id.action_migrate -> {
val skipPre = preferences.skipPreMigration().get()
PreMigrationController.navigateToMigration(skipPre, router, selectedMangas.mapNotNull { it.id })
destroyActionModeIfNeeded()
}
// SY <--
else -> return false
}
return true
@ -539,11 +551,13 @@ class LibraryController(
// Notify the presenter a manga is being opened.
presenter.onOpenManga()
// SY -->
if (preferences.eh_useNewMangaInterface().get()) {
router.pushController(MangaAllInOneController(manga).withFadeTransaction())
} else {
router.pushController(MangaController(manga).withFadeTransaction())
}
// SY <--
}
/**
@ -652,6 +666,7 @@ class LibraryController(
destroyActionModeIfNeeded()
}
// SY -->
override fun onAttach(view: View) {
super.onAttach(view)
@ -673,6 +688,7 @@ class LibraryController(
// EXH
cleanupSyncState()
}
// SY <--
private fun selectAllCategoryManga() {
adapter?.categories?.getOrNull(binding.libraryPager.currentItem)?.id?.let {

View File

@ -15,7 +15,9 @@ import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
abstract class LibraryHolder(
view: View,
// SY -->
val adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>
// SY <--
) : BaseFlexibleViewHolder(view, adapter) {
/**
@ -26,6 +28,7 @@ abstract class LibraryHolder(
*/
abstract fun onSetValues(item: LibraryItem)
// SY -->
/**
* Called when an item is released.
*
@ -35,4 +38,5 @@ abstract class LibraryHolder(
super.onItemReleased(position)
(adapter as? LibraryCategoryAdapter)?.onItemReleaseListener?.onItemReleased(position)
}
// SY <--
}

View File

@ -28,8 +28,10 @@ class LibraryItem(val manga: LibraryManga, private val libraryDisplayMode: Prefe
AbstractFlexibleItem<LibraryHolder>(), IFilterable<String> {
private val sourceManager: SourceManager = Injekt.get()
// SY -->
private val trackManager: TrackManager = Injekt.get()
private val db: DatabaseHelper = Injekt.get()
// SY <--
var downloadCount = -1
var unreadCount = -1
@ -80,6 +82,7 @@ class LibraryItem(val manga: LibraryManga, private val libraryDisplayMode: Prefe
holder.onSetValues(this)
}
// SY -->
/**
* Returns true if this item is draggable.
*/
@ -138,6 +141,7 @@ class LibraryItem(val manga: LibraryManga, private val libraryDisplayMode: Prefe
return@any false
}
}
// SY <--
private fun containsGenre(tag: String, genres: List<String>?): Boolean {
return if (tag.startsWith("-")) {

View File

@ -32,7 +32,9 @@ import kotlinx.android.synthetic.main.source_list_item.unread_text
class LibraryListHolder(
private val view: View,
// SY -->
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>
// SY <--
) : LibraryHolder(view, adapter) {
/**

View File

@ -120,6 +120,7 @@ class LibraryPresenter(
}
}
// SY -->
/**
* Applies library filters to the given map of manga.
*
@ -171,6 +172,7 @@ class LibraryPresenter(
return map.mapValues { entry -> entry.value.filter(filterFn) }
}
// SY <--
/**
* Sets downloaded chapter count to each manga.
@ -244,9 +246,11 @@ class LibraryPresenter(
?: latestChapterManga.size
manga1latestChapter.compareTo(manga2latestChapter)
}
// SY -->
LibrarySort.DRAG_AND_DROP -> {
0
}
// SY <--
else -> throw Exception("Unknown sorting mode")
}
}
@ -393,6 +397,7 @@ class LibraryPresenter(
db.setMangaCategories(mc, mangas)
}
// SY -->
fun migrateManga(prevManga: Manga, manga: Manga, replace: Boolean) {
val source = sourceManager.get(manga.source) ?: return
@ -469,6 +474,7 @@ class LibraryPresenter(
db.updateMangaTitle(manga).executeAsBlocking()
}
}
// SY <--
/**
* Update cover with local file.

View File

@ -65,18 +65,23 @@ class LibrarySettingsSheet(
* Returns true if there's at least one filter from [FilterGroup] active.
*/
fun hasActiveFilters(): Boolean {
// SY -->
return filterGroup.items.any { it.state != STATE_IGNORE }
// SY <--
}
inner class FilterGroup : Group {
// SY -->
private val downloaded = Item.TriStateGroup(R.string.action_filter_downloaded, this)
private val unread = Item.TriStateGroup(R.string.action_filter_unread, this)
private val completed = Item.TriStateGroup(R.string.completed, this)
private val tracked = Item.TriStateGroup(R.string.tracked, this)
private val lewd = Item.TriStateGroup(R.string.lewd, this)
// SY <--
override val header = null
// SY -->
override val items = (
if (Injekt.get<TrackManager>().hasLoggedServices()) {
listOf(downloaded, unread, completed, tracked, lewd)
@ -84,8 +89,10 @@ class LibrarySettingsSheet(
listOf(downloaded, unread, completed, lewd)
}
)
// SY <--
override val footer = null
// SY -->
override fun initModels() { // j2k changes
try {
downloaded.state = preferences.filterDownloaded().get()
@ -120,6 +127,7 @@ class LibrarySettingsSheet(
adapter.notifyItemChanged(item)
}
// SY <--
}
}
@ -141,11 +149,13 @@ class LibrarySettingsSheet(
private val lastChecked = Item.MultiSort(R.string.action_sort_last_checked, this)
private val unread = Item.MultiSort(R.string.action_filter_unread, this)
private val latestChapter = Item.MultiSort(R.string.action_sort_latest_chapter, this)
// SY -->
private val dragAndDrop = Item.MultiSort(R.string.action_sort_drag_and_drop, this)
// SY <--
override val header = null
override val items =
listOf(alphabetically, lastRead, lastChecked, unread, total, latestChapter, dragAndDrop)
listOf(alphabetically, lastRead, lastChecked, unread, total, latestChapter /* SY --> */, dragAndDrop /* SY <-- */)
override val footer = null
override fun initModels() {
@ -167,7 +177,9 @@ class LibrarySettingsSheet(
total.state = if (sorting == LibrarySort.TOTAL) order else Item.MultiSort.SORT_NONE
latestChapter.state =
if (sorting == LibrarySort.LATEST_CHAPTER) order else Item.MultiSort.SORT_NONE
// SY -->
dragAndDrop.state = if (sorting == LibrarySort.DRAG_AND_DROP) order else Item.MultiSort.SORT_NONE
// SY <--
}
override fun onItemClicked(item: Item) {
@ -178,6 +190,7 @@ class LibrarySettingsSheet(
(it as Item.MultiStateGroup).state =
Item.MultiSort.SORT_NONE
}
// SY -->
if (item == dragAndDrop) {
item.state = Item.MultiSort.SORT_ASC
} else {
@ -188,6 +201,7 @@ class LibrarySettingsSheet(
else -> throw Exception("Unknown state")
}
}
// SY <--
preferences.librarySortingMode().set(
when (item) {
@ -197,7 +211,9 @@ class LibrarySettingsSheet(
unread -> LibrarySort.UNREAD
total -> LibrarySort.TOTAL
latestChapter -> LibrarySort.LATEST_CHAPTER
// SY -->
dragAndDrop -> LibrarySort.DRAG_AND_DROP
// SY <--
else -> throw Exception("Unknown sorting")
}
)

View File

@ -8,7 +8,9 @@ object LibrarySort {
const val UNREAD = 3
const val TOTAL = 4
const val LATEST_CHAPTER = 6
// SY -->
const val DRAG_AND_DROP = 7
// SY <--
@Deprecated("Removed in favor of searching by source")
const val SOURCE = 5

View File

@ -18,7 +18,7 @@ class ChangelogDialogController : DialogController() {
val activity = activity!!
val view = WhatsNewRecyclerView(activity)
return MaterialDialog(activity)
.title(res = if (BuildConfig.DEBUG || syDebugVersion != "0") R.string.notices else R.string.changelog)
.title(res = if (BuildConfig.DEBUG /* SY --> */ || syDebugVersion != "0" /* SY <-- */) R.string.notices else R.string.changelog)
.customView(view = view)
.positiveButton(R.string.action_close)
}
@ -27,7 +27,7 @@ class ChangelogDialogController : DialogController() {
override fun initAttrs(attrs: AttributeSet?, defStyle: Int) {
mRowLayoutId = R.layout.changelog_row_layout
mRowHeaderLayoutId = R.layout.changelog_header_layout
mChangeLogFileResourceId = if (BuildConfig.DEBUG || syDebugVersion != "0") R.raw.changelog_debug else R.raw.changelog_release
mChangeLogFileResourceId = if (BuildConfig.DEBUG /* SY --> */ || syDebugVersion != "0"/* SY <-- */) R.raw.changelog_debug else R.raw.changelog_release
}
}
}

View File

@ -38,7 +38,6 @@ import eu.kanade.tachiyomi.ui.recent.history.HistoryController
import eu.kanade.tachiyomi.ui.recent.updates.UpdatesController
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.system.toast
import exh.EH_SOURCE_ID
import exh.EIGHTMUSES_SOURCE_ID
import exh.EXHMigrations
@ -78,6 +77,7 @@ class MainActivity : BaseActivity<MainActivityBinding>() {
private var isConfirmingExit: Boolean = false
private var isHandlingShortcut: Boolean = false
// SY -->
// Idle-until-urgent
private var firstPaint = false
private val iuuQueue = LinkedList<() -> Unit>()
@ -94,6 +94,7 @@ class MainActivity : BaseActivity<MainActivityBinding>() {
iuuQueue += task
}
}
// SY <--
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -186,7 +187,9 @@ class MainActivity : BaseActivity<MainActivityBinding>() {
if (EXHMigrations.upgrade(preferences)) {
ChangelogDialogController().showDialog(router)
}
// EXH <--
// SY -->
initWhenIdle {
// Upload settings
if (preferences.enableExhentai().get() &&
@ -198,7 +201,9 @@ class MainActivity : BaseActivity<MainActivityBinding>() {
EHentaiUpdateWorker.scheduleBackground(this)
}
// SY <--
}
// SY -->
if (!preferences.eh_isHentaiEnabled().get()) {
if (EH_SOURCE_ID !in BlacklistedSources.HIDDEN_SOURCES) {
BlacklistedSources.HIDDEN_SOURCES += EH_SOURCE_ID
@ -225,7 +230,7 @@ class MainActivity : BaseActivity<MainActivityBinding>() {
BlacklistedSources.HIDDEN_SOURCES += HBROWSE_SOURCE_ID
}
}
// EXH <--
// SY -->
setExtensionsBadge()
preferences.extensionUpdatesCount().asFlow()
@ -295,11 +300,13 @@ class MainActivity : BaseActivity<MainActivityBinding>() {
router.popToRoot()
}
setSelectedNavItem(R.id.nav_library)
// SY -->
if (preferences.eh_useNewMangaInterface().get()) {
router.pushController(RouterTransaction.with(MangaAllInOneController(extras)))
} else {
router.pushController(RouterTransaction.with(MangaController(extras)))
}
// SY <--
}
SHORTCUT_DOWNLOADS -> {
if (router.backstackSize > 1) {

View File

@ -401,7 +401,7 @@ class MangaAllInOneHolder(
fun setTrackingIcon(tracked: Boolean) {
if (tracked) {
binding.btnTracking.setIconResource(R.drawable.ic_cloud_white_24dp)
binding.btnTracking.setIconResource(R.drawable.ic_cloud_24dp)
}
}
}

View File

@ -37,6 +37,7 @@ class TrackController(val fromAllInOne: Boolean = false, val manga: Manga? = nul
}
override fun createPresenter(): TrackPresenter {
// SY -->
return (
if (fromAllInOne && manga != null) {
TrackPresenter(manga)
@ -44,6 +45,7 @@ class TrackController(val fromAllInOne: Boolean = false, val manga: Manga? = nul
TrackPresenter((parentController as MangaController).manga!!)
}
)
// SY <--
}
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
@ -72,11 +74,13 @@ class TrackController(val fromAllInOne: Boolean = false, val manga: Manga? = nul
val atLeastOneLink = trackings.any { it.track != null }
adapter?.items = trackings
binding.swipeRefresh.isEnabled = atLeastOneLink
// SY -->
if (!fromAllInOne) {
(parentController as? MangaController)?.setTrackingIcon(atLeastOneLink)
} else {
(parentController as? MangaAllInOneController)?.setTrackingIcon(atLeastOneLink)
}
// SY <--
}
fun onSearchResults(results: List<TrackSearch>) {

View File

@ -48,7 +48,7 @@ class AboutController : SettingsController() {
preference {
titleRes = R.string.version
summary = if (syDebugVersion != "0") {
summary = if (BuildConfig.DEBUG /* SY --> */ || syDebugVersion != "0" /* SY --> */) {
"Preview r$syDebugVersion (${BuildConfig.COMMIT_SHA})"
} else {
"Stable ${BuildConfig.VERSION_NAME}"
@ -71,7 +71,9 @@ class AboutController : SettingsController() {
titleRes = R.string.changelog
onClick {
// SY -->
ChangelogDialogController().showDialog(router)
// SY <--
}
}
@ -96,13 +98,16 @@ class AboutController : SettingsController() {
}
preference {
title = "GitHub"
// SY -->
val url = "https://github.com/jobobby04/TachiyomiSY"
// SY <--
summary = url
onClick {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(intent)
}
}
// SY -->
preference {
title = "Original Tachiyomi GitHub "
val url = "https://github.com/inorichi/tachiyomi"
@ -112,6 +117,7 @@ class AboutController : SettingsController() {
startActivity(intent)
}
}
// SY <--
preference {
titleRes = R.string.label_extensions
val url = "https://github.com/inorichi/tachiyomi-extensions"

View File

@ -67,6 +67,7 @@ class MoreController :
router.pushController(DownloadController().withFadeTransaction())
}
}
// SY -->
if (preferences.eh_isHentaiEnabled().get()) {
preference {
titleRes = R.string.eh_batch_add
@ -77,6 +78,7 @@ class MoreController :
}
}
}
// SY <--
}
preferenceCategory {

View File

@ -647,10 +647,12 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
viewer = newViewer
binding.viewerContainer.addView(newViewer.getView())
// SY -->
val defaultReaderType = manga.defaultReaderType()
if (preferences.eh_useAutoWebtoon().get() && manga.viewer == 0 && defaultReaderType != null && defaultReaderType == WEBTOON) {
binding.root.snack(resources.getString(R.string.eh_auto_webtoon_snack), Snackbar.LENGTH_LONG)
} else if (preferences.showReadingMode()) {
// SY <--
showReadingModeSnackbar(presenter.getMangaViewer())
}

View File

@ -466,6 +466,7 @@ class ReaderPresenter(
*/
fun getMangaViewer(): Int {
val manga = manga ?: return preferences.defaultViewer()
// SY -->
return if (manga.viewer == 0 && preferences.eh_useAutoWebtoon().get()) {
manga.defaultReaderType() ?: if (manga.viewer == 0) preferences.defaultViewer() else manga.viewer
} else if (manga.viewer == 0) {
@ -473,6 +474,7 @@ class ReaderPresenter(
} else {
manga.viewer
}
// SY <--
}
/**

View File

@ -89,7 +89,9 @@ class ReaderSettingsSheet(private val activity: ReaderActivity) : BottomSheetDia
keepscreen.bindToPreference(preferences.keepScreenOn())
long_tap.bindToPreference(preferences.readWithLongTap())
always_show_chapter_transition.bindToPreference(preferences.alwaysShowChapterTransition())
// SY -->
auto_webtoon_mode.bindToPreference(preferences.eh_useAutoWebtoon())
// SY <--
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
true_color.visible()

View File

@ -45,7 +45,7 @@ class HttpPageLoader(
*/
private val subscriptions = CompositeSubscription()
private val preloadSize = prefs.eh_preload_size().get()
private val preloadSize = /* SY --> */ prefs.eh_preload_size().get() /* SY <-- */
init {
// EXH -->
@ -102,6 +102,7 @@ class HttpPageLoader(
.getPageListFromCache(chapter.chapter)
.onErrorResumeNext { source.fetchPageList(chapter.chapter) }
.map { pages ->
// SY -->
val rp = pages.mapIndexed { index, page ->
// Don't trust sources and use our own indexing
ReaderPage(index, page.url, page.imageUrl)
@ -114,6 +115,7 @@ class HttpPageLoader(
}.forEach { queue.offer(it) }
}
rp
// SY <--
}
}

View File

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.recent
import android.text.format.DateUtils
import android.view.View
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractHeaderItem
import eu.davidea.flexibleadapter.items.IFlexible
@ -16,11 +17,11 @@ class DateSectionItem(val date: Date) : AbstractHeaderItem<DateSectionItem.Holde
return R.layout.recent_section_item
}
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<androidx.recyclerview.widget.RecyclerView.ViewHolder>>): Holder {
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): Holder {
return Holder(view, adapter)
}
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<androidx.recyclerview.widget.RecyclerView.ViewHolder>>, holder: Holder, position: Int, payloads: List<Any?>?) {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>, holder: Holder, position: Int, payloads: List<Any?>?) {
holder.bind(this)
}

View File

@ -181,7 +181,7 @@ class HistoryController :
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.recently_read, menu)
inflater.inflate(R.menu.history, menu)
val searchItem = menu.findItem(R.id.action_search)
val searchView = searchItem.actionView as SearchView
searchView.maxWidth = Int.MAX_VALUE

View File

@ -287,11 +287,13 @@ class UpdatesController :
}
private fun openManga(chapter: UpdatesItem) {
// SY -->
if (Injekt.get<PreferencesHelper>().eh_useNewMangaInterface().get()) {
router.pushController(MangaAllInOneController(chapter.manga).withFadeTransaction())
} else {
router.pushController(MangaController(chapter.manga).withFadeTransaction())
}
// SY <--
}
/**

View File

@ -133,7 +133,7 @@ class SettingsAdvancedController : SettingsController() {
}
}
// <-- EXH
// --> EXH
preferenceCategory {
title = "Developer tools"
isPersistent = false

View File

@ -20,6 +20,7 @@ class SettingsBrowseController : SettingsController() {
override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) {
titleRes = R.string.browse
// SY -->
preferenceCategory {
titleRes = R.string.label_sources
@ -51,6 +52,7 @@ class SettingsBrowseController : SettingsController() {
defaultValue = false
}
}
// SY <--
preferenceCategory {
titleRes = R.string.label_extensions

View File

@ -209,6 +209,7 @@ class SettingsLibraryController : SettingsController() {
}
}
// SY -->
if (preferences.skipPreMigration().get() || preferences.migrationSources().get()
.isNotEmpty()
) {
@ -223,6 +224,7 @@ class SettingsLibraryController : SettingsController() {
}
}
}
// SY <--
}
class LibraryColumnsDialog : DialogController() {

View File

@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.ui.setting
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.more.AboutController
import eu.kanade.tachiyomi.util.preference.iconRes
import eu.kanade.tachiyomi.util.preference.iconTint
import eu.kanade.tachiyomi.util.preference.onClick
@ -66,6 +65,7 @@ class SettingsMainController : SettingsController() {
titleRes = R.string.pref_category_security
onClick { navigateTo(SettingsSecurityController()) }
}
// SY -->
if (preferences.eh_isHentaiEnabled().get()) {
preference {
iconRes = R.drawable.eh_ic_ehlogo_red_24dp
@ -86,18 +86,13 @@ class SettingsMainController : SettingsController() {
onClick { navigateTo(SettingsHlController()) }
}
}
// SY <--
preference {
iconRes = R.drawable.ic_code_24dp
iconTint = tintColor
titleRes = R.string.pref_category_advanced
onClick { navigateTo(SettingsAdvancedController()) }
}
preference {
iconRes = R.drawable.ic_info_24dp
iconTint = tintColor
titleRes = R.string.pref_category_about
onClick { navigateTo(AboutController()) }
}
}
private fun navigateTo(controller: SettingsController) {

View File

@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.util.preference.preferenceCategory
import eu.kanade.tachiyomi.util.preference.summaryRes
import eu.kanade.tachiyomi.util.preference.switchPreference
import eu.kanade.tachiyomi.util.preference.titleRes
import eu.kanade.tachiyomi.util.system.hasDisplayCutout
class SettingsReaderController : SettingsController() {
@ -29,7 +30,7 @@ class SettingsReaderController : SettingsController() {
R.string.vertical_viewer, R.string.webtoon_viewer, R.string.vertical_plus_viewer
)
entryValues = arrayOf("1", "2", "3", "4", "5")
defaultValue = "1"
defaultValue = "2"
summary = "%s"
}
intListPreference {
@ -64,6 +65,15 @@ class SettingsReaderController : SettingsController() {
titleRes = R.string.pref_fullscreen
defaultValue = true
}
if (activity?.hasDisplayCutout() == true) {
switchPreference {
key = Keys.cutoutShort
titleRes = R.string.pref_cutout_short
defaultValue = true
}
}
switchPreference {
key = Keys.keepScreenOn
titleRes = R.string.pref_keep_screen_on
@ -223,6 +233,7 @@ class SettingsReaderController : SettingsController() {
defaultValue = true
}
}
// EXH <--
preferenceCategory {
titleRes = R.string.pager_viewer

View File

@ -79,6 +79,7 @@ class WebViewActivity : BaseActivity<WebviewActivityBinding>() {
}
binding.webview.settings.javaScriptEnabled = true
binding.webview.settings.domStorageEnabled = true
binding.webview.webChromeClient = object : WebChromeClient() {
override fun onProgressChanged(view: WebView?, newProgress: Int) {

View File

@ -1,55 +0,0 @@
package eu.kanade.tachiyomi.util
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
/**
* Field that can be initialized later. Users can suspend while waiting for the field to initialize.
*
* @author nulldev
*/
class DeferredField<T> {
@Volatile
var content: T? = null
@Volatile
var initialized = false
private set
private val mutex = Mutex(true)
/**
* Initialize the field
*/
fun initialize(content: T) {
// Fast-path new listeners
this.content = content
initialized = true
// Notify current listeners
mutex.unlock()
}
fun set(content: T) {
mutex.tryLock()
this.content = content
initialized = true
// Notify current listeners
mutex.unlock()
}
/**
* Will only suspend if !initialized.
*/
suspend fun get(): T {
// Check if field is initialized and return immediately if it is
if (initialized) return content as T
// Wait for field to initialize
mutex.withLock {}
// Field is initialized, return value
return content as T
}
}

View File

@ -40,9 +40,11 @@ import kotlinx.coroutines.launch
* @param duration the duration of the toast. Defaults to short.
*/
fun Context.toast(@StringRes resource: Int, duration: Int = Toast.LENGTH_SHORT) {
// SY -->
GlobalScope.launch(Dispatchers.Main) {
Toast.makeText(this@toast, resource, duration).show()
}
// SY <--
}
/**
@ -52,9 +54,11 @@ fun Context.toast(@StringRes resource: Int, duration: Int = Toast.LENGTH_SHORT)
* @param duration the duration of the toast. Defaults to short.
*/
fun Context.toast(text: String?, duration: Int = Toast.LENGTH_SHORT) {
// SY -->
GlobalScope.launch(Dispatchers.Main) {
Toast.makeText(this@toast, text.orEmpty(), duration).show()
}
// SY <--
}
/**

View File

@ -48,9 +48,11 @@ object LocaleHelper {
* Returns Display name of a string language code
*/
fun getSourceDisplayName(lang: String?, context: Context): String {
// SY -->
if (lang != null && lang.contains("custom|")) {
return lang.split("|")[1]
}
// SY <--
return when (lang) {
"" -> context.getString(R.string.other_source)
SourcePresenter.LAST_USED_KEY -> context.getString(R.string.last_used_source)

View File

@ -100,17 +100,13 @@ fun ExtendedFloatingActionButton.shrinkOnScroll(recycler: RecyclerView) {
* @param items List of strings that are shown as individual chips.
* @param onClick Optional on click listener for each chip.
*/
fun ChipGroup.setChips(items: List<String>?, onClick: (item: String) -> Unit = {}, onLongClick: (item: String) -> Unit = {}) {
fun ChipGroup.setChips(items: List<String>?, onClick: (item: String) -> Unit = {}) {
removeAllViews()
items?.forEach { item ->
val chip = Chip(context).apply {
text = item
setOnClickListener { onClick(item) }
setOnLongClickListener {
onLongClick(item)
false
}
}
addView(chip)

View File

@ -71,11 +71,13 @@ open class ExtendedNavigationView @JvmOverloads constructor(
* @param context any context.
* @param resId the vector resource to load and tint
*/
// SY -->
fun tintVector(context: Context, resId: Int, colorId: Int = R.attr.colorAccent): Drawable {
return VectorDrawableCompat.create(context.resources, resId, context.theme)!!.apply {
setTint(context.getResourceColor(colorId))
}
}
// SY <--
}
/**
@ -106,6 +108,7 @@ open class ExtendedNavigationView @JvmOverloads constructor(
}
}
// SY -->
class TriStateGroup(resId: Int, group: Group) : MultiStateGroup(resId, group) {
companion object {
@ -128,6 +131,7 @@ open class ExtendedNavigationView @JvmOverloads constructor(
}
}
}
// SY <--
}
/**

View File

@ -4,7 +4,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.viewpager.widget.PagerAdapter
abstract class ViewPagerAdapter : androidx.viewpager.widget.PagerAdapter() {
abstract class ViewPagerAdapter : PagerAdapter() {
protected abstract fun createView(container: ViewGroup, position: Int): View

View File

@ -9,8 +9,9 @@ import kotlinx.coroutines.sync.withLock
* @author nulldev
*/
class DeferredField<T> {
@Volatile
private var content: T? = null
var content: T? = null
@Volatile
var initialized = false
@ -30,17 +31,25 @@ class DeferredField<T> {
mutex.unlock()
}
fun set(content: T) {
mutex.tryLock()
this.content = content
initialized = true
// Notify current listeners
mutex.unlock()
}
/**
* Will only suspend if !initialized.
*/
suspend fun get(): T {
// Check if field is initialized and return immediately if it is
if (initialized) return content!!
if (initialized) return content as T
// Wait for field to initialize
mutex.withLock {}
// Field is initialized, return value
return content!!
return content as T
}
}

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="?android:attr/textColorSecondary" />
</shape>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#ffffff"
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#ffffff"
android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM17,13l-5,5 -5,-5h3V9h4v4h3z"/>
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M21,2L3,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h7v2L8,20v2h8v-2h-2v-2h7c1.1,0 2,-0.9 2,-2L23,4c0,-1.1 -0.9,-2 -2,-2zM21,16L3,16L3,4h18v12z"/>
</vector>

View File

@ -4,6 +4,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,19h-2v-2h2v2zM15.07,11.25l-0.9,0.92C13.45,12.9 13,13.5 13,15h-2v-0.5c0,-1.1 0.45,-2.1 1.17,-2.83l1.24,-1.26c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2L8,9c0,-2.21 1.79,-4 4,-4s4,1.79 4,4c0,0.88 -0.36,1.68 -0.93,2.25z"/>
android:pathData="M11,18h2v-2h-2v2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM12,6c-2.21,0 -4,1.79 -4,4h2c0,-1.1 0.9,-2 2,-2s2,0.9 2,2c0,2 -3,1.75 -3,5h2c0,-2.25 3,-2.5 3,-5 0,-2.21 -1.79,-4 -4,-4z"
android:fillColor="#000000"/>
</vector>

View File

@ -5,5 +5,5 @@
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/>
android:pathData="M11,7h2v2h-2zM11,11h2v6h-2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
</vector>

View File

@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M8.59,16.34l4.58,-4.59 -4.58,-4.59L10,5.75l6,6 -6,6z"/>
</vector>

Some files were not shown because too many files have changed in this diff Show More