162

Android サポート ライブラリのFileProviderを使用して、内部ファイルを外部アプリケーションと正しく共有する (OPEN ではない) 方法を探しています。

ドキュメントの例に従って、

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.example.android.supportv4.my_files"
    android:grantUriPermissions="true"
    android:exported="false">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/my_paths" />
</provider>

次のように、ShareCompat を使用してファイルを他のアプリと共有します。

ShareCompat.IntentBuilder.from(activity)
.setStream(uri) // uri from FileProvider
.setType("text/html")
.getIntent()
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)

FLAG_GRANT_READ_URI_PERMISSIONdataはインテントの で指定された Uri の許可のみを付与し、EXTRA_STREAMエクストラの値 (によって設定されたものsetStream) を付与しないため、機能しません。

プロバイダーを に設定android:exportedしてセキュリティを侵害しようとしましたが、それ自体がエクスポートされているかどうかを内部的にチェックし、エクスポートされている場合は例外をスローします。trueFileProvider

4

10 に答える 10

173

サポート ライブラリから使用FileProviderすると、他のアプリが特定の Uri を読み取るためのアクセス許可を (実行時に) 手動で付与および取り消す必要があります。Context.grantUriPermissionおよびContext.revokeUriPermissionメソッドを使用します。

例えば:

//grant permision for app with package "packegeName", eg. before starting other app via intent
context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);

//revoke permisions
context.revokeUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);

最後の手段として、パッケージ名を指定できない場合は、特定のインテントを処理できるすべてのアプリにアクセス許可を付与できます。

//grant permisions for all apps that can handle given intent
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
...
List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
    String packageName = resolveInfo.activityInfo.packageName;
    context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}

ドキュメントによる代替方法:

  • setData() を呼び出して、コンテンツ URI を Intent に入れます。
  • 次に、FLAG_GRANT_READ_URI_PERMISSION または FLAG_GRANT_WRITE_URI_PERMISSION、あるいはその両方を指定して Intent.setFlags() メソッドを呼び出します。
  • 最後に、インテントを別のアプリに送信します。ほとんどの場合、setResult() を呼び出してこれを行います。

    Intent で付与されたアクセス許可は、受信側の Activity のスタックがアクティブである間、有効なままです。スタックが終了すると、
    権限は自動的に削除されます。
    クライアント アプリの 1 つのアクティビティに付与されたアクセス許可
    は、そのアプリの他のコンポーネントに自動的に拡張されます。

ところで。必要に応じて、FileProvider のソースをコピーし、メソッドを変更attachInfoして、プロバイダーがエクスポートされているかどうかをチェックしないようにすることができます。

于 2013-08-20T09:50:06.143 に答える
15

私が知る限り、これは新しいバージョンの Android でのみ機能するため、おそらく別の方法を考え出す必要があります。このソリューションは 4.4 では機能しますが、4.0 または 2.3.3 では機能しません。そのため、これは、任意の Android デバイスで実行することを意図したアプリのコンテンツを共有するための便利な方法ではありません。

manifest.xml 内:

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.mydomain.myapp.SharingActivity"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

権限の指定方法に注意してください。URI を作成して共有インテントを起動するアクティビティを指定する必要があります。この場合、アクティビティは SharingActivity と呼ばれます。この要件は、Google のドキュメントから明らかではありません。

file_paths.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="just_a_name" path=""/>
</paths>

パスの指定方法に注意してください。上記のデフォルトは、プライベート内部ストレージのルートです。

SharingActivity.java 内:

Uri contentUri = FileProvider.getUriForFile(getActivity(),
"com.mydomain.myapp.SharingActivity", myFile);
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.setType("image/jpeg");
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(shareIntent, "Share with"));

この例では、JPEG 画像を共有しています。

最後に、ファイルを適切に保存し、次のようにしてアクセスできることを確認することをお勧めします。

File myFile = getActivity().getFileStreamPath("mySavedImage.jpeg");
if(myFile != null){
    Log.d(TAG, "File found, file description: "+myFile.toString());
}else{
    Log.w(TAG, "File not found!");
}
于 2014-01-19T22:37:08.887 に答える
9

私のアプリでは FileProvider は問題なく動作し、ファイル ディレクトリに保存されている内部ファイルを Gmail や Yahoo などの電子メール クライアントに添付できます。

Androidのドキュメントに記載されているように、マニフェストに次のように配置しました。

<provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.package.name.fileprovider"
        android:grantUriPermissions="true"
        android:exported="false">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/filepaths" />
    </provider>

また、私のファイルはルート ファイル ディレクトリに保存されていたため、filepaths.xml は次のようになりました。

 <paths>
<files-path path="." name="name" />

今コードで:

 File file=new File(context.getFilesDir(),"test.txt");

 Intent shareIntent = new Intent(android.content.Intent.ACTION_SEND_MULTIPLE);

 shareIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,
                                     "Test");

 shareIntent.setType("text/plain");

 shareIntent.putExtra(android.content.Intent.EXTRA_EMAIL,
                                 new String[] {"email-address you want to send the file to"});

   Uri uri = FileProvider.getUriForFile(context,"com.package.name.fileprovider",
                                                   file);

                ArrayList<Uri> uris = new ArrayList<Uri>();
                uris.add(uri);

                shareIntent .putParcelableArrayListExtra(Intent.EXTRA_STREAM,
                                                        uris);


                try {
                   context.startActivity(Intent.createChooser(shareIntent , "Email:").addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));                                                      


                }
                catch(ActivityNotFoundException e) {
                    Toast.makeText(context,
                                   "Sorry No email Application was found",
                                   Toast.LENGTH_SHORT).show();
                }
            }

これは私のために働いた.これが役立つことを願っています:)

于 2014-10-28T13:27:27.023 に答える
6

カメラから画像を取得する場合、これらのソリューションはいずれも Android 4.4 では機能しません。この場合、バージョンを確認することをお勧めします。

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (intent.resolveActivity(getContext().getPackageManager()) != null) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        uri = Uri.fromFile(file);
    } else {
        uri = FileProvider.getUriForFile(getContext(), getContext().getPackageName() + ".provider", file);
    }
    intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
    startActivityForResult(intent, CAMERA_REQUEST);
}
于 2018-03-01T16:10:37.733 に答える
1

上記の回答を改善するためだけに: NullPointerEx を取得している場合:

コンテキストなしで getApplicationContext() を使用することもできます

                List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(takePictureIntent, PackageManager.MATCH_DEFAULT_ONLY);
                for (ResolveInfo resolveInfo : resInfoList) {
                    String packageName = resolveInfo.activityInfo.packageName;
                    grantUriPermission(packageName, photoURI, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
                }
于 2017-01-01T09:23:51.623 に答える