Andorid 7.1(API 25)にApp Shortcut機能が導入されました。 App ShortcutはLauncherでアプリのアイコンを長押しすると、あらかじめ設定されたアクティビティに直接移動したり、特定のインテントを実行するShorutcutアイコンを表示する機能です。
Gmailを例にとると、次の図のようにアイコンを長く押したときに「Compose」というShortcutが見えます。これ押すとすぐにメールを作成することができるページに移動するように実装されました。
この記事で使用された例では、GitHub - AppShortcutsで確認することができます。
Shortcutの種類
Androidは、次のような種類のShortcutを提供します。
- Static shortcuts
- Dynamic shortcuts
- Pinned shortcuts
Static shortcuts
AppのManifestに定義するShortcutをStatic Shortcut呼ばれます。アプリのビルド時にShortcutが決まるため、Runtimeの変更はできません。
アイコンを長押しするとStatic shotcutsを示しています。
Dynamic shortcuts
ShortcutManager APIを利用して、動的に追加、削除が可能なShortcutをDynamic Shortcut呼ばれます。 Dynamic shortcutもStatic shortcutと同じようにできました。ただしRuntimeに追加すると、削除が可能であることが違います。
Pinned shortcuts
StaticとDynamicはアプリのアイコンを長押したときに見えるShortcutです。 Pinned Shortcutはこの両方と異なり、AppアイコンのようLauncherに登録されているShortcutです。
以下は、YoutubeアプリがLauncherに登録したPinned Shortcutの様子です。 Appのアイコンと分離されて独立して表示されます。
静的ショートカット구현
Static shortcutsは、次のようにAppのManifestに定義します。 Launcherに表示されるActivityに android.app.shortcuts
というmetadataを追加します。
そして @xml/shortcuts
というxmlファイルのShortcutの内容を定義します。
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
</activity>
次のコードは、 /res/xml/shortcuts.xml
に実装したものです。
<shortcuts>
の中に登録したい数だけ<shortcut>
を定義します。
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<shortcut
android:shortcutId="email"
android:enabled="true"
android:icon="@drawable/add"
android:shortcutShortLabel="@string/add_shortcut_short_label"
android:shortcutLongLabel="@string/add_shortcut_long_label"
android:shortcutDisabledMessage="@string/add_shortcut_disabled_message">
<intent
android:action="com.example.appshortcuts.ADD_WEBSITE"
android:targetPackage="com.example.appshortcuts"
android:targetClass="com.example.appshortcuts.AddWebActivity" />
</shortcut>
</shortcuts>
<shortcut>
の属性の意味は次のとおりです。
- shortcutId:Shortcutの固有のIDです。 Shrotcutを登録したり、検索するとき、このIDを使用します
- enabled:この値がtrueであれば、Shortcutアイコンを押したときにどのような機能が動作します。 falseなら実行しないようにshortcutDisabledMessageに設定されたメッセージがToastで表示されます
- icon:ShortcutのIconです
- shortcutShortLabel:ShortcutのLabelです。可能な場合は10字以内で入力してください
- shortcutLongLabel:ShortcutのLabelです。可能な場合は、25字以内で入力してください
- shortcutDisabledMessage:無効化されShortcutを実行させるときに、このメッセージを表示します。したがって、このShortcutは無効となり、使用が不可能であることをユーザーに知らせることができるメッセージならお勧めします
- intent:Shortcutアイコンをクリックしたときに実行されるインテントです。私のアプリのActivityを実行させることができ、または他のアプリを起動することができます
動作確認
今のアプリをビルドして、デバイスにインストールしましょう。
次のようにアプリのアイコンを長押しするとStatic shortcutが見えます。 Shortcutを押してみて上記の設定された AddWebActivity
が実行されます。
ダイナミックショートカット등록
ShortcutManagerを通じてDynamic Shortcutを登録することができます。
まず、 ShortcutInfo.Builder()
でShortcutInfoを生成します。
ここで設定するLabelなどの属性は、上記のStatic Shortcutを作成アプリケーションにおいて紹介したものと属性と同じです。
最後に、 ShortcutManager.setDynamicShortcuts()
のリストに渡されたすべてのShortcutInfoがDynamic Shortcutに登録されます。
val context = applicationContext
val shortcutManager = getSystemService<ShortcutManager>(ShortcutManager::class.java)
val shortcut = ShortcutInfo.Builder(context, "shortcutId1")
.setShortLabel("Website")
.setLongLabel("Open the website")
.setDisabledMessage("This shortcut is disabled")
.setIcon(Icon.createWithResource(context, R.drawable.link))
.setIntent(Intent(Intent.ACTION_VIEW,
Uri.parse("https://www.mysite.example.com/")))
.build()
shortcutManager.setDynamicShortcuts(listOf(shortcut))
上記のコードを実行させると、 shortcutId1
というIDでShortcutが登録されます。
そして、次のようにアプリのアイコンを長押ししてみると、Dynamic Shortcutが追加されたことを確認することができます。
ダイナミックショートカット삭제
ShortcutManager.removeDynamicShortcuts()
に登録されShortcut IDをリストに転送すれば、転送されたすべてのIDに対応するShortcutが削除されます。
すべてのShortcutを削除したい場合は removeAllDynamicShortcuts()
を使用することもできます。
val context = applicationContext
val shortcutManager = getSystemService<ShortcutManager>(ShortcutManager::class.java)
shortcutManager.removeDynamicShortcuts(listOf("shortcutId1"))
// shortcutManager.removeAllDynamicShortcuts()
Shortcutアップデート
既に登録されているShortcutのIDを知っていれば、Shortcutの情報を変更することができます。 変更が可能なShortcutはPinned ShortcutとDyanmic Shortcutです。
以下は、 shortcutId1
に登録されたShorcutのLabelを変更するコードです。
val context = applicationContext
val shortcutManager = getSystemService<ShortcutManager>(ShortcutManager::class.java)
val shortcut = ShortcutInfo.Builder(context, "shortcutId1")
.setShortLabel("Website!")
.setLongLabel("Open! the website")
.setDisabledMessage("This shortcut is disabled")
.setIcon(Icon.createWithResource(context, R.drawable.link))
.setIntent(Intent(Intent.ACTION_VIEW,
Uri.parse("https://www.mysite.example.com/")))
.build()
shortcutManager.updateShortcuts(listOf(shortcut))
上記のコードを実行して、LauncherでDynamic shortcutを確認するとLabelが変更されたことを確認することができます。
ピン留めされたショートカット등록
Pinned Shortcutsを登録することもDynamic Shortcutと似ています。 ShortcutInfoを作成する方法は同一であり、登録する方法が少し異なります。
Dynamic shortcutはAppに登録するShortcutであり、Pinned ShortcutはLauncherに登録するShortcutですよ。
そのため、Pinned Shortcutを登録するとき、LauncherがPinned shortcutをサポートしている ShortcutManager.isRequestPinShortcutSupported()
APIを確認する必要があります。
trueを返した場合サポートすることを意味します。
ShortcutManager.requestPinShortcut()
でShortcutを登録するとされるが、追加でIntentSenderを引数として渡す必要があります。
このAPIを呼び出すと、ポップアップを浮かべ、ユーザーにLauncherに登録するかどうか聞いてみるのですが、このような結果を受信して処理するためにIntentSenderが必要です。
次のように実装すればPinned Shortcutを登録することができます。
val context = applicationContext
val shortcut = ShortcutInfo.Builder(context, "pinnedShortcutId1")
.setShortLabel("Website")
.setLongLabel("Open the website")
.setDisabledMessage("This shortcut is disabled")
.setIcon(Icon.createWithResource(context, R.drawable.link))
.setIntent(Intent(Intent.ACTION_VIEW,
Uri.parse("https://www.mysite.example.com/")))
.build()
val shortcutManager = getSystemService<ShortcutManager>(ShortcutManager::class.java)
if (shortcutManager.isRequestPinShortcutSupported) {
val pinnedShortcutCallbackIntent =
shortcutManager.createShortcutResultIntent(shortcut)
val successCallback = PendingIntent.getBroadcast(context, /* request code */ 0,
pinnedShortcutCallbackIntent, /* flags */ 0)
shortcutManager.requestPinShortcut(shortcut, successCallback.intentSender)
}
上記のコードを実行してみると、次のようなポップアップが表示されます。
Add
ボタンを押すと、次のようにLauncherにShortcutが追加されます。
Shortcut無効
Dynamic shortcutの場合、私のアプリに登録されたので削除が可能ですが、Pinned ShortcutはLauncherに登録されたShortcutあるため、アプリから直接削除することができません。 何らかの理由でShortcutこの動作されず、従ってユーザーがShortcutを使用しないようにするには、disableする方法があります。
次のように ShortcutManager.disableShortcuts()
にShortcut IDをリストに渡すと、そのShortcutが無効になります。
再びenableに変更する場合は enableShortcuts()
APIを使用してください。
val context = applicationContext
val shortcutManager = getSystemService<ShortcutManager>(ShortcutManager::class.java)
shortcutManager.disableShortcuts(listOf("pinnedShortcutId1"))
// shortcutManager.enableShortcuts(listOf("pinnedShortcutId1"))
ShortcutInfoインポート
次のAPIを使用すると、それぞれのShortcutInfoを取得することができます。
- getManifestShortcuts()
- getDynamicShortcuts()
- getPinnedShortcuts()
SDK API 30以上では ShortcutManager.getShortcuts()
APIを提供し、次のようなフラグを引数として渡して欲しいShortcutの情報を得ることができます。
FLAG_MATCH_MANIFEST
FLAG_MATCH_DYNAMIC
FLAG_MATCH_PINNED
以下は、上記のAPIを利用して、ShortcutInfoを持ってきて、ログに出力する例です。
val context = applicationContext
val shortcutManager = getSystemService<ShortcutManager>(ShortcutManager::class.java)
// Static shortcuts
val manifestShortcuts: List<ShortcutInfo> = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
shortcutManager.getShortcuts(ShortcutManager.FLAG_MATCH_MANIFEST)
} else {
shortcutManager.getManifestShortcuts()
}
for (shortcut:ShortcutInfo in manifestShortcuts) {
Log.d(TAG, "Manifest ShortcutInfo: $shortcut")
}
// Dynamic shortcuts
val dynamicShortcuts = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
shortcutManager.getShortcuts(ShortcutManager.FLAG_MATCH_DYNAMIC)
} else {
shortcutManager.getDynamicShortcuts()
}
for (shortcut:ShortcutInfo in dynamicShortcuts) {
Log.d(TAG, "Dynamic ShortcutInfo: $shortcut")
}
// Pinned shortcuts
val pinnedShortcuts = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
shortcutManager.getShortcuts(ShortcutManager.FLAG_MATCH_PINNED)
} else {
shortcutManager.getPinnedShortcuts()
}
for (shortcut:ShortcutInfo in pinnedShortcuts) {
Log.d(TAG, "Pinned ShortcutInfo: $shortcut")
}
参考
この記事で使用された例では、GitHub - AppShortcutsで確認することができます。
Related Posts
- エラー解決:android gradle plugin requires java 11 to run. you are currently using java 1.8.
- Android - コルーチンとRetrofitによる非同期通信の例
- Android - コルーチンでURL画像を読み込む
- Android - 振動、Vibrator、VibrationEffectの例
- Some problems were found with the configuration of task
- Query method parameters should either be a type that can be converted into a database column or a List
- UbuntuでAndroid 12オープンソースをダウンロードしてビルド
- Android - ViewModelを生成する方法
- Android - Transformations.map(), switchMap() の違い
- Android-Transformations.distinctUntilChanged()소개
- Android - TabLayoutの実装方法(+ ViewPager2)
- Android - 携帯電話の電話番号を取得する方法
- Android 12 - Splash Screens
- Android 12 - インクリメンタルインストール
- Android - adbコマンドでbugreportログファイルの抽出
- Android - adbコマンドでAppデータを削除する
- Android - adbコマンドでアプリ無効化、有効化
- Android - adbコマンドで特定のパッケージのPIDを検索
- Android - adbコマンドでパーミッションGrantまたはRevoke
- Android - adbコマンドで特定のパッケージのプロセスの終了
- Android - adbコマンドでapkのインストール、削除、
- Android - adb push、pullでファイルのコピー、ダウンロード
- Android - adbコマンドでscreen capture保存
- Android - adbコマンドでSystemアプリの削除、インストール
- Android - adbコマンドでsettings value確認、変更、
- Android 12 - IntentFilterのexported明示的な宣言
- Android - adbコマンドで工場出荷時の(Factory reset)
- Android - adb logcatコマンドでログ出力
- Android - adbコマンドでメモリダンプ(dump-heap)
- Android - adbコマンドでApp強制終了(force-stop)
- Android - adbコマンドでServiceの実行、終了
- Android - adbコマンドでActivity実行
- Android - adbコマンドでBroadcast配信
- Android - PackageManagerにPackage情報を取得する
- Android - ACTION_BOOT_COMPLETEDイベント受信