From ef907425b015708f0cb2873bab489b09ed985013 Mon Sep 17 00:00:00 2001 From: Arnaud Vergnet Date: Mon, 26 Dec 2022 00:17:22 +0100 Subject: [PATCH 01/30] chore: simplify place page controller Signed-off-by: Arnaud Vergnet --- android/res/layout/activity_map.xml | 1 - android/src/app/organicmaps/MwmActivity.java | 11 +- .../widget/placepage/Closable.java | 6 - .../widget/placepage/PlacePageController.java | 294 ++++++++++++++- .../PlacePageControllerComposite.java | 187 ---------- .../widget/placepage/PlacePageFactory.java | 33 -- .../widget/placepage/PlacePageView.java | 26 +- .../placepage/RichPlacePageController.java | 349 ------------------ .../placepage/SimplePlacePageController.java | 288 --------------- 9 files changed, 293 insertions(+), 902 deletions(-) delete mode 100644 android/src/app/organicmaps/widget/placepage/Closable.java delete mode 100644 android/src/app/organicmaps/widget/placepage/PlacePageControllerComposite.java delete mode 100644 android/src/app/organicmaps/widget/placepage/PlacePageFactory.java delete mode 100644 android/src/app/organicmaps/widget/placepage/RichPlacePageController.java delete mode 100644 android/src/app/organicmaps/widget/placepage/SimplePlacePageController.java diff --git a/android/res/layout/activity_map.xml b/android/res/layout/activity_map.xml index e8484881d2..5ff52639db 100644 --- a/android/res/layout/activity_map.xml +++ b/android/res/layout/activity_map.xml @@ -72,5 +72,4 @@ android:orientation="horizontal" /> - diff --git a/android/src/app/organicmaps/MwmActivity.java b/android/src/app/organicmaps/MwmActivity.java index d47ac999c8..a3d5d17cf1 100644 --- a/android/src/app/organicmaps/MwmActivity.java +++ b/android/src/app/organicmaps/MwmActivity.java @@ -81,11 +81,9 @@ import app.organicmaps.util.log.Logger; import app.organicmaps.widget.menu.MainMenu; import app.organicmaps.widget.placepage.PlacePageController; import app.organicmaps.widget.placepage.PlacePageData; -import app.organicmaps.widget.placepage.PlacePageFactory; import app.organicmaps.widget.placepage.RoutingModeListener; import app.organicmaps.util.Config; import app.organicmaps.util.Counters; -import app.organicmaps.util.LocationUtils; import app.organicmaps.util.SharingUtils; import app.organicmaps.util.ThemeSwitcher; import app.organicmaps.util.ThemeUtils; @@ -98,7 +96,6 @@ import java.util.ArrayList; import java.util.Objects; import java.util.Stack; -import static app.organicmaps.util.concurrency.UiThread.runLater; import static app.organicmaps.widget.placepage.PlacePageButtons.PLACEPAGE_MORE_MENU_ID; public class MwmActivity extends BaseMwmFragmentActivity @@ -384,10 +381,8 @@ public class MwmActivity extends BaseMwmFragmentActivity setContentView(R.layout.activity_map); UiUtils.setupTransparentStatusBar(this); - mPlacePageController = PlacePageFactory.createCompositePlacePageController( - this, this); + mPlacePageController = new PlacePageController(this, this); mPlacePageController.initialize(this); - mPlacePageController.onActivityCreated(this, savedInstanceState); mSearchController = new FloatingSearchToolbarController(this, this); mSearchController.getToolbar() @@ -1008,7 +1003,6 @@ public class MwmActivity extends BaseMwmFragmentActivity mOnmapDownloader.onResume(); mNavigationController.onActivityResumed(this); - mPlacePageController.onActivityResumed(this); refreshLightStatusBar(); } @@ -1035,7 +1029,6 @@ public class MwmActivity extends BaseMwmFragmentActivity TtsPlayer.INSTANCE.stop(); if (mOnmapDownloader != null) mOnmapDownloader.onPause(); - mPlacePageController.onActivityPaused(this); mNavigationController.onActivityPaused(this); super.onPause(); } @@ -1052,7 +1045,6 @@ public class MwmActivity extends BaseMwmFragmentActivity LocationState.nativeSetListener(this); LocationHelper.INSTANCE.addListener(this); onMyPositionModeChanged(LocationState.nativeGetMode()); - mPlacePageController.onActivityStarted(this); mSearchController.attach(this); if (!Config.isScreenSleepEnabled()) Utils.keepScreenOn(true, getWindow()); @@ -1068,7 +1060,6 @@ public class MwmActivity extends BaseMwmFragmentActivity LocationState.nativeRemoveListener(); LocationHelper.INSTANCE.detach(); RoutingController.get().detach(); - mPlacePageController.onActivityStopped(this); IsolinesManager.from(getApplicationContext()).detach(); mSearchController.detach(); Utils.keepScreenOn(false, getWindow()); diff --git a/android/src/app/organicmaps/widget/placepage/Closable.java b/android/src/app/organicmaps/widget/placepage/Closable.java deleted file mode 100644 index 13d80af2de..0000000000 --- a/android/src/app/organicmaps/widget/placepage/Closable.java +++ /dev/null @@ -1,6 +0,0 @@ -package app.organicmaps.widget.placepage; - -public interface Closable -{ - void closePlacePage(); -} diff --git a/android/src/app/organicmaps/widget/placepage/PlacePageController.java b/android/src/app/organicmaps/widget/placepage/PlacePageController.java index e1ffe88057..16e6fa809c 100644 --- a/android/src/app/organicmaps/widget/placepage/PlacePageController.java +++ b/android/src/app/organicmaps/widget/placepage/PlacePageController.java @@ -1,32 +1,298 @@ package app.organicmaps.widget.placepage; +import android.annotation.SuppressLint; import android.app.Activity; -import android.app.Application; +import android.content.res.Resources; +import android.location.Location; import android.os.Bundle; +import android.view.View; +import android.view.ViewGroup; import androidx.annotation.NonNull; - import androidx.annotation.Nullable; +import androidx.core.view.GestureDetectorCompat; +import app.organicmaps.Framework; +import app.organicmaps.R; import app.organicmaps.base.Initializable; import app.organicmaps.base.Savable; -import app.organicmaps.base.Supportable; +import app.organicmaps.bookmarks.data.MapObject; +import app.organicmaps.location.LocationHelper; +import app.organicmaps.location.LocationListener; +import app.organicmaps.util.UiUtils; +import app.organicmaps.util.Utils; import app.organicmaps.util.bottomsheet.MenuBottomSheetItem; +import app.organicmaps.util.log.Logger; +import com.google.android.material.bottomsheet.BottomSheetBehavior; import java.util.ArrayList; +import java.util.Objects; -public interface PlacePageController extends Initializable, - Savable, - Application.ActivityLifecycleCallbacks, - Supportable +public class PlacePageController implements Initializable, + Savable, + LocationListener, + PlacePageView.OnPlacePageRequestCloseListener { - void openFor(@NonNull PlacePageData data); - void close(boolean deactivateMapSelection); - boolean isClosed(); - int getPlacePageWidth(); - @Nullable - ArrayList getMenuBottomSheetItems(); + private static final String TAG = PlacePageController.class.getSimpleName(); - interface SlideListener + private static final float PREVIEW_PLUS_RATIO = 0.45f; + @NonNull + private final SlideListener mSlideListener; + @Nullable + private final RoutingModeListener mRoutingModeListener; + @SuppressWarnings("NullableProblems") + @NonNull + private BottomSheetBehavior mPlacePageBehavior; + @SuppressWarnings("NullableProblems") + @NonNull + private View mButtonsLayout; + @SuppressWarnings("NullableProblems") + @NonNull + private PlacePageView mPlacePage; + private int mViewportMinHeight; + private boolean mDeactivateMapSelection = true; + @NonNull + private final BottomSheetChangedListener mBottomSheetChangedListener = new BottomSheetChangedListener() + { + @Override + public void onSheetHidden() + { + onHiddenInternal(); + } + + @Override + public void onSheetDetailsOpened() + { + // No op. + } + + @Override + public void onSheetCollapsed() + { + mPlacePage.resetScroll(); + setPeekHeight(); + } + + @Override + public void onSheetSliding(int top) + { + mSlideListener.onPlacePageSlide(top); + // mPlacePageTracker.onMove(); + } + + @Override + public void onSheetSlideFinish() + { + PlacePageUtils.moveViewportUp(mPlacePage, mViewportMinHeight); + } + }; + @NonNull + private final BottomSheetBehavior.BottomSheetCallback mSheetCallback + = new DefaultBottomSheetCallback(mBottomSheetChangedListener); + + public PlacePageController(@NonNull SlideListener listener, + @Nullable RoutingModeListener routingModeListener) + { + mSlideListener = listener; + mRoutingModeListener = routingModeListener; + } + + private void onHiddenInternal() + { + if (mDeactivateMapSelection) + Framework.nativeDeactivatePopup(); + mDeactivateMapSelection = true; + PlacePageUtils.moveViewportUp(mPlacePage, mViewportMinHeight); + UiUtils.invisible(mButtonsLayout); + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public void initialize(@Nullable Activity activity) + { + Objects.requireNonNull(activity); + Resources res = activity.getResources(); + mViewportMinHeight = res.getDimensionPixelSize(R.dimen.viewport_min_height); + mPlacePage = activity.findViewById(R.id.placepage); + mPlacePageBehavior = BottomSheetBehavior.from(mPlacePage); + mPlacePageBehavior.addBottomSheetCallback(mSheetCallback); + mPlacePageBehavior.setHideable(true); + mPlacePageBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); + PlacePageGestureListener ppGestureListener = new PlacePageGestureListener(mPlacePageBehavior); + GestureDetectorCompat gestureDetector = new GestureDetectorCompat(activity, ppGestureListener); + mPlacePage.addPlacePageGestureListener(ppGestureListener); + mPlacePage.setOnTouchListener((v, event) -> gestureDetector.onTouchEvent(event)); + mPlacePage.setOnPlacePageRequestCloseListener(this); + mPlacePage.setRoutingModeListener(mRoutingModeListener); + mPlacePage.setOnPlacePageContentChangeListener(this::setPeekHeight); + + mButtonsLayout = activity.findViewById(R.id.pp_buttons_layout); + ViewGroup buttons = mButtonsLayout.findViewById(R.id.container); + mPlacePage.initButtons(buttons); + UiUtils.bringViewToFrontOf(mButtonsLayout, mPlacePage); + LocationHelper.INSTANCE.addListener(this); + + mButtonsLayout.setOnApplyWindowInsetsListener((view, windowInsets) -> { + UiUtils.setViewInsetsPaddingNoTop(mButtonsLayout, windowInsets); + return windowInsets; + }); + mPlacePage.requestApplyInsets(); + } + + public int getPlacePageWidth() + { + return mPlacePage.getWidth(); + } + + @Nullable + public ArrayList getMenuBottomSheetItems() + { + return mPlacePage.getMenuBottomSheetItems(); + } + + @Override + public void destroy() + { + LocationHelper.INSTANCE.removeListener(this); + } + + public void openFor(@NonNull PlacePageData data) + { + mDeactivateMapSelection = true; + MapObject object = (MapObject) data; + mPlacePage.setMapObject(object, (isSameObject) -> { + @BottomSheetBehavior.State + int state = mPlacePageBehavior.getState(); + if (isSameObject && !PlacePageUtils.isHiddenState(state)) + return; + + mPlacePage.resetScroll(); + + if (object.getOpeningMode() == MapObject.OPENING_MODE_DETAILS) + { + mPlacePageBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); + return; + } + + UiUtils.show(mButtonsLayout); + mPlacePage.post(() -> { + setPeekHeight(); + mPlacePageBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); + }); + }); + } + + private void setPeekHeight() + { + final int peekHeight = calculatePeekHeight(); + if (peekHeight == mPlacePageBehavior.getPeekHeight()) + return; + + @BottomSheetBehavior.State + int currentState = mPlacePageBehavior.getState(); + if (PlacePageUtils.isSettlingState(currentState) || PlacePageUtils.isDraggingState(currentState)) + { + Logger.d(TAG, "Sheet state inappropriate, ignore."); + return; + } + + final boolean shouldAnimate = PlacePageUtils.isCollapsedState(currentState) && mPlacePageBehavior.getPeekHeight() > 0; + mPlacePageBehavior.setPeekHeight(peekHeight, shouldAnimate); + } + + private int calculatePeekHeight() + { + // Buttons layout padding is the navigation bar height. + // Bottom sheets are displayed above it so we need to remove it from the computed size + final int organicPeekHeight = mPlacePage.getPreviewHeight() + + mButtonsLayout.getHeight() - mButtonsLayout.getPaddingBottom(); + final MapObject object = mPlacePage.getMapObject(); + if (object != null) + { + @MapObject.OpeningMode + int mode = object.getOpeningMode(); + if (mode == MapObject.OPENING_MODE_PREVIEW_PLUS) + { + View parent = (View) mPlacePage.getParent(); + int promoPeekHeight = (int) (parent.getHeight() * PREVIEW_PLUS_RATIO); + return Math.max(promoPeekHeight, organicPeekHeight); + } + } + + return organicPeekHeight; + } + + public void close(boolean deactivateMapSelection) + { + mDeactivateMapSelection = deactivateMapSelection; + mPlacePageBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); + mPlacePage.reset(); + } + + public boolean isClosed() + { + return PlacePageUtils.isHiddenState(mPlacePageBehavior.getState()); + } + + @Override + public void onLocationUpdated(@NonNull Location location) + { + mPlacePage.refreshLocation(location); + } + + @Override + public void onCompassUpdated(double north) + { + @BottomSheetBehavior.State + int currentState = mPlacePageBehavior.getState(); + if (PlacePageUtils.isHiddenState(currentState)) + return; + + mPlacePage.refreshAzimuth(north); + } + + @Override + public void onSave(@NonNull Bundle outState) + { + outState.putParcelable(PlacePageUtils.EXTRA_PLACE_PAGE_DATA, mPlacePage.getMapObject()); + } + + @Override + public void onRestore(@NonNull Bundle inState) + { + if (mPlacePageBehavior.getState() == BottomSheetBehavior.STATE_HIDDEN) + return; + + if (!Framework.nativeHasPlacePageInfo()) + { + close(false); + return; + } + + MapObject object = Utils.getParcelable(inState, PlacePageUtils.EXTRA_PLACE_PAGE_DATA, MapObject.class); + if (object == null) + return; + + @BottomSheetBehavior.State + int state = mPlacePageBehavior.getState(); + mPlacePage.setMapObject(object, (isSameObject) -> restorePlacePageState(state)); + } + + private void restorePlacePageState(@BottomSheetBehavior.State int state) + { + mPlacePage.post(() -> { + mPlacePageBehavior.setState(state); + UiUtils.show(mButtonsLayout); + setPeekHeight(); + }); + } + + @Override + public void onPlacePageRequestClose() + { + close(true); + } + + public interface SlideListener { void onPlacePageSlide(int top); } diff --git a/android/src/app/organicmaps/widget/placepage/PlacePageControllerComposite.java b/android/src/app/organicmaps/widget/placepage/PlacePageControllerComposite.java deleted file mode 100644 index 5a258f73fa..0000000000 --- a/android/src/app/organicmaps/widget/placepage/PlacePageControllerComposite.java +++ /dev/null @@ -1,187 +0,0 @@ -package app.organicmaps.widget.placepage; - -import android.app.Activity; -import android.os.Bundle; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import app.organicmaps.util.bottomsheet.MenuBottomSheetItem; -import app.organicmaps.util.Utils; - -import java.util.ArrayList; -import java.util.List; - -class PlacePageControllerComposite implements PlacePageController -{ - @NonNull - private final PlacePageController.SlideListener mSlideListener; - @Nullable - private final RoutingModeListener mRoutingModeListener; - @NonNull - private final List mControllers = new ArrayList<>(); - @SuppressWarnings("NullableProblems") - @NonNull - private PlacePageController mActiveController; - - PlacePageControllerComposite(@NonNull SlideListener slideListener, - @Nullable RoutingModeListener routingModeListener) - { - mSlideListener = slideListener; - mRoutingModeListener = routingModeListener; - } - - @Override - public int getPlacePageWidth() - { - return mActiveController.getPlacePageWidth(); - } - - @Override - @Nullable - public ArrayList getMenuBottomSheetItems() - { - return mActiveController.getMenuBottomSheetItems(); - } - - @Override - public void openFor(@NonNull PlacePageData data) - { - boolean support = mActiveController.support(data); - if (support) - { - mActiveController.openFor(data); - return; - } - - mActiveController.close(false); - PlacePageController controller = findControllerFor(data); - if (controller == null) - throw new UnsupportedOperationException("Place page data '" + data + "' not supported " + - "by existing controllers"); - mActiveController = controller; - mActiveController.openFor(data); - } - - @Override - public void close(boolean deactivateMapSelection) - { - mActiveController.close(deactivateMapSelection); - } - - @Override - public boolean isClosed() - { - return mActiveController.isClosed(); - } - - @Override - public void onActivityCreated(Activity activity, Bundle savedInstanceState) - { - mActiveController.onActivityCreated(activity, savedInstanceState); - } - - @Override - public void onActivityStarted(Activity activity) - { - mActiveController.onActivityStarted(activity); - } - - @Override - public void onActivityResumed(Activity activity) - { - mActiveController.onActivityResumed(activity); - } - - @Override - public void onActivityPaused(Activity activity) - { - mActiveController.onActivityPaused(activity); - } - - @Override - public void onActivityStopped(Activity activity) - { - mActiveController.onActivityStopped(activity); - } - - @Override - public void onActivitySaveInstanceState(Activity activity, Bundle outState) - { - mActiveController.onActivitySaveInstanceState(activity, outState); - } - - @Override - public void onActivityDestroyed(Activity activity) - { - mActiveController.onActivityDestroyed(activity); - } - - @Override - public void initialize(@Nullable Activity activity) - { - if (!mControllers.isEmpty()) - throw new AssertionError("Place page controllers already initialized!"); - - PlacePageController richController = - PlacePageFactory.createRichController(mSlideListener, mRoutingModeListener); - richController.initialize(activity); - mControllers.add(richController); - - PlacePageController elevationProfileController = - PlacePageFactory.createElevationProfilePlacePageController(mSlideListener); - elevationProfileController.initialize(activity); - mControllers.add(elevationProfileController); - - mActiveController = richController; - } - - - @Override - public void destroy() - { - if (mControllers.isEmpty()) - throw new AssertionError("Place page controllers already destroyed!"); - - for (PlacePageController controller: mControllers) - controller.destroy(); - - mControllers.clear(); - } - - @Override - public void onSave(@NonNull Bundle outState) - { - mActiveController.onSave(outState); - } - - @Override - public void onRestore(@NonNull Bundle inState) - { - PlacePageData data = Utils.getParcelable(inState, PlacePageUtils.EXTRA_PLACE_PAGE_DATA, PlacePageData.class); - if (data != null) - { - PlacePageController controller = findControllerFor(data); - if (controller != null) - mActiveController = controller; - } - mActiveController.onRestore(inState); - } - - @Nullable - private PlacePageController findControllerFor(@NonNull PlacePageData object) - { - for (PlacePageController controller : mControllers) - { - if (controller.support(object)) - return controller; - } - - return null; - } - - @Override - public boolean support(@NonNull PlacePageData object) - { - return mActiveController.support(object); - } -} diff --git a/android/src/app/organicmaps/widget/placepage/PlacePageFactory.java b/android/src/app/organicmaps/widget/placepage/PlacePageFactory.java deleted file mode 100644 index 182118e239..0000000000 --- a/android/src/app/organicmaps/widget/placepage/PlacePageFactory.java +++ /dev/null @@ -1,33 +0,0 @@ -package app.organicmaps.widget.placepage; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import app.organicmaps.R; - -public class PlacePageFactory -{ - @NonNull - public static PlacePageController createCompositePlacePageController( - @NonNull PlacePageController.SlideListener slideListener, - @NonNull RoutingModeListener routingModeListener) - { - return new PlacePageControllerComposite(slideListener, routingModeListener); - } - - @NonNull - static PlacePageController createRichController( - @NonNull PlacePageController.SlideListener listener, - @Nullable RoutingModeListener routingModeListener) - { - return new RichPlacePageController(listener, routingModeListener); - } - - @NonNull - static PlacePageController createElevationProfilePlacePageController( - @NonNull PlacePageController.SlideListener listener) - { - ElevationProfileViewRenderer renderer = new ElevationProfileViewRenderer(); - return new SimplePlacePageController(R.id.elevation_profile, renderer, renderer, listener); - } -} diff --git a/android/src/app/organicmaps/widget/placepage/PlacePageView.java b/android/src/app/organicmaps/widget/placepage/PlacePageView.java index 614fda225f..c1bb249856 100644 --- a/android/src/app/organicmaps/widget/placepage/PlacePageView.java +++ b/android/src/app/organicmaps/widget/placepage/PlacePageView.java @@ -195,6 +195,8 @@ public class PlacePageView extends NestedScrollViewClickFixed private OnPlacePageContentChangeListener mOnPlacePageContentChangeListener; + private OnPlacePageRequestCloseListener mOnPlacePageRequestCloseListener; + private final MapManager.StorageCallback mStorageCallback = new MapManager.StorageCallback() { @Override @@ -250,9 +252,6 @@ public class PlacePageView extends NestedScrollViewClickFixed } }; - @Nullable - private Closable mClosable; - @Nullable private PlacePageGestureListener mPlacePageGestureListener; @@ -269,9 +268,9 @@ public class PlacePageView extends NestedScrollViewClickFixed mPlacePageGestureListener = ppGestureListener; } - void addClosable(@NonNull Closable closable) + void setOnPlacePageRequestCloseListener(@NonNull OnPlacePageRequestCloseListener listener) { - mClosable = closable; + mOnPlacePageRequestCloseListener = listener; } @Override @@ -577,11 +576,11 @@ public class PlacePageView extends NestedScrollViewClickFixed if (!controller.isPlanning()) { controller.prepare(mMapObject, null); - close(); + mOnPlacePageRequestCloseListener.onPlacePageRequestClose(); } else if (controller.setStartPoint(mMapObject)) { - close(); + mOnPlacePageRequestCloseListener.onPlacePageRequestClose(); } } @@ -590,7 +589,7 @@ public class PlacePageView extends NestedScrollViewClickFixed if (RoutingController.get().isPlanning()) { RoutingController.get().setEndPoint(mMapObject); - close(); + mOnPlacePageRequestCloseListener.onPlacePageRequestClose(); } else { @@ -1554,12 +1553,6 @@ public class PlacePageView extends NestedScrollViewClickFixed return true; } - private void close() - { - if (mClosable != null) - mClosable.closePlacePage(); - } - void reset() { resetScroll(); @@ -1696,4 +1689,9 @@ public class PlacePageView extends NestedScrollViewClickFixed { void OnPlacePageContentChange(); } + + interface OnPlacePageRequestCloseListener + { + void onPlacePageRequestClose(); + } } diff --git a/android/src/app/organicmaps/widget/placepage/RichPlacePageController.java b/android/src/app/organicmaps/widget/placepage/RichPlacePageController.java deleted file mode 100644 index 0fc63a740d..0000000000 --- a/android/src/app/organicmaps/widget/placepage/RichPlacePageController.java +++ /dev/null @@ -1,349 +0,0 @@ -package app.organicmaps.widget.placepage; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.res.Resources; -import android.location.Location; -import android.os.Bundle; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.view.GestureDetectorCompat; -import com.google.android.material.bottomsheet.BottomSheetBehavior; -import app.organicmaps.Framework; -import app.organicmaps.R; -import app.organicmaps.bookmarks.data.MapObject; -import app.organicmaps.location.LocationHelper; -import app.organicmaps.location.LocationListener; -import app.organicmaps.util.UiUtils; -import app.organicmaps.util.Utils; -import app.organicmaps.util.bottomsheet.MenuBottomSheetItem; -import app.organicmaps.util.log.Logger; - -import java.util.ArrayList; -import java.util.Objects; - -public class RichPlacePageController implements PlacePageController, LocationListener, Closable -{ - private static final String TAG = RichPlacePageController.class.getSimpleName(); - - private static final float PREVIEW_PLUS_RATIO = 0.45f; - @SuppressWarnings("NullableProblems") - @NonNull - private BottomSheetBehavior mPlacePageBehavior; - @SuppressWarnings("NullableProblems") - @NonNull - private View mButtonsLayout; - @SuppressWarnings("NullableProblems") - @NonNull - private PlacePageView mPlacePage; - private int mViewportMinHeight; - @NonNull - private final SlideListener mSlideListener; - @Nullable - private final RoutingModeListener mRoutingModeListener; - @NonNull - private final BottomSheetChangedListener mBottomSheetChangedListener = new BottomSheetChangedListener() - { - @Override - public void onSheetHidden() - { - onHiddenInternal(); - } - - @Override - public void onSheetDetailsOpened() - { - // No op. - } - - @Override - public void onSheetCollapsed() - { - mPlacePage.resetScroll(); - setPeekHeight(); - } - - @Override - public void onSheetSliding(int top) - { - mSlideListener.onPlacePageSlide(top); - // mPlacePageTracker.onMove(); - } - - @Override - public void onSheetSlideFinish() - { - PlacePageUtils.moveViewportUp(mPlacePage, mViewportMinHeight); - } - }; - - @NonNull - private final BottomSheetBehavior.BottomSheetCallback mSheetCallback - = new DefaultBottomSheetCallback(mBottomSheetChangedListener); - - private boolean mDeactivateMapSelection = true; - - private void onHiddenInternal() - { - if (mDeactivateMapSelection) - Framework.nativeDeactivatePopup(); - mDeactivateMapSelection = true; - PlacePageUtils.moveViewportUp(mPlacePage, mViewportMinHeight); - UiUtils.invisible(mButtonsLayout); - } - - RichPlacePageController(@NonNull SlideListener listener, - @Nullable RoutingModeListener routingModeListener) - { - mSlideListener = listener; - mRoutingModeListener = routingModeListener; - } - - @SuppressLint("ClickableViewAccessibility") - @Override - public void initialize(@Nullable Activity activity) - { - Objects.requireNonNull(activity); - Resources res = activity.getResources(); - mViewportMinHeight = res.getDimensionPixelSize(R.dimen.viewport_min_height); - mPlacePage = activity.findViewById(R.id.placepage); - mPlacePageBehavior = BottomSheetBehavior.from(mPlacePage); - mPlacePageBehavior.addBottomSheetCallback(mSheetCallback); - mPlacePageBehavior.setHideable(true); - mPlacePageBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); - PlacePageGestureListener ppGestureListener = new PlacePageGestureListener(mPlacePageBehavior); - GestureDetectorCompat gestureDetector = new GestureDetectorCompat(activity, ppGestureListener); - mPlacePage.addPlacePageGestureListener(ppGestureListener); - mPlacePage.setOnTouchListener((v, event) -> gestureDetector.onTouchEvent(event)); - mPlacePage.addClosable(this); - mPlacePage.setRoutingModeListener(mRoutingModeListener); - mPlacePage.setOnPlacePageContentChangeListener(this::setPeekHeight); - - mButtonsLayout = activity.findViewById(R.id.pp_buttons_layout); - ViewGroup buttons = mButtonsLayout.findViewById(R.id.container); - mPlacePage.initButtons(buttons); - UiUtils.bringViewToFrontOf(mButtonsLayout, mPlacePage); - LocationHelper.INSTANCE.addListener(this); - - mButtonsLayout.setOnApplyWindowInsetsListener((view, windowInsets) -> { - UiUtils.setViewInsetsPaddingNoTop(mButtonsLayout, windowInsets); - return windowInsets; - }); - mPlacePage.requestApplyInsets(); - } - - public int getPlacePageWidth() - { - return mPlacePage.getWidth(); - } - - @Override - @Nullable - public ArrayList getMenuBottomSheetItems() - { - return mPlacePage.getMenuBottomSheetItems(); - } - - @Override - public void destroy() - { - LocationHelper.INSTANCE.removeListener(this); - } - - @Override - public void openFor(@NonNull PlacePageData data) - { - mDeactivateMapSelection = true; - MapObject object = (MapObject) data; - mPlacePage.setMapObject(object, (isSameObject) -> { - @BottomSheetBehavior.State - int state = mPlacePageBehavior.getState(); - if (isSameObject && !PlacePageUtils.isHiddenState(state)) - return; - - mPlacePage.resetScroll(); - - if (object.getOpeningMode() == MapObject.OPENING_MODE_DETAILS) - { - mPlacePageBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); - return; - } - - UiUtils.show(mButtonsLayout); - openPlacePage(); - }); - - } - - private void openPlacePage() - { - mPlacePage.post(() -> { - setPeekHeight(); - mPlacePageBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); - }); - } - - private void setPeekHeight() - { - final int peekHeight = calculatePeekHeight(); - if (peekHeight == mPlacePageBehavior.getPeekHeight()) - return; - - @BottomSheetBehavior.State - int currentState = mPlacePageBehavior.getState(); - if (PlacePageUtils.isSettlingState(currentState) || PlacePageUtils.isDraggingState(currentState)) - { - Logger.d(TAG, "Sheet state inappropriate, ignore."); - return; - } - - final boolean shouldAnimate = PlacePageUtils.isCollapsedState(currentState) && mPlacePageBehavior.getPeekHeight() > 0; - mPlacePageBehavior.setPeekHeight(peekHeight, shouldAnimate); - } - - private int calculatePeekHeight() - { - // Buttons layout padding is the navigation bar height. - // Bottom sheets are displayed above it so we need to remove it from the computed size - final int organicPeekHeight = mPlacePage.getPreviewHeight() + - mButtonsLayout.getHeight() - mButtonsLayout.getPaddingBottom(); - final MapObject object = mPlacePage.getMapObject(); - if (object != null) - { - @MapObject.OpeningMode - int mode = object.getOpeningMode(); - if (mode == MapObject.OPENING_MODE_PREVIEW_PLUS) - { - View parent = (View) mPlacePage.getParent(); - int promoPeekHeight = (int) (parent.getHeight() * PREVIEW_PLUS_RATIO); - return Math.max(promoPeekHeight, organicPeekHeight); - } - } - - return organicPeekHeight; - } - - @Override - public void close(boolean deactivateMapSelection) - { - mDeactivateMapSelection = deactivateMapSelection; - mPlacePageBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); - mPlacePage.reset(); - } - - @Override - public boolean isClosed() - { - return PlacePageUtils.isHiddenState(mPlacePageBehavior.getState()); - } - - @Override - public void onLocationUpdated(@NonNull Location location) - { - mPlacePage.refreshLocation(location); - } - - @Override - public void onCompassUpdated(double north) - { - @BottomSheetBehavior.State - int currentState = mPlacePageBehavior.getState(); - if (PlacePageUtils.isHiddenState(currentState)) - return; - - mPlacePage.refreshAzimuth(north); - } - - @Override - public void onSave(@NonNull Bundle outState) - { - outState.putParcelable(PlacePageUtils.EXTRA_PLACE_PAGE_DATA, mPlacePage.getMapObject()); - } - - @Override - public void onRestore(@NonNull Bundle inState) - { - if (mPlacePageBehavior.getState() == BottomSheetBehavior.STATE_HIDDEN) - return; - - if (!Framework.nativeHasPlacePageInfo()) - { - close(false); - return; - } - - MapObject object = Utils.getParcelable(inState, PlacePageUtils.EXTRA_PLACE_PAGE_DATA, MapObject.class); - if (object == null) - return; - - @BottomSheetBehavior.State - int state = mPlacePageBehavior.getState(); - mPlacePage.setMapObject(object, (isSameObject) -> restorePlacePageState(state)); - } - - private void restorePlacePageState(@BottomSheetBehavior.State int state) - { - mPlacePage.post(() -> { - mPlacePageBehavior.setState(state); - UiUtils.show(mButtonsLayout); - setPeekHeight(); - }); - } - - @Override - public void onActivityCreated(Activity activity, Bundle savedInstanceState) - { - // No op. - } - - @Override - public void onActivityStarted(Activity activity) - { - // No op. - } - - @Override - public void onActivityResumed(Activity activity) - { - // No op. - } - - @Override - public void onActivityPaused(Activity activity) - { - // No op. - } - - @Override - public void onActivityStopped(Activity activity) - { - // No op. - } - - @Override - public void onActivitySaveInstanceState(Activity activity, Bundle outState) - { - // No op. - } - - @Override - public void onActivityDestroyed(Activity activity) - { - // No op. - } - - @Override - public void closePlacePage() - { - close(true); - } - - @Override - public boolean support(@NonNull PlacePageData data) - { - return data instanceof MapObject; - } -} diff --git a/android/src/app/organicmaps/widget/placepage/SimplePlacePageController.java b/android/src/app/organicmaps/widget/placepage/SimplePlacePageController.java deleted file mode 100644 index c7996a6024..0000000000 --- a/android/src/app/organicmaps/widget/placepage/SimplePlacePageController.java +++ /dev/null @@ -1,288 +0,0 @@ -package app.organicmaps.widget.placepage; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.app.Application; -import android.os.Bundle; -import android.view.MotionEvent; -import android.view.View; - -import androidx.annotation.IdRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.view.GestureDetectorCompat; - -import com.google.android.material.bottomsheet.BottomSheetBehavior; -import app.organicmaps.Framework; -import app.organicmaps.R; -import app.organicmaps.util.UiUtils; -import app.organicmaps.util.bottomsheet.MenuBottomSheetItem; - -import java.util.ArrayList; -import java.util.Objects; - -public class SimplePlacePageController implements PlacePageController -{ - @SuppressWarnings("NullableProblems") - @NonNull - private Application mApplication; - @SuppressWarnings("NullableProblems") - @NonNull - private View mSheet; - @SuppressWarnings("NullableProblems") - @NonNull - private BottomSheetBehavior mSheetBehavior; - @NonNull - private final SlideListener mSlideListener; - private int mViewportMinHeight; - private int mViewPortMinWidth; - @NonNull - private final PlacePageViewRenderer mViewRenderer; - @Nullable - private final PlacePageStateListener mStateListener; - @NonNull - private final BottomSheetChangedListener mBottomSheetChangedListener = - new BottomSheetChangedListener() - { - @Override - public void onSheetHidden() - { - onHiddenInternal(); - if (mStateListener != null) - mStateListener.onPlacePageClosed(); - } - - @Override - public void onSheetDetailsOpened() - { - if (UiUtils.isLandscape(mApplication)) - PlacePageUtils.moveViewPortRight(mSheet, mViewPortMinWidth); - if (mStateListener != null) - mStateListener.onPlacePageDetails(); - } - - @Override - public void onSheetCollapsed() - { - if (UiUtils.isLandscape(mApplication)) - PlacePageUtils.moveViewPortRight(mSheet, mViewPortMinWidth); - if (mStateListener != null) - mStateListener.onPlacePagePreview(); - } - - @Override - public void onSheetSliding(int top) - { - if (UiUtils.isLandscape(mApplication)) - return; - - mSlideListener.onPlacePageSlide(top); - } - - @Override - public void onSheetSlideFinish() - { - if (UiUtils.isLandscape(mApplication)) - return; - - PlacePageUtils.moveViewportUp(mSheet, mViewportMinHeight); - } - }; - - private final BottomSheetBehavior.BottomSheetCallback mSheetCallback - = new DefaultBottomSheetCallback(mBottomSheetChangedListener); - - private boolean mDeactivateMapSelection = true; - @IdRes - private final int mSheetResId; - - SimplePlacePageController(int sheetResId, @NonNull PlacePageViewRenderer renderer, - @Nullable PlacePageStateListener stateListener, - @NonNull SlideListener slideListener) - { - mSheetResId = sheetResId; - mSlideListener = slideListener; - mViewRenderer = renderer; - mStateListener = stateListener; - } - - public int getPlacePageWidth() - { - return mSheet.getWidth(); - } - - @Override - @Nullable - public ArrayList getMenuBottomSheetItems() - { - return null; - } - - @Override - public void openFor(@NonNull PlacePageData data) - { - mDeactivateMapSelection = true; - mViewRenderer.render(data); - if (mSheetBehavior.getSkipCollapsed()) - mSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); - else - mSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); - } - - @Override - public void close(boolean deactivateMapSelection) - { - mDeactivateMapSelection = deactivateMapSelection; - mSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); - } - - @Override - public boolean isClosed() - { - return PlacePageUtils.isHiddenState(mSheetBehavior.getState()); - } - - @Override - public void onActivityCreated(Activity activity, Bundle savedInstanceState) - { - - } - - @Override - public void onActivityStarted(Activity activity) - { - - } - - @Override - public void onActivityResumed(Activity activity) - { - - } - - @Override - public void onActivityPaused(Activity activity) - { - - } - - @Override - public void onActivityStopped(Activity activity) - { - - } - - @Override - public void onActivitySaveInstanceState(Activity activity, Bundle outState) - { - - } - - @Override - public void onActivityDestroyed(Activity activity) - { - - } - - @SuppressLint("ClickableViewAccessibility") - @Override - public void initialize(@Nullable Activity activity) - { - Objects.requireNonNull(activity); - mApplication = activity.getApplication(); - mSheet = activity.findViewById(mSheetResId); - mViewportMinHeight = mSheet.getResources().getDimensionPixelSize(R.dimen.viewport_min_height); - mViewPortMinWidth = mSheet.getResources().getDimensionPixelSize(R.dimen.viewport_min_width); - mSheetBehavior = BottomSheetBehavior.from(mSheet); - mSheetBehavior.addBottomSheetCallback(mSheetCallback); - mSheetBehavior.setHideable(true); - mSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); - boolean isLandscape = UiUtils.isLandscape(mApplication); - GestureDetectorCompat gestureDetector = new GestureDetectorCompat( - activity, new SimplePlacePageGestureListener(mSheetBehavior, isLandscape)); - mSheet.setOnTouchListener((v, event) -> gestureDetector.onTouchEvent(event)); - mViewRenderer.initialize(mSheet); - } - - @Override - public void destroy() - { - mViewRenderer.destroy(); - } - - @Override - public void onSave(@NonNull Bundle outState) - { - mViewRenderer.onSave(outState); - } - - @Override - public void onRestore(@NonNull Bundle inState) - { - if (PlacePageUtils.isHiddenState(mSheetBehavior.getState())) - return; - - if (!Framework.nativeHasPlacePageInfo()) - { - close(false); - return; - } - - mViewRenderer.onRestore(inState); - if (UiUtils.isLandscape(mApplication)) - { - // In case when bottom sheet was collapsed for vertical orientation then after rotation - // we should expand bottom sheet forcibly for horizontal orientation. It's by design. - if (!PlacePageUtils.isHiddenState(mSheetBehavior.getState())) - { - mSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); - } - return; - } - } - - private void onHiddenInternal() - { - mViewRenderer.onHide(); - if (mDeactivateMapSelection) - Framework.nativeDeactivatePopup(); - mDeactivateMapSelection = true; - if (UiUtils.isLandscape(mApplication)) - { - PlacePageUtils.moveViewPortRight(mSheet, mViewPortMinWidth); - return; - } - - PlacePageUtils.moveViewportUp(mSheet, mViewportMinHeight); - } - - @Override - public boolean support(@NonNull PlacePageData data) - { - return mViewRenderer.support(data); - } - - private static class SimplePlacePageGestureListener extends PlacePageGestureListener - { - private final boolean mLandscape; - - SimplePlacePageGestureListener(@NonNull BottomSheetBehavior bottomSheetBehavior, - boolean landscape) - { - super(bottomSheetBehavior); - mLandscape = landscape; - } - - @Override - public boolean onSingleTapConfirmed(MotionEvent e) - { - if (mLandscape) - { - getBottomSheetBehavior().setState(BottomSheetBehavior.STATE_HIDDEN); - return false; - } - - return super.onSingleTapConfirmed(e); - } - } -} -- 2.45.3 From 356b315263b0279ff92f82b4798214e5f6271ef2 Mon Sep 17 00:00:00 2001 From: Arnaud Vergnet Date: Mon, 26 Dec 2022 11:19:11 +0100 Subject: [PATCH 02/30] android: reduce coupling with place page view and buttons Signed-off-by: Arnaud Vergnet --- .../widget/placepage/PlacePageButtons.java | 18 +++++++++--------- .../widget/placepage/PlacePageView.java | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/android/src/app/organicmaps/widget/placepage/PlacePageButtons.java b/android/src/app/organicmaps/widget/placepage/PlacePageButtons.java index 36222dad56..16d611e9b5 100644 --- a/android/src/app/organicmaps/widget/placepage/PlacePageButtons.java +++ b/android/src/app/organicmaps/widget/placepage/PlacePageButtons.java @@ -12,6 +12,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; +import app.organicmaps.MwmActivity; import app.organicmaps.MwmApplication; import app.organicmaps.R; import app.organicmaps.routing.RoutingController; @@ -29,7 +30,7 @@ public final class PlacePageButtons public static final String PLACEPAGE_MORE_MENU_ID = "PLACEPAGE_MORE_MENU_BOTTOM_SHEET"; private final int mMaxButtons; - private final PlacePageView mPlacePage; + private final MwmActivity mMwmActivity; private final ViewGroup mFrame; private final ItemListener mItemListener; @@ -244,13 +245,13 @@ public final class PlacePageButtons void onItemClick(PlacePageButton item); } - PlacePageButtons(PlacePageView placePage, ViewGroup frame, ItemListener itemListener) + PlacePageButtons(MwmActivity mwmActivity, ViewGroup frame, ItemListener itemListener) { - mPlacePage = placePage; + mMwmActivity = mwmActivity; mFrame = frame; mItemListener = itemListener; - mMaxButtons = mPlacePage.getContext().getResources().getInteger(R.integer.pp_buttons_max); + mMaxButtons = mMwmActivity.getResources().getInteger(R.integer.pp_buttons_max); } private @NonNull List collectButtons(List items) @@ -320,7 +321,7 @@ public final class PlacePageButtons for (int i = mMaxButtons; i < mMoreItems.size(); i++) { final PlacePageButton bsItem = mMoreItems.get(i); - int iconRes = bsItem.getIcon().getEnabledStateResId(mPlacePage.getContext()); + int iconRes = bsItem.getIcon().getEnabledStateResId(mMwmActivity); items.add(new MenuBottomSheetItem(bsItem.getTitle(), iconRes, () -> mItemListener.onItemClick(bsItem))); } return items; @@ -329,21 +330,20 @@ public final class PlacePageButtons private void showPopup() { MenuBottomSheetFragment.newInstance(PLACEPAGE_MORE_MENU_ID) - .show(mPlacePage.requireActivity().getSupportFragmentManager(), PLACEPAGE_MORE_MENU_ID); + .show(mMwmActivity.getSupportFragmentManager(), PLACEPAGE_MORE_MENU_ID); } private View createButton(@NonNull final List items, @NonNull final PlacePageButton current) { - Context context = mPlacePage.getContext(); - LayoutInflater inflater = LayoutInflater.from(context); + LayoutInflater inflater = LayoutInflater.from(mMwmActivity); View parent = inflater.inflate(R.layout.place_page_button, mFrame, false); ImageView icon = parent.findViewById(R.id.icon); TextView title = parent.findViewById(R.id.title); title.setText(current.getTitle()); - icon.setImageResource(current.getIcon().getEnabledStateResId(context)); + icon.setImageResource(current.getIcon().getEnabledStateResId(mMwmActivity)); mItemListener.onPrepareVisibleView(current, parent, icon, title); if (current == Item.MORE) mMoreItems = items; diff --git a/android/src/app/organicmaps/widget/placepage/PlacePageView.java b/android/src/app/organicmaps/widget/placepage/PlacePageView.java index c1bb249856..6e9c2e1563 100644 --- a/android/src/app/organicmaps/widget/placepage/PlacePageView.java +++ b/android/src/app/organicmaps/widget/placepage/PlacePageView.java @@ -447,7 +447,7 @@ public class PlacePageView extends NestedScrollViewClickFixed public void initButtons(@NonNull ViewGroup buttons) { - mButtons = new PlacePageButtons(this, buttons, new PlacePageButtons.ItemListener() + mButtons = new PlacePageButtons(requireActivity(), buttons, new PlacePageButtons.ItemListener() { public void onPrepareVisibleView(@NonNull PlacePageButtons.PlacePageButton item, @NonNull View frame, @NonNull ImageView icon, -- 2.45.3 From 14c01964c2f0d72c4a6630cc600fd8a4e10678d0 Mon Sep 17 00:00:00 2001 From: Arnaud Vergnet Date: Wed, 28 Dec 2022 17:33:50 +0100 Subject: [PATCH 03/30] android: move place page buttons to fragment Signed-off-by: Arnaud Vergnet --- android/res/layout/activity_map.xml | 16 +- android/res/layout/pp_buttons_fragment.xml | 16 + android/res/values/styles-place_page.xml | 2 + android/src/app/organicmaps/MwmActivity.java | 13 +- .../widget/placepage/PlacePageButton.java | 40 ++ .../placepage/PlacePageButtonFactory.java | 71 +++ .../widget/placepage/PlacePageButtons.java | 458 +++++------------ .../placepage/PlacePageButtonsViewModel.java | 22 + .../widget/placepage/PlacePageController.java | 76 ++- .../widget/placepage/PlacePageView.java | 475 ++++++++---------- 10 files changed, 547 insertions(+), 642 deletions(-) create mode 100644 android/res/layout/pp_buttons_fragment.xml create mode 100644 android/src/app/organicmaps/widget/placepage/PlacePageButton.java create mode 100644 android/src/app/organicmaps/widget/placepage/PlacePageButtonFactory.java create mode 100644 android/src/app/organicmaps/widget/placepage/PlacePageButtonsViewModel.java diff --git a/android/res/layout/activity_map.xml b/android/res/layout/activity_map.xml index 5ff52639db..5a2ea417ea 100644 --- a/android/res/layout/activity_map.xml +++ b/android/res/layout/activity_map.xml @@ -58,18 +58,8 @@ android:layout_height="wrap_content" android:fillViewport="true" app:layout_behavior="@string/placepage_behavior" /> - - - - + android:layout_height="match_parent" /> diff --git a/android/res/layout/pp_buttons_fragment.xml b/android/res/layout/pp_buttons_fragment.xml new file mode 100644 index 0000000000..7a71087ebf --- /dev/null +++ b/android/res/layout/pp_buttons_fragment.xml @@ -0,0 +1,16 @@ + + + + + \ No newline at end of file diff --git a/android/res/values/styles-place_page.xml b/android/res/values/styles-place_page.xml index bdddaed873..5e89e69e44 100644 --- a/android/res/values/styles-place_page.xml +++ b/android/res/values/styles-place_page.xml @@ -6,6 +6,7 @@ center true 10sp + ?iconTint