diff --git a/app/build.gradle b/app/build.gradle
index d2200e0a33..3c464147e5 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -3,6 +3,7 @@ import java.text.SimpleDateFormat
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-kapt'
if (file("custom.gradle").exists()) {
apply from: "custom.gradle"
@@ -169,10 +170,12 @@ dependencies {
compile "uy.kohesive.injekt:injekt-core:1.16.1"
// Image library
- compile 'com.github.bumptech.glide:glide:3.8.0'
- compile 'com.github.bumptech.glide:okhttp3-integration:1.5.0@aar'
+ compile 'com.github.bumptech.glide:glide:4.1.1'
+ compile 'com.github.bumptech.glide:okhttp3-integration:4.1.1'
+ kapt 'com.github.bumptech.glide:compiler:4.1.1'
+
// Transformations
- compile 'jp.wasabeef:glide-transformations:2.0.2'
+ compile 'jp.wasabeef:glide-transformations:3.0.1'
// Logging
compile 'com.jakewharton.timber:timber:4.5.1'
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index dac8ffbf90..cc477df740 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -24,6 +24,7 @@
# Glide specific rules #
# https://github.com/bumptech/glide
-keep public class * implements com.bumptech.glide.module.GlideModule
+-keep public class * extends com.bumptech.glide.AppGlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
**[] $VALUES;
public *;
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 74ddb8c840..0c88aa9d76 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -95,10 +95,6 @@
android:name=".data.backup.BackupRestoreService"
android:exported="false"/>
-
-
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/glide/FileFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/glide/FileFetcher.kt
index 6e1e06ff4a..a795e2e059 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/glide/FileFetcher.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/glide/FileFetcher.kt
@@ -1,35 +1,51 @@
-package eu.kanade.tachiyomi.data.glide
-
-import com.bumptech.glide.Priority
-import com.bumptech.glide.load.data.DataFetcher
-import java.io.File
-import java.io.IOException
-import java.io.InputStream
-
-open class FileFetcher(private val file: File) : DataFetcher {
-
- private var data: InputStream? = null
-
- override fun loadData(priority: Priority): InputStream {
- data = file.inputStream()
- return data!!
- }
-
- override fun cleanup() {
- data?.let { data ->
- try {
- data.close()
- } catch (e: IOException) {
- // Ignore
- }
- }
- }
-
- override fun cancel() {
- // Do nothing.
- }
-
- override fun getId(): String {
- return file.toString()
- }
+package eu.kanade.tachiyomi.data.glide
+
+import android.content.ContentValues.TAG
+import android.util.Log
+import com.bumptech.glide.Priority
+import com.bumptech.glide.load.DataSource
+import com.bumptech.glide.load.data.DataFetcher
+import java.io.*
+
+open class FileFetcher(private val file: File) : DataFetcher {
+
+ private var data: InputStream? = null
+
+ override fun loadData(priority: Priority, callback: DataFetcher.DataCallback) {
+ loadFromFile(callback)
+ }
+
+ protected fun loadFromFile(callback: DataFetcher.DataCallback) {
+ try {
+ data = FileInputStream(file)
+ } catch (e: FileNotFoundException) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Failed to open file", e)
+ }
+ callback.onLoadFailed(e)
+ return
+ }
+
+ callback.onDataReady(data)
+ }
+
+ override fun cleanup() {
+ try {
+ data?.close()
+ } catch (e: IOException) {
+ // Ignored.
+ }
+ }
+
+ override fun cancel() {
+ // Do nothing.
+ }
+
+ override fun getDataClass(): Class {
+ return InputStream::class.java
+ }
+
+ override fun getDataSource(): DataSource {
+ return DataSource.LOCAL
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/glide/LibraryMangaUrlFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/glide/LibraryMangaUrlFetcher.kt
new file mode 100644
index 0000000000..0f8b55fcb6
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/glide/LibraryMangaUrlFetcher.kt
@@ -0,0 +1,72 @@
+package eu.kanade.tachiyomi.data.glide
+
+import com.bumptech.glide.Priority
+import com.bumptech.glide.load.data.DataFetcher
+import eu.kanade.tachiyomi.data.database.models.Manga
+import java.io.File
+import java.io.FileNotFoundException
+import java.io.InputStream
+
+/**
+ * A [DataFetcher] for loading a cover of a library manga.
+ * It tries to load the cover from our custom cache, and if it's not found, it fallbacks to network
+ * and copies the result to the cache.
+ *
+ * @param networkFetcher the network fetcher for this cover.
+ * @param manga the manga of the cover to load.
+ * @param file the file where this cover should be. It may exists or not.
+ */
+class LibraryMangaUrlFetcher(private val networkFetcher: DataFetcher,
+ private val manga: Manga,
+ private val file: File)
+: FileFetcher(file) {
+
+ override fun loadData(priority: Priority, callback: DataFetcher.DataCallback) {
+ if (!file.exists()) {
+ networkFetcher.loadData(priority, object : DataFetcher.DataCallback {
+ override fun onDataReady(data: InputStream?) {
+ if (data != null) {
+ val tmpFile = File(file.path + ".tmp")
+ try {
+ // Retrieve destination stream, create parent folders if needed.
+ val output = try {
+ tmpFile.outputStream()
+ } catch (e: FileNotFoundException) {
+ tmpFile.parentFile.mkdirs()
+ tmpFile.outputStream()
+ }
+
+ // Copy the file and rename to the original.
+ data.use { output.use { data.copyTo(output) } }
+ tmpFile.renameTo(file)
+ } catch (e: Exception) {
+ tmpFile.delete()
+ callback.onLoadFailed(e)
+ }
+ loadFromFile(callback)
+ } else {
+ callback.onLoadFailed(Exception("Null data"))
+ }
+ }
+
+ override fun onLoadFailed(e: Exception) {
+ callback.onLoadFailed(e)
+ }
+
+ })
+ } else {
+ loadFromFile(callback)
+ }
+ }
+
+ override fun cleanup() {
+ super.cleanup()
+ networkFetcher.cleanup()
+ }
+
+ override fun cancel() {
+ super.cancel()
+ networkFetcher.cancel()
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaFileFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaFileFetcher.kt
deleted file mode 100644
index 5e594e4969..0000000000
--- a/app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaFileFetcher.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package eu.kanade.tachiyomi.data.glide
-
-import eu.kanade.tachiyomi.data.database.models.Manga
-import java.io.File
-
-open class MangaFileFetcher(private val file: File, private val manga: Manga) : FileFetcher(file) {
-
- /**
- * Returns the id for this manga's cover.
- *
- * Appending the file's modified date to the url, we can force Glide to skip its memory and disk
- * lookup step and fetch from our custom cache. This allows us to invalidate Glide's cache when
- * the file has changed. If the file doesn't exist it will append a 0.
- */
- override fun getId(): String {
- return manga.thumbnail_url + file.lastModified()
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaModelLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaModelLoader.kt
index f5342c451e..682a2b39c8 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaModelLoader.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaModelLoader.kt
@@ -1,23 +1,24 @@
package eu.kanade.tachiyomi.data.glide
-import android.content.Context
import android.util.LruCache
-import com.bumptech.glide.Glide
import com.bumptech.glide.integration.okhttp3.OkHttpStreamFetcher
-import com.bumptech.glide.load.data.DataFetcher
+import com.bumptech.glide.load.Options
import com.bumptech.glide.load.model.*
-import com.bumptech.glide.load.model.stream.StreamModelLoader
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.database.models.Manga
+import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.HttpSource
+import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.io.File
import java.io.InputStream
+
/**
* A class for loading a cover associated with a [Manga] that can be present in our own cache.
- * Coupled with [MangaUrlFetcher], this class allows to implement the following flow:
+ * Coupled with [LibraryMangaUrlFetcher], this class allows to implement the following flow:
*
* - Check in RAM LRU.
* - Check in disk LRU.
@@ -26,7 +27,7 @@ import java.io.InputStream
*
* @param context the application context.
*/
-class MangaModelLoader(context: Context) : StreamModelLoader {
+class MangaModelLoader : ModelLoader {
/**
* Cover cache where persistent covers are stored.
@@ -39,16 +40,15 @@ class MangaModelLoader(context: Context) : StreamModelLoader {
private val sourceManager: SourceManager by injectLazy()
/**
- * Base network loader.
+ * Default network client.
*/
- private val baseUrlLoader = Glide.buildModelLoader(GlideUrl::class.java,
- InputStream::class.java, context)
+ private val defaultClient = Injekt.get().client
/**
* LRU cache whose key is the thumbnail url of the manga, and the value contains the request url
* and the file where it should be stored in case the manga is a favorite.
*/
- private val lruCache = LruCache>(100)
+ private val lruCache = LruCache(100)
/**
* Map where request headers are stored for a source.
@@ -60,12 +60,17 @@ class MangaModelLoader(context: Context) : StreamModelLoader {
*/
class Factory : ModelLoaderFactory {
- override fun build(context: Context, factories: GenericLoaderFactory)
- = MangaModelLoader(context)
+ override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader {
+ return MangaModelLoader()
+ }
override fun teardown() {}
}
+ override fun handles(model: Manga): Boolean {
+ return true
+ }
+
/**
* Returns a fetcher for the given manga or null if the url is empty.
*
@@ -73,10 +78,8 @@ class MangaModelLoader(context: Context) : StreamModelLoader {
* @param width the width of the view where the resource will be loaded.
* @param height the height of the view where the resource will be loaded.
*/
- override fun getResourceFetcher(manga: Manga,
- width: Int,
- height: Int): DataFetcher? {
-
+ override fun buildLoadData(manga: Manga, width: Int, height: Int,
+ options: Options?): ModelLoader.LoadData? {
// Check thumbnail is not null or empty
val url = manga.thumbnail_url
if (url == null || url.isEmpty()) {
@@ -85,26 +88,28 @@ class MangaModelLoader(context: Context) : StreamModelLoader {
if (url.startsWith("http")) {
val source = sourceManager.get(manga.source) as? HttpSource
-
- // Obtain the request url and the file for this url from the LRU cache, or calculate it
- // and add them to the cache.
- val (glideUrl, file) = lruCache.get(url) ?:
- Pair(GlideUrl(url, getHeaders(manga, source)), coverCache.getCoverFile(url)).apply {
- lruCache.put(url, this)
- }
+ val glideUrl = GlideUrl(url, getHeaders(manga, source))
// Get the resource fetcher for this request url.
- val networkFetcher = source?.let { OkHttpStreamFetcher(it.client, glideUrl) }
- ?: baseUrlLoader.getResourceFetcher(glideUrl, width, height)
+ val networkFetcher = OkHttpStreamFetcher(source?.client ?: defaultClient, glideUrl)
+
+ if (!manga.favorite) {
+ return ModelLoader.LoadData(glideUrl, networkFetcher)
+ }
+
+ // Obtain the file for this url from the LRU cache, or retrieve and add it to the cache.
+ val file = lruCache.getOrPut(glideUrl) { coverCache.getCoverFile(url) }
+
+ val libraryFetcher = LibraryMangaUrlFetcher(networkFetcher, manga, file)
// Return an instance of the fetcher providing the needed elements.
- return MangaUrlFetcher(networkFetcher, file, manga)
+ return ModelLoader.LoadData(MangaSignature(manga, file), libraryFetcher)
} else {
// Get the file from the url, removing the scheme if present.
val file = File(url.substringAfter("file://"))
// Return an instance of the fetcher providing the needed elements.
- return MangaFileFetcher(file, manga)
+ return ModelLoader.LoadData(MangaSignature(manga, file), FileFetcher(file))
}
}
@@ -127,4 +132,15 @@ class MangaModelLoader(context: Context) : StreamModelLoader {
}
}
+ private inline fun LruCache.getOrPut(key: K, defaultValue: () -> V): V {
+ val value = get(key)
+ return if (value == null) {
+ val answer = defaultValue()
+ put(key, answer)
+ answer
+ } else {
+ value
+ }
+ }
+
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaSignature.kt b/app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaSignature.kt
new file mode 100644
index 0000000000..aa3ebf6f95
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaSignature.kt
@@ -0,0 +1,27 @@
+package eu.kanade.tachiyomi.data.glide
+
+import com.bumptech.glide.load.Key
+import eu.kanade.tachiyomi.data.database.models.Manga
+import java.io.File
+import java.security.MessageDigest
+
+class MangaSignature(manga: Manga, file: File) : Key {
+
+ private val key = manga.thumbnail_url + file.lastModified()
+
+ override fun equals(other: Any?): Boolean {
+ return if (other is MangaSignature) {
+ key == other.key
+ } else {
+ false
+ }
+ }
+
+ override fun hashCode(): Int {
+ return key.hashCode()
+ }
+
+ override fun updateDiskCacheKey(md: MessageDigest) {
+ md.update(key.toByteArray(Key.CHARSET))
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaUrlFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaUrlFetcher.kt
deleted file mode 100644
index 1933095835..0000000000
--- a/app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaUrlFetcher.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-package eu.kanade.tachiyomi.data.glide
-
-import com.bumptech.glide.Priority
-import com.bumptech.glide.load.data.DataFetcher
-import eu.kanade.tachiyomi.data.database.models.Manga
-import java.io.File
-import java.io.FileNotFoundException
-import java.io.InputStream
-
-/**
- * A [DataFetcher] for loading a cover of a manga depending on its favorite status.
- * If the manga is favorite, it tries to load the cover from our cache, and if it's not found, it
- * fallbacks to network and copies it to the cache.
- * If the manga is not favorite, it tries to delete the cover from our cache and always fallback
- * to network for fetching.
- *
- * @param networkFetcher the network fetcher for this cover.
- * @param file the file where this cover should be. It may exists or not.
- * @param manga the manga of the cover to load.
- */
-class MangaUrlFetcher(private val networkFetcher: DataFetcher,
- private val file: File,
- private val manga: Manga)
-: MangaFileFetcher(file, manga) {
-
- override fun loadData(priority: Priority): InputStream {
- if (manga.favorite) {
- synchronized(file) {
- if (!file.exists()) {
- val tmpFile = File(file.path + ".tmp")
- try {
- // Retrieve source stream.
- val input = networkFetcher.loadData(priority)
- ?: throw Exception("Couldn't open source stream")
-
- // Retrieve destination stream, create parent folders if needed.
- val output = try {
- tmpFile.outputStream()
- } catch (e: FileNotFoundException) {
- tmpFile.parentFile.mkdirs()
- tmpFile.outputStream()
- }
-
- // Copy the file and rename to the original.
- input.use { output.use { input.copyTo(output) } }
- tmpFile.renameTo(file)
- } catch (e: Exception) {
- tmpFile.delete()
- throw e
- }
- }
- }
- return super.loadData(priority)
- } else {
- if (file.exists()) {
- file.delete()
- }
- return networkFetcher.loadData(priority)
- }
- }
-
- override fun cancel() {
- networkFetcher.cancel()
- }
-
- override fun cleanup() {
- super.cleanup()
- networkFetcher.cleanup()
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/glide/AppGlideModule.kt b/app/src/main/java/eu/kanade/tachiyomi/data/glide/TachiGlideModule.kt
similarity index 50%
rename from app/src/main/java/eu/kanade/tachiyomi/data/glide/AppGlideModule.kt
rename to app/src/main/java/eu/kanade/tachiyomi/data/glide/TachiGlideModule.kt
index b1b722acbb..457f8d2286 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/glide/AppGlideModule.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/glide/TachiGlideModule.kt
@@ -1,12 +1,18 @@
package eu.kanade.tachiyomi.data.glide
import android.content.Context
+import android.graphics.drawable.Drawable
import com.bumptech.glide.Glide
import com.bumptech.glide.GlideBuilder
+import com.bumptech.glide.Registry
+import com.bumptech.glide.annotation.GlideModule
import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader
+import com.bumptech.glide.load.DecodeFormat
import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory
import com.bumptech.glide.load.model.GlideUrl
-import com.bumptech.glide.module.GlideModule
+import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
+import com.bumptech.glide.module.AppGlideModule
+import com.bumptech.glide.request.RequestOptions
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.network.NetworkHelper
import uy.kohesive.injekt.Injekt
@@ -16,17 +22,20 @@ import java.io.InputStream
/**
* Class used to update Glide module settings
*/
-class AppGlideModule : GlideModule {
+@GlideModule
+class TachiGlideModule : AppGlideModule() {
override fun applyOptions(context: Context, builder: GlideBuilder) {
- // Set the cache size of Glide to 15 MiB
- builder.setDiskCache(InternalCacheDiskCacheFactory(context, 15 * 1024 * 1024))
+ builder.setDiskCache(InternalCacheDiskCacheFactory(context, 50 * 1024 * 1024))
+ builder.setDefaultRequestOptions(RequestOptions().format(DecodeFormat.PREFER_RGB_565))
+ builder.setDefaultTransitionOptions(Drawable::class.java,
+ DrawableTransitionOptions.withCrossFade())
}
- override fun registerComponents(context: Context, glide: Glide) {
+ override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
val networkFactory = OkHttpUrlLoader.Factory(Injekt.get().client)
- glide.register(GlideUrl::class.java, InputStream::class.java, networkFactory)
- glide.register(Manga::class.java, InputStream::class.java, MangaModelLoader.Factory())
+ registry.replace(GlideUrl::class.java, InputStream::class.java, networkFactory)
+ registry.append(Manga::class.java, InputStream::class.java, MangaModelLoader.Factory())
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueGridHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueGridHolder.kt
index 4cd6554fa1..3fdba1e2e7 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueGridHolder.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueGridHolder.kt
@@ -1,10 +1,10 @@
package eu.kanade.tachiyomi.ui.catalogue
import android.view.View
-import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.data.database.models.Manga
+import eu.kanade.tachiyomi.data.glide.GlideApp
import eu.kanade.tachiyomi.widget.StateImageViewTarget
import kotlinx.android.synthetic.main.catalogue_grid_item.view.*
@@ -36,16 +36,15 @@ class CatalogueGridHolder(private val view: View, private val adapter: FlexibleA
}
override fun setImage(manga: Manga) {
- Glide.clear(view.thumbnail)
+ GlideApp.with(view.context).clear(view.thumbnail)
if (!manga.thumbnail_url.isNullOrEmpty()) {
- Glide.with(view.context)
+ GlideApp.with(view.context)
.load(manga)
- .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .diskCacheStrategy(DiskCacheStrategy.DATA)
.centerCrop()
.skipMemoryCache(true)
.placeholder(android.R.color.transparent)
.into(StateImageViewTarget(view.thumbnail, view.progress))
-
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueListHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueListHolder.kt
index 5b782b1679..a12ec77d25 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueListHolder.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueListHolder.kt
@@ -1,12 +1,11 @@
package eu.kanade.tachiyomi.ui.catalogue
import android.view.View
-import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.data.database.models.Manga
+import eu.kanade.tachiyomi.data.glide.GlideApp
import eu.kanade.tachiyomi.util.getResourceColor
-import jp.wasabeef.glide.transformations.CropCircleTransformation
import kotlinx.android.synthetic.main.catalogue_list_item.view.*
/**
@@ -37,13 +36,13 @@ class CatalogueListHolder(private val view: View, adapter: FlexibleAdapter<*>) :
}
override fun setImage(manga: Manga) {
- Glide.clear(view.thumbnail)
+ GlideApp.with(view.context).clear(view.thumbnail)
if (!manga.thumbnail_url.isNullOrEmpty()) {
- Glide.with(view.context)
+ GlideApp.with(view.context)
.load(manga)
- .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .diskCacheStrategy(DiskCacheStrategy.DATA)
.centerCrop()
- .bitmapTransform(CropCircleTransformation(view.context))
+ .circleCrop()
.dontAnimate()
.skipMemoryCache(true)
.placeholder(android.R.color.transparent)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchCardHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchCardHolder.kt
index 02c102b8fa..e35a2ceb78 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchCardHolder.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchCardHolder.kt
@@ -1,10 +1,10 @@
package eu.kanade.tachiyomi.ui.catalogue.global_search
import android.view.View
-import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.data.database.models.Manga
+import eu.kanade.tachiyomi.data.glide.GlideApp
import eu.kanade.tachiyomi.widget.StateImageViewTarget
import kotlinx.android.synthetic.main.catalogue_global_search_controller_card_item.view.*
@@ -28,11 +28,11 @@ class CatalogueSearchCardHolder(view: View, adapter: CatalogueSearchCardAdapter)
}
fun setImage(manga: Manga) {
- Glide.clear(itemView.itemImage)
+ GlideApp.with(itemView.context).clear(itemView.itemImage)
if (!manga.thumbnail_url.isNullOrEmpty()) {
- Glide.with(itemView.context)
+ GlideApp.with(itemView.context)
.load(manga)
- .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .diskCacheStrategy(DiskCacheStrategy.DATA)
.centerCrop()
.skipMemoryCache(true)
.placeholder(android.R.color.transparent)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGridHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGridHolder.kt
index fcf3789e4b..b13616c8d0 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGridHolder.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGridHolder.kt
@@ -1,10 +1,10 @@
package eu.kanade.tachiyomi.ui.library
import android.view.View
-import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.data.database.models.Manga
+import eu.kanade.tachiyomi.data.glide.GlideApp
import kotlinx.android.synthetic.main.catalogue_grid_item.view.*
/**
@@ -38,10 +38,10 @@ class LibraryGridHolder(
}
// Update the cover.
- Glide.clear(view.thumbnail)
- Glide.with(view.context)
+ GlideApp.with(view.context).clear(view.thumbnail)
+ GlideApp.with(view.context)
.load(manga)
- .diskCacheStrategy(DiskCacheStrategy.RESULT)
+ .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.centerCrop()
.into(view.thumbnail)
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt
index 6de8995323..5895b5acb8 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt
@@ -1,11 +1,10 @@
package eu.kanade.tachiyomi.ui.library
import android.view.View
-import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.data.database.models.Manga
-import jp.wasabeef.glide.transformations.CropCircleTransformation
+import eu.kanade.tachiyomi.data.glide.GlideApp
import kotlinx.android.synthetic.main.catalogue_list_item.view.*
/**
@@ -46,12 +45,12 @@ class LibraryListHolder(
}
// Update the cover.
- Glide.clear(itemView.thumbnail)
- Glide.with(itemView.context)
+ GlideApp.with(itemView.context).clear(itemView.thumbnail)
+ GlideApp.with(itemView.context)
.load(manga)
- .diskCacheStrategy(DiskCacheStrategy.RESULT)
+ .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.centerCrop()
- .bitmapTransform(CropCircleTransformation(itemView.context))
+ .circleCrop()
.dontAnimate()
.into(itemView.thumbnail)
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt
index 651ded8785..b9b982d049 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt
@@ -1,8 +1,10 @@
package eu.kanade.tachiyomi.ui.manga.info
+import android.app.Dialog
import android.app.PendingIntent
import android.content.Intent
import android.graphics.Bitmap
+import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Build
import android.os.Bundle
@@ -12,20 +14,22 @@ import android.support.v4.content.pm.ShortcutManagerCompat
import android.support.v4.graphics.drawable.IconCompat
import android.view.*
import com.afollestad.materialdialogs.MaterialDialog
-import com.bumptech.glide.BitmapRequestBuilder
-import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
-import com.bumptech.glide.load.resource.bitmap.CenterCrop
+import com.bumptech.glide.load.resource.bitmap.RoundedCorners
+import com.bumptech.glide.request.target.SimpleTarget
+import com.bumptech.glide.request.transition.Transition
import com.jakewharton.rxbinding.support.v4.widget.refreshes
import com.jakewharton.rxbinding.view.clicks
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Manga
+import eu.kanade.tachiyomi.data.glide.GlideApp
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
+import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog
import eu.kanade.tachiyomi.ui.main.MainActivity
@@ -33,15 +37,9 @@ import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.getResourceColor
import eu.kanade.tachiyomi.util.snack
import eu.kanade.tachiyomi.util.toast
-import jp.wasabeef.glide.transformations.CropCircleTransformation
import jp.wasabeef.glide.transformations.CropSquareTransformation
import jp.wasabeef.glide.transformations.MaskTransformation
-import jp.wasabeef.glide.transformations.RoundedCornersTransformation
import kotlinx.android.synthetic.main.manga_info_controller.view.*
-import rx.Observable
-import rx.android.schedulers.AndroidSchedulers
-import rx.schedulers.Schedulers
-import rx.subscriptions.Subscriptions
import uy.kohesive.injekt.injectLazy
import java.text.DecimalFormat
@@ -157,16 +155,16 @@ class MangaInfoController : NucleusController(),
// Set cover if it wasn't already.
if (manga_cover.drawable == null && !manga.thumbnail_url.isNullOrEmpty()) {
- Glide.with(context)
+ GlideApp.with(context)
.load(manga)
- .diskCacheStrategy(DiskCacheStrategy.RESULT)
+ .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.centerCrop()
.into(manga_cover)
if (backdrop != null) {
- Glide.with(context)
+ GlideApp.with(context)
.load(manga)
- .diskCacheStrategy(DiskCacheStrategy.RESULT)
+ .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.centerCrop()
.into(backdrop)
}
@@ -316,51 +314,78 @@ class MangaInfoController : NucleusController(),
}
/**
- * Choose the shape of the icon
- * Only use for pre Oreo devices.
+ * Add a shortcut of the manga to the home screen
*/
- private fun chooseIconDialog() {
- val activity = activity ?: return
-
- val modes = intArrayOf(R.string.circular_icon,
- R.string.rounded_icon,
- R.string.square_icon,
- R.string.star_icon)
-
- val request = Glide.with(activity).load(presenter.manga).asBitmap()
-
- fun getIcon(i: Int): Bitmap? = when (i) {
- 0 -> request.transform(CropCircleTransformation(activity)).toIcon()
- 1 -> request.transform(RoundedCornersTransformation(activity, 5, 0)).toIcon()
- 2 -> request.transform(CropSquareTransformation(activity)).toIcon()
- 3 -> request.transform(CenterCrop(activity),
- MaskTransformation(activity, R.drawable.mask_star)).toIcon()
- else -> null
+ private fun addToHomeScreen() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ // TODO are transformations really unsupported or is it just the Pixel Launcher?
+ createShortcutForShape()
+ } else {
+ ChooseShapeDialog(this).showDialog(router)
}
-
- val dialog = MaterialDialog.Builder(activity)
- .title(R.string.icon_shape)
- .negativeText(android.R.string.cancel)
- .items(modes.map { activity.getString(it) })
- .itemsCallback { _, _, i, _ ->
- Observable.fromCallable { getIcon(i) }
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe({ icon ->
- if (icon != null) createShortcut(icon)
- }, {
- activity.toast(R.string.icon_creation_fail)
- })
- }
- .show()
-
- untilDestroySubscriptions.add(Subscriptions.create { dialog.dismiss() })
}
- private fun BitmapRequestBuilder.toIcon() = this.into(96,96).get()
+ /**
+ * Dialog to choose a shape for the icon.
+ */
+ private class ChooseShapeDialog(bundle: Bundle? = null) : DialogController(bundle) {
+
+ constructor(target: MangaInfoController) : this() {
+ targetController = target
+ }
+
+ override fun onCreateDialog(savedViewState: Bundle?): Dialog {
+ val modes = intArrayOf(R.string.circular_icon,
+ R.string.rounded_icon,
+ R.string.square_icon,
+ R.string.star_icon)
+
+ return MaterialDialog.Builder(activity!!)
+ .title(R.string.icon_shape)
+ .negativeText(android.R.string.cancel)
+ .items(modes.map { activity?.getString(it) })
+ .itemsCallback { _, _, i, _ ->
+ (targetController as? MangaInfoController)?.createShortcutForShape(i)
+ }
+ .build()
+ }
+ }
+
+ /**
+ * Retrieves the bitmap of the shortcut with the requested shape and calls [createShortcut] when
+ * the resource is available.
+ *
+ * @param i The shape index to apply. No transformation is performed if the parameter is not
+ * provided.
+ */
+ private fun createShortcutForShape(i: Int = 0) {
+ GlideApp.with(activity)
+ .asBitmap()
+ .load(presenter.manga)
+ .diskCacheStrategy(DiskCacheStrategy.NONE)
+ .apply {
+ when (i) {
+ 0 -> circleCrop()
+ 1 -> transform(RoundedCorners(5))
+ 2 -> transform(CropSquareTransformation())
+ 3 -> centerCrop().transform(MaskTransformation(R.drawable.mask_star))
+ }
+ }
+ .into(object : SimpleTarget(96, 96) {
+ override fun onResourceReady(resource: Bitmap, transition: Transition?) {
+ createShortcut(resource)
+ }
+
+ override fun onLoadFailed(errorDrawable: Drawable?) {
+ activity?.toast(R.string.icon_creation_fail)
+ }
+ })
+ }
/**
* Create shortcut using ShortcutManager.
+ *
+ * @param icon The image of the shortcut.
*/
private fun createShortcut(icon: Bitmap) {
val activity = activity ?: return
@@ -375,49 +400,29 @@ class MangaInfoController : NucleusController(),
// Check if shortcut placement is supported
if (ShortcutManagerCompat.isRequestPinShortcutSupported(activity)) {
+ val shortcutId = "manga-shortcut-${presenter.manga.title}-${presenter.source.name}"
// Create shortcut info
- val pinShortcutInfo = ShortcutInfoCompat.Builder(activity, "manga-shortcut-${presenter.manga.title}-${presenter.source.name}")
+ val shortcutInfo = ShortcutInfoCompat.Builder(activity, shortcutId)
.setShortLabel(presenter.manga.title)
.setIcon(IconCompat.createWithBitmap(icon))
- .setIntent(shortcutIntent).build()
+ .setIntent(shortcutIntent)
+ .build()
- val successCallback: PendingIntent
-
- successCallback = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val successCallback = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create the CallbackIntent.
- val pinnedShortcutCallbackIntent = ShortcutManagerCompat.createShortcutResultIntent(activity, pinShortcutInfo)
+ val intent = ShortcutManagerCompat.createShortcutResultIntent(activity, shortcutInfo)
// Configure the intent so that the broadcast receiver gets the callback successfully.
- PendingIntent.getBroadcast(activity, 0, pinnedShortcutCallbackIntent, 0)
- } else{
+ PendingIntent.getBroadcast(activity, 0, intent, 0)
+ } else {
NotificationReceiver.shortcutCreatedBroadcast(activity)
}
// Request shortcut.
- ShortcutManagerCompat.requestPinShortcut(activity, pinShortcutInfo,
+ ShortcutManagerCompat.requestPinShortcut(activity, shortcutInfo,
successCallback.intentSender)
}
}
- /**
- * Add a shortcut of the manga to the home screen
- */
- private fun addToHomeScreen() {
- // Get bitmap icon
- val bitmap = Glide.with(activity).load(presenter.manga).asBitmap()
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
- Observable.fromCallable {
- bitmap.toIcon()
- }
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe({icon ->
- createShortcut(icon)
- })
- }else{
- chooseIconDialog()
- }
- }
-}
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt
index 38441d3b8a..ab690c8195 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt
@@ -3,9 +3,9 @@ package eu.kanade.tachiyomi.ui.reader
import android.content.Context
import android.graphics.Bitmap
import android.support.v4.app.NotificationCompat
-import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.data.glide.GlideApp
import eu.kanade.tachiyomi.data.notification.NotificationHandler
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.data.notification.Notifications
@@ -34,12 +34,12 @@ class SaveImageNotifier(private val context: Context) {
* @param file image file containing downloaded page image.
*/
fun onComplete(file: File) {
- val bitmap = Glide.with(context)
- .load(file)
+ val bitmap = GlideApp.with(context)
.asBitmap()
+ .load(file)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
- .into(720, 1280)
+ .submit(720, 1280)
.get()
if (bitmap != null) {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/RecentChapterHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/RecentChapterHolder.kt
index 0c3a1f0309..37a72f5373 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/RecentChapterHolder.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/RecentChapterHolder.kt
@@ -2,14 +2,13 @@ package eu.kanade.tachiyomi.ui.recent_updates
import android.view.View
import android.widget.PopupMenu
-import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.download.model.Download
+import eu.kanade.tachiyomi.data.glide.GlideApp
import eu.kanade.tachiyomi.util.getResourceColor
import eu.kanade.tachiyomi.util.setVectorCompat
-import jp.wasabeef.glide.transformations.CropCircleTransformation
import kotlinx.android.synthetic.main.recent_chapters_item.view.*
/**
@@ -68,12 +67,12 @@ class RecentChapterHolder(private val view: View, private val adapter: RecentCha
view.chapter_menu_icon.setVectorCompat(R.drawable.ic_more_horiz_black_24dp, view.context.getResourceColor(R.attr.icon_color))
// Set cover
- Glide.clear(itemView.manga_cover)
+ GlideApp.with(itemView.context).clear(itemView.manga_cover)
if (!item.manga.thumbnail_url.isNullOrEmpty()) {
- Glide.with(itemView.context)
+ GlideApp.with(itemView.context)
.load(item.manga)
- .diskCacheStrategy(DiskCacheStrategy.RESULT)
- .bitmapTransform(CropCircleTransformation(view.context))
+ .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
+ .circleCrop()
.into(itemView.manga_cover)
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadHolder.kt
index 2d4464ffa9..ecabfb8730 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadHolder.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadHolder.kt
@@ -1,11 +1,11 @@
package eu.kanade.tachiyomi.ui.recently_read
import android.view.View
-import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory
+import eu.kanade.tachiyomi.data.glide.GlideApp
import kotlinx.android.synthetic.main.recently_read_item.view.*
import java.util.*
@@ -58,15 +58,15 @@ class RecentlyReadHolder(
itemView.last_read.text = adapter.dateFormat.format(Date(history.last_read))
// Set cover
- Glide.clear(itemView.cover)
+ GlideApp.with(itemView.context).clear(itemView.cover)
if (!manga.thumbnail_url.isNullOrEmpty()) {
- Glide.with(itemView.context)
+ GlideApp.with(itemView.context)
.load(manga)
- .diskCacheStrategy(DiskCacheStrategy.RESULT)
+ .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.centerCrop()
.into(itemView.cover)
}
-
}
+
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/StateImageViewTarget.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/StateImageViewTarget.kt
index af12756dd5..75d6e74c84 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/widget/StateImageViewTarget.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/widget/StateImageViewTarget.kt
@@ -5,9 +5,8 @@ import android.support.graphics.drawable.VectorDrawableCompat
import android.view.View
import android.widget.ImageView
import android.widget.ImageView.ScaleType
-import com.bumptech.glide.load.resource.drawable.GlideDrawable
-import com.bumptech.glide.request.animation.GlideAnimation
-import com.bumptech.glide.request.target.GlideDrawableImageViewTarget
+import com.bumptech.glide.request.target.ImageViewTarget
+import com.bumptech.glide.request.transition.Transition
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.getResourceColor
import eu.kanade.tachiyomi.util.gone
@@ -26,16 +25,23 @@ class StateImageViewTarget(view: ImageView,
val progress: View? = null,
val errorDrawableRes: Int = R.drawable.ic_broken_image_grey_24dp,
val errorScaleType: ScaleType = ScaleType.CENTER) :
- GlideDrawableImageViewTarget(view) {
+
+ ImageViewTarget(view) {
+
+ private var resource: Drawable? = null
private val imageScaleType = view.scaleType
+ override fun setResource(resource: Drawable?) {
+ view.setImageDrawable(resource)
+ }
+
override fun onLoadStarted(placeholder: Drawable?) {
progress?.visible()
super.onLoadStarted(placeholder)
}
- override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
+ override fun onLoadFailed(errorDrawable: Drawable?) {
progress?.gone()
view.scaleType = errorScaleType
@@ -49,9 +55,10 @@ class StateImageViewTarget(view: ImageView,
super.onLoadCleared(placeholder)
}
- override fun onResourceReady(resource: GlideDrawable?, animation: GlideAnimation?) {
+ override fun onResourceReady(resource: Drawable?, transition: Transition?) {
progress?.gone()
view.scaleType = imageScaleType
- super.onResourceReady(resource, animation)
+ super.onResourceReady(resource, transition)
+ this.resource = resource
}
}
\ No newline at end of file