超ラクチンに端末の画像を複数選択するためのライブラリを作った話

Android の端末に保存されている画像を、アルバム（ディレクトリ）を跨って複数枚選択するためのライブラリを作りました。

https://github.com/nohana/Laevatein

## Intent の Action による画像選択の問題

`Intent.ACTION_PICK` や `Intent.ACTION_GET_CONTENT` を用いれば、ギャラリーアプリや KitKat のファイルセレクタが立ち上がって、使いたい画像を選択することができます。

ただし、この方法では、画像を 1 枚しか選択できないため、複数枚を同時に取り扱おうとすると、ユーザに何度も選択画面との往来をさせてしまいます。

一方で、`Intent.ACTION_SEND_MULTIPLE` という Action も宣言されており、ギャラリーアプリなどで複数選択して共有ボタンを押すと、この Action で Intent が発火され、選択した複数の画像 Uri が取得できます。

しかし、ギャラリーアプリからの共有 Intent では、複数のアルバム（ディレクトリ）から一部の写真だけをピックアップしてくる、という使い方ができません。

また、アプリを使用するコンテキストで、ギャラリーアプリから選択して目的のアプリを立ち上げるという動作を説明するのは難易度が高かったりします。

## このライブラリが目指したもの

まず、アプリに組み込んで使えること、そして、複数のアルバムから一部をピックアップして、複数の写真を選択することを達成できることが、このライブラリの目指したところです。

そしてなにより、単純な API で使用できることも重要でした。

## 動作する様子

リポジトリには、サンプルアプリも同封されています。

ライブラリの機能を呼び出すと、以下の様な、すべての写真一覧が表示されます。

![device-2014-03-28-162335.png](https://qiita-image-store.s3.amazonaws.com/0/31829/84021fcc-51a4-8027-dff4-06dcbf50a8d6.png)

画像と、画像にオーバレイする形でチェックボックスが表示され、チェックマークをつけると選択したことになります。

アルバムの絞込は、NavigationDrawer で行います。

![device-2014-03-28-162413.png](https://qiita-image-store.s3.amazonaws.com/0/31829/99abd674-c026-2b98-0b23-aba7a91a463c.png)

端末内で MediaStore が知っている Bucket 毎にアルバムがわかれています。

画像の選択状態は、アルバムを切り替えても保存されています。
そこで、全体でどの写真を選んだかを見るためのビューも用意してあります。

![device-2014-03-28-162626.png](https://qiita-image-store.s3.amazonaws.com/0/31829/fcdfba6b-0922-a1ac-5823-5f6ec8c3915c.png)

これで、アクションバーの完了ボタンを押すと、選択した画像の Uri が、呼び出した Activity の onActivityResult に渡されます。

## 使い方

API の呼び出しは、Picasso とよく似た手順で行います。

```java

public class SomeActivity extends Activity {
  public static final int REQUEST_CODE_CHOOSE = 1;

  public void onClickButton(View view) {
    // call chooser on click button like this
    Laevatein.from(this).choose(MimeType.of(MimeType.JPEG)).forResult(REQUEST_CODE_CHOOSE);
  }
}
```

これで選択画面が立ち上がります。

選択が終わったら、以下のようにして結果を取り出します。

```java

public class SomeActivity extends Activity {
  public static final int REQUEST_CODE_CHOOSE = 1;

  @Override
  protected void onActivityResult(int requestCode, int resultcode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    switch (requestCode) {
    case REQUEST_CODE_CHOOSE:
      if (resultCode == RESULT_OK) {
        // Get result and proceed your work from here...
        List<Uri> selected = Laevatein.obtainResult(data);
      }
      break;
    default:
      break;
    }
  }
}
```

## カスタマイズ

基本的に、テーマは全てアプリの持つテーマに合わせられます。ですので、ActionBar の装飾だったり、チェックボックスのスタイルだったりは、ライブラリ用に特別な実装を必要としません。

画像一覧のセルの見た目を変更したい場合は、以下のようにします。

```java

Laevatein.from(this).choose(MimeType.of(MimeType.JPEG))
        .bindEachImageWith(R.layout.hoge, R.id.image_view_id, R.id.checkbox_id)
        .forResult(1);
```

選択できる画像について、制限のある仕様を設定したい場合、以下の様な API が提供されています。

```java

Laevatein.from(this).choose(MimeType.of(MimeType.JPEG))
        .count(0, 100) // 選べる数を 0 ~ 100 枚に制限
        .quality(10, 10000) // 選べる画像の画質を、ピクセルサイズで 10px から 10000px に制限
        .forResult(1);
```

選択している最中に写真をとって、それを選択状態にしておきたい場合は以下のようにします。

```java

Laevatein.from(this).choose(MimeType.of(MimeType.JPEG))
        .capture(true)
        .forResult(1);
```

選び終わった後に、選択をやり直したい場合は、以下のようにします。

```java

List<Uri> list; // 選択したものを保持するリスト

Laevatein.from(this).choose(MimeType.of(MimeType.JPEG))
        .resume(list)
        .forResult(1);
```

Picasso のようにメソッドをつなげることで、様々な組み合わせで API を呼び出し、仕様を設定することができます。

## 今後の展望

KitKat で登場したファイルセレクタの利点は、ドキュメントプロバイダを持っているアプリからのデータのインポートが出来る点にあります。これをこのライブラリでも取り扱うことが出来るようになれば、Dropbox などからも画像を選択できるようになり、より便利に使える様になると思います。

## コントリビュート

Issue を立てて修正を依頼していただくか、Pull Request で差分を投げていただくかで、コントリビュートしていただけます。