ファイルの共有

コンテンツ URI を使用してファイルを共有するようにアプリを設定したら、他のアプリの 作成できるようになります。このようなリクエストに対応する一つの方法は、ファイルの選択です。 他のアプリケーションが呼び出せるサーバーアプリからインターフェースを提供します。この方法では アプリケーションを使用して、ユーザーがサーバーアプリからファイルを選択し、選択したファイルの 指定します。

このレッスンでは、アプリでファイル選択 Activity を作成する方法について説明します。 リクエストに応答する Cloud Functions の関数を作成しました

ファイル リクエストを受信する

クライアント アプリからファイルのリクエストを受信し、コンテンツ URI を返すには、アプリで ファイル選択 Activity を指定します。クライアント アプリは、 アクションを含む Intent を指定して startActivityForResult() を呼び出すことで Activity ACTION_PICK。クライアント アプリが startActivityForResult() さん、アプリは次のことができます ユーザーが選択したファイルのコンテンツ URI の形式で結果をクライアント アプリに返す。

クライアント アプリでファイルのリクエストを実装する方法については、レッスンをご覧ください。 共有ファイルのリクエスト

ファイル選択アクティビティを作成する

ファイル選択 Activity を設定するには、まず マニフェストで Activity(インテント フィルタを指定) アクション ACTION_PICK、 カテゴリ: CATEGORY_DEFAULTCATEGORY_OPENABLE。MIME タイプのフィルタも追加する アプリが他のアプリに配信するファイルの 詳細が表示されます次のスニペットは、Cloud Storage バケットを 新しい Activity とインテント フィルタ:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    ...
        <application>
        ...
            <activity
                android:name=".FileSelectActivity"
                android:label="@File Selector" >
                <intent-filter>
                    <action
                        android:name="android.intent.action.PICK"/>
                    <category
                        android:name="android.intent.category.DEFAULT"/>
                    <category
                        android:name="android.intent.category.OPENABLE"/>
                    <data android:mimeType="text/plain"/>
                    <data android:mimeType="image/*"/>
                </intent-filter>
            </activity>

コードでファイル選択アクティビティを定義する

次に、利用可能なファイルを表示する Activity サブクラスを定義します。 内部ストレージにアプリの files/images/ ディレクトリに配置し、ユーザーが クリックします。次のスニペットは、この変数を定義する方法を示しています。 Activity を呼び出して、ユーザーの選択に応答します。

Kotlin

class MainActivity : Activity() {

    // The path to the root of this app's internal storage
    private lateinit var privateRootDir: File
    // The path to the "images" subdirectory
    private lateinit var imagesDir: File
    // Array of files in the images subdirectory
    private lateinit var imageFiles: Array<File>
    // Array of filenames corresponding to imageFiles
    private lateinit var imageFilenames: Array<String>

    // Initialize the Activity
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // Set up an Intent to send back to apps that request a file
        resultIntent = Intent("com.example.myapp.ACTION_RETURN_FILE")
        // Get the files/ subdirectory of internal storage
        privateRootDir = filesDir
        // Get the files/images subdirectory;
        imagesDir = File(privateRootDir, "images")
        // Get the files in the images subdirectory
        imageFiles = imagesDir.listFiles()
        // Set the Activity's result to null to begin with
        setResult(Activity.RESULT_CANCELED, null)
        /*
         * Display the file names in the ListView fileListView.
         * Back the ListView with the array imageFilenames, which
         * you can create by iterating through imageFiles and
         * calling File.getAbsolutePath() for each File
         */
        ...
    }
    ...
}

Java

public class MainActivity extends Activity {
    // The path to the root of this app's internal storage
    private File privateRootDir;
    // The path to the "images" subdirectory
    private File imagesDir;
    // Array of files in the images subdirectory
    File[] imageFiles;
    // Array of filenames corresponding to imageFiles
    String[] imageFilenames;
    // Initialize the Activity
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        // Set up an Intent to send back to apps that request a file
        resultIntent =
                new Intent("com.example.myapp.ACTION_RETURN_FILE");
        // Get the files/ subdirectory of internal storage
        privateRootDir = getFilesDir();
        // Get the files/images subdirectory;
        imagesDir = new File(privateRootDir, "images");
        // Get the files in the images subdirectory
        imageFiles = imagesDir.listFiles();
        // Set the Activity's result to null to begin with
        setResult(Activity.RESULT_CANCELED, null);
        /*
         * Display the file names in the ListView fileListView.
         * Back the ListView with the array imageFilenames, which
         * you can create by iterating through imageFiles and
         * calling File.getAbsolutePath() for each File
         */
         ...
    }
    ...
}

ファイル選択に応答する

ユーザーが共有ファイルを選択したら、アプリケーションは選択されたファイルを確認し、 ファイルのコンテンツ URI を生成します。Activity には ListView 内の使用可能なファイルのリスト(ユーザーがファイル名をクリックしたとき) システムはメソッド onItemClick() を呼び出します。これにより、選択されたファイルを取得できます。

インテントを使用して、あるアプリから別のアプリにファイルの URI を送信する場合、 他の Pod が使用している URI を できます。Android 6.0(API レベル 23)以降を搭載しているデバイスの場合 特殊な 特に、Android のそのバージョンでは権限モデルが変更されたため、 READ_EXTERNAL_STORAGEになる 危険な権限(受信側のアプリにはない可能性があります)

こうした考慮事項を念頭に置き、Terraform での使用は Uri.fromFile()、これは これにはいくつかの欠点があります。このメソッドでは次の処理が行われます。

  • プロファイル間でのファイル共有が許可されません。
  • アプリに以下が必要です。 WRITE_EXTERNAL_STORAGE Android 4.4(API レベル 19)以前を搭載しているデバイスでは、この権限が必要になります。
  • 受信側のアプリに READ_EXTERNAL_STORAGE 権限は、 その権限を持たない重要な共有ターゲット(Gmail など)では失敗します。

Uri.fromFile() を使用する代わりに、 URI 権限を使用して、他のアプリ アクセスを制御できます。URI 権限は file:// URI では機能しません。 Uri.fromFile() によって生成された コンテンツ プロバイダに関連付けられた URI に対して機能します。「 FileProvider API は 作成すると便利です。この方法は、標準外のファイルにも適用できます。 外部ストレージに保存されますが、インテントを送信するアプリのローカル ストレージに保存されます。

onItemClick() で、 選択したファイルのファイル名の File オブジェクトを取得し、引数として getUriForFile()、および 権限のある認証情報で FileProvider<provider> 要素。 結果として得られるコンテンツ URI には、認証局、ファイルのパス セグメント、 ディレクトリ(XML メタデータで指定)とそのファイルを含むファイル名( あります。FileProvider がディレクトリをパスにマッピングする方法 XML メタデータに基づくセグメントについては、セクションで 共有可能なディレクトリを指定する

次のスニペットは、選択したファイルを検出して、そのコンテンツ URI を取得する方法を示しています。

Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // Define a listener that responds to clicks on a file in the ListView
        fileListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
            /*
             * Get a File for the selected file name.
             * Assume that the file names are in the
             * imageFilename array.
             */
            val requestFile = File(imageFilenames[position])
            /*
             * Most file-related method calls need to be in
             * try-catch blocks.
             */
            // Use the FileProvider to get a content URI
            val fileUri: Uri? = try {
                FileProvider.getUriForFile(
                        this@MainActivity,
                        "com.example.myapp.fileprovider",
                        requestFile)
            } catch (e: IllegalArgumentException) {
                Log.e("File Selector",
                        "The selected file can't be shared: $requestFile")
                null
            }
            ...
        }
        ...
    }

Java

    protected void onCreate(Bundle savedInstanceState) {
        ...
        // Define a listener that responds to clicks on a file in the ListView
        fileListView.setOnItemClickListener(
                new AdapterView.OnItemClickListener() {
            @Override
            /*
             * When a filename in the ListView is clicked, get its
             * content URI and send it to the requesting app
             */
            public void onItemClick(AdapterView<?> adapterView,
                    View view,
                    int position,
                    long rowId) {
                /*
                 * Get a File for the selected file name.
                 * Assume that the file names are in the
                 * imageFilename array.
                 */
                File requestFile = new File(imageFilename[position]);
                /*
                 * Most file-related method calls need to be in
                 * try-catch blocks.
                 */
                // Use the FileProvider to get a content URI
                try {
                    fileUri = FileProvider.getUriForFile(
                            MainActivity.this,
                            "com.example.myapp.fileprovider",
                            requestFile);
                } catch (IllegalArgumentException e) {
                    Log.e("File Selector",
                          "The selected file can't be shared: " + requestFile.toString());
                }
                ...
            }
        });
        ...
    }

コンテンツ URI は、ディレクトリ内に存在するファイルに対してのみ生成できます。 <paths> 要素を含むメタデータ ファイル内で指定した 共有可能なディレクトリを指定するをご覧ください。電話 getUriForFile(): 指定していないパスで File を指定すると、 IllegalArgumentException

ファイルの権限を付与する

他のアプリと共有するファイルのコンテンツ URI を取得できたら、次は クライアント アプリにファイルへのアクセスを許可します。アクセスを許可するには、次の方法でクライアント アプリに権限を付与します。 コンテンツ URI を Intent に追加し、次に権限フラグを Intent。付与する権限は一時的なものであり、期限切れになる 受信側のアプリのタスクスタックが完了すると自動的にトリガーされます。

次のコード スニペットは、ファイルの読み取り権限を設定する方法を示しています。

Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // Define a listener that responds to clicks on a file in the ListView
        fileListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
            ...
            if (fileUri != null) {
                // Grant temporary read permission to the content URI
                resultIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                ...
            }
            ...
        }
        ...
    }

Java

    protected void onCreate(Bundle savedInstanceState) {
        ...
        // Define a listener that responds to clicks in the ListView
        fileListView.setOnItemClickListener(
                new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView,
                    View view,
                    int position,
                    long rowId) {
                ...
                if (fileUri != null) {
                    // Grant temporary read permission to the content URI
                    resultIntent.addFlags(
                        Intent.FLAG_GRANT_READ_URI_PERMISSION);
                }
                ...
             }
             ...
        });
    ...
    }

注意: setFlags() の呼び出しは、 一時的なアクセス権限を使用してファイルへのアクセス権を安全に付与する、という方法があります。電話をかけない Context.grantUriPermission() メソッド( 付与します。このメソッドでは、権限を取り消すことができるのは、 Context.revokeUriPermission() を呼び出しています。

Uri.fromFile() は使用しないでください。受信するアプリを強制的に READ_EXTERNAL_STORAGE 権限を付与する。 複数のユーザー間や複数のバージョン間で共有しようとすると、 Android 4.4(API レベル 19)よりも前のバージョンの Android WRITE_EXTERNAL_STORAGE が必要です。 また、Gmail アプリなどの非常に重要な共有ターゲットには、 READ_EXTERNAL_STORAGE で発生し、 この呼び出しは失敗します。 代わりに、URI 権限を使用して、他のアプリに特定の URI へのアクセス権を付与できます。 一方、URI 権限は、 Uri.fromFile()、彼らは コンテンツ プロバイダに関連付けられた URI を処理します。そのために独自のルールを実装するのではなく FileProvider を使用できます。また、使用すべきです。 詳しくは、ファイル共有をご覧ください。

リクエスト元のアプリとファイルを共有する

ファイルをリクエストしたアプリとファイルを共有するには、Intent を渡します。 コンテンツ URI と setResult() への権限を含む。先ほど定義した Activity が終了すると、 システムがコンテンツ URI を含む Intent をクライアント アプリに送信する。 次のコード スニペットは、その方法を示しています。

Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // Define a listener that responds to clicks on a file in the ListView
        fileListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
            ...
            if (fileUri != null) {
                ...
                // Put the Uri and MIME type in the result Intent
                resultIntent.setDataAndType(fileUri, contentResolver.getType(fileUri))
                // Set the result
                setResult(Activity.RESULT_OK, resultIntent)
            } else {
                resultIntent.setDataAndType(null, "")
                setResult(RESULT_CANCELED, resultIntent)
            }
        }
    }

Java

    protected void onCreate(Bundle savedInstanceState) {
        ...
        // Define a listener that responds to clicks on a file in the ListView
        fileListView.setOnItemClickListener(
                new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView,
                    View view,
                    int position,
                    long rowId) {
                ...
                if (fileUri != null) {
                    ...
                    // Put the Uri and MIME type in the result Intent
                    resultIntent.setDataAndType(
                            fileUri,
                            getContentResolver().getType(fileUri));
                    // Set the result
                    MainActivity.this.setResult(Activity.RESULT_OK,
                            resultIntent);
                    } else {
                        resultIntent.setDataAndType(null, "");
                        MainActivity.this.setResult(RESULT_CANCELED,
                                resultIntent);
                    }
                }
        });

ユーザーがファイルを選択したら、すぐにクライアント アプリに戻る方法を提供する。 たとえば、チェックマークまたは完了ボタンを提供します。メソッドを ボタンの android:onClick 属性。メソッドで、次を呼び出します。 finish()。次に例を示します。

Kotlin

    fun onDoneClick(v: View) {
        // Associate a method with the Done button
        finish()
    }

Java

    public void onDoneClick(View v) {
        // Associate a method with the Done button
        finish();
    }

その他の関連情報については、以下をご覧ください。