NFC を使用して別のデバイスにファイルを送信する

このレッスンでは、Android Beam ファイル転送を使用して、大きなファイルを別のデバイスに送信するようにアプリを設計する方法を示します。ファイルを送信するには、NFC と外部ストレージを使用する権限をリクエストし、デバイスが NFC をサポートしていることをテストして確認し、Android Beam ファイル転送に URI を提供します。

Android Beam ファイル転送機能には次の要件があります。

  1. 大きなファイルの Android Beam ファイル転送は、Android 4.1(API レベル 16)以降でのみ使用できます。
  2. 転送するファイルは外部ストレージに保存する必要があります。外部ストレージの使用について詳しくは、外部ストレージの使用をご覧ください。
  3. 転送する各ファイルは、誰でも読み取り可能でなければなりません。この権限を設定するには、メソッド File.setReadable(true,false) 呼び出します。
  4. 転送するファイルのファイル URI を指定する必要があります。Android Beam ファイル転送では、FileProvider.getUriForFile によって生成されたコンテンツ URI を処理することはできません。

マニフェストで機能を宣言する

まず、アプリ マニフェストを編集して、アプリに必要な権限と機能を宣言します。

権限をリクエストする

NFC を使用して外部ストレージからファイルを送信するために、アプリで Android Beam ファイル転送を使用できるようにするには、アプリ マニフェストで次の権限をリクエストする必要があります。

NFC
アプリに NFC 経由でのデータ送信を許可します。この権限を指定するには、次の要素を <manifest> 要素の子として追加します。
        <uses-permission android:name="android.permission.NFC" />
    
READ_EXTERNAL_STORAGE
アプリに外部ストレージからの読み取りを許可します。この権限を指定するには、次の要素を <manifest> 要素の子として追加します。
        <uses-permission
                android:name="android.permission.READ_EXTERNAL_STORAGE" />
    

注: Android 4.2.2(API レベル 17)以降では、この権限は適用されません。プラットフォームの将来のバージョンでは、外部ストレージからの読み取りを希望するアプリでこの権限が必要になる可能性があります。上位互換性を確保するため、必要になる前に、現時点で権限をリクエストしてください。

NFC 機能を指定する

アプリで NFC を使用することを指定するには、<uses-feature> 要素を <manifest> 要素の子として追加します。android:required 属性を true に設定して、NFC が存在しないとアプリが機能しないことを示します。

次のスニペットは、<uses-feature> 要素の指定方法を示しています。

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

アプリがオプションとして NFC のみを使用しているにもかかわらず、NFC が存在しない場合でも機能する場合は、android:requiredfalse に設定して、コード内で NFC をテストする必要があります。

Android Beam ファイル転送を指定する

Android Beam ファイル転送は Android 4.1(API レベル 16)以降でのみ使用できるため、アプリの機能の主要部分が Android Beam ファイル転送に依存している場合、<uses-sdk> 要素と android:minSdkVersion="16" 属性を指定する必要があります。または、必要に応じて android:minSdkVersion を別の値に設定し、後述のように、コード内でプラットフォーム バージョンをテストすることもできます。

Android Beam ファイル転送のサポートをテストする

アプリ マニフェストで NFC がオプションであることを指定するには、次の要素を使用します。

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

属性 android:required="false" を設定した場合、コード内で NFC のサポートと Android Beam ファイル転送のサポートをテストする必要があります。

コード内で Android Beam ファイル転送のサポートをテストするには、まず、引数 FEATURE_NFC を指定して PackageManager.hasSystemFeature() を呼び出すことにより、デバイスが NFC をサポートしていることをテストします。次に、SDK_INT の値をテストして、Android バージョンが Android Beam ファイル転送をサポートしていることを確認します。Android Beam ファイル転送がサポートされている場合、NFC コントローラのインスタンスを取得します。これにより、NFC ハードウェアと通信できるようになります。次に例を示します。

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

ファイルを提供するコールバック メソッドを作成する

デバイスが Android Beam ファイル転送をサポートしていることを確認したら、ユーザーが別の NFC 対応デバイスにファイルを送信しようとしていることを Android Beam ファイル転送が検出したときにシステムが呼び出すコールバック メソッドを追加します。このコールバック メソッドでは、Uri オブジェクトの配列を返します。Android Beam ファイル転送は、これらの URI で表されるファイルを受信デバイスにコピーします。

コールバック メソッドを追加するには、NfcAdapter.CreateBeamUrisCallback インターフェースとそのメソッド createBeamUris() を実装します。次のスニペットは、この実装方法を示しています。

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

インターフェースを実装したら、setBeamPushUrisCallback() を呼び出して、Android Beam ファイル転送へのコールバックを提供します。次のスニペットは、この実装方法を示しています。

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

注: アプリの NfcAdapter インスタンスを介して NFC フレームワークに Uri オブジェクトの配列を直接提供することもできます。NFC タッチイベントが発生する前に転送する URI を定義できる場合は、この方法を選択してください。 この方法について詳しくは、NfcAdapter.setBeamPushUris() をご覧ください。

送信するファイルを指定する

ファイルを別の NFC 対応デバイスに転送するには、各ファイルのファイル URI(file スキームを含む URI)を取得し、その URI を Uri オブジェクトの配列に追加します。ファイルを転送するには、ファイルの永続的な読み取りアクセス権も必要です。たとえば、次のスニペットは、ファイル名からファイル URI を取得し、その URI を配列に追加する方法を示しています。

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

その他の関連情報については、ストレージ オプションをご覧ください。

このページに関連するサンプルコードについては、Android BeamLargeFiles のサンプルをご覧ください。