fix: Don't exceed the maximum number of created shortcuts (#771)
Previous code created one shortcut per account, which could exceed the maximum number of shortcuts allowed, causing a crash. Fix this by creating no more than the max number of shortcuts while ensuring that the active account is always included. Fixes #752
This commit is contained in:
parent
33e1b418cf
commit
597833d660
|
@ -2625,8 +2625,8 @@
|
|||
<issue
|
||||
id="ReportShortcutUsage"
|
||||
message="Calling this method indicates use of dynamic shortcuts, but there are no calls to methods that track shortcut usage, such as `pushDynamicShortcut` or `reportShortcutUsed`. Calling these methods is recommended, as they track shortcut usage and allow launchers to adjust which shortcuts appear based on activation history. Please see https://developer.android.com/develop/ui/views/launch/shortcuts/managing-shortcuts#track-usage"
|
||||
errorLine1=" ShortcutManagerCompat.addDynamicShortcuts(context, listOf(shortcutInfo))"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
errorLine1=" ShortcutManagerCompat.setDynamicShortcuts(context, shortcuts)"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/app/pachli/util/ShareShortcutHelper.kt"
|
||||
line="96"
|
||||
|
|
|
@ -120,7 +120,7 @@ import app.pachli.updatecheck.UpdateCheck
|
|||
import app.pachli.usecase.DeveloperToolsUseCase
|
||||
import app.pachli.usecase.LogoutUsecase
|
||||
import app.pachli.util.getDimension
|
||||
import app.pachli.util.updateShortcut
|
||||
import app.pachli.util.updateShortcuts
|
||||
import at.connyduck.calladapter.networkresult.fold
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.RequestManager
|
||||
|
@ -1085,7 +1085,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
|
|||
updateProfiles()
|
||||
|
||||
externalScope.launch {
|
||||
updateShortcut(applicationContext, accountManager.activeAccount!!)
|
||||
updateShortcuts(applicationContext, accountManager)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,10 +21,12 @@ import android.content.Intent
|
|||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.text.TextUtils
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.core.app.Person
|
||||
import androidx.core.content.pm.ShortcutInfoCompat
|
||||
import androidx.core.content.pm.ShortcutManagerCompat
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import app.pachli.core.accounts.AccountManager
|
||||
import app.pachli.core.database.model.AccountEntity
|
||||
import app.pachli.core.designsystem.R as DR
|
||||
import app.pachli.core.navigation.MainActivityIntent
|
||||
|
@ -33,67 +35,65 @@ import java.util.concurrent.ExecutionException
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
suspend fun updateShortcut(context: Context, account: AccountEntity) = withContext(Dispatchers.IO) {
|
||||
suspend fun updateShortcuts(context: Context, accountManager: AccountManager) = withContext(Dispatchers.IO) {
|
||||
val innerSize = context.resources.getDimensionPixelSize(DR.dimen.adaptive_bitmap_inner_size)
|
||||
val outerSize = context.resources.getDimensionPixelSize(DR.dimen.adaptive_bitmap_outer_size)
|
||||
|
||||
val bmp = try {
|
||||
if (TextUtils.isEmpty(account.profilePictureUrl)) {
|
||||
Glide.with(context)
|
||||
.asBitmap()
|
||||
.load(DR.drawable.avatar_default)
|
||||
.submit(innerSize, innerSize)
|
||||
.get()
|
||||
} else {
|
||||
Glide.with(context)
|
||||
.asBitmap()
|
||||
.load(account.profilePictureUrl)
|
||||
.error(DR.drawable.avatar_default)
|
||||
.submit(innerSize, innerSize)
|
||||
.get()
|
||||
val maxShortcuts = ShortcutManagerCompat.getMaxShortcutCountPerActivity(context)
|
||||
|
||||
val shortcuts = accountManager.getAllAccountsOrderedByActive().take(maxShortcuts).mapNotNull { account ->
|
||||
val drawable = try {
|
||||
if (TextUtils.isEmpty(account.profilePictureUrl)) {
|
||||
AppCompatResources.getDrawable(context, DR.drawable.avatar_default)
|
||||
} else {
|
||||
Glide.with(context)
|
||||
.asDrawable()
|
||||
.load(account.profilePictureUrl)
|
||||
.error(DR.drawable.avatar_default)
|
||||
.submit(innerSize, innerSize)
|
||||
.get()
|
||||
}
|
||||
} catch (e: ExecutionException) {
|
||||
// The `.error` handler isn't always used. For example, Glide throws
|
||||
// ExecutionException if the URL does not point at an image. Fallback to
|
||||
// the default avatar (https://github.com/bumptech/glide/issues/4672).
|
||||
AppCompatResources.getDrawable(context, DR.drawable.avatar_default)
|
||||
} ?: return@mapNotNull null
|
||||
|
||||
// inset the loaded bitmap inside a 108dp transparent canvas so it looks good as adaptive icon
|
||||
val outBmp = Bitmap.createBitmap(outerSize, outerSize, Bitmap.Config.ARGB_8888)
|
||||
|
||||
val canvas = Canvas(outBmp)
|
||||
val border = (outerSize - innerSize) / 2
|
||||
drawable.setBounds(border, border, border + innerSize, border + innerSize)
|
||||
drawable.draw(canvas)
|
||||
|
||||
val icon = IconCompat.createWithAdaptiveBitmap(outBmp)
|
||||
|
||||
val person = Person.Builder()
|
||||
.setIcon(icon)
|
||||
.setName(account.displayName)
|
||||
.setKey(account.identifier)
|
||||
.build()
|
||||
|
||||
// This intent will be sent when the user clicks on one of the launcher shortcuts. Intent from share sheet will be different
|
||||
val intent = MainActivityIntent(context).apply {
|
||||
action = Intent.ACTION_SEND
|
||||
type = "text/plain"
|
||||
putExtra(ShortcutManagerCompat.EXTRA_SHORTCUT_ID, account.id.toString())
|
||||
}
|
||||
} catch (e: ExecutionException) {
|
||||
// The `.error` handler isn't always used. For example, Glide throws
|
||||
// ExecutionException if the URL does not point at an image. Fallback to
|
||||
// the default avatar (https://github.com/bumptech/glide/issues/4672).
|
||||
Glide.with(context)
|
||||
.asBitmap()
|
||||
.load(DR.drawable.avatar_default)
|
||||
.submit(innerSize, innerSize)
|
||||
.get()
|
||||
|
||||
ShortcutInfoCompat.Builder(context, account.id.toString())
|
||||
.setIntent(intent)
|
||||
.setCategories(setOf("app.pachli.Share"))
|
||||
.setShortLabel(account.displayName)
|
||||
.setPerson(person)
|
||||
.setLongLived(true)
|
||||
.setIcon(icon)
|
||||
.build()
|
||||
}
|
||||
|
||||
// inset the loaded bitmap inside a 108dp transparent canvas so it looks good as adaptive icon
|
||||
val outBmp = Bitmap.createBitmap(outerSize, outerSize, Bitmap.Config.ARGB_8888)
|
||||
|
||||
val canvas = Canvas(outBmp)
|
||||
canvas.drawBitmap(bmp, (outerSize - innerSize).toFloat() / 2f, (outerSize - innerSize).toFloat() / 2f, null)
|
||||
|
||||
val icon = IconCompat.createWithAdaptiveBitmap(outBmp)
|
||||
|
||||
val person = Person.Builder()
|
||||
.setIcon(icon)
|
||||
.setName(account.displayName)
|
||||
.setKey(account.identifier)
|
||||
.build()
|
||||
|
||||
// This intent will be sent when the user clicks on one of the launcher shortcuts. Intent from share sheet will be different
|
||||
val intent = MainActivityIntent(context).apply {
|
||||
action = Intent.ACTION_SEND
|
||||
type = "text/plain"
|
||||
putExtra(ShortcutManagerCompat.EXTRA_SHORTCUT_ID, account.id.toString())
|
||||
}
|
||||
|
||||
val shortcutInfo = ShortcutInfoCompat.Builder(context, account.id.toString())
|
||||
.setIntent(intent)
|
||||
.setCategories(setOf("app.pachli.Share"))
|
||||
.setShortLabel(account.displayName)
|
||||
.setPerson(person)
|
||||
.setLongLived(true)
|
||||
.setIcon(icon)
|
||||
.build()
|
||||
|
||||
ShortcutManagerCompat.addDynamicShortcuts(context, listOf(shortcutInfo))
|
||||
ShortcutManagerCompat.setDynamicShortcuts(context, shortcuts)
|
||||
}
|
||||
|
||||
fun removeShortcut(context: Context, account: AccountEntity) {
|
||||
|
|
Loading…
Reference in New Issue