forked from organicmaps/organicmaps
[android] Implement main menu bottom sheet controller
This commit is contained in:
parent
870cdd7a63
commit
e90471c167
13 changed files with 347 additions and 23 deletions
24
android/res/layout-land/main_menu_bottom_sheet.xml
Normal file
24
android/res/layout-land/main_menu_bottom_sheet.xml
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.core.widget.NestedScrollView
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/main_menu_sheet"
|
||||
android:layout_width="@dimen/main_menu_bottom_sheet_width"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?cardBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:fillViewport="true"
|
||||
app:behavior_defaultState="hidden"
|
||||
app:behavior_skipAnchored="true"
|
||||
app:behavior_skipCollapsed="true"
|
||||
app:behavior_hideable="true"
|
||||
app:behavior_peekHeight="0dp"
|
||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<include layout="@layout/menu_content" />
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
|
@ -97,6 +97,7 @@
|
|||
<include layout="@layout/divider_horizontal"/>
|
||||
</FrameLayout>
|
||||
<include layout="@layout/elevation_profile_bottom_sheet" />
|
||||
<include layout="@layout/main_menu_bottom_sheet" />
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/app_bar"
|
||||
app:layout_behavior="@string/placepage_toolbar_behavior"
|
||||
|
|
24
android/res/layout/main_menu_bottom_sheet.xml
Normal file
24
android/res/layout/main_menu_bottom_sheet.xml
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.core.widget.NestedScrollView
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/main_menu_sheet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?cardBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:fillViewport="true"
|
||||
app:behavior_defaultState="hidden"
|
||||
app:behavior_hideable="true"
|
||||
app:behavior_skipAnchored="true"
|
||||
app:behavior_skipCollapsed="true"
|
||||
app:behavior_peekHeight="0dp"
|
||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<include layout="@layout/menu_content" />
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
|
@ -111,6 +111,7 @@
|
|||
<dimen name="menu_line_button_inset_top">-20dp</dimen>
|
||||
<dimen name="menu_line_button_inset_edge">-16dp</dimen>
|
||||
<dimen name="menu_list_item_height">48dp</dimen>
|
||||
<dimen name="main_menu_bottom_sheet_width">375dp</dimen>
|
||||
|
||||
<!-- Nav menu -->
|
||||
<dimen name="nav_elevation">6dp</dimen>
|
||||
|
|
|
@ -124,6 +124,8 @@ import com.mapswithme.maps.tips.TutorialAction;
|
|||
import com.mapswithme.maps.widget.FadeView;
|
||||
import com.mapswithme.maps.widget.menu.BaseMenu;
|
||||
import com.mapswithme.maps.widget.menu.MainMenu;
|
||||
import com.mapswithme.maps.widget.menu.MenuController;
|
||||
import com.mapswithme.maps.widget.menu.MenuStateObserver;
|
||||
import com.mapswithme.maps.widget.menu.MyPositionButton;
|
||||
import com.mapswithme.maps.widget.placepage.PlacePageController;
|
||||
import com.mapswithme.maps.widget.placepage.PlacePageData;
|
||||
|
@ -271,6 +273,9 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
@SuppressWarnings("NullableProblems")
|
||||
@NonNull
|
||||
private PlacePageController mPlacePageController;
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@NonNull
|
||||
private MenuController mMainMenuController;
|
||||
@Nullable
|
||||
private Tutorial mTutorial;
|
||||
@Nullable
|
||||
|
@ -528,6 +533,10 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
mPlacePageController.initialize(this);
|
||||
mPlacePageController.onActivityCreated(this, savedInstanceState);
|
||||
|
||||
// TODO: set state observer.
|
||||
mMainMenuController = com.mapswithme.maps.widget.menu.Factory.createMainMenuController(new MainMenuStateObserver());
|
||||
mMainMenuController.initialize(findViewById(R.id.coordinator));
|
||||
|
||||
boolean isLaunchByDeepLink = getIntent().getBooleanExtra(EXTRA_LAUNCH_BY_DEEP_LINK, false);
|
||||
initViews(isLaunchByDeepLink);
|
||||
|
||||
|
@ -826,8 +835,11 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
public void closeMenu(@Nullable Runnable procAfterClose)
|
||||
{
|
||||
mFadeView.fadeOut();
|
||||
mMainMenu.close(true, procAfterClose);
|
||||
mMainMenuController.close();
|
||||
if (procAfterClose != null)
|
||||
procAfterClose.run();
|
||||
}
|
||||
|
||||
private boolean closePositionChooser()
|
||||
{
|
||||
if (UiUtils.isVisible(mPositionChooser))
|
||||
|
@ -1355,18 +1367,12 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
{
|
||||
super.onResume();
|
||||
mSearchController.refreshToolbar();
|
||||
mMainMenu.onResume(new Runnable()
|
||||
mMainMenu.onResume(null);
|
||||
if (Framework.nativeIsInChoosePositionMode())
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (Framework.nativeIsInChoosePositionMode())
|
||||
{
|
||||
UiUtils.show(mPositionChooser);
|
||||
setFullscreen(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
UiUtils.show(mPositionChooser);
|
||||
setFullscreen(true);
|
||||
}
|
||||
if (mOnmapDownloader != null)
|
||||
mOnmapDownloader.onResume();
|
||||
|
||||
|
@ -1460,6 +1466,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
mToggleMapLayerController.detachCore();
|
||||
TrafficManager.INSTANCE.detachAll();
|
||||
mPlacePageController.destroy();
|
||||
mMainMenuController.destroy();
|
||||
SearchEngine.INSTANCE.removeListener(this);
|
||||
}
|
||||
|
||||
|
@ -1756,11 +1763,6 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
return this;
|
||||
}
|
||||
|
||||
public MainMenu getMainMenu()
|
||||
{
|
||||
return mMainMenu;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showSearch()
|
||||
{
|
||||
|
@ -1782,12 +1784,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
return;
|
||||
}
|
||||
|
||||
if (mIsTabletLayout)
|
||||
{
|
||||
mMainMenu.setEnabled(MainMenu.Item.POINT_TO_POINT, !RoutingController.get().isPlanning());
|
||||
mMainMenu.setEnabled(MainMenu.Item.SEARCH, !RoutingController.get().isWaitingPoiPick());
|
||||
}
|
||||
else if (RoutingController.get().isPlanning())
|
||||
if (RoutingController.get().isPlanning())
|
||||
{
|
||||
mMainMenu.setState(MainMenu.State.ROUTE_PREPARE, false, mIsFullscreen);
|
||||
return;
|
||||
|
@ -2666,6 +2663,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
return;
|
||||
|
||||
Toast.makeText(getActivity(), "Open bottom sheet menu!", Toast.LENGTH_SHORT).show();
|
||||
getActivity().mMainMenuController.open();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2857,4 +2855,20 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
: UiUtils.getStatusBarHeight(getApplicationContext()));
|
||||
}
|
||||
}
|
||||
|
||||
private class MainMenuStateObserver implements MenuStateObserver
|
||||
{
|
||||
|
||||
@Override
|
||||
public void onMenuOpen()
|
||||
{
|
||||
mFadeView.fadeIn();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMenuClosed()
|
||||
{
|
||||
mFadeView.fadeOut();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
package com.mapswithme.maps.widget.menu;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
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 com.mapswithme.util.log.Logger;
|
||||
import com.mapswithme.util.log.LoggerFactory;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class BottomSheetMenuController implements MenuController
|
||||
{
|
||||
private static final Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.MISC);
|
||||
private static final String TAG = BottomSheetMenuController.class.getSimpleName();
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@NonNull
|
||||
private BottomSheetBehavior<View> mSheetBehavior;
|
||||
@IdRes
|
||||
private final int mSheetResId;
|
||||
@NonNull
|
||||
private final MenuRenderer mMenuRenderer;
|
||||
@Nullable
|
||||
private MenuStateObserver mStateObserver;
|
||||
private final BottomSheetBehavior.BottomSheetCallback mSheetCallback
|
||||
= new BottomSheetBehavior.BottomSheetCallback()
|
||||
{
|
||||
@Override
|
||||
public void onStateChanged(@NonNull View view, int state)
|
||||
{
|
||||
LOGGER.d(TAG, "State change, new = " + BottomSheetMenuUtils.toString(state));
|
||||
if (BottomSheetMenuUtils.isSettlingState(state) || BottomSheetMenuUtils.isDraggingState(state))
|
||||
return;
|
||||
|
||||
if (mStateObserver == null)
|
||||
return;
|
||||
|
||||
if (BottomSheetMenuUtils.isHiddenState(state))
|
||||
{
|
||||
mStateObserver.onMenuClosed();
|
||||
return;
|
||||
}
|
||||
|
||||
mStateObserver.onMenuOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSlide(@NonNull View view, float v)
|
||||
{
|
||||
// Do nothing by default.
|
||||
}
|
||||
};
|
||||
|
||||
BottomSheetMenuController(int sheetResId, @NonNull MenuRenderer menuRenderer,
|
||||
@Nullable MenuStateObserver stateObserver)
|
||||
{
|
||||
mSheetResId = sheetResId;
|
||||
mMenuRenderer = menuRenderer;
|
||||
mStateObserver = stateObserver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open()
|
||||
{
|
||||
mMenuRenderer.render();
|
||||
mSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
mSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public void initialize(@Nullable View view)
|
||||
{
|
||||
Objects.requireNonNull(view);
|
||||
View sheet = view.findViewById(mSheetResId);
|
||||
Objects.requireNonNull(sheet);
|
||||
mSheetBehavior = BottomSheetBehavior.from(sheet);
|
||||
// FIXME: bottom sheet should be hidden by default without this call.
|
||||
mSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
|
||||
mSheetBehavior.setBottomSheetCallback(mSheetCallback);
|
||||
GestureDetectorCompat gestureDetector = new GestureDetectorCompat(
|
||||
view.getContext(), new BottomSheetMenuGestureListener(mSheetBehavior));
|
||||
sheet.setOnTouchListener((v, event) -> gestureDetector.onTouchEvent(event));
|
||||
mMenuRenderer.initialize(sheet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy()
|
||||
{
|
||||
mMenuRenderer.destroy();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package com.mapswithme.maps.widget.menu;
|
||||
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
import com.trafi.anchorbottomsheetbehavior.AnchorBottomSheetBehavior;
|
||||
|
||||
public class BottomSheetMenuGestureListener extends GestureDetector.SimpleOnGestureListener
|
||||
{
|
||||
@NonNull
|
||||
private final BottomSheetBehavior<View> mBottomSheetBehavior;
|
||||
|
||||
public BottomSheetMenuGestureListener(@NonNull BottomSheetBehavior<View> bottomSheetBehavior)
|
||||
{
|
||||
mBottomSheetBehavior = bottomSheetBehavior;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSingleTapConfirmed(MotionEvent e)
|
||||
{
|
||||
@AnchorBottomSheetBehavior.State
|
||||
int state = mBottomSheetBehavior.getState();
|
||||
if (!BottomSheetMenuUtils.isHiddenState(state) && !BottomSheetMenuUtils.isDraggingState(state)
|
||||
&& !BottomSheetMenuUtils.isSettlingState(state))
|
||||
{
|
||||
mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package com.mapswithme.maps.widget.menu;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
|
||||
class BottomSheetMenuUtils
|
||||
{
|
||||
static boolean isSettlingState(int state)
|
||||
{
|
||||
return state == BottomSheetBehavior.STATE_SETTLING;
|
||||
}
|
||||
|
||||
static boolean isDraggingState(int state)
|
||||
{
|
||||
return state == BottomSheetBehavior.STATE_DRAGGING;
|
||||
}
|
||||
|
||||
static boolean isCollapsedState(int state)
|
||||
{
|
||||
return state == BottomSheetBehavior.STATE_COLLAPSED;
|
||||
}
|
||||
|
||||
static boolean isExpandedState(int state)
|
||||
{
|
||||
return state == BottomSheetBehavior.STATE_EXPANDED;
|
||||
}
|
||||
|
||||
static boolean isHiddenState(int state)
|
||||
{
|
||||
return state == BottomSheetBehavior.STATE_HIDDEN;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
static String toString(int state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case BottomSheetBehavior.STATE_EXPANDED:
|
||||
return "EXPANDED";
|
||||
case BottomSheetBehavior.STATE_COLLAPSED:
|
||||
return "COLLAPSED";
|
||||
case BottomSheetBehavior.STATE_DRAGGING:
|
||||
return "DRAGGING";
|
||||
case BottomSheetBehavior.STATE_SETTLING:
|
||||
return "SETTLING";
|
||||
case BottomSheetBehavior.STATE_HIDDEN:
|
||||
return "HIDDEN";
|
||||
default:
|
||||
throw new AssertionError("Unsupported state detected: " + state);
|
||||
}
|
||||
}
|
||||
}
|
18
android/src/com/mapswithme/maps/widget/menu/Factory.java
Normal file
18
android/src/com/mapswithme/maps/widget/menu/Factory.java
Normal file
|
@ -0,0 +1,18 @@
|
|||
package com.mapswithme.maps.widget.menu;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.mapswithme.maps.R;
|
||||
|
||||
public class Factory
|
||||
{
|
||||
@NonNull
|
||||
public static MenuController createMainMenuController(@Nullable MenuStateObserver stateObserver)
|
||||
{
|
||||
return new BottomSheetMenuController(R.id.main_menu_sheet, new MainMenuRenderer(),
|
||||
stateObserver);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.mapswithme.maps.widget.menu;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class MainMenuRenderer implements MenuRenderer
|
||||
{
|
||||
@Override
|
||||
public void render()
|
||||
{
|
||||
// TODO: Implement.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(@Nullable View view)
|
||||
{
|
||||
// TODO: Implement.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy()
|
||||
{
|
||||
// TODO: Implement.
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package com.mapswithme.maps.widget.menu;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import com.mapswithme.maps.base.Initializable;
|
||||
|
||||
public interface MenuController extends Initializable<View>
|
||||
{
|
||||
void open();
|
||||
void close();
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package com.mapswithme.maps.widget.menu;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import com.mapswithme.maps.base.Initializable;
|
||||
|
||||
public interface MenuRenderer extends Initializable<View>
|
||||
{
|
||||
void render();
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package com.mapswithme.maps.widget.menu;
|
||||
|
||||
public interface MenuStateObserver
|
||||
{
|
||||
void onMenuOpen();
|
||||
void onMenuClosed();
|
||||
}
|
Loading…
Add table
Reference in a new issue