Android7の場合ActionModeメニューのカスタマイズ

# WebViewのActionModeメニューのカスタマイズ
WebViewの文字を選択する時に表示されるActionModeメニューをカスタマイズするプログラムはAndroid7でクラッシュになったため、発生原因と対応策をメモします。

# Action Modeメニュー表示場所
Android6以前は、アプリ画面の一上のActionBarにActionModeのメニューが表示されますが、Android6からiOSのようにFloating Action Modeになり、浮かんでいるコンテキストメニューが表示されるようになりました。

#独自のActionModeメニューの定義
ActionModeが表示された時に、OSにより表示されるメニューをクリアして、独自なメニューに入れ替える。

- メニュー定義

```xml:mymenu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_menu_item_search"
        android:icon="@android:drawable/ic_menu_search"
        android:onClick="onActionItemClicked"
        android:orderInCategory="1"
        app:showAsAction="always"
        android:title="@string/action_menu_item_search">
    </item>
    <item
        android:id="@+id/action_menu_item_copy"
        android:icon="@drawable/ic_menu_copy"
        android:onClick="onActionItemClicked"
        android:orderInCategory="2"
        app:showAsAction="always"
        android:title="@string/action_menu_item_copy">
    </item>


</menu>
```

- ActionMode起動メソッド

```java:MyActivity
    @Override
    public void onActionModeStarted(ActionMode mode) {
        if (actionMode == null) {
            actionMode = mode;
            Menu menu = mode.getMenu();
            menu.clear();

            // Inflate your own menu items
            MenuInflater inflater = mode.getMenuInflater();
            inflater.inflate(R.menu.mymenu, menu);
        }
        super.onActionModeStarted(mode);
    }
```

# Android7 以降の場合InflateExceptionが発生
Android6までは問題なく動きましたが、Android7.0とAndroid7.1.1でInflateExceptionが発生しました。

```java:エラースタック
W/System.err: android.view.InflateException: Couldn't resolve menu item onClick handler onActionItemClicked in class android.app.ContextImpl
W/System.err:     at android.view.MenuInflater$InflatedOnMenuItemClickListener.<init>(MenuInflater.java:242)
W/System.err:     at android.view.MenuInflater$MenuState.setItem(MenuInflater.java:460)
W/System.err:     at android.view.MenuInflater$MenuState.addItem(MenuInflater.java:494)
W/System.err:     at android.view.MenuInflater.parseMenu(MenuInflater.java:190)
W/System.err:     at android.view.MenuInflater.inflate(MenuInflater.java:111)
W/System.err:     at com.myapp.joke.MyActivity.onActionModeStarted(MyActivity.java:349)
W/System.err:     at android.support.v7.view.WindowCallbackWrapper.onActionModeStarted(WindowCallbackWrapper.java:158)
W/System.err:     at com.android.internal.policy.DecorView.startActionMode(DecorView.java:868)
W/System.err:     at com.android.internal.policy.DecorView.startActionModeForChild(DecorView.java:816)
W/System.err:     at android.view.ViewGroup.startActionModeForChild(ViewGroup.java:828)
W/System.err:     at android.view.ViewGroup.startActionModeForChild(ViewGroup.java:828)
W/System.err:     at android.view.ViewGroup.startActionModeForChild(ViewGroup.java:828)
W/System.err:     at android.view.ViewGroup.startActionModeForChild(ViewGroup.java:828)
W/System.err:     at android.view.ViewGroup.startActionModeForChild(ViewGroup.java:828)
W/System.err:     at android.view.ViewGroup.startActionModeForChild(ViewGroup.java:828)
W/System.err:     at android.view.ViewGroup.startActionModeForChild(ViewGroup.java:828)
W/System.err:     at android.view.View.startActionMode(View.java:5828)
W/System.err:     at org.chromium.content.browser.ContentViewCore.startFloatingActionMode(ContentViewCore.java:2253)
W/System.err:     at org.chromium.content.browser.ContentViewCore.startActionMode(ContentViewCore.java:2238)
W/System.err:     at org.chromium.content.browser.ContentViewCore.showSelectActionMode(ContentViewCore.java:2213)
W/System.err:     at org.chromium.content.browser.ContentViewCore.onSelectionEvent(ContentViewCore.java:2319)
W/System.err:     at org.chromium.android_webview.AwContents.nativeOnDraw(Native Method)
W/System.err:     at org.chromium.android_webview.AwContents.access$4700(AwContents.java:98)
W/System.err:     at org.chromium.android_webview.AwContents$AwViewMethodsImpl.onDraw(AwContents.java:2980)
W/System.err:     at org.chromium.android_webview.AwContents.onDraw(AwContents.java:1307)
W/System.err:     at com.android.webview.chromium.WebViewChromium.onDraw(WebViewChromium.java:1697)
W/System.err:     at android.webkit.WebView.onDraw(WebView.java:2534)
W/System.err:     at android.view.View.draw(View.java:17185)
W/System.err:     at android.view.View.updateDisplayListIfDirty(View.java:16167)
W/System.err:     at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3711)
W/System.err:     at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3691)
W/System.err:     at android.view.View.updateDisplayListIfDirty(View.java:16130)
W/System.err:     at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3711)
W/System.err:     at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3691)
W/System.err:     at android.view.View.updateDisplayListIfDirty(View.java:16130)
W/System.err:     at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3711)
W/System.err:     at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3691)
W/System.err:     at android.view.View.updateDisplayListIfDirty(View.java:16130)
W/System.err:     at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3711)
W/System.err:     at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3691)
W/System.err:     at android.view.View.updateDisplayListIfDirty(View.java:16130)
W/System.err:     at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3711)
W/System.err:     at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3691)
W/System.err:     at android.view.View.updateDisplayListIfDirty(View.java:16130)
W/System.err:     at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3711)
W/System.err:     at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3691)
W/System.err:     at android.view.View.updateDisplayListIfDirty(View.java:16130)
W/System.err:     at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3711)
W/System.err:     at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3691)
W/System.err:     at android.view.View.updateDisplayListIfDirty(View.java:16130)
W/System.err:     at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3711)
W/System.err:     at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3691)
W/System.err:     at android.view.View.updateDisplayListIfDirty(View.java:16130)
W/System.err:     at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:648)
W/System.err:     at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:654)
W/System.err:     at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:762)
W/System.err:     at android.view.ViewRootImpl.draw(ViewRootImpl.java:2800)
W/System.err:     at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2608)
W/System.err:     at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2215)
W/System.err:     at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1254)
W/System.err:     at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6337)
W/System.err:     at android.view.Choreographer$CallbackRecord.run(Choreographer.java:874)
W/System.err:     at android.view.Choreographer.doCallbacks(Choreographer.java:686)
W/System.err:     at android.view.Choreographer.doFrame(Choreographer.java:621)
W/System.err:     at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:860)
W/System.err:     at android.os.Handler.handleCallback(Handler.java:751)
W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:95)
W/System.err:     at android.os.Looper.loop(Looper.java:154)
W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:6119)
W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
W/System.err: Caused by: java.lang.NoSuchMethodException: onActionItemClicked [interface android.view.MenuItem]
W/System.err:     at java.lang.Class.getMethod(Class.java:1981)
W/System.err:     at java.lang.Class.getMethod(Class.java:1637)
W/System.err:     at android.view.MenuInflater$InflatedOnMenuItemClickListener.<init>(MenuInflater.java:240)
W/System.err: 	... 71 more
```

# 例外の原因
WebViewから生成されるFloatingActionModeのコンテキストがMyActivityではなく、DecorViewになっています。DecorViewのベースコンテキストがApplicationであります。Menu定義ファイルに設定されたonclickメソッド「onActionItemClicked」がMyActivityに定義されていますが、DecorViewからベースコンテキストを探しても見つからないため、InflateExceptionがスローされます。

# 臨時対応策
他の良い方法があるかもしれませんが、臨時対策として、リフレクションでMenuInflaterのコンテキストをMyActivityに入れ替えます。

```java:MyActivity修正後
    public void onActionModeStarted(ActionMode mode) {
        if (actionMode == null) {
            actionMode = mode;
            Menu menu = mode.getMenu();
            menu.clear();

            // Inflate your own menu items
            MenuInflater inflater = mode.getMenuInflater();
            if(Build.VERSION.SDK_INT >= 24) {
                try {
                    Field field = FieldUtils.getField(inflater.getClass(), "mContext", true);
                    field.set(inflater, this);
                } catch (IllegalAccessException e) {
                }
            }
            inflater.inflate(R.menu.mymenu, menu);
        }
        super.onActionModeStarted(mode);
    }
```