From f5c2df2694c21d167f5670b10182f04cff4de230 Mon Sep 17 00:00:00 2001 From: Arnaud Vergnet Date: Thu, 16 Mar 2023 18:12:13 +0100 Subject: [PATCH] [android] improve place page fragment abstraction Signed-off-by: Arnaud Vergnet --- android/res/layout/activity_map.xml | 24 +- .../layout/place_page_container_fragment.xml | 29 ++ android/src/app/organicmaps/MwmActivity.java | 64 +--- .../maplayer/MapButtonsController.java | 22 +- .../widget/placepage/PlacePageButtons.java | 25 +- .../widget/placepage/PlacePageController.java | 355 +++++++++++------- .../widget/placepage/PlacePageView.java | 197 +--------- .../widget/placepage/PlacePageViewModel.java | 22 ++ .../sections/PlacePageBookmarkFragment.java | 12 +- .../sections/PlacePageLinksFragment.java | 17 +- .../PlacePageOpeningHoursFragment.java | 22 +- .../sections/PlacePagePhoneFragment.java | 13 +- .../sections/PlacePageWikipediaFragment.java | 17 +- 13 files changed, 373 insertions(+), 446 deletions(-) create mode 100644 android/res/layout/place_page_container_fragment.xml diff --git a/android/res/layout/activity_map.xml b/android/res/layout/activity_map.xml index 9e74ce13d2..abe4998ff8 100644 --- a/android/res/layout/activity_map.xml +++ b/android/res/layout/activity_map.xml @@ -1,7 +1,6 @@ - - - - - + android:layout_height="match_parent" + android:name="app.organicmaps.widget.placepage.PlacePageController" /> diff --git a/android/res/layout/place_page_container_fragment.xml b/android/res/layout/place_page_container_fragment.xml new file mode 100644 index 0000000000..f53af67f3a --- /dev/null +++ b/android/res/layout/place_page_container_fragment.xml @@ -0,0 +1,29 @@ + + + + + + + + + \ No newline at end of file diff --git a/android/src/app/organicmaps/MwmActivity.java b/android/src/app/organicmaps/MwmActivity.java index 3e0a973f44..106a35c8e2 100644 --- a/android/src/app/organicmaps/MwmActivity.java +++ b/android/src/app/organicmaps/MwmActivity.java @@ -26,6 +26,7 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentFactory; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; +import androidx.lifecycle.ViewModelProvider; import app.organicmaps.Framework.PlacePageActivationListener; import app.organicmaps.api.Const; import app.organicmaps.background.Notifier; @@ -88,18 +89,15 @@ import app.organicmaps.util.bottomsheet.MenuBottomSheetFragment; import app.organicmaps.util.bottomsheet.MenuBottomSheetItem; import app.organicmaps.util.log.Logger; import app.organicmaps.widget.menu.MainMenu; -import app.organicmaps.widget.placepage.PlacePageButtons; import app.organicmaps.widget.placepage.PlacePageController; import app.organicmaps.widget.placepage.PlacePageData; -import app.organicmaps.widget.placepage.PlacePageView; +import app.organicmaps.widget.placepage.PlacePageViewModel; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import java.util.ArrayList; import java.util.Objects; import java.util.Stack; -import static app.organicmaps.widget.placepage.PlacePageButtons.PLACEPAGE_MORE_MENU_ID; - public class MwmActivity extends BaseMwmFragmentActivity implements PlacePageActivationListener, View.OnTouchListener, @@ -112,12 +110,11 @@ public class MwmActivity extends BaseMwmFragmentActivity RoutingBottomMenuListener, BookmarkManager.BookmarksLoadingListener, FloatingSearchToolbarController.SearchToolbarListener, - PlacePageController.SlideListener, NoConnectionListener, MenuBottomSheetFragment.MenuBottomSheetInterfaceWithHeader, + PlacePageController.PlacePageRouteSettingsListener, + MapButtonsController.MapButtonClickListener, ToggleMapLayerFragment.LayerItemClickListener, - PlacePageButtons.PlacePageButtonClickListener, - PlacePageView.PlacePageViewListener { private static final String TAG = MwmActivity.class.getSimpleName(); @@ -184,15 +181,14 @@ public class MwmActivity extends BaseMwmFragmentActivity private boolean mRestoreRoutingPlanFragmentNeeded; @Nullable private Bundle mSavedForTabletState; - @SuppressWarnings("NotNullFieldNotInitialized") - @NonNull - private PlacePageController mPlacePageController; private MapButtonsController.LayoutMode mCurrentLayoutMode; private String mDonatesUrl; private int mNavBarHeight; + private PlacePageViewModel mPlacePageViewModel; + @Nullable private WindowInsetsCompat mCurrentWindowInsets; @@ -380,8 +376,7 @@ public class MwmActivity extends BaseMwmFragmentActivity setContentView(R.layout.activity_map); UiUtils.setupTransparentStatusBar(this); - mPlacePageController = new PlacePageController(this, this); - mPlacePageController.initialize(this); + mPlacePageViewModel = new ViewModelProvider(this).get(PlacePageViewModel.class); mSearchController = new FloatingSearchToolbarController(this, this); mSearchController.getToolbar() @@ -616,7 +611,6 @@ public class MwmActivity extends BaseMwmFragmentActivity LocationState.nativeGetMode(), this::onMapButtonClick, (v) -> closeSearchToolbar(true, true), - mPlacePageController, this::updateBottomWidgetsOffset); @@ -687,10 +681,10 @@ public class MwmActivity extends BaseMwmFragmentActivity */ public boolean closePlacePage() { - if (mPlacePageController.isClosed()) + if (mPlacePageViewModel.getMapObject().getValue() == null) return false; - mPlacePageController.close(true); + mPlacePageViewModel.setMapObject(null); return true; } @@ -817,7 +811,6 @@ public class MwmActivity extends BaseMwmFragmentActivity @Override protected void onSaveInstanceState(@NonNull Bundle outState) { - mPlacePageController.onSave(outState); if (!mIsTabletLayout && RoutingController.get().isPlanning()) mRoutingPlanInplaceController.onSaveState(outState); @@ -848,7 +841,6 @@ public class MwmActivity extends BaseMwmFragmentActivity protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); - mPlacePageController.onRestore(savedInstanceState); if (mIsTabletLayout) { RoutingPlanFragment fragment = (RoutingPlanFragment) getFragment(RoutingPlanFragment.class); @@ -1054,8 +1046,6 @@ public class MwmActivity extends BaseMwmFragmentActivity { super.onSafeDestroy(); mNavigationController.destroy(); - //TrafficManager.INSTANCE.detachAll(); - mPlacePageController.destroy(); } @Override @@ -1134,8 +1124,8 @@ public class MwmActivity extends BaseMwmFragmentActivity public void onPlacePageActivated(@NonNull PlacePageData data) { setFullscreen(false); - - mPlacePageController.openFor(data); + // This will open the place page + mPlacePageViewModel.setMapObject((MapObject) data); } // Called from JNI. @@ -1168,12 +1158,6 @@ public class MwmActivity extends BaseMwmFragmentActivity UiUtils.setFullscreen(this, isFullscreen); } - @Override - public void onPlacePageSlide(int top) - { - mMapButtonsController.move(top); - } - @Override public boolean onTouch(View view, MotionEvent event) { @@ -1835,8 +1819,6 @@ public class MwmActivity extends BaseMwmFragmentActivity items.add(new MenuBottomSheetItem(R.string.share_my_location, R.drawable.ic_share, this::onShareLocationOptionSelected)); return items; } - else if (id.equals(PLACEPAGE_MORE_MENU_ID)) - return mPlacePageController.getMenuBottomSheetItems(id); return null; } @@ -1849,30 +1831,6 @@ public class MwmActivity extends BaseMwmFragmentActivity return null; } - @Override - public void onPlacePageButtonClick(PlacePageButtons.ButtonType item) - { - mPlacePageController.onPlacePageButtonClick(item); - } - - @Override - public void onPlacePageContentChanged(int previewHeight, int frameHeight) - { - mPlacePageController.onPlacePageContentChanged(previewHeight, frameHeight); - } - - @Override - public void onPlacePageRequestClose() - { - mPlacePageController.onPlacePageRequestClose(); - } - - @Override - public void onPlacePageRequestToggleState() - { - mPlacePageController.onPlacePageRequestToggleState(); - } - @Override public void onPlacePageRequestToggleRouteSettings(@NonNull RoadType roadType) { diff --git a/android/src/app/organicmaps/maplayer/MapButtonsController.java b/android/src/app/organicmaps/maplayer/MapButtonsController.java index ca948cf4c5..de7dfb813f 100644 --- a/android/src/app/organicmaps/maplayer/MapButtonsController.java +++ b/android/src/app/organicmaps/maplayer/MapButtonsController.java @@ -13,6 +13,7 @@ import androidx.annotation.Nullable; import androidx.annotation.OptIn; import androidx.core.view.ViewCompat; import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; import app.organicmaps.R; import app.organicmaps.downloader.MapManager; import app.organicmaps.downloader.UpdateInfo; @@ -21,7 +22,7 @@ import app.organicmaps.util.Config; import app.organicmaps.util.ThemeUtils; import app.organicmaps.util.UiUtils; import app.organicmaps.widget.menu.MyPositionButton; -import app.organicmaps.widget.placepage.PlacePageController; +import app.organicmaps.widget.placepage.PlacePageViewModel; import com.google.android.material.badge.BadgeDrawable; import com.google.android.material.badge.BadgeUtils; import com.google.android.material.floatingactionbutton.FloatingActionButton; @@ -49,11 +50,11 @@ public class MapButtonsController extends Fragment private MapButtonClickListener mMapButtonClickListener; private View.OnClickListener mOnSearchCanceledListener; - private PlacePageController mPlacePageController; private OnBottomButtonsHeightChangedListener mOnBottomButtonsHeightChangedListener; private LayoutMode mLayoutMode; private int mMyPositionMode; + private PlacePageViewModel mViewModel; @Nullable @Override @@ -136,6 +137,8 @@ public class MapButtonsController extends Fragment return windowInsets; }); + mViewModel = new ViewModelProvider(requireActivity()).get(PlacePageViewModel.class); + return mFrame; } @@ -144,13 +147,12 @@ public class MapButtonsController extends Fragment return mLayoutMode; } - public void init(LayoutMode layoutMode, int myPositionMode, MapButtonClickListener mapButtonClickListener, @NonNull View.OnClickListener onSearchCanceledListener, PlacePageController placePageController, OnBottomButtonsHeightChangedListener onBottomButtonsHeightChangedListener) + public void init(LayoutMode layoutMode, int myPositionMode, MapButtonClickListener mapButtonClickListener, @NonNull View.OnClickListener onSearchCanceledListener, OnBottomButtonsHeightChangedListener onBottomButtonsHeightChangedListener) { mLayoutMode = layoutMode; mMyPositionMode = myPositionMode; mMapButtonClickListener = mapButtonClickListener; mOnSearchCanceledListener = onSearchCanceledListener; - mPlacePageController = placePageController; mOnBottomButtonsHeightChangedListener = onBottomButtonsHeightChangedListener; } @@ -209,7 +211,10 @@ public class MapButtonsController extends Fragment private boolean isBehindPlacePage(View v) { - return !(mContentWidth / 2 > (mPlacePageController.getPlacePageWidth() / 2.0) + v.getWidth()); + if (mViewModel == null) + return false; + final int placePageWidth = mViewModel.getPlacePageWidth().getValue(); + return !(mContentWidth / 2 > (placePageWidth / 2.0) + v.getWidth()); } private boolean isMoving(View v) @@ -301,9 +306,16 @@ public class MapButtonsController extends Fragment return (int) (translation + v.getTop()); } + @Override + public void onPause() + { + super.onPause(); + mViewModel.getPlacePageDistanceToTop().removeObserver(this::move); + } public void onResume() { super.onResume(); + mViewModel.getPlacePageDistanceToTop().observe(requireActivity(), this::move); mSearchWheel.onResume(); updateMenuBadge(); } diff --git a/android/src/app/organicmaps/widget/placepage/PlacePageButtons.java b/android/src/app/organicmaps/widget/placepage/PlacePageButtons.java index abecaa59b9..6cd35663bb 100644 --- a/android/src/app/organicmaps/widget/placepage/PlacePageButtons.java +++ b/android/src/app/organicmaps/widget/placepage/PlacePageButtons.java @@ -1,6 +1,5 @@ package app.organicmaps.widget.placepage; -import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -50,28 +49,24 @@ public final class PlacePageButtons extends Fragment implements Observer buttons) diff --git a/android/src/app/organicmaps/widget/placepage/PlacePageController.java b/android/src/app/organicmaps/widget/placepage/PlacePageController.java index 590929cb26..e3b15a7eb7 100644 --- a/android/src/app/organicmaps/widget/placepage/PlacePageController.java +++ b/android/src/app/organicmaps/widget/placepage/PlacePageController.java @@ -1,96 +1,82 @@ package app.organicmaps.widget.placepage; import android.animation.ValueAnimator; -import android.annotation.SuppressLint; import android.app.Activity; +import android.content.Intent; import android.content.res.Resources; import android.os.Bundle; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; import androidx.core.widget.NestedScrollViewClickFixed; import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import androidx.interpolator.view.animation.FastOutSlowInInterpolator; -import androidx.lifecycle.Lifecycle; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProvider; import app.organicmaps.Framework; import app.organicmaps.MwmActivity; import app.organicmaps.R; +import app.organicmaps.api.Const; import app.organicmaps.api.ParsedMwmRequest; -import app.organicmaps.base.Initializable; -import app.organicmaps.base.Savable; +import app.organicmaps.bookmarks.data.BookmarkManager; import app.organicmaps.bookmarks.data.MapObject; import app.organicmaps.bookmarks.data.RoadWarningMarkType; import app.organicmaps.routing.RoutingController; import app.organicmaps.settings.RoadType; +import app.organicmaps.util.SharingUtils; import app.organicmaps.util.UiUtils; import app.organicmaps.util.bottomsheet.MenuBottomSheetFragment; 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.List; -import java.util.Objects; -public class PlacePageController implements Initializable, - Savable, - PlacePageView.PlacePageViewListener, - PlacePageButtons.PlacePageButtonClickListener, - MenuBottomSheetFragment.MenuBottomSheetInterface, - Observer +public class PlacePageController extends Fragment implements + PlacePageView.PlacePageViewListener, + PlacePageButtons.PlacePageButtonClickListener, + MenuBottomSheetFragment.MenuBottomSheetInterface, + Observer { private static final String TAG = PlacePageController.class.getSimpleName(); private static final String PLACE_PAGE_BUTTONS_FRAGMENT_TAG = "PLACE_PAGE_BUTTONS"; private static final String PLACE_PAGE_FRAGMENT_TAG = "PLACE_PAGE"; private static final float PREVIEW_PLUS_RATIO = 0.45f; - @NonNull - private final SlideListener mSlideListener; - @NonNull - private final BottomSheetBehavior mPlacePageBehavior; - @NonNull - private final NestedScrollViewClickFixed mPlacePage; - @NonNull - private final ViewGroup mPlacePageContainer; - private final ViewGroup mCoordinator; - private final int mViewportMinHeight; - private final AppCompatActivity mMwmActivity; - private final int mButtonsHeight; - private final int mMaxButtons; - private final PlacePageViewModel mViewModel; + private BottomSheetBehavior mPlacePageBehavior; + private NestedScrollViewClickFixed mPlacePage; + private ViewGroup mPlacePageContainer; + private ViewGroup mCoordinator; + private int mViewportMinHeight; + private int mButtonsHeight; + private int mMaxButtons; + private PlacePageViewModel mViewModel; private int mPreviewHeight; private int mFrameHeight; - private boolean mDeactivateMapSelection = true; @Nullable private MapObject mMapObject; + @Nullable + private MapObject mPreviousMapObject; private WindowInsetsCompat mCurrentWindowInsets; private boolean mShouldCollapse; private int mDistanceToTop; private ValueAnimator mCustomPeekHeightAnimator; - - class DefaultBottomSheetCallback extends BottomSheetBehavior.BottomSheetCallback + private PlacePageRouteSettingsListener mPlacePageRouteSettingsListener; + private final BottomSheetBehavior.BottomSheetCallback mDefaultBottomSheetCallback = new BottomSheetBehavior.BottomSheetCallback() { @Override public void onStateChanged(@NonNull View bottomSheet, int newState) { - final Lifecycle.State state = mMwmActivity.getLifecycle().getCurrentState(); - if (!state.isAtLeast(Lifecycle.State.RESUMED)) - { - Logger.e(TAG, "Called in the wrong activity state=" + state); - return; - } - Logger.d(TAG, "State change, new = " + PlacePageUtils.toString(newState)); if (PlacePageUtils.isSettlingState(newState) || PlacePageUtils.isDraggingState(newState)) return; @@ -104,51 +90,48 @@ public class PlacePageController implements Initializable, @Override public void onSlide(@NonNull View bottomSheet, float slideOffset) { - final Lifecycle.State state = mMwmActivity.getLifecycle().getCurrentState(); - if (!state.isAtLeast(Lifecycle.State.RESUMED)) - { - Logger.e(TAG, "Called in the wrong activity state=" + state); - return; - } stopCustomPeekHeightAnimation(); mDistanceToTop = bottomSheet.getTop(); - mSlideListener.onPlacePageSlide(mDistanceToTop); + mViewModel.setPlacePageDistanceToTop(mDistanceToTop); } + }; + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) + { + return inflater.inflate(R.layout.place_page_container_fragment, container, false); } - @SuppressLint("ClickableViewAccessibility") - public PlacePageController(@Nullable Activity activity, - @NonNull SlideListener listener) + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - mSlideListener = listener; + super.onViewCreated(view, savedInstanceState); + final FragmentActivity activity = requireActivity(); + mPlacePageRouteSettingsListener = (MwmActivity) activity; - Objects.requireNonNull(activity); - mMwmActivity = (AppCompatActivity) activity; - mCoordinator = mMwmActivity.findViewById(R.id.coordinator); - Resources res = activity.getResources(); + final Resources res = activity.getResources(); mViewportMinHeight = res.getDimensionPixelSize(R.dimen.viewport_min_height); - mPlacePage = activity.findViewById(R.id.placepage); - mPlacePageContainer = activity.findViewById(R.id.placepage_container); + mButtonsHeight = (int) res.getDimension(R.dimen.place_page_buttons_height); + mMaxButtons = res.getInteger(R.integer.pp_buttons_max); + + mCoordinator = activity.findViewById(R.id.coordinator); + mPlacePage = view.findViewById(R.id.placepage); + mPlacePageContainer = view.findViewById(R.id.placepage_container); mPlacePageBehavior = BottomSheetBehavior.from(mPlacePage); mShouldCollapse = true; - mPlacePageBehavior.addBottomSheetCallback(new DefaultBottomSheetCallback()); mPlacePageBehavior.setHideable(true); mPlacePageBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); mPlacePageBehavior.setFitToContents(true); mPlacePageBehavior.setSkipCollapsed(true); - UiUtils.bringViewToFrontOf(activity.findViewById(R.id.pp_buttons_fragment), mPlacePage); + UiUtils.bringViewToFrontOf(view.findViewById(R.id.pp_buttons_fragment), mPlacePage); - mPlacePage.requestApplyInsets(); + mViewModel = new ViewModelProvider(requireActivity()).get(PlacePageViewModel.class); - mButtonsHeight = (int) mMwmActivity.getResources() - .getDimension(R.dimen.place_page_buttons_height); - mMaxButtons = mMwmActivity.getResources().getInteger(R.integer.pp_buttons_max); - mViewModel = new ViewModelProvider(mMwmActivity).get(PlacePageViewModel.class); - - ViewCompat.setOnApplyWindowInsetsListener(mPlacePage, (view, windowInsets) -> { + ViewCompat.setOnApplyWindowInsetsListener(mPlacePage, (v, windowInsets) -> { mCurrentWindowInsets = windowInsets; return windowInsets; }); @@ -181,19 +164,12 @@ public class PlacePageController implements Initializable, private void onHiddenInternal() { - if (mDeactivateMapSelection) - Framework.nativeDeactivatePopup(); - mDeactivateMapSelection = true; + Framework.nativeDeactivatePopup(); PlacePageUtils.moveViewportUp(mPlacePage, mViewportMinHeight); resetPlacePageHeightBounds(); removePlacePageFragments(); } - public int getPlacePageWidth() - { - return mPlacePage.getWidth(); - } - @Nullable public ArrayList getMenuBottomSheetItems(String id) { @@ -203,24 +179,27 @@ public class PlacePageController implements Initializable, ArrayList items = new ArrayList<>(); for (int i = mMaxButtons - 1; i < currentItems.size(); i++) { - final PlacePageButton bsItem = PlacePageButtonFactory.createButton(currentItems.get(i), mMwmActivity); + final PlacePageButton bsItem = PlacePageButtonFactory.createButton(currentItems.get(i), requireActivity()); items.add(new MenuBottomSheetItem( bsItem.getTitle(), bsItem.getIcon(), - () -> ((PlacePageButtons.PlacePageButtonClickListener) mMwmActivity).onPlacePageButtonClick(bsItem.getType()) + () -> onPlacePageButtonClick(bsItem.getType()) )); } return items; } - public void openFor(@NonNull PlacePageData data) + private void open() { - mDeactivateMapSelection = true; - MapObject mapObject = (MapObject) data; - final MapObject previousMapObject = mViewModel.getMapObject().getValue(); // Only collapse the place page if the data is different from the one already available - mShouldCollapse = PlacePageUtils.isHiddenState(mPlacePageBehavior.getState()) || !MapObject.same(previousMapObject, mapObject); - mViewModel.setMapObject(mapObject); + mShouldCollapse = PlacePageUtils.isHiddenState(mPlacePageBehavior.getState()) || !MapObject.same(mPreviousMapObject, mMapObject); + mPreviousMapObject = mMapObject; + // Place page will automatically open when the bottom sheet content is loaded so we can compute the peek height + } + + private void close() + { + mPlacePageBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); } private void resetPlacePageHeightBounds() @@ -299,7 +278,7 @@ public class PlacePageController implements Initializable, mPlacePageBehavior.setPeekHeight(value); // The place page is not firing the slide callbacks when using this animation, so we must call them manually mDistanceToTop = parentHeight - value - bottomInsets; - mSlideListener.onPlacePageSlide(mDistanceToTop); + mViewModel.setPlacePageDistanceToTop(mDistanceToTop); if (value == peekHeight) { PlacePageUtils.moveViewportUp(mPlacePage, mViewportMinHeight); @@ -317,37 +296,12 @@ public class PlacePageController implements Initializable, return mPreviewHeight + mButtonsHeight; } - public void close(boolean deactivateMapSelection) - { - mDeactivateMapSelection = deactivateMapSelection; - mPlacePageBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); - } - - public boolean isClosed() - { - return PlacePageUtils.isHiddenState(mPlacePageBehavior.getState()); - } - - @Override - public void onSave(@NonNull Bundle outState) - { - // no op - } - - @Override - public void onRestore(@NonNull Bundle inState) - { - if (mPlacePageBehavior.getState() == BottomSheetBehavior.STATE_HIDDEN) - return; - if (!Framework.nativeHasPlacePageInfo()) - close(false); - } - @Override public void onPlacePageContentChanged(int previewHeight, int frameHeight) { mPreviewHeight = previewHeight; mFrameHeight = frameHeight; + mViewModel.setPlacePageWidth(mPlacePage.getWidth()); // Make sure to update the peek height on the UI thread to prevent weird animation jumps mPlacePage.post(() -> { setPeekHeight(); @@ -357,12 +311,6 @@ public class PlacePageController implements Initializable, }); } - @Override - public void onPlacePageRequestClose() - { - close(true); - } - @Override public void onPlacePageRequestToggleState() { @@ -375,24 +323,150 @@ public class PlacePageController implements Initializable, mPlacePageBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); } - @Override - public void onPlacePageRequestToggleRouteSettings(@NonNull RoadType roadType) - { - // no op - } - @Override public void onPlacePageButtonClick(PlacePageButtons.ButtonType item) { - @Nullable final PlacePageView placePageFragment = (PlacePageView) mMwmActivity.getSupportFragmentManager() - .findFragmentByTag(PLACE_PAGE_FRAGMENT_TAG); - if (placePageFragment != null) - placePageFragment.onPlacePageButtonClick(item); + switch (item) + { + case BOOKMARK_SAVE: + case BOOKMARK_DELETE: + onBookmarkBtnClicked(); + break; + + case SHARE: + onShareBtnClicked(); + break; + + case BACK: + onBackBtnClicked(); + break; + + case ROUTE_FROM: + onRouteFromBtnClicked(); + break; + + case ROUTE_TO: + onRouteToBtnClicked(); + break; + + case ROUTE_ADD: + onRouteAddBtnClicked(); + break; + + case ROUTE_REMOVE: + onRouteRemoveBtnClicked(); + break; + + case ROUTE_AVOID_TOLL: + onAvoidTollBtnClicked(); + break; + + case ROUTE_AVOID_UNPAVED: + onAvoidUnpavedBtnClicked(); + break; + + case ROUTE_AVOID_FERRY: + onAvoidFerryBtnClicked(); + break; + } + } + + private void onBookmarkBtnClicked() + { + // No need to call setMapObject here as the native methods will reopen the place page + if (MapObject.isOfType(MapObject.BOOKMARK, mMapObject)) + Framework.nativeDeleteBookmarkFromMapObject(); + else + BookmarkManager.INSTANCE.addNewBookmark(mMapObject.getLat(), mMapObject.getLon()); + } + + private void onShareBtnClicked() + { + if (mMapObject != null) + SharingUtils.shareMapObject(requireContext(), mMapObject); + } + + private void onBackBtnClicked() + { + if (mMapObject == null) + return; + final ParsedMwmRequest request = ParsedMwmRequest.getCurrentRequest(); + if (request != null && request.isPickPointMode()) + { + final Intent result = new Intent(); + result.putExtra(Const.EXTRA_POINT_LAT, mMapObject.getLat()) + .putExtra(Const.EXTRA_POINT_LON, mMapObject.getLon()) + .putExtra(Const.EXTRA_POINT_NAME, mMapObject.getTitle()) + .putExtra(Const.EXTRA_POINT_ID, mMapObject.getApiId()) + .putExtra(Const.EXTRA_ZOOM_LEVEL, Framework.nativeGetDrawScale()); + requireActivity().setResult(Activity.RESULT_OK, result); + ParsedMwmRequest.setCurrentRequest(null); + } + requireActivity().finish(); + } + + private void onRouteFromBtnClicked() + { + if (mMapObject == null) + return; + RoutingController controller = RoutingController.get(); + if (!controller.isPlanning()) + { + controller.prepare(mMapObject, null); + close(); + } + else if (controller.setStartPoint(mMapObject)) + close(); + } + + private void onRouteToBtnClicked() + { + if (mMapObject == null) + return; + if (RoutingController.get().isPlanning()) + { + RoutingController.get().setEndPoint(mMapObject); + close(); + } + else + ((MwmActivity) requireActivity()).startLocationToPoint(mMapObject); + } + + private void onRouteAddBtnClicked() + { + if (mMapObject != null) + RoutingController.get().addStop(mMapObject); + } + + private void onRouteRemoveBtnClicked() + { + if (mMapObject != null) + RoutingController.get().removeStop(mMapObject); + } + + private void onAvoidUnpavedBtnClicked() + { + onAvoidBtnClicked(RoadType.Dirty); + } + + private void onAvoidFerryBtnClicked() + { + onAvoidBtnClicked(RoadType.Ferry); + } + + private void onAvoidTollBtnClicked() + { + onAvoidBtnClicked(RoadType.Toll); + } + + private void onAvoidBtnClicked(@NonNull RoadType roadType) + { + mPlacePageRouteSettingsListener.onPlacePageRequestToggleRouteSettings(roadType); } private void removePlacePageFragments() { - final FragmentManager fm = mMwmActivity.getSupportFragmentManager(); + final FragmentManager fm = getChildFragmentManager(); final Fragment placePageButtonsFragment = fm.findFragmentByTag(PLACE_PAGE_BUTTONS_FRAGMENT_TAG); final Fragment placePageFragment = fm.findFragmentByTag(PLACE_PAGE_FRAGMENT_TAG); @@ -401,23 +475,21 @@ public class PlacePageController implements Initializable, fm.beginTransaction() .setReorderingAllowed(true) .remove(placePageButtonsFragment) - .commitNow(); + .commit(); } if (placePageFragment != null) { - // Make sure to synchronously remove the fragment so setting the map object to null - // won't impact the fragment fm.beginTransaction() .setReorderingAllowed(true) .remove(placePageFragment) - .commitNow(); + .commit(); } mViewModel.setMapObject(null); } private void createPlacePageFragments() { - final FragmentManager fm = mMwmActivity.getSupportFragmentManager(); + final FragmentManager fm = getChildFragmentManager(); if (fm.findFragmentByTag(PLACE_PAGE_FRAGMENT_TAG) == null) { fm.beginTransaction() @@ -486,29 +558,42 @@ public class PlacePageController implements Initializable, mMapObject = mapObject; if (mapObject != null) { + open(); createPlacePageFragments(); updateButtons( mapObject, MapObject.isOfType(MapObject.API_POINT, mMapObject), !MapObject.isOfType(MapObject.MY_POSITION, mMapObject)); - } + } else + close(); } @Override - public void initialize(@Nullable Activity activity) + public void onStart() { - Objects.requireNonNull(activity); - mViewModel.getMapObject().observe((MwmActivity) activity, this); + super.onStart(); + mPlacePageBehavior.addBottomSheetCallback(mDefaultBottomSheetCallback); + mViewModel.getMapObject().observe(requireActivity(), this); } @Override - public void destroy() + public void onResume() { + super.onResume(); + if (mPlacePageBehavior.getState() != BottomSheetBehavior.STATE_HIDDEN && !Framework.nativeHasPlacePageInfo()) + mViewModel.setMapObject(null); + } + + @Override + public void onStop() + { + super.onStop(); + mPlacePageBehavior.removeBottomSheetCallback(mDefaultBottomSheetCallback); mViewModel.getMapObject().removeObserver(this); } - public interface SlideListener + public interface PlacePageRouteSettingsListener { - void onPlacePageSlide(int top); + void onPlacePageRequestToggleRouteSettings(@NonNull RoadType roadType); } } diff --git a/android/src/app/organicmaps/widget/placepage/PlacePageView.java b/android/src/app/organicmaps/widget/placepage/PlacePageView.java index 2392c98c93..21ee2ebb45 100644 --- a/android/src/app/organicmaps/widget/placepage/PlacePageView.java +++ b/android/src/app/organicmaps/widget/placepage/PlacePageView.java @@ -1,8 +1,6 @@ package app.organicmaps.widget.placepage; -import android.app.Activity; import android.content.Context; -import android.content.Intent; import android.location.Location; import android.os.Bundle; import android.text.SpannableStringBuilder; @@ -29,9 +27,6 @@ import app.organicmaps.Framework; import app.organicmaps.MwmActivity; import app.organicmaps.MwmApplication; import app.organicmaps.R; -import app.organicmaps.api.Const; -import app.organicmaps.api.ParsedMwmRequest; -import app.organicmaps.bookmarks.data.BookmarkManager; import app.organicmaps.bookmarks.data.DistanceAndAzimut; import app.organicmaps.bookmarks.data.MapObject; import app.organicmaps.bookmarks.data.Metadata; @@ -42,8 +37,6 @@ import app.organicmaps.editor.Editor; import app.organicmaps.location.LocationHelper; import app.organicmaps.location.LocationListener; import app.organicmaps.routing.RoutingController; -import app.organicmaps.settings.RoadType; -import app.organicmaps.util.SharingUtils; import app.organicmaps.util.StringUtils; import app.organicmaps.util.UiUtils; import app.organicmaps.util.concurrency.UiThread; @@ -63,7 +56,6 @@ import static android.view.View.VISIBLE; public class PlacePageView extends Fragment implements View.OnClickListener, View.OnLongClickListener, - PlacePageButtons.PlacePageButtonClickListener, LocationListener, Observer @@ -181,6 +173,9 @@ public class PlacePageView extends Fragment implements View.OnClickListener, MwmApplication.prefs(requireContext()).getInt( PREF_COORDINATES_FORMAT, CoordinatesFormat.LatLonDecimal.getId())); + Fragment parentFragment = getParentFragment(); + mPlacePageViewListener = (PlacePageViewListener) parentFragment; + mFrame = view; mFrame.setOnClickListener((v) -> mPlacePageViewListener.onPlacePageRequestToggleState()); @@ -243,186 +238,30 @@ public class PlacePageView extends Fragment implements View.OnClickListener, mDownloaderIcon = new DownloaderStatusIcon(mPreview.findViewById(R.id.downloader_status_frame)); mDownloaderInfo = mPreview.findViewById(R.id.tv__downloader_details); - - mMapObject = mViewModel.getMapObject().getValue(); } @Override - public void onAttach(@NonNull Context context) + public void onStart() { - super.onAttach(context); - mPlacePageViewListener = (MwmActivity) context; - } - - @Override - public void onResume() - { - super.onResume(); + super.onStart(); mViewModel.getMapObject().observe(requireActivity(), this); LocationHelper.INSTANCE.addListener(this); - } - - @Override - public void onPause() - { - super.onPause(); - // Unsubscribe from events as soon as the fragment becomes inactive - // to prevent unwanted side effects - mViewModel.getMapObject().removeObserver(this); - LocationHelper.INSTANCE.removeListener(this); + setCurrentCountry(); } @Override public void onStop() { super.onStop(); - // Safely detach the country once the fragment is hidden from the user - // It is safer to call this here than in onPause as the app could go from onPause to - // onResume without killing the fragment. - // In this case we would not want to detach the country. + mViewModel.getMapObject().removeObserver(this); + LocationHelper.INSTANCE.removeListener(this); detachCountry(); } - @Override - public void onPlacePageButtonClick(PlacePageButtons.ButtonType item) - { - switch (item) - { - case BOOKMARK_SAVE: - case BOOKMARK_DELETE: - onBookmarkBtnClicked(); - break; - - case SHARE: - onShareBtnClicked(); - break; - - case BACK: - onBackBtnClicked(); - break; - - case ROUTE_FROM: - onRouteFromBtnClicked(); - break; - - case ROUTE_TO: - onRouteToBtnClicked(); - break; - - case ROUTE_ADD: - onRouteAddBtnClicked(); - break; - - case ROUTE_REMOVE: - onRouteRemoveBtnClicked(); - break; - - case ROUTE_AVOID_TOLL: - onAvoidTollBtnClicked(); - break; - - case ROUTE_AVOID_UNPAVED: - onAvoidUnpavedBtnClicked(); - break; - - case ROUTE_AVOID_FERRY: - onAvoidFerryBtnClicked(); - break; - } - } - - private void onBookmarkBtnClicked() - { - // No need to call setMapObject here as the native methods will reopen the place page - if (MapObject.isOfType(MapObject.BOOKMARK, mMapObject)) - Framework.nativeDeleteBookmarkFromMapObject(); - else - BookmarkManager.INSTANCE.addNewBookmark(mMapObject.getLat(), mMapObject.getLon()); - } - - private void onShareBtnClicked() - { - SharingUtils.shareMapObject(requireContext(), mMapObject); - } - - private void onBackBtnClicked() - { - final ParsedMwmRequest request = ParsedMwmRequest.getCurrentRequest(); - if (request != null && request.isPickPointMode()) - { - final Intent result = new Intent(); - result.putExtra(Const.EXTRA_POINT_LAT, mMapObject.getLat()) - .putExtra(Const.EXTRA_POINT_LON, mMapObject.getLon()) - .putExtra(Const.EXTRA_POINT_NAME, mMapObject.getTitle()) - .putExtra(Const.EXTRA_POINT_ID, mMapObject.getApiId()) - .putExtra(Const.EXTRA_ZOOM_LEVEL, Framework.nativeGetDrawScale()); - requireActivity().setResult(Activity.RESULT_OK, result); - ParsedMwmRequest.setCurrentRequest(null); - } - requireActivity().finish(); - } - - private void onRouteFromBtnClicked() - { - RoutingController controller = RoutingController.get(); - if (!controller.isPlanning()) - { - controller.prepare(mMapObject, null); - mPlacePageViewListener.onPlacePageRequestClose(); - } - else if (controller.setStartPoint(mMapObject)) - { - mPlacePageViewListener.onPlacePageRequestClose(); - } - } - - private void onRouteToBtnClicked() - { - if (RoutingController.get().isPlanning()) - { - RoutingController.get().setEndPoint(mMapObject); - mPlacePageViewListener.onPlacePageRequestClose(); - } - else - { - ((MwmActivity) requireActivity()).startLocationToPoint(mMapObject); - } - } - - private void onRouteAddBtnClicked() - { - RoutingController.get().addStop(mMapObject); - } - - private void onRouteRemoveBtnClicked() - { - RoutingController.get().removeStop(mMapObject); - } - - private void onAvoidUnpavedBtnClicked() - { - onAvoidBtnClicked(RoadType.Dirty); - } - - private void onAvoidFerryBtnClicked() - { - onAvoidBtnClicked(RoadType.Ferry); - } - - private void onAvoidTollBtnClicked() - { - onAvoidBtnClicked(RoadType.Toll); - } - - private void onAvoidBtnClicked(@NonNull RoadType roadType) - { - mPlacePageViewListener.onPlacePageRequestToggleRouteSettings(roadType); - } - private void setCurrentCountry() { - if (mCurrentCountry != null) - throw new AssertionError("country should be detached before!"); + if (mCurrentCountry != null || mMapObject == null) + return; String country = MapManager.nativeGetSelectedCountry(); if (country != null && !RoutingController.get().isNavigating()) attachCountry(country); @@ -455,7 +294,7 @@ public class PlacePageView extends Fragment implements View.OnClickListener, fm.beginTransaction() .setReorderingAllowed(true) .remove(fragment) - .commitNow(); + .commit(); } } @@ -504,7 +343,7 @@ public class PlacePageView extends Fragment implements View.OnClickListener, int end = text.lastIndexOf("★") + 1; if (start > -1) { - sb.setSpan(new ForegroundColorSpan(ContextCompat.getColor(getContext(), R.color.base_yellow)), + sb.setSpan(new ForegroundColorSpan(ContextCompat.getColor(requireContext(), R.color.base_yellow)), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); } mTvSubtitle.setText(sb); @@ -758,8 +597,10 @@ public class PlacePageView extends Fragment implements View.OnClickListener, } @Override - public void onChanged(MapObject mapObject) + public void onChanged(@Nullable MapObject mapObject) { + if (mapObject == null) + return; if (!mapObject.sameAs(mMapObject)) { detachCountry(); @@ -778,6 +619,8 @@ public class PlacePageView extends Fragment implements View.OnClickListener, @Override public void onLocationUpdated(@NonNull Location location) { + if (mMapObject == null) + return; if (MapObject.isOfType(MapObject.MY_POSITION, mMapObject)) refreshMyPosition(location); else @@ -787,7 +630,7 @@ public class PlacePageView extends Fragment implements View.OnClickListener, @Override public void onCompassUpdated(double north) { - if (MapObject.isOfType(MapObject.MY_POSITION, mMapObject)) + if (mMapObject == null || MapObject.isOfType(MapObject.MY_POSITION, mMapObject)) return; final Location location = LocationHelper.INSTANCE.getSavedLocation(); @@ -815,10 +658,6 @@ public class PlacePageView extends Fragment implements View.OnClickListener, // Called when the content has actually changed and we are ready to compute the peek height void onPlacePageContentChanged(int previewHeight, int frameHeight); - void onPlacePageRequestClose(); - void onPlacePageRequestToggleState(); - - void onPlacePageRequestToggleRouteSettings(@NonNull RoadType roadType); } } diff --git a/android/src/app/organicmaps/widget/placepage/PlacePageViewModel.java b/android/src/app/organicmaps/widget/placepage/PlacePageViewModel.java index be0241737d..fa8ed2f3d6 100644 --- a/android/src/app/organicmaps/widget/placepage/PlacePageViewModel.java +++ b/android/src/app/organicmaps/widget/placepage/PlacePageViewModel.java @@ -11,6 +11,8 @@ public class PlacePageViewModel extends ViewModel { private final MutableLiveData> mCurrentButtons = new MutableLiveData<>(); private final MutableLiveData mMapObject = new MutableLiveData<>(); + private final MutableLiveData mPlacePageWidth = new MutableLiveData<>(); + private final MutableLiveData mPlacePageDistanceToTop = new MutableLiveData<>(); public LiveData> getCurrentButtons() { @@ -31,4 +33,24 @@ public class PlacePageViewModel extends ViewModel { mMapObject.setValue(mapObject); } + + public MutableLiveData getPlacePageWidth() + { + return mPlacePageWidth; + } + + public void setPlacePageWidth(int width) + { + mPlacePageWidth.setValue(width); + } + + public MutableLiveData getPlacePageDistanceToTop() + { + return mPlacePageDistanceToTop; + } + + public void setPlacePageDistanceToTop(int top) + { + mPlacePageDistanceToTop.setValue(top); + } } diff --git a/android/src/app/organicmaps/widget/placepage/sections/PlacePageBookmarkFragment.java b/android/src/app/organicmaps/widget/placepage/sections/PlacePageBookmarkFragment.java index b400548424..db15d99893 100644 --- a/android/src/app/organicmaps/widget/placepage/sections/PlacePageBookmarkFragment.java +++ b/android/src/app/organicmaps/widget/placepage/sections/PlacePageBookmarkFragment.java @@ -75,16 +75,16 @@ public class PlacePageBookmarkFragment extends Fragment implements View.OnClickL } @Override - public void onResume() + public void onStart() { - super.onResume(); + super.onStart(); mViewModel.getMapObject().observe(requireActivity(), this); } @Override - public void onPause() + public void onStop() { - super.onPause(); + super.onStop(); mViewModel.getMapObject().removeObserver(this); } @@ -143,12 +143,12 @@ public class PlacePageBookmarkFragment extends Fragment implements View.OnClickL } @Override - public void onChanged(MapObject mapObject) + public void onChanged(@Nullable MapObject mapObject) { // MapObject could be something else than a bookmark if the user already has the place page // opened and clicks on a non-bookmarked POI. // This callback would be called before the fragment had time to be destroyed - if (mapObject.getMapObjectType() == MapObject.BOOKMARK) + if (mapObject != null && mapObject.getMapObjectType() == MapObject.BOOKMARK) { currentBookmark = (Bookmark) mapObject; updateBookmarkDetails(); diff --git a/android/src/app/organicmaps/widget/placepage/sections/PlacePageLinksFragment.java b/android/src/app/organicmaps/widget/placepage/sections/PlacePageLinksFragment.java index c7d1dee048..f552832bd6 100644 --- a/android/src/app/organicmaps/widget/placepage/sections/PlacePageLinksFragment.java +++ b/android/src/app/organicmaps/widget/placepage/sections/PlacePageLinksFragment.java @@ -218,23 +218,26 @@ public class PlacePageLinksFragment extends Fragment implements Observer { PlacePageUtils.copyToClipboard(requireContext(), mFrame, TimeFormatUtils.formatTimetables(getResources(), ohStr, timetables)); @@ -173,7 +170,7 @@ public class PlacePageOpeningHoursFragment extends Fragment implements Observer< // Show that place is closed today. if (!containsCurrentWeekday) { - refreshTodayOpeningHours(resources.getString(R.string.day_off_today), ContextCompat.getColor(getContext(), R.color.base_red)); + refreshTodayOpeningHours(resources.getString(R.string.day_off_today), ContextCompat.getColor(requireContext(), R.color.base_red)); UiUtils.hide(mTodayNonBusinessTime); } } @@ -181,22 +178,23 @@ public class PlacePageOpeningHoursFragment extends Fragment implements Observer< } @Override - public void onResume() + public void onStart() { - super.onResume(); + super.onStart(); mViewModel.getMapObject().observe(requireActivity(), this); } @Override - public void onPause() + public void onStop() { - super.onPause(); + super.onStop(); mViewModel.getMapObject().removeObserver(this); } @Override - public void onChanged(MapObject mapObject) + public void onChanged(@Nullable MapObject mapObject) { - refreshOpeningHours(); + if (mapObject != null) + refreshOpeningHours(mapObject); } } diff --git a/android/src/app/organicmaps/widget/placepage/sections/PlacePagePhoneFragment.java b/android/src/app/organicmaps/widget/placepage/sections/PlacePagePhoneFragment.java index 52b432d1c1..fe4dacc56e 100644 --- a/android/src/app/organicmaps/widget/placepage/sections/PlacePagePhoneFragment.java +++ b/android/src/app/organicmaps/widget/placepage/sections/PlacePagePhoneFragment.java @@ -40,22 +40,23 @@ public class PlacePagePhoneFragment extends Fragment implements Observer