Google은 흑인 공동체를 위한 인종 간 평등을 진전시키기 위해 노력하고 있습니다. Google에서 어떤 노력을 하고 있는지 확인하세요.

NFC로 다른 기기에서 파일 수신

Android Beam 파일 전송은 수신 기기의 특정 디렉터리에 파일을 복사합니다. 또한, Android 미디어 스캐너를 사용하여 복사된 파일을 검사하고 MediaStore 제공자에 미디어 파일 항목을 추가합니다. 이 과정에서는 파일 복사가 완료되었을 때 응답하는 방법과 수신 기기에서 복사된 파일을 찾는 방법에 관해 설명합니다.

데이터 표시 요청에 응답

Android Beam 파일 전송은 수신 기기에 파일 복사를 완료하면 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 Beam 파일 전송이 ACTION_VIEW 인텐트의 유일한 소스는 아닙니다. 수신 기기의 다른 앱도 이 작업으로 Intent를 전송할 수 있습니다. 이 상황을 처리하는 방법은 콘텐츠 URI에서 디렉터리 가져오기 섹션에서 설명합니다.

파일 권한 요청

Android Beam 파일 전송이 기기에 복사한 파일을 읽으려면 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 Beam 파일 전송은 모든 파일을 한 번의 전송으로 수신 기기의 하나의 디렉터리에 복사합니다. Android Beam 파일 전송 알림에서 전송한 콘텐츠 Intent의 URI는 처음 전송된 파일을 가리킵니다. 그러나, 앱은 Android Beam 파일 전송 외의 다른 소스에서 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
                    }
                }
            }
            ...
        }
        ...
    }
    

자바

    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 Beam 파일 전송에서 디렉터리 경로는 다른 전송된 파일이 있는 경우 그 파일의 위치를 가리킵니다. 디렉터리 경로를 가져오려면 file: 접두사를 제외한 URI에서 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
                }
        ...
    

자바

        ...
        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 콘텐츠 제공업체에 저장된 디렉터리 및 파일 이름을 가리킬 수 있습니다. URI의 권한 값을 테스트하여 MediaStore의 콘텐츠 URI를 감지할 수 있습니다. MediaStore의 콘텐츠 URI는 Android Beam 파일 전송 또는 다른 앱에서 가져올 수 있지만, 두 경우 모두 콘텐츠 URI의 디렉터리와 파일 이름을 검색할 수 있습니다.

MediaStore가 아닌 콘텐츠 제공업체의 콘텐츠 URI가 포함된 수신 ACTION_VIEW 인텐트도 수신할 수 있습니다. 이 경우 콘텐츠 URI는 MediaStore 권한 값을 포함하지 않으며 콘텐츠 URI는 일반적으로 디렉터리를 가리키지 않습니다.

참고: Android Beam 파일 전송의 경우 첫 번째 수신 파일이 미디어 관련 파일임을 나타내는 'audio/*', 'image/*', 또는 'video/*'의 MIME 유형을 갖는다면 ACTION_VIEW 인텐트에서 콘텐츠 URI를 수신합니다. Android Beam 파일 전송은 전송된 파일이 저장된 디렉터리에서 미디어 스캐너를 실행하여 전송한 미디어 파일의 색인을 생성합니다. 미디어 스캐너는 결과를 MediaStore 콘텐츠 제공업체에 기록한 다음, 첫 번째 파일의 콘텐츠 URI를 Android Beam 파일 전송에 다시 전달합니다. 이 콘텐츠 URI는 Intent 알림에서 수신됩니다. 첫 번째 파일의 디렉터리를 가져오려면 콘텐츠 URI를 사용하여 MediaStore에서 가져옵니다.

콘텐츠 제공업체 확인

콘텐츠 URI에서 파일 디렉터리를 가져올 수 있는지 확인하려면 URI의 권한을 가져오도록 Uri.getAuthority()를 호출하여 URI와 연결된 콘텐츠 제공업체를 결정합니다. 그 결과로 다음의 두 가지 값이 가능합니다.

MediaStore.AUTHORITY
URI는 MediaStore에서 추적하는 하나 이상의 파일입니다. MediaStore에서 전체 파일 이름을 가져오고 파일 이름에서 디렉터리를 얻습니다.
기타 권한 값
다른 콘텐츠 제공업체의 콘텐츠 URI. 콘텐츠 URI와 연결된 데이터를 표시하지만, 파일 디렉터리는 가져오지 않습니다.

MediaStore 콘텐츠 URI의 디렉터리를 가져오려면 Uri 인수의 수신 콘텐츠 URI와 프로젝션의 MediaColumns.DATA 열을 지정하는 쿼리를 실행합니다. 반환된 Cursor에는 URI가 나타내는 파일의 전체 경로와 이름이 포함됩니다. 이 경로에는 Android Beam 파일 전송에서 기기로 복사한 다른 모든 파일도 포함됩니다.

다음 스니펫은 콘텐츠 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
                    }
                }
        ...
    

자바

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

콘텐츠 제공업체에서 데이터를 가져오는 방법에 관해 자세히 알아보려면 제공자로부터 데이터 가져오기 섹션을 참조하세요.

추가 정보는 다음을 참조하세요.