Google は、黒人コミュニティに対する人種平等の促進に取り組んでいます。取り組みを見る

NFC を使用して別のデバイスからファイルを受信する

Android ビームファイル転送機能は、受信側デバイスの特別なディレクトリにファイルをコピーします。また、Android Media Scanner を使用して、コピーされたファイルをスキャンし、メディア ファイルのエントリを MediaStore プロバイダに追加します。このレッスンでは、ファイルのコピーが完了したときに応答する方法と、受信側デバイス上でコピーファイルを見つける方法について説明します。

データを表示するリクエストに応答する

Android ビームファイル転送機能は、受信側デバイスへのファイルのコピーを完了すると、ACTION_VIEW アクションを指定した Intent、転送した最初のファイルの MIME タイプ、最初のファイルをポイントする URI を格納した通知を送信します。ユーザーが通知をタップすると、このインテントがシステムに送信されます。アプリがこのインテントに応答するようにするには、応答する必要がある Activity<activity> 要素に <intent-filter> 要素を追加します。<intent-filter> 要素内に、次の子要素を追加します。

<action android:name="android.intent.action.VIEW" />
通知から送信された ACTION_VIEW インテントと一致します。
<category android:name="android.intent.category.CATEGORY_DEFAULT" />
明示的なカテゴリのない Intent と一致します。
<data android:mimeType="mime-type" />
MIME タイプと一致します。アプリが処理できる MIME タイプだけを指定してください。

たとえば、com.example.android.nfctransfer.ViewActivity アクティビティをトリガーするインテント フィルタを追加する方法を次のスニペットに示します。

        <activity
            android:name="com.example.android.nfctransfer.ViewActivity"
            android:label="Android Beam Viewer" >
            ...
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                ...
            </intent-filter>
        </activity>
    

注: Android ビームファイル転送は、ACTION_VIEW インテントの唯一のソースではありません。受信側デバイス上の別のアプリも、このアクションを使用して Intent を送信できます。この状況を処理する方法については、コンテンツ URI からディレクトリを取得するをご覧ください。

ファイル パーミッションをリクエストする

Android ビームファイル転送によってデバイスにコピーされたファイルを読み取るには、READ_EXTERNAL_STORAGE パーミッションをリクエストします。たとえば、次のようになります。

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

転送されたファイルをアプリ独自のストレージ領域にコピーする場合は、代わりに WRITE_EXTERNAL_STORAGE パーミッションをリクエストします。WRITE_EXTERNAL_STORAGE には READ_EXTERNAL_STORAGE が含まれます。

注: Android 4.2.2(API レベル 17)以降、READ_EXTERNAL_STORAGE パーミッションは、ユーザーが選択した場合に限り適用されます。将来のプラットフォーム バージョンでは、あらゆる場合にこのパーミッションが必要になる可能性があります。前方互換性を確保するため、必要になる前に、現時点でパーミッションをリクエストするようにしてください。

アプリは自身の内部ストレージ領域を制御する権限を持っているため、転送されたファイルを内部ストレージ領域にコピーする際に書き込みパーミッションをリクエストする必要はありません。

コピーファイル用のディレクトリを取得する

Android ビームファイル転送は、1 回の転送ですべてのファイルを受信側デバイスの 1 つのディレクトリにコピーします。Android ビームファイル転送の通知によって送信されるコンテンツ Intent 内の URI は、最初に転送されたファイルをポイントします。ただし、アプリは、Android ビームファイル転送以外のソースから ACTION_VIEW インテントを受け取ることもあります。受信した Intent の処理方法を決定するには、そのスキームとオーソリティを調べる必要があります。

URI のスキームを取得するには、Uri.getScheme() を呼び出します。スキームを判断し、それに応じて URI を処理する方法を次のコード スニペットに示します。

Kotlin

    class MainActivity : Activity() {
        ...
        // A File object containing the path to the transferred files
        private var parentPath: File? = null
        ...
        /*
         * Called from onNewIntent() for a SINGLE_TOP Activity
         * or onCreate() for a new Activity. For onNewIntent(),
         * remember to call setIntent() to store the most
         * current Intent
         *
         */
        private fun handleViewIntent() {
            ...
            /*
             * For ACTION_VIEW, the Activity is being asked to display data.
             * Get the URI.
             */
            if (TextUtils.equals(intent.action, Intent.ACTION_VIEW)) {
                // Get the URI from the Intent
                intent.data?.also { beamUri ->
                    /*
                     * Test for the type of URI, by getting its scheme value
                     */
                    parentPath = when (beamUri.scheme) {
                        "file" -> handleFileUri(beamUri)
                        "content" -> handleContentUri(beamUri)
                        else -> null
                    }
                }
            }
            ...
        }
        ...
    }
    

Java

    public class MainActivity extends Activity {
        ...
        // A File object containing the path to the transferred files
        private File parentPath;
        // Incoming Intent
        private Intent intent;
        ...
        /*
         * Called from onNewIntent() for a SINGLE_TOP Activity
         * or onCreate() for a new Activity. For onNewIntent(),
         * remember to call setIntent() to store the most
         * current Intent
         *
         */
        private void handleViewIntent() {
            ...
            // Get the Intent action
            intent = getIntent();
            String action = intent.getAction();
            /*
             * For ACTION_VIEW, the Activity is being asked to display data.
             * Get the URI.
             */
            if (TextUtils.equals(action, Intent.ACTION_VIEW)) {
                // Get the URI from the Intent
                Uri beamUri = intent.getData();
                /*
                 * Test for the type of URI, by getting its scheme value
                 */
                if (TextUtils.equals(beamUri.getScheme(), "file")) {
                    parentPath = handleFileUri(beamUri);
                } else if (TextUtils.equals(
                        beamUri.getScheme(), "content")) {
                    parentPath = handleContentUri(beamUri);
                }
            }
            ...
        }
        ...
    }
    

ファイル URI からディレクトリを取得する

受信した Intent にファイル URI が含まれる場合、この URI には、ファイルの完全なファイル名(ディレクトリのフルパスとファイル名)が含まれています。Android ビームファイル転送の場合、このディレクトリ パスは、転送された他のファイルがある場合は、その場所をポイントします。ディレクトリ パスを取得するには、URI のパス部分を取得します。この部分には、file: プレフィックスを除くすべての URI が含まれます。パス部分から File を作成して、File の親パスを取得します。

Kotlin

        ...
        fun handleFileUri(beamUri: Uri): File? =
                // Get the path part of the URI
                beamUri.path.let { fileName ->
                    // Create a File object for this filename
                    File(fileName)
                            // Get the file's parent directory
                            .parentFile
                }
        ...
    

Java

        ...
        public File handleFileUri(Uri beamUri) {
            // Get the path part of the URI
            String fileName = beamUri.getPath();
            // Create a File object for this filename
            File copiedFile = new File(fileName);
            // Get the file's parent directory
            return copiedFile.getParentFile();
        }
        ...
    

コンテンツ URI からディレクトリを取得する

受信した Intent にコンテンツ URI が含まれる場合、この URI は、MediaStore コンテンツ プロバイダ内に保存されているディレクトリとファイル名をポイントしていることがあります。MediaStore 用のコンテンツ URI を検出するには、URI のオーソリティ値をテストします。MediaStore 用のコンテンツ URI の送信元は、Android ビームファイル転送の可能性も、別のアプリの可能性もありますが、どちらの場合でも、コンテンツ URI のディレクトリとファイル名を取得できます。

また、MediaStore 以外のコンテンツ プロバイダ用のコンテンツ URI を含む受信 ACTION_VIEW インテントを受信することもあります。この場合、コンテンツ URI には MediaStore オーソリティ値が含まれておらず、通常はディレクトリをポイントしていません。

注: Android ビームファイル転送の場合、最初の受信ファイルの MIME タイプが「audio/*」や、「image/*」、「video/*」であれば、ファイルがメディア関連であることを示しており、ACTION_VIEW インテント内でコンテンツ URI を受け取ります。Android ビームファイル転送は、転送されたファイルを保存するディレクトリ上で Media Scanner を実行することにより、転送するメディア ファイルをインデックスに登録します。Media Scanner は、スキャン結果を MediaStore コンテンツ プロバイダに書き込み、最初のファイルのコンテンツ URI を Android ビームファイル転送に渡します。このコンテンツ URI は、通知 Intent で受け取る URI です。最初のファイルのディレクトリを取得するには、コンテンツ URI を使用して MediaStore から取得します。

コンテンツ プロバイダを判別する

コンテンツ URI からファイル ディレクトリを取得できるかどうかを判断するには、Uri.getAuthority() を呼び出して URI のオーソリティを取得することによって、URI に関連付けられているコンテンツ プロバイダを判別します。取得結果は、次の 2 つの値のいずれかになります。

MediaStore.AUTHORITY
MediaStore によってトラッキングされるファイルの URI です。MediaStore から完全なファイル名を取得し、ファイル名からディレクトリを取得します。
他のオーソリティ値
別のコンテンツ プロバイダに由来するコンテンツ URI です。コンテンツ URI に関連付けられたデータを表示しますが、ファイル ディレクトリは取得しません。

MediaStore コンテンツ URI のディレクトリを取得するには、Uri 引数用の受信コンテンツ URI と、射影用の MediaColumns.DATA 列を指定するクエリを実行します。返される Cursor には、URI で示されるファイルのフルパスと名前が格納されます。このパスには、Android ビームファイル転送がデバイスにコピーした他のすべてのファイルも含まれます。

コンテンツ URI のオーソリティをテストし、転送されるファイルのパスとファイル名を取得する方法を次のスニペットに示します。

Kotlin

        ...
        private fun handleContentUri(beamUri: Uri): File? =
                // Test the authority of the URI
                if (beamUri.authority == MediaStore.AUTHORITY) {
                    /*
                     * Handle content URIs for other content providers
                     */
                    ...
                // For a MediaStore content URI
                } else {
                    // Get the column that contains the file name
                    val projection = arrayOf(MediaStore.MediaColumns.DATA)
                    val pathCursor = contentResolver.query(beamUri, projection, null, null, null)
                    // Check for a valid cursor
                    if (pathCursor?.moveToFirst() == true) {
                        // Get the column index in the Cursor
                        pathCursor.getColumnIndex(MediaStore.MediaColumns.DATA).let { filenameIndex ->
                            // Get the full file name including path
                            pathCursor.getString(filenameIndex).let { fileName ->
                                // Create a File object for the filename
                                File(fileName)
                            }.parentFile // Return the parent directory of the file
                        }
                    } else {
                        // The query didn't work; return null
                        null
                    }
                }
        ...
    

Java

        ...
        public String handleContentUri(Uri beamUri) {
            // Position of the filename in the query Cursor
            int filenameIndex;
            // File object for the filename
            File copiedFile;
            // The filename stored in MediaStore
            String fileName;
            // Test the authority of the URI
            if (!TextUtils.equals(beamUri.getAuthority(), MediaStore.AUTHORITY)) {
                /*
                 * Handle content URIs for other content providers
                 */
            // For a MediaStore content URI
            } else {
                // Get the column that contains the file name
                String[] projection = { MediaStore.MediaColumns.DATA };
                Cursor pathCursor =
                        getContentResolver().query(beamUri, projection,
                        null, null, null);
                // Check for a valid cursor
                if (pathCursor != null &&
                        pathCursor.moveToFirst()) {
                    // Get the column index in the Cursor
                    filenameIndex = pathCursor.getColumnIndex(
                            MediaStore.MediaColumns.DATA);
                    // Get the full file name including path
                    fileName = pathCursor.getString(filenameIndex);
                    // Create a File object for the filename
                    copiedFile = new File(fileName);
                    // Return the parent directory of the file
                    return copiedFile.getParentFile();
                 } else {
                    // The query didn't work; return null
                    return null;
                 }
            }
        }
        ...
    

コンテンツ プロバイダからデータを取得する方法については、プロバイダからデータを取得するをご覧ください。

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