Android适配 - 桌面快捷方式

谷歌官方在Android 7.1(25)新增了桌面长按弹出菜单,并且在8.0(26)以后可以固定快捷方式至桌面上。围绕桌面快捷方式的需求也比较多,例如微信将联系人、小程序都可以添加至桌面;简书将“写文章”添加至桌面;高德将“坐标信息”添加到桌面。

相关Sample代码链接 https://github.com/scauzhangpeng/Shortcut
权限

谷歌原生系统、三星系统不需要用户主动打开权限,在AndroidManifest声明即可
华为、小米、OPPO、vivo、魅族需要“创建桌面快捷方式”权限

由于各厂家ROM的Launcher改动并且原生也不需要申请权限,所以:

  • 无法检测用户是否开启了“创建桌面快捷方式”权限(已有方法检查,可以检查华为、小米、OPPO、VIVO,目前手上测试机都测试通过,后面会给出机型和版本列表,360手机助手这个APP可以有提示权限未开启。根据http://www.lefo.me/2017/05/19/shortcut-permission/>的思路已经整理好相关代码)
  • 无法按照运行时权限一样申请“创建桌面快捷方式”权限
  • 魅族:系统自动弹窗询问是否可以添加桌面快捷方式

借鉴各大APP的做法,大致上都是弹窗文案指引,并且可以跳转至系统权限设置:

微信支付宝简书
微信在华为手机上没有“前往设置”支付宝都没有做权限设置跳转华为跳转到具体的“设置单项权限”界面
需要华为接口权限,因此只能跳转如下界面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WItqF4BH-1579749312003)(https://s2.ax1x.com/2019/06/02/V8W2Yd.png =150x)] 微信小程序-OPPO[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RfGtCtNT-1579749312005)(https://s2.ax1x.com/2019/06/02/V8W6Te.png =150x)]简书-写文章-华为 简书-写文章-华为
OPPO 8.1.0
ColorOs 5.2.1
小米 8.1.0
MIUI 9.5
华为 8.1.0
EMUI 8.1.0

跳转至系统权限设置,在不同厂商机型表现得略有不同(OPPO和vivo只有路径二和三)

  • 路径一:设置 - 应用列表 - XX应用 - 权限 - 创建桌面快捷方式
  • 路径二:设置 - 权限列表 - 创建桌面快捷方式 - xx应用
  • 路径三:自带手机管家 - 权限列表 - 创建桌面快捷方式 - xx应用
图标

自定义图标以及形状(图片bitmap不能太大,不然会超过binder最大数据量显示导致异常)

  • 圆形图片:部分机型(三星、vivo、小米)会带一个圆角白色的背景,导致非常丑
  • 圆角图片:如果图片不会过大,正常的Launcher的Icon标准是可以的
  • 正方形图片(推荐):让系统Launcher自动适配裁剪(三星手机部分版本会存在创建的快捷方式图标偏大,猜测由于正常App图标加了Padding,推荐直接根据桌面图标进行合成裁剪)

系统自动添加应用Icon在快捷方式图标的右下角

  • 8.0以上系统自动,并且无法设置不添加
  • 8.0以前可以代码设置是否添加,@see ShortcutInfoCompat.Builder#setAlwaysBadged()

图标的删除方式

  • 用户手动清除应用数据会一并清除所有的桌面快捷方式(仅支持8.0以上)
  • 用户手动删除桌面快捷方式
  • 用户卸载应用
唯一性

在Android 8.0(26)以下,创建快捷方式的方法是发广播给Launcher,无法对快捷方式进行管理;在Android 8.0以上才有相应的API支持管理桌面快捷方式。

是否创建成功

  • 8.0以下采用发广播给Launcher,是否创建成功无法知道或者说回调广播不准确
  • 8.0以上采用系统API,可以同步返回操作是否成功,然后成功后异步广播回调。

根据ID去重还是根据名称去重

  • 8.0以下都是厂商的Launcher的做法,去重依据是根据名称是否一致,去重的效果可能是:
OPPO华为
无提示,不更新有提示已存在,不更新
  • 8.0以上可以根据ID进行判断是否存在,然后再进行创建或者更新操作
    @see ShortcutManager#getPinnedShortcuts()
    @see ShortcutInfo#getId()

  • 华为有点特殊,8.0以上(8.0以及8.1的bug,9.0已经正常)按照ID + 名称去重,不允许同名称的桌面快捷方式即使ID不一致(微信将两个联系人改同样的备注名,然后添加到桌面,即使ID不一致也会提示重复)(目前的做法是检测到重名的,随机生成一个UUID,然后创建成功后再进行更新原名称操作)

源码分析

Support包下的兼容类:ShortcutManagerCompat#requestPinShortcut()

    /**
     * Request to create a pinned shortcut.
     * <p>On API <= 25 it creates a legacy shortcut with the provided icon, label and intent. For
     * newer APIs it will create a {@link android.content.pm.ShortcutInfo} object which can be
     * updated by the app.
     *
     * <p>Use {@link android.app.PendingIntent#getIntentSender()} to create a {@link IntentSender}.
     *
     * @param shortcut new shortcut to pin
     * @param callback if not null, this intent will be sent when the shortcut is pinned
     *
     * @return {@code true} if the launcher supports this feature
     *
     * @see #isRequestPinShortcutSupported
     * @see IntentSender
     * @see android.app.PendingIntent#getIntentSender()
     */
    public static boolean requestPinShortcut(@NonNull final Context context,
            @NonNull ShortcutInfoCompat shortcut, @Nullable final IntentSender callback) {
        if (Build.VERSION.SDK_INT >= 26) {
            return context.getSystemService(ShortcutManager.class).requestPinShortcut(
                    shortcut.toShortcutInfo(), callback);
        }

        if (!isRequestPinShortcutSupported(context)) {
            return false;
        }
        Intent intent = shortcut.addToIntent(new Intent(ACTION_INSTALL_SHORTCUT));

        // If the callback is null, just send the broadcast
        if (callback == null) {
            context.sendBroadcast(intent);
            return true;
        }

        // Otherwise send the callback when the intent has successfully been dispatched.
        context.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                try {
                    callback.sendIntent(context, 0, null, null, null);
                } catch (IntentSender.SendIntentException e) {
                    // Ignore
                }
            }
        }, null, Activity.RESULT_OK, null, null);
        return true;
    }

源码简洁并配以简单易懂的注释,显得十分优雅。

  1. 8.0以上调用系统API,返回结果;
  2. 8.0以下判断当前系统Launcher是否支持创建固定桌面快捷方式
  3. 当前支持创建固定桌面快捷方式,发送广播;

分析2,源码中如何判断Launcher是否支持创建固定桌面快捷方式

    /**
     * @return {@code true} if the launcher supports {@link #requestPinShortcut},
     * {@code false} otherwise
     */
    public static boolean isRequestPinShortcutSupported(@NonNull Context context) {
        if (Build.VERSION.SDK_INT >= 26) {
            return context.getSystemService(ShortcutManager.class).isRequestPinShortcutSupported();
        }

        if (ContextCompat.checkSelfPermission(context, INSTALL_SHORTCUT_PERMISSION)
                != PackageManager.PERMISSION_GRANTED) {
            return false;
        }
        for (ResolveInfo info : context.getPackageManager().queryBroadcastReceivers(
                new Intent(ACTION_INSTALL_SHORTCUT), 0)) {
            String permission = info.activityInfo.permission;
            if (TextUtils.isEmpty(permission) || INSTALL_SHORTCUT_PERMISSION.equals(permission)) {
                return true;
            }
        }
        return false;
    }

这个检测相当严谨,本人有点想将源码复制出来,去掉这个检测,因为发送广播的方式也不会影响程序运行,只管尝试发广播即可。

  1. 8.0以上根据新API进行检测
  2. 8.0以下检测是否有注册权限,如果没有则返回fasle
  3. 检测是否有Launcher接收ACTION_INSTALL_SHORTCUT这个Action的广播,如果有再判断一次它是否注册了权限

分析3,发送广播之前重新构造了Intent,Intent intent = shortcut.addToIntent(new Intent(ACTION_INSTALL_SHORTCUT));

    @VisibleForTesting
    Intent addToIntent(Intent outIntent) {
        outIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, mIntents[mIntents.length - 1])
                .putExtra(Intent.EXTRA_SHORTCUT_NAME, mLabel.toString());
        if (mIcon != null) {
            Drawable badge = null;
            if (mIsAlwaysBadged) {
                PackageManager pm = mContext.getPackageManager();
                if (mActivity != null) {
                    try {
                        badge = pm.getActivityIcon(mActivity);
                    } catch (PackageManager.NameNotFoundException e) {
                        // Ignore
                    }
                }
                if (badge == null) {
                    badge = mContext.getApplicationInfo().loadIcon(pm);
                }
            }
            mIcon.addToShortcutIntent(outIntent, badge);
        }
        return outIntent;
    }

源码也就是将Intent的各种参数设置进去,然后再返回即可

  • action :@see Intent#ACTION_INSTALL_SHORTCUT
  • Intent.EXTRA_SHORTCUT_INTENT :快捷方式启动的Intent
  • Intent.EXTRA_SHORTCUT_NAME :快捷方式的名称
  • 如果设置 @see ShortcutInfoCompat.Builder#setAlwaysBadged()则将Activity组件的Icon或者APP的Icon和给定的图画在一起,Badged在右下角,@see IconCompat#addToShortcutIntent()
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Android适配是指将应用程序在不同的Android设备上进行优化和调整,以确保应用能够在各种屏幕尺寸、分辨率和设备类型上正常运行。适配涉及到以下几个方面: 1. 布局适配:根据不同屏幕尺寸和分辨率调整布局,保证界面元素的合理排列和显示效果。可以使用ConstraintLayout、LinearLayout等布局管理器来实现自适应布局。 2. 图片适配:提供不同分辨率的图片资源,并根据设备的屏幕密度加载合适的图片。可以通过资源文件夹的命名方式(如drawable-hdpi、drawable-xhdpi等)来管理不同分辨率的图片。 3. 字体适配:根据设备的屏幕密度和用户设置的字体大小,调整应用中的文字大小和样式。可以使用sp(可缩放像素)作为字体大小的单位,以支持用户自定义字体大小。 4. 功能适配:根据设备的硬件特性和系统版本,判断是否支持某些功能或API,并进行相应的处理。可以使用特定的API或兼容性库来实现功能适配。 5. 版本适配:针对不同的Android版本,处理其特有的行为和API差异。可以使用条件判断、动态加载类等方式来处理不同版本的兼容性问题。 6. 设备适配:根据设备的硬件特性和屏幕尺寸,调整应用的功能和界面。可以使用资源限定符、特定的API等来实现设备适配。 以上是Android适配的一些常见方面,具体的适配策略和方法,可以根据应用的需求和目标设备的特点进行选择和实施。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值