2012/06/27

Android:ShareActionProvider

ShareActionProviderは、共有アクションを提供するクラスです。

共有アクションに応答できるActivityを管理し、ユーザの選択履歴を記録することで
"よく使う共有アイテム"のような機能を提供します。

●動作イメージ

初回は「共有アクション」のみ表示。


一度でも共有アイテムを選択すると、選択履歴として蓄積され"よく使う共有アイテム"
には専用のUIが用意されます。


メニューアイテムがoverflowの場合は、共有可能なアイテムをサブメニューとして表示
します。



共有可能なアイテムは"よく使う順"にソートされた状態でリスト表示されます。


●使い方

ShareActionProviderはメニューアイテムと関連付けされます。
メニューアイテムがActionItemとして表示されているか、OverflowItemとして表示されて
いるかによって若干振る舞いが変化します。
しかし、開発者はメニューアイテムがActionItem状態かOverflowItem状態かを意識する
必要はありません。
状態の違いからくる動作差異はShareActionProviderによってカプセル化されています。

ShareActionProviderを使う手順はシンプルです。
 1. メニューアイテムにShareActionProviderを関連付ける
 2. メニュー生成時にShareActionProviderインスタンスを取得する
 3. 共有IntentをShareActionProviderに設定する

1はメニューリソースで指定可能です。
関連付けにはitem要素のandroid:actionProviderClass属性で行います。
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/menu_item_share_action"
        android:showAsAction="always"
        android:title="@string/menu_share1"
        android:actionProviderClass="android.widget.ShareActionProvider" />
</menu>
2はメニュー生成時のコールバックメソッドonCreateOptionsMenuで可能です。
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_share_action, menu);
    MenuItem shareAction = menu.findItem(R.id.menu_item_share_action);
    mShareActionProvider
        = (ShareActionProvider)shareAction.getActionProvider();

    return true;
}
3はShareActionProvider.setShareIntent(intent)メソッドで可能です。
下記は0-9が押されたら、キーラベルを共有するコードです。
Cキーが押された場合は共有インテントをクリアします。
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (KeyEvent.KEYCODE_9 >= keyCode && keyCode >= KeyEvent.KEYCODE_0) {
        Intent sharedIntent = new Intent(Intent.ACTION_SEND);
        sharedIntent.setType("text/plain");
        sharedIntent.putExtra(
                Intent.EXTRA_TEXT, String.valueOf(event.getDisplayLabel()));

        doShare(sharedIntent);
    } else if (KeyEvent.KEYCODE_C == keyCode) {
        doShare(null);
    }

    return super.onKeyDown(keyCode, event);
}

private void doShare(Intent intent) {
    mShareActionProvider.setShareIntent(intent);
}
ShareActionProviderはsetShareIntentを契機に共有アイテムのリストを更新します。
setShareIntentにnullが渡された場合は、共有アイテム無しとして扱われます。



この状態ではActionItemを押下しても無反応。



setShareIntentに共有Intentを設定すると共有アイテムリストが自動更新される。



setShareIntentにnullを設定すると共有アイテム無しとなる。


ちなみに、"よく使う共有アイテム"を長押下すると共有アイテムリストが表示される機能
も搭載されています。



●堅牢性

ShareActionProviderは堅牢性も備えています。
例えば、"よく使う共有アイテム"の対象となっているパッケージが無効化されていた場合、
別のよく使う共有アイテムを表示するようになっています。

しかし、共有アイテムリスト表示中にバックグラウンドへ一時退避して、リストに含まれ
るパッケージを無効化した後にアプリを復帰させると、共有アイテムリストに含まれるパ
ッケージは表示され続け、これを選択するとActivityNotFound例外で強制終了します。
ただし、これは特異な手順となるため対応の必要性はほぼ無いでしょう。


●注意

通常、ActionItemが選択されるとonOptionsItemSelectedが呼ばれます。
しかし、ActionItemのShareActionProviderのアイテムを選択しても、onOptionsItemSelected
は呼ばれません。※メニューがoverflow状態の場合は呼ばれます。

共有アイテムの中に同じパッケージ名をもつアイテムが複数ある場合は注意が必要です。
例えば、"android.intent.action.VIEW"には下記2つのActivityが反応します。
 - com.android.development/com.android.development.AppHwPref
 - com.android.development/com.android.development.ProcessInfo
"よく使う共有アイテム"にAppHwPrefが登録されていると仮定します。
あなたはこれを選択してAppHwPrefを起動します。これは正しく起動できるはずです。
しかし、もう一度これを選択した場合は"ProcessInfo"が起動されます。
ユーザの意図に反する動作のように見えますが、、、
ShareActionProviderは"よく使う共有アイテム"に属するコンポーネントのパッケージが
複数ある場合は、選択する都度スイッチするような動作になっています。


●その他

ShareActionProviderはどのようにして、共有アクション履歴を管理しているのでしょうか
これは、アプリローカルに選択履歴をxmlファイルとして保存することで実現しています。
デフォルトでは、/data/data/<ApplicationPackageName>/files配下にshare_history.xml
の名前で保存されます。

share_history.xmlは下記のような内容になっています。
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<historical-records>
    <historical-record activity="com.android.calendar/com.android.calendar.EventInfoActivity" time="1340782021420" weight="1.0" />
    <historical-record activity="com.android.development/com.android.development.ProcessInfo" time="1340783164738" weight="1.0" />
    <historical-record activity="com.android.development/com.android.development.ProcessInfo" time="1340783171474" weight="1.0" />
    <historical-record activity="com.android.development/com.android.development.AppHwPref" time="1340783189589" weight="1.0" />
    <historical-record activity="com.android.development/com.android.development.ProcessInfo" time="1340783191079" weight="1.0" />
    <historical-record activity="com.android.development/com.android.development.AppHwPref" time="1340783195569" weight="1.0" />
    <historical-record activity="com.android.development/com.android.development.ProcessInfo" time="1340783197179" weight="1.0" />
</historical-records>
共有アイテムが選択されるとhistorical-record要素が末尾に追加されていきます。

選択履歴のxmlファイルはsetShareHistoryFileNameメソッドで指定可能です。


共有アクションを実装したい場合、ShareActionProviderは使いやすさも確保できる便利
なクラスといえそうです。

以上です。