[android] improve place page fragment abstraction

Signed-off-by: Arnaud Vergnet <arnaud.vergnet@mailo.com>
This commit is contained in:
Arnaud Vergnet 2023-03-16 18:12:13 +01:00 committed by Alexander Borsuk
parent c48be8c3f5
commit f5c2df2694
13 changed files with 373 additions and 446 deletions

View file

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinator"
android:layout_width="match_parent"
@ -51,26 +50,9 @@
android:paddingBottom="@dimen/margin_base"
android:visibility="invisible" />
</RelativeLayout>
<androidx.core.widget.NestedScrollViewClickFixed
android:id="@+id/placepage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="?attr/bottomSheetStyle"
android:fillViewport="true"
app:layout_behavior="@string/placepage_behavior" >
<RelativeLayout
android:id="@+id/placepage_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?panel" >
<androidx.fragment.app.FragmentContainerView
android:id="@+id/placepage_fragment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
</androidx.core.widget.NestedScrollViewClickFixed>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/pp_buttons_fragment"
android:id="@+id/place_page_container_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent"
android:name="app.organicmaps.widget.placepage.PlacePageController" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.core.widget.NestedScrollViewClickFixed
android:id="@+id/placepage"
style="?attr/bottomSheetStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true"
app:layout_behavior="@string/placepage_behavior">
<RelativeLayout
android:id="@+id/placepage_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?panel">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/placepage_fragment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
</androidx.core.widget.NestedScrollViewClickFixed>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/pp_buttons_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -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)
{

View file

@ -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();
}

View file

@ -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<List<Pl
return windowInsets;
});
mMaxButtons = getResources().getInteger(R.integer.pp_buttons_max);
mViewModel.getCurrentButtons().observe(requireActivity(), this);
Fragment parentFragment = getParentFragment();
mItemListener = (PlacePageButtonClickListener) parentFragment;
createButtons(mViewModel.getCurrentButtons().getValue());
}
@Override
public void onAttach(@NonNull Context context)
public void onStart()
{
super.onAttach(context);
try
{
mItemListener = (PlacePageButtonClickListener) context;
}
catch (ClassCastException e)
{
throw new ClassCastException(context + " must implement PlacePageButtonClickListener");
}
super.onStart();
mViewModel.getCurrentButtons().observe(requireActivity(), this);
}
@Override
public void onDestroyView()
public void onStop()
{
super.onDestroyView();
super.onStop();
mViewModel.getCurrentButtons().removeObserver(this);
}
@ -94,7 +89,7 @@ public final class PlacePageButtons extends Fragment implements Observer<List<Pl
private void showMoreBottomSheet()
{
MenuBottomSheetFragment.newInstance(PLACEPAGE_MORE_MENU_ID)
.show(requireActivity().getSupportFragmentManager(), PLACEPAGE_MORE_MENU_ID);
.show(getParentFragmentManager(), PLACEPAGE_MORE_MENU_ID);
}
private void createButtons(@Nullable List<ButtonType> buttons)

View file

@ -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<Activity>,
Savable<Bundle>,
PlacePageView.PlacePageViewListener,
PlacePageButtons.PlacePageButtonClickListener,
MenuBottomSheetFragment.MenuBottomSheetInterface,
Observer<MapObject>
public class PlacePageController extends Fragment implements
PlacePageView.PlacePageViewListener,
PlacePageButtons.PlacePageButtonClickListener,
MenuBottomSheetFragment.MenuBottomSheetInterface,
Observer<MapObject>
{
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<View> 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<View> 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<Activity>,
@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<Activity>,
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<MenuBottomSheetItem> getMenuBottomSheetItems(String id)
{
@ -203,24 +179,27 @@ public class PlacePageController implements Initializable<Activity>,
ArrayList<MenuBottomSheetItem> 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<Activity>,
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<Activity>,
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<Activity>,
});
}
@Override
public void onPlacePageRequestClose()
{
close(true);
}
@Override
public void onPlacePageRequestToggleState()
{
@ -375,24 +323,150 @@ public class PlacePageController implements Initializable<Activity>,
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<Activity>,
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<Activity>,
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);
}
}

View file

@ -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<MapObject>
@ -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);
}
}

View file

@ -11,6 +11,8 @@ public class PlacePageViewModel extends ViewModel
{
private final MutableLiveData<List<PlacePageButtons.ButtonType>> mCurrentButtons = new MutableLiveData<>();
private final MutableLiveData<MapObject> mMapObject = new MutableLiveData<>();
private final MutableLiveData<Integer> mPlacePageWidth = new MutableLiveData<>();
private final MutableLiveData<Integer> mPlacePageDistanceToTop = new MutableLiveData<>();
public LiveData<List<PlacePageButtons.ButtonType>> getCurrentButtons()
{
@ -31,4 +33,24 @@ public class PlacePageViewModel extends ViewModel
{
mMapObject.setValue(mapObject);
}
public MutableLiveData<Integer> getPlacePageWidth()
{
return mPlacePageWidth;
}
public void setPlacePageWidth(int width)
{
mPlacePageWidth.setValue(width);
}
public MutableLiveData<Integer> getPlacePageDistanceToTop()
{
return mPlacePageDistanceToTop;
}
public void setPlacePageDistanceToTop(int top)
{
mPlacePageDistanceToTop.setValue(top);
}
}

View file

@ -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();

View file

@ -218,23 +218,26 @@ public class PlacePageLinksFragment extends Fragment implements Observer<MapObje
}
@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)
{
mMapObject = mapObject;
refreshLinks();
if (mapObject != null)
{
mMapObject = mapObject;
refreshLinks();
}
}
}

View file

@ -27,7 +27,6 @@ import app.organicmaps.util.UiUtils;
import app.organicmaps.util.Utils;
import app.organicmaps.widget.placepage.PlacePageUtils;
import app.organicmaps.widget.placepage.PlacePageViewModel;
import app.organicmaps.widget.placepage.sections.PlaceOpeningHoursAdapter;
import java.util.Calendar;
import java.util.Locale;
@ -91,11 +90,9 @@ public class PlacePageOpeningHoursFragment extends Fragment implements Observer<
mTodayOpenTime.setTextColor(color);
}
private void refreshOpeningHours()
private void refreshOpeningHours(MapObject mapObject)
{
final String ohStr = mViewModel.getMapObject()
.getValue()
.getMetadata(Metadata.MetadataType.FMD_OPEN_HOURS);
final String ohStr = mapObject.getMetadata(Metadata.MetadataType.FMD_OPEN_HOURS);
final Timetable[] timetables = OpeningHours.nativeTimetablesFromString(ohStr);
mFrame.setOnLongClickListener((v) -> {
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);
}
}

View file

@ -40,22 +40,23 @@ public class PlacePagePhoneFragment extends Fragment implements Observer<MapObje
}
@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)
{
mPhoneAdapter.refreshPhones(mapObject.getMetadata(Metadata.MetadataType.FMD_PHONE_NUMBER));
if (mapObject != null)
mPhoneAdapter.refreshPhones(mapObject.getMetadata(Metadata.MetadataType.FMD_PHONE_NUMBER));
}
}

View file

@ -112,23 +112,26 @@ public class PlacePageWikipediaFragment extends Fragment implements Observer<Map
}
@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)
{
mMapObject = mapObject;
updateViews();
if (mapObject != null)
{
mMapObject = mapObject;
updateViews();
}
}
}