Added code to prevent OutOfMemory error. Made notification optional. Can now save image on long press. Bug fixes

This commit is contained in:
Bram van de Kerkhof 2016-10-06 21:51:51 +02:00
parent 1210691fdd
commit 4975787afa
14 changed files with 180 additions and 48 deletions

View File

@ -47,6 +47,8 @@ class DownloadManager(
private val threadsSubject = BehaviorSubject.create<Int>()
private var threadsSubscription: Subscription? = null
private var notificationSubscription: Subscription? = null
val queue = DownloadQueue()
val imageFilenameRegex = "[^\\sa-zA-Z0-9.-]".toRegex()
@ -66,6 +68,12 @@ class DownloadManager(
downloadNotifier.multipleDownloadThreads = it > 1
}
notificationSubscription = preferences.showMangaDownloadNotification().asObservable()
.subscribe {
downloadNotifier.onClear()
downloadNotifier.showNotification = it
}
downloadsSubscription = downloadsQueueSubject.flatMap { Observable.from(it) }
.lift(DynamicConcurrentMergeOperator<Download, Download>({ downloadChapter(it) }, threadsSubject))
.onBackpressureBuffer()
@ -107,6 +115,10 @@ class DownloadManager(
threadsSubscription?.unsubscribe()
}
if (notificationSubscription != null) {
notificationSubscription?.unsubscribe()
}
}
// Create a download object for every chapter and add them to the downloads queue
@ -188,7 +200,7 @@ class DownloadManager(
DiskUtils.createDirectory(download.directory)
val pageListObservable: Observable<List<Page>> = if (download.pages == null)
// Pull page list from network and add them to download object
// Pull page list from network and add them to download object
download.source.fetchPageListFromNetwork(download.chapter)
.doOnNext { pages ->
download.pages = pages

View File

@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.data.download.model.DownloadQueue
import eu.kanade.tachiyomi.util.notificationManager
import eu.kanade.tachiyomi.util.toast
/**
* DownloadNotifier is used to show notifications when downloading one or multiple chapters.
@ -40,6 +41,11 @@ class DownloadNotifier(private val context: Context) {
*/
internal var multipleDownloadThreads = false
/**
* Value determining if notification should be shown
*/
internal var showNotification = true
/**
* Called when download progress changes.
* Note: Only accepted when multi download active.
@ -47,9 +53,8 @@ class DownloadNotifier(private val context: Context) {
* @param queue the queue containing downloads.
*/
internal fun onProgressChange(queue: DownloadQueue) {
if (multipleDownloadThreads) {
if (multipleDownloadThreads && showNotification)
doOnProgressChange(null, queue)
}
}
/**
@ -60,9 +65,8 @@ class DownloadNotifier(private val context: Context) {
* @param queue the queue containing downloads
*/
internal fun onProgressChange(download: Download, queue: DownloadQueue) {
if (!multipleDownloadThreads) {
if (!multipleDownloadThreads && showNotification)
doOnProgressChange(download, queue)
}
}
/**
@ -86,7 +90,7 @@ class DownloadNotifier(private val context: Context) {
}
// Create notification
with (notificationBuilder) {
with(notificationBuilder) {
// Check if icon needs refresh
if (!isDownloading) {
setSmallIcon(android.R.drawable.stat_sys_download)
@ -127,17 +131,18 @@ class DownloadNotifier(private val context: Context) {
* @param download download object containing download information
*/
private fun onComplete(download: Download?) {
// Create notification.
with(notificationBuilder) {
setContentTitle(download?.chapter?.name ?: context.getString(R.string.app_name))
setContentText(context.getString(R.string.update_check_notification_download_complete))
setSmallIcon(android.R.drawable.stat_sys_download_done)
setProgress(0, 0, false)
if (showNotification) {
// Create notification.
with(notificationBuilder) {
setContentTitle(download?.chapter?.name ?: context.getString(R.string.app_name))
setContentText(context.getString(R.string.update_check_notification_download_complete))
setSmallIcon(android.R.drawable.stat_sys_download_done)
setProgress(0, 0, false)
}
// Show notification.
context.notificationManager.notify(notificationId, notificationBuilder.build())
}
// Show notification.
context.notificationManager.notify(notificationId, notificationBuilder.build())
// Reset initial values
isDownloading = false
initialQueueSize = 0
@ -158,14 +163,17 @@ class DownloadNotifier(private val context: Context) {
*/
internal fun onError(error: String? = null, chapter: String? = null) {
// Create notification
with(notificationBuilder) {
setContentTitle(chapter ?: context.getString(R.string.download_notifier_title_error))
setContentText(error ?: context.getString(R.string.download_notifier_unkown_error))
setSmallIcon(android.R.drawable.stat_sys_warning)
setProgress(0, 0, false)
if (showNotification) {
with(notificationBuilder) {
setContentTitle(chapter ?: context.getString(R.string.download_notifier_title_error))
setContentText(error ?: context.getString(R.string.download_notifier_unkown_error))
setSmallIcon(android.R.drawable.stat_sys_warning)
setProgress(0, 0, false)
}
context.notificationManager.notify(Constants.NOTIFICATION_DOWNLOAD_CHAPTER_ERROR_ID, notificationBuilder.build())
} else {
context.toast(error ?: context.getString(R.string.download_notifier_unkown_error))
}
context.notificationManager.notify(Constants.NOTIFICATION_DOWNLOAD_CHAPTER_ERROR_ID, notificationBuilder.build())
// Reset download information
onClear()
isDownloading = false

View File

@ -1,11 +1,10 @@
package eu.kanade.tachiyomi.data.download
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.support.v4.app.NotificationCompat
import eu.kanade.tachiyomi.Constants
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.decodeSampledBitmap
import eu.kanade.tachiyomi.util.notificationManager
import java.io.File
@ -52,9 +51,9 @@ class ImageNotifier(private val context: Context) {
/**
* Called when image download is complete
* @param bitmap image file containing downloaded page image
* @param file image file containing downloaded page image
*/
fun onComplete(bitmap: Bitmap, file: File) {
fun onComplete(file: File) {
with(notificationBuilder) {
if (isDownloading) {
setProgress(0, 0, false)
@ -62,8 +61,8 @@ class ImageNotifier(private val context: Context) {
}
setContentTitle(context.getString(R.string.picture_saved))
setSmallIcon(R.drawable.ic_insert_photo_black_24dp)
setLargeIcon(bitmap)
setStyle(NotificationCompat.BigPictureStyle().bigPicture(bitmap))
setLargeIcon(file.decodeSampledBitmap(100, 100))
setStyle(NotificationCompat.BigPictureStyle().bigPicture(file.decodeSampledBitmap(1024, 1024)))
setAutoCancel(true)
// Clear old actions if they exist
@ -84,10 +83,6 @@ class ImageNotifier(private val context: Context) {
context.notificationManager.notify(notificationId, notificationBuilder.build())
}
fun onComplete(file: File) {
onComplete(convertToBitmap(file), file)
}
/**
* Clears the notification message
*/
@ -112,13 +107,4 @@ class ImageNotifier(private val context: Context) {
isDownloading = false
}
/**
* Converts file to bitmap
*/
fun convertToBitmap(image: File): Bitmap {
val options = BitmapFactory.Options()
options.inPreferredConfig = Bitmap.Config.ARGB_8888
return BitmapFactory.decodeFile(image.absolutePath, options)
}
}
}

View File

@ -72,6 +72,10 @@ class PreferenceKeys(context: Context) {
val removeAfterMarkedAsRead = context.getString(R.string.pref_remove_after_marked_as_read_key)
val showMangaDownloadNotification = context.getString(R.string.pref_notifications_manga_download_key)
val showSavePageNotification = context.getString(R.string.pref_notifications_single_page_key)
val libraryUpdateInterval = context.getString(R.string.pref_library_update_interval_key)
val libraryUpdateRestriction = context.getString(R.string.pref_library_update_restriction_key)

View File

@ -122,6 +122,10 @@ class PreferencesHelper(context: Context) {
fun removeAfterMarkedAsRead() = prefs.getBoolean(keys.removeAfterMarkedAsRead, false)
fun showMangaDownloadNotification() = rxPrefs.getBoolean(keys.showMangaDownloadNotification, true)
fun showSavePageNotification() = prefs.getBoolean(keys.showSavePageNotification, false)
fun libraryUpdateInterval() = rxPrefs.getInteger(keys.libraryUpdateInterval, 0)
fun libraryUpdateRestriction() = prefs.getStringSet(keys.libraryUpdateRestriction, emptySet())

View File

@ -145,8 +145,6 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
when (item.itemId) {
R.id.action_settings -> ReaderSettingsDialog().show(supportFragmentManager, "settings")
R.id.action_custom_filter -> ReaderCustomFilterDialog().show(supportFragmentManager, "filter")
R.id.action_save_page -> presenter.savePage()
R.id.action_set_as_cover -> presenter.setCover()
else -> return super.onOptionsItemSelected(item)
}
return true
@ -230,6 +228,22 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
// Ignore
}
fun onLongPress() {
MaterialDialog.Builder(this).apply {
title = "Choose"
items(R.array.reader_image_options)
.itemsIds(R.array.reader_image_options_values)
itemsCallback { materialDialog, view, i, charSequence ->
when (i) {
0 -> presenter.setCover()
1 -> presenter.shareImage()
2 -> presenter.savePage()
}
}.show()
}
}
/**
* Called from the presenter at startup, allowing to prepare the selected reader.
*/

View File

@ -1,5 +1,7 @@
package eu.kanade.tachiyomi.ui.reader
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.os.Environment
import eu.kanade.tachiyomi.R
@ -576,6 +578,19 @@ class ReaderPresenter : BasePresenter<ReaderActivity>() {
return false
}
fun shareImage() {
chapter.pages?.get(chapter.last_page_read)?.let { page ->
val shareIntent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_STREAM, Uri.parse(page.imagePath))
flags = Intent.FLAG_ACTIVITY_NEW_TASK
type = "image/jpeg"
}
context.startActivity(Intent.createChooser(shareIntent, context.resources.getText(R.string.action_share))
.apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK })
}
}
/**
* Save page to local storage
* @throws IOException
@ -595,7 +610,10 @@ class ReaderPresenter : BasePresenter<ReaderActivity>() {
//Check if file doesn't already exist
if (destFile.exists()) {
imageNotifier.onComplete(destFile)
if (prefs.showSavePageNotification())
imageNotifier.onComplete(destFile)
else
context.toast(context.getString(R.string.page_downloaded, destFile.path))
} else {
if (inputFile.exists()) {
// Copy file
@ -606,7 +624,10 @@ class ReaderPresenter : BasePresenter<ReaderActivity>() {
{ imageNotifier.onComplete(it) },
{ error ->
Timber.e(error.message)
imageNotifier.onError(error.message)
if (prefs.showSavePageNotification())
imageNotifier.onError(error.message)
else
context.toast(error.message)
})
}
}

View File

@ -185,6 +185,11 @@ abstract class PagerReader : BaseReader() {
}
return true
}
override fun onLongPress(e: MotionEvent?) {
super.onLongPress(e)
readerActivity.onLongPress()
}
})
}

View File

@ -140,6 +140,11 @@ class WebtoonReader : BaseReader() {
}
return true
}
override fun onLongPress(e: MotionEvent?) {
super.onLongPress(e)
readerActivity.onLongPress()
}
})
}

View File

@ -0,0 +1,40 @@
package eu.kanade.tachiyomi.util
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import java.io.File
fun File.decodeSampledBitmap(reqWidth: Int, reqHeight: Int): Bitmap {
// First decode with inJustDecodeBounds=true to check dimensions
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeFile(this.absolutePath, options)
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(this.absolutePath, options)
}
fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
// Raw height and width of image
val height = options.outHeight
val width = options.outWidth
var inSampleSize = 1
if (height > reqHeight || width > reqWidth) {
val halfHeight = height / 2
val halfWidth = width / 2
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
inSampleSize *= 2
}
}
return inSampleSize
}

View File

@ -174,4 +174,16 @@
<item>3</item>
</string-array>
<string-array name="reader_image_options">
<item>@string/set_as_cover</item>
<item>@string/share_image</item>
<item>@string/save_image</item>
</string-array>
<string-array name="reader_image_options_values">
<item>0</item>
<item>1</item>
<item>2</item>
</string-array>
</resources>

View File

@ -48,7 +48,8 @@
<string name="pref_download_only_over_wifi_key">pref_download_only_over_wifi_key</string>
<string name="pref_remove_after_marked_as_read_key">pref_remove_after_marked_as_read_key</string>
<string name="pref_category_remove_after_read_key">pref_category_remove_after_read_key</string>
<string name="pref_notifications_single_page_key">notifications_single_page</string>
<string name="pref_notifications_manga_download_key">notifications_manga_download</string>
<string name="pref_last_used_category_key">last_used_category</string>
<string name="pref_source_languages">pref_source_languages</string>

View File

@ -281,6 +281,11 @@
<!-- Reader activity -->
<string name="custom_filter">Custom filter</string>
<string name="set_as_cover">Set as cover</string>
<string name="share_image">Share image</string>
<string name="save_image">Save image</string>
<string name="cover_updated">Cover updated</string>
<string name="page_downloaded">Page copied to %1$s</string>
<string name="downloading">Downloading…</string>
<string name="download_progress">Downloaded %1$d%%</string>
<string name="chapter_progress">Page: %1$d</string>

View File

@ -40,6 +40,21 @@
android:summary="%s"
android:title="@string/pref_remove_after_read" />
<PreferenceCategory
android:persistent="false"
android:title="@string/pref_notifications" />
<SwitchPreference
android:defaultValue="true"
android:key="@string/pref_notifications_manga_download_key"
android:title="@string/pref_notifications_manga_download" />
<SwitchPreference
android:defaultValue="false"
android:key="@string/pref_notifications_single_page_key"
android:title="@string/pref_notifications_single_page" />
</PreferenceScreen>
</PreferenceScreen>