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

Commit d5ef53f

Browse files
committed
download progress, mapsactivity support local files
1 parent 80f6cce commit d5ef53f

File tree

2 files changed

+111
-50
lines changed

2 files changed

+111
-50
lines changed

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

Lines changed: 66 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,13 @@
77
import android.content.Context;
88
import android.content.Intent;
99
import android.content.IntentFilter;
10+
import android.database.Cursor;
1011
import android.net.Uri;
1112
import android.os.Build;
1213
import android.os.Bundle;
14+
import android.os.Handler;
15+
import android.os.Looper;
16+
import android.os.Message;
1317
import android.util.Log;
1418
import android.view.MenuItem;
1519
import android.view.View;
@@ -27,6 +31,8 @@
2731
import java.io.IOException;
2832
import java.io.InputStream;
2933
import java.io.OutputStream;
34+
import java.util.concurrent.ExecutorService;
35+
import java.util.concurrent.Executors;
3036
import java.util.zip.ZipEntry;
3137
import java.util.zip.ZipInputStream;
3238

@@ -37,6 +43,7 @@
3743
public class DownloadActivity extends BaseActivity {
3844

3945
private static final String TAG = DownloadActivity.class.getSimpleName();
46+
private static final int UPDATE_DOWNLOAD_PROGRESS = 1;
4047

4148
private static final String OPENANDROMAPS_MAP_HOST = "ftp.gwdg.de";
4249
private static final String OPENANDROMAPS_THEME_HOST = "www.openandromaps.org";
@@ -50,9 +57,10 @@ public class DownloadActivity extends BaseActivity {
5057
private Uri downloadUri;
5158
private DownloadType downloadType = DownloadType.MAP;
5259
private ActivityDownloadBinding binding;
53-
private long downloadID;
60+
private Long downloadId = null;
5461
private DownloadManager downloadManager;
5562
private DownloadBroadcastReceiver downloadBroadcastReceiver;
63+
private final ExecutorService executor = Executors.newFixedThreadPool(1);
5664

5765
@SuppressLint("UnspecifiedRegisterReceiverFlag")
5866
@Override
@@ -109,7 +117,9 @@ protected void onCreate(Bundle savedInstanceState) {
109117

110118
binding.downloadInfo.setText(downloadUri.toString());
111119
binding.startDownloadButton.setOnClickListener((view) -> {
112-
startDownload();
120+
if (downloadId == null) {
121+
startDownload();
122+
}
113123
});
114124
} else {
115125
binding.downloadInfo.setText(R.string.no_download_uri_found);
@@ -147,7 +157,7 @@ public void startDownload() {
147157
}
148158
}
149159

150-
var filesDir = getFilesDir().toPath();
160+
var filesDir = getExternalFilesDir(null).toPath();
151161
var downloadDir = filesDir.resolve(downloadType.subdir);
152162
downloadDir.toFile().mkdir();
153163
var filename = downloadUri.getLastPathSegment();
@@ -159,6 +169,7 @@ public void startDownload() {
159169
.setMessage(getString(downloadType.getOverwriteMessageId(), filename))
160170
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
161171
file.toFile().delete();
172+
dialog.dismiss();
162173
startDownload();
163174
})
164175
.setNegativeButton(android.R.string.cancel, null)
@@ -175,56 +186,72 @@ public void startDownload() {
175186
.setAllowedOverMetered(true)
176187
.setAllowedOverRoaming(true);
177188
downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
178-
downloadID = downloadManager.enqueue(request);
179-
observeProgress(downloadManager, downloadID);
189+
downloadId = downloadManager.enqueue(request);
190+
observeDownload();
180191
}
181192

182-
private void observeProgress(DownloadManager downloadManager, long downloadId) {
183-
var query = new DownloadManager.Query().setFilterById(downloadId);
184-
var downloading = true;
193+
private void observeDownload() {
194+
binding.progressBar.setVisibility(View.VISIBLE);
185195
binding.progressBar.setIndeterminate(false);
186196
binding.progressBar.setMax(100);
187-
188-
while (downloading) {
189-
binding.progressBar.setVisibility(View.VISIBLE);
190-
try (var cursor = downloadManager.query(query)) {
191-
if (cursor.moveToFirst()) {
192-
var statusIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
193-
var progressIndex = cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR);
194-
var totalIndex = cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES);
195-
196-
int status = cursor.getInt(statusIndex);
197-
long downloaded = cursor.getLong(progressIndex);
198-
long total = cursor.getLong(totalIndex);
199-
int progress = (int) ((downloaded * 100L) / total);
200-
201-
if (status == DownloadManager.STATUS_SUCCESSFUL || status == DownloadManager.STATUS_FAILED) {
202-
downloading = false;
203-
}
204-
205-
if (total >= 0) {
206-
binding.progressBar.setProgress(progress);
197+
binding.progressBar.setProgress(0);
198+
199+
executor.execute(() -> {
200+
int progress = 0;
201+
boolean isDownloadFinished = false;
202+
while (!isDownloadFinished) {
203+
try (Cursor cursor = downloadManager.query(new DownloadManager.Query().setFilterById(downloadId))) {
204+
if (cursor.moveToFirst()) {
205+
int downloadStatus = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
206+
switch (downloadStatus) {
207+
case DownloadManager.STATUS_RUNNING:
208+
long totalBytes = cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
209+
if (totalBytes > 0) {
210+
long downloadedBytes = cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
211+
progress = (int) (downloadedBytes * 100 / totalBytes);
212+
}
213+
214+
break;
215+
case DownloadManager.STATUS_SUCCESSFUL:
216+
progress = 100;
217+
isDownloadFinished = true;
218+
break;
219+
case DownloadManager.STATUS_PAUSED:
220+
case DownloadManager.STATUS_PENDING:
221+
break;
222+
case DownloadManager.STATUS_FAILED:
223+
isDownloadFinished = true;
224+
break;
225+
}
226+
var message = Message.obtain();
227+
message.what = UPDATE_DOWNLOAD_PROGRESS;
228+
message.arg1 = progress;
229+
mainHandler.sendMessage(message);
207230
}
208231
}
209232
}
233+
});
234+
}
210235

211-
try {
212-
Thread.sleep(1000); // Wait for 1 second before querying again
213-
} catch (InterruptedException ignored) {
214-
236+
private final Handler mainHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
237+
@Override
238+
public boolean handleMessage(@NonNull Message msg) {
239+
if (msg.what == UPDATE_DOWNLOAD_PROGRESS) {
240+
int downloadProgress = msg.arg1;
241+
binding.progressBar.setProgress(downloadProgress);
215242
}
243+
return true;
216244
}
217-
binding.progressBar.setVisibility(View.GONE);
218-
}
245+
});
219246

220247
private class DownloadBroadcastReceiver extends BroadcastReceiver {
221248
@Override
222249
public void onReceive(Context context, Intent intent) {
223250
var id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
224-
if (downloadID != id) {
251+
if (downloadId != id) {
225252
return;
226253
}
227-
try (var cursor = downloadManager.query(new DownloadManager.Query().setFilterById(downloadID))) {
254+
try (var cursor = downloadManager.query(new DownloadManager.Query().setFilterById(downloadId))) {
228255
if (cursor.moveToFirst()) {
229256
int status = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
230257
if (status == DownloadManager.STATUS_SUCCESSFUL) {
@@ -237,6 +264,8 @@ public void onReceive(Context context, Intent intent) {
237264
}
238265

239266
private void downloadEnded(Uri downloadedUri) {
267+
executor.shutdown();
268+
mainHandler.removeCallbacksAndMessages(null);
240269
var destinationDir = downloadType.getDirectoryUri();
241270
if (destinationDir == null) {
242271
if (downloadType.isExtractMapFromZIP()) {
@@ -286,6 +315,7 @@ private void downloadEnded(Uri downloadedUri) {
286315
}
287316

288317
binding.progressBar.setVisibility(View.GONE);
318+
downloadId = null;
289319
Toast.makeText(this, downloadType.getSuccessMessageId(), Toast.LENGTH_LONG).show();
290320
}
291321

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

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@
7979
import java.util.ArrayList;
8080
import java.util.Collections;
8181
import java.util.Objects;
82-
import java.util.Set;
8382
import java.util.concurrent.atomic.AtomicInteger;
8483
import java.util.zip.ZipInputStream;
8584

@@ -327,28 +326,43 @@ protected ThemeFile getRenderTheme() {
327326
return null;
328327
}
329328
try {
330-
var renderThemeFile = DocumentFile.fromSingleUri(getApplication(), mapTheme);
331-
assert renderThemeFile != null;
332-
var themeFileUri = renderThemeFile.getUri();
333-
if (Objects.requireNonNull(renderThemeFile.getName(), "Theme files must have a name").endsWith(".zip")) {
334-
var fragment = themeFileUri.getFragment();
335-
if (fragment != null) {
336-
themeFileUri = themeFileUri.buildUpon().fragment(null).build();
337-
} else {
338-
throw new RuntimeException("Fragment missing, which indicates the theme inside the zip file");
329+
if (mapTheme.getScheme().equals("file")) {
330+
var themeFile = new File(mapTheme.getPath());
331+
if (themeFile.exists() && themeFile.getName().endsWith(".zip")) {
332+
var themeFileUri = Uri.fromFile(themeFile);
333+
var fragment = mapTheme.getFragment();
334+
if (fragment != null) {
335+
themeFileUri = themeFileUri.buildUpon().fragment(null).build();
336+
} else {
337+
throw new RuntimeException("Fragment missing, which indicates the theme inside the zip file");
338+
}
339+
return new ZipRenderTheme(fragment, new ZipXmlThemeResourceProvider(new ZipInputStream(new BufferedInputStream(getContentResolver().openInputStream(themeFileUri)))));
340+
}
341+
return new StreamRenderTheme("/assets/", new FileInputStream(themeFile));
342+
} else {
343+
var renderThemeFile = DocumentFile.fromSingleUri(getApplication(), mapTheme);
344+
assert renderThemeFile != null;
345+
var themeFileUri = renderThemeFile.getUri();
346+
if (Objects.requireNonNull(renderThemeFile.getName(), "Theme files must have a name").endsWith(".zip")) {
347+
var fragment = themeFileUri.getFragment();
348+
if (fragment != null) {
349+
themeFileUri = themeFileUri.buildUpon().fragment(null).build();
350+
} else {
351+
throw new RuntimeException("Fragment missing, which indicates the theme inside the zip file");
352+
}
353+
return new ZipRenderTheme(fragment, new ZipXmlThemeResourceProvider(new ZipInputStream(new BufferedInputStream(getContentResolver().openInputStream(themeFileUri)))));
339354
}
340-
return new ZipRenderTheme(fragment, new ZipXmlThemeResourceProvider(new ZipInputStream(new BufferedInputStream(getContentResolver().openInputStream(themeFileUri)))));
355+
return new StreamRenderTheme("/assets/", getContentResolver().openInputStream(themeFileUri));
341356
}
342-
return new StreamRenderTheme("/assets/", getContentResolver().openInputStream(themeFileUri));
343357
} catch (Exception e) {
344358
Log.e(TAG, "Error loading theme " + mapTheme, e);
345359
}
346360
return null;
347361
}
348362

349363
protected MultiMapFileTileSource getMapFile() {
350-
MultiMapFileTileSource tileSource = new MultiMapFileTileSource();
351-
Set<Uri> mapFiles = PreferencesUtils.getMapUris();
364+
var tileSource = new MultiMapFileTileSource();
365+
var mapFiles = PreferencesUtils.getMapUris();
352366
if (mapFiles.isEmpty()) {
353367
return null;
354368
}
@@ -359,6 +373,12 @@ protected MultiMapFileTileSource getMapFile() {
359373
.filter(documentFile -> documentFile != null && documentFile.canRead())
360374
.forEach(documentFile -> readMapFile(tileSource, mapsCount, documentFile));
361375

376+
mapFiles.stream()
377+
.filter(uri -> uri.getScheme().equals("file"))
378+
.map(uri -> new File(uri.getPath()))
379+
.filter(File::exists)
380+
.forEach(file -> readMapFile(tileSource, mapsCount, file));
381+
362382
if (mapsCount.get() == 0 && !mapFiles.isEmpty()) {
363383
Toast.makeText(this, R.string.error_loading_offline_map, Toast.LENGTH_LONG).show();
364384
}
@@ -378,6 +398,17 @@ private void readMapFile(MultiMapFileTileSource mapDataStore, AtomicInteger maps
378398
}
379399
}
380400

401+
private void readMapFile(MultiMapFileTileSource mapDataStore, AtomicInteger mapsCount, File file) {
402+
try {
403+
var tileSource = new MapFileTileSource();
404+
tileSource.setMapFile(file.getPath());
405+
mapDataStore.add(tileSource);
406+
mapsCount.getAndIncrement();
407+
} catch (Exception e) {
408+
Log.e(TAG, "Can't open mapFile", e);
409+
}
410+
}
411+
381412
protected void loadTheme() {
382413
if (renderTheme != null) {
383414
renderTheme.dispose();

0 commit comments

Comments
 (0)