La transferencia de archivos de Android Beam copia los archivos a un directorio especial en el dispositivo receptor. También escanea los archivos copiados con el escáner multimedia de Android y agrega entradas para los archivos multimedia al proveedor de MediaStore
. En esta lección, se muestra cómo responder cuando se completa la copia del archivo y cómo ubicar los archivos copiados en el dispositivo receptor.
Cómo responder a una solicitud para mostrar datos
Cuando la transferencia de archivos de Android Beam termina de copiar archivos en el dispositivo receptor, publica una notificación que contiene un Intent
con la acción ACTION_VIEW
, el tipo de MIME del primer archivo que se transfirió y un URI que apunta al primer archivo. Cuando el usuario hace clic en la notificación, este Intent se envía al sistema. Para que la app responda a este intent, agrega un elemento <intent-filter>
para el elemento <activity>
de la Activity
que debería responder.
En el elemento <intent-filter>
, agrega los siguientes elementos secundarios:
-
<action android:name="android.intent.action.VIEW" />
-
Coincide con el intent
ACTION_VIEW
enviado desde la notificación. -
<category android:name="android.intent.category.CATEGORY_DEFAULT" />
-
Coincide con un
Intent
que no tiene una categoría explícita. -
<data android:mimeType="mime-type" />
- Coincide con un tipo de MIME. Especifica solo los tipos de MIME que la app puede controlar.
Por ejemplo, en el siguiente fragmento, se muestra cómo agregar un filtro de intents que active la actividad 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>
Nota: La transferencia de archivos de Android Beam no es la única fuente de un intent ACTION_VIEW
. Otras apps en el dispositivo receptor también pueden enviar un Intent
con esta acción.
En la sección Cómo obtener el directorio de una URI de contenido, se explica cómo controlar esta situación.
Cómo solicitar permisos de archivo
Para leer los archivos que la transferencia de archivos de Android Beam copia en el dispositivo, solicita el permiso READ_EXTERNAL_STORAGE
. Por ejemplo:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Si quieres copiar los archivos transferidos al área de almacenamiento de tu propia app, en su lugar, solicita el permiso WRITE_EXTERNAL_STORAGE
.
WRITE_EXTERNAL_STORAGE
incluye READ_EXTERNAL_STORAGE
.
Nota: A partir de Android 4.2.2 (API nivel 17), el permiso READ_EXTERNAL_STORAGE
solo se aplica si el usuario elige hacerlo. Es posible que en versiones futuras de la plataforma se requiera este permiso en todos los casos. Para garantizar la compatibilidad con las versiones futuras, solicita el permiso ahora, antes de que sea obligatorio.
Dado que tu app tiene control sobre su área de almacenamiento interno, no necesitas solicitar permiso de escritura para copiar un archivo transferido al área de almacenamiento interno.
Cómo obtener el directorio para los archivos copiados
La transferencia de archivos de Android Beam copia todos los archivos de una sola transferencia a un directorio en el dispositivo receptor. El URI del Intent
de contenido enviado por la notificación de transferencia de archivos de Android Beam apunta al primer archivo transferido. Sin embargo, es posible que tu app también reciba un intent ACTION_VIEW
de una fuente que no sea la transferencia de archivos de Android Beam. Para determinar cómo debes controlar el Intent
entrante, debes examinar su esquema y su autoridad.
A fin de obtener el esquema del URI, llama a Uri.getScheme()
. En el siguiente fragmento de código, se muestra cómo determinar el esquema y controlar correctamente el 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); } } ... } ... }
Cómo obtener el directorio de un URI de archivo
Si el Intent
entrante tiene un URI de archivo, ese URI contendrá el nombre absoluto del archivo, incluida la ruta de acceso completa al directorio y el nombre del archivo. Para la transferencia de archivos de Android Beam, la ruta de acceso del directorio apunta a la ubicación del resto de los archivos transferidos, si corresponde. Para obtener la ruta de acceso del directorio, obtén la parte de la ruta del URI, que contiene todo el URI, excepto el prefijo file:
. Crea un File
desde la parte de la ruta de acceso y luego obtén la ruta principal del 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(); } ...
Cómo obtener el directorio de un URI de contenido
Si el Intent
entrante contiene un URI de contenido, ese URI puede apuntar a un directorio y un nombre de archivo almacenados en el proveedor de contenido de MediaStore
. Puedes detectar un URI de contenido para MediaStore
probando el valor de autoridad del URI. Un URI de contenido de MediaStore
puede provenir de la transferencia de archivos de Android Beam o de otra app, pero en ambos casos puedes recuperar un directorio y un nombre de archivo para el URI de contenido.
También puedes recibir un intent ACTION_VIEW
entrante que contenga un URI de contenido para un proveedor de contenido que no sea MediaStore
. En ese caso, el URI de contenido no incluirá el valor de autoridad de MediaStore
y, por lo general, no apuntará a un directorio.
Nota: Para la transferencia de archivos de Android Beam, recibes un URI de contenido en el intent ACTION_VIEW
si el primer archivo entrante tiene un tipo de MIME "audio/*", "image/*" o "video/*", lo que indica que el archivo es un archivo multimedia. La transferencia de archivos de Android Beam indexa los archivos multimedia que transfiere ejecutando el escáner multimedia en el directorio donde almacena los archivos transferidos. El escáner multimedia escribe los resultados en el proveedor de contenido MediaStore
y luego pasa un URI de contenido para el primer archivo a la transferencia de archivos de Android Beam. Este URI de contenido es el que recibes en el Intent
de notificación. Para obtener el directorio del primer archivo, lo recuperas de MediaStore
utilizando el URI de contenido.
Cómo determinar el proveedor de contenido
Para determinar si puedes recuperar un directorio de archivos desde el URI de contenido, determina el proveedor de contenido asociado con el URI llamando a Uri.getAuthority()
para obtener la autoridad correspondiente. El resultado tiene dos valores posibles:
-
MediaStore.AUTHORITY
-
El URI es para uno o varios archivos con seguimiento de
MediaStore
. Recupera el nombre completo del archivo deMediaStore
y obtiene el directorio del nombre del archivo. - Cualquier otro valor de autoridad
- Un URI de contenido de otro proveedor de contenido. Muestra los datos asociados con el URI de contenido, pero no obtiene el directorio de archivos.
A fin de obtener el directorio de un URI de contenido de MediaStore
, ejecuta una consulta que especifique el URI de contenido entrante correspondiente al argumento Uri
y la columna MediaColumns.DATA
para la proyección. El Cursor
que se muestra contiene la ruta de acceso completa y el nombre del archivo representados por el URI. Esta ruta de acceso también contiene todos los demás archivos que la transferencia de archivos de Android Beam copió en el dispositivo.
En el siguiente fragmento, se muestra cómo probar la autoridad de la URI de contenido y recuperar la ruta y el nombre del archivo transferido:
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; } } } ...
Para obtener más información sobre cómo recuperar datos de un proveedor de contenido, consulta la sección Cómo recuperar los datos del proveedor.
Para obtener información adicional relacionada, consulta: