Androidのホームショートカット作成時の挙動
ICSの頃にLauncher2のソースを読んだときには、行儀良くショットカットを作るには以下のような記述が必要でした。
//ホームに作成したいショートカットをIntentとして作成
//(ここではActivityが自身のショートカットをホームに作成すると仮定)
Intent shortcutIntent = new Intent();
shortcutIntent.setClassName(getPackageName(), getClass().getName());
shortcutIntent.setAction(Intent.ACTION_MAIN);
shortcutIntent.addCategory(Intent.CATEGORY_LAUNCHER);
//続いてショートカット作成のためのintentを生成
Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, "ショートカット名");
intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
intent.putExtra("duplicate",false);
sendBroadcast(intent);
なお、ショートカットを作成するには、com.android.launcher.permission.INSTALL_SHORTCUT
のパーミッションをManifestに記述する必要があります。
ちょっとした解説
intent.putExtra("duplicate",false);
は同じショートカットが生成されるのを防止するために必須でした。
キーの"duplicate"
はLauncher
クラスにEXTRA_SHORTCUT_DUPLICATE
として定数定義されているのですが、パッケージプライベートのためにこのように指定するしかありません。指定しなかった場合のデフォルトはtrue
(同一のショートカットの重複を許可する)です。
何をもって「同一のショートカット」と判定しているのかは、LauncherModel
のshortcutExists()
を読めば分かります。Intent.EXTRA_SHORTCUT_NAME
で指定した名前とintent
の内容をtoUri(0)
で文字列化したものとを、比較しているようです。
Intent.ACTION_MAIN
とIntent.CATEGORY_LAUNCHER
の付与は、アプリがアンインストールされた際にショートカットも自動的に削除されるために必須でした。これを付与しないとアプリ削除後も無効なショートカットが残り続けます。
コンポーネント名だけでショートカット用のIntent
を作成した場合には、Launcher2がIntent.ACTION_VIEW
を付与します。しかしアプリケーション削除時に消されるのは、そのパッケージのIntent.ACTION_MAIN
、Intent.CATEGORY_LAUNCHER
のショートカットだけなのです。
Launcher2自体にはパッケージの削除を関知するブロードキャストレシーバが存在しなかったため、具体的にこの処理がどこで行われているのか追跡していないのですが、アプリがアンインストールされると、システムがcom.android.launcher.action.UNINSTALL_SHORTCUT
を投げてショートカットの削除を行っているのだと推測しています。
UninstallShortcutReceiver
はショートカットの同一性をIntent
のfilterEquals()
で判定しており、ActionとCategoryを一致させる必要があるのです。
KitKatでの挙動の変化
Intent.EXTRA_SHORTCUT_NAME
の指定が任意になりました。未指定の場合、PackageManager
から自動的にアプリ名を取得して設定します。
Intent.ACTION_MAIN
とIntent.CATEGORY_LAUNCHER
も指定する必要がなくなりました。アプリがアンインストールされると、そのアプリのパッケージのショートカットは全て消えます。
ただし判定はコンポーネント名で行われていると思われるので、ウェブサイトをACTION_VIEW
で表示するようなものについては残ります。
また、Intent.ACTION_MAIN
とIntent.CATEGORY_LAUNCHER
が明示的に指定されている場合、Intent.FLAG_ACTIVITY_NEW_TASK
とIntent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
の起動フラグが設定されるようになりました。
duplicate
についても、指定する必要がなくなりました。Androidのソースコード上では、相変わらず指定されない場合、デフォルト値としてtrue
が与えられるはずなのですが、ショートカットが多重に生成されることはありません。
//ホームに作成したいショートカットをIntentとして作成
//(ここではActivityが自身のショートカットをホームに作成すると仮定)
Intent shortcutIntent = new Intent();
shortcutIntent.setClassName(getPackageName(), getClass().getName());
//続いてショートカット作成のためのintentを生成
Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
sendBroadcast(intent);
よって、これだけの記述でよくなりました。(下位互換を考えるとIntent.EXTRA_SHORTCUT_NAME
を指定する必要はあります)
余談
手元のNexus5だと、 duplicate
をtrue
にして複数のショートカット生成しようとすると、NullPointerException
で落ちます 。
そもそもduplicate
をtrue
にしてもショートカットを重複させることができなくなっています。しかしソースコード上では相変わらずこの値を見ているので、何がどうバグってるのか謎。