2016/11/07

Android: ExoPlayer - Demo application

翻訳ページ:
https://google.github.io/ExoPlayer/demo-application.html

Demo application

The ExoPlayer demo app serves two primary purposes:

  • To provide a relatively simple yet fully featured example of ExoPlayer usage. The demo app can be used as a convenient starting point from which to develop your own application.
  • To make it easy to try ExoPlayer. The demo app can be used to test playback of your own content in addition to the included samples.

This page describes how to get, compile and run the demo app. It also describes how to use it to play your own media.

ExoPlayerのデモアプリは次の2つの大きな理由のために用意してあります:

  • シンプルでありながら全てのExoPlayer機能の使い方を例示していること. デモアプリがアプリ開発を始める際の開始点としてとして便利であること.
  • ExoPlayerをお手軽に試すことができること. デモアプリにオリジナルコンテンツを追加して再生のテストにも使えること.

このページではどうやってデモアプリを取得し, コンパイルし, 実行するのかを説明します.
また, あなたが用意したメディアをどうやって再生するのかについても説明します.

Getting the code

The source code for the demo app can be found in the demo folder of our GitHub project. If you haven’t already done so, clone the project into a local directory:

git clone https://github.com/google/ExoPlayer.git

Next, open the project in Android Studio. You should see the following in the Android Project view (the relevant folders of the demo app have been expanded):

デモアプリのソースコードはGitHubプロジェクトから取得できます. まだ取得していないのであればローカルディレクトリにcloneしてください.

git clone https://github.com/google/ExoPlayer.git

次に, AndroidStudioでAndroid Projectビューを開くと次の表示を確認できます.

Figure 1. The project in Android Studio

Compiling and running

To compile and run the demo app, select and run the demo configuration in Android Studio. The demo app will install and run on a connected Android device. We recommend using a physical device if possible. If you wish to use an emulator instead, please read FAQ - Does ExoPlayer support emulators? and ensure that your Virtual Device uses a system image with an API level of at least 23.

AndroidStudioのConfigurationからdemoを選択し, デモアプリをコンパイル, 実行してください. 接続されているデバイスにデモアプリがインストールされて実行されます. 可能であればエミュレータではなく物理的なデバイスの使用を推奨します.

もし代わりにエミュレータを使用したいのであればFAQ - Does ExoPlayer support emulators?を読んでください. そうすればAPI lv23以降の仮想デバイスで使うことができます.

Figure 2. SampleChooserActivity and PlayerActivity

The demo app presents of a list of samples (SampleChooserActivity). Selecting a sample will open a second activity (PlayerActivity) for playback. The demo features playback controls and track selection functionality. It also has an EventLogger class that outputs useful debug information to the system log. This logging can be viewed (along with error level logging for other tags) with the command:

デモアプリのSampleChooserActivityにサンプルのリストがあります. リスト項目を選択すればそれを再生するためのPlayerActivityが起動します. デモは再生コントロールとトラック選択の機能を持っています. また, システムログに有用なデバッグ情報を出力するEventLoggerクラスを持っています. 次のコマンドでEventLoggerの有効にして, ほかのログはエラー情報のみ表示することができます.

adb logcat EventLogger:V *:E

Including extension decoders

ExoPlayer has a number of extensions that allow use of bundled software decoders, including VP9, Opus, FLAC and FFMPEG (audio only). The demo app can be built to include and use these extensions as follows:

  1. Build each of the extensions that you want to include. Note that this is a manual process. Refer to the README.md file in each extension for instructions.
  2. In Android Studio’s Build Variants view, change the build variant for the demo module from demoDebug to demo_extDebug, as shown in Figure 3.
  3. Compile, install and run the demo configuration as normal.

ExoPlayerはいくつかのエクステンションがありVP9, Opus, FLAC and FFMPEG (audio only)といったソフトウェアデコーダを使うことができます. デモアプリでこれらを使うための手順は次の通りです:

  1. 取り込みたいExtensionをビルドします. これは手作業での変更になります. それぞれのExtensionにあるReadmeには手順が示されています.
  2. Figure 3を参考に, AndroidStudioのビルドバリアントビューでデモモジュールのビルドバリアントをdemoDebugからdemo_extDebugに切り替えます.
  3. いつもの通りデモをコンパイル, インストール, 実行します.


Figure 3. Selecting the demo_extDebug build variant

By default an extension decoder will be used only if a suitable platform decoder does not exist. It is possible to indicate that extension decoders should be preferred, as described in the sections below.

デフォルトのデコーダ拡張は適切なプラットフォームデコーダが存在しない時だけ使われる。これはのそセクションで拡張デコーダが望ましいことを示すことができる。

Playing your own content

There are multiple ways to play your own content in the demo app.

デモアプリで手持ちのコンテンツを再生するにはいくつかの方法があります.

1. Editing assets/media.exolist.json

The samples listed in the demo app are loaded from assets/media.exolist.json. By editing this JSON file it’s possible to add and remove samples from the demo app. The schema is as follows, where [O] indicates an optional attribute.

デモアプリではasset/media.exolist.jsonから読み込まれたサンプルコンテンツがリストされています. このJSONファイルを編集することでこれらのサンプルに追加したり削除したりすることができます. JSONの形式は次の通り. [O]はオプションの属性です.

[
  {
    "name": "Name of heading",
    "samples": [
      {
        "name": "Name of sample",
        "uri": "The URI/URL of the sample",
        "extension": "[O] Sample type hint. Values: mpd, ism, m3u8",
        "prefer_extension_decoders": "[O] Boolean to prefer extension decoders",
        "drm_scheme": "[O] Drm scheme if protected. Values: widevine, playready",
        "drm_license_url": "[O] URL of the license server if protected",
        "drm_key_request_properties": "[O] Key request headers if protected"
      },
      ...etc
    ]
  },
  ...etc
]

Playlists of samples can be specified using the schema:

サンプルのプレイリストはスキーマを使って特定できる.

[
  {
    "name": "Name of heading",
    "samples": [
      {
        "name": "Name of playlist sample",
        "prefer_extension_decoders": "[O] Boolean to prefer extension decoders",
        "drm_scheme": "[O] Drm scheme if protected. Values: widevine, playready",
        "drm_license_url": "[O] URL of the license server if protected",
        "drm_key_request_properties": "[O] Key request headers if protected"
        "playlist": [
          {
            "uri": "The URI/URL of the first sample in the playlist",
            "extension": "[O] Sample type hint. Values: mpd, ism, m3u8"
          },
          {
            "uri": "The URI/URL of the first sample in the playlist",
            "extension": "[O] Sample type hint. Values: mpd, ism, m3u8"
          },
          ...etc
        ]
      },
      ...etc
    ]
  },
  ...etc
]

If required, key request headers are specified as an object containing a string attribute for each header:

もし必要なら, それぞれのヘッダに文字列を含むオブジェクトのキーリクエストヘッダを指定できます.

"drm_key_request_properties": {
  "name1": "value1",
  "name2": "value2",
  ...etc
}

2. Loading an external exolist.json file

The demo app can load external JSON files using the schema above and named according to the *.exolist.json convention. For example if you host such a file at https://yourdomain.com/samples.exolist.json, you can open it in the demo app using:

デモアプリは上記のスキーマを使った*.exolist.jsonにマッチするJSONを読み込むことができます. もしファイルをhttps://yourdomain.com/samples.exolist.jsonでホストしているならデモアプリを使って次のコマンドで開くことができます.

adb shell am start -d https://yourdomain.com/samples.exolist.json

Clicking a *.exolist.json link (e.g. in the browser or an email client) on a device with the demo app installed will also open it in the demo app. Hence hosting a *.exolist.json JSON file provides a simple way of distributing content for others to try in the demo app.

ブラウザやemailクライアントで*.exolist.jsonのリンクをクリックするとデモアプリがインストールされていれば起動することができます. デモアプリで試すには*.exolist.jsonのJSONファイルをホストするのがコンテンツを他へ配信するのに簡単な方法です.

3. Firing an intent

Intents can be used to bypass the list of samples and launch directly into playback. To play a single sample set the intent’s action to com.google.android.exoplayer.demo.action.VIEW and its data URI to that of the sample to play. Such an intent can be fired from the terminal using:

Intentはサンプルリストをバイパスするのと直接再生することに使えます. 1つのサンプルセットを再生するにはアクションcom.google.android.exoplayer.demo.action.VIEWとデータのURIが設定されたIntentを使います. そうしたIntentをターミナルから発行するには次のコマンドを使います.

adb shell am start -a com.google.android.exoplayer.demo.action.VIEW \
    -d https://yourdomain.com/sample.mp4

Supported optional extras for a single sample intent are:

  • extension [String] Sample type hint. Valid values: mpd, ism, m3u8
  • prefer_extension_decoders [Boolean] Whether extension decoders are preferred to platform ones
  • drm_scheme_uuid [String] Drm scheme UUID if protected
  • drm_license_url [String] Url of the license server if protected
  • drm_key_request_properties [String array] Key request headers packed as name1, value1, name2, value2 etc. if protected

IntentがサポートしているオプショナルなExtraは次の通り:

  • extension [String] サンプルの種類. 使える値は mpd ism m3u8
  • prefer_extension_decoders [Boolean] extensionデコーダーがプラットフォームに適しているかどうか
  • drm_scheme_uuid [String] 保護されている場合のDrm形式のUUID
  • drm_license_url [String] 保護されている場合のライセンスサーバーURL
  • drm_key_request_properties [String array] 保護されている場合, name1, value1, name2, value2の形でパッケージされたキーリクエストヘッダ

When using adb shell am start to fire an intent, an optional string extra can be set with --es (e.g. --es extension mpd). An optional boolean extra can be set with --ez (e.g. --ez prefer_extension_decoders TRUE). An optional string array extra can be set with --esa (e.g. --esa drm_key_request_properties name1,value1).

Intentの発行にadb shell am startを使うなら, Extraオプションの文字列は--es (e.g. --es extension mpd)で指定できる. booleanであれば--ez (e.g. --ez prefer_extension_decoders TRUE). String配列であれば--esa (e.g. --esa drm_key_request_properties name1,value1)を使って設定することができる.

To play a playlist of samples set the intent’s action to com.google.android.exoplayer.demo.action.VIEW_LIST and use a uri_list string array extra instead of a data URI. For example:

サンプルのプレイリストを再生するにはIntentのアクションcom.google.android.exoplayer.demo.action.VIEW_LISTと文字配列uri_listにデータURIを指定します. 例えば,

adb shell am start -a com.google.android.exoplayer.demo.action.VIEW_LIST \
    --esa uri_list https://a.com/sample1.mp4,https://b.com/sample2.mp4

Supported optional extras for a playlist intent are:

プレイリストのIntentでサポートされているオプションは次の通り:

  • extension_list [String array] Sample type hints. Entries may be empty or one of: mpd, ism, m3u8
  • prefer_extension_decoders, drm_scheme_uuid, drm_license_url and drm_key_request_properties, all as described above
  • extension_list [String array] サンプルタイプのヒント. 内容は空かmpd, ism, m3u8のうちどれかです
  • prefer_extension_decoders, drm_scheme_uuid, drm_license_urldrm_key_request_propertiesそれぞれは上記で説明されています.
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.

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