From 9912d1930230f8addd331b9b37938677ff8c8f48 Mon Sep 17 00:00:00 2001 From: Andrei Shkrob Date: Fri, 12 Sep 2025 09:46:30 +0200 Subject: [PATCH] [android] Get rid of MapFragment Signed-off-by: Andrei Shkrob --- .../java/app/organicmaps/MapFragment.java | 210 ------------------ .../java/app/organicmaps/MwmActivity.java | 97 +++----- .../app/organicmaps/car/SurfaceRenderer.java | 23 +- .../app/src/main/res/layout/activity_map.xml | 4 +- .../app/src/main/res/layout/fragment_map.xml | 7 - .../main/java/app/organicmaps/sdk/Map.java | 10 +- .../app/organicmaps/sdk/MapController.java | 102 +++++++++ .../java/app/organicmaps/sdk/MapView.java | 177 +++++++++++++++ .../java/app/organicmaps/sdk/util/Utils.java | 7 + 9 files changed, 329 insertions(+), 308 deletions(-) delete mode 100644 android/app/src/main/java/app/organicmaps/MapFragment.java delete mode 100644 android/app/src/main/res/layout/fragment_map.xml create mode 100644 android/sdk/src/main/java/app/organicmaps/sdk/MapController.java create mode 100644 android/sdk/src/main/java/app/organicmaps/sdk/MapView.java diff --git a/android/app/src/main/java/app/organicmaps/MapFragment.java b/android/app/src/main/java/app/organicmaps/MapFragment.java deleted file mode 100644 index 7bfa884d5..000000000 --- a/android/app/src/main/java/app/organicmaps/MapFragment.java +++ /dev/null @@ -1,210 +0,0 @@ -package app.organicmaps; - -import android.content.Context; -import android.os.Build; -import android.os.Bundle; -import android.util.DisplayMetrics; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.SurfaceHolder; -import android.view.SurfaceView; -import android.view.View; -import android.view.ViewGroup; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.content.res.ConfigurationHelper; -import app.organicmaps.base.BaseMwmFragment; -import app.organicmaps.sdk.Map; -import app.organicmaps.sdk.MapRenderingListener; -import app.organicmaps.sdk.display.DisplayType; -import app.organicmaps.sdk.util.log.Logger; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; - -public class MapFragment extends BaseMwmFragment implements View.OnTouchListener, SurfaceHolder.Callback -{ - private static final String TAG = MapFragment.class.getSimpleName(); - - @NonNull - private final Map mMap = new Map(DisplayType.Device); - - public void updateCompassOffset(int offsetX, int offsetY) - { - mMap.updateCompassOffset(requireContext(), offsetX, offsetY, true); - } - - public void updateBottomWidgetsOffset(int offsetX, int offsetY) - { - mMap.updateBottomWidgetsOffset(requireContext(), offsetX, offsetY); - } - - public void updateMyPositionRoutingOffset(int offsetY) - { - mMap.updateMyPositionRoutingOffset(offsetY); - } - - public void destroySurface(boolean activityIsChangingConfigurations) - { - mMap.onSurfaceDestroyed(activityIsChangingConfigurations, isAdded()); - } - - public boolean isContextCreated() - { - return mMap.isContextCreated(); - } - - @Override - public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) - { - Logger.d(TAG); - int densityDpi; - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) - densityDpi = ConfigurationHelper.getDensityDpi(requireContext().getResources()); - else - densityDpi = getDensityDpiOld(); - - mMap.onSurfaceCreated(requireContext(), surfaceHolder.getSurface(), surfaceHolder.getSurfaceFrame(), densityDpi); - } - - @Override - public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) - { - Logger.d(TAG); - mMap.onSurfaceChanged(requireContext(), surfaceHolder.getSurface(), surfaceHolder.getSurfaceFrame(), - surfaceHolder.isCreating()); - } - - @Override - public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) - { - Logger.d(TAG); - mMap.onSurfaceDestroyed(requireActivity().isChangingConfigurations(), true); - } - - @Override - public void onAttach(Context context) - { - Logger.d(TAG); - super.onAttach(context); - - mMap.setLocationHelper(MwmApplication.from(requireContext()).getLocationHelper()); - mMap.setMapRenderingListener((MapRenderingListener) context); - mMap.setCallbackUnsupported(this::reportUnsupported); - } - - @Override - public void onDetach() - { - Logger.d(TAG); - super.onDetach(); - mMap.setMapRenderingListener(null); - mMap.setCallbackUnsupported(null); - } - - @Override - public void onCreate(Bundle b) - { - Logger.d(TAG); - super.onCreate(b); - setRetainInstance(true); - boolean launchByDeepLink = false; - Bundle args = getArguments(); - if (args != null) - launchByDeepLink = args.getBoolean(Map.ARG_LAUNCH_BY_DEEP_LINK); - mMap.onCreate(launchByDeepLink); - } - - @Override - public void onStart() - { - Logger.d(TAG); - super.onStart(); - mMap.onStart(); - } - - @Override - public void onStop() - { - Logger.d(TAG); - super.onStop(); - mMap.onStop(); - } - - @Override - public void onPause() - { - Logger.d(TAG); - super.onPause(); - mMap.onPause(requireContext()); - } - - @Override - public void onResume() - { - Logger.d(TAG); - super.onResume(); - mMap.onResume(); - } - - @Override - public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) - { - Logger.d(TAG); - final View view = inflater.inflate(R.layout.fragment_map, container, false); - final SurfaceView mSurfaceView = view.findViewById(R.id.map_surfaceview); - mSurfaceView.getHolder().addCallback(this); - return view; - } - - @Override - public boolean onTouch(View view, MotionEvent event) - { - int action = event.getActionMasked(); - int pointerIndex = event.getActionIndex(); - switch (action) - { - case MotionEvent.ACTION_POINTER_UP -> action = Map.NATIVE_ACTION_UP; - case MotionEvent.ACTION_UP -> - { - action = Map.NATIVE_ACTION_UP; - pointerIndex = 0; - } - case MotionEvent.ACTION_POINTER_DOWN -> action = Map.NATIVE_ACTION_DOWN; - case MotionEvent.ACTION_DOWN -> - { - action = Map.NATIVE_ACTION_DOWN; - pointerIndex = 0; - } - case MotionEvent.ACTION_MOVE -> - { - action = Map.NATIVE_ACTION_MOVE; - pointerIndex = Map.INVALID_POINTER_MASK; - } - case MotionEvent.ACTION_CANCEL -> action = Map.NATIVE_ACTION_CANCEL; - } - Map.onTouch(action, event, pointerIndex); - return true; - } - - public void notifyOnSurfaceDestroyed(@NonNull Runnable task) - { - mMap.onSurfaceDestroyed(false, true); - task.run(); - } - - private void reportUnsupported() - { - new MaterialAlertDialogBuilder(requireContext(), R.style.MwmTheme_AlertDialog) - .setMessage(R.string.unsupported_phone) - .setCancelable(false) - .setPositiveButton(R.string.close, (dlg, which) -> requireActivity().moveTaskToBack(true)) - .show(); - } - - private int getDensityDpiOld() - { - final DisplayMetrics metrics = new DisplayMetrics(); - requireActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics); - return metrics.densityDpi; - } -} diff --git a/android/app/src/main/java/app/organicmaps/MwmActivity.java b/android/app/src/main/java/app/organicmaps/MwmActivity.java index 55f603462..bed77cabd 100644 --- a/android/app/src/main/java/app/organicmaps/MwmActivity.java +++ b/android/app/src/main/java/app/organicmaps/MwmActivity.java @@ -84,6 +84,7 @@ import app.organicmaps.routing.RoutingPlanInplaceController; import app.organicmaps.sdk.ChoosePositionMode; import app.organicmaps.sdk.Framework; import app.organicmaps.sdk.Map; +import app.organicmaps.sdk.MapController; import app.organicmaps.sdk.MapRenderingListener; import app.organicmaps.sdk.PlacePageActivationListener; import app.organicmaps.sdk.Router; @@ -137,10 +138,10 @@ import java.util.ArrayList; import java.util.Objects; public class MwmActivity extends BaseMwmFragmentActivity - implements PlacePageActivationListener, View.OnTouchListener, MapRenderingListener, RoutingController.Container, - LocationListener, SensorListener, LocationState.ModeChangeListener, - RoutingPlanInplaceController.RoutingPlanListener, RoutingBottomMenuListener, - BookmarkManager.BookmarksLoadingListener, FloatingSearchToolbarController.SearchToolbarListener, + implements PlacePageActivationListener, MapRenderingListener, RoutingController.Container, LocationListener, + SensorListener, LocationState.ModeChangeListener, RoutingPlanInplaceController.RoutingPlanListener, + RoutingBottomMenuListener, BookmarkManager.BookmarksLoadingListener, + FloatingSearchToolbarController.SearchToolbarListener, MenuBottomSheetFragment.MenuBottomSheetInterfaceWithHeader, PlacePageController.PlacePageRouteSettingsListener, MapButtonsController.MapButtonClickListener, DisplayChangedListener @@ -170,8 +171,9 @@ public class MwmActivity extends BaseMwmFragmentActivity private static final String POWER_SAVE_DISCLAIMER_SHOWN = "POWER_SAVE_DISCLAIMER_SHOWN"; - @Nullable - private MapFragment mMapFragment; + @SuppressWarnings("NotNullFieldNotInitialized") + @NonNull + private MapController mMapController; private View mPointChooser; private MaterialToolbar mPointChooserToolbar; @@ -459,7 +461,7 @@ public class MwmActivity extends BaseMwmFragmentActivity { mRemoveDisplayListener = false; startActivity(new Intent(this, MapPlaceholderActivity.class)); - Objects.requireNonNull(mMapFragment).notifyOnSurfaceDestroyed(onTaskFinishedCallback); + mMapController.setOnDestroyListener(onTaskFinishedCallback); finish(); } @@ -536,7 +538,7 @@ public class MwmActivity extends BaseMwmFragmentActivity updateViewsInsets(); if (getIntent().getBooleanExtra(EXTRA_UPDATE_THEME, false)) - ThemeSwitcher.INSTANCE.restart(isMapRendererActive()); + ThemeSwitcher.INSTANCE.restart(mMapController.isRenderingActive()); /* * onRenderingInitializationFinished() hook is not called when MwmActivity is recreated with the already @@ -604,7 +606,10 @@ public class MwmActivity extends BaseMwmFragmentActivity private void initViews(boolean isLaunchByDeeplink) { - initMap(isLaunchByDeeplink); + mMapController = new MapController(findViewById(R.id.map), MwmApplication.from(this).getLocationHelper(), this, + this::reportUnsupported, isLaunchByDeeplink); + getLifecycle().addObserver(mMapController); + initNavigationButtons(); if (!mIsTabletLayout) @@ -737,29 +742,6 @@ public class MwmActivity extends BaseMwmFragmentActivity finish(); } - private void initMap(boolean isLaunchByDeepLink) - { - final FragmentManager manager = getSupportFragmentManager(); - mMapFragment = (MapFragment) manager.findFragmentByTag(MapFragment.class.getName()); - if (mMapFragment == null) - { - Bundle args = new Bundle(); - args.putBoolean(Map.ARG_LAUNCH_BY_DEEP_LINK, isLaunchByDeepLink); - final FragmentFactory factory = manager.getFragmentFactory(); - mMapFragment = (MapFragment) factory.instantiate(getClassLoader(), MapFragment.class.getName()); - mMapFragment.setArguments(args); - manager.beginTransaction() - .replace(R.id.map_fragment_container, mMapFragment, MapFragment.class.getName()) - .commit(); - } - - View container = findViewById(R.id.map_fragment_container); - if (container != null) - { - container.setOnTouchListener(this); - } - } - private void initNavigationButtons() { prepareNavigationButtons(); @@ -1188,7 +1170,7 @@ public class MwmActivity extends BaseMwmFragmentActivity { setIntent(intent); super.onNewIntent(intent); - if (isMapRendererActive()) + if (mMapController.isRenderingActive()) processIntent(); if (intent.getAction() != null && intent.getAction().equals(TrackRecordingService.STOP_TRACK_RECORDING)) { @@ -1198,17 +1180,12 @@ public class MwmActivity extends BaseMwmFragmentActivity } } - private boolean isMapRendererActive() - { - return mMapFragment != null && Map.isEngineCreated() && mMapFragment.isContextCreated(); - } - @CallSuper @Override protected void onResume() { super.onResume(); - ThemeSwitcher.INSTANCE.restart(isMapRendererActive()); + ThemeSwitcher.INSTANCE.restart(mMapController.isRenderingActive()); refreshSearchToolbar(); setFullscreen(isFullscreen()); makeNavigationBarTransparentInLightMode(); @@ -1226,15 +1203,6 @@ public class MwmActivity extends BaseMwmFragmentActivity MwmApplication.from(this).getSensorHelper().addListener(this); } - @Override - public void recreate() - { - // Explicitly destroy surface before activity recreation. - if (mMapFragment != null) - mMapFragment.destroySurface(true); - super.recreate(); - } - @Override protected void onResumeFragments() { @@ -1436,12 +1404,6 @@ public class MwmActivity extends BaseMwmFragmentActivity return super.onGenericMotionEvent(event); } - @Override - public boolean onTouch(View view, MotionEvent event) - { - return mMapFragment != null && mMapFragment.onTouch(view, event); - } - public void customOnNavigateUp() { if (removeCurrentFragment(true)) @@ -1457,10 +1419,7 @@ public class MwmActivity extends BaseMwmFragmentActivity void updateCompassOffset(int offsetY, int offsetX) { - if (mMapFragment == null || !mMapFragment.isAdded()) - return; - - mMapFragment.updateCompassOffset(offsetX, offsetY); + mMapController.updateCompassOffset(offsetX, offsetY); final double north = MwmApplication.from(this).getSensorHelper().getSavedNorth(); if (!Double.isNaN(north)) @@ -1479,9 +1438,6 @@ public class MwmActivity extends BaseMwmFragmentActivity public void updateBottomWidgetsOffset(int offsetX) { - if (mMapFragment == null || !mMapFragment.isAdded()) - return; - int offsetY = mNavBarHeight; final Float bottomButtonHeight = mMapButtonsViewModel.getBottomButtonsHeight().getValue(); if (bottomButtonHeight != null) @@ -1496,8 +1452,8 @@ public class MwmActivity extends BaseMwmFragmentActivity if (mDisplayManager.isDeviceDisplayUsed()) { - mMapFragment.updateBottomWidgetsOffset(offsetX, offsetY); - mMapFragment.updateMyPositionRoutingOffset(offsetY); + mMapController.updateBottomWidgetsOffset(offsetX, offsetY); + mMapController.updateMyPositionRoutingOffset(offsetY); } } @@ -1715,7 +1671,7 @@ public class MwmActivity extends BaseMwmFragmentActivity public void onNavigationCancelled() { closeFloatingToolbarsAndPanels(true); - ThemeSwitcher.INSTANCE.restart(isMapRendererActive()); + ThemeSwitcher.INSTANCE.restart(mMapController.isRenderingActive()); if (mRoutingPlanInplaceController == null) return; @@ -1731,7 +1687,7 @@ public class MwmActivity extends BaseMwmFragmentActivity public void onNavigationStarted() { closeFloatingToolbarsAndPanels(true); - ThemeSwitcher.INSTANCE.restart(isMapRendererActive()); + ThemeSwitcher.INSTANCE.restart(mMapController.isRenderingActive()); mMapButtonsViewModel.setLayoutMode(MapButtonsController.LayoutMode.navigation); refreshLightStatusBar(); @@ -1767,7 +1723,7 @@ public class MwmActivity extends BaseMwmFragmentActivity public void onResetToPlanningState() { closeFloatingToolbarsAndPanels(true); - ThemeSwitcher.INSTANCE.restart(isMapRendererActive()); + ThemeSwitcher.INSTANCE.restart(mMapController.isRenderingActive()); NavigationService.stopService(this); mMapButtonsViewModel.setSearchOption(null); mMapButtonsViewModel.setLayoutMode(MapButtonsController.LayoutMode.planning); @@ -2596,4 +2552,13 @@ public class MwmActivity extends BaseMwmFragmentActivity window.setNavigationBarContrastEnforced(false); } } + + private void reportUnsupported() + { + new MaterialAlertDialogBuilder(this, R.style.MwmTheme_AlertDialog) + .setMessage(R.string.unsupported_phone) + .setCancelable(false) + .setPositiveButton(R.string.close, (dlg, which) -> this.moveTaskToBack(true)) + .show(); + } } diff --git a/android/app/src/main/java/app/organicmaps/car/SurfaceRenderer.java b/android/app/src/main/java/app/organicmaps/car/SurfaceRenderer.java index 810dff898..da4d36806 100644 --- a/android/app/src/main/java/app/organicmaps/car/SurfaceRenderer.java +++ b/android/app/src/main/java/app/organicmaps/car/SurfaceRenderer.java @@ -8,19 +8,16 @@ 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.MwmApplication; -import app.organicmaps.R; import app.organicmaps.sdk.Framework; import app.organicmaps.sdk.Map; import app.organicmaps.sdk.MapRenderingListener; import app.organicmaps.sdk.settings.UnitLocale; -import app.organicmaps.sdk.util.concurrency.UiThread; import app.organicmaps.sdk.util.log.Logger; public class SurfaceRenderer implements DefaultLifecycleObserver, SurfaceCallback, MapRenderingListener @@ -96,7 +93,7 @@ public class SurfaceRenderer implements DefaultLifecycleObserver, SurfaceCallbac mSurface.release(); mSurface = null; } - mMap.onSurfaceDestroyed(false, true); + mMap.onSurfaceDestroyed(false); } @Override @@ -112,7 +109,6 @@ public class SurfaceRenderer implements DefaultLifecycleObserver, SurfaceCallbac { Logger.d(TAG); mMap.onStart(); - mMap.setCallbackUnsupported(this::reportUnsupported); } @Override @@ -121,14 +117,14 @@ public class SurfaceRenderer implements DefaultLifecycleObserver, SurfaceCallbac Logger.d(TAG); mMap.onResume(); if (MwmApplication.from(mCarContext).getDisplayManager().isCarDisplayUsed()) - UiThread.runLater(() -> mMap.updateMyPositionRoutingOffset(0)); + mMap.updateMyPositionRoutingOffset(0); } @Override public void onPause(@NonNull LifecycleOwner owner) { Logger.d(TAG); - mMap.onPause(mCarContext); + mMap.onPause(); } @Override @@ -199,7 +195,8 @@ public class SurfaceRenderer implements DefaultLifecycleObserver, SurfaceCallbac } mCarContext.getCarService(AppManager.class).setSurfaceCallback(null); - mMap.onSurfaceDestroyed(false, true); + mMap.onPause(); + mMap.onSurfaceDestroyed(false); mMap.onStop(); mMap.setCallbackUnsupported(null); mMap.setMapRenderingListener(null); @@ -217,9 +214,8 @@ public class SurfaceRenderer implements DefaultLifecycleObserver, SurfaceCallbac mCarContext.getCarService(AppManager.class).setSurfaceCallback(this); mMap.onStart(); - mMap.setCallbackUnsupported(this::reportUnsupported); mMap.setMapRenderingListener(this); - UiThread.runLater(() -> mMap.updateMyPositionRoutingOffset(0)); + mMap.updateMyPositionRoutingOffset(0); mIsRunning = true; } @@ -229,13 +225,6 @@ public class SurfaceRenderer implements DefaultLifecycleObserver, SurfaceCallbac return mIsRunning; } - private void reportUnsupported() - { - String message = mCarContext.getString(R.string.unsupported_phone); - Logger.e(TAG, message); - CarToast.makeText(mCarContext, message, CarToast.LENGTH_LONG).show(); - } - @Override public void onRenderingCreated() { diff --git a/android/app/src/main/res/layout/activity_map.xml b/android/app/src/main/res/layout/activity_map.xml index abe4998ff..e03f573cb 100644 --- a/android/app/src/main/res/layout/activity_map.xml +++ b/android/app/src/main/res/layout/activity_map.xml @@ -9,8 +9,8 @@ android:id="@+id/map_container" android:layout_width="match_parent" android:layout_height="match_parent"> - - \ No newline at end of file diff --git a/android/sdk/src/main/java/app/organicmaps/sdk/Map.java b/android/sdk/src/main/java/app/organicmaps/sdk/Map.java index a60c3976e..d9bf89f1d 100644 --- a/android/sdk/src/main/java/app/organicmaps/sdk/Map.java +++ b/android/sdk/src/main/java/app/organicmaps/sdk/Map.java @@ -21,7 +21,6 @@ public final class Map void report(); } - public static final String ARG_LAUNCH_BY_DEEP_LINK = "launch_by_deep_link"; private static final String TAG = Map.class.getSimpleName(); // Should correspond to android::MultiTouchAction from Framework.cpp @@ -220,11 +219,10 @@ public final class Map mMapRenderingListener.onRenderingRestored(); } - public void onSurfaceDestroyed(boolean activityIsChangingConfigurations, boolean isAdded) + public void onSurfaceDestroyed(boolean activityIsChangingConfigurations) { - Logger.d(TAG, "mSurfaceCreated = " + mSurfaceCreated + ", mSurfaceAttached = " + mSurfaceAttached - + ", isAdded = " + isAdded); - if (!mSurfaceCreated || !mSurfaceAttached || !isAdded) + Logger.d(TAG, "mSurfaceCreated = " + mSurfaceCreated + ", mSurfaceAttached = " + mSurfaceAttached); + if (!mSurfaceCreated || !mSurfaceAttached) return; nativeDetachSurface(!activityIsChangingConfigurations); @@ -261,7 +259,7 @@ public final class Map nativeSetRenderingInitializationFinishedListener(null); } - public void onPause(final Context context) + public void onPause() { mUiThemeOnPause = Config.UiTheme.getCurrent(); diff --git a/android/sdk/src/main/java/app/organicmaps/sdk/MapController.java b/android/sdk/src/main/java/app/organicmaps/sdk/MapController.java new file mode 100644 index 000000000..d38673314 --- /dev/null +++ b/android/sdk/src/main/java/app/organicmaps/sdk/MapController.java @@ -0,0 +1,102 @@ +package app.organicmaps.sdk; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.lifecycle.DefaultLifecycleObserver; +import androidx.lifecycle.LifecycleOwner; +import app.organicmaps.sdk.location.LocationHelper; +import app.organicmaps.sdk.util.log.Logger; + +public class MapController implements DefaultLifecycleObserver +{ + private static final String TAG = MapController.class.getSimpleName(); + + private final MapView mMapView; + private final Map mMap; + + @Nullable + private Runnable mOnSurfaceDestroyedListener = null; + + public MapController(@NonNull MapView mapView, @NonNull LocationHelper locationHelper, + @NonNull MapRenderingListener mapRenderingListener, + @NonNull Map.CallbackUnsupported callbackUnsupported, boolean launchByDeepLink) + { + mMapView = mapView; + mMap = mMapView.getMap(); + mMap.onCreate(launchByDeepLink); + mMap.setLocationHelper(locationHelper); + mMap.setMapRenderingListener(mapRenderingListener); + mMap.setCallbackUnsupported(callbackUnsupported); + } + + public MapView getView() + { + return mMapView; + } + + public boolean isRenderingActive() + { + return Map.isEngineCreated() && mMap.isContextCreated(); + } + + public void updateCompassOffset(int offsetX, int offsetY) + { + mMap.updateCompassOffset(mMapView.getContext(), offsetX, offsetY, true); + } + + public void updateBottomWidgetsOffset(int offsetX, int offsetY) + { + mMap.updateBottomWidgetsOffset(mMapView.getContext(), offsetX, offsetY); + } + + public void updateMyPositionRoutingOffset(int offsetY) + { + mMap.updateMyPositionRoutingOffset(offsetY); + } + + public void setOnDestroyListener(@NonNull Runnable task) + { + mOnSurfaceDestroyedListener = task; + } + + @Override + public void onStart(@NonNull LifecycleOwner owner) + { + Logger.d(TAG); + mMap.onStart(); + } + + @Override + public void onResume(@NonNull LifecycleOwner owner) + { + Logger.d(TAG); + mMap.onResume(); + } + + @Override + public void onPause(@NonNull LifecycleOwner owner) + { + Logger.d(TAG); + mMap.onPause(); + } + + @Override + public void onStop(@NonNull LifecycleOwner owner) + { + Logger.d(TAG); + mMap.onStop(); + } + + @Override + public void onDestroy(@NonNull LifecycleOwner owner) + { + Logger.d(TAG); + mMap.setMapRenderingListener(null); + mMap.setCallbackUnsupported(null); + if (mOnSurfaceDestroyedListener != null) + { + mOnSurfaceDestroyedListener.run(); + mOnSurfaceDestroyedListener = null; + } + } +} diff --git a/android/sdk/src/main/java/app/organicmaps/sdk/MapView.java b/android/sdk/src/main/java/app/organicmaps/sdk/MapView.java new file mode 100644 index 000000000..7f89c299a --- /dev/null +++ b/android/sdk/src/main/java/app/organicmaps/sdk/MapView.java @@ -0,0 +1,177 @@ +package app.organicmaps.sdk; + +import android.app.Activity; +import android.content.Context; +import android.content.ContextWrapper; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import androidx.annotation.NonNull; +import androidx.core.content.res.ConfigurationHelper; +import app.organicmaps.sdk.display.DisplayType; +import app.organicmaps.sdk.util.Utils; +import app.organicmaps.sdk.util.log.Logger; + +public class MapView extends SurfaceView +{ + private static final String TAG = MapView.class.getSimpleName(); + + private class SurfaceHolderCallback implements SurfaceHolder.Callback + { + @Override + public void surfaceCreated(@NonNull SurfaceHolder holder) + { + Logger.d(TAG); + mMap.onSurfaceCreated(MapView.this.getContext(), holder.getSurface(), holder.getSurfaceFrame(), + ConfigurationHelper.getDensityDpi(MapView.this.getResources())); + } + + @Override + public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) + { + Logger.d(TAG); + mMap.onSurfaceChanged(MapView.this.getContext(), holder.getSurface(), holder.getSurfaceFrame(), + holder.isCreating()); + } + + @Override + public void surfaceDestroyed(@NonNull SurfaceHolder holder) + { + Logger.d(TAG); + mMap.onSurfaceDestroyed(isHostActivityChangingConfigurations()); + } + } + + @NonNull + private final Map mMap; + + public MapView(Context context) + { + this(context, null); + } + + public MapView(Context context, AttributeSet attrs) + { + this(context, attrs, 0); + } + + public MapView(Context context, AttributeSet attrs, int defStyleAttr) + { + this(context, attrs, defStyleAttr, 0); + } + + public MapView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) + { + super(context, attrs, defStyleAttr, defStyleRes); + mMap = new Map(DisplayType.Device); + super.getHolder().addCallback(new SurfaceHolderCallback()); + } + + public final void onDraw(@NonNull Canvas canvas) + { + super.onDraw(canvas); + if (isInEditMode()) + drawMapPreview(canvas); + } + + @Override + public boolean onTouchEvent(@NonNull MotionEvent event) + { + int action = event.getActionMasked(); + int pointerIndex = event.getActionIndex(); + switch (action) + { + case MotionEvent.ACTION_POINTER_UP -> action = Map.NATIVE_ACTION_UP; + case MotionEvent.ACTION_UP -> + { + action = Map.NATIVE_ACTION_UP; + pointerIndex = 0; + } + case MotionEvent.ACTION_POINTER_DOWN -> action = Map.NATIVE_ACTION_DOWN; + case MotionEvent.ACTION_DOWN -> + { + action = Map.NATIVE_ACTION_DOWN; + pointerIndex = 0; + } + case MotionEvent.ACTION_MOVE -> + { + action = Map.NATIVE_ACTION_MOVE; + pointerIndex = Map.INVALID_POINTER_MASK; + } + case MotionEvent.ACTION_CANCEL -> action = Map.NATIVE_ACTION_CANCEL; + } + Map.onTouch(action, event, pointerIndex); + performClick(); + return true; + } + + @Override + public boolean performClick() + { + super.performClick(); + return false; + } + + @Override + public final SurfaceHolder getHolder() + { + throw new UnsupportedOperationException("Not supported."); + } + + @NonNull + Map getMap() + { + return mMap; + } + + /// The function is called only in the design mode of Android Studio. + private void drawMapPreview(@NonNull Canvas canvas) + { + final int w = getWidth(); + final int h = getHeight(); + + // Background + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setStyle(Paint.Style.FILL); + if (Utils.isDarkMode(getContext())) + paint.setColor(Color.rgb(30, 30, 30)); + else + paint.setColor(Color.rgb(245, 242, 230)); + canvas.drawRect(0, 0, w, h, paint); + + // Grid lines (lat/lon) + paint.setColor(Color.LTGRAY); + paint.setStrokeWidth(2f); + final int step = Math.min(w, h) / 6; + for (int i = 0; i < Math.max(w, h); i += step) + { + if (i < w) + canvas.drawLine(i, 0, i, h, paint); + if (i < h) + canvas.drawLine(0, i, w, i, paint); + } + } + + private boolean isHostActivityChangingConfigurations() + { + Activity activity = findActivity(getContext()); + return activity != null && activity.isChangingConfigurations(); + } + + private static Activity findActivity(Context context) + { + while (context instanceof ContextWrapper) + { + if (context instanceof Activity) + { + return (Activity) context; + } + context = ((ContextWrapper) context).getBaseContext(); + } + return null; + } +} diff --git a/android/sdk/src/main/java/app/organicmaps/sdk/util/Utils.java b/android/sdk/src/main/java/app/organicmaps/sdk/util/Utils.java index 2f5cfdbad..eefef19f7 100644 --- a/android/sdk/src/main/java/app/organicmaps/sdk/util/Utils.java +++ b/android/sdk/src/main/java/app/organicmaps/sdk/util/Utils.java @@ -6,6 +6,7 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.res.Configuration; import android.content.res.Resources; import android.os.Build; import android.text.TextUtils; @@ -346,4 +347,10 @@ public class Utils { return context.getResources().getDimensionPixelSize(id); } + + public static boolean isDarkMode(@NonNull Context context) + { + return (context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) + == Configuration.UI_MODE_NIGHT_YES; + } }