88import android .content .pm .PackageManager ;
99import android .content .res .Configuration ;
1010import android .database .ContentObserver ;
11- import android .graphics .Bitmap ;
12- import android .graphics .BitmapFactory ;
13- import android .graphics .Canvas ;
1411import android .net .Uri ;
15- import android .opengl .GLException ;
16- import android .opengl .GLSurfaceView ;
1712import android .os .Bundle ;
1813import android .os .Handler ;
1914import android .text .Html ;
2015import android .text .SpannableString ;
2116import android .text .method .LinkMovementMethod ;
2217import android .text .util .Linkify ;
23- import android .util .DisplayMetrics ;
2418import android .util .Log ;
2519import android .view .Menu ;
2620import android .view .MenuItem ;
3327import androidx .annotation .NonNull ;
3428import androidx .appcompat .app .AlertDialog ;
3529import androidx .core .content .ContextCompat ;
36- import androidx .core .content .FileProvider ;
3730import androidx .documentfile .provider .DocumentFile ;
3831
3932import org .oscim .android .MapPreferences ;
7467import java .io .BufferedInputStream ;
7568import java .io .File ;
7669import java .io .FileInputStream ;
77- import java .io .FileOutputStream ;
78- import java .nio .IntBuffer ;
7970import java .util .ArrayList ;
8071import java .util .Collections ;
8172import java .util .Objects ;
82- import java .util .Set ;
8373import java .util .concurrent .atomic .AtomicInteger ;
8474import 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-
9076import de .storchp .opentracks .osmplugin .dashboardapi .APIConstants ;
9177import de .storchp .opentracks .osmplugin .dashboardapi .Track ;
9278import 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