App Shortcutsに対応したアプリの一覧を取得する

# App Shortcutsとは？
Android 7.1から使えるようになった、アプリの特定の機能のショートカットをホームに作成できる仕組みです。
https://developer.android.com/guide/topics/ui/shortcuts.html

すでに実装例などがいくつか出ているので、ご存知の方も多いと思います。

## この記事は？
App Shortcutsの実装方法はもうすでに紹介されているので、ここではApp Shortcutsに対応しているアプリの一覧と、具体的なショートカットを表示する方法を紹介したいと思います :muscle:

# ホームアプリを作成する
早速ですが、他アプリのショートカット情報を取得するには、ホームアプリとして設定されている必要があります。
https://developer.android.com/reference/android/content/pm/LauncherApps.html#hasShortcutHostPermission()
> Only the default launcher can access the shortcut information.

Static Shortcutsならまだしも、Dynamic Shortcutsはユーザのアプリ使用状況などが反映されるので、プライバシーやセキュリティの観点から、このような仕様になっているのでしょう。

また、App Shortcutsに関する情報を取得するので、`minSdkVersion`を25にする必要があります。
（普段の開発ではこんなに高くできないので何かワクワクしますね :smile: ）

# ホームアプリとして宣言する
まずは、AndroidManifest.xmlで、ホームアプリ宣言をします。

```xml:AndroidManifest.xml
<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.HOME" />
    </intent-filter>
</activity>
```

`android.intent.category.HOME`を追加すればOKです。
（`android.intent.category.LAUNCHER`はいらないかも）

# ホームアプリとして設定する
アプリをインストールしたら、Settings > Apps > 右上の歯車アイコン > Home app から、このアプリをホームアプリとして設定します。
（ホームアプリを変更した場合でも、設定画面は通知バーから開けるので、そこから同じ手順で元に戻せます）

# ショートカット情報を取得する


## LauncherAppsの取得
```java
LauncherApps launcherApps = (LauncherApps) getSystemService(Context.LAUNCHER_APPS_SERVICE);
if (!launcherApps.hasShortcutHostPermission()) {
    // ホームアプリとして設定されていない
    return;
}
```

上述の通り、ホームアプリとして設定されていないと、何もできません。

# アプリケーションを取得する
```java
Intent intent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> resolveInfoList = getPackageManager().queryIntentActivities(intent, 0);
for (ResolveInfo resolveInfo : resolveInfoList) {
    if (resolveInfo.activityInfo == null) continue;
    ApplicationInfo applicationInfo = resolveInfo.activityInfo.applicationInfo;
    // ...
}
```

ここまでは、従来のホームアプリと同じだと思います。

# ショートカット情報を取得する
```java
int queryFlags = LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC | LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED | LauncherApps.ShortcutQuery.FLAG_MATCH_MANIFEST;
List<ShortcutInfo> shortcutInfoList = launcherApps.getShortcuts(new LauncherApps.ShortcutQuery().setPackage(applicationInfo.packageName).setQueryFlags(queryFlags), UserHandle.getUserHandleForUid(applicationInfo.uid));
for (ShortcutInfo shortcutInfo : shortcutInfoList) {
    // ...
}
```

[ShortcutQuery](https://developer.android.com/reference/android/content/pm/LauncherApps.ShortcutQuery.html)を適切に指定することで、全てのショートカットを取得してみます。

# アイコンの取得
ショートカットのアイコンは、[getShortcutIconDrawable()](https://developer.android.com/reference/android/content/pm/LauncherApps.html#getShortcutIconDrawable(android.content.pm.ShortcutInfo, int))で取得します。

```java
Drawable shortcutIcon = launcherApps.getShortcutIconDrawable(shortcutInfo, getResources().getDisplayMetrics().densityDpi);
```

2つ目の引数であるdensityは、0にすればデフォルトっぽくなりそうでしたが、普通にクラッシュしたので上のようにしました。
また、[getShortcutIconDrawable()](https://developer.android.com/reference/android/content/pm/LauncherApps.html#getShortcutIconDrawable(android.content.pm.ShortcutInfo, int))と[getShortcutBadgedIconDrawable()](https://developer.android.com/reference/android/content/pm/LauncherApps.html#getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int))の違いはよく分かりませんでした。

エミュレータでやってみるとこんな感じ。
![device-2016-12-16-235515 - Copy.png](https://qiita-image-store.s3.amazonaws.com/0/92361/1f31c766-4cd2-3bc5-efc1-840e411d3eb6.png)

アイコンの大きさがバラバラなのはよく分かりませんでした :sweat_smile:

# ショートカットの起動
[ShortcutInfo](https://developer.android.com/reference/android/content/pm/ShortcutInfo.html)に[getIntent()](https://developer.android.com/reference/android/content/pm/ShortcutInfo.html#getIntent())や[getIntents()](https://developer.android.com/reference/android/content/pm/ShortcutInfo.html#getIntents())があるので、これをstartすればいいじゃんと思いましたが、どうやらそうではないようです :astonished:

```java
startActivity(shortcutInfo.getIntent()); // NG
startActivities(shortcutInfo.getIntents()); // NG
```

正しくは[startShortcut()](https://developer.android.com/reference/android/content/pm/LauncherApps.html#startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle))を使います。

```java
launcherApps.startShortcut(shortcutInfo, null, null));
```

ただなぜか、設定アプリのWifiショートカットだけは、`ActivityNotFoundException`で起動できませんでした :sweat_smile:

# まとめ
App Shortcutsをホームアプリの側から調べてみました。
ホームアプリを作っている人はあまりいないと思いますが、通常のアプリでApp Shortcutsに対応しようとしている人の役にも立つかなと思います。

また、今回のソースコード全体は以下のリポジトリに置いてあります。
https://github.com/oxsoft/AppShortcutsViewer

何か間違いや意見などがありましたらコメントしていただけるとありがたいです :bow: