forked from organicmaps/organicmaps
[android] Add UI elements for user route management
For users to interact with this feature the following was added: - Button in the map screen to enter a bottom sheet menu - Fragment for the bottom sheet menu. Contains a button to save the selected route, the button is disabled if there are not enough route points; a recycle view with an item for each user route. - A route item contains a button with the name of the route, used to load the route points; a button to rename the route; and a button to delete the route. Checks are in place to confirm before deleting a route or overwriting an existing one. Notes: - The map screen button has a placeholder icon and a placeholder location when not in navigation mode. - All the text is still hard coded in English and is to be implemented correctly shortly. - It might make sense to move this from a bottom sheet next to the bookmarks, as to keep every user saved element together. Signed-off-by: Fábio Gomes <gabriel.gomes@tecnico.ulisboa.pt>
This commit is contained in:
parent
7ab0c25a13
commit
a1cb87bca6
13 changed files with 671 additions and 24 deletions
|
@ -72,6 +72,7 @@ import app.organicmaps.location.SensorHelper;
|
|||
import app.organicmaps.location.SensorListener;
|
||||
import app.organicmaps.maplayer.MapButtonsController;
|
||||
import app.organicmaps.maplayer.MapButtonsViewModel;
|
||||
import app.organicmaps.maplayer.MyRoutesFragment;
|
||||
import app.organicmaps.maplayer.ToggleMapLayerFragment;
|
||||
import app.organicmaps.maplayer.isolines.IsolinesManager;
|
||||
import app.organicmaps.maplayer.isolines.IsolinesState;
|
||||
|
@ -156,6 +157,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
|
||||
private static final String MAIN_MENU_ID = "MAIN_MENU_BOTTOM_SHEET";
|
||||
private static final String LAYERS_MENU_ID = "LAYERS_MENU_BOTTOM_SHEET";
|
||||
private static final String MYROUTES_MENU_ID = "MYROUTES_MENU_BOTTOM_SHEET";
|
||||
|
||||
@Nullable
|
||||
private MapFragment mMapFragment;
|
||||
|
@ -774,6 +776,11 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
showBottomSheet(MAIN_MENU_ID);
|
||||
}
|
||||
case help -> showHelp();
|
||||
case myRoutes ->
|
||||
{
|
||||
closeFloatingPanels();
|
||||
showBottomSheet(MYROUTES_MENU_ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -892,6 +899,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
{
|
||||
closeBottomSheet(LAYERS_MENU_ID);
|
||||
closeBottomSheet(MAIN_MENU_ID);
|
||||
closeBottomSheet(MYROUTES_MENU_ID);
|
||||
closePlacePage();
|
||||
}
|
||||
|
||||
|
@ -1147,7 +1155,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
public void onBackPressed()
|
||||
{
|
||||
final RoutingController routingController = RoutingController.get();
|
||||
if (!closeBottomSheet(MAIN_MENU_ID) && !closeBottomSheet(LAYERS_MENU_ID) &&
|
||||
if (!closeBottomSheet(MAIN_MENU_ID) && !closeBottomSheet(LAYERS_MENU_ID) && !closeBottomSheet(MYROUTES_MENU_ID) &&
|
||||
!collapseNavMenu() && !closePlacePage() && !closeSearchToolbar(true, true) &&
|
||||
!closeSidePanel() && !closePositionChooser() &&
|
||||
!routingController.resetToPlanningStateIfNavigating() && !routingController.cancel())
|
||||
|
@ -2164,6 +2172,8 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
{
|
||||
if (id.equals(LAYERS_MENU_ID))
|
||||
return new ToggleMapLayerFragment();
|
||||
else if (id.equals(MYROUTES_MENU_ID))
|
||||
return new MyRoutesFragment();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,8 @@ public class MapButtonsController extends Fragment
|
|||
private View mBottomButtonsFrame;
|
||||
@Nullable
|
||||
private FloatingActionButton mToggleMapLayerButton;
|
||||
@Nullable
|
||||
private FloatingActionButton mMyRoutesButton;
|
||||
|
||||
@Nullable
|
||||
private MyPositionButton mNavMyPosition;
|
||||
|
@ -104,6 +106,11 @@ public class MapButtonsController extends Fragment
|
|||
final View myPosition = mFrame.findViewById(R.id.my_position);
|
||||
mNavMyPosition = new MyPositionButton(myPosition, (v) -> mMapButtonClickListener.onMapButtonClick(MapButtons.myPosition));
|
||||
|
||||
mMyRoutesButton = mFrame.findViewById(R.id.btn_myroutes);
|
||||
if (mMyRoutesButton != null)
|
||||
{
|
||||
mMyRoutesButton.setOnClickListener(view -> mMapButtonClickListener.onMapButtonClick(MapButtons.myRoutes));
|
||||
}
|
||||
// Some buttons do not exist in navigation mode
|
||||
mToggleMapLayerButton = mFrame.findViewById(R.id.layers_button);
|
||||
if (mToggleMapLayerButton != null)
|
||||
|
@ -143,6 +150,8 @@ public class MapButtonsController extends Fragment
|
|||
mButtonsMap.put(MapButtons.bookmarks, bookmarksButton);
|
||||
mButtonsMap.put(MapButtons.search, searchButton);
|
||||
|
||||
if (mMyRoutesButton != null)
|
||||
mButtonsMap.put(MapButtons.myRoutes, mMyRoutesButton);
|
||||
if (mToggleMapLayerButton != null)
|
||||
mButtonsMap.put(MapButtons.toggleMapLayer, mToggleMapLayerButton);
|
||||
if (menuButton != null)
|
||||
|
@ -181,6 +190,11 @@ public class MapButtonsController extends Fragment
|
|||
case bookmarks:
|
||||
case menu:
|
||||
UiUtils.showIf(show, buttonView);
|
||||
break;
|
||||
case myRoutes:
|
||||
UiUtils.showIf(show, mMyRoutesButton);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -350,7 +364,8 @@ public class MapButtonsController extends Fragment
|
|||
search,
|
||||
bookmarks,
|
||||
menu,
|
||||
help
|
||||
help,
|
||||
myRoutes
|
||||
}
|
||||
|
||||
public interface MapButtonClickListener
|
||||
|
|
|
@ -0,0 +1,265 @@
|
|||
package app.organicmaps.maplayer;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemClock;
|
||||
import android.text.InputFilter;
|
||||
import android.text.InputType;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import app.organicmaps.Framework;
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.util.bottomsheet.MenuBottomSheetFragment;
|
||||
|
||||
public class MyRoutesFragment extends Fragment
|
||||
{
|
||||
private static final String MYROUTES_MENU_ID = "MYROUTES_MENU_BOTTOM_SHEET";
|
||||
@Nullable
|
||||
private RoutesAdapter mAdapter;
|
||||
private MapButtonsViewModel mMapButtonsViewModel;
|
||||
private String mEditText = "";
|
||||
private String mDialogCaller = "";
|
||||
private RouteBottomSheetItem mCurrentItem = null;
|
||||
private static final String SAVE_ID = "SAVE_ID";
|
||||
private static final String RENAME_ID = "RENAME_ID";
|
||||
private static final String DELETE_ID = "DELETE_ID";
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
|
||||
{
|
||||
View mRoot = inflater.inflate(R.layout.fragment_myroutes, container, false);
|
||||
|
||||
mMapButtonsViewModel = new ViewModelProvider(requireActivity()).get(MapButtonsViewModel.class);
|
||||
MaterialButton mCloseButton = mRoot.findViewById(R.id.close_button);
|
||||
mCloseButton.setOnClickListener(view -> closeMyRoutesBottomSheet());
|
||||
|
||||
Button mSaveButton = mRoot.findViewById(R.id.save_button);
|
||||
if (Framework.nativeGetRoutePoints().length >= 2)
|
||||
mSaveButton.setOnClickListener(view -> onSaveButtonClick());
|
||||
else
|
||||
mSaveButton.setEnabled(false);
|
||||
|
||||
initRecycler(mRoot);
|
||||
return mRoot;
|
||||
}
|
||||
|
||||
private void initRecycler(@NonNull View root)
|
||||
{
|
||||
RecyclerView recycler = root.findViewById(R.id.recycler);
|
||||
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(requireContext(),
|
||||
LinearLayoutManager.VERTICAL,
|
||||
false);
|
||||
recycler.setLayoutManager(layoutManager);
|
||||
mAdapter = new RoutesAdapter(getRouteItems());
|
||||
recycler.setAdapter(mAdapter);
|
||||
recycler.setNestedScrollingEnabled(false);
|
||||
}
|
||||
|
||||
private List<RouteBottomSheetItem> getRouteItems()
|
||||
{
|
||||
String[] savedRouteNames = Framework.nativeGetUserRouteNames();
|
||||
List<RouteBottomSheetItem> items = new ArrayList<>();
|
||||
for (String routeName : savedRouteNames)
|
||||
items.add(createItem(routeName));
|
||||
return items;
|
||||
}
|
||||
|
||||
private RouteBottomSheetItem createItem(String routeName)
|
||||
{
|
||||
return RouteBottomSheetItem.create(routeName, this::onItemTitleClick, this::onItemRenameClick, this::onItemDeleteClick);
|
||||
}
|
||||
|
||||
private void onSaveButtonClick()
|
||||
{
|
||||
mEditText = "";
|
||||
mDialogCaller = SAVE_ID;
|
||||
showTextInputDialog("");
|
||||
}
|
||||
|
||||
private void checkSave()
|
||||
{
|
||||
String newRouteName = mEditText;
|
||||
|
||||
if (newRouteName.isEmpty())
|
||||
return;
|
||||
if (Framework.nativeHasSavedUserRoute(newRouteName))
|
||||
{
|
||||
showConfirmationDialog(newRouteName + " already exists",
|
||||
"Overwrite existing " + newRouteName + "?",
|
||||
"Overwrite");
|
||||
return;
|
||||
}
|
||||
save(false);
|
||||
}
|
||||
|
||||
private void save(boolean isOverwrite)
|
||||
{
|
||||
String newRouteName = mEditText;
|
||||
|
||||
if (isOverwrite)
|
||||
Framework.nativeDeleteUserRoute(newRouteName);
|
||||
|
||||
Framework.nativeSaveUserRoutePoints(newRouteName);
|
||||
|
||||
if (!isOverwrite)
|
||||
mAdapter.addRoute(createItem(newRouteName));
|
||||
}
|
||||
|
||||
private void onItemTitleClick(@NonNull View v, @NonNull RouteBottomSheetItem item)
|
||||
{
|
||||
Framework.nativeLoadUserRoutePoints(item.getRouteName());
|
||||
}
|
||||
|
||||
private void onItemRenameClick(@NonNull View v, @NonNull RouteBottomSheetItem item)
|
||||
{
|
||||
mEditText = "";
|
||||
mDialogCaller = RENAME_ID;
|
||||
mCurrentItem = item;
|
||||
showTextInputDialog(item.getRouteName());
|
||||
}
|
||||
|
||||
private void checkRename()
|
||||
{
|
||||
String newRouteName = mEditText;
|
||||
|
||||
if (newRouteName.isEmpty())
|
||||
return;
|
||||
|
||||
if (newRouteName.equals(mCurrentItem.getRouteName()))
|
||||
return;
|
||||
if (Framework.nativeHasSavedUserRoute(newRouteName))
|
||||
{
|
||||
showConfirmationDialog(newRouteName + " already exists",
|
||||
"Overwrite existing " + newRouteName + "?",
|
||||
"Overwrite");
|
||||
return;
|
||||
}
|
||||
rename(false);
|
||||
}
|
||||
|
||||
private void rename(boolean isOverwrite)
|
||||
{
|
||||
String newRouteName = mEditText;
|
||||
|
||||
if (isOverwrite)
|
||||
{
|
||||
Framework.nativeDeleteUserRoute(newRouteName);
|
||||
// Sometimes delete takes too long and renaming cannot happen; thus, sleep
|
||||
SystemClock.sleep(250); // TODO(Fábio Gomes) find a better solution
|
||||
}
|
||||
|
||||
Framework.nativeRenameUserRoute(mCurrentItem.getRouteName(), newRouteName);
|
||||
|
||||
mAdapter.removeRoute(mCurrentItem);
|
||||
if (!isOverwrite)
|
||||
mAdapter.addRoute(createItem(newRouteName));
|
||||
}
|
||||
|
||||
private void onItemDeleteClick(@NonNull View v, @NonNull RouteBottomSheetItem item)
|
||||
{
|
||||
mDialogCaller = DELETE_ID;
|
||||
mCurrentItem = item;
|
||||
showConfirmationDialog("Delete " + item.getRouteName() + "?",
|
||||
"This action cannot be undone.",
|
||||
"Delete");
|
||||
}
|
||||
|
||||
private void delete()
|
||||
{
|
||||
Framework.nativeDeleteUserRoute(mCurrentItem.getRouteName());
|
||||
mAdapter.removeRoute(mCurrentItem);
|
||||
}
|
||||
|
||||
private void closeMyRoutesBottomSheet()
|
||||
{
|
||||
MenuBottomSheetFragment bottomSheet =
|
||||
(MenuBottomSheetFragment) requireActivity().getSupportFragmentManager().findFragmentByTag(MYROUTES_MENU_ID);
|
||||
if (bottomSheet != null)
|
||||
bottomSheet.dismiss();
|
||||
}
|
||||
|
||||
private void showConfirmationDialog(String title, String message, String buttonText)
|
||||
{
|
||||
AlertDialog dialog = new AlertDialog.Builder(this.getContext(), R.style.MwmTheme_AlertDialog)
|
||||
.setTitle(title)
|
||||
.setMessage(message)
|
||||
.setPositiveButton(buttonText, (dialogInterface, i) -> {
|
||||
switch (mDialogCaller) {
|
||||
case SAVE_ID -> save(true);
|
||||
case RENAME_ID -> rename(true);
|
||||
case DELETE_ID -> delete();
|
||||
}
|
||||
})
|
||||
.setNegativeButton("Cancel", (dialogInterface, i) -> {
|
||||
switch (mDialogCaller) {
|
||||
case SAVE_ID, RENAME_ID -> retryInput();
|
||||
}
|
||||
})
|
||||
.create();
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void showTextInputDialog(String defaultText)
|
||||
{
|
||||
EditText input = new EditText(this.getContext());
|
||||
input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES);
|
||||
input.setFilters(getInputFilters());
|
||||
input.setText(defaultText);
|
||||
|
||||
AlertDialog dialog = new AlertDialog.Builder(this.getContext(), R.style.MwmTheme_AlertDialog)
|
||||
.setTitle("Route Name")
|
||||
.setView(input)
|
||||
.setPositiveButton("OK", (dialogInterface, i) -> {
|
||||
mEditText = input.getText().toString();
|
||||
switch (mDialogCaller) {
|
||||
case SAVE_ID -> checkSave();
|
||||
case RENAME_ID -> checkRename();
|
||||
}
|
||||
})
|
||||
.setNegativeButton("Cancel", null)
|
||||
.create();
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void retryInput()
|
||||
{
|
||||
showTextInputDialog(mEditText);
|
||||
}
|
||||
|
||||
private InputFilter[] getInputFilters()
|
||||
{
|
||||
InputFilter filter = (source, start, end, dest, dstart, dend) -> {
|
||||
for (int i = start; i < end; i++)
|
||||
{
|
||||
char current = source.charAt(i);
|
||||
if (!Character.isSpaceChar(current) && !Character.isLetterOrDigit(current))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
return new InputFilter[] {filter, new InputFilter.LengthFilter(32)};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package app.organicmaps.maplayer;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import app.organicmaps.adapter.OnItemClickListener;
|
||||
|
||||
public class RouteBottomSheetItem
|
||||
{
|
||||
@NonNull
|
||||
private final String mRouteName;
|
||||
@NonNull
|
||||
private final OnItemClickListener<RouteBottomSheetItem> mItemTitleClickListener;
|
||||
@NonNull
|
||||
private final OnItemClickListener<RouteBottomSheetItem> mItemRenameClickListener;
|
||||
@NonNull
|
||||
private final OnItemClickListener<RouteBottomSheetItem> mItemDeleteClickListener;
|
||||
|
||||
RouteBottomSheetItem(@NonNull String routeName,
|
||||
@NonNull OnItemClickListener<RouteBottomSheetItem> itemTitleClickListener,
|
||||
@NonNull OnItemClickListener<RouteBottomSheetItem> itemRenameClickListener,
|
||||
@NonNull OnItemClickListener<RouteBottomSheetItem> itemDeleteClickListener)
|
||||
{
|
||||
mRouteName = routeName;
|
||||
mItemTitleClickListener = itemTitleClickListener;
|
||||
mItemRenameClickListener = itemRenameClickListener;
|
||||
mItemDeleteClickListener = itemDeleteClickListener;
|
||||
}
|
||||
|
||||
public static RouteBottomSheetItem create(@NonNull String routeName,
|
||||
@NonNull OnItemClickListener<RouteBottomSheetItem> routeItemTitleClickListener,
|
||||
@NonNull OnItemClickListener<RouteBottomSheetItem> routeItemRenameClickListener,
|
||||
@NonNull OnItemClickListener<RouteBottomSheetItem> routeItemDeleteClickListener)
|
||||
{
|
||||
return new RouteBottomSheetItem(routeName, routeItemTitleClickListener, routeItemRenameClickListener, routeItemDeleteClickListener);
|
||||
}
|
||||
|
||||
public String getRouteName()
|
||||
{
|
||||
return mRouteName;
|
||||
}
|
||||
|
||||
public void onTitleClick(@NonNull View v, @NonNull RouteBottomSheetItem item)
|
||||
{
|
||||
mItemTitleClickListener.onItemClick(v, item);
|
||||
}
|
||||
|
||||
public void onRenameClick(@NonNull View v, @NonNull RouteBottomSheetItem item)
|
||||
{
|
||||
mItemRenameClickListener.onItemClick(v, item);
|
||||
}
|
||||
|
||||
public void onDeleteClick(@NonNull View v, @NonNull RouteBottomSheetItem item)
|
||||
{
|
||||
mItemDeleteClickListener.onItemClick(v, item);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package app.organicmaps.maplayer;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.adapter.OnItemClickListener;
|
||||
|
||||
class RouteHolder extends RecyclerView.ViewHolder
|
||||
{
|
||||
@NonNull
|
||||
final TextView mTitle;
|
||||
@NonNull
|
||||
final ImageView mRenameButton;
|
||||
@NonNull
|
||||
final ImageView mDeleteButton;
|
||||
@Nullable
|
||||
RouteBottomSheetItem mItem;
|
||||
@Nullable
|
||||
OnItemClickListener<RouteBottomSheetItem> mTitleListener;
|
||||
@Nullable
|
||||
OnItemClickListener<RouteBottomSheetItem> mRenameListener;
|
||||
@Nullable
|
||||
OnItemClickListener<RouteBottomSheetItem> mDeleteListener;
|
||||
|
||||
RouteHolder(@NonNull View root)
|
||||
{
|
||||
super(root);
|
||||
mTitle = root.findViewById(R.id.name);
|
||||
mTitle.setOnClickListener(this::onItemTitleClicked);
|
||||
mRenameButton = root.findViewById(R.id.rename);
|
||||
mRenameButton.setOnClickListener(this::onItemRenameClicked);
|
||||
mDeleteButton = root.findViewById(R.id.delete);
|
||||
mDeleteButton.setOnClickListener(this::onItemDeleteClicked);
|
||||
}
|
||||
|
||||
public void onItemTitleClicked(@NonNull View v)
|
||||
{
|
||||
if (mTitleListener != null && mItem != null)
|
||||
mTitleListener.onItemClick(v, mItem);
|
||||
}
|
||||
|
||||
public void onItemRenameClicked(@NonNull View v)
|
||||
{
|
||||
if (mRenameListener != null && mItem != null)
|
||||
mRenameListener.onItemClick(v, mItem);
|
||||
}
|
||||
|
||||
public void onItemDeleteClicked(@NonNull View v)
|
||||
{
|
||||
if (mDeleteListener != null && mItem != null)
|
||||
mDeleteListener.onItemClick(v, mItem);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package app.organicmaps.maplayer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.util.SharedPropertiesUtils;
|
||||
import app.organicmaps.util.UiUtils;
|
||||
import app.organicmaps.util.log.Logger;
|
||||
|
||||
public class RoutesAdapter extends RecyclerView.Adapter<RouteHolder>
|
||||
{
|
||||
@NonNull
|
||||
private final List<RouteBottomSheetItem> mItems;
|
||||
|
||||
public RoutesAdapter(@NonNull List<RouteBottomSheetItem> items)
|
||||
{
|
||||
mItems = items;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RouteHolder onCreateViewHolder(ViewGroup parent, int viewType)
|
||||
{
|
||||
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
||||
View root = inflater.inflate(R.layout.item_myroute_button, parent, false);
|
||||
return new RouteHolder(root);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(RouteHolder holder, int position)
|
||||
{
|
||||
RouteBottomSheetItem item = mItems.get(position);
|
||||
holder.mItem = item;
|
||||
|
||||
holder.mTitle.setSelected(true);
|
||||
holder.mTitle.setText(item.getRouteName());
|
||||
|
||||
holder.mTitleListener = item::onTitleClick;
|
||||
holder.mRenameListener = item::onRenameClick;
|
||||
holder.mDeleteListener = item::onDeleteClick;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount()
|
||||
{
|
||||
return mItems.size();
|
||||
}
|
||||
|
||||
public void addRoute(@NonNull RouteBottomSheetItem item)
|
||||
{
|
||||
// Compare strings toUpperCase to ignore case
|
||||
String routeName = item.getRouteName().toUpperCase();
|
||||
String iName;
|
||||
// Find index to add ordered
|
||||
int pos = mItems.size();
|
||||
for (int i = 0; i < mItems.size(); i++)
|
||||
{
|
||||
iName = mItems.get(i).getRouteName().toUpperCase();
|
||||
if(routeName.compareTo(iName) < 0)
|
||||
{
|
||||
pos = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mItems.add(pos, item);
|
||||
notifyItemInserted(pos);
|
||||
}
|
||||
|
||||
public void removeRoute(@NonNull RouteBottomSheetItem item)
|
||||
{
|
||||
int pos = mItems.indexOf(item);
|
||||
mItems.remove(item);
|
||||
notifyItemRemoved(pos);
|
||||
}
|
||||
}
|
|
@ -33,6 +33,14 @@
|
|||
android:layout_marginBottom="@dimen/margin_half"
|
||||
app:layout_constraintBottom_toTopOf="@+id/btn_bookmarks"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
<include
|
||||
layout="@layout/map_buttons_myroutes"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginBottom="@dimen/margin_half"
|
||||
app:layout_constraintBottom_toTopOf="@+id/btn_search"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/map_buttons_inner_right"
|
||||
|
|
|
@ -57,4 +57,12 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignParentEnd="true" />
|
||||
<include
|
||||
layout="@layout/map_buttons_myroutes"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginBottom="@dimen/margin_half"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_marginStart="@dimen/margin_half" />
|
||||
</RelativeLayout>
|
65
android/app/src/main/res/layout/fragment_myroutes.xml
Normal file
65
android/app/src/main/res/layout/fragment_myroutes.xml
Normal file
|
@ -0,0 +1,65 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical">
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
<TextView
|
||||
android:id="@+id/layers_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_base"
|
||||
android:layout_marginTop="@dimen/margin_base"
|
||||
android:text="My Routes"
|
||||
android:textAppearance="?fontHeadline6"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/close_button"
|
||||
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"
|
||||
android:layout_width="@dimen/place_page_top_button"
|
||||
android:layout_height="@dimen/place_page_top_button"
|
||||
android:layout_marginEnd="@dimen/margin_half_plus"
|
||||
android:layout_marginTop="@dimen/margin_half_plus"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/close"
|
||||
app:icon="@drawable/ic_close"
|
||||
app:iconGravity="textStart"
|
||||
app:iconPadding="0dp"
|
||||
app:iconSize="24dp"
|
||||
app:iconTint="?iconTint"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Button.Round" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<Button
|
||||
android:id="@+id/save_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Save Current Route"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:padding="16dp"/>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?cardBackground"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="@dimen/action_bar_extended_height"
|
||||
android:scrollbars="none"/>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
<View
|
||||
android:id="@+id/divider_bottom"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?dividerHorizontal"/>
|
||||
</LinearLayout>
|
50
android/app/src/main/res/layout/item_myroute_button.xml
Normal file
50
android/app/src/main/res/layout/item_myroute_button.xml
Normal file
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
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:layout_width="match_parent"
|
||||
android:layout_height="@dimen/height_item_edit_bookmark"
|
||||
android:background="?clickableBackground">
|
||||
|
||||
<Button
|
||||
android:id="@+id/name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:layout_marginTop="@dimen/margin_base"
|
||||
android:layout_marginStart="8dp"
|
||||
android:paddingHorizontal="8dp"
|
||||
android:layout_toStartOf="@+id/rename"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:ellipsize="middle"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?fontBody1"
|
||||
tools:text="Route name looooooooooooooooooongasdasdasd"
|
||||
android:gravity="center"
|
||||
android:clickable="true"
|
||||
android:focusable="true"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/rename"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:layout_toStartOf="@+id/delete"
|
||||
android:paddingHorizontal="8dp"
|
||||
app:srcCompat="@drawable/ic_edit"
|
||||
app:tint="?secondary"
|
||||
android:importantForAccessibility="no" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/delete"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:paddingHorizontal="@dimen/margin_half"
|
||||
app:srcCompat="@drawable/ic_delete"
|
||||
app:tint="?secondary"
|
||||
android:importantForAccessibility="no" />
|
||||
</RelativeLayout>
|
|
@ -8,32 +8,43 @@
|
|||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
tools:background="@color/bg_primary">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/map_buttons_inner_left"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="@dimen/map_buttons_bottom_margin"
|
||||
android:layout_marginBottom="@dimen/map_buttons_bottom_margin"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:padding="@dimen/nav_frame_padding"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
<include
|
||||
layout="@layout/map_buttons_bookmarks"
|
||||
android:id="@+id/map_buttons_inner_left"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="@dimen/map_buttons_bottom_margin"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:padding="@dimen/nav_frame_padding"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<include
|
||||
layout="@layout/map_buttons_search"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginBottom="@dimen/margin_half"
|
||||
app:layout_constraintBottom_toTopOf="@+id/btn_bookmarks"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
layout="@layout/map_buttons_bookmarks"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<include
|
||||
layout="@layout/map_buttons_search"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginBottom="@dimen/margin_half"
|
||||
app:layout_constraintBottom_toTopOf="@+id/btn_bookmarks"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<include
|
||||
layout="@layout/map_buttons_myroutes"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginBottom="@dimen/margin_half"
|
||||
app:layout_constraintBottom_toTopOf="@+id/btn_search"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/map_buttons_inner_right"
|
||||
|
|
|
@ -57,4 +57,11 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignParentEnd="true" />
|
||||
<include
|
||||
layout="@layout/map_buttons_myroutes"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginBottom="@dimen/margin_half"
|
||||
android:layout_toStartOf="@+id/map_buttons_bottom" />
|
||||
</RelativeLayout>
|
8
android/app/src/main/res/layout/map_buttons_myroutes.xml
Normal file
8
android/app/src/main/res/layout/map_buttons_myroutes.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/btn_myroutes"
|
||||
style="@style/MwmWidget.MapButton"
|
||||
android:contentDescription="@string/tracks_title"
|
||||
app:srcCompat="@drawable/ic_bookmark_start" />
|
Loading…
Add table
Reference in a new issue