onOptionsItemSelected の簡略化

# ■ 概要
[onOptionsItemSelected()](https://developer.android.com/reference/android/app/Activity#onOptionsItemSelected(android.view.MenuItem)) は、もっと簡易な呼び出しがあっても良さそうな気がするので、試しに作ってみたという話。

# ■ 仕様
本家で[こんな感じの説明](https://developer.android.com/guide/topics/ui/menus#RespondingOptionsMenu)がされているので、仕様を以下のようにしてみた：

- 呼び出し方法
  - itemId と、それに対応する処理を指定する。
- 実行内容
  - 指定された itemId が対象だった場合は、対応する処理を実行し、問答無用で true を返し、super は呼ばない。[^1]
  - 指定された itemId が対象でなかった場合は、super を呼び出す。

# ■ 例
## ☆ 従来の書き方
```kotlin
// For 1 menu item.
override fun onOptionsItemSelected(item: MenuItem): Boolean =
    when (item.itemId) {
        android.R.id.home -> {
            finish()
            true
        }
        else -> super.onOptionsItemSelected(item)
    }

// For multiple menu items.
override fun onOptionsItemSelected(item: MenuItem): Boolean =
    when (item.itemId) {
        android.R.id.home -> {
            finish()
            true
        }
        R.id.menu_foo -> {
            foo("bar")
            true
        }
        else -> super.onOptionsItemSelected(item)
```

## ☆ 簡略化した場合
おまじないは結構長いが[^2]、本質的な部分はほぼ宣言的に書けるようになる。

```kotlin
// For 1 menu item.
override fun onOptionsItemSelected(item: MenuItem): Boolean = onOptionsItemSelected(item, { super.onOptionsItemSelected(item) },
    android.R.id.home, ::finish
)

// For multiple menu items.
override fun onOptionsItemSelected(item: MenuItem): Boolean = onOptionsItemSelected(item, { super.onOptionsItemSelected(item) }, mapOf(
    android.R.id.home to ::finish,
    R.id.menu_foo     to { foo("bar") }
))
```

java から呼ぶ場合の例：

```java
// For multiple menu items.
public boolean onOptionsItemSelected(MenuItem item) {
    return onOptionsItemSelectedForJava(item, () -> super.onOptionsItemSelected(item), ImmutableMap.of(
        android.R.id.home, this::finish,
        R.id.menu_foo    , () -> foo("bar")
    ));
}
```

# ■ コード
Activity も Fragment も同一コードで対応できます。[^3]

```kotlin
/**
 * A utility extension function for [Activity.onOptionsItemSelected] and [Fragment.onOptionsItemSelected].
 *
 * The following process will be executed:
 * - If [targetItemId] is selected, run [onSelected] and return true.
 * - If [targetItemId] is not selected, call <code>super.onOptionsItemSelected(item)</code> and return the result.
 */
fun onOptionsItemSelected(item: MenuItem, callSuper: () -> Boolean, targetItemId: Int, onSelected: Runnable): Boolean =
    when (item.itemId) {
        targetItemId -> {
            onSelected.run()
            true
        }
        else         -> callSuper()
    }

/**
 * @see [onOptionsItemSelected]
 */
fun onOptionsItemSelected(item: MenuItem, callSuper: () -> Boolean, targetItemIdAndOnSelectedMap: Map<Int, () -> Unit>): Boolean =
    targetItemIdAndOnSelectedMap[item.itemId]?.run { invoke(); true } ?: callSuper()

/**
 * @see [onOptionsItemSelected]
 */
fun onOptionsItemSelectedForJava(item: MenuItem, callSuper: () -> Boolean, targetItemIdAndOnSelectedMap: Map<Int, Runnable>): Boolean =
    targetItemIdAndOnSelectedMap[item.itemId]?.run { run(); true } ?: callSuper()
```

# ■ 考察
- 対象の menuItem が存在した際に super を呼ばずに true を返す方式は、親クラスの item 処理を完全に上書きしてしまうかつ Fragment に item を伝播しないと割り切るということになる。しかしそもそも、同じ menuItem に対する処理を、継承や Fragment への伝播で拡散させるのは設計的に複雑すぎる気がするので、割り切りはアリな気がする。
- super の呼び出しは caller sensitive なので、minSdk を API26 以上にできるまでは我慢ですかね。

ということで、ひとまずおしまい。

[^1]: 親クラスの処理も呼ばないし Fragment への伝播も行わない。
[^2]: super の呼び出しは caller sensitive なので、minSdk を API26 以上にできるまでは我慢。
[^3]: API26 以上なら super 呼び出しをライブラリ側に記述することが可能。その際は、普通にやるなら Activity と Fragment 用の extension になって、コードが別になるはず。(Reflection を使えば入り口だけ分けてその他のコードは共通化できそうだけど)