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にしてもショートカットを重複させることができなくなっています。しかしソースコード上では相変わらずこの値を見ているので、何がどうバグってるのか謎。