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; } } } ...
コンテンツ プロバイダからデータを取得する方法については、プロバイダからデータを取得するをご覧ください。
その他の関連情報については、以下をご覧ください。