From 19ab52216bc1011bd8d1c6cf6b39970712699f06 Mon Sep 17 00:00:00 2001 From: Andrew Shkrob Date: Mon, 21 Nov 2022 19:48:34 +0100 Subject: [PATCH 1/7] [android] Add Android Auto support Signed-off-by: Andrew Shkrob --- android/AndroidManifest.xml | 18 ++ android/build.gradle | 3 + android/gradle.properties | 2 +- android/res/values/car_hosts.xml | 11 ++ android/res/xml/automotive_app_desc.xml | 4 + .../car/NavigationCarAppService.java | 46 +++++ .../app/organicmaps/car/NavigationScreen.java | 84 +++++++++ .../organicmaps/car/NavigationSession.java | 22 +++ .../app/organicmaps/car/SurfaceRenderer.java | 175 ++++++++++++++++++ 9 files changed, 364 insertions(+), 1 deletion(-) create mode 100644 android/res/values/car_hosts.xml create mode 100644 android/res/xml/automotive_app_desc.xml create mode 100644 android/src/app/organicmaps/car/NavigationCarAppService.java create mode 100644 android/src/app/organicmaps/car/NavigationScreen.java create mode 100644 android/src/app/organicmaps/car/NavigationSession.java create mode 100644 android/src/app/organicmaps/car/SurfaceRenderer.java diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 9167327a9c..4c6095089b 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -38,6 +38,8 @@ //--> + + @@ -723,6 +725,14 @@ android:name="app.organicmaps.background.OsmUploadService" android:permission="android.permission.BIND_JOB_SERVICE" android:exported="false"/> + + + + + + + + + + + diff --git a/android/build.gradle b/android/build.gradle index 96c989afd2..8d3066b70c 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -84,6 +84,7 @@ dependencies { implementation 'androidx.annotation:annotation:1.5.0' implementation 'androidx.appcompat:appcompat:1.7.0-alpha01' + implementation 'androidx.car.app:app:1.3.0-beta01' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.fragment:fragment:1.5.4' // Lifecycle is added as a workaround for duplicate classes error caused by some outdated dependency: @@ -97,6 +98,8 @@ dependencies { implementation 'androidx.work:work-runtime:2.7.1' implementation 'com.google.android.material:material:1.8.0-alpha02' implementation 'com.google.code.gson:gson:2.10' + // Fix for app/organicmaps/util/FileUploadWorker.java:14: error: cannot access ListenableFuture + implementation 'com.google.guava:guava:29.0-android' implementation 'com.timehop.stickyheadersrecyclerview:library:0.4.3@aar' implementation 'com.github.devnullorthrow:MPAndroidChart:3.2.0-alpha' implementation 'net.jcip:jcip-annotations:1.0' diff --git a/android/gradle.properties b/android/gradle.properties index c9cf2e5f8f..5608450632 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,4 +1,4 @@ -propMinSdkVersion=21 +propMinSdkVersion=23 propTargetSdkVersion=33 propCompileSdkVersion=33 propBuildToolsVersion=33.0.0 diff --git a/android/res/values/car_hosts.xml b/android/res/values/car_hosts.xml new file mode 100644 index 0000000000..cc47a82af6 --- /dev/null +++ b/android/res/values/car_hosts.xml @@ -0,0 +1,11 @@ + + + + fdb00c43dbde8b51cb312aa81d3b5fa17713adb94b28f598d77f8eb89daceedf,com.google.android.projection.gearhead + 70811a3eacfd2e83e18da9bfede52df16ce91f2e69a44d21f18ab66991130771,com.google.android.projection.gearhead + 1975b2f17177bc89a5dff31f9e64a6cae281a53dc1d1d59b1d147fe1c82afa00,com.google.android.projection.gearhead + c241ffbc8e287c4e9a4ad19632ba1b1351ad361d5177b7d7b29859bd2b7fc631,com.google.android.apps.automotive.templates.host + dd66deaf312d8daec7adbe85a218ecc8c64f3b152f9b5998d5b29300c2623f61,com.google.android.apps.automotive.templates.host + 50e603d333c6049a37bd751375d08f3bd0abebd33facd30bd17b64b89658b421,com.google.android.apps.automotive.templates.host + + diff --git a/android/res/xml/automotive_app_desc.xml b/android/res/xml/automotive_app_desc.xml new file mode 100644 index 0000000000..83b683397c --- /dev/null +++ b/android/res/xml/automotive_app_desc.xml @@ -0,0 +1,4 @@ + + + + diff --git a/android/src/app/organicmaps/car/NavigationCarAppService.java b/android/src/app/organicmaps/car/NavigationCarAppService.java new file mode 100644 index 0000000000..a78529f294 --- /dev/null +++ b/android/src/app/organicmaps/car/NavigationCarAppService.java @@ -0,0 +1,46 @@ +package app.organicmaps.car; + +import android.content.pm.ApplicationInfo; + +import androidx.annotation.NonNull; +import androidx.car.app.CarAppService; +import androidx.car.app.Session; +import androidx.car.app.validation.HostValidator; + +import app.organicmaps.MwmApplication; +import app.organicmaps.R; + +import java.io.IOException; + +public final class NavigationCarAppService extends CarAppService +{ + @NonNull + @Override + public HostValidator createHostValidator() + { + if ((getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) + { + return HostValidator.ALLOW_ALL_HOSTS_VALIDATOR; + } + else + { + return new HostValidator.Builder(getApplicationContext()) + .addAllowedHosts(R.array.hosts_allowlist) + .build(); + } + } + + @NonNull + @Override + public Session onCreateSession() + { + try + { + MwmApplication.from(getApplicationContext()).init(); + } catch (IOException e) + { + e.printStackTrace(); + } + return new NavigationSession(); + } +} diff --git a/android/src/app/organicmaps/car/NavigationScreen.java b/android/src/app/organicmaps/car/NavigationScreen.java new file mode 100644 index 0000000000..012c1b4eef --- /dev/null +++ b/android/src/app/organicmaps/car/NavigationScreen.java @@ -0,0 +1,84 @@ +package app.organicmaps.car; + +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.car.app.CarContext; +import androidx.car.app.CarToast; +import androidx.car.app.Screen; +import androidx.car.app.model.Action; +import androidx.car.app.model.ActionStrip; +import androidx.car.app.model.CarIcon; +import androidx.car.app.model.Template; +import androidx.car.app.navigation.model.NavigationTemplate; +import androidx.core.graphics.drawable.IconCompat; + +import app.organicmaps.R; +import app.organicmaps.location.LocationHelper; +import app.organicmaps.util.LocationUtils; + +public class NavigationScreen extends Screen +{ + private static final String TAG = NavigationScreen.class.getSimpleName(); + + @NonNull + private final SurfaceRenderer mSurfaceRenderer; + + protected NavigationScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer) + { + super(carContext); + mSurfaceRenderer = surfaceRenderer; + } + + @NonNull + @Override + public Template onGetTemplate() + { + Log.d(TAG, "onGetTemplate"); + NavigationTemplate.Builder builder = new NavigationTemplate.Builder(); + ActionStrip.Builder actionStripBuilder = new ActionStrip.Builder(); + actionStripBuilder.addAction(new Action.Builder().setIcon(new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.ic_settings)).build()) + .setOnClickListener(this::settings) + .build()); + + Action panAction = new Action.Builder(Action.PAN).build(); + Action location = new Action.Builder().setIcon(new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.ic_not_follow)).build()) + .setOnClickListener(this::location) + .build(); + Action zoomIn = new Action.Builder().setIcon(new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.ic_plus)).build()) + .setOnClickListener(this::zoomIn) + .build(); + Action zoomOut = new Action.Builder().setIcon(new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.ic_minus)).build()) + .setOnClickListener(this::zoomOut) + .build(); + + ActionStrip mapActionStrip = new ActionStrip.Builder().addAction(location) + .addAction(zoomIn) + .addAction(zoomOut) + .addAction(panAction) + .build(); + builder.setMapActionStrip(mapActionStrip); + builder.setActionStrip(actionStripBuilder.build()); + return builder.build(); + } + + private void location() + { + CarToast.makeText(getCarContext(), "Location", CarToast.LENGTH_LONG).show(); + } + + private void zoomOut() + { + mSurfaceRenderer.onZoomOut(); + } + + private void zoomIn() + { + mSurfaceRenderer.onZoomIn(); + } + + private void settings() + { + CarToast.makeText(getCarContext(), "Settings", CarToast.LENGTH_LONG).show(); + } +} diff --git a/android/src/app/organicmaps/car/NavigationSession.java b/android/src/app/organicmaps/car/NavigationSession.java new file mode 100644 index 0000000000..85402fd110 --- /dev/null +++ b/android/src/app/organicmaps/car/NavigationSession.java @@ -0,0 +1,22 @@ +package app.organicmaps.car; + +import android.content.Intent; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.car.app.Screen; +import androidx.car.app.Session; + +public final class NavigationSession extends Session +{ + @Nullable + private SurfaceRenderer mNavigationSurface; + + @NonNull + @Override + public Screen onCreateScreen(@NonNull Intent intent) + { + mNavigationSurface = new SurfaceRenderer(getCarContext(), getLifecycle()); + return new NavigationScreen(getCarContext(), mNavigationSurface); + } +} diff --git a/android/src/app/organicmaps/car/SurfaceRenderer.java b/android/src/app/organicmaps/car/SurfaceRenderer.java new file mode 100644 index 0000000000..9b4692a21c --- /dev/null +++ b/android/src/app/organicmaps/car/SurfaceRenderer.java @@ -0,0 +1,175 @@ +package app.organicmaps.car; + +import android.graphics.Rect; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.car.app.AppManager; +import androidx.car.app.CarContext; +import androidx.car.app.CarToast; +import androidx.car.app.SurfaceCallback; +import androidx.car.app.SurfaceContainer; +import androidx.lifecycle.DefaultLifecycleObserver; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; + +import app.organicmaps.Map; +import app.organicmaps.R; + +public class SurfaceRenderer implements DefaultLifecycleObserver, SurfaceCallback +{ + private static final String TAG = SurfaceRenderer.class.getSimpleName(); + + private final CarContext mCarContext; + private final Map mMap; + + @Nullable + private Rect mVisibleArea; + @Nullable + private Rect mStableArea; + + public SurfaceRenderer(@NonNull CarContext carContext, @NonNull Lifecycle lifecycle) + { + Log.d(TAG, "SurfaceRenderer()"); + mCarContext = carContext; + mMap = new Map(); + lifecycle.addObserver(this); + } + + @Override + public void onSurfaceAvailable(@NonNull SurfaceContainer surfaceContainer) + { + Log.d(TAG, "Surface available " + surfaceContainer); + mMap.onSurfaceCreated( + mCarContext, + surfaceContainer.getSurface(), + new Rect(0, 0, surfaceContainer.getWidth(), surfaceContainer.getHeight()), + surfaceContainer.getDpi() + ); + } + + @Override + public void onVisibleAreaChanged(@NonNull Rect visibleArea) + { + Log.d(TAG, "Visible area changed. stableArea: " + mStableArea + " visibleArea:" + visibleArea); + mVisibleArea = visibleArea; + } + + @Override + public void onStableAreaChanged(@NonNull Rect stableArea) + { + Log.d(TAG, "Stable area changed. stableArea: " + mStableArea + " visibleArea:" + mVisibleArea); + mStableArea = stableArea; + } + + @Override + public void onSurfaceDestroyed(@NonNull SurfaceContainer surfaceContainer) + { + Log.d(TAG, "Surface destroyed"); + mMap.onSurfaceDestroyed(false, true); + } + + @Override + public void onCreate(@NonNull LifecycleOwner owner) + { + Log.d(TAG, "onCreate"); + mCarContext.getCarService(AppManager.class).setSurfaceCallback(this); + + boolean launchByDeepLink = false; + mMap.onCreate(launchByDeepLink); + } + + @Override + public void onStart(@NonNull LifecycleOwner owner) + { + Log.d(TAG, "onStart"); + mMap.onStart(); + mMap.setCallbackUnsupported(this::reportUnsupported); + } + + @Override + public void onStop(@NonNull LifecycleOwner owner) + { + Log.d(TAG, "onStop"); + mMap.onStop(); + mMap.setCallbackUnsupported(null); + } + + @Override + public void onPause(@NonNull LifecycleOwner owner) + { + Log.d(TAG, "onPause"); + mMap.onPause(mCarContext); + } + + @Override + public void onResume(@NonNull LifecycleOwner owner) + { + Log.d(TAG, "onResume"); + mMap.onResume(); + } + + @Override + public void onScroll(float distanceX, float distanceY) + { + Log.d(TAG, "onScroll: distanceX: " + distanceX + ", distanceY: " + distanceY); + mMap.onScroll(distanceX, distanceY); + } + + @Override + public void onFling(float velocityX, float velocityY) + { + Log.d(TAG, "onFling: velocityX: " + velocityX + ", velocityY: " + velocityY); + } + + public void onZoomIn() + { + Map.zoomIn(); + } + + public void onZoomOut() + { + Map.zoomOut(); + } + + @Override + public void onScale(float focusX, float focusY, float scaleFactor) + { + Log.d(TAG, "onScale: focusX: " + focusX + ", focusY: " + focusY + ", scaleFactor: " + scaleFactor); + float x = focusX; + float y = focusY; + + Rect visibleArea = mVisibleArea; + if (visibleArea != null) + { + // If a focal point value is negative, use the center point of the visible area. + if (x < 0) + { + x = visibleArea.centerX(); + } + if (y < 0) + { + y = visibleArea.centerY(); + } + } + + boolean animated = Float.compare(scaleFactor, 2f) == 0; + + Map.onScale(scaleFactor, x, y, animated); + } + + @Override + public void onClick(float x, float y) + { + Log.d(TAG, "onClick: x: " + x + ", y: " + y); + Map.onTouch(x, y); + } + + private void reportUnsupported() + { + String message = mCarContext.getString(R.string.unsupported_phone); + Log.e(TAG, mCarContext.getString(R.string.unsupported_phone)); + CarToast.makeText(mCarContext, message, CarToast.LENGTH_LONG).show(); + } +} -- 2.45.3 From ba62a208c4ce34736a2efaf5340b12b431551e7d Mon Sep 17 00:00:00 2001 From: Andrew Shkrob Date: Sun, 27 Nov 2022 01:41:51 +0100 Subject: [PATCH 2/7] [android-auto] Add Screens Add Settings, Bookmarks, Categories, Search screens. Signed-off-by: Andrew Shkrob --- android/res/drawable/ic_check_box.xml | 5 + android/res/drawable/ic_check_box_checked.xml | 5 + .../car/NavigationCarAppService.java | 10 -- .../app/organicmaps/car/NavigationScreen.java | 84 ----------- .../organicmaps/car/NavigationSession.java | 112 +++++++++++++- .../src/app/organicmaps/car/OMController.java | 40 +++++ .../src/app/organicmaps/car/UiHelpers.java | 80 ++++++++++ .../car/screens/BookmarksScreen.java | 140 ++++++++++++++++++ .../car/screens/CategoriesScreen.java | 93 ++++++++++++ .../organicmaps/car/screens/ErrorScreen.java | 32 ++++ .../organicmaps/car/screens/MapScreen.java | 46 ++++++ .../car/screens/NavigationScreen.java | 103 +++++++++++++ .../organicmaps/car/screens/SearchScreen.java | 61 ++++++++ .../settings/DrivingOptionsScreen.java | 54 +++++++ .../car/screens/settings/HelpScreen.java | 73 +++++++++ .../car/screens/settings/SettingsScreen.java | 77 ++++++++++ 16 files changed, 915 insertions(+), 100 deletions(-) create mode 100644 android/res/drawable/ic_check_box.xml create mode 100644 android/res/drawable/ic_check_box_checked.xml delete mode 100644 android/src/app/organicmaps/car/NavigationScreen.java create mode 100644 android/src/app/organicmaps/car/OMController.java create mode 100644 android/src/app/organicmaps/car/UiHelpers.java create mode 100644 android/src/app/organicmaps/car/screens/BookmarksScreen.java create mode 100644 android/src/app/organicmaps/car/screens/CategoriesScreen.java create mode 100644 android/src/app/organicmaps/car/screens/ErrorScreen.java create mode 100644 android/src/app/organicmaps/car/screens/MapScreen.java create mode 100644 android/src/app/organicmaps/car/screens/NavigationScreen.java create mode 100644 android/src/app/organicmaps/car/screens/SearchScreen.java create mode 100644 android/src/app/organicmaps/car/screens/settings/DrivingOptionsScreen.java create mode 100644 android/src/app/organicmaps/car/screens/settings/HelpScreen.java create mode 100644 android/src/app/organicmaps/car/screens/settings/SettingsScreen.java diff --git a/android/res/drawable/ic_check_box.xml b/android/res/drawable/ic_check_box.xml new file mode 100644 index 0000000000..50101a198b --- /dev/null +++ b/android/res/drawable/ic_check_box.xml @@ -0,0 +1,5 @@ + + + diff --git a/android/res/drawable/ic_check_box_checked.xml b/android/res/drawable/ic_check_box_checked.xml new file mode 100644 index 0000000000..5c0babf540 --- /dev/null +++ b/android/res/drawable/ic_check_box_checked.xml @@ -0,0 +1,5 @@ + + + diff --git a/android/src/app/organicmaps/car/NavigationCarAppService.java b/android/src/app/organicmaps/car/NavigationCarAppService.java index a78529f294..4e4c2593b6 100644 --- a/android/src/app/organicmaps/car/NavigationCarAppService.java +++ b/android/src/app/organicmaps/car/NavigationCarAppService.java @@ -7,11 +7,8 @@ import androidx.car.app.CarAppService; import androidx.car.app.Session; import androidx.car.app.validation.HostValidator; -import app.organicmaps.MwmApplication; import app.organicmaps.R; -import java.io.IOException; - public final class NavigationCarAppService extends CarAppService { @NonNull @@ -34,13 +31,6 @@ public final class NavigationCarAppService extends CarAppService @Override public Session onCreateSession() { - try - { - MwmApplication.from(getApplicationContext()).init(); - } catch (IOException e) - { - e.printStackTrace(); - } return new NavigationSession(); } } diff --git a/android/src/app/organicmaps/car/NavigationScreen.java b/android/src/app/organicmaps/car/NavigationScreen.java deleted file mode 100644 index 012c1b4eef..0000000000 --- a/android/src/app/organicmaps/car/NavigationScreen.java +++ /dev/null @@ -1,84 +0,0 @@ -package app.organicmaps.car; - -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.car.app.CarContext; -import androidx.car.app.CarToast; -import androidx.car.app.Screen; -import androidx.car.app.model.Action; -import androidx.car.app.model.ActionStrip; -import androidx.car.app.model.CarIcon; -import androidx.car.app.model.Template; -import androidx.car.app.navigation.model.NavigationTemplate; -import androidx.core.graphics.drawable.IconCompat; - -import app.organicmaps.R; -import app.organicmaps.location.LocationHelper; -import app.organicmaps.util.LocationUtils; - -public class NavigationScreen extends Screen -{ - private static final String TAG = NavigationScreen.class.getSimpleName(); - - @NonNull - private final SurfaceRenderer mSurfaceRenderer; - - protected NavigationScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer) - { - super(carContext); - mSurfaceRenderer = surfaceRenderer; - } - - @NonNull - @Override - public Template onGetTemplate() - { - Log.d(TAG, "onGetTemplate"); - NavigationTemplate.Builder builder = new NavigationTemplate.Builder(); - ActionStrip.Builder actionStripBuilder = new ActionStrip.Builder(); - actionStripBuilder.addAction(new Action.Builder().setIcon(new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.ic_settings)).build()) - .setOnClickListener(this::settings) - .build()); - - Action panAction = new Action.Builder(Action.PAN).build(); - Action location = new Action.Builder().setIcon(new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.ic_not_follow)).build()) - .setOnClickListener(this::location) - .build(); - Action zoomIn = new Action.Builder().setIcon(new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.ic_plus)).build()) - .setOnClickListener(this::zoomIn) - .build(); - Action zoomOut = new Action.Builder().setIcon(new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.ic_minus)).build()) - .setOnClickListener(this::zoomOut) - .build(); - - ActionStrip mapActionStrip = new ActionStrip.Builder().addAction(location) - .addAction(zoomIn) - .addAction(zoomOut) - .addAction(panAction) - .build(); - builder.setMapActionStrip(mapActionStrip); - builder.setActionStrip(actionStripBuilder.build()); - return builder.build(); - } - - private void location() - { - CarToast.makeText(getCarContext(), "Location", CarToast.LENGTH_LONG).show(); - } - - private void zoomOut() - { - mSurfaceRenderer.onZoomOut(); - } - - private void zoomIn() - { - mSurfaceRenderer.onZoomIn(); - } - - private void settings() - { - CarToast.makeText(getCarContext(), "Settings", CarToast.LENGTH_LONG).show(); - } -} diff --git a/android/src/app/organicmaps/car/NavigationSession.java b/android/src/app/organicmaps/car/NavigationSession.java index 85402fd110..1cbeffbcb3 100644 --- a/android/src/app/organicmaps/car/NavigationSession.java +++ b/android/src/app/organicmaps/car/NavigationSession.java @@ -1,22 +1,122 @@ package app.organicmaps.car; import android.content.Intent; +import android.util.Log; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; +import androidx.car.app.CarToast; import androidx.car.app.Screen; +import androidx.car.app.ScreenManager; import androidx.car.app.Session; +import androidx.car.app.model.Action; +import androidx.car.app.model.ActionStrip; +import androidx.car.app.model.CarIcon; +import androidx.car.app.navigation.model.MapController; +import androidx.core.graphics.drawable.IconCompat; +import androidx.lifecycle.DefaultLifecycleObserver; +import androidx.lifecycle.LifecycleOwner; -public final class NavigationSession extends Session +import app.organicmaps.MwmApplication; +import app.organicmaps.R; +import app.organicmaps.car.screens.ErrorScreen; +import app.organicmaps.car.screens.NavigationScreen; +import app.organicmaps.car.screens.settings.SettingsScreen; + +import java.io.IOException; + +public final class NavigationSession extends Session implements DefaultLifecycleObserver { - @Nullable - private SurfaceRenderer mNavigationSurface; + private static final String TAG = NavigationSession.class.getSimpleName(); + + + private OMController mMapController; + + boolean mInitFailed = false; + + public NavigationSession() + { + getLifecycle().addObserver(this); + } @NonNull @Override public Screen onCreateScreen(@NonNull Intent intent) { - mNavigationSurface = new SurfaceRenderer(getCarContext(), getLifecycle()); - return new NavigationScreen(getCarContext(), mNavigationSurface); + Log.d(TAG, "onCreateScreen()"); + if (mInitFailed) + return new ErrorScreen(getCarContext()); + + createMapController(); + return new NavigationScreen(getCarContext(), mMapController); + } + + @Override + public void onCreate(@NonNull LifecycleOwner owner) + { + Log.d(TAG, "onCreate()"); + init(); + } + + @Override + public void onResume(@NonNull LifecycleOwner owner) + { + Log.d(TAG, "onResume()"); + init(); + } + + private void init() + { + mInitFailed = false; + MwmApplication app = MwmApplication.from(getCarContext()); + try + { + app.init(); + } catch (IOException e) + { + mInitFailed = true; + Log.e(TAG, "Failed to initialize the app."); + } + } + + private void createMapController() + { + final CarIcon iconPlus = new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.ic_plus)).build(); + final CarIcon iconMinus = new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.ic_minus)).build(); + final CarIcon iconLocation = new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.ic_not_follow)).build(); + final CarIcon iconSettings = new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.ic_settings)).build(); + + SurfaceRenderer surfaceRenderer = new SurfaceRenderer(getCarContext(), getLifecycle()); + + Action panAction = new Action.Builder(Action.PAN).build(); + Action location = new Action.Builder().setIcon(iconLocation).setOnClickListener(this::location).build(); + Action zoomIn = new Action.Builder().setIcon(iconPlus).setOnClickListener(this::zoomIn).build(); + Action zoomOut = new Action.Builder().setIcon(iconMinus).setOnClickListener(this::zoomOut).build(); + ActionStrip mapActionStrip = new ActionStrip.Builder().addAction(location).addAction(zoomIn).addAction(zoomOut).addAction(panAction).build(); + MapController mapController = new MapController.Builder().setMapActionStrip(mapActionStrip).build(); + + Action settings = new Action.Builder().setIcon(iconSettings).setOnClickListener(this::openSettings).build(); + ActionStrip actionStrip = new ActionStrip.Builder().addAction(settings).build(); + + mMapController = new OMController(surfaceRenderer, mapController, actionStrip); + } + + private void location() + { + CarToast.makeText(getCarContext(), "Location", CarToast.LENGTH_LONG).show(); + } + + private void zoomOut() + { + mMapController.getSurfaceRenderer().onZoomOut(); + } + + private void zoomIn() + { + mMapController.getSurfaceRenderer().onZoomIn(); + } + + private void openSettings() + { + getCarContext().getCarService(ScreenManager.class).push(new SettingsScreen(getCarContext(), mMapController)); } } diff --git a/android/src/app/organicmaps/car/OMController.java b/android/src/app/organicmaps/car/OMController.java new file mode 100644 index 0000000000..61b05f646d --- /dev/null +++ b/android/src/app/organicmaps/car/OMController.java @@ -0,0 +1,40 @@ +package app.organicmaps.car; + +import androidx.annotation.NonNull; +import androidx.car.app.model.ActionStrip; +import androidx.car.app.navigation.model.MapController; + +public class OMController +{ + @NonNull + private final SurfaceRenderer mSurfaceRenderer; + @NonNull + private final MapController mMapController; + @NonNull + private final ActionStrip mActionStrip; + + public OMController(@NonNull SurfaceRenderer surfaceRenderer, @NonNull MapController mapController, @NonNull ActionStrip actionStrip) + { + mSurfaceRenderer = surfaceRenderer; + mMapController = mapController; + mActionStrip = actionStrip; + } + + @NonNull + public SurfaceRenderer getSurfaceRenderer() + { + return mSurfaceRenderer; + } + + @NonNull + public MapController getMapController() + { + return mMapController; + } + + @NonNull + public ActionStrip getActionStrip() + { + return mActionStrip; + } +} diff --git a/android/src/app/organicmaps/car/UiHelpers.java b/android/src/app/organicmaps/car/UiHelpers.java new file mode 100644 index 0000000000..89497899c3 --- /dev/null +++ b/android/src/app/organicmaps/car/UiHelpers.java @@ -0,0 +1,80 @@ +package app.organicmaps.car; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; +import androidx.car.app.CarContext; +import androidx.car.app.ScreenManager; +import androidx.car.app.model.CarIcon; +import androidx.car.app.model.Row; +import androidx.core.graphics.drawable.IconCompat; + +import app.organicmaps.R; +import app.organicmaps.routing.RoutingOptions; +import app.organicmaps.settings.RoadType; + +public final class UiHelpers +{ + public interface PrefsGetter + { + boolean get(); + } + + public interface PrefsSetter + { + void set(boolean newValue); + } + + @Nullable + private static CarIcon mCheckboxIcon; + @Nullable + private static CarIcon mCheckboxSelectedIcon; + + @NonNull + public static Row createSharedPrefsCheckbox( + @NonNull CarContext context, @StringRes int titleRes, PrefsGetter getter, PrefsSetter setter) + { + if (mCheckboxIcon == null || mCheckboxSelectedIcon == null) + initCheckboxIcons(context); + Row.Builder builder = new Row.Builder(); + builder.setTitle(context.getString(titleRes)); + builder.setOnClickListener(() -> { + setter.set(!getter.get()); + context.getCarService(ScreenManager.class).getTop().invalidate(); + }); + if (getter.get()) + builder.setImage(mCheckboxSelectedIcon); + else + builder.setImage(mCheckboxIcon); + + return builder.build(); + } + + @NonNull + public static Row createDrivingOptionCheckbox( + @NonNull CarContext context, RoadType roadType, @StringRes int titleRes) + { + if (mCheckboxIcon == null || mCheckboxSelectedIcon == null) + initCheckboxIcons(context); + Row.Builder builder = new Row.Builder(); + builder.setTitle(context.getString(titleRes)); + builder.setOnClickListener(() -> { + if (RoutingOptions.hasOption(roadType)) + RoutingOptions.removeOption(roadType); + else + RoutingOptions.addOption(roadType); + context.getCarService(ScreenManager.class).getTop().invalidate(); + }); + if (RoutingOptions.hasOption(roadType)) + builder.setImage(mCheckboxSelectedIcon); + else + builder.setImage(mCheckboxIcon); + return builder.build(); + } + + private static void initCheckboxIcons(@NonNull CarContext context) + { + mCheckboxIcon = new CarIcon.Builder(IconCompat.createWithResource(context, R.drawable.ic_check_box)).build(); + mCheckboxSelectedIcon = new CarIcon.Builder(IconCompat.createWithResource(context, R.drawable.ic_check_box_checked)).build(); + } +} diff --git a/android/src/app/organicmaps/car/screens/BookmarksScreen.java b/android/src/app/organicmaps/car/screens/BookmarksScreen.java new file mode 100644 index 0000000000..4e9b36ea4f --- /dev/null +++ b/android/src/app/organicmaps/car/screens/BookmarksScreen.java @@ -0,0 +1,140 @@ +package app.organicmaps.car.screens; + +import android.graphics.drawable.Drawable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.car.app.CarContext; +import androidx.car.app.constraints.ConstraintManager; +import androidx.car.app.model.Action; +import androidx.car.app.model.CarIcon; +import androidx.car.app.model.Header; +import androidx.car.app.model.ItemList; +import androidx.car.app.model.Row; +import androidx.car.app.model.Template; +import androidx.car.app.navigation.model.MapTemplate; +import androidx.core.graphics.drawable.IconCompat; + +import app.organicmaps.R; +import app.organicmaps.bookmarks.data.BookmarkCategory; +import app.organicmaps.bookmarks.data.BookmarkInfo; +import app.organicmaps.bookmarks.data.BookmarkManager; +import app.organicmaps.car.OMController; +import app.organicmaps.util.Graphics; + +import java.util.ArrayList; +import java.util.List; + +public class BookmarksScreen extends MapScreen +{ + private final int MAX_CATEGORIES_SIZE; + + @Nullable + private BookmarkCategory mBookmarkCategory; + + public BookmarksScreen(@NonNull CarContext carContext, @NonNull OMController mapController) + { + super(carContext, mapController); + MAX_CATEGORIES_SIZE = getCarContext().getCarService(ConstraintManager.class).getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_LIST); + } + + private BookmarksScreen(@NonNull CarContext carContext, @NonNull OMController mapController, @NonNull BookmarkCategory bookmarkCategory) + { + this(carContext, mapController); + mBookmarkCategory = bookmarkCategory; + } + + @NonNull + @Override + public Template onGetTemplate() + { + + MapTemplate.Builder builder = new MapTemplate.Builder(); + builder.setHeader(createHeader()); + builder.setMapController(getMapController()); + builder.setActionStrip(getActionStrip()); + if (mBookmarkCategory == null) + builder.setItemList(createBookmarkCategoriesList()); + else + builder.setItemList(createBookmarksList()); + return builder.build(); + } + + @NonNull + private Header createHeader() + { + Header.Builder builder = new Header.Builder(); + builder.setStartHeaderAction(Action.BACK); + if (mBookmarkCategory == null) + builder.setTitle(getCarContext().getString(R.string.bookmarks)); + else + builder.setTitle(mBookmarkCategory.getName()); + return builder.build(); + } + + @NonNull + private ItemList createBookmarkCategoriesList() + { + final List bookmarkCategories = getBookmarks(); + final int categoriesSize = Math.min(bookmarkCategories.size(), MAX_CATEGORIES_SIZE); + + ItemList.Builder builder = new ItemList.Builder(); + for (int i = 0; i < categoriesSize; ++i) + { + final BookmarkCategory bookmarkCategory = bookmarkCategories.get(i); + + Row.Builder itemBuilder = new Row.Builder(); + itemBuilder.setTitle(bookmarkCategory.getName()); + itemBuilder.addText(bookmarkCategory.getDescription()); + itemBuilder.setOnClickListener(() -> getScreenManager().push(new BookmarksScreen(getCarContext(), getOMController(), bookmarkCategory))); + itemBuilder.setBrowsable(true); + builder.addItem(itemBuilder.build()); + } + return builder.build(); + } + + @NonNull + private ItemList createBookmarksList() + { + final long bookmarkCategoryId = mBookmarkCategory.getId(); + final int bookmarkCategoriesSize = Math.min(mBookmarkCategory.getBookmarksCount(), MAX_CATEGORIES_SIZE); + + ItemList.Builder builder = new ItemList.Builder(); + for (int i = 0; i < bookmarkCategoriesSize; ++i) + { + final long bookmarkId = BookmarkManager.INSTANCE.getBookmarkIdByPosition(bookmarkCategoryId, i); + final BookmarkInfo bookmarkInfo = new BookmarkInfo(bookmarkCategoryId, bookmarkId); + + Row.Builder itemBuilder = new Row.Builder(); + itemBuilder.setTitle(bookmarkInfo.getName()); + if (!bookmarkInfo.getAddress().isEmpty()) + itemBuilder.addText(bookmarkInfo.getAddress()); + if (!bookmarkInfo.getFeatureType().isEmpty()) + itemBuilder.addText(bookmarkInfo.getFeatureType()); + final Drawable icon = Graphics.drawCircleAndImage(bookmarkInfo.getIcon().argb(), + R.dimen.track_circle_size, + bookmarkInfo.getIcon().getResId(), + R.dimen.bookmark_icon_size, + getCarContext()); + itemBuilder.setImage(new CarIcon.Builder(IconCompat.createWithBitmap(Graphics.drawableToBitmap(icon))).build()); + builder.addItem(itemBuilder.build()); + } + return builder.build(); + } + + @NonNull + private static List getBookmarks() + { + List bookmarkCategories = new ArrayList<>(BookmarkManager.INSTANCE.getCategories()); + + List toRemove = new ArrayList<>(); + for (BookmarkCategory bookmarkCategory : bookmarkCategories) + { + if (bookmarkCategory.getBookmarksCount() == 0) + toRemove.add(bookmarkCategory); + } + bookmarkCategories.removeAll(toRemove); + + return bookmarkCategories; + } +} diff --git a/android/src/app/organicmaps/car/screens/CategoriesScreen.java b/android/src/app/organicmaps/car/screens/CategoriesScreen.java new file mode 100644 index 0000000000..4bd74ced23 --- /dev/null +++ b/android/src/app/organicmaps/car/screens/CategoriesScreen.java @@ -0,0 +1,93 @@ +package app.organicmaps.car.screens; + +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; +import androidx.car.app.CarContext; +import androidx.car.app.constraints.ConstraintManager; +import androidx.car.app.model.Action; +import androidx.car.app.model.CarIcon; +import androidx.car.app.model.Header; +import androidx.car.app.model.ItemList; +import androidx.car.app.model.Row; +import androidx.car.app.model.Template; +import androidx.car.app.navigation.model.MapTemplate; +import androidx.core.graphics.drawable.IconCompat; + +import app.organicmaps.R; +import app.organicmaps.car.OMController; + +import java.util.Arrays; +import java.util.List; + +public class CategoriesScreen extends MapScreen +{ + private static class CategoryData + { + @StringRes + public final int nameResId; + + @DrawableRes + public final int iconResId; + + public CategoryData(int nameResId, int iconResId) + { + this.nameResId = nameResId; + this.iconResId = iconResId; + } + } + + private static final List CATEGORIES = Arrays.asList( + new CategoryData(R.string.fuel, R.drawable.ic_category_fuel), + new CategoryData(R.string.parking, R.drawable.ic_category_parking), + new CategoryData(R.string.eat, R.drawable.ic_category_eat), + new CategoryData(R.string.food, R.drawable.ic_category_food), + new CategoryData(R.string.hotel, R.drawable.ic_category_hotel), + new CategoryData(R.string.toilet, R.drawable.ic_category_toilet), + new CategoryData(R.string.rv, R.drawable.ic_category_rv) + ); + + private final int MAX_CATEGORIES_SIZE; + + public CategoriesScreen(@NonNull CarContext carContext, @NonNull OMController mapController) + { + super(carContext, mapController); + MAX_CATEGORIES_SIZE = getCarContext().getCarService(ConstraintManager.class).getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_LIST); + } + + @NonNull + @Override + public Template onGetTemplate() + { + MapTemplate.Builder builder = new MapTemplate.Builder(); + builder.setHeader(createHeader()); + builder.setMapController(getMapController()); + builder.setActionStrip(getActionStrip()); + builder.setItemList(createCategoriesList()); + return builder.build(); + } + + @NonNull + private Header createHeader() + { + Header.Builder builder = new Header.Builder(); + builder.setStartHeaderAction(Action.BACK); + builder.setTitle(getCarContext().getString(R.string.categories)); + return builder.build(); + } + + @NonNull + private ItemList createCategoriesList() + { + ItemList.Builder builder = new ItemList.Builder(); + int categoriesSize = Math.min(CATEGORIES.size(), MAX_CATEGORIES_SIZE); + for (int i = 0; i < categoriesSize; ++i) + { + Row.Builder itemBuilder = new Row.Builder(); + itemBuilder.setTitle(getCarContext().getString(CATEGORIES.get(i).nameResId)); + itemBuilder.setImage(new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), CATEGORIES.get(i).iconResId)).build()); + builder.addItem(itemBuilder.build()); + } + return builder.build(); + } +} diff --git a/android/src/app/organicmaps/car/screens/ErrorScreen.java b/android/src/app/organicmaps/car/screens/ErrorScreen.java new file mode 100644 index 0000000000..bcdaf8525f --- /dev/null +++ b/android/src/app/organicmaps/car/screens/ErrorScreen.java @@ -0,0 +1,32 @@ +package app.organicmaps.car.screens; + +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.car.app.CarContext; +import androidx.car.app.Screen; +import androidx.car.app.model.LongMessageTemplate; +import androidx.car.app.model.Template; + +import app.organicmaps.R; + +public class ErrorScreen extends Screen +{ + private static final String TAG = ErrorScreen.class.getSimpleName(); + + public ErrorScreen(@NonNull CarContext carContext) + { + super(carContext); + } + + @NonNull + @Override + public Template onGetTemplate() + { + Log.d(TAG, "onGetTemplate"); + LongMessageTemplate.Builder builder = new LongMessageTemplate.Builder(getCarContext().getString(R.string.dialog_error_storage_message)); + builder.setTitle(getCarContext().getString(R.string.dialog_error_storage_title)); + + return builder.build(); + } +} diff --git a/android/src/app/organicmaps/car/screens/MapScreen.java b/android/src/app/organicmaps/car/screens/MapScreen.java new file mode 100644 index 0000000000..5876ee897c --- /dev/null +++ b/android/src/app/organicmaps/car/screens/MapScreen.java @@ -0,0 +1,46 @@ +package app.organicmaps.car.screens; + +import androidx.annotation.NonNull; +import androidx.car.app.CarContext; +import androidx.car.app.Screen; +import androidx.car.app.model.ActionStrip; +import androidx.car.app.navigation.model.MapController; + +import app.organicmaps.car.OMController; +import app.organicmaps.car.SurfaceRenderer; + +public abstract class MapScreen extends Screen +{ + @NonNull + private final OMController mMapController; + + public MapScreen(@NonNull CarContext carContext, @NonNull OMController mapController) + { + super(carContext); + mMapController = mapController; + } + + @NonNull + public OMController getOMController() + { + return mMapController; + } + + @NonNull + public SurfaceRenderer getSurfaceRenderer() + { + return mMapController.getSurfaceRenderer(); + } + + @NonNull + public MapController getMapController() + { + return mMapController.getMapController(); + } + + @NonNull + public ActionStrip getActionStrip() + { + return mMapController.getActionStrip(); + } +} diff --git a/android/src/app/organicmaps/car/screens/NavigationScreen.java b/android/src/app/organicmaps/car/screens/NavigationScreen.java new file mode 100644 index 0000000000..36e4225268 --- /dev/null +++ b/android/src/app/organicmaps/car/screens/NavigationScreen.java @@ -0,0 +1,103 @@ +package app.organicmaps.car.screens; + +import androidx.annotation.NonNull; +import androidx.car.app.CarContext; +import androidx.car.app.model.Action; +import androidx.car.app.model.CarIcon; +import androidx.car.app.model.Header; +import androidx.car.app.model.Item; +import androidx.car.app.model.ItemList; +import androidx.car.app.model.Row; +import androidx.car.app.model.Template; +import androidx.car.app.navigation.model.MapTemplate; +import androidx.core.graphics.drawable.IconCompat; + +import app.organicmaps.R; +import app.organicmaps.car.OMController; + +public class NavigationScreen extends MapScreen +{ + public NavigationScreen(@NonNull CarContext carContext, @NonNull OMController mapController) + { + super(carContext, mapController); + } + + @NonNull + @Override + public Template onGetTemplate() + { + MapTemplate.Builder builder = new MapTemplate.Builder(); + builder.setHeader(createHeader()); + builder.setMapController(getMapController()); + builder.setActionStrip(getActionStrip()); + builder.setItemList(createList()); + return builder.build(); + } + + @NonNull + private Header createHeader() + { + Header.Builder builder = new Header.Builder(); + builder.setStartHeaderAction(new Action.Builder(Action.APP_ICON).build()); + builder.setTitle(getCarContext().getString(R.string.app_name)); + return builder.build(); + } + + @NonNull + private ItemList createList() + { + ItemList.Builder builder = new ItemList.Builder(); + builder.addItem(createSearchItem()); + builder.addItem(createCategoriesItem()); + builder.addItem(createBookmarksItem()); + return builder.build(); + } + + @NonNull + private Item createSearchItem() + { + final CarIcon iconSearch = new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.ic_search)).build(); + + Row.Builder builder = new Row.Builder(); + builder.setTitle(getCarContext().getString(R.string.search)); + builder.setImage(iconSearch); + builder.setBrowsable(true); + builder.setOnClickListener(this::openSearch); + return builder.build(); + } + + @NonNull + private Item createCategoriesItem() + { + Row.Builder builder = new Row.Builder(); + builder.setTitle(getCarContext().getString(R.string.categories)); + builder.setBrowsable(true); + builder.setOnClickListener(this::openCategories); + return builder.build(); + } + + @NonNull + private Item createBookmarksItem() + { + Row.Builder builder = new Row.Builder(); + builder.setTitle(getCarContext().getString(R.string.bookmarks)); + builder.setBrowsable(true); + builder.setOnClickListener(this::openBookmarks); + return builder.build(); + } + + private void openSearch() + { + getScreenManager().push(new SearchScreen(getCarContext())); + } + + private void openCategories() + { + getScreenManager().push(new CategoriesScreen(getCarContext(), getOMController())); + } + + private void openBookmarks() + { + getScreenManager().push(new BookmarksScreen(getCarContext(), getOMController())); + } +} diff --git a/android/src/app/organicmaps/car/screens/SearchScreen.java b/android/src/app/organicmaps/car/screens/SearchScreen.java new file mode 100644 index 0000000000..d461d5cf30 --- /dev/null +++ b/android/src/app/organicmaps/car/screens/SearchScreen.java @@ -0,0 +1,61 @@ +package app.organicmaps.car.screens; + +import androidx.annotation.NonNull; +import androidx.car.app.CarContext; +import androidx.car.app.Screen; +import androidx.car.app.constraints.ConstraintManager; +import androidx.car.app.model.Action; +import androidx.car.app.model.CarIcon; +import androidx.car.app.model.ItemList; +import androidx.car.app.model.Row; +import androidx.car.app.model.SearchTemplate; +import androidx.car.app.model.Template; +import androidx.core.graphics.drawable.IconCompat; + +import app.organicmaps.R; +import app.organicmaps.search.SearchRecents; + +public class SearchScreen extends Screen implements SearchTemplate.SearchCallback +{ + private final int MAX_RESULTS_SIZE; + private ItemList mResults; + private String mSearchText = ""; + + public SearchScreen(@NonNull CarContext carContext) + { + super(carContext); + MAX_RESULTS_SIZE = getCarContext().getCarService(ConstraintManager.class).getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_LIST); + } + + @NonNull + @Override + public Template onGetTemplate() + { + SearchTemplate.Builder builder = new SearchTemplate.Builder(this); + builder.setHeaderAction(Action.BACK); + builder.setShowKeyboardByDefault(false); + if (mSearchText.isEmpty() || mResults == null) + loadRecents(); + builder.setItemList(mResults); + builder.setInitialSearchText(mSearchText); + return builder.build(); + } + + private void loadRecents() + { + final CarIcon iconRecent = new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.ic_search_recent)).build(); + + ItemList.Builder builder = new ItemList.Builder(); + builder.setNoItemsMessage(getCarContext().getString(R.string.search_history_text)); + SearchRecents.refresh(); + int recentsSize = Math.min(SearchRecents.getSize(), MAX_RESULTS_SIZE); + for (int i = 0; i < recentsSize; ++i) + { + Row.Builder itemBuilder = new Row.Builder(); + itemBuilder.setTitle(SearchRecents.get(i)); + itemBuilder.setImage(iconRecent); + builder.addItem(itemBuilder.build()); + } + mResults = builder.build(); + } +} diff --git a/android/src/app/organicmaps/car/screens/settings/DrivingOptionsScreen.java b/android/src/app/organicmaps/car/screens/settings/DrivingOptionsScreen.java new file mode 100644 index 0000000000..dc35a0b886 --- /dev/null +++ b/android/src/app/organicmaps/car/screens/settings/DrivingOptionsScreen.java @@ -0,0 +1,54 @@ +package app.organicmaps.car.screens.settings; + +import androidx.annotation.NonNull; +import androidx.car.app.CarContext; +import androidx.car.app.model.Action; +import androidx.car.app.model.Header; +import androidx.car.app.model.ItemList; +import androidx.car.app.model.Template; +import androidx.car.app.navigation.model.MapTemplate; + +import app.organicmaps.R; +import app.organicmaps.car.OMController; +import app.organicmaps.car.UiHelpers; +import app.organicmaps.car.screens.MapScreen; +import app.organicmaps.settings.RoadType; + +public class DrivingOptionsScreen extends MapScreen +{ + public DrivingOptionsScreen(@NonNull CarContext carContext, @NonNull OMController mapController) + { + super(carContext, mapController); + } + + @NonNull + @Override + public Template onGetTemplate() + { + MapTemplate.Builder builder = new MapTemplate.Builder(); + builder.setHeader(createHeader()); + builder.setMapController(getMapController()); + builder.setItemList(createDrivingOptionsList()); + return builder.build(); + } + + @NonNull + private Header createHeader() + { + Header.Builder builder = new Header.Builder(); + builder.setStartHeaderAction(Action.BACK); + builder.setTitle(getCarContext().getString(R.string.driving_options_subheader)); + return builder.build(); + } + + @NonNull + private ItemList createDrivingOptionsList() + { + ItemList.Builder builder = new ItemList.Builder(); + builder.addItem(UiHelpers.createDrivingOptionCheckbox(getCarContext(), RoadType.Toll, R.string.avoid_tolls)); + builder.addItem(UiHelpers.createDrivingOptionCheckbox(getCarContext(), RoadType.Dirty, R.string.avoid_unpaved)); + builder.addItem(UiHelpers.createDrivingOptionCheckbox(getCarContext(), RoadType.Ferry, R.string.avoid_ferry)); + builder.addItem(UiHelpers.createDrivingOptionCheckbox(getCarContext(), RoadType.Motorway, R.string.avoid_motorways)); + return builder.build(); + } +} diff --git a/android/src/app/organicmaps/car/screens/settings/HelpScreen.java b/android/src/app/organicmaps/car/screens/settings/HelpScreen.java new file mode 100644 index 0000000000..6a02678e04 --- /dev/null +++ b/android/src/app/organicmaps/car/screens/settings/HelpScreen.java @@ -0,0 +1,73 @@ +package app.organicmaps.car.screens.settings; + +import androidx.annotation.NonNull; +import androidx.car.app.CarContext; +import androidx.car.app.model.Action; +import androidx.car.app.model.Header; +import androidx.car.app.model.Item; +import androidx.car.app.model.ItemList; +import androidx.car.app.model.Row; +import androidx.car.app.model.Template; +import androidx.car.app.navigation.model.MapTemplate; + +import app.organicmaps.BuildConfig; +import app.organicmaps.Framework; +import app.organicmaps.R; +import app.organicmaps.car.OMController; +import app.organicmaps.car.screens.MapScreen; +import app.organicmaps.util.DateUtils; + +public class HelpScreen extends MapScreen +{ + public HelpScreen(@NonNull CarContext carContext, @NonNull OMController mapController) + { + super(carContext, mapController); + } + + @NonNull + @Override + public Template onGetTemplate() + { + MapTemplate.Builder builder = new MapTemplate.Builder(); + builder.setHeader(createHeader()); + builder.setMapController(getMapController()); + builder.setItemList(createSettingsList()); + return builder.build(); + } + + @NonNull + private Header createHeader() + { + Header.Builder builder = new Header.Builder(); + builder.setStartHeaderAction(Action.BACK); + builder.setTitle(getCarContext().getString(R.string.help)); + return builder.build(); + } + + @NonNull + private ItemList createSettingsList() + { + ItemList.Builder builder = new ItemList.Builder(); + builder.addItem(createVersionInfo()); + builder.addItem(createDataVersionInfo()); + return builder.build(); + } + + @NonNull + private Item createVersionInfo() + { + return new Row.Builder() + .setTitle(getCarContext().getString(R.string.app_name)) + .addText(BuildConfig.VERSION_NAME) + .build(); + } + + @NonNull + private Item createDataVersionInfo() + { + return new Row.Builder() + .setTitle(getCarContext().getString(R.string.data_version, "")) + .addText(DateUtils.getLocalDate(Framework.nativeGetDataVersion())) + .build(); + } +} diff --git a/android/src/app/organicmaps/car/screens/settings/SettingsScreen.java b/android/src/app/organicmaps/car/screens/settings/SettingsScreen.java new file mode 100644 index 0000000000..7fad3c8d39 --- /dev/null +++ b/android/src/app/organicmaps/car/screens/settings/SettingsScreen.java @@ -0,0 +1,77 @@ +package app.organicmaps.car.screens.settings; + +import androidx.annotation.NonNull; +import androidx.car.app.CarContext; +import androidx.car.app.model.Action; +import androidx.car.app.model.Header; +import androidx.car.app.model.Item; +import androidx.car.app.model.ItemList; +import androidx.car.app.model.Row; +import androidx.car.app.model.Template; +import androidx.car.app.navigation.model.MapTemplate; + +import app.organicmaps.R; +import app.organicmaps.car.OMController; +import app.organicmaps.car.UiHelpers; +import app.organicmaps.car.screens.MapScreen; +import app.organicmaps.util.Config; + +public class SettingsScreen extends MapScreen +{ + + public SettingsScreen(@NonNull CarContext carContext, @NonNull OMController mapController) + { + super(carContext, mapController); + } + + @NonNull + @Override + public Template onGetTemplate() + { + MapTemplate.Builder builder = new MapTemplate.Builder(); + builder.setHeader(createHeader()); + builder.setMapController(getMapController()); + builder.setItemList(createSettingsList()); + return builder.build(); + } + + @NonNull + private Header createHeader() + { + Header.Builder builder = new Header.Builder(); + builder.setStartHeaderAction(Action.BACK); + builder.setTitle(getCarContext().getString(R.string.settings)); + return builder.build(); + } + + @NonNull + private ItemList createSettingsList() + { + ItemList.Builder builder = new ItemList.Builder(); + builder.addItem(createRoutingOptionsItem()); + builder.addItem(UiHelpers.createSharedPrefsCheckbox(getCarContext(), R.string.big_font, Config::isLargeFontsSize, Config::setLargeFontsSize)); + builder.addItem(UiHelpers.createSharedPrefsCheckbox(getCarContext(), R.string.transliteration_title, Config::isTransliteration, Config::setTransliteration)); + builder.addItem(createHelpItem()); + return builder.build(); + } + + @NonNull + private Item createRoutingOptionsItem() + { + Row.Builder builder = new Row.Builder(); + builder.setTitle(getCarContext().getString(R.string.driving_options_title)); + builder.setOnClickListener(() -> getScreenManager().push(new DrivingOptionsScreen(getCarContext(), getOMController()))); + builder.setBrowsable(true); + return builder.build(); + } + + @NonNull + private Item createHelpItem() + { + Row.Builder builder = new Row.Builder(); + builder.setTitle(getCarContext().getString(R.string.help)); + builder.setOnClickListener(() -> getScreenManager().push(new HelpScreen(getCarContext(), getOMController()))); + builder.setBrowsable(true); + return builder.build(); + } +} -- 2.45.3 From e0cb3e4bdf61b9af8627de1f764a4722deaef930 Mon Sep 17 00:00:00 2001 From: Andrew Shkrob Date: Thu, 8 Dec 2022 20:51:24 +0100 Subject: [PATCH 3/7] [android-auto] Update car.app library to 1.3.0-rc01 version Signed-off-by: Andrew Shkrob --- android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index 8d3066b70c..8f23e41966 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -84,7 +84,7 @@ dependencies { implementation 'androidx.annotation:annotation:1.5.0' implementation 'androidx.appcompat:appcompat:1.7.0-alpha01' - implementation 'androidx.car.app:app:1.3.0-beta01' + implementation 'androidx.car.app:app:1.3.0-rc01' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.fragment:fragment:1.5.4' // Lifecycle is added as a workaround for duplicate classes error caused by some outdated dependency: -- 2.45.3 From 90662aed813e88201717d2406449996d58f100b3 Mon Sep 17 00:00:00 2001 From: Andrew Shkrob Date: Sun, 11 Dec 2022 14:43:52 +0100 Subject: [PATCH 4/7] [android-auto] Nit fixes Signed-off-by: Andrew Shkrob --- android/AndroidManifest.xml | 2 +- .../app/organicmaps/car/NavigationSession.java | 1 - .../src/app/organicmaps/car/SurfaceRenderer.java | 7 ++----- android/src/app/organicmaps/car/UiHelpers.java | 15 +++++---------- .../organicmaps/car/screens/BookmarksScreen.java | 14 ++++---------- .../app/organicmaps/car/screens/SearchScreen.java | 3 ++- 6 files changed, 14 insertions(+), 28 deletions(-) diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 4c6095089b..b3b955f59c 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -726,7 +726,7 @@ android:permission="android.permission.BIND_JOB_SERVICE" android:exported="false"/> diff --git a/android/src/app/organicmaps/car/NavigationSession.java b/android/src/app/organicmaps/car/NavigationSession.java index 1cbeffbcb3..8a9625c365 100644 --- a/android/src/app/organicmaps/car/NavigationSession.java +++ b/android/src/app/organicmaps/car/NavigationSession.java @@ -28,7 +28,6 @@ public final class NavigationSession extends Session implements DefaultLifecycle { private static final String TAG = NavigationSession.class.getSimpleName(); - private OMController mMapController; boolean mInitFailed = false; diff --git a/android/src/app/organicmaps/car/SurfaceRenderer.java b/android/src/app/organicmaps/car/SurfaceRenderer.java index 9b4692a21c..7db089925e 100644 --- a/android/src/app/organicmaps/car/SurfaceRenderer.java +++ b/android/src/app/organicmaps/car/SurfaceRenderer.java @@ -76,6 +76,7 @@ public class SurfaceRenderer implements DefaultLifecycleObserver, SurfaceCallbac Log.d(TAG, "onCreate"); mCarContext.getCarService(AppManager.class).setSurfaceCallback(this); + // TODO: Properly process deep links from other apps on AA. boolean launchByDeepLink = false; mMap.onCreate(launchByDeepLink); } @@ -145,16 +146,12 @@ public class SurfaceRenderer implements DefaultLifecycleObserver, SurfaceCallbac { // If a focal point value is negative, use the center point of the visible area. if (x < 0) - { x = visibleArea.centerX(); - } if (y < 0) - { y = visibleArea.centerY(); - } } - boolean animated = Float.compare(scaleFactor, 2f) == 0; + final boolean animated = Float.compare(scaleFactor, 2f) == 0; Map.onScale(scaleFactor, x, y, animated); } diff --git a/android/src/app/organicmaps/car/UiHelpers.java b/android/src/app/organicmaps/car/UiHelpers.java index 89497899c3..a85d09ee6c 100644 --- a/android/src/app/organicmaps/car/UiHelpers.java +++ b/android/src/app/organicmaps/car/UiHelpers.java @@ -34,19 +34,17 @@ public final class UiHelpers public static Row createSharedPrefsCheckbox( @NonNull CarContext context, @StringRes int titleRes, PrefsGetter getter, PrefsSetter setter) { + final boolean getterValue = getter.get(); + if (mCheckboxIcon == null || mCheckboxSelectedIcon == null) initCheckboxIcons(context); Row.Builder builder = new Row.Builder(); builder.setTitle(context.getString(titleRes)); builder.setOnClickListener(() -> { - setter.set(!getter.get()); + setter.set(!getterValue); context.getCarService(ScreenManager.class).getTop().invalidate(); }); - if (getter.get()) - builder.setImage(mCheckboxSelectedIcon); - else - builder.setImage(mCheckboxIcon); - + builder.setImage(getterValue ? mCheckboxSelectedIcon : mCheckboxIcon); return builder.build(); } @@ -65,10 +63,7 @@ public final class UiHelpers RoutingOptions.addOption(roadType); context.getCarService(ScreenManager.class).getTop().invalidate(); }); - if (RoutingOptions.hasOption(roadType)) - builder.setImage(mCheckboxSelectedIcon); - else - builder.setImage(mCheckboxIcon); + builder.setImage(RoutingOptions.hasOption(roadType) ? mCheckboxSelectedIcon : mCheckboxIcon); return builder.build(); } diff --git a/android/src/app/organicmaps/car/screens/BookmarksScreen.java b/android/src/app/organicmaps/car/screens/BookmarksScreen.java index 4e9b36ea4f..349f41b3ad 100644 --- a/android/src/app/organicmaps/car/screens/BookmarksScreen.java +++ b/android/src/app/organicmaps/car/screens/BookmarksScreen.java @@ -35,7 +35,8 @@ public class BookmarksScreen extends MapScreen public BookmarksScreen(@NonNull CarContext carContext, @NonNull OMController mapController) { super(carContext, mapController); - MAX_CATEGORIES_SIZE = getCarContext().getCarService(ConstraintManager.class).getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_LIST); + final ConstraintManager constraintManager = getCarContext().getCarService(ConstraintManager.class); + MAX_CATEGORIES_SIZE = constraintManager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_LIST); } private BookmarksScreen(@NonNull CarContext carContext, @NonNull OMController mapController, @NonNull BookmarkCategory bookmarkCategory) @@ -48,15 +49,11 @@ public class BookmarksScreen extends MapScreen @Override public Template onGetTemplate() { - MapTemplate.Builder builder = new MapTemplate.Builder(); builder.setHeader(createHeader()); builder.setMapController(getMapController()); builder.setActionStrip(getActionStrip()); - if (mBookmarkCategory == null) - builder.setItemList(createBookmarkCategoriesList()); - else - builder.setItemList(createBookmarksList()); + builder.setItemList(mBookmarkCategory == null ? createBookmarkCategoriesList() : createBookmarksList()); return builder.build(); } @@ -65,10 +62,7 @@ public class BookmarksScreen extends MapScreen { Header.Builder builder = new Header.Builder(); builder.setStartHeaderAction(Action.BACK); - if (mBookmarkCategory == null) - builder.setTitle(getCarContext().getString(R.string.bookmarks)); - else - builder.setTitle(mBookmarkCategory.getName()); + builder.setTitle(mBookmarkCategory == null ? getCarContext().getString(R.string.bookmarks) : mBookmarkCategory.getName()); return builder.build(); } diff --git a/android/src/app/organicmaps/car/screens/SearchScreen.java b/android/src/app/organicmaps/car/screens/SearchScreen.java index d461d5cf30..f6464aa869 100644 --- a/android/src/app/organicmaps/car/screens/SearchScreen.java +++ b/android/src/app/organicmaps/car/screens/SearchScreen.java @@ -24,7 +24,8 @@ public class SearchScreen extends Screen implements SearchTemplate.SearchCallbac public SearchScreen(@NonNull CarContext carContext) { super(carContext); - MAX_RESULTS_SIZE = getCarContext().getCarService(ConstraintManager.class).getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_LIST); + final ConstraintManager constraintManager = getCarContext().getCarService(ConstraintManager.class); + MAX_RESULTS_SIZE = constraintManager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_LIST); } @NonNull -- 2.45.3 From 05a1411862c926fd4a75f2d25a877d01221b7717 Mon Sep 17 00:00:00 2001 From: Andrew Shkrob Date: Sun, 11 Dec 2022 15:48:04 +0100 Subject: [PATCH 5/7] [android-auto] Remove `OMController` and move checkbox creator functions to the Screen classes Signed-off-by: Andrew Shkrob --- .../organicmaps/car/NavigationSession.java | 60 +------------ .../src/app/organicmaps/car/OMController.java | 40 --------- .../src/app/organicmaps/car/UiHelpers.java | 84 +++++++------------ .../car/screens/BookmarksScreen.java | 23 ++--- .../car/screens/CategoriesScreen.java | 14 ++-- .../organicmaps/car/screens/MapScreen.java | 29 +------ .../car/screens/NavigationScreen.java | 15 ++-- .../settings/DrivingOptionsScreen.java | 44 ++++++++-- .../car/screens/settings/HelpScreen.java | 9 +- .../car/screens/settings/SettingsScreen.java | 52 ++++++++++-- 10 files changed, 148 insertions(+), 222 deletions(-) delete mode 100644 android/src/app/organicmaps/car/OMController.java diff --git a/android/src/app/organicmaps/car/NavigationSession.java b/android/src/app/organicmaps/car/NavigationSession.java index 8a9625c365..e500aa5f16 100644 --- a/android/src/app/organicmaps/car/NavigationSession.java +++ b/android/src/app/organicmaps/car/NavigationSession.java @@ -4,23 +4,14 @@ import android.content.Intent; import android.util.Log; import androidx.annotation.NonNull; -import androidx.car.app.CarToast; import androidx.car.app.Screen; -import androidx.car.app.ScreenManager; import androidx.car.app.Session; -import androidx.car.app.model.Action; -import androidx.car.app.model.ActionStrip; -import androidx.car.app.model.CarIcon; -import androidx.car.app.navigation.model.MapController; -import androidx.core.graphics.drawable.IconCompat; import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.LifecycleOwner; import app.organicmaps.MwmApplication; -import app.organicmaps.R; import app.organicmaps.car.screens.ErrorScreen; import app.organicmaps.car.screens.NavigationScreen; -import app.organicmaps.car.screens.settings.SettingsScreen; import java.io.IOException; @@ -28,13 +19,13 @@ public final class NavigationSession extends Session implements DefaultLifecycle { private static final String TAG = NavigationSession.class.getSimpleName(); - private OMController mMapController; - - boolean mInitFailed = false; + private final SurfaceRenderer mSurfaceRenderer; + private boolean mInitFailed = false; public NavigationSession() { getLifecycle().addObserver(this); + mSurfaceRenderer = new SurfaceRenderer(getCarContext(), getLifecycle()); } @NonNull @@ -45,8 +36,7 @@ public final class NavigationSession extends Session implements DefaultLifecycle if (mInitFailed) return new ErrorScreen(getCarContext()); - createMapController(); - return new NavigationScreen(getCarContext(), mMapController); + return new NavigationScreen(getCarContext(), mSurfaceRenderer); } @Override @@ -76,46 +66,4 @@ public final class NavigationSession extends Session implements DefaultLifecycle Log.e(TAG, "Failed to initialize the app."); } } - - private void createMapController() - { - final CarIcon iconPlus = new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.ic_plus)).build(); - final CarIcon iconMinus = new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.ic_minus)).build(); - final CarIcon iconLocation = new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.ic_not_follow)).build(); - final CarIcon iconSettings = new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.ic_settings)).build(); - - SurfaceRenderer surfaceRenderer = new SurfaceRenderer(getCarContext(), getLifecycle()); - - Action panAction = new Action.Builder(Action.PAN).build(); - Action location = new Action.Builder().setIcon(iconLocation).setOnClickListener(this::location).build(); - Action zoomIn = new Action.Builder().setIcon(iconPlus).setOnClickListener(this::zoomIn).build(); - Action zoomOut = new Action.Builder().setIcon(iconMinus).setOnClickListener(this::zoomOut).build(); - ActionStrip mapActionStrip = new ActionStrip.Builder().addAction(location).addAction(zoomIn).addAction(zoomOut).addAction(panAction).build(); - MapController mapController = new MapController.Builder().setMapActionStrip(mapActionStrip).build(); - - Action settings = new Action.Builder().setIcon(iconSettings).setOnClickListener(this::openSettings).build(); - ActionStrip actionStrip = new ActionStrip.Builder().addAction(settings).build(); - - mMapController = new OMController(surfaceRenderer, mapController, actionStrip); - } - - private void location() - { - CarToast.makeText(getCarContext(), "Location", CarToast.LENGTH_LONG).show(); - } - - private void zoomOut() - { - mMapController.getSurfaceRenderer().onZoomOut(); - } - - private void zoomIn() - { - mMapController.getSurfaceRenderer().onZoomIn(); - } - - private void openSettings() - { - getCarContext().getCarService(ScreenManager.class).push(new SettingsScreen(getCarContext(), mMapController)); - } } diff --git a/android/src/app/organicmaps/car/OMController.java b/android/src/app/organicmaps/car/OMController.java deleted file mode 100644 index 61b05f646d..0000000000 --- a/android/src/app/organicmaps/car/OMController.java +++ /dev/null @@ -1,40 +0,0 @@ -package app.organicmaps.car; - -import androidx.annotation.NonNull; -import androidx.car.app.model.ActionStrip; -import androidx.car.app.navigation.model.MapController; - -public class OMController -{ - @NonNull - private final SurfaceRenderer mSurfaceRenderer; - @NonNull - private final MapController mMapController; - @NonNull - private final ActionStrip mActionStrip; - - public OMController(@NonNull SurfaceRenderer surfaceRenderer, @NonNull MapController mapController, @NonNull ActionStrip actionStrip) - { - mSurfaceRenderer = surfaceRenderer; - mMapController = mapController; - mActionStrip = actionStrip; - } - - @NonNull - public SurfaceRenderer getSurfaceRenderer() - { - return mSurfaceRenderer; - } - - @NonNull - public MapController getMapController() - { - return mMapController; - } - - @NonNull - public ActionStrip getActionStrip() - { - return mActionStrip; - } -} diff --git a/android/src/app/organicmaps/car/UiHelpers.java b/android/src/app/organicmaps/car/UiHelpers.java index a85d09ee6c..c7980b89c6 100644 --- a/android/src/app/organicmaps/car/UiHelpers.java +++ b/android/src/app/organicmaps/car/UiHelpers.java @@ -1,75 +1,47 @@ package app.organicmaps.car; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.StringRes; import androidx.car.app.CarContext; +import androidx.car.app.CarToast; import androidx.car.app.ScreenManager; +import androidx.car.app.model.Action; +import androidx.car.app.model.ActionStrip; import androidx.car.app.model.CarIcon; -import androidx.car.app.model.Row; +import androidx.car.app.navigation.model.MapController; import androidx.core.graphics.drawable.IconCompat; import app.organicmaps.R; -import app.organicmaps.routing.RoutingOptions; -import app.organicmaps.settings.RoadType; +import app.organicmaps.car.screens.settings.SettingsScreen; public final class UiHelpers { - public interface PrefsGetter + public static ActionStrip createSettingsActionStrip(@NonNull CarContext context, @NonNull SurfaceRenderer surfaceRenderer) { - boolean get(); + final CarIcon iconSettings = new CarIcon.Builder(IconCompat.createWithResource(context, R.drawable.ic_settings)).build(); + final Action settings = new Action.Builder().setIcon(iconSettings).setOnClickListener( + () -> context.getCarService(ScreenManager.class).push(new SettingsScreen(context, surfaceRenderer)) + ).build(); + return new ActionStrip.Builder().addAction(settings).build(); } - public interface PrefsSetter + public static MapController createMapController(@NonNull CarContext context, @NonNull SurfaceRenderer surfaceRenderer) { - void set(boolean newValue); - } + final CarIcon iconPlus = new CarIcon.Builder(IconCompat.createWithResource(context, R.drawable.ic_plus)).build(); + final CarIcon iconMinus = new CarIcon.Builder(IconCompat.createWithResource(context, R.drawable.ic_minus)).build(); + final CarIcon iconLocation = new CarIcon.Builder(IconCompat.createWithResource(context, R.drawable.ic_not_follow)).build(); - @Nullable - private static CarIcon mCheckboxIcon; - @Nullable - private static CarIcon mCheckboxSelectedIcon; - - @NonNull - public static Row createSharedPrefsCheckbox( - @NonNull CarContext context, @StringRes int titleRes, PrefsGetter getter, PrefsSetter setter) - { - final boolean getterValue = getter.get(); - - if (mCheckboxIcon == null || mCheckboxSelectedIcon == null) - initCheckboxIcons(context); - Row.Builder builder = new Row.Builder(); - builder.setTitle(context.getString(titleRes)); - builder.setOnClickListener(() -> { - setter.set(!getterValue); - context.getCarService(ScreenManager.class).getTop().invalidate(); - }); - builder.setImage(getterValue ? mCheckboxSelectedIcon : mCheckboxIcon); - return builder.build(); - } - - @NonNull - public static Row createDrivingOptionCheckbox( - @NonNull CarContext context, RoadType roadType, @StringRes int titleRes) - { - if (mCheckboxIcon == null || mCheckboxSelectedIcon == null) - initCheckboxIcons(context); - Row.Builder builder = new Row.Builder(); - builder.setTitle(context.getString(titleRes)); - builder.setOnClickListener(() -> { - if (RoutingOptions.hasOption(roadType)) - RoutingOptions.removeOption(roadType); - else - RoutingOptions.addOption(roadType); - context.getCarService(ScreenManager.class).getTop().invalidate(); - }); - builder.setImage(RoutingOptions.hasOption(roadType) ? mCheckboxSelectedIcon : mCheckboxIcon); - return builder.build(); - } - - private static void initCheckboxIcons(@NonNull CarContext context) - { - mCheckboxIcon = new CarIcon.Builder(IconCompat.createWithResource(context, R.drawable.ic_check_box)).build(); - mCheckboxSelectedIcon = new CarIcon.Builder(IconCompat.createWithResource(context, R.drawable.ic_check_box_checked)).build(); + final Action panAction = new Action.Builder(Action.PAN).build(); + final Action location = new Action.Builder().setIcon(iconLocation).setOnClickListener( + () -> CarToast.makeText(context, "Location", CarToast.LENGTH_LONG).show() + ).build(); + final Action zoomIn = new Action.Builder().setIcon(iconPlus).setOnClickListener(surfaceRenderer::onZoomIn).build(); + final Action zoomOut = new Action.Builder().setIcon(iconMinus).setOnClickListener(surfaceRenderer::onZoomOut).build(); + final ActionStrip mapActionStrip = new ActionStrip.Builder() + .addAction(location) + .addAction(zoomIn) + .addAction(zoomOut) + .addAction(panAction) + .build(); + return new MapController.Builder().setMapActionStrip(mapActionStrip).build(); } } diff --git a/android/src/app/organicmaps/car/screens/BookmarksScreen.java b/android/src/app/organicmaps/car/screens/BookmarksScreen.java index 349f41b3ad..42cb9dee3b 100644 --- a/android/src/app/organicmaps/car/screens/BookmarksScreen.java +++ b/android/src/app/organicmaps/car/screens/BookmarksScreen.java @@ -19,7 +19,8 @@ import app.organicmaps.R; import app.organicmaps.bookmarks.data.BookmarkCategory; import app.organicmaps.bookmarks.data.BookmarkInfo; import app.organicmaps.bookmarks.data.BookmarkManager; -import app.organicmaps.car.OMController; +import app.organicmaps.car.SurfaceRenderer; +import app.organicmaps.car.UiHelpers; import app.organicmaps.util.Graphics; import java.util.ArrayList; @@ -32,16 +33,16 @@ public class BookmarksScreen extends MapScreen @Nullable private BookmarkCategory mBookmarkCategory; - public BookmarksScreen(@NonNull CarContext carContext, @NonNull OMController mapController) + public BookmarksScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer) { - super(carContext, mapController); + super(carContext, surfaceRenderer); final ConstraintManager constraintManager = getCarContext().getCarService(ConstraintManager.class); MAX_CATEGORIES_SIZE = constraintManager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_LIST); } - private BookmarksScreen(@NonNull CarContext carContext, @NonNull OMController mapController, @NonNull BookmarkCategory bookmarkCategory) + private BookmarksScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer, @NonNull BookmarkCategory bookmarkCategory) { - this(carContext, mapController); + this(carContext, surfaceRenderer); mBookmarkCategory = bookmarkCategory; } @@ -51,8 +52,8 @@ public class BookmarksScreen extends MapScreen { MapTemplate.Builder builder = new MapTemplate.Builder(); builder.setHeader(createHeader()); - builder.setMapController(getMapController()); - builder.setActionStrip(getActionStrip()); + builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer())); + builder.setActionStrip(UiHelpers.createSettingsActionStrip(getCarContext(), getSurfaceRenderer())); builder.setItemList(mBookmarkCategory == null ? createBookmarkCategoriesList() : createBookmarksList()); return builder.build(); } @@ -80,7 +81,7 @@ public class BookmarksScreen extends MapScreen Row.Builder itemBuilder = new Row.Builder(); itemBuilder.setTitle(bookmarkCategory.getName()); itemBuilder.addText(bookmarkCategory.getDescription()); - itemBuilder.setOnClickListener(() -> getScreenManager().push(new BookmarksScreen(getCarContext(), getOMController(), bookmarkCategory))); + itemBuilder.setOnClickListener(() -> getScreenManager().push(new BookmarksScreen(getCarContext(), getSurfaceRenderer(), bookmarkCategory))); itemBuilder.setBrowsable(true); builder.addItem(itemBuilder.build()); } @@ -99,7 +100,7 @@ public class BookmarksScreen extends MapScreen final long bookmarkId = BookmarkManager.INSTANCE.getBookmarkIdByPosition(bookmarkCategoryId, i); final BookmarkInfo bookmarkInfo = new BookmarkInfo(bookmarkCategoryId, bookmarkId); - Row.Builder itemBuilder = new Row.Builder(); + final Row.Builder itemBuilder = new Row.Builder(); itemBuilder.setTitle(bookmarkInfo.getName()); if (!bookmarkInfo.getAddress().isEmpty()) itemBuilder.addText(bookmarkInfo.getAddress()); @@ -119,9 +120,9 @@ public class BookmarksScreen extends MapScreen @NonNull private static List getBookmarks() { - List bookmarkCategories = new ArrayList<>(BookmarkManager.INSTANCE.getCategories()); + final List bookmarkCategories = new ArrayList<>(BookmarkManager.INSTANCE.getCategories()); - List toRemove = new ArrayList<>(); + final List toRemove = new ArrayList<>(); for (BookmarkCategory bookmarkCategory : bookmarkCategories) { if (bookmarkCategory.getBookmarksCount() == 0) diff --git a/android/src/app/organicmaps/car/screens/CategoriesScreen.java b/android/src/app/organicmaps/car/screens/CategoriesScreen.java index 4bd74ced23..4595273821 100644 --- a/android/src/app/organicmaps/car/screens/CategoriesScreen.java +++ b/android/src/app/organicmaps/car/screens/CategoriesScreen.java @@ -15,7 +15,8 @@ import androidx.car.app.navigation.model.MapTemplate; import androidx.core.graphics.drawable.IconCompat; import app.organicmaps.R; -import app.organicmaps.car.OMController; +import app.organicmaps.car.SurfaceRenderer; +import app.organicmaps.car.UiHelpers; import java.util.Arrays; import java.util.List; @@ -49,10 +50,11 @@ public class CategoriesScreen extends MapScreen private final int MAX_CATEGORIES_SIZE; - public CategoriesScreen(@NonNull CarContext carContext, @NonNull OMController mapController) + public CategoriesScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer) { - super(carContext, mapController); - MAX_CATEGORIES_SIZE = getCarContext().getCarService(ConstraintManager.class).getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_LIST); + super(carContext, surfaceRenderer); + final ConstraintManager constraintManager = getCarContext().getCarService(ConstraintManager.class); + MAX_CATEGORIES_SIZE = constraintManager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_LIST); } @NonNull @@ -61,8 +63,8 @@ public class CategoriesScreen extends MapScreen { MapTemplate.Builder builder = new MapTemplate.Builder(); builder.setHeader(createHeader()); - builder.setMapController(getMapController()); - builder.setActionStrip(getActionStrip()); + builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer())); + builder.setActionStrip(UiHelpers.createSettingsActionStrip(getCarContext(), getSurfaceRenderer())); builder.setItemList(createCategoriesList()); return builder.build(); } diff --git a/android/src/app/organicmaps/car/screens/MapScreen.java b/android/src/app/organicmaps/car/screens/MapScreen.java index 5876ee897c..38ab3de45d 100644 --- a/android/src/app/organicmaps/car/screens/MapScreen.java +++ b/android/src/app/organicmaps/car/screens/MapScreen.java @@ -3,44 +3,23 @@ package app.organicmaps.car.screens; import androidx.annotation.NonNull; import androidx.car.app.CarContext; import androidx.car.app.Screen; -import androidx.car.app.model.ActionStrip; -import androidx.car.app.navigation.model.MapController; -import app.organicmaps.car.OMController; import app.organicmaps.car.SurfaceRenderer; public abstract class MapScreen extends Screen { @NonNull - private final OMController mMapController; + private final SurfaceRenderer mSurfaceRenderer; - public MapScreen(@NonNull CarContext carContext, @NonNull OMController mapController) + public MapScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer) { super(carContext); - mMapController = mapController; - } - - @NonNull - public OMController getOMController() - { - return mMapController; + mSurfaceRenderer = surfaceRenderer; } @NonNull public SurfaceRenderer getSurfaceRenderer() { - return mMapController.getSurfaceRenderer(); - } - - @NonNull - public MapController getMapController() - { - return mMapController.getMapController(); - } - - @NonNull - public ActionStrip getActionStrip() - { - return mMapController.getActionStrip(); + return mSurfaceRenderer; } } diff --git a/android/src/app/organicmaps/car/screens/NavigationScreen.java b/android/src/app/organicmaps/car/screens/NavigationScreen.java index 36e4225268..477113f8d0 100644 --- a/android/src/app/organicmaps/car/screens/NavigationScreen.java +++ b/android/src/app/organicmaps/car/screens/NavigationScreen.java @@ -13,13 +13,14 @@ import androidx.car.app.navigation.model.MapTemplate; import androidx.core.graphics.drawable.IconCompat; import app.organicmaps.R; -import app.organicmaps.car.OMController; +import app.organicmaps.car.SurfaceRenderer; +import app.organicmaps.car.UiHelpers; public class NavigationScreen extends MapScreen { - public NavigationScreen(@NonNull CarContext carContext, @NonNull OMController mapController) + public NavigationScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer) { - super(carContext, mapController); + super(carContext, surfaceRenderer); } @NonNull @@ -28,8 +29,8 @@ public class NavigationScreen extends MapScreen { MapTemplate.Builder builder = new MapTemplate.Builder(); builder.setHeader(createHeader()); - builder.setMapController(getMapController()); - builder.setActionStrip(getActionStrip()); + builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer())); + builder.setActionStrip(UiHelpers.createSettingsActionStrip(getCarContext(), getSurfaceRenderer())); builder.setItemList(createList()); return builder.build(); } @@ -93,11 +94,11 @@ public class NavigationScreen extends MapScreen private void openCategories() { - getScreenManager().push(new CategoriesScreen(getCarContext(), getOMController())); + getScreenManager().push(new CategoriesScreen(getCarContext(), getSurfaceRenderer())); } private void openBookmarks() { - getScreenManager().push(new BookmarksScreen(getCarContext(), getOMController())); + getScreenManager().push(new BookmarksScreen(getCarContext(), getSurfaceRenderer())); } } diff --git a/android/src/app/organicmaps/car/screens/settings/DrivingOptionsScreen.java b/android/src/app/organicmaps/car/screens/settings/DrivingOptionsScreen.java index dc35a0b886..3142500653 100644 --- a/android/src/app/organicmaps/car/screens/settings/DrivingOptionsScreen.java +++ b/android/src/app/organicmaps/car/screens/settings/DrivingOptionsScreen.java @@ -1,24 +1,36 @@ package app.organicmaps.car.screens.settings; import androidx.annotation.NonNull; +import androidx.annotation.StringRes; import androidx.car.app.CarContext; import androidx.car.app.model.Action; +import androidx.car.app.model.CarIcon; import androidx.car.app.model.Header; import androidx.car.app.model.ItemList; +import androidx.car.app.model.Row; import androidx.car.app.model.Template; import androidx.car.app.navigation.model.MapTemplate; +import androidx.core.graphics.drawable.IconCompat; import app.organicmaps.R; -import app.organicmaps.car.OMController; +import app.organicmaps.car.SurfaceRenderer; import app.organicmaps.car.UiHelpers; import app.organicmaps.car.screens.MapScreen; +import app.organicmaps.routing.RoutingOptions; import app.organicmaps.settings.RoadType; public class DrivingOptionsScreen extends MapScreen { - public DrivingOptionsScreen(@NonNull CarContext carContext, @NonNull OMController mapController) + @NonNull + private final CarIcon mCheckboxIcon; + @NonNull + private final CarIcon mCheckboxSelectedIcon; + + public DrivingOptionsScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer) { - super(carContext, mapController); + super(carContext, surfaceRenderer); + mCheckboxIcon = new CarIcon.Builder(IconCompat.createWithResource(carContext, R.drawable.ic_check_box)).build(); + mCheckboxSelectedIcon = new CarIcon.Builder(IconCompat.createWithResource(carContext, R.drawable.ic_check_box_checked)).build(); } @NonNull @@ -27,7 +39,7 @@ public class DrivingOptionsScreen extends MapScreen { MapTemplate.Builder builder = new MapTemplate.Builder(); builder.setHeader(createHeader()); - builder.setMapController(getMapController()); + builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer())); builder.setItemList(createDrivingOptionsList()); return builder.build(); } @@ -45,10 +57,26 @@ public class DrivingOptionsScreen extends MapScreen private ItemList createDrivingOptionsList() { ItemList.Builder builder = new ItemList.Builder(); - builder.addItem(UiHelpers.createDrivingOptionCheckbox(getCarContext(), RoadType.Toll, R.string.avoid_tolls)); - builder.addItem(UiHelpers.createDrivingOptionCheckbox(getCarContext(), RoadType.Dirty, R.string.avoid_unpaved)); - builder.addItem(UiHelpers.createDrivingOptionCheckbox(getCarContext(), RoadType.Ferry, R.string.avoid_ferry)); - builder.addItem(UiHelpers.createDrivingOptionCheckbox(getCarContext(), RoadType.Motorway, R.string.avoid_motorways)); + builder.addItem(createDrivingOptionCheckbox(RoadType.Toll, R.string.avoid_tolls)); + builder.addItem(createDrivingOptionCheckbox(RoadType.Dirty, R.string.avoid_unpaved)); + builder.addItem(createDrivingOptionCheckbox(RoadType.Ferry, R.string.avoid_ferry)); + builder.addItem(createDrivingOptionCheckbox(RoadType.Motorway, R.string.avoid_motorways)); + return builder.build(); + } + + @NonNull + private Row createDrivingOptionCheckbox(RoadType roadType, @StringRes int titleRes) + { + Row.Builder builder = new Row.Builder(); + builder.setTitle(getCarContext().getString(titleRes)); + builder.setOnClickListener(() -> { + if (RoutingOptions.hasOption(roadType)) + RoutingOptions.removeOption(roadType); + else + RoutingOptions.addOption(roadType); + DrivingOptionsScreen.this.invalidate(); + }); + builder.setImage(RoutingOptions.hasOption(roadType) ? mCheckboxSelectedIcon : mCheckboxIcon); return builder.build(); } } diff --git a/android/src/app/organicmaps/car/screens/settings/HelpScreen.java b/android/src/app/organicmaps/car/screens/settings/HelpScreen.java index 6a02678e04..aa1ffab2f9 100644 --- a/android/src/app/organicmaps/car/screens/settings/HelpScreen.java +++ b/android/src/app/organicmaps/car/screens/settings/HelpScreen.java @@ -13,15 +13,16 @@ import androidx.car.app.navigation.model.MapTemplate; import app.organicmaps.BuildConfig; import app.organicmaps.Framework; import app.organicmaps.R; -import app.organicmaps.car.OMController; +import app.organicmaps.car.SurfaceRenderer; +import app.organicmaps.car.UiHelpers; import app.organicmaps.car.screens.MapScreen; import app.organicmaps.util.DateUtils; public class HelpScreen extends MapScreen { - public HelpScreen(@NonNull CarContext carContext, @NonNull OMController mapController) + public HelpScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer) { - super(carContext, mapController); + super(carContext, surfaceRenderer); } @NonNull @@ -30,7 +31,7 @@ public class HelpScreen extends MapScreen { MapTemplate.Builder builder = new MapTemplate.Builder(); builder.setHeader(createHeader()); - builder.setMapController(getMapController()); + builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer())); builder.setItemList(createSettingsList()); return builder.build(); } diff --git a/android/src/app/organicmaps/car/screens/settings/SettingsScreen.java b/android/src/app/organicmaps/car/screens/settings/SettingsScreen.java index 7fad3c8d39..58acd4942f 100644 --- a/android/src/app/organicmaps/car/screens/settings/SettingsScreen.java +++ b/android/src/app/organicmaps/car/screens/settings/SettingsScreen.java @@ -1,27 +1,46 @@ package app.organicmaps.car.screens.settings; import androidx.annotation.NonNull; +import androidx.annotation.StringRes; import androidx.car.app.CarContext; import androidx.car.app.model.Action; +import androidx.car.app.model.CarIcon; import androidx.car.app.model.Header; import androidx.car.app.model.Item; import androidx.car.app.model.ItemList; import androidx.car.app.model.Row; import androidx.car.app.model.Template; import androidx.car.app.navigation.model.MapTemplate; +import androidx.core.graphics.drawable.IconCompat; import app.organicmaps.R; -import app.organicmaps.car.OMController; +import app.organicmaps.car.SurfaceRenderer; import app.organicmaps.car.UiHelpers; import app.organicmaps.car.screens.MapScreen; import app.organicmaps.util.Config; public class SettingsScreen extends MapScreen { - - public SettingsScreen(@NonNull CarContext carContext, @NonNull OMController mapController) + private interface PrefsGetter { - super(carContext, mapController); + boolean get(); + } + + private interface PrefsSetter + { + void set(boolean newValue); + } + + @NonNull + private final CarIcon mCheckboxIcon; + @NonNull + private final CarIcon mCheckboxSelectedIcon; + + public SettingsScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer) + { + super(carContext, surfaceRenderer); + mCheckboxIcon = new CarIcon.Builder(IconCompat.createWithResource(carContext, R.drawable.ic_check_box)).build(); + mCheckboxSelectedIcon = new CarIcon.Builder(IconCompat.createWithResource(carContext, R.drawable.ic_check_box_checked)).build(); } @NonNull @@ -30,7 +49,7 @@ public class SettingsScreen extends MapScreen { MapTemplate.Builder builder = new MapTemplate.Builder(); builder.setHeader(createHeader()); - builder.setMapController(getMapController()); + builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer())); builder.setItemList(createSettingsList()); return builder.build(); } @@ -49,8 +68,8 @@ public class SettingsScreen extends MapScreen { ItemList.Builder builder = new ItemList.Builder(); builder.addItem(createRoutingOptionsItem()); - builder.addItem(UiHelpers.createSharedPrefsCheckbox(getCarContext(), R.string.big_font, Config::isLargeFontsSize, Config::setLargeFontsSize)); - builder.addItem(UiHelpers.createSharedPrefsCheckbox(getCarContext(), R.string.transliteration_title, Config::isTransliteration, Config::setTransliteration)); + builder.addItem(createSharedPrefsCheckbox(R.string.big_font, Config::isLargeFontsSize, Config::setLargeFontsSize)); + builder.addItem(createSharedPrefsCheckbox(R.string.transliteration_title, Config::isTransliteration, Config::setTransliteration)); builder.addItem(createHelpItem()); return builder.build(); } @@ -60,7 +79,7 @@ public class SettingsScreen extends MapScreen { Row.Builder builder = new Row.Builder(); builder.setTitle(getCarContext().getString(R.string.driving_options_title)); - builder.setOnClickListener(() -> getScreenManager().push(new DrivingOptionsScreen(getCarContext(), getOMController()))); + builder.setOnClickListener(() -> getScreenManager().push(new DrivingOptionsScreen(getCarContext(), getSurfaceRenderer()))); builder.setBrowsable(true); return builder.build(); } @@ -70,8 +89,23 @@ public class SettingsScreen extends MapScreen { Row.Builder builder = new Row.Builder(); builder.setTitle(getCarContext().getString(R.string.help)); - builder.setOnClickListener(() -> getScreenManager().push(new HelpScreen(getCarContext(), getOMController()))); + builder.setOnClickListener(() -> getScreenManager().push(new HelpScreen(getCarContext(), getSurfaceRenderer()))); builder.setBrowsable(true); return builder.build(); } + + @NonNull + private Row createSharedPrefsCheckbox(@StringRes int titleRes, PrefsGetter getter, PrefsSetter setter) + { + final boolean getterValue = getter.get(); + + Row.Builder builder = new Row.Builder(); + builder.setTitle(getCarContext().getString(titleRes)); + builder.setOnClickListener(() -> { + setter.set(!getterValue); + SettingsScreen.this.invalidate(); + }); + builder.setImage(getterValue ? mCheckboxSelectedIcon : mCheckboxIcon); + return builder.build(); + } } -- 2.45.3 From 8fe646042249220e565cad1ecd9457f6efd2fe06 Mon Sep 17 00:00:00 2001 From: Andrew Shkrob Date: Sun, 11 Dec 2022 16:58:37 +0100 Subject: [PATCH 6/7] [android-auto] Remove car_hosts.xml and refactor createHostValidator function Signed-off-by: Andrew Shkrob --- android/res/values/car_hosts.xml | 11 ----------- .../car/NavigationCarAppService.java | 18 ++++++------------ 2 files changed, 6 insertions(+), 23 deletions(-) delete mode 100644 android/res/values/car_hosts.xml diff --git a/android/res/values/car_hosts.xml b/android/res/values/car_hosts.xml deleted file mode 100644 index cc47a82af6..0000000000 --- a/android/res/values/car_hosts.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - fdb00c43dbde8b51cb312aa81d3b5fa17713adb94b28f598d77f8eb89daceedf,com.google.android.projection.gearhead - 70811a3eacfd2e83e18da9bfede52df16ce91f2e69a44d21f18ab66991130771,com.google.android.projection.gearhead - 1975b2f17177bc89a5dff31f9e64a6cae281a53dc1d1d59b1d147fe1c82afa00,com.google.android.projection.gearhead - c241ffbc8e287c4e9a4ad19632ba1b1351ad361d5177b7d7b29859bd2b7fc631,com.google.android.apps.automotive.templates.host - dd66deaf312d8daec7adbe85a218ecc8c64f3b152f9b5998d5b29300c2623f61,com.google.android.apps.automotive.templates.host - 50e603d333c6049a37bd751375d08f3bd0abebd33facd30bd17b64b89658b421,com.google.android.apps.automotive.templates.host - - diff --git a/android/src/app/organicmaps/car/NavigationCarAppService.java b/android/src/app/organicmaps/car/NavigationCarAppService.java index 4e4c2593b6..3bcadafb78 100644 --- a/android/src/app/organicmaps/car/NavigationCarAppService.java +++ b/android/src/app/organicmaps/car/NavigationCarAppService.java @@ -1,13 +1,11 @@ package app.organicmaps.car; -import android.content.pm.ApplicationInfo; - import androidx.annotation.NonNull; import androidx.car.app.CarAppService; import androidx.car.app.Session; import androidx.car.app.validation.HostValidator; -import app.organicmaps.R; +import app.organicmaps.BuildConfig; public final class NavigationCarAppService extends CarAppService { @@ -15,16 +13,12 @@ public final class NavigationCarAppService extends CarAppService @Override public HostValidator createHostValidator() { - if ((getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) - { + if (BuildConfig.DEBUG) return HostValidator.ALLOW_ALL_HOSTS_VALIDATOR; - } - else - { - return new HostValidator.Builder(getApplicationContext()) - .addAllowedHosts(R.array.hosts_allowlist) - .build(); - } + + return new HostValidator.Builder(getApplicationContext()) + .addAllowedHosts(androidx.car.app.R.array.hosts_allowlist_sample) + .build(); } @NonNull -- 2.45.3 From 31b6b48e221500df5263032f74564db7c5ee507d Mon Sep 17 00:00:00 2001 From: Andrew Shkrob Date: Wed, 14 Dec 2022 19:53:59 +0100 Subject: [PATCH 7/7] [core] Rendering on many surfaces Signed-off-by: Andrew Shkrob --- android/jni/app/organicmaps/Framework.cpp | 193 ++++---- android/jni/app/organicmaps/Framework.hpp | 49 +- android/jni/app/organicmaps/Map.cpp | 78 ++-- android/src/app/organicmaps/Map.java | 113 ++--- android/src/app/organicmaps/MapFragment.java | 2 +- android/src/app/organicmaps/MwmActivity.java | 12 +- .../app/organicmaps/car/SurfaceRenderer.java | 8 +- drape/stipple_pen_resource.cpp | 4 +- drape_frontend/drape_engine.hpp | 2 + map/benchmark_tools.cpp | 3 +- map/framework.cpp | 435 +++++++++--------- map/framework.hpp | 43 +- shaders/program_params.hpp | 2 - 13 files changed, 503 insertions(+), 441 deletions(-) diff --git a/android/jni/app/organicmaps/Framework.cpp b/android/jni/app/organicmaps/Framework.cpp index ad17613965..c6ab5586d4 100644 --- a/android/jni/app/organicmaps/Framework.cpp +++ b/android/jni/app/organicmaps/Framework.cpp @@ -107,7 +107,6 @@ enum MultiTouchAction Framework::Framework() : m_lastCompass(0.0) - , m_isSurfaceDestroyed(false) , m_isChoosePositionMode(false) { m_work.GetTrafficManager().SetStateListener(bind(&Framework::TrafficStateChanged, this, _1)); @@ -163,15 +162,22 @@ void Framework::IsolinesSchemeStateChanged(IsolinesManager::IsolinesState state) m_onIsolinesStateChangedFn(state); } -bool Framework::DestroySurfaceOnDetach() +bool Framework::DestroySurfaceOnDetach(df::DrapeEngineId engineId) { - if (m_vulkanContextFactory) + if (m_drapeEngines[engineId].m_vulkanContextFactory) return false; return true; } -bool Framework::CreateDrapeEngine(JNIEnv * env, jobject jSurface, int densityDpi, bool firstLaunch, - bool launchByDeepLink, uint32_t appVersionCode) +df::DrapeEngineId Framework::CreateDrapeEngineId() +{ + df::DrapeEngineId engineId = m_work.CreateDrapeEngineId(); + m_drapeEngines[engineId]; + return engineId; +} + +bool Framework::CreateDrapeEngine(JNIEnv * env, df::DrapeEngineId engineId, jobject jSurface, int densityDpi, + bool firstLaunch, bool launchByDeepLink, uint32_t appVersionCode) { // Vulkan is supported only since Android 8.0, because some Android devices with Android 7.x // have fatal driver issue, which can lead to process termination and whole OS destabilization. @@ -183,34 +189,36 @@ bool Framework::CreateDrapeEngine(JNIEnv * env, jobject jSurface, int densityDpi if (vulkanForbidden) LOG(LWARNING, ("Vulkan API is forbidden on this device.")); + DrapeEngineData &drapeEngineData = m_drapeEngines[engineId]; + if (m_work.LoadPreferredGraphicsAPI() == dp::ApiVersion::Vulkan && !vulkanForbidden) { - m_vulkanContextFactory = + drapeEngineData.m_vulkanContextFactory = make_unique_dp(appVersionCode, sdkVersion); - if (!CastFactory(m_vulkanContextFactory)->IsVulkanSupported()) + if (!CastFactory(drapeEngineData.m_vulkanContextFactory)->IsVulkanSupported()) { LOG(LWARNING, ("Vulkan API is not supported.")); - m_vulkanContextFactory.reset(); + drapeEngineData.m_vulkanContextFactory.reset(); } - if (m_vulkanContextFactory) + if (drapeEngineData.m_vulkanContextFactory) { - auto f = CastFactory(m_vulkanContextFactory); + auto f = CastFactory(drapeEngineData.m_vulkanContextFactory); f->SetSurface(env, jSurface); if (!f->IsValid()) { LOG(LWARNING, ("Invalid Vulkan API context.")); - m_vulkanContextFactory.reset(); + drapeEngineData.m_vulkanContextFactory.reset(); } } } AndroidOGLContextFactory * oglFactory = nullptr; - if (!m_vulkanContextFactory) + if (!drapeEngineData.m_vulkanContextFactory) { - m_oglContextFactory = make_unique_dp( + drapeEngineData.m_oglContextFactory = make_unique_dp( new AndroidOGLContextFactory(env, jSurface)); - oglFactory = m_oglContextFactory->CastFactory(); + oglFactory = drapeEngineData.m_oglContextFactory->CastFactory(); if (!oglFactory->IsValid()) { LOG(LWARNING, ("Invalid GL context.")); @@ -219,9 +227,9 @@ bool Framework::CreateDrapeEngine(JNIEnv * env, jobject jSurface, int densityDpi } ::Framework::DrapeCreationParams p; - if (m_vulkanContextFactory) + if (drapeEngineData.m_vulkanContextFactory) { - auto f = CastFactory(m_vulkanContextFactory); + auto f = CastFactory(drapeEngineData.m_vulkanContextFactory); p.m_apiVersion = dp::ApiVersion::Vulkan; p.m_surfaceWidth = f->GetWidth(); p.m_surfaceHeight = f->GetHeight(); @@ -238,33 +246,35 @@ bool Framework::CreateDrapeEngine(JNIEnv * env, jobject jSurface, int densityDpi p.m_isChoosePositionMode = m_isChoosePositionMode; p.m_hints.m_isFirstLaunch = firstLaunch; p.m_hints.m_isLaunchByDeepLink = launchByDeepLink; - ASSERT(!m_guiPositions.empty(), ("GUI elements must be set-up before engine is created")); - p.m_widgetsInitInfo = m_guiPositions; + ASSERT(!drapeEngineData.m_guiPositions.empty(), ("GUI elements must be set-up before engine is created")); + p.m_widgetsInitInfo = drapeEngineData.m_guiPositions; m_work.SetMyPositionModeListener(bind(&Framework::MyPositionModeChanged, this, _1, _2)); - if (m_vulkanContextFactory) - m_work.CreateDrapeEngine(make_ref(m_vulkanContextFactory), move(p)); + if (drapeEngineData.m_vulkanContextFactory) + m_work.CreateDrapeEngine(engineId, make_ref(drapeEngineData.m_vulkanContextFactory), move(p)); else - m_work.CreateDrapeEngine(make_ref(m_oglContextFactory), move(p)); + m_work.CreateDrapeEngine(engineId, make_ref(drapeEngineData.m_oglContextFactory), move(p)); + m_work.EnterForeground(); return true; } -bool Framework::IsDrapeEngineCreated() const +bool Framework::IsDrapeEngineCreated(df::DrapeEngineId engineId) const { - return m_work.IsDrapeEngineCreated(); + return m_work.IsDrapeEngineCreated(engineId); } -void Framework::Resize(JNIEnv * env, jobject jSurface, int w, int h) +void Framework::Resize(JNIEnv * env, df::DrapeEngineId engineId, jobject jSurface, int w, int h) { - if (m_vulkanContextFactory) + auto& drapeEngineData = m_drapeEngines[engineId]; + if (drapeEngineData.m_vulkanContextFactory) { - auto vulkanContextFactory = CastFactory(m_vulkanContextFactory); + auto vulkanContextFactory = CastFactory(drapeEngineData.m_vulkanContextFactory); if (vulkanContextFactory->GetWidth() != w || vulkanContextFactory->GetHeight() != h) { - m_vulkanContextFactory->SetPresentAvailable(false); + drapeEngineData.m_vulkanContextFactory->SetPresentAvailable(false); m_work.SetRenderingDisabled(false /* destroySurface */); vulkanContextFactory->ChangeSurface(env, jSurface, w, h); @@ -275,32 +285,33 @@ void Framework::Resize(JNIEnv * env, jobject jSurface, int w, int h) } else { - m_oglContextFactory->CastFactory()->UpdateSurfaceSize(w, h); + drapeEngineData.m_oglContextFactory->CastFactory()->UpdateSurfaceSize(w, h); } - m_work.OnSize(w, h); + m_work.OnSize(engineId, w, h); } -void Framework::DetachSurface(bool destroySurface) +void Framework::DetachSurface(df::DrapeEngineId engineId, bool destroySurface) { LOG(LINFO, ("Detach surface started. destroySurface =", destroySurface)); - if (m_vulkanContextFactory) + auto& drapeEngineData = m_drapeEngines[engineId]; + if (drapeEngineData.m_vulkanContextFactory) { - m_vulkanContextFactory->SetPresentAvailable(false); + drapeEngineData.m_vulkanContextFactory->SetPresentAvailable(false); } else { - ASSERT(m_oglContextFactory != nullptr, ()); - m_oglContextFactory->SetPresentAvailable(false); + ASSERT(drapeEngineData.m_oglContextFactory != nullptr, ()); + drapeEngineData.m_oglContextFactory->SetPresentAvailable(false); } if (destroySurface) { LOG(LINFO, ("Destroy surface.")); - m_isSurfaceDestroyed = true; + drapeEngineData.m_isSurfaceDestroyed = true; m_work.OnDestroySurface(); } - if (m_vulkanContextFactory) + if (drapeEngineData.m_vulkanContextFactory) { // With Vulkan we don't need to recreate all graphics resources, // we have to destroy only resources bound with surface (swapchains, @@ -309,25 +320,25 @@ void Framework::DetachSurface(bool destroySurface) m_work.SetRenderingDisabled(false /* destroySurface */); // Allow pipeline dump only on enter background. - CastFactory(m_vulkanContextFactory)->ResetSurface(destroySurface /* allowPipelineDump */); + CastFactory(drapeEngineData.m_vulkanContextFactory)->ResetSurface(destroySurface /* allowPipelineDump */); } else { m_work.SetRenderingDisabled(destroySurface); - auto factory = m_oglContextFactory->CastFactory(); + auto factory = drapeEngineData.m_oglContextFactory->CastFactory(); factory->ResetSurface(); } LOG(LINFO, ("Detach surface finished.")); } -bool Framework::AttachSurface(JNIEnv * env, jobject jSurface) +bool Framework::AttachSurface(JNIEnv * env, df::DrapeEngineId engineId, jobject jSurface) { LOG(LINFO, ("Attach surface started.")); - + auto& drapeEngineData = m_drapeEngines[engineId]; int w = 0, h = 0; - if (m_vulkanContextFactory) + if (drapeEngineData.m_vulkanContextFactory) { - auto factory = CastFactory(m_vulkanContextFactory); + auto factory = CastFactory(drapeEngineData.m_vulkanContextFactory); factory->SetSurface(env, jSurface); if (!factory->IsValid()) { @@ -339,8 +350,8 @@ bool Framework::AttachSurface(JNIEnv * env, jobject jSurface) } else { - ASSERT(m_oglContextFactory != nullptr, ()); - auto factory = m_oglContextFactory->CastFactory(); + ASSERT(drapeEngineData.m_oglContextFactory != nullptr, ()); + auto factory = drapeEngineData.m_oglContextFactory->CastFactory(); factory->SetSurface(env, jSurface); if (!factory->IsValid()) { @@ -351,25 +362,25 @@ bool Framework::AttachSurface(JNIEnv * env, jobject jSurface) h = factory->GetHeight(); } - ASSERT(!m_guiPositions.empty(), ("GUI elements must be set-up before engine is created")); + ASSERT(!drapeEngineData.m_guiPositions.empty(), ("GUI elements must be set-up before engine is created")); - if (m_vulkanContextFactory) + if (drapeEngineData.m_vulkanContextFactory) { - m_vulkanContextFactory->SetPresentAvailable(true); + drapeEngineData.m_vulkanContextFactory->SetPresentAvailable(true); m_work.SetRenderingEnabled(); } else { - m_oglContextFactory->SetPresentAvailable(true); - m_work.SetRenderingEnabled(make_ref(m_oglContextFactory)); + drapeEngineData.m_oglContextFactory->SetPresentAvailable(true); + m_work.SetRenderingEnabled(make_ref(drapeEngineData.m_oglContextFactory)); } - if (m_isSurfaceDestroyed) + if (drapeEngineData.m_isSurfaceDestroyed) { LOG(LINFO, ("Recover surface, viewport size:", w, h)); - bool const recreateContextDependentResources = (m_vulkanContextFactory == nullptr); - m_work.OnRecoverSurface(w, h, recreateContextDependentResources); - m_isSurfaceDestroyed = false; + bool const recreateContextDependentResources = (drapeEngineData.m_vulkanContextFactory == nullptr); + m_work.OnRecoverSurface(engineId, w, h, recreateContextDependentResources); + drapeEngineData.m_isSurfaceDestroyed = false; } LOG(LINFO, ("Attach surface finished.")); @@ -377,26 +388,28 @@ bool Framework::AttachSurface(JNIEnv * env, jobject jSurface) return true; } -void Framework::PauseSurfaceRendering() +void Framework::PauseSurfaceRendering(df::DrapeEngineId engineId) { - if (m_vulkanContextFactory) - m_vulkanContextFactory->SetPresentAvailable(false); - if (m_oglContextFactory) - m_oglContextFactory->SetPresentAvailable(false); + auto& drapeEngineData = m_drapeEngines[engineId]; + if (drapeEngineData.m_vulkanContextFactory) + drapeEngineData.m_vulkanContextFactory->SetPresentAvailable(false); + if (drapeEngineData.m_oglContextFactory) + drapeEngineData.m_oglContextFactory->SetPresentAvailable(false); LOG(LINFO, ("Pause surface rendering.")); } -void Framework::ResumeSurfaceRendering() +void Framework::ResumeSurfaceRendering(df::DrapeEngineId engineId) { - if (m_vulkanContextFactory) + auto& drapeEngineData = m_drapeEngines[engineId]; + if (drapeEngineData.m_vulkanContextFactory) { - if (CastFactory(m_vulkanContextFactory)->IsValid()) - m_vulkanContextFactory->SetPresentAvailable(true); + if (CastFactory(drapeEngineData.m_vulkanContextFactory)->IsValid()) + drapeEngineData.m_vulkanContextFactory->SetPresentAvailable(true); } - if (m_oglContextFactory) + if (drapeEngineData.m_oglContextFactory) { - AndroidOGLContextFactory * factory = m_oglContextFactory->CastFactory(); + AndroidOGLContextFactory * factory = drapeEngineData.m_oglContextFactory->CastFactory(); if (factory->IsValid()) factory->SetPresentAvailable(true); } @@ -412,10 +425,13 @@ void Framework::MarkMapStyle(MapStyle mapStyle) { // In case of Vulkan rendering we don't recreate geometry and textures data, so // we need use SetMapStyle instead of MarkMapStyle in all cases. - if (m_vulkanContextFactory) - m_work.SetMapStyle(mapStyle); - else - m_work.MarkMapStyle(mapStyle); + for (const auto& [engineId, engineData] : m_drapeEngines) + { + if (engineData.m_vulkanContextFactory) + m_work.SetMapStyle(mapStyle); + else + m_work.MarkMapStyle(mapStyle); + } } MapStyle Framework::GetMapStyle() const @@ -471,17 +487,17 @@ void Framework::ShowNode(CountryId const & idx, bool zoomToDownloadButton) } } -void Framework::Scale(double factor, m2::PointD const & pxPoint, bool isAnim) +void Framework::Scale(df::DrapeEngineId engineId, double factor, m2::PointD const & pxPoint, bool isAnim) { - m_work.Scale(factor, pxPoint, isAnim); + m_work.Scale(engineId, factor, pxPoint, isAnim); } -void Framework::Move(double factorX, double factorY, bool isAnim) +void Framework::Move(df::DrapeEngineId engineId, double factorX, double factorY, bool isAnim) { - m_work.Move(factorX, factorY, isAnim); + m_work.Move(engineId, factorX, factorY, isAnim); } -void Framework::Touch(int action, Finger const & f1, Finger const & f2, uint8_t maskedPointer) +void Framework::Touch(df::DrapeEngineId engineId, int action, Finger const & f1, Finger const & f2, uint8_t maskedPointer) { MultiTouchAction eventType = static_cast(action); df::TouchEvent event; @@ -514,7 +530,7 @@ void Framework::Touch(int action, Finger const & f1, Finger const & f2, uint8_t event.SetSecondTouch(touch); event.SetFirstMaskedPointer(maskedPointer); - m_work.TouchEvent(event); + m_work.TouchEvent(engineId, event); } m2::PointD Framework::GetViewportCenter() const @@ -527,14 +543,14 @@ void Framework::AddString(string const & name, string const & value) m_work.AddString(name, value); } -void Framework::Scale(::Framework::EScaleMode mode) +void Framework::Scale(df::DrapeEngineId engineId, ::Framework::EScaleMode mode) { - m_work.Scale(mode, true); + m_work.Scale(engineId, mode, true); } -void Framework::Scale(m2::PointD const & centerPt, int targetZoom, bool animate) +void Framework::Scale(df::DrapeEngineId engineId, m2::PointD const & centerPt, int targetZoom, bool animate) { - ref_ptr engine = m_work.GetDrapeEngine(); + ref_ptr engine = m_work.GetDrapeEngine(engineId); if (engine) engine->SetModelViewCenter(centerPt, targetZoom, animate, false); } @@ -658,27 +674,27 @@ location::EMyPositionMode Framework::GetMyPositionMode() const void Framework::SwitchMyPositionNextMode() { - ASSERT(IsDrapeEngineCreated(), ()); + ASSERT(IsDrapeEngineCreated(0), ()); m_work.SwitchMyPositionNextMode(); } -void Framework::SetupWidget(gui::EWidget widget, float x, float y, dp::Anchor anchor) +void Framework::SetupWidget(df::DrapeEngineId engineId, gui::EWidget widget, float x, float y, dp::Anchor anchor) { - m_guiPositions[widget] = gui::Position(m2::PointF(x, y), anchor); + m_drapeEngines[engineId].m_guiPositions[widget] = gui::Position(m2::PointF(x, y), anchor); } -void Framework::ApplyWidgets() +void Framework::ApplyWidgets(df::DrapeEngineId engineId) { gui::TWidgetsLayoutInfo layout; - for (auto const & widget : m_guiPositions) + for (auto const & widget : m_drapeEngines[engineId].m_guiPositions) layout[widget.first] = widget.second.m_pixelPivot; - m_work.SetWidgetLayout(move(layout)); + m_work.SetWidgetLayout(engineId, move(layout)); } -void Framework::CleanWidgets() +void Framework::CleanWidgets(df::DrapeEngineId engineId) { - m_guiPositions.clear(); + m_drapeEngines[engineId].m_guiPositions.clear(); } void Framework::SetupMeasurementSystem() @@ -1628,7 +1644,8 @@ Java_app_organicmaps_Framework_nativeGetAutoZoomEnabled(JNIEnv *, jclass) JNIEXPORT void JNICALL Java_app_organicmaps_Framework_nativeZoomToPoint(JNIEnv * env, jclass, jdouble lat, jdouble lon, jint zoom, jboolean animate) { - g_framework->Scale(m2::PointD(mercator::FromLatLon(lat, lon)), zoom, animate); + // TODO: fix + g_framework->Scale(0, m2::PointD(mercator::FromLatLon(lat, lon)), zoom, animate); } JNIEXPORT jobject JNICALL @@ -1691,7 +1708,7 @@ Java_app_organicmaps_Framework_nativeGetActiveObjectFormattedCuisine(JNIEnv * en JNIEXPORT void JNICALL Java_app_organicmaps_Framework_nativeSetVisibleRect(JNIEnv * env, jclass, jint left, jint top, jint right, jint bottom) { - frm()->SetVisibleViewport(m2::RectD(left, top, right, bottom)); +// frm()->SetVisibleViewport(m2::RectD(left, top, right, bottom)); } JNIEXPORT jboolean JNICALL diff --git a/android/jni/app/organicmaps/Framework.hpp b/android/jni/app/organicmaps/Framework.hpp index 9cc3d5e265..04625bf19e 100644 --- a/android/jni/app/organicmaps/Framework.hpp +++ b/android/jni/app/organicmaps/Framework.hpp @@ -50,8 +50,14 @@ namespace android class Framework : private power_management::PowerManager::Subscriber { private: - drape_ptr m_oglContextFactory; - drape_ptr m_vulkanContextFactory; + struct DrapeEngineData + { + drape_ptr m_oglContextFactory; + drape_ptr m_vulkanContextFactory; + bool m_isSurfaceDestroyed; + std::map m_guiPositions; + }; + std::unordered_map m_drapeEngines; ::Framework m_work; math::LowPassVector m_sensors[2]; @@ -59,10 +65,6 @@ namespace android std::string m_searchQuery; - bool m_isSurfaceDestroyed; - - std::map m_guiPositions; - void TrafficStateChanged(TrafficManager::TrafficState state); void TransitSchemeStateChanged(TransitReadManager::TransitSchemeState state); void IsolinesSchemeStateChanged(IsolinesManager::IsolinesState state); @@ -89,14 +91,15 @@ namespace android void OnLocationUpdated(location::GpsInfo const & info); void OnCompassUpdated(location::CompassInfo const & info, bool forceRedraw); - bool CreateDrapeEngine(JNIEnv * env, jobject jSurface, int densityDpi, bool firstLaunch, - bool launchByDeepLink, uint32_t appVersionCode); - bool IsDrapeEngineCreated() const; - bool DestroySurfaceOnDetach(); - void DetachSurface(bool destroySurface); - bool AttachSurface(JNIEnv * env, jobject jSurface); - void PauseSurfaceRendering(); - void ResumeSurfaceRendering(); + df::DrapeEngineId CreateDrapeEngineId(); + bool CreateDrapeEngine(JNIEnv * env, df::DrapeEngineId engineId, jobject jSurface, int densityDpi, + bool firstLaunch, bool launchByDeepLink, uint32_t appVersionCode); + bool IsDrapeEngineCreated(df::DrapeEngineId engineId) const; + bool DestroySurfaceOnDetach(df::DrapeEngineId engineId); + void DetachSurface(df::DrapeEngineId engineId, bool destroySurface); + bool AttachSurface(JNIEnv * env, df::DrapeEngineId engineId, jobject jSurface); + void PauseSurfaceRendering(df::DrapeEngineId engineId); + void ResumeSurfaceRendering(df::DrapeEngineId engineId); void SetMapStyle(MapStyle mapStyle); void MarkMapStyle(MapStyle mapStyle); @@ -112,7 +115,7 @@ namespace android return m_work.GetRoutingManager().GetLastUsedRouter(); } - void Resize(JNIEnv * env, jobject jSurface, int w, int h); + void Resize(JNIEnv * env, df::DrapeEngineId engineId, jobject jSurface, int w, int h); struct Finger { @@ -127,11 +130,11 @@ namespace android float m_x, m_y; }; - void Scale(double factor, m2::PointD const & pxPoint, bool isAnim); + void Scale(df::DrapeEngineId engineId, double factor, m2::PointD const & pxPoint, bool isAnim); - void Move(double factorX, double factorY, bool isAnim); + void Move(df::DrapeEngineId engineId, double factorX, double factorY, bool isAnim); - void Touch(int action, Finger const & f1, Finger const & f2, uint8_t maskedPointer); + void Touch(df::DrapeEngineId engineId, int action, Finger const & f1, Finger const & f2, uint8_t maskedPointer); bool Search(search::EverywhereSearchParams const & params); std::string GetLastSearchQuery() { return m_searchQuery; } @@ -145,8 +148,8 @@ namespace android void AddString(std::string const & name, std::string const & value); - void Scale(::Framework::EScaleMode mode); - void Scale(m2::PointD const & centerPt, int targetZoom, bool animate); + void Scale(df::DrapeEngineId engineId, ::Framework::EScaleMode mode); + void Scale(df::DrapeEngineId engineId, m2::PointD const & centerPt, int targetZoom, bool animate); void ReplaceBookmark(kml::MarkId markId, kml::BookmarkData & bm); void MoveBookmark(kml::MarkId markId, kml::MarkGroupId curCat, kml::MarkGroupId newCat); @@ -180,9 +183,9 @@ namespace android void SetChoosePositionMode(bool isChoosePositionMode, bool isBusiness, bool hasPosition, m2::PointD const & position); bool GetChoosePositionMode(); - void SetupWidget(gui::EWidget widget, float x, float y, dp::Anchor anchor); - void ApplyWidgets(); - void CleanWidgets(); + void SetupWidget(df::DrapeEngineId engineId, gui::EWidget widget, float x, float y, dp::Anchor anchor); + void ApplyWidgets(df::DrapeEngineId engineId); + void CleanWidgets(df::DrapeEngineId engineId); place_page::Info & GetPlacePageInfo(); diff --git a/android/jni/app/organicmaps/Map.cpp b/android/jni/app/organicmaps/Map.cpp index 616b5dbb5b..aeec603d5f 100644 --- a/android/jni/app/organicmaps/Map.cpp +++ b/android/jni/app/organicmaps/Map.cpp @@ -22,21 +22,27 @@ void OnRenderingInitializationFinished(std::shared_ptr const & listener extern "C" { +JNIEXPORT jlong JNICALL +Java_app_organicmaps_Map_nativeCreateEngineId(JNIEnv * env, jclass) +{ + return g_framework->CreateDrapeEngineId(); +} + JNIEXPORT jboolean JNICALL Java_app_organicmaps_Map_nativeCreateEngine(JNIEnv * env, jclass, - jobject surface, jint density, - jboolean firstLaunch, + jlong engineId, jobject surface, + jint density, jboolean firstLaunch, jboolean isLaunchByDeepLink, jint appVersionCode) { - return g_framework->CreateDrapeEngine(env, surface, density, firstLaunch, isLaunchByDeepLink, + return g_framework->CreateDrapeEngine(env, engineId, surface, density, firstLaunch, isLaunchByDeepLink, base::asserted_cast(appVersionCode)); } JNIEXPORT jboolean JNICALL -Java_app_organicmaps_Map_nativeIsEngineCreated(JNIEnv *, jclass) +Java_app_organicmaps_Map_nativeIsEngineCreated(JNIEnv *, jclass, jlong engineId) { - return g_framework->IsDrapeEngineCreated(); + return g_framework->IsDrapeEngineCreated(static_cast(engineId)); } JNIEXPORT jboolean JNICALL @@ -47,72 +53,72 @@ Java_app_organicmaps_Map_nativeShowMapForUrl(JNIEnv * env, jclass, jstring url) JNIEXPORT void JNICALL Java_app_organicmaps_Map_nativeSetRenderingInitializationFinishedListener( - JNIEnv *, jclass, jobject listener) + JNIEnv *, jclass, jlong engineId, jobject listener) { if (listener) { - g_framework->NativeFramework()->SetGraphicsContextInitializationHandler( + g_framework->NativeFramework()->SetGraphicsContextInitializationHandler(engineId, std::bind(&OnRenderingInitializationFinished, jni::make_global_ref(listener))); } else { - g_framework->NativeFramework()->SetGraphicsContextInitializationHandler(nullptr); + g_framework->NativeFramework()->SetGraphicsContextInitializationHandler(engineId, nullptr); } } JNIEXPORT jboolean JNICALL -Java_app_organicmaps_Map_nativeAttachSurface(JNIEnv * env, jclass, jobject surface) +Java_app_organicmaps_Map_nativeAttachSurface(JNIEnv * env, jclass, jlong engineId, jobject surface) { - return g_framework->AttachSurface(env, surface); + return g_framework->AttachSurface(env, engineId, surface); } JNIEXPORT void JNICALL -Java_app_organicmaps_Map_nativeDetachSurface(JNIEnv *, jclass, jboolean destroySurface) +Java_app_organicmaps_Map_nativeDetachSurface(JNIEnv *, jclass, jlong engineId, jboolean destroySurface) { - g_framework->DetachSurface(destroySurface); + g_framework->DetachSurface(engineId, destroySurface); } JNIEXPORT void JNICALL -Java_app_organicmaps_Map_nativeSurfaceChanged(JNIEnv * env, jclass, jobject surface, jint w, jint h) +Java_app_organicmaps_Map_nativeSurfaceChanged(JNIEnv * env, jclass, jlong engineId, jobject surface, jint w, jint h) { - g_framework->Resize(env, surface, w, h); + g_framework->Resize(env, engineId, surface, w, h); } JNIEXPORT jboolean JNICALL -Java_app_organicmaps_Map_nativeDestroySurfaceOnDetach(JNIEnv *, jclass) +Java_app_organicmaps_Map_nativeDestroySurfaceOnDetach(JNIEnv *, jclass, jlong engineId) { - return g_framework->DestroySurfaceOnDetach(); + return g_framework->DestroySurfaceOnDetach(engineId); } JNIEXPORT void JNICALL -Java_app_organicmaps_Map_nativePauseSurfaceRendering(JNIEnv *, jclass) +Java_app_organicmaps_Map_nativePauseSurfaceRendering(JNIEnv *, jclass, jlong engineId) { - g_framework->PauseSurfaceRendering(); + g_framework->PauseSurfaceRendering(engineId); } JNIEXPORT void JNICALL -Java_app_organicmaps_Map_nativeResumeSurfaceRendering(JNIEnv *, jclass) +Java_app_organicmaps_Map_nativeResumeSurfaceRendering(JNIEnv *, jclass, jlong engineId) { - g_framework->ResumeSurfaceRendering(); + g_framework->ResumeSurfaceRendering(engineId); } JNIEXPORT void JNICALL -Java_app_organicmaps_Map_nativeApplyWidgets(JNIEnv *, jclass) +Java_app_organicmaps_Map_nativeApplyWidgets(JNIEnv *, jclass, jlong engineId) { - g_framework->ApplyWidgets(); + g_framework->ApplyWidgets(engineId); } JNIEXPORT void JNICALL -Java_app_organicmaps_Map_nativeCleanWidgets(JNIEnv *, jclass) +Java_app_organicmaps_Map_nativeCleanWidgets(JNIEnv *, jclass, jlong engineId) { - g_framework->CleanWidgets(); + g_framework->CleanWidgets(engineId); } JNIEXPORT void JNICALL Java_app_organicmaps_Map_nativeSetupWidget( - JNIEnv *, jclass, jint widget, jfloat x, jfloat y, jint anchor) + JNIEnv *, jclass, jlong engineId, jint widget, jfloat x, jfloat y, jint anchor) { - g_framework->SetupWidget(static_cast(widget), x, y, static_cast(anchor)); + g_framework->SetupWidget(engineId, static_cast(widget), x, y, static_cast(anchor)); } JNIEXPORT void JNICALL @@ -126,37 +132,37 @@ Java_app_organicmaps_Map_nativeCompassUpdated(JNIEnv *, jclass, jdouble north, j JNIEXPORT void JNICALL Java_app_organicmaps_Map_nativeMove( - JNIEnv *, jclass, jdouble factorX, jdouble factorY, jboolean isAnim) + JNIEnv *, jclass, jlong engineId, jdouble factorX, jdouble factorY, jboolean isAnim) { - g_framework->Move(factorX, factorY, isAnim); + g_framework->Move(engineId, factorX, factorY, isAnim); } JNIEXPORT void JNICALL -Java_app_organicmaps_Map_nativeScalePlus(JNIEnv *, jclass) +Java_app_organicmaps_Map_nativeScalePlus(JNIEnv *, jclass, jlong engineId) { - g_framework->Scale(::Framework::SCALE_MAG); + g_framework->Scale(engineId, ::Framework::SCALE_MAG); } JNIEXPORT void JNICALL -Java_app_organicmaps_Map_nativeScaleMinus(JNIEnv *, jclass) +Java_app_organicmaps_Map_nativeScaleMinus(JNIEnv *, jclass, jlong engineId) { - g_framework->Scale(::Framework::SCALE_MIN); + g_framework->Scale(engineId, ::Framework::SCALE_MIN); } JNIEXPORT void JNICALL Java_app_organicmaps_Map_nativeScale( - JNIEnv *, jclass, jdouble factor, jdouble focusX, jdouble focusY, jboolean isAnim) + JNIEnv *, jclass, jlong engineId, jdouble factor, jdouble focusX, jdouble focusY, jboolean isAnim) { - g_framework->Scale(factor, {focusX, focusY}, isAnim); + g_framework->Scale(static_cast(engineId), factor, {focusX, focusY}, isAnim); } JNIEXPORT void JNICALL -Java_app_organicmaps_Map_nativeOnTouch(JNIEnv *, jclass, jint action, +Java_app_organicmaps_Map_nativeOnTouch(JNIEnv *, jclass, jlong engineId, jint action, jint id1, jfloat x1, jfloat y1, jint id2, jfloat x2, jfloat y2, jint maskedPointer) { - g_framework->Touch(action, + g_framework->Touch(engineId, action, android::Framework::Finger(id1, x1, y1), android::Framework::Finger(id2, x2, y2), maskedPointer); } diff --git a/android/src/app/organicmaps/Map.java b/android/src/app/organicmaps/Map.java index df547cef44..c3d7add4f6 100644 --- a/android/src/app/organicmaps/Map.java +++ b/android/src/app/organicmaps/Map.java @@ -50,6 +50,8 @@ public final class Map public static final int INVALID_POINTER_MASK = 0xFF; public static final int INVALID_TOUCH_ID = -1; + private final long mEngineId; + private int mCurrentCompassOffsetX; private int mCurrentCompassOffsetY; private int mBottomWidgetOffsetX; @@ -70,6 +72,8 @@ public final class Map public Map() { + mEngineId = nativeCreateEngineId(); + Logger.d(TAG, "Created engineId: " + mEngineId); onCreate(false); } @@ -88,9 +92,9 @@ public final class Map final int navPadding = UiUtils.dimen(context, R.dimen.nav_frame_padding); final int marginX = UiUtils.dimen(context, R.dimen.margin_compass) + navPadding; final int marginY = UiUtils.dimen(context, R.dimen.margin_compass_top) + navPadding; - nativeSetupWidget(WIDGET_COMPASS, mWidth - x - marginX, y + marginY, ANCHOR_CENTER); + nativeSetupWidget(mEngineId, WIDGET_COMPASS, mWidth - x - marginX, y + marginY, ANCHOR_CENTER); if (forceRedraw && mSurfaceCreated) - nativeApplyWidgets(); + nativeApplyWidgets(mEngineId); mCurrentCompassOffsetX = x; mCurrentCompassOffsetY = y; } @@ -119,8 +123,8 @@ public final class Map public void onSurfaceCreated(final Context context, final Surface surface, Rect surfaceFrame, int surfaceDpi) { - if (nativeIsEngineCreated()) - nativeDetachSurface(true); + if (nativeIsEngineCreated(mEngineId)) + nativeDetachSurface(mEngineId, true); if (isThemeChangingProcess(context)) { @@ -129,9 +133,9 @@ public final class Map } Logger.d(TAG, "mSurfaceCreated = " + mSurfaceCreated); - if (nativeIsEngineCreated()) + if (nativeIsEngineCreated(mEngineId)) { - if (!nativeAttachSurface(surface)) + if (!nativeAttachSurface(mEngineId, surface)) { if (mCallbackUnsupported != null) mCallbackUnsupported.report(); @@ -140,7 +144,7 @@ public final class Map mSurfaceCreated = true; mSurfaceAttached = true; mRequireResize = true; - nativeResumeSurfaceRendering(); + nativeResumeSurfaceRendering(mEngineId); return; } @@ -148,7 +152,7 @@ public final class Map setupWidgets(context, surfaceFrame.width(), surfaceFrame.height()); final boolean firstStart = LocationHelper.INSTANCE.isInFirstRun(); - if (!nativeCreateEngine(surface, surfaceDpi, firstStart, mLaunchByDeepLink, BuildConfig.VERSION_CODE)) + if (!nativeCreateEngine(mEngineId, surface, surfaceDpi, firstStart, mLaunchByDeepLink, BuildConfig.VERSION_CODE)) { if (mCallbackUnsupported != null) mCallbackUnsupported.report(); @@ -160,7 +164,7 @@ public final class Map mSurfaceCreated = true; mSurfaceAttached = true; - nativeResumeSurfaceRendering(); + nativeResumeSurfaceRendering(mEngineId); if (mMapRenderingListener != null) mMapRenderingListener.onRenderingCreated(); } @@ -177,11 +181,11 @@ public final class Map if (!mSurfaceCreated || (!mRequireResize && isSurfaceCreating)) return; - nativeSurfaceChanged(surface, surfaceFrame.width(), surfaceFrame.height()); + nativeSurfaceChanged(mEngineId, surface, surfaceFrame.width(), surfaceFrame.height()); mRequireResize = false; setupWidgets(context, surfaceFrame.width(), surfaceFrame.height()); - nativeApplyWidgets(); + nativeApplyWidgets(mEngineId); if (mMapRenderingListener != null) mMapRenderingListener.onRenderingRestored(); } @@ -192,8 +196,8 @@ public final class Map if (!mSurfaceCreated || !mSurfaceAttached || !isAdded) return; - nativeDetachSurface(!activityIsChangingConfigurations); - mSurfaceCreated = !nativeDestroySurfaceOnDetach(); + nativeDetachSurface(mEngineId, !activityIsChangingConfigurations); + mSurfaceCreated = !nativeDestroySurfaceOnDetach(mEngineId); mSurfaceAttached = false; } @@ -218,12 +222,12 @@ public final class Map public void onStart() { - nativeSetRenderingInitializationFinishedListener(mMapRenderingListener); + nativeSetRenderingInitializationFinishedListener(mEngineId, mMapRenderingListener); } public void onStop() { - nativeSetRenderingInitializationFinishedListener(null); + nativeSetRenderingInitializationFinishedListener(mEngineId, null); } public void onPause(final Context context) @@ -232,14 +236,14 @@ public final class Map // Pause/Resume can be called without surface creation/destroy. if (mSurfaceAttached) - nativePauseSurfaceRendering(); + nativePauseSurfaceRendering(mEngineId); } public void onResume() { // Pause/Resume can be called without surface creation/destroy. if (mSurfaceAttached) - nativeResumeSurfaceRendering(); + nativeResumeSurfaceRendering(mEngineId); } boolean isContextCreated() @@ -249,46 +253,46 @@ public final class Map public void onScroll(float distanceX, float distanceY) { - Map.nativeMove(-distanceX / ((float) mWidth), distanceY / ((float) mHeight), false); + nativeMove(mEngineId, -distanceX / ((float) mWidth), distanceY / ((float) mHeight), false); } - public static void zoomIn() + public void zoomIn() { - nativeScalePlus(); + nativeScalePlus(mEngineId); } - public static void zoomOut() + public void zoomOut() { - nativeScaleMinus(); + nativeScaleMinus(mEngineId); } - public static void onScale(double factor, double focusX, double focusY, boolean isAnim) + public void onScale(double factor, double focusX, double focusY, boolean isAnim) { - nativeScale(factor, focusX, focusY, isAnim); + nativeScale(mEngineId, factor, focusX, focusY, isAnim); } - public static void onTouch(int actionType, MotionEvent event, int pointerIndex) + public void onTouch(int actionType, MotionEvent event, int pointerIndex) { if (event.getPointerCount() == 1) { - nativeOnTouch(actionType, event.getPointerId(0), event.getX(), event.getY(), Map.INVALID_TOUCH_ID, 0, 0, 0); + nativeOnTouch(mEngineId, actionType, event.getPointerId(0), event.getX(), event.getY(), Map.INVALID_TOUCH_ID, 0, 0, 0); } else { - nativeOnTouch(actionType, + nativeOnTouch(mEngineId, actionType, event.getPointerId(0), event.getX(0), event.getY(0), event.getPointerId(1), event.getX(1), event.getY(1), pointerIndex); } } - public static void onTouch(float x, float y) + public void onTouch(float x, float y) { - nativeOnTouch(Map.NATIVE_ACTION_UP, 0, x, y, Map.INVALID_TOUCH_ID, 0, 0, 0); + nativeOnTouch(mEngineId, Map.NATIVE_ACTION_UP, 0, x, y, Map.INVALID_TOUCH_ID, 0, 0, 0); } public static boolean isEngineCreated() { - return nativeIsEngineCreated(); + return nativeIsEngineCreated(0); } public static boolean showMapForUrl(String url) @@ -301,30 +305,30 @@ public final class Map mHeight = height; mWidth = width; - nativeCleanWidgets(); + nativeCleanWidgets(mEngineId); setupBottomWidgetsOffset(context, mBottomWidgetOffsetX, mBottomWidgetOffsetY); - nativeSetupWidget(WIDGET_SCALE_FPS_LABEL, UiUtils.dimen(context, R.dimen.margin_base), UiUtils.dimen(context, R.dimen.margin_base), ANCHOR_LEFT_TOP); + nativeSetupWidget(mEngineId, WIDGET_SCALE_FPS_LABEL, UiUtils.dimen(context, R.dimen.margin_base), UiUtils.dimen(context, R.dimen.margin_base), ANCHOR_LEFT_TOP); setupCompass(context, mCurrentCompassOffsetX, mCurrentCompassOffsetY, false); } private void setupRuler(final Context context, int offsetX, int offsetY) { - nativeSetupWidget(WIDGET_RULER, + nativeSetupWidget(mEngineId, WIDGET_RULER, UiUtils.dimen(context, R.dimen.margin_ruler) + offsetX, mHeight - UiUtils.dimen(context, R.dimen.margin_ruler) - offsetY, ANCHOR_LEFT_BOTTOM); if (mSurfaceCreated) - nativeApplyWidgets(); + nativeApplyWidgets(mEngineId); } private void setupAttribution(final Context context, int offsetX, int offsetY) { - nativeSetupWidget(WIDGET_COPYRIGHT, + nativeSetupWidget(mEngineId, WIDGET_COPYRIGHT, UiUtils.dimen(context, R.dimen.margin_ruler) + offsetX, mHeight - UiUtils.dimen(context, R.dimen.margin_ruler) - offsetY, ANCHOR_LEFT_BOTTOM); if (mSurfaceCreated) - nativeApplyWidgets(); + nativeApplyWidgets(mEngineId); } private boolean isThemeChangingProcess(final Context context) @@ -333,33 +337,34 @@ public final class Map } // Engine - private static native boolean nativeCreateEngine(Surface surface, int density, - boolean firstLaunch, + private static native long nativeCreateEngineId(); + private static native boolean nativeCreateEngine(long engineId, Surface surface, + int density, boolean firstLaunch, boolean isLaunchByDeepLink, int appVersionCode); - private static native boolean nativeIsEngineCreated(); + private static native boolean nativeIsEngineCreated(long engineId); private static native void nativeSetRenderingInitializationFinishedListener( - @Nullable MapRenderingListener listener); + long engineId, @Nullable MapRenderingListener listener); private static native boolean nativeShowMapForUrl(String url); // Surface - private static native boolean nativeAttachSurface(Surface surface); - private static native void nativeDetachSurface(boolean destroySurface); - private static native void nativeSurfaceChanged(Surface surface, int w, int h); - private static native boolean nativeDestroySurfaceOnDetach(); - private static native void nativePauseSurfaceRendering(); - private static native void nativeResumeSurfaceRendering(); + private static native boolean nativeAttachSurface(long engineId, Surface surface); + private static native void nativeDetachSurface(long engineId, boolean destroySurface); + private static native void nativeSurfaceChanged(long engineId, Surface surface, int w, int h); + private static native boolean nativeDestroySurfaceOnDetach(long engineId); + private static native void nativePauseSurfaceRendering(long engineId); + private static native void nativeResumeSurfaceRendering(long engineId); // Widgets - private static native void nativeApplyWidgets(); - private static native void nativeCleanWidgets(); - private static native void nativeSetupWidget(int widget, float x, float y, int anchor); + private static native void nativeApplyWidgets(long engineId); + private static native void nativeCleanWidgets(long engineId); + private static native void nativeSetupWidget(long engineId, int widget, float x, float y, int anchor); private static native void nativeCompassUpdated(double north, boolean forceRedraw); // Events - private static native void nativeMove(double factorX, double factorY, boolean isAnim); - private static native void nativeScalePlus(); - private static native void nativeScaleMinus(); - private static native void nativeScale(double factor, double focusX, double focusY, boolean isAnim); - private static native void nativeOnTouch(int actionType, int id1, float x1, float y1, int id2, float x2, float y2, int maskedPointer); + private static native void nativeMove(long engineId, double factorX, double factorY, boolean isAnim); + private static native void nativeScalePlus(long engineId); + private static native void nativeScaleMinus(long engineId); + private static native void nativeScale(long engineId, double factor, double focusX, double focusY, boolean isAnim); + private static native void nativeOnTouch(long engineId, int actionType, int id1, float x1, float y1, int id2, float x2, float y2, int maskedPointer); } diff --git a/android/src/app/organicmaps/MapFragment.java b/android/src/app/organicmaps/MapFragment.java index 326d8cfe59..d77eea855c 100644 --- a/android/src/app/organicmaps/MapFragment.java +++ b/android/src/app/organicmaps/MapFragment.java @@ -164,7 +164,7 @@ public class MapFragment extends BaseMwmFragment implements View.OnTouchListener action = Map.NATIVE_ACTION_CANCEL; break; } - Map.onTouch(action, event, pointerIndex); + mMap.onTouch(action, event, pointerIndex); return true; } diff --git a/android/src/app/organicmaps/MwmActivity.java b/android/src/app/organicmaps/MwmActivity.java index d47ac999c8..3c5737de56 100644 --- a/android/src/app/organicmaps/MwmActivity.java +++ b/android/src/app/organicmaps/MwmActivity.java @@ -642,10 +642,12 @@ public class MwmActivity extends BaseMwmFragmentActivity switch (button) { case zoomIn: - Map.zoomIn(); + // TODO: fix + // Map.zoomIn(); break; case zoomOut: - Map.zoomOut(); + // TODO: fix + // Map.zoomOut(); break; case myPosition: LocationState.nativeSwitchToNextMode(); @@ -1715,10 +1717,12 @@ public class MwmActivity extends BaseMwmFragmentActivity switch (keyCode) { case KeyEvent.KEYCODE_DPAD_DOWN: - Map.zoomOut(); + // TODO: fix + // Map.zoomOut(); return true; case KeyEvent.KEYCODE_DPAD_UP: - Map.zoomIn(); + // TODO: fix + // Map.zoomIn(); return true; case KeyEvent.KEYCODE_ESCAPE: Intent currIntent = getIntent(); diff --git a/android/src/app/organicmaps/car/SurfaceRenderer.java b/android/src/app/organicmaps/car/SurfaceRenderer.java index 7db089925e..d6ce72dbc8 100644 --- a/android/src/app/organicmaps/car/SurfaceRenderer.java +++ b/android/src/app/organicmaps/car/SurfaceRenderer.java @@ -126,12 +126,12 @@ public class SurfaceRenderer implements DefaultLifecycleObserver, SurfaceCallbac public void onZoomIn() { - Map.zoomIn(); + mMap.zoomIn(); } public void onZoomOut() { - Map.zoomOut(); + mMap.zoomOut(); } @Override @@ -153,14 +153,14 @@ public class SurfaceRenderer implements DefaultLifecycleObserver, SurfaceCallbac final boolean animated = Float.compare(scaleFactor, 2f) == 0; - Map.onScale(scaleFactor, x, y, animated); + mMap.onScale(scaleFactor, x, y, animated); } @Override public void onClick(float x, float y) { Log.d(TAG, "onClick: x: " + x + ", y: " + y); - Map.onTouch(x, y); + mMap.onTouch(x, y); } private void reportUnsupported() diff --git a/drape/stipple_pen_resource.cpp b/drape/stipple_pen_resource.cpp index 59b7ea24b5..9a575f7ec9 100644 --- a/drape/stipple_pen_resource.cpp +++ b/drape/stipple_pen_resource.cpp @@ -188,8 +188,8 @@ void StipplePenIndex::UploadResources(ref_ptr context, ref_ // Assume that all patterns are initialized when creating texture (ReserveResource) and uploaded once. // Should provide additional logic like in ColorPalette::UploadResources, if we want multiple uploads. - if (m_uploadCalled) - LOG(LERROR, ("Multiple stipple pen texture uploads are not supported")); +// if (m_uploadCalled) +// LOG(LERROR, ("Multiple stipple pen texture uploads are not supported")); m_uploadCalled = true; uint32_t height = 0; diff --git a/drape_frontend/drape_engine.hpp b/drape_frontend/drape_engine.hpp index 6cc60655b0..c4954d3a35 100644 --- a/drape_frontend/drape_engine.hpp +++ b/drape_frontend/drape_engine.hpp @@ -46,6 +46,8 @@ namespace df class UserMarksProvider; class MapDataProvider; +using DrapeEngineId = std::size_t; + class DrapeEngine { public: diff --git a/map/benchmark_tools.cpp b/map/benchmark_tools.cpp index 15c5dee692..bc03964480 100644 --- a/map/benchmark_tools.cpp +++ b/map/benchmark_tools.cpp @@ -57,7 +57,8 @@ void RunScenario(Framework * framework, std::shared_ptr handle) auto & scenarioData = handle->m_scenariosToRun[handle->m_currentScenario]; - framework->GetDrapeEngine()->RunScenario(std::move(scenarioData), + // TODO: fix + framework->GetDrapeEngine(0)->RunScenario(std::move(scenarioData), [handle](std::string const & name) { #ifdef DRAPE_MEASURER_BENCHMARK diff --git a/map/framework.cpp b/map/framework.cpp index 403c4a2c69..f579c336c7 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -153,8 +153,11 @@ pair Framework::RegisterMap(LocalCountryFile c void Framework::OnLocationError(TLocationError /*error*/) { m_trafficManager.UpdateMyPosition(TrafficManager::MyPosition()); - if (m_drapeEngine != nullptr) - m_drapeEngine->LoseLocation(); + for (const auto& [_, drapeEngineData] : m_drapeEngines) + { + if (drapeEngineData.m_drapeEngine) + drapeEngineData.m_drapeEngine->LoseLocation(); + } } void Framework::OnLocationUpdate(GpsInfo const & info) @@ -193,14 +196,18 @@ void Framework::OnCompassUpdate(CompassInfo const & info) CompassInfo const & rInfo = info; #endif - if (m_drapeEngine != nullptr) - m_drapeEngine->SetCompassInfo(rInfo); + for (const auto& [_, drapeEngineData] : m_drapeEngines) + { + if (drapeEngineData.m_drapeEngine) + drapeEngineData.m_drapeEngine->SetCompassInfo(rInfo); + } } void Framework::SwitchMyPositionNextMode() { - if (m_drapeEngine != nullptr) - m_drapeEngine->SwitchMyPositionNextMode(); + for (const auto& [engineId, drapeEngineData] : m_drapeEngines) + if (drapeEngineData.m_drapeEngine) + drapeEngineData.m_drapeEngine->SwitchMyPositionNextMode(); } void Framework::SetMyPositionModeListener(TMyPositionModeChanged && fn) @@ -215,7 +222,10 @@ void Framework::SetMyPositionPendingTimeoutListener(df::DrapeEngine::UserPositio EMyPositionMode Framework::GetMyPositionMode() const { - return m_drapeEngine ? m_drapeEngine->GetMyPositionMode() : PendingPosition; + for (const auto& [engineId, drapeEngineData] : m_drapeEngines) + if (drapeEngineData.m_drapeEngine) + return drapeEngineData.m_drapeEngine->GetMyPositionMode(); + return PendingPosition; } TrafficManager & Framework::GetTrafficManager() @@ -804,11 +814,11 @@ void Framework::ShowBookmark(Bookmark const * mark) auto es = GetBookmarkManager().GetEditSession(); es.SetIsVisible(mark->GetGroupId(), true /* visible */); - if (m_drapeEngine != nullptr) - { - m_drapeEngine->SetModelViewCenter(mark->GetPivot(), scale, true /* isAnim */, - true /* trackVisibleViewport */); - } +// if (m_drapeEngines.begin()->second.m_drapeEngine) +// { +// m_drapeEngines.begin()->second.m_drapeEngine->SetModelViewCenter(mark->GetPivot(), scale, true /* isAnim */, +// true /* trackVisibleViewport */); +// } ActivateMapSelection(); } @@ -867,13 +877,13 @@ void Framework::ShowFeature(FeatureID const & featureId) info.m_match = place_page::BuildInfo::Match::FeatureOnly; m_currentPlacePageInfo = BuildPlacePageInfo(info); - if (m_drapeEngine != nullptr) - { - auto const pt = m_currentPlacePageInfo->GetMercator(); - auto const scale = scales::GetUpperComfortScale(); - m_drapeEngine->SetModelViewCenter(pt, scale, true /* isAnim */, true /* trackVisibleViewport */); - } - ActivateMapSelection(); +// if (m_drapeEngines.begin()->second.m_drapeEngine) +// { +// auto const pt = m_currentPlacePageInfo->GetMercator(); +// auto const scale = scales::GetUpperComfortScale(); +// m_drapeEngines.begin()->second.m_drapeEngine->SetModelViewCenter(pt, scale, true /* isAnim */, true /* trackVisibleViewport */); +// } +// ActivateMapSelection(); } void Framework::AddBookmarksFile(string const & filePath, bool isTemporaryFile) @@ -907,8 +917,8 @@ void Framework::LoadViewport() m2::AnyRectD rect; if (settings::Get("ScreenClipRect", rect) && df::GetWorldRect().IsRectInside(rect.GetGlobalRect())) { - if (m_drapeEngine != nullptr) - m_drapeEngine->SetModelViewAnyRect(rect, false /* isAnim */, false /* useVisibleViewport */); +// if (m_drapeEngines.begin()->second.m_drapeEngine) +// m_drapeEngines.begin()->second.m_drapeEngine->SetModelViewAnyRect(rect, false /* isAnim */, false /* useVisibleViewport */); } else { @@ -918,10 +928,10 @@ void Framework::LoadViewport() void Framework::ShowAll() { - if (m_drapeEngine == nullptr) - return; - m_drapeEngine->SetModelViewAnyRect(m2::AnyRectD(m_featuresFetcher.GetWorldRect()), false /* isAnim */, - false /* useVisibleViewport */); +// if (!m_drapeEngines.begin()->second.m_drapeEngine) +// return; +// m_drapeEngines.begin()->second.m_drapeEngine->SetModelViewAnyRect(m2::AnyRectD(m_featuresFetcher.GetWorldRect()), false /* isAnim */, +// false /* useVisibleViewport */); } m2::PointD Framework::GetVisiblePixelCenter() const @@ -937,8 +947,8 @@ m2::PointD const & Framework::GetViewportCenter() const void Framework::SetViewportCenter(m2::PointD const & pt, int zoomLevel /* = -1 */, bool isAnim /* = true */) { - if (m_drapeEngine != nullptr) - m_drapeEngine->SetModelViewCenter(pt, zoomLevel, isAnim, false /* trackVisibleViewport */); +// if (m_drapeEngines.begin()->second.m_drapeEngine) +// m_drapeEngines.begin()->second.m_drapeEngine->SetModelViewCenter(pt, zoomLevel, isAnim, false /* trackVisibleViewport */); } m2::RectD Framework::GetCurrentViewport() const @@ -946,9 +956,9 @@ m2::RectD Framework::GetCurrentViewport() const return m_currentModelView.ClipRect(); } -void Framework::SetVisibleViewport(m2::RectD const & rect) +void Framework::SetVisibleViewport(df::DrapeEngineId engineId, m2::RectD const & rect) { - if (m_drapeEngine == nullptr) + if (!m_drapeEngines[engineId].m_drapeEngine) return; double constexpr kEps = 0.5; @@ -960,22 +970,22 @@ void Framework::SetVisibleViewport(m2::RectD const & rect) return; m_visibleViewport = rect; - m_drapeEngine->SetVisibleViewport(rect); + m_drapeEngines[engineId].m_drapeEngine->SetVisibleViewport(rect); } void Framework::ShowRect(m2::RectD const & rect, int maxScale, bool animation, bool useVisibleViewport) { - if (m_drapeEngine == nullptr) - return; - - m_drapeEngine->SetModelViewRect(rect, true /* applyRotation */, maxScale /* zoom */, animation, - useVisibleViewport); +// if (!m_drapeEngines.begin()->second.m_drapeEngine) +// return; +// +// m_drapeEngines.begin()->second.m_drapeEngine->SetModelViewRect(rect, true /* applyRotation */, maxScale /* zoom */, animation, +// useVisibleViewport); } void Framework::ShowRect(m2::AnyRectD const & rect, bool animation, bool useVisibleViewport) { - if (m_drapeEngine != nullptr) - m_drapeEngine->SetModelViewAnyRect(rect, animation, useVisibleViewport); +// if (m_drapeEngines.begin()->second.m_drapeEngine) +// m_drapeEngines.begin()->second.m_drapeEngine->SetModelViewAnyRect(rect, animation, useVisibleViewport); } void Framework::GetTouchRect(m2::PointD const & center, uint32_t pxRadius, m2::AnyRectD & rect) @@ -991,26 +1001,26 @@ void Framework::SetViewportListener(TViewportChangedFn const & fn) #if defined(OMIM_OS_MAC) || defined(OMIM_OS_LINUX) void Framework::NotifyGraphicsReady(TGraphicsReadyFn const & fn, bool needInvalidate) { - if (m_drapeEngine != nullptr) - m_drapeEngine->NotifyGraphicsReady(fn, needInvalidate); +// if (m_drapeEngines.begin()->second.m_drapeEngine) +// m_drapeEngines.begin()->second.m_drapeEngine->NotifyGraphicsReady(fn, needInvalidate); } #endif void Framework::StopLocationFollow() { - if (m_drapeEngine != nullptr) - m_drapeEngine->StopLocationFollow(); +// if (m_drapeEngines.begin()->second.m_drapeEngine) +// m_drapeEngines.begin()->second.m_drapeEngine->StopLocationFollow(); } -void Framework::OnSize(int w, int h) +void Framework::OnSize(df::DrapeEngineId engineId, int w, int h) { - if (m_drapeEngine != nullptr) - m_drapeEngine->Resize(std::max(w, 2), std::max(h, 2)); + if (m_drapeEngines[engineId].m_drapeEngine != nullptr) + m_drapeEngines[engineId].m_drapeEngine->Resize(std::max(w, 2), std::max(h, 2)); /// @todo Expected that DrapeEngine::Resize does all the work, but nope .. /// - Strange, but seems like iOS works fine without it. /// - Test Android screen orientation and position mark in map and navigation modes. - SetVisibleViewport(m2::RectD(0, 0, w, h)); + SetVisibleViewport(engineId,m2::RectD(0, 0, w, h)); } namespace @@ -1024,57 +1034,57 @@ double ScaleModeToFactor(Framework::EScaleMode mode) } // namespace -void Framework::Scale(EScaleMode mode, bool isAnim) +void Framework::Scale(df::DrapeEngineId engineId, EScaleMode mode, bool isAnim) { - Scale(ScaleModeToFactor(mode), isAnim); + Scale(engineId, ScaleModeToFactor(mode), isAnim); } -void Framework::Scale(Framework::EScaleMode mode, m2::PointD const & pxPoint, bool isAnim) +void Framework::Scale(df::DrapeEngineId engineId, Framework::EScaleMode mode, m2::PointD const & pxPoint, bool isAnim) { - Scale(ScaleModeToFactor(mode), pxPoint, isAnim); + Scale(engineId, ScaleModeToFactor(mode), pxPoint, isAnim); } -void Framework::Scale(double factor, bool isAnim) +void Framework::Scale(df::DrapeEngineId engineId, double factor, bool isAnim) { - Scale(factor, GetVisiblePixelCenter(), isAnim); + Scale(engineId, factor, GetVisiblePixelCenter(), isAnim); } -void Framework::Scale(double factor, m2::PointD const & pxPoint, bool isAnim) +void Framework::Scale(df::DrapeEngineId engineId, double factor, m2::PointD const & pxPoint, bool isAnim) { - if (m_drapeEngine != nullptr) - m_drapeEngine->Scale(factor, pxPoint, isAnim); + if (m_drapeEngines[engineId].m_drapeEngine != nullptr) + m_drapeEngines[engineId].m_drapeEngine->Scale(factor, pxPoint, isAnim); } -void Framework::Move(double factorX, double factorY, bool isAnim) +void Framework::Move(df::DrapeEngineId engineId, double factorX, double factorY, bool isAnim) { - if (m_drapeEngine != nullptr) - m_drapeEngine->Move(factorX, factorY, isAnim); + if (m_drapeEngines[engineId].m_drapeEngine != nullptr) + m_drapeEngines[engineId].m_drapeEngine->Move(factorX, factorY, isAnim); } void Framework::Rotate(double azimuth, bool isAnim) { - if (m_drapeEngine != nullptr) - m_drapeEngine->Rotate(azimuth, isAnim); +// if (m_drapeEngines.begin()->second.m_drapeEngine != nullptr) +// m_drapeEngines.begin()->second.m_drapeEngine->Rotate(azimuth, isAnim); } -void Framework::TouchEvent(df::TouchEvent const & touch) +void Framework::TouchEvent(df::DrapeEngineId engineId, df::TouchEvent const & touch) { - if (m_drapeEngine != nullptr) - m_drapeEngine->AddTouchEvent(touch); + if (m_drapeEngines[engineId].m_drapeEngine != nullptr) + m_drapeEngines[engineId].m_drapeEngine->AddTouchEvent(touch); } int Framework::GetDrawScale() const { - if (m_drapeEngine != nullptr) - return df::GetDrawTileScale(m_currentModelView); +// if (m_drapeEngines.begin()->second.m_drapeEngine != nullptr) +// return df::GetDrawTileScale(m_currentModelView); return 0; } void Framework::RunFirstLaunchAnimation() { - if (m_drapeEngine != nullptr) - m_drapeEngine->RunFirstLaunchAnimation(); +// if (m_drapeEngines.begin()->second.m_drapeEngine != nullptr) +// m_drapeEngines.begin()->second.m_drapeEngine->RunFirstLaunchAnimation(); } bool Framework::IsCountryLoadedByName(string_view name) const @@ -1084,8 +1094,8 @@ bool Framework::IsCountryLoadedByName(string_view name) const void Framework::InvalidateRect(m2::RectD const & rect) { - if (m_drapeEngine != nullptr) - m_drapeEngine->InvalidateRect(rect); +// if (m_drapeEngines.begin()->second.m_drapeEngine != nullptr) +// m_drapeEngines.begin()->second.m_drapeEngine->InvalidateRect(rect); } void Framework::ClearAllCaches() @@ -1128,8 +1138,9 @@ void Framework::MemoryWarning() void Framework::EnterBackground() { - if (m_drapeEngine) - m_drapeEngine->OnEnterBackground(); + for (const auto& [engineId, drapeEngineData] : m_drapeEngines) + if (drapeEngineData.m_drapeEngine) + drapeEngineData.m_drapeEngine->OnEnterBackground(); SaveViewport(); @@ -1145,8 +1156,9 @@ void Framework::EnterBackground() void Framework::EnterForeground() { - if (m_drapeEngine) - m_drapeEngine->OnEnterForeground(); + for (const auto& [engineId, drapeEngineData] : m_drapeEngines) + if (drapeEngineData.m_drapeEngine) + drapeEngineData.m_drapeEngine->OnEnterForeground(); m_trafficManager.OnEnterForeground(); } @@ -1275,12 +1287,12 @@ void Framework::SelectSearchResult(search::Result const & result, bool animation m_currentPlacePageInfo = BuildPlacePageInfo(info); if (m_currentPlacePageInfo) { - if (m_drapeEngine) { - if (scale < 0) - scale = GetFeatureViewportScale(m_currentPlacePageInfo->GetTypes()); - m2::PointD const center = m_currentPlacePageInfo->GetMercator(); - m_drapeEngine->SetModelViewCenter(center, scale, animation, true /* trackVisibleViewport */); - } +// if (m_drapeEngines.begin()->second.m_drapeEngine) { +// if (scale < 0) +// scale = GetFeatureViewportScale(m_currentPlacePageInfo->GetTypes()); +// m2::PointD const center = m_currentPlacePageInfo->GetMercator(); +// m_drapeEngines.begin()->second.m_drapeEngine->SetModelViewCenter(center, scale, animation, true /* trackVisibleViewport */); +// } ActivateMapSelection(); } @@ -1422,7 +1434,14 @@ bool Framework::GetDistanceAndAzimut(m2::PointD const & point, return (d < 25000.0); } -void Framework::CreateDrapeEngine(ref_ptr contextFactory, DrapeCreationParams && params) +df::DrapeEngineId Framework::CreateDrapeEngineId() +{ + df::DrapeEngineId engineId = static_cast(random()); + m_drapeEngines[engineId]; + return engineId; +} + +void Framework::CreateDrapeEngine(df::DrapeEngineId engineId, ref_ptr contextFactory, DrapeCreationParams && params) { auto idReadFn = [this](df::MapDataProvider::TReadCallback const & fn, m2::RectD const & r, @@ -1451,12 +1470,12 @@ void Framework::CreateDrapeEngine(ref_ptr contextFac { }; - auto onGraphicsContextInitialized = [this]() + auto onGraphicsContextInitialized = [this, engineId]() { - GetPlatform().RunTask(Platform::Thread::Gui, [this]() + GetPlatform().RunTask(Platform::Thread::Gui, [this, engineId]() { - if (m_onGraphicsContextInitialized) - m_onGraphicsContextInitialized(); + if (m_drapeEngines[engineId].m_onGraphicsContextInitialized) + m_drapeEngines[engineId].m_onGraphicsContextInitialized(); }); }; @@ -1488,26 +1507,27 @@ void Framework::CreateDrapeEngine(ref_ptr contextFac isAutozoomEnabled, simplifiedTrafficColors, std::move(overlaysShowStatsFn), std::move(onGraphicsContextInitialized)); - m_drapeEngine = make_unique_dp(std::move(p)); - m_drapeEngine->SetModelViewListener([this](ScreenBase const & screen) + DrapeEngineData &drapeEngineData = m_drapeEngines[engineId]; + drapeEngineData.m_drapeEngine = make_unique_dp(std::move(p)); + drapeEngineData.m_drapeEngine->SetModelViewListener([this](ScreenBase const & screen) { GetPlatform().RunTask(Platform::Thread::Gui, [this, screen](){ OnViewportChanged(screen); }); }); - m_drapeEngine->SetTapEventInfoListener([this](df::TapInfo const & tapInfo) + drapeEngineData.m_drapeEngine->SetTapEventInfoListener([this](df::TapInfo const & tapInfo) { GetPlatform().RunTask(Platform::Thread::Gui, [this, tapInfo]() { OnTapEvent(place_page::BuildInfo(tapInfo)); }); }); - m_drapeEngine->SetUserPositionListener([this](m2::PointD const & position, bool hasPosition) + drapeEngineData.m_drapeEngine->SetUserPositionListener([this](m2::PointD const & position, bool hasPosition) { GetPlatform().RunTask(Platform::Thread::Gui, [this, position, hasPosition]() { OnUserPositionChanged(position, hasPosition); }); }); - m_drapeEngine->SetUserPositionPendingTimeoutListener([this]() + drapeEngineData.m_drapeEngine->SetUserPositionPendingTimeoutListener([this]() { GetPlatform().RunTask(Platform::Thread::Gui, [this]() { @@ -1516,7 +1536,7 @@ void Framework::CreateDrapeEngine(ref_ptr contextFac }); }); - OnSize(params.m_surfaceWidth, params.m_surfaceHeight); + OnSize(engineId, params.m_surfaceWidth, params.m_surfaceHeight); Allow3dMode(allow3d, allow3dBuildings); @@ -1525,15 +1545,15 @@ void Framework::CreateDrapeEngine(ref_ptr contextFac if (m_connectToGpsTrack) GpsTracker::Instance().Connect(bind(&Framework::OnUpdateGpsTrackPointsCallback, this, _1, _2)); - GetBookmarkManager().SetDrapeEngine(make_ref(m_drapeEngine)); - m_drapeApi.SetDrapeEngine(make_ref(m_drapeEngine)); - m_routingManager.SetDrapeEngine(make_ref(m_drapeEngine), allow3d); - m_trafficManager.SetDrapeEngine(make_ref(m_drapeEngine)); - m_transitManager.SetDrapeEngine(make_ref(m_drapeEngine)); - m_isolinesManager.SetDrapeEngine(make_ref(m_drapeEngine)); - m_searchMarks.SetDrapeEngine(make_ref(m_drapeEngine)); +// GetBookmarkManager().SetDrapeEngine(make_ref(drapeEngineData.m_drapeEngine)); + drapeEngineData.m_drapeApi.SetDrapeEngine(make_ref(drapeEngineData.m_drapeEngine)); +// m_routingManager.SetDrapeEngine(make_ref(drapeEngineData.m_drapeEngine), allow3d); +// m_trafficManager.SetDrapeEngine(make_ref(drapeEngineData.m_drapeEngine)); +// m_transitManager.SetDrapeEngine(make_ref(drapeEngineData.m_drapeEngine)); +// m_isolinesManager.SetDrapeEngine(make_ref(drapeEngineData.m_drapeEngine)); +// m_searchMarks.SetDrapeEngine(make_ref(drapeEngineData.m_drapeEngine)); - InvalidateUserMarks(); +// InvalidateUserMarks(); auto const transitSchemeEnabled = LoadTransitSchemeEnabled(); m_transitManager.EnableTransitSchemeMode(transitSchemeEnabled); @@ -1543,20 +1563,21 @@ void Framework::CreateDrapeEngine(ref_ptr contextFac if (!settings::Get(kShowDebugInfo, showDebugInfo)) showDebugInfo = false; if (showDebugInfo) - m_drapeEngine->ShowDebugInfo(showDebugInfo); + drapeEngineData.m_drapeEngine->ShowDebugInfo(showDebugInfo); benchmark::RunGraphicsBenchmark(this); } -void Framework::OnRecoverSurface(int width, int height, bool recreateContextDependentResources) +void Framework::OnRecoverSurface(df::DrapeEngineId engineId, int width, int height, bool recreateContextDependentResources) { - if (m_drapeEngine) + DrapeEngineData &drapeEngineData = m_drapeEngines[engineId]; + if (drapeEngineData.m_drapeEngine) { - m_drapeEngine->RecoverSurface(width, height, recreateContextDependentResources); + drapeEngineData.m_drapeEngine->RecoverSurface(width, height, recreateContextDependentResources); InvalidateUserMarks(); - m_drapeApi.Invalidate(); + drapeEngineData.m_drapeApi.Invalidate(); } m_trafficManager.OnRecoverSurface(); @@ -1571,86 +1592,88 @@ void Framework::OnDestroySurface() void Framework::UpdateVisualScale(double vs) { - if (m_drapeEngine != nullptr) - m_drapeEngine->UpdateVisualScale(vs, m_isRenderingEnabled); +// if (m_drapeEngines.begin()->second.m_drapeEngine != nullptr) +// m_drapeEngines.begin()->second.m_drapeEngine->UpdateVisualScale(vs, m_isRenderingEnabled); } void Framework::UpdateMyPositionRoutingOffset(bool useDefault, int offsetY) { - if (m_drapeEngine != nullptr) - m_drapeEngine->UpdateMyPositionRoutingOffset(useDefault, offsetY); +// if (m_drapeEngines.begin()->second.m_drapeEngine != nullptr) +// m_drapeEngines.begin()->second.m_drapeEngine->UpdateMyPositionRoutingOffset(useDefault, offsetY); } -ref_ptr Framework::GetDrapeEngine() +ref_ptr Framework::GetDrapeEngine(df::DrapeEngineId engineId) { - return make_ref(m_drapeEngine); + return make_ref(m_drapeEngines[engineId].m_drapeEngine); } void Framework::DestroyDrapeEngine() { - if (m_drapeEngine != nullptr) + for (auto& [engineId, drapeEngineData] : m_drapeEngines) { - m_drapeApi.SetDrapeEngine(nullptr); - m_routingManager.SetDrapeEngine(nullptr, false); - m_trafficManager.SetDrapeEngine(nullptr); - m_transitManager.SetDrapeEngine(nullptr); - m_isolinesManager.SetDrapeEngine(nullptr); - m_searchMarks.SetDrapeEngine(nullptr); - GetBookmarkManager().SetDrapeEngine(nullptr); - + if (drapeEngineData.m_drapeEngine != nullptr) + { + drapeEngineData.m_drapeApi.SetDrapeEngine(nullptr); + m_routingManager.SetDrapeEngine(nullptr, false); + m_trafficManager.SetDrapeEngine(nullptr); + m_transitManager.SetDrapeEngine(nullptr); + m_isolinesManager.SetDrapeEngine(nullptr); + m_searchMarks.SetDrapeEngine(nullptr); + GetBookmarkManager().SetDrapeEngine(nullptr); + drapeEngineData.m_drapeEngine.reset(); + } m_trafficManager.Teardown(); GpsTracker::Instance().Disconnect(); - m_drapeEngine.reset(); } } void Framework::SetRenderingEnabled(ref_ptr contextFactory) { m_isRenderingEnabled = true; - if (m_drapeEngine) - m_drapeEngine->SetRenderingEnabled(contextFactory); +// if (m_drapeEngines.begin()->second.m_drapeEngine) +// m_drapeEngines.begin()->second.m_drapeEngine->SetRenderingEnabled(contextFactory); } void Framework::SetRenderingDisabled(bool destroySurface) { m_isRenderingEnabled = false; - if (m_drapeEngine) - m_drapeEngine->SetRenderingDisabled(destroySurface); +// if (m_drapeEngines.begin()->second.m_drapeEngine) +// m_drapeEngines.begin()->second.m_drapeEngine->SetRenderingDisabled(destroySurface); } -void Framework::SetGraphicsContextInitializationHandler(df::OnGraphicsContextInitialized && handler) +void Framework::SetGraphicsContextInitializationHandler(df::DrapeEngineId engineId, df::OnGraphicsContextInitialized && handler) { - m_onGraphicsContextInitialized = std::move(handler); + m_drapeEngines[engineId].m_onGraphicsContextInitialized = std::move(handler); } void Framework::EnableDebugRectRendering(bool enabled) { - if (m_drapeEngine) - m_drapeEngine->EnableDebugRectRendering(enabled); +// if (m_drapeEngines.begin()->second.m_drapeEngine) +// m_drapeEngines.begin()->second.m_drapeEngine->EnableDebugRectRendering(enabled); } void Framework::ConnectToGpsTracker() { m_connectToGpsTrack = true; - if (m_drapeEngine) - { - m_drapeEngine->ClearGpsTrackPoints(); - GpsTracker::Instance().Connect(bind(&Framework::OnUpdateGpsTrackPointsCallback, this, _1, _2)); - } +// if (m_drapeEngines.begin()->second.m_drapeEngine) +// { +// m_drapeEngines.begin()->second.m_drapeEngine->ClearGpsTrackPoints(); +// GpsTracker::Instance().Connect(bind(&Framework::OnUpdateGpsTrackPointsCallback, this, _1, _2)); +// } } void Framework::DisconnectFromGpsTracker() { m_connectToGpsTrack = false; GpsTracker::Instance().Disconnect(); - if (m_drapeEngine) - m_drapeEngine->ClearGpsTrackPoints(); +// if (m_drapeEngines.begin()->second.m_drapeEngine) +// m_drapeEngines.begin()->second.m_drapeEngine->ClearGpsTrackPoints(); } void Framework::OnUpdateGpsTrackPointsCallback(vector> && toAdd, pair const & toRemove) { - ASSERT(m_drapeEngine.get() != nullptr, ()); +// ASSERT(m_drapeEngines.begin()->second.m_drapeEngine.get() != nullptr, ()); vector pointsAdd; pointsAdd.reserve(toAdd.size()); @@ -1674,7 +1697,7 @@ void Framework::OnUpdateGpsTrackPointsCallback(vectorUpdateGpsTrackPoints(std::move(pointsAdd), std::move(indicesRemove)); +// m_drapeEngines.begin()->second.m_drapeEngine->UpdateGpsTrackPoints(std::move(pointsAdd), std::move(indicesRemove)); } void Framework::MarkMapStyle(MapStyle mapStyle) @@ -1694,11 +1717,11 @@ void Framework::MarkMapStyle(MapStyle mapStyle) void Framework::SetMapStyle(MapStyle mapStyle) { - MarkMapStyle(mapStyle); - if (m_drapeEngine != nullptr) - m_drapeEngine->UpdateMapStyle(); - InvalidateUserMarks(); - UpdateMinBuildingsTapZoom(); +// MarkMapStyle(mapStyle); +// if (m_drapeEngines.begin()->second.m_drapeEngine != nullptr) +// m_drapeEngines.begin()->second.m_drapeEngine->UpdateMapStyle(); +// InvalidateUserMarks(); +// UpdateMinBuildingsTapZoom(); } MapStyle Framework::GetMapStyle() const @@ -1713,10 +1736,10 @@ void Framework::SetupMeasurementSystem() m_routingManager.SetTurnNotificationsUnits(measurement_utils::GetMeasurementUnits()); } -void Framework::SetWidgetLayout(gui::TWidgetsLayoutInfo && layout) +void Framework::SetWidgetLayout(df::DrapeEngineId engineId, gui::TWidgetsLayoutInfo && layout) { - ASSERT(m_drapeEngine != nullptr, ()); - m_drapeEngine->SetWidgetLayout(std::move(layout)); + ASSERT(m_drapeEngines[engineId].m_drapeEngine != nullptr, ()); + m_drapeEngines[engineId].m_drapeEngine->SetWidgetLayout(std::move(layout)); } bool Framework::ShowMapForURL(string const & url) @@ -1776,8 +1799,8 @@ bool Framework::ShowMapForURL(string const & url) // ShowRect function interferes with ActivateMapSelection and we have strange behaviour as a result. // Use more obvious SetModelViewCenter here. - if (m_drapeEngine) - m_drapeEngine->SetModelViewCenter(point, scale, true, true); +// if (m_drapeEngines.begin()->second.m_drapeEngine) +// m_drapeEngines.begin()->second.m_drapeEngine->SetModelViewCenter(point, scale, true, true); if (result != NO_NEED_CLICK) { @@ -1971,12 +1994,12 @@ void Framework::ActivateMapSelection() auto const selObj = m_currentPlacePageInfo->GetSelectedObject(); CHECK_NOT_EQUAL(selObj, df::SelectionShape::OBJECT_EMPTY, ("Empty selections are impossible.")); - if (m_drapeEngine) - { - auto const & bi = m_currentPlacePageInfo->GetBuildInfo(); - m_drapeEngine->SelectObject(selObj, m_currentPlacePageInfo->GetMercator(), featureId, - bi.m_needAnimationOnSelection, bi.m_isGeometrySelectionAllowed, true); - } +// if (m_drapeEngines.begin()->second.m_drapeEngine) +// { +// auto const & bi = m_currentPlacePageInfo->GetBuildInfo(); +// m_drapeEngines.begin()->second.m_drapeEngine->SelectObject(selObj, m_currentPlacePageInfo->GetMercator(), featureId, +// bi.m_needAnimationOnSelection, bi.m_isGeometrySelectionAllowed, true); +// } /// @todo Current android logic is strange (see SetPlacePageListeners comments), so skip assert. //ASSERT(m_onPlacePageOpen, ()); @@ -1998,8 +2021,8 @@ void Framework::DeactivateMapSelection(bool notifyUI) m_currentPlacePageInfo = {}; - if (m_drapeEngine != nullptr) - m_drapeEngine->DeselectObject(); +// if (m_drapeEngines.begin()->second.m_drapeEngine != nullptr) +// m_drapeEngines.begin()->second.m_drapeEngine->DeselectObject(); } } @@ -2047,13 +2070,13 @@ void Framework::OnTapEvent(place_page::BuildInfo const & buildInfo) { if (m_currentPlacePageInfo->GetTrackId() == prevTrackId) { - if (m_drapeEngine) - { - m_drapeEngine->SelectObject(df::SelectionShape::ESelectedObject::OBJECT_TRACK, - m_currentPlacePageInfo->GetMercator(), FeatureID(), - false /* isAnim */, false /* isGeometrySelectionAllowed */, - true /* isSelectionShapeVisible */); - } +// if (m_drapeEngines.begin()->second.m_drapeEngine) +// { +// m_drapeEngines.begin()->second.m_drapeEngine->SelectObject(df::SelectionShape::ESelectedObject::OBJECT_TRACK, +// m_currentPlacePageInfo->GetMercator(), FeatureID(), +// false /* isAnim */, false /* isGeometrySelectionAllowed */, +// true /* isSelectionShapeVisible */); +// } return; } GetBookmarkManager().UpdateElevationMyPosition(m_currentPlacePageInfo->GetTrackId()); @@ -2071,8 +2094,8 @@ void Framework::OnTapEvent(place_page::BuildInfo const & buildInfo) void Framework::InvalidateRendering() { - if (m_drapeEngine) - m_drapeEngine->Invalidate(); + if (m_drapeEngines.begin()->second.m_drapeEngine) + m_drapeEngines.begin()->second.m_drapeEngine->Invalidate(); } void Framework::UpdateMinBuildingsTapZoom() @@ -2198,16 +2221,16 @@ std::optional Framework::BuildPlacePageInfo( auto const isFeatureMatchingEnabled = buildInfo.IsFeatureMatchingEnabled(); // Using VisualParams inside FindTrackInTapPosition/GetDefaultTapRect requires drapeEngine. - if (m_drapeEngine != nullptr && buildInfo.IsTrackMatchingEnabled() && !buildInfo.m_isLongTap && - !(isFeatureMatchingEnabled && selectedFeature.IsValid())) - { - auto const trackSelectionInfo = FindTrackInTapPosition(buildInfo); - if (trackSelectionInfo.m_trackId != kml::kInvalidTrackId) - { - BuildTrackPlacePage(trackSelectionInfo, outInfo); - return outInfo; - } - } +// if (m_drapeEngines.begin()->second.m_drapeEngine != nullptr && buildInfo.IsTrackMatchingEnabled() && !buildInfo.m_isLongTap && +// !(isFeatureMatchingEnabled && selectedFeature.IsValid())) +// { +// auto const trackSelectionInfo = FindTrackInTapPosition(buildInfo); +// if (trackSelectionInfo.m_trackId != kml::kInvalidTrackId) +// { +// BuildTrackPlacePage(trackSelectionInfo, outInfo); +// return outInfo; +// } +// } if (isFeatureMatchingEnabled && !selectedFeature.IsValid()) selectedFeature = FindBuildingAtPoint(buildInfo.m_mercator); @@ -2402,16 +2425,16 @@ void Framework::SaveTransliteration(bool allowTranslit) void Framework::Allow3dMode(bool allow3d, bool allow3dBuildings) { - if (m_drapeEngine == nullptr) - return; - - if (!m_powerManager.IsFacilityEnabled(power_management::Facility::PerspectiveView)) - allow3d = false; - - if (!m_powerManager.IsFacilityEnabled(power_management::Facility::Buildings3d)) - allow3dBuildings = false; - - m_drapeEngine->Allow3dMode(allow3d, allow3dBuildings); +// if (m_drapeEngines.begin()->second.m_drapeEngine == nullptr) +// return; +// +// if (!m_powerManager.IsFacilityEnabled(power_management::Facility::PerspectiveView)) +// allow3d = false; +// +// if (!m_powerManager.IsFacilityEnabled(power_management::Facility::Buildings3d)) +// allow3dBuildings = false; +// +// m_drapeEngines.begin()->second.m_drapeEngine->Allow3dMode(allow3d, allow3dBuildings); } void Framework::Save3dMode(bool allow3d, bool allow3dBuildings) @@ -2446,10 +2469,10 @@ void Framework::SetLargeFontsSize(bool isLargeSize) { double const scaleFactor = isLargeSize ? kLargeFontsScaleFactor : 1.0; - ASSERT(m_drapeEngine.get() != nullptr, ()); - m_drapeEngine->SetFontScaleFactor(scaleFactor); +// ASSERT(m_drapeEngines.begin()->second.m_drapeEngine.get() != nullptr, ()); +// m_drapeEngines.begin()->second.m_drapeEngine->SetFontScaleFactor(scaleFactor); - InvalidateRect(GetCurrentViewport()); +// InvalidateRect(GetCurrentViewport()); } bool Framework::LoadTrafficEnabled() @@ -2491,8 +2514,8 @@ void Framework::AllowAutoZoom(bool allowAutoZoom) routing::RouterType const type = m_routingManager.GetRouter(); bool const isPedestrianRoute = type == RouterType::Pedestrian; - if (m_drapeEngine != nullptr) - m_drapeEngine->AllowAutoZoom(allowAutoZoom && !isPedestrianRoute); +// if (m_drapeEngines.begin()->second.m_drapeEngine != nullptr) +// m_drapeEngines.begin()->second.m_drapeEngine->AllowAutoZoom(allowAutoZoom && !isPedestrianRoute); } void Framework::SaveAutoZoom(bool allowAutoZoom) @@ -2529,11 +2552,11 @@ void Framework::SaveIsolinesEnabled(bool enabled) void Framework::EnableChoosePositionMode(bool enable, bool enableBounds, bool applyPosition, m2::PointD const & position) { - if (m_drapeEngine != nullptr) - { - m_drapeEngine->EnableChoosePositionMode(enable, - enableBounds ? GetSelectedFeatureTriangles() : vector(), applyPosition, position); - } +// if (m_drapeEngines.begin()->second.m_drapeEngine != nullptr) +// { +// m_drapeEngines.begin()->second.m_drapeEngine->EnableChoosePositionMode(enable, +// enableBounds ? GetSelectedFeatureTriangles() : vector(), applyPosition, position); +// } } vector Framework::GetSelectedFeatureTriangles() const @@ -2561,8 +2584,8 @@ vector Framework::GetSelectedFeatureTriangles() const void Framework::BlockTapEvents(bool block) { - if (m_drapeEngine != nullptr) - m_drapeEngine->BlockTapEvents(block); +// if (m_drapeEngines.begin()->second.m_drapeEngine != nullptr) +// m_drapeEngines.begin()->second.m_drapeEngine->BlockTapEvents(block); } bool Framework::ParseDrapeDebugCommand(string const & query) @@ -2589,14 +2612,14 @@ bool Framework::ParseDrapeDebugCommand(string const & query) if (query == "?aa" || query == "effect:antialiasing") { - m_drapeEngine->SetPosteffectEnabled(df::PostprocessRenderer::Antialiasing, - true /* enabled */); +// m_drapeEngines.begin()->second.m_drapeEngine->SetPosteffectEnabled(df::PostprocessRenderer::Antialiasing, +// true /* enabled */); return true; } if (query == "?no-aa" || query == "effect:no-antialiasing") { - m_drapeEngine->SetPosteffectEnabled(df::PostprocessRenderer::Antialiasing, - false /* enabled */); +// m_drapeEngines.begin()->second.m_drapeEngine->SetPosteffectEnabled(df::PostprocessRenderer::Antialiasing, +// false /* enabled */); return true; } if (query == "?scheme") @@ -2621,29 +2644,29 @@ bool Framework::ParseDrapeDebugCommand(string const & query) } if (query == "?debug-info") { - m_drapeEngine->ShowDebugInfo(true /* shown */); +// m_drapeEngines.begin()->second.m_drapeEngine->ShowDebugInfo(true /* shown */); return true; } if (query == "?debug-info-always") { - m_drapeEngine->ShowDebugInfo(true /* shown */); - settings::Set(kShowDebugInfo, true); +// m_drapeEngines.begin()->second.m_drapeEngine->ShowDebugInfo(true /* shown */); +// settings::Set(kShowDebugInfo, true); return true; } if (query == "?no-debug-info") { - m_drapeEngine->ShowDebugInfo(false /* shown */); - settings::Set(kShowDebugInfo, false); +// m_drapeEngines.begin()->second.m_drapeEngine->ShowDebugInfo(false /* shown */); +// settings::Set(kShowDebugInfo, false); return true; } if (query == "?debug-rect") { - m_drapeEngine->EnableDebugRectRendering(true /* shown */); +// m_drapeEngines.begin()->second.m_drapeEngine->EnableDebugRectRendering(true /* shown */); return true; } if (query == "?no-debug-rect") { - m_drapeEngine->EnableDebugRectRendering(false /* shown */); +// m_drapeEngines.begin()->second.m_drapeEngine->EnableDebugRectRendering(false /* shown */); return true; } #if defined(OMIM_METAL_AVAILABLE) @@ -3168,7 +3191,7 @@ void Framework::OnRouteFollow(routing::RouterType type) // TODO. We need to sync two enums VehicleType and RouterType to be able to pass // GetRoutingSettings(type).m_matchRoute to the FollowRoute() instead of |isPedestrianRoute|. // |isArrowGlued| parameter fully corresponds to |m_matchRoute| in RoutingSettings. - m_drapeEngine->FollowRoute(scale, scale3d, enableAutoZoom, !isPedestrianRoute /* isArrowGlued */); + m_drapeEngines.begin()->second.m_drapeEngine->FollowRoute(scale, scale3d, enableAutoZoom, !isPedestrianRoute /* isArrowGlued */); } // RoutingManager::Delegate diff --git a/map/framework.hpp b/map/framework.hpp index f5426438a6..a1041a7c96 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -162,7 +162,13 @@ protected: using TViewportChangedFn = df::DrapeEngine::ModelViewChangedHandler; TViewportChangedFn m_viewportChangedFn; - drape_ptr m_drapeEngine; + struct DrapeEngineData + { + drape_ptr m_drapeEngine; + df::OnGraphicsContextInitialized m_onGraphicsContextInitialized; + df::DrapeApi m_drapeApi; + }; + std::unordered_map m_drapeEngines; StorageDownloadingPolicy m_storageDownloadingPolicy; storage::Storage m_storage; @@ -175,8 +181,6 @@ protected: SearchMarks m_searchMarks; - df::DrapeApi m_drapeApi; - bool m_isRenderingEnabled; // Note. |m_powerManager| should be declared before |m_routingManager| @@ -213,7 +217,7 @@ public: explicit Framework(FrameworkParams const & params = {}); virtual ~Framework() override; - df::DrapeApi & GetDrapeApi() { return m_drapeApi; } + df::DrapeApi & GetDrapeApi() { return m_drapeEngines.begin()->second.m_drapeApi; } /// \returns true if there're unsaved changes in map with |countryId| and false otherwise. /// \note It works for group and leaf node. @@ -394,17 +398,18 @@ public: df::Hints m_hints; }; - void CreateDrapeEngine(ref_ptr contextFactory, DrapeCreationParams && params); - ref_ptr GetDrapeEngine(); - bool IsDrapeEngineCreated() const { return m_drapeEngine != nullptr; } + df::DrapeEngineId CreateDrapeEngineId(); + void CreateDrapeEngine(df::DrapeEngineId engineId, ref_ptr contextFactory, DrapeCreationParams && params); + ref_ptr GetDrapeEngine(df::DrapeEngineId engineId); + bool IsDrapeEngineCreated(df::DrapeEngineId engineId) const { return m_drapeEngines.count(engineId) != 0 && m_drapeEngines.at(engineId).m_drapeEngine != nullptr; } void DestroyDrapeEngine(); /// Called when graphics engine should be temporarily paused and then resumed. void SetRenderingEnabled(ref_ptr contextFactory = nullptr); void SetRenderingDisabled(bool destroySurface); - void SetGraphicsContextInitializationHandler(df::OnGraphicsContextInitialized && handler); + void SetGraphicsContextInitializationHandler(df::DrapeEngineId engineId, df::OnGraphicsContextInitialized && handler); - void OnRecoverSurface(int width, int height, bool recreateContextDependentResources); + void OnRecoverSurface(df::DrapeEngineId engineId, int width, int height, bool recreateContextDependentResources); void OnDestroySurface(); void UpdateVisualScale(double vs); @@ -419,8 +424,6 @@ private: /// Depends on initialized Drape engine. void LoadViewport(); - df::OnGraphicsContextInitialized m_onGraphicsContextInitialized; - public: void ConnectToGpsTracker(); void DisconnectFromGpsTracker(); @@ -431,7 +434,7 @@ public: void SetupMeasurementSystem(); - void SetWidgetLayout(gui::TWidgetsLayoutInfo && layout); + void SetWidgetLayout(df::DrapeEngineId engineId, gui::TWidgetsLayoutInfo && layout); void PrepareToShutdown(); @@ -493,7 +496,7 @@ public: void SetViewportCenter(m2::PointD const & pt, int zoomLevel = -1, bool isAnim = true); m2::RectD GetCurrentViewport() const; - void SetVisibleViewport(m2::RectD const & rect); + void SetVisibleViewport(df::DrapeEngineId engineId, m2::RectD const & rect); /// - Check minimal visible scale according to downloaded countries. void ShowRect(m2::RectD const & rect, int maxScale = -1, bool animation = true, @@ -512,7 +515,7 @@ public: void StopLocationFollow(); /// Resize event from window. - void OnSize(int w, int h); + void OnSize(df::DrapeEngineId engineId, int w, int h); enum EScaleMode { @@ -522,20 +525,20 @@ public: SCALE_MIN_LIGHT }; - void Scale(EScaleMode mode, bool isAnim); - void Scale(EScaleMode mode, m2::PointD const & pxPoint, bool isAnim); - void Scale(double factor, bool isAnim); - void Scale(double factor, m2::PointD const & pxPoint, bool isAnim); + void Scale(df::DrapeEngineId engineId, EScaleMode mode, bool isAnim); + void Scale(df::DrapeEngineId engineId, EScaleMode mode, m2::PointD const & pxPoint, bool isAnim); + void Scale(df::DrapeEngineId engineId, double factor, bool isAnim); + void Scale(df::DrapeEngineId engineId, double factor, m2::PointD const & pxPoint, bool isAnim); /// Moves the viewport a distance of factorX * viewportWidth and factorY * viewportHeight. /// E.g. factorX == 1.0 moves the map one screen size to the right, factorX == -0.5 moves the map /// half screen size to the left, factorY == -2.0 moves the map two sizes down, /// factorY = 1.5 moves the map one and a half size up. - void Move(double factorX, double factorY, bool isAnim); + void Move(df::DrapeEngineId engineId, double factorX, double factorY, bool isAnim); void Rotate(double azimuth, bool isAnim); - void TouchEvent(df::TouchEvent const & touch); + void TouchEvent(df::DrapeEngineId engineId, df::TouchEvent const & touch); int GetDrawScale() const; diff --git a/shaders/program_params.hpp b/shaders/program_params.hpp index fe7c02ba80..235935c5d4 100644 --- a/shaders/program_params.hpp +++ b/shaders/program_params.hpp @@ -40,8 +40,6 @@ private: for (auto const p : GetBoundPrograms()) \ { \ auto const programName = DebugPrint(p); \ - CHECK(params.find(programName) == params.cend(), \ - ("Program has already bound", programName)); \ params[programName] = GetName(); \ } \ } -- 2.45.3