android ContentProviderで取得したvideoのパスを取得して再生する

今回やりたかったことは以下
・ギャラリーから動画を取得して
・その動画のパスを取得して
・その動画を再生

ギャラリー等の他のアプリからデータを取得する場合にContentProviderというクラスを使ってローカルDBからデータを取得する。検証したのはAndroid5以上。Android5未満のアプリはたぶん自分は手つけないので今回は考えない。

実装する必要があるのは以下の4つ
・ストレージへの読み込み権限追加
・ギャラリー起動
・ギャラリーからの戻り値でContentProviderを使ってファイルのパスを取得
・VideoViewで動画を再生

ストレージへの読み込み権限追加

AndroidManifest.xml

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
ギャラリー起動

ギャラリーを起動する

Intent intent = new Intent();
intent.setType("video/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Select Video"), REQUEST_TAKE_GALLERY_VIDEO);
ContentProviderを使ってファイルのパスを取得

やってることはローカルDBから、「content://」形式のパスをキーに絶対パスを取得してる。今回は「MediaStore.MediaColumns.DATA」というカラムを指定してデータのパスを取得しているが例えば「MediaStore.MediaColumns.MIME_TYPE」とかやればデータのMimeTypeが取得したりもできる。「_id=?」とかの部分は検索条件指定している。

public void onActivityResult(int requestCode, int resultCode, Intent data) {

    if (resultCode != RESULT_OK) {
        return;
    } else if (requestCode != REQUEST_TAKE_GALLERY_VIDEO) {
        return;
    }

    // data.getData() : content://com.android.providers.media.documents/document/video%3A34

    // ファイルパス取得
    String strDocId = DocumentsContract.getDocumentId(data.getData());
    String[] strSplittedDocId = strDocId.split(":");
    String strId = strSplittedDocId[strSplittedDocId.length - 1];

    Cursor cursor = getContentResolver().query(
        MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
        new String[]{ MediaStore.MediaColumns.DATA },
        "_id=?",
        new String[]{strId},
        null
    );

    if (!cursor.moveToFirst()) {
        // failed
        cursor.close();
    } else {
        // success
        String inputPath = cursor.getString(0);
        cursor.close();

        // inputPath : /storage/emulated/0/DCIM/Camera/VID_20190313_214028.mp4
    }
}

ギャラリーから返ってきた
・「content://com.android.providers.media.documents/document/video%3A34」というキーを使って
・「/storage/emulated/0/DCIM/Camera/VID_20190313_214028.mp4」というパスを取得した

VideoViewで動画を再生
VideoView videoView =(VideoView)findViewById(R.id.videoView);
MediaController mediaController= new MediaController(MainActivity.this);
mediaController.setAnchorView(videoView);
videoView.setMediaController(mediaController);
// videoView.setVideoURI(data.getData());
videoView.setVideoPath(inputPath);
videoView.requestFocus();
videoView.start();

取得したパスをsetVideoPath()でセットしてstart()を呼べば動画が再生される。setVideoURI()を使えばContentProviderからファイルの絶対パスを取得しなくても良いのだが今回はファイルのパスの仕方を調べたかったのでせっかくなのでこうした...

まとめ

というか全体のコード

AndroidManifest.xml

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

MainActivity.java

package xxx.yyy.zzz

import android.content.Intent;
import android.database.Cursor;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.MediaController;
import android.widget.VideoView;

public class MainActivity extends AppCompatActivity {

    private static final int REQUEST_TAKE_GALLERY_VIDEO = 3;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Intent intent = new Intent();
        intent.setType("video/*");
        intent.setAction(Intent.ACTION_GET_CONTENT);
        startActivityForResult(Intent.createChooser(intent, "Select Video"), REQUEST_TAKE_GALLERY_VIDEO);
    }

    public void onActivityResult(int requestCode, int resultCode, Intent data) {

        if (resultCode != RESULT_OK) {
            return;
        } else if (requestCode != REQUEST_TAKE_GALLERY_VIDEO) {
            return;
        }

        // ファイルパス取得
        String strDocId = DocumentsContract.getDocumentId(data.getData());
        String[] strSplittedDocId = strDocId.split(":");
        String strId = strSplittedDocId[strSplittedDocId.length - 1];

        Cursor crsCursor = getContentResolver().query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, new String[]{MediaStore.MediaColumns.DATA}
                , "_id=?", new String[]{strId}, null);

        if (!crsCursor.moveToFirst()) {
            crsCursor.close();
            return;
        }
        String inputPath = crsCursor.getString(0);
        crsCursor.close();
}

activity_main.xml

<VideoView android:id="@+id/videoView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

ちなみに実際のアプリ開発で利用する際は権限の許可をユーザーに求める処理も必要だが今回は試したかっただけなので書いてない。なのでAndroidの設定画面より等アプリのストレージ権限を追加しないと以下のようなエラーが出る。

java.lang.SecurityException: Permission Denial:
reading com.android.providers.media.MediaProvider uri content://media/external/video/media
from pid=9783, uid=10090 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()

Kotolinに慣れようと思いつつググるJavaのコードばかり出てくるのでなかなかやれてない。時間がないと言いわけしつつ、、以上です