Added option to download page or set page as cover
This commit is contained in:
parent
5b1f4f189b
commit
2991906a85
@ -86,9 +86,9 @@
|
|||||||
|
|
||||||
<receiver android:name=".data.updater.UpdateNotificationReceiver"/>
|
<receiver android:name=".data.updater.UpdateNotificationReceiver"/>
|
||||||
|
|
||||||
<receiver
|
<receiver android:name=".data.library.LibraryUpdateService$CancelUpdateReceiver" />
|
||||||
android:name=".data.library.LibraryUpdateService$CancelUpdateReceiver">
|
|
||||||
</receiver>
|
<receiver android:name=".data.download.ImageNotificationReceiver" />
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="eu.kanade.tachiyomi.data.glide.AppGlideModule"
|
android:name="eu.kanade.tachiyomi.data.glide.AppGlideModule"
|
||||||
|
@ -5,4 +5,5 @@ object Constants {
|
|||||||
const val NOTIFICATION_UPDATER_ID = 2
|
const val NOTIFICATION_UPDATER_ID = 2
|
||||||
const val NOTIFICATION_DOWNLOAD_CHAPTER_ID = 3
|
const val NOTIFICATION_DOWNLOAD_CHAPTER_ID = 3
|
||||||
const val NOTIFICATION_DOWNLOAD_CHAPTER_ERROR_ID = 4
|
const val NOTIFICATION_DOWNLOAD_CHAPTER_ERROR_ID = 4
|
||||||
|
const val NOTIFICATION_DOWNLOAD_IMAGE_ID = 5
|
||||||
}
|
}
|
||||||
|
@ -297,7 +297,7 @@ class DownloadManager(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the filename for an image given the page
|
// Get the filename for an image given the page
|
||||||
private fun getImageFilename(page: Page): String {
|
fun getImageFilename(page: Page): String {
|
||||||
val url = page.imageUrl
|
val url = page.imageUrl
|
||||||
val number = String.format("%03d", page.pageNumber + 1)
|
val number = String.format("%03d", page.pageNumber + 1)
|
||||||
|
|
||||||
|
@ -0,0 +1,90 @@
|
|||||||
|
package eu.kanade.tachiyomi.data.download
|
||||||
|
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.util.notificationManager
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class ImageNotificationReceiver : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
when (intent.action) {
|
||||||
|
ACTION_SHARE_IMAGE -> {
|
||||||
|
shareImage(context, intent.getStringExtra(EXTRA_FILE_LOCATION))
|
||||||
|
context.notificationManager.cancel(intent.getIntExtra(NOTIFICATION_ID, 5))
|
||||||
|
}
|
||||||
|
ACTION_SHOW_IMAGE ->
|
||||||
|
showImage(context, intent.getStringExtra(EXTRA_FILE_LOCATION))
|
||||||
|
ACTION_DELETE_IMAGE -> {
|
||||||
|
deleteImage(intent.getStringExtra(EXTRA_FILE_LOCATION))
|
||||||
|
context.notificationManager.cancel(intent.getIntExtra(NOTIFICATION_ID, 5))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteImage(path: String) {
|
||||||
|
val file = File(path)
|
||||||
|
if (file.exists()) file.delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun shareImage(context: Context, path: String) {
|
||||||
|
val shareIntent = Intent().apply {
|
||||||
|
action = Intent.ACTION_SEND
|
||||||
|
putExtra(Intent.EXTRA_STREAM, Uri.parse(path))
|
||||||
|
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 })
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showImage(context: Context, path: String) {
|
||||||
|
val intent = Intent().apply {
|
||||||
|
action = Intent.ACTION_VIEW
|
||||||
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK
|
||||||
|
setDataAndType(Uri.parse("file://" + path), "image/*")
|
||||||
|
}
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val ACTION_SHARE_IMAGE = "eu.kanade.SHARE_IMAGE"
|
||||||
|
|
||||||
|
const val ACTION_SHOW_IMAGE = "eu.kanade.SHOW_IMAGE"
|
||||||
|
|
||||||
|
const val ACTION_DELETE_IMAGE = "eu.kanade.DELETE_IMAGE"
|
||||||
|
|
||||||
|
const val EXTRA_FILE_LOCATION = "file_location"
|
||||||
|
|
||||||
|
const val NOTIFICATION_ID = "notification_id"
|
||||||
|
|
||||||
|
fun shareImageIntent(context: Context, path: String, notificationId: Int): PendingIntent {
|
||||||
|
val intent = Intent(context, ImageNotificationReceiver::class.java).apply {
|
||||||
|
action = ACTION_SHARE_IMAGE
|
||||||
|
putExtra(EXTRA_FILE_LOCATION, path)
|
||||||
|
putExtra(NOTIFICATION_ID, notificationId)
|
||||||
|
}
|
||||||
|
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showImageIntent(context: Context, path: String): PendingIntent {
|
||||||
|
val intent = Intent(context, ImageNotificationReceiver::class.java).apply {
|
||||||
|
action = ACTION_SHOW_IMAGE
|
||||||
|
putExtra(EXTRA_FILE_LOCATION, path)
|
||||||
|
}
|
||||||
|
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteImageIntent(context: Context, path: String, notificationId: Int): PendingIntent {
|
||||||
|
val intent = Intent(context, ImageNotificationReceiver::class.java).apply {
|
||||||
|
action = ACTION_DELETE_IMAGE
|
||||||
|
putExtra(EXTRA_FILE_LOCATION, path)
|
||||||
|
putExtra(NOTIFICATION_ID, notificationId)
|
||||||
|
}
|
||||||
|
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,124 @@
|
|||||||
|
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.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 bitmap image file containing downloaded page image
|
||||||
|
*/
|
||||||
|
fun onComplete(bitmap: Bitmap, 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)
|
||||||
|
setLargeIcon(bitmap)
|
||||||
|
setStyle(NotificationCompat.BigPictureStyle().bigPicture(bitmap))
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onComplete(file: File) {
|
||||||
|
onComplete(convertToBitmap(file), file)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -184,10 +184,9 @@ class MangaInfoFragment : BaseRxFragment<MangaInfoPresenter>() {
|
|||||||
val url = source.mangaDetailsRequest(presenter.manga).url().toString()
|
val url = source.mangaDetailsRequest(presenter.manga).url().toString()
|
||||||
val sharingIntent = Intent(Intent.ACTION_SEND).apply {
|
val sharingIntent = Intent(Intent.ACTION_SEND).apply {
|
||||||
type = "text/plain"
|
type = "text/plain"
|
||||||
putExtra(android.content.Intent.EXTRA_SUBJECT, presenter.manga.title)
|
|
||||||
putExtra(android.content.Intent.EXTRA_TEXT, resources.getString(R.string.share_text, presenter.manga.title, url))
|
putExtra(android.content.Intent.EXTRA_TEXT, resources.getString(R.string.share_text, presenter.manga.title, url))
|
||||||
}
|
}
|
||||||
startActivity(Intent.createChooser(sharingIntent, resources.getText(R.string.share_subject)))
|
startActivity(Intent.createChooser(sharingIntent, resources.getText(R.string.action_share)))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
context.toast(e.message)
|
context.toast(e.message)
|
||||||
}
|
}
|
||||||
|
@ -145,6 +145,8 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
|
|||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.action_settings -> ReaderSettingsDialog().show(supportFragmentManager, "settings")
|
R.id.action_settings -> ReaderSettingsDialog().show(supportFragmentManager, "settings")
|
||||||
R.id.action_custom_filter -> ReaderCustomFilterDialog().show(supportFragmentManager, "filter")
|
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)
|
else -> return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
@ -1,15 +1,23 @@
|
|||||||
package eu.kanade.tachiyomi.ui.reader
|
package eu.kanade.tachiyomi.ui.reader
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.os.Environment
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
||||||
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||||
import eu.kanade.tachiyomi.data.database.models.History
|
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.GET
|
||||||
|
import eu.kanade.tachiyomi.data.network.NetworkHelper
|
||||||
|
import eu.kanade.tachiyomi.data.network.ProgressListener
|
||||||
|
import eu.kanade.tachiyomi.data.network.newCallWithProgress
|
||||||
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
|
||||||
@ -17,6 +25,8 @@ 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.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.saveTo
|
||||||
|
import eu.kanade.tachiyomi.util.toast
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.Subscription
|
import rx.Subscription
|
||||||
import rx.android.schedulers.AndroidSchedulers
|
import rx.android.schedulers.AndroidSchedulers
|
||||||
@ -24,6 +34,8 @@ import rx.schedulers.Schedulers
|
|||||||
import timber.log.Timber
|
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.InputStream
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -31,6 +43,11 @@ import java.util.*
|
|||||||
*/
|
*/
|
||||||
class ReaderPresenter : BasePresenter<ReaderActivity>() {
|
class ReaderPresenter : BasePresenter<ReaderActivity>() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Network helper
|
||||||
|
*/
|
||||||
|
private val network: NetworkHelper by injectLazy()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Preferences.
|
* Preferences.
|
||||||
*/
|
*/
|
||||||
@ -61,6 +78,11 @@ class ReaderPresenter : BasePresenter<ReaderActivity>() {
|
|||||||
*/
|
*/
|
||||||
val chapterCache: ChapterCache by injectLazy()
|
val chapterCache: ChapterCache by injectLazy()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cover cache.
|
||||||
|
*/
|
||||||
|
val coverCache: CoverCache by injectLazy()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manga being read.
|
* Manga being read.
|
||||||
*/
|
*/
|
||||||
@ -88,6 +110,20 @@ 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
|
||||||
|
*/
|
||||||
|
private val pictureDirectory: String by lazy {
|
||||||
|
Environment.getExternalStorageDirectory().absolutePath + File.separator +
|
||||||
|
Environment.DIRECTORY_PICTURES + File.separator +
|
||||||
|
context.getString(R.string.app_name) + File.separator
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Chapter list for the active manga. It's retrieved lazily and should be accessed for the first
|
* Chapter list for the active manga. It's retrieved lazily and should be accessed for the first
|
||||||
* time in a background thread to avoid blocking the UI.
|
* time in a background thread to avoid blocking the UI.
|
||||||
@ -365,7 +401,9 @@ class ReaderPresenter : BasePresenter<ReaderActivity>() {
|
|||||||
val removeAfterReadSlots = prefs.removeAfterReadSlots()
|
val removeAfterReadSlots = prefs.removeAfterReadSlots()
|
||||||
when (removeAfterReadSlots) {
|
when (removeAfterReadSlots) {
|
||||||
// Setting disabled
|
// Setting disabled
|
||||||
-1 -> { /**Empty function**/ }
|
-1 -> {
|
||||||
|
/**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.
|
||||||
@ -508,4 +546,87 @@ class ReaderPresenter : BasePresenter<ReaderActivity>() {
|
|||||||
db.insertManga(manga).executeAsBlocking()
|
db.insertManga(manga).executeAsBlocking()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update cover with page file.
|
||||||
|
*/
|
||||||
|
internal fun setCover() {
|
||||||
|
chapter.pages?.get(chapter.last_page_read)?.let {
|
||||||
|
// Update cover to selected file, show error if something went wrong
|
||||||
|
try {
|
||||||
|
if (editCoverWithStream(File(it.imagePath).inputStream(), manga)) {
|
||||||
|
context.toast(R.string.cover_updated)
|
||||||
|
} else {
|
||||||
|
throw Exception("Stream copy failed")
|
||||||
|
}
|
||||||
|
} catch(e: Exception) {
|
||||||
|
context.toast(R.string.notification_manga_update_failed)
|
||||||
|
Timber.e(e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save page to local storage
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
@Throws(IOException::class)
|
||||||
|
internal fun savePage() {
|
||||||
|
chapter.pages?.get(chapter.last_page_read)?.let { page ->
|
||||||
|
// File where the image will be saved
|
||||||
|
val destFile = File(pictureDirectory, manga.title + " - " + chapter.name +
|
||||||
|
" - " + downloadManager.getImageFilename(page))
|
||||||
|
|
||||||
|
if (destFile.exists()) {
|
||||||
|
imageNotifier.onComplete(destFile)
|
||||||
|
} else {
|
||||||
|
// Progress of the download
|
||||||
|
var savedProgress = 0
|
||||||
|
|
||||||
|
val progressListener = object : ProgressListener {
|
||||||
|
override fun update(bytesRead: Long, contentLength: Long, done: Boolean) {
|
||||||
|
val progress = (100 * bytesRead / contentLength).toInt()
|
||||||
|
if (progress > savedProgress) {
|
||||||
|
savedProgress = progress
|
||||||
|
imageNotifier.onProgressChange(progress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download and save the image.
|
||||||
|
Observable.fromCallable { ->
|
||||||
|
network.client.newCallWithProgress(GET(page.imageUrl!!), progressListener).execute()
|
||||||
|
}.map {
|
||||||
|
response ->
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
response.body().source().saveTo(destFile)
|
||||||
|
imageNotifier.onComplete(destFile)
|
||||||
|
} else {
|
||||||
|
response.close()
|
||||||
|
throw Exception("Unsuccessful response")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.subscribe({}, { error ->
|
||||||
|
Timber.e(error.message)
|
||||||
|
imageNotifier.onError(error.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
9
app/src/main/res/drawable/ic_insert_photo_black_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_insert_photo_black_24dp.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<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>
|
@ -223,7 +223,6 @@
|
|||||||
<string name="manga_info_status_label">Status</string>
|
<string name="manga_info_status_label">Status</string>
|
||||||
<string name="manga_info_source_label">Source</string>
|
<string name="manga_info_source_label">Source</string>
|
||||||
<string name="manga_info_genres_label">Genres</string>
|
<string name="manga_info_genres_label">Genres</string>
|
||||||
<string name="share_subject">Share…</string>
|
|
||||||
<string name="share_text">Check out %1$s! at %2$s</string>
|
<string name="share_text">Check out %1$s! at %2$s</string>
|
||||||
<string name="circular_icon">Circular icon</string>
|
<string name="circular_icon">Circular icon</string>
|
||||||
<string name="rounded_icon">Rounded icon</string>
|
<string name="rounded_icon">Rounded icon</string>
|
||||||
@ -267,10 +266,18 @@
|
|||||||
<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>
|
||||||
|
|
||||||
|
<!-- Image notifier -->
|
||||||
|
<string name="picture_saved">Picture saved</string>
|
||||||
|
<string name="saving_picture">Saving picture</string>
|
||||||
|
|
||||||
<!-- Reader activity -->
|
<!-- Reader activity -->
|
||||||
<string name="custom_filter">Custom filter</string>
|
<string name="custom_filter">Custom filter</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user