2016/10/23

Android: AppShortcut - 要点

! warning ! 
この投稿はAndroid 7.1 Developer Preview1 - App Shortcutの内容をもとにしています. 
今後のバージョンアップで内容が変更されている可能性にご注意ください.

App Shortcutについては下記の投稿もご参考ください.

App Shortcut のバリエーション

App Shortcutには2種類あり, さらにショートカットの状態にも2種類あります.

Static Shortcut(Manifest Shortcut)
リソースファイルで定義される静的なショートカットです.

Dynamic Shortcut
ShortcutManagerのAPIを使って動的に管理されるショートカットです.

さらに, ショートカットはランチャーへ”ピン留め”(Pin)できる機能があり, Static/Dynamic Shortcutどちらもピン留めが可能です.

Pinned Static Shortcut
Static Shortcutからランチャーへピン留めされたショートカットです.

Pinned Dynamic Shortcut
Dynamic Shortcutからランチャーへピン留めされたショートカットです.

App Shortcut のピン留め

ショートカットには更新できるタイプのものと更新できないタイプのものがあります.
Static Shortcutは不変で, アプリのアップデートでのみ変更できます. これは, ピン留めされたStatic Shortcutも同様です.

Action StaticShortcut DynamicShortcut Pinned StaticS. Pinned DynamicS.
Update Ver.UP only always Ver.UP only always

ピン留めされたショートカットは種別を問わずアプリから削除することができず, ユーザ操作を必要とします.

Action StaticShortcut DynamicShortcut Pinned StaticS. Pinned DynamicS.
Delete Ver.UP only always x x

Static Shortcutはアプリから無効化することができません. ショートカットの定義を削除したバージョンにアップデートされるとStatic Shortcutは無効化されます.

Action StaticShortcut DynamicShortcut Pinned StaticS. Pinned DynamicS.
Disable Ver.UP only always Ver.UP only always

ピン留めされたショートカットはバックアップ/リストアや, ショートカットの定義を削除したアプリへのアップデートでも残る性質があります. ショートカットが有効である限りそれに設定されたIntentが飛んでくることを考慮する必要があります.

Action StaticShortcut DynamicShortcut Pinned StaticS. Pinned DynamicS.
Remain via Ver.UP no yes yes(disable) yes

下表は上記をまとめたものです.

Action StaticShortcut DynamicShortcut Pinned StaticS. Pinned DynamicS.
Update Ver.UP only always Ver.UP only always
Delete Ver.UP only always x x
Disable Ver.UP only always Ver.UP only always
Remain via Ver.UP no yes yes(disable) yes

ShortcutManager API

Static Shortcutは不変であるため, アプリがショートカットを管理するのは主にDynamic Shortcutになります. ショートカットを追加, 変更, 削除, 無効化, およびそれをサポートするAPIは次の通りです.

ShortcutManager

disableShortcuts(List shortcutIds, CharSequence disabledMessage)
ピン留めされたショートカットを無効化し, ショートカット選択時のエラーメッセージを指定できます.

getDynamicShortcuts()
すべてのDynamic Shortcutを取得します.

_getManifestShortcuts()
すべてのStatic Shortcutを取得します.

getPinnedShortcuts()
すべてのピン留めされたショートカットを取得します.

removeDynamicShortcuts(List shortcutIds)
引数shortcutIdsのDynamic Shortcutを削除します.

addDynamicShortcuts(List shortcutInfoList)
Dynamic Shortcutを追加します. すでに存在しているショートカットと同じIDのものがある場合は更新されます. このAPIはRate-limitの対象です.

setDynamicShortcuts(List shortcutInfoList)
Dynamic Shortcutを登録します. すでに存在しているショートカットと同じIDのものがある場合は置き換えられます. ピン留めされたショートカットは不変でないものだけが置き換えられます. このAPIはRate-limitの対象です.

updateShortcuts(List shortcutInfoList)
すでに存在するショートカットと同じIDのものを更新します. ピン留めされたショートカットは不変でないものだけが更新できます. このAPIはRate-limitの対象です.

ShortcutInfo

getExtras()
当該ショートカットに設定された, 任意の情報を格納できるextraを取得します.

isDeclaredInManifest()
当該ショートカットが, マニフェストで定義されるStatic Shortcutであるか否かを取得できます.

isDynamic()
当該ショートカットが, Dynamic Shortcutであるか否かを取得できます.

isEnabled()
当該ショートカットが, 有効な状態であるか否かを取得できます.

isImmutable()
当該ショートカットが, 変更できない不変なものであるか否かを取得できます.

isPinned()
当該ショートカットが, ピン留めされたショートカットであるか否かを取得できます.

Shortcut Listの並び順

ショートカットリストはランチャーアイコンに近い方から優先度(rank)の昇順で並べます(これはランチャーアプリの実装に依存します). 例えばNexus Launcherであればリストの並ぶ方向はランチャーアイコンの位置によって変わります.

Shortcutの期限と再利用

ショートカットはユーザに特定のアクションへ素早くアクセスさせるための手段です. それぞれのショートカットはそれを特定するために一意で不変なIDを割り当てられます. このIDはショートカットを登録するアプリが任意に指定できますが, 次の点を考慮し, これを厳守する必要があります.

  • ピン留めされたショートカットに”残る性質”がある
  • バックアップ/リストアで他端末に復元される可能性
  • 古いバージョンで登録した既にサポートしないショートカットが新しいバージョンのアプリに対して実行される

ショートカットに向かないパラメータ

ショートカットはピン留めされると”残ります”. そして, 異なる端末にリストアされる可能性もあります.
例えば, Registration IDのような期限付きの情報をショートカットに紐付けるなどした場合, それが期限切れとなっている可能性を考慮する必要があります.
また, 端末固有の情報(IMEIなど)は異なる端末にリストアされた場合に間違った情報となることに注意が必要です.

ショートカットに登録しておく情報は恒久的に変わらない類のものであれば問題ないのですが, そうでない場合はショートカットの情報が古くなったケースや, 端末の状況が変わりショートカットの情報が正しいものではなくなるケースを考慮したコードを用意しておく必要があります.
そういったコードは, 一般的に起動されるアクティビティのonCreateに記述します.

Dynamic Shortcut IDを無理やりStatic Shortcut IDに

Dynamic Shortcutで使用されているIDを, アプリバージョンアップを経てStatic ShortcutのIDで再利用した場合の仕様は現状記載がありません.
Android7.1 emulatorのNexus Launcherアプリで確認する限り, すでに登録されているDynamic Shortcut IDをStatic Shortcutで上書きしようとしても失敗し, Dynamic/Static Shortcutは両方存在しなくなりました.
事前にDynamic Shortcutをピン留めしていた場合, これはDynamic Shortcutのピン留めショートカットとして振る舞うため無効化が可能な状態になります.

このあたりの動作はLauncherアプリの実装に依存するところもあるでしょうし, そもそもShortcut IDの一意性や永続性を厳守すれば考えなくてもよい問題です.
Shortcut IDは一意に, 意味が変わるようであれば新規IDを払い出し, IDの再利用は避けるのが吉です.

Launcher Applicationの実装

Android7.1の端末であればApp Shortcut機能を持ったランチャーアプリが必ず搭載されているわけではありません. App Shortcutの機能をサポートするかどうか, ピン留めをサポートするか, App Shortcutをどのようなジェスチャーを契機に表示するかなど, そのほとんどがランチャーアプリに依存します. ショートカットのShort labelも必ず表示されるとは限りません.

Shortcutのセキュリティ

他のアプリはあなたのショートカットメタデータにアクセスすることができませんが, ランチャーアプリに限ってはこれにアクセスすることが可能です. そのため, このメタデータにユーザ情報などのセンシティブなデータを含めないようにすべきです.

以上です.

2016/10/22

Android: AppShortcutの状態をログで確認する方法

! warning ! 
この投稿はAndroid 7.1 Developer Preview1 - App Shortcutの内容をもとにしています. 
今後のバージョンアップで内容が変更されている可能性にご注意ください.

App Shortcutについては下記の投稿もご参考ください.

ショートカットの情報をログ上から確認, 分析することができます. 以下はそのログを取得する方法と確認方法です.

ショートカットサービスの状態をダンプするコマンド

adb shell dumpsys shortcut

試験環境

  • Launcherアプリ: com.google.android.apps.nexuslauncher/.NexusLauncherActivity
  • ショートカット登録元アプリ: com.example.android.shortcutsample
  • Static Shortcut ID: add_website
  • Dynamic Shortcut ID: http://www.android.com
  • ピン留めされたDynamic Shortcut ID: http://www.android.com
  • 動作確認アプリ: GitHub - android-AppShortcuts
  • デバイス: Android7.1 エミュレータ
DUMP OF SERVICE shortcut:
Now: [1477132500708] 2016-10-22 19:35:00  Raw last reset: [1477131784076] 2016-10-22 19:23:04  Last reset: [1477131784076] 2016-10-22 19:23:04  Next reset: [1477218184076] 2016-10-23 19:23:04  Config:    Max icon dim: 252
    Icon format: PNG
    Icon quality: 100
    saveDelayMillis: 3000
    resetInterval: 86400000
    maxUpdatesPerInterval: 10
    maxShortcutsPerActivity: 5

  Stats:
    getHomeActivities(): count=8, total=5ms, avg=0.6ms
    Launcher permission check: count=169, total=46ms, avg=0.3ms
    getPackageInfo(): count=0, total=0ms, avg=0.0ms
    getPackageInfo(SIG): count=3, total=1ms, avg=0.3ms
    getApplicationInfo: count=41, total=12ms, avg=0.3ms
    cleanupDanglingBitmaps: count=0, total=0ms, avg=0.0ms
    getActivity+metadata: count=38, total=3ms, avg=0.1ms
    getInstalledPackages: count=1, total=11ms, avg=11.0ms
    checkPackageChanges: count=1, total=181ms, avg=181.0ms
    getApplicationResources: count=24, total=6ms, avg=0.3ms
    resourceNameLookup: count=21, total=16ms, avg=0.8ms
    getLauncherActivity: count=1, total=0ms, avg=0.0ms
    checkLauncherActivity: count=117, total=42ms, avg=0.4ms
    isActivityEnabled: count=64, total=7ms, avg=0.1ms
    packageUpdateCheck: count=169, total=80ms, avg=0.5ms
    asyncPreloadUserDelay: count=1, total=1ms, avg=1.0ms

  #Failures: 0

  User: 0  Known locales: en-US  Last app scan: [1477131791206] 2016-10-22 19:23:11  Last app scan FP: Android/sdk_google_phone_x86_64/generic_x86_64:7.1.1/NPF10D/3354678:userdebug/test-keys
      Cached launcher: ComponentInfo{com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity}
      Last known launcher: ComponentInfo{com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity}

      Launcher: com.google.android.apps.nexuslauncher  Package user: 0  Owner user: 0

        PackageInfo:
          IsShadow: false
          Version: -1
          Last package update time: 0


        Package: com.example.android.shortcutsample  User: 0
          Pinned: add_website

      Package: com.google.android.apps.messaging  UID: 10056
        Calls: 0
        Last known FG: 734535
        Last reset: [1477131784076] 2016-10-22 19:23:04

        PackageInfo:
          IsShadow: false
          Version: 19519170
          Last package update time: 1476484657000

        Shortcuts:
          ShortcutInfo {id=manifest-shortcut-new_message, flags=0x1a4 [ImMIrSr], packageName=com.google.android.apps.messaging, activity=ComponentInfo{com.google.android.apps.messaging/com.google.android.apps.messaging.ui.ConversationListActivity}, shortLabel=New conversation, resId=2131362572[shortcut_new_message_text], longLabel=New conversation, resId=2131362572[shortcut_new_message_text], disabledMessage=Shortcut disabled, resId=2131362573[shortcut_disabled_text], categories={android.shortcut.media, android.shortcut.conversation}, icon=null, rank=0, timestamp=1477131796298, intents=[Intent { act=android.intent.action.VIEW flg=0x1000c000 cmp=com.google.android.apps.messaging/.ui.conversationlist.ConversationListActivity }/PersistableBundle[{via_shortcut=true}], Intent { act=android.intent.action.VIEW cmp=com.google.android.apps.messaging/.ui.conversation.ConversationActivity }/PersistableBundle[{via_shortcut=true}]], extras=null, iconRes=2130838596[drawable/ic_new_message_48dp_clr], bitmapPath=null}
        Total bitmap size: 0 (0 B)

      Package: com.android.contacts  UID: 10003
        Calls: 0
        Last known FG: 0
        Last reset: [1477131784076] 2016-10-22 19:23:04

        PackageInfo:
          IsShadow: false
          Version: 10422
          Last package update time: 1476485678000

        Shortcuts:
          ShortcutInfo {id=shortcut-add-contact, flags=0x1a4 [ImMIrSr], packageName=com.android.contacts, activity=ComponentInfo{com.android.contacts/com.android.contacts.activities.PeopleActivity}, shortLabel=Add Contact, resId=2131427691[menu_new_contact_action_bar], longLabel=null, resId=0[null], disabledMessage=null, resId=0[null], categories=null, icon=null, rank=0, timestamp=1477131791323, intents=[Intent { act=android.intent.action.INSERT dat=content://com.android.contacts/contacts flg=0x1000c000 cmp=com.android.contacts/.activities.CompactContactEditorActivity }/null], extras=null, iconRes=2130837528[drawable/ic_add_circle_24dp], bitmapPath=null}
        Total bitmap size: 0 (0 B)

      Package: com.example.android.shortcutsample  UID: 10071
        Calls: 0
        Last known FG: 734536
        Last reset: [1477131784076] 2016-10-22 19:23:04

        PackageInfo:
          IsShadow: false
          Version: 2
          Last package update time: 1477132350724

        Shortcuts:
          ShortcutInfo {id=add_website, flags=0x1a6 [ImMPIrSr], packageName=com.example.android.shortcutsample, activity=ComponentInfo{com.example.android.shortcutsample/com.example.android.appshortcuts.Main}, shortLabel=Add Website, resId=2131099649[add_new_website_short], longLabel=Add New Website, resId=2131099648[add_new_website], disabledMessage=null, resId=0[null], categories=null, icon=null, rank=0, timestamp=1477132350808, intents=[Intent { act=com.example.android.shortcutsample.ADD_WEBSITE flg=0x1000c000 cmp=com.example.android.shortcutsample/com.example.android.appshortcuts.Main }/null], extras=null, iconRes=2130837504[drawable/add], bitmapPath=null}
          ShortcutInfo {id=http://www.android.com, flags=0x85 [DIrSr], packageName=com.example.android.shortcutsample, activity=ComponentInfo{com.example.android.shortcutsample/com.example.android.appshortcuts.Main}, shortLabel=www.android.com, resId=0[null], longLabel=http://www.android.com, resId=0[null], disabledMessage=null, resId=0[null], categories=null, icon=null, rank=0, timestamp=1477132350808, intents=[Intent { act=android.intent.action.VIEW dat=http://www.android.com/... }/null], extras=PersistableBundle[{com.example.android.shortcutsample.EXTRA_LAST_REFRESH=1477131998183}], iconRes=2130837505[drawable/link], bitmapPath=null}
        Total bitmap size: 0 (0 B)

      Package: com.google.android.apps.maps  UID: 10047
        Calls: 0
        Last known FG: 0
        Last reset: [1477131784076] 2016-10-22 19:23:04

        PackageInfo:
          IsShadow: false
          Version: 934100020
          Last package update time: 1476484652000

        Shortcuts:
          ShortcutInfo {id=manifest_home, flags=0x1a4 [ImMIrSr], packageName=com.google.android.apps.maps, activity=ComponentInfo{com.google.android.apps.maps/com.google.android.maps.MapsActivity}, shortLabel=Home, resId=2131821417[HOME_LOCATION], longLabel=null, resId=0[null], disabledMessage=null, resId=0[null], categories=null, icon=null, rank=0, timestamp=1477131791304, intents=[Intent { act=android.intent.action.VIEW flg=0x1000c000 cmp=com.google.android.apps.maps/.LauncherShortcutActivity }/PersistableBundle[{extra_destination_home_key=true}]], extras=null, iconRes=2130838242[drawable/ic_qu_home_launcher], bitmapPath=null}
          ShortcutInfo {id=manifest_work, flags=0x1a4 [ImMIrSr], packageName=com.google.android.apps.maps, activity=ComponentInfo{com.google.android.apps.maps/com.google.android.maps.MapsActivity}, shortLabel=Work, resId=2131822823[WORK_LOCATION], longLabel=null, resId=0[null], disabledMessage=null, resId=0[null], categories=null, icon=null, rank=1, timestamp=1477131791304, intents=[Intent { act=android.intent.action.VIEW flg=0x1000c000 cmp=com.google.android.apps.maps/.LauncherShortcutActivity }/PersistableBundle[{extra_destination_work_key=true}]], extras=null, iconRes=2130838389[drawable/ic_qu_work_launcher], bitmapPath=null}
        Total bitmap size: 0 (0 B)

      Package: com.android.chrome  UID: 10030
        Calls: 0
        Last known FG: 0
        Last reset: [1477131784076] 2016-10-22 19:23:04

        PackageInfo:
          IsShadow: false
          Version: 278513462
          Last package update time: 1476484675000

        Shortcuts:
          ShortcutInfo {id=new-tab-shortcut, flags=0x1a4 [ImMIrSr], packageName=com.android.chrome, activity=ComponentInfo{com.android.chrome/com.google.android.apps.chrome.Main}, shortLabel=New tab, resId=2131362518[menu_new_tab], longLabel=null, resId=0[null], disabledMessage=null, resId=0[null], categories=null, icon=null, rank=0, timestamp=1477131791288, intents=[Intent { act=chromium.shortcut.action.OPEN_NEW_TAB flg=0x1000c000 cmp=com.android.chrome/org.chromium.chrome.browser.LauncherShortcutActivity }/null], extras=null, iconRes=2130838003[drawable/shortcut_newtab], bitmapPath=null}
          ShortcutInfo {id=new-incognito-tab-shortcut, flags=0x1a4 [ImMIrSr], packageName=com.android.chrome, activity=ComponentInfo{com.android.chrome/com.google.android.apps.chrome.Main}, shortLabel=Incognito Tab, resId=2131362608[accessibility_tabstrip_incognito_identifier], longLabel=New incognito tab, resId=2131362519[menu_new_incognito_tab], disabledMessage=null, resId=0[null], categories=null, icon=null, rank=1, timestamp=1477131791288, intents=[Intent { act=chromium.shortcut.action.OPEN_NEW_INCOGNITO_TAB flg=0x1000c000 cmp=com.android.chrome/org.chromium.chrome.browser.LauncherShortcutActivity }/null], extras=null, iconRes=2130838002[drawable/shortcut_incognito], bitmapPath=null}
        Total bitmap size: 0 (0 B)

      Package: com.android.settings  UID: 1000
        Calls: 0
        Last known FG: 734537
        Last reset: [1477131784076] 2016-10-22 19:23:04

        PackageInfo:
          IsShadow: false
          Version: 25
          Last package update time: 1476485764000

        Shortcuts:
          ShortcutInfo {id=manifest-shortcut-wifi, flags=0x1a4 [ImMIrSr], packageName=com.android.settings, activity=ComponentInfo{com.android.settings/com.android.settings.Settings}, shortLabel=Wi‑Fi, resId=2131624867[wifi_settings], longLabel=null, resId=0[null], disabledMessage=null, resId=0[null], categories=null, icon=null, rank=0, timestamp=1477131806966, intents=[Intent { act=android.settings.WIFI_SETTINGS flg=0x1000c000 }/null], extras=null, iconRes=2130837751[drawable/ic_shortcut_wireless], bitmapPath=null}
          ShortcutInfo {id=manifest-shortcut-battery, flags=0x1a4 [ImMIrSr], packageName=com.android.settings, activity=ComponentInfo{com.android.settings/com.android.settings.Settings}, shortLabel=Battery, resId=2131625951[power_usage_summary_title], longLabel=null, resId=0[null], disabledMessage=null, resId=0[null], categories=null, icon=null, rank=2, timestamp=1477131806967, intents=[Intent { act=android.intent.action.POWER_USAGE_SUMMARY flg=0x1000c000 }/null], extras=null, iconRes=2130837749[drawable/ic_shortcut_battery], bitmapPath=null}
          ShortcutInfo {id=manifest-shortcut-data-usage, flags=0x1a4 [ImMIrSr], packageName=com.android.settings, activity=ComponentInfo{com.android.settings/com.android.settings.Settings}, shortLabel=Data usage, resId=2131626277[data_usage_summary_title], longLabel=null, resId=0[null], disabledMessage=null, resId=0[null], categories=null, icon=null, rank=1, timestamp=1477131806966, intents=[Intent { act=android.intent.action.MAIN flg=0x1000c000 cmp=com.android.settings/.Settings$DataUsageSummaryActivity }/null], extras=null, iconRes=2130837750[drawable/ic_shortcut_data_usage], bitmapPath=null}
        Total bitmap size: 0 (0 B)

      Bitmap directories: 
        Path: bitmaps/ has 0 files, size=0 (0 B)

  UID state:
    UID=1001 state=0  [FG]  last FG=21184
    UID=1037 state=16  last FG=0
    UID=10001 state=16  last FG=584955
    UID=10002 state=16  last FG=0
    UID=10005 state=16  last FG=0
    UID=10006 state=16  last FG=0
    UID=10008 state=16  last FG=160015
    UID=10010 state=3  [FG]  last FG=24883
    UID=10012 state=3  [FG]  last FG=719232
    UID=10013 state=16  last FG=0
    UID=10014 state=2  [FG]  last FG=719096
    UID=10017 state=16  last FG=25336
    UID=10021 state=0  [FG]  last FG=21184
    UID=10022 state=2  [FG]  last FG=719232
    UID=10027 state=16  last FG=0
    UID=10030 state=16  last FG=0
    UID=10033 state=16  last FG=0
    UID=10036 state=16  last FG=0
    UID=10041 state=16  last FG=0
    UID=10043 state=7  last FG=0
    UID=10047 state=16  last FG=0
    UID=10055 state=16  last FG=0
    UID=10056 state=16  last FG=33915
    UID=10058 state=3  [FG]  last FG=734456
    UID=10060 state=16  last FG=0
    UID=10071 state=16  last FG=715122
--------- 0.008s was the duration of dumpsys shortcut

ログ解析

maxShortcutsPerActivity: 5

1アプリが持てるStatic/Dynamic Shortcutの数をログ上から確認できる.

Launcher: com.google.android.apps.nexuslauncher Package user: 0 Owner user: 0

ユーザが使用しているランチャーアプリをここで確認できる. 実際にショートカット機能をどこまでサポートしているアプリであるのかを知るために重要な情報になる.

Package: com.example.android.shortcutsample User: 0
Pinned: add_website

ショートカットの情報がログから得ることができる.
今回用意したアプリ(com.example.android.shortcutsample)が登録したadd_websiteのショートカットがピン留めされていることが見てとれる.

Package: com.example.android.shortcutsample UID: 10071
Calls: 0
Last known FG: 734536
Last reset: [1477131784076] 2016-10-22 19:23:04

ランチャーアプリが最後に当該アプリがフォアグラウンドに遷移したことを検知した時刻を知ることができる.
ショートカットの更新がRate-Limitに達している可能性があるかをこの時刻から掴むことができる.

Shortcuts:
ShortcutInfo {id=add_website, flags=0x1a6 [ImMPIrSr], packageName=com.example.android.shortcutsample, activity=ComponentInfo{com.example.android.shortcutsample/com.example.android.appshortcuts.Main}, shortLabel=Add Website, resId=2131099649[add_new_website_short], longLabel=Add New Website, resId=2131099648[add_new_website], disabledMessage=null, resId=0[null], categories=null, icon=null, rank=0, timestamp=1477132350808, intents=[Intent { act=com.example.android.shortcutsample.ADD_WEBSITE flg=0x1000c000 cmp=com.example.android.shortcutsample/com.example.android.appshortcuts.Main }/null], extras=null, iconRes=2130837504[drawable/add], bitmapPath=null}
ShortcutInfo {id=http://www.android.com, flags=0x85 [DIrSr], packageName=com.example.android.shortcutsample, activity=ComponentInfo{com.example.android.shortcutsample/com.example.android.appshortcuts.Main}, shortLabel=www.android.com, resId=0[null], longLabel=http://www.android.com, resId=0[null], disabledMessage=null, resId=0[null], categories=null, icon=null, rank=0, timestamp=1477132350808, intents=[Intent { act=android.intent.action.VIEW dat=http://www.android.com/… }/null], extras=PersistableBundle[{com.example.android.shortcutsample.EXTRA_LAST_REFRESH=1477131998183}], iconRes=2130837505[drawable/link], bitmapPath=null}
Total bitmap size: 0 (0 B)

アプリが登録しているショートカット情報の詳細を確認できる.

UID state:
:
UID=10071 state=16 last FG=715122

各アプリの状態についても確認できる.

以上.

Android: App Shortcut - ShortcutManager

! warning ! 
この投稿はAndroid 7.1 Developer Preview1 - App Shortcutの内容をもとにしています. 
今後のバージョンアップで内容が変更されている可能性にご注意ください.

本稿は下記ドキュメントの翻訳です.
https://developer.android.com/reference/android/content/pm/ShortcutManager.html

ShortcutManager

The ShortcutManager manages “launcher shortcuts” (or simply “shortcuts”). Shortcuts provide users with quick access to activities other than an application’s main activity in the currently-active launcher. For example, an email application may publish the “compose new email” action, which will directly open the compose activity. The ShortcutInfo class contains information about each of the shortcuts themselves.

ShortcutManagerは”Launcher shortcut”(または単に”ショートカット”)を管理します. ショートカットはランチャーにあるアプリのMainアクティビティ以外のアクティビティに素早くアクセスする手段をユーザに提供します. 例えば, E-Mailアプリケーションであれば”メールを作成する”アクションを提供し, 直接メールを作成するアクティビティを起動します. ShortcutInfoクラスはショートカットに関する情報を保持します.

Dynamic Shortcuts and Manifest Shortcuts

There are two ways to publish shortcuts: manifest shortcuts and dynamic shortcuts.

  • Manifest shortcuts are declared in a resource XML, which is referenced in the publisher application’s AndroidManifest.xml file. Manifest shortcuts are published when an application is installed, and the details of these shortcuts change when an application is upgraded with an updated XML file. Manifest shortcuts are immutable, and their definitions, such as icons and labels, cannot be changed dynamically without upgrading the publisher application.
  • Dynamic shortcuts are published at runtime using the ShortcutManager APIs. Applications can publish, update, and remove dynamic shortcuts at runtime.

ショートカットを提供する方法にはManifest shortcutとDynamic shortcutの2つがあります.

  • Manifest shortcut: XMLリソースファイルで定義されるショートカット. このリソースファイルはショートカットを提供するアプリのAndroidManifest.xmlから参照されます. Manifest shortcutはアプリがインストールされた時やアプリがアップデートされた際に提供される. Manifest shortcutは不変で, 事前に専用のアイコンやラベルが定義され, アプリをアップデートした場合を除いてこれを動的に変更することができません.
  • Dynamic shortcut: ShortcutManagerのAPIを使って動的に提供されます. アプリケーションはショートカットを動的に作成, 更新, 削除することができます.

Only “main” activities—activities that handle the MAIN action and the LAUNCHER category—can have shortcuts. If an application has multiple main activities, these activities will have different sets of shortcuts.

Dynamic shortcuts and manifest shortcuts are shown in the currently active launcher when the user long-presses on an application launcher icon. The actual gesture may be different depending on the launcher application.

Each launcher icon can have at most getMaxShortcutCountPerActivity() number of dynamic and manifest shortcuts combined.

IntentAction.MAINCategory.LAUNCHERを受け取る”Main”なアクティビティだけがショートカットを持つことができます. アプリが複数の”Main”アクティビティを持っていた場合, ことなるショートカットのセットを持つことになります.

Dynamic shortcutとManifest shortcutはランチャーアイコンをロングタップした際に表示されます. ショートカットが表示されるためのジェスチャーは固定ではなくランチャーアプリの実装によって異なる場合があります.

それぞれのショートカットのランチャーアイコンはDynamic shortcutとManifest shortcutを合わせたgetMaxShortcutCountPerActivity()の数だけ指定できる.

Pinning Shortcuts

Launcher applications allow users to “pin” shortcuts so they’re easier to access. Both manifest and dynamic shortcuts can be pinned. Pinned shortcuts cannot be removed by publisher applications; they’re removed only when the user removes them, when the publisher application is uninstalled, or when the user performs the “clear data” action on the publisher application from the device’s Settings application.
However, the publisher application can disable pinned shortcuts so they cannot be started. See the following sections for details.

ランチャーアプリはショートカットの”ピン留め”を容易にできる機能を提供します. Manifest shortcutとDynamic shortcut両方でピン留めは可能です. ピン留めされたショートカットはそれを提供しているアプリから削除することはできません. ピン留めされたショートカットが削除されるのはユーザによる削除操作がなされた場合や, それを提供しているアプリがアンインストールされた場合, または, アプリケーション設定から当該アプリのデータ消去をした場合です.
一方で, ショートカットを提供しているアプリはピン留めされたショートカットを無効化してショートカットからアプリを起動させなくすることができます. 以降のセクションで詳細を説明します.

Updating and Disabling Shortcuts

When a dynamic shortcut is pinned, even when the publisher removes it as a dynamic shortcut, the pinned shortcut will still be visible and launchable. This allows an application to have more than getMaxShortcutCountPerActivity() number of shortcuts.

Dynamic shortcutがピン留めされた場合, それに相当するDynamic shortcutが削除されたとしてもピン留めされたショートカットは残り続け, 依然として起動可能です. これはアプリがgetMaxShortcutCountPerActivity()の数以上のショートカットを持てることを意味します.

For example, suppose getMaxShortcutCountPerActivity() is 5:

  • A chat application publishes 5 dynamic shortcuts for the 5 most recent conversations, “c1” - “c5”.
  • The user pins all 5 of the shortcuts.
  • Later, the user has started 3 additional conversations (“c6”, “c7”, and “c8”), so the publisher application re-publishes its dynamic shortcuts. The new dynamic shortcut list is: “c4”, “c5”, “c6”, “c7”, and “c8”. The publisher application has to remove “c1”, “c2”, and “c3” because it can’t have more than 5 dynamic shortcuts.
  • However, even though “c1”, “c2” and “c3” are no longer dynamic shortcuts, the pinned shortcuts for these conversations are still available and launchable.
  • At this point, the user can access a total of 8 shortcuts that link to activities in the publisher application, including the 3 pinned shortcuts, even though it’s allowed to have at most 5 dynamic shortcuts.
  • The application can use updateShortcuts(List) to update any of the existing 8 shortcuts, when, for example, the chat peers’ icons have changed.

例えば, getMaxShortcutCountPerActivity()が5と仮定すると…

  • チャットアプリは5つのDynamic shortcutを提供し, 間近の5つのチャット”c1” - “c5”を提供する
  • ユーザはそれらのDynamic shortcutを5つ全てピン留めする
  • そのあと, ユーザは新たに3つのチャットを開始する(”c6”, “c7”と”c8”). チャットアプリはこれら3つのチャットをDynamic shortcutとして再登録する. 新しいDynamic shortcutのリストは”c4”, “c5”, “c6”, “c7”, “c8”になる. この時, ショートカットを提供するチャットアプリは5つ以上のショートカットを持つことができないので”c1”, “c2”, “c3”のショートカットを削除する必要がある.
  • しかし, “c1”, “c2”, “c3”のチャットルームが使われなくなり, Dynamic shortcutから削除されたとしても, ピン留めされたそれらのチャットは依然として有効で, そこからアプリを起動しようとすることもできる.
  • ここでのポイントは, ユーザは合計で8つのショートカットにアクセスでき, そのうちの3つはピン留めされたショートカットであり, 5つはDynamic shortcutであるということ.
  • アプリはupdateShortcuts(List)を使って登録済みの8つのショートカットを更新することができる(例えばチャットルームのアイコン変更など)

The addDynamicShortcuts(List) and setDynamicShortcuts(List) methods can also be used to update existing shortcuts with the same IDs, but they cannot be used for updating non-dynamic, pinned shortcuts because these two methods try to convert the given lists of shortcuts to dynamic shortcuts.

addDynamicShortcuts(List)setDynamicShortcuts(List)メソッドは既に登録済みの同じIDのショートカットを更新することができます. しかし, Dynamic shortcutでないものや, ピン留めされたショートカットは更新することができません. これらのメソッドは引数で与えられたショートカットリストをDyamic shortcutに変換することを試みるものであるからです.

Disabling Manifest Shortcuts

When an application is upgraded and the new version no longer uses a manifest shortcut that appeared in the previous version, this deprecated shortcut will no longer be published as a manifest shortcut.
If the deprecated shortcut is pinned, then the pinned shortcut will remain on the launcher, but it will be disabled automatically. Note that, in this case, the pinned shortcut is no longer a manifest shortcut, but it’s still immutable and cannot be updated using the ShortcutManager APIs.

アプリがアップデートされて, 過去バージョンでは使えていたManifest shortcutが新しいバージョンで使えなくなった場合, この使えなくなったショートカットは提供されなくなります. もしこのショートカットがピン留めされていた場合はランチャーに残り続けます. しかし使えなくなったショートカットは自動で無効化されます. 注意すべき点として, このケースにおいてピン留めされたショートカットはManifest shortcutであり, 使えなくなったもののそれは不変であり, ShortcutManager APIを使っても更新することができないということです.

Disabling Dynamic Shortcuts

Sometimes pinned shortcuts become obsolete and may not be usable. For example, a pinned shortcut to a group chat will be unusable when the associated group chat is deleted. In cases like this, applications should use disableShortcuts(List), which will remove the specified dynamic shortcuts and also make any specified pinned shortcuts un-launchable. The disableShortcuts(List, CharSequence) method can also be used to disabled shortcuts and show users a custom error message when they attempt to launch the disabled shortcuts.

ある時, ピン留めされたDynamic shortcutが使われなくなり無効化された場合. 例えば所属するチャットグループが削除されて使えなくなったようなケースで はdisableShortcuts(List)を使用すべきです. これによって特定のショートカットを削除して, ピン留めされたショートカットであっても起動できなくする(無効化する)ことができます. disableShortcuts(List, CharSequence)メソッドはショートカットを無効化して, これを起動しようとした時に好きなエラーメッセージを表示させることができます.

Publishing Manifest Shortcuts

In order to add manifest shortcuts to your application, first add <meta-data android:name="android.app.shortcuts" /> to your main activity in AndroidManifest.xml:

アプリにManifest shortcutを追加するには, まずAndroidManifest.xmlで<meta-data android:name="android.app.shortcuts" />をMainアクティビティに追加します.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.example.myapplication">
   <application . . .>
     <activity android:name="Main">
       <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>
 </manifest>

Then, define your application’s manifest shortcuts in the res/xml/shortcuts.xml file:

そして, アプリのManifest shortcutを/res/xml/shortcuts.xmlファイルとして定義します.

 <shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
   <shortcut
     android:shortcutId="compose"
     android:enabled="true"
     android:icon="@drawable/compose_icon"
     android:shortcutShortLabel="@string/compose_shortcut_short_label1"
     android:shortcutLongLabel="@string/compose_shortcut_long_label1"
     android:shortcutDisabledMessage="@string/compose_disabled_message1"
     >
     <intent
       android:action="android.intent.action.VIEW"
       android:targetPackage="com.example.myapplication"
       android:targetClass="com.example.myapplication.ComposeActivity" />
     <!-- more intents can go here; see below -->
     <categories android:name="android.shortcut.conversation" />
   </shortcut>
   <!-- more shortcuts can go here -->
 </shortcuts>

The following list includes descriptions for the different attributes within a manifest shortcut:

android:shortcutId
Mandatory shortcut ID

android:enabled
Default is true. Can be set to false in order to disable a manifest shortcut that was published in a previous version and and set a custom disabled message. If a custom disabled message is not needed, then a manifest shortcut can be simply removed from the XML file rather than keeping it with enabled=”false”.

android:icon
Shortcut icon.

android:shortcutShortLabel
Mandatory shortcut short label. See setShortLabel(CharSequence).

android:shortcutLongLabel
Shortcut long label. See setLongLabel(CharSequence).

android:shortcutDisabledMessage
When android:enabled is set to false, this attribute is used to display a custom disabled message.

intent
Intent to launch when the user selects the shortcut. android:action is mandatory. See Using intents for the other supported tags. You can provide multiple intents for a single shortcut so that an activity is launched with other activities in the back stack. See TaskStackBuilder for details.

categories
Specify shortcut categories. Currently only SHORTCUT_CATEGORY_CONVERSATION is defined in the framework.

Manifest shortcutで定義されるそれぞれの属性について:

android:shortcutId
ショートカットID(必須の属性)

android:enabled
デフォルト値はtrue. これをfalseに設定することで過去バージョンで提供していたManifest shortcutが今バージョンから提供されなくなった場合の無効化を行い, カスタムエラーメッセージを設定することができる. もし, カスタムエラーメッセージが必要ない場合は単純にこのショートカット定義をXMLから削除すればよい. 引き続きショートカットを保持しておきたいならenabled="false"とする.

android:icon
ショートカットアイコン

android:shortcutShortLabel
短いショートカットラベル(必須の属性). setShortLabel(CharSequence)を参照.

android:shortcutLongLabel
長いショートカットラベル. setLongLabel(CharSequence)を参照.

android:shortcutDisabledMessage
android:enabledfalseが設定されている場合, この属性はカスタムエラーメッセージとして使用される.

intent
ユーザがショートカットを選択した際に起動されるIntent. android:actionは必須の属性である. Using intentsintentタグがサポートする他の属性が記載されている. あなたは1つのショートカットに対してmultiple-intentを設定することができる. これによりバックスタックに積むアクティビティを指定しつつアクティビティを起動することができる. TaskStackBuilderにより詳細が記載されている.

categories
ショートカットのカテゴリを指定する. 現状ではSHORTCUT_CATEGORY_CONVERSATIONのみが定義されている.

Publishing Dynamic Shortcuts

Applications can publish dynamic shortcuts with setDynamicShortcuts(List) or addDynamicShortcuts(List). The updateShortcuts(List) method can also be used to update existing, mutable shortcuts. Use removeDynamicShortcuts(List) or removeAllDynamicShortcuts() to remove dynamic shortcuts.

Example:

アプリはDynamic shortcutをsetDynamicShortcuts(List)addDynamicShortcuts(List)で登録することができます. updateShortcuts(List)メソッドは既に登録済みの変更可能なショートカットの更新に使うことができます. removeDynamicShortcuts(List)removeAllDynamicShortcuts()はDynamic shortcutの削除に使えます.
下記はこれらの使用例です:

ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);

 ShortcutInfo shortcut = new ShortcutInfo.Builder(this, "id1")
     .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.mysite.com/")))
     .setShortLabel("Web site")
     .setLongLabel("Open the web site")
     .setIcon(Icon.createWithResource(context, R.drawable.icon_website))
     .build();

 shortcutManager.setDynamicShortcuts(Arrays.asList(shortcut));

Shortcut Intents

Dynamic shortcuts can be published with any set of Intent flags. Typically, FLAG_ACTIVITY_CLEAR_TASK is specified, possibly along with other flags; otherwise, if the application is already running, the application is simply brought to the foreground, and the target activity may not appear.
The setIntents(Intent[]) method can be used instead of setIntent(Intent) with TaskStackBuilder in order to launch an activity with other activities in the back stack. When the user selects a shortcut to load an activity with a back stack, then presses the back key, a “parent” activity will be shown instead of the user being navigated back to the launcher.

Dynamic shortcutはIntentフラグを指定して登録することができます. 一般的にはFLAG_ACTIVITY_CLEAR_TASKが他のフラグと一緒に指定されます. もしアプリケーションが既に実行中であれば単に前面に遷移して, 起動ターゲットであったアクティビティは起動されてきません.

setIntents(Intent[])メソッドはバックスタックにあらかじめアクティビティ順序を定義して起動するTaskStackBuildersetIntent(Intent)の組み合わせの代わりに使うことができます. ユーザがショートカットを選択した時に, アクティビティをバックスタック付きで起動することができ, バックキーでアクティビティを破棄した場合に, ランチャーではなく親アクティビティを表示させるようなことができます.

Manifest shortcuts can also have multiple intents to achieve the same effect. In order to associate multiple Intent objects with a shortcut, simply list multiple elements within a single element. The last intent specifies what the user will see when they launch a shortcut.

Manifest shortcuts cannot have custom intent flags. The first intent of a manifest shortcut will always have FLAG_ACTIVITY_NEW_TASK and FLAG_ACTIVITY_CLEAR_TASK set. This means, when the application is already running, all the existing activities will be destroyed when a manifest shortcut is launched. If this behavior is not desirable, you can use a trampoline activity, or an invisible activity that starts another activity in onCreate(Bundle), then calls finish(). The first activity should include an attribute setting of android:taskAffinity=”” in the application’s AndroidManifest.xml file, and the intent within the manifest shortcut should point at this first activity.

Manifest shortcutも同じ効果をもたらすmultiple-intentを持つことができます. multiple-intentをショートカットに指定するには, 複数の<intent>要素を1つの<shortcut>要素に定義します. 最後に定義されたIntentがショートカット選択時にユーザから見えるアクティビティとなります.

Manifest shortcutはintentフラグを好きに指定することができません. まず, Manifest shortcutのintentは常にFLAG_ACTIVITY_NEW_TASKFLAG_ACTIVITY_CLEAR_TASKのフラグがセットされます. これは, Manifest shortcutから起動される場合, アプリが実行中であれば既にあるアクティビティが破棄されてからショートカットによるアクティビティが起動されることを意味しています. この動作を望まないのであれば, トランポリンアクティビティ(訳注:起動Intentをハンドルする目的だけのアクティビティ)を使うか, 透明アクティビティから別のアクティビティをonCreate(Bundle)で起動して即finish()するかです. 最初のアクティビティの属性にはandroid:taskAffinity=""を含めておく必要があります. そして, ショートカットからIntentでこのアクティビティを指定して起動します.

Showing New Information in a Shortcut

In order to avoid confusion, you should not use %29”>updateShortcuts(List) to update a shortcut so that it contains conceptually different information.

For example, a phone application may publish the most frequently called contact as a dynamic shortcut. Over time, this contact may change; when it does, the application should represent the changed contact with a new shortcut that contains a different ID, using either setDynamicShortcuts(List) or addDynamicShortcuts(List), rather than updating the existing shortcut with updateShortcuts(List). This is because when the shortcut is pinned, changing it to reference a different contact will likely confuse the user.

On the other hand, when the contact’s information has changed, such as the name or picture, the application should use updateShortcuts(List) so that the pinned shortcut is updated too.

混乱を避けるためにupdateShortcuts(List)を使って異なる情報の内容でショートカットを更新するべきではありません. 例えば, Phoneアプリはよく連絡する相手へのDynamic shortcutをショートカットに登録します. 時間が過ぎて, 連絡先が変更された場合, アプリは新しいショートカットと新しいショートカットIDをもって変更後の連絡先へのショートカットを登録します. この時, updateShortcuts(List)を使うのではなくsetDynamicShortcuts(List)addDynamicShortcuts(List)のどちらかを使うようにします.
こうする理由は, ショートカットがピン留めされていた場合, これを変更すると異なる連絡先を参照することになるためユーザが混乱してしまいます.

別のケースとして, 顔写真や名前といった類の連絡先情報が変更された場合には, updateShortcuts(List)を使ってピン留めされたショートカットを更新するべきです.

Shortcut Display Order

When the launcher displays the shortcuts that are associated with a particular launcher icon, the shortcuts should appear in the following order:

  • First show manifest shortcuts (if isDeclaredInManifest() is true), and then show dynamic shortcuts (if isDynamic() is true).
  • Within each category of shortcuts (manifest and dynamic), sort the shortcuts in order of increasing rank according to getRank().

ランチャーがショートカットを表示する際にはそれに紐づく指定のランチャーアイコンが表示されます. ショートカットの並び順は次の通りです:

  • まず最初にManifest shortcut(isDeclaredInManifest()true)を表示し, 続いてDynamic shortcut(isDynamic()true)を表示する.
  • それぞれのショートカットカテゴリ(Manifest shortcutとDynamic shortcut)の中で, getRank()の値にならって並び替えられる.

Shortcut ranks are non-negative sequential integers that determine the order in which shortcuts appear, assuming that the shortcuts are all in the same category. Ranks of existing shortcuts can be updated with updateShortcuts(List); you can use addDynamicShortcuts(List) and setDynamicShortcuts(List), too.

Ranks are auto-adjusted so that they’re unique for each target activity in each category (dynamic or manifest). For example, if there are 3 dynamic shortcuts with ranks 0, 1 and 2, adding another dynamic shortcut with a rank of 1 represents a request to place this shortcut at the second position. In response, the third and fourth shortcuts move closer to the bottom of the shortcut list, with their ranks changing to 2 and 3, respectively.

ショートカットランクは負の数ではないシーケンシャルな数値で, 同じカテゴリ内でのショートカットの並び順を決定します. 登録済みのショートカットのランクはupdateShortcuts(List)で更新することができます. addDynamicShortcuts(List)setDynamicShortcuts(List)でも同じことが可能です.

ショートカットランクは, ショートカットを登録したそれぞれのアクティビティの各カテゴリ(Manifest shortcutかDynamic shortcut)毎に一意な数値が自動で割り当てられます. 例えば, 3つのDynamic shortcutが0, 1, 2のランクを持っていたとして, ここに新しいランク1のDynamic shortcutを追加した場合, 新しいショートカットは2番目に配置されることになります. 3番目と4番目のショートカットはショートカットリストの下方向に移動されます. そしてそれらのランクは2と3に変更されます.

Rate Limiting

Calls to setDynamicShortcuts(List), addDynamicShortcuts(List), and updateShortcuts(List) may be rate-limited when called by background applications, or applications with no foreground activity or service. When you attempt to call these methods from a background application after exceeding the rate limit, these APIs return false.
Applications with a foreground activity or service are not rate-limited.

Rate-limiting will be reset upon certain events, so that even background applications can call these APIs again until the rate limit is reached again. These events include the following:

  • When an application comes to the foreground.
  • When the system locale changes.
  • When the user performs an “inline reply” action on a notification.

When rate-limiting is active, isRateLimitingActive() returns true.

setDynamicShortcuts(List), addDynamicShortcuts(List), updateShortcuts(List)のAPIをアプリがバックグランドの状態やアクティビティがないアプリ, またはサービスから呼び出す場合, 呼び出し頻度の限度に注意する必要があります. 呼び出し頻度の限度に到達した場合, これらのメソッドはfalseを返します.

呼び出し頻度の限度は下記のイベントによりリセットされ, アプリはバックグランドなどの状態でも再びAPIを呼び出すことができるようになります.

  • アプリがフォアグラウンド状態に遷移した
  • システムのロケールが変更された
  • ユーザがNotificationのインライン返信を行った

APIの呼び出し頻度が限度に到達している場合, isRateLimitingActive()trueを返します.

Resetting rate-limiting for testing

If your application is rate-limited during development or testing, you can use the “Reset ShortcutManager rate-limiting” development option or the following adb command to reset it:

開発やテスト中に呼び出し限度回数に到達した場合に, 設定アプリの開発者オプションから”Reset ShortcutManager rate-limiting”を選択するか, 下記のadbコマンドでこれを解除することができます.

 adb shell cmd shortcut reset-throttling [ --user USER-ID ]

Handling System Locale Changes

Applications should update dynamic and pinned shortcuts when the system locale changes using the ACTION_LOCALE_CHANGED broadcast.
When the system locale changes, rate-limiting is reset, so even background applications can set dynamic shortcuts, add dynamic shortcuts, and update shortcuts until the rate limit is reached again.

アプリはACTION_LOCALE_CHANGEDのブロードキャストイベントを検知して, Dynamic shortcutとピン留めされたショートカットを更新する必要があります. ロケールが変更されるとAPIの呼び出し制限は解除されます. バックグラウンドアプリでもDynamic shortcutを追加, 変更することができるようになります.

Backup and Restore

When an application has the android:allowBackup="true" attribute assignment included in its AndroidManifest.xml file, pinned shortcuts are backed up automatically and are restored when the user sets up a new device.

アプリがAndroidManifest.xmlandroid:allowBackup="true"を設定している場合, ピン留めされたショートカットは自動的にシステムによってバックアップされ, 新しいデバイスのセットアップ時に自動的にリストアされます.

Categories of Shortcuts that are Backed Up

  • Pinned shortcuts are backed up. Bitmap icons are not backed up by the system, but launcher applications should back them up and restore them so that the user still sees icons for pinned shortcuts on the launcher. Applications can always use updateShortcuts(List) to re-publish icons.
  • Manifest shortcuts are not backed up, but when an application is re-installed on a new device, they are re-published from the AndroidManifest.xml file, anyway.
  • Dynamic shortcuts are not backed up.

Because dynamic shortcuts are not restored, it is recommended that applications check currently-published dynamic shortcuts using getDynamicShortcuts() each time they are launched, and they should re-publish dynamic shortcuts when necessary.

  • ピン留めされたショートカットはバックアップされる. しかし, Bitmapアイコンはシステムにバックアップされない. ただし, ランチャーアプリはこれらをバックアップ/リストアしてユーザが同じピン留めされたショートカットを見られるようにすべきである. ショートカットの登録元アプリはupdateShortcuts(List)を使っていつでもアイコンを再登録することができる.
  • Manifest shortcutはバックアップされない. ただし, アプリが新しいデバイスに再インストールされるタイミングでショートカットがAndroidManifest.xmlをもとに再登録される.
  • Dynamic shortcutはバックアップされない

Dynamic shortcutがリストアされない理由は, アプリがgetDynamicShortcuts()で現在のDynamic shortcutをアクティビティが起動される際に確認して, 必要に応じてそれらを再登録することが望ましいためです.


 public class MainActivity extends Activity {
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);

         ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);

         if (shortcutManager.getDynamicShortcuts().size() == 0) {
             // Application restored; re-publish dynamic shortcuts.

             if (shortcutManager.getPinnedShortcuts().size() > 0) {
                 // Pinned shortcuts have been restored.  Use updateShortcuts() to make sure
                 // they have up-to-date information.
             }
         }
     }
 }

Backup/restore and shortcut IDs

Because pinned shortcuts are backed up and restored on new devices, shortcut IDs should be meaningful across devices; that is, IDs should contain either stable, constant strings or server-side identifiers, rather than identifiers generated locally that might not make sense on other devices.

ピン留めされたショートカットはバックアップ/リストアされます. 古いデバイス, 新しいデバイスを通してショートカットのIDは意味を持ちます. IDは永続的で変化しないか, サーバサイドで一意であるべきで, ローカルで生成されたIDは他のデバイスにおいては意味を持たない可能性があります.

Report Shortcut Usage and Prediction

Launcher applications may be capable of predicting which shortcuts will most likely be used at a given time by examining the shortcut usage history data.
In order to provide launchers with such data, publisher applications should report the shortcuts that are used with reportShortcutUsed(String) when a shortcut is selected, or when an action equivalent to a shortcut is taken by the user even if it wasn’t started with the shortcut.

For example, suppose a GPS navigation application supports “navigate to work” as a shortcut. It should then report when the user selects this shortcut and when the user chooses to navigate to work within the application itself. This helps the launcher application learn that the user wants to navigate to work at a certain time every weekday, and it can then show this shortcut in a suggestion list at the right time.

ランチャーアプリはどのショートカットがユーザに好かれるかということをショートカットの使用履歴情報から推測する機能をもっている場合があります. ショートカットを登録するアプリはショートカットが選択された場合や, ショートカットから起動されなくてもそれがショートカットの達成しようとするアクションであればreportShortcutUsed(String)を使ってランチャーにそのことをレポートするべきです.

例えば, GPSナビゲーションをサポートしたアプリであれば”職場への経路案内”をショートカットに登録しているかもしれません. アプリはそのショートカットが選択された時と, さらにユーザがショートカットを使わずにアプリを起動して職場への経路選択アクションをとった場合にレポートするべきです. これはランチャーアプリがユーザが平日に職場への案内を期待していることを学ぶ良い機会になります. そしてそれはユーザにとって必要な時にショートカットを提案することを可能にします.

Launcher API

The LauncherApps class provides APIs for launcher applications to access shortcuts.

LauncherAppsクラスはランチャーアプリ向けにショートカットへアクセスする
APIを提供しています.

Direct Boot and Shortcuts

All shortcut information is stored in credential encrypted storage, so no shortcuts can be accessed when the user is locked.

すべてのショートカット情報はクレデンシャル領域に格納されます. ユーザが端末をロックしている場合ショートカットにアクセスすることができません.

2016/10/21

Android: App Shortcut - 概要

! warning !
この投稿はAndroid 7.1 Developer Preview1 - App Shortcutの内容をもとにしています.
今後のバージョンアップで内容が変更されている可能性にご注意ください.

本稿は下記ドキュメントの翻訳です.
https://developer.android.com/preview/shortcuts.html

App Shortcut

Android 7.1 allows you to define shortcuts to specific actions in your app. These shortcuts can be displayed in a supported launcher, such as the one provided with Nexus and Pixel devices. Shortcuts let your users quickly start common or recommended tasks within your app.

Android7.1でアプリが持つ特定のアクションをショートカットとして定義できるようになりました.
これらのショートカットはこの機能をサポートしているNexusやPixelデバイスのようなランチャー上で表示されるようになります.
ショートカットはアプリの共通的なアクションやおススメに素早くアクセスできます.

Each shortcut references one or more intents, each of which launches a specific action in your app when users select the shortcut. Examples of actions you can express as shortcuts include the following:

  • Navigating users to a particular location in a mapping app.
  • Sending messages to a friend in a communication app.
  • Playing the next episode of a TV show in a media app.
  • Loading the last save point in a gaming app.

それぞれのショートカットはユーザがショートカットを選択した際に特定のアクションを起動する1つ以上のintentを参照しています. 例えば次のようなランチャーショートカットを表現できます.

  • マップアプリの特定のロケーションにナビゲートする
  • メッセージアプリで友人にメッセージを送信する
  • メディアアプリでTV番組の次のエピソードを再生する
  • ゲームアプリのセーブの最終ポイントから始める

You can publish two different types of shortcuts for your app:

アプリは異なる2タイプのショートカットを提供することができます.

  • Static shortcuts are defined in a resource file that is packaged into an APK. Therefore, you must wait until you update your entire app to change the details of these static shortcuts.
  • Dynamic shortcuts are published at runtime using the ShortcutManager API. During runtime, your app can publish, update, and remove its dynamic shortcuts.
  • Static Shortcutはアプリの静的なリソースファイルに定義されます. Static Shortcutを更新するにはアプリのアップデートが必要になります
  • Dynamic shortcutShortcutManagerのAPIを使って実行時にショートカットを構築します. アプリ実行中にショートカットを動的に追加したり, 更新したり, 削除したりできます.

You can publish up to five shortcuts (static shortcuts and dynamic shortcuts combined) at a time for your app. Users, however, can copy your app’s shortcuts onto the launcher, creating pinned shortcuts. Users can create and access an unlimited number of pinned shortcuts that trigger actions in your app. Your app cannot remove these pinned shortcuts, but it can disable them.

アプリはStatic shortcutとDynamic shortcutを合わせて5つまで提供することができます. (訳注: Shortcutの上限数はShortcutManagerのAPIgetMaxShortcutCountPerActivityを参照)
しかし, ユーザはアプリのショートカットをランチャーにコピーしてピン留めしておくことができます. ピン留めできる数に上限はなく, これを選択するとショートカットと同様にアプリで定義されたアクションが実行されます. アプリからそれらのピン留めされたショートカットを削除することはできませんが, 無効化することは可能です.

Note: Although other apps can’t access the metadata within your shortcuts, the launcher itself can access this data. Therefore, these metadata should conceal sensitive user information.

Note: 他のアプリはあなたのショートカットメタデータにアクセスすることができませんが, ランチャーアプリに限ってはこれにアクセスすることが可能です. そのため, このメタデータにユーザ情報などのセンシティブなデータを含めないようにすべきです.

Using Static Shortcuts

Static shortcuts should provide links to generic actions within your app, and these actions should remain consistent over the lifetime of your app’s current version. Good candidates for static shortcuts include viewing sent messages, setting an alarm, and displaying a user’s exercise activity for the day.

Static shortcutはアプリでの一般的なアクションを提供するべきです. また, それらのアクションは(アプリのバージョンが同じであれば)常に一貫したものであるべきです. 例えば, 送信済みメッセージの確認や, アラームの設定, その日の運動量といったようなものが推奨されます.

To create a static shortcut, complete the following sequence of steps:

  1. In your app’s manifest file (AndroidManifest.xml), find an activity whose intent filters are set to the android.intent.action.MAIN action and the android.intent.category.LAUNCHER category.
  2. Add a <meta-data> element to this activity that references the resource file where the app’s shortcuts are defined:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                package="com.example.myapplication">
      <application ... >
        <activity android:name="Main">
          <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>
    </manifest>
  3. Create a new resource file (res/xml/shortcuts.xml) where the app’s manifest shortcuts are defined.

  4. In this new resource file, add a root element, which contains a list of elements. Each element, in turn, contains information about a static shortcut, including its icon, its description labels, and the intents that it launches within the app:
    <shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
      <shortcut
        android:shortcutId="compose"
        android:enabled="true"
        android:icon="@drawable/compose_icon"
        android:shortcutShortLabel="@string/compose_shortcut_short_label1"
        android:shortcutLongLabel="@string/compose_shortcut_long_label1"
        android:shortcutDisabledMessage="@string/compose_disabled_message1">
        <intent
          android:action="android.intent.action.VIEW"
          android:targetPackage="com.example.myapplication"
          android:targetClass="com.example.myapplication.ComposeActivity" />
        <!-- If your shortcut is associated with multiple intents, include them
               here. The last intent in the list is what the user sees when they
               launch this shortcut. -->
        <categories android:name="android.shortcut.conversation" />
      </shortcut>
      <!-- Specify more shortcuts here. -->
    </shortcuts>

Static shortcutを作成するステップ:

  1. アプリのAndroidManifest.xmlから android.intent.action.MAINアクションとandroid.intent.category.LAUNCHERカテゴリを設定したインテントフィルタを持つアクティビティを探す.
  2. アクティビティに<meta-data>要素でショートカット情報が定義されたリソースファイルを指定します.

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                 package="com.example.myapplication">
      <application ... >
        <activity android:name="Main">
          <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>
    </manifest>
  3. Static shortcut用の新しいリソースファイルを作成します(res/xml/shortcuts.xml

  4. 新しいリソースファイルで, ルート要素にあたる<shortcuts>を定義します. この要素には子要素として<shortcut>要素のリストが含まれます. それぞれの<shortcut>要素はStatic shortcutの情報を並び順に従って定義していきます. 定義する情報はアイコン, ラベル, intent情報が含まれます.

    <shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
      <shortcut
        android:shortcutId="compose"
        android:enabled="true"
        android:icon="@drawable/compose_icon"
        android:shortcutShortLabel="@string/compose_shortcut_short_label1"
        android:shortcutLongLabel="@string/compose_shortcut_long_label1"
        android:shortcutDisabledMessage="@string/compose_disabled_message1">
        <intent
          android:action="android.intent.action.VIEW"
          android:targetPackage="com.example.myapplication"
          android:targetClass="com.example.myapplication.ComposeActivity" />
        <!-- If your shortcut is associated with multiple intents, include them
             here. The last intent in the list is what the user sees when they
             launch this shortcut. -->
        <categories android:name="android.shortcut.conversation" />
      </shortcut>
      <!-- Specify more shortcuts here. -->
    </shortcuts>

Using Dynamic Shortcuts

Dynamic shortcuts should provide links to specific, context-sensitive actions within your app. These actions can change between uses of your app, and they can change even while your app is running. Good candidates for dynamic shortcuts include calling a specific person, navigating to a specific location, and viewing the current score for a specific game.

Dynamic shortcutは特定の状況に特化したリンクを提供します. このアクションはアプリの更新の必要なしに変更することが可能で, アプリ実行中であったも変更ができます. Dynamic shortcutを使用した良い例として特定の相手への音声通話や, 特定の場所をマップで開くナビゲーション, ゲームの現在のランキングやスコアを表示するといったものがあります.

The ShortcutManager API allows you to complete the following operations on dynamic shortcuts:

ShortcutManager APIでDynamic shortcutを操作することができます:

  • Publish: setDynamicShortcuts(List)でDynamic shortcutのリストを再構築します. またはaddDynamicShortcuts(List)で既存のリストに引数のショートカットリストを追加します.
  • Update: updateShortcuts(List)を使ってDynamic shortcutを更新します
  • Remove: removeDynamicShortcuts(List)を使ってDynamic shortcutを削除するか, removeAllDynamicShortcuts()を使ってすべてのショートカットを削除します.

An example of creating a dynamic shortcut and associating it with your app appears in the following code snippet:

次のコードはアプリに連携するDynamic shortcutを作成するものです:

ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);

ShortcutInfo shortcut = new ShortcutInfo.Builder(this, "id1")
    .setShortLabel("Web site")
    .setLongLabel("Open the web site")
    .setIcon(Icon.createWithResource(context, R.drawable.icon_website))
    .setIntent(new Intent(Intent.ACTION_VIEW,
                   Uri.parse("https://www.mysite.example.com/")))
    .build();

shortcutManager.setDynamicShortcuts(Arrays.asList(shortcut));

Tracking Shortcut Usage

To determine the situations during which static and dynamic shortcuts should appear, the launcher examines the activation history of shortcuts. You can keep track of when users complete specific actions within your app by calling the reportShortcutUsed(String) method, passing in the ID of a shortcut, when either of the following events occur:

  • The user selects the shortcut with the given ID.
  • The user opens the app and manually completes the action corresponding to the same shortcut.

どのStatic shortcutとDynamic shortcutを表示するかを決定するにあたり, ランチャーはそれぞれのショートカットの使用履歴を調べます. アプリはユーザが特定のアクションを終えた際にreportShortcutUsed(String)メソッドに関連するショートカットIDを指定することで使用履歴に残すことができます. 次のどちらかのイベントが発生した場合にこのメソッドを呼び出します.

  • ユーザがショートカットを選択した場合
  • ユーザがショートカットを使うことなくアプリを起動し, ショートカットに関連するタスクを完了した場合

Disabling Shortcuts

Because users can pin any of your app’s shortcuts to the device’s launcher, it’s possible that these pinned shortcuts could direct users to actions within your app that are out of date or that no longer exist. To manage this situation, you can disable the dynamic shortcuts that you don’t want users to select by calling disableShortcuts(List), which removes the specified dynamic shortcuts and disables any pinned copies of these dynamic shortcuts. You can also use a variation of this method, disableShortcuts(List, CharSequence), to define an error message that should appear when users attempt to launch a disabled dynamic shortcut.

Note: If you remove some of your app’s static shortcuts when you update your app, the system disables these shortcuts automatically.

使用期限が切れてしまったようなアクションや, すでに存在しないアクションへのショートカットがランチャーにピン留めされていることもありえます. こういったショートカットを使用されたくない場合はdisableShortcuts(List)でDynamic shortcutリンクを無効にすることができます. これによってDynamic shortcutは削除されて, そのコピーであるピン留めされたショートカットは無効化されます. 同じ目的で, disableShortcuts(List, CharSequence)を使えば無効化されたDynamic shortcutを選択した際のエラーメッセージを指定することができます.

Note: もしアプリの アップデートを通してStatic shortcutを削除した場合システムはそれらのショートカットを自動で無効化します.

Testing Shortcuts

Several devices, including Nexus and Pixel devices, contain a default launcher that supports shortcuts. To test your app’s shortcuts, install your app on a device that uses one of these supported launchers. You should then be able to perform the following actions:

  • Long-tap on your app’s launcher icon to view the shortcuts that you’ve defined for your app.
  • Tap and drag an shortcut to pin it to the device’s launcher.

You can use these interactions to test the effects of adding, updating, disabling, and removing shortcuts.

NexusやPixelデバイスといったデバイスのランチャーはApp Shortcut機能をサポートしています. アプリのショートカット機能をテストするにはそういったApp Shortcut機能をサポートしたランチャーのあるデバイスで実施します. テストでは次のようなアクションをテストします:

  • アプリアイコンのロングタップでアプリが定義したショートカットを表示する
  • ショートカットをドラッグしてランチャーにピン留めする

これらのインタラクションを使ってショートカットの追加, 更新, 無効化, 削除の影響をテストします.

Assigning Multiple Intents

When creating a shortcut using ShortcutInfo.Builder, you can use setIntents(Intent[]) instead of setIntent(Intent). By calling the version of this method with an array parameter, you can launch multiple activities within your app when the user selects a shortcut, placing all but the last activity in the list on the back stack. If the user then decides to press the device’s back button, they will see another activity in your app instead of returning to the device’s launcher.

ShortcutInfo.Builderを使ってショートカットを作る際, setIntent(Intent)の代わりにsetIntents(Intent[])を使いことができます. このメソッドでショートカットを作成すると複数のアクティビティをまとめて起動することができます. ただし, 起動されるのは最後のアクティビティのみで残りはバックスタックにリストされます. もしこの状態でユーザがバックキーを押すとランチャーの代わりにバックスタックにある順番に積まれた別のアクティビティが表示される動作になります.
(訳注:TaskStackBuilder

Rate Limiting

When using the setDynamicShortcuts(List), addDynamicShortcuts(List), and updateShortcuts(List) methods, keep in mind that you might only be able to call these methods a specific number of times in a background app, an app with no activities or services currently in the foreground. In a production environment, you can reset this rate limiting by bringing your app to the foreground.

If you encounter rate limiting during development or testing, you can select Reset ShortcutManager rate-limiting within the device’s Developer Options settings, or you can enter the following command in adb:

$ adb shell cmd shortcut reset-throttling [ --user your-user-id ]

setDynamicShortcuts(List), addDynamicShortcuts(List)updateShortcuts(List)を使う際 , アプリがバックグラウンドにいる場合や, アクティビティを持たないアプリあるいはフォアグラウンドサービスが動作している場合にこのメソッドを呼ぶ頻度に制限が設けられていることを忘れてはいけません.

開発やテスト中にこの制限に引っかかった場合, 開発者オプションのReset ShortcutManager rate-limitingを選択するか次のadbコマンドでこれを解除することができます.

$ adb shell cmd shortcut reset-throttling [ --user your-user-id ]

Backup and Restore

If you allow users to back up and restore your app when changing devices by including the android:allowBackup=”true” attribute assignment in your app’s manifest file, keep in mind that only pinned shortcuts are restored to the device’s launcher automatically. Also, the system doesn’t back up icons associated with pinned shortcuts, so you should save your shortcuts’ images in your app so that it’s easy to restore them on a new device.

Static shortcuts are re-published automatically, but only after the user re-installs your app on a new device. Dynamic shortcuts, on the other hand, aren’t backed up, so you must include logic in your app to re-publish them in the event that a user opens your app on a new device.

AndroidManifest.xmlandroid:allowBackup="true"としてアプリのバックアップとリストアをユーザに許可している場合, ピン留めされたショートカットはランチャー上に自動でリストア(ピン留め)されることを覚えておいてください. また, シスてはピン留めされたショートカットに紐づくアプリアイコンをバックアップしませんので, ショートカットの画像はアプリで保持する必要があります. これで新しい端末へ簡単にリストアされることになります.

Static shortcutはあなたのアプリが再インストールされた後で自動的に再登録されます. Dynamic shortcutは自動でバックアップされませんのでアプリが新しい端末上で起動された場合にDynamic shortcutを再登録するコードを含めるようにしてください.

Best Practices

When designing and creating your app’s shortcuts, you should follow these guidelines:

Follow the shortcuts design guidelines
To make your app’s shortcuts visually consistent with the shortcuts used for system apps, follow the Shortcuts Design Guidelines.

ショートカットのデザインガイドラインを参考にする
アプリのショートカットをシステムアプリが使っているような見た目に統一するためにも, ショートカットのデザインガイドラインを参考にしてください.

Publish only four distinct shortcuts
Although the API currently supports a combination of up to five static shortcuts and dynamic shortcuts for your app at any given time, it’s recommended that you publish only four distinct shortcuts at any time to improve the shortcuts’ visual appearance in the launcher.

4つの異なるショートカットを登録する
APIは現状Static shortcutとDynamic shortcutあわせて5まで登録できるようサポートしてはいますが, ランチャーでの見た目を良くするためにもショートカットは異なる4つまでに留めることを推奨します.

Limit shortcut description length
Space is limited within the menu that shows your app’s shortcuts in the launcher. When possible, limit the length of the “short description” of a shortcut to 10 characters, and limit the length of the “long description” to 25 characters.

ショートカットの説明文字列長
ランチャーで表示できるショートカットのメニュー表示領域には限りがあります. ショートカットの短い説明文には10文字, 長い説明分でも25文字以内に収める必要があります.
(訳注:全角ではさらに文字数を減らす必要がある)

Maintain shortcut and action usage history
For each shortcut that you create, consider the different ways in which a user can accomplish the same task directly within your app. Remember to call reportShortcutUsed(String) in each of these situations so that the launcher maintains an accurate history of the actions representing your shortcuts.

ショートカットと使用履歴の管理
あなたが作成したすべてのショートカットについて, ショートカットを使わずにユーザがアプリを直接起動する等の異なる方法でショートカットが成そうとするものと同じ内容のタスクを達成した場合, reportShortcutUsed(String)を呼ぶことを忘れないでください. ランチャーはショートカットを提供するために正確なアクションの使用履歴を管理しています.

Update shortcuts only when their meaning is retained
When changing dynamic shortcuts, call updateShortcuts(List) only when changing the information of a shortcut that has retained its meaning. Otherwise, you should use addDynamicShortcuts(List) or setDynamicShortcuts(List) instead to create a shortcut with a new ID that represents a new meaning.

For example, if you created a shortcut for navigating to a supermarket, it would be appropriate to just update the shortcut if the name of the supermarket changed but its location stayed the same. If the user started shopping at a different supermarket location, however, it would be better to create a new shortcut.

ショートカットを更新する際にその意味を変えない
updateShortcuts(List)でDynamic shortcutを変更する際, 変更するのはショートカットの情報だけにして, そのショートカットの意味は変更しないでください. 意味を変更する場合にはaddDynamicShortcuts(List)setDynamicShortcuts(List)を代わりに使って新しい意味のショートカットを新しいショートカットIDで作り直します.

Dynamic shortcuts aren’t preserved during backup and restore
Dynamic shortcuts aren’t preserved when a device undergoes a backup and restore operation. For this reason, it’s recommended that you check the number of objects returned by getDynamicShortcuts() each time you launch your app and re-publish dynamic shortcuts as needed, as shown in the following code snippet:

Dynamic shortcutはバックアップ/リストアで引き継がれない
Dynamic shortcutの情報は端末のバックアップ/リストア操作で引き継がれません. この理由は, アプリが起動された際にgetDynamicShortcuts()メソッドでショートカットの数を確認して, Dynamic shortcutを必要に応じて再登録することが望ましいからです.
次のコードはその一例です:

public class MainActivity extends Activity {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ShortcutManager shortcutManager =
                getSystemService(ShortcutManager.class);

        if (shortcutManager.getDynamicShortcuts().size() == 0) {
            // Application restored. Need to re-publish dynamic shortcuts.
            if (shortcutManager.getPinnedShortcuts().size() > 0) {
                // Pinned shortcuts have been restored. Use
                // updateShortcuts(List) to make sure they
                // contain up-to-date information.
            }
        }
    }
    // ...
}