web-dev-qa-db-ja.com

Android Q(Android 10)のダウンロードディレクトリにファイルを直接ダウンロードする方法

他の人と共有できるチャットアプリを開発しているとしましょう(ファイル、MIMEタイプの制限はありません)。たとえば、画像、ビデオ、ドキュメントだけでなく、Zip、rar、apkなどの圧縮ファイル、または頻度の低い種類のファイルたとえば、photoshopファイルやautocadファイルなどです。

Android 9以下では、これらのファイルを直接ダウンロードディレクトリにダウンロードしますが、Android 10では、ダウンロード先を尋ねる意図をユーザーに示さない限り、これは不可能です...

無理だよ?しかし、なぜGoogle Chromeや他のブラウザがそれを実行できるのでしょうか?実際、Android 10でユーザーに尋ねることなく、ファイルをダウンロードディレクトリにダウンロードします。

最初にWhatsappを分析して、それをどのように達成したかを確認しましたが、AndroidManifestのrequestLegacyExternalStorage属性を使用しています。しかし、私はChromeを分析し、requestLegacyExternalStorageを使用せずにAndroid 10をターゲットにしています。そんなことがあるものか?

私はもう数日間アプリがAndroid 10(Q)のダウンロードディレクトリにファイルを直接ダウンロードする方法を尋ねずにグーグルしているChromeと同じように、ユーザーが配置する場所。

開発者向けドキュメントのAndroid、Stackoverflowに関する多くの質問、インターネットおよびGoogleグループでのブログ投稿を読みましたが、それでもAndroid 9とまったく同じことを続ける方法が見つかりません。私を十分に満足させる解決策でさえ。

これまでに試したこと:

  • ACTION_CREATE_DOCUMENTインテントを使用してSAFを開き、許可を求めますが、サイレントで開く方法はないようです。アクティビティは常に開かれ、ユーザーにファイルの配置場所を尋ねます。しかし、私はすべてのファイルでこのインテントを開くはずですか?私のアプリは、バックグラウンドで自動的にチャットファイルをダウンロードできます。実行可能なソリューションではありません。

  • アプリの冒頭でSAFを使用して、ダウンロードコンテンツの任意のディレクトリを指すURIでアクセスを許可します。

        StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
        i = sm.getPrimaryStorageVolume().createOpenDocumentTreeIntent();
    

    ユーザーに許可を求めるのは醜い活動ではありませんか?これはGoogle Chromeが行うことではありませんが。

  • または、ACTION_CREATE_DOCUMENTを使用して、onActivityResult()で取得したUriを保存し、grantPermission()およびgetContentResolver()。takePersistableUriPermission()を使用します。しかし、これはディレクトリではなくファイルを作成します。

  • また、MediaStore.Downloads.INTERNAL_CONTENT_URIまたはMediaStore.Downloads.EXTERNAL_CONTENT_URIを取得してContext.getContentResolver.insert()を使用してファイルを保存しようとしましたが、実際には@NonNullとして注釈が付けられていますが、実際にはこれらが返されます...ヌル

  • AndroidManifest.xmlのApplicationラベルの属性としてrequestLegacyExternalStorage = "false"を追加します。しかし、これは、開発者が変更を加えてコードを適応させるまでの時間を稼ぐための開発者向けのパッチにすぎません。さらに、これはGoogle Chromeが行うことではありません。

  • getFilesDir()とgetExternalFilesDir()とgetExternalFilesDirs()は引き続き使用できますが、これらのディレクトリに保存されているファイルは、アプリをアンインストールすると削除されます。ユーザーは、アプリをアンインストールするときにファイルを保持することを期待しています。繰り返しますが、私にとっては実行可能な解決策ではありません。

私の一時的な解決策:

RequestLegacyExternalStorage = "false"を追加せずに、どこにでもダウンロードできるようにする回避策を見つけました。

これは、次を使用してFileオブジェクトからUriを取得することで構成されます。

val downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
val file = File(downloadDir, fileName)
val authority = "${context.packageName}.provider"
val accessibleUri = FileProvider.getUriForFile(context, authority, file)

Provider_paths.xmlがある

<paths>
    <external-path name="external_files" path="."/>
</paths>

そして、AndroidManifest.xmlに設定します。

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

問題:

これは、Android Qで非推奨になっているgetExternalStoragePublicDirectoryメソッドを利用しており、Android 11で極端に削除される可能性があります。実際のパスを知っているため、手動で独自のパスを作成できると考えることができます(/ storage/emulated/0/Download /)そしてFileオブジェクトを作成し続けますが、GoogleがAndroid 11のダウンロードディレクトリパスの変更を決定した場合はどうなりますか?

これは長期的な解決策ではないので、

私の質問:

非推奨のメソッドを使用せずにこれを達成するにはどうすればよいですか?そしてボーナスの質問Google Chromeがダウンロードディレクトリにアクセスする方法

6
Rubén Viguera

Android DownloadManager.Requestを使用できます。 Android 9まではWRITE_EXTERNAL_STORAGEパーミッションが必要です。Android 10/Q以降では、パーミッションは必要ありません(パーミッション自体を処理しているようです)。

後でファイルを開く場合は、代わりにユーザーの許可が必要になります(外部アプリ(PDFリーダーなど)内でのみファイルを開く場合も同様です)。

次のようなダウンロードマネージャーを使用できます。

DownloadManager.Request request = new DownloadManager.Request(<download uri>);
request.addRequestHeader("Accept", "application/pdf");

// Save the file in the "Downloads" folder of SDCARD
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS,filename);

DownloadManager downloadManager = (DownloadManager) mActivity.get()
                    .getSystemService(Context.DOWNLOAD_SERVICE);
downloadManager.enqueue(request);

これは参照です: https://developer.Android.com/reference/kotlin/Android/app/DownloadManager.Request?hl=en#setDestinationInExternalPublicDir(kotlin.String、%20kotlin.String) - https://developer.Android.com/training/data-storage

2
beal