Migration to Material Components theme and style.

New night theme mainly white on black.
Fixes #1122.
Fixes #957.
This commit is contained in:
Román Ginés
2022-02-06 20:33:17 +01:00
committed by Román Martínez
parent 72f7b12da6
commit 79e45f7aea
49 changed files with 892 additions and 829 deletions
@@ -120,10 +120,10 @@ public class StatisticsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
return;
}
this.value.setText(statisticData.hasValue() ? statisticData.getValue() : context.getString(R.string.value_unknown));
this.value.setTextAppearance(context, statisticData.getField().isPrimary() ? R.style.StatsPrimaryValue : R.style.StatsSecondaryValue);
this.value.setTextAppearance(context, statisticData.getField().isPrimary() ? R.style.TextAppearance_OpenTracks_PrimaryValue : R.style.TextAppearance_OpenTracks_SecondaryValue);
this.unit.setText(statisticData.getUnit());
this.descMain.setText(statisticData.getField().getTitle());
this.descMain.setTextAppearance(context, statisticData.getField().isPrimary() ? R.style.StatsPrimaryLabel : R.style.StatsSecondaryLabel);
this.descMain.setTextAppearance(context, statisticData.getField().isPrimary() ? R.style.TextAppearance_OpenTracks_PrimaryHeader : R.style.TextAppearance_OpenTracks_SecondaryHeader);
if (statisticData.hasDescription()) {
this.descSecondary.setVisibility(View.VISIBLE);
this.descSecondary.setText(statisticData.getDescription());
@@ -147,8 +147,8 @@ public class StatisticsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
if (statisticData == null) {
return;
}
this.value.setTextAppearance(context, statisticData.getField().isPrimary() ? R.style.StatsPrimaryValue : R.style.StatsSecondaryValue);
this.descMain.setTextAppearance(context, statisticData.getField().isPrimary() ? R.style.StatsPrimaryLabel : R.style.StatsSecondaryLabel);
this.value.setTextAppearance(context, statisticData.getField().isPrimary() ? R.style.TextAppearance_OpenTracks_PrimaryValue : R.style.TextAppearance_OpenTracks_SecondaryValue);
this.descMain.setTextAppearance(context, statisticData.getField().isPrimary() ? R.style.TextAppearance_OpenTracks_PrimaryHeader : R.style.TextAppearance_OpenTracks_SecondaryHeader);
}
}
@@ -78,7 +78,7 @@ class ChooseActivityTypeImageAdapter extends BaseAdapter {
}
if (position == selected) {
imageView.setBackgroundColor(ContextCompat.getColor(parent.getContext(), R.color.colorAccent));
imageView.setBackgroundColor(ContextCompat.getColor(parent.getContext(), R.color.opentracks_secondary_color));
} else {
imageView.setBackgroundColor(Color.TRANSPARENT);
}
@@ -2,8 +2,6 @@ package de.dennisguse.opentracks.settings;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ScrollView;
import androidx.annotation.NonNull;
@@ -17,6 +15,7 @@ import androidx.recyclerview.widget.RecyclerView;
import java.util.stream.IntStream;
import de.dennisguse.opentracks.AbstractActivity;
import de.dennisguse.opentracks.ui.util.ArrayAdapterFilterDisabled;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.databinding.ActivitySettingsCustomLayoutBinding;
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
@@ -80,23 +79,16 @@ public class SettingsCustomLayoutEditActivity extends AbstractActivity implement
itemTouchHelper.attachToRecyclerView(recyclerViewVisible);
// Spinner with items per row.
ArrayAdapter<Integer> spinnerAdapter = new ArrayAdapter<>(this,
ArrayAdapterFilterDisabled<Integer> rowsOptionAdapter = new ArrayAdapterFilterDisabled<>(this,
android.R.layout.simple_spinner_dropdown_item,
IntStream.of(getResources().getIntArray(R.array.stats_custom_layout_fields_columns_per_row)).boxed().toArray(Integer[]::new));
viewBinding.spinnerOptions.setAdapter(spinnerAdapter);
viewBinding.spinnerOptions.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
numColumns = position + 1;
gridLayoutManager.setSpanCount(numColumns);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
viewBinding.rowsOptions.setAdapter(rowsOptionAdapter);
viewBinding.rowsOptions.setOnItemClickListener((parent, view, position, id) -> {
numColumns = position + 1;
gridLayoutManager.setSpanCount(numColumns);
});
viewBinding.spinnerOptions.setSelection(numColumns - 1);
viewBinding.rowsOptions.setText(rowsOptionAdapter.getItem(numColumns - 1).toString(), false);
// Recycler view with not visible stats.
layoutFieldsHidden = StatisticsUtils.filterVisible(layout, false);
@@ -5,11 +5,10 @@ import android.content.Context;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CheckBox;
import android.widget.DatePicker;
import android.widget.GridLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -17,6 +16,8 @@ import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.button.MaterialButtonToggleGroup;
import com.google.android.material.textfield.TextInputEditText;
import java.time.LocalDate;
@@ -61,17 +62,17 @@ public class FilterDialogFragment extends DialogFragment {
LayoutInflater inflater = LayoutInflater.from(getActivity());
View layout = inflater.inflate(R.layout.fragment_filter_dialog, null, false);
GridLayout itemsLayout = layout.findViewById(R.id.filter_items);
MaterialButtonToggleGroup itemsLayout = layout.findViewById(R.id.filter_items);
builder.setView(layout);
for (FilterItem item : filterItems) {
View view = inflater.inflate(R.layout.fragment_filter_dialog_item, null);
CheckBox checkBox = view.findViewById(R.id.filter_dialog_check_button);
checkBox.setText(item.value);
checkBox.setChecked(item.isChecked);
checkBox.setTag(item.id);
checkBox.setOnClickListener(v -> item.isChecked = !item.isChecked);
MaterialButton button = view.findViewById(R.id.filter_dialog_check_button);
button.setText(item.value);
button.setChecked(item.isChecked);
button.setTag(item.id);
button.setOnClickListener(v -> item.isChecked = !item.isChecked);
itemsLayout.addView(view);
}
@@ -43,8 +43,8 @@ public class SettingsCustomLayoutEditAdapter extends RecyclerView.Adapter<Recycl
viewHolder.title.setText(field.getTitle());
viewHolder.value.setText(StatisticsUtils.emptyValue(context, field.getKey()));
viewHolder.title.setTextAppearance(context, field.isVisible() ? (field.isPrimary() ? R.style.StatsPrimaryLabel : R.style.StatsSecondaryLabel) : R.style.StatsLabelHidden);
viewHolder.value.setTextAppearance(context, field.isVisible() ? (field.isPrimary() ? R.style.StatsPrimaryValue : R.style.StatsSecondaryValue) : R.style.StatsValueHidden);
viewHolder.title.setTextAppearance(context, field.isVisible() ? (field.isPrimary() ? R.style.TextAppearance_OpenTracks_PrimaryHeader : R.style.TextAppearance_OpenTracks_SecondaryHeader) : R.style.TextAppearance_OpenTracks_HiddenHeader);
viewHolder.value.setTextAppearance(context, field.isVisible() ? (field.isPrimary() ? R.style.TextAppearance_OpenTracks_PrimaryValue : R.style.TextAppearance_OpenTracks_SecondaryValue) : R.style.TextAppearance_OpenTracks_HiddenValue);
viewHolder.statusIcon.setVisibility(field.isVisible() ? View.GONE : View.VISIBLE);
viewHolder.statusIcon.setImageDrawable(AppCompatResources.getDrawable(context, R.drawable.ic_baseline_visibility_off_24));
viewHolder.moveIcon.setVisibility(field.isVisible() ? View.VISIBLE : View.GONE);
@@ -5,7 +5,6 @@ import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.TextView;
@@ -33,6 +32,7 @@ public class IntervalsFragment extends Fragment {
private static final String FROM_TOP_TO_BOTTOM_KEY = "fromTopToBottom";
private static final String TRACK_ID_KEY = "trackId";
private static final String SELECTED_INTERVAL_KEY = "selectedIntervalKey";
private IntervalStatisticsModel viewModel;
protected IntervalStatisticsAdapter.StackMode stackModeListView;
@@ -41,7 +41,7 @@ public class IntervalsFragment extends Fragment {
private Track.Id trackId;
private boolean metricUnits;
private IntervalStatisticsAdapter adapter;
private ArrayAdapter<IntervalStatisticsModel.IntervalOption> spinnerAdapter;
private ArrayAdapter<IntervalStatisticsModel.IntervalOption> intervalsAdapter;
private boolean isReportSpeed;
@@ -50,8 +50,8 @@ public class IntervalsFragment extends Fragment {
protected final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
if (PreferencesUtils.isKey(R.string.stats_units_key, key) || PreferencesUtils.isKey(R.string.stats_rate_key, key)) {
updateIntervals(PreferencesUtils.isMetricUnits(), selectedInterval);
if (spinnerAdapter != null) {
spinnerAdapter.notifyDataSetChanged();
if (intervalsAdapter != null) {
intervalsAdapter.notifyDataSetChanged();
}
}
};
@@ -76,6 +76,15 @@ public class IntervalsFragment extends Fragment {
super.onCreate(savedInstanceState);
stackModeListView = getArguments().getBoolean(FROM_TOP_TO_BOTTOM_KEY, true) ? IntervalStatisticsAdapter.StackMode.STACK_FROM_TOP : IntervalStatisticsAdapter.StackMode.STACK_FROM_BOTTOM;
trackId = getArguments().getParcelable(TRACK_ID_KEY);
if (savedInstanceState != null) {
selectedInterval = (IntervalStatisticsModel.IntervalOption) savedInstanceState.getSerializable(SELECTED_INTERVAL_KEY);
}
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable(SELECTED_INTERVAL_KEY, selectedInterval);
}
@Override
@@ -93,7 +102,7 @@ public class IntervalsFragment extends Fragment {
// TODO handle empty view: before we did viewBinding.intervalList.setEmptyView(viewBinding.intervalListEmptyView);
viewBinding.intervalList.setAdapter(adapter);
spinnerAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, IntervalStatisticsModel.IntervalOption.values()) {
intervalsAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, IntervalStatisticsModel.IntervalOption.values()) {
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
@@ -112,17 +121,13 @@ public class IntervalsFragment extends Fragment {
}
};
viewBinding.spinnerIntervals.setAdapter(spinnerAdapter);
viewBinding.spinnerIntervals.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
updateIntervals(metricUnits, IntervalStatisticsModel.IntervalOption.values()[i]);
}
viewBinding.intervalsDropdown.setAdapter(intervalsAdapter);
viewBinding.intervalsDropdown.setOnItemClickListener((parent, view1, position, id) -> updateIntervals(metricUnits, IntervalStatisticsModel.IntervalOption.values()[position]));
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
viewBinding.intervalsDropdown.setText(
getContext().getString(R.string.value_integer_kilometer, Integer.parseInt(selectedInterval != null ? selectedInterval.toString() : IntervalStatisticsModel.IntervalOption.values()[0].toString())),
false
);
}
@Override
@@ -173,6 +178,7 @@ public class IntervalsFragment extends Fragment {
if (viewModel == null) {
return;
}
viewBinding.intervalRate.setText(isReportSpeed ? getString(R.string.stats_speed) : getString(R.string.stats_pace));
LiveData<List<IntervalStatistics.Interval>> liveData = viewModel.getIntervalStats(trackId, metricUnits, selectedInterval);
liveData.observe(getActivity(), intervalList -> adapter.swapData(intervalList, metricUnits, isReportSpeed));
@@ -0,0 +1,35 @@
package de.dennisguse.opentracks.ui.util;
import android.content.Context;
import android.widget.ArrayAdapter;
import android.widget.Filter;
import androidx.annotation.NonNull;
// https://github.com/material-components/material-components-android/issues/1464
public class ArrayAdapterFilterDisabled<T> extends ArrayAdapter<T> {
public ArrayAdapterFilterDisabled(@NonNull Context context, int resource, @NonNull T[] objects) {
super(context, resource, objects);
}
@NonNull
@Override
public Filter getFilter() {
return new NeverFilter();
}
private class NeverFilter extends Filter {
protected FilterResults performFiltering(CharSequence prefix) {
return new FilterResults();
}
protected void publishResults(CharSequence constraint, FilterResults results) {
if (ArrayAdapterFilterDisabled.this.getCount() > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
}
@@ -0,0 +1,22 @@
package de.dennisguse.opentracks.ui.util;
import android.content.Context;
import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.material.textfield.MaterialAutoCompleteTextView;
// https://github.com/material-components/material-components-android/issues/1464
public class AutoCompleteTextViewForDropdown extends MaterialAutoCompleteTextView {
public AutoCompleteTextViewForDropdown(@NonNull final Context context, @Nullable final AttributeSet attributeSet) {
super(context, attributeSet);
}
@Override
public boolean getFreezesText() {
return false;
}
}
@@ -17,11 +17,9 @@
package de.dennisguse.opentracks.ui.util;
import android.content.Context;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.time.OffsetDateTime;
@@ -77,11 +75,9 @@ public class ListItemUtils {
TextView timeDistanceTextView = view.findViewById(R.id.list_item_time_distance);
String timeDistanceText;
if (isRecording) {
timeDistanceTextView.setTextColor(context.getResources().getColor(isPaused ? android.R.color.white : R.color.recording_text));
timeDistanceText = context.getString(isPaused ? R.string.generic_paused : R.string.generic_recording);
} else {
// Match list_item_time_distance in list_item.xml
timeDistanceTextView.setTextAppearance(context, R.style.TextSmall);
timeDistanceText = getTimeDistance(totalTime, totalDistance);
if (markerCount > 0) {
timeDistanceText += " \u2027";
@@ -132,10 +128,6 @@ public class ListItemUtils {
timeDistanceTextView.setSingleLine(true);
setTextView(context, categoryDescriptionTextView, categoryDescription, hasPhoto);
}
// Adjust iconImageView layout gravity
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) iconImageView.getLayoutParams();
params.gravity = timeDistanceTextView.getVisibility() == View.GONE && markerCountIcon.getVisibility() == View.GONE ? Gravity.TOP : Gravity.CENTER_VERTICAL;
}
/**
@@ -42,14 +42,14 @@ public class ThemeUtils {
}
public static int getFontSizeSmallInPx(Context context) {
TypedArray typedArray = context.obtainStyledAttributes(R.style.TextSmall, new int[]{android.R.attr.textSize});
TypedArray typedArray = context.obtainStyledAttributes(R.style.TextAppearance_MaterialComponents_Body2, new int[]{android.R.attr.textSize});
int fontSize = typedArray.getDimensionPixelSize(0, 12);
typedArray.recycle();
return fontSize;
}
public static int getFontSizeMediumInPx(Context context) {
TypedArray typedArray = context.obtainStyledAttributes(R.style.TextMedium, new int[]{android.R.attr.textSize});
TypedArray typedArray = context.obtainStyledAttributes(R.style.TextAppearance_MaterialComponents_Body1, new int[]{android.R.attr.textSize});
int fontSize = typedArray.getDimensionPixelSize(0, 15);
typedArray.recycle();
return fontSize;