Совместное использование файлов

После того как вы настроили свое приложение для обмена файлами с использованием URI контента, вы можете отвечать на запросы других приложений для обмена этими файлами. Один из способов ответить на эти запросы это обеспечить интерфейс выбора файла с помощью серверного приложения, который другие приложения смогут вызвать. Такой подход позволяет клиентскому приложению дать пользователю возможность выбрать файл с помощью серверного приложения, а затем получить содержимое выбранного файла используя URI.

Этот урок покажет вам, как создать Activity для выбора файла в вашем приложении, которая реагирует на файловые запросы.

Получение файловых запросов

Для получения запросов на файлы из клиентских приложений и отвечать с помощью URI содержимого, ваше приложение должно обеспечить выбор файлов с помощью специальной Activity. Клиентские приложения запускают эту Activity с помощью вызова startActivityForResult() с Intent содержащим действие ACTION_PICK. Когда клиентское приложение вызывает startActivityForResult(), ваше приложение может вернуть результат в клиентскую программу в виде URI содержимого файла, выбранного пользователем.

Чтобы узнать, как реализовать запрос на получение файла в клиентском приложении, см. урок Запрос файла.

Создание деятельности выбора файла

Чтобы настроить выбор файлов через Activity, начните с указания Activity в вашем манифесте, наряду с фильтром намерений, который соответствует действию ACTION_PICK и категориям CATEGORY_DEFAULT и CATEGORY_OPENABLE. Кроме того, добавьте фильтр MIME типа для файлов вашего приложения, которые будут использоваться в других приложениях. В следующем фрагменте показано, как указать новую Activity и фильтр намерения:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    ...
        <application>
        ...
            <activity
                android:name=".FileSelectActivity"
                android:label="@"File Selector" >
                <intent-filter>
                    <action
                        android:name="android.intent.action.PICK"/>
                    <category
                        android:name="android.intent.category.DEFAULT"/>
                    <category
                        android:name="android.intent.category.OPENABLE"/>
                    <data android:mimeType="text/plain"/>
                    <data android:mimeType="image/*"/>
                </intent-filter>
            </activity>

Определите деятельность выбора файла в коде

Затем, определите Activity подкласс, который отображает файлы доступные из каталога вашего приложения files/images/ во внутреннем хранилище, и позвольте пользователю выбрать нужный файл. В следующем фрагменте показано, как определить эту Activity и отвечать на выбор пользователя:

public class MainActivity extends Activity {
    // The path to the root of this app's internal storage
    private File mPrivateRootDir;
    // The path to the "images" subdirectory
    private File mImagesDir;
    // Array of files in the images subdirectory
    File[] mImageFiles;
    // Array of filenames corresponding to mImageFiles
    String[] mImageFilenames;
    // Initialize the Activity
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        // Set up an Intent to send back to apps that request a file
        mResultIntent =
                new Intent("com.example.myapp.ACTION_RETURN_FILE");
        // Get the files/ subdirectory of internal storage
        mPrivateRootDir = getFilesDir();
        // Get the files/images subdirectory;
        mImagesDir = new File(mPrivateRootDir, "images");
        // Get the files in the images subdirectory
        mImageFiles = mImagesDir.listFiles();
        // Set the Activity's result to null to begin with
        setResult(Activity.RESULT_CANCELED, null);
        /*
         * Display the file names in the ListView mFileListView.
         * Back the ListView with the array mImageFilenames, which
         * you can create by iterating through mImageFiles and
         * calling File.getAbsolutePath() for each File
         */
         ...
    }
    ...
}

Ответить на выбор файла

После того, как пользователь выбирает файл, приложение должно определить, какой файл был выбран, а затем сгенерировать URI контента для файла. Поскольку Activity отображает список доступных файлов в ListView, когда пользователь щелкает имя файла система вызывает метод onItemClick(), в котором вы можете получить выбранный файл.

В onItemClick(), получите File объект для имени выбранного файла и передайте его в качестве аргумента в getUriForFile(), вместе с авторитетным источником, который вы указали в <provider> элементе для FileProvider. Результирующий URI контента содержит авторитетный источник, сегмент пути соответствующего каталога, содержащего файл (как указано в мета-данных XML), а также имя файла, включая его расширение. Как FileProvider устанавливает соответствие каталогов и сегментов пути на основе XML мета-данных описывается в разделе Укажите каталоги совместного использования.

В следующем фрагменте показано, как определить выбранный файл и получить URI контента для него:

    protected void onCreate(Bundle savedInstanceState) {
        ...
        // Define a listener that responds to clicks on a file in the ListView
        mFileListView.setOnItemClickListener(
                new AdapterView.OnItemClickListener() {
            @Override
            /*
             * When a filename in the ListView is clicked, get its
             * content URI and send it to the requesting app
             */
            public void onItemClick(AdapterView<?> adapterView,
                    View view,
                    int position,
                    long rowId) {
                /*
                 * Get a File for the selected file name.
                 * Assume that the file names are in the
                 * mImageFilename array.
                 */
                File requestFile = new File(mImageFilename[position]);
                /*
                 * Most file-related method calls need to be in
                 * try-catch blocks.
                 */
                // Use the FileProvider to get a content URI
                try {
                    fileUri = FileProvider.getUriForFile(
                            MainActivity.this,
                            "com.example.myapp.fileprovider",
                            requestFile);
                } catch (IllegalArgumentException e) {
                    Log.e("File Selector",
                          "The selected file can't be shared: " +
                          clickedFilename);
                }
                ...
            }
        });
        ...
    }

Помните, что вы можете сгенерировать только URI контента для файлов, находящихся в каталогах, которые указанны в файле мета-данных, содержащем <paths> элементы, как описано в разделе Укажите каталоги совместного использования. Если вы вызовите getUriForFile() для File в каталоге, который не был указан, то вы получите IllegalArgumentException.

Предоставление разрешений для файла

Теперь, когда у вас есть URI контента для файла, доступ к которому вы хотите предоставить другим приложениям, вам нужно позволить клиентскому приложению получить доступ к файлу. Чтобы разрешить доступ, предоставьте разрешение клиентскому приложению, добавив URI контента в Intent , а затем установив флаги разрешений на Intent. Предоставляемые разрешения являются временными и их срок действия истекает автоматически, когда стек задачи принимающего приложения завершается.

Следующий фрагмент кода показывает, как задать разрешение на чтение файла:

    protected void onCreate(Bundle savedInstanceState) {
        ...
        // Define a listener that responds to clicks in the ListView
        mFileListView.setOnItemClickListener(
                new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView,
                    View view,
                    int position,
                    long rowId) {
                ...
                if (fileUri != null) {
                    // Grant temporary read permission to the content URI
                    mResultIntent.addFlags(
                        Intent.FLAG_GRANT_READ_URI_PERMISSION);
                }
                ...
             }
             ...
        });
    ...
    }

Внимание: Вызов setFlags() метода является единственным способом для безопасного предоставления доступа к файлам с помощью разрешения на временный доступ. Избегайте вызова метода Context.grantUriPermission() для URI контента файла, так как этот метод предоставляет доступ, который вы можете отменить только вызвав Context.revokeUriPermission().

Предоставить доступ к файлу запрашивающему приложению

Чтобы предоставить доступ к файлу приложению, которое его просило, передайте Intent содержащий URI контента и разрешение в setResult(). Когда Activity , которую вы только что определили, завершается, система посылает Intent , содержащий URI контента, в клиентскую программу. Следующий фрагмент кода показывает, как это сделать:

    protected void onCreate(Bundle savedInstanceState) {
        ...
        // Define a listener that responds to clicks on a file in the ListView
        mFileListView.setOnItemClickListener(
                new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView,
                    View view,
                    int position,
                    long rowId) {
                ...
                if (fileUri != null) {
                    ...
                    // Put the Uri and MIME type in the result Intent
                    mResultIntent.setDataAndType(
                            fileUri,
                            getContentResolver().getType(fileUri));
                    // Set the result
                    MainActivity.this.setResult(Activity.RESULT_OK,
                            mResultIntent);
                    } else {
                        mResultIntent.setDataAndType(null, "");
                        MainActivity.this.setResult(RESULT_CANCELED,
                                mResultIntent);
                    }
                }
        });

Предоставьте пользователям возможность немедленно вернуться в клиентскую программу, как только они выбрали файл. Один из способов сделать это заключается в предоставлении галочки или кнопки Готово . Свяжите метод с кнопкой при помощью android:onClick атрибута. В методе, вызовите finish(). Например:

    public void onDoneClick(View v) {
        // Associate a method with the Done button
        finish();
    }