Android 14対応：画像ファイルinputからカメラアプリやメディアライブラリを開く方法

## はじめに
この記事では、Android 14で導入されたウェブ開発の課題について取り上げます。具体的には、type="file" accept="image/*" というinput elementでは、ユーザーが写真のアップロードかカメラの使用かの選択ができなくなった問題です。この記事では、何が変わったのか、既存のアプリにどのような影響があるのか、そしてAndroidだけでなくiOSを含む全OSで一貫性のあるアプリを実現可能な修正方法を2つ紹介します。

## 問題の理解
まず、Android の以前のバージョンと Android 14 が画像ファイルinputをどのように扱っているかを理解しましょう。まず、画像ファイルのみのinput elementを含むシンプルなウェブページを作成します。次に、iOS の Safari と Android 14 および 10 の Chrome でページを開きます。

```html
<input type="file" accept="image/*" />
```

iOS での動作を見てみましょう。選択肢を提供するポップアップウィンドウが表示されます。これはiOSの全てのブラウザにおいて一貫した振る舞いです。これはApp StoreがWebKit、つまりSafariエンジンの使用を要求しているためです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588855/5311dd0c-e482-c75e-bf50-ecd292951ba5.jpeg" alt="iOSfileinput1" width="300">

以下は Android 10 でのスクリーンショットです。ポップアップを必要とせず、ユーザーは写真を直接アップロードするか、左上のタイルをクリックしてカメラを開くことができます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588855/0efc61db-29fc-aea4-72fe-98a456f0e077.jpeg" alt="android10fileinput1" width="300">

それでは、Android 14での挙動を見てみましょう。Android 14では、以前のバージョン（Android 13）で導入されたPhoto Picker機能が標準的な対応として用いられていますが、このPhoto Pickerではカメラを開くオプションは提供されていません。この機能は、アプリがユーザーのメディアライブラリ全体ではなく、特定のメディアに対するアクセスを許可します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588855/d1150c36-8327-4551-784d-24d7c5991ee0.jpeg" alt="android14fileinput1" width="300">

画像ファイルのinputを必要とし、ユーザーがカメラかファイルアップロードの選択を必要とするアプリの場合、Android 14以前では、上記のinput elementを使用して、ユーザーにこの選択を簡単に提供できました。この機能はiOSとAndroidの両方のユーザーに対応していました。しかし、Android 14での挙動の変更により、これはもはや適用されません。

## 修正提案

### 修正 1: accept="image/*;capture=camera"

```html
<input id="input-2" type="file" accept="image/*;capture=camera" />
``` 

このinput elementは、前述の例と似ていますが、結果は異なります。見てみましょう：

Android 10

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588855/880df7a1-95b6-2f81-9641-422a02dcfbb2.jpeg" alt="android10fileinput2" width="300">

 Android 14
 
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588855/360d9b1f-ed0c-932b-4267-e532b2e128f0.jpeg" alt="android14fileinput2" width="300">

iOS
 
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588855/5b747668-d953-7cb0-1663-47c53800fcc2.jpeg" alt="iOSfileinput2" width="300">

AndroidとiOSの両方で機能する単一のinput elementを使用でき、この記事の冒頭で紹介したinputを置き換えると同時にAndroid14の問題を解決します。ただし、capture属性をaccept属性内に含めることは、標準的なHTML仕様ではありません。

この動作は、特定のブラウザが非標準の組み合わせをどのように解釈するかによるものです。ブラウザはaccept属性内にcapture=cameraがあると認識し、標準のファイル選択機能と並行してカメラキャプチャ機能を有効にするかもしれません。これは非標準であるため、動作は予測不可能です。一部のブラウザは非標準部分を無視するかもしれませんし、予期しない方法で解釈するかもしれません。

### 修正 2: capture="camera"

```html
<input type="file" accept="image/*" capture="camera" />
```

上記のinputは、iOS と両方の Android バージョンで直接カメラを開きます。明白な修正かもしれませんが、いくつかの欠点があります：

 - ユーザーに選択肢を提供したい場合、追加のinput elementが必要になり、UIが複雑になります。
 - iOS では、capture 属性を持たないファイルinputでユーザーに選択肢が提供されるため、追加のボタンは混乱を招く可能性があります。

以下のようなものを使用してユーザーの OS をチェックすることができます：

``` javascript
async function isAndroid() {
  if (navigator.userAgentData) {
    const brands = await navigator.userAgentData.brands;
    return brands.some(brand => brand.brand.toLowerCase().includes('android'));
  } else {
    return /Android/i.test(navigator.userAgent);
  }
}
```

これにより、Android デバイスに対してのみ第二のinputを条件付きでレンダリングすることができます。iOS のようなカスタムポップアップウィンドウを提供し、ユーザーに選択肢を提供することもできます。display: none; を持つinput elementを使用して、魅力的で使いやすい UI を作成することができます。

このような修正は、いくらかの設定が必要ですが、標準的な HTML 慣行に従っており、予測可能で将来にわたって安定した振る舞いが期待できます。