Shortcut fix Oreo (#1026)

* Moved to Android O Shortcutmanager

* Re-added possibility to change icon shape pre oreo.
This commit is contained in:
Bram van de Kerkhof 2017-10-10 12:05:33 +02:00 committed by inorichi
parent 5c662b1ae1
commit deec65446f
4 changed files with 105 additions and 72 deletions

View file

@ -24,7 +24,6 @@
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="android.app.shortcuts"

View file

@ -41,6 +41,8 @@ class NotificationReceiver : BroadcastReceiver() {
ACTION_RESUME_DOWNLOADS -> DownloadService.start(context)
// Clear the download queue
ACTION_CLEAR_DOWNLOADS -> downloadManager.clearQueue(true)
// Show message notification created
ACTION_SHORTCUT_CREATED -> context.toast(R.string.shortcut_created)
// Launch share activity and dismiss notification
ACTION_SHARE_IMAGE -> shareImage(context, intent.getStringExtra(EXTRA_FILE_LOCATION),
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1))
@ -161,6 +163,9 @@ class NotificationReceiver : BroadcastReceiver() {
// Called to clear downloads.
private const val ACTION_CLEAR_DOWNLOADS = "$ID.$NAME.ACTION_CLEAR_DOWNLOADS"
// Called to notify user shortcut is created.
private const val ACTION_SHORTCUT_CREATED = "$ID.$NAME.ACTION_SHORTCUT_CREATED"
// Called to dismiss notification.
private const val ACTION_DISMISS_NOTIFICATION = "$ID.$NAME.ACTION_DISMISS_NOTIFICATION"
@ -199,6 +204,13 @@ class NotificationReceiver : BroadcastReceiver() {
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
}
internal fun shortcutCreatedBroadcast(context: Context) : PendingIntent {
val intent = Intent(context, NotificationReceiver::class.java).apply {
action = ACTION_SHORTCUT_CREATED
}
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
}
/**
* Returns [PendingIntent] that starts a service which dismissed the notification
*

View file

@ -1,14 +1,18 @@
package eu.kanade.tachiyomi.ui.manga.info
import android.app.PendingIntent
import android.content.Intent
import android.graphics.Bitmap
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.support.customtabs.CustomTabsIntent
import android.support.v4.content.pm.ShortcutInfoCompat
import android.support.v4.content.pm.ShortcutManagerCompat
import android.support.v4.graphics.drawable.IconCompat
import android.view.*
import com.afollestad.materialdialogs.MaterialDialog
import com.bumptech.glide.BitmapRequestBuilder
import com.bumptech.glide.BitmapTypeRequest
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.resource.bitmap.CenterCrop
@ -17,6 +21,7 @@ import com.jakewharton.rxbinding.view.clicks
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.model.SManga
@ -181,7 +186,7 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
/**
* Toggles the favorite status and asks for confirmation to delete downloaded chapters.
*/
fun toggleFavorite() {
private fun toggleFavorite() {
val view = view
val isNowFavorite = presenter.toggleFavorite()
@ -197,7 +202,7 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
/**
* Open the manga in browser.
*/
fun openInBrowser() {
private fun openInBrowser() {
val context = view?.context ?: return
val source = presenter.source as? HttpSource ?: return
@ -288,11 +293,11 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
if (manga.favorite) {
val categories = presenter.getCategories()
val defaultCategory = categories.find { it.id == preferences.defaultCategory() }
if (defaultCategory != null) {
presenter.moveMangaToCategory(manga, defaultCategory)
} else if (categories.size <= 1) { // default or the one from the user
when {
defaultCategory != null -> presenter.moveMangaToCategory(manga, defaultCategory)
categories.size <= 1 -> // default or the one from the user
presenter.moveMangaToCategory(manga, categories.firstOrNull())
} else {
else -> {
val ids = presenter.getMangaCategoryIds(manga)
val preselected = ids.mapNotNull { id ->
categories.indexOfFirst { it.id == id }.takeIf { it != -1 }
@ -303,6 +308,7 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
}
}
}
}
override fun updateCategoriesForMangas(mangas: List<Manga>, categories: List<Category>) {
val manga = mangas.firstOrNull() ?: return
@ -310,38 +316,10 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
}
/**
* Add the manga to the home screen
* Choose the shape of the icon
* Only use for pre Oreo devices.
*/
fun addToHomeScreen() {
val activity = activity ?: return
val mangaControllerArgs = parentController?.args ?: return
val shortcutIntent = activity.intent
.setAction(MainActivity.SHORTCUT_MANGA)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
.putExtra(MangaController.MANGA_EXTRA,
mangaControllerArgs.getLong(MangaController.MANGA_EXTRA))
val addIntent = Intent("com.android.launcher.action.INSTALL_SHORTCUT")
.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent)
//Set shortcut title
val dialog = MaterialDialog.Builder(activity)
.title(R.string.shortcut_title)
.input("", presenter.manga.title, { _, text ->
//Set shortcut title
addIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, text.toString())
reshapeIconBitmap(addIntent,
Glide.with(activity).load(presenter.manga).asBitmap())
})
.negativeText(android.R.string.cancel)
.show()
untilDestroySubscriptions.add(Subscriptions.create { dialog.dismiss() })
}
fun reshapeIconBitmap(addIntent: Intent, request: BitmapTypeRequest<out Any>) {
private fun chooseIconDialog() {
val activity = activity ?: return
val modes = intArrayOf(R.string.circular_icon,
@ -349,16 +327,9 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
R.string.square_icon,
R.string.star_icon)
fun BitmapRequestBuilder<out Any, Bitmap>.toIcon(): Bitmap {
return this.into(96, 96).get()
}
val request = Glide.with(activity).load(presenter.manga).asBitmap()
// i = 0: Circular icon
// i = 1: Rounded icon
// i = 2: Square icon
// i = 3: Star icon (because boredom)
fun getIcon(i: Int): Bitmap? {
return when (i) {
fun getIcon(i: Int): Bitmap? = when (i) {
0 -> request.transform(CropCircleTransformation(activity)).toIcon()
1 -> request.transform(RoundedCornersTransformation(activity, 5, 0)).toIcon()
2 -> request.transform(CropSquareTransformation(activity)).toIcon()
@ -366,7 +337,6 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
MaskTransformation(activity, R.drawable.mask_star)).toIcon()
else -> null
}
}
val dialog = MaterialDialog.Builder(activity)
.title(R.string.icon_shape)
@ -377,7 +347,7 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ icon ->
if (icon != null) createShortcut(addIntent, icon)
if (icon != null) createShortcut(icon)
}, {
activity.toast(R.string.icon_creation_fail)
})
@ -387,16 +357,67 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
untilDestroySubscriptions.add(Subscriptions.create { dialog.dismiss() })
}
fun createShortcut(addIntent: Intent, icon: Bitmap) {
private fun BitmapRequestBuilder<out Any, Bitmap>.toIcon() = this.into(96,96).get()
/**
* Create shortcut using ShortcutManager.
*/
private fun createShortcut(icon: Bitmap) {
val activity = activity ?: return
val mangaControllerArgs = parentController?.args ?: return
//Send shortcut intent
addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon)
activity.sendBroadcast(addIntent)
//Go to launcher to show this shiny new shortcut!
val startMain = Intent(Intent.ACTION_MAIN)
startMain.addCategory(Intent.CATEGORY_HOME).flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(startMain)
// Create the shortcut intent.
val shortcutIntent = activity.intent
.setAction(MainActivity.SHORTCUT_MANGA)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
.putExtra(MangaController.MANGA_EXTRA,
mangaControllerArgs.getLong(MangaController.MANGA_EXTRA))
// Check if shortcut placement is supported
if (ShortcutManagerCompat.isRequestPinShortcutSupported(activity)) {
// Create shortcut info
val pinShortcutInfo = ShortcutInfoCompat.Builder(activity, "manga-shortcut-${presenter.manga.title}-${presenter.source.name}")
.setShortLabel(presenter.manga.title)
.setIcon(IconCompat.createWithBitmap(icon))
.setIntent(shortcutIntent).build()
val successCallback: PendingIntent
successCallback = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create the CallbackIntent.
val pinnedShortcutCallbackIntent = ShortcutManagerCompat.createShortcutResultIntent(activity, pinShortcutInfo)
// Configure the intent so that the broadcast receiver gets the callback successfully.
PendingIntent.getBroadcast(activity, 0, pinnedShortcutCallbackIntent, 0)
} else{
NotificationReceiver.shortcutCreatedBroadcast(activity)
}
// Request shortcut.
ShortcutManagerCompat.requestPinShortcut(activity, pinShortcutInfo,
successCallback.intentSender)
}
}
/**
* Add a shortcut of the manga to the home screen
*/
private fun addToHomeScreen() {
// Get bitmap icon
val bitmap = Glide.with(activity).load(presenter.manga).asBitmap()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
Observable.fromCallable {
bitmap.toIcon()
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({icon ->
createShortcut(icon)
})
}else{
chooseIconDialog()
}
}
}

View file

@ -311,6 +311,7 @@
<string name="square_icon">Square icon</string>
<string name="star_icon">Star icon</string>
<string name="shortcut_title">Shortcut title</string>
<string name="shortcut_created">Shortcut was added to home screen.</string>
<string name="icon_shape">Icon shape</string>
<string name="icon_creation_fail">Failed to create shortcut!</string>
<string name="delete_downloads_for_manga">Delete downloaded chapters?</string>