mirror of
https://github.com/mihonapp/mihon.git
synced 2025-06-28 12:07:52 +02:00
Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
dcbd72e64d | |||
52e1e93f9d | |||
5b1f4f189b | |||
d77a1e6925 | |||
19c713ebb2 | |||
90e0e0b72a | |||
22bbcaeed0 | |||
d7b8015df7 | |||
c1ac47e1ce | |||
e375101132 | |||
05b14bae7b | |||
eb15fe3898 | |||
4f5518bdd8 | |||
c9e1e6e020 | |||
ade73e6892 |
@ -42,8 +42,8 @@ android {
|
|||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 24
|
targetSdkVersion 24
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
versionCode 11
|
versionCode 13
|
||||||
versionName "0.3.0"
|
versionName "0.3.2"
|
||||||
|
|
||||||
buildConfigField "String", "COMMIT_COUNT", "\"${getCommitCount()}\""
|
buildConfigField "String", "COMMIT_COUNT", "\"${getCommitCount()}\""
|
||||||
buildConfigField "String", "COMMIT_SHA", "\"${getGitSha()}\""
|
buildConfigField "String", "COMMIT_SHA", "\"${getGitSha()}\""
|
||||||
@ -93,7 +93,7 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
// Modified dependencies
|
// Modified dependencies
|
||||||
compile 'com.github.inorichi:subsampling-scale-image-view:2d9c854'
|
compile 'com.github.inorichi:subsampling-scale-image-view:96d2c7f'
|
||||||
compile 'com.github.inorichi:ReactiveNetwork:69092ed'
|
compile 'com.github.inorichi:ReactiveNetwork:69092ed'
|
||||||
|
|
||||||
// Android support library
|
// Android support library
|
||||||
@ -108,7 +108,7 @@ dependencies {
|
|||||||
|
|
||||||
compile 'com.android.support:multidex:1.0.1'
|
compile 'com.android.support:multidex:1.0.1'
|
||||||
|
|
||||||
compile 'com.google.android.gms:play-services-gcm:9.6.1'
|
compile 'com.google.android.gms:play-services-gcm:9.8.0'
|
||||||
|
|
||||||
// ReactiveX
|
// ReactiveX
|
||||||
compile 'io.reactivex:rxandroid:1.2.1'
|
compile 'io.reactivex:rxandroid:1.2.1'
|
||||||
|
5
app/proguard-rules.pro
vendored
5
app/proguard-rules.pro
vendored
@ -1,5 +1,10 @@
|
|||||||
-dontobfuscate
|
-dontobfuscate
|
||||||
|
|
||||||
|
-keep class eu.kanade.tachiyomi.**
|
||||||
|
|
||||||
|
-keep class com.hippo.image.** { *; }
|
||||||
|
-keep interface com.hippo.image.** { *; }
|
||||||
|
|
||||||
# OkHttp
|
# OkHttp
|
||||||
-keepattributes Signature
|
-keepattributes Signature
|
||||||
-keepattributes *Annotation*
|
-keepattributes *Annotation*
|
||||||
|
@ -7,7 +7,7 @@ import com.google.gson.reflect.TypeToken
|
|||||||
import com.jakewharton.disklrucache.DiskLruCache
|
import com.jakewharton.disklrucache.DiskLruCache
|
||||||
import eu.kanade.tachiyomi.data.source.model.Page
|
import eu.kanade.tachiyomi.data.source.model.Page
|
||||||
import eu.kanade.tachiyomi.util.DiskUtils
|
import eu.kanade.tachiyomi.util.DiskUtils
|
||||||
import eu.kanade.tachiyomi.util.saveImageTo
|
import eu.kanade.tachiyomi.util.saveTo
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import okio.Okio
|
import okio.Okio
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
@ -185,7 +185,7 @@ class ChapterCache(private val context: Context) {
|
|||||||
* @throws IOException image error.
|
* @throws IOException image error.
|
||||||
*/
|
*/
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun putImageToCache(imageUrl: String, response: Response, reencode: Boolean) {
|
fun putImageToCache(imageUrl: String, response: Response) {
|
||||||
// Initialize editor (edits the values for an entry).
|
// Initialize editor (edits the values for an entry).
|
||||||
var editor: DiskLruCache.Editor? = null
|
var editor: DiskLruCache.Editor? = null
|
||||||
|
|
||||||
@ -195,7 +195,7 @@ class ChapterCache(private val context: Context) {
|
|||||||
editor = diskCache.edit(key) ?: throw IOException("Unable to edit key")
|
editor = diskCache.edit(key) ?: throw IOException("Unable to edit key")
|
||||||
|
|
||||||
// Get OutputStream and write image with Okio.
|
// Get OutputStream and write image with Okio.
|
||||||
response.body().source().saveImageTo(editor.newOutputStream(0), reencode)
|
response.body().source().saveTo(editor.newOutputStream(0))
|
||||||
|
|
||||||
diskCache.flush()
|
diskCache.flush()
|
||||||
editor.commit()
|
editor.commit()
|
||||||
|
@ -264,7 +264,7 @@ class DownloadManager(
|
|||||||
val file = File(directory, filename)
|
val file = File(directory, filename)
|
||||||
try {
|
try {
|
||||||
file.parentFile.mkdirs()
|
file.parentFile.mkdirs()
|
||||||
it.body().source().saveImageTo(file.outputStream(), preferences.reencodeImage())
|
it.body().source().saveTo(file.outputStream())
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
it.close()
|
it.close()
|
||||||
file.delete()
|
file.delete()
|
||||||
|
@ -44,8 +44,6 @@ class PreferenceKeys(context: Context) {
|
|||||||
|
|
||||||
val readWithVolumeKeys = context.getString(R.string.pref_read_with_volume_keys_key)
|
val readWithVolumeKeys = context.getString(R.string.pref_read_with_volume_keys_key)
|
||||||
|
|
||||||
val reencodeImage = context.getString(R.string.pref_reencode_key)
|
|
||||||
|
|
||||||
val portraitColumns = context.getString(R.string.pref_library_columns_portrait_key)
|
val portraitColumns = context.getString(R.string.pref_library_columns_portrait_key)
|
||||||
|
|
||||||
val landscapeColumns = context.getString(R.string.pref_library_columns_landscape_key)
|
val landscapeColumns = context.getString(R.string.pref_library_columns_landscape_key)
|
||||||
|
@ -70,8 +70,6 @@ class PreferencesHelper(context: Context) {
|
|||||||
|
|
||||||
fun readWithVolumeKeys() = rxPrefs.getBoolean(keys.readWithVolumeKeys, false)
|
fun readWithVolumeKeys() = rxPrefs.getBoolean(keys.readWithVolumeKeys, false)
|
||||||
|
|
||||||
fun reencodeImage() = prefs.getBoolean(keys.reencodeImage, false)
|
|
||||||
|
|
||||||
fun portraitColumns() = rxPrefs.getInteger(keys.portraitColumns, 0)
|
fun portraitColumns() = rxPrefs.getInteger(keys.portraitColumns, 0)
|
||||||
|
|
||||||
fun landscapeColumns() = rxPrefs.getInteger(keys.landscapeColumns, 0)
|
fun landscapeColumns() = rxPrefs.getInteger(keys.landscapeColumns, 0)
|
||||||
|
@ -431,7 +431,7 @@ abstract class OnlineSource() : Source {
|
|||||||
private fun cacheImage(page: Page): Observable<Page> {
|
private fun cacheImage(page: Page): Observable<Page> {
|
||||||
page.status = Page.DOWNLOAD_IMAGE
|
page.status = Page.DOWNLOAD_IMAGE
|
||||||
return imageResponse(page)
|
return imageResponse(page)
|
||||||
.doOnNext { chapterCache.putImageToCache(page.imageUrl!!, it, preferences.reencodeImage()) }
|
.doOnNext { chapterCache.putImageToCache(page.imageUrl!!, it) }
|
||||||
.map { page }
|
.map { page }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
|
|||||||
|
|
||||||
setMenuVisibility(menuVisible)
|
setMenuVisibility(menuVisible)
|
||||||
|
|
||||||
maxBitmapSize = Math.min(2048, GLUtil.getMaxTextureSize())
|
maxBitmapSize = GLUtil.getMaxTextureSize()
|
||||||
|
|
||||||
left_chapter.setOnClickListener {
|
left_chapter.setOnClickListener {
|
||||||
if (viewer != null) {
|
if (viewer != null) {
|
||||||
|
@ -332,9 +332,9 @@ class ReaderPresenter : BasePresenter<ReaderActivity>() {
|
|||||||
fun retryPage(page: Page?) {
|
fun retryPage(page: Page?) {
|
||||||
if (page != null && source is OnlineSource) {
|
if (page != null && source is OnlineSource) {
|
||||||
page.status = Page.QUEUE
|
page.status = Page.QUEUE
|
||||||
if (page.imagePath != null) {
|
val path = page.imagePath
|
||||||
val file = File(page.imagePath)
|
if (!path.isNullOrEmpty() && !page.chapter.isDownloaded) {
|
||||||
chapterCache.removeFileFromCache(file.name)
|
chapterCache.removeFileFromCache(File(path).name)
|
||||||
}
|
}
|
||||||
loader.retryPage(page)
|
loader.retryPage(page)
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,11 @@ abstract class BaseReader : BaseFragment() {
|
|||||||
* Skia decoder.
|
* Skia decoder.
|
||||||
*/
|
*/
|
||||||
const val SKIA_DECODER = 1
|
const val SKIA_DECODER = 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image decoder.
|
||||||
|
*/
|
||||||
|
const val IMAGE_DECODER = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -201,15 +206,16 @@ abstract class BaseReader : BaseFragment() {
|
|||||||
fun setDecoderClass(value: Int) {
|
fun setDecoderClass(value: Int) {
|
||||||
when (value) {
|
when (value) {
|
||||||
RAPID_DECODER -> {
|
RAPID_DECODER -> {
|
||||||
// Using Skia because Rapid isn't stable. Rapid is still used for region decoding.
|
bitmapDecoderClass = RapidImageDecoder::class.java
|
||||||
// https://github.com/inorichi/tachiyomi/issues/97
|
|
||||||
//bitmapDecoderClass = RapidImageDecoder.class;
|
|
||||||
regionDecoderClass = RapidImageRegionDecoder::class.java
|
regionDecoderClass = RapidImageRegionDecoder::class.java
|
||||||
bitmapDecoderClass = SkiaImageDecoder::class.java
|
|
||||||
}
|
}
|
||||||
SKIA_DECODER -> {
|
SKIA_DECODER -> {
|
||||||
regionDecoderClass = SkiaImageRegionDecoder::class.java
|
|
||||||
bitmapDecoderClass = SkiaImageDecoder::class.java
|
bitmapDecoderClass = SkiaImageDecoder::class.java
|
||||||
|
regionDecoderClass = SkiaImageRegionDecoder::class.java
|
||||||
|
}
|
||||||
|
IMAGE_DECODER -> {
|
||||||
|
bitmapDecoderClass = IImageDecoder::class.java
|
||||||
|
regionDecoderClass = IImageRegionDecoder::class.java
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,72 +1,40 @@
|
|||||||
package eu.kanade.tachiyomi.ui.reader.viewer.base
|
package eu.kanade.tachiyomi.ui.reader.viewer.base
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.support.v4.content.ContextCompat
|
import android.support.v4.content.ContextCompat
|
||||||
import android.view.Gravity
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
|
||||||
import android.widget.Button
|
|
||||||
import android.widget.LinearLayout
|
|
||||||
import android.widget.TextView
|
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.source.model.Page
|
import eu.kanade.tachiyomi.data.source.model.Page
|
||||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||||
|
import kotlinx.android.synthetic.main.page_decode_error.view.*
|
||||||
|
|
||||||
class PageDecodeErrorLayout(context: Context) : LinearLayout(context) {
|
class PageDecodeErrorLayout(
|
||||||
|
val view: View,
|
||||||
/**
|
val page: Page,
|
||||||
* Text color for black theme.
|
val theme: Int,
|
||||||
*/
|
val retryListener: () -> Unit
|
||||||
private val whiteColor = ContextCompat.getColor(context, R.color.textColorSecondaryDark)
|
) {
|
||||||
|
|
||||||
/**
|
|
||||||
* Text color for white theme.
|
|
||||||
*/
|
|
||||||
private val blackColor = ContextCompat.getColor(context, R.color.textColorSecondaryLight)
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
orientation = LinearLayout.VERTICAL
|
val textColor = if (theme == ReaderActivity.BLACK_THEME)
|
||||||
setGravity(Gravity.CENTER)
|
ContextCompat.getColor(view.context, R.color.textColorSecondaryDark)
|
||||||
|
else
|
||||||
|
ContextCompat.getColor(view.context, R.color.textColorSecondaryLight)
|
||||||
|
|
||||||
|
view.decode_error_text.setTextColor(textColor)
|
||||||
|
|
||||||
|
view.decode_retry.setOnClickListener {
|
||||||
|
retryListener()
|
||||||
|
}
|
||||||
|
|
||||||
|
view.decode_open_browser.setOnClickListener {
|
||||||
|
val intent = android.content.Intent(android.content.Intent.ACTION_VIEW, Uri.parse(page.imageUrl))
|
||||||
|
view.context.startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page.imageUrl == null) {
|
||||||
|
view.decode_open_browser.visibility = View.GONE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(context: Context, page: Page, theme: Int, retryListener: () -> Unit) : this(context) {
|
|
||||||
|
|
||||||
// Error message.
|
|
||||||
TextView(context).apply {
|
|
||||||
gravity = Gravity.CENTER
|
|
||||||
setText(R.string.decode_image_error)
|
|
||||||
setTextColor(if (theme == ReaderActivity.BLACK_THEME) whiteColor else blackColor)
|
|
||||||
addView(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retry button.
|
|
||||||
Button(context).apply {
|
|
||||||
layoutParams = ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)
|
|
||||||
setText(R.string.action_retry)
|
|
||||||
setOnClickListener {
|
|
||||||
removeAllViews()
|
|
||||||
retryListener()
|
|
||||||
}
|
|
||||||
addView(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open in browser button.
|
|
||||||
Button(context).apply {
|
|
||||||
layoutParams = ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)
|
|
||||||
setText(R.string.action_open_in_browser)
|
|
||||||
setOnClickListener { v ->
|
|
||||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(page.imageUrl))
|
|
||||||
context.startActivity(intent)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (page.imageUrl == null) {
|
|
||||||
visibility = View.GONE
|
|
||||||
}
|
|
||||||
addView(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -12,11 +12,9 @@ import eu.kanade.tachiyomi.R
|
|||||||
import eu.kanade.tachiyomi.data.source.model.Page
|
import eu.kanade.tachiyomi.data.source.model.Page
|
||||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||||
import eu.kanade.tachiyomi.ui.reader.viewer.base.PageDecodeErrorLayout
|
import eu.kanade.tachiyomi.ui.reader.viewer.base.PageDecodeErrorLayout
|
||||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerReader.Companion.ALIGN_CENTER
|
|
||||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerReader.Companion.ALIGN_LEFT
|
|
||||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerReader.Companion.ALIGN_RIGHT
|
|
||||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.horizontal.RightToLeftReader
|
import eu.kanade.tachiyomi.ui.reader.viewer.pager.horizontal.RightToLeftReader
|
||||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.vertical.VerticalReader
|
import eu.kanade.tachiyomi.ui.reader.viewer.pager.vertical.VerticalReader
|
||||||
|
import eu.kanade.tachiyomi.util.inflate
|
||||||
import kotlinx.android.synthetic.main.chapter_image.view.*
|
import kotlinx.android.synthetic.main.chapter_image.view.*
|
||||||
import kotlinx.android.synthetic.main.item_pager_reader.view.*
|
import kotlinx.android.synthetic.main.item_pager_reader.view.*
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
@ -33,8 +31,12 @@ class PageView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
|||||||
/**
|
/**
|
||||||
* Page of a chapter.
|
* Page of a chapter.
|
||||||
*/
|
*/
|
||||||
var page: Page? = null
|
lateinit var page: Page
|
||||||
private set
|
|
||||||
|
/**
|
||||||
|
* Subscription for status changes of the page.
|
||||||
|
*/
|
||||||
|
private var statusSubscription: Subscription? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscription for progress changes of the page.
|
* Subscription for progress changes of the page.
|
||||||
@ -42,11 +44,11 @@ class PageView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
|||||||
private var progressSubscription: Subscription? = null
|
private var progressSubscription: Subscription? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscription for status changes of the page.
|
* Layout of decode error.
|
||||||
*/
|
*/
|
||||||
private var statusSubscription: Subscription? = null
|
private var decodeErrorLayout: View? = null
|
||||||
|
|
||||||
fun initialize(reader: PagerReader, page: Page?) {
|
fun initialize(reader: PagerReader, page: Page) {
|
||||||
val activity = reader.activity as ReaderActivity
|
val activity = reader.activity as ReaderActivity
|
||||||
|
|
||||||
when (activity.readerTheme) {
|
when (activity.readerTheme) {
|
||||||
@ -71,19 +73,11 @@ class PageView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
|||||||
setOnTouchListener { v, motionEvent -> reader.gestureDetector.onTouchEvent(motionEvent) }
|
setOnTouchListener { v, motionEvent -> reader.gestureDetector.onTouchEvent(motionEvent) }
|
||||||
setOnImageEventListener(object : SubsamplingScaleImageView.DefaultOnImageEventListener() {
|
setOnImageEventListener(object : SubsamplingScaleImageView.DefaultOnImageEventListener() {
|
||||||
override fun onReady() {
|
override fun onReady() {
|
||||||
when (reader.zoomType) {
|
onImageDecoded(reader)
|
||||||
ALIGN_LEFT -> setScaleAndCenter(scale, PointF(0f, 0f))
|
|
||||||
ALIGN_RIGHT -> setScaleAndCenter(scale, PointF(sWidth.toFloat(), 0f))
|
|
||||||
ALIGN_CENTER -> {
|
|
||||||
val newCenter = center
|
|
||||||
newCenter.y = 0f
|
|
||||||
setScaleAndCenter(scale, newCenter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onImageLoadError(e: Exception) {
|
override fun onImageLoadError(e: Exception) {
|
||||||
onImageDecodeError(activity)
|
onImageDecodeError(reader)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -95,21 +89,15 @@ class PageView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (page != null) {
|
this.page = page
|
||||||
this.page = page
|
observeStatus()
|
||||||
observeStatus()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cleanup() {
|
override fun onDetachedFromWindow() {
|
||||||
unsubscribeProgress()
|
unsubscribeProgress()
|
||||||
unsubscribeStatus()
|
unsubscribeStatus()
|
||||||
image_view.setOnTouchListener(null)
|
image_view.setOnTouchListener(null)
|
||||||
image_view.setOnImageEventListener(null)
|
image_view.setOnImageEventListener(null)
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDetachedFromWindow() {
|
|
||||||
cleanup()
|
|
||||||
super.onDetachedFromWindow()
|
super.onDetachedFromWindow()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +108,6 @@ class PageView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
|||||||
*/
|
*/
|
||||||
private fun observeStatus() {
|
private fun observeStatus() {
|
||||||
statusSubscription?.unsubscribe()
|
statusSubscription?.unsubscribe()
|
||||||
val page = page ?: return
|
|
||||||
|
|
||||||
val statusSubject = SerializedSubject(PublishSubject.create<Int>())
|
val statusSubject = SerializedSubject(PublishSubject.create<Int>())
|
||||||
page.setStatusSubject(statusSubject)
|
page.setStatusSubject(statusSubject)
|
||||||
@ -135,7 +122,6 @@ class PageView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
|||||||
*/
|
*/
|
||||||
private fun observeProgress() {
|
private fun observeProgress() {
|
||||||
progressSubscription?.unsubscribe()
|
progressSubscription?.unsubscribe()
|
||||||
val page = page ?: return
|
|
||||||
|
|
||||||
progressSubscription = Observable.interval(100, TimeUnit.MILLISECONDS)
|
progressSubscription = Observable.interval(100, TimeUnit.MILLISECONDS)
|
||||||
.map { page.progress }
|
.map { page.progress }
|
||||||
@ -154,18 +140,18 @@ class PageView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
|||||||
*/
|
*/
|
||||||
private fun processStatus(status: Int) {
|
private fun processStatus(status: Int) {
|
||||||
when (status) {
|
when (status) {
|
||||||
Page.QUEUE -> hideError()
|
Page.QUEUE -> setQueued()
|
||||||
Page.LOAD_PAGE -> onLoading()
|
Page.LOAD_PAGE -> setLoading()
|
||||||
Page.DOWNLOAD_IMAGE -> {
|
Page.DOWNLOAD_IMAGE -> {
|
||||||
observeProgress()
|
observeProgress()
|
||||||
onDownloading()
|
setDownloading()
|
||||||
}
|
}
|
||||||
Page.READY -> {
|
Page.READY -> {
|
||||||
onReady()
|
setImage()
|
||||||
unsubscribeProgress()
|
unsubscribeProgress()
|
||||||
}
|
}
|
||||||
Page.ERROR -> {
|
Page.ERROR -> {
|
||||||
onError()
|
setError()
|
||||||
unsubscribeProgress()
|
unsubscribeProgress()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,7 +161,7 @@ class PageView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
|||||||
* Unsubscribes from the status subscription.
|
* Unsubscribes from the status subscription.
|
||||||
*/
|
*/
|
||||||
private fun unsubscribeStatus() {
|
private fun unsubscribeStatus() {
|
||||||
page?.setStatusSubject(null)
|
page.setStatusSubject(null)
|
||||||
statusSubscription?.unsubscribe()
|
statusSubscription?.unsubscribe()
|
||||||
statusSubscription = null
|
statusSubscription = null
|
||||||
}
|
}
|
||||||
@ -188,10 +174,23 @@ class PageView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
|||||||
progressSubscription = null
|
progressSubscription = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the page is queued.
|
||||||
|
*/
|
||||||
|
private fun setQueued() {
|
||||||
|
progress_container.visibility = View.VISIBLE
|
||||||
|
progress_text.visibility = View.INVISIBLE
|
||||||
|
retry_button.visibility = View.GONE
|
||||||
|
decodeErrorLayout?.let {
|
||||||
|
removeView(it)
|
||||||
|
decodeErrorLayout = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the page is loading.
|
* Called when the page is loading.
|
||||||
*/
|
*/
|
||||||
private fun onLoading() {
|
private fun setLoading() {
|
||||||
progress_container.visibility = View.VISIBLE
|
progress_container.visibility = View.VISIBLE
|
||||||
progress_text.visibility = View.VISIBLE
|
progress_text.visibility = View.VISIBLE
|
||||||
progress_text.setText(R.string.downloading)
|
progress_text.setText(R.string.downloading)
|
||||||
@ -200,7 +199,7 @@ class PageView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
|||||||
/**
|
/**
|
||||||
* Called when the page is downloading.
|
* Called when the page is downloading.
|
||||||
*/
|
*/
|
||||||
private fun onDownloading() {
|
private fun setDownloading() {
|
||||||
progress_container.visibility = View.VISIBLE
|
progress_container.visibility = View.VISIBLE
|
||||||
progress_text.visibility = View.VISIBLE
|
progress_text.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
@ -208,42 +207,57 @@ class PageView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
|||||||
/**
|
/**
|
||||||
* Called when the page is ready.
|
* Called when the page is ready.
|
||||||
*/
|
*/
|
||||||
private fun onReady() {
|
private fun setImage() {
|
||||||
page?.imagePath?.let { path ->
|
val path = page.imagePath
|
||||||
if (File(path).exists()) {
|
if (path != null && File(path).exists()) {
|
||||||
image_view.setImage(ImageSource.uri(path))
|
progress_text.visibility = View.INVISIBLE
|
||||||
progress_container.visibility = View.GONE
|
image_view.setImage(ImageSource.uri(path))
|
||||||
} else {
|
} else {
|
||||||
page?.status = Page.ERROR
|
page.status = Page.ERROR
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the page has an error.
|
* Called when the page has an error.
|
||||||
*/
|
*/
|
||||||
private fun onError() {
|
private fun setError() {
|
||||||
progress_container.visibility = View.GONE
|
progress_container.visibility = View.GONE
|
||||||
retry_button.visibility = View.VISIBLE
|
retry_button.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hides the error layout.
|
* Called when the image is decoded and going to be displayed.
|
||||||
*/
|
*/
|
||||||
private fun hideError() {
|
private fun onImageDecoded(reader: PagerReader) {
|
||||||
retry_button.visibility = View.GONE
|
progress_container.visibility = View.GONE
|
||||||
|
|
||||||
|
with(image_view) {
|
||||||
|
when (reader.zoomType) {
|
||||||
|
PagerReader.ALIGN_LEFT -> setScaleAndCenter(scale, PointF(0f, 0f))
|
||||||
|
PagerReader.ALIGN_RIGHT -> setScaleAndCenter(scale, PointF(sWidth.toFloat(), 0f))
|
||||||
|
PagerReader.ALIGN_CENTER -> setScaleAndCenter(scale, center.apply { y = 0f })
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when an image fails to decode.
|
* Called when an image fails to decode.
|
||||||
*/
|
*/
|
||||||
private fun onImageDecodeError(activity: ReaderActivity) {
|
private fun onImageDecodeError(reader: PagerReader) {
|
||||||
page?.let { page ->
|
progress_container.visibility = View.GONE
|
||||||
val errorLayout = PageDecodeErrorLayout(context, page, activity.readerTheme,
|
|
||||||
{ activity.presenter.retryPage(page) })
|
|
||||||
|
|
||||||
addView(errorLayout)
|
if (decodeErrorLayout != null || !reader.isAdded) return
|
||||||
}
|
|
||||||
|
val activity = reader.activity as ReaderActivity
|
||||||
|
|
||||||
|
val layout = inflate(R.layout.page_decode_error)
|
||||||
|
PageDecodeErrorLayout(layout, page, activity.readerTheme, {
|
||||||
|
if (reader.isAdded) {
|
||||||
|
activity.presenter.retryPage(page)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
decodeErrorLayout = layout
|
||||||
|
addView(layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -15,7 +15,7 @@ class PagerReaderAdapter(private val reader: PagerReader) : ViewPagerAdapter() {
|
|||||||
/**
|
/**
|
||||||
* Pages stored in the adapter.
|
* Pages stored in the adapter.
|
||||||
*/
|
*/
|
||||||
var pages: List<Page>? = null
|
var pages: List<Page> = emptyList()
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
@ -23,17 +23,15 @@ class PagerReaderAdapter(private val reader: PagerReader) : ViewPagerAdapter() {
|
|||||||
|
|
||||||
override fun createView(container: ViewGroup, position: Int): View {
|
override fun createView(container: ViewGroup, position: Int): View {
|
||||||
val view = container.inflate(R.layout.item_pager_reader) as PageView
|
val view = container.inflate(R.layout.item_pager_reader) as PageView
|
||||||
view.initialize(reader, pages?.getOrNull(position))
|
view.initialize(reader, pages[position])
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of pages.
|
* Returns the number of pages.
|
||||||
*
|
|
||||||
* @return the number of pages or 0 if the list is null.
|
|
||||||
*/
|
*/
|
||||||
override fun getCount(): Int {
|
override fun getCount(): Int {
|
||||||
return pages?.size ?: 0
|
return pages.size
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ class WebtoonAdapter(val fragment: WebtoonReader) : RecyclerView.Adapter<Webtoon
|
|||||||
* @param holder the holder to recycle.
|
* @param holder the holder to recycle.
|
||||||
*/
|
*/
|
||||||
override fun onViewRecycled(holder: WebtoonHolder) {
|
override fun onViewRecycled(holder: WebtoonHolder) {
|
||||||
holder.unsubscribeStatus()
|
holder.onRecycle()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,16 +6,20 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import com.davemorrissey.labs.subscaleview.ImageSource
|
import com.davemorrissey.labs.subscaleview.ImageSource
|
||||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.source.model.Page
|
import eu.kanade.tachiyomi.data.source.model.Page
|
||||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||||
import eu.kanade.tachiyomi.ui.reader.viewer.base.PageDecodeErrorLayout
|
import eu.kanade.tachiyomi.ui.reader.viewer.base.PageDecodeErrorLayout
|
||||||
|
import eu.kanade.tachiyomi.util.inflate
|
||||||
import kotlinx.android.synthetic.main.chapter_image.view.*
|
import kotlinx.android.synthetic.main.chapter_image.view.*
|
||||||
import kotlinx.android.synthetic.main.item_webtoon_reader.view.*
|
import kotlinx.android.synthetic.main.item_webtoon_reader.view.*
|
||||||
|
import rx.Observable
|
||||||
import rx.Subscription
|
import rx.Subscription
|
||||||
import rx.android.schedulers.AndroidSchedulers
|
import rx.android.schedulers.AndroidSchedulers
|
||||||
import rx.subjects.PublishSubject
|
import rx.subjects.PublishSubject
|
||||||
import rx.subjects.SerializedSubject
|
import rx.subjects.SerializedSubject
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holder for webtoon reader for a single page of a chapter.
|
* Holder for webtoon reader for a single page of a chapter.
|
||||||
@ -38,10 +42,15 @@ class WebtoonHolder(private val view: View, private val adapter: WebtoonAdapter)
|
|||||||
*/
|
*/
|
||||||
private var statusSubscription: Subscription? = null
|
private var statusSubscription: Subscription? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscription for progress changes of the page.
|
||||||
|
*/
|
||||||
|
private var progressSubscription: Subscription? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Layout of decode error.
|
* Layout of decode error.
|
||||||
*/
|
*/
|
||||||
private var decodeErrorLayout: PageDecodeErrorLayout? = null
|
private var decodeErrorLayout: View? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
with(view.image_view) {
|
with(view.image_view) {
|
||||||
@ -56,9 +65,8 @@ class WebtoonHolder(private val view: View, private val adapter: WebtoonAdapter)
|
|||||||
setVerticalScrollingParent(true)
|
setVerticalScrollingParent(true)
|
||||||
setOnTouchListener(adapter.touchListener)
|
setOnTouchListener(adapter.touchListener)
|
||||||
setOnImageEventListener(object : SubsamplingScaleImageView.DefaultOnImageEventListener() {
|
setOnImageEventListener(object : SubsamplingScaleImageView.DefaultOnImageEventListener() {
|
||||||
override fun onImageLoaded() {
|
override fun onReady() {
|
||||||
// When the image is loaded, reset the minimum height to avoid gaps
|
onImageDecoded()
|
||||||
view.frame_container.minimumHeight = 30
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onImageLoadError(e: Exception) {
|
override fun onImageLoadError(e: Exception) {
|
||||||
@ -67,16 +75,9 @@ class WebtoonHolder(private val view: View, private val adapter: WebtoonAdapter)
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avoid to create a lot of view holders taking twice the screen height,
|
view.progress_container.minimumHeight = view.resources.displayMetrics.heightPixels * 2
|
||||||
// saving memory and a possible OOM. When the first image is loaded in this holder,
|
|
||||||
// the minimum size will be removed.
|
|
||||||
// Doing this we get sequential holder instantiation.
|
|
||||||
view.frame_container.minimumHeight = view.resources.displayMetrics.heightPixels * 2
|
|
||||||
|
|
||||||
// Leave some space between progress bars
|
view.setOnTouchListener(adapter.touchListener)
|
||||||
view.progress.minimumHeight = 300
|
|
||||||
|
|
||||||
view.frame_container.setOnTouchListener(adapter.touchListener)
|
|
||||||
view.retry_button.setOnTouchListener { v, event ->
|
view.retry_button.setOnTouchListener { v, event ->
|
||||||
if (event.action == MotionEvent.ACTION_UP) {
|
if (event.action == MotionEvent.ACTION_UP) {
|
||||||
readerActivity.presenter.retryPage(page)
|
readerActivity.presenter.retryPage(page)
|
||||||
@ -92,13 +93,23 @@ class WebtoonHolder(private val view: View, private val adapter: WebtoonAdapter)
|
|||||||
* @param page the page to bind.
|
* @param page the page to bind.
|
||||||
*/
|
*/
|
||||||
fun onSetValues(page: Page) {
|
fun onSetValues(page: Page) {
|
||||||
|
this.page = page
|
||||||
|
observeStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the view is recycled and added to the view pool.
|
||||||
|
*/
|
||||||
|
fun onRecycle() {
|
||||||
|
unsubscribeStatus()
|
||||||
|
unsubscribeProgress()
|
||||||
decodeErrorLayout?.let {
|
decodeErrorLayout?.let {
|
||||||
(view as ViewGroup).removeView(it)
|
(view as ViewGroup).removeView(it)
|
||||||
decodeErrorLayout = null
|
decodeErrorLayout = null
|
||||||
}
|
}
|
||||||
|
view.image_view.recycle()
|
||||||
this.page = page
|
view.image_view.visibility = View.GONE
|
||||||
observeStatus()
|
view.progress_container.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -107,17 +118,38 @@ class WebtoonHolder(private val view: View, private val adapter: WebtoonAdapter)
|
|||||||
* @see processStatus
|
* @see processStatus
|
||||||
*/
|
*/
|
||||||
private fun observeStatus() {
|
private fun observeStatus() {
|
||||||
page?.let { page ->
|
unsubscribeStatus()
|
||||||
val statusSubject = SerializedSubject(PublishSubject.create<Int>())
|
|
||||||
page.setStatusSubject(statusSubject)
|
|
||||||
|
|
||||||
statusSubscription?.unsubscribe()
|
val page = page ?: return
|
||||||
statusSubscription = statusSubject.startWith(page.status)
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe { processStatus(it) }
|
|
||||||
|
|
||||||
webtoonReader.subscriptions.add(statusSubscription)
|
val statusSubject = SerializedSubject(PublishSubject.create<Int>())
|
||||||
}
|
page.setStatusSubject(statusSubject)
|
||||||
|
|
||||||
|
statusSubscription = statusSubject.startWith(page.status)
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe { processStatus(it) }
|
||||||
|
|
||||||
|
addSubscription(statusSubscription)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observes the progress of the page and updates view.
|
||||||
|
*/
|
||||||
|
private fun observeProgress() {
|
||||||
|
unsubscribeProgress()
|
||||||
|
|
||||||
|
val page = page ?: return
|
||||||
|
|
||||||
|
progressSubscription = Observable.interval(100, TimeUnit.MILLISECONDS)
|
||||||
|
.map { page.progress }
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.onBackpressureLatest()
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe { progress ->
|
||||||
|
view.progress_text.text = view.context.getString(R.string.download_progress, progress)
|
||||||
|
}
|
||||||
|
|
||||||
|
addSubscription(progressSubscription)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -127,104 +159,131 @@ class WebtoonHolder(private val view: View, private val adapter: WebtoonAdapter)
|
|||||||
*/
|
*/
|
||||||
private fun processStatus(status: Int) {
|
private fun processStatus(status: Int) {
|
||||||
when (status) {
|
when (status) {
|
||||||
Page.QUEUE -> onQueue()
|
Page.QUEUE -> setQueued()
|
||||||
Page.LOAD_PAGE -> onLoading()
|
Page.LOAD_PAGE -> setLoading()
|
||||||
Page.DOWNLOAD_IMAGE -> onLoading()
|
Page.DOWNLOAD_IMAGE -> {
|
||||||
Page.READY -> onReady()
|
observeProgress()
|
||||||
Page.ERROR -> onError()
|
setDownloading()
|
||||||
|
}
|
||||||
|
Page.READY -> {
|
||||||
|
setImage()
|
||||||
|
unsubscribeProgress()
|
||||||
|
}
|
||||||
|
Page.ERROR -> {
|
||||||
|
setError()
|
||||||
|
unsubscribeProgress()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a subscription to a list of subscriptions that will automatically unsubscribe when the
|
||||||
|
* activity or the reader is destroyed.
|
||||||
|
*/
|
||||||
|
private fun addSubscription(subscription: Subscription?) {
|
||||||
|
webtoonReader.subscriptions.add(subscription)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a subscription from the list of subscriptions.
|
||||||
|
*/
|
||||||
|
private fun removeSubscription(subscription: Subscription?) {
|
||||||
|
subscription?.let { webtoonReader.subscriptions.remove(it) }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unsubscribes from the status subscription.
|
* Unsubscribes from the status subscription.
|
||||||
*/
|
*/
|
||||||
fun unsubscribeStatus() {
|
private fun unsubscribeStatus() {
|
||||||
statusSubscription?.unsubscribe()
|
page?.setStatusSubject(null)
|
||||||
|
removeSubscription(statusSubscription)
|
||||||
statusSubscription = null
|
statusSubscription = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsubscribes from the progress subscription.
|
||||||
|
*/
|
||||||
|
private fun unsubscribeProgress() {
|
||||||
|
removeSubscription(progressSubscription)
|
||||||
|
progressSubscription = null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the page is queued.
|
||||||
|
*/
|
||||||
|
private fun setQueued() = with(view) {
|
||||||
|
progress_container.visibility = View.VISIBLE
|
||||||
|
progress_text.visibility = View.INVISIBLE
|
||||||
|
retry_container.visibility = View.GONE
|
||||||
|
decodeErrorLayout?.let {
|
||||||
|
(view as ViewGroup).removeView(it)
|
||||||
|
decodeErrorLayout = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the page is loading.
|
* Called when the page is loading.
|
||||||
*/
|
*/
|
||||||
private fun onLoading() {
|
private fun setLoading() = with(view) {
|
||||||
setRetryButtonVisible(false)
|
progress_container.visibility = View.VISIBLE
|
||||||
setImageVisible(false)
|
progress_text.visibility = View.VISIBLE
|
||||||
setProgressVisible(true)
|
progress_text.setText(R.string.downloading)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the page is downloading
|
||||||
|
*/
|
||||||
|
private fun setDownloading() = with(view) {
|
||||||
|
progress_container.visibility = View.VISIBLE
|
||||||
|
progress_text.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the page is ready.
|
* Called when the page is ready.
|
||||||
*/
|
*/
|
||||||
private fun onReady() {
|
private fun setImage() = with(view) {
|
||||||
setRetryButtonVisible(false)
|
val path = page?.imagePath
|
||||||
setProgressVisible(false)
|
if (path != null && File(path).exists()) {
|
||||||
setImageVisible(true)
|
progress_text.visibility = View.INVISIBLE
|
||||||
|
image_view.visibility = View.VISIBLE
|
||||||
page?.imagePath?.let { path ->
|
image_view.setImage(ImageSource.uri(path))
|
||||||
if (File(path).exists()) {
|
} else {
|
||||||
view.image_view.setImage(ImageSource.uri(path))
|
page?.status = Page.ERROR
|
||||||
view.progress.visibility = View.GONE
|
|
||||||
} else {
|
|
||||||
page?.status = Page.ERROR
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the page has an error.
|
* Called when the page has an error.
|
||||||
*/
|
*/
|
||||||
private fun onError() {
|
private fun setError() = with(view) {
|
||||||
setImageVisible(false)
|
progress_container.visibility = View.GONE
|
||||||
setProgressVisible(false)
|
retry_container.visibility = View.VISIBLE
|
||||||
setRetryButtonVisible(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the page is queued.
|
* Called when the image is decoded and going to be displayed.
|
||||||
*/
|
*/
|
||||||
private fun onQueue() {
|
private fun onImageDecoded() {
|
||||||
setImageVisible(false)
|
view.progress_container.visibility = View.GONE
|
||||||
setRetryButtonVisible(false)
|
|
||||||
setProgressVisible(false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the image fails to decode.
|
* Called when the image fails to decode.
|
||||||
*/
|
*/
|
||||||
private fun onImageDecodeError() {
|
private fun onImageDecodeError() {
|
||||||
page?.let { page ->
|
view.progress_container.visibility = View.GONE
|
||||||
decodeErrorLayout = PageDecodeErrorLayout(view.context, page, readerActivity.readerTheme,
|
|
||||||
{ readerActivity.presenter.retryPage(page) })
|
|
||||||
|
|
||||||
(view as ViewGroup).addView(decodeErrorLayout)
|
val page = page ?: return
|
||||||
}
|
if (decodeErrorLayout != null || !webtoonReader.isAdded) return
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
val layout = (view as ViewGroup).inflate(R.layout.page_decode_error)
|
||||||
* Sets the visibility of the progress bar.
|
PageDecodeErrorLayout(layout, page, readerActivity.readerTheme, {
|
||||||
*
|
if (webtoonReader.isAdded) {
|
||||||
* @param visible whether to show it or not.
|
readerActivity.presenter.retryPage(page)
|
||||||
*/
|
}
|
||||||
private fun setProgressVisible(visible: Boolean) {
|
})
|
||||||
view.progress.visibility = if (visible) View.VISIBLE else View.GONE
|
decodeErrorLayout = layout
|
||||||
}
|
view.addView(layout)
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the visibility of the image view.
|
|
||||||
*
|
|
||||||
* @param visible whether to show it or not.
|
|
||||||
*/
|
|
||||||
private fun setImageVisible(visible: Boolean) {
|
|
||||||
view.image_view.visibility = if (visible) View.VISIBLE else View.GONE
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the visibility of the retry button.
|
|
||||||
*
|
|
||||||
* @param visible whether to show it or not.
|
|
||||||
*/
|
|
||||||
private fun setRetryButtonVisible(visible: Boolean) {
|
|
||||||
view.retry_button.visibility = if (visible) View.VISIBLE else View.GONE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,7 +29,7 @@ object ChapterRecognition {
|
|||||||
* Regex used to remove unwanted tags
|
* Regex used to remove unwanted tags
|
||||||
* Example Prison School 12 v.1 vol004 version1243 volume64 -R> Prison School 12
|
* Example Prison School 12 v.1 vol004 version1243 volume64 -R> Prison School 12
|
||||||
*/
|
*/
|
||||||
private val unwanted = Regex("""(?:(v|ver|vol|version|volume|season).?[0-9]+)""")
|
private val unwanted = Regex("""(?:(v|ver|vol|version|volume|season|s).?[0-9]+)""")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Regex used to remove unwanted whitespace
|
* Regex used to remove unwanted whitespace
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package eu.kanade.tachiyomi.util
|
package eu.kanade.tachiyomi.util
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
import okio.BufferedSource
|
import okio.BufferedSource
|
||||||
import okio.Okio
|
import okio.Okio
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -39,24 +37,3 @@ fun BufferedSource.saveTo(stream: OutputStream) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the given source to an output stream and closes both resources.
|
|
||||||
* The source is expected to be an image, and it may reencode the image.
|
|
||||||
*
|
|
||||||
* @param stream the stream where the source is copied.
|
|
||||||
* @param reencode whether to reencode the image or not.
|
|
||||||
*/
|
|
||||||
fun BufferedSource.saveImageTo(stream: OutputStream, reencode: Boolean = false) {
|
|
||||||
if (reencode) {
|
|
||||||
use {
|
|
||||||
val bitmap = BitmapFactory.decodeStream(it.inputStream())
|
|
||||||
stream.use {
|
|
||||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, it)
|
|
||||||
}
|
|
||||||
bitmap.recycle()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
saveTo(stream)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<eu.kanade.tachiyomi.ui.reader.viewer.pager.PageView
|
<eu.kanade.tachiyomi.ui.reader.viewer.pager.PageView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
@ -1,31 +1,51 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<FrameLayout
|
<LinearLayout
|
||||||
android:id="@+id/frame_container"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="match_parent">
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:id="@+id/progress_container"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/progress"
|
android:id="@+id/progress"
|
||||||
style="?android:attr/progressBarStyleLarge"
|
style="?android:attr/progressBarStyleLarge"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical|center_horizontal"/>
|
android:layout_gravity="center_horizontal"/>
|
||||||
|
|
||||||
<Button
|
<TextView
|
||||||
android:id="@+id/retry_button"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical|center_horizontal"
|
android:layout_marginTop="16dp"
|
||||||
android:text="@string/action_retry"
|
android:id="@+id/progress_text"
|
||||||
android:visibility="gone"/>
|
android:layout_gravity="center"
|
||||||
|
android:visibility="invisible"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
</FrameLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<include layout="@layout/chapter_image"/>
|
<include layout="@layout/chapter_image"/>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="192dp"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:id="@+id/retry_container"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/retry_button"
|
||||||
|
android:text="@string/action_retry"
|
||||||
|
android:layout_gravity="center"/>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
32
app/src/main/res/layout/page_decode_error.xml
Normal file
32
app/src/main/res/layout/page_decode_error.xml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="512dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/decode_error_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/decode_image_error"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:gravity="center"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/decode_retry"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:text="@string/action_retry"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/decode_open_browser"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:text="@string/action_open_in_browser"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -1,6 +1,16 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<changelog bulletedList="true">
|
<changelog bulletedList="true">
|
||||||
|
|
||||||
|
<changelogversion versionName="v0.3.2" changeDate="">
|
||||||
|
<changelogtext>Added a new image decoder. It should be faster than Rapid and more reliable than Skia.</changelogtext>
|
||||||
|
|
||||||
|
<changelogtext>Removed the advanced setting reencode images. Use the new image decoder instead.</changelogtext>
|
||||||
|
</changelogversion>
|
||||||
|
|
||||||
|
<changelogversion versionName="v0.3.1" changeDate="">
|
||||||
|
<changelogtext>Fixed a crash when opening latest updates. ([a href="https://github.com/inorichi/tachiyomi/issues/495"]#495[/a])</changelogtext>
|
||||||
|
</changelogversion>
|
||||||
|
|
||||||
<changelogversion versionName="v0.3.0" changeDate="">
|
<changelogversion versionName="v0.3.0" changeDate="">
|
||||||
<changelogtext>Added a new tab to show latest manga updates from the catalogues. ([a href="https://github.com/inorichi/tachiyomi/issues/61"]#61[/a])</changelogtext>
|
<changelogtext>Added a new tab to show latest manga updates from the catalogues. ([a href="https://github.com/inorichi/tachiyomi/issues/61"]#61[/a])</changelogtext>
|
||||||
|
|
||||||
|
@ -114,8 +114,6 @@
|
|||||||
<string name="vertical_viewer">Vertical</string>
|
<string name="vertical_viewer">Vertical</string>
|
||||||
<string name="webtoon_viewer">Webtoon</string>
|
<string name="webtoon_viewer">Webtoon</string>
|
||||||
<string name="pref_image_decoder">Decodificador de imagen</string>
|
<string name="pref_image_decoder">Decodificador de imagen</string>
|
||||||
<string name="rapid_decoder">Rapid</string>
|
|
||||||
<string name="skia_decoder">Skia</string>
|
|
||||||
<string name="pref_image_scale_type">Tipo de escalado</string>
|
<string name="pref_image_scale_type">Tipo de escalado</string>
|
||||||
<string name="scale_type_fit_screen">Ajustar a la pantalla</string>
|
<string name="scale_type_fit_screen">Ajustar a la pantalla</string>
|
||||||
<string name="scale_type_stretch">Estirado</string>
|
<string name="scale_type_stretch">Estirado</string>
|
||||||
@ -167,8 +165,6 @@
|
|||||||
<string name="clear_database_completed">Entradas borradas</string>
|
<string name="clear_database_completed">Entradas borradas</string>
|
||||||
<string name="pref_show_warning_message">Mostrar advertencias</string>
|
<string name="pref_show_warning_message">Mostrar advertencias</string>
|
||||||
<string name="pref_show_warning_message_summary">Mostrar mensajes de advertencia durante la sincronización de la librería</string>
|
<string name="pref_show_warning_message_summary">Mostrar mensajes de advertencia durante la sincronización de la librería</string>
|
||||||
<string name="pref_reencode"> Recodificar imágenes</string>
|
|
||||||
<string name="pref_reencode_summary">Habilitar recodificación si las imágenes no pueden ser decodificadas. Para mejores resultados, usar junto a Skia</string>
|
|
||||||
|
|
||||||
<!-- About section -->
|
<!-- About section -->
|
||||||
<string name="version">Versión</string>
|
<string name="version">Versión</string>
|
||||||
|
@ -119,8 +119,6 @@
|
|||||||
<string name="vertical_viewer">Vertical</string>
|
<string name="vertical_viewer">Vertical</string>
|
||||||
<string name="webtoon_viewer">Webtoon</string>
|
<string name="webtoon_viewer">Webtoon</string>
|
||||||
<string name="pref_image_decoder">Descodificador de imagem</string>
|
<string name="pref_image_decoder">Descodificador de imagem</string>
|
||||||
<string name="rapid_decoder">Rapid</string>
|
|
||||||
<string name="skia_decoder">Skia</string>
|
|
||||||
<string name="pref_image_scale_type">Tipo de escala</string>
|
<string name="pref_image_scale_type">Tipo de escala</string>
|
||||||
<string name="scale_type_fit_screen">Ajustar ao ecrã</string>
|
<string name="scale_type_fit_screen">Ajustar ao ecrã</string>
|
||||||
<string name="scale_type_stretch">Esticar</string>
|
<string name="scale_type_stretch">Esticar</string>
|
||||||
@ -169,8 +167,6 @@
|
|||||||
<string name="clear_database_completed">Eliminar entradas</string>
|
<string name="clear_database_completed">Eliminar entradas</string>
|
||||||
<string name="pref_show_warning_message">Mostrar avisos</string>
|
<string name="pref_show_warning_message">Mostrar avisos</string>
|
||||||
<string name="pref_show_warning_message_summary">Mostrar mensagens de avisos durante a sincronização</string>
|
<string name="pref_show_warning_message_summary">Mostrar mensagens de avisos durante a sincronização</string>
|
||||||
<string name="pref_reencode">Recodificar imagens</string>
|
|
||||||
<string name="pref_reencode_summary">Permitir recodificação se as imagens não puderam ser descodificadas. Para melhores resultados utilize Skia</string>
|
|
||||||
|
|
||||||
<!-- About section -->
|
<!-- About section -->
|
||||||
<string name="version">Versão</string>
|
<string name="version">Versão</string>
|
||||||
|
@ -67,13 +67,15 @@
|
|||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="image_decoders">
|
<string-array name="image_decoders">
|
||||||
<item>@string/rapid_decoder</item>
|
<item>Rapid</item>
|
||||||
<item>@string/skia_decoder</item>
|
<item>Skia</item>
|
||||||
|
<item>Image</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="image_decoders_values">
|
<string-array name="image_decoders_values">
|
||||||
<item>0</item>
|
<item>0</item>
|
||||||
<item>1</item>
|
<item>1</item>
|
||||||
|
<item>2</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="image_scale_type">
|
<string-array name="image_scale_type">
|
||||||
|
@ -39,7 +39,6 @@
|
|||||||
<string name="pref_image_decoder_key">pref_image_decoder_key</string>
|
<string name="pref_image_decoder_key">pref_image_decoder_key</string>
|
||||||
<string name="pref_read_with_volume_keys_key">reader_volume_keys</string>
|
<string name="pref_read_with_volume_keys_key">reader_volume_keys</string>
|
||||||
<string name="pref_read_with_tapping_key">reader_tap</string>
|
<string name="pref_read_with_tapping_key">reader_tap</string>
|
||||||
<string name="pref_reencode_key">reencode_image</string>
|
|
||||||
<string name="pref_filter_downloaded_key">pref_filter_downloaded_key</string>
|
<string name="pref_filter_downloaded_key">pref_filter_downloaded_key</string>
|
||||||
<string name="pref_filter_unread_key">pref_filter_unread_key</string>
|
<string name="pref_filter_unread_key">pref_filter_unread_key</string>
|
||||||
|
|
||||||
|
@ -123,8 +123,6 @@
|
|||||||
<string name="vertical_viewer">Vertical</string>
|
<string name="vertical_viewer">Vertical</string>
|
||||||
<string name="webtoon_viewer">Webtoon</string>
|
<string name="webtoon_viewer">Webtoon</string>
|
||||||
<string name="pref_image_decoder">Image decoder</string>
|
<string name="pref_image_decoder">Image decoder</string>
|
||||||
<string name="rapid_decoder">Rapid</string>
|
|
||||||
<string name="skia_decoder">Skia</string>
|
|
||||||
<string name="pref_image_scale_type">Scale type</string>
|
<string name="pref_image_scale_type">Scale type</string>
|
||||||
<string name="scale_type_fit_screen">Fit screen</string>
|
<string name="scale_type_fit_screen">Fit screen</string>
|
||||||
<string name="scale_type_stretch">Stretch</string>
|
<string name="scale_type_stretch">Stretch</string>
|
||||||
@ -178,8 +176,6 @@
|
|||||||
<string name="clear_database_completed">Entries deleted</string>
|
<string name="clear_database_completed">Entries deleted</string>
|
||||||
<string name="pref_refresh_library_metadata">Refresh library metadata</string>
|
<string name="pref_refresh_library_metadata">Refresh library metadata</string>
|
||||||
<string name="pref_refresh_library_metadata_summary">Updates covers, genres, description and manga status information</string>
|
<string name="pref_refresh_library_metadata_summary">Updates covers, genres, description and manga status information</string>
|
||||||
<string name="pref_reencode">Reencode images</string>
|
|
||||||
<string name="pref_reencode_summary">Enable reencoding if images can\'t be decoded. Expect best results with Skia</string>
|
|
||||||
|
|
||||||
<!-- About section -->
|
<!-- About section -->
|
||||||
<string name="version">Version</string>
|
<string name="version">Version</string>
|
||||||
|
@ -25,12 +25,6 @@
|
|||||||
android:summary="@string/pref_refresh_library_metadata_summary"
|
android:summary="@string/pref_refresh_library_metadata_summary"
|
||||||
android:title="@string/pref_refresh_library_metadata"/>
|
android:title="@string/pref_refresh_library_metadata"/>
|
||||||
|
|
||||||
<SwitchPreference
|
|
||||||
android:defaultValue="false"
|
|
||||||
android:key="@string/pref_reencode_key"
|
|
||||||
android:summary="@string/pref_reencode_summary"
|
|
||||||
android:title="@string/pref_reencode"/>
|
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
@ -429,4 +429,15 @@ class ChapterRecognitionTest {
|
|||||||
assertThat(chapter.chapter_number).isEqualTo(7f)
|
assertThat(chapter.chapter_number).isEqualTo(7f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for chapters in format sx - chapter xx
|
||||||
|
*/
|
||||||
|
@Test fun chapterContainingSeasonCase2() {
|
||||||
|
createManga("The Gamer")
|
||||||
|
|
||||||
|
createChapter("S3 - Chapter 20")
|
||||||
|
ChapterRecognition.parseChapterNumber(chapter, manga)
|
||||||
|
assertThat(chapter.chapter_number).isEqualTo(20f)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ buildscript {
|
|||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:2.2.1'
|
classpath 'com.android.tools.build:gradle:2.2.2'
|
||||||
classpath 'com.github.ben-manes:gradle-versions-plugin:0.13.0'
|
classpath 'com.github.ben-manes:gradle-versions-plugin:0.13.0'
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
|
Reference in New Issue
Block a user