From 65f42dd3e58f34bd2cb8acdced50ddbc81035b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n?= Date: Mon, 24 Aug 2020 17:24:22 +0200 Subject: [PATCH] Interval Stats: improve code and fixed some UI issues. --- .../viewmodels/IntervalStatisticsTest.java | 18 +++--- .../opentracks/TrackRecordingActivity.java | 4 +- .../adapters/IntervalStatisticsAdapter.java | 13 +++-- .../fragments/IntervalsRecordingFragment.java | 36 +++++------- .../fragments/StatisticsRecordedFragment.java | 7 +-- .../StatisticsRecordingFragment.java | 8 ++- .../opentracks/util/UnitConversions.java | 6 +- .../viewmodels/IntervalStatistics.java | 55 ++++++++++-------- .../viewmodels/IntervalStatisticsModel.java | 26 --------- .../opentracks/views/IntervalListView.java | 58 +++++++++++++------ .../views/IntervalReverseListView.java | 31 ---------- src/main/res/layout/interval_list_view.xml | 3 +- 12 files changed, 118 insertions(+), 147 deletions(-) delete mode 100644 src/main/java/de/dennisguse/opentracks/views/IntervalReverseListView.java diff --git a/src/androidTest/java/de/dennisguse/opentracks/viewmodels/IntervalStatisticsTest.java b/src/androidTest/java/de/dennisguse/opentracks/viewmodels/IntervalStatisticsTest.java index 3fd020b15..44edae733 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/viewmodels/IntervalStatisticsTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/viewmodels/IntervalStatisticsTest.java @@ -23,9 +23,7 @@ public class IntervalStatisticsTest { private List buildTrackPoints(int numberOfTrackPoints) { Pair pair = TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), numberOfTrackPoints); - List trackPoints = Arrays.asList(pair.second); - - return trackPoints; + return Arrays.asList(pair.second); } private TrackStatistics buildTrackStatistics(List trackPoints) { @@ -40,10 +38,10 @@ public class IntervalStatisticsTest { * Tests that build method compute the distance correctly comparing the result with TrackStatisticsUpdater result. */ @Test - public void testBuild_1() { + public void testBuild_1() { // With 50 points and interval distance of 1000m. - // given + // given List trackPoints = buildTrackPoints(50); TrackStatistics trackStatistics = buildTrackStatistics(trackPoints); float distanceInterval = 1000f; @@ -127,8 +125,8 @@ public class IntervalStatisticsTest { double totalDistance = 0d; long totalTime = 0L; for (IntervalStatistics.Interval i : intervalList) { - totalDistance += i.getDistance(); - totalTime += ((i.getDistance() / i.getSpeed()) * UnitConversions.S_TO_MS); + totalDistance += i.getDistance_m(); + totalTime += ((i.getDistance_m() / i.getSpeed_ms()) * UnitConversions.S_TO_MS); } // then @@ -136,9 +134,9 @@ public class IntervalStatisticsTest { Assert.assertEquals(trackStatistics.getTotalTime() * UnitConversions.MS_TO_S, totalTime * UnitConversions.MS_TO_S, 0.1); Assert.assertEquals(intervalList.size(), (int) Math.ceil(trackStatistics.getTotalDistance() / distanceInterval)); for (int i = 0; i < intervalList.size() - 1; i++) { - Assert.assertEquals(intervalList.get(i).getDistance(), distanceInterval, 0.001); - totalDistance -= intervalList.get(i).getDistance(); + Assert.assertEquals(intervalList.get(i).getDistance_m(), distanceInterval, 0.001); + totalDistance -= intervalList.get(i).getDistance_m(); } - Assert.assertEquals(intervalList.get(intervalList.size() - 1).getDistance(), totalDistance, 0.01); + Assert.assertEquals(intervalList.get(intervalList.size() - 1).getDistance_m(), totalDistance, 0.01); } } \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java b/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java index 30f718653..cd5dbf28b 100644 --- a/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java +++ b/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java @@ -381,9 +381,9 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc public Fragment getItem(int position) { switch (position) { case 0: - return new StatisticsRecordingFragment(); + return StatisticsRecordingFragment.newInstance(); case 1: - return new IntervalsRecordingFragment(); + return IntervalsRecordingFragment.newInstance(); case 2: return ChartFragment.newInstance(false); case 3: diff --git a/src/main/java/de/dennisguse/opentracks/adapters/IntervalStatisticsAdapter.java b/src/main/java/de/dennisguse/opentracks/adapters/IntervalStatisticsAdapter.java index 16c91483f..834a9a02e 100644 --- a/src/main/java/de/dennisguse/opentracks/adapters/IntervalStatisticsAdapter.java +++ b/src/main/java/de/dennisguse/opentracks/adapters/IntervalStatisticsAdapter.java @@ -21,7 +21,7 @@ import de.dennisguse.opentracks.viewmodels.IntervalStatistics; public class IntervalStatisticsAdapter extends ArrayAdapter { private boolean metricUnits; - private float accDistance = 0f; + private float sumDistance_m = 0f; public IntervalStatisticsAdapter(Context context, List intervalList) { super(context, R.layout.interval_stats_list_item, intervalList); @@ -48,11 +48,14 @@ public class IntervalStatisticsAdapter extends ArrayAdapter speedParts = StringUtils.getSpeedParts(getContext(), interval.getSpeed(), metricUnits, true); + sumDistance_m += interval.getDistance_m(); + viewHolder.distance.setText(StringUtils.formatDistance(getContext(), sumDistance_m, metricUnits)); + + Pair speedParts = StringUtils.getSpeedParts(getContext(), interval.getSpeed_ms(), metricUnits, true); viewHolder.speed.setText(speedParts.first + " " + speedParts.second); - viewHolder.pace.setText(StringUtils.getSpeedParts(getContext(), interval.getSpeed(), metricUnits, false).first); + + Pair paceParts = StringUtils.getSpeedParts(getContext(), interval.getSpeed_ms(), metricUnits, false); + viewHolder.pace.setText(paceParts.first + " " + paceParts.second); return intervalView; } diff --git a/src/main/java/de/dennisguse/opentracks/fragments/IntervalsRecordingFragment.java b/src/main/java/de/dennisguse/opentracks/fragments/IntervalsRecordingFragment.java index a6342e919..a52806630 100644 --- a/src/main/java/de/dennisguse/opentracks/fragments/IntervalsRecordingFragment.java +++ b/src/main/java/de/dennisguse/opentracks/fragments/IntervalsRecordingFragment.java @@ -20,7 +20,6 @@ import de.dennisguse.opentracks.util.UnitConversions; import de.dennisguse.opentracks.viewmodels.IntervalStatistics; import de.dennisguse.opentracks.viewmodels.IntervalStatisticsModel; import de.dennisguse.opentracks.views.IntervalListView; -import de.dennisguse.opentracks.views.IntervalReverseListView; /** * A fragment to display the intervals from recording track. @@ -29,29 +28,27 @@ public class IntervalsRecordingFragment extends Fragment implements IntervalList private static final String TAG = IntervalsRecordingFragment.class.getSimpleName(); + // Refreshing intervals stats it's not so demanding so 5 seconds is enough to balance performance and user experience. private static final long UI_UPDATE_INTERVAL = 5 * UnitConversions.ONE_SECOND_MS; private IntervalStatisticsModel viewModel; - private IntervalReverseListView intervalListView; + private IntervalListView.IntervalReverseListView intervalListView; - private class IntervalRunner implements Runnable { - private boolean stopped = false; + public static Fragment newInstance() { + return new IntervalsRecordingFragment(); + } + private final Runnable intervalRunner = new Runnable() { @Override public void run() { - if (!stopped) { + if (isResumed()) { updateIntervals(); intervalHandler.postDelayed(intervalRunner, UI_UPDATE_INTERVAL); } } + }; - public void stop() { - stopped = true; - } - } - - private Handler intervalHandler = null; - private IntervalRunner intervalRunner = null; + private Handler intervalHandler; @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -64,8 +61,9 @@ public class IntervalsRecordingFragment extends Fragment implements IntervalList intervalHandler = new Handler(); - intervalListView = new IntervalReverseListView(getActivity(), this); + intervalListView = new IntervalListView.IntervalReverseListView(getActivity(), this); intervalListView.setId(View.generateViewId()); + intervalListView.findViewById(R.id.interval_title_label).setVisibility(View.GONE); LinearLayout linearLayout = view.findViewById(R.id.root_view); linearLayout.removeAllViews(); linearLayout.addView(intervalListView); @@ -76,19 +74,13 @@ public class IntervalsRecordingFragment extends Fragment implements IntervalList @Override public void onResume() { super.onResume(); - if (intervalRunner == null) { - intervalRunner = new IntervalRunner(); - intervalRunner.run(); - } + intervalHandler.post(intervalRunner); } @Override public void onPause() { super.onPause(); - if (intervalRunner != null) { - intervalRunner.stop(); - intervalRunner = null; - } + intervalHandler.removeCallbacks(intervalRunner); } @Override @@ -106,7 +98,7 @@ public class IntervalsRecordingFragment extends Fragment implements IntervalList * @param interval intervals will split in this interval if not null. If it's null then view model will use the default one. */ private void updateIntervals(@Nullable IntervalStatisticsModel.IntervalOption interval) { - if (viewModel == null | intervalListView == null) { + if (viewModel == null || intervalListView == null) { return; } diff --git a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java index 5a1d58374..bdc3a8077 100644 --- a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java +++ b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java @@ -54,12 +54,12 @@ public class StatisticsRecordedFragment extends Fragment implements IntervalList private static final String TRACK_ID_KEY = "trackId"; - private TrackStatistics trackStatistics = null; + private TrackStatistics trackStatistics; private String category = ""; private Track track; - private IntervalStatisticsModel viewModel = null; - private IntervalListView intervalListView = null; + private IntervalStatisticsModel viewModel; + private IntervalListView intervalListView; private 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)) { @@ -299,7 +299,6 @@ public class StatisticsRecordedFragment extends Fragment implements IntervalList speedMovingUnit.setText(parts.second); } - // Set intervals. addIntervals(); } diff --git a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java index 2573429f8..c9b4e4cbd 100644 --- a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java +++ b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java @@ -55,13 +55,17 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi private TrackRecordingServiceConnection trackRecordingServiceConnection = new TrackRecordingServiceConnection(); - private TrackPoint lastTrackPoint = null; - private TrackStatistics lastTrackStatistics = null; + private TrackPoint lastTrackPoint; + private TrackStatistics lastTrackStatistics; private String category = ""; @Deprecated //TODO This should be handled somewhere else; not in the UI. private int recordingGpsAccuracy; + public static Fragment newInstance() { + return new StatisticsRecordingFragment(); + } + private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() { @Override public void onSharedPreferenceChanged(SharedPreferences preferences, String key) { diff --git a/src/main/java/de/dennisguse/opentracks/util/UnitConversions.java b/src/main/java/de/dennisguse/opentracks/util/UnitConversions.java index aefdf51ea..9b53bfde3 100644 --- a/src/main/java/de/dennisguse/opentracks/util/UnitConversions.java +++ b/src/main/java/de/dennisguse/opentracks/util/UnitConversions.java @@ -51,8 +51,6 @@ public class UnitConversions { public static final double MM_TO_M = 0.001; // Distance - // multiplication factor to convert miles to meters - public static final double MI_TO_M = 1609.344; // multiplication factor to convert miles to feet public static final double MI_TO_FT = 5280.0; // multiplication factor to convert feet to miles @@ -67,6 +65,10 @@ public class UnitConversions { public static final double M_TO_FT = M_TO_MI * MI_TO_FT; // multiplication factor to convert meters per second to kilometers per hour public static final double MPS_TO_KMH = M_TO_KM / (S_TO_MIN * MIN_TO_HR); + // multiplication factor to convert miles to km + public static final double MI_TO_KM = 1 / KM_TO_MI; + // multiplication factor to convert miles to m + public static final double MI_TO_M = MI_TO_KM * KM_TO_M; private UnitConversions() { } diff --git a/src/main/java/de/dennisguse/opentracks/viewmodels/IntervalStatistics.java b/src/main/java/de/dennisguse/opentracks/viewmodels/IntervalStatistics.java index 6ba4f0f83..8ce33e02e 100644 --- a/src/main/java/de/dennisguse/opentracks/viewmodels/IntervalStatistics.java +++ b/src/main/java/de/dennisguse/opentracks/viewmodels/IntervalStatistics.java @@ -8,10 +8,10 @@ import de.dennisguse.opentracks.util.LocationUtils; import de.dennisguse.opentracks.util.UnitConversions; public class IntervalStatistics { - List intervalList; + List intervalList = new ArrayList<>(); public void build(List trackPointList, float distanceInterval) { - intervalList = new ArrayList<>(); + intervalList.clear(); if (trackPointList == null || trackPointList.size() == 0) { return; @@ -23,27 +23,22 @@ public class IntervalStatistics { TrackPoint trackPoint = trackPointList.get(i); if (LocationUtils.isValidLocation(trackPoint.getLocation()) && LocationUtils.isValidLocation(prevTrackPoint.getLocation())) { - interval.distance += prevTrackPoint.distanceTo(trackPoint); - interval.time += trackPoint.getTime() - prevTrackPoint.getTime(); + interval.distance_m += prevTrackPoint.distanceTo(trackPoint); + interval.time_ms += trackPoint.getTime() - prevTrackPoint.getTime(); - if (interval.distance >= distanceInterval) { - float adjustFactor = distanceInterval / interval.distance; + if (interval.distance_m >= distanceInterval) { + float adjustFactor = distanceInterval / interval.distance_m; Interval adjustedInterval = new Interval(interval); - adjustedInterval.distance *= adjustFactor; - adjustedInterval.time *= adjustFactor; + adjustedInterval.adjust(adjustFactor); intervalList.add(adjustedInterval); - Interval newInterval = new Interval(); - newInterval.distance = interval.distance - adjustedInterval.distance; - newInterval.time = interval.time - adjustedInterval.time; - - interval = newInterval; + interval = new Interval(interval.distance_m - adjustedInterval.distance_m, interval.time_ms - adjustedInterval.time_ms); } } } - if (interval.distance > 1f) { + if (interval.distance_m > 1f) { intervalList.add(interval); } } @@ -53,28 +48,38 @@ public class IntervalStatistics { } public static class Interval { - private float distance = 0f; - private float time = 0f; - - public float getDistance() { - return distance; - } + private float distance_m = 0f; + private float time_ms = 0f; public Interval() {} + public Interval(float distance_m, float time_ms) { + this.distance_m = distance_m; + this.time_ms = time_ms; + } + public Interval(Interval i) { - distance = i.distance; - time = i.time; + distance_m = i.distance_m; + time_ms = i.time_ms; + } + + public float getDistance_m() { + return distance_m; + } + + public void adjust(float adjustFactor) { + distance_m *= adjustFactor; + time_ms *= adjustFactor; } /** * @return speed of the interval in m/s. */ - public float getSpeed() { - if (distance == 0f) { + public float getSpeed_ms() { + if (distance_m == 0f) { return 0f; } - return distance / (float) (time * UnitConversions.MS_TO_S); + return distance_m / (float) (time_ms * UnitConversions.MS_TO_S); } } } diff --git a/src/main/java/de/dennisguse/opentracks/viewmodels/IntervalStatisticsModel.java b/src/main/java/de/dennisguse/opentracks/viewmodels/IntervalStatisticsModel.java index a4d66e550..9021ac0a3 100644 --- a/src/main/java/de/dennisguse/opentracks/viewmodels/IntervalStatisticsModel.java +++ b/src/main/java/de/dennisguse/opentracks/viewmodels/IntervalStatisticsModel.java @@ -83,34 +83,8 @@ public class IntervalStatisticsModel extends AndroidViewModel { this.value = value; } - /** - * @param pos position of the interval option. - * @return the interval option that is in the position pos. - */ - public static IntervalOption getIntervalOption(int pos) { - if (values().length > pos) { - return values()[pos]; - } else { - return OPTION_1; - } - } - public int getValue() { return value; } - - /** - * @return a string array with all options. - */ - public static String[] getAllValues() { - IntervalOption[] options = values(); - String[] values = new String[options.length]; - - for (int i = 0; i < options.length; i++) { - values[i] = String.valueOf(options[i].getValue()); - } - - return values; - } } } diff --git a/src/main/java/de/dennisguse/opentracks/views/IntervalListView.java b/src/main/java/de/dennisguse/opentracks/views/IntervalListView.java index 8073eb1bf..dbff2c7bf 100644 --- a/src/main/java/de/dennisguse/opentracks/views/IntervalListView.java +++ b/src/main/java/de/dennisguse/opentracks/views/IntervalListView.java @@ -9,6 +9,7 @@ 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; @@ -28,37 +29,35 @@ public class IntervalListView extends LinearLayout { protected Spinner spinnerIntervals; protected TextView spinnerIntervalsUnit; - protected Context context; 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(context) ? context.getString(R.string.unit_kilometer) : context.getString(R.string.unit_mile)); + spinnerIntervalsUnit.setText(PreferencesUtils.isMetricUnits(getContext()) ? getContext().getString(R.string.unit_kilometer) : getContext().getString(R.string.unit_mile)); } } }; public IntervalListView(Context context, IntervalListListener listener) { super(context); - this.context = context; this.listener = listener; PreferencesUtils.register(getContext(), sharedPreferenceChangeListener); - init(); - } - protected void init() { inflate(getContext(), R.layout.interval_list_view, this); linearLayoutIntervals = findViewById(R.id.interval_list); spinnerIntervals = findViewById(R.id.spinner_intervals); - spinnerIntervals.setAdapter(new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, IntervalStatisticsModel.IntervalOption.getAllValues())); + + 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(context) ? context.getString(R.string.unit_kilometer) : context.getString(R.string.unit_mile)); + 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.getIntervalOption(i); + IntervalStatisticsModel.IntervalOption interval = IntervalStatisticsModel.IntervalOption.values()[i]; listener.intervalChanged(interval); } @@ -75,22 +74,47 @@ public class IntervalListView extends LinearLayout { linearLayoutIntervals = null; spinnerIntervals = null; spinnerIntervalsUnit = null; - context = null; listener = null; } public void display(List intervalList) { - if (intervalList != null) { - 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); - } + 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); } + + /** + * 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/java/de/dennisguse/opentracks/views/IntervalReverseListView.java b/src/main/java/de/dennisguse/opentracks/views/IntervalReverseListView.java deleted file mode 100644 index 7c22966cb..000000000 --- a/src/main/java/de/dennisguse/opentracks/views/IntervalReverseListView.java +++ /dev/null @@ -1,31 +0,0 @@ -package de.dennisguse.opentracks.views; - -import android.content.Context; -import android.view.View; - -import java.util.List; - -import de.dennisguse.opentracks.adapters.IntervalStatisticsAdapter; -import de.dennisguse.opentracks.viewmodels.IntervalStatistics; - -/** - * 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 class IntervalReverseListView extends IntervalListView { - - public IntervalReverseListView(Context context, IntervalListListener listener) { - super(context, listener); - } - - public void display(List intervalList) { - if (intervalList != null) { - 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/interval_list_view.xml b/src/main/res/layout/interval_list_view.xml index 28a0f16af..f0e9a5f0b 100644 --- a/src/main/res/layout/interval_list_view.xml +++ b/src/main/res/layout/interval_list_view.xml @@ -6,6 +6,7 @@ android:orientation="vertical"> + android:gravity="center" />