『Android Tip』-- Shortcuts 快捷方式

開篇

Shortcuts 功能跟隨着 Android7.1 Nougat 一塊兒誕生,其主要目在於用戶能夠定義一些經常使用的操做路徑,以快捷方式的形式存在,這些快捷方式展現在能夠支持的設備上,幫助用戶快速啓動經常使用或者推薦的頁面和行爲。php

最近也是有 Shortcut 相關的需求須要開發,特此進行了總結,但願能夠幫助到你們。ShortcutsDemojava

概覽

快捷方式通常是以兩種方式存在:android

一種經過 長按 應用 icon,此時會彈出列表彈窗,裏面展現須要操做的路徑,對於沒有配置快捷方式的應用,通常只會展現分享或者應用信息的入口(不一樣手機可能展現不同)。若是該應用配置了快捷方式,那麼在列表中則會展現對應的快捷方式入口。git

沒有配置

配置的

如上圖所示,配置和沒有配置快捷方式的區別就在於,列表中是否配置自定義的快捷方式入口。github

另外一種則以桌面快捷方式的形式存在,同一種行爲能夠存在多個相同的桌面快捷方式。shell

pinedShow

如上圖,對於 Chrome-打開新的標籤頁 這種行爲能夠有多個相同的桌面快捷方式。緩存

Shortcuts 類型

每一個快捷方式均可以攜帶一個或多個 intent,當用戶點擊快捷方式時,每一個 intent 都會觸發應用中對應的操做,通常快捷方式的建立類型取決於你具體快捷方式存在的形態和你想賦予他什麼樣的行爲。能夠如下面的例子做爲參考:bash

  • 在天氣應用中,想查看最近幾每天氣的趨勢
  • 在電子郵件應用中,想建立新的電子郵件
  • 在地圖應用中,定位一個具體的位置
  • 在聊天應用中,向指定好友發送信息
  • 在媒體應用中,播放電視節目的下一集
  • 在遊戲應用中,加載遊戲最後一個保存的時間點

... ...app

你能夠爲你的應用發佈如下類型的快捷方式‘ide

  • 靜態的快捷方式: 其直接會打包到 apk 或 apk bundle 中,安裝完應用便存在快捷方式入口。
  • 動態的快捷方式: 只有在應用運行時纔會建立,能夠隨時的更新、添加和刪除對應的快捷方式。
  • 桌面快捷方式: 必須在用戶受權的狀況下,能夠主動的添加快捷方式到桌面,一樣能夠拷貝動態和靜態的快捷方式到桌面。

Shortcuts 限制條件

雖然對於一個應用程序通常能夠建立五個快捷方式,其中包括靜態和動態的,可是但多數的設備上只能展現 四個

可是桌面快捷方式是不作限制的,不過桌面快捷方式非用戶主動刪除的話,是無法移除的,只能經過禁用的方式讓該桌面快捷方式失效。

使用

快捷方式能夠幫助用戶快速訪問經常使用的路徑和頁面,從而爲用戶提供特定類型的內容。代碼以上傳至 ShortcutsDemo

Shortcuts 類型選擇

那該如何選擇快捷方式類型,這取決你的快捷方式是應用驅動仍是用戶驅動。雖然靜態快捷方式意圖不可更改,動態的可更改,可是這兩種都是屬於應用驅動。若是用戶想自定義想要的意圖,經過桌面快捷方式形式的展示,那這就是用戶驅動。

怎麼理解呢?用簡書做爲例子進行講解:

image-20190511163016540

  • 靜態快捷方式: 這種最適合那種在整個程序的生命週期中,意圖不會改變,始完成整同一種行爲。鑑於程序通常只能顯示四個快捷方式,那靜態的快捷方式通常對於那種比較常見的行爲很是有用和有必要。

    例如上圖簡書中,像「搜索」入口就比較常見,不須要傳遞參數或傳遞的參數不會改變,那這種就建議使用靜態快捷方式。

  • 動態快捷方式: 這種通常對意圖較爲敏感的操做。意圖可能在應用運行中發生改變,須要更新快捷方式。

    例如簡書入口中的 「個人公開文章」,由於多帳號的緣由,可能切換帳號,那跳轉的頁面所攜帶的參數就會改變,快捷方式就須要更新,這種就須要使用靜態快捷方式。

  • 桌面快捷方式: 這種容許用戶自定義跳轉意圖。

    例如簡書支持將關注的人建立快捷方式到桌面,下次直接能夠訪問該人的動態信息,這種徹底是用戶自發的建立,因此使用桌面快捷方式。

Shortcuts 類型建立

有了上述類型的具體描述,下面針對這三種快捷方式的建立進行例子介紹。

建立靜態快捷方式

靜態快捷方式提供應用程序內的通用跳轉,這些通常在整個程序的生命週期內是保持不變的。

經過如下步驟完成靜態快捷方式的建立:

  1. AndroidManifest.xml 中找到配置 android.intent.action.MAINandroid.intent.category.LAUNCHERActivity

  2. 添加 <meta-data> 元素到 Activity 中

    <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>
    複製代碼
  3. 建立新的資源文件: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/add" android:shortcutDisabledMessage="@string/static_disabled_message" android:shortcutId="staticId" android:shortcutLongLabel="@string/static_shortcut_long_label" android:shortcutShortLabel="@string/static_shortcut_short_label">
            <intent android:action="android.intent.action.VIEW" android:data="content://xiaweizi.com/fromStaticShortcut" android:targetClass="com.example.xiaweizi.shortcutsdemo.TestActivity" android:targetPackage="com.example.xiaweizi.shortcutsdemo" />
            <categories android:name="android.shortcut.conversation" />
        </shortcut>
    </shortcuts>
    複製代碼

具體參數配置說明以下:

標籤名稱 描述
android:shortcutId shortcut 的惟一標識,更新刪除都須要他最爲關鍵字
android:shortcutShortLabel 描述快捷方式的簡明短語,若是可能儘量控制爲 10 個字符
android:shortcutLongLabel 描述快捷方式的擴展短語,若是空間足夠會顯示此值,儘量控制在 25 個字符
android:shortcutDisabledMessage 若是桌面快捷方式被禁用,點擊會彈出此提示內容
android:enabled 控制桌面快捷方式是否被禁用
android:icon 快捷方式旁邊的圖標
android:action intent 跳轉的 action
android:targetPackage 跳轉頁面的包名
android:targetClass 跳轉頁面的完整路徑
  1. 若是有數據的傳遞,要有對應的解析

    if (getIntent().getData() != null && TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
      Uri uri = getIntent().getData();
      List<String> pathSegments = uri.getPathSegments();
      if (pathSegments != null && pathSegments.size() > 0) {
        tvTest.setText(pathSegments.get(0));
      }
    }
    複製代碼

最終的運行效果:

staticShortcut

建立動態快捷方式

動態快捷方式提供向指向應用內特定的跳轉或數據傳遞,這些跳轉和數據可能會在應用執行中發生變化。

此時須要藉助 ShortcutManager 提供的 API 來完成動態快捷方式的相應操做:

  • 建立: 使用 setDynamicShortcuts() 從新定義動態快捷方式的完整列表
  • 添加: 使用 addDynamicShortcut() 來擴充現有的動態快捷方式列表
  • 更新: 使用 updateShortcuts() 方法進行更新現有的動態快捷方式列表
  • 刪除: 使用 removeDynamicShortcuts() 移除一組動態快捷方式,或者使用 removeAllDynamicShortcuts() 移除全部動態快捷方式

以建立爲例,其餘差很少,自行嘗試,具體的操做可參考下面的代碼:

  1. 建立 ShortcutInfo 對象

    @TargetApi(Build.VERSION_CODES.N_MR1)
    private ShortcutInfo createShortcutInfo1() {
      return new ShortcutInfo.Builder(this, ID_DYNAMIC_1)
        .setShortLabel(getString(R.string.dynamic_shortcut_short_label1))
        .setLongLabel(getString(R.string.dynamic_shortcut_long_label1))
        .setIcon(Icon.createWithResource(this, R.drawable.add))
        .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("http://xiaweizi.cn/")))
        .build();
    }
    複製代碼
  2. 調用 setDynamicShortcuts() 覆蓋掉以前的,從新設置新的動態快捷方式列表

    private void setDynamicShortcuts() {
      if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N_MR1) {
        ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);
        List<ShortcutInfo> shortcutInfo = new ArrayList<>();
        shortcutInfo.add(createShortcutInfo1());
        shortcutInfo.add(createShortcutInfo2());
        if (shortcutManager != null) {
          shortcutManager.setDynamicShortcuts(shortcutInfo);
        }
      }
    }
    複製代碼
  3. 能夠配置 label 的字體顏色

    @TargetApi(Build.VERSION_CODES.N_MR1)
    private ShortcutInfo createShortcutInfo2() {
      Intent intent = new Intent(this, TestActivity.class);
      intent.setAction(Intent.ACTION_VIEW);
      intent.putExtra("key", "fromDynamicShortcut");
      ForegroundColorSpan colorSpan = new ForegroundColorSpan(Color.BLUE);
      String label = getResources().getString(R.string.dynamic_shortcut_short_label2);
      SpannableStringBuilder colouredLabel = new SpannableStringBuilder(label);
      colouredLabel.setSpan(colorSpan, 0, label.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
      return new ShortcutInfo.Builder(this, ID_DYNAMIC_2)
        .setShortLabel(colouredLabel)
        .setLongLabel(getString(R.string.dynamic_shortcut_long_label2))
        .setIcon(Icon.createWithResource(this, R.drawable.link))
        .setIntent(intent)
        .build();
    }
    複製代碼

最終的運行效果:

dynamicShortcut

建立桌面快捷方式

在 Android 8.0(API26)或者更高的版本上,能夠建立桌面快捷方式。與靜態和動態快捷方式不一樣,桌面快捷方式支持在設備上單獨的 icon 展現。

若是想建立桌面快捷方式,按照如下步驟進行完成:

  1. 使用 isRequestPinShortcutSupported() 來驗證設備是否支持應用建立桌面快捷方式
  2. 根據快捷方式是否已經存在,用下面兩種方式之一來建立 ShortcutInfo 對象:
    1. 若是快捷方式已經存在,請建立僅包含現有快捷方式 id 的 ShortcutInfo 對象,系統自動查找並帶上與快捷方式有關的全部相關數據
    2. 若是要固定新的快捷方式,請建立一個 ShortcutInfo 對象,其中包含新的快捷方式 id、意圖和短標籤
  3. 嘗試經過調用 requestPinShortcut() 將快捷方式固定到設備桌面上,在此過程當中,能夠傳入 pendingIntent 對象,該對象僅在快捷方式成功固定時告知應用。

具體的代碼可參考下面:

private void createPinnedShortcuts() {
  if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
    ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);
    if (shortcutManager != null && shortcutManager.isRequestPinShortcutSupported()) {
      Intent intent = new Intent(this, TestActivity.class);
      intent.setAction(Intent.ACTION_VIEW);
      intent.putExtra("key", "fromPinnedShortcut");
      ShortcutInfo pinShortcutInfo = new ShortcutInfo.Builder(this, "my-shortcut")
        .setShortLabel(getString(R.string.pinned_shortcut_short_label2))
        .setLongLabel(getString(R.string.pinned_shortcut_long_label2))
        .setIcon(Icon.createWithResource(this, R.drawable.add))
        .setIntent(intent)
        .build();
      Intent pinnedShortcutCallbackIntent = shortcutManager.createShortcutResultIntent(pinShortcutInfo);
      PendingIntent successCallback = PendingIntent.getBroadcast(this, 0,
                                                                 pinnedShortcutCallbackIntent, 0);
      boolean b = shortcutManager.requestPinShortcut(pinShortcutInfo, successCallback.getIntentSender());
      Utils.showToast(this, "set pinned shortcuts " + (b ? "success" : "failed") + "!");
    }
  }
}
複製代碼

最終運行效果:

pinnedShortcut

好了,基礎簡單的使用就介紹到這了,相信對 Shortcuts 的使用場景和具體實現都有必定的瞭解了,接下來開始介紹進階的使用。

Shortcuts 進階使用

快捷方式建立完成後,可能還須要對其進行管理,好比動態更新或者禁用某些快捷方式,此時就須要瞭解一些進階的使用了。

移除 Shortcut

對於 靜態快捷方式 而言,其在一開始就打包到了 apk 或者 apk bundle 中,是不容許對其進行更改的,除非發佈新的版本覆蓋掉以前的快捷方式,否則會一直存在。

對於 動態快捷方式 ,既然能夠在代碼中進行建立,一樣也能夠在代碼中進行移除,這個以前也介紹過。

而對於 桌面快捷方式,它直接展現在桌面上,始終可見,僅在如下狀況才能刪除桌面快捷方式。

  • 用戶主動移除
  • 卸載與快捷方式的應用
  • 用戶在應用信息中,清除所有的緩存數據

Shortcut 展現順序

對於一個應用上全部的快捷方式,展現的規則按照如下順序:

  1. 靜態快捷方式: isDeclaredInManifest() 放回 true 的快捷方式
  2. 動態快捷方式ShortcutInfo.isDynamic() 返回 true 的快捷方式

在每種快捷方式中,又會按照 ShortcutInfo.getRank() 按等級遞增的順序排序。等級是非負的,連續的整數, 調用 updateShortcuts(List)addDynamicShortcuts(List)setDynamicShortcuts(List) 時,能夠更新現有快捷方式的等級。

排名是自動調整的,所以它們對於每種類型的快捷方式都是惟一的。 例如,有三個 rank 分別爲 0、1和2 的動態快捷方式,此時再添加一個 rank 爲 1 的動態快捷方式放在第二的位置上,那最後兩個就會被順延一個位置,rank 變成 2和3。

Shortcut intents 配置

若是但願應用在用戶激活快捷方式時執行多項操做,則能夠將其配置爲觸發後多項活動。你能夠經過分配多個 intent,啓動一個 activity 到另外一個 activity,具體的要取決你快捷方式的類型。

使用 ShortcutInfo.Builder 建立快捷方式時,可使用 setIntents() 而不是 setIntent()。經過調用 setIntents() 你能夠在用戶點擊快捷方式時觸發多個 activity,將除列表中最後一個 activity 以外的全部 activity 放在後續堆棧中。若是此時點擊返回按鈕,會按照以前存儲的堆棧 activity 順序進行展現,而不是直接回到首頁。

好比按照下面代碼配置多個 intent:

@TargetApi(Build.VERSION_CODES.N_MR1)
private ShortcutInfo createShortcutInfo1() {
  Intent intent1 = new Intent(Intent.ACTION_VIEW, Uri.parse("http://xiaweizi.cn/"));
  Intent intent = new Intent(this, TestActivity.class);
  intent.setAction(Intent.ACTION_VIEW);
  intent.putExtra("key", "fromDynamicShortcut");
  return new ShortcutInfo.Builder(this, ID_DYNAMIC_1)
    .setShortLabel(getString(R.string.dynamic_shortcut_short_label1))
    .setLongLabel(getString(R.string.dynamic_shortcut_long_label1))
    .setIcon(Icon.createWithResource(this, R.drawable.add))
    .setIntents(new Intent[]{intent, intent1})
    .build();
}
複製代碼

那麼它會一次觸發 intent 和 intent1,此時 intent 被壓入 intent1 的棧底,點擊返回,則展現 intent 的信息。如圖:

intents

還有一個問題,靜態快捷方式是不能擁有自定的 intent flag 的,靜態快捷方式始終設置爲 Intent.FLAG_ACTIVITY_NEW_TASKIntent.FLAG_ACTIVITY_CLEAR_TASK 這意味着,當應用程序已經在運行時,啓動靜態快捷方式時,應用中全部的活動都將被銷燬。若是不但願出現這種狀況,可使用 trampoline activity,或者在 Activity.onCreate(Bundle) 中啓動一個 不可見的 activity,而後調用 Activity.finish()

  1. AndroidManifest.xml 中, trampoline activity 應用設置 android:taskAffinity=""
  2. 在快捷方式資源文件中,靜態快捷方式中的 intent 應引用 trampoline activity

更新 pinned Shortcuts

每一個應用最多包含 getMaxShortcutCountPerActivity() 個快捷方式,其中包括動態和靜態的總和。可是桌面快捷方式的數量是不限制的。

當動態快捷方式被放置到桌面時,即便代碼中將該動態快捷方式移除,桌面的還依然存在,所以對於桌面的快捷方式是不止 getMaxShortcutCountPerActivity 的限制的。

假設 getMaxShortcutCountPerActivity() 的值爲4:

  1. 聊天應用程序發佈四個動態快捷方式,表示最近的四個對話(c1,c2,c3,c4)

  2. 用戶將全部的快捷方式複製到桌面

  3. 而後用戶又啓動三個額外的最近對話(c5,c6和c7),這是從新發布更新動態快捷方式,那新的快捷方式列表爲:c4,c5,c6,c7。改應用必須刪除過 c1,c2喝c3,由於只能展現四個快捷方式,可是桌面已經保存的這三個快捷方式是能夠正常訪問的。

    用戶如今其實能夠總共訪問七個快捷方式,其中包括四個最大的動態快捷方式和桌面的三個快捷方式

  4. 應用程序可使用 updateShortcuts(List) 來更新上述七個任意快捷方式

  5. 使用 addDynamicShortcuts()setDynamicShortcuts() 一樣能夠更新具備相同 shortcutId 的快捷方式對象,可是他們不能跟新非動態的快捷方式。

系統設置更改

系統設置的更改,好比修改系統的語言,Shortcuts 是不能動態更新的,此時須要建立廣播監聽 Intent.ACTION_LOCALE_CHANGED ,當收到廣播時從新更新快捷方式,保證快捷方式展現沒有問題。

處理前:

before

處理後:

after

track Shortcuts

爲了肯定靜態和動態快捷方式以哪一種方式出現,每次啓動都會檢查快捷方式的激活歷史記錄。能夠經過調用 reportShortcutUsed() 方法傳入其 shortcutId,提升 action 的響應速度。

ShortCuts 頻率限制

當使用 setDynamicShortcuts()addDynamicShortcuts()updateDynamicShortcuts() 方法時,須要注意的是,你可能在後臺調用這方法是有固定的次數限制的,那能夠調用方法的次數限制就是 rate limiting。此功能用於防止 ShortcutManager 過分消耗設備資源。

當處於 rate limiting 中,isRateLimitingActive() 返回 true,可是在某些操做執行會重置這個值,所以即便是在後臺應用程序也能夠調用 shortcutManager方法,直到再次達到速率限制。這些操做包括:

  1. 應用再次回到前臺
  2. 系統區域設置更改
  3. 用戶在通知欄處理嵌入的交互操做

若是在開發或者測試中遇到次數被限制的狀況,能夠在 開發者選項中 -> 重置 ShortcutsManager 調用頻率限制 來恢復。或者使用 adb 命令

adb shell cmd shortcut reset-throttling [ --user your-user-id ]
複製代碼

建議

在設計和建立快捷方式時,請遵循如下建議:

遵循設計準則

要使應用程序的快捷方式與系統應用程序使用的快捷方式在視覺上保持一致,請遵循 快捷方式設計指南

僅發佈四個不一樣的快捷方式

儘管 API 目前支持給任何應用最多五個快捷方式(靜態和動態),但仍是建議僅發佈四個不一樣的快捷方式,以改善在設備上的視覺效果。

限制快捷方式的描述長度

快捷方式的菜單空間有限,在桌面展現應用程序須要考慮到這個因素。若是能夠的話,將快捷方式的 shortLable 長度限制在 10 個字符,並將 longLable 長度限制在 25個字符。

記錄快捷方式和其操做的歷史記錄

對於建立的每一個快捷方式,請考慮用戶在應用中是否能夠直接用不一樣方式來完成相同的任務,須要記住的是,這種狀況下,調用 reportShortcutUsed() ,這樣 launcher 就能夠提升 shortcut 對應的 actions 的響應速度。

只有在 shortcuts 的意義存在時更新

當改變更態快捷方式時,只有在 shortcut 仍然保持它的含義時,調用 updateShortcuts() 方法改變它的信息,不然,應該使用 addDynamicShortcuts()setDynamicShortcuts() 建立一個具備新含義的 shortcutId 的快捷方式。

例如,若是咱們已經建立了導航到一個超市的快捷方式,若是超市的名稱改變了可是位置並無變化時,只更新信息是合適的,可是若是用戶開始再一個不一樣位置的超市購物時,最好就是建立一個新的快捷方式。

每次打開 APP 都須要檢查快捷方式

在備份或恢復時,動態 shortcuts 不會被保存, 正是由於這個緣由,推薦咱們在須要 APP 啓動和從新發布動態快捷方式時,檢查 getDynamicShortcuts() 的對象的數量。

參考自

官方Demo

官方文檔

原文地址 kua

相關文章
相關標籤/搜索