11package de .storchp .opentracks .osmplugin ;
22
33import android .annotation .SuppressLint ;
4+ import android .app .Activity ;
45import android .app .DownloadManager ;
56import android .content .BroadcastReceiver ;
67import android .content .Context ;
1516import android .widget .Toast ;
1617
1718import androidx .activity .OnBackPressedCallback ;
19+ import androidx .activity .result .ActivityResultLauncher ;
20+ import androidx .activity .result .contract .ActivityResultContracts ;
21+ import androidx .annotation .NonNull ;
1822import androidx .appcompat .app .AlertDialog ;
23+ import androidx .documentfile .provider .DocumentFile ;
24+
25+ import java .io .File ;
26+ import java .io .FileOutputStream ;
27+ import java .io .IOException ;
28+ import java .io .InputStream ;
29+ import java .io .OutputStream ;
30+ import java .util .zip .ZipEntry ;
31+ import java .util .zip .ZipInputStream ;
1932
2033import de .storchp .opentracks .osmplugin .databinding .ActivityDownloadBinding ;
34+ import de .storchp .opentracks .osmplugin .utils .FileUtil ;
2135import de .storchp .opentracks .osmplugin .utils .PreferencesUtils ;
2236
2337public class DownloadActivity extends BaseActivity {
@@ -37,6 +51,7 @@ public class DownloadActivity extends BaseActivity {
3751 private DownloadType downloadType = DownloadType .MAP ;
3852 private ActivityDownloadBinding binding ;
3953 private long downloadID ;
54+ private DownloadManager downloadManager ;
4055 private DownloadBroadcastReceiver downloadBroadcastReceiver ;
4156
4257 @ SuppressLint ("UnspecifiedRegisterReceiverFlag" )
@@ -115,7 +130,23 @@ public void handleOnBackPressed() {
115130 }
116131 }
117132
133+ protected final ActivityResultLauncher <Intent > directoryIntentLauncher = registerForActivityResult (new ActivityResultContracts .StartActivityForResult (),
134+ result -> {
135+ if (result .getResultCode () == Activity .RESULT_OK ) {
136+ startDownload ();
137+ }
138+ });
139+
118140 public void startDownload () {
141+ var directoryUri = downloadType .getDirectoryUri ();
142+ if (directoryUri != null ) {
143+ var directoryFile = FileUtil .getDocumentFileFromTreeUri (this , directoryUri );
144+ if (directoryFile != null && !directoryFile .canWrite ()) {
145+ directoryIntentLauncher .launch (new Intent (this , downloadType .getDirectoryChooser ()));
146+ return ;
147+ }
148+ }
149+
119150 var filesDir = getFilesDir ().toPath ();
120151 var downloadDir = filesDir .resolve (downloadType .subdir );
121152 downloadDir .toFile ().mkdir ();
@@ -143,7 +174,7 @@ public void startDownload() {
143174 .setRequiresCharging (false )
144175 .setAllowedOverMetered (true )
145176 .setAllowedOverRoaming (true );
146- var downloadManager = (DownloadManager ) getSystemService (DOWNLOAD_SERVICE );
177+ downloadManager = (DownloadManager ) getSystemService (DOWNLOAD_SERVICE );
147178 downloadID = downloadManager .enqueue (request );
148179 observeProgress (downloadManager , downloadID );
149180 }
@@ -186,20 +217,95 @@ private void observeProgress(DownloadManager downloadManager, long downloadId) {
186217 binding .progressBar .setVisibility (View .GONE );
187218 }
188219
189- private void downloadEnded () {
190- binding .progressBar .setVisibility (View .GONE );
191- Toast .makeText (this , downloadType .getSuccessMessageId (), Toast .LENGTH_LONG ).show ();
192- // TODO: unzip if necessary
193- }
194-
195220 private class DownloadBroadcastReceiver extends BroadcastReceiver {
196221 @ Override
197222 public void onReceive (Context context , Intent intent ) {
198- long id = intent .getLongExtra (DownloadManager .EXTRA_DOWNLOAD_ID , -1 );
199- if (downloadID = = id ) {
200- downloadEnded () ;
223+ var id = intent .getLongExtra (DownloadManager .EXTRA_DOWNLOAD_ID , -1 );
224+ if (downloadID ! = id ) {
225+ return ;
201226 }
227+ try (var cursor = downloadManager .query (new DownloadManager .Query ().setFilterById (downloadID ))) {
228+ if (cursor .moveToFirst ()) {
229+ int status = cursor .getInt (cursor .getColumnIndexOrThrow (DownloadManager .COLUMN_STATUS ));
230+ if (status == DownloadManager .STATUS_SUCCESSFUL ) {
231+ var uri = cursor .getString (cursor .getColumnIndexOrThrow (DownloadManager .COLUMN_LOCAL_URI ));
232+ downloadEnded (Uri .parse (uri ));
233+ }
234+ }
235+ }
236+ }
237+ }
238+
239+ private void downloadEnded (Uri downloadedUri ) {
240+ var destinationDir = downloadType .getDirectoryUri ();
241+ if (destinationDir == null ) {
242+ if (downloadType .isExtractMapFromZIP ()) {
243+ try (var inputStream = getContentResolver ().openInputStream (downloadedUri )) {
244+ var zis = new ZipInputStream (inputStream );
245+ ZipEntry ze ;
246+ boolean foundMapInZip = false ;
247+ while (!foundMapInZip && (ze = zis .getNextEntry ()) != null ) {
248+ var filename = ze .getName ();
249+ if (filename .endsWith (".map" )) {
250+ var downloadedFile = new File (downloadedUri .getPath ());
251+ var targetFile = new File (downloadedFile .getParent (), filename );
252+ copy (zis , new FileOutputStream (targetFile ));
253+ foundMapInZip = true ;
254+ }
255+ }
256+ } catch (Exception e ) {
257+ throw new RuntimeException (e );
258+ } finally {
259+ new File (downloadedUri .getPath ()).delete ();
260+ }
261+ }
262+ } else {
263+ try (var inputStream = getContentResolver ().openInputStream (downloadedUri )) {
264+ if (downloadType .isExtractMapFromZIP ()) {
265+ var zis = new ZipInputStream (inputStream );
266+ ZipEntry ze ;
267+ boolean foundMapInZip = false ;
268+ while (!foundMapInZip && (ze = zis .getNextEntry ()) != null ) {
269+ var filename = ze .getName ();
270+ if (filename .endsWith (".map" )) {
271+ var targetFile = createBinaryDocumentFile (destinationDir , filename );
272+ copy (zis , getContentResolver ().openOutputStream (targetFile .getUri (), "wt" ));
273+ foundMapInZip = true ;
274+ }
275+ }
276+ } else {
277+ var filename = downloadedUri .getLastPathSegment ();
278+ var targetFile = createBinaryDocumentFile (destinationDir , filename );
279+ copy (inputStream , getContentResolver ().openOutputStream (targetFile .getUri (), "wt" ));
280+ }
281+ } catch (Exception e ) {
282+ throw new RuntimeException (e );
283+ } finally {
284+ new File (downloadedUri .getPath ()).delete ();
285+ }
286+ }
287+
288+ binding .progressBar .setVisibility (View .GONE );
289+ Toast .makeText (this , downloadType .getSuccessMessageId (), Toast .LENGTH_LONG ).show ();
290+ }
291+
292+ private @ NonNull DocumentFile createBinaryDocumentFile (Uri destinationDir , String filename ) {
293+ var directoryFile = FileUtil .getDocumentFileFromTreeUri (this , destinationDir );
294+ var targetFile = directoryFile .createFile ("application/binary" , filename );
295+ if (targetFile == null ) {
296+ throw new RuntimeException ("Unable to create file: " + filename );
297+ }
298+ return targetFile ;
299+ }
300+
301+ private void copy (InputStream input , OutputStream output ) throws IOException {
302+ var data = new byte [4096 ];
303+ int count ;
304+ while ((count = input .read (data )) != -1 ) {
305+ output .write (data , 0 , count );
202306 }
307+ input .close ();
308+ output .close ();
203309 }
204310
205311 @ Override
@@ -223,7 +329,7 @@ public void navigateUp() {
223329 finish ();
224330 }
225331
226- private enum DownloadType {
332+ public enum DownloadType {
227333
228334 MAP (R .string .download_map , R .string .overwrite_map_question , R .string .download_success , R .string .download_failed , false , "maps" ) {
229335 @ Override
0 commit comments