Android App Shortcuts実装

Andorid 7.1(API 25)にApp Shortcut機能が導入されました。 App ShortcutはLauncherでアプリのアイコンを長押しすると、あらかじめ設定されたアクティビティに直接移動したり、特定のインテントを実行するShorutcutアイコンを表示する機能です。

Gmailを例にとると、次の図のようにアイコンを長く押したときに「Compose」というShortcutが見えます。これ押すとすぐにメールを作成することができるページに移動するように実装されました。 Static shortcut example

この記事で使用された例では、GitHub - AppShortcutsで確認することができます。

Shortcutの種類

Androidは、次のような種類のShortcutを提供します。

  • Static shortcuts
  • Dynamic shortcuts
  • Pinned shortcuts

Static shortcuts

AppのManifestに定義するShortcutをStatic Shortcut呼ばれます。アプリのビルド時にShortcutが決まるため、Runtimeの変更はできません。 アイコンを長押しするとStatic shotcutsを示しています。 Static shortcut example

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のアイコンと分離されて独立して表示されます。 Pinned shortcut example

静的ショートカット구현

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が実行されます。 static shortcut example

ダイナミックショートカット등록

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が追加されたことを確認することができます。 dynamic shortcut example

ダイナミックショートカット삭제

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)
}

上記のコードを実行してみると、次のようなポップアップが表示されます。 Pinned Shortcut example

Addボタンを押すと、次のようにLauncherにShortcutが追加されます。 Pinned Shortcut example

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

codechachaCopyright ©2019 codechacha