Откройте загруженный файл на Android N с помощью FileProvider

я должен исправить наше приложение для Android N из-за изменений FileProvider. Я в основном прочитал все об этой теме для последнего нашего, но никакое решение не найдено для меня.

вот наш предыдущий код, который запускает загрузки из нашего приложения, хранит их в Download папка и вызывает ACTION_VIEW намерение как soons как DownloadManager говорит, что он закончил загрузку:

BroadcastReceiver onComplete = new BroadcastReceiver() {
    public void onReceive(Context ctxt, Intent intent) {
        Log.d(TAG, "Download commplete");

        // Check for our download
        long referenceId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
        if (mDownloadReference == referenceId) {
            DownloadManager.Query query = new DownloadManager.Query();
            query.setFilterById(mDownloadReference);
            Cursor c = mDownloadManager.query(query);
            if (c.moveToFirst()) {
                int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
                if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) {
                    String localUri = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
                    String fileExtension = MimeTypeMap.getFileExtensionFromUrl(localUri);
                    String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension);

                    if (mimeType != null) {
                        Intent openFileIntent = new Intent(Intent.ACTION_VIEW);
                        openFileIntent.setDataAndTypeAndNormalize(Uri.parse(localUri), mimeType);
                        openFileIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);

                        try {
                            mAcme.startActivity(openFileIntent);
                        }
                        catch (ActivityNotFoundException e) {
                            // Ignore if no activity was found.
                        }
                    }
                }
            }
        }
    }
};

это работает на Android M, но ломается на N из-за популярного FileUriExposedException. Я сейчас пытался исправить это с помощью FileProvider, но я не могу заставить его работать. Он ломается, когда я пытаюсь получить URI содержимого:

Failed to find configured root that contains /file:/storage/emulated/0/Download/test.pdf

на localUri вернулся из DownloadManager для файл:

file:///storage/emulated/0/Download/test.pdf

на Environment.getExternalStorageDirectory() возвращает /storage/emulated/0 а это код для преобразования:

File file = new File(localUri);
Log.d(TAG, localUri + " - " + Environment.getExternalStorageDirectory());
Uri contentUri = FileProvider.getUriForFile(ctxt, "my.file.provider", file);

С AndroidManifest.xml

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="my.file.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths"/>
    </provider>

на file_paths.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_path" path="." />
</paths>

и я пробовал все значения, которые мог найдите в этом xml-файле. :(

4 ответов


благодаря @greenaps я смог решить эту проблему. Локальный URI, извлеченный из DownlodManager с префиксом file://. Это должно быть отброшено для нового FileProvider:

if (localUri.substring(0, 7).matches("file://")) {
    localUri =  localUri.substring(7);
}
File file = new File(localUri);

здесь изменение в AndroidManifest.в XML

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="${applicationId}.provider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths"/>
</provider>

изменить путь к файлу

Uri contentUri;
if(Build.VERSION.SDK_INT == 24){
         contentUri = FileProvider.getUriForFile(MainActivity.this,
              getApplicationContext().getPackageName() + ".provider",
                    file);
 } else{
         contentUri = Uri.fromFile(file);
        }

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

чтобы уведомить Пользователя дополнительно, я показываю тост, когда загрузка будет завершена.

DownloadManager mManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);

String url = "your URL";
String filename = "file.pdf";

// Set up the request.
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url))
            .setTitle("Test")
            .setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename)
            .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
            .setDescription("Downloading...")
            .setMimeType("application/pdf");

request.allowScanningByMediaScanner();
mManager.enqueue(request);

BroadcastReceiver:

public class DownloadReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        switch (intent.getAction()) {
            case DownloadManager.ACTION_DOWNLOAD_COMPLETE:
                Toast.makeText(context, "Download completed", Toast.LENGTH_SHORT).show();
                break;
        }
    }

}

может быть это более четкое решение ?

Uri uriParser = Uri.parse(downloadedPackageUriString);
File downloadedFile = new File(uriParser.getPath());

до

файл:///хранение/эмуляция / 0 / загрузка / ZS%20Gac%C3%ADkov%C3%A1.формат PDF

после

/ хранение / эмуляция/0/загрузка / ZS Gacíková.формат PDF