2016-10-12 6 views
9

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

Вот наш предшествующий код, который начинает загрузку из нашего приложения, сохраняет их в папке Download и называет ACTION_VIEW намерение, как Соонса как 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-файле. :(

+0

Убедитесь, что вы находитесь на последней версии Android библиотек поддержки. Если вы, вне манжеты, это похоже на ошибку в текущем 'FileProvider'. – CommonsWare

+3

'файл: /// storage/emulated/0/Download/test.pdf'. Вы не можете использовать это как есть. Удалите из него «файл: //». Вы должны использовать допустимый путь к файлу: '/ storage/emulated/0/Download/test.pdf'. – greenapps

+0

@CommonsWare Я работаю на v24.2.1 – althaus

ответ

6

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

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

Пожалуйста, в следующий раз, когда вы найдете ответ в комментарии, пригласите плакат опубликовать его комментарий в качестве ответа. – greenapps

+0

Не стесняйтесь публиковать свой ответ. Для этого я еще не принял мою. – althaus

+0

можете ли вы отправить полный ответ? – emaillenin

3

здесь изменение 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); 
     } 
+0

Это не добавляет никакой ценности, поскольку я делаю это уже. – althaus

1

У меня была аналогичная проблема, и я решил ее не автоматически открывать файл, но показывал уведомление «Загрузить полное», а затем позволял системе 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; 
     } 
    } 

}