Reader seekbar back to ignoring gestures

Sharesheet update:
Now shows a preview of images shared, a preview of the manga you're sharing on android 10
Sharing via the saved image notification now shows the share sheet
This commit is contained in:
Jay 2019-11-02 21:43:15 -07:00
parent fee4328ad0
commit d263f03cc7
5 changed files with 100 additions and 23 deletions

View File

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.data.notification
import android.app.Notification import android.app.Notification
import android.app.PendingIntent import android.app.PendingIntent
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.ClipData
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Handler import android.os.Handler
@ -86,12 +87,16 @@ class NotificationReceiver : BroadcastReceiver() {
val uri = File(path).getUriCompat(context) val uri = File(path).getUriCompat(context)
putExtra(Intent.EXTRA_STREAM, uri) putExtra(Intent.EXTRA_STREAM, uri)
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION
clipData = ClipData.newRawUri(null, uri)
type = "image/*" type = "image/*"
} }
// Dismiss notification // Close Navigation Shade
dismissNotification(context, notificationId) context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
// Launch share activity // Launch share activity
context.startActivity(intent) val shareIntent = Intent.createChooser(intent, context.getString(R.string
.action_share))
shareIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION
context.startActivity(shareIntent)
} }
/** /**
@ -106,8 +111,7 @@ class NotificationReceiver : BroadcastReceiver() {
val db = DatabaseHelper(context) val db = DatabaseHelper(context)
val manga = db.getManga(mangaId).executeAsBlocking() val manga = db.getManga(mangaId).executeAsBlocking()
val chapter = db.getChapter(chapterId).executeAsBlocking() val chapter = db.getChapter(chapterId).executeAsBlocking()
val it = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS) context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
context.sendBroadcast(it)
if (manga != null && chapter != null) { if (manga != null && chapter != null) {
val intent = ReaderActivity.newIntent(context, manga, chapter).apply { val intent = ReaderActivity.newIntent(context, manga, chapter).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP

View File

@ -20,6 +20,7 @@ import android.widget.Toast
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.resource.bitmap.RoundedCorners import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.target.SimpleTarget import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.transition.Transition import com.bumptech.glide.request.transition.Transition
import com.jakewharton.rxbinding.support.v4.widget.refreshes import com.jakewharton.rxbinding.support.v4.widget.refreshes
@ -46,6 +47,7 @@ import jp.wasabeef.glide.transformations.CropSquareTransformation
import jp.wasabeef.glide.transformations.MaskTransformation import jp.wasabeef.glide.transformations.MaskTransformation
import kotlinx.android.synthetic.main.manga_info_controller.* import kotlinx.android.synthetic.main.manga_info_controller.*
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.io.File
import java.text.DateFormat import java.text.DateFormat
import java.text.DecimalFormat import java.text.DecimalFormat
import java.util.Date import java.util.Date
@ -161,7 +163,7 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
when (item.itemId) { when (item.itemId) {
R.id.action_open_in_browser -> openInBrowser() R.id.action_open_in_browser -> openInBrowser()
R.id.action_open_in_web_view -> openInWebView() R.id.action_open_in_web_view -> openInWebView()
R.id.action_share -> shareManga() R.id.action_share -> prepareToShareManga()
R.id.action_add_to_home_screen -> addToHomeScreen() R.id.action_add_to_home_screen -> addToHomeScreen()
else -> return super.onOptionsItemSelected(item) else -> return super.onOptionsItemSelected(item)
} }
@ -325,15 +327,40 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
/** /**
* Called to run Intent with [Intent.ACTION_SEND], which show share dialog. * Called to run Intent with [Intent.ACTION_SEND], which show share dialog.
*/ */
private fun shareManga() { private fun prepareToShareManga() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
GlideApp.with(activity!!).asBitmap().load(presenter.manga).into(object :
CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
presenter.shareManga(resource)
}
override fun onLoadCleared(placeholder: Drawable?) {}
override fun onLoadFailed(errorDrawable: Drawable?) {
shareManga()
}
})
else shareManga()
}
/**
* Called to run Intent with [Intent.ACTION_SEND], which show share dialog.
*/
fun shareManga(cover: File? = null) {
val context = view?.context ?: return val context = view?.context ?: return
val source = presenter.source as? HttpSource ?: return val source = presenter.source as? HttpSource ?: return
val stream = cover?.getUriCompat(context)
try { try {
val url = source.mangaDetailsRequest(presenter.manga).url.toString() val url = source.mangaDetailsRequest(presenter.manga).url.toString()
val intent = Intent(Intent.ACTION_SEND).apply { val intent = Intent(Intent.ACTION_SEND).apply {
type = "text/plain" type = "text/*"
putExtra(Intent.EXTRA_TEXT, url) putExtra(Intent.EXTRA_TEXT, url)
putExtra(Intent.EXTRA_TITLE, presenter.manga.title)
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
if (stream != null) {
clipData = ClipData.newRawUri(null, stream)
}
} }
startActivity(Intent.createChooser(intent, context.getString(R.string.action_share))) startActivity(Intent.createChooser(intent, context.getString(R.string.action_share)))
} catch (e: Exception) { } catch (e: Exception) {
@ -523,11 +550,13 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
3 -> centerCrop().transform(MaskTransformation(R.drawable.mask_star)) 3 -> centerCrop().transform(MaskTransformation(R.drawable.mask_star))
} }
} }
.into(object : SimpleTarget<Bitmap>(96, 96) { .into(object : CustomTarget<Bitmap>(96, 96) {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) { override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
createShortcut(resource) createShortcut(resource)
} }
override fun onLoadCleared(placeholder: Drawable?) { }
override fun onLoadFailed(errorDrawable: Drawable?) { override fun onLoadFailed(errorDrawable: Drawable?) {
activity?.toast(R.string.icon_creation_fail) activity?.toast(R.string.icon_creation_fail)
} }

View File

@ -1,5 +1,7 @@
package eu.kanade.tachiyomi.ui.manga.info package eu.kanade.tachiyomi.ui.manga.info
import android.app.Application
import android.graphics.Bitmap
import android.os.Bundle import android.os.Bundle
import com.jakewharton.rxrelay.BehaviorRelay import com.jakewharton.rxrelay.BehaviorRelay
import com.jakewharton.rxrelay.PublishRelay import com.jakewharton.rxrelay.PublishRelay
@ -11,6 +13,8 @@ import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.util.DiskUtil
import eu.kanade.tachiyomi.util.ImageUtil
import eu.kanade.tachiyomi.util.isNullOrUnsubscribed import eu.kanade.tachiyomi.util.isNullOrUnsubscribed
import rx.Observable import rx.Observable
import rx.Subscription import rx.Subscription
@ -18,6 +22,11 @@ import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers import rx.schedulers.Schedulers
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.InputStream
import java.io.OutputStream
import java.util.* import java.util.*
/** /**
@ -116,6 +125,35 @@ class MangaInfoPresenter(
toggleFavorite() toggleFavorite()
} }
fun shareManga(cover:Bitmap) {
val context = Injekt.get<Application>()
val destDir = File(context.cacheDir, "shared_image")
Observable.fromCallable { destDir.deleteRecursively() } // Keep only the last shared file
.map { saveImage(cover, destDir, manga) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeFirst(
{ view, file -> view.shareManga(file) },
{ view, error -> view.shareManga() }
)
}
private fun saveImage(cover:Bitmap, directory: File, manga: Manga): File? {
directory.mkdirs()
// Build destination file.
val filename = DiskUtil.buildValidFilename("${manga.title} - Cover.jpg")
val destFile = File(directory, filename)
val stream: OutputStream = FileOutputStream(destFile)
cover.compress(Bitmap.CompressFormat.JPEG,75,stream)
stream.flush()
stream.close()
return destFile
}
/** /**
* Get the default, and user categories. * Get the default, and user categories.
* *

View File

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.reader
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.ProgressDialog import android.app.ProgressDialog
import android.content.ClipData
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
@ -156,17 +157,6 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>(),
config = ReaderConfig() config = ReaderConfig()
initializeMenu() initializeMenu()
val container: ViewGroup = findViewById(R.id.reader_container)
val readerBHeight = reader_menu_bottom.layoutParams.height
container.doOnApplyWindowInsets { _, insets, padding ->
val bottomInset = if (Build.VERSION.SDK_INT >= 29)
(insets.mandatorySystemGestureInsets.bottom - insets.systemWindowInsetBottom)
else 0
reader_menu_bottom.updateLayoutParams<ViewGroup.MarginLayoutParams> {
height = readerBHeight + bottomInset
}
reader_menu_bottom.updatePaddingRelative(bottom = padding.bottom + bottomInset)
}
} }
/** /**
@ -312,6 +302,9 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>(),
systemUi?.show() systemUi?.show()
reader_menu.visibility = View.VISIBLE reader_menu.visibility = View.VISIBLE
reader_menu_bottom.visibility = View.VISIBLE reader_menu_bottom.visibility = View.VISIBLE
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
window.navigationBarColor = getResourceColor(R.attr.colorPrimaryDark)
}
if (animate) { if (animate) {
if (!menuStickyVisible) { if (!menuStickyVisible) {
val toolbarAnimation = AnimationUtils.loadAnimation(this, R.anim.enter_from_top) val toolbarAnimation = AnimationUtils.loadAnimation(this, R.anim.enter_from_top)
@ -513,6 +506,7 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>(),
val intent = Intent(Intent.ACTION_SEND).apply { val intent = Intent(Intent.ACTION_SEND).apply {
putExtra(Intent.EXTRA_STREAM, stream) putExtra(Intent.EXTRA_STREAM, stream)
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION
clipData = ClipData.newRawUri(null, stream)
type = "image/*" type = "image/*"
} }
startActivity(Intent.createChooser(intent, getString(R.string.action_share))) startActivity(Intent.createChooser(intent, getString(R.string.action_share)))
@ -570,6 +564,9 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>(),
setMenuVisibility(false) setMenuVisibility(false)
menuStickyVisible = false menuStickyVisible = false
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
window.navigationBarColor = getColor(android.R.color.transparent)
}
reader_menu_bottom.visibility = View.GONE reader_menu_bottom.visibility = View.GONE
reader_menu.visibility = View.VISIBLE reader_menu.visibility = View.VISIBLE
val toolbarAnimation = AnimationUtils.loadAnimation(this, R.anim.enter_from_top) val toolbarAnimation = AnimationUtils.loadAnimation(this, R.anim.enter_from_top)
@ -579,9 +576,6 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>(),
} }
}) })
toolbar.startAnimation(toolbarAnimation) toolbar.startAnimation(toolbarAnimation)
/*val bottomAnimation = AnimationUtils.loadAnimation(this, R.anim
.enter_from_bottom)
reader_menu_bottom.startAnimation(bottomAnimation)*/
} }
} }
else { else {

View File

@ -20,6 +20,8 @@ class ReaderSeekBar @JvmOverloads constructor(
* Whether the seekbar should draw from right to left. * Whether the seekbar should draw from right to left.
*/ */
var isRTL = false var isRTL = false
private val boundingBox: Rect = Rect()
private val exclusions = listOf(boundingBox)
/** /**
* Draws the seekbar, translating the canvas if using a right to left reader. * Draws the seekbar, translating the canvas if using a right to left reader.
@ -43,4 +45,14 @@ class ReaderSeekBar @JvmOverloads constructor(
} }
return super.onTouchEvent(event) return super.onTouchEvent(event)
} }
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
if (Build.VERSION.SDK_INT >= 29) {
if (changed) {
boundingBox.set(left, top, right, bottom)
systemGestureExclusionRects = exclusions
}
}
}
} }