package com.Iori.Util; import android.app.Activity; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager; import android.database.Cursor; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Build; import android.os.Parcelable; import android.provider.Settings; import android.text.TextUtils; import static android.content.Context.SHORTCUT_SERVICE; public class InstallUtil { public static void createShortCut(Activity act, int iconResId, int appnameResId) { // com.android.launcher.permission.INSTALL_SHORTCUT // <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/> // <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" /> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { ShortcutManager scm = (ShortcutManager) act.getSystemService(SHORTCUT_SERVICE); String shortcutID = act.getString(appnameResId); for (ShortcutInfo info : scm.getPinnedShortcuts()) { if (shortcutID.equals(info.getId())) { //判断快捷方式是否已存在 return; } } Intent launcherIntent = new Intent(act, act.getClass());//设置网络页面intent launcherIntent.setAction(Intent.ACTION_VIEW); ShortcutInfo si = new ShortcutInfo.Builder(act, shortcutID) .setIcon(Icon.createWithResource(act, iconResId)) .setShortLabel(act.getString(appnameResId)) .setIntent(launcherIntent) .build(); assert scm != null; scm.requestPinShortcut(si, null); return; } Intent shortcutintent = new Intent( "com.android.launcher.action.INSTALL_SHORTCUT"); // 不允许重复创建 shortcutintent.putExtra("duplicate", false); // 需要现实的名称 shortcutintent.putExtra(Intent.EXTRA_SHORTCUT_NAME, act.getString(appnameResId)); // 快捷图片 Parcelable icon = Intent.ShortcutIconResource.fromContext( act.getApplicationContext(), iconResId); shortcutintent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, icon); // 点击快捷图片,运行的程序主入口 shortcutintent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent(act.getApplicationContext(), act.getClass())); // 发送广播 if(isShortCutExist(act.getApplicationContext(),act.getString(appnameResId)) == false) { act.sendBroadcast(shortcutintent); } } /** * 检查快捷方式是否存在 <br/> * <font color=red>注意:</font> 有些手机无法判断是否已经创建过快捷方式<br/> * 因此,在创建快捷方式时,请添加<br/> * shortcutIntent.putExtra("duplicate", false);// 不允许重复创建<br/> * 最好使用{@link #isShortCutExist(Context, String, Intent)} * 进行判断,因为可能有些应用生成的快捷方式名称是一样的的<br/> */ public static boolean isShortCutExist(Context context, String title) { boolean result = false; try { ContentResolver cr = context.getContentResolver(); Uri uri = getUriFromLauncher(context); Cursor c = cr.query(uri, new String[]{"title"}, "title=? ", new String[]{title}, null); if (c != null && c.getCount() > 0) { result = true; } if (c != null && !c.isClosed()) { c.close(); } } catch (Exception e) { result = false; e.printStackTrace(); } return result; } /** * 不一定所有的手机都有效,因为国内大部分手机的桌面不是系统原生的<br/> * 更多请参考{@link #isShortCutExist(Context, String)}<br/> * 桌面有两种,系统桌面(ROM自带)与第三方桌面,一般只考虑系统自带<br/> * 第三方桌面如果没有实现系统响应的方法是无法判断的,比如GO桌面<br/> */ public static boolean isShortCutExist(Context context, String title, Intent intent) { boolean result = false; try { ContentResolver cr = context.getContentResolver(); Uri uri = getUriFromLauncher(context); Cursor c = cr.query(uri, new String[]{"title", "intent"}, "title=? and intent=?", new String[]{title, intent.toUri(0)}, null); if (c != null && c.getCount() > 0) { result = true; } if (c != null && !c.isClosed()) { c.close(); } } catch (Exception ex) { result = false; ex.printStackTrace(); } return result; } private static Uri getUriFromLauncher(Context context) { StringBuilder uriStr = new StringBuilder(); String authority = LauncherUtil.getAuthorityFromPermissionDefault(context); if (authority == null || authority.trim().equals("")) { authority = LauncherUtil.getAuthorityFromPermission(context, LauncherUtil.getCurrentLauncherPackageName(context) + ".permission.READ_SETTINGS"); } //uriStr.append("content://"); if (TextUtils.isEmpty(authority)) { int sdkInt = android.os.Build.VERSION.SDK_INT; if (sdkInt < 8) { // Android 2.1.x(API 7)以及以下的 uriStr.append("com.android.launcher.settings"); } else if (sdkInt < 19) {// Android 4.4以下 uriStr.append("com.android.launcher2.settings"); } else {// 4.4以及以上 uriStr.append("com.android.launcher3.settings"); } } else { uriStr.append(authority); } //uriStr.append("/favorites?notify=true"); return Uri.parse(uriStr.toString()); } }
<!-- 快捷方式相关 --> <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" /> <uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" /> <uses-permission android:name="com.android.launcher2.permission.READ_SETTINGS" /> <uses-permission android:name="com.android.launcher2.permission.WRITE_SETTINGS" /> <uses-permission android:name="com.android.launcher3.permission.READ_SETTINGS" /> <uses-permission android:name="com.android.launcher3.permission.WRITE_SETTINGS" /> <uses-permission android:name="com.google.android.launcher.permission.READ_SETTINGS" /> <uses-permission android:name="com.google.android.launcher.permission.WRITE_SETTINGS" /> <uses-permission android:name="org.adw.launcher.permission.READ_SETTINGS" /> <uses-permission android:name="org.adw.launcher.permission.WRITE_SETTINGS" /> <uses-permission android:name="com.htc.launcher.permission.READ_SETTINGS" /> <uses-permission android:name="com.htc.launcher.permission.WRITE_SETTINGS" /> <uses-permission android:name="com.qihoo360.launcher.permission.READ_SETTINGS" /> <uses-permission android:name="com.qihoo360.launcher.permission.WRITE_SETTINGS" /> <uses-permission android:name="com.lge.launcher.permission.READ_SETTINGS" /> <uses-permission android:name="com.lge.launcher.permission.WRITE_SETTINGS" /> <uses-permission android:name="net.qihoo.launcher.permission.READ_SETTINGS" /> <uses-permission android:name="net.qihoo.launcher.permission.WRITE_SETTINGS" /> <uses-permission android:name="org.adwfreak.launcher.permission.READ_SETTINGS" /> <uses-permission android:name="org.adwfreak.launcher.permission.WRITE_SETTINGS" /> <uses-permission android:name="org.adw.launcher_donut.permission.READ_SETTINGS" /> <uses-permission android:name="org.adw.launcher_donut.permission.WRITE_SETTINGS" /> <uses-permission android:name="com.huawei.launcher3.permission.READ_SETTINGS" /> <uses-permission android:name="com.huawei.launcher3.permission.WRITE_SETTINGS" /> <uses-permission android:name="com.fede.launcher.permission.READ_SETTINGS" /> <uses-permission android:name="com.fede.launcher.permission.WRITE_SETTINGS" /> <uses-permission android:name="com.sec.android.app.twlauncher.settings.READ_SETTINGS" /> <uses-permission android:name="com.sec.android.app.twlauncher.settings.WRITE_SETTINGS" /> <uses-permission android:name="com.anddoes.launcher.permission.READ_SETTINGS" /> <uses-permission android:name="com.anddoes.launcher.permission.WRITE_SETTINGS" /> <uses-permission android:name="com.tencent.qqlauncher.permission.READ_SETTINGS" /> <uses-permission android:name="com.tencent.qqlauncher.permission.WRITE_SETTINGS" /> <uses-permission android:name="com.huawei.launcher2.permission.READ_SETTINGS" /> <uses-permission android:name="com.huawei.launcher2.permission.WRITE_SETTINGS" /> <uses-permission android:name="com.android.mylauncher.permission.READ_SETTINGS" /> <uses-permission android:name="com.android.mylauncher.permission.WRITE_SETTINGS" /> <uses-permission android:name="com.ebproductions.android.launcher.permission.READ_SETTINGS" /> <uses-permission android:name="com.ebproductions.android.launcher.permission.WRITE_SETTINGS" /> <uses-permission android:name="com.oppo.launcher.permission.READ_SETTINGS" /> <uses-permission android:name="com.oppo.launcher.permission.WRITE_SETTINGS" /> <uses-permission android:name="com.lenovo.launcher.permission.READ_SETTINGS" /> <uses-permission android:name="com.lenovo.launcher.permission.WRITE_SETTINGS" /> <uses-permission android:name="com.huawei.android.launcher.permission.READ_SETTINGS" /> <uses-permission android:name="com.huawei.android.launcher.permission.WRITE_SETTINGS" /> <uses-permission android:name="telecom.mdesk.permission.READ_SETTINGS" /> <uses-permission android:name="telecom.mdesk.permission.WRITE_SETTINGS" /> <uses-permission android:name="dianxin.permission.ACCESS_LAUNCHER_DATA" />
Android 10 和 11 采坑记录之 open failed: EACCES (Permission denied)
1、打开文件异常 open failed: EACCES (Permission denied),提示权限拒绝
第一步 检查AndroidManifest 中存储的读写权限是否添加:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNA" />
第二步 存储的读写权限已添加 ,这时候就检查应用是否授权存储权限,打开设置—>权限管理—>您的应用—>存储权限:
备注:在Android 6.0 以后添加权限管理,存储权限需要手动授权。这里推荐两个比较不错的第三方库,有空大家可以去了解一下
//GitHub地址:https://github.com/tbruyelle/RxPermissions
//最新版本 0.12
implementation 'com.github.tbruyelle:rxpermissions:0.12'
//GitHub地址:https://github.com/permissions-dispatcher/PermissionsDispatcher
//最新版本 4.8.0
implementation "org.permissionsdispatcher:permissionsdispatcher:${latest.version}"
annotationProcessor "org.permissionsdispatcher:permissionsdispatcher-processor:${latest.version}"
以上都没问题。那很有可能是 android10 特性分区储存变更,不建议访问绝对路径文件。这个时候坚持访问绝对路径需要在application属性下添加以下代码:
android:requestLegacyExternalStorage="true"
另外建议应用文件创建 把
Environment.getExternalStorageDirectory()(储存卡根目录)
变更为应用私有存储
context.getExternalFilesDir(null)(android/data/包名/files/)
Android 11 强制使用分区存储,一旦强制使用分区存储,APP将不能直接访问外部存储。
application添加 android:requestLegacyExternalStorage=“true” 的配置在 Android 11上面是无效了,所以提供了 preserveLegacyExternalStorage 标记,在application添加android:preserveLegacyExternalStorage="true" 即可
由于 preserveLegacyExternalStorage 标记Android SDK 30 才有,所以你需要将项目Android SDK编译版本到 30
备注:如运行项目遇报错error: attribute android:preserveLegacyExternalStorage not found;解决办法如下两种:
1.升级Android SDK编译版本到30,即升级tuyaversion插件即可
2.如果不想升级编译版本,需要在Manifest的application标签内增加:tools:remove="preserveLegacyExternalStorage" ,移除此属性。原理见:https://developer.android.com/studio/build/manifest-merge。 (建议升级,否则会影响上架Google Play:https://developer.android.com/distribute/play-policies )
Android 快捷方式 shortcuts 使用
先上两个图
官方说明
快捷方式可帮助用户快速访问应用程序的各个部分,从而为用户提供特定类型的内容。
使用快捷方式交付内容的方式取决于您的用例以及快捷方式的上下文是应用程序驱动还是用户驱动。尽管静态快捷方式的上下文不会更改,而动态快捷方式的上下文会不断更改,但是两种情况下的上下文都是由您的应用程序驱动的。如果用户选择自己希望您的应用向其交付内容的方式(例如固定快捷方式),则上下文由用户定义。
有三种方式
1.静态快捷方式
最适合在用户与应用程序互动的整个生命周期内使用一致结构链接到内容的应用程序。由于大多数启动器一次 只能显示四个快捷方式,因此静态快捷方式对于常见活动很有用。
1.1 在清单文件中添加meda-data
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts"/>
</activity>
<activity android:name=".ShortcutActivity"/>
</application>
1.2 创建shortcuts资源文件,res/xml/shortcuts.xml
一个shortcut标签表示一个快捷方式,这里我创建了两个,但是id不同其他都是相同的。
必须提供一个android:shortcutId和 android:shortcutShortLabel。所有其他值都是可选的。
各个属性定义:
shortcutId:id
shortcutShortLabel:简短的短语,描述了快捷方式的用途。尽可能将快捷方式的“简短描述”的长度限制为10个字符。
shortcutLongLabel:描述快捷方式用途的扩展短语。如果有足够的空间,启动器将显示该值而不是shortcutShortLabel。如果可能,将快捷方式的“详细描述”的长度限制为25个字符。
shortcutDisabledMessage:当用户尝试启动禁用的快捷方式时,在受支持的启动器中显示的消息。该消息应向用户说明为什么现在禁用了快捷方式。如果android:enabled为,则此属性的值无效true。
enabled:默认值是true,如果将其设置为false,还应该设置一个android:shortcutDisabledMessage,以说明为什么禁用了快捷方式。如果您不需要提供此类消息,则最简单的方法是从XML文件中完全删除快捷方式。
icon:图标,也可以是包含图像的资源文件。
<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<shortcut
android:shortcutId="compose"
android:icon="@mipmap/ic_launcher"
android:shortcutShortLabel="@string/addShortcuts"
android:shortcutLongLabel="@string/addNewWebsite"
android:shortcutDisabledMessage="@string/shortcutsMessage">
<intent
android:action="android.intent.action.VIEW"
android:targetClass="com.shy.shortcutstest.ShortcutActivity"
android:targetPackage="com.shy.shortcutstest" />
</shortcut>
<shortcut
android:shortcutId="compose1"
android:icon="@mipmap/ic_launcher"
android:shortcutShortLabel="@string/addShortcuts"
android:shortcutLongLabel="@string/addNewWebsite"
android:shortcutDisabledMessage="@string/shortcutsMessage">
<intent
android:action="android.intent.action.VIEW"
android:targetClass="com.shy.shortcutstest.ShortcutActivity"
android:targetPackage="com.shy.shortcutstest" />
</shortcut>
</shortcuts>
附上资源文件
<resources>
<string name="addShortcuts">Shortcuts</string>
<string name="addNewWebsite">AddNewWebsite</string>
<string name="shortcutsMessage">shortcutsMessage</string>
</resources>
创建好点击快捷方式的activity,也就是shortcut中intent,targetClass和targetOackage的指向。此时就可以跳转到制定的activity了。
2.动态快捷方式
动态快捷方式提供了指向应用程序中特定于上下文的特定操作的链接。这些操作可能会在您的应用使用之间发生变化,甚至在您的应用运行时也会发生变化。
在某个特定的时期为某个页面,或某个动作,甚至可以参与到周期较长的业务逻辑当中,添加一个快捷方式,以快速到达当前状态。
2.1 获得ShortcutManager
mSm = (ShortcutManager) getSystemService(Context.SHORTCUT_SERVICE);
2.2 调用ShortcutManager的api执行添加、设置、删除快捷方式的操作
setDynamicShortcuts():重新定义动态快捷键的完整列表。
addDynamicShortcuts():以增加动态快捷键的现有列表。
updateShortcuts():更新。
removeDynamicShortcuts():删除一组。
removeAllDynamicShortcuts():删除全部。
传入参数都是list,list中实际都是ShortcutInfo对象。
添加
Intent intent = new Intent(this, ShortcutActivity2.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra("name","孙三");
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N_MR1) {
ShortcutInfo builder = new ShortcutInfo.Builder(this, "id")
.setShortLabel("动态标签")
.setLongLabel("长长动态标签")
.setIcon(Icon.createWithResource(this, R.mipmap.ic_launcher))
.setIntent(intent)
.build();
mSm.addDynamicShortcuts(Arrays.asList(builder));
}
删除
List<String> id = new ArrayList<>();
id.add("id");
mSm.removeDynamicShortcuts(id);
ShortcutManager中还提供了获得快捷方式的函数,
mSm.getDynamicShortcuts(); //获得动态的快捷方式
mSm.getPinnedShortcuts(); //获得固定的快捷方式
mSm.getManifestShortcuts(); //获得静态的快捷方式
3. 固定快捷方式
在Android 8.0(API级别26)及更高版本上,您可以创建固定的快捷方式。与静态和动态快捷方式不同,固定的快捷方式在支持的启动器中显示为单独的图标。图1显示了这两种快捷方式之间的区别。
Intent intent = new Intent(this, ShortcutActivity2.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra("name", "孙三");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (mSm.isRequestPinShortcutSupported()) { // 验证设备是否支持
//创建固定的快捷方式时,如果有这个快捷方式id,系统会自动绑定有关的所有信息,
//如果要固定新的 就重新创建一个
ShortcutInfo pinShortcutInfo = new ShortcutInfo.Builder(this, "id")
.setShortLabel("固定的")
.setIntent(intent)
.build();
Intent pinnedShortcutCallbackIntent = mSm.createShortcutResultIntent(pinShortcutInfo);
PendingIntent successCallback = PendingIntent.getBroadcast(this, 0, pinnedShortcutCallbackIntent, 0);
//尝试添加到桌面
mSm.requestPinShortcut(pinShortcutInfo, successCallback.getIntentSender());
}
}
当固定到屏幕后,可以调用updateShortcuts()更新。
Android 8.0应用快捷方式(ShortcutManager)的使用
在Android系统上,有时候会看到在桌面上长按应用图标弹出应用快捷列表,例如美团、支付宝、微博、印象笔记、QQ邮箱等,效果图如下所示:
用户可以通过这些快捷键,直接访问应用具体的界面,并且长按这些快捷键可以在桌面上创建一个该快捷键的应用图标,比如长按上图中的收钱快捷键,拖动即可在桌面创建一个支付宝收钱快捷方式。
这个效果是Android 7.1之后新增的一个功能:应用快捷方式ShortcutManager,官方API地址:
https://developer.android.google.cn/reference/android/content/pm/ShortcutManager.html,接下来,我们将演示该快捷方式的具体实现方法:
- 静态的: 在xml中定义, 适用于一些通用的动作.
- 动态的: 由ShortcutManager发布, 可以根据用户的行为或者偏好添加, 可以动态更新.
注:该快捷方式只能在Android 7.1的设备上显示,且每一个应用目前最多可以有5个shortcuts(静态 + 动态)。
静态ShortcutManager的使用
静态的Shortcuts是写在xml中的,除非应用更新,否则不能被修改。
首先在res/xml/创建一个shortcuts.xml文件,内容如下所示:
<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<shortcut
android:enabled="true"
android:icon="@drawable/ic_search"
android:shortcutDisabledMessage="@string/lable_shortcut_static_search_disable"
android:shortcutId="shortcut_id_search"
android:shortcutLongLabel="@string/lable_shortcut_static_search_disable"
android:shortcutShortLabel="@string/lable_shortcut_static_search_disable">
<intent
android:action="android.intent.action.VIEW"
android:targetClass="com.cjxj.androiddemo.activity.MainActivity"
android:targetPackage="com.cjxj.androiddemo" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
</shortcuts>
其中属性分别表示:
- shortcutId表示 shortcut 唯一标识符,相同的 shortcutId 会被覆盖。必须字段。
- shortcutShortLabel为将 shortcut 拖动到桌面时显示的名字,官方建议不超过 10 个字符,必须字段。
- shortcutLongLabel为 shortcut 列表中每个 shortcut 的名字,不宜过长,如果过长或未设置默认会显示 ShortLabel,官方建议不超过 25 个字符。可选字段。
- icon为 shortcut 的 icon,在列表展示和拖动到桌面时显示需要,可选字段。
- enabled表示 shortcut 是否可用,false 表示禁用。
- shortcutDisabledMessage为已固定在桌面的 shortcut 被 Disabled 后点击时的 Toast 提示内容。可选字段。
- intent为点击 shortcut 时响应的 Intent,必须字段。
这里可以添加多个 Intent,但点击时不会启动所有 Intent,而是启动最后一个 Intent,在这个 Intent 回退时会启动它前面一个 Intent,相当于自动将所有 Intent 添加到了堆栈。
然后在应用的Manifest文件中配置启动界面的Activity,内容如下:
<activity
android:name=".activity.HomeListActivity"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="android.app.shortcuts"
android:resource="@xml/shortcuts"/>
</activity>
至此,我们就添加了一个搜索功能的快捷方式,效果如下:
动态ShortcutManager的使用
动态是通过 ShortcutManager API 进行操作,可以动态添加、修改、删除。
if (Build.VERSION.SDK_INT >= 25) {
ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(this, "shortcut_id_search")
.setShortLabelResId(R.string.lable_shortcut_static_search_disable)
.setLongLabelResId(R.string.lable_shortcut_static_search_disable)
.setIcon(Icon.createWithResource(this, R.drawable.ic_search))
.setIntent(new Intent(this, MainActivity.class))
.build();
ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);
//这样就可以通过长按图标显示出快捷方式了
shortcutManager.setDynamicShortcuts(Arrays.asList(shortcutInfo));
}
通过ShortcutInfo.Builder新建 ShortcutInfo,再通过shortcutManager添加即可。其中:
- setDynamicShortcuts(List)可以替换并添加所有 shortcut 列表;
- addDynamicShortcuts(List)可以添加新的 shortcut 到列表,超过最大个数会报异常;
- updateShortcuts(List)可以更新一组 shortcuts;
- removeDynamicShortcuts(List)和removeAllDynamicShortcuts() 可以删除部分或所有 shortcuts。
至此,关于应用快捷方式的使用就介绍结束了,如有问题,欢迎留言。
内容简介:就在前几天,跟一同事车回家,他用的是iOS版高德,每次发车前,重力长按高德icon,弹出shortcuts,很方便就进入回家的导航,也就是iOS 3D Touch功能。如下面这张图,截图来自647 iPhone X 。今天得空研究了一下,Android 在Android 7.1(API 25) 添加了App快捷方式的新功能,由ShortcutManager类来管理,这样开发者可以随意定义快速进入到指定的Activity或打开指定网页。目前有很多App已经有了这个特性,接了个图如下:下面我们就详细探讨一下这
就在前几天,跟一同事车回家,他用的是iOS版高德,每次发车前,重力长按高德icon,弹出shortcuts,很方便就进入回家的导航,也就是iOS 3D Touch功能。如下面这张图,截图来自647 iPhone X 。
今天得空研究了一下,Android 在Android 7.1(API 25) 添加了App快捷方式的新功能,由ShortcutManager类来管理,这样开发者可以随意定义快速进入到指定的Activity或打开指定网页。目前有很多App已经有了这个特性,接了个图如下:
![](https://img2022.cnblogs.com/blog/47234/202211/47234-20221108141401657-1681004649.png)
下面我们就详细探讨一下这个特性。
实现Shortcuts的两种形式
静态Shortcuts
所谓的静态就是在工程中配置,利用xml写死。在APK中包含一个资源文件来描述Shortcut,目录res/xml/shortcuts.xml。这种方法做不到热更新,需要从新发布App才可。
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<shortcut
android:shortcutId="static shortcut"
android:enabled="true"
android:icon="@mipmap/ic_launcher"
android:shortcutShortLabel="@string/shortcut_short_name"
android:shortcutLongLabel="@string/shortcut_long_name"
android:shortcutDisabledMessage="@string/shortcut_disable_msg">
<intent
android:action="android.intent.action.VIEW"
android:targetPackage="d.shortcuts"
android:targetClass="d.shortcuts.MainActivity" />
<categories android:name="android.shortcut.conversation"/>
</shortcut>
</shortcuts>
复制代码
<application ...
...>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts"/>
</activity>
</application>
复制代码
这种方式适合百年不变的场景,不然真是不够灵活。
动态Shortcuts
动态Shortcuts在运行时,通过ShortcutManager API来进行注册。用这种方式可以在运行时,动态的发布、更新和删除shortcut。官方给出了几个场景可以作为shortcut的例子,比如:
- 在地图类app中,指导用户到特定的位置;
- 在社交类app中,发送消息给一个朋友;
- 在媒体类app中,播放视频的下一片段;
- 在游戏类app中,下载最后保存的要点;
动态安装
给Activity页面构建shortcut
private void setupShortcutsForActivity() {
mShortcutManager = getSystemService(ShortcutManager.class);
List<ShortcutInfo> infos = new ArrayList<>();
for (int i = 0; i < mShortcutManager.getMaxShortcutCountPerActivity(); i++) {
Intent intent = new Intent(this, Main2Activity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra("info", "this is info!");
ShortcutInfo info = new ShortcutInfo.Builder(this, "ID:" + i)
.setShortLabel("short label")
.setLongLabel("long label")
.setIcon(Icon.createWithResource(this, R.mipmap.ic_launcher))
.setIntent(intent)
.build();
infos.add(info);
}
mShortcutManager.setDynamicShortcuts(infos);
}
复制代码
使用URL构建shortcut,打开默认浏览器,如下
private ShortcutInfo createShortcutForUrl(String urlAsString) {
final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mContext, urlAsString);
final Uri uri = Uri.parse(urlAsString);
b.setIntent(new Intent(Intent.ACTION_VIEW, uri));
setSiteInformation(b, uri)
setExtras(b);
return b.build();
}
private ShortcutInfo.Builder setSiteInformation(ShortcutInfo.Builder b, Uri uri) {
b.setShortLabel(uri.getHost());
b.setLongLabel(uri.toString());
Bitmap bmp = fetchFavicon(uri);
if (bmp != null) {
b.setIcon(Icon.createWithBitmap(bmp));
} else {
b.setIcon(Icon.createWithResource(mContext, R.drawable.link));
}
return b;
}
private ShortcutInfo.Builder setExtras(ShortcutInfo.Builder b) {
final PersistableBundle extras = new PersistableBundle();
extras.putLong(EXTRA_LAST_REFRESH, System.currentTimeMillis());
b.setExtras(extras);
return b;
}
//注意要异步Task执行
private Bitmap fetchFavicon(Uri uri) {
final Uri iconUri = uri.buildUpon().