瞅一瞅Android_N_Shortcut

Android N Launcher带来了Shortcut新特性,类似于iOS的Force Touch,支持用户在长按应用时,弹出该应用的其他快捷入口。

如下图所示:



该接口针对API level 25 及以上的手机系统版本,shortcuts可以定义intent使得app可以在支持的桌面上建立快捷入口。
shortcuts有以下功能:

  1. 地图app中给用户指定位置导航;
  2. im app中发送消息;
  3. 视频app中快速观看下一集电视剧;
  4. 游戏app中快速加载上一次的进度;

开发者可以使用两种类型的shortcuts:

  1. 静态shortcuts :开发者在apk中定义资源文件,因此必须等app更新后,shortcuts才能随之更新;
  2. 动态shortcuts:应用可以在运行时使用ShortcutManager动态创建,更新,删除shortcuts数据。

如何创建静态和动态shortuts,可参考 https://developer.android.com/guide/topics/ui/shortcuts.html

桌面是典型的MVC架构工程,以下便从MVC的角度来分析Shortcut功能,参考代码为Android 7.1 Launcher3工程代代码。


M(Model)

桌面的加载从LauncherModel类开始,此类负责后台子线程读取/修改应用db数据,以下为负责后台加载桌面数据的子线程run方法,第三步为加载Shortcut数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void run() {
......
keep_running: {
if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
loadAndBindWorkspace(); //加载主界面应用
......
// second step
if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
loadAndBindAllApps(); //加载所有应用界面
.......
// third step
if (DEBUG_LOADERS) Log.d(TAG, "step 3: loading deep shortcuts");
loadAndBindDeepShortcuts();//加载shortcut数据
}
.......
}

loadAndBindDeepShortcuts方法实现如下,可知此方法会将从系统中查询到的shortcut数据保存到本地HashMap成员变量mBgDeepShortcutMap{key:ComponentName, value:ShortcutIdList}里, 并在bindDeepShortcuts方法中将mBgDeepShortcutMap更新到Launcher.java中的HashMap成员变量mDeepShortcutMap, Launcher文件相当于MVC中的C(Controller)角色:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void loadAndBindDeepShortcuts() {
.......
if (!mDeepShortcutsLoaded) {
mBgDeepShortcutMap.clear();
mHasShortcutHostPermission = mDeepShortcutManager.hasHostPermission();
if (mHasShortcutHostPermission) { //确认权限
for (UserHandleCompat user : mUserManager.getUserProfiles()) {
if (mUserManager.isUserUnlocked(user)) {
List<ShortcutInfoCompat> shortcuts = mDeepShortcutManager
.queryForAllShortcuts(user); //从系统中读取shortcut信息
updateDeepShortcutMap(null, user, shortcuts);
}
}
}
.......
bindDeepShortcuts();
}

由Google API Guides可知,上述加载Shortcut过程对静态/动态shortcut并无做区分,只需要桌面在加载过程时简单去查询即可获得相关数据。


C(Controller)

目前shortcut的触发场景只有桌面长按触发,根据使用体验便可知Launcher的onLongClick事件处理。根据之前在LauncherModel更新的HashMap数据mDeepShortcutMap来判断长按对应的app是否含有shortcut数据,来决定是否展开shortcut菜单,看代码更是一目了然:

1
2
3
4
5
6
7
8
9
10
11
if (!(itemUnderLongClick instanceof Folder || isAllAppsButton)) {
......
if (itemUnderLongClick instanceof BubbleTextView) {
BubbleTextView icon = (BubbleTextView) itemUnderLongClick;
if (icon.hasDeepShortcuts()) {
DeepShortcutsContainer dsc = DeepShortcutsContainer.showForIcon(icon);
......
}
}
mWorkspace.startDrag(longClickCellInfo, dragOptions);
}

根据代码可知,展开shortcut菜单和拖拽事件可以同时进行,实际用户体验不优雅,估计这是谷歌Launcher开发者的无奈之举,毕竟需要兼容所有Android设备,没有类似iPhone的压力屏。


V(View)

View受Controller影响,根据Controller的逻辑,桌面弹出菜单View类关系如下,DeepShortcutsContainer是整个菜单界面,DeepShortcutsView是它的子View,也是每个菜单项,具体的动画分析在其他文档中解析:




—————————————————-
至此,以简单的流程图总结桌面的shortcut加载和启动流程,其中动态和静态的shortcut数据都是由系统来控制,一旦有外部数据源变化,桌面只需要向系统重新query即可获得所有数据,简单易维护,这是Launcher3作为系统app沾到的大大好处,如果是独立app来做,需要其他策略支持: