Fixes + API 16 support

This commit is contained in:
Bram van de Kerkhof 2016-10-13 17:54:07 +02:00
parent 8ff8ab4f27
commit c2b113ac0a
28 changed files with 219 additions and 278 deletions

View File

@ -88,7 +88,7 @@
<receiver android:name=".data.library.LibraryUpdateService$CancelUpdateReceiver" /> <receiver android:name=".data.library.LibraryUpdateService$CancelUpdateReceiver" />
<receiver android:name=".data.download.ImageNotificationReceiver" /> <receiver android:name=".ui.reader.notification.ImageNotificationReceiver" />
<meta-data <meta-data
android:name="eu.kanade.tachiyomi.data.glide.AppGlideModule" android:name="eu.kanade.tachiyomi.data.glide.AppGlideModule"

View File

@ -41,11 +41,6 @@ class DownloadNotifier(private val context: Context) {
*/ */
internal var multipleDownloadThreads = false internal var multipleDownloadThreads = false
/**
* Value determining if notification should be shown
*/
internal var showNotification = true
/** /**
* Called when download progress changes. * Called when download progress changes.
* Note: Only accepted when multi download active. * Note: Only accepted when multi download active.
@ -53,7 +48,7 @@ class DownloadNotifier(private val context: Context) {
* @param queue the queue containing downloads. * @param queue the queue containing downloads.
*/ */
internal fun onProgressChange(queue: DownloadQueue) { internal fun onProgressChange(queue: DownloadQueue) {
if (multipleDownloadThreads && showNotification) if (multipleDownloadThreads)
doOnProgressChange(null, queue) doOnProgressChange(null, queue)
} }
@ -65,7 +60,7 @@ class DownloadNotifier(private val context: Context) {
* @param queue the queue containing downloads * @param queue the queue containing downloads
*/ */
internal fun onProgressChange(download: Download, queue: DownloadQueue) { internal fun onProgressChange(download: Download, queue: DownloadQueue) {
if (!multipleDownloadThreads && showNotification) if (!multipleDownloadThreads)
doOnProgressChange(download, queue) doOnProgressChange(download, queue)
} }
@ -131,18 +126,17 @@ class DownloadNotifier(private val context: Context) {
* @param download download object containing download information * @param download download object containing download information
*/ */
private fun onComplete(download: Download?) { private fun onComplete(download: Download?) {
if (showNotification) { // Create notification.
// Create notification. with(notificationBuilder) {
with(notificationBuilder) { setContentTitle(download?.chapter?.name ?: context.getString(R.string.app_name))
setContentTitle(download?.chapter?.name ?: context.getString(R.string.app_name)) setContentText(context.getString(R.string.update_check_notification_download_complete))
setContentText(context.getString(R.string.update_check_notification_download_complete)) setSmallIcon(android.R.drawable.stat_sys_download_done)
setSmallIcon(android.R.drawable.stat_sys_download_done) setProgress(0, 0, false)
setProgress(0, 0, false)
}
// Show notification.
context.notificationManager.notify(notificationId, notificationBuilder.build())
} }
// Show notification.
context.notificationManager.notify(notificationId, notificationBuilder.build())
// Reset initial values // Reset initial values
isDownloading = false isDownloading = false
initialQueueSize = 0 initialQueueSize = 0
@ -163,17 +157,13 @@ class DownloadNotifier(private val context: Context) {
*/ */
internal fun onError(error: String? = null, chapter: String? = null) { internal fun onError(error: String? = null, chapter: String? = null) {
// Create notification // Create notification
if (showNotification) { with(notificationBuilder) {
with(notificationBuilder) { setContentTitle(chapter ?: context.getString(R.string.download_notifier_title_error))
setContentTitle(chapter ?: context.getString(R.string.download_notifier_title_error)) setContentText(error ?: context.getString(R.string.download_notifier_unkown_error))
setContentText(error ?: context.getString(R.string.download_notifier_unkown_error)) setSmallIcon(android.R.drawable.stat_sys_warning)
setSmallIcon(android.R.drawable.stat_sys_warning) setProgress(0, 0, false)
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 // Reset download information
onClear() onClear()
isDownloading = false isDownloading = false

View File

@ -1,132 +0,0 @@
package eu.kanade.tachiyomi.data.download
import android.content.Context
import android.graphics.Bitmap
import android.support.v4.app.NotificationCompat
import com.bumptech.glide.Glide
import com.bumptech.glide.request.animation.GlideAnimation
import com.bumptech.glide.request.target.SimpleTarget
import eu.kanade.tachiyomi.Constants
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.notificationManager
import java.io.File
class ImageNotifier(private val context: Context) {
/**
* Notification builder.
*/
private val notificationBuilder = NotificationCompat.Builder(context)
/**
* Id of the notification.
*/
private val notificationId: Int
get() = Constants.NOTIFICATION_DOWNLOAD_IMAGE_ID
/**
* Status of download. Used for correct notification icon.
*/
private var isDownloading = false
/**
* Called when download progress changes.
* @param progress progress value in range [0,100]
*/
fun onProgressChange(progress: Int) {
with(notificationBuilder) {
if (!isDownloading) {
setContentTitle(context.getString(R.string.saving_picture))
setSmallIcon(android.R.drawable.stat_sys_download)
setLargeIcon(null)
setStyle(null)
// Clear old actions if they exist
if (!mActions.isEmpty())
mActions.clear()
isDownloading = true
}
setProgress(100, progress, false)
}
// Displays the progress bar on notification
context.notificationManager.notify(notificationId, notificationBuilder.build())
}
/**
* Called when image download is complete
* @param file image file containing downloaded page image
*/
fun onComplete(file: File) {
with(notificationBuilder) {
if (isDownloading) {
setProgress(0, 0, false)
isDownloading = false
}
setContentTitle(context.getString(R.string.picture_saved))
setSmallIcon(R.drawable.ic_insert_photo_black_24dp)
Glide.with(context).load(file).asBitmap().into(object : SimpleTarget<Bitmap>(96, 96) {
/**
* The method that will be called when the resource load has finished.
* @param resource the loaded resource.
*/
override fun onResourceReady(resource: Bitmap?, glideAnimation: GlideAnimation<in Bitmap>?) {
setLargeIcon(resource)
context.notificationManager.notify(notificationId, notificationBuilder.build())
}
})
Glide.with(context).load(file).asBitmap().into(object : SimpleTarget<Bitmap>(720, 1280) {
/**
* The method that will be called when the resource load has finished.
* @param resource the loaded resource.
*/
override fun onResourceReady(resource: Bitmap?, glideAnimation: GlideAnimation<in Bitmap>?) {
setStyle(NotificationCompat.BigPictureStyle().bigPicture(resource))
context.notificationManager.notify(notificationId, notificationBuilder.build())
}
})
setAutoCancel(true)
// Clear old actions if they exist
if (!mActions.isEmpty())
mActions.clear()
setContentIntent(ImageNotificationReceiver.showImageIntent(context, file.absolutePath))
// Share action
addAction(R.drawable.ic_share_white_24dp,
context.getString(R.string.action_share),
ImageNotificationReceiver.shareImageIntent(context, file.absolutePath, notificationId))
// Delete action
addAction(R.drawable.ic_delete_white_24dp,
context.getString(R.string.action_delete),
ImageNotificationReceiver.deleteImageIntent(context, file.absolutePath, notificationId))
}
// Displays the progress bar on notification
context.notificationManager.notify(notificationId, notificationBuilder.build())
}
/**
* Clears the notification message
*/
internal fun onClear() {
context.notificationManager.cancel(notificationId)
}
/**
* Called on error while downloading image
* @param error string containing error information
*/
internal fun onError(error: String?) {
// Create notification
with(notificationBuilder) {
setContentTitle(context.getString(R.string.download_notifier_title_error))
setContentText(error ?: context.getString(R.string.download_notifier_unkown_error))
setSmallIcon(android.R.drawable.ic_menu_report_image)
setProgress(0, 0, false)
}
context.notificationManager.notify(notificationId, notificationBuilder.build())
isDownloading = false
}
}

View File

@ -5,6 +5,7 @@ import android.content.Intent
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Color import android.graphics.Color
import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Build.VERSION_CODES.KITKAT import android.os.Build.VERSION_CODES.KITKAT
import android.os.Bundle import android.os.Bundle
@ -229,19 +230,17 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
} }
fun onLongPress(page: Page) { fun onLongPress(page: Page) {
MaterialDialog.Builder(this).apply { MaterialDialog.Builder(this)
title = "Choose" .title(getString(R.string.options))
items(R.array.reader_image_options) .items(R.array.reader_image_options)
.itemsIds(R.array.reader_image_options_values) .itemsIds(R.array.reader_image_options_values)
itemsCallback { materialDialog, view, i, charSequence -> .itemsCallback { materialDialog, view, i, charSequence ->
when (i) { when (i) {
0 -> presenter.setCover(page) 0 -> presenter.setCover(page)
1 -> presenter.shareImage(page) 1 -> shareImage(page)
2 -> presenter.savePage(page) 2 -> presenter.savePage(page)
} }
}.show() }.show()
}
} }
/** /**
@ -409,16 +408,16 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
private fun setRotation(rotation: Int) { private fun setRotation(rotation: Int) {
when (rotation) { when (rotation) {
// Rotation free // Rotation free
1 -> requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED 1 -> requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
// Lock in current rotation // Lock in current rotation
2 -> { 2 -> {
val currentOrientation = resources.configuration.orientation val currentOrientation = resources.configuration.orientation
setRotation(if (currentOrientation == Configuration.ORIENTATION_PORTRAIT) 3 else 4) setRotation(if (currentOrientation == Configuration.ORIENTATION_PORTRAIT) 3 else 4)
} }
// Lock in portrait // Lock in portrait
3 -> requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT 3 -> requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
// Lock in landscape // Lock in landscape
4 -> requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE 4 -> requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
} }
} }
@ -471,6 +470,21 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
} }
} }
/**
* Start a share intent that lets user share image
*
* @param page page object containing image information.
*/
fun shareImage(page: 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"
}
startActivity(Intent.createChooser(shareIntent, resources.getText(R.string.action_share)))
}
/** /**
* Sets the brightness of the screen. Range is [-75, 100]. * Sets the brightness of the screen. Range is [-75, 100].
* From -75 to -1 a semi-transparent black view is shown at the top with the minimum brightness. * From -75 to -1 a semi-transparent black view is shown at the top with the minimum brightness.

View File

@ -1,7 +1,5 @@
package eu.kanade.tachiyomi.ui.reader package eu.kanade.tachiyomi.ui.reader
import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Environment import android.os.Environment
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
@ -13,15 +11,14 @@ import eu.kanade.tachiyomi.data.database.models.History
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaSync import eu.kanade.tachiyomi.data.database.models.MangaSync
import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.ImageNotifier
import eu.kanade.tachiyomi.data.mangasync.MangaSyncManager import eu.kanade.tachiyomi.data.mangasync.MangaSyncManager
import eu.kanade.tachiyomi.data.mangasync.UpdateMangaSyncService import eu.kanade.tachiyomi.data.mangasync.UpdateMangaSyncService
import eu.kanade.tachiyomi.data.network.NetworkHelper
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.source.SourceManager import eu.kanade.tachiyomi.data.source.SourceManager
import eu.kanade.tachiyomi.data.source.model.Page import eu.kanade.tachiyomi.data.source.model.Page
import eu.kanade.tachiyomi.data.source.online.OnlineSource import eu.kanade.tachiyomi.data.source.online.OnlineSource
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.ui.reader.notification.ImageNotifier
import eu.kanade.tachiyomi.util.RetryWithDelay import eu.kanade.tachiyomi.util.RetryWithDelay
import eu.kanade.tachiyomi.util.SharedData import eu.kanade.tachiyomi.util.SharedData
import eu.kanade.tachiyomi.util.toast import eu.kanade.tachiyomi.util.toast
@ -33,19 +30,12 @@ import timber.log.Timber
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.io.InputStream
import java.util.* import java.util.*
/** /**
* Presenter of [ReaderActivity]. * Presenter of [ReaderActivity].
*/ */
class ReaderPresenter : BasePresenter<ReaderActivity>() { class ReaderPresenter : BasePresenter<ReaderActivity>() {
/**
* Network helper
*/
private val network: NetworkHelper by injectLazy()
/** /**
* Preferences. * Preferences.
*/ */
@ -108,11 +98,6 @@ class ReaderPresenter : BasePresenter<ReaderActivity>() {
*/ */
private val source by lazy { sourceManager.get(manga.source)!! } private val source by lazy { sourceManager.get(manga.source)!! }
/**
*
*/
val imageNotifier by lazy { ImageNotifier(context) }
/** /**
* Directory of pictures * Directory of pictures
*/ */
@ -398,13 +383,13 @@ class ReaderPresenter : BasePresenter<ReaderActivity>() {
if (chapter.read) { if (chapter.read) {
val removeAfterReadSlots = prefs.removeAfterReadSlots() val removeAfterReadSlots = prefs.removeAfterReadSlots()
when (removeAfterReadSlots) { when (removeAfterReadSlots) {
// Setting disabled // Setting disabled
-1 -> { -1 -> {
/**Empty function**/ /**Empty function**/
} }
// Remove current read chapter // Remove current read chapter
0 -> deleteChapter(chapter, manga) 0 -> deleteChapter(chapter, manga)
// Remove previous chapter specified by user in settings. // Remove previous chapter specified by user in settings.
else -> getAdjacentChaptersStrategy(chapter, removeAfterReadSlots) else -> getAdjacentChaptersStrategy(chapter, removeAfterReadSlots)
.first?.let { deleteChapter(it, manga) } .first?.let { deleteChapter(it, manga) }
} }
@ -548,43 +533,21 @@ class ReaderPresenter : BasePresenter<ReaderActivity>() {
* Update cover with page file. * Update cover with page file.
*/ */
internal fun setCover(page: Page) { internal fun setCover(page: Page) {
// Update cover to selected file, show error if something went wrong try {
try { if (manga.favorite) {
if (editCoverWithStream(File(page.imagePath).inputStream(), manga)) { if (manga.thumbnail_url != null) {
coverCache.copyToCache(manga.thumbnail_url!!, File(page.imagePath).inputStream())
context.toast(R.string.cover_updated) context.toast(R.string.cover_updated)
} else { } else {
throw Exception("Stream copy failed") throw Exception("Image url not found")
} }
} catch(e: Exception) { } else {
context.toast(R.string.notification_manga_update_failed) context.toast(R.string.notification_first_add_to_library)
Timber.e(e.message)
} }
} } catch (error: Exception) {
context.toast(R.string.notification_cover_update_failed)
/** Timber.e(error)
* Called to copy image to cache
* @param inputStream the new cover.
* @param manga the manga edited.
* @return true if the cover is updated, false otherwise
*/
@Throws(IOException::class)
private fun editCoverWithStream(inputStream: InputStream, manga: Manga): Boolean {
if (manga.thumbnail_url != null && manga.favorite) {
coverCache.copyToCache(manga.thumbnail_url!!, inputStream)
return true
} }
return false
}
fun shareImage(page: 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 })
} }
/** /**
@ -593,6 +556,9 @@ class ReaderPresenter : BasePresenter<ReaderActivity>() {
*/ */
@Throws(IOException::class) @Throws(IOException::class)
internal fun savePage(page: Page) { internal fun savePage(page: Page) {
// Used to show image notification
val imageNotifier = ImageNotifier(context)
// Location of image file. // Location of image file.
val inputFile = File(page.imagePath) val inputFile = File(page.imagePath)
@ -602,23 +568,20 @@ class ReaderPresenter : BasePresenter<ReaderActivity>() {
//Remove the notification if already exist (user feedback) //Remove the notification if already exist (user feedback)
imageNotifier.onClear() imageNotifier.onClear()
if (inputFile.exists()) {
//Check if file doesn't already exist // Copy file
if (destFile.exists()) { Observable.fromCallable { inputFile.copyTo(destFile, true) }
imageNotifier.onComplete(destFile) .subscribeOn(Schedulers.io())
} else { .observeOn(AndroidSchedulers.mainThread())
if (inputFile.exists()) { .subscribe(
// Copy file {
Observable.fromCallable { inputFile.copyTo(destFile) } // Show notification
.observeOn(AndroidSchedulers.mainThread()) imageNotifier.onComplete(it)
.subscribeOn(Schedulers.io()) },
.subscribe( { error ->
{ imageNotifier.onComplete(it) }, Timber.e(error)
{ error -> imageNotifier.onError(error.message)
Timber.e(error.message) })
imageNotifier.onError(error.message)
})
}
} }
} }
} }

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.data.download package eu.kanade.tachiyomi.ui.reader.notification
import android.app.PendingIntent import android.app.PendingIntent
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
@ -9,6 +9,10 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.notificationManager import eu.kanade.tachiyomi.util.notificationManager
import java.io.File import java.io.File
/**
* The BroadcastReceiver of [ImageNotifier]
* Intent calls should be made from this class.
*/
class ImageNotificationReceiver : BroadcastReceiver() { class ImageNotificationReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
when (intent.action) { when (intent.action) {
@ -25,23 +29,36 @@ class ImageNotificationReceiver : BroadcastReceiver() {
} }
} }
fun deleteImage(path: String) { /**
* Called to delete image
* @param path path of file
*/
private fun deleteImage(path: String) {
val file = File(path) val file = File(path)
if (file.exists()) file.delete() if (file.exists()) file.delete()
} }
fun shareImage(context: Context, path: String) { /**
* Called to start share intent to share image
* @param context context of application
* @param path path of file
*/
private fun shareImage(context: Context, path: String) {
val shareIntent = Intent().apply { val shareIntent = Intent().apply {
action = Intent.ACTION_SEND action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_STREAM, Uri.parse(path)) putExtra(Intent.EXTRA_STREAM, Uri.parse(path))
flags = Intent.FLAG_ACTIVITY_NEW_TASK
type = "image/jpeg" type = "image/jpeg"
} }
context.startActivity(Intent.createChooser(shareIntent, context.resources.getText(R.string.action_share)) 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 }) .apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK })
} }
fun showImage(context: Context, path: String) { /**
* Called to show image in gallery application
* @param context context of application
* @param path path of file
*/
private fun showImage(context: Context, path: String) {
val intent = Intent().apply { val intent = Intent().apply {
action = Intent.ACTION_VIEW action = Intent.ACTION_VIEW
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK
@ -51,17 +68,17 @@ class ImageNotificationReceiver : BroadcastReceiver() {
} }
companion object { companion object {
const val ACTION_SHARE_IMAGE = "eu.kanade.SHARE_IMAGE" private const val ACTION_SHARE_IMAGE = "eu.kanade.SHARE_IMAGE"
const val ACTION_SHOW_IMAGE = "eu.kanade.SHOW_IMAGE" private const val ACTION_SHOW_IMAGE = "eu.kanade.SHOW_IMAGE"
const val ACTION_DELETE_IMAGE = "eu.kanade.DELETE_IMAGE" private const val ACTION_DELETE_IMAGE = "eu.kanade.DELETE_IMAGE"
const val EXTRA_FILE_LOCATION = "file_location" private const val EXTRA_FILE_LOCATION = "file_location"
const val NOTIFICATION_ID = "notification_id" private const val NOTIFICATION_ID = "notification_id"
fun shareImageIntent(context: Context, path: String, notificationId: Int): PendingIntent { internal fun shareImageIntent(context: Context, path: String, notificationId: Int): PendingIntent {
val intent = Intent(context, ImageNotificationReceiver::class.java).apply { val intent = Intent(context, ImageNotificationReceiver::class.java).apply {
action = ACTION_SHARE_IMAGE action = ACTION_SHARE_IMAGE
putExtra(EXTRA_FILE_LOCATION, path) putExtra(EXTRA_FILE_LOCATION, path)
@ -70,7 +87,7 @@ class ImageNotificationReceiver : BroadcastReceiver() {
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
} }
fun showImageIntent(context: Context, path: String): PendingIntent { internal fun showImageIntent(context: Context, path: String): PendingIntent {
val intent = Intent(context, ImageNotificationReceiver::class.java).apply { val intent = Intent(context, ImageNotificationReceiver::class.java).apply {
action = ACTION_SHOW_IMAGE action = ACTION_SHOW_IMAGE
putExtra(EXTRA_FILE_LOCATION, path) putExtra(EXTRA_FILE_LOCATION, path)
@ -78,7 +95,7 @@ class ImageNotificationReceiver : BroadcastReceiver() {
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
} }
fun deleteImageIntent(context: Context, path: String, notificationId: Int): PendingIntent { internal fun deleteImageIntent(context: Context, path: String, notificationId: Int): PendingIntent {
val intent = Intent(context, ImageNotificationReceiver::class.java).apply { val intent = Intent(context, ImageNotificationReceiver::class.java).apply {
action = ACTION_DELETE_IMAGE action = ACTION_DELETE_IMAGE
putExtra(EXTRA_FILE_LOCATION, path) putExtra(EXTRA_FILE_LOCATION, path)

View File

@ -0,0 +1,93 @@
package eu.kanade.tachiyomi.ui.reader.notification
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 com.bumptech.glide.request.animation.GlideAnimation
import com.bumptech.glide.request.target.SimpleTarget
import eu.kanade.tachiyomi.Constants
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.notificationManager
import java.io.File
/**
* Class used to show BigPictureStyle notifications
*/
class ImageNotifier(private val context: Context) {
/**
* Notification builder.
*/
private val notificationBuilder = NotificationCompat.Builder(context)
/**
* Id of the notification.
*/
private val notificationId: Int
get() = Constants.NOTIFICATION_DOWNLOAD_IMAGE_ID
/**
* Called when image download/copy is complete
* @param file image file containing downloaded page image
*/
fun onComplete(file: File) {
with(notificationBuilder) {
Glide.with(context).load(file).asBitmap().diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true).into(object : SimpleTarget<Bitmap>(720, 1280) {
/**
* The method that will be called when the resource load has finished.
* @param resource the loaded resource.
*/
override fun onResourceReady(resource: Bitmap?, glideAnimation: GlideAnimation<in Bitmap>?) {
setContentTitle(context.getString(R.string.picture_saved))
setSmallIcon(R.drawable.ic_insert_photo_white_24dp)
setStyle(NotificationCompat.BigPictureStyle().bigPicture(resource))
setLargeIcon(resource)
setAutoCancel(true)
// Clear old actions if they exist
if (!mActions.isEmpty())
mActions.clear()
setContentIntent(ImageNotificationReceiver.showImageIntent(context, file.absolutePath))
// Share action
addAction(R.drawable.ic_share_grey_24dp,
context.getString(R.string.action_share),
ImageNotificationReceiver.shareImageIntent(context, file.absolutePath, notificationId))
// Delete action
addAction(R.drawable.ic_delete_grey_24dp,
context.getString(R.string.action_delete),
ImageNotificationReceiver.deleteImageIntent(context, file.absolutePath, notificationId))
updateNotification()
}
})
}
}
/**
* Clears the notification message
*/
fun onClear() {
context.notificationManager.cancel(notificationId)
}
private fun updateNotification() {
// Displays the progress bar on notification
context.notificationManager.notify(notificationId, notificationBuilder.build())
}
/**
* Called on error while downloading image
* @param error string containing error information
*/
fun onError(error: String?) {
// Create notification
with(notificationBuilder) {
setContentTitle(context.getString(R.string.download_notifier_title_error))
setContentText(error ?: context.getString(R.string.unknown_error))
setSmallIcon(android.R.drawable.ic_menu_report_image)
}
updateNotification()
}
}

View File

@ -11,6 +11,7 @@ import eu.kanade.tachiyomi.ui.reader.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.viewer.base.BaseReader import eu.kanade.tachiyomi.ui.reader.viewer.base.BaseReader
import eu.kanade.tachiyomi.ui.reader.viewer.pager.horizontal.LeftToRightReader import eu.kanade.tachiyomi.ui.reader.viewer.pager.horizontal.LeftToRightReader
import eu.kanade.tachiyomi.ui.reader.viewer.pager.horizontal.RightToLeftReader import eu.kanade.tachiyomi.ui.reader.viewer.pager.horizontal.RightToLeftReader
import eu.kanade.tachiyomi.util.toast
import rx.subscriptions.CompositeSubscription import rx.subscriptions.CompositeSubscription
/** /**
@ -187,8 +188,13 @@ abstract class PagerReader : BaseReader() {
} }
override fun onLongPress(e: MotionEvent?) { override fun onLongPress(e: MotionEvent?) {
super.onLongPress(e) if (isAdded) {
readerActivity.onLongPress(adapter.pages!![pager.currentItem]) val page = adapter.pages.getOrNull(pager.currentItem)
if (page != null)
readerActivity.onLongPress(page)
else
context.toast(getString(R.string.unknown_error))
}
} }
}) })
} }

View File

@ -6,9 +6,11 @@ import android.view.*
import android.view.GestureDetector.SimpleOnGestureListener import android.view.GestureDetector.SimpleOnGestureListener
import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
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.ReaderChapter import eu.kanade.tachiyomi.ui.reader.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.viewer.base.BaseReader import eu.kanade.tachiyomi.ui.reader.viewer.base.BaseReader
import eu.kanade.tachiyomi.util.toast
import eu.kanade.tachiyomi.widget.PreCachingLayoutManager import eu.kanade.tachiyomi.widget.PreCachingLayoutManager
import rx.subscriptions.CompositeSubscription import rx.subscriptions.CompositeSubscription
@ -142,17 +144,21 @@ class WebtoonReader : BaseReader() {
} }
override fun onLongPress(e: MotionEvent) { override fun onLongPress(e: MotionEvent) {
super.onLongPress(e) if (isAdded) {
val a = recycler.findChildViewUnder(e.rawX, e.rawY) val child = recycler.findChildViewUnder(e.rawX, e.rawY)
val i = recycler.getChildAdapterPosition(a) val position = recycler.getChildAdapterPosition(child)
readerActivity.onLongPress(adapter.getItem(i)) val page = adapter.pages?.getOrNull(position)
if (page != null)
readerActivity.onLongPress(page)
else
context.toast(getString(R.string.unknown_error))
}
} }
}) })
} }
/** /**
* Called when a new chapter is set in [BaseReader]. * Called when a new chapter is set in [BaseReader].
*
* @param chapter the chapter set. * @param chapter the chapter set.
* @param currentPage the initial page to display. * @param currentPage the initial page to display.
*/ */
@ -167,7 +173,6 @@ class WebtoonReader : BaseReader() {
/** /**
* Called when a chapter is appended in [BaseReader]. * Called when a chapter is appended in [BaseReader].
*
* @param chapter the chapter appended. * @param chapter the chapter appended.
*/ */
override fun onChapterAppended(chapter: ReaderChapter) { override fun onChapterAppended(chapter: ReaderChapter) {
@ -191,7 +196,6 @@ class WebtoonReader : BaseReader() {
/** /**
* Sets the active page. * Sets the active page.
*
* @param pageNumber the index of the page from [pages]. * @param pageNumber the index of the page from [pages].
*/ */
override fun setActivePage(pageNumber: Int) { override fun setActivePage(pageNumber: Int) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 485 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 928 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 594 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 570 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z" />
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

View File

@ -48,8 +48,6 @@
<string name="pref_download_only_over_wifi_key">pref_download_only_over_wifi_key</string> <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_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_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_last_used_category_key">last_used_category</string>
<string name="pref_source_languages">pref_source_languages</string> <string name="pref_source_languages">pref_source_languages</string>

View File

@ -267,11 +267,6 @@
<string name="status">Status</string> <string name="status">Status</string>
<string name="chapters">Chapters</string> <string name="chapters">Chapters</string>
<!-- Reader Activity -->
<string name="custom_filter">Custom filter</string>
<string name="save_page">Download page</string>
<string name="set_as_cover">Set as cover</string>
<string name="cover_updated">Cover updated</string>
<!-- Dialog remove recently view --> <!-- Dialog remove recently view -->
<string name="dialog_remove_recently_description">This will remove the read date of this chapter. Are you sure?</string> <string name="dialog_remove_recently_description">This will remove the read date of this chapter. Are you sure?</string>
<string name="dialog_remove_recently_reset">Reset all chapters for this manga</string> <string name="dialog_remove_recently_reset">Reset all chapters for this manga</string>
@ -279,6 +274,7 @@
<!-- Image notifier --> <!-- Image notifier -->
<string name="picture_saved">Picture saved</string> <string name="picture_saved">Picture saved</string>
<string name="saving_picture">Saving picture</string> <string name="saving_picture">Saving picture</string>
<string name="options">Options</string>
<!-- Reader activity --> <!-- Reader activity -->
<string name="custom_filter">Custom filter</string> <string name="custom_filter">Custom filter</string>
@ -315,6 +311,7 @@
<string name="notification_no_new_chapters">No new chapters found</string> <string name="notification_no_new_chapters">No new chapters found</string>
<string name="notification_new_chapters">New chapters found for:</string> <string name="notification_new_chapters">New chapters found for:</string>
<string name="notification_manga_update_failed">Failed to update manga:</string> <string name="notification_manga_update_failed">Failed to update manga:</string>
<string name="notification_cover_update_failed">Failed to update cover</string>
<string name="notification_first_add_to_library">Please add the manga to your library before doing this</string> <string name="notification_first_add_to_library">Please add the manga to your library before doing this</string>
<string name="notification_not_connected_to_ac_title">Sync canceled</string> <string name="notification_not_connected_to_ac_title">Sync canceled</string>
<string name="notification_not_connected_to_ac_body">Not connected to AC power</string> <string name="notification_not_connected_to_ac_body">Not connected to AC power</string>