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

Commit 199c6ea

Browse files
committed
Merge branch 'refs/heads/download_manager'
2 parents 20e9126 + a0d7304 commit 199c6ea

File tree

12 files changed

+389
-344
lines changed

12 files changed

+389
-344
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: 213 additions & 163 deletions
Large diffs are not rendered by default.

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

Lines changed: 20 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, 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(), Uri.fromFile(file), 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(), file.getUri(), 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,14 @@ 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+
} else {
80+
deleted = fileItem.documentFile().delete();
81+
}
7482
if (deleted) {
7583
items.remove(position);
76-
PreferencesUtils.getMapUris().remove(uri);
7784
adapter.notifyDataSetChanged();
7885
} else {
7986
Toast.makeText(MapSelectionActivity.this, R.string.delete_map_error, Toast.LENGTH_LONG).show();

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

Lines changed: 45 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,13 @@
88
import android.content.pm.PackageManager;
99
import android.content.res.Configuration;
1010
import android.database.ContentObserver;
11-
import android.graphics.Bitmap;
12-
import android.graphics.BitmapFactory;
13-
import android.graphics.Canvas;
1411
import android.net.Uri;
15-
import android.opengl.GLException;
16-
import android.opengl.GLSurfaceView;
1712
import android.os.Bundle;
1813
import android.os.Handler;
1914
import android.text.Html;
2015
import android.text.SpannableString;
2116
import android.text.method.LinkMovementMethod;
2217
import android.text.util.Linkify;
23-
import android.util.DisplayMetrics;
2418
import android.util.Log;
2519
import android.view.Menu;
2620
import android.view.MenuItem;
@@ -33,7 +27,6 @@
3327
import androidx.annotation.NonNull;
3428
import androidx.appcompat.app.AlertDialog;
3529
import androidx.core.content.ContextCompat;
36-
import androidx.core.content.FileProvider;
3730
import androidx.documentfile.provider.DocumentFile;
3831

3932
import org.oscim.android.MapPreferences;
@@ -74,19 +67,12 @@
7467
import java.io.BufferedInputStream;
7568
import java.io.File;
7669
import java.io.FileInputStream;
77-
import java.io.FileOutputStream;
78-
import java.nio.IntBuffer;
7970
import java.util.ArrayList;
8071
import java.util.Collections;
8172
import java.util.Objects;
82-
import java.util.Set;
8373
import java.util.concurrent.atomic.AtomicInteger;
8474
import java.util.zip.ZipInputStream;
8575

86-
import javax.microedition.khronos.egl.EGL10;
87-
import javax.microedition.khronos.egl.EGLContext;
88-
import javax.microedition.khronos.opengles.GL10;
89-
9076
import de.storchp.opentracks.osmplugin.dashboardapi.APIConstants;
9177
import de.storchp.opentracks.osmplugin.dashboardapi.Track;
9278
import de.storchp.opentracks.osmplugin.dashboardapi.TrackPoint;
@@ -310,7 +296,6 @@ public void navigateUp() {
310296
@Override
311297
public boolean onCreateOptionsMenu(@NonNull Menu menu) {
312298
super.onCreateOptionsMenu(menu, true);
313-
// TODO: menu.findItem(R.id.share).setVisible(true);
314299
return true;
315300
}
316301

@@ -327,28 +312,43 @@ protected ThemeFile getRenderTheme() {
327312
return null;
328313
}
329314
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");
315+
if ("file".equals(mapTheme.getScheme())) {
316+
var themeFile = new File(mapTheme.getPath());
317+
if (themeFile.exists() && themeFile.getName().endsWith(".zip")) {
318+
var themeFileUri = Uri.fromFile(themeFile);
319+
var fragment = mapTheme.getFragment();
320+
if (fragment != null) {
321+
themeFileUri = themeFileUri.buildUpon().fragment(null).build();
322+
} else {
323+
throw new RuntimeException("Fragment missing, which indicates the theme inside the zip file");
324+
}
325+
return new ZipRenderTheme(fragment, new ZipXmlThemeResourceProvider(new ZipInputStream(new BufferedInputStream(getContentResolver().openInputStream(themeFileUri)))));
326+
}
327+
return new StreamRenderTheme("/assets/", new FileInputStream(themeFile));
328+
} else {
329+
var renderThemeFile = DocumentFile.fromSingleUri(getApplication(), mapTheme);
330+
assert renderThemeFile != null;
331+
var themeFileUri = renderThemeFile.getUri();
332+
if (Objects.requireNonNull(renderThemeFile.getName(), "Theme files must have a name").endsWith(".zip")) {
333+
var fragment = themeFileUri.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)))));
339340
}
340-
return new ZipRenderTheme(fragment, new ZipXmlThemeResourceProvider(new ZipInputStream(new BufferedInputStream(getContentResolver().openInputStream(themeFileUri)))));
341+
return new StreamRenderTheme("/assets/", getContentResolver().openInputStream(themeFileUri));
341342
}
342-
return new StreamRenderTheme("/assets/", getContentResolver().openInputStream(themeFileUri));
343343
} catch (Exception e) {
344344
Log.e(TAG, "Error loading theme " + mapTheme, e);
345345
}
346346
return null;
347347
}
348348

349349
protected MultiMapFileTileSource getMapFile() {
350-
MultiMapFileTileSource tileSource = new MultiMapFileTileSource();
351-
Set<Uri> mapFiles = PreferencesUtils.getMapUris();
350+
var tileSource = new MultiMapFileTileSource();
351+
var mapFiles = PreferencesUtils.getMapUris();
352352
if (mapFiles.isEmpty()) {
353353
return null;
354354
}
@@ -359,6 +359,12 @@ protected MultiMapFileTileSource getMapFile() {
359359
.filter(documentFile -> documentFile != null && documentFile.canRead())
360360
.forEach(documentFile -> readMapFile(tileSource, mapsCount, documentFile));
361361

362+
mapFiles.stream()
363+
.filter(uri -> "file".equals(uri.getScheme()))
364+
.map(uri -> new File(uri.getPath()))
365+
.filter(File::exists)
366+
.forEach(file -> readMapFile(tileSource, mapsCount, file));
367+
362368
if (mapsCount.get() == 0 && !mapFiles.isEmpty()) {
363369
Toast.makeText(this, R.string.error_loading_offline_map, Toast.LENGTH_LONG).show();
364370
}
@@ -378,6 +384,17 @@ private void readMapFile(MultiMapFileTileSource mapDataStore, AtomicInteger maps
378384
}
379385
}
380386

387+
private void readMapFile(MultiMapFileTileSource mapDataStore, AtomicInteger mapsCount, File file) {
388+
try {
389+
var tileSource = new MapFileTileSource();
390+
tileSource.setMapFile(file.getPath());
391+
mapDataStore.add(tileSource);
392+
mapsCount.getAndIncrement();
393+
} catch (Exception e) {
394+
Log.e(TAG, "Can't open mapFile", e);
395+
}
396+
}
397+
381398
protected void loadTheme() {
382399
if (renderTheme != null) {
383400
renderTheme.dispose();
@@ -476,104 +493,11 @@ public boolean onOptionsItemSelected(MenuItem item) {
476493
var intent = new Intent(this, MainActivity.class);
477494
startActivity(intent);
478495
return true;
479-
} else if (item.getItemId() == R.id.share) {
480-
sharePicture();
481-
return true;
482496
}
483497

484498
return super.onOptionsItemSelected(item);
485499
}
486500

487-
private void sharePicture() {
488-
// prepare rendering
489-
var view = binding.map.mainView;
490-
glSurfaceView = binding.map.mapView;
491-
492-
binding.map.sharePictureTitle.setText(R.string.share_picture_title);
493-
binding.map.controls.setVisibility(View.INVISIBLE);
494-
binding.map.attribution.setVisibility(View.INVISIBLE);
495-
496-
// draw
497-
var canvas = new Canvas();
498-
var toBeCropped = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
499-
//canvas.setBitmap(toBeCropped);
500-
501-
captureBitmap(canvas::setBitmap);
502-
view.draw(canvas);
503-
504-
var bitmapOptions = new BitmapFactory.Options();
505-
bitmapOptions.inTargetDensity = 1;
506-
toBeCropped.setDensity(Bitmap.DENSITY_NONE);
507-
508-
int cropFromTop = (int) (70 * ((float) getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT));
509-
int fromHere = toBeCropped.getHeight() - cropFromTop;
510-
var croppedBitmap = Bitmap.createBitmap(toBeCropped, 0, cropFromTop, toBeCropped.getWidth(), fromHere);
511-
512-
try {
513-
var sharedFolderPath = new File(this.getCacheDir(), "shared");
514-
sharedFolderPath.mkdir();
515-
var file = new File(sharedFolderPath, System.currentTimeMillis() + ".png");
516-
var out = new FileOutputStream(file);
517-
croppedBitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
518-
out.close();
519-
var share = new Intent(Intent.ACTION_SEND);
520-
share.putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".fileprovider", file));
521-
share.setType("image/png");
522-
startActivity(Intent.createChooser(share, "send"));
523-
} catch (Exception exception) {
524-
Log.e(TAG, "Error sharing Bitmap", exception);
525-
}
526-
527-
binding.map.controls.setVisibility(View.VISIBLE);
528-
binding.map.attribution.setVisibility(View.VISIBLE);
529-
binding.map.sharePictureTitle.setText("");
530-
}
531-
532-
private GLSurfaceView glSurfaceView;
533-
private Bitmap snapshotBitmap;
534-
535-
private interface BitmapReadyCallbacks {
536-
void onBitmapReady(Bitmap bitmap);
537-
}
538-
539-
private void captureBitmap(final BitmapReadyCallbacks bitmapReadyCallbacks) {
540-
glSurfaceView.queueEvent(() -> {
541-
EGL10 egl = (EGL10) EGLContext.getEGL();
542-
GL10 gl = (GL10) egl.eglGetCurrentContext().getGL();
543-
snapshotBitmap = createBitmapFromGLSurface(0, 0, glSurfaceView.getWidth(), glSurfaceView.getHeight(), gl);
544-
runOnUiThread(() -> bitmapReadyCallbacks.onBitmapReady(snapshotBitmap));
545-
});
546-
547-
}
548-
549-
private Bitmap createBitmapFromGLSurface(int x, int y, int w, int h, GL10 gl) {
550-
int[] bitmapBuffer = new int[w * h];
551-
int[] bitmapSource = new int[w * h];
552-
IntBuffer intBuffer = IntBuffer.wrap(bitmapBuffer);
553-
intBuffer.position(0);
554-
555-
try {
556-
gl.glReadPixels(x, y, w, h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, intBuffer);
557-
int offset1, offset2;
558-
for (int i = 0; i < h; i++) {
559-
offset1 = i * w;
560-
offset2 = (h - i - 1) * w;
561-
for (int j = 0; j < w; j++) {
562-
int texturePixel = bitmapBuffer[offset1 + j];
563-
int blue = (texturePixel >> 16) & 0xff;
564-
int red = (texturePixel << 16) & 0x00ff0000;
565-
int pixel = (texturePixel & 0xff00ff00) | red | blue;
566-
bitmapSource[offset2 + j] = pixel;
567-
}
568-
}
569-
} catch (GLException e) {
570-
Log.e(TAG, "createBitmapFromGLSurface: " + e.getMessage(), e);
571-
return null;
572-
}
573-
574-
return Bitmap.createBitmap(bitmapSource, w, h, Bitmap.Config.ARGB_8888);
575-
}
576-
577501
private void readTrackpoints(Uri data, boolean update, int protocolVersion) {
578502
Log.i(TAG, "Loading trackpoints from " + data);
579503

0 commit comments

Comments
 (0)