mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-12-24 03:01:50 +01:00
Update to Kotlin 1.4.10, coroutines 1.3.9, Kotlinter 3.0.2 (#594)
This commit is contained in:
parent
f10f8c6b64
commit
9f15f25f43
@ -26,11 +26,11 @@ import uy.kohesive.injekt.registry.default.DefaultRegistrar
|
|||||||
import java.security.Security
|
import java.security.Security
|
||||||
|
|
||||||
@ReportsCrashes(
|
@ReportsCrashes(
|
||||||
formUri = "https://collector.tracepot.com/e90773ff",
|
formUri = "https://collector.tracepot.com/e90773ff",
|
||||||
reportType = org.acra.sender.HttpSender.Type.JSON,
|
reportType = org.acra.sender.HttpSender.Type.JSON,
|
||||||
httpMethod = org.acra.sender.HttpSender.Method.PUT,
|
httpMethod = org.acra.sender.HttpSender.Method.PUT,
|
||||||
buildConfigClass = BuildConfig::class,
|
buildConfigClass = BuildConfig::class,
|
||||||
excludeMatchingSharedPreferencesKeys = [".*username.*", ".*password.*", ".*token.*"]
|
excludeMatchingSharedPreferencesKeys = [".*username.*", ".*password.*", ".*token.*"]
|
||||||
)
|
)
|
||||||
open class App : Application(), LifecycleObserver {
|
open class App : Application(), LifecycleObserver {
|
||||||
|
|
||||||
|
@ -75,7 +75,8 @@ class BackupCreateService : Service() {
|
|||||||
startForeground(Notifications.ID_BACKUP_PROGRESS, notifier.showBackupProgress().build())
|
startForeground(Notifications.ID_BACKUP_PROGRESS, notifier.showBackupProgress().build())
|
||||||
|
|
||||||
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(
|
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(
|
||||||
PowerManager.PARTIAL_WAKE_LOCK, "${javaClass.name}:WakeLock"
|
PowerManager.PARTIAL_WAKE_LOCK,
|
||||||
|
"${javaClass.name}:WakeLock"
|
||||||
)
|
)
|
||||||
wakeLock.acquire()
|
wakeLock.acquire()
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ import uy.kohesive.injekt.api.get
|
|||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class BackupCreatorJob(private val context: Context, workerParams: WorkerParameters) :
|
class BackupCreatorJob(private val context: Context, workerParams: WorkerParameters) :
|
||||||
Worker(context, workerParams) {
|
Worker(context, workerParams) {
|
||||||
|
|
||||||
override fun doWork(): Result {
|
override fun doWork(): Result {
|
||||||
val preferences = Injekt.get<PreferencesHelper>()
|
val preferences = Injekt.get<PreferencesHelper>()
|
||||||
@ -37,10 +37,13 @@ class BackupCreatorJob(private val context: Context, workerParams: WorkerParamet
|
|||||||
val interval = prefInterval ?: preferences.backupInterval().getOrDefault()
|
val interval = prefInterval ?: preferences.backupInterval().getOrDefault()
|
||||||
if (interval > 0) {
|
if (interval > 0) {
|
||||||
val request = PeriodicWorkRequestBuilder<BackupCreatorJob>(
|
val request = PeriodicWorkRequestBuilder<BackupCreatorJob>(
|
||||||
interval.toLong(), TimeUnit.HOURS,
|
interval.toLong(),
|
||||||
10, TimeUnit.MINUTES)
|
TimeUnit.HOURS,
|
||||||
.addTag(TAG)
|
10,
|
||||||
.build()
|
TimeUnit.MINUTES
|
||||||
|
)
|
||||||
|
.addTag(TAG)
|
||||||
|
.build()
|
||||||
|
|
||||||
WorkManager.getInstance().enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.REPLACE, request)
|
WorkManager.getInstance().enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.REPLACE, request)
|
||||||
} else {
|
} else {
|
||||||
|
@ -103,7 +103,8 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) {
|
|||||||
|
|
||||||
private fun initParser(): Gson = when (version) {
|
private fun initParser(): Gson = when (version) {
|
||||||
1 -> GsonBuilder().create()
|
1 -> GsonBuilder().create()
|
||||||
2 -> GsonBuilder()
|
2 ->
|
||||||
|
GsonBuilder()
|
||||||
.registerTypeAdapter<MangaImpl>(MangaTypeAdapter.build())
|
.registerTypeAdapter<MangaImpl>(MangaTypeAdapter.build())
|
||||||
.registerTypeHierarchyAdapter<ChapterImpl>(ChapterTypeAdapter.build())
|
.registerTypeHierarchyAdapter<ChapterImpl>(ChapterTypeAdapter.build())
|
||||||
.registerTypeAdapter<CategoryImpl>(CategoryTypeAdapter.build())
|
.registerTypeAdapter<CategoryImpl>(CategoryTypeAdapter.build())
|
||||||
@ -176,14 +177,14 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) {
|
|||||||
val numberOfBackups = numberOfBackups()
|
val numberOfBackups = numberOfBackups()
|
||||||
val backupRegex = Regex("""tachiyomi_\d+-\d+-\d+_\d+-\d+.json""")
|
val backupRegex = Regex("""tachiyomi_\d+-\d+-\d+_\d+-\d+.json""")
|
||||||
dir.listFiles { _, filename -> backupRegex.matches(filename) }
|
dir.listFiles { _, filename -> backupRegex.matches(filename) }
|
||||||
.orEmpty()
|
.orEmpty()
|
||||||
.sortedByDescending { it.name }
|
.sortedByDescending { it.name }
|
||||||
.drop(numberOfBackups - 1)
|
.drop(numberOfBackups - 1)
|
||||||
.forEach { it.delete() }
|
.forEach { it.delete() }
|
||||||
|
|
||||||
// Create new file to place backup
|
// Create new file to place backup
|
||||||
val newFile = dir.createFile(Backup.getDefaultFilename())
|
val newFile = dir.createFile(Backup.getDefaultFilename())
|
||||||
?: throw Exception("Couldn't create backup file")
|
?: throw Exception("Couldn't create backup file")
|
||||||
|
|
||||||
newFile.openOutputStream().bufferedWriter().use {
|
newFile.openOutputStream().bufferedWriter().use {
|
||||||
parser.toJson(root, it)
|
parser.toJson(root, it)
|
||||||
@ -192,7 +193,7 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) {
|
|||||||
return newFile.uri.toString()
|
return newFile.uri.toString()
|
||||||
} else {
|
} else {
|
||||||
val file = UniFile.fromUri(context, uri)
|
val file = UniFile.fromUri(context, uri)
|
||||||
?: throw Exception("Couldn't create backup file")
|
?: throw Exception("Couldn't create backup file")
|
||||||
file.openOutputStream().bufferedWriter().use {
|
file.openOutputStream().bufferedWriter().use {
|
||||||
parser.toJson(root, it)
|
parser.toJson(root, it)
|
||||||
}
|
}
|
||||||
@ -514,7 +515,7 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) {
|
|||||||
* @return [Manga], null if not found
|
* @return [Manga], null if not found
|
||||||
*/
|
*/
|
||||||
internal fun getMangaFromDatabase(manga: Manga): Manga? =
|
internal fun getMangaFromDatabase(manga: Manga): Manga? =
|
||||||
databaseHelper.getManga(manga.url, manga.source).executeAsBlocking()
|
databaseHelper.getManga(manga.url, manga.source).executeAsBlocking()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns list containing manga from library
|
* Returns list containing manga from library
|
||||||
@ -522,7 +523,7 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) {
|
|||||||
* @return [Manga] from library
|
* @return [Manga] from library
|
||||||
*/
|
*/
|
||||||
internal fun getFavoriteManga(): List<Manga> =
|
internal fun getFavoriteManga(): List<Manga> =
|
||||||
databaseHelper.getFavoriteMangas().executeAsBlocking()
|
databaseHelper.getFavoriteMangas().executeAsBlocking()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts manga and returns id
|
* Inserts manga and returns id
|
||||||
@ -530,7 +531,7 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) {
|
|||||||
* @return id of [Manga], null if not found
|
* @return id of [Manga], null if not found
|
||||||
*/
|
*/
|
||||||
internal fun insertManga(manga: Manga): Long? =
|
internal fun insertManga(manga: Manga): Long? =
|
||||||
databaseHelper.insertManga(manga).executeAsBlocking().insertedId()
|
databaseHelper.insertManga(manga).executeAsBlocking().insertedId()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts list of chapters
|
* Inserts list of chapters
|
||||||
|
@ -121,7 +121,9 @@ class BackupRestoreService : Service() {
|
|||||||
super.onCreate()
|
super.onCreate()
|
||||||
startForeground(Notifications.ID_RESTORE_PROGRESS, progressNotification.build())
|
startForeground(Notifications.ID_RESTORE_PROGRESS, progressNotification.build())
|
||||||
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(
|
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(
|
||||||
PowerManager.PARTIAL_WAKE_LOCK, "BackupRestoreService:WakeLock")
|
PowerManager.PARTIAL_WAKE_LOCK,
|
||||||
|
"BackupRestoreService:WakeLock"
|
||||||
|
)
|
||||||
wakeLock.acquire(TimeUnit.HOURS.toMillis(3))
|
wakeLock.acquire(TimeUnit.HOURS.toMillis(3))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -358,13 +360,13 @@ class BackupRestoreService : Service() {
|
|||||||
*/
|
*/
|
||||||
private val progressNotification by lazy {
|
private val progressNotification by lazy {
|
||||||
NotificationCompat.Builder(this, Notifications.CHANNEL_BACKUP_RESTORE)
|
NotificationCompat.Builder(this, Notifications.CHANNEL_BACKUP_RESTORE)
|
||||||
.setContentTitle(getString(R.string.app_name))
|
.setContentTitle(getString(R.string.app_name))
|
||||||
.setSmallIcon(R.drawable.ic_tachi)
|
.setSmallIcon(R.drawable.ic_tachi)
|
||||||
.setOngoing(true)
|
.setOngoing(true)
|
||||||
.setOnlyAlertOnce(true)
|
.setOnlyAlertOnce(true)
|
||||||
.setAutoCancel(false)
|
.setAutoCancel(false)
|
||||||
.setColor(ContextCompat.getColor(this, R.color.colorAccent))
|
.setColor(ContextCompat.getColor(this, R.color.colorAccent))
|
||||||
.addAction(R.drawable.ic_close_24dp, getString(android.R.string.cancel), cancelIntent)
|
.addAction(R.drawable.ic_close_24dp, getString(android.R.string.cancel), cancelIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -382,12 +384,20 @@ class BackupRestoreService : Service() {
|
|||||||
* @param total the total progress.
|
* @param total the total progress.
|
||||||
*/
|
*/
|
||||||
private fun showProgressNotification(current: Int, total: Int, title: String) {
|
private fun showProgressNotification(current: Int, total: Int, title: String) {
|
||||||
notificationManager.notify(Notifications.ID_RESTORE_PROGRESS, progressNotification
|
notificationManager.notify(
|
||||||
|
Notifications.ID_RESTORE_PROGRESS,
|
||||||
|
progressNotification
|
||||||
.setContentTitle(title.chop(30))
|
.setContentTitle(title.chop(30))
|
||||||
.setContentText(getString(R.string.restoring_progress, restoreProgress,
|
.setContentText(
|
||||||
totalAmount))
|
getString(
|
||||||
|
R.string.restoring_progress,
|
||||||
|
restoreProgress,
|
||||||
|
totalAmount
|
||||||
|
)
|
||||||
|
)
|
||||||
.setProgress(total, current, false)
|
.setProgress(total, current, false)
|
||||||
.build())
|
.build()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -395,8 +405,14 @@ class BackupRestoreService : Service() {
|
|||||||
*/
|
*/
|
||||||
private fun showResultNotification(path: String?, file: String?) {
|
private fun showResultNotification(path: String?, file: String?) {
|
||||||
|
|
||||||
val content = mutableListOf(getString(R.string.restore_completed_content, restoreProgress
|
val content = mutableListOf(
|
||||||
.toString(), errors.size.toString()))
|
getString(
|
||||||
|
R.string.restore_completed_content,
|
||||||
|
restoreProgress
|
||||||
|
.toString(),
|
||||||
|
errors.size.toString()
|
||||||
|
)
|
||||||
|
)
|
||||||
val sourceMissingCount = sourcesMissing.distinct().size
|
val sourceMissingCount = sourcesMissing.distinct().size
|
||||||
if (sourceMissingCount > 0) {
|
if (sourceMissingCount > 0) {
|
||||||
val sources = sourcesMissing.distinct().filter { it.toLongOrNull() == null }
|
val sources = sourcesMissing.distinct().filter { it.toLongOrNull() == null }
|
||||||
@ -408,20 +424,29 @@ class BackupRestoreService : Service() {
|
|||||||
if (sources.isEmpty()) {
|
if (sources.isEmpty()) {
|
||||||
content.add(
|
content.add(
|
||||||
resources.getQuantityString(
|
resources.getQuantityString(
|
||||||
R.plurals.sources_missing, sourceMissingCount, sourceMissingCount
|
R.plurals.sources_missing,
|
||||||
|
sourceMissingCount,
|
||||||
|
sourceMissingCount
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
content.add(
|
content.add(
|
||||||
resources.getQuantityString(
|
resources.getQuantityString(
|
||||||
R.plurals.sources_missing, sourceMissingCount, sourceMissingCount
|
R.plurals.sources_missing,
|
||||||
|
sourceMissingCount,
|
||||||
|
sourceMissingCount
|
||||||
) + ": " + missingSourcesString
|
) + ": " + missingSourcesString
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (lincensedManga > 0)
|
if (lincensedManga > 0)
|
||||||
content.add(resources.getQuantityString(R.plurals.licensed_manga, lincensedManga,
|
content.add(
|
||||||
lincensedManga))
|
resources.getQuantityString(
|
||||||
|
R.plurals.licensed_manga,
|
||||||
|
lincensedManga,
|
||||||
|
lincensedManga
|
||||||
|
)
|
||||||
|
)
|
||||||
val trackingErrors = trackingErrors.distinct()
|
val trackingErrors = trackingErrors.distinct()
|
||||||
if (trackingErrors.isNotEmpty()) {
|
if (trackingErrors.isNotEmpty()) {
|
||||||
val trackingErrorsString = trackingErrors.distinct().joinToString("\n")
|
val trackingErrorsString = trackingErrors.distinct().joinToString("\n")
|
||||||
@ -433,15 +458,21 @@ class BackupRestoreService : Service() {
|
|||||||
val restoreString = content.joinToString("\n")
|
val restoreString = content.joinToString("\n")
|
||||||
|
|
||||||
val resultNotification = NotificationCompat.Builder(this, Notifications.CHANNEL_BACKUP_RESTORE)
|
val resultNotification = NotificationCompat.Builder(this, Notifications.CHANNEL_BACKUP_RESTORE)
|
||||||
.setContentTitle(getString(R.string.restore_completed))
|
.setContentTitle(getString(R.string.restore_completed))
|
||||||
.setContentText(restoreString)
|
.setContentText(restoreString)
|
||||||
.setStyle(NotificationCompat.BigTextStyle().bigText(restoreString))
|
.setStyle(NotificationCompat.BigTextStyle().bigText(restoreString))
|
||||||
.setSmallIcon(R.drawable.ic_tachi)
|
.setSmallIcon(R.drawable.ic_tachi)
|
||||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||||
.setColor(ContextCompat.getColor(this, R.color.colorAccent))
|
.setColor(ContextCompat.getColor(this, R.color.colorAccent))
|
||||||
if (errors.size > 0 && !path.isNullOrEmpty() && !file.isNullOrEmpty()) {
|
if (errors.size > 0 && !path.isNullOrEmpty() && !file.isNullOrEmpty()) {
|
||||||
resultNotification.addAction(R.drawable.ic_close_24dp, getString(R.string
|
resultNotification.addAction(
|
||||||
.view_all_errors), getErrorLogIntent(path, file))
|
R.drawable.ic_close_24dp,
|
||||||
|
getString(
|
||||||
|
R.string
|
||||||
|
.view_all_errors
|
||||||
|
),
|
||||||
|
getErrorLogIntent(path, file)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
notificationManager.notify(Notifications.ID_RESTORE_COMPLETE, resultNotification.build())
|
notificationManager.notify(Notifications.ID_RESTORE_COMPLETE, resultNotification.build())
|
||||||
}
|
}
|
||||||
@ -451,11 +482,11 @@ class BackupRestoreService : Service() {
|
|||||||
*/
|
*/
|
||||||
private fun showErrorNotification(errorMessage: String) {
|
private fun showErrorNotification(errorMessage: String) {
|
||||||
val resultNotification = NotificationCompat.Builder(this, Notifications.CHANNEL_BACKUP_RESTORE)
|
val resultNotification = NotificationCompat.Builder(this, Notifications.CHANNEL_BACKUP_RESTORE)
|
||||||
.setContentTitle(getString(R.string.restore_error))
|
.setContentTitle(getString(R.string.restore_error))
|
||||||
.setContentText(errorMessage)
|
.setContentText(errorMessage)
|
||||||
.setSmallIcon(R.drawable.ic_error_24dp)
|
.setSmallIcon(R.drawable.ic_error_24dp)
|
||||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||||
.setColor(ContextCompat.getColor(this, R.color.md_red_500))
|
.setColor(ContextCompat.getColor(this, R.color.md_red_500))
|
||||||
notificationManager.notify(Notifications.ID_RESTORE_ERROR, resultNotification.build())
|
notificationManager.notify(Notifications.ID_RESTORE_ERROR, resultNotification.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -477,7 +508,7 @@ class BackupRestoreService : Service() {
|
|||||||
* @return true if the service is running, false otherwise.
|
* @return true if the service is running, false otherwise.
|
||||||
*/
|
*/
|
||||||
fun isRunning(context: Context): Boolean =
|
fun isRunning(context: Context): Boolean =
|
||||||
context.isServiceRunning(BackupRestoreService::class.java)
|
context.isServiceRunning(BackupRestoreService::class.java)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts a service to restore a backup from Json
|
* Starts a service to restore a backup from Json
|
||||||
|
@ -46,10 +46,12 @@ class ChapterCache(private val context: Context) {
|
|||||||
private val gson: Gson by injectLazy()
|
private val gson: Gson by injectLazy()
|
||||||
|
|
||||||
/** Cache class used for cache management. */
|
/** Cache class used for cache management. */
|
||||||
private val diskCache = DiskLruCache.open(File(context.cacheDir, PARAMETER_CACHE_DIRECTORY),
|
private val diskCache = DiskLruCache.open(
|
||||||
PARAMETER_APP_VERSION,
|
File(context.cacheDir, PARAMETER_CACHE_DIRECTORY),
|
||||||
PARAMETER_VALUE_COUNT,
|
PARAMETER_APP_VERSION,
|
||||||
PARAMETER_CACHE_SIZE)
|
PARAMETER_VALUE_COUNT,
|
||||||
|
PARAMETER_CACHE_SIZE
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns directory of cache.
|
* Returns directory of cache.
|
||||||
|
@ -83,7 +83,8 @@ class CoverCache(val context: Context) {
|
|||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
context.toast(
|
context.toast(
|
||||||
context.getString(
|
context.getString(
|
||||||
R.string.deleted_, Formatter.formatFileSize(context, deletedSize)
|
R.string.deleted_,
|
||||||
|
Formatter.formatFileSize(context, deletedSize)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -111,7 +112,8 @@ class CoverCache(val context: Context) {
|
|||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
context.toast(
|
context.toast(
|
||||||
context.getString(
|
context.getString(
|
||||||
R.string.deleted_, Formatter.formatFileSize(context, deletedSize)
|
R.string.deleted_,
|
||||||
|
Formatter.formatFileSize(context, deletedSize)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -30,8 +30,13 @@ import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory
|
|||||||
* This class provides operations to manage the database through its interfaces.
|
* This class provides operations to manage the database through its interfaces.
|
||||||
*/
|
*/
|
||||||
open class DatabaseHelper(context: Context) :
|
open class DatabaseHelper(context: Context) :
|
||||||
MangaQueries, ChapterQueries, TrackQueries, CategoryQueries, MangaCategoryQueries,
|
MangaQueries,
|
||||||
HistoryQueries, SearchMetadataQueries {
|
ChapterQueries,
|
||||||
|
TrackQueries,
|
||||||
|
CategoryQueries,
|
||||||
|
MangaCategoryQueries,
|
||||||
|
HistoryQueries,
|
||||||
|
SearchMetadataQueries {
|
||||||
|
|
||||||
private val configuration = SupportSQLiteOpenHelper.Configuration.builder(context)
|
private val configuration = SupportSQLiteOpenHelper.Configuration.builder(context)
|
||||||
.name(DbOpenCallback.DATABASE_NAME)
|
.name(DbOpenCallback.DATABASE_NAME)
|
||||||
@ -39,15 +44,15 @@ MangaQueries, ChapterQueries, TrackQueries, CategoryQueries, MangaCategoryQuerie
|
|||||||
.build()
|
.build()
|
||||||
|
|
||||||
override val db = DefaultStorIOSQLite.builder()
|
override val db = DefaultStorIOSQLite.builder()
|
||||||
.sqliteOpenHelper(RequerySQLiteOpenHelperFactory().create(configuration))
|
.sqliteOpenHelper(RequerySQLiteOpenHelperFactory().create(configuration))
|
||||||
.addTypeMapping(Manga::class.java, MangaTypeMapping())
|
.addTypeMapping(Manga::class.java, MangaTypeMapping())
|
||||||
.addTypeMapping(Chapter::class.java, ChapterTypeMapping())
|
.addTypeMapping(Chapter::class.java, ChapterTypeMapping())
|
||||||
.addTypeMapping(Track::class.java, TrackTypeMapping())
|
.addTypeMapping(Track::class.java, TrackTypeMapping())
|
||||||
.addTypeMapping(Category::class.java, CategoryTypeMapping())
|
.addTypeMapping(Category::class.java, CategoryTypeMapping())
|
||||||
.addTypeMapping(MangaCategory::class.java, MangaCategoryTypeMapping())
|
.addTypeMapping(MangaCategory::class.java, MangaCategoryTypeMapping())
|
||||||
.addTypeMapping(SearchMetadata::class.java, SearchMetadataTypeMapping())
|
.addTypeMapping(SearchMetadata::class.java, SearchMetadataTypeMapping())
|
||||||
.addTypeMapping(History::class.java, HistoryTypeMapping())
|
.addTypeMapping(History::class.java, HistoryTypeMapping())
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
inline fun inTransaction(block: () -> Unit) = db.inTransaction(block)
|
inline fun inTransaction(block: () -> Unit) = db.inTransaction(block)
|
||||||
|
|
||||||
|
@ -44,8 +44,10 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
|
|||||||
db.execSQL(ChapterTable.sourceOrderUpdateQuery)
|
db.execSQL(ChapterTable.sourceOrderUpdateQuery)
|
||||||
|
|
||||||
// Fix kissmanga covers after supporting cloudflare
|
// Fix kissmanga covers after supporting cloudflare
|
||||||
db.execSQL("""UPDATE mangas SET thumbnail_url =
|
db.execSQL(
|
||||||
REPLACE(thumbnail_url, '93.174.95.110', 'kissmanga.com') WHERE source = 4""")
|
"""UPDATE mangas SET thumbnail_url =
|
||||||
|
REPLACE(thumbnail_url, '93.174.95.110', 'kissmanga.com') WHERE source = 4"""
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (oldVersion < 3) {
|
if (oldVersion < 3) {
|
||||||
// Initialize history tables
|
// Initialize history tables
|
||||||
|
@ -19,22 +19,22 @@ import eu.kanade.tachiyomi.data.database.tables.CategoryTable.COL_ORDER
|
|||||||
import eu.kanade.tachiyomi.data.database.tables.CategoryTable.TABLE
|
import eu.kanade.tachiyomi.data.database.tables.CategoryTable.TABLE
|
||||||
|
|
||||||
class CategoryTypeMapping : SQLiteTypeMapping<Category>(
|
class CategoryTypeMapping : SQLiteTypeMapping<Category>(
|
||||||
CategoryPutResolver(),
|
CategoryPutResolver(),
|
||||||
CategoryGetResolver(),
|
CategoryGetResolver(),
|
||||||
CategoryDeleteResolver()
|
CategoryDeleteResolver()
|
||||||
)
|
)
|
||||||
|
|
||||||
class CategoryPutResolver : DefaultPutResolver<Category>() {
|
class CategoryPutResolver : DefaultPutResolver<Category>() {
|
||||||
|
|
||||||
override fun mapToInsertQuery(obj: Category) = InsertQuery.builder()
|
override fun mapToInsertQuery(obj: Category) = InsertQuery.builder()
|
||||||
.table(TABLE)
|
.table(TABLE)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
override fun mapToUpdateQuery(obj: Category) = UpdateQuery.builder()
|
override fun mapToUpdateQuery(obj: Category) = UpdateQuery.builder()
|
||||||
.table(TABLE)
|
.table(TABLE)
|
||||||
.where("$COL_ID = ?")
|
.where("$COL_ID = ?")
|
||||||
.whereArgs(obj.id)
|
.whereArgs(obj.id)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
override fun mapToContentValues(obj: Category) = ContentValues(4).apply {
|
override fun mapToContentValues(obj: Category) = ContentValues(4).apply {
|
||||||
put(COL_ID, obj.id)
|
put(COL_ID, obj.id)
|
||||||
@ -76,8 +76,8 @@ class CategoryGetResolver : DefaultGetResolver<Category>() {
|
|||||||
class CategoryDeleteResolver : DefaultDeleteResolver<Category>() {
|
class CategoryDeleteResolver : DefaultDeleteResolver<Category>() {
|
||||||
|
|
||||||
override fun mapToDeleteQuery(obj: Category) = DeleteQuery.builder()
|
override fun mapToDeleteQuery(obj: Category) = DeleteQuery.builder()
|
||||||
.table(TABLE)
|
.table(TABLE)
|
||||||
.where("$COL_ID = ?")
|
.where("$COL_ID = ?")
|
||||||
.whereArgs(obj.id)
|
.whereArgs(obj.id)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
@ -27,22 +27,22 @@ import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_URL
|
|||||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.TABLE
|
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.TABLE
|
||||||
|
|
||||||
class ChapterTypeMapping : SQLiteTypeMapping<Chapter>(
|
class ChapterTypeMapping : SQLiteTypeMapping<Chapter>(
|
||||||
ChapterPutResolver(),
|
ChapterPutResolver(),
|
||||||
ChapterGetResolver(),
|
ChapterGetResolver(),
|
||||||
ChapterDeleteResolver()
|
ChapterDeleteResolver()
|
||||||
)
|
)
|
||||||
|
|
||||||
class ChapterPutResolver : DefaultPutResolver<Chapter>() {
|
class ChapterPutResolver : DefaultPutResolver<Chapter>() {
|
||||||
|
|
||||||
override fun mapToInsertQuery(obj: Chapter) = InsertQuery.builder()
|
override fun mapToInsertQuery(obj: Chapter) = InsertQuery.builder()
|
||||||
.table(TABLE)
|
.table(TABLE)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
override fun mapToUpdateQuery(obj: Chapter) = UpdateQuery.builder()
|
override fun mapToUpdateQuery(obj: Chapter) = UpdateQuery.builder()
|
||||||
.table(TABLE)
|
.table(TABLE)
|
||||||
.where("$COL_ID = ?")
|
.where("$COL_ID = ?")
|
||||||
.whereArgs(obj.id)
|
.whereArgs(obj.id)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
override fun mapToContentValues(obj: Chapter) = ContentValues(11).apply {
|
override fun mapToContentValues(obj: Chapter) = ContentValues(11).apply {
|
||||||
put(COL_ID, obj.id)
|
put(COL_ID, obj.id)
|
||||||
@ -83,8 +83,8 @@ class ChapterGetResolver : DefaultGetResolver<Chapter>() {
|
|||||||
class ChapterDeleteResolver : DefaultDeleteResolver<Chapter>() {
|
class ChapterDeleteResolver : DefaultDeleteResolver<Chapter>() {
|
||||||
|
|
||||||
override fun mapToDeleteQuery(obj: Chapter) = DeleteQuery.builder()
|
override fun mapToDeleteQuery(obj: Chapter) = DeleteQuery.builder()
|
||||||
.table(TABLE)
|
.table(TABLE)
|
||||||
.where("$COL_ID = ?")
|
.where("$COL_ID = ?")
|
||||||
.whereArgs(obj.id)
|
.whereArgs(obj.id)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
@ -18,22 +18,22 @@ import eu.kanade.tachiyomi.data.database.tables.HistoryTable.COL_TIME_READ
|
|||||||
import eu.kanade.tachiyomi.data.database.tables.HistoryTable.TABLE
|
import eu.kanade.tachiyomi.data.database.tables.HistoryTable.TABLE
|
||||||
|
|
||||||
class HistoryTypeMapping : SQLiteTypeMapping<History>(
|
class HistoryTypeMapping : SQLiteTypeMapping<History>(
|
||||||
HistoryPutResolver(),
|
HistoryPutResolver(),
|
||||||
HistoryGetResolver(),
|
HistoryGetResolver(),
|
||||||
HistoryDeleteResolver()
|
HistoryDeleteResolver()
|
||||||
)
|
)
|
||||||
|
|
||||||
open class HistoryPutResolver : DefaultPutResolver<History>() {
|
open class HistoryPutResolver : DefaultPutResolver<History>() {
|
||||||
|
|
||||||
override fun mapToInsertQuery(obj: History) = InsertQuery.builder()
|
override fun mapToInsertQuery(obj: History) = InsertQuery.builder()
|
||||||
.table(TABLE)
|
.table(TABLE)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
override fun mapToUpdateQuery(obj: History) = UpdateQuery.builder()
|
override fun mapToUpdateQuery(obj: History) = UpdateQuery.builder()
|
||||||
.table(TABLE)
|
.table(TABLE)
|
||||||
.where("$COL_ID = ?")
|
.where("$COL_ID = ?")
|
||||||
.whereArgs(obj.id)
|
.whereArgs(obj.id)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
override fun mapToContentValues(obj: History) = ContentValues(4).apply {
|
override fun mapToContentValues(obj: History) = ContentValues(4).apply {
|
||||||
put(COL_ID, obj.id)
|
put(COL_ID, obj.id)
|
||||||
@ -56,8 +56,8 @@ class HistoryGetResolver : DefaultGetResolver<History>() {
|
|||||||
class HistoryDeleteResolver : DefaultDeleteResolver<History>() {
|
class HistoryDeleteResolver : DefaultDeleteResolver<History>() {
|
||||||
|
|
||||||
override fun mapToDeleteQuery(obj: History) = DeleteQuery.builder()
|
override fun mapToDeleteQuery(obj: History) = DeleteQuery.builder()
|
||||||
.table(TABLE)
|
.table(TABLE)
|
||||||
.where("$COL_ID = ?")
|
.where("$COL_ID = ?")
|
||||||
.whereArgs(obj.id)
|
.whereArgs(obj.id)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
@ -16,22 +16,22 @@ import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable.COL_MANGA_ID
|
|||||||
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable.TABLE
|
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable.TABLE
|
||||||
|
|
||||||
class MangaCategoryTypeMapping : SQLiteTypeMapping<MangaCategory>(
|
class MangaCategoryTypeMapping : SQLiteTypeMapping<MangaCategory>(
|
||||||
MangaCategoryPutResolver(),
|
MangaCategoryPutResolver(),
|
||||||
MangaCategoryGetResolver(),
|
MangaCategoryGetResolver(),
|
||||||
MangaCategoryDeleteResolver()
|
MangaCategoryDeleteResolver()
|
||||||
)
|
)
|
||||||
|
|
||||||
class MangaCategoryPutResolver : DefaultPutResolver<MangaCategory>() {
|
class MangaCategoryPutResolver : DefaultPutResolver<MangaCategory>() {
|
||||||
|
|
||||||
override fun mapToInsertQuery(obj: MangaCategory) = InsertQuery.builder()
|
override fun mapToInsertQuery(obj: MangaCategory) = InsertQuery.builder()
|
||||||
.table(TABLE)
|
.table(TABLE)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
override fun mapToUpdateQuery(obj: MangaCategory) = UpdateQuery.builder()
|
override fun mapToUpdateQuery(obj: MangaCategory) = UpdateQuery.builder()
|
||||||
.table(TABLE)
|
.table(TABLE)
|
||||||
.where("$COL_ID = ?")
|
.where("$COL_ID = ?")
|
||||||
.whereArgs(obj.id)
|
.whereArgs(obj.id)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
override fun mapToContentValues(obj: MangaCategory) = ContentValues(3).apply {
|
override fun mapToContentValues(obj: MangaCategory) = ContentValues(3).apply {
|
||||||
put(COL_ID, obj.id)
|
put(COL_ID, obj.id)
|
||||||
@ -52,8 +52,8 @@ class MangaCategoryGetResolver : DefaultGetResolver<MangaCategory>() {
|
|||||||
class MangaCategoryDeleteResolver : DefaultDeleteResolver<MangaCategory>() {
|
class MangaCategoryDeleteResolver : DefaultDeleteResolver<MangaCategory>() {
|
||||||
|
|
||||||
override fun mapToDeleteQuery(obj: MangaCategory) = DeleteQuery.builder()
|
override fun mapToDeleteQuery(obj: MangaCategory) = DeleteQuery.builder()
|
||||||
.table(TABLE)
|
.table(TABLE)
|
||||||
.where("$COL_ID = ?")
|
.where("$COL_ID = ?")
|
||||||
.whereArgs(obj.id)
|
.whereArgs(obj.id)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
@ -31,22 +31,22 @@ import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_VIEWER
|
|||||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.TABLE
|
import eu.kanade.tachiyomi.data.database.tables.MangaTable.TABLE
|
||||||
|
|
||||||
class MangaTypeMapping : SQLiteTypeMapping<Manga>(
|
class MangaTypeMapping : SQLiteTypeMapping<Manga>(
|
||||||
MangaPutResolver(),
|
MangaPutResolver(),
|
||||||
MangaGetResolver(),
|
MangaGetResolver(),
|
||||||
MangaDeleteResolver()
|
MangaDeleteResolver()
|
||||||
)
|
)
|
||||||
|
|
||||||
class MangaPutResolver : DefaultPutResolver<Manga>() {
|
class MangaPutResolver : DefaultPutResolver<Manga>() {
|
||||||
|
|
||||||
override fun mapToInsertQuery(obj: Manga) = InsertQuery.builder()
|
override fun mapToInsertQuery(obj: Manga) = InsertQuery.builder()
|
||||||
.table(TABLE)
|
.table(TABLE)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
override fun mapToUpdateQuery(obj: Manga) = UpdateQuery.builder()
|
override fun mapToUpdateQuery(obj: Manga) = UpdateQuery.builder()
|
||||||
.table(TABLE)
|
.table(TABLE)
|
||||||
.where("$COL_ID = ?")
|
.where("$COL_ID = ?")
|
||||||
.whereArgs(obj.id)
|
.whereArgs(obj.id)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
override fun mapToContentValues(obj: Manga) = ContentValues(15).apply {
|
override fun mapToContentValues(obj: Manga) = ContentValues(15).apply {
|
||||||
put(COL_ID, obj.id)
|
put(COL_ID, obj.id)
|
||||||
@ -101,8 +101,8 @@ open class MangaGetResolver : DefaultGetResolver<Manga>(), BaseMangaGetResolver
|
|||||||
class MangaDeleteResolver : DefaultDeleteResolver<Manga>() {
|
class MangaDeleteResolver : DefaultDeleteResolver<Manga>() {
|
||||||
|
|
||||||
override fun mapToDeleteQuery(obj: Manga) = DeleteQuery.builder()
|
override fun mapToDeleteQuery(obj: Manga) = DeleteQuery.builder()
|
||||||
.table(TABLE)
|
.table(TABLE)
|
||||||
.where("$COL_ID = ?")
|
.where("$COL_ID = ?")
|
||||||
.whereArgs(obj.id)
|
.whereArgs(obj.id)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
@ -25,22 +25,22 @@ import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_TRACKING_URL
|
|||||||
import eu.kanade.tachiyomi.data.database.tables.TrackTable.TABLE
|
import eu.kanade.tachiyomi.data.database.tables.TrackTable.TABLE
|
||||||
|
|
||||||
class TrackTypeMapping : SQLiteTypeMapping<Track>(
|
class TrackTypeMapping : SQLiteTypeMapping<Track>(
|
||||||
TrackPutResolver(),
|
TrackPutResolver(),
|
||||||
TrackGetResolver(),
|
TrackGetResolver(),
|
||||||
TrackDeleteResolver()
|
TrackDeleteResolver()
|
||||||
)
|
)
|
||||||
|
|
||||||
class TrackPutResolver : DefaultPutResolver<Track>() {
|
class TrackPutResolver : DefaultPutResolver<Track>() {
|
||||||
|
|
||||||
override fun mapToInsertQuery(obj: Track) = InsertQuery.builder()
|
override fun mapToInsertQuery(obj: Track) = InsertQuery.builder()
|
||||||
.table(TABLE)
|
.table(TABLE)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
override fun mapToUpdateQuery(obj: Track) = UpdateQuery.builder()
|
override fun mapToUpdateQuery(obj: Track) = UpdateQuery.builder()
|
||||||
.table(TABLE)
|
.table(TABLE)
|
||||||
.where("$COL_ID = ?")
|
.where("$COL_ID = ?")
|
||||||
.whereArgs(obj.id)
|
.whereArgs(obj.id)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
override fun mapToContentValues(obj: Track) = ContentValues(10).apply {
|
override fun mapToContentValues(obj: Track) = ContentValues(10).apply {
|
||||||
put(COL_ID, obj.id)
|
put(COL_ID, obj.id)
|
||||||
@ -77,8 +77,8 @@ class TrackGetResolver : DefaultGetResolver<Track>() {
|
|||||||
class TrackDeleteResolver : DefaultDeleteResolver<Track>() {
|
class TrackDeleteResolver : DefaultDeleteResolver<Track>() {
|
||||||
|
|
||||||
override fun mapToDeleteQuery(obj: Track) = DeleteQuery.builder()
|
override fun mapToDeleteQuery(obj: Track) = DeleteQuery.builder()
|
||||||
.table(TABLE)
|
.table(TABLE)
|
||||||
.where("$COL_ID = ?")
|
.where("$COL_ID = ?")
|
||||||
.whereArgs(obj.id)
|
.whereArgs(obj.id)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,8 @@ class LibraryManga : MangaImpl() {
|
|||||||
|
|
||||||
fun createHide(categoryId: Int, title: String): LibraryManga =
|
fun createHide(categoryId: Int, title: String): LibraryManga =
|
||||||
createBlank(categoryId).apply {
|
createBlank(categoryId).apply {
|
||||||
this.title = title
|
this.title = title
|
||||||
status = -1
|
status = -1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,13 +73,14 @@ interface Manga : SManga {
|
|||||||
genre?.split(",")?.map { it.trim().toLowerCase(Locale.US) } ?: emptyList()
|
genre?.split(",")?.map { it.trim().toLowerCase(Locale.US) } ?: emptyList()
|
||||||
return if (currentTags.any { tag -> tag.startsWith("japanese") || isMangaTag(tag) }) {
|
return if (currentTags.any { tag -> tag.startsWith("japanese") || isMangaTag(tag) }) {
|
||||||
TYPE_MANGA
|
TYPE_MANGA
|
||||||
} else if (currentTags.any { tag -> tag.startsWith("english") || isComicTag(tag) } || isComicSource(
|
} else if (currentTags.any { tag -> tag.startsWith("english") || isComicTag(tag) } ||
|
||||||
sourceName
|
isComicSource(sourceName)
|
||||||
)) {
|
) {
|
||||||
TYPE_COMIC
|
TYPE_COMIC
|
||||||
} else if (currentTags.any { tag ->
|
} else if (currentTags.any { tag ->
|
||||||
tag.startsWith("chinese") || isManhuaTag(tag)
|
tag.startsWith("chinese") || isManhuaTag(tag)
|
||||||
} || sourceName.contains("manhua", true)) {
|
} || sourceName.contains("manhua", true)
|
||||||
|
) {
|
||||||
TYPE_MANHUA
|
TYPE_MANHUA
|
||||||
} else if (currentTags.any { tag -> isManhwaTag(tag) } || isWebtoonSource(sourceName)) {
|
} else if (currentTags.any { tag -> isManhwaTag(tag) } || isWebtoonSource(sourceName)) {
|
||||||
TYPE_MANHWA
|
TYPE_MANHWA
|
||||||
|
@ -74,7 +74,8 @@ open class MangaImpl : Manga {
|
|||||||
|
|
||||||
override fun copyFrom(other: SManga) {
|
override fun copyFrom(other: SManga) {
|
||||||
if (other is MangaImpl && other::ogTitle.isInitialized &&
|
if (other is MangaImpl && other::ogTitle.isInitialized &&
|
||||||
!other.title.isBlank() && other.ogTitle != ogTitle) {
|
!other.title.isBlank() && other.ogTitle != ogTitle
|
||||||
|
) {
|
||||||
val oldTitle = ogTitle
|
val oldTitle = ogTitle
|
||||||
title = other.ogTitle
|
title = other.ogTitle
|
||||||
val db: DownloadManager by injectLazy()
|
val db: DownloadManager by injectLazy()
|
||||||
|
@ -10,20 +10,24 @@ import eu.kanade.tachiyomi.data.database.tables.CategoryTable
|
|||||||
interface CategoryQueries : DbProvider {
|
interface CategoryQueries : DbProvider {
|
||||||
|
|
||||||
fun getCategories() = db.get()
|
fun getCategories() = db.get()
|
||||||
.listOfObjects(Category::class.java)
|
.listOfObjects(Category::class.java)
|
||||||
.withQuery(Query.builder()
|
.withQuery(
|
||||||
.table(CategoryTable.TABLE)
|
Query.builder()
|
||||||
.orderBy(CategoryTable.COL_ORDER)
|
.table(CategoryTable.TABLE)
|
||||||
.build())
|
.orderBy(CategoryTable.COL_ORDER)
|
||||||
.prepare()
|
.build()
|
||||||
|
)
|
||||||
|
.prepare()
|
||||||
|
|
||||||
fun getCategoriesForManga(manga: Manga) = db.get()
|
fun getCategoriesForManga(manga: Manga) = db.get()
|
||||||
.listOfObjects(Category::class.java)
|
.listOfObjects(Category::class.java)
|
||||||
.withQuery(RawQuery.builder()
|
.withQuery(
|
||||||
.query(getCategoriesForMangaQuery())
|
RawQuery.builder()
|
||||||
.args(manga.id)
|
.query(getCategoriesForMangaQuery())
|
||||||
.build())
|
.args(manga.id)
|
||||||
.prepare()
|
.build()
|
||||||
|
)
|
||||||
|
.prepare()
|
||||||
|
|
||||||
fun insertCategory(category: Category) = db.put().`object`(category).prepare()
|
fun insertCategory(category: Category) = db.put().`object`(category).prepare()
|
||||||
|
|
||||||
|
@ -19,68 +19,82 @@ import java.util.Date
|
|||||||
interface ChapterQueries : DbProvider {
|
interface ChapterQueries : DbProvider {
|
||||||
|
|
||||||
fun getChapters(manga: Manga) = db.get()
|
fun getChapters(manga: Manga) = db.get()
|
||||||
.listOfObjects(Chapter::class.java)
|
.listOfObjects(Chapter::class.java)
|
||||||
.withQuery(Query.builder()
|
.withQuery(
|
||||||
.table(ChapterTable.TABLE)
|
Query.builder()
|
||||||
.where("${ChapterTable.COL_MANGA_ID} = ?")
|
.table(ChapterTable.TABLE)
|
||||||
.whereArgs(manga.id)
|
.where("${ChapterTable.COL_MANGA_ID} = ?")
|
||||||
.build())
|
.whereArgs(manga.id)
|
||||||
.prepare()
|
.build()
|
||||||
|
)
|
||||||
|
.prepare()
|
||||||
|
|
||||||
fun getRecentChapters(date: Date) = db.get()
|
fun getRecentChapters(date: Date) = db.get()
|
||||||
.listOfObjects(MangaChapter::class.java)
|
.listOfObjects(MangaChapter::class.java)
|
||||||
.withQuery(RawQuery.builder()
|
.withQuery(
|
||||||
.query(getRecentsQuery())
|
RawQuery.builder()
|
||||||
.args(date.time)
|
.query(getRecentsQuery())
|
||||||
.observesTables(ChapterTable.TABLE)
|
.args(date.time)
|
||||||
.build())
|
.observesTables(ChapterTable.TABLE)
|
||||||
.withGetResolver(MangaChapterGetResolver.INSTANCE)
|
.build()
|
||||||
.prepare()
|
)
|
||||||
|
.withGetResolver(MangaChapterGetResolver.INSTANCE)
|
||||||
|
.prepare()
|
||||||
|
|
||||||
fun getUpdatedManga(date: Date, search: String = "", endless: Boolean) = db.get()
|
fun getUpdatedManga(date: Date, search: String = "", endless: Boolean) = db.get()
|
||||||
.listOfObjects(MangaChapterHistory::class.java)
|
.listOfObjects(MangaChapterHistory::class.java)
|
||||||
.withQuery(RawQuery.builder()
|
.withQuery(
|
||||||
.query(getRecentsQueryDistinct(search.sqLite, endless))
|
RawQuery.builder()
|
||||||
.args(date.time)
|
.query(getRecentsQueryDistinct(search.sqLite, endless))
|
||||||
.observesTables(ChapterTable.TABLE)
|
.args(date.time)
|
||||||
.build())
|
.observesTables(ChapterTable.TABLE)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
|
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
|
||||||
.prepare()
|
.prepare()
|
||||||
|
|
||||||
fun getChapter(id: Long) = db.get()
|
fun getChapter(id: Long) = db.get()
|
||||||
.`object`(Chapter::class.java)
|
.`object`(Chapter::class.java)
|
||||||
.withQuery(Query.builder()
|
.withQuery(
|
||||||
.table(ChapterTable.TABLE)
|
Query.builder()
|
||||||
.where("${ChapterTable.COL_ID} = ?")
|
.table(ChapterTable.TABLE)
|
||||||
.whereArgs(id)
|
.where("${ChapterTable.COL_ID} = ?")
|
||||||
.build())
|
.whereArgs(id)
|
||||||
.prepare()
|
.build()
|
||||||
|
)
|
||||||
|
.prepare()
|
||||||
|
|
||||||
fun getChapter(url: String) = db.get()
|
fun getChapter(url: String) = db.get()
|
||||||
.`object`(Chapter::class.java)
|
.`object`(Chapter::class.java)
|
||||||
.withQuery(Query.builder()
|
.withQuery(
|
||||||
.table(ChapterTable.TABLE)
|
Query.builder()
|
||||||
.where("${ChapterTable.COL_URL} = ?")
|
.table(ChapterTable.TABLE)
|
||||||
.whereArgs(url)
|
.where("${ChapterTable.COL_URL} = ?")
|
||||||
.build())
|
.whereArgs(url)
|
||||||
.prepare()
|
.build()
|
||||||
|
)
|
||||||
|
.prepare()
|
||||||
|
|
||||||
fun getChapters(url: String) = db.get()
|
fun getChapters(url: String) = db.get()
|
||||||
.listOfObjects(Chapter::class.java)
|
.listOfObjects(Chapter::class.java)
|
||||||
.withQuery(Query.builder()
|
.withQuery(
|
||||||
.table(ChapterTable.TABLE)
|
Query.builder()
|
||||||
.where("${ChapterTable.COL_URL} = ?")
|
.table(ChapterTable.TABLE)
|
||||||
.whereArgs(url)
|
.where("${ChapterTable.COL_URL} = ?")
|
||||||
.build())
|
.whereArgs(url)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
.prepare()
|
.prepare()
|
||||||
|
|
||||||
fun getChapter(url: String, mangaId: Long) = db.get()
|
fun getChapter(url: String, mangaId: Long) = db.get()
|
||||||
.`object`(Chapter::class.java)
|
.`object`(Chapter::class.java)
|
||||||
.withQuery(Query.builder()
|
.withQuery(
|
||||||
.table(ChapterTable.TABLE)
|
Query.builder()
|
||||||
.where("${ChapterTable.COL_URL} = ? AND ${ChapterTable.COL_MANGA_ID} = ?")
|
.table(ChapterTable.TABLE)
|
||||||
.whereArgs(url, mangaId)
|
.where("${ChapterTable.COL_URL} = ? AND ${ChapterTable.COL_MANGA_ID} = ?")
|
||||||
.build())
|
.whereArgs(url, mangaId)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
.prepare()
|
.prepare()
|
||||||
|
|
||||||
fun insertChapter(chapter: Chapter) = db.put().`object`(chapter).prepare()
|
fun insertChapter(chapter: Chapter) = db.put().`object`(chapter).prepare()
|
||||||
@ -92,22 +106,22 @@ interface ChapterQueries : DbProvider {
|
|||||||
fun deleteChapters(chapters: List<Chapter>) = db.delete().objects(chapters).prepare()
|
fun deleteChapters(chapters: List<Chapter>) = db.delete().objects(chapters).prepare()
|
||||||
|
|
||||||
fun updateChaptersBackup(chapters: List<Chapter>) = db.put()
|
fun updateChaptersBackup(chapters: List<Chapter>) = db.put()
|
||||||
.objects(chapters)
|
.objects(chapters)
|
||||||
.withPutResolver(ChapterBackupPutResolver())
|
.withPutResolver(ChapterBackupPutResolver())
|
||||||
.prepare()
|
.prepare()
|
||||||
|
|
||||||
fun updateChapterProgress(chapter: Chapter) = db.put()
|
fun updateChapterProgress(chapter: Chapter) = db.put()
|
||||||
.`object`(chapter)
|
.`object`(chapter)
|
||||||
.withPutResolver(ChapterProgressPutResolver())
|
.withPutResolver(ChapterProgressPutResolver())
|
||||||
.prepare()
|
.prepare()
|
||||||
|
|
||||||
fun updateChaptersProgress(chapters: List<Chapter>) = db.put()
|
fun updateChaptersProgress(chapters: List<Chapter>) = db.put()
|
||||||
.objects(chapters)
|
.objects(chapters)
|
||||||
.withPutResolver(ChapterProgressPutResolver())
|
.withPutResolver(ChapterProgressPutResolver())
|
||||||
.prepare()
|
.prepare()
|
||||||
|
|
||||||
fun fixChaptersSourceOrder(chapters: List<Chapter>) = db.put()
|
fun fixChaptersSourceOrder(chapters: List<Chapter>) = db.put()
|
||||||
.objects(chapters)
|
.objects(chapters)
|
||||||
.withPutResolver(ChapterSourceOrderPutResolver())
|
.withPutResolver(ChapterSourceOrderPutResolver())
|
||||||
.prepare()
|
.prepare()
|
||||||
}
|
}
|
||||||
|
@ -26,14 +26,16 @@ interface HistoryQueries : DbProvider {
|
|||||||
* @offset offset the db by
|
* @offset offset the db by
|
||||||
*/
|
*/
|
||||||
fun getRecentManga(date: Date, offset: Int = 0, search: String = "") = db.get()
|
fun getRecentManga(date: Date, offset: Int = 0, search: String = "") = db.get()
|
||||||
.listOfObjects(MangaChapterHistory::class.java)
|
.listOfObjects(MangaChapterHistory::class.java)
|
||||||
.withQuery(RawQuery.builder()
|
.withQuery(
|
||||||
.query(getRecentMangasQuery(offset, search.sqLite))
|
RawQuery.builder()
|
||||||
.args(date.time)
|
.query(getRecentMangasQuery(offset, search.sqLite))
|
||||||
.observesTables(HistoryTable.TABLE)
|
.args(date.time)
|
||||||
.build())
|
.observesTables(HistoryTable.TABLE)
|
||||||
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
|
.build()
|
||||||
.prepare()
|
)
|
||||||
|
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
|
||||||
|
.prepare()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns history of recent manga containing last read chapter in 25s
|
* Returns history of recent manga containing last read chapter in 25s
|
||||||
@ -42,11 +44,13 @@ interface HistoryQueries : DbProvider {
|
|||||||
*/
|
*/
|
||||||
fun getRecentlyAdded(date: Date, search: String = "", endless: Boolean) = db.get()
|
fun getRecentlyAdded(date: Date, search: String = "", endless: Boolean) = db.get()
|
||||||
.listOfObjects(MangaChapterHistory::class.java)
|
.listOfObjects(MangaChapterHistory::class.java)
|
||||||
.withQuery(RawQuery.builder()
|
.withQuery(
|
||||||
.query(getRecentAdditionsQuery(search.sqLite, endless))
|
RawQuery.builder()
|
||||||
.args(date.time)
|
.query(getRecentAdditionsQuery(search.sqLite, endless))
|
||||||
.observesTables(MangaTable.TABLE)
|
.args(date.time)
|
||||||
.build())
|
.observesTables(MangaTable.TABLE)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
|
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
|
||||||
.prepare()
|
.prepare()
|
||||||
|
|
||||||
@ -57,11 +61,13 @@ interface HistoryQueries : DbProvider {
|
|||||||
*/
|
*/
|
||||||
fun getRecentMangaLimit(date: Date, limit: Int = 0, search: String = "") = db.get()
|
fun getRecentMangaLimit(date: Date, limit: Int = 0, search: String = "") = db.get()
|
||||||
.listOfObjects(MangaChapterHistory::class.java)
|
.listOfObjects(MangaChapterHistory::class.java)
|
||||||
.withQuery(RawQuery.builder()
|
.withQuery(
|
||||||
.query(getRecentMangasLimitQuery(limit, search.sqLite))
|
RawQuery.builder()
|
||||||
.args(date.time)
|
.query(getRecentMangasLimitQuery(limit, search.sqLite))
|
||||||
.observesTables(HistoryTable.TABLE)
|
.args(date.time)
|
||||||
.build())
|
.observesTables(HistoryTable.TABLE)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
|
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
|
||||||
.prepare()
|
.prepare()
|
||||||
|
|
||||||
@ -72,31 +78,37 @@ interface HistoryQueries : DbProvider {
|
|||||||
*/
|
*/
|
||||||
fun getRecentsWithUnread(date: Date, search: String = "", endless: Boolean) = db.get()
|
fun getRecentsWithUnread(date: Date, search: String = "", endless: Boolean) = db.get()
|
||||||
.listOfObjects(MangaChapterHistory::class.java)
|
.listOfObjects(MangaChapterHistory::class.java)
|
||||||
.withQuery(RawQuery.builder()
|
.withQuery(
|
||||||
.query(getRecentReadWithUnreadChapters(search.sqLite, endless))
|
RawQuery.builder()
|
||||||
.args(date.time)
|
.query(getRecentReadWithUnreadChapters(search.sqLite, endless))
|
||||||
.observesTables(HistoryTable.TABLE)
|
.args(date.time)
|
||||||
.build())
|
.observesTables(HistoryTable.TABLE)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
|
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
|
||||||
.prepare()
|
.prepare()
|
||||||
|
|
||||||
fun getHistoryByMangaId(mangaId: Long) = db.get()
|
fun getHistoryByMangaId(mangaId: Long) = db.get()
|
||||||
.listOfObjects(History::class.java)
|
.listOfObjects(History::class.java)
|
||||||
.withQuery(RawQuery.builder()
|
.withQuery(
|
||||||
.query(getHistoryByMangaId())
|
RawQuery.builder()
|
||||||
.args(mangaId)
|
.query(getHistoryByMangaId())
|
||||||
.observesTables(HistoryTable.TABLE)
|
.args(mangaId)
|
||||||
.build())
|
.observesTables(HistoryTable.TABLE)
|
||||||
.prepare()
|
.build()
|
||||||
|
)
|
||||||
|
.prepare()
|
||||||
|
|
||||||
fun getHistoryByChapterUrl(chapterUrl: String) = db.get()
|
fun getHistoryByChapterUrl(chapterUrl: String) = db.get()
|
||||||
.`object`(History::class.java)
|
.`object`(History::class.java)
|
||||||
.withQuery(RawQuery.builder()
|
.withQuery(
|
||||||
.query(getHistoryByChapterUrl())
|
RawQuery.builder()
|
||||||
.args(chapterUrl)
|
.query(getHistoryByChapterUrl())
|
||||||
.observesTables(HistoryTable.TABLE)
|
.args(chapterUrl)
|
||||||
.build())
|
.observesTables(HistoryTable.TABLE)
|
||||||
.prepare()
|
.build()
|
||||||
|
)
|
||||||
|
.prepare()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the history last read.
|
* Updates the history last read.
|
||||||
@ -104,9 +116,9 @@ interface HistoryQueries : DbProvider {
|
|||||||
* @param history history object
|
* @param history history object
|
||||||
*/
|
*/
|
||||||
fun updateHistoryLastRead(history: History) = db.put()
|
fun updateHistoryLastRead(history: History) = db.put()
|
||||||
.`object`(history)
|
.`object`(history)
|
||||||
.withPutResolver(HistoryLastReadPutResolver())
|
.withPutResolver(HistoryLastReadPutResolver())
|
||||||
.prepare()
|
.prepare()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the history last read.
|
* Updates the history last read.
|
||||||
@ -114,21 +126,25 @@ interface HistoryQueries : DbProvider {
|
|||||||
* @param historyList history object list
|
* @param historyList history object list
|
||||||
*/
|
*/
|
||||||
fun updateHistoryLastRead(historyList: List<History>) = db.put()
|
fun updateHistoryLastRead(historyList: List<History>) = db.put()
|
||||||
.objects(historyList)
|
.objects(historyList)
|
||||||
.withPutResolver(HistoryLastReadPutResolver())
|
.withPutResolver(HistoryLastReadPutResolver())
|
||||||
.prepare()
|
.prepare()
|
||||||
|
|
||||||
fun deleteHistory() = db.delete()
|
fun deleteHistory() = db.delete()
|
||||||
.byQuery(DeleteQuery.builder()
|
.byQuery(
|
||||||
.table(HistoryTable.TABLE)
|
DeleteQuery.builder()
|
||||||
.build())
|
.table(HistoryTable.TABLE)
|
||||||
.prepare()
|
.build()
|
||||||
|
)
|
||||||
|
.prepare()
|
||||||
|
|
||||||
fun deleteHistoryNoLastRead() = db.delete()
|
fun deleteHistoryNoLastRead() = db.delete()
|
||||||
.byQuery(DeleteQuery.builder()
|
.byQuery(
|
||||||
.table(HistoryTable.TABLE)
|
DeleteQuery.builder()
|
||||||
.where("${HistoryTable.COL_LAST_READ} = ?")
|
.table(HistoryTable.TABLE)
|
||||||
.whereArgs(0)
|
.where("${HistoryTable.COL_LAST_READ} = ?")
|
||||||
.build())
|
.whereArgs(0)
|
||||||
.prepare()
|
.build()
|
||||||
|
)
|
||||||
|
.prepare()
|
||||||
}
|
}
|
||||||
|
@ -15,12 +15,14 @@ interface MangaCategoryQueries : DbProvider {
|
|||||||
fun insertMangasCategories(mangasCategories: List<MangaCategory>) = db.put().objects(mangasCategories).prepare()
|
fun insertMangasCategories(mangasCategories: List<MangaCategory>) = db.put().objects(mangasCategories).prepare()
|
||||||
|
|
||||||
fun deleteOldMangasCategories(mangas: List<Manga>) = db.delete()
|
fun deleteOldMangasCategories(mangas: List<Manga>) = db.delete()
|
||||||
.byQuery(DeleteQuery.builder()
|
.byQuery(
|
||||||
.table(MangaCategoryTable.TABLE)
|
DeleteQuery.builder()
|
||||||
.where("${MangaCategoryTable.COL_MANGA_ID} IN (${Queries.placeholders(mangas.size)})")
|
.table(MangaCategoryTable.TABLE)
|
||||||
.whereArgs(*mangas.map { it.id }.toTypedArray())
|
.where("${MangaCategoryTable.COL_MANGA_ID} IN (${Queries.placeholders(mangas.size)})")
|
||||||
.build())
|
.whereArgs(*mangas.map { it.id }.toTypedArray())
|
||||||
.prepare()
|
.build()
|
||||||
|
)
|
||||||
|
.prepare()
|
||||||
|
|
||||||
fun setMangaCategories(mangasCategories: List<MangaCategory>, mangas: List<Manga>) {
|
fun setMangaCategories(mangasCategories: List<MangaCategory>, mangas: List<Manga>) {
|
||||||
db.inTransaction {
|
db.inTransaction {
|
||||||
|
@ -22,67 +22,77 @@ import eu.kanade.tachiyomi.data.database.tables.MangaTable
|
|||||||
interface MangaQueries : DbProvider {
|
interface MangaQueries : DbProvider {
|
||||||
|
|
||||||
fun getMangas() = db.get()
|
fun getMangas() = db.get()
|
||||||
.listOfObjects(Manga::class.java)
|
.listOfObjects(Manga::class.java)
|
||||||
.withQuery(Query.builder()
|
.withQuery(
|
||||||
.table(MangaTable.TABLE)
|
Query.builder()
|
||||||
.build())
|
.table(MangaTable.TABLE)
|
||||||
.prepare()
|
.build()
|
||||||
|
)
|
||||||
|
.prepare()
|
||||||
|
|
||||||
fun getLibraryMangas() = db.get()
|
fun getLibraryMangas() = db.get()
|
||||||
.listOfObjects(LibraryManga::class.java)
|
.listOfObjects(LibraryManga::class.java)
|
||||||
.withQuery(RawQuery.builder()
|
.withQuery(
|
||||||
.query(libraryQuery)
|
RawQuery.builder()
|
||||||
.observesTables(MangaTable.TABLE, ChapterTable.TABLE, MangaCategoryTable.TABLE, CategoryTable.TABLE)
|
.query(libraryQuery)
|
||||||
.build())
|
.observesTables(MangaTable.TABLE, ChapterTable.TABLE, MangaCategoryTable.TABLE, CategoryTable.TABLE)
|
||||||
.withGetResolver(LibraryMangaGetResolver.INSTANCE)
|
.build()
|
||||||
.prepare()
|
)
|
||||||
|
.withGetResolver(LibraryMangaGetResolver.INSTANCE)
|
||||||
|
.prepare()
|
||||||
|
|
||||||
fun getFavoriteMangas() = db.get()
|
fun getFavoriteMangas() = db.get()
|
||||||
.listOfObjects(Manga::class.java)
|
.listOfObjects(Manga::class.java)
|
||||||
.withQuery(Query.builder()
|
.withQuery(
|
||||||
.table(MangaTable.TABLE)
|
Query.builder()
|
||||||
.where("${MangaTable.COL_FAVORITE} = ?")
|
.table(MangaTable.TABLE)
|
||||||
.whereArgs(1)
|
.where("${MangaTable.COL_FAVORITE} = ?")
|
||||||
.orderBy(MangaTable.COL_TITLE)
|
.whereArgs(1)
|
||||||
.build())
|
.orderBy(MangaTable.COL_TITLE)
|
||||||
.prepare()
|
.build()
|
||||||
|
)
|
||||||
|
.prepare()
|
||||||
|
|
||||||
fun getManga(url: String, sourceId: Long) = db.get()
|
fun getManga(url: String, sourceId: Long) = db.get()
|
||||||
.`object`(Manga::class.java)
|
.`object`(Manga::class.java)
|
||||||
.withQuery(Query.builder()
|
.withQuery(
|
||||||
.table(MangaTable.TABLE)
|
Query.builder()
|
||||||
.where("${MangaTable.COL_URL} = ? AND ${MangaTable.COL_SOURCE} = ?")
|
.table(MangaTable.TABLE)
|
||||||
.whereArgs(url, sourceId)
|
.where("${MangaTable.COL_URL} = ? AND ${MangaTable.COL_SOURCE} = ?")
|
||||||
.build())
|
.whereArgs(url, sourceId)
|
||||||
.prepare()
|
.build()
|
||||||
|
)
|
||||||
|
.prepare()
|
||||||
|
|
||||||
fun getManga(id: Long) = db.get()
|
fun getManga(id: Long) = db.get()
|
||||||
.`object`(Manga::class.java)
|
.`object`(Manga::class.java)
|
||||||
.withQuery(Query.builder()
|
.withQuery(
|
||||||
.table(MangaTable.TABLE)
|
Query.builder()
|
||||||
.where("${MangaTable.COL_ID} = ?")
|
.table(MangaTable.TABLE)
|
||||||
.whereArgs(id)
|
.where("${MangaTable.COL_ID} = ?")
|
||||||
.build())
|
.whereArgs(id)
|
||||||
.prepare()
|
.build()
|
||||||
|
)
|
||||||
|
.prepare()
|
||||||
|
|
||||||
fun insertManga(manga: Manga) = db.put().`object`(manga).prepare()
|
fun insertManga(manga: Manga) = db.put().`object`(manga).prepare()
|
||||||
|
|
||||||
fun insertMangas(mangas: List<Manga>) = db.put().objects(mangas).prepare()
|
fun insertMangas(mangas: List<Manga>) = db.put().objects(mangas).prepare()
|
||||||
|
|
||||||
fun updateFlags(manga: Manga) = db.put()
|
fun updateFlags(manga: Manga) = db.put()
|
||||||
.`object`(manga)
|
.`object`(manga)
|
||||||
.withPutResolver(MangaFlagsPutResolver())
|
.withPutResolver(MangaFlagsPutResolver())
|
||||||
.prepare()
|
.prepare()
|
||||||
|
|
||||||
fun updateLastUpdated(manga: Manga) = db.put()
|
fun updateLastUpdated(manga: Manga) = db.put()
|
||||||
.`object`(manga)
|
.`object`(manga)
|
||||||
.withPutResolver(MangaLastUpdatedPutResolver())
|
.withPutResolver(MangaLastUpdatedPutResolver())
|
||||||
.prepare()
|
.prepare()
|
||||||
|
|
||||||
fun updateMangaFavorite(manga: Manga) = db.put()
|
fun updateMangaFavorite(manga: Manga) = db.put()
|
||||||
.`object`(manga)
|
.`object`(manga)
|
||||||
.withPutResolver(MangaFavoritePutResolver())
|
.withPutResolver(MangaFavoritePutResolver())
|
||||||
.prepare()
|
.prepare()
|
||||||
|
|
||||||
fun updateMangaAdded(manga: Manga) = db.put()
|
fun updateMangaAdded(manga: Manga) = db.put()
|
||||||
.`object`(manga)
|
.`object`(manga)
|
||||||
@ -90,14 +100,14 @@ interface MangaQueries : DbProvider {
|
|||||||
.prepare()
|
.prepare()
|
||||||
|
|
||||||
fun updateMangaViewer(manga: Manga) = db.put()
|
fun updateMangaViewer(manga: Manga) = db.put()
|
||||||
.`object`(manga)
|
.`object`(manga)
|
||||||
.withPutResolver(MangaViewerPutResolver())
|
.withPutResolver(MangaViewerPutResolver())
|
||||||
.prepare()
|
.prepare()
|
||||||
|
|
||||||
fun updateMangaTitle(manga: Manga) = db.put()
|
fun updateMangaTitle(manga: Manga) = db.put()
|
||||||
.`object`(manga)
|
.`object`(manga)
|
||||||
.withPutResolver(MangaTitlePutResolver())
|
.withPutResolver(MangaTitlePutResolver())
|
||||||
.prepare()
|
.prepare()
|
||||||
|
|
||||||
fun updateMangaInfo(manga: Manga) = db.put()
|
fun updateMangaInfo(manga: Manga) = db.put()
|
||||||
.`object`(manga)
|
.`object`(manga)
|
||||||
@ -114,27 +124,33 @@ interface MangaQueries : DbProvider {
|
|||||||
fun deleteMangas(mangas: List<Manga>) = db.delete().objects(mangas).prepare()
|
fun deleteMangas(mangas: List<Manga>) = db.delete().objects(mangas).prepare()
|
||||||
|
|
||||||
fun deleteMangasNotInLibrary() = db.delete()
|
fun deleteMangasNotInLibrary() = db.delete()
|
||||||
.byQuery(DeleteQuery.builder()
|
.byQuery(
|
||||||
.table(MangaTable.TABLE)
|
DeleteQuery.builder()
|
||||||
.where("${MangaTable.COL_FAVORITE} = ?")
|
.table(MangaTable.TABLE)
|
||||||
.whereArgs(0)
|
.where("${MangaTable.COL_FAVORITE} = ?")
|
||||||
.build())
|
.whereArgs(0)
|
||||||
.prepare()
|
.build()
|
||||||
|
)
|
||||||
|
.prepare()
|
||||||
|
|
||||||
fun deleteMangas() = db.delete()
|
fun deleteMangas() = db.delete()
|
||||||
.byQuery(DeleteQuery.builder()
|
.byQuery(
|
||||||
.table(MangaTable.TABLE)
|
DeleteQuery.builder()
|
||||||
.build())
|
.table(MangaTable.TABLE)
|
||||||
.prepare()
|
.build()
|
||||||
|
)
|
||||||
|
.prepare()
|
||||||
|
|
||||||
fun getLastReadManga() = db.get()
|
fun getLastReadManga() = db.get()
|
||||||
.listOfObjects(Manga::class.java)
|
.listOfObjects(Manga::class.java)
|
||||||
.withQuery(RawQuery.builder()
|
.withQuery(
|
||||||
.query(getLastReadMangaQuery())
|
RawQuery.builder()
|
||||||
.observesTables(MangaTable.TABLE)
|
.query(getLastReadMangaQuery())
|
||||||
.build())
|
.observesTables(MangaTable.TABLE)
|
||||||
.prepare()
|
.build()
|
||||||
|
)
|
||||||
|
.prepare()
|
||||||
|
|
||||||
fun getTotalChapterManga() = db.get().listOfObjects(Manga::class.java)
|
fun getTotalChapterManga() = db.get().listOfObjects(Manga::class.java)
|
||||||
.withQuery(RawQuery.builder().query(getTotalChapterMangaQuery()).observesTables(MangaTable.TABLE).build()).prepare()
|
.withQuery(RawQuery.builder().query(getTotalChapterMangaQuery()).observesTables(MangaTable.TABLE).build()).prepare()
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,8 @@ import eu.kanade.tachiyomi.data.database.tables.MangaTable as Manga
|
|||||||
/**
|
/**
|
||||||
* Query to get the manga from the library, with their categories and unread count.
|
* Query to get the manga from the library, with their categories and unread count.
|
||||||
*/
|
*/
|
||||||
val libraryQuery = """
|
val libraryQuery =
|
||||||
|
"""
|
||||||
SELECT M.*, COALESCE(MC.${MangaCategory.COL_CATEGORY_ID}, 0) AS ${Manga.COL_CATEGORY}
|
SELECT M.*, COALESCE(MC.${MangaCategory.COL_CATEGORY_ID}, 0) AS ${Manga.COL_CATEGORY}
|
||||||
FROM (
|
FROM (
|
||||||
SELECT ${Manga.TABLE}.*, COALESCE(C.unread, 0) AS ${Manga.COL_UNREAD}, COALESCE(R.hasread, 0) AS ${Manga.COL_HAS_READ}
|
SELECT ${Manga.TABLE}.*, COALESCE(C.unread, 0) AS ${Manga.COL_UNREAD}, COALESCE(R.hasread, 0) AS ${Manga.COL_HAS_READ}
|
||||||
@ -40,7 +41,8 @@ val libraryQuery = """
|
|||||||
/**
|
/**
|
||||||
* Query to get the recent chapters of manga from the library up to a date.
|
* Query to get the recent chapters of manga from the library up to a date.
|
||||||
*/
|
*/
|
||||||
fun getRecentsQuery() = """
|
fun getRecentsQuery() =
|
||||||
|
"""
|
||||||
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, * FROM ${Manga.TABLE} JOIN ${Chapter.TABLE}
|
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, * FROM ${Manga.TABLE} JOIN ${Chapter.TABLE}
|
||||||
ON ${Manga.TABLE}.${Manga.COL_ID} = ${Chapter.TABLE}.${Chapter.COL_MANGA_ID}
|
ON ${Manga.TABLE}.${Manga.COL_ID} = ${Chapter.TABLE}.${Chapter.COL_MANGA_ID}
|
||||||
WHERE ${Manga.COL_FAVORITE} = 1
|
WHERE ${Manga.COL_FAVORITE} = 1
|
||||||
@ -52,7 +54,8 @@ fun getRecentsQuery() = """
|
|||||||
/**
|
/**
|
||||||
* Query to get the recently added manga
|
* Query to get the recently added manga
|
||||||
*/
|
*/
|
||||||
fun getRecentAdditionsQuery(search: String, endless: Boolean) = """
|
fun getRecentAdditionsQuery(search: String, endless: Boolean) =
|
||||||
|
"""
|
||||||
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, * FROM ${Manga.TABLE}
|
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, * FROM ${Manga.TABLE}
|
||||||
WHERE ${Manga.COL_FAVORITE} = 1
|
WHERE ${Manga.COL_FAVORITE} = 1
|
||||||
AND ${Manga.COL_DATE_ADDED} > ?
|
AND ${Manga.COL_DATE_ADDED} > ?
|
||||||
@ -64,7 +67,8 @@ fun getRecentAdditionsQuery(search: String, endless: Boolean) = """
|
|||||||
/**
|
/**
|
||||||
* Query to get the manga with recently uploaded chapters
|
* Query to get the manga with recently uploaded chapters
|
||||||
*/
|
*/
|
||||||
fun getRecentsQueryDistinct(search: String, endless: Boolean) = """
|
fun getRecentsQueryDistinct(search: String, endless: Boolean) =
|
||||||
|
"""
|
||||||
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, ${Manga.TABLE}.*, ${Chapter.TABLE}.*
|
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, ${Manga.TABLE}.*, ${Chapter.TABLE}.*
|
||||||
FROM ${Manga.TABLE}
|
FROM ${Manga.TABLE}
|
||||||
JOIN ${Chapter.TABLE}
|
JOIN ${Chapter.TABLE}
|
||||||
@ -92,7 +96,8 @@ fun getRecentsQueryDistinct(search: String, endless: Boolean) = """
|
|||||||
* and are read after the given time period
|
* and are read after the given time period
|
||||||
* @return return limit is 25
|
* @return return limit is 25
|
||||||
*/
|
*/
|
||||||
fun getRecentMangasQuery(offset: Int = 0, search: String = "") = """
|
fun getRecentMangasQuery(offset: Int = 0, search: String = "") =
|
||||||
|
"""
|
||||||
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, ${Manga.TABLE}.*, ${Chapter.TABLE}.*, ${History.TABLE}.*
|
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, ${Manga.TABLE}.*, ${Chapter.TABLE}.*, ${History.TABLE}.*
|
||||||
FROM ${Manga.TABLE}
|
FROM ${Manga.TABLE}
|
||||||
JOIN ${Chapter.TABLE}
|
JOIN ${Chapter.TABLE}
|
||||||
@ -118,7 +123,8 @@ fun getRecentMangasQuery(offset: Int = 0, search: String = "") = """
|
|||||||
* The select statement returns all information of chapters that have the same id as the chapter in max_last_read
|
* The select statement returns all information of chapters that have the same id as the chapter in max_last_read
|
||||||
* and are read after the given time period
|
* and are read after the given time period
|
||||||
*/
|
*/
|
||||||
fun getRecentMangasLimitQuery(limit: Int = 25, search: String = "") = """
|
fun getRecentMangasLimitQuery(limit: Int = 25, search: String = "") =
|
||||||
|
"""
|
||||||
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, ${Manga.TABLE}.*, ${Chapter.TABLE}.*, ${History.TABLE}.*
|
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, ${Manga.TABLE}.*, ${Chapter.TABLE}.*, ${History.TABLE}.*
|
||||||
FROM ${Manga.TABLE}
|
FROM ${Manga.TABLE}
|
||||||
JOIN ${Chapter.TABLE}
|
JOIN ${Chapter.TABLE}
|
||||||
@ -145,7 +151,8 @@ fun getRecentMangasLimitQuery(limit: Int = 25, search: String = "") = """
|
|||||||
* The select statement returns all information of chapters that have the same id as the chapter in max_last_read
|
* The select statement returns all information of chapters that have the same id as the chapter in max_last_read
|
||||||
* and are read after the given time period
|
* and are read after the given time period
|
||||||
*/
|
*/
|
||||||
fun getRecentReadWithUnreadChapters(search: String = "", endless: Boolean) = """
|
fun getRecentReadWithUnreadChapters(search: String = "", endless: Boolean) =
|
||||||
|
"""
|
||||||
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, ${Manga.TABLE}.*, ${Chapter.TABLE}.*, ${History.TABLE}.*
|
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, ${Manga.TABLE}.*, ${Chapter.TABLE}.*, ${History.TABLE}.*
|
||||||
FROM (
|
FROM (
|
||||||
SELECT ${Manga.TABLE}.*
|
SELECT ${Manga.TABLE}.*
|
||||||
@ -178,7 +185,8 @@ fun getRecentReadWithUnreadChapters(search: String = "", endless: Boolean) = """
|
|||||||
${if (endless) "" else "LIMIT 8"}
|
${if (endless) "" else "LIMIT 8"}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
fun getHistoryByMangaId() = """
|
fun getHistoryByMangaId() =
|
||||||
|
"""
|
||||||
SELECT ${History.TABLE}.*
|
SELECT ${History.TABLE}.*
|
||||||
FROM ${History.TABLE}
|
FROM ${History.TABLE}
|
||||||
JOIN ${Chapter.TABLE}
|
JOIN ${Chapter.TABLE}
|
||||||
@ -186,7 +194,8 @@ fun getHistoryByMangaId() = """
|
|||||||
WHERE ${Chapter.TABLE}.${Chapter.COL_MANGA_ID} = ? AND ${History.TABLE}.${History.COL_CHAPTER_ID} = ${Chapter.TABLE}.${Chapter.COL_ID}
|
WHERE ${Chapter.TABLE}.${Chapter.COL_MANGA_ID} = ? AND ${History.TABLE}.${History.COL_CHAPTER_ID} = ${Chapter.TABLE}.${Chapter.COL_ID}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
fun getHistoryByChapterUrl() = """
|
fun getHistoryByChapterUrl() =
|
||||||
|
"""
|
||||||
SELECT ${History.TABLE}.*
|
SELECT ${History.TABLE}.*
|
||||||
FROM ${History.TABLE}
|
FROM ${History.TABLE}
|
||||||
JOIN ${Chapter.TABLE}
|
JOIN ${Chapter.TABLE}
|
||||||
@ -194,7 +203,8 @@ fun getHistoryByChapterUrl() = """
|
|||||||
WHERE ${Chapter.TABLE}.${Chapter.COL_URL} = ? AND ${History.TABLE}.${History.COL_CHAPTER_ID} = ${Chapter.TABLE}.${Chapter.COL_ID}
|
WHERE ${Chapter.TABLE}.${Chapter.COL_URL} = ? AND ${History.TABLE}.${History.COL_CHAPTER_ID} = ${Chapter.TABLE}.${Chapter.COL_ID}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
fun getLastReadMangaQuery() = """
|
fun getLastReadMangaQuery() =
|
||||||
|
"""
|
||||||
SELECT ${Manga.TABLE}.*, MAX(${History.TABLE}.${History.COL_LAST_READ}) AS max
|
SELECT ${Manga.TABLE}.*, MAX(${History.TABLE}.${History.COL_LAST_READ}) AS max
|
||||||
FROM ${Manga.TABLE}
|
FROM ${Manga.TABLE}
|
||||||
JOIN ${Chapter.TABLE}
|
JOIN ${Chapter.TABLE}
|
||||||
@ -206,7 +216,8 @@ fun getLastReadMangaQuery() = """
|
|||||||
ORDER BY max DESC
|
ORDER BY max DESC
|
||||||
"""
|
"""
|
||||||
|
|
||||||
fun getTotalChapterMangaQuery() = """
|
fun getTotalChapterMangaQuery() =
|
||||||
|
"""
|
||||||
SELECT ${Manga.TABLE}.*
|
SELECT ${Manga.TABLE}.*
|
||||||
FROM ${Manga.TABLE}
|
FROM ${Manga.TABLE}
|
||||||
JOIN ${Chapter.TABLE}
|
JOIN ${Chapter.TABLE}
|
||||||
@ -218,7 +229,8 @@ fun getTotalChapterMangaQuery() = """
|
|||||||
/**
|
/**
|
||||||
* Query to get the categories for a manga.
|
* Query to get the categories for a manga.
|
||||||
*/
|
*/
|
||||||
fun getCategoriesForMangaQuery() = """
|
fun getCategoriesForMangaQuery() =
|
||||||
|
"""
|
||||||
SELECT ${Category.TABLE}.* FROM ${Category.TABLE}
|
SELECT ${Category.TABLE}.* FROM ${Category.TABLE}
|
||||||
JOIN ${MangaCategory.TABLE} ON ${Category.TABLE}.${Category.COL_ID} =
|
JOIN ${MangaCategory.TABLE} ON ${Category.TABLE}.${Category.COL_ID} =
|
||||||
${MangaCategory.TABLE}.${MangaCategory.COL_CATEGORY_ID}
|
${MangaCategory.TABLE}.${MangaCategory.COL_CATEGORY_ID}
|
||||||
|
@ -10,35 +10,43 @@ interface SearchMetadataQueries : DbProvider {
|
|||||||
|
|
||||||
fun getSearchMetadataForManga(mangaId: Long) = db.get()
|
fun getSearchMetadataForManga(mangaId: Long) = db.get()
|
||||||
.`object`(SearchMetadata::class.java)
|
.`object`(SearchMetadata::class.java)
|
||||||
.withQuery(Query.builder()
|
.withQuery(
|
||||||
.table(SearchMetadataTable.TABLE)
|
Query.builder()
|
||||||
.where("${SearchMetadataTable.COL_MANGA_ID} = ?")
|
.table(SearchMetadataTable.TABLE)
|
||||||
.whereArgs(mangaId)
|
.where("${SearchMetadataTable.COL_MANGA_ID} = ?")
|
||||||
.build())
|
.whereArgs(mangaId)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
.prepare()
|
.prepare()
|
||||||
|
|
||||||
fun getSearchMetadata() = db.get()
|
fun getSearchMetadata() = db.get()
|
||||||
.listOfObjects(SearchMetadata::class.java)
|
.listOfObjects(SearchMetadata::class.java)
|
||||||
.withQuery(Query.builder()
|
.withQuery(
|
||||||
.table(SearchMetadataTable.TABLE)
|
Query.builder()
|
||||||
.build())
|
.table(SearchMetadataTable.TABLE)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
.prepare()
|
.prepare()
|
||||||
|
|
||||||
fun getSearchMetadataByIndexedExtra(extra: String) = db.get()
|
fun getSearchMetadataByIndexedExtra(extra: String) = db.get()
|
||||||
.listOfObjects(SearchMetadata::class.java)
|
.listOfObjects(SearchMetadata::class.java)
|
||||||
.withQuery(Query.builder()
|
.withQuery(
|
||||||
.table(SearchMetadataTable.TABLE)
|
Query.builder()
|
||||||
.where("${SearchMetadataTable.COL_INDEXED_EXTRA} = ?")
|
.table(SearchMetadataTable.TABLE)
|
||||||
.whereArgs(extra)
|
.where("${SearchMetadataTable.COL_INDEXED_EXTRA} = ?")
|
||||||
.build())
|
.whereArgs(extra)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
.prepare()
|
.prepare()
|
||||||
|
|
||||||
fun insertSearchMetadata(metadata: SearchMetadata) = db.put().`object`(metadata).prepare()
|
fun insertSearchMetadata(metadata: SearchMetadata) = db.put().`object`(metadata).prepare()
|
||||||
|
|
||||||
fun deleteSearchMetadata(metadata: SearchMetadata) = db.delete().`object`(metadata).prepare()
|
fun deleteSearchMetadata(metadata: SearchMetadata) = db.delete().`object`(metadata).prepare()
|
||||||
|
|
||||||
fun deleteAllSearchMetadata() = db.delete().byQuery(DeleteQuery.builder()
|
fun deleteAllSearchMetadata() = db.delete().byQuery(
|
||||||
.table(SearchMetadataTable.TABLE)
|
DeleteQuery.builder()
|
||||||
.build())
|
.table(SearchMetadataTable.TABLE)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
.prepare()
|
.prepare()
|
||||||
}
|
}
|
||||||
|
@ -11,23 +11,27 @@ import eu.kanade.tachiyomi.data.track.TrackService
|
|||||||
interface TrackQueries : DbProvider {
|
interface TrackQueries : DbProvider {
|
||||||
|
|
||||||
fun getTracks(manga: Manga) = db.get()
|
fun getTracks(manga: Manga) = db.get()
|
||||||
.listOfObjects(Track::class.java)
|
.listOfObjects(Track::class.java)
|
||||||
.withQuery(Query.builder()
|
.withQuery(
|
||||||
.table(TrackTable.TABLE)
|
Query.builder()
|
||||||
.where("${TrackTable.COL_MANGA_ID} = ?")
|
.table(TrackTable.TABLE)
|
||||||
.whereArgs(manga.id)
|
.where("${TrackTable.COL_MANGA_ID} = ?")
|
||||||
.build())
|
.whereArgs(manga.id)
|
||||||
.prepare()
|
.build()
|
||||||
|
)
|
||||||
|
.prepare()
|
||||||
|
|
||||||
fun insertTrack(track: Track) = db.put().`object`(track).prepare()
|
fun insertTrack(track: Track) = db.put().`object`(track).prepare()
|
||||||
|
|
||||||
fun insertTracks(tracks: List<Track>) = db.put().objects(tracks).prepare()
|
fun insertTracks(tracks: List<Track>) = db.put().objects(tracks).prepare()
|
||||||
|
|
||||||
fun deleteTrackForManga(manga: Manga, sync: TrackService) = db.delete()
|
fun deleteTrackForManga(manga: Manga, sync: TrackService) = db.delete()
|
||||||
.byQuery(DeleteQuery.builder()
|
.byQuery(
|
||||||
.table(TrackTable.TABLE)
|
DeleteQuery.builder()
|
||||||
.where("${TrackTable.COL_MANGA_ID} = ? AND ${TrackTable.COL_SYNC_ID} = ?")
|
.table(TrackTable.TABLE)
|
||||||
.whereArgs(manga.id, sync.id)
|
.where("${TrackTable.COL_MANGA_ID} = ? AND ${TrackTable.COL_SYNC_ID} = ?")
|
||||||
.build())
|
.whereArgs(manga.id, sync.id)
|
||||||
.prepare()
|
.build()
|
||||||
|
)
|
||||||
|
.prepare()
|
||||||
}
|
}
|
||||||
|
@ -20,10 +20,10 @@ class ChapterBackupPutResolver : PutResolver<Chapter>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun mapToUpdateQuery(chapter: Chapter) = UpdateQuery.builder()
|
fun mapToUpdateQuery(chapter: Chapter) = UpdateQuery.builder()
|
||||||
.table(ChapterTable.TABLE)
|
.table(ChapterTable.TABLE)
|
||||||
.where("${ChapterTable.COL_URL} = ?")
|
.where("${ChapterTable.COL_URL} = ?")
|
||||||
.whereArgs(chapter.url)
|
.whereArgs(chapter.url)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
fun mapToContentValues(chapter: Chapter) = ContentValues(3).apply {
|
fun mapToContentValues(chapter: Chapter) = ContentValues(3).apply {
|
||||||
put(ChapterTable.COL_READ, chapter.read)
|
put(ChapterTable.COL_READ, chapter.read)
|
||||||
|
@ -20,10 +20,10 @@ class ChapterProgressPutResolver : PutResolver<Chapter>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun mapToUpdateQuery(chapter: Chapter) = UpdateQuery.builder()
|
fun mapToUpdateQuery(chapter: Chapter) = UpdateQuery.builder()
|
||||||
.table(ChapterTable.TABLE)
|
.table(ChapterTable.TABLE)
|
||||||
.where("${ChapterTable.COL_ID} = ?")
|
.where("${ChapterTable.COL_ID} = ?")
|
||||||
.whereArgs(chapter.id)
|
.whereArgs(chapter.id)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
fun mapToContentValues(chapter: Chapter) = ContentValues(3).apply {
|
fun mapToContentValues(chapter: Chapter) = ContentValues(3).apply {
|
||||||
put(ChapterTable.COL_READ, chapter.read)
|
put(ChapterTable.COL_READ, chapter.read)
|
||||||
|
@ -20,10 +20,10 @@ class ChapterSourceOrderPutResolver : PutResolver<Chapter>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun mapToUpdateQuery(chapter: Chapter) = UpdateQuery.builder()
|
fun mapToUpdateQuery(chapter: Chapter) = UpdateQuery.builder()
|
||||||
.table(ChapterTable.TABLE)
|
.table(ChapterTable.TABLE)
|
||||||
.where("${ChapterTable.COL_URL} = ? AND ${ChapterTable.COL_MANGA_ID} = ?")
|
.where("${ChapterTable.COL_URL} = ? AND ${ChapterTable.COL_MANGA_ID} = ?")
|
||||||
.whereArgs(chapter.url, chapter.manga_id)
|
.whereArgs(chapter.url, chapter.manga_id)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
fun mapToContentValues(chapter: Chapter) = ContentValues(1).apply {
|
fun mapToContentValues(chapter: Chapter) = ContentValues(1).apply {
|
||||||
put(ChapterTable.COL_SOURCE_ORDER, chapter.source_order)
|
put(ChapterTable.COL_SOURCE_ORDER, chapter.source_order)
|
||||||
|
@ -19,11 +19,13 @@ class HistoryLastReadPutResolver : HistoryPutResolver() {
|
|||||||
override fun performPut(@NonNull db: StorIOSQLite, @NonNull history: History): PutResult = db.inTransactionReturn {
|
override fun performPut(@NonNull db: StorIOSQLite, @NonNull history: History): PutResult = db.inTransactionReturn {
|
||||||
val updateQuery = mapToUpdateQuery(history)
|
val updateQuery = mapToUpdateQuery(history)
|
||||||
|
|
||||||
val cursor = db.lowLevel().query(Query.builder()
|
val cursor = db.lowLevel().query(
|
||||||
|
Query.builder()
|
||||||
.table(updateQuery.table())
|
.table(updateQuery.table())
|
||||||
.where(updateQuery.where())
|
.where(updateQuery.where())
|
||||||
.whereArgs(updateQuery.whereArgs())
|
.whereArgs(updateQuery.whereArgs())
|
||||||
.build())
|
.build()
|
||||||
|
)
|
||||||
|
|
||||||
val putResult: PutResult
|
val putResult: PutResult
|
||||||
|
|
||||||
@ -48,10 +50,10 @@ class HistoryLastReadPutResolver : HistoryPutResolver() {
|
|||||||
* @param obj history object
|
* @param obj history object
|
||||||
*/
|
*/
|
||||||
override fun mapToUpdateQuery(obj: History) = UpdateQuery.builder()
|
override fun mapToUpdateQuery(obj: History) = UpdateQuery.builder()
|
||||||
.table(HistoryTable.TABLE)
|
.table(HistoryTable.TABLE)
|
||||||
.where("${HistoryTable.COL_CHAPTER_ID} = ?")
|
.where("${HistoryTable.COL_CHAPTER_ID} = ?")
|
||||||
.whereArgs(obj.chapter_id)
|
.whereArgs(obj.chapter_id)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create content query
|
* Create content query
|
||||||
|
@ -42,8 +42,8 @@ class MangaChapterHistoryGetResolver : DefaultGetResolver<MangaChapterHistory>()
|
|||||||
val chapter =
|
val chapter =
|
||||||
if (!cursor.isNull(cursor.getColumnIndex(ChapterTable.COL_MANGA_ID))) chapterResolver
|
if (!cursor.isNull(cursor.getColumnIndex(ChapterTable.COL_MANGA_ID))) chapterResolver
|
||||||
.mapFromCursor(
|
.mapFromCursor(
|
||||||
cursor
|
cursor
|
||||||
) else ChapterImpl()
|
) else ChapterImpl()
|
||||||
|
|
||||||
// Get history object
|
// Get history object
|
||||||
val history =
|
val history =
|
||||||
|
@ -20,10 +20,10 @@ class MangaFavoritePutResolver : PutResolver<Manga>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder()
|
fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder()
|
||||||
.table(MangaTable.TABLE)
|
.table(MangaTable.TABLE)
|
||||||
.where("${MangaTable.COL_ID} = ?")
|
.where("${MangaTable.COL_ID} = ?")
|
||||||
.whereArgs(manga.id)
|
.whereArgs(manga.id)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
fun mapToContentValues(manga: Manga) = ContentValues(1).apply {
|
fun mapToContentValues(manga: Manga) = ContentValues(1).apply {
|
||||||
put(MangaTable.COL_FAVORITE, manga.favorite)
|
put(MangaTable.COL_FAVORITE, manga.favorite)
|
||||||
|
@ -20,10 +20,10 @@ class MangaFlagsPutResolver : PutResolver<Manga>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder()
|
fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder()
|
||||||
.table(MangaTable.TABLE)
|
.table(MangaTable.TABLE)
|
||||||
.where("${MangaTable.COL_ID} = ?")
|
.where("${MangaTable.COL_ID} = ?")
|
||||||
.whereArgs(manga.id)
|
.whereArgs(manga.id)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
fun mapToContentValues(manga: Manga) = ContentValues(1).apply {
|
fun mapToContentValues(manga: Manga) = ContentValues(1).apply {
|
||||||
put(MangaTable.COL_CHAPTER_FLAGS, manga.chapter_flags)
|
put(MangaTable.COL_CHAPTER_FLAGS, manga.chapter_flags)
|
||||||
|
@ -20,10 +20,10 @@ class MangaLastUpdatedPutResolver : PutResolver<Manga>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder()
|
fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder()
|
||||||
.table(MangaTable.TABLE)
|
.table(MangaTable.TABLE)
|
||||||
.where("${MangaTable.COL_ID} = ?")
|
.where("${MangaTable.COL_ID} = ?")
|
||||||
.whereArgs(manga.id)
|
.whereArgs(manga.id)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
fun mapToContentValues(manga: Manga) = ContentValues(1).apply {
|
fun mapToContentValues(manga: Manga) = ContentValues(1).apply {
|
||||||
put(MangaTable.COL_LAST_UPDATE, manga.last_update)
|
put(MangaTable.COL_LAST_UPDATE, manga.last_update)
|
||||||
|
@ -20,10 +20,10 @@ class MangaTitlePutResolver : PutResolver<Manga>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder()
|
fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder()
|
||||||
.table(MangaTable.TABLE)
|
.table(MangaTable.TABLE)
|
||||||
.where("${MangaTable.COL_ID} = ?")
|
.where("${MangaTable.COL_ID} = ?")
|
||||||
.whereArgs(manga.id)
|
.whereArgs(manga.id)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
fun mapToContentValues(manga: Manga) = ContentValues(1).apply {
|
fun mapToContentValues(manga: Manga) = ContentValues(1).apply {
|
||||||
put(MangaTable.COL_TITLE, manga.title)
|
put(MangaTable.COL_TITLE, manga.title)
|
||||||
|
@ -20,10 +20,10 @@ class MangaViewerPutResolver : PutResolver<Manga>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder()
|
fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder()
|
||||||
.table(MangaTable.TABLE)
|
.table(MangaTable.TABLE)
|
||||||
.where("${MangaTable.COL_ID} = ?")
|
.where("${MangaTable.COL_ID} = ?")
|
||||||
.whereArgs(manga.id)
|
.whereArgs(manga.id)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
fun mapToContentValues(manga: Manga) = ContentValues(1).apply {
|
fun mapToContentValues(manga: Manga) = ContentValues(1).apply {
|
||||||
put(MangaTable.COL_VIEWER, manga.viewer)
|
put(MangaTable.COL_VIEWER, manga.viewer)
|
||||||
|
@ -15,7 +15,8 @@ object CategoryTable {
|
|||||||
const val COL_MANGA_ORDER = "manga_order"
|
const val COL_MANGA_ORDER = "manga_order"
|
||||||
|
|
||||||
val createTableQuery: String
|
val createTableQuery: String
|
||||||
get() = """CREATE TABLE $TABLE(
|
get() =
|
||||||
|
"""CREATE TABLE $TABLE(
|
||||||
$COL_ID INTEGER NOT NULL PRIMARY KEY,
|
$COL_ID INTEGER NOT NULL PRIMARY KEY,
|
||||||
$COL_NAME TEXT NOT NULL,
|
$COL_NAME TEXT NOT NULL,
|
||||||
$COL_ORDER INTEGER NOT NULL,
|
$COL_ORDER INTEGER NOT NULL,
|
||||||
|
@ -31,7 +31,8 @@ object ChapterTable {
|
|||||||
const val COL_SOURCE_ORDER = "source_order"
|
const val COL_SOURCE_ORDER = "source_order"
|
||||||
|
|
||||||
val createTableQuery: String
|
val createTableQuery: String
|
||||||
get() = """CREATE TABLE $TABLE(
|
get() =
|
||||||
|
"""CREATE TABLE $TABLE(
|
||||||
$COL_ID INTEGER NOT NULL PRIMARY KEY,
|
$COL_ID INTEGER NOT NULL PRIMARY KEY,
|
||||||
$COL_MANGA_ID INTEGER NOT NULL,
|
$COL_MANGA_ID INTEGER NOT NULL,
|
||||||
$COL_URL TEXT NOT NULL,
|
$COL_URL TEXT NOT NULL,
|
||||||
@ -54,7 +55,7 @@ object ChapterTable {
|
|||||||
|
|
||||||
val createUnreadChaptersIndexQuery: String
|
val createUnreadChaptersIndexQuery: String
|
||||||
get() = "CREATE INDEX ${TABLE}_unread_by_manga_index ON $TABLE($COL_MANGA_ID, $COL_READ) " +
|
get() = "CREATE INDEX ${TABLE}_unread_by_manga_index ON $TABLE($COL_MANGA_ID, $COL_READ) " +
|
||||||
"WHERE $COL_READ = 0"
|
"WHERE $COL_READ = 0"
|
||||||
|
|
||||||
val sourceOrderUpdateQuery: String
|
val sourceOrderUpdateQuery: String
|
||||||
get() = "ALTER TABLE $TABLE ADD COLUMN $COL_SOURCE_ORDER INTEGER DEFAULT 0"
|
get() = "ALTER TABLE $TABLE ADD COLUMN $COL_SOURCE_ORDER INTEGER DEFAULT 0"
|
||||||
|
@ -31,7 +31,8 @@ object HistoryTable {
|
|||||||
* query to create history table
|
* query to create history table
|
||||||
*/
|
*/
|
||||||
val createTableQuery: String
|
val createTableQuery: String
|
||||||
get() = """CREATE TABLE $TABLE(
|
get() =
|
||||||
|
"""CREATE TABLE $TABLE(
|
||||||
$COL_ID INTEGER NOT NULL PRIMARY KEY,
|
$COL_ID INTEGER NOT NULL PRIMARY KEY,
|
||||||
$COL_CHAPTER_ID INTEGER NOT NULL UNIQUE,
|
$COL_CHAPTER_ID INTEGER NOT NULL UNIQUE,
|
||||||
$COL_LAST_READ LONG,
|
$COL_LAST_READ LONG,
|
||||||
|
@ -11,7 +11,8 @@ object MangaCategoryTable {
|
|||||||
const val COL_CATEGORY_ID = "category_id"
|
const val COL_CATEGORY_ID = "category_id"
|
||||||
|
|
||||||
val createTableQuery: String
|
val createTableQuery: String
|
||||||
get() = """CREATE TABLE $TABLE(
|
get() =
|
||||||
|
"""CREATE TABLE $TABLE(
|
||||||
$COL_ID INTEGER NOT NULL PRIMARY KEY,
|
$COL_ID INTEGER NOT NULL PRIMARY KEY,
|
||||||
$COL_MANGA_ID INTEGER NOT NULL,
|
$COL_MANGA_ID INTEGER NOT NULL,
|
||||||
$COL_CATEGORY_ID INTEGER NOT NULL,
|
$COL_CATEGORY_ID INTEGER NOT NULL,
|
||||||
|
@ -45,7 +45,8 @@ object MangaTable {
|
|||||||
const val COL_DATE_ADDED = "date_added"
|
const val COL_DATE_ADDED = "date_added"
|
||||||
|
|
||||||
val createTableQuery: String
|
val createTableQuery: String
|
||||||
get() = """CREATE TABLE $TABLE(
|
get() =
|
||||||
|
"""CREATE TABLE $TABLE(
|
||||||
$COL_ID INTEGER NOT NULL PRIMARY KEY,
|
$COL_ID INTEGER NOT NULL PRIMARY KEY,
|
||||||
$COL_SOURCE INTEGER NOT NULL,
|
$COL_SOURCE INTEGER NOT NULL,
|
||||||
$COL_URL TEXT NOT NULL,
|
$COL_URL TEXT NOT NULL,
|
||||||
@ -71,7 +72,7 @@ object MangaTable {
|
|||||||
|
|
||||||
val createLibraryIndexQuery: String
|
val createLibraryIndexQuery: String
|
||||||
get() = "CREATE INDEX library_${COL_FAVORITE}_index ON $TABLE($COL_FAVORITE) " +
|
get() = "CREATE INDEX library_${COL_FAVORITE}_index ON $TABLE($COL_FAVORITE) " +
|
||||||
"WHERE $COL_FAVORITE = 1"
|
"WHERE $COL_FAVORITE = 1"
|
||||||
|
|
||||||
val addHideTitle: String
|
val addHideTitle: String
|
||||||
get() = "ALTER TABLE $TABLE ADD COLUMN $COL_HIDE_TITLE INTEGER DEFAULT 0"
|
get() = "ALTER TABLE $TABLE ADD COLUMN $COL_HIDE_TITLE INTEGER DEFAULT 0"
|
||||||
|
@ -15,7 +15,8 @@ object SearchMetadataTable {
|
|||||||
|
|
||||||
// Insane foreign, primary key to avoid touch manga table
|
// Insane foreign, primary key to avoid touch manga table
|
||||||
val createTableQuery: String
|
val createTableQuery: String
|
||||||
get() = """CREATE TABLE $TABLE(
|
get() =
|
||||||
|
"""CREATE TABLE $TABLE(
|
||||||
$COL_MANGA_ID INTEGER NOT NULL PRIMARY KEY,
|
$COL_MANGA_ID INTEGER NOT NULL PRIMARY KEY,
|
||||||
$COL_UPLOADER TEXT,
|
$COL_UPLOADER TEXT,
|
||||||
$COL_EXTRA TEXT NOT NULL,
|
$COL_EXTRA TEXT NOT NULL,
|
||||||
|
@ -27,7 +27,8 @@ object TrackTable {
|
|||||||
const val COL_TRACKING_URL = "remote_url"
|
const val COL_TRACKING_URL = "remote_url"
|
||||||
|
|
||||||
val createTableQuery: String
|
val createTableQuery: String
|
||||||
get() = """CREATE TABLE $TABLE(
|
get() =
|
||||||
|
"""CREATE TABLE $TABLE(
|
||||||
$COL_ID INTEGER NOT NULL PRIMARY KEY,
|
$COL_ID INTEGER NOT NULL PRIMARY KEY,
|
||||||
$COL_MANGA_ID INTEGER NOT NULL,
|
$COL_MANGA_ID INTEGER NOT NULL,
|
||||||
$COL_SYNC_ID INTEGER NOT NULL,
|
$COL_SYNC_ID INTEGER NOT NULL,
|
||||||
|
@ -251,7 +251,9 @@ class DownloadManager(val context: Context) {
|
|||||||
queue.remove(chapters)
|
queue.remove(chapters)
|
||||||
val chapterDirs =
|
val chapterDirs =
|
||||||
provider.findChapterDirs(chapters, manga, source) + provider.findTempChapterDirs(
|
provider.findChapterDirs(chapters, manga, source) + provider.findTempChapterDirs(
|
||||||
chapters, manga, source
|
chapters,
|
||||||
|
manga,
|
||||||
|
source
|
||||||
)
|
)
|
||||||
chapterDirs.forEach { it.delete() }
|
chapterDirs.forEach { it.delete() }
|
||||||
cache.removeChapters(chapters, manga)
|
cache.removeChapters(chapters, manga)
|
||||||
|
@ -24,7 +24,7 @@ internal class DownloadNotifier(private val context: Context) {
|
|||||||
*/
|
*/
|
||||||
private val notification by lazy {
|
private val notification by lazy {
|
||||||
NotificationCompat.Builder(context, Notifications.CHANNEL_DOWNLOADER)
|
NotificationCompat.Builder(context, Notifications.CHANNEL_DOWNLOADER)
|
||||||
.setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher))
|
.setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -78,16 +78,21 @@ internal class DownloadNotifier(private val context: Context) {
|
|||||||
setContentIntent(NotificationHandler.openDownloadManagerPendingActivity(context))
|
setContentIntent(NotificationHandler.openDownloadManagerPendingActivity(context))
|
||||||
isDownloading = true
|
isDownloading = true
|
||||||
// Pause action
|
// Pause action
|
||||||
addAction(R.drawable.ic_pause_24dp,
|
addAction(
|
||||||
|
R.drawable.ic_pause_24dp,
|
||||||
context.getString(R.string.pause),
|
context.getString(R.string.pause),
|
||||||
NotificationReceiver.pauseDownloadsPendingBroadcast(context))
|
NotificationReceiver.pauseDownloadsPendingBroadcast(context)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (download != null) {
|
if (download != null) {
|
||||||
val title = download.manga.title.chop(15)
|
val title = download.manga.title.chop(15)
|
||||||
val quotedTitle = Pattern.quote(title)
|
val quotedTitle = Pattern.quote(title)
|
||||||
val chapter = download.chapter.name.replaceFirst("$quotedTitle[\\s]*[-]*[\\s]*"
|
val chapter = download.chapter.name.replaceFirst(
|
||||||
.toRegex(RegexOption.IGNORE_CASE), "")
|
"$quotedTitle[\\s]*[-]*[\\s]*"
|
||||||
|
.toRegex(RegexOption.IGNORE_CASE),
|
||||||
|
""
|
||||||
|
)
|
||||||
setContentTitle("$title - $chapter".chop(30))
|
setContentTitle("$title - $chapter".chop(30))
|
||||||
setContentText(
|
setContentText(
|
||||||
context.getString(R.string.downloading)
|
context.getString(R.string.downloading)
|
||||||
@ -124,17 +129,21 @@ internal class DownloadNotifier(private val context: Context) {
|
|||||||
setContentIntent(NotificationHandler.openDownloadManagerPendingActivity(context))
|
setContentIntent(NotificationHandler.openDownloadManagerPendingActivity(context))
|
||||||
isDownloading = true
|
isDownloading = true
|
||||||
// Pause action
|
// Pause action
|
||||||
addAction(R.drawable.ic_pause_24dp,
|
addAction(
|
||||||
context.getString(R.string.pause),
|
R.drawable.ic_pause_24dp,
|
||||||
NotificationReceiver.pauseDownloadsPendingBroadcast(context))
|
context.getString(R.string.pause),
|
||||||
|
NotificationReceiver.pauseDownloadsPendingBroadcast(context)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val title = download.manga.title.chop(15)
|
val title = download.manga.title.chop(15)
|
||||||
val quotedTitle = Pattern.quote(title)
|
val quotedTitle = Pattern.quote(title)
|
||||||
val chapter = download.chapter.name.replaceFirst("$quotedTitle[\\s]*[-]*[\\s]*".toRegex(RegexOption.IGNORE_CASE), "")
|
val chapter = download.chapter.name.replaceFirst("$quotedTitle[\\s]*[-]*[\\s]*".toRegex(RegexOption.IGNORE_CASE), "")
|
||||||
setContentTitle("$title - $chapter".chop(30))
|
setContentTitle("$title - $chapter".chop(30))
|
||||||
setContentText(context.getString(R.string.downloading_progress)
|
setContentText(
|
||||||
.format(download.downloadedImages, download.pages!!.size))
|
context.getString(R.string.downloading_progress)
|
||||||
|
.format(download.downloadedImages, download.pages!!.size)
|
||||||
|
)
|
||||||
setStyle(null)
|
setStyle(null)
|
||||||
setProgress(download.pages!!.size, download.downloadedImages, false)
|
setProgress(download.pages!!.size, download.downloadedImages, false)
|
||||||
}
|
}
|
||||||
|
@ -161,13 +161,17 @@ class DownloadService : Service() {
|
|||||||
*/
|
*/
|
||||||
private fun listenNetworkChanges() {
|
private fun listenNetworkChanges() {
|
||||||
subscriptions += ReactiveNetwork.observeNetworkConnectivity(applicationContext)
|
subscriptions += ReactiveNetwork.observeNetworkConnectivity(applicationContext)
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe({ state -> onNetworkStateChanged(state)
|
.subscribe(
|
||||||
}, {
|
{ state ->
|
||||||
|
onNetworkStateChanged(state)
|
||||||
|
},
|
||||||
|
{
|
||||||
toast(R.string.could_not_download_chapter_can_try_again)
|
toast(R.string.could_not_download_chapter_can_try_again)
|
||||||
stopSelf()
|
stopSelf()
|
||||||
})
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -80,9 +80,9 @@ class DownloadStore(
|
|||||||
*/
|
*/
|
||||||
fun restore(): List<Download> {
|
fun restore(): List<Download> {
|
||||||
val objs = preferences.all
|
val objs = preferences.all
|
||||||
.mapNotNull { it.value as? String }
|
.mapNotNull { it.value as? String }
|
||||||
.mapNotNull { deserialize(it) }
|
.mapNotNull { deserialize(it) }
|
||||||
.sortedBy { it.order }
|
.sortedBy { it.order }
|
||||||
|
|
||||||
val downloads = mutableListOf<Download>()
|
val downloads = mutableListOf<Download>()
|
||||||
if (objs.isNotEmpty()) {
|
if (objs.isNotEmpty()) {
|
||||||
|
@ -288,23 +288,23 @@ class Downloader(
|
|||||||
val pageListObservable = if (download.pages == null) {
|
val pageListObservable = if (download.pages == null) {
|
||||||
// Pull page list from network and add them to download object
|
// Pull page list from network and add them to download object
|
||||||
download.source.fetchPageList(download.chapter).doOnNext { pages ->
|
download.source.fetchPageList(download.chapter).doOnNext { pages ->
|
||||||
if (pages.isEmpty()) {
|
if (pages.isEmpty()) {
|
||||||
throw Exception("Page list is empty")
|
throw Exception("Page list is empty")
|
||||||
}
|
|
||||||
download.pages = pages
|
|
||||||
}
|
}
|
||||||
|
download.pages = pages
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Or if the page list already exists, start from the file
|
// Or if the page list already exists, start from the file
|
||||||
Observable.just(download.pages!!)
|
Observable.just(download.pages!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
pageListObservable.doOnNext { _ ->
|
pageListObservable.doOnNext { _ ->
|
||||||
// Delete all temporary (unfinished) files
|
// Delete all temporary (unfinished) files
|
||||||
tmpDir.listFiles()?.filter { it.name!!.endsWith(".tmp") }?.forEach { it.delete() }
|
tmpDir.listFiles()?.filter { it.name!!.endsWith(".tmp") }?.forEach { it.delete() }
|
||||||
|
|
||||||
download.downloadedImages = 0
|
download.downloadedImages = 0
|
||||||
download.status = Download.DOWNLOADING
|
download.status = Download.DOWNLOADING
|
||||||
}
|
}
|
||||||
// Get all the URLs to the source images, fetch pages if necessary
|
// Get all the URLs to the source images, fetch pages if necessary
|
||||||
.flatMap { download.source.fetchAllImageUrlsFromPageList(it) }
|
.flatMap { download.source.fetchAllImageUrlsFromPageList(it) }
|
||||||
// Start downloading images, consider we can have downloaded images already
|
// Start downloading images, consider we can have downloaded images already
|
||||||
@ -447,7 +447,7 @@ class Downloader(
|
|||||||
private fun getImageExtension(response: Response, file: UniFile): String {
|
private fun getImageExtension(response: Response, file: UniFile): String {
|
||||||
// Read content type if available.
|
// Read content type if available.
|
||||||
val mime = response.body?.contentType()?.let { ct -> "${ct.type}/${ct.subtype}" }
|
val mime = response.body?.contentType()?.let { ct -> "${ct.type}/${ct.subtype}" }
|
||||||
// Else guess from the uri.
|
// Else guess from the uri.
|
||||||
?: context.contentResolver.getType(file.uri)
|
?: context.contentResolver.getType(file.uri)
|
||||||
// Else read magic numbers.
|
// Else read magic numbers.
|
||||||
?: ImageUtil.findImageType { file.openInputStream() }?.mime
|
?: ImageUtil.findImageType { file.openInputStream() }?.mime
|
||||||
|
@ -13,7 +13,7 @@ class DownloadQueue(
|
|||||||
private val store: DownloadStore,
|
private val store: DownloadStore,
|
||||||
private val queue: MutableList<Download> = CopyOnWriteArrayList<Download>()
|
private val queue: MutableList<Download> = CopyOnWriteArrayList<Download>()
|
||||||
) :
|
) :
|
||||||
List<Download> by queue {
|
List<Download> by queue {
|
||||||
|
|
||||||
private val statusSubject = PublishSubject.create<Download>()
|
private val statusSubject = PublishSubject.create<Download>()
|
||||||
|
|
||||||
@ -80,8 +80,8 @@ List<Download> by queue {
|
|||||||
fun getStatusObservable(): Observable<Download> = statusSubject.onBackpressureBuffer()
|
fun getStatusObservable(): Observable<Download> = statusSubject.onBackpressureBuffer()
|
||||||
|
|
||||||
fun getUpdatedObservable(): Observable<List<Download>> = updatedRelay.onBackpressureBuffer()
|
fun getUpdatedObservable(): Observable<List<Download>> = updatedRelay.onBackpressureBuffer()
|
||||||
.startWith(Unit)
|
.startWith(Unit)
|
||||||
.map { this }
|
.map { this }
|
||||||
|
|
||||||
private fun setPagesFor(download: Download) {
|
private fun setPagesFor(download: Download) {
|
||||||
if (download.status == Download.DOWNLOADING) {
|
if (download.status == Download.DOWNLOADING) {
|
||||||
@ -105,23 +105,23 @@ List<Download> by queue {
|
|||||||
|
|
||||||
fun getProgressObservable(): Observable<Download> {
|
fun getProgressObservable(): Observable<Download> {
|
||||||
return statusSubject.onBackpressureBuffer()
|
return statusSubject.onBackpressureBuffer()
|
||||||
.startWith(getActiveDownloads())
|
.startWith(getActiveDownloads())
|
||||||
.flatMap { download ->
|
.flatMap { download ->
|
||||||
if (download.status == Download.DOWNLOADING) {
|
if (download.status == Download.DOWNLOADING) {
|
||||||
val pageStatusSubject = PublishSubject.create<Int>()
|
val pageStatusSubject = PublishSubject.create<Int>()
|
||||||
setPagesSubject(download.pages, pageStatusSubject)
|
setPagesSubject(download.pages, pageStatusSubject)
|
||||||
downloadListeners.forEach { it.updateDownload(download) }
|
downloadListeners.forEach { it.updateDownload(download) }
|
||||||
return@flatMap pageStatusSubject
|
return@flatMap pageStatusSubject
|
||||||
.onBackpressureBuffer()
|
.onBackpressureBuffer()
|
||||||
.filter { it == Page.READY }
|
.filter { it == Page.READY }
|
||||||
.map { download }
|
.map { download }
|
||||||
} else if (download.status == Download.DOWNLOADED || download.status == Download.ERROR) {
|
} else if (download.status == Download.DOWNLOADED || download.status == Download.ERROR) {
|
||||||
setPagesSubject(download.pages, null)
|
setPagesSubject(download.pages, null)
|
||||||
downloadListeners.forEach { it.updateDownload(download) }
|
downloadListeners.forEach { it.updateDownload(download) }
|
||||||
}
|
|
||||||
Observable.just(download)
|
|
||||||
}
|
}
|
||||||
.filter { it.status == Download.DOWNLOADING }
|
Observable.just(download)
|
||||||
|
}
|
||||||
|
.filter { it.status == Download.DOWNLOADING }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setPagesSubject(pages: List<Page>?, subject: PublishSubject<Int>?) {
|
private fun setPagesSubject(pages: List<Page>?, subject: PublishSubject<Int>?) {
|
||||||
|
@ -20,7 +20,9 @@ class CoverViewTarget(
|
|||||||
progress?.gone()
|
progress?.gone()
|
||||||
view.scaleType = ImageView.ScaleType.CENTER
|
view.scaleType = ImageView.ScaleType.CENTER
|
||||||
val vector = VectorDrawableCompat.create(
|
val vector = VectorDrawableCompat.create(
|
||||||
view.context.resources, R.drawable.ic_broken_image_24dp, null
|
view.context.resources,
|
||||||
|
R.drawable.ic_broken_image_24dp,
|
||||||
|
null
|
||||||
)
|
)
|
||||||
vector?.setTint(view.context.getResourceColor(android.R.attr.textColorSecondary))
|
vector?.setTint(view.context.getResourceColor(android.R.attr.textColorSecondary))
|
||||||
view.setImageDrawable(vector)
|
view.setImageDrawable(vector)
|
||||||
|
@ -70,7 +70,8 @@ class MangaFetcher : Fetcher<Manga> {
|
|||||||
return fileLoader(coverFile)
|
return fileLoader(coverFile)
|
||||||
}
|
}
|
||||||
val (_, body) = awaitGetCall(
|
val (_, body) = awaitGetCall(
|
||||||
manga, if (manga.favorite) {
|
manga,
|
||||||
|
if (manga.favorite) {
|
||||||
!options.networkCachePolicy.readEnabled
|
!options.networkCachePolicy.readEnabled
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -29,7 +29,8 @@ class CustomMangaManager(val context: Context) {
|
|||||||
|
|
||||||
val json = try {
|
val json = try {
|
||||||
Gson().fromJson(
|
Gson().fromJson(
|
||||||
Scanner(editJson).useDelimiter("\\Z").next(), JsonObject::class.java
|
Scanner(editJson).useDelimiter("\\Z").next(),
|
||||||
|
JsonObject::class.java
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
null
|
null
|
||||||
@ -83,7 +84,12 @@ class CustomMangaManager(val context: Context) {
|
|||||||
|
|
||||||
fun Manga.toJson(): MangaJson {
|
fun Manga.toJson(): MangaJson {
|
||||||
return MangaJson(
|
return MangaJson(
|
||||||
id!!, title, author, artist, description, genre?.split(", ")?.toTypedArray()
|
id!!,
|
||||||
|
title,
|
||||||
|
author,
|
||||||
|
artist,
|
||||||
|
description,
|
||||||
|
genre?.split(", ")?.toTypedArray()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ import uy.kohesive.injekt.api.get
|
|||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class LibraryUpdateJob(private val context: Context, workerParams: WorkerParameters) :
|
class LibraryUpdateJob(private val context: Context, workerParams: WorkerParameters) :
|
||||||
Worker(context, workerParams) {
|
Worker(context, workerParams) {
|
||||||
|
|
||||||
override fun doWork(): Result {
|
override fun doWork(): Result {
|
||||||
LibraryUpdateService.start(context)
|
LibraryUpdateService.start(context)
|
||||||
@ -37,16 +37,19 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
|||||||
NetworkType.CONNECTED
|
NetworkType.CONNECTED
|
||||||
|
|
||||||
val constraints = Constraints.Builder()
|
val constraints = Constraints.Builder()
|
||||||
.setRequiredNetworkType(wifiRestriction)
|
.setRequiredNetworkType(wifiRestriction)
|
||||||
.setRequiresCharging(acRestriction)
|
.setRequiresCharging(acRestriction)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
val request = PeriodicWorkRequestBuilder<LibraryUpdateJob>(
|
val request = PeriodicWorkRequestBuilder<LibraryUpdateJob>(
|
||||||
interval.toLong(), TimeUnit.HOURS,
|
interval.toLong(),
|
||||||
10, TimeUnit.MINUTES)
|
TimeUnit.HOURS,
|
||||||
.addTag(TAG)
|
10,
|
||||||
.setConstraints(constraints)
|
TimeUnit.MINUTES
|
||||||
.build()
|
)
|
||||||
|
.addTag(TAG)
|
||||||
|
.setConstraints(constraints)
|
||||||
|
.build()
|
||||||
|
|
||||||
WorkManager.getInstance().enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.REPLACE, request)
|
WorkManager.getInstance().enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.REPLACE, request)
|
||||||
} else {
|
} else {
|
||||||
|
@ -131,60 +131,73 @@ class LibraryUpdateNotifier(private val context: Context) {
|
|||||||
val manga = it.key
|
val manga = it.key
|
||||||
val chapters = it.value
|
val chapters = it.value
|
||||||
val chapterNames = chapters.map { chapter -> chapter.name }
|
val chapterNames = chapters.map { chapter -> chapter.name }
|
||||||
notifications.add(Pair(context.notification(Notifications.CHANNEL_NEW_CHAPTERS) {
|
notifications.add(
|
||||||
setSmallIcon(R.drawable.ic_tachi)
|
Pair(
|
||||||
try {
|
context.notification(Notifications.CHANNEL_NEW_CHAPTERS) {
|
||||||
val request = GetRequest.Builder(context).data(manga)
|
setSmallIcon(R.drawable.ic_tachi)
|
||||||
.networkCachePolicy(CachePolicy.DISABLED)
|
try {
|
||||||
.transformations(CircleCropTransformation())
|
val request = GetRequest.Builder(context).data(manga)
|
||||||
.size(width = ICON_SIZE, height = ICON_SIZE).build()
|
.networkCachePolicy(CachePolicy.DISABLED)
|
||||||
|
.transformations(CircleCropTransformation())
|
||||||
|
.size(width = ICON_SIZE, height = ICON_SIZE).build()
|
||||||
|
|
||||||
Coil.imageLoader(context).execute(request).drawable?.let { drawable ->
|
Coil.imageLoader(context).execute(request).drawable?.let { drawable ->
|
||||||
setLargeIcon((drawable as BitmapDrawable).bitmap)
|
setLargeIcon((drawable as BitmapDrawable).bitmap)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
||||||
}
|
setContentTitle(manga.title)
|
||||||
setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
color = ContextCompat.getColor(context, R.color.colorAccent)
|
||||||
setContentTitle(manga.title)
|
val chaptersNames = if (chapterNames.size > MAX_CHAPTERS) {
|
||||||
color = ContextCompat.getColor(context, R.color.colorAccent)
|
"${chapterNames.take(MAX_CHAPTERS - 1).joinToString(", ")}, " +
|
||||||
val chaptersNames = if (chapterNames.size > MAX_CHAPTERS) {
|
context.resources.getQuantityString(
|
||||||
"${chapterNames.take(MAX_CHAPTERS - 1)
|
R.plurals.notification_and_n_more,
|
||||||
.joinToString(", ")}, " + context.resources.getQuantityString(
|
(chapterNames.size - (MAX_CHAPTERS - 1)),
|
||||||
R.plurals.notification_and_n_more,
|
(chapterNames.size - (MAX_CHAPTERS - 1))
|
||||||
(chapterNames.size - (MAX_CHAPTERS - 1)),
|
)
|
||||||
(chapterNames.size - (MAX_CHAPTERS - 1))
|
} else chapterNames.joinToString(", ")
|
||||||
)
|
setContentText(chaptersNames)
|
||||||
} else chapterNames.joinToString(", ")
|
setStyle(NotificationCompat.BigTextStyle().bigText(chaptersNames))
|
||||||
setContentText(chaptersNames)
|
priority = NotificationCompat.PRIORITY_HIGH
|
||||||
setStyle(NotificationCompat.BigTextStyle().bigText(chaptersNames))
|
setGroup(Notifications.GROUP_NEW_CHAPTERS)
|
||||||
priority = NotificationCompat.PRIORITY_HIGH
|
setContentIntent(
|
||||||
setGroup(Notifications.GROUP_NEW_CHAPTERS)
|
NotificationReceiver.openChapterPendingActivity(
|
||||||
setContentIntent(
|
context,
|
||||||
NotificationReceiver.openChapterPendingActivity(
|
manga,
|
||||||
context, manga, chapters.first()
|
chapters.first()
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
addAction(
|
||||||
|
R.drawable.ic_eye_24dp,
|
||||||
|
context.getString(R.string.mark_as_read),
|
||||||
|
NotificationReceiver.markAsReadPendingBroadcast(
|
||||||
|
context,
|
||||||
|
manga,
|
||||||
|
chapters,
|
||||||
|
Notifications.ID_NEW_CHAPTERS
|
||||||
|
)
|
||||||
|
)
|
||||||
|
addAction(
|
||||||
|
R.drawable.ic_book_24dp,
|
||||||
|
context.getString(R.string.view_chapters),
|
||||||
|
NotificationReceiver.openChapterPendingActivity(
|
||||||
|
context,
|
||||||
|
manga,
|
||||||
|
Notifications.ID_NEW_CHAPTERS
|
||||||
|
)
|
||||||
|
)
|
||||||
|
setAutoCancel(true)
|
||||||
|
},
|
||||||
|
manga.id.hashCode()
|
||||||
)
|
)
|
||||||
addAction(
|
)
|
||||||
R.drawable.ic_eye_24dp,
|
|
||||||
context.getString(R.string.mark_as_read),
|
|
||||||
NotificationReceiver.markAsReadPendingBroadcast(
|
|
||||||
context, manga, chapters, Notifications.ID_NEW_CHAPTERS
|
|
||||||
)
|
|
||||||
)
|
|
||||||
addAction(
|
|
||||||
R.drawable.ic_book_24dp,
|
|
||||||
context.getString(R.string.view_chapters),
|
|
||||||
NotificationReceiver.openChapterPendingActivity(
|
|
||||||
context, manga, Notifications.ID_NEW_CHAPTERS
|
|
||||||
)
|
|
||||||
)
|
|
||||||
setAutoCancel(true)
|
|
||||||
}, manga.id.hashCode()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationManagerCompat.from(context).apply {
|
NotificationManagerCompat.from(context).apply {
|
||||||
|
|
||||||
notify(Notifications.ID_NEW_CHAPTERS,
|
notify(
|
||||||
|
Notifications.ID_NEW_CHAPTERS,
|
||||||
context.notification(Notifications.CHANNEL_NEW_CHAPTERS) {
|
context.notification(Notifications.CHANNEL_NEW_CHAPTERS) {
|
||||||
setSmallIcon(R.drawable.ic_tachi)
|
setSmallIcon(R.drawable.ic_tachi)
|
||||||
setLargeIcon(notificationBitmap)
|
setLargeIcon(notificationBitmap)
|
||||||
@ -193,14 +206,18 @@ class LibraryUpdateNotifier(private val context: Context) {
|
|||||||
if (updates.size > 1) {
|
if (updates.size > 1) {
|
||||||
setContentText(
|
setContentText(
|
||||||
context.resources.getQuantityString(
|
context.resources.getQuantityString(
|
||||||
R.plurals.for_n_titles, updates.size, updates.size
|
R.plurals.for_n_titles,
|
||||||
|
updates.size,
|
||||||
|
updates.size
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
setStyle(
|
setStyle(
|
||||||
NotificationCompat.BigTextStyle()
|
NotificationCompat.BigTextStyle()
|
||||||
.bigText(updates.keys.joinToString("\n") {
|
.bigText(
|
||||||
it.title.chop(45)
|
updates.keys.joinToString("\n") {
|
||||||
})
|
it.title.chop(45)
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
setContentText(updates.keys.first().title.chop(45))
|
setContentText(updates.keys.first().title.chop(45))
|
||||||
@ -211,7 +228,8 @@ class LibraryUpdateNotifier(private val context: Context) {
|
|||||||
setGroupSummary(true)
|
setGroupSummary(true)
|
||||||
setContentIntent(getNotificationIntent())
|
setContentIntent(getNotificationIntent())
|
||||||
setAutoCancel(true)
|
setAutoCancel(true)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
notifications.forEach {
|
notifications.forEach {
|
||||||
notify(it.second, it.first)
|
notify(it.second, it.first)
|
||||||
|
@ -8,8 +8,9 @@ import eu.kanade.tachiyomi.data.database.models.Manga
|
|||||||
object LibraryUpdateRanker {
|
object LibraryUpdateRanker {
|
||||||
|
|
||||||
val rankingScheme = listOf(
|
val rankingScheme = listOf(
|
||||||
(this::lexicographicRanking)(),
|
(this::lexicographicRanking)(),
|
||||||
(this::latestFirstRanking)())
|
(this::latestFirstRanking)()
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a total ordering over all the Mangas.
|
* Provides a total ordering over all the Mangas.
|
||||||
@ -22,7 +23,7 @@ object LibraryUpdateRanker {
|
|||||||
*/
|
*/
|
||||||
fun latestFirstRanking(): Comparator<Manga> {
|
fun latestFirstRanking(): Comparator<Manga> {
|
||||||
return Comparator { mangaFirst: Manga,
|
return Comparator { mangaFirst: Manga,
|
||||||
mangaSecond: Manga ->
|
mangaSecond: Manga ->
|
||||||
compareValues(mangaSecond.last_update, mangaFirst.last_update)
|
compareValues(mangaSecond.last_update, mangaFirst.last_update)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -35,7 +36,7 @@ object LibraryUpdateRanker {
|
|||||||
*/
|
*/
|
||||||
fun lexicographicRanking(): Comparator<Manga> {
|
fun lexicographicRanking(): Comparator<Manga> {
|
||||||
return Comparator { mangaFirst: Manga,
|
return Comparator { mangaFirst: Manga,
|
||||||
mangaSecond: Manga ->
|
mangaSecond: Manga ->
|
||||||
compareValues(mangaFirst.title, mangaSecond.title)
|
compareValues(mangaFirst.title, mangaSecond.title)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,16 +134,18 @@ class LibraryUpdateService(
|
|||||||
val selectedScheme = preferences.libraryUpdatePrioritization().getOrDefault()
|
val selectedScheme = preferences.libraryUpdatePrioritization().getOrDefault()
|
||||||
val savedMangasList = intent.getLongArrayExtra(KEY_MANGAS)?.asList()
|
val savedMangasList = intent.getLongArrayExtra(KEY_MANGAS)?.asList()
|
||||||
|
|
||||||
val mangaList = (if (savedMangasList != null) {
|
val mangaList = (
|
||||||
val mangas = db.getLibraryMangas().executeAsBlocking().filter {
|
if (savedMangasList != null) {
|
||||||
it.id in savedMangasList
|
val mangas = db.getLibraryMangas().executeAsBlocking().filter {
|
||||||
}.distinctBy { it.id }
|
it.id in savedMangasList
|
||||||
val categoryId = intent.getIntExtra(KEY_CATEGORY, -1)
|
}.distinctBy { it.id }
|
||||||
if (categoryId > -1) categoryIds.add(categoryId)
|
val categoryId = intent.getIntExtra(KEY_CATEGORY, -1)
|
||||||
mangas
|
if (categoryId > -1) categoryIds.add(categoryId)
|
||||||
} else {
|
mangas
|
||||||
getMangaToUpdate(intent, target)
|
} else {
|
||||||
}).sortedWith(rankingScheme[selectedScheme])
|
getMangaToUpdate(intent, target)
|
||||||
|
}
|
||||||
|
).sortedWith(rankingScheme[selectedScheme])
|
||||||
// Update favorite manga. Destroy service when completed or in case of an error.
|
// Update favorite manga. Destroy service when completed or in case of an error.
|
||||||
launchTarget(target, mangaList, startId)
|
launchTarget(target, mangaList, startId)
|
||||||
return START_REDELIVER_INTENT
|
return START_REDELIVER_INTENT
|
||||||
@ -157,7 +159,8 @@ class LibraryUpdateService(
|
|||||||
super.onCreate()
|
super.onCreate()
|
||||||
notifier = LibraryUpdateNotifier(this)
|
notifier = LibraryUpdateNotifier(this)
|
||||||
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(
|
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(
|
||||||
PowerManager.PARTIAL_WAKE_LOCK, "LibraryUpdateService:WakeLock"
|
PowerManager.PARTIAL_WAKE_LOCK,
|
||||||
|
"LibraryUpdateService:WakeLock"
|
||||||
)
|
)
|
||||||
wakeLock.acquire(TimeUnit.MINUTES.toMillis(30))
|
wakeLock.acquire(TimeUnit.MINUTES.toMillis(30))
|
||||||
startForeground(Notifications.ID_LIBRARY_PROGRESS, notifier.progressNotificationBuilder.build())
|
startForeground(Notifications.ID_LIBRARY_PROGRESS, notifier.progressNotificationBuilder.build())
|
||||||
@ -247,7 +250,9 @@ class LibraryUpdateService(
|
|||||||
val hasDLs = try {
|
val hasDLs = try {
|
||||||
requestSemaphore.withPermit {
|
requestSemaphore.withPermit {
|
||||||
updateMangaInSource(
|
updateMangaInSource(
|
||||||
it.key, downloadNew, categoriesToDownload
|
it.key,
|
||||||
|
downloadNew,
|
||||||
|
categoriesToDownload
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@ -351,13 +356,15 @@ class LibraryUpdateService(
|
|||||||
var hasDownloads = false
|
var hasDownloads = false
|
||||||
while (count < mangaToUpdateMap[source]!!.size) {
|
while (count < mangaToUpdateMap[source]!!.size) {
|
||||||
val shouldDownload =
|
val shouldDownload =
|
||||||
(downloadNew && (categoriesToDownload.isEmpty() || mangaToUpdateMap[source]!![count].category in categoriesToDownload || db.getCategoriesForManga(
|
(
|
||||||
mangaToUpdateMap[source]!![count]
|
downloadNew && (
|
||||||
).executeOnIO().any { (it.id ?: -1) in categoriesToDownload }))
|
categoriesToDownload.isEmpty() ||
|
||||||
if (updateMangaChapters(
|
mangaToUpdateMap[source]!![count].category in categoriesToDownload ||
|
||||||
mangaToUpdateMap[source]!![count], this.count.andIncrement, shouldDownload
|
db.getCategoriesForManga(mangaToUpdateMap[source]!![count])
|
||||||
)
|
.executeOnIO().any { (it.id ?: -1) in categoriesToDownload }
|
||||||
) {
|
)
|
||||||
|
)
|
||||||
|
if (updateMangaChapters(mangaToUpdateMap[source]!![count], this.count.andIncrement, shouldDownload)) {
|
||||||
hasDownloads = true
|
hasDownloads = true
|
||||||
}
|
}
|
||||||
count++
|
count++
|
||||||
@ -372,47 +379,47 @@ class LibraryUpdateService(
|
|||||||
shouldDownload: Boolean
|
shouldDownload: Boolean
|
||||||
):
|
):
|
||||||
Boolean {
|
Boolean {
|
||||||
try {
|
try {
|
||||||
var hasDownloads = false
|
var hasDownloads = false
|
||||||
if (job?.isCancelled == true) {
|
if (job?.isCancelled == true) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
notifier.showProgressNotification(manga, progress, mangaToUpdate.size)
|
||||||
|
val source = sourceManager.get(manga.source) as? HttpSource ?: return false
|
||||||
|
val fetchedChapters = withContext(Dispatchers.IO) {
|
||||||
|
source.fetchChapterList(manga).toBlocking().single()
|
||||||
|
} ?: emptyList()
|
||||||
|
if (fetchedChapters.isNotEmpty()) {
|
||||||
|
val newChapters = syncChaptersWithSource(db, fetchedChapters, manga, source)
|
||||||
|
if (newChapters.first.isNotEmpty()) {
|
||||||
|
if (shouldDownload) {
|
||||||
|
downloadChapters(manga, newChapters.first.sortedBy { it.chapter_number })
|
||||||
|
hasDownloads = true
|
||||||
|
}
|
||||||
|
newUpdates[manga] =
|
||||||
|
newChapters.first.sortedBy { it.chapter_number }.toTypedArray()
|
||||||
|
}
|
||||||
|
if (deleteRemoved && newChapters.second.isNotEmpty()) {
|
||||||
|
val removedChapters = newChapters.second.filter {
|
||||||
|
downloadManager.isChapterDownloaded(it, manga)
|
||||||
|
}
|
||||||
|
if (removedChapters.isNotEmpty()) {
|
||||||
|
downloadManager.deleteChapters(removedChapters, manga, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newChapters.first.size + newChapters.second.size > 0) listener?.onUpdateManga(
|
||||||
|
manga
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return hasDownloads
|
||||||
|
} catch (e: Exception) {
|
||||||
|
if (e !is CancellationException) {
|
||||||
|
failedUpdates[manga] = e.message
|
||||||
|
Timber.e("Failed updating: ${manga.title}: $e")
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
notifier.showProgressNotification(manga, progress, mangaToUpdate.size)
|
|
||||||
val source = sourceManager.get(manga.source) as? HttpSource ?: return false
|
|
||||||
val fetchedChapters = withContext(Dispatchers.IO) {
|
|
||||||
source.fetchChapterList(manga).toBlocking().single()
|
|
||||||
} ?: emptyList()
|
|
||||||
if (fetchedChapters.isNotEmpty()) {
|
|
||||||
val newChapters = syncChaptersWithSource(db, fetchedChapters, manga, source)
|
|
||||||
if (newChapters.first.isNotEmpty()) {
|
|
||||||
if (shouldDownload) {
|
|
||||||
downloadChapters(manga, newChapters.first.sortedBy { it.chapter_number })
|
|
||||||
hasDownloads = true
|
|
||||||
}
|
|
||||||
newUpdates[manga] =
|
|
||||||
newChapters.first.sortedBy { it.chapter_number }.toTypedArray()
|
|
||||||
}
|
|
||||||
if (deleteRemoved && newChapters.second.isNotEmpty()) {
|
|
||||||
val removedChapters = newChapters.second.filter {
|
|
||||||
downloadManager.isChapterDownloaded(it, manga)
|
|
||||||
}
|
|
||||||
if (removedChapters.isNotEmpty()) {
|
|
||||||
downloadManager.deleteChapters(removedChapters, manga, source)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (newChapters.first.size + newChapters.second.size > 0) listener?.onUpdateManga(
|
|
||||||
manga
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return hasDownloads
|
|
||||||
} catch (e: Exception) {
|
|
||||||
if (e !is CancellationException) {
|
|
||||||
failedUpdates[manga] = e.message
|
|
||||||
Timber.e("Failed updating: ${manga.title}: $e")
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun downloadChapters(manga: Manga, chapters: List<Chapter>) {
|
private fun downloadChapters(manga: Manga, chapters: List<Chapter>) {
|
||||||
// We don't want to start downloading while the library is updating, because websites
|
// We don't want to start downloading while the library is updating, because websites
|
||||||
|
@ -58,29 +58,41 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
// Clear the download queue
|
// Clear the download queue
|
||||||
ACTION_CLEAR_DOWNLOADS -> downloadManager.clearQueue(true)
|
ACTION_CLEAR_DOWNLOADS -> downloadManager.clearQueue(true)
|
||||||
// Launch share activity and dismiss notification
|
// Launch share activity and dismiss notification
|
||||||
ACTION_SHARE_IMAGE -> shareImage(context, intent.getStringExtra(EXTRA_FILE_LOCATION),
|
ACTION_SHARE_IMAGE -> shareImage(
|
||||||
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1))
|
context,
|
||||||
|
intent.getStringExtra(EXTRA_FILE_LOCATION),
|
||||||
|
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1)
|
||||||
|
)
|
||||||
// Delete image from path and dismiss notification
|
// Delete image from path and dismiss notification
|
||||||
ACTION_DELETE_IMAGE -> deleteImage(context, intent.getStringExtra(EXTRA_FILE_LOCATION),
|
ACTION_DELETE_IMAGE -> deleteImage(
|
||||||
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1))
|
context,
|
||||||
|
intent.getStringExtra(EXTRA_FILE_LOCATION),
|
||||||
|
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1)
|
||||||
|
)
|
||||||
// Cancel library update and dismiss notification
|
// Cancel library update and dismiss notification
|
||||||
ACTION_CANCEL_LIBRARY_UPDATE -> cancelLibraryUpdate(context)
|
ACTION_CANCEL_LIBRARY_UPDATE -> cancelLibraryUpdate(context)
|
||||||
ACTION_CANCEL_RESTORE -> cancelRestoreUpdate(context)
|
ACTION_CANCEL_RESTORE -> cancelRestoreUpdate(context)
|
||||||
// Share backup file
|
// Share backup file
|
||||||
ACTION_SHARE_BACKUP ->
|
ACTION_SHARE_BACKUP ->
|
||||||
shareBackup(
|
shareBackup(
|
||||||
context, intent.getParcelableExtra(EXTRA_URI),
|
context,
|
||||||
|
intent.getParcelableExtra(EXTRA_URI),
|
||||||
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1)
|
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1)
|
||||||
)
|
)
|
||||||
// Open reader activity
|
// Open reader activity
|
||||||
ACTION_OPEN_CHAPTER -> {
|
ACTION_OPEN_CHAPTER -> {
|
||||||
openChapter(context, intent.getLongExtra(EXTRA_MANGA_ID, -1),
|
openChapter(
|
||||||
intent.getLongExtra(EXTRA_CHAPTER_ID, -1))
|
context,
|
||||||
|
intent.getLongExtra(EXTRA_MANGA_ID, -1),
|
||||||
|
intent.getLongExtra(EXTRA_CHAPTER_ID, -1)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
ACTION_MARK_AS_READ -> {
|
ACTION_MARK_AS_READ -> {
|
||||||
val notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1)
|
val notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1)
|
||||||
if (notificationId > -1) dismissNotification(
|
if (notificationId > -1) dismissNotification(
|
||||||
context, notificationId, intent.getIntExtra(EXTRA_GROUP_ID, 0)
|
context,
|
||||||
|
notificationId,
|
||||||
|
intent.getIntExtra(EXTRA_GROUP_ID, 0)
|
||||||
)
|
)
|
||||||
val urls = intent.getStringArrayExtra(EXTRA_CHAPTER_URL) ?: return
|
val urls = intent.getStringArrayExtra(EXTRA_CHAPTER_URL) ?: return
|
||||||
val mangaId = intent.getLongExtra(EXTRA_MANGA_ID, -1)
|
val mangaId = intent.getLongExtra(EXTRA_MANGA_ID, -1)
|
||||||
@ -342,7 +354,7 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
context: Context,
|
context: Context,
|
||||||
notificationId: Int,
|
notificationId: Int,
|
||||||
groupId: Int? =
|
groupId: Int? =
|
||||||
null
|
null
|
||||||
) {
|
) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
val groupKey = context.notificationManager.activeNotifications.find {
|
val groupKey = context.notificationManager.activeNotifications.find {
|
||||||
@ -377,8 +389,13 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
clipData = ClipData.newRawUri(null, uri)
|
clipData = ClipData.newRawUri(null, uri)
|
||||||
type = "image/*"
|
type = "image/*"
|
||||||
}
|
}
|
||||||
return PendingIntent.getActivity(context, 0, shareIntent, PendingIntent
|
return PendingIntent.getActivity(
|
||||||
.FLAG_CANCEL_CURRENT)
|
context,
|
||||||
|
0,
|
||||||
|
shareIntent,
|
||||||
|
PendingIntent
|
||||||
|
.FLAG_CANCEL_CURRENT
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -409,11 +426,16 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
context: Context,
|
context: Context,
|
||||||
manga: Manga,
|
manga: Manga,
|
||||||
chapter:
|
chapter:
|
||||||
Chapter
|
Chapter
|
||||||
): PendingIntent {
|
): PendingIntent {
|
||||||
val newIntent = ReaderActivity.newIntent(context, manga, chapter)
|
val newIntent = ReaderActivity.newIntent(context, manga, chapter)
|
||||||
return PendingIntent.getActivity(context, manga.id.hashCode(), newIntent, PendingIntent
|
return PendingIntent.getActivity(
|
||||||
.FLAG_UPDATE_CURRENT)
|
context,
|
||||||
|
manga.id.hashCode(),
|
||||||
|
newIntent,
|
||||||
|
PendingIntent
|
||||||
|
.FLAG_UPDATE_CURRENT
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -424,16 +446,19 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
*/
|
*/
|
||||||
internal fun openChapterPendingActivity(context: Context, manga: Manga, groupId: Int):
|
internal fun openChapterPendingActivity(context: Context, manga: Manga, groupId: Int):
|
||||||
PendingIntent {
|
PendingIntent {
|
||||||
val newIntent =
|
val newIntent =
|
||||||
Intent(context, MainActivity::class.java).setAction(MainActivity.SHORTCUT_MANGA)
|
Intent(context, MainActivity::class.java).setAction(MainActivity.SHORTCUT_MANGA)
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||||
.putExtra(MangaDetailsController.MANGA_EXTRA, manga.id)
|
.putExtra(MangaDetailsController.MANGA_EXTRA, manga.id)
|
||||||
.putExtra("notificationId", manga.id.hashCode())
|
.putExtra("notificationId", manga.id.hashCode())
|
||||||
.putExtra("groupId", groupId)
|
.putExtra("groupId", groupId)
|
||||||
return PendingIntent.getActivity(
|
return PendingIntent.getActivity(
|
||||||
context, manga.id.hashCode(), newIntent, PendingIntent.FLAG_UPDATE_CURRENT
|
context,
|
||||||
)
|
manga.id.hashCode(),
|
||||||
}
|
newIntent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns [PendingIntent] that opens the error log file in an external viewer
|
* Returns [PendingIntent] that opens the error log file in an external viewer
|
||||||
@ -462,7 +487,10 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
Intent(context, MainActivity::class.java).setAction(MainActivity.SHORTCUT_EXTENSIONS)
|
Intent(context, MainActivity::class.java).setAction(MainActivity.SHORTCUT_EXTENSIONS)
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||||
return PendingIntent.getActivity(
|
return PendingIntent.getActivity(
|
||||||
context, 0, newIntent, PendingIntent.FLAG_UPDATE_CURRENT
|
context,
|
||||||
|
0,
|
||||||
|
newIntent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -473,7 +501,7 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
val toLaunch = Intent(Intent.ACTION_VIEW).apply {
|
val toLaunch = Intent(Intent.ACTION_VIEW).apply {
|
||||||
setDataAndType(uri, "text/plain")
|
setDataAndType(uri, "text/plain")
|
||||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK or
|
||||||
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
}
|
}
|
||||||
return PendingIntent.getActivity(context, 0, toLaunch, 0)
|
return PendingIntent.getActivity(context, 0, toLaunch, 0)
|
||||||
}
|
}
|
||||||
@ -488,19 +516,19 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
context: Context,
|
context: Context,
|
||||||
manga: Manga,
|
manga: Manga,
|
||||||
chapters:
|
chapters:
|
||||||
Array<Chapter>,
|
Array<Chapter>,
|
||||||
groupId: Int
|
groupId: Int
|
||||||
):
|
):
|
||||||
PendingIntent {
|
PendingIntent {
|
||||||
val newIntent = Intent(context, NotificationReceiver::class.java).apply {
|
val newIntent = Intent(context, NotificationReceiver::class.java).apply {
|
||||||
action = ACTION_MARK_AS_READ
|
action = ACTION_MARK_AS_READ
|
||||||
putExtra(EXTRA_CHAPTER_URL, chapters.map { it.url }.toTypedArray())
|
putExtra(EXTRA_CHAPTER_URL, chapters.map { it.url }.toTypedArray())
|
||||||
putExtra(EXTRA_MANGA_ID, manga.id)
|
putExtra(EXTRA_MANGA_ID, manga.id)
|
||||||
putExtra(EXTRA_NOTIFICATION_ID, manga.id.hashCode())
|
putExtra(EXTRA_NOTIFICATION_ID, manga.id.hashCode())
|
||||||
putExtra(EXTRA_GROUP_ID, groupId)
|
putExtra(EXTRA_GROUP_ID, groupId)
|
||||||
|
}
|
||||||
|
return PendingIntent.getBroadcast(context, manga.id.hashCode(), newIntent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, manga.id.hashCode(), newIntent, PendingIntent.FLAG_UPDATE_CURRENT)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns [PendingIntent] that starts a service which stops the library update
|
* Returns [PendingIntent] that starts a service which stops the library update
|
||||||
|
@ -61,37 +61,44 @@ object Notifications {
|
|||||||
fun createChannels(context: Context) {
|
fun createChannels(context: Context) {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
||||||
|
|
||||||
val channels = listOf(NotificationChannel(
|
val channels = listOf(
|
||||||
CHANNEL_COMMON,
|
NotificationChannel(
|
||||||
context.getString(R.string.common),
|
CHANNEL_COMMON,
|
||||||
NotificationManager.IMPORTANCE_LOW
|
context.getString(R.string.common),
|
||||||
), NotificationChannel(
|
NotificationManager.IMPORTANCE_LOW
|
||||||
CHANNEL_LIBRARY,
|
),
|
||||||
context.getString(R.string.updating_library),
|
NotificationChannel(
|
||||||
NotificationManager.IMPORTANCE_LOW
|
CHANNEL_LIBRARY,
|
||||||
).apply {
|
context.getString(R.string.updating_library),
|
||||||
setShowBadge(false)
|
NotificationManager.IMPORTANCE_LOW
|
||||||
}, NotificationChannel(
|
).apply {
|
||||||
CHANNEL_DOWNLOADER,
|
setShowBadge(false)
|
||||||
context.getString(R.string.downloads),
|
},
|
||||||
NotificationManager.IMPORTANCE_LOW
|
NotificationChannel(
|
||||||
).apply {
|
CHANNEL_DOWNLOADER,
|
||||||
setShowBadge(false)
|
context.getString(R.string.downloads),
|
||||||
}, NotificationChannel(
|
NotificationManager.IMPORTANCE_LOW
|
||||||
CHANNEL_UPDATES_TO_EXTS,
|
).apply {
|
||||||
context.getString(R.string.extension_updates),
|
setShowBadge(false)
|
||||||
NotificationManager.IMPORTANCE_DEFAULT
|
},
|
||||||
), NotificationChannel(
|
NotificationChannel(
|
||||||
CHANNEL_NEW_CHAPTERS,
|
CHANNEL_UPDATES_TO_EXTS,
|
||||||
context.getString(R.string.new_chapters),
|
context.getString(R.string.extension_updates),
|
||||||
NotificationManager.IMPORTANCE_DEFAULT
|
NotificationManager.IMPORTANCE_DEFAULT
|
||||||
), NotificationChannel(
|
),
|
||||||
CHANNEL_BACKUP_RESTORE,
|
NotificationChannel(
|
||||||
context.getString(R.string.restoring_backup),
|
CHANNEL_NEW_CHAPTERS,
|
||||||
NotificationManager.IMPORTANCE_LOW
|
context.getString(R.string.new_chapters),
|
||||||
).apply {
|
NotificationManager.IMPORTANCE_DEFAULT
|
||||||
setShowBadge(false)
|
),
|
||||||
})
|
NotificationChannel(
|
||||||
|
CHANNEL_BACKUP_RESTORE,
|
||||||
|
context.getString(R.string.restoring_backup),
|
||||||
|
NotificationManager.IMPORTANCE_LOW
|
||||||
|
).apply {
|
||||||
|
setShowBadge(false)
|
||||||
|
}
|
||||||
|
)
|
||||||
context.notificationManager.createNotificationChannels(channels)
|
context.notificationManager.createNotificationChannels(channels)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,12 +43,20 @@ class PreferencesHelper(val context: Context) {
|
|||||||
private val flowPrefs = FlowSharedPreferences(prefs)
|
private val flowPrefs = FlowSharedPreferences(prefs)
|
||||||
|
|
||||||
private val defaultDownloadsDir = Uri.fromFile(
|
private val defaultDownloadsDir = Uri.fromFile(
|
||||||
File(Environment.getExternalStorageDirectory().absolutePath + File.separator +
|
File(
|
||||||
context.getString(R.string.app_name), "downloads"))
|
Environment.getExternalStorageDirectory().absolutePath + File.separator +
|
||||||
|
context.getString(R.string.app_name),
|
||||||
|
"downloads"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
private val defaultBackupDir = Uri.fromFile(
|
private val defaultBackupDir = Uri.fromFile(
|
||||||
File(Environment.getExternalStorageDirectory().absolutePath + File.separator +
|
File(
|
||||||
context.getString(R.string.app_name), "backup"))
|
Environment.getExternalStorageDirectory().absolutePath + File.separator +
|
||||||
|
context.getString(R.string.app_name),
|
||||||
|
"backup"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
fun getInt(key: String, default: Int?) = rxPrefs.getInteger(key, default)
|
fun getInt(key: String, default: Int?) = rxPrefs.getInteger(key, default)
|
||||||
fun getStringPref(key: String, default: String?) = rxPrefs.getString(key, default)
|
fun getStringPref(key: String, default: String?) = rxPrefs.getString(key, default)
|
||||||
@ -130,9 +138,9 @@ class PreferencesHelper(val context: Context) {
|
|||||||
|
|
||||||
fun setTrackCredentials(sync: TrackService, username: String, password: String) {
|
fun setTrackCredentials(sync: TrackService, username: String, password: String) {
|
||||||
prefs.edit()
|
prefs.edit()
|
||||||
.putString(Keys.trackUsername(sync.id), username)
|
.putString(Keys.trackUsername(sync.id), username)
|
||||||
.putString(Keys.trackPassword(sync.id), password)
|
.putString(Keys.trackPassword(sync.id), password)
|
||||||
.apply()
|
.apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun trackToken(sync: TrackService) = rxPrefs.getString(Keys.trackToken(sync.id), "")
|
fun trackToken(sync: TrackService) = rxPrefs.getString(Keys.trackToken(sync.id), "")
|
||||||
|
@ -60,7 +60,7 @@ abstract class TrackService(val id: Int) {
|
|||||||
|
|
||||||
open val isLogged: Boolean
|
open val isLogged: Boolean
|
||||||
get() = getUsername().isNotEmpty() &&
|
get() = getUsername().isNotEmpty() &&
|
||||||
getPassword().isNotEmpty()
|
getPassword().isNotEmpty()
|
||||||
|
|
||||||
fun getUsername() = preferences.trackUsername(this)!!
|
fun getUsername() = preferences.trackUsername(this)!!
|
||||||
|
|
||||||
|
@ -237,7 +237,8 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
|||||||
.appendQueryParameter("response_type", "token")
|
.appendQueryParameter("response_type", "token")
|
||||||
.build()!!
|
.build()!!
|
||||||
|
|
||||||
fun addToLibraryQuery() = """
|
fun addToLibraryQuery() =
|
||||||
|
"""
|
||||||
|mutation AddManga(${'$'}mangaId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus) {
|
|mutation AddManga(${'$'}mangaId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus) {
|
||||||
|SaveMediaListEntry (mediaId: ${'$'}mangaId, progress: ${'$'}progress, status: ${'$'}status) {
|
|SaveMediaListEntry (mediaId: ${'$'}mangaId, progress: ${'$'}progress, status: ${'$'}status) {
|
||||||
| id
|
| id
|
||||||
@ -246,7 +247,8 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
|||||||
|}
|
|}
|
||||||
|""".trimMargin()
|
|""".trimMargin()
|
||||||
|
|
||||||
fun deleteFromLibraryQuery() = """
|
fun deleteFromLibraryQuery() =
|
||||||
|
"""
|
||||||
|mutation DeleteManga(${'$'}listId: Int) {
|
|mutation DeleteManga(${'$'}listId: Int) {
|
||||||
|DeleteMediaListEntry (id: ${'$'}listId) {
|
|DeleteMediaListEntry (id: ${'$'}listId) {
|
||||||
|deleted
|
|deleted
|
||||||
@ -254,7 +256,8 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
|||||||
|}
|
|}
|
||||||
|}""".trimMargin()
|
|}""".trimMargin()
|
||||||
|
|
||||||
fun updateInLibraryQuery() = """
|
fun updateInLibraryQuery() =
|
||||||
|
"""
|
||||||
|mutation UpdateManga(${'$'}listId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus, ${'$'}score: Int) {
|
|mutation UpdateManga(${'$'}listId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus, ${'$'}score: Int) {
|
||||||
|SaveMediaListEntry (id: ${'$'}listId, progress: ${'$'}progress, status: ${'$'}status, scoreRaw: ${'$'}score) {
|
|SaveMediaListEntry (id: ${'$'}listId, progress: ${'$'}progress, status: ${'$'}status, scoreRaw: ${'$'}score) {
|
||||||
|id
|
|id
|
||||||
@ -264,7 +267,8 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
|||||||
|}
|
|}
|
||||||
|""".trimMargin()
|
|""".trimMargin()
|
||||||
|
|
||||||
fun searchQuery() = """
|
fun searchQuery() =
|
||||||
|
"""
|
||||||
|query Search(${'$'}query: String) {
|
|query Search(${'$'}query: String) {
|
||||||
|Page (perPage: 50) {
|
|Page (perPage: 50) {
|
||||||
|media(search: ${'$'}query, type: MANGA, format_not_in: [NOVEL]) {
|
|media(search: ${'$'}query, type: MANGA, format_not_in: [NOVEL]) {
|
||||||
@ -289,7 +293,8 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
|||||||
|}
|
|}
|
||||||
|""".trimMargin()
|
|""".trimMargin()
|
||||||
|
|
||||||
fun findLibraryMangaQuery() = """
|
fun findLibraryMangaQuery() =
|
||||||
|
"""
|
||||||
|query (${'$'}id: Int!, ${'$'}manga_id: Int!) {
|
|query (${'$'}id: Int!, ${'$'}manga_id: Int!) {
|
||||||
|Page {
|
|Page {
|
||||||
|mediaList(userId: ${'$'}id, type: MANGA, mediaId: ${'$'}manga_id) {
|
|mediaList(userId: ${'$'}id, type: MANGA, mediaId: ${'$'}manga_id) {
|
||||||
@ -320,7 +325,8 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
|||||||
|}
|
|}
|
||||||
|""".trimMargin()
|
|""".trimMargin()
|
||||||
|
|
||||||
fun currentUserQuery() = """
|
fun currentUserQuery() =
|
||||||
|
"""
|
||||||
|query User {
|
|query User {
|
||||||
|Viewer {
|
|Viewer {
|
||||||
|id
|
|id
|
||||||
|
@ -38,8 +38,8 @@ class AnilistInterceptor(private val anilist: Anilist, private var token: String
|
|||||||
|
|
||||||
// Add the authorization header to the original request.
|
// Add the authorization header to the original request.
|
||||||
val authRequest = originalRequest.newBuilder()
|
val authRequest = originalRequest.newBuilder()
|
||||||
.addHeader("Authorization", "Bearer ${oauth!!.access_token}")
|
.addHeader("Authorization", "Bearer ${oauth!!.access_token}")
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
return chain.proceed(authRequest)
|
return chain.proceed(authRequest)
|
||||||
}
|
}
|
||||||
|
@ -36,25 +36,28 @@ class BangumiInterceptor(val bangumi: Bangumi, val gson: Gson) : Interceptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val authRequest = if (originalRequest.method == "GET") originalRequest.newBuilder()
|
val authRequest = if (originalRequest.method == "GET") originalRequest.newBuilder()
|
||||||
.header("User-Agent", "Tachiyomi")
|
.header("User-Agent", "Tachiyomi")
|
||||||
.url(originalRequest.url.newBuilder()
|
.url(
|
||||||
.addQueryParameter("access_token", currAuth.access_token).build())
|
originalRequest.url.newBuilder()
|
||||||
.build() else originalRequest.newBuilder()
|
.addQueryParameter("access_token", currAuth.access_token).build()
|
||||||
.post(addTocken(currAuth.access_token, originalRequest.body as FormBody))
|
)
|
||||||
.header("User-Agent", "Tachiyomi")
|
.build() else originalRequest.newBuilder()
|
||||||
.build()
|
.post(addTocken(currAuth.access_token, originalRequest.body as FormBody))
|
||||||
|
.header("User-Agent", "Tachiyomi")
|
||||||
|
.build()
|
||||||
|
|
||||||
return chain.proceed(authRequest)
|
return chain.proceed(authRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun newAuth(oauth: OAuth) {
|
fun newAuth(oauth: OAuth) {
|
||||||
this.oauth = OAuth(
|
this.oauth = OAuth(
|
||||||
oauth.access_token,
|
oauth.access_token,
|
||||||
oauth.token_type,
|
oauth.token_type,
|
||||||
System.currentTimeMillis() / 1000,
|
System.currentTimeMillis() / 1000,
|
||||||
oauth.expires_in,
|
oauth.expires_in,
|
||||||
oauth.refresh_token,
|
oauth.refresh_token,
|
||||||
this.oauth?.user_id)
|
this.oauth?.user_id
|
||||||
|
)
|
||||||
|
|
||||||
bangumi.saveToken(oauth)
|
bangumi.saveToken(oauth)
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,8 @@ data class OAuth(
|
|||||||
val refresh_token: String?,
|
val refresh_token: String?,
|
||||||
val user_id: Long?
|
val user_id: Long?
|
||||||
) {
|
) {
|
||||||
// Access token refresh before expired
|
// Access token refresh before expired
|
||||||
fun isExpired() = (System.currentTimeMillis() / 1000) > (created_at + expires_in - 3600)
|
fun isExpired() = (System.currentTimeMillis() / 1000) > (created_at + expires_in - 3600)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Status(
|
data class Status(
|
||||||
|
@ -30,10 +30,10 @@ class KitsuInterceptor(val kitsu: Kitsu, val gson: Gson) : Interceptor {
|
|||||||
|
|
||||||
// Add the authorization header to the original request.
|
// Add the authorization header to the original request.
|
||||||
val authRequest = originalRequest.newBuilder()
|
val authRequest = originalRequest.newBuilder()
|
||||||
.addHeader("Authorization", "Bearer ${oauth!!.access_token}")
|
.addHeader("Authorization", "Bearer ${oauth!!.access_token}")
|
||||||
.header("Accept", "application/vnd.api+json")
|
.header("Accept", "application/vnd.api+json")
|
||||||
.header("Content-Type", "application/vnd.api+json")
|
.header("Content-Type", "application/vnd.api+json")
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
return chain.proceed(authRequest)
|
return chain.proceed(authRequest)
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
|
|||||||
val request = Request.Builder().url(url.toString()).get().build()
|
val request = Request.Builder().url(url.toString()).get().build()
|
||||||
|
|
||||||
val urlMangas = "$apiUrl/mangas".toUri().buildUpon().appendPath(track.media_id.toString())
|
val urlMangas = "$apiUrl/mangas".toUri().buildUpon().appendPath(track.media_id.toString())
|
||||||
.build()
|
.build()
|
||||||
val requestMangas = Request.Builder().url(urlMangas.toString()).get().build()
|
val requestMangas = Request.Builder().url(urlMangas.toString()).get().build()
|
||||||
|
|
||||||
val requestMangasResponse = authClient.newCall(requestMangas).execute()
|
val requestMangasResponse = authClient.newCall(requestMangas).execute()
|
||||||
|
@ -29,9 +29,9 @@ class ShikimoriInterceptor(val shikimori: Shikimori, val gson: Gson) : Intercept
|
|||||||
}
|
}
|
||||||
// Add the authorization header to the original request.
|
// Add the authorization header to the original request.
|
||||||
val authRequest = originalRequest.newBuilder()
|
val authRequest = originalRequest.newBuilder()
|
||||||
.addHeader("Authorization", "Bearer ${oauth!!.access_token}")
|
.addHeader("Authorization", "Bearer ${oauth!!.access_token}")
|
||||||
.header("User-Agent", "Tachiyomi")
|
.header("User-Agent", "Tachiyomi")
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
return chain.proceed(authRequest)
|
return chain.proceed(authRequest)
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,10 @@ class UpdaterJob(private val context: Context, workerParams: WorkerParameters) :
|
|||||||
android.R.drawable.stat_sys_download_done,
|
android.R.drawable.stat_sys_download_done,
|
||||||
context.getString(R.string.download),
|
context.getString(R.string.download),
|
||||||
PendingIntent.getService(
|
PendingIntent.getService(
|
||||||
context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -62,15 +65,18 @@ class UpdaterJob(private val context: Context, workerParams: WorkerParameters) :
|
|||||||
|
|
||||||
fun setupTask() {
|
fun setupTask() {
|
||||||
val constraints = Constraints.Builder()
|
val constraints = Constraints.Builder()
|
||||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
val request = PeriodicWorkRequestBuilder<UpdaterJob>(
|
val request = PeriodicWorkRequestBuilder<UpdaterJob>(
|
||||||
1, TimeUnit.DAYS,
|
1,
|
||||||
1, TimeUnit.HOURS)
|
TimeUnit.DAYS,
|
||||||
.addTag(TAG)
|
1,
|
||||||
.setConstraints(constraints)
|
TimeUnit.HOURS
|
||||||
.build()
|
)
|
||||||
|
.addTag(TAG)
|
||||||
|
.setConstraints(constraints)
|
||||||
|
.build()
|
||||||
|
|
||||||
WorkManager.getInstance().enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.REPLACE, request)
|
WorkManager.getInstance().enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.REPLACE, request)
|
||||||
}
|
}
|
||||||
|
@ -75,13 +75,17 @@ internal class UpdaterNotifier(private val context: Context) {
|
|||||||
setProgress(0, 0, false)
|
setProgress(0, 0, false)
|
||||||
// Install action
|
// Install action
|
||||||
setContentIntent(NotificationHandler.installApkPendingActivity(context, uri))
|
setContentIntent(NotificationHandler.installApkPendingActivity(context, uri))
|
||||||
addAction(R.drawable.ic_system_update_24dp,
|
addAction(
|
||||||
context.getString(R.string.install),
|
R.drawable.ic_system_update_24dp,
|
||||||
NotificationHandler.installApkPendingActivity(context, uri))
|
context.getString(R.string.install),
|
||||||
|
NotificationHandler.installApkPendingActivity(context, uri)
|
||||||
|
)
|
||||||
// Cancel action
|
// Cancel action
|
||||||
addAction(R.drawable.ic_close_24dp,
|
addAction(
|
||||||
context.getString(R.string.cancel),
|
R.drawable.ic_close_24dp,
|
||||||
NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_UPDATER))
|
context.getString(R.string.cancel),
|
||||||
|
NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_UPDATER)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
notification.show()
|
notification.show()
|
||||||
}
|
}
|
||||||
@ -99,13 +103,17 @@ internal class UpdaterNotifier(private val context: Context) {
|
|||||||
setProgress(0, 0, false)
|
setProgress(0, 0, false)
|
||||||
color = ContextCompat.getColor(context, R.color.colorAccent)
|
color = ContextCompat.getColor(context, R.color.colorAccent)
|
||||||
// Retry action
|
// Retry action
|
||||||
addAction(R.drawable.ic_refresh_24dp,
|
addAction(
|
||||||
context.getString(R.string.retry),
|
R.drawable.ic_refresh_24dp,
|
||||||
UpdaterService.downloadApkPendingService(context, url))
|
context.getString(R.string.retry),
|
||||||
|
UpdaterService.downloadApkPendingService(context, url)
|
||||||
|
)
|
||||||
// Cancel action
|
// Cancel action
|
||||||
addAction(R.drawable.ic_close_24dp,
|
addAction(
|
||||||
context.getString(R.string.cancel),
|
R.drawable.ic_close_24dp,
|
||||||
NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_UPDATER))
|
context.getString(R.string.cancel),
|
||||||
|
NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_UPDATER)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
notification.show(Notifications.ID_UPDATER)
|
notification.show(Notifications.ID_UPDATER)
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,8 @@ class UpdaterService : Service() {
|
|||||||
startForeground(Notifications.ID_UPDATER, notifier.onDownloadStarted(getString(R.string.app_name)).build())
|
startForeground(Notifications.ID_UPDATER, notifier.onDownloadStarted(getString(R.string.app_name)).build())
|
||||||
|
|
||||||
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(
|
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(
|
||||||
PowerManager.PARTIAL_WAKE_LOCK, "${javaClass.name}:WakeLock"
|
PowerManager.PARTIAL_WAKE_LOCK,
|
||||||
|
"${javaClass.name}:WakeLock"
|
||||||
)
|
)
|
||||||
wakeLock.acquire()
|
wakeLock.acquire()
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,10 @@ interface GithubService {
|
|||||||
companion object {
|
companion object {
|
||||||
fun create(): GithubService {
|
fun create(): GithubService {
|
||||||
val restAdapter = Retrofit.Builder()
|
val restAdapter = Retrofit.Builder()
|
||||||
.baseUrl("https://api.github.com")
|
.baseUrl("https://api.github.com")
|
||||||
.addConverterFactory(GsonConverterFactory.create())
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
.client(Injekt.get<NetworkHelper>().client)
|
.client(Injekt.get<NetworkHelper>().client)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
return restAdapter.create(GithubService::class.java)
|
return restAdapter.create(GithubService::class.java)
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import eu.kanade.tachiyomi.BuildConfig
|
|||||||
import eu.kanade.tachiyomi.data.updater.UpdateChecker
|
import eu.kanade.tachiyomi.data.updater.UpdateChecker
|
||||||
import eu.kanade.tachiyomi.data.updater.UpdateResult
|
import eu.kanade.tachiyomi.data.updater.UpdateResult
|
||||||
|
|
||||||
class GithubUpdateChecker : UpdateChecker() {
|
class GithubUpdateChecker : UpdateChecker() {
|
||||||
|
|
||||||
private val service: GithubService = GithubService.create()
|
private val service: GithubService = GithubService.create()
|
||||||
|
|
||||||
|
@ -80,8 +80,7 @@ class ExtensionManager(
|
|||||||
context.packageManager.getApplicationIcon(pkgName)
|
context.packageManager.getApplicationIcon(pkgName)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
null
|
null
|
||||||
}
|
} else null
|
||||||
else null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -136,16 +135,16 @@ class ExtensionManager(
|
|||||||
val extensions = ExtensionLoader.loadExtensions(context)
|
val extensions = ExtensionLoader.loadExtensions(context)
|
||||||
|
|
||||||
installedExtensions = extensions
|
installedExtensions = extensions
|
||||||
.filterIsInstance<LoadResult.Success>()
|
.filterIsInstance<LoadResult.Success>()
|
||||||
.map { it.extension }
|
.map { it.extension }
|
||||||
installedExtensions
|
installedExtensions
|
||||||
.flatMap { it.sources }
|
.flatMap { it.sources }
|
||||||
// overwrite is needed until the bundled sources are removed
|
// overwrite is needed until the bundled sources are removed
|
||||||
.forEach { sourceManager.registerSource(it, true) }
|
.forEach { sourceManager.registerSource(it, true) }
|
||||||
|
|
||||||
untrustedExtensions = extensions
|
untrustedExtensions = extensions
|
||||||
.filterIsInstance<LoadResult.Untrusted>()
|
.filterIsInstance<LoadResult.Untrusted>()
|
||||||
.map { it.extension }
|
.map { it.extension }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -251,7 +250,7 @@ class ExtensionManager(
|
|||||||
*/
|
*/
|
||||||
fun updateExtension(extension: Extension.Installed): Observable<InstallStep> {
|
fun updateExtension(extension: Extension.Installed): Observable<InstallStep> {
|
||||||
val availableExt = availableExtensions.find { it.pkgName == extension.pkgName }
|
val availableExt = availableExtensions.find { it.pkgName == extension.pkgName }
|
||||||
?: return Observable.empty()
|
?: return Observable.empty()
|
||||||
return installExtension(availableExt)
|
return installExtension(availableExt)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,10 +295,10 @@ class ExtensionManager(
|
|||||||
nowTrustedExtensions.map { extension ->
|
nowTrustedExtensions.map { extension ->
|
||||||
async { ExtensionLoader.loadExtensionFromPkgName(ctx, extension.pkgName) }
|
async { ExtensionLoader.loadExtensionFromPkgName(ctx, extension.pkgName) }
|
||||||
}.map { it.await() }.forEach { result ->
|
}.map { it.await() }.forEach { result ->
|
||||||
if (result is LoadResult.Success) {
|
if (result is LoadResult.Success) {
|
||||||
registerNewExtension(result.extension)
|
registerNewExtension(result.extension)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,12 +45,15 @@ class ExtensionUpdateJob(private val context: Context, workerParams: WorkerParam
|
|||||||
val preferences: PreferencesHelper by injectLazy()
|
val preferences: PreferencesHelper by injectLazy()
|
||||||
preferences.extensionUpdatesCount().set(names.size)
|
preferences.extensionUpdatesCount().set(names.size)
|
||||||
NotificationManagerCompat.from(context).apply {
|
NotificationManagerCompat.from(context).apply {
|
||||||
notify(Notifications.ID_UPDATES_TO_EXTS,
|
notify(
|
||||||
|
Notifications.ID_UPDATES_TO_EXTS,
|
||||||
context.notification(Notifications.CHANNEL_UPDATES_TO_EXTS) {
|
context.notification(Notifications.CHANNEL_UPDATES_TO_EXTS) {
|
||||||
setContentTitle(
|
setContentTitle(
|
||||||
context.resources.getQuantityString(
|
context.resources.getQuantityString(
|
||||||
R.plurals.extension_updates_available, names
|
R.plurals.extension_updates_available,
|
||||||
.size, names.size
|
names
|
||||||
|
.size,
|
||||||
|
names.size
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val extNames = names.joinToString(", ")
|
val extNames = names.joinToString(", ")
|
||||||
@ -64,7 +67,8 @@ class ExtensionUpdateJob(private val context: Context, workerParams: WorkerParam
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
setAutoCancel(true)
|
setAutoCancel(true)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,8 +84,11 @@ class ExtensionUpdateJob(private val context: Context, workerParams: WorkerParam
|
|||||||
.build()
|
.build()
|
||||||
|
|
||||||
val request = PeriodicWorkRequestBuilder<ExtensionUpdateJob>(
|
val request = PeriodicWorkRequestBuilder<ExtensionUpdateJob>(
|
||||||
12, TimeUnit.HOURS,
|
12,
|
||||||
1, TimeUnit.HOURS)
|
TimeUnit.HOURS,
|
||||||
|
1,
|
||||||
|
TimeUnit.HOURS
|
||||||
|
)
|
||||||
.addTag(TAG)
|
.addTag(TAG)
|
||||||
.setConstraints(constraints)
|
.setConstraints(constraints)
|
||||||
.build()
|
.build()
|
||||||
|
@ -18,9 +18,9 @@ class ExtensionInstallActivity : Activity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
val installIntent = Intent(Intent.ACTION_INSTALL_PACKAGE)
|
val installIntent = Intent(Intent.ACTION_INSTALL_PACKAGE)
|
||||||
.setDataAndType(intent.data, intent.type)
|
.setDataAndType(intent.data, intent.type)
|
||||||
.putExtra(Intent.EXTRA_RETURN_RESULT, true)
|
.putExtra(Intent.EXTRA_RETURN_RESULT, true)
|
||||||
.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
startActivityForResult(installIntent, INSTALL_REQUEST_CODE)
|
startActivityForResult(installIntent, INSTALL_REQUEST_CODE)
|
||||||
|
@ -19,7 +19,7 @@ import kotlinx.coroutines.async
|
|||||||
* @param listener The listener that should be notified of extension installation events.
|
* @param listener The listener that should be notified of extension installation events.
|
||||||
*/
|
*/
|
||||||
internal class ExtensionInstallReceiver(private val listener: Listener) :
|
internal class ExtensionInstallReceiver(private val listener: Listener) :
|
||||||
BroadcastReceiver() {
|
BroadcastReceiver() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers this broadcast receiver
|
* Registers this broadcast receiver
|
||||||
@ -94,7 +94,7 @@ internal class ExtensionInstallReceiver(private val listener: Listener) :
|
|||||||
*/
|
*/
|
||||||
private suspend fun getExtensionFromIntent(context: Context, intent: Intent?): LoadResult {
|
private suspend fun getExtensionFromIntent(context: Context, intent: Intent?): LoadResult {
|
||||||
val pkgName = getPackageNameFromIntent(intent)
|
val pkgName = getPackageNameFromIntent(intent)
|
||||||
?: return LoadResult.Error("Package name not found")
|
?: return LoadResult.Error("Package name not found")
|
||||||
return GlobalScope.async(Dispatchers.Default, CoroutineStart.DEFAULT) { ExtensionLoader.loadExtensionFromPkgName(context, pkgName) }.await()
|
return GlobalScope.async(Dispatchers.Default, CoroutineStart.DEFAULT) { ExtensionLoader.loadExtensionFromPkgName(context, pkgName) }.await()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ internal class ExtensionInstaller(private val context: Context) {
|
|||||||
fun uninstallApk(pkgName: String) {
|
fun uninstallApk(pkgName: String) {
|
||||||
val packageUri = "package:$pkgName".toUri()
|
val packageUri = "package:$pkgName".toUri()
|
||||||
val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri)
|
val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri)
|
||||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
}
|
}
|
||||||
|
@ -36,9 +36,9 @@ internal object ExtensionLoader {
|
|||||||
* List of the trusted signatures.
|
* List of the trusted signatures.
|
||||||
*/
|
*/
|
||||||
var trustedSignatures = mutableSetOf<String>() +
|
var trustedSignatures = mutableSetOf<String>() +
|
||||||
Injekt.get<PreferencesHelper>().trustedSignatures().getOrDefault() +
|
Injekt.get<PreferencesHelper>().trustedSignatures().getOrDefault() +
|
||||||
// inorichi's key
|
// inorichi's key
|
||||||
"7ce04da7773d41b489f4693a366c36bcd0a11fc39b547168553c285bd7348e23"
|
"7ce04da7773d41b489f4693a366c36bcd0a11fc39b547168553c285bd7348e23"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a list of all the installed extensions initialized concurrently.
|
* Return a list of all the installed extensions initialized concurrently.
|
||||||
@ -103,8 +103,10 @@ internal object ExtensionLoader {
|
|||||||
// Validate lib version
|
// Validate lib version
|
||||||
val majorLibVersion = versionName.substringBefore('.').toInt()
|
val majorLibVersion = versionName.substringBefore('.').toInt()
|
||||||
if (majorLibVersion < LIB_VERSION_MIN || majorLibVersion > LIB_VERSION_MAX) {
|
if (majorLibVersion < LIB_VERSION_MIN || majorLibVersion > LIB_VERSION_MAX) {
|
||||||
val exception = Exception("Lib version is $majorLibVersion, while only versions " +
|
val exception = Exception(
|
||||||
"$LIB_VERSION_MIN to $LIB_VERSION_MAX are allowed")
|
"Lib version is $majorLibVersion, while only versions " +
|
||||||
|
"$LIB_VERSION_MIN to $LIB_VERSION_MAX are allowed"
|
||||||
|
)
|
||||||
Timber.w(exception)
|
Timber.w(exception)
|
||||||
return LoadResult.Error(exception)
|
return LoadResult.Error(exception)
|
||||||
}
|
}
|
||||||
@ -122,30 +124,30 @@ internal object ExtensionLoader {
|
|||||||
val classLoader = PathClassLoader(appInfo.sourceDir, null, context.classLoader)
|
val classLoader = PathClassLoader(appInfo.sourceDir, null, context.classLoader)
|
||||||
|
|
||||||
val sources = appInfo.metaData.getString(METADATA_SOURCE_CLASS)!!
|
val sources = appInfo.metaData.getString(METADATA_SOURCE_CLASS)!!
|
||||||
.split(";")
|
.split(";")
|
||||||
.map {
|
.map {
|
||||||
val sourceClass = it.trim()
|
val sourceClass = it.trim()
|
||||||
if (sourceClass.startsWith("."))
|
if (sourceClass.startsWith("."))
|
||||||
pkgInfo.packageName + sourceClass
|
pkgInfo.packageName + sourceClass
|
||||||
else
|
else
|
||||||
sourceClass
|
sourceClass
|
||||||
}
|
}
|
||||||
.flatMap {
|
.flatMap {
|
||||||
try {
|
try {
|
||||||
val obj = Class.forName(it, false, classLoader).newInstance()
|
val obj = Class.forName(it, false, classLoader).newInstance()
|
||||||
when (obj) {
|
when (obj) {
|
||||||
is Source -> listOf(obj)
|
is Source -> listOf(obj)
|
||||||
is SourceFactory -> obj.createSources()
|
is SourceFactory -> obj.createSources()
|
||||||
else -> throw Exception("Unknown source class type! ${obj.javaClass}")
|
else -> throw Exception("Unknown source class type! ${obj.javaClass}")
|
||||||
}
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
Timber.e(e, "Extension load error: $extName.")
|
|
||||||
return LoadResult.Error(e)
|
|
||||||
}
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Timber.e(e, "Extension load error: $extName.")
|
||||||
|
return LoadResult.Error(e)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
val langs = sources.filterIsInstance<CatalogueSource>()
|
val langs = sources.filterIsInstance<CatalogueSource>()
|
||||||
.map { it.lang }
|
.map { it.lang }
|
||||||
.toSet()
|
.toSet()
|
||||||
|
|
||||||
val lang = when (langs.size) {
|
val lang = when (langs.size) {
|
||||||
0 -> ""
|
0 -> ""
|
||||||
|
@ -58,17 +58,19 @@ fun Call.asObservable(): Observable<Response> {
|
|||||||
// Based on https://github.com/gildor/kotlin-coroutines-okhttp
|
// Based on https://github.com/gildor/kotlin-coroutines-okhttp
|
||||||
suspend fun Call.await(): Response {
|
suspend fun Call.await(): Response {
|
||||||
return suspendCancellableCoroutine { continuation ->
|
return suspendCancellableCoroutine { continuation ->
|
||||||
enqueue(object : Callback {
|
enqueue(
|
||||||
override fun onResponse(call: Call, response: Response) {
|
object : Callback {
|
||||||
continuation.resume(response)
|
override fun onResponse(call: Call, response: Response) {
|
||||||
}
|
continuation.resume(response)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onFailure(call: Call, e: IOException) {
|
override fun onFailure(call: Call, e: IOException) {
|
||||||
// Don't bother with resuming the continuation if it is already cancelled.
|
// Don't bother with resuming the continuation if it is already cancelled.
|
||||||
if (continuation.isCancelled) return
|
if (continuation.isCancelled) return
|
||||||
continuation.resumeWithException(e)
|
continuation.resumeWithException(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
|
|
||||||
continuation.invokeOnCancellation {
|
continuation.invokeOnCancellation {
|
||||||
try {
|
try {
|
||||||
@ -91,14 +93,14 @@ fun Call.asObservableSuccess(): Observable<Response> {
|
|||||||
|
|
||||||
fun OkHttpClient.newCallWithProgress(request: Request, listener: ProgressListener): Call {
|
fun OkHttpClient.newCallWithProgress(request: Request, listener: ProgressListener): Call {
|
||||||
val progressClient = newBuilder()
|
val progressClient = newBuilder()
|
||||||
.cache(null)
|
.cache(null)
|
||||||
.addNetworkInterceptor { chain ->
|
.addNetworkInterceptor { chain ->
|
||||||
val originalResponse = chain.proceed(chain.request())
|
val originalResponse = chain.proceed(chain.request())
|
||||||
originalResponse.newBuilder()
|
originalResponse.newBuilder()
|
||||||
.body(ProgressResponseBody(originalResponse.body!!, listener))
|
.body(ProgressResponseBody(originalResponse.body!!, listener))
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
return progressClient.newCall(request)
|
return progressClient.newCall(request)
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,10 @@ fun GET(
|
|||||||
): Request {
|
): Request {
|
||||||
|
|
||||||
return Request.Builder()
|
return Request.Builder()
|
||||||
.url(url)
|
.url(url)
|
||||||
.headers(headers)
|
.headers(headers)
|
||||||
.cacheControl(cache)
|
.cacheControl(cache)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun POST(
|
fun POST(
|
||||||
@ -32,9 +32,9 @@ fun POST(
|
|||||||
): Request {
|
): Request {
|
||||||
|
|
||||||
return Request.Builder()
|
return Request.Builder()
|
||||||
.url(url)
|
.url(url)
|
||||||
.post(body)
|
.post(body)
|
||||||
.headers(headers)
|
.headers(headers)
|
||||||
.cacheControl(cache)
|
.cacheControl(cache)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,10 @@ class UserAgentInterceptor : Interceptor {
|
|||||||
|
|
||||||
return if (originalRequest.header("User-Agent").isNullOrEmpty()) {
|
return if (originalRequest.header("User-Agent").isNullOrEmpty()) {
|
||||||
val newRequest = originalRequest
|
val newRequest = originalRequest
|
||||||
.newBuilder()
|
.newBuilder()
|
||||||
.removeHeader("User-Agent")
|
.removeHeader("User-Agent")
|
||||||
.addHeader("User-Agent", HttpSource.DEFAULT_USERAGENT)
|
.addHeader("User-Agent", HttpSource.DEFAULT_USERAGENT)
|
||||||
.build()
|
.build()
|
||||||
chain.proceed(newRequest)
|
chain.proceed(newRequest)
|
||||||
} else {
|
} else {
|
||||||
chain.proceed(originalRequest)
|
chain.proceed(originalRequest)
|
||||||
|
@ -143,7 +143,7 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
|||||||
|
|
||||||
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
|
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
|
||||||
val baseDirs = getBaseDirectories(context)
|
val baseDirs = getBaseDirectories(context)
|
||||||
baseDirs
|
baseDirs
|
||||||
.mapNotNull { File(it, manga.url).listFiles()?.toList() }
|
.mapNotNull { File(it, manga.url).listFiles()?.toList() }
|
||||||
.flatten()
|
.flatten()
|
||||||
.filter { it.extension == "json" }.firstOrNull()?.apply {
|
.filter { it.extension == "json" }.firstOrNull()?.apply {
|
||||||
@ -241,10 +241,12 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
|||||||
date_upload = chapterFile.lastModified()
|
date_upload = chapterFile.lastModified()
|
||||||
ChapterRecognition.parseChapterNumber(this, manga)
|
ChapterRecognition.parseChapterNumber(this, manga)
|
||||||
}
|
}
|
||||||
}.sortedWith(Comparator { c1, c2 ->
|
}.sortedWith(
|
||||||
val c = c2.chapter_number.compareTo(c1.chapter_number)
|
Comparator { c1, c2 ->
|
||||||
if (c == 0) c2.name.compareToCaseInsensitiveNaturalOrder(c1.name) else c
|
val c = c2.chapter_number.compareTo(c1.chapter_number)
|
||||||
})
|
if (c == 0) c2.name.compareToCaseInsensitiveNaturalOrder(c1.name) else c
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return Observable.just(chapters)
|
return Observable.just(chapters)
|
||||||
}
|
}
|
||||||
@ -289,18 +291,22 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
|||||||
return when (format) {
|
return when (format) {
|
||||||
is Format.Directory -> {
|
is Format.Directory -> {
|
||||||
val entry = format.file.listFiles()
|
val entry = format.file.listFiles()
|
||||||
?.sortedWith(Comparator<File> { f1, f2 ->
|
?.sortedWith(
|
||||||
f1.name.compareToCaseInsensitiveNaturalOrder(f2.name)
|
Comparator<File> { f1, f2 ->
|
||||||
})
|
f1.name.compareToCaseInsensitiveNaturalOrder(f2.name)
|
||||||
|
}
|
||||||
|
)
|
||||||
?.find { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } }
|
?.find { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } }
|
||||||
|
|
||||||
entry?.let { updateCover(context, manga, it.inputStream()) }
|
entry?.let { updateCover(context, manga, it.inputStream()) }
|
||||||
}
|
}
|
||||||
is Format.Zip -> {
|
is Format.Zip -> {
|
||||||
ZipFile(format.file).use { zip ->
|
ZipFile(format.file).use { zip ->
|
||||||
val entry = zip.entries().toList().sortedWith(Comparator<ZipEntry> { f1, f2 ->
|
val entry = zip.entries().toList().sortedWith(
|
||||||
f1.name.compareToCaseInsensitiveNaturalOrder(f2.name)
|
Comparator<ZipEntry> { f1, f2 ->
|
||||||
}).find {
|
f1.name.compareToCaseInsensitiveNaturalOrder(f2.name)
|
||||||
|
}
|
||||||
|
).find {
|
||||||
!it.isDirectory && ImageUtil.isImage(it.name) {
|
!it.isDirectory && ImageUtil.isImage(it.name) {
|
||||||
zip.getInputStream(it)
|
zip.getInputStream(it)
|
||||||
}
|
}
|
||||||
@ -311,9 +317,11 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
|||||||
}
|
}
|
||||||
is Format.Rar -> {
|
is Format.Rar -> {
|
||||||
Archive(format.file).use { archive ->
|
Archive(format.file).use { archive ->
|
||||||
val entry = archive.fileHeaders.sortedWith(Comparator<FileHeader> { f1, f2 ->
|
val entry = archive.fileHeaders.sortedWith(
|
||||||
f1.fileNameString.compareToCaseInsensitiveNaturalOrder(f2.fileNameString)
|
Comparator<FileHeader> { f1, f2 ->
|
||||||
}).find {
|
f1.fileNameString.compareToCaseInsensitiveNaturalOrder(f2.fileNameString)
|
||||||
|
}
|
||||||
|
).find {
|
||||||
!it.isDirectory && ImageUtil.isImage(it.fileNameString) {
|
!it.isDirectory && ImageUtil.isImage(it.fileNameString) {
|
||||||
archive.getInputStream(it)
|
archive.getInputStream(it)
|
||||||
}
|
}
|
||||||
|
@ -21,13 +21,24 @@ open class SourceManager(private val context: Context) {
|
|||||||
|
|
||||||
private val delegatedSources = listOf(
|
private val delegatedSources = listOf(
|
||||||
DelegatedSource(
|
DelegatedSource(
|
||||||
"reader.kireicake.com", 5509224355268673176, KireiCake()
|
"reader.kireicake.com",
|
||||||
), DelegatedSource(
|
5509224355268673176,
|
||||||
"jaiminisbox.com", 9064882169246918586, FoolSlide("jaiminis", "/reader")
|
KireiCake()
|
||||||
), DelegatedSource(
|
),
|
||||||
"mangadex.org", 2499283573021220255, MangaDex()
|
DelegatedSource(
|
||||||
), DelegatedSource(
|
"jaiminisbox.com",
|
||||||
"mangaplus.shueisha.co.jp", 1998944621602463790, MangaPlus()
|
9064882169246918586,
|
||||||
|
FoolSlide("jaiminis", "/reader")
|
||||||
|
),
|
||||||
|
DelegatedSource(
|
||||||
|
"mangadex.org",
|
||||||
|
2499283573021220255,
|
||||||
|
MangaDex()
|
||||||
|
),
|
||||||
|
DelegatedSource(
|
||||||
|
"mangaplus.shueisha.co.jp",
|
||||||
|
1998944621602463790,
|
||||||
|
MangaPlus()
|
||||||
)
|
)
|
||||||
).associateBy { it.sourceId }
|
).associateBy { it.sourceId }
|
||||||
|
|
||||||
@ -92,8 +103,10 @@ open class SourceManager(private val context: Context) {
|
|||||||
private fun getSourceNotInstalledException(): Exception {
|
private fun getSourceNotInstalledException(): Exception {
|
||||||
return SourceNotFoundException(
|
return SourceNotFoundException(
|
||||||
context.getString(
|
context.getString(
|
||||||
R.string.source_not_installed_, id.toString()
|
R.string.source_not_installed_,
|
||||||
), id
|
id.toString()
|
||||||
|
),
|
||||||
|
id
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,10 +90,10 @@ abstract class HttpSource : CatalogueSource {
|
|||||||
*/
|
*/
|
||||||
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
|
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
|
||||||
return client.newCall(popularMangaRequest(page))
|
return client.newCall(popularMangaRequest(page))
|
||||||
.asObservableSuccess()
|
.asObservableSuccess()
|
||||||
.map { response ->
|
.map { response ->
|
||||||
popularMangaParse(response)
|
popularMangaParse(response)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -120,10 +120,10 @@ abstract class HttpSource : CatalogueSource {
|
|||||||
*/
|
*/
|
||||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
||||||
return client.newCall(searchMangaRequest(page, query, filters))
|
return client.newCall(searchMangaRequest(page, query, filters))
|
||||||
.asObservableSuccess()
|
.asObservableSuccess()
|
||||||
.map { response ->
|
.map { response ->
|
||||||
searchMangaParse(response)
|
searchMangaParse(response)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -149,10 +149,10 @@ abstract class HttpSource : CatalogueSource {
|
|||||||
*/
|
*/
|
||||||
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> {
|
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> {
|
||||||
return client.newCall(latestUpdatesRequest(page))
|
return client.newCall(latestUpdatesRequest(page))
|
||||||
.asObservableSuccess()
|
.asObservableSuccess()
|
||||||
.map { response ->
|
.map { response ->
|
||||||
latestUpdatesParse(response)
|
latestUpdatesParse(response)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -177,10 +177,10 @@ abstract class HttpSource : CatalogueSource {
|
|||||||
*/
|
*/
|
||||||
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
|
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
|
||||||
return client.newCall(mangaDetailsRequest(manga))
|
return client.newCall(mangaDetailsRequest(manga))
|
||||||
.asObservableSuccess()
|
.asObservableSuccess()
|
||||||
.map { response ->
|
.map { response ->
|
||||||
mangaDetailsParse(response).apply { initialized = true }
|
mangaDetailsParse(response).apply { initialized = true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -209,10 +209,10 @@ abstract class HttpSource : CatalogueSource {
|
|||||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
||||||
return if (manga.status != SManga.LICENSED) {
|
return if (manga.status != SManga.LICENSED) {
|
||||||
client.newCall(chapterListRequest(manga))
|
client.newCall(chapterListRequest(manga))
|
||||||
.asObservableSuccess()
|
.asObservableSuccess()
|
||||||
.map { response ->
|
.map { response ->
|
||||||
chapterListParse(response)
|
chapterListParse(response)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Observable.error(Exception("Licensed - No chapters to show"))
|
Observable.error(Exception("Licensed - No chapters to show"))
|
||||||
}
|
}
|
||||||
@ -242,10 +242,10 @@ abstract class HttpSource : CatalogueSource {
|
|||||||
*/
|
*/
|
||||||
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
|
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
|
||||||
return client.newCall(pageListRequest(chapter))
|
return client.newCall(pageListRequest(chapter))
|
||||||
.asObservableSuccess()
|
.asObservableSuccess()
|
||||||
.map { response ->
|
.map { response ->
|
||||||
pageListParse(response)
|
pageListParse(response)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -273,8 +273,8 @@ abstract class HttpSource : CatalogueSource {
|
|||||||
*/
|
*/
|
||||||
open fun fetchImageUrl(page: Page): Observable<String> {
|
open fun fetchImageUrl(page: Page): Observable<String> {
|
||||||
return client.newCall(imageUrlRequest(page))
|
return client.newCall(imageUrlRequest(page))
|
||||||
.asObservableSuccess()
|
.asObservableSuccess()
|
||||||
.map { imageUrlParse(it) }
|
.map { imageUrlParse(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -301,7 +301,7 @@ abstract class HttpSource : CatalogueSource {
|
|||||||
*/
|
*/
|
||||||
fun fetchImage(page: Page): Observable<Response> {
|
fun fetchImage(page: Page): Observable<Response> {
|
||||||
return client.newCallWithProgress(imageRequest(page), page)
|
return client.newCallWithProgress(imageRequest(page), page)
|
||||||
.asObservableSuccess()
|
.asObservableSuccess()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,12 +14,12 @@ fun HttpSource.getImageUrl(page: Page): Observable<Page> {
|
|||||||
|
|
||||||
fun HttpSource.fetchAllImageUrlsFromPageList(pages: List<Page>): Observable<Page> {
|
fun HttpSource.fetchAllImageUrlsFromPageList(pages: List<Page>): Observable<Page> {
|
||||||
return Observable.from(pages)
|
return Observable.from(pages)
|
||||||
.filter { !it.imageUrl.isNullOrEmpty() }
|
.filter { !it.imageUrl.isNullOrEmpty() }
|
||||||
.mergeWith(fetchRemainingImageUrlsFromPageList(pages))
|
.mergeWith(fetchRemainingImageUrlsFromPageList(pages))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun HttpSource.fetchRemainingImageUrlsFromPageList(pages: List<Page>): Observable<Page> {
|
fun HttpSource.fetchRemainingImageUrlsFromPageList(pages: List<Page>): Observable<Page> {
|
||||||
return Observable.from(pages)
|
return Observable.from(pages)
|
||||||
.filter { it.imageUrl.isNullOrEmpty() }
|
.filter { it.imageUrl.isNullOrEmpty() }
|
||||||
.concatMap { getImageUrl(it) }
|
.concatMap { getImageUrl(it) }
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ import uy.kohesive.injekt.Injekt
|
|||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
open class FoolSlide(override val domainName: String, private val urlModifier: String = "") :
|
open class FoolSlide(override val domainName: String, private val urlModifier: String = "") :
|
||||||
DelegatedHttpSource
|
DelegatedHttpSource
|
||||||
() {
|
() {
|
||||||
|
|
||||||
override fun canOpenUrl(uri: Uri): Boolean = true
|
override fun canOpenUrl(uri: Uri): Boolean = true
|
||||||
@ -34,7 +34,11 @@ DelegatedHttpSource
|
|||||||
val chapterNumber = uri.pathSegments.getOrNull(4 + offset) ?: return null
|
val chapterNumber = uri.pathSegments.getOrNull(4 + offset) ?: return null
|
||||||
val subChapterNumber = uri.pathSegments.getOrNull(5 + offset)?.toIntOrNull()?.toString()
|
val subChapterNumber = uri.pathSegments.getOrNull(5 + offset)?.toIntOrNull()?.toString()
|
||||||
return "$urlModifier/read/" + listOfNotNull(
|
return "$urlModifier/read/" + listOfNotNull(
|
||||||
mangaName, lang, volume, chapterNumber, subChapterNumber
|
mangaName,
|
||||||
|
lang,
|
||||||
|
volume,
|
||||||
|
chapterNumber,
|
||||||
|
subChapterNumber
|
||||||
).joinToString("/") + "/"
|
).joinToString("/") + "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,8 +99,11 @@ DelegatedHttpSource
|
|||||||
private fun allowAdult(request: Request) = allowAdult(request.url.toString())
|
private fun allowAdult(request: Request) = allowAdult(request.url.toString())
|
||||||
|
|
||||||
private fun allowAdult(url: String): Request {
|
private fun allowAdult(url: String): Request {
|
||||||
return POST(url, body = FormBody.Builder()
|
return POST(
|
||||||
.add("adult", "true")
|
url,
|
||||||
.build())
|
body = FormBody.Builder()
|
||||||
|
.add("adult", "true")
|
||||||
|
.build()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,8 @@ class KireiCake : FoolSlide("kireicake") {
|
|||||||
this.url = url
|
this.url = url
|
||||||
source = delegate?.id ?: -1
|
source = delegate?.id ?: -1
|
||||||
title = document.select("$mangaDetailsInfoSelector li:has(b:contains(title))").first()
|
title = document.select("$mangaDetailsInfoSelector li:has(b:contains(title))").first()
|
||||||
?.ownText()?.substringAfter(":")?.trim() ?: url.split("/").last().replace(
|
?.ownText()?.substringAfter(":")?.trim()
|
||||||
"_", " " + ""
|
?: url.split("/").last().replace("_", " " + "").capitalizeWords()
|
||||||
).capitalizeWords()
|
|
||||||
description =
|
description =
|
||||||
document.select("$mangaDetailsInfoSelector li:has(b:contains(description))").first()
|
document.select("$mangaDetailsInfoSelector li:has(b:contains(description))").first()
|
||||||
?.ownText()?.substringAfter(":")
|
?.ownText()?.substringAfter(":")
|
||||||
|
@ -61,9 +61,13 @@ class MangaPlus : DelegatedHttpSource() {
|
|||||||
context.getString(R.string.chapter_not_found)
|
context.getString(R.string.chapter_not_found)
|
||||||
)
|
)
|
||||||
if (manga != null) {
|
if (manga != null) {
|
||||||
Triple(trueChapter, manga.apply {
|
Triple(
|
||||||
this.title = trimmedTitle
|
trueChapter,
|
||||||
}, chapters.orEmpty())
|
manga.apply {
|
||||||
|
this.title = trimmedTitle
|
||||||
|
},
|
||||||
|
chapters.orEmpty()
|
||||||
|
)
|
||||||
} else null
|
} else null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,12 +33,16 @@ class CenteredToolbar@JvmOverloads constructor(context: Context, attrs: Attribut
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun showDropdown(down: Boolean = true) {
|
fun showDropdown(down: Boolean = true) {
|
||||||
toolbar_title.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ic_blank_24dp, 0,
|
toolbar_title.setCompoundDrawablesRelativeWithIntrinsicBounds(
|
||||||
if (down) {
|
R.drawable.ic_blank_24dp,
|
||||||
R.drawable.ic_arrow_drop_down_24dp
|
0,
|
||||||
} else {
|
if (down) {
|
||||||
R.drawable.ic_arrow_drop_up_24dp
|
R.drawable.ic_arrow_drop_down_24dp
|
||||||
}, 0)
|
} else {
|
||||||
|
R.drawable.ic_arrow_drop_up_24dp
|
||||||
|
},
|
||||||
|
0
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hideDropdown() {
|
fun hideDropdown() {
|
||||||
|
@ -21,7 +21,9 @@ class MaterialFastScroll @JvmOverloads constructor(context: Context, attrs: Attr
|
|||||||
var scrollOffset = 0
|
var scrollOffset = 0
|
||||||
init {
|
init {
|
||||||
setViewsToUse(
|
setViewsToUse(
|
||||||
R.layout.material_fastscroll, R.id.fast_scroller_bubble, R.id.fast_scroller_handle
|
R.layout.material_fastscroll,
|
||||||
|
R.id.fast_scroller_bubble,
|
||||||
|
R.id.fast_scroller_handle
|
||||||
)
|
)
|
||||||
autoHideEnabled = true
|
autoHideEnabled = true
|
||||||
ignoreTouchesOutsideHandle = false
|
ignoreTouchesOutsideHandle = false
|
||||||
@ -85,7 +87,8 @@ class MaterialFastScroll @JvmOverloads constructor(context: Context, attrs: Attr
|
|||||||
val targetPos = getTargetPos(y)
|
val targetPos = getTargetPos(y)
|
||||||
if (layoutManager is StaggeredGridLayoutManager) {
|
if (layoutManager is StaggeredGridLayoutManager) {
|
||||||
(layoutManager as StaggeredGridLayoutManager).scrollToPositionWithOffset(
|
(layoutManager as StaggeredGridLayoutManager).scrollToPositionWithOffset(
|
||||||
targetPos, scrollOffset
|
targetPos,
|
||||||
|
scrollOffset
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(layoutManager as LinearLayoutManager).scrollToPositionWithOffset(targetPos, scrollOffset)
|
(layoutManager as LinearLayoutManager).scrollToPositionWithOffset(targetPos, scrollOffset)
|
||||||
|
@ -119,7 +119,10 @@ class MaterialMenuSheet(
|
|||||||
isElevated = elevate
|
isElevated = elevate
|
||||||
elevationAnimator?.cancel()
|
elevationAnimator?.cancel()
|
||||||
elevationAnimator = ObjectAnimator.ofFloat(
|
elevationAnimator = ObjectAnimator.ofFloat(
|
||||||
title_layout, "elevation", title_layout.elevation, if (elevate) 10f else 0f
|
title_layout,
|
||||||
|
"elevation",
|
||||||
|
title_layout.elevation,
|
||||||
|
if (elevate) 10f else 0f
|
||||||
)
|
)
|
||||||
elevationAnimator?.start()
|
elevationAnimator?.start()
|
||||||
}
|
}
|
||||||
|
@ -14,31 +14,34 @@ import kotlinx.android.extensions.LayoutContainer
|
|||||||
import kotlinx.android.synthetic.*
|
import kotlinx.android.synthetic.*
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
abstract class BaseController(bundle: Bundle? = null) : RestoreViewOnCreateController(bundle),
|
abstract class BaseController(bundle: Bundle? = null) :
|
||||||
LayoutContainer {
|
RestoreViewOnCreateController(bundle),
|
||||||
|
LayoutContainer {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
addLifecycleListener(object : LifecycleListener() {
|
addLifecycleListener(
|
||||||
override fun postCreateView(controller: Controller, view: View) {
|
object : LifecycleListener() {
|
||||||
onViewCreated(view)
|
override fun postCreateView(controller: Controller, view: View) {
|
||||||
}
|
onViewCreated(view)
|
||||||
|
}
|
||||||
|
|
||||||
override fun preCreateView(controller: Controller) {
|
override fun preCreateView(controller: Controller) {
|
||||||
Timber.d("Create view for ${controller.instance()}")
|
Timber.d("Create view for ${controller.instance()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun preAttach(controller: Controller, view: View) {
|
override fun preAttach(controller: Controller, view: View) {
|
||||||
Timber.d("Attach view for ${controller.instance()}")
|
Timber.d("Attach view for ${controller.instance()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun preDetach(controller: Controller, view: View) {
|
override fun preDetach(controller: Controller, view: View) {
|
||||||
Timber.d("Detach view for ${controller.instance()}")
|
Timber.d("Detach view for ${controller.instance()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun preDestroyView(controller: Controller, view: View) {
|
override fun preDestroyView(controller: Controller, view: View) {
|
||||||
Timber.d("Destroy view for ${controller.instance()}")
|
Timber.d("Destroy view for ${controller.instance()}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val containerView: View?
|
override val containerView: View?
|
||||||
@ -96,17 +99,19 @@ abstract class BaseController(bundle: Bundle? = null) : RestoreViewOnCreateContr
|
|||||||
*/
|
*/
|
||||||
var expandActionViewFromInteraction = false
|
var expandActionViewFromInteraction = false
|
||||||
fun MenuItem.fixExpand(onExpand: ((MenuItem) -> Boolean)? = null, onCollapse: ((MenuItem) -> Boolean)? = null) {
|
fun MenuItem.fixExpand(onExpand: ((MenuItem) -> Boolean)? = null, onCollapse: ((MenuItem) -> Boolean)? = null) {
|
||||||
setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
|
setOnActionExpandListener(
|
||||||
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
object : MenuItem.OnActionExpandListener {
|
||||||
return onExpand?.invoke(item) ?: true
|
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
||||||
}
|
return onExpand?.invoke(item) ?: true
|
||||||
|
}
|
||||||
|
|
||||||
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
||||||
activity?.invalidateOptionsMenu()
|
activity?.invalidateOptionsMenu()
|
||||||
|
|
||||||
return onCollapse?.invoke(item) ?: true
|
return onCollapse?.invoke(item) ?: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
|
|
||||||
if (expandActionViewFromInteraction) {
|
if (expandActionViewFromInteraction) {
|
||||||
expandActionViewFromInteraction = false
|
expandActionViewFromInteraction = false
|
||||||
|
@ -87,10 +87,12 @@ abstract class DialogController : RestoreViewOnCreateController {
|
|||||||
*/
|
*/
|
||||||
fun showDialog(router: Router, tag: String?) {
|
fun showDialog(router: Router, tag: String?) {
|
||||||
dismissed = false
|
dismissed = false
|
||||||
router.pushController(RouterTransaction.with(this)
|
router.pushController(
|
||||||
|
RouterTransaction.with(this)
|
||||||
.pushChangeHandler(SimpleSwapChangeHandler(false))
|
.pushChangeHandler(SimpleSwapChangeHandler(false))
|
||||||
.popChangeHandler(SimpleSwapChangeHandler(false))
|
.popChangeHandler(SimpleSwapChangeHandler(false))
|
||||||
.tag(tag))
|
.tag(tag)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,8 +7,9 @@ import nucleus.factory.PresenterFactory
|
|||||||
import nucleus.presenter.Presenter
|
import nucleus.presenter.Presenter
|
||||||
|
|
||||||
@Suppress("LeakingThis")
|
@Suppress("LeakingThis")
|
||||||
abstract class NucleusController<P : Presenter<*>>(val bundle: Bundle? = null) : RxController(bundle),
|
abstract class NucleusController<P : Presenter<*>>(val bundle: Bundle? = null) :
|
||||||
PresenterFactory<P> {
|
RxController(bundle),
|
||||||
|
PresenterFactory<P> {
|
||||||
|
|
||||||
private val delegate = NucleusConductorDelegate(this)
|
private val delegate = NucleusConductorDelegate(this)
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ abstract class BaseFlexibleViewHolder(
|
|||||||
adapter: FlexibleAdapter<*>,
|
adapter: FlexibleAdapter<*>,
|
||||||
stickyHeader: Boolean = false
|
stickyHeader: Boolean = false
|
||||||
) :
|
) :
|
||||||
FlexibleViewHolder(view, adapter, stickyHeader), LayoutContainer {
|
FlexibleViewHolder(view, adapter, stickyHeader), LayoutContainer {
|
||||||
|
|
||||||
override val containerView: View?
|
override val containerView: View?
|
||||||
get() = itemView
|
get() = itemView
|
||||||
|
@ -24,7 +24,7 @@ open class BasePresenter<V> : RxPresenter<V>() {
|
|||||||
* @param onError function to execute when the observable throws an error.
|
* @param onError function to execute when the observable throws an error.
|
||||||
*/
|
*/
|
||||||
fun <T> Observable<T>.subscribeFirst(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit)? = null) =
|
fun <T> Observable<T>.subscribeFirst(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit)? = null) =
|
||||||
compose(deliverFirst<T>()).subscribe(split(onNext, onError)).apply { add(this) }
|
compose(deliverFirst<T>()).subscribe(split(onNext, onError)).apply { add(this) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribes an observable with [deliverLatestCache] and adds it to the presenter's lifecycle
|
* Subscribes an observable with [deliverLatestCache] and adds it to the presenter's lifecycle
|
||||||
@ -34,7 +34,7 @@ open class BasePresenter<V> : RxPresenter<V>() {
|
|||||||
* @param onError function to execute when the observable throws an error.
|
* @param onError function to execute when the observable throws an error.
|
||||||
*/
|
*/
|
||||||
fun <T> Observable<T>.subscribeLatestCache(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit)? = null) =
|
fun <T> Observable<T>.subscribeLatestCache(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit)? = null) =
|
||||||
compose(deliverLatestCache<T>()).subscribe(split(onNext, onError)).apply { add(this) }
|
compose(deliverLatestCache<T>()).subscribe(split(onNext, onError)).apply { add(this) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribes an observable with [deliverReplay] and adds it to the presenter's lifecycle
|
* Subscribes an observable with [deliverReplay] and adds it to the presenter's lifecycle
|
||||||
@ -44,7 +44,7 @@ open class BasePresenter<V> : RxPresenter<V>() {
|
|||||||
* @param onError function to execute when the observable throws an error.
|
* @param onError function to execute when the observable throws an error.
|
||||||
*/
|
*/
|
||||||
fun <T> Observable<T>.subscribeReplay(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit)? = null) =
|
fun <T> Observable<T>.subscribeReplay(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit)? = null) =
|
||||||
compose(deliverReplay<T>()).subscribe(split(onNext, onError)).apply { add(this) }
|
compose(deliverReplay<T>()).subscribe(split(onNext, onError)).apply { add(this) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribes an observable with [DeliverWithView] and adds it to the presenter's lifecycle
|
* Subscribes an observable with [DeliverWithView] and adds it to the presenter's lifecycle
|
||||||
@ -54,7 +54,7 @@ open class BasePresenter<V> : RxPresenter<V>() {
|
|||||||
* @param onError function to execute when the observable throws an error.
|
* @param onError function to execute when the observable throws an error.
|
||||||
*/
|
*/
|
||||||
fun <T> Observable<T>.subscribeWithView(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit)? = null) =
|
fun <T> Observable<T>.subscribeWithView(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit)? = null) =
|
||||||
compose(DeliverWithView<V, T>(view())).subscribe(split(onNext, onError)).apply { add(this) }
|
compose(DeliverWithView<V, T>(view())).subscribe(split(onNext, onError)).apply { add(this) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A deliverable that only emits to the view if attached, otherwise the event is ignored.
|
* A deliverable that only emits to the view if attached, otherwise the event is ignored.
|
||||||
@ -63,11 +63,11 @@ open class BasePresenter<V> : RxPresenter<V>() {
|
|||||||
|
|
||||||
override fun call(observable: Observable<T>): Observable<Delivery<View, T>> {
|
override fun call(observable: Observable<T>): Observable<Delivery<View, T>> {
|
||||||
return observable
|
return observable
|
||||||
.materialize()
|
.materialize()
|
||||||
.filter { notification -> !notification.isOnCompleted }
|
.filter { notification -> !notification.isOnCompleted }
|
||||||
.flatMap { notification ->
|
.flatMap { notification ->
|
||||||
view.take(1).filter { it != null }.map { Delivery(it, notification) }
|
view.take(1).filter { it != null }.map { Delivery(it, notification) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import eu.davidea.flexibleadapter.FlexibleAdapter
|
|||||||
* @param controller The containing controller.
|
* @param controller The containing controller.
|
||||||
*/
|
*/
|
||||||
class CategoryAdapter(controller: CategoryController) :
|
class CategoryAdapter(controller: CategoryController) :
|
||||||
FlexibleAdapter<CategoryItem>(null, controller, true) {
|
FlexibleAdapter<CategoryItem>(null, controller, true) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listener called when an item of the list is released.
|
* Listener called when an item of the list is released.
|
||||||
|
@ -22,10 +22,11 @@ import kotlinx.android.synthetic.main.categories_controller.*
|
|||||||
/**
|
/**
|
||||||
* Controller to manage the categories for the users' library.
|
* Controller to manage the categories for the users' library.
|
||||||
*/
|
*/
|
||||||
class CategoryController(bundle: Bundle? = null) : BaseController(bundle),
|
class CategoryController(bundle: Bundle? = null) :
|
||||||
FlexibleAdapter.OnItemClickListener,
|
BaseController(bundle),
|
||||||
FlexibleAdapter.OnItemMoveListener,
|
FlexibleAdapter.OnItemClickListener,
|
||||||
CategoryAdapter.CategoryItemListener {
|
FlexibleAdapter.OnItemMoveListener,
|
||||||
|
CategoryAdapter.CategoryItemListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adapter containing category items.
|
* Adapter containing category items.
|
||||||
@ -147,12 +148,14 @@ class CategoryController(bundle: Bundle? = null) : BaseController(bundle),
|
|||||||
adapter?.restoreDeletedItems()
|
adapter?.restoreDeletedItems()
|
||||||
undoing = true
|
undoing = true
|
||||||
}
|
}
|
||||||
addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
|
addCallback(
|
||||||
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
|
object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
|
||||||
super.onDismissed(transientBottomBar, event)
|
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
|
||||||
if (!undoing) confirmDelete()
|
super.onDismissed(transientBottomBar, event)
|
||||||
|
if (!undoing) confirmDelete()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
}
|
}
|
||||||
(activity as? MainActivity)?.setUndoSnackBar(snack)
|
(activity as? MainActivity)?.setUndoSnackBar(snack)
|
||||||
}
|
}
|
||||||
|
@ -51,16 +51,22 @@ class CategoryHolder(view: View, val adapter: CategoryAdapter) : BaseFlexibleVie
|
|||||||
createCategory = category.order == CREATE_CATEGORY_ORDER
|
createCategory = category.order == CREATE_CATEGORY_ORDER
|
||||||
if (createCategory) {
|
if (createCategory) {
|
||||||
title.setTextColor(ContextCompat.getColor(itemView.context, R.color.text_color_hint))
|
title.setTextColor(ContextCompat.getColor(itemView.context, R.color.text_color_hint))
|
||||||
regularDrawable = ContextCompat.getDrawable(itemView.context, R.drawable
|
regularDrawable = ContextCompat.getDrawable(
|
||||||
.ic_add_24dp)
|
itemView.context,
|
||||||
|
R.drawable
|
||||||
|
.ic_add_24dp
|
||||||
|
)
|
||||||
image.gone()
|
image.gone()
|
||||||
edit_button.setImageDrawable(null)
|
edit_button.setImageDrawable(null)
|
||||||
edit_text.setText("")
|
edit_text.setText("")
|
||||||
edit_text.hint = title.text
|
edit_text.hint = title.text
|
||||||
} else {
|
} else {
|
||||||
title.setTextColor(ContextCompat.getColor(itemView.context, R.color.textColorPrimary))
|
title.setTextColor(ContextCompat.getColor(itemView.context, R.color.textColorPrimary))
|
||||||
regularDrawable = ContextCompat.getDrawable(itemView.context, R.drawable
|
regularDrawable = ContextCompat.getDrawable(
|
||||||
.ic_drag_handle_24dp)
|
itemView.context,
|
||||||
|
R.drawable
|
||||||
|
.ic_drag_handle_24dp
|
||||||
|
)
|
||||||
image.visible()
|
image.visible()
|
||||||
edit_text.setText(title.text)
|
edit_text.setText(title.text)
|
||||||
}
|
}
|
||||||
@ -80,7 +86,8 @@ class CategoryHolder(view: View, val adapter: CategoryAdapter) : BaseFlexibleVie
|
|||||||
if (!createCategory) {
|
if (!createCategory) {
|
||||||
reorder.setImageDrawable(
|
reorder.setImageDrawable(
|
||||||
ContextCompat.getDrawable(
|
ContextCompat.getDrawable(
|
||||||
itemView.context, R.drawable.ic_delete_24dp
|
itemView.context,
|
||||||
|
R.drawable.ic_delete_24dp
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
reorder.setOnClickListener {
|
reorder.setOnClickListener {
|
||||||
@ -96,8 +103,13 @@ class CategoryHolder(view: View, val adapter: CategoryAdapter) : BaseFlexibleVie
|
|||||||
reorder.setOnTouchListener { _, _ -> true }
|
reorder.setOnTouchListener { _, _ -> true }
|
||||||
}
|
}
|
||||||
edit_text.clearFocus()
|
edit_text.clearFocus()
|
||||||
edit_button.drawable?.mutate()?.setTint(ContextCompat.getColor(itemView.context, R
|
edit_button.drawable?.mutate()?.setTint(
|
||||||
.color.gray_button))
|
ContextCompat.getColor(
|
||||||
|
itemView.context,
|
||||||
|
R
|
||||||
|
.color.gray_button
|
||||||
|
)
|
||||||
|
)
|
||||||
reorder.setImageDrawable(regularDrawable)
|
reorder.setImageDrawable(regularDrawable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -105,7 +117,8 @@ class CategoryHolder(view: View, val adapter: CategoryAdapter) : BaseFlexibleVie
|
|||||||
private fun submitChanges() {
|
private fun submitChanges() {
|
||||||
if (edit_text.visibility == View.VISIBLE) {
|
if (edit_text.visibility == View.VISIBLE) {
|
||||||
if (adapter.categoryItemListener
|
if (adapter.categoryItemListener
|
||||||
.onCategoryRename(adapterPosition, edit_text.text.toString())) {
|
.onCategoryRename(adapterPosition, edit_text.text.toString())
|
||||||
|
) {
|
||||||
isEditing(false)
|
isEditing(false)
|
||||||
edit_text.inputType = InputType.TYPE_NULL
|
edit_text.inputType = InputType.TYPE_NULL
|
||||||
if (!createCategory)
|
if (!createCategory)
|
||||||
@ -119,8 +132,11 @@ class CategoryHolder(view: View, val adapter: CategoryAdapter) : BaseFlexibleVie
|
|||||||
private fun showKeyboard() {
|
private fun showKeyboard() {
|
||||||
val inputMethodManager: InputMethodManager =
|
val inputMethodManager: InputMethodManager =
|
||||||
itemView.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
itemView.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
inputMethodManager.showSoftInput(edit_text, WindowManager.LayoutParams
|
inputMethodManager.showSoftInput(
|
||||||
.SOFT_INPUT_ADJUST_PAN)
|
edit_text,
|
||||||
|
WindowManager.LayoutParams
|
||||||
|
.SOFT_INPUT_ADJUST_PAN
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,7 +70,8 @@ class ManageCategoryDialog(bundle: Bundle? = null) :
|
|||||||
preferences.downloadNew().set(true)
|
preferences.downloadNew().set(true)
|
||||||
}
|
}
|
||||||
if (preferences.libraryUpdateInterval().getOrDefault() > 0 &&
|
if (preferences.libraryUpdateInterval().getOrDefault() > 0 &&
|
||||||
!updatePref(preferences.libraryUpdateCategories(), view.include_global)) {
|
!updatePref(preferences.libraryUpdateCategories(), view.include_global)
|
||||||
|
) {
|
||||||
preferences.libraryUpdateInterval().set(0)
|
preferences.libraryUpdateInterval().set(0)
|
||||||
LibraryUpdateJob.setupTask(0)
|
LibraryUpdateJob.setupTask(0)
|
||||||
}
|
}
|
||||||
@ -99,9 +100,11 @@ class ManageCategoryDialog(bundle: Bundle? = null) :
|
|||||||
view.title.hint = category.name
|
view.title.hint = category.name
|
||||||
view.title.append(category.name)
|
view.title.append(category.name)
|
||||||
val downloadNew = preferences.downloadNew().getOrDefault()
|
val downloadNew = preferences.downloadNew().getOrDefault()
|
||||||
setCheckbox(view.download_new,
|
setCheckbox(
|
||||||
|
view.download_new,
|
||||||
preferences.downloadNewCategories(),
|
preferences.downloadNewCategories(),
|
||||||
true)
|
true
|
||||||
|
)
|
||||||
if (downloadNew && preferences.downloadNewCategories().getOrDefault().isEmpty())
|
if (downloadNew && preferences.downloadNewCategories().getOrDefault().isEmpty())
|
||||||
view.download_new.gone()
|
view.download_new.gone()
|
||||||
else if (!downloadNew)
|
else if (!downloadNew)
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user