Add custom track colors

This commit is contained in:
Vitaliy 2020-08-01 13:35:42 +03:00
parent 30a7ebcb30
commit 094bd14c62
7 changed files with 437 additions and 18 deletions

View file

@ -563,6 +563,7 @@ dependencies {
implementation ("com.github.HITGIF:TextFieldBoxes:1.4.5"){
exclude group: 'com.android.support'
}
implementation 'com.jaredrummler:colorpicker:1.1.0'
huaweiImplementation files('libs/huawei-android-drm_v2.5.2.300.jar')
freehuaweiImplementation files('libs/huawei-android-drm_v2.5.2.300.jar')

View file

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="wrap_content"
android:orientation="vertical"
android:paddingLeft="@dimen/content_padding"
android:paddingRight="@dimen/content_padding">
<com.jaredrummler.android.colorpicker.ColorPickerView
android:id="@+id/color_picker_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/content_padding_small"
app:cpv_alphaChannelVisible="true" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.jaredrummler.android.colorpicker.ColorPanelView
android:id="@+id/color_panel_new"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
app:cpv_colorShape="square" />
<Space
android:layout_width="@dimen/content_padding"
android:layout_height="match_parent" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/color_hex_text_input"
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:boxBackgroundColor="@color/material_text_input_layout_bg"
app:hintEnabled="false"
app:prefixText="#">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/color_hex_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textMultiLine"
android:maxLines="4"
android:minHeight="@dimen/bottom_sheet_list_item_height"
android:paddingStart="@dimen/content_padding_small"
android:paddingLeft="@dimen/content_padding_small"
android:paddingEnd="@dimen/content_padding_small"
android:paddingRight="@dimen/content_padding_small"
android:textSize="@dimen/default_list_text_size"
tools:text="F1FF00" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</LinearLayout>

View file

@ -40,7 +40,7 @@
</LinearLayout>
<net.osmand.plus.widgets.FlowLayout
<com.google.android.material.internal.FlowLayout
android:id="@+id/select_color"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

View file

@ -2197,6 +2197,7 @@ public class OsmandSettings {
public final CommonPreference<String> CURRENT_TRACK_WIDTH = new StringPreference("current_track_width", "").makeGlobal().cache();
public final CommonPreference<Boolean> CURRENT_TRACK_SHOW_ARROWS = new BooleanPreference("current_track_show_arrows", false).makeGlobal().cache();
public final CommonPreference<Boolean> CURRENT_TRACK_SHOW_START_FINISH = new BooleanPreference("current_track_show_start_finish", true).makeGlobal().cache();
public final ListStringPreference CUSTOM_TRACK_COLORS = (ListStringPreference) new ListStringPreference("custom_track_colors", null, ",").makeGlobal();
// this value string is synchronized with settings_pref.xml preference name
public final CommonPreference<Integer> SAVE_TRACK_INTERVAL = new IntPreference("save_track_interval", 5000).makeProfile();

View file

@ -0,0 +1,228 @@
package net.osmand.plus.track;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import com.jaredrummler.android.colorpicker.ColorPanelView;
import com.jaredrummler.android.colorpicker.ColorPickerView;
import com.jaredrummler.android.colorpicker.ColorPickerView.OnColorChangedListener;
import net.osmand.AndroidUtils;
import net.osmand.PlatformUtil;
import net.osmand.plus.R;
import net.osmand.plus.UiUtilities;
import net.osmand.plus.base.MenuBottomSheetDialogFragment;
import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem;
import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem;
import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem;
import org.apache.commons.logging.Log;
public class CustomColorBottomSheet extends MenuBottomSheetDialogFragment implements OnColorChangedListener {
private static final String TAG = CustomColorBottomSheet.class.getSimpleName();
private static final Log log = PlatformUtil.getLog(CustomColorBottomSheet.class);
private static final String NEW_SELECTED_COLOR = "new_selected_color";
private static final String PREV_SELECTED_COLOR = "prev_selected_color";
private ColorPickerView colorPicker;
private ColorPanelView newColorPanel;
private EditText hexEditText;
private boolean fromEditText;
@ColorInt
private int prevColor;
@ColorInt
private int newColor;
@Override
public void createMenuItems(Bundle savedInstanceState) {
if (savedInstanceState != null) {
newColor = savedInstanceState.getInt(NEW_SELECTED_COLOR);
prevColor = savedInstanceState.getInt(PREV_SELECTED_COLOR);
} else {
Bundle args = getArguments();
if (args != null) {
prevColor = args.getInt(PREV_SELECTED_COLOR);
newColor = prevColor != -1 ? prevColor : Color.RED;
}
}
items.add(new TitleItem(getString(R.string.select_color)));
BaseBottomSheetItem item = new SimpleBottomSheetItem.Builder()
.setCustomView(createPickerView())
.create();
items.add(item);
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt(NEW_SELECTED_COLOR, newColor);
outState.putInt(PREV_SELECTED_COLOR, prevColor);
super.onSaveInstanceState(outState);
}
private View createPickerView() {
LayoutInflater themedInflater = UiUtilities.getMaterialInflater(getActivity(), nightMode);
View colorView = themedInflater.inflate(R.layout.custom_color_picker, null);
colorPicker = colorView.findViewById(R.id.color_picker_view);
newColorPanel = colorView.findViewById(R.id.color_panel_new);
hexEditText = colorView.findViewById(R.id.color_hex_edit_text);
setHex(newColor);
newColorPanel.setColor(newColor);
colorPicker.setColor(newColor, true);
colorPicker.setOnColorChangedListener(this);
hexEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if (hexEditText.isFocused()) {
int color = parseColorString(s.toString());
if (color != colorPicker.getColor()) {
fromEditText = true;
colorPicker.setColor(color, true);
}
}
}
});
return colorView;
}
@Override
public void onColorChanged(int newColor) {
this.newColor = newColor;
if (newColorPanel != null) {
newColorPanel.setColor(newColor);
}
if (!fromEditText && hexEditText != null) {
setHex(newColor);
Activity activity = getActivity();
if (activity != null && hexEditText.hasFocus()) {
AndroidUtils.hideSoftKeyboard(activity, hexEditText);
hexEditText.clearFocus();
}
}
fromEditText = false;
}
private void setHex(int color) {
hexEditText.setText(String.format("%08X", color));
}
@Override
protected int getRightBottomButtonTextId() {
return R.string.shared_string_apply;
}
@Override
protected void onRightBottomButtonClick() {
Fragment target = getTargetFragment();
if (target instanceof ColorPickerListener) {
((ColorPickerListener) target).onColorSelected(prevColor, newColor);
}
dismiss();
}
public static void showInstance(@NonNull FragmentManager fragmentManager, Fragment target, int prevColor) {
try {
if (!fragmentManager.isStateSaved() && fragmentManager.findFragmentByTag(CustomColorBottomSheet.TAG) == null) {
Bundle args = new Bundle();
args.putInt(PREV_SELECTED_COLOR, prevColor);
CustomColorBottomSheet customColorBottomSheet = new CustomColorBottomSheet();
customColorBottomSheet.setArguments(args);
customColorBottomSheet.setTargetFragment(target, 0);
customColorBottomSheet.show(fragmentManager, CustomColorBottomSheet.TAG);
}
} catch (RuntimeException e) {
log.error(e);
}
}
private static int parseColorString(String colorString) throws NumberFormatException {
int a, r, g, b = 0;
if (colorString.startsWith("#")) {
colorString = colorString.substring(1);
}
if (colorString.length() == 0) {
r = 0;
a = 255;
g = 0;
} else if (colorString.length() <= 2) {
a = 255;
r = 0;
b = Integer.parseInt(colorString, 16);
g = 0;
} else if (colorString.length() == 3) {
a = 255;
r = Integer.parseInt(colorString.substring(0, 1), 16);
g = Integer.parseInt(colorString.substring(1, 2), 16);
b = Integer.parseInt(colorString.substring(2, 3), 16);
} else if (colorString.length() == 4) {
a = 255;
r = Integer.parseInt(colorString.substring(0, 2), 16);
g = r;
r = 0;
b = Integer.parseInt(colorString.substring(2, 4), 16);
} else if (colorString.length() == 5) {
a = 255;
r = Integer.parseInt(colorString.substring(0, 1), 16);
g = Integer.parseInt(colorString.substring(1, 3), 16);
b = Integer.parseInt(colorString.substring(3, 5), 16);
} else if (colorString.length() == 6) {
a = 255;
r = Integer.parseInt(colorString.substring(0, 2), 16);
g = Integer.parseInt(colorString.substring(2, 4), 16);
b = Integer.parseInt(colorString.substring(4, 6), 16);
} else if (colorString.length() == 7) {
a = Integer.parseInt(colorString.substring(0, 1), 16);
r = Integer.parseInt(colorString.substring(1, 3), 16);
g = Integer.parseInt(colorString.substring(3, 5), 16);
b = Integer.parseInt(colorString.substring(5, 7), 16);
} else if (colorString.length() == 8) {
a = Integer.parseInt(colorString.substring(0, 2), 16);
r = Integer.parseInt(colorString.substring(2, 4), 16);
g = Integer.parseInt(colorString.substring(4, 6), 16);
b = Integer.parseInt(colorString.substring(6, 8), 16);
} else {
b = -1;
g = -1;
r = -1;
a = -1;
}
return Color.argb(a, r, g, b);
}
public interface ColorPickerListener {
void onColorSelected(@ColorInt int prevColor, @ColorInt int newColor);
}
}

View file

@ -41,6 +41,7 @@ import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.routepreparationmenu.cards.BaseCard;
import net.osmand.plus.routepreparationmenu.cards.BaseCard.CardListener;
import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.track.CustomColorBottomSheet.ColorPickerListener;
import net.osmand.plus.track.SplitTrackAsyncTask.SplitTrackListener;
import net.osmand.render.RenderingRulesStorage;
import net.osmand.util.Algorithms;
@ -57,9 +58,9 @@ import static net.osmand.plus.dialogs.ConfigureMapMenu.CURRENT_TRACK_COLOR_ATTR;
import static net.osmand.plus.dialogs.GpxAppearanceAdapter.TRACK_WIDTH_BOLD;
import static net.osmand.plus.dialogs.GpxAppearanceAdapter.TRACK_WIDTH_MEDIUM;
public class TrackAppearanceFragment extends ContextMenuScrollFragment implements CardListener {
public class TrackAppearanceFragment extends ContextMenuScrollFragment implements CardListener, ColorPickerListener {
public static final String TAG = TrackAppearanceFragment.class.getSimpleName();
public static final String TAG = TrackAppearanceFragment.class.getName();
private static final Log log = PlatformUtil.getLog(TrackAppearanceFragment.class);
@ -76,6 +77,7 @@ public class TrackAppearanceFragment extends ContextMenuScrollFragment implement
private TrackWidthCard trackWidthCard;
private SplitIntervalCard splitIntervalCard;
private TrackColoringCard trackColoringCard;
private ImageView trackIcon;
@ -324,6 +326,11 @@ public class TrackAppearanceFragment extends ContextMenuScrollFragment implement
}
@Override
public void onColorSelected(int prevColor, int newColor) {
trackColoringCard.onColorSelected(prevColor, newColor);
}
@Override
protected int applyPosY(int currentY, boolean needCloseMenu, boolean needMapAdjust, int previousMenuState, int newMenuState, int dZoom, boolean animated) {
int y = super.applyPosY(currentY, needCloseMenu, needMapAdjust, previousMenuState, newMenuState, dZoom, animated);
@ -555,7 +562,7 @@ public class TrackAppearanceFragment extends ContextMenuScrollFragment implement
directionArrowsCard.setListener(this);
cardsContainer.addView(directionArrowsCard.build(mapActivity));
TrackColoringCard trackColoringCard = new TrackColoringCard(mapActivity, trackDrawInfo);
trackColoringCard = new TrackColoringCard(mapActivity, trackDrawInfo, this);
trackColoringCard.setListener(this);
cardsContainer.addView(trackColoringCard.build(mapActivity));

View file

@ -1,12 +1,13 @@
package net.osmand.plus.track;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.ColorInt;
@ -14,10 +15,14 @@ import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.internal.FlowLayout;
import net.osmand.AndroidUtils;
import net.osmand.PlatformUtil;
import net.osmand.plus.R;
import net.osmand.plus.UiUtilities;
import net.osmand.plus.activities.MapActivity;
@ -25,16 +30,22 @@ import net.osmand.plus.dialogs.GpxAppearanceAdapter.AppearanceListItem;
import net.osmand.plus.dialogs.GpxAppearanceAdapter.GpxAppearanceAdapterType;
import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.routepreparationmenu.cards.BaseCard;
import net.osmand.plus.widgets.FlowLayout;
import net.osmand.plus.track.CustomColorBottomSheet.ColorPickerListener;
import net.osmand.util.Algorithms;
import org.apache.commons.logging.Log;
import java.util.ArrayList;
import java.util.List;
import static net.osmand.plus.dialogs.GpxAppearanceAdapter.getAppearanceItems;
public class TrackColoringCard extends BaseCard {
public class TrackColoringCard extends BaseCard implements ColorPickerListener {
public static final int INVALID_VALUE = -1;
private final static String SOLID_COLOR = "solid_color";
private static final Log log = PlatformUtil.getLog(TrackColoringCard.class);
private TrackDrawInfo trackDrawInfo;
@ -42,10 +53,15 @@ public class TrackColoringCard extends BaseCard {
private TrackAppearanceItem selectedAppearanceItem;
private List<TrackAppearanceItem> appearanceItems;
public TrackColoringCard(MapActivity mapActivity, TrackDrawInfo trackDrawInfo) {
private List<Integer> customColors;
private Fragment target;
public TrackColoringCard(MapActivity mapActivity, TrackDrawInfo trackDrawInfo, Fragment target) {
super(mapActivity);
this.target = target;
this.trackDrawInfo = trackDrawInfo;
appearanceItems = getGradientAppearanceItems();
customColors = getCustomColors();
}
@Override
@ -67,6 +83,25 @@ public class TrackColoringCard extends BaseCard {
AndroidUiHelper.updateVisibility(view.findViewById(R.id.top_divider), isShowDivider());
}
private List<Integer> getCustomColors() {
List<Integer> colors = new ArrayList<>();
List<String> colorNames = app.getSettings().CUSTOM_TRACK_COLORS.getStringsList();
if (colorNames != null) {
for (String colorHex : colorNames) {
try {
if (!Algorithms.isEmpty(colorHex)) {
int color = Algorithms.parseColor(colorHex);
colors.add(color);
}
} catch (IllegalArgumentException e) {
log.error(e);
}
}
}
return colors;
}
private List<TrackAppearanceItem> getGradientAppearanceItems() {
List<TrackAppearanceItem> items = new ArrayList<>();
items.add(new TrackAppearanceItem(SOLID_COLOR, app.getString(R.string.track_coloring_solid), R.drawable.ic_action_circle));
@ -80,6 +115,16 @@ public class TrackColoringCard extends BaseCard {
private void createColorSelector() {
FlowLayout selectColor = view.findViewById(R.id.select_color);
selectColor.removeAllViews();
for (int color : customColors) {
selectColor.addView(createColorItemView(color, selectColor, true));
}
if (customColors.size() < 6) {
selectColor.addView(createAddCustomColorItemView(selectColor));
}
selectColor.addView(createDividerView(selectColor));
List<Integer> colors = new ArrayList<>();
for (AppearanceListItem appearanceListItem : getAppearanceItems(app, GpxAppearanceAdapterType.TRACK_COLOR)) {
if (!colors.contains(appearanceListItem.getColor())) {
@ -87,20 +132,13 @@ public class TrackColoringCard extends BaseCard {
}
}
for (int color : colors) {
selectColor.addView(createColorItemView(color, selectColor), new FlowLayout.LayoutParams(0, 0));
selectColor.addView(createColorItemView(color, selectColor, false));
}
updateColorSelector(trackDrawInfo.getColor(), selectColor);
}
private View createColorItemView(@ColorInt final int color, final FlowLayout rootView) {
FrameLayout colorItemView = (FrameLayout) UiUtilities.getInflater(rootView.getContext(), nightMode)
.inflate(R.layout.point_editor_button, rootView, false);
ImageView outline = colorItemView.findViewById(R.id.outline);
outline.setImageDrawable(
UiUtilities.tintDrawable(AppCompatResources.getDrawable(app, R.drawable.bg_point_circle_contour),
ContextCompat.getColor(app,
nightMode ? R.color.stroked_buttons_and_links_outline_dark
: R.color.stroked_buttons_and_links_outline_light)));
private View createColorItemView(@ColorInt final int color, final FlowLayout rootView, boolean customColor) {
View colorItemView = createCircleView(rootView);
ImageView backgroundCircle = colorItemView.findViewById(R.id.background);
backgroundCircle.setImageDrawable(UiUtilities.tintDrawable(AppCompatResources.getDrawable(app, R.drawable.bg_point_circle), color));
backgroundCircle.setOnClickListener(new View.OnClickListener() {
@ -116,10 +154,68 @@ public class TrackColoringCard extends BaseCard {
}
}
});
if (customColor) {
backgroundCircle.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
CustomColorBottomSheet.showInstance(mapActivity.getSupportFragmentManager(), target, color);
}
return false;
}
});
}
colorItemView.setTag(color);
return colorItemView;
}
private View createAddCustomColorItemView(FlowLayout rootView) {
View colorItemView = createCircleView(rootView);
ImageView backgroundCircle = colorItemView.findViewById(R.id.background);
int bgColorId = nightMode ? R.color.activity_background_color_dark : R.color.activity_background_color_light;
Drawable backgroundIcon = app.getUIUtilities().getIcon(R.drawable.bg_point_circle, bgColorId);
ImageView icon = colorItemView.findViewById(R.id.icon);
icon.setVisibility(View.VISIBLE);
int activeColorResId = nightMode ? R.color.icon_color_active_dark : R.color.icon_color_active_light;
icon.setImageDrawable(app.getUIUtilities().getIcon(R.drawable.ic_action_add, activeColorResId));
backgroundCircle.setImageDrawable(backgroundIcon);
backgroundCircle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
CustomColorBottomSheet.showInstance(mapActivity.getSupportFragmentManager(), target, TrackColoringCard.INVALID_VALUE);
}
}
});
return colorItemView;
}
private View createDividerView(FlowLayout rootView) {
LayoutInflater themedInflater = UiUtilities.getInflater(view.getContext(), nightMode);
View divider = themedInflater.inflate(R.layout.simple_divider_item, rootView, false);
LinearLayout dividerContainer = new LinearLayout(view.getContext());
dividerContainer.addView(divider);
dividerContainer.setPadding(0, AndroidUtils.dpToPx(app, 1), 0, AndroidUtils.dpToPx(app, 5));
return dividerContainer;
}
private View createCircleView(ViewGroup rootView) {
LayoutInflater themedInflater = UiUtilities.getInflater(view.getContext(), nightMode);
View circleView = themedInflater.inflate(R.layout.point_editor_button, rootView, false);
ImageView outline = circleView.findViewById(R.id.outline);
int colorId = nightMode ? R.color.stroked_buttons_and_links_outline_dark : R.color.stroked_buttons_and_links_outline_light;
Drawable contourIcon = app.getUIUtilities().getIcon(R.drawable.bg_point_circle_contour, colorId);
outline.setImageDrawable(contourIcon);
return circleView;
}
private void updateColorSelector(int color, View rootView) {
View oldColor = rootView.findViewWithTag(trackDrawInfo.getColor());
if (oldColor != null) {
@ -175,6 +271,29 @@ public class TrackColoringCard extends BaseCard {
updateColorSelector();
}
@Override
public void onColorSelected(int prevColor, int newColor) {
if (prevColor == INVALID_VALUE && customColors.size() < 6) {
customColors.add(newColor);
} else if (!Algorithms.isEmpty(customColors)) {
int index = customColors.indexOf(prevColor);
if (index != INVALID_VALUE) {
customColors.set(index, newColor);
}
}
saveCustomColors();
updateContent();
}
private void saveCustomColors() {
List<String> colorNames = new ArrayList<>();
for (Integer color : customColors) {
String colorHex = Algorithms.colorToString(color);
colorNames.add(colorHex);
}
app.getSettings().CUSTOM_TRACK_COLORS.setStringsList(colorNames);
}
private class TrackColoringAdapter extends RecyclerView.Adapter<TrackAppearanceViewHolder> {
private List<TrackAppearanceItem> items;