Skip to content
This repository was archived by the owner on Aug 16, 2025. It is now read-only.

Commit 80f6cce

Browse files
committed
default download directory is getExternalFilesDir()
1 parent 578f5e3 commit 80f6cce

File tree

7 files changed

+213
-40
lines changed

7 files changed

+213
-40
lines changed

src/main/java/de/storchp/opentracks/osmplugin/DirectoryChooserActivity.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,16 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
2020
protected final ActivityResultLauncher<Intent> directoryIntentLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
2121
result -> {
2222
if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
23-
onActivityResultCustom(result.getData());
23+
onActivityResultOk(result.getData());
24+
} else {
25+
onActivityResultCancel();
2426
}
2527
finish();
2628
});
2729

28-
abstract void onActivityResultCustom(@NonNull Intent resultData);
30+
abstract void onActivityResultOk(@NonNull Intent resultData);
31+
32+
abstract void onActivityResultCancel();
2933

3034
@Override
3135
public void onCreate(Bundle savedInstanceState) {
@@ -58,21 +62,33 @@ protected void releaseOldPermissions() {
5862
public static class MapDirectoryChooserActivity extends DirectoryChooserActivity {
5963

6064
@Override
61-
void onActivityResultCustom(@NonNull final Intent resultData) {
65+
void onActivityResultOk(@NonNull final Intent resultData) {
6266
takePersistableUriPermission(resultData.getData(), resultData);
6367
PreferencesUtils.setMapDirectoryUri(resultData.getData());
6468
releaseOldPermissions();
6569
}
70+
71+
@Override
72+
void onActivityResultCancel() {
73+
PreferencesUtils.setMapDirectoryUri(null);
74+
releaseOldPermissions();
75+
}
6676
}
6777

6878
public static class ThemeDirectoryChooserActivity extends DirectoryChooserActivity {
6979

7080
@Override
71-
void onActivityResultCustom(@NonNull final Intent resultData) {
81+
void onActivityResultOk(@NonNull final Intent resultData) {
7282
takePersistableUriPermission(resultData.getData(), resultData);
7383
PreferencesUtils.setMapThemeDirectoryUri(resultData.getData());
7484
releaseOldPermissions();
7585
}
86+
87+
@Override
88+
void onActivityResultCancel() {
89+
PreferencesUtils.setMapThemeDirectoryUri(null);
90+
releaseOldPermissions();
91+
}
7692
}
7793

7894
}

src/main/java/de/storchp/opentracks/osmplugin/DownloadActivity.java

Lines changed: 117 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package de.storchp.opentracks.osmplugin;
22

33
import android.annotation.SuppressLint;
4+
import android.app.Activity;
45
import android.app.DownloadManager;
56
import android.content.BroadcastReceiver;
67
import android.content.Context;
@@ -15,9 +16,22 @@
1516
import android.widget.Toast;
1617

1718
import androidx.activity.OnBackPressedCallback;
19+
import androidx.activity.result.ActivityResultLauncher;
20+
import androidx.activity.result.contract.ActivityResultContracts;
21+
import androidx.annotation.NonNull;
1822
import 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

2033
import de.storchp.opentracks.osmplugin.databinding.ActivityDownloadBinding;
34+
import de.storchp.opentracks.osmplugin.utils.FileUtil;
2135
import de.storchp.opentracks.osmplugin.utils.PreferencesUtils;
2236

2337
public 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

src/main/java/de/storchp/opentracks/osmplugin/MapSelectionActivity.java

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
package de.storchp.opentracks.osmplugin;
22

3+
import android.net.Uri;
34
import android.os.Bundle;
4-
import android.util.Log;
55
import android.view.MenuItem;
66
import android.widget.Toast;
77

88
import androidx.activity.OnBackPressedCallback;
99
import androidx.appcompat.app.AlertDialog;
1010
import androidx.appcompat.app.AppCompatActivity;
1111

12+
import java.nio.file.Files;
1213
import java.util.ArrayList;
1314
import java.util.Arrays;
1415

@@ -21,8 +22,6 @@
2122

2223
public class MapSelectionActivity extends AppCompatActivity {
2324

24-
private static final String TAG = MapSelectionActivity.class.getSimpleName();
25-
2625
private MapItemAdapter adapter;
2726

2827
@Override
@@ -36,15 +35,23 @@ protected void onCreate(Bundle savedInstanceState) {
3635

3736
var items = new ArrayList<FileItem>();
3837
if (!BuildConfig.offline) {
39-
items.add(new FileItem(getString(R.string.online_osm_mapnick), null));
38+
items.add(new FileItem(getString(R.string.online_osm_mapnick), null, null));
4039
}
4140
var mapDirectory = PreferencesUtils.getMapDirectoryUri();
42-
if (mapDirectory != null) {
41+
if (mapDirectory == null) {
42+
var filesDir = getExternalFilesDir(null).toPath();
43+
var mapDir = filesDir.resolve(DownloadActivity.DownloadType.MAP.getSubdir());
44+
if (Files.exists(mapDir)) {
45+
Arrays.stream(mapDir.toFile().listFiles())
46+
.filter(file -> file.isFile() && file.exists() && file.getName().endsWith(".map"))
47+
.forEach(file -> items.add(new FileItem(file.getName(), file, null)));
48+
}
49+
} else {
4350
var documentsTree = FileUtil.getDocumentFileFromTreeUri(this, mapDirectory);
4451
if (documentsTree != null) {
4552
Arrays.stream(documentsTree.listFiles())
4653
.filter(file -> file.isFile() && file.getName().endsWith(".map"))
47-
.forEach(file -> items.add(new FileItem(file.getName(), file.getUri())));
54+
.forEach(file -> items.add(new FileItem(file.getName(), null, file)));
4855
}
4956
}
5057
adapter = new MapItemAdapter(this, items, PreferencesUtils.getMapUris());
@@ -57,8 +64,7 @@ protected void onCreate(Bundle savedInstanceState) {
5764
});
5865
binding.mapList.setOnItemLongClickListener((parent, view, position, id) -> {
5966
var fileItem = items.get(position);
60-
var uri = fileItem.uri();
61-
if (uri == null) {
67+
if (fileItem.file() == null && fileItem.documentFile() == null) {
6268
// online map can't be deleted
6369
return false;
6470
}
@@ -67,13 +73,16 @@ protected void onCreate(Bundle savedInstanceState) {
6773
.setTitle(R.string.app_name)
6874
.setMessage(getString(R.string.delete_map_question, fileItem.name()))
6975
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
70-
Log.d(TAG, "Delete " + fileItem.name());
71-
var file = FileUtil.getDocumentFileFromTreeUri(MapSelectionActivity.this, fileItem.uri());
72-
assert file != null;
73-
boolean deleted = file.delete();
76+
boolean deleted;
77+
if (fileItem.file() != null) {
78+
deleted = fileItem.file().delete();
79+
PreferencesUtils.getMapUris().remove(Uri.fromFile(fileItem.file()));
80+
} else {
81+
deleted = fileItem.documentFile().delete();
82+
PreferencesUtils.getMapUris().remove(fileItem.documentFile().getUri());
83+
}
7484
if (deleted) {
7585
items.remove(position);
76-
PreferencesUtils.getMapUris().remove(uri);
7786
adapter.notifyDataSetChanged();
7887
} else {
7988
Toast.makeText(MapSelectionActivity.this, R.string.delete_map_error, Toast.LENGTH_LONG).show();

0 commit comments

Comments
 (0)