diff options
| author | Harsh Shandilya <me@msfjarvis.dev> | 2021-04-21 18:07:35 +0530 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-04-21 18:07:35 +0530 |
| commit | 6ff01f5e1ee90c6203bc9bd0189eea33c42d6db0 (patch) | |
| tree | ab6300f076404f202b4e4fd378c8c8fd70b03957 /app/src/main/java/dev/msfjarvis/aps/util | |
| parent | 53c3431ef0d1d5cacadf2ae0d540a6b8189029d4 (diff) | |
| download | HardenedPass-6ff01f5e1ee90c6203bc9bd0189eea33c42d6db0.tar.gz HardenedPass-6ff01f5e1ee90c6203bc9bd0189eea33c42d6db0.zip | |
Refactor app shortcut handling (#1392)
Diffstat (limited to 'app/src/main/java/dev/msfjarvis/aps/util')
| -rw-r--r-- | app/src/main/java/dev/msfjarvis/aps/util/shortcuts/ShortcutHandler.kt | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/app/src/main/java/dev/msfjarvis/aps/util/shortcuts/ShortcutHandler.kt b/app/src/main/java/dev/msfjarvis/aps/util/shortcuts/ShortcutHandler.kt new file mode 100644 index 00000000..8e36c9af --- /dev/null +++ b/app/src/main/java/dev/msfjarvis/aps/util/shortcuts/ShortcutHandler.kt @@ -0,0 +1,106 @@ +/* + * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. + * SPDX-License-Identifier: GPL-3.0-only + */ + +package dev.msfjarvis.aps.util.shortcuts + +import android.content.Context +import android.content.Intent +import android.content.pm.ShortcutInfo +import android.content.pm.ShortcutManager +import android.graphics.drawable.Icon +import android.os.Build +import androidx.annotation.RequiresApi +import androidx.core.content.getSystemService +import com.github.ajalt.timberkt.d +import dagger.Reusable +import dagger.hilt.android.qualifiers.ApplicationContext +import dev.msfjarvis.aps.R +import dev.msfjarvis.aps.data.password.PasswordItem +import javax.inject.Inject + +@Reusable +class ShortcutHandler +@Inject +constructor( + @ApplicationContext val context: Context, +) { + + private companion object { + + // The max shortcut count from the system is set to 15 for some godforsaken reason, which + // makes zero sense and is why our update logic just never worked. Capping it at 4 which is + // what most launchers seem to have agreed upon is the only reasonable solution. + private const val MAX_SHORTCUT_COUNT = 4 + } + + /** + * Creates a + * [dynamic shortcut](https://developer.android.com/guide/topics/ui/shortcuts/creating-shortcuts#dynamic) + * that shows up with the app icon on long press. The list of items is capped to + * [MAX_SHORTCUT_COUNT] and older items are removed by a simple LRU sweep. + */ + fun addDynamicShortcut(item: PasswordItem, intent: Intent) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) return + val shortcutManager: ShortcutManager = context.getSystemService() ?: return + val shortcut = buildShortcut(item, intent) + val shortcuts = shortcutManager.dynamicShortcuts + // If we're above or equal to the maximum shortcuts allowed, drop the last item. + if (shortcuts.size >= MAX_SHORTCUT_COUNT) { + shortcuts.removeLast() + } + // Reverse the list so we can append our new shortcut at the 'end'. + shortcuts.reverse() + shortcuts.add(shortcut) + // Reverse it again, so the previous items are now in the correct order and our new item + // is at the front like it's supposed to. + shortcuts.reverse() + // Write back the new shortcuts. + shortcutManager.dynamicShortcuts = shortcuts.map(::rebuildShortcut) + } + + /** + * Creates a + * [pinned shortcut](https://developer.android.com/guide/topics/ui/shortcuts/creating-shortcuts#pinned) + * which presents a UI to users, allowing manual placement on the launcher screen. This method is + * a no-op if the user's default launcher does not support pinned shortcuts. + */ + fun addPinnedShortcut(item: PasswordItem, intent: Intent) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return + val shortcutManager: ShortcutManager = context.getSystemService() ?: return + if (!shortcutManager.isRequestPinShortcutSupported) { + d { "addPinnedShortcut: pin shortcuts unsupported" } + return + } + val shortcut = buildShortcut(item, intent) + shortcutManager.requestPinShortcut(shortcut, null) + } + + /** Creates a [ShortcutInfo] from [item] and assigns [intent] to it. */ + @RequiresApi(Build.VERSION_CODES.N_MR1) + private fun buildShortcut(item: PasswordItem, intent: Intent): ShortcutInfo { + return ShortcutInfo.Builder(context, item.fullPathToParent) + .setShortLabel(item.toString()) + .setLongLabel(item.fullPathToParent + item.toString()) + .setIcon(Icon.createWithResource(context, R.drawable.ic_lock_open_24px)) + .setIntent(intent) + .build() + } + + /** + * Takes an existing [ShortcutInfo] and builds a fresh instance of [ShortcutInfo] with the same + * data, which ensures that the get/set dance in [addDynamicShortcut] does not cause invalidation + * of icon assets, resulting in invisible icons in all but the newest launcher shortcut. + */ + @RequiresApi(Build.VERSION_CODES.N_MR1) + private fun rebuildShortcut(shortcut: ShortcutInfo): ShortcutInfo { + // Non-null assertions are fine since we know these values aren't null. + return ShortcutInfo.Builder(context, shortcut.id) + .setShortLabel(shortcut.shortLabel!!) + .setLongLabel(shortcut.longLabel!!) + .setIcon(Icon.createWithResource(context, R.drawable.ic_lock_open_24px)) + .setIntent(shortcut.intent!!) + .build() + } +} |
