Receber arquivos de outro dispositivo com NFC

A transferência de arquivos do "Android Beam" copia arquivos para um diretório especial no dispositivo receptor. Ela também verifica os arquivos copiados usando o Android Media Scanner e adiciona entradas para arquivos de mídia ao provedor MediaStore. Esta lição mostra como responder quando a cópia do arquivo estiver concluída e como localizar os arquivos copiados no dispositivo receptor.

Responder a uma solicitação para exibir dados

Quando a transferência de arquivos do "Android Beam" termina de copiar arquivos para o dispositivo receptor, ela posta uma notificação contendo uma Intent com a ação ACTION_VIEW, o tipo MIME do primeiro arquivo transferido e um URI que aponta para o primeiro arquivo. Quando o usuário clica na notificação, essa intent é enviada ao sistema. Para que seu app responda a essa intent, adicione um elemento <intent-filter> para o elemento <activity> do Activity que deve responder. No elemento <intent-filter>, adicione os seguintes elementos filhos:

<action android:name="android.intent.action.VIEW" />
Corresponde à intent ACTION_VIEW enviada a partir da notificação.
<category android:name="android.intent.category.CATEGORY_DEFAULT" />
Corresponde a uma Intent que não possui uma categoria explícita.
<data android:mimeType="mime-type" />
Corresponde a um tipo MIME. Especifique apenas os tipos MIME que o app pode processar.

Por exemplo, o snippet a seguir mostra como adicionar um filtro de intent que aciona a atividade 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>
    

Observação: a transferência de arquivos do "Android Beam" não é a única fonte de uma intent ACTION_VIEW. Outros apps no dispositivo receptor também podem enviar uma Intent com essa ação. Esse processamento é discutido na seção Ver o diretório de um URI de conteúdo.

Solicitar permissões de arquivo

Para ler os arquivos dos quais o "Android Beam" transfere cópias para o dispositivo, solicite a permissão READ_EXTERNAL_STORAGE. Por exemplo:

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

Se você quiser copiar os arquivos transferidos para a área de armazenamento do seu app, solicite a permissão WRITE_EXTERNAL_STORAGE. WRITE_EXTERNAL_STORAGE inclui READ_EXTERNAL_STORAGE.

Observação: a partir do Android 4.2.2 (API de nível 17), a permissão READ_EXTERNAL_STORAGE só será aplicada se o usuário optar por fazer isso. Futuras versões da plataforma podem exigir essa permissão em todos os casos. Para garantir a compatibilidade direta, solicite a permissão agora, antes que ela seja obrigatória.

Como o app tem controle sobre a própria área de armazenamento interno, você não precisa solicitar permissão de gravação para copiar um arquivo transferido para sua área de armazenamento interno.

Ver o diretório de arquivos copiados

A transferência de arquivos do "Android Beam" copia todos os arquivos em uma única transferência para um diretório no dispositivo receptor. O URI na Intent de conteúdo enviado pela notificação de transferência de arquivos do "Android Beam" aponta para o primeiro arquivo transferido. No entanto, seu app também pode receber uma intent ACTION_VIEW de uma origem que não seja a transferência de arquivos do "Android Beam". Para determinar como processar a Intent recebida, você precisa examinar o esquema e a autoridade dela.

Para receber o esquema do URI, chame Uri.getScheme(). O snippet de código a seguir mostra como determinar o esquema e processar o URI adequadamente:

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);
                }
            }
            ...
        }
        ...
    }
    

Ver o diretório de um URI de arquivo

Se a Intent recebida contiver um URI de arquivo, o URI conterá o nome absoluto de um arquivo, incluindo o caminho completo do diretório e o nome do arquivo. Na transferência de arquivos do "Android Beam", o caminho do diretório apontará para o local dos outros arquivos transferidos, se houver. Para ver o caminho do diretório, observe a parte do caminho do URI, que contém todo o URI, exceto o prefixo do file:. Crie um File a partir da parte do caminho e receba o caminho pai do 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();
        }
        ...
    

Ver o diretório de um URI de conteúdo

Se a Intent recebida contiver um URI de conteúdo, o URI poderá apontar para um diretório e nome de arquivo armazenados no provedor de conteúdo da MediaStore. Você pode detectar um URI de conteúdo para MediaStore testando o valor de autoridade do URI. Um URI de conteúdo do MediaStore pode vir da transferência de arquivos do "Android Beam" ou de outro app, mas, nos dois casos, é possível recuperar um diretório e nome de arquivo para o URI de conteúdo.

Você também pode receber um intent ACTION_VIEW de entrada que contém um URI de conteúdo para um provedor que não seja o MediaStore. Nesse caso, o URI de conteúdo não contém o valor de autoridade MediaStore, e o URI de conteúdo geralmente não aponta para um diretório.

Observação: para transferência de arquivos do "Android Beam", você receberá um URI de conteúdo na intent ACTION_VIEW se o primeiro arquivo recebido tiver um tipo MIME de "audio/*", "image/*" ou "video/*", indicando que o arquivo é relacionado a mídia. A transferência de arquivos do "Android Beam" indexa os arquivos de mídia transferidos executando o Media Scanner no diretório em que armazena os arquivos transferidos. O Media Scanner grava os resultados no provedor de conteúdo MediaStore e passa um URI de conteúdo para o primeiro arquivo de volta à transferência de arquivos do "Android Beam". Esse URI de conteúdo é aquele que você recebe na Intent de notificação. Para ver o diretório do primeiro arquivo, recupere-o de MediaStore usando o URI de conteúdo.

Determinar o provedor de conteúdo

Para determinar se você pode recuperar um diretório de arquivos do URI de conteúdo, determine o provedor de conteúdo associado ao URI chamando Uri.getAuthority() para ver a autoridade do URI. O resultado tem dois valores possíveis:

MediaStore.AUTHORITY
O URI é para um arquivo ou arquivos rastreados por MediaStore. Recupere o nome completo do arquivo de MediaStore e veja o diretório no nome do arquivo.
Qualquer outro valor de autoridade
Um URI de conteúdo de outro provedor de conteúdo. Exiba os dados associados ao URI do conteúdo, mas não veja o diretório do arquivo.

Para ver o diretório de um URI de conteúdo do MediaStore, execute uma consulta que especifique o URI de conteúdo recebido para o argumento Uri e a coluna MediaColumns.DATA para a projeção. O Cursor retornado contém o caminho completo e o nome do arquivo representado pelo URI. Esse caminho também contém todos os outros arquivos que a transferência de arquivos do Android Beam acabou de copiar para o dispositivo.

O snippet a seguir mostra como testar a autoridade do URI de conteúdo e recuperar o caminho e o nome do arquivo 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 saber mais sobre como recuperar dados de um provedor de conteúdo, consulte a seção Recuperação de dados do provedor.

Para ver mais informações relacionadas, consulte: