最近公司有个需求,如何让用户快速使用App里面的某一个小功能,很显然创建桌面快捷很符合这个需求。效果页面可以参考微信或者支付宝的小程序添加到桌面快捷。
由于android7.1以上出了新的创建快捷方式,使用新的api ShortcutManager管理一个应用程序的快捷方式,ShortcutManager支持两种创建快捷方式,分别为:静态快捷方式和动态快捷方式。只要长按APP图标就会弹出快捷方式,通过点击快捷键,用户可以快速访问任意一个Activity。长按拖动快捷方式还可以生成桌面快捷(注:这种生成桌面快捷是不需要用户手动开启创建桌面快捷方式权限)。
android8.0在android7.1 ShortcutManager原有的静态快捷方式,动态快捷方式的基础上增加了固定快捷方式。
android7.1以后的长按APP图标效果:
废话不多说,直接上代码:
首先添加权限,在AndroidManiFest.xml里面添加下面权限:
为了匹配各大手机的快捷方式还需添加以下的权限,从名字也可以看出是各大手机的权限:
1、android7.1以下的老式创建快捷方式,相信做过快捷方式的都有用过。
/*android7.1之前,生成快捷方式*/
private static void createShortCut(Context context, String actionType) {
//创建快捷方式的Intent
Intent intent = new Intent("com.android.launcher.action.INSTALL_SHORTCUT");
// 不允许重复创建,不是根据快捷方式的名字判断重复的
intent.putExtra("duplicate", false);
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, context.getString(R.string.openDoor_oneKey));
Parcelable icon = Intent.ShortcutIconResource.fromContext(context.getApplicationContext(), R.mipmap.onekey);
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, icon);
//这种方式只能进入指定的activity,在桌面长按时,只会出现删除一个选项
Intent inte = new Intent(Intent.ACTION_MAIN, Uri.EMPTY, context.getApplicationContext(), ShortCutActivity.class);
inte.putExtra(ShortcutsReciever.ACTION_NAME, actionType);
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, inte);
context.sendBroadcast(intent);
}
经测试部分华为手机需要手动开启创建桌面快捷方式权限才能创建成功,而且无法在app里面去动态申请这个权限。
参考微信支付宝的权限提示,自己也可以根据需求做自己的权限提示。
点击了解详情进入
2、android7.1和以上版本 的ShortcutManager使用静态快捷方式和动态快捷方式创建桌面快捷方式。这种方式创建快捷的好处就是不需要用户手动去打开创建桌面快捷方式的权限。只要长按APP图标就会弹出快捷方式,通过点击快捷键,用户可以快速访问任意一个Activity。长按拖动弹出的快捷方式图标还可以在桌面生成老式的桌面快捷方式。
1)动态创建
if (Build.VERSION.SDK_INT >= 25) {//api25 7.1以上系统使用新的快捷方式,静态快捷方式,动态快捷方式,
ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class);
//点击快捷方式跳转页面ShortCutActivity.class
Intent inte = new Intent(Intent.ACTION_MAIN, Uri.EMPTY, context.getApplicationContext(), ShortCutActivity.class);
inte.putExtra(ShortcutsReciever.ACTION_NAME, actionType);
inte.setAction(Intent.ACTION_VIEW);
//设置创建快捷方式的名称和图标
ShortcutInfo info = new ShortcutInfo.Builder(context, mPinShortcutId)
.setIcon(Icon.createWithResource(context, R.mipmap.onekey))
.setShortLabel(context.getString(R.string.openDoor_oneKey))
.setIntent(inte)
.build();
//动态快捷方式,可以通过长按图标显示出快捷方式了
shortcutManager.setDynamicShortcuts(Arrays.asList(info));
}
2)静态创建快捷方式
静态方式就是通过xml文件进行配置,先配置快捷方式的选项:
res/xml/static_shortcuts.xml 主要配置如下参数:id、图标、标题、意图,还有分类,是否可用。
配置完图标的之后,再打开AndroidManiFest.xml配置启动的Activity
AndroidManiFest.xml
操作:长按拖动弹出的快捷方式图标在桌面生成老式的桌面快捷方式。
生成的桌面快捷方式
3、android8.0创建固定快捷方式,其创建的静态快捷方式,动态快捷方式代码跟7.1一样。固定快捷方式其实就是老式的创建快捷方式的效果,会直接在桌面生成一个桌面快捷方式。当然也需要用户手动开启创建桌面快捷方式权限才能创建成功。
//点击快捷方式跳转页面ShortCutActivity.class
Intent inte = new Intent(Intent.ACTION_MAIN, Uri.EMPTY, context.getApplicationContext(), ShortCutActivity.class);
inte.putExtra(ShortcutsReciever.ACTION_NAME, actionType);
inte.setAction(Intent.ACTION_VIEW);
//设置创建快捷方式的名称和图标
ShortcutInfo info = new ShortcutInfo.Builder(context, mPinShortcutId)
.setIcon(Icon.createWithResource(context, R.mipmap.onekey))
.setShortLabel(context.getString(R.string.openDoor_oneKey))
.setIntent(inte)
.build();
//8.0固定快捷方式
if (Build.VERSION.SDK_INT >= 26 && shortcutManager.isRequestPinShortcutSupported()) {
//8.0后新增了使用固定快捷方式,先判断手机是否支持固定快捷方式
Intent reIn = new Intent(context, ShortcutsReciever.class);
reIn.setAction(ShortcutsReciever.ACTION_0E);
PendingIntent shortcutCallbackIntent = PendingIntent.getBroadcast(context, 0, reIn, PendingIntent.FLAG_UPDATE_CURRENT);
shortcutManager.requestPinShortcut(info, shortcutCallbackIntent.getIntentSender());
}
这里要创建一个广播监听快捷方式是否创建成功
public class ShortcutsReciever extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
LogUtils.v("ShortcutsReciever---", "onReceive : " + intent.getAction());
}
}
4、至此创建方式已经完成了,有时候我们可能还需要判断一下桌面是否创建了快捷方式,才好去判断是否弹出提示用户创建快捷方式的弹框。源码如下:7.1以上的好判断,就获取创建快捷方式的所有ShortcutInfo,然后判断其ID是否存在自己创建的快捷方式的id。7.1以下的就要做各种手机和安卓版本的兼容了。
/**
* 是否创建了快捷方式
*
* @return
*/
public static boolean hasShortcut(Context context) {
if (Build.VERSION.SDK_INT >= 25) {
ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class);
List infos = shortcutManager.getPinnedShortcuts();
for (int i = 0; i < infos.size(); i++) {
ShortcutInfo info = infos.get(i);
if (info.getId().equals(mPinShortcutId)) {
return true;
}
}
} else {
return isShortCutExist(context, context.getString(R.string.openDoor_oneKey));
}
return false;
}
public static boolean isShortCutExist(Context context, String appName) {
boolean isInstallShortcut = false;
if (null == context || TextUtils.isEmpty(appName))
return isInstallShortcut;
String AUTHORITY = getAuthority(context);
LogUtils.i("ShortCutUtil", "AUTHORITY = " + AUTHORITY);
final ContentResolver cr = context.getContentResolver();
if (!TextUtils.isEmpty(AUTHORITY)) {
try {
final Uri CONTENT_URI = Uri.parse(AUTHORITY);
Cursor c = cr.query(CONTENT_URI, new String[]{"title", "iconResource"}, "title=?", new String[]{appName}, null);
if (c != null && c.getCount() > 0) {
isInstallShortcut = true;
}
if (null != c && !c.isClosed())
c.close();
} catch (Exception e) {
// TODO: handle exception
}
}
return isInstallShortcut;
}
public static String getAuthority(Context context) {
// 获取默认
String authority = getAuthorityFromPermissionDefault(context);
LogUtils.i("ShortCutUtil", "获取默认 AUTHORITY = " + authority);
// 获取特殊第三方
if (authority == null || authority.trim().equals("")) {
String packageName = getCurrentLauncherPackageName(context);
packageName += ".permission.READ_SETTINGS";
authority = getThirdAuthorityFromPermission(context, packageName);
}
LogUtils.i("ShortCutUtil", "获取特殊第三方 AUTHORITY = " + authority);
// 还是获取不到,直接写死
if (TextUtils.isEmpty(authority)) {
int sdkInt = android.os.Build.VERSION.SDK_INT;
if (sdkInt < 8) { // Android 2.1.x(API 7)以及以下的
authority = "com.android.launcher.settings";
} else if (sdkInt < 19) {// Android 4.4以下
authority = "com.android.launcher2.settings";
} else {// 4.4以及以上
authority = "com.android.launcher3.settings";
}
}
LogUtils.i("ShortCutUtil", "写死 AUTHORITY = " + authority);
authority = "content://" + authority + "/favorites?notify=true";
return authority;
}
public static String getAuthorityFromPermissionDefault(Context context) {
return getThirdAuthorityFromPermission(context, "com.android.launcher.permission.READ_SETTINGS");
}
public static String getThirdAuthorityFromPermission(Context context, String permission) {
if (TextUtils.isEmpty(permission)) {
return "";
}
try {
List packs = context.getPackageManager().getInstalledPackages(PackageManager.GET_PROVIDERS);
if (packs == null) {
return "";
}
for (PackageInfo pack : packs) {
ProviderInfo[] providers = pack.providers;
if (providers != null) {
for (ProviderInfo provider : providers) {
if (permission.equals(provider.readPermission) || permission.equals(provider.writePermission)) {
if (!TextUtils.isEmpty(provider.authority)// 精准匹配launcher.settings,再一次验证
&& (provider.authority).contains(".launcher.settings"))
return provider.authority;
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
public static String getCurrentLauncherPackageName(Context context) {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
ResolveInfo res = context.getPackageManager().resolveActivity(intent, 0);
if (res == null || res.activityInfo == null) {
// should not happen. A home is always installed, isn't it?
return "";
}
if (res.activityInfo.packageName.equals("android")) {
return "";
} else {
return res.activityInfo.packageName;
}
}
shortcuts扩展:
不管是静态形式还是动态形式,每个应用最多可以注册4个Shortcuts。
显示的顺序问题:添加在前的显示在下方,后面添加的显示在上面。
移除快捷方式
void removeDynamicShortcuts(@NonNull List shortcutIds);
移除快捷方式,并且拖到launcher上的图标会变灰,这个方法底层最终调用的方法跟removeDynamicShortcuts一样
void disableShortcuts(@NonNull List shortcutIds);
使拖到launcher的图标可用。 静态添加的快捷方式不能使用该方法,使用会报错
void enableShortcuts(@NonNull List shortcutIds);
更新快捷方式,例如图标、标题
boolean updateShortcuts(List shortcutInfoList);
参考文章:
https://www.jianshu.com/p/184db7c259ec
https://blog.csdn.net/zhanggang740/article/details/78554031