2012年4月9日月曜日

標準ランチャー周りの役に立たない知識

ホームにショートカットをおきたいとかランチャに対して何かをしたいことはあると思うんだけど、アプリの機能ごとにショートカットを作るなど、説明自体は割りと転がってるんだけど、標準ランチャの内部にまで突っ込んだ話は見ないので。

まずアプリでホームにショートカットを生成したいとき。

//ショートカットを作りたいアクティビティのインテントを作る
Intent shortcutIntent = new Intent();
shortcutIntent.setClassName("com.hoge.hoge", "hogeActivity");

//ショートカットを登録するインテントを作る
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");
setBroadcast(intent);

実際テストコードを書いてないけど、INSTALL_SHORTCUTのパーミッションを与えれば、これで生成して問題なく使えるはず。アイコンも設定できるけど、それは他の場所を参考にして下さい。

標準launcherはINSTALL_SHORTCUTのブロードキャストを受け取ると、そのインテントからuriを受け取り、アクションがなければACTION_VIEWを付与してショートカットを生成する。

しかしこの場合、アプリケーションを削除してもショートカットが残り続けるため手動で削除しなければならない。

なぜかというと、標準launcherはパッケージのアンインストールを感知する(正確にはcom.android.launcher.action.UNINSTALL_SHORTCUTのインテントを受け取るんだけど、これを誰が投げてるのかは分からない…)と、dbに格納されたショートカットとuriを比較して、同じだった場合にのみ削除を行うのだけれど。

このとき比較に、Intent#filterEqualsを使っているので、アクションとカテゴリも一致させないと同じuriだと認識されない

巷に転がっているショートカット生成コードは、classNameしか一致させていないので、パッケージ削除時にショートカットが削除対象から漏れてしまう。

//ショートカットを作りたいアクティビティのインテントを作る
Intent shortcutIntent = new Intent();
shortcutIntent.setClassName("com.hoge.hoge", "hogeActivity");
shortcutIntent.setAction(Intent.ACTION_MAIN);
shortcutIntent.addCategory(Intent.CATEGORY_LAUNCHER);

以上のようにアクションとカテゴリを追加すれば、アンインストール時に消えるショートカットになる。見ているのはclassNameとactionとcategoryだけなので、他にデータを付与したり、アイコンやショートカットの名前を変えても問題ない。

しかしこれでもまだ問題がある。ショートカット作成インテントを投げる度に無尽蔵にショートカットが作成されてしまう。どうすればいいのか?

標準launcherのコンテンツプロバイダ、content://com.android.launcher2.setteings/favoritesから、現在ホームに存在するショートカット一覧を取得して、もしすでに存在する場合にはショートカット生成インテントは飛ばさない、とやるのが一つの方法。

余談だけどREAD_SETTINGSパーミッションは必要とはいえ、protectionLevelはnormalなので、「他人のホーム画面に並べてるアプリ情報を抜いて趣味嗜好を観察する」とか、「自分のアプリがホームで一軍として利用されているか」とか、そういう使い道もできてしまう。

実はそんな面倒なことしなくても、duplicateをfalseにしておけば、ショートカットが重複して生成するのを抑制することができる。デフォルトの挙動はtrueなのが、不親切な感じ…。

//ショートカットを登録するインテントを作る
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);
setBroadcast(intent);

0 件のコメント:

コメントを投稿