はじめに
突然ですが、App Shortcuts は使っていますか?
アプリアイコンを長押しすると出てくる、これです。
地味ですし、一見気づきにくいので、なかなか使われなさそうな機能ですが、
実は簡単に導入できます。今回はApp Shortcutsの実装について紹介します。
以下はショートカットの種類と概要です。
| 種類 | 対応開始バージョン | 最大設定可能数 | 推奨用途 |
|---|---|---|---|
| 静的ショートカット | Android 7.1 (API 25) | 通常は4つ前後※ | 固定アクション(新規作成・検索・ホーム遷移など) |
| 動的ショートカット | Android 7.1 (API 25) | 静的と共通の上限制約 | 利用履歴や文脈に応じて差し替えるアクション(最近開いたコンテンツ、特定ユーザーへのアクセスなど) |
| ピン留めショートカット | Android 8.0 (API 26) | システム上の数値制限なし | ユーザー自身が永続的に置きたい導線。ユーザー主導でホームに固定する用途 |
※ShortcutManager.getMaxShortcutCountPerActivity() で取得される上限

静的ショートカット
静的ショートカットは、定義したいショートカット情報を記載した新規XML(例:shortcut.xml)と、
AndroidManifest.xmlへショートカットする情報を記載すれば反映されます。
新規XMLは以下のように記載します。この例では一覧ページを静的ショートカットとして定義してます。
<!-- shortcut.xml -->
<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<shortcut android:shortcutId="shortcut_list" android:enabled="true" android:icon="@drawable/ic_list"
android:shortcutShortLabel="@string/label_list" android:shortcutLongLabel="@string/label_list">
<intent android:action="android.intent.action.VIEW" android:targetPackage="jp.co.appshortcutsample"
android:targetClass="jp.co.appshortcutsample.MainActivity">
<extra android:name="shortcut_id" android:value="shortcut_list" />
</intent>
</shortcut>
</shortcuts>
AndroidManifest.xmlには既存のactivityスコープ内に追記します。
<!--AndroidManifest.xml(抜粋)-->
<application>
<activity android:name="Main">
<!-- 以下を既存のactivityスコープ内に追記する -->
<meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts" />
</activity>
</application>
ドキュメントページでは extra の指定がなかったことと、Intent.EXTRA_SHORTCUT_ID といった定数が存在していたことから、
Intent から shortcutId が取得できるのかと思ったのですが、実際には取得できませんでした。中身を見た限りでは extra 以外に shortcutId
が存在していなさそうなので、明示的に渡してあげる必要がありそうです。
https://developer.android.com/develop/ui/views/launch/shortcuts/creating-shortcuts#static
またオプションとして、categories や capability-binding を設定できます。
これは、システムにショートカット遷移先の機能を紐付けるためのラベルのようなもの、という認識ではありますが、App Shortcuts としては現状、特に意味を持っていなさそうでした。
categories は、システムとして定義されている android.shortcut.conversation のほか、実装者が自由に名前をつけることができます。
https://developer.android.com/reference/android/content/pm/ShortcutInfo#SHORTCUT_CATEGORY_CONVERSATION
capability-binding には action.intent で指定できる値を設定する想定で、それ以外の場合は不明として扱われるようです。
動的ショートカット
動的ショートカットの場合は、以下のように ShortcutInfoCompat.Builderで XML で指定したような要素をコード上で設定します。
作成したショートカット情報を、ShortcutManagerCompat.pushDynamicShortcutで 1 つずつ、
またはShortcutManagerCompat.setDynamicShortcutsでまとめて登録します。
どちらの場合も、静的・動的を合わせた最大数を超過しても特にエラーは発生しませんが、最大数以上は登録されません。
pushDynamicShortcutの場合は古いものから順に削除され、setDynamicShortcutsの場合は超過分が無視されます。
細かい点として、setDynamicShortcutsは指定した要素を追加ではなく「置き換え」として扱うため、注意が必要です。
また、APIレートリミットが存在するので、短期間に何度も更新することは避けたほうが良いです。
```kotlin
/**
* AppShortcutからShortcutInfoCompatを作成
*/
private fun createShortcutInfo(context: Context, shortcut: AppShortcut): ShortcutInfoCompat {
val intent = Intent(context, MainActivity::class.java).apply {
action = Intent.ACTION_VIEW
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
// intentからshortcutIdが取得できないので明示的に渡す。特定できれば良いのでキーは何でも良い
// 静的ショートカット同様にこちらでショートカットを判定できるようにする必要がある
putExtra("shortcut_id", [対応するshortcutId])
// 普通のintentなのでもちろん追加でパラメータを渡すことも可能(初期値の設定とか)
putExtra("extra_data", "何かしらの値")
}
ShortcutInfoCompat.Builder(context, [対応するshortcutId])
.setShortLabel("shortcutShortLabelにあたる値")
.setLongLabel("shortcutLongLabelにあたる値")
.setIcon("iconにあたるリソース")
.setIntent(intent)
.build()
}
```
ピン留めショートカット
ピン留めショートカットは、ユーザーが静的・動的ショートカットから選択してホーム画面に追加するほか、アプリ側からリクエストを送って追加してもらうこともできます。
ShortcutManagerCompat.requestPinShortcutでショートカットを指定するだけで、標準リクエストのモーダルダイアログを表示してくれます。

// (UI抜粋)
TextButton(
onClick = {
// Pinned Shortcutを作成
val success = AppShortcutsManager.requestPinEditShortcut(context)
if (!success) {
android.widget.Toast.makeText(
context,
"ショートカットの追加に失敗しました",
android.widget.Toast.LENGTH_SHORT
).show()
}
showPinShortcutDialog = false
onDismiss()
}
) {
Text(stringResource(id = R.string.app_shortcuts_pin_dialog_confirm))
}
...
fun requestPinEditShortcut(context: Context): Boolean {
// Pinned Shortcutがサポートされているかチェック
if (!ShortcutManagerCompat.isRequestPinShortcutSupported(context)) {
return false
}
// 動的ショートカットと同じ作り方
val shortcutInfo = createShortcutInfo(context, AppShortcut.Edit)
return ShortcutManagerCompat.requestPinShortcut(context, shortcutInfo, null)
}
```
おわりに
部分的に公式と異なる箇所はあったものの、簡単に実装することができました。
実際のところ、あまり目立たない機能なため、機能追加する機会も多くなさそうですが、地味ながら捗る機能だと思います。ぜひ対応してみてはいかがでしょうか?








