2016年8月31日水曜日

play video from android internal storage

自アプリ内のコンテンツの再生を、暗黙的インテントで他アプリに依頼するとします。

本当は、getFilesDir()を使って/data/data/パッケージ名/files下に保存して、ファイルプロバイダーで依頼するのがいいと思います。

しかしですね、諸事情で/data/data/パッケージ名/app_a/b/c/d/e/にコンテンツを保存せざるを得ないとします。
主に仕様とか仕様とか仕様とか。

しかも、コンテンツがクソ重くてコピーもままならず、このままインテントで再生を依頼するしかないと仮定します。


この場合、コードとしてはこんな感じになります。

play video from android internal storage

(以下引用)

Uri fileUri = Uri.fromFile(new File(String file_path));
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(fileUri, URLConnection.guessContentTypeFromName(fileUri.toString()));
startActivity(intent);
このURLConnection#guessContentTypeFromName、優秀です。
この呪文1つ唱えるだけでテキストでも動画でも画像でも再生できます。
拡張子で場合分けする必要はありません。
(再生に対応するアプリが存在すればね)

さて……。
これで再生できれば、もうここから先を読む必要はありません。

どうです?
再生できました?


ここからは、再生できなかった人向けです。
しかも、こういうケースです。

暗黙的インテントで対応するアプリを選ぶダイアログが起動する
再生するアプリで再生を行うと「この動画は再生できません」的メッセージ
Logcatでログを確認するとAwesomePlayerとかからpermission deniedって言われてる
他にも(1, -2147483648)とか

こういう場合、ログに素直に従って再生コンテンツのパーミッションを確認しましょう。

adbコマンドで/data/下にアクセスできるかどうかがポイントですね。
lsでpermission deniedと言われても、run-as パッケージ名でアクセスできる可能性があります。

コンテンツの保存されているディレクトリへ向けて、1つずつパーミッションを確認していきましょう。
run-asができるかどうかは機種依存らしいので、ダメなら別の方法で確認します。

コンテンツを一旦/data/data/パッケージ名/app_aに移動させて、暗黙的インテントで再生できるか確認してみてください。
app_aはgetDir()で作成するのが一般的と思いますが、この時にファイルアクセスを設定できます。
Context.MODE_WORLD_READABLEとか付ければいいんじゃないですかね、たぶん。

さて、どうでした?
途中のディレクトリも含めて他アプリから読み取り可能なパーミッションでしたか?
/data/data/パッケージ名/app_a直下であれば再生可能でしたか?

自分の場合はrun-asで確認できまして、途中のディレクトリのパーミッションの設定が他アプリから読み取り可能ではありませんでした。

原因は、/data/data/パッケージ名/app_aディレクトリ以下をmkdirs()で作ったことだったっぽいです。
mkdir()だと親ディレクトリのパーミッションを引き継ぐとどこかで読んだ気がするのですが、mkdirs()は違うんですかね?

対処法ですが、File#setReadable(boolean executable, boolean ownerOnly)とかでmkdir()したディレクトリを読み取り可能にしてあげれば良いです。

int mode = Context.MODE_WORLD_WRITABLE | CONTEXT.MODE_WORLD_READABLE;
// /data/data/パッケージ名/直下にフォルダを作るとapp_がくっつきます
File root = getDir("a", mode);
File second = new File(root, "b");
if (!second.exists()) {
second.mkdir();
second.setExecutable(true, false);
second.setWritable(true, false);
second.setReadable(true, false);
}

0 件のコメント:

コメントを投稿