diff --git a/OsmAnd/res/layout/quick_action_switchable_action.xml b/OsmAnd/res/layout/quick_action_switchable_action.xml index e2035042a6..b55a361706 100644 --- a/OsmAnd/res/layout/quick_action_switchable_action.xml +++ b/OsmAnd/res/layout/quick_action_switchable_action.xml @@ -6,35 +6,43 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical"> - + android:background="?attr/bg_color"> - + android:background="?android:selectableItemBackground" + android:minHeight="56dp" + android:orientation="horizontal"> - + - + + + + + diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index 48640bd554..1b938c7b4c 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -11,6 +11,10 @@ Thx - Hardy --> + Profiles selected for this action not found. + Change application profile + Add profile + Taping action button will switch between selected profiles. Back to editing Restore default items order Add / Edit Favorite diff --git a/OsmAnd/src/net/osmand/plus/dialogs/SelectMapViewQuickActionsBottomSheet.java b/OsmAnd/src/net/osmand/plus/dialogs/SelectMapViewQuickActionsBottomSheet.java index 7ed1e98ea6..70d91e21d1 100644 --- a/OsmAnd/src/net/osmand/plus/dialogs/SelectMapViewQuickActionsBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/dialogs/SelectMapViewQuickActionsBottomSheet.java @@ -4,6 +4,7 @@ package net.osmand.plus.dialogs; import android.app.Activity; import android.content.Context; import android.content.res.ColorStateList; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; @@ -22,6 +23,7 @@ import androidx.core.widget.NestedScrollView; import androidx.fragment.app.FragmentManager; import net.osmand.AndroidUtils; +import net.osmand.plus.ApplicationMode; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandSettings; import net.osmand.plus.R; @@ -34,8 +36,6 @@ import net.osmand.plus.quickaction.QuickAction; import net.osmand.plus.quickaction.QuickActionRegistry; import net.osmand.plus.quickaction.SwitchableAction; import net.osmand.plus.quickaction.actions.MapStyleAction; -import net.osmand.plus.render.RendererRegistry; -import net.osmand.render.RenderingRulesStorage; import java.util.List; @@ -45,12 +45,17 @@ public class SelectMapViewQuickActionsBottomSheet extends MenuBottomSheetDialogF private static final String SELECTED_ITEM_KEY = "selected_item"; + public static final String DIALOG_TYPE_KEY = "dialog_type"; + public static final String PROFILE_DIALOG_TYPE = "profile_dialog_type"; + public static final String MAP_DIALOG_TYPE = "map_dialog_type"; + private LinearLayout itemsContainer; private View.OnClickListener onClickListener; private ColorStateList rbColorList; private String selectedItem; private QuickAction action; + private String dialogType; @Override public void createMenuItems(Bundle savedInstanceState) { @@ -63,6 +68,7 @@ public class SelectMapViewQuickActionsBottomSheet extends MenuBottomSheetDialogF return; } long id = args.getLong(SwitchableAction.KEY_ID); + dialogType = args.getString(DIALOG_TYPE_KEY, MAP_DIALOG_TYPE); OsmandApplication app = mapActivity.getMyApplication(); QuickActionRegistry quickActionRegistry = app.getQuickActionRegistry(); @@ -189,7 +195,19 @@ public class SelectMapViewQuickActionsBottomSheet extends MenuBottomSheetDialogF rb.setChecked(selected); CompoundButtonCompat.setButtonTintList(rb, rbColorList); ImageView imageView = (ImageView) view.findViewById(R.id.icon); - imageView.setImageDrawable(getContentIcon(action.getIconRes())); + imageView.setImageDrawable(getItemIcon(tag)); + } + + private Drawable getItemIcon(String tag) { + if (PROFILE_DIALOG_TYPE.equals(dialogType)) { + ApplicationMode appMode = ApplicationMode.valueOfStringKey(tag, null); + if (appMode != null) { + int iconId = appMode.getIconRes(); + int colorId = appMode.getIconColorInfo().getColor(nightMode); + return getIcon(iconId, colorId); + } + } + return getContentIcon(action.getIconRes()); } @ColorInt diff --git a/OsmAnd/src/net/osmand/plus/profiles/ProfileDataObject.java b/OsmAnd/src/net/osmand/plus/profiles/ProfileDataObject.java index 666a7603a2..ee43175601 100644 --- a/OsmAnd/src/net/osmand/plus/profiles/ProfileDataObject.java +++ b/OsmAnd/src/net/osmand/plus/profiles/ProfileDataObject.java @@ -10,6 +10,7 @@ public class ProfileDataObject implements Comparable { private int iconRes; private String stringKey; private boolean isSelected; + private boolean isEnabled; private ProfileIconColors iconColor; public ProfileDataObject(String name, String description, String stringKey, int iconRes, boolean isSelected, ProfileIconColors iconColor) { @@ -41,6 +42,14 @@ public class ProfileDataObject implements Comparable { isSelected = selected; } + public boolean isEnabled() { + return isEnabled; + } + + public void setEnabled(boolean enabled) { + isEnabled = enabled; + } + public String getStringKey() { return stringKey; } diff --git a/OsmAnd/src/net/osmand/plus/profiles/SelectMultipleProfileBottomSheet.java b/OsmAnd/src/net/osmand/plus/profiles/SelectMultipleProfileBottomSheet.java new file mode 100644 index 0000000000..01e80e6a47 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/profiles/SelectMultipleProfileBottomSheet.java @@ -0,0 +1,161 @@ +package net.osmand.plus.profiles; + +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.view.View; +import android.widget.CompoundButton; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; + +import net.osmand.CallbackWithObject; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.plus.UiUtilities; +import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; +import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem; +import net.osmand.plus.settings.NavigationFragment; +import net.osmand.plus.settings.bottomsheets.BasePreferenceBottomSheet; + +import java.util.ArrayList; +import java.util.List; + +public class SelectMultipleProfileBottomSheet extends BasePreferenceBottomSheet { + + public static final String TAG = SelectMultipleProfileBottomSheet.class.getSimpleName(); + public static final String SELECTED_KEYS = "selected_keys"; + public static final String DISABLED_KEYS = "disabled_keys"; + + private List profiles = new ArrayList<>(); + private CallbackWithObject> callback; + private List selectedProfiles; + private List disabledProfiles; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Bundle args = getArguments(); + if (args != null) { + selectedProfiles = args.getStringArrayList(SELECTED_KEYS); + disabledProfiles = args.getStringArrayList(DISABLED_KEYS); + refreshProfiles(getMyApplication()); + } + } + + private void refreshProfiles(OsmandApplication app) { + profiles.clear(); + profiles.addAll(NavigationFragment.getBaseProfiles(app)); + for (ProfileDataObject profile : profiles) { + String key = profile.getStringKey(); + profile.setSelected(selectedProfiles.contains(key)); + profile.setEnabled(!disabledProfiles.contains(key)); + } + } + + @Override + public void createMenuItems(Bundle savedInstanceState) { + items.add(new TitleItem(getString(R.string.application_profiles))); + + for (int i = 0; i < profiles.size(); i++) { + addProfileItem(profiles.get(i)); + } + } + + private void addProfileItem(final ProfileDataObject profile) { + OsmandApplication app = requiredMyApplication(); + View itemView = UiUtilities.getInflater(app, nightMode) + .inflate(R.layout.bottom_sheet_item_with_descr_and_checkbox_56dp, null); + + int profileColorId = profile.getIconColor(nightMode); + int activeColorId = nightMode ? + R.color.active_color_primary_dark : R.color.active_color_primary_light; + int disableColorId = nightMode ? + R.color.icon_color_default_dark : R.color.icon_color_default_light; + boolean enable = profile.isEnabled(); + + TextView tvTitle = itemView.findViewById(R.id.title); + TextView tvDescription = itemView.findViewById(R.id.description); + ImageView ivIcon = itemView.findViewById(R.id.icon); + final CompoundButton compoundButton = itemView.findViewById(R.id.compound_button); + + tvTitle.setText(profile.getName()); + tvDescription.setText(profile.getDescription()); + + if (!enable) { + tvTitle.setTextColor(ContextCompat.getColor(app, disableColorId)); + tvDescription.setTextColor(ContextCompat.getColor(app, disableColorId)); + } + + Drawable drawableIcon = app.getUIUtilities().getIcon( + profile.getIconRes(), enable ? profileColorId : disableColorId); + ivIcon.setImageDrawable(drawableIcon); + UiUtilities.setupCompoundButton(nightMode, ContextCompat.getColor(app, + enable ? activeColorId : disableColorId), compoundButton); + compoundButton.setChecked(profile.isSelected()); + + View.OnClickListener l = !enable ? null : new View.OnClickListener() { + @Override + public void onClick(View v) { + boolean selected = !profile.isSelected(); + profile.setSelected(selected); + compoundButton.setChecked(selected); + } + }; + + items.add(new BaseBottomSheetItem.Builder() + .setCustomView(itemView) + .setOnClickListener(l) + .create()); + } + + @Override + protected void onDismissButtonClickAction() { + super.onDismissButtonClickAction(); + dismiss(); + } + + @Override + protected int getRightBottomButtonTextId() { + return R.string.shared_string_apply; + } + + @Override + protected void onRightBottomButtonClick() { + if (callback != null) { + List selectedProfileKeys = new ArrayList<>(); + for (ProfileDataObject profile : profiles) { + if (profile.isSelected() && profile.isEnabled()) { + selectedProfileKeys.add(profile.getStringKey()); + } + } + callback.processResult(selectedProfileKeys); + } + dismiss(); + } + + public void setCallback(CallbackWithObject> callback) { + this.callback = callback; + } + + public static void showInstance(@NonNull MapActivity mapActivity, + @Nullable List selectedProfiles, + @Nullable List disabledProfiles, + boolean usedOnMap, + CallbackWithObject> callback) { + SelectMultipleProfileBottomSheet fragment = new SelectMultipleProfileBottomSheet(); + Bundle args = new Bundle(); + args.putStringArrayList(SELECTED_KEYS, selectedProfiles != null ? + new ArrayList<>(selectedProfiles) : new ArrayList()); + args.putStringArrayList(DISABLED_KEYS, disabledProfiles != null ? + new ArrayList<>(disabledProfiles) : new ArrayList()); + fragment.setArguments(args); + fragment.setUsedOnMap(usedOnMap); + fragment.setCallback(callback); + fragment.show(mapActivity.getSupportFragmentManager(), SelectMultipleProfileBottomSheet.TAG); + } + +} diff --git a/OsmAnd/src/net/osmand/plus/quickaction/QuickActionRegistry.java b/OsmAnd/src/net/osmand/plus/quickaction/QuickActionRegistry.java index e778b5e0b7..ceca652233 100644 --- a/OsmAnd/src/net/osmand/plus/quickaction/QuickActionRegistry.java +++ b/OsmAnd/src/net/osmand/plus/quickaction/QuickActionRegistry.java @@ -35,6 +35,7 @@ import net.osmand.plus.quickaction.actions.ShowHideFavoritesAction; import net.osmand.plus.quickaction.actions.ShowHideGpxTracksAction; import net.osmand.plus.quickaction.actions.ShowHidePoiAction; import net.osmand.plus.quickaction.actions.ShowHideTransportLinesAction; +import net.osmand.plus.quickaction.actions.SwitchProfileAction; import net.osmand.util.Algorithms; import java.lang.reflect.Type; @@ -234,6 +235,7 @@ public class QuickActionRegistry { quickActionTypes.add(NavAutoZoomMapAction.TYPE); quickActionTypes.add(NavStartStopAction.TYPE); quickActionTypes.add(NavResumePauseAction.TYPE); + quickActionTypes.add(SwitchProfileAction.TYPE); OsmandPlugin.registerQuickActionTypesPlugins(quickActionTypes); Map quickActionTypesInt = new TreeMap<>(); diff --git a/OsmAnd/src/net/osmand/plus/quickaction/SwitchableAction.java b/OsmAnd/src/net/osmand/plus/quickaction/SwitchableAction.java index b2ab9c892d..327d3f87a1 100644 --- a/OsmAnd/src/net/osmand/plus/quickaction/SwitchableAction.java +++ b/OsmAnd/src/net/osmand/plus/quickaction/SwitchableAction.java @@ -1,6 +1,7 @@ package net.osmand.plus.quickaction; import android.content.Context; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -11,6 +12,8 @@ import android.widget.EditText; import android.widget.ImageView; import android.widget.TextView; +import androidx.annotation.ColorRes; +import androidx.annotation.DrawableRes; import androidx.annotation.StringRes; import androidx.appcompat.widget.SwitchCompat; import androidx.core.view.MotionEventCompat; @@ -28,6 +31,9 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import static net.osmand.plus.dialogs.SelectMapViewQuickActionsBottomSheet.DIALOG_TYPE_KEY; +import static net.osmand.plus.dialogs.SelectMapViewQuickActionsBottomSheet.MAP_DIALOG_TYPE; + public abstract class SwitchableAction extends QuickAction { public static final String KEY_ID = "id"; @@ -56,11 +62,18 @@ public abstract class SwitchableAction extends QuickAction { View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.quick_action_switchable_action, parent, false); - - SwitchCompat showDialog = (SwitchCompat) view.findViewById(R.id.saveButton); + + final SwitchCompat showDialog = (SwitchCompat) view.findViewById(R.id.saveButton); if (!getParams().isEmpty()) { showDialog.setChecked(Boolean.valueOf(getParams().get(KEY_DIALOG))); } + view.findViewById(R.id.saveButtonContainer).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + boolean selected = showDialog.isChecked(); + showDialog.setChecked(!selected); + } + }); RecyclerView list = (RecyclerView) view.findViewById(R.id.list); Adapter adapter = new Adapter(activity, new QuickActionListFragment.OnStartDragListener() { @@ -110,11 +123,16 @@ public abstract class SwitchableAction extends QuickAction { public abstract void executeWithParams(MapActivity activity, String params); public abstract String getTranslatedItemName(Context context, String item); - + protected void showChooseDialog(FragmentManager fm) { + showChooseDialog(fm, MAP_DIALOG_TYPE); + } + + protected void showChooseDialog(FragmentManager fm, String dialogType) { SelectMapViewQuickActionsBottomSheet fragment = new SelectMapViewQuickActionsBottomSheet(); Bundle args = new Bundle(); args.putLong(KEY_ID, id); + args.putString(DIALOG_TYPE_KEY, dialogType); fragment.setArguments(args); fragment.show(fm, SelectMapViewQuickActionsBottomSheet.TAG); } @@ -145,6 +163,12 @@ public abstract class SwitchableAction extends QuickAction { public void onBindViewHolder(final Adapter.ItemHolder holder, final int position) { final T item = itemsList.get(position); + OsmandApplication app = (OsmandApplication) context.getApplicationContext(); + + Drawable icon = app.getUIUtilities().getIcon( + getItemIconRes(app, item), getItemIconColorRes(app, item)); + holder.icon.setImageDrawable(icon); + holder.title.setText(getItemName(context, item)); holder.handleView.setOnTouchListener(new View.OnTouchListener() { @@ -181,6 +205,10 @@ public abstract class SwitchableAction extends QuickAction { return itemsList.size(); } + public List getItemsList() { + return itemsList; + } + public void deleteItem(int position) { if (position == -1) { @@ -262,6 +290,7 @@ public abstract class SwitchableAction extends QuickAction { public TextView title; public ImageView handleView; public ImageView closeBtn; + public ImageView icon; public ItemHolder(View itemView) { super(itemView); @@ -269,6 +298,7 @@ public abstract class SwitchableAction extends QuickAction { title = (TextView) itemView.findViewById(R.id.title); handleView = (ImageView) itemView.findViewById(R.id.handle_view); closeBtn = (ImageView) itemView.findViewById(R.id.closeImageButton); + icon = (ImageView) itemView.findViewById(R.id.imageView); } } } @@ -279,6 +309,17 @@ public abstract class SwitchableAction extends QuickAction { protected abstract String getItemName(Context context, T item); + @DrawableRes + protected int getItemIconRes(Context context, T item) { + return R.drawable.ic_map; + } + + @ColorRes + protected int getItemIconColorRes(OsmandApplication app, T item) { + boolean nightMode = !app.getSettings().isLightContent(); + return nightMode ? R.color.icon_color_default_dark : R.color.icon_color_default_light; + } + protected abstract @StringRes int getAddBtnText(); diff --git a/OsmAnd/src/net/osmand/plus/quickaction/actions/SwitchProfileAction.java b/OsmAnd/src/net/osmand/plus/quickaction/actions/SwitchProfileAction.java new file mode 100644 index 0000000000..8717ffc904 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/quickaction/actions/SwitchProfileAction.java @@ -0,0 +1,236 @@ +package net.osmand.plus.quickaction.actions; + +import android.content.Context; +import android.text.TextUtils; +import android.view.View; +import android.widget.Toast; + +import androidx.appcompat.widget.SwitchCompat; +import androidx.core.util.Pair; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +import net.osmand.CallbackWithObject; +import net.osmand.plus.ApplicationMode; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.OsmandSettings; +import net.osmand.plus.R; +import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.profiles.SelectMultipleProfileBottomSheet; +import net.osmand.plus.quickaction.QuickAction; +import net.osmand.plus.quickaction.QuickActionType; +import net.osmand.plus.quickaction.SwitchableAction; +import net.osmand.plus.views.MapQuickActionLayer; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import static net.osmand.plus.dialogs.SelectMapViewQuickActionsBottomSheet.PROFILE_DIALOG_TYPE; + +public class SwitchProfileAction extends SwitchableAction> { + + private final static String KEY_PROFILES = "profiles"; + + public static final QuickActionType TYPE = new QuickActionType(32, + "profile.change", SwitchProfileAction.class) + .nameRes(R.string.change_application_profile) + .iconRes(R.drawable.ic_action_manage_profiles) + .category(QuickActionType.NAVIGATION); + + public SwitchProfileAction() { + super(TYPE); + } + + public SwitchProfileAction(QuickAction quickAction) { + super(quickAction); + } + + @Override + protected String getTitle(List> filters) { + List profileNames = new ArrayList<>(); + for (Pair p : filters) { + profileNames.add(p.second); + } + return TextUtils.join(", ", profileNames); + } + + @Override + protected void saveListToParams(List> list) { + getParams().put(getListKey(), new Gson().toJson(list)); + } + + @Override + public List> loadListFromParams() { + String json = getParams().get(getListKey()); + + if (json == null || json.isEmpty()) return new ArrayList<>(); + + Type listType = new TypeToken>>() { + }.getType(); + + List> list = new Gson().fromJson(json, listType); + + Iterator> it = list.iterator(); + while (it.hasNext()) { + ApplicationMode appMode = getModeForKey(it.next().first); + if (appMode == null) { + it.remove(); + } + } + + return list; + } + + @Override + public void execute(MapActivity activity) { + OsmandSettings settings = activity.getMyApplication().getSettings(); + List> profiles = loadListFromParams(); + + if (profiles.size() == 0) { + Toast.makeText(activity, activity.getString(R.string.profiles_for_action_not_found), + Toast.LENGTH_SHORT).show(); + return; + } + + boolean showDialog = Boolean.valueOf(getParams().get(KEY_DIALOG)); + if (showDialog) { + showChooseDialog(activity.getSupportFragmentManager(), PROFILE_DIALOG_TYPE); + return; + } + + int index = -1; + final String currentSource = settings.getApplicationMode().getStringKey(); + + for (int idx = 0; idx < profiles.size(); idx++) { + if (currentSource.equals(profiles.get(idx).first)) { + index = idx; + break; + } + } + + String nextSource = profiles.get(0).first; + + if (index >= 0 && index + 1 < profiles.size()) { + nextSource = profiles.get(index + 1).first; + } + executeWithParams(activity, nextSource); + + super.execute(activity); + } + + @Override + public void executeWithParams(MapActivity activity, String params) { + OsmandApplication app = activity.getMyApplication(); + OsmandSettings settings = app.getSettings(); + + ApplicationMode appMode = getModeForKey(params); + if (appMode != null) { + settings.APPLICATION_MODE.set(appMode); + + app.getQuickActionRegistry().setQuickActionFabState(true); + + MapQuickActionLayer mil = activity.getMapLayers().getMapQuickActionLayer(); + if (mil != null) { + mil.refreshLayer(); + } + + String message = String.format(activity.getString( + R.string.application_profile_changed), appMode.toHumanString()); + Toast.makeText(activity, message, Toast.LENGTH_SHORT).show(); + } + } + + @Override + public String getTranslatedItemName(Context context, String item) { + return getModeForKey(item).toHumanString(); + } + + @Override + protected String getItemName(Context context, Pair item) { + return item.second; + } + + @Override + protected int getAddBtnText() { + return R.string.shared_string_add_profile; + } + + @Override + protected int getDiscrHint() { + return R.string.quick_action_switch_profile_descr; + } + + @Override + protected int getDiscrTitle() { + return R.string.application_profiles; + } + + @Override + protected String getListKey() { + return KEY_PROFILES; + } + + @Override + protected int getItemIconRes(Context context, Pair item) { + ApplicationMode appMode = getModeForKey(item.first); + if (appMode != null) { + return appMode.getIconRes(); + } + return super.getItemIconRes(context, item); + } + + @Override + protected int getItemIconColorRes(OsmandApplication app, Pair item) { + ApplicationMode appMode = getModeForKey(item.first); + if (appMode != null) { + boolean nightMode = !app.getSettings().isLightContent(); + return appMode.getIconColorInfo().getColor(nightMode); + } + return super.getItemIconColorRes(app, item); + } + + @Override + protected View.OnClickListener getOnAddBtnClickListener(final MapActivity activity, final Adapter adapter) { + return new View.OnClickListener() { + @Override + public void onClick(View v) { + List selectedProfilesKeys = new ArrayList<>(); + for (Pair item : adapter.getItemsList()) { + selectedProfilesKeys.add(item.first); + } + SelectMultipleProfileBottomSheet.showInstance(activity, selectedProfilesKeys, + selectedProfilesKeys, false, new CallbackWithObject>() { + @Override + public boolean processResult(List result) { + if (result == null || result.size() == 0) { + return false; + } + for (String item : result) { + ApplicationMode appMode = getModeForKey(item); + if (appMode != null) { + Pair profile = new Pair<>( + appMode.getStringKey(), appMode.toHumanString()); + adapter.addItem(profile, activity); + } + } + return true; + } + }); + } + }; + } + + private ApplicationMode getModeForKey(String key) { + return ApplicationMode.valueOfStringKey(key, null); + } + + @Override + public boolean fillParams(View root, MapActivity activity) { + getParams().put(KEY_DIALOG, Boolean.toString(((SwitchCompat) root.findViewById(R.id.saveButton)).isChecked())); + return super.fillParams(root, activity); + } + +}