never create more than the allowed number of shortcuts (#4389)

The only crash so far in the 25.0-beta1 crash reports. Probably not a
regression though as that code did not change in a while.

```
Exception java.lang.IllegalArgumentException: Max number of dynamic shortcuts exceeded
  at android.os.Parcel.createExceptionOrNull (Parcel.java:3032)
  at android.os.Parcel.createException (Parcel.java:3012)
  at android.os.Parcel.readException (Parcel.java:2995)
  at android.os.Parcel.readException (Parcel.java:2937)
  at android.content.pm.IShortcutService$Stub$Proxy.addDynamicShortcuts (IShortcutService.java:618)
  at android.content.pm.ShortcutManager.addDynamicShortcuts (ShortcutManager.java:240)
  at androidx.core.content.pm.ShortcutManagerCompat.addDynamicShortcuts (ShortcutManagerCompat.java:334)
  at com.keylesspalace.tusky.util.ShareShortcutHelper$updateShortcut$1.invokeSuspend (ShareShortcutHelper.kt:96)
  at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33)
  at kotlinx.coroutines.DispatchedTask.run (DispatchedTask.kt:104)
  at android.os.Handler.handleCallback (Handler.java:984)
  at android.os.Handler.dispatchMessage (Handler.java:104)
  at android.os.Looper.loopOnce (Looper.java:238)
  at android.os.Looper.loop (Looper.java:357)
  at android.app.ActivityThread.main (ActivityThread.java:8094)
  at java.lang.reflect.Method.invoke
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:548)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:957)
Caused by android.os.RemoteException: Remote stack trace:
  at com.android.server.pm.ShortcutService.enforceMaxActivityShortcuts (ShortcutService.java:1768)
  at com.android.server.pm.ShortcutPackage.enforceShortcutCountsBeforeOperation (ShortcutPackage.java:1551)
  at com.android.server.pm.ShortcutService.addDynamicShortcuts (ShortcutService.java:2161)
  at android.content.pm.IShortcutService$Stub.onTransact (IShortcutService.java:281)
  at android.os.Binder.execTransactInternal (Binder.java:1294)
```
This commit is contained in:
Konrad Pozniak 2024-04-25 17:08:46 +02:00 committed by GitHub
parent fe7103f2b9
commit f2ffba1679
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 48 additions and 47 deletions

View File

@ -1066,7 +1066,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
} }
updateProfiles() updateProfiles()
shareShortcutHelper.updateShortcut(accountManager.activeAccount!!) shareShortcutHelper.updateShortcuts()
} }
@SuppressLint("CheckResult") @SuppressLint("CheckResult")

View File

@ -21,7 +21,6 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.Canvas import android.graphics.Canvas
import android.text.TextUtils
import androidx.core.app.Person import androidx.core.app.Person
import androidx.core.content.pm.ShortcutInfoCompat import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.content.pm.ShortcutManagerCompat
@ -30,70 +29,72 @@ import com.bumptech.glide.Glide
import com.keylesspalace.tusky.MainActivity import com.keylesspalace.tusky.MainActivity
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.db.AccountEntity import com.keylesspalace.tusky.db.AccountEntity
import com.keylesspalace.tusky.db.AccountManager
import com.keylesspalace.tusky.di.ApplicationScope import com.keylesspalace.tusky.di.ApplicationScope
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class ShareShortcutHelper @Inject constructor( class ShareShortcutHelper @Inject constructor(
private val context: Context, private val context: Context,
private val accountManager: AccountManager,
@ApplicationScope private val externalScope: CoroutineScope @ApplicationScope private val externalScope: CoroutineScope
) { ) {
fun updateShortcut(account: AccountEntity) { fun updateShortcuts() {
externalScope.launch { externalScope.launch(Dispatchers.IO) {
val innerSize = context.resources.getDimensionPixelSize(R.dimen.adaptive_bitmap_inner_size) val innerSize = context.resources.getDimensionPixelSize(R.dimen.adaptive_bitmap_inner_size)
val outerSize = context.resources.getDimensionPixelSize(R.dimen.adaptive_bitmap_outer_size) val outerSize = context.resources.getDimensionPixelSize(R.dimen.adaptive_bitmap_outer_size)
val bmp = if (TextUtils.isEmpty(account.profilePictureUrl)) { val maxNumberOfShortcuts = ShortcutManagerCompat.getMaxShortcutCountPerActivity(context)
Glide.with(context)
.asBitmap() val shortcuts = accountManager.accounts.take(maxNumberOfShortcuts).map { account ->
.load(R.drawable.avatar_default)
.submitAsync(innerSize, innerSize) val bmp = Glide.with(context)
} else {
Glide.with(context)
.asBitmap() .asBitmap()
.load(account.profilePictureUrl) .load(account.profilePictureUrl)
.placeholder(R.drawable.avatar_default)
.error(R.drawable.avatar_default) .error(R.drawable.avatar_default)
.submitAsync(innerSize, innerSize) .submitAsync(innerSize, innerSize)
// 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 = Intent(context, MainActivity::class.java).apply {
action = Intent.ACTION_SEND
type = "text/plain"
putExtra(ShortcutManagerCompat.EXTRA_SHORTCUT_ID, account.id.toString())
}
ShortcutInfoCompat.Builder(context, account.id.toString())
.setIntent(intent)
.setCategories(setOf("com.keylesspalace.tusky.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 ShortcutManagerCompat.addDynamicShortcuts(context, shortcuts)
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 = Intent(context, MainActivity::class.java).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("com.keylesspalace.tusky.Share"))
.setShortLabel(account.displayName)
.setPerson(person)
.setLongLived(true)
.setIcon(icon)
.build()
ShortcutManagerCompat.addDynamicShortcuts(context, listOf(shortcutInfo))
} }
} }