From 684762db58449de17f594aa77bc94d203d404446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n?= Date: Wed, 9 Sep 2020 11:54:51 +0200 Subject: [PATCH] Intervals refactoring: ListView instead of LinearLayout with a list of views. --- .../adapters/IntervalStatisticsAdapter.java | 3 +- .../fragments/IntervalsRecordedFragment.java | 92 ++++++++----- .../fragments/IntervalsRecordingFragment.java | 92 ++++++++----- .../opentracks/views/IntervalListView.java | 122 ------------------ src/main/res/layout/about.xml | 2 +- src/main/res/layout/interval_list_view.xml | 24 +++- src/main/res/layout/intervals_recording.xml | 14 -- src/main/res/values/strings.xml | 2 + 8 files changed, 148 insertions(+), 203 deletions(-) delete mode 100644 src/main/java/de/dennisguse/opentracks/views/IntervalListView.java delete mode 100644 src/main/res/layout/intervals_recording.xml diff --git a/src/main/java/de/dennisguse/opentracks/adapters/IntervalStatisticsAdapter.java b/src/main/java/de/dennisguse/opentracks/adapters/IntervalStatisticsAdapter.java index 834a9a02e..596dbe959 100644 --- a/src/main/java/de/dennisguse/opentracks/adapters/IntervalStatisticsAdapter.java +++ b/src/main/java/de/dennisguse/opentracks/adapters/IntervalStatisticsAdapter.java @@ -1,6 +1,7 @@ package de.dennisguse.opentracks.adapters; import android.content.Context; +import android.util.Log; import android.util.Pair; import android.view.LayoutInflater; import android.view.View; @@ -48,7 +49,7 @@ public class IntervalStatisticsAdapter extends ArrayAdapter speedParts = StringUtils.getSpeedParts(getContext(), interval.getSpeed_ms(), metricUnits, true); diff --git a/src/main/java/de/dennisguse/opentracks/fragments/IntervalsRecordedFragment.java b/src/main/java/de/dennisguse/opentracks/fragments/IntervalsRecordedFragment.java index 6323415b6..cec53df34 100644 --- a/src/main/java/de/dennisguse/opentracks/fragments/IntervalsRecordedFragment.java +++ b/src/main/java/de/dennisguse/opentracks/fragments/IntervalsRecordedFragment.java @@ -1,10 +1,15 @@ package de.dennisguse.opentracks.fragments; +import android.content.SharedPreferences; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.LinearLayout; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.Spinner; +import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -12,26 +17,43 @@ import androidx.fragment.app.Fragment; import androidx.lifecycle.LiveData; import androidx.lifecycle.ViewModelProvider; +import java.util.Arrays; + import de.dennisguse.opentracks.R; +import de.dennisguse.opentracks.adapters.IntervalStatisticsAdapter; import de.dennisguse.opentracks.content.data.Track; +import de.dennisguse.opentracks.util.PreferencesUtils; import de.dennisguse.opentracks.viewmodels.IntervalStatistics; import de.dennisguse.opentracks.viewmodels.IntervalStatisticsModel; -import de.dennisguse.opentracks.views.IntervalListView; /** * A fragment to display the intervals from recorded track. */ -public class IntervalsRecordedFragment extends Fragment implements IntervalListView.IntervalListListener { +public class IntervalsRecordedFragment extends Fragment { private static final String TAG = IntervalsRecordedFragment.class.getSimpleName(); private static final String TRACK_ID_KEY = "trackId"; private IntervalStatisticsModel viewModel; - private IntervalListView intervalListView; + private ListView intervalListView; + private IntervalStatisticsModel.IntervalOption selectedInterval; + private IntervalStatisticsAdapter adapter; + + protected Spinner spinnerIntervals; + protected TextView spinnerIntervalsUnit; private Track.Id trackId; + protected final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (preferences, key) -> { + if (PreferencesUtils.isKey(getContext(), R.string.stats_units_key, key) || PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key)) { + if (spinnerIntervalsUnit != null) { + spinnerIntervalsUnit.setText(PreferencesUtils.isMetricUnits(getContext()) ? getContext().getString(R.string.unit_kilometer) : getContext().getString(R.string.unit_mile)); + intervalChanged(); + } + } + }; + public static Fragment newInstance(Track.Id trackId) { Bundle bundle = new Bundle(); bundle.putParcelable(TRACK_ID_KEY, trackId); @@ -43,63 +65,73 @@ public class IntervalsRecordedFragment extends Fragment implements IntervalListV @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - return inflater.inflate(R.layout.intervals_recording, container, false); + return inflater.inflate(R.layout.interval_list_view, container, false); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); + PreferencesUtils.register(getContext(), sharedPreferenceChangeListener); + trackId = getArguments().getParcelable(TRACK_ID_KEY); - intervalListView = new IntervalListView(getActivity(), this); - intervalListView.setId(View.generateViewId()); - LinearLayout linearLayout = view.findViewById(R.id.root_view); - linearLayout.removeAllViews(); - linearLayout.addView(intervalListView); + intervalListView = view.findViewById(R.id.interval_list); + intervalListView.setEmptyView(view.findViewById(R.id.interval_list_empty_view)); viewModel = new ViewModelProvider(this).get(IntervalStatisticsModel.class); - intervalChanged(null); + spinnerIntervals = view.findViewById(R.id.spinner_intervals); + + int[] intValues = Arrays.stream(IntervalStatisticsModel.IntervalOption.values()).mapToInt(i -> i.getValue()).toArray(); + + spinnerIntervals.setAdapter(new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, Arrays.stream(intValues).mapToObj(String::valueOf).toArray(String[]::new))); + spinnerIntervalsUnit = view.findViewById(R.id.spinner_intervals_unit); + spinnerIntervalsUnit.setText(PreferencesUtils.isMetricUnits(getContext()) ? getContext().getString(R.string.unit_kilometer) : getContext().getString(R.string.unit_mile)); + + spinnerIntervals.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView adapterView, View view, int i, long l) { + selectedInterval = IntervalStatisticsModel.IntervalOption.values()[i]; + intervalChanged(); + } + + @Override + public void onNothingSelected(AdapterView adapterView) { + + } + }); + + intervalChanged(); } @Override public void onDestroy() { super.onDestroy(); - intervalListView.destroy(); + PreferencesUtils.unregister(getContext(), sharedPreferenceChangeListener); + intervalListView = null; + adapter = null; + spinnerIntervals = null; + spinnerIntervalsUnit = null; viewModel = null; } /** * Update intervals through {@link IntervalStatisticsModel} view model. - * - * @param interval intervals will split in this interval if not null. If it's null then view model will use the default one. */ - @Override - public void intervalChanged(@Nullable IntervalStatisticsModel.IntervalOption interval) { + public void intervalChanged() { if (viewModel == null || intervalListView == null) { return; } - LiveData liveData = viewModel.getIntervalStats(trackId, interval); + LiveData liveData = viewModel.getIntervalStats(trackId, selectedInterval); liveData.observe(getActivity(), intervalStatistics -> { if (intervalStatistics != null) { - intervalListView.display(intervalStatistics.getIntervalList()); + adapter = new IntervalStatisticsAdapter(getContext(), intervalStatistics.getIntervalList()); + intervalListView.setAdapter(adapter); } }); } - - @Override - public void unitChanged() { - if (viewModel != null) { - LiveData liveData = viewModel.getIntervalStats(trackId, null); - liveData.observe(getActivity(), intervalStatistics -> { - if (intervalStatistics != null) { - intervalListView.display(intervalStatistics.getIntervalList()); - } - }); - } - } } \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/fragments/IntervalsRecordingFragment.java b/src/main/java/de/dennisguse/opentracks/fragments/IntervalsRecordingFragment.java index 8ef36dbae..e32c89e50 100644 --- a/src/main/java/de/dennisguse/opentracks/fragments/IntervalsRecordingFragment.java +++ b/src/main/java/de/dennisguse/opentracks/fragments/IntervalsRecordingFragment.java @@ -1,11 +1,16 @@ package de.dennisguse.opentracks.fragments; +import android.content.SharedPreferences; import android.os.Bundle; import android.os.Handler; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.LinearLayout; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.Spinner; +import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -13,18 +18,21 @@ import androidx.fragment.app.Fragment; import androidx.lifecycle.LiveData; import androidx.lifecycle.ViewModelProvider; +import java.util.Arrays; + import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.TrackRecordingActivity; +import de.dennisguse.opentracks.adapters.IntervalStatisticsAdapter; import de.dennisguse.opentracks.content.data.Track; +import de.dennisguse.opentracks.util.PreferencesUtils; import de.dennisguse.opentracks.util.UnitConversions; import de.dennisguse.opentracks.viewmodels.IntervalStatistics; import de.dennisguse.opentracks.viewmodels.IntervalStatisticsModel; -import de.dennisguse.opentracks.views.IntervalListView; /** * A fragment to display the intervals from recording track. */ -public class IntervalsRecordingFragment extends Fragment implements IntervalListView.IntervalListListener, TrackRecordingActivity.OnTrackIdListener { +public class IntervalsRecordingFragment extends Fragment implements TrackRecordingActivity.OnTrackIdListener { private static final String TAG = IntervalsRecordingFragment.class.getSimpleName(); @@ -34,11 +42,24 @@ public class IntervalsRecordingFragment extends Fragment implements IntervalList private static final long UI_UPDATE_INTERVAL = 5 * UnitConversions.ONE_SECOND_MS; private IntervalStatisticsModel viewModel; - private IntervalListView.IntervalReverseListView intervalListView; + private ListView intervalListView; private IntervalStatisticsModel.IntervalOption selectedInterval; + private IntervalStatisticsAdapter adapter; + + protected Spinner spinnerIntervals; + protected TextView spinnerIntervalsUnit; private Track.Id trackId; + protected final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (preferences, key) -> { + if (PreferencesUtils.isKey(getContext(), R.string.stats_units_key, key) || PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key)) { + if (spinnerIntervalsUnit != null) { + spinnerIntervalsUnit.setText(PreferencesUtils.isMetricUnits(getContext()) ? getContext().getString(R.string.unit_kilometer) : getContext().getString(R.string.unit_mile)); + intervalChanged(); + } + } + }; + public static Fragment newInstance(Track.Id trackId) { Bundle bundle = new Bundle(); bundle.putParcelable(TRACK_ID_KEY, trackId); @@ -62,25 +83,45 @@ public class IntervalsRecordingFragment extends Fragment implements IntervalList @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - return inflater.inflate(R.layout.intervals_recording, container, false); + return inflater.inflate(R.layout.interval_list_view, container, false); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); + PreferencesUtils.register(getContext(), sharedPreferenceChangeListener); + trackId = getArguments().getParcelable(TRACK_ID_KEY); ((TrackRecordingActivity) getActivity()).setTrackIdListener(this); intervalHandler = new Handler(); - intervalListView = new IntervalListView.IntervalReverseListView(getActivity(), this); - intervalListView.setId(View.generateViewId()); - LinearLayout linearLayout = view.findViewById(R.id.root_view); - linearLayout.removeAllViews(); - linearLayout.addView(intervalListView); + intervalListView = view.findViewById(R.id.interval_list); + intervalListView.setEmptyView(view.findViewById(R.id.interval_list_empty_view)); viewModel = new ViewModelProvider(this).get(IntervalStatisticsModel.class); + + spinnerIntervals = view.findViewById(R.id.spinner_intervals); + + int[] intValues = Arrays.stream(IntervalStatisticsModel.IntervalOption.values()).mapToInt(i -> i.getValue()).toArray(); + + spinnerIntervals.setAdapter(new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, Arrays.stream(intValues).mapToObj(String::valueOf).toArray(String[]::new))); + spinnerIntervalsUnit = view.findViewById(R.id.spinner_intervals_unit); + spinnerIntervalsUnit.setText(PreferencesUtils.isMetricUnits(getContext()) ? getContext().getString(R.string.unit_kilometer) : getContext().getString(R.string.unit_mile)); + + spinnerIntervals.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView adapterView, View view, int i, long l) { + selectedInterval = IntervalStatisticsModel.IntervalOption.values()[i]; + intervalChanged(); + } + + @Override + public void onNothingSelected(AdapterView adapterView) { + + } + }); } @Override @@ -99,45 +140,34 @@ public class IntervalsRecordingFragment extends Fragment implements IntervalList public void onDestroy() { super.onDestroy(); - intervalListView.destroy(); + PreferencesUtils.unregister(getContext(), sharedPreferenceChangeListener); + intervalListView = null; + adapter = null; + spinnerIntervals = null; + spinnerIntervalsUnit = null; viewModel = null; } /** * Update intervals through {@link IntervalStatisticsModel} view model. - * - * @param interval intervals will split in this interval if not null. If it's null then view model will use the default one. */ - @Override - public void intervalChanged(@Nullable IntervalStatisticsModel.IntervalOption interval) { + public void intervalChanged() { if (viewModel == null || intervalListView == null) { return; } - selectedInterval = interval; - LiveData liveData = viewModel.getIntervalStats(trackId, interval); + LiveData liveData = viewModel.getIntervalStats(trackId, selectedInterval); liveData.observe(getActivity(), intervalStatistics -> { if (intervalStatistics != null) { - intervalListView.display(intervalStatistics.getIntervalList()); + adapter = new IntervalStatisticsAdapter(getContext(), intervalStatistics.getIntervalList()); + intervalListView.setAdapter(adapter); } }); } private void updateIntervals() { - intervalChanged(selectedInterval); - } - - @Override - public void unitChanged() { - if (viewModel != null) { - LiveData liveData = viewModel.getIntervalStats(trackId, selectedInterval); - liveData.observe(getActivity(), intervalStatistics -> { - if (intervalStatistics != null) { - intervalListView.display(intervalStatistics.getIntervalList()); - } - }); - } + intervalChanged(); } @Override diff --git a/src/main/java/de/dennisguse/opentracks/views/IntervalListView.java b/src/main/java/de/dennisguse/opentracks/views/IntervalListView.java deleted file mode 100644 index b02d4ede6..000000000 --- a/src/main/java/de/dennisguse/opentracks/views/IntervalListView.java +++ /dev/null @@ -1,122 +0,0 @@ -package de.dennisguse.opentracks.views; - -import android.content.Context; -import android.content.SharedPreferences; -import android.view.View; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.LinearLayout; -import android.widget.Spinner; -import android.widget.TextView; - -import java.util.Arrays; -import java.util.List; - -import de.dennisguse.opentracks.R; -import de.dennisguse.opentracks.adapters.IntervalStatisticsAdapter; -import de.dennisguse.opentracks.util.PreferencesUtils; -import de.dennisguse.opentracks.viewmodels.IntervalStatistics; -import de.dennisguse.opentracks.viewmodels.IntervalStatisticsModel; - -/** - * LinearLayout view used to build a list of intervals. - * See {@link IntervalStatisticsAdapter}. - */ -public class IntervalListView extends LinearLayout { - - protected IntervalStatisticsAdapter adapter; - protected LinearLayout linearLayoutIntervals; - protected Spinner spinnerIntervals; - protected TextView spinnerIntervalsUnit; - - protected IntervalListListener listener; - - protected final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (preferences, key) -> { - if (PreferencesUtils.isKey(getContext(), R.string.stats_units_key, key) || PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key)) { - if (spinnerIntervalsUnit != null) { - spinnerIntervalsUnit.setText(PreferencesUtils.isMetricUnits(getContext()) ? getContext().getString(R.string.unit_kilometer) : getContext().getString(R.string.unit_mile)); - listener.unitChanged(); - } - } - }; - - public IntervalListView(Context context, IntervalListListener listener) { - super(context); - this.listener = listener; - PreferencesUtils.register(getContext(), sharedPreferenceChangeListener); - - inflate(getContext(), R.layout.interval_list_view, this); - linearLayoutIntervals = findViewById(R.id.interval_list); - spinnerIntervals = findViewById(R.id.spinner_intervals); - - int[] intValues = Arrays.stream(IntervalStatisticsModel.IntervalOption.values()).mapToInt(i -> i.getValue()).toArray(); - - spinnerIntervals.setAdapter(new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, Arrays.stream(intValues).mapToObj(String::valueOf).toArray(String[]::new))); - spinnerIntervalsUnit = findViewById(R.id.spinner_intervals_unit); - spinnerIntervalsUnit.setText(PreferencesUtils.isMetricUnits(getContext()) ? getContext().getString(R.string.unit_kilometer) : getContext().getString(R.string.unit_mile)); - - spinnerIntervals.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView adapterView, View view, int i, long l) { - IntervalStatisticsModel.IntervalOption interval = IntervalStatisticsModel.IntervalOption.values()[i]; - listener.intervalChanged(interval); - } - - @Override - public void onNothingSelected(AdapterView adapterView) { - - } - }); - } - - public void destroy() { - PreferencesUtils.unregister(getContext(), sharedPreferenceChangeListener); - adapter = null; - linearLayoutIntervals = null; - spinnerIntervals = null; - spinnerIntervalsUnit = null; - listener = null; - } - - public void display(List intervalList) { - if (intervalList == null) { - return; - } - - adapter = new IntervalStatisticsAdapter(getContext(), intervalList); - linearLayoutIntervals.removeAllViews(); - for (int i = 0; i < adapter.getCount(); i++) { - View intervalView = adapter.getView(i, null, linearLayoutIntervals); - linearLayoutIntervals.addView(intervalView); - } - } - - public interface IntervalListListener { - void intervalChanged(IntervalStatisticsModel.IntervalOption interval); - void unitChanged(); - } - - /** - * LinearLayout view used to build a list of intervals in a reverse mode, the last one will appear in the first position on the LinearLayout. - * This class is an specialization of {@link IntervalListView} that display the views contained in the LinearLayout in a reverse mode. - */ - public static class IntervalReverseListView extends IntervalListView { - - public IntervalReverseListView(Context context, IntervalListListener listener) { - super(context, listener); - } - - public void display(List intervalList) { - if (intervalList == null) { - return; - } - - adapter = new IntervalStatisticsAdapter(getContext(), intervalList); - linearLayoutIntervals.removeAllViews(); - for (int i = 0; i < adapter.getCount(); i++) { - View intervalView = adapter.getView(i, null, linearLayoutIntervals); - linearLayoutIntervals.addView(intervalView, 0); - } - } - } -} diff --git a/src/main/res/layout/about.xml b/src/main/res/layout/about.xml index ae4f43365..1d0ceb53f 100644 --- a/src/main/res/layout/about.xml +++ b/src/main/res/layout/about.xml @@ -97,4 +97,4 @@ - \ No newline at end of file + diff --git a/src/main/res/layout/interval_list_view.xml b/src/main/res/layout/interval_list_view.xml index 57937abc8..df0b45af0 100644 --- a/src/main/res/layout/interval_list_view.xml +++ b/src/main/res/layout/interval_list_view.xml @@ -9,7 +9,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" - android:layout_marginBottom="8dp"> + android:layout_margin="8dp"> + android:layout_margin="8dp"> - + + + android:layout_margin="8dp"/> + + \ No newline at end of file diff --git a/src/main/res/layout/intervals_recording.xml b/src/main/res/layout/intervals_recording.xml deleted file mode 100644 index 93baa1b2e..000000000 --- a/src/main/res/layout/intervals_recording.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index c01cf9b1a..c781324fd 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -443,6 +443,8 @@ limitations under the License. Start recording your next adventure here Record your first track to see aggregated statistics + + There are not any interval yet %1$d tracks