Enviar arquivos para outro dispositivo com NFC

Esta lição mostra como projetar seu app para enviar arquivos grandes para outro dispositivo usando a transferência de arquivos do "Android Beam". Para enviar arquivos, você solicita permissão para usar NFC e armazenamento externo, testa para garantir que seu dispositivo é compatível com NFC e fornece URIs para a transferência de arquivos do "Android Beam".

O recurso de transferência de arquivos do "Android Beam" tem os seguintes requisitos:

  1. A transferência de arquivos grandes do "Android Beam" está disponível apenas no Android 4.1 (API de nível 16) e versões mais recentes.
  2. Os arquivos que você quer transferir precisam residir no armazenamento externo. Para saber mais sobre o uso do armazenamento externo, leia Como usar o armazenamento externo.
  3. Cada arquivo que você quer transferir precisa ser legível para todos. Você pode definir essa permissão chamando o método File.setReadable(true,false).
  4. Você precisa fornecer um URI de arquivo para os arquivos que quer transferir. A transferência de arquivos do "Android Beam" não consegue processar URIs de conteúdo gerados por FileProvider.getUriForFile.

Declarar recursos no manifesto

Primeiro, edite o manifesto do aplicativo para declarar as permissões e os recursos necessários.

Solicitar permissões

Para permitir que seu app use a transferência de arquivos do "Android Beam" para enviar arquivos de armazenamento externo usando NFC, você precisa solicitar as seguintes permissões no manifesto do aplicativo:

NFC
Permite que seu app envie dados por NFC. Para especificar essa permissão, adicione o seguinte elemento como filho do elemento <manifest>:
        <uses-permission android:name="android.permission.NFC" />
    
READ_EXTERNAL_STORAGE
Permite que seu app leia a partir do armazenamento externo. Para especificar essa permissão, adicione o seguinte elemento como um filho do elemento <manifest>:
        <uses-permission
                android:name="android.permission.READ_EXTERNAL_STORAGE" />
    

Observação: a partir do Android 4.2.2 (API de nível 17), essa permissão não é aplicada. Versões futuras da plataforma podem exigi-la para apps que querem ler a partir do armazenamento externo. Para garantir a compatibilidade direta, solicite a permissão agora, antes que ela se torne obrigatória.

Especificar o recurso NFC

Especifique que seu app usa NFC adicionando um elemento <uses-feature> como filho do elemento <manifest>. Defina o atributo android:required como true para indicar que seu app só funcionará se houver NFC presente.

O snippet a seguir mostra como especificar o elemento <uses-feature>:

    <uses-feature
        android:name="android.hardware.nfc"
        android:required="true" />

Observe que se seu app usar NFC apenas como uma opção, mas ainda funcionar se esse recurso não estiver presente, você precisa definir android:required como false e testar a NFC no código.

Especificar a transferência de arquivos do "Android Beam"

Como a transferência de arquivos do "Android Beam" está disponível apenas no Android 4.1 (API de nível 16) e versões mais recentes, se seu app depender da transferência de arquivos do "Android Beam" para uma parte essencial da funcionalidade, você precisará especificar o elemento <uses-sdk> com o atributo android:minSdkVersion="16". Caso contrário, você pode definir android:minSdkVersion como outro valor, conforme necessário, e testar a versão da plataforma no código, conforme descrito na seção a seguir.

Testar suporte de transferência de arquivos do "Android Beam"

Para especificar no manifesto do aplicativo que a NFC é opcional, use o seguinte elemento:

    <uses-feature android:name="android.hardware.nfc" android:required="false" />

Se você definir o atributo android:required="false", precisará testar a compatibilidade com NFC e com a transferência de arquivos do "Android Beam" no código.

Para testar a compatibilidade com a transferência de arquivos do "Android Beam" no código, comece testando se o dispositivo é compatível com NFC chamando PackageManager.hasSystemFeature() com o argumento FEATURE_NFC. Em seguida, verifique se a versão do Android é compatível com a transferência de arquivos do Android Beam testando o valor de SDK_INT. Se a transferência de arquivos do Android Beam for compatível, acesse uma instância do controlador de NFC, que permite a comunicação com o hardware NFC. Exemplo:

Kotlin

    class MainActivity : Activity() {
        ...
        private lateinit var nfcAdapter: NfcAdapter
        // Flag to indicate that Android Beam is available
        private var androidBeamAvailable = false
        ...
        override fun onCreate(savedInstanceState: Bundle?) {
            ...
            androidBeamAvailable = if (!packageManager.hasSystemFeature(PackageManager.FEATURE_NFC)) {
                // NFC isn't available on the device
                /*
                  * Disable NFC features here.
                  * For example, disable menu items or buttons that activate
                  * NFC-related features
                  */
                false
            // Android Beam file transfer isn't supported
            } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
                // If Android Beam isn't available, don't continue.
                androidBeamAvailable = false
                /*
                 * Disable Android Beam file transfer features here.
                 */
                ...
                false
            } else {
                // Android Beam file transfer is available, continue
                nfcAdapter = NfcAdapter.getDefaultAdapter(this)
                ...
                true
            }
        }
        ...
    }
    

Java

    public class MainActivity extends Activity {
        ...
        NfcAdapter nfcAdapter;
        // Flag to indicate that Android Beam is available
        boolean androidBeamAvailable  = false;
        ...
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            ...
            // NFC isn't available on the device
            if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
                /*
                 * Disable NFC features here.
                 * For example, disable menu items or buttons that activate
                 * NFC-related features
                 */
                ...
            // Android Beam file transfer isn't supported
            } else if (Build.VERSION.SDK_INT <
                    Build.VERSION_CODES.JELLY_BEAN_MR1) {
                // If Android Beam isn't available, don't continue.
                androidBeamAvailable = false;
                /*
                 * Disable Android Beam file transfer features here.
                 */
                ...
            // Android Beam file transfer is available, continue
            } else {
                androidBeamAvailable = true;
                nfcAdapter = NfcAdapter.getDefaultAdapter(this);
            ...
            }
        }
        ...
    }
    

Criar um método de callback que fornece arquivos

Depois de verificar se o dispositivo é compatível com a transferência de arquivos do "Android Beam", adicione um método de callback que o sistema invoque quando a transferência de arquivos do "Android Beam" detectar que o usuário quer enviar arquivos para outro dispositivo compatível com NFC. Nesse método de callback, retorne uma matriz de objetos Uri. A transferência de arquivos do "Android Beam" copia os arquivos representados por esses URIs para o dispositivo receptor.

Para adicionar o método de callback, implemente a interface NfcAdapter.CreateBeamUrisCallback e o método de callback createBeamUris(). O snippet a seguir mostra como fazer isso:

Kotlin

    public class MainActivity : Activity() {
        ...
        // List of URIs to provide to Android Beam
        private val fileUris = mutableListOf<Uri>()
        ...
        /**
         * Callback that Android Beam file transfer calls to get
         * files to share
         */
        private inner class FileUriCallback : NfcAdapter.CreateBeamUrisCallback {
            /**
             * Create content URIs as needed to share with another device
             */
            override fun createBeamUris(event: NfcEvent): Array<Uri> {
                return fileUris.toTypedArray()
            }
        }
        ...
    }
    

Java

    public class MainActivity extends Activity {
        ...
        // List of URIs to provide to Android Beam
        private Uri[] fileUris = new Uri[10];
        ...
        /**
         * Callback that Android Beam file transfer calls to get
         * files to share
         */
        private class FileUriCallback implements
                NfcAdapter.CreateBeamUrisCallback {
            public FileUriCallback() {
            }
            /**
             * Create content URIs as needed to share with another device
             */
            @Override
            public Uri[] createBeamUris(NfcEvent event) {
                return fileUris;
            }
        }
        ...
    }
    

Depois de implementar a interface, forneça o callback para a transferência de arquivos do "Android Beam" chamando setBeamPushUrisCallback(). O snippet a seguir mostra como fazer isso:

Kotlin

    class MainActivity : Activity() {
        ...
        private lateinit var nfcAdapter: NfcAdapter
        // Flag to indicate that Android Beam is available
        private var androidBeamAvailable = false
        ...
        override fun onCreate(savedInstanceState: Bundle?) {
            ...
            // Android Beam file transfer is available, continue
            nfcAdapter = NfcAdapter.getDefaultAdapter(this).apply {

                /*
                 * Instantiate a new FileUriCallback to handle requests for
                 * URIs
                 */
                fileUriCallback = FileUriCallback()
                // Set the dynamic callback for URI requests.
                nfcAdapter.setBeamPushUrisCallback(fileUriCallback, this@MainActivity)
            }
            ...
        }
        ...
    }
    

Java

    public class MainActivity extends Activity {
        ...
        // Instance that returns available files from this app
        private FileUriCallback fileUriCallback;
        ...
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            ...
            // Android Beam file transfer is available, continue
            ...
            nfcAdapter = NfcAdapter.getDefaultAdapter(this);
            /*
             * Instantiate a new FileUriCallback to handle requests for
             * URIs
             */
            fileUriCallback = new FileUriCallback();
            // Set the dynamic callback for URI requests.
            nfcAdapter.setBeamPushUrisCallback(fileUriCallback,this);
            ...
        }
        ...
    }
    

Observação: você também pode fornecer a matriz de objetos Uri diretamente para o framework de NFC por meio da instância NfcAdapter do seu app. Escolha essa abordagem se você puder definir os URIs a serem transferidos antes que o evento de toque de NFC ocorra. Para saber mais sobre essa abordagem, consulte NfcAdapter.setBeamPushUris().

Especificar os arquivos a serem enviados

Para transferir um ou mais arquivos para outro dispositivo compatível com NFC, consiga um URI de arquivo (um URI com um esquema file) para cada arquivo e adicione o URI a uma matriz de objetos Uri. Para transferir um arquivo, você também precisa ter acesso de leitura permanente ao arquivo. Por exemplo, o snippet a seguir mostra como conseguir um URI de arquivo a partir de um nome de arquivo e, em seguida, adicionar o URI à matriz:

Kotlin

            /*
             * Create a list of URIs, get a File,
             * and set its permissions
             */
            val fileUris = mutableListOf<Uri>()
            val transferFile = "transferimage.jpg"
            val extDir = getExternalFilesDir(null)
            val requestFile = File(extDir, transferFile).apply {
                setReadable(true, false)
            }
            // Get a URI for the File and add it to the list of URIs
            Uri.fromFile(requestFile)?.also { fileUri ->
                fileUris += fileUri
            } ?: Log.e("My Activity", "No File URI available for file.")
    

Java

            /*
             * Create a list of URIs, get a File,
             * and set its permissions
             */
            private Uri[] fileUris = new Uri[10];
            String transferFile = "transferimage.jpg";
            File extDir = getExternalFilesDir(null);
            File requestFile = new File(extDir, transferFile);
            requestFile.setReadable(true, false);
            // Get a URI for the File and add it to the list of URIs
            fileUri = Uri.fromFile(requestFile);
            if (fileUri != null) {
                fileUris[0] = fileUri;
            } else {
                Log.e("My Activity", "No File URI available for file.");
            }
    

Para mais informações relacionadas, consulte Opções de armazenamento

Para ver um código de amostra relacionado a esta página, consulte a Amostra Android BeamLargeFiles (link em inglês)