lawnchair feat: add open in store shortcut (#6461)
需求简介 需求很简单,就是你长按一个app,然后如果它不是系统应用,有来源,就显示来源,然可以跳转到对应的来源去
需求拆解 可以拆成几个部分,就是长按这个app,filter,跳转intent
然后长按这里
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 kotlin override fun getSupportedShortcuts(container: Int): Stream<SystemShortcut.Factory<*>> = Stream.concat( super.getSupportedShortcuts(container), // AOSP 原有的快捷方式(App Info 等) Stream.concat( Stream.of(LawnchairShortcut.UNINSTALL, LawnchairShortcut.CUSTOMIZE, LawnchairShortcut.OPEN_IN_STORE), if (LawnchairApp.isRecentsEnabled) Stream.of(LawnchairShortcut.PAUSE_APPS) else Stream.empty(), ), ) Stream.concat 就是把多个流拼接成一个流。这里做的事情是: 最终的快捷方式列表 = AOSP 默认的 + Lawnchair 自己加的 ├── UNINSTALL(卸载) ├── CUSTOMIZE(自定义图标) ├── OPEN_IN_STORE(在商店打开)← 新加的 └── PAUSE_APPS(暂停应用,仅 root 有)
这里的流说实话没太懂,后面补习一下吧。TODO:补习流 然后具体来实现,就是你这个shortcutMenu需要添加一个类似于其他实现的自定义的一个东西 这里的工厂上一个Single Abstract Method接口,这里就kotlin语法糖,直接Factory{lambda}可以实现
1 2 3 4 public static class Factory <T extends ActivityContext > { public SystemShortcut<T> getShortcut (T activity, ItemInfo itemInfo, View originalView) ; }
具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 val OPEN_IN_STORE = SystemShortcut.Factory { activity: ActivityContext, itemInfo: ItemInfo, originalView: View -> if (itemInfo.itemType != ITEM_TYPE_APPLICATION) return @Factory null val packageName = itemInfo.targetComponent?.packageName ?: return @Factory null val context = activity.asContext() val installer = PackageManagerHelper.INSTANCE.get (context) .getAppInstallerPackage(packageName) ?: return @Factory null if (installer == "com.google.android.packageinstaller" || installer == "com.android.packageinstaller" ) { return @Factory null } OpenInStore(activity, itemInfo, originalView, packageName, installer) }
然后这里就是看是否合法,不合法只退出factory这层,kotlin的return会直接退出函数,如果不指定层级的话。 然后这里实际上就是希望通过系统的getAppInstallerPackage方法来拿到最开始install的包名
具体shortcut 需要实现点击和跳转
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class OpenInStore ( target: ActivityContext, itemInfo: ItemInfo, originalView: View, private val packageName: String, private val installerPackage: String, ) : SystemShortcut<ActivityContext>( R.drawable.ic_open_in_store, R.string.open_in_store_drop_target_label, target, itemInfo, originalView, ) { override fun onClick (v: View ) { dismissTaskMenuView() val intent = buildIntent() ?: return mTarget.startActivitySafely(v, intent, mItemInfo) } private fun buildIntent () : Intent? { val uri = when (installerPackage) { "com.android.vending" , "org.gdroid.gdroid" , "com.aurora.store" , -> "market://details?id=$packageName " "org.fdroid.fdroid" -> "https://f-droid.org/packages/$packageName /" "com.github.librecaptcha.apps.fdroidclient" , "com.looker.droidify" , -> "droidify://details?id=$packageName " else -> "market://details?id=$packageName " } return Intent(Intent.ACTION_VIEW, Uri.parse(uri)) } }
直接返回继承SystemShortcut的匿名类也行,只需要重写这俩方法,不写名字也行,不过匿名感觉太丑了,这里符合语义就搞了个类。然后就是非法判断加intent构建,intent就根据不同包名返回需要跳到的位置即可。