forked from upstream-mirrors/OpenTracks
IntervalsFragment/IntervalStatisticsModel refactoring: better performance with AndroidViewModel (it doesn't recreate the model every track point done anymore).
Also, it doesn't jump anymore. Fixes #669.
This commit is contained in:
@@ -268,7 +268,7 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
|
|||||||
case 0:
|
case 0:
|
||||||
return StatisticsRecordedFragment.newInstance(trackId);
|
return StatisticsRecordedFragment.newInstance(trackId);
|
||||||
case 1:
|
case 1:
|
||||||
return IntervalsFragment.newInstance();
|
return IntervalsFragment.newInstance(true);
|
||||||
case 2:
|
case 2:
|
||||||
return ChartFragment.newInstance(false);
|
return ChartFragment.newInstance(false);
|
||||||
case 3:
|
case 3:
|
||||||
|
|||||||
@@ -369,7 +369,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
case 0:
|
case 0:
|
||||||
return StatisticsRecordingFragment.newInstance();
|
return StatisticsRecordingFragment.newInstance();
|
||||||
case 1:
|
case 1:
|
||||||
return IntervalsFragment.IntervalsRecordingFragment.newInstance();
|
return IntervalsFragment.newInstance(false);
|
||||||
case 2:
|
case 2:
|
||||||
return ChartFragment.newInstance(false);
|
return ChartFragment.newInstance(false);
|
||||||
case 3:
|
case 3:
|
||||||
|
|||||||
@@ -4,73 +4,84 @@ import android.content.Context;
|
|||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
|
||||||
import de.dennisguse.opentracks.util.StringUtils;
|
import de.dennisguse.opentracks.util.StringUtils;
|
||||||
import de.dennisguse.opentracks.viewmodels.IntervalStatistics;
|
import de.dennisguse.opentracks.viewmodels.IntervalStatistics;
|
||||||
|
|
||||||
public class IntervalStatisticsAdapter extends ArrayAdapter<IntervalStatistics.Interval> {
|
public class IntervalStatisticsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||||
|
|
||||||
|
private List<IntervalStatistics.Interval> intervalList;
|
||||||
|
private final Context context;
|
||||||
private final StackMode stackMode;
|
private final StackMode stackMode;
|
||||||
private final boolean metricUnits;
|
private boolean metricUnits;
|
||||||
private final String category;
|
private boolean isReportSpeed;
|
||||||
|
|
||||||
public IntervalStatisticsAdapter(Context context, List<IntervalStatistics.Interval> intervalList, String category, StackMode stackMode) {
|
public IntervalStatisticsAdapter(Context context, StackMode stackMode, boolean metricUnits, boolean isReportSpeed) {
|
||||||
super(context, R.layout.interval_stats_list_item, intervalList);
|
this.metricUnits = metricUnits;
|
||||||
metricUnits = PreferencesUtils.isMetricUnits(PreferencesUtils.getSharedPreferences(context), context);
|
this.context = context;
|
||||||
this.category = category;
|
|
||||||
this.stackMode = stackMode;
|
this.stackMode = stackMode;
|
||||||
|
this.isReportSpeed = isReportSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO Check preference handling! Should not be accessed in getView()
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public View getView(int position, @Nullable View intervalView, @NonNull ViewGroup parent) {
|
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
int actualPosition = stackMode == StackMode.STACK_FROM_TOP ? position : getCount() - 1 - position;
|
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.interval_stats_list_item, parent, false);
|
||||||
IntervalStatistics.Interval interval = getItem(actualPosition);
|
return new IntervalStatisticsAdapter.ViewHolder(view);
|
||||||
ViewHolder viewHolder;
|
}
|
||||||
|
|
||||||
if (intervalView == null) {
|
@Override
|
||||||
viewHolder = new ViewHolder();
|
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||||
|
int actualPosition = stackMode == StackMode.STACK_FROM_TOP ? position : getItemCount() - 1 - position;
|
||||||
intervalView = LayoutInflater.from(getContext()).inflate(R.layout.interval_stats_list_item, parent, false);
|
int nextPosition = actualPosition + 1;
|
||||||
|
boolean isLast = actualPosition == getItemCount() - 1;
|
||||||
viewHolder.distance = intervalView.findViewById(R.id.interval_item_distance);
|
IntervalStatisticsAdapter.ViewHolder viewHolder = (IntervalStatisticsAdapter.ViewHolder) holder;
|
||||||
viewHolder.rate = intervalView.findViewById(R.id.interval_item_rate);
|
IntervalStatistics.Interval interval = intervalList.get(actualPosition);
|
||||||
viewHolder.gain = intervalView.findViewById(R.id.interval_item_gain);
|
viewHolder.itemView.setTag(actualPosition);
|
||||||
viewHolder.loss = intervalView.findViewById(R.id.interval_item_loss);
|
|
||||||
|
|
||||||
intervalView.setTag(viewHolder);
|
|
||||||
} else {
|
|
||||||
viewHolder = (ViewHolder) intervalView.getTag();
|
|
||||||
}
|
|
||||||
|
|
||||||
float sumDistance_m;
|
float sumDistance_m;
|
||||||
if (actualPosition + 1 == getCount() && actualPosition > 0) {
|
if (isLast && actualPosition > 0) {
|
||||||
sumDistance_m = actualPosition * getItem(actualPosition - 1).getDistance_m() + interval.getDistance_m();
|
sumDistance_m = actualPosition * intervalList.get(actualPosition - 1).getDistance_m() + interval.getDistance_m();
|
||||||
} else {
|
} else {
|
||||||
sumDistance_m = (actualPosition + 1) * interval.getDistance_m();
|
sumDistance_m = nextPosition * interval.getDistance_m();
|
||||||
}
|
}
|
||||||
viewHolder.distance.setText(StringUtils.formatDistance(getContext(), sumDistance_m, metricUnits));
|
viewHolder.distance.setText(StringUtils.formatDistance(context, sumDistance_m, metricUnits));
|
||||||
|
|
||||||
if (PreferencesUtils.isReportSpeed(PreferencesUtils.getSharedPreferences(getContext()), getContext(), category)) {
|
viewHolder.rate.setText(StringUtils.formatSpeed(context, interval.getSpeed_ms(), metricUnits, isReportSpeed));
|
||||||
viewHolder.rate.setText(StringUtils.formatSpeed(getContext(), interval.getSpeed_ms(), metricUnits, true));
|
|
||||||
} else {
|
viewHolder.gain.setText(StringUtils.formatDistance(context, interval.getGain_m(), metricUnits));
|
||||||
viewHolder.rate.setText(StringUtils.formatSpeed(getContext(), interval.getSpeed_ms(), metricUnits, false));
|
viewHolder.loss.setText(StringUtils.formatDistance(context, interval.getLoss_m(), metricUnits));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
if (intervalList == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return intervalList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<IntervalStatistics.Interval> swapData(List<IntervalStatistics.Interval> data, boolean metricUnits, boolean isReportSpeed) {
|
||||||
|
if (intervalList == data && this.metricUnits == metricUnits && this.isReportSpeed == isReportSpeed) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
viewHolder.gain.setText(StringUtils.formatDistance(getContext(), interval.getGain_m(), metricUnits));
|
this.metricUnits = metricUnits;
|
||||||
viewHolder.loss.setText(StringUtils.formatDistance(getContext(), interval.getLoss_m(), metricUnits));
|
this.isReportSpeed = isReportSpeed;
|
||||||
|
intervalList = data;
|
||||||
|
|
||||||
return intervalView;
|
if (data != null) {
|
||||||
|
this.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,10 +92,18 @@ public class IntervalStatisticsAdapter extends ArrayAdapter<IntervalStatistics.I
|
|||||||
STACK_FROM_TOP
|
STACK_FROM_TOP
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ViewHolder {
|
private static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
private TextView distance;
|
TextView distance;
|
||||||
private TextView rate;
|
TextView rate;
|
||||||
private TextView gain;
|
TextView gain;
|
||||||
private TextView loss;
|
TextView loss;
|
||||||
|
|
||||||
|
public ViewHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
distance = itemView.findViewById(R.id.interval_item_distance);
|
||||||
|
rate = itemView.findViewById(R.id.interval_item_rate);
|
||||||
|
gain = itemView.findViewById(R.id.interval_item_gain);
|
||||||
|
loss = itemView.findViewById(R.id.interval_item_loss);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package de.dennisguse.opentracks.fragments;
|
|||||||
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -14,6 +13,11 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.TrackActivityDataHubInterface;
|
import de.dennisguse.opentracks.TrackActivityDataHubInterface;
|
||||||
@@ -25,7 +29,6 @@ import de.dennisguse.opentracks.content.data.Track;
|
|||||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
import de.dennisguse.opentracks.databinding.IntervalListViewBinding;
|
import de.dennisguse.opentracks.databinding.IntervalListViewBinding;
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||||
import de.dennisguse.opentracks.util.UnitConversions;
|
|
||||||
import de.dennisguse.opentracks.viewmodels.IntervalStatistics;
|
import de.dennisguse.opentracks.viewmodels.IntervalStatistics;
|
||||||
import de.dennisguse.opentracks.viewmodels.IntervalStatisticsModel;
|
import de.dennisguse.opentracks.viewmodels.IntervalStatisticsModel;
|
||||||
|
|
||||||
@@ -36,6 +39,8 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
|||||||
|
|
||||||
private static final String TAG = IntervalsFragment.class.getSimpleName();
|
private static final String TAG = IntervalsFragment.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final String FROM_TOP_TO_BOTTOM_KEY = "fromTopToBottom";
|
||||||
|
|
||||||
private IntervalStatisticsModel viewModel;
|
private IntervalStatisticsModel viewModel;
|
||||||
protected IntervalStatisticsAdapter.StackMode stackModeListView;
|
protected IntervalStatisticsAdapter.StackMode stackModeListView;
|
||||||
private IntervalStatisticsModel.IntervalOption selectedInterval;
|
private IntervalStatisticsModel.IntervalOption selectedInterval;
|
||||||
@@ -47,22 +52,38 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
|||||||
private SharedPreferences sharedPreferences;
|
private SharedPreferences sharedPreferences;
|
||||||
|
|
||||||
private TrackDataHub trackDataHub;
|
private TrackDataHub trackDataHub;
|
||||||
private String category;
|
private boolean isReportSpeed;
|
||||||
|
|
||||||
private IntervalListViewBinding viewBinding;
|
private IntervalListViewBinding viewBinding;
|
||||||
|
|
||||||
protected final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
|
protected final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
|
||||||
if (PreferencesUtils.isKey(getContext(), R.string.stats_units_key, key) || PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key)) {
|
if (PreferencesUtils.isKey(getContext(), R.string.stats_units_key, key) || PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key)) {
|
||||||
metricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, getContext());
|
metricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, getContext());
|
||||||
if (adapter != null) {
|
uploadIntervals();
|
||||||
adapter.notifyDataSetChanged();
|
if (spinnerAdapter != null) {
|
||||||
spinnerAdapter.notifyDataSetChanged();
|
spinnerAdapter.notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public static Fragment newInstance() {
|
/**
|
||||||
return new IntervalsFragment();
|
* Creates an instance of this class.
|
||||||
|
*
|
||||||
|
* @param fromTopToBottom If true then the intervals are shown from top to bottom (the first interval on top). Otherwise the intervals are shown from bottom to top.
|
||||||
|
* @return IntervalsFragment instance.
|
||||||
|
*/
|
||||||
|
public static Fragment newInstance(boolean fromTopToBottom) {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putBoolean(FROM_TOP_TO_BOTTOM_KEY, fromTopToBottom);
|
||||||
|
IntervalsFragment intervalsFragment = new IntervalsFragment();
|
||||||
|
intervalsFragment.setArguments(bundle);
|
||||||
|
return intervalsFragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
stackModeListView = getArguments().getBoolean(FROM_TOP_TO_BOTTOM_KEY, true) ? IntervalStatisticsAdapter.StackMode.STACK_FROM_TOP : IntervalStatisticsAdapter.StackMode.STACK_FROM_BOTTOM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -76,14 +97,11 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
|||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
sharedPreferences = PreferencesUtils.getSharedPreferences(getContext());
|
sharedPreferences = PreferencesUtils.getSharedPreferences(getContext());
|
||||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
|
||||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
|
||||||
|
|
||||||
viewBinding.intervalList.setEmptyView(viewBinding.intervalListEmptyView);
|
adapter = new IntervalStatisticsAdapter(getContext(), stackModeListView, metricUnits, isReportSpeed);
|
||||||
|
viewBinding.intervalList.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
stackModeListView = IntervalStatisticsAdapter.StackMode.STACK_FROM_TOP;
|
// TODO handle empty view: before we did viewBinding.intervalList.setEmptyView(viewBinding.intervalListEmptyView);
|
||||||
|
viewBinding.intervalList.setAdapter(adapter);
|
||||||
viewModel = new IntervalStatisticsModel();
|
|
||||||
|
|
||||||
spinnerAdapter = new ArrayAdapter<IntervalStatisticsModel.IntervalOption>(getContext(), android.R.layout.simple_spinner_dropdown_item, IntervalStatisticsModel.IntervalOption.values()) {
|
spinnerAdapter = new ArrayAdapter<IntervalStatisticsModel.IntervalOption>(getContext(), android.R.layout.simple_spinner_dropdown_item, IntervalStatisticsModel.IntervalOption.values()) {
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -109,7 +127,7 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
|||||||
@Override
|
@Override
|
||||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||||
selectedInterval = IntervalStatisticsModel.IntervalOption.values()[i];
|
selectedInterval = IntervalStatisticsModel.IntervalOption.values()[i];
|
||||||
loadIntervals();
|
uploadIntervals();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -121,6 +139,10 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
|||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
|
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||||
|
viewModel = new ViewModelProvider(getActivity()).get(IntervalStatisticsModel.class);
|
||||||
|
loadIntervals();
|
||||||
resumeTrackDataHub();
|
resumeTrackDataHub();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +150,6 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
|||||||
public void onPause() {
|
public void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
pauseTrackDataHub();
|
pauseTrackDataHub();
|
||||||
|
|
||||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,10 +176,15 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
|||||||
if (viewModel == null) {
|
if (viewModel == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
LiveData<List<IntervalStatistics.Interval>> liveData = viewModel.getIntervalStats(metricUnits, selectedInterval);
|
||||||
|
liveData.observe(getActivity(), intervalList -> adapter.swapData(intervalList, metricUnits, isReportSpeed));
|
||||||
|
}
|
||||||
|
|
||||||
IntervalStatistics intervalStatistics = viewModel.getIntervalStats(metricUnits, selectedInterval);
|
private synchronized void uploadIntervals() {
|
||||||
adapter = new IntervalStatisticsAdapter(getContext(), intervalStatistics.getIntervalList(), category, stackModeListView);
|
if (viewModel == null) {
|
||||||
viewBinding.intervalList.setAdapter(adapter);
|
return;
|
||||||
|
}
|
||||||
|
viewModel.upload(metricUnits, selectedInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -185,11 +211,11 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
|||||||
getActivity().runOnUiThread(() -> {
|
getActivity().runOnUiThread(() -> {
|
||||||
if (isResumed()) {
|
if (isResumed()) {
|
||||||
// Set category.
|
// Set category.
|
||||||
category = track != null ? track.getCategory() : "";
|
String category = track != null ? track.getCategory() : "";
|
||||||
|
|
||||||
// Set rate label.
|
// Set rate label.
|
||||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, getContext(), category); //TODO Handle sharedPreferenceChangeListener
|
isReportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, getContext(), category);
|
||||||
viewBinding.intervalRate.setText(reportSpeed ? R.string.stats_speed : R.string.stats_pace);
|
viewBinding.intervalRate.setText(isReportSpeed ? R.string.stats_speed : R.string.stats_pace);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -219,7 +245,7 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
|||||||
@Override
|
@Override
|
||||||
public void onNewTrackPointsDone(@NonNull TrackPoint unused) {
|
public void onNewTrackPointsDone(@NonNull TrackPoint unused) {
|
||||||
if (isResumed()) {
|
if (isResumed()) {
|
||||||
runOnUiThread(this::loadIntervals);
|
runOnUiThread(viewModel::onNewTrackPoints);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,48 +275,4 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
|||||||
fragmentActivity.runOnUiThread(runnable);
|
fragmentActivity.runOnUiThread(runnable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class IntervalsRecordingFragment extends IntervalsFragment {
|
|
||||||
// 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 Handler intervalHandler;
|
|
||||||
|
|
||||||
private final Runnable intervalRunner = new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (isResumed()) {
|
|
||||||
updateIntervals();
|
|
||||||
intervalHandler.postDelayed(intervalRunner, UI_UPDATE_INTERVAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Fragment newInstance() {
|
|
||||||
return new IntervalsRecordingFragment();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
|
||||||
super.onViewCreated(view, savedInstanceState);
|
|
||||||
intervalHandler = new Handler();
|
|
||||||
stackModeListView = IntervalStatisticsAdapter.StackMode.STACK_FROM_BOTTOM;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
intervalHandler.post(intervalRunner);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
intervalHandler.removeCallbacks(intervalRunner);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateIntervals() {
|
|
||||||
loadIntervals();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,6 @@ public class IntervalStatistics {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public List<Interval> getIntervalList() {
|
public List<Interval> getIntervalList() {
|
||||||
return intervalList;
|
return intervalList;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
package de.dennisguse.opentracks.viewmodels;
|
package de.dennisguse.opentracks.viewmodels;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.lifecycle.AndroidViewModel;
|
||||||
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -12,33 +17,67 @@ import de.dennisguse.opentracks.util.UnitConversions;
|
|||||||
* This model is used to load intervals for a track.
|
* This model is used to load intervals for a track.
|
||||||
* It uses a default interval but it can be set from outside to manage the interval length.
|
* It uses a default interval but it can be set from outside to manage the interval length.
|
||||||
*/
|
*/
|
||||||
public class IntervalStatisticsModel {
|
public class IntervalStatisticsModel extends AndroidViewModel {
|
||||||
|
|
||||||
private final List<TrackPoint> trackPoints = new ArrayList<>();
|
private final List<TrackPoint> trackPoints = new ArrayList<>();
|
||||||
|
private MutableLiveData<List<IntervalStatistics.Interval>> intervalsLiveData;
|
||||||
|
private float distanceInterval;
|
||||||
|
|
||||||
public IntervalStatistics getIntervalStats(boolean metricUnits, @Nullable IntervalOption interval) {
|
public IntervalStatisticsModel(@NonNull Application application) {
|
||||||
|
super(application);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MutableLiveData<List<IntervalStatistics.Interval>> getIntervalStats(boolean metricUnits, @Nullable IntervalOption interval) {
|
||||||
synchronized (trackPoints) {
|
synchronized (trackPoints) {
|
||||||
if (interval == null) {
|
if (intervalsLiveData == null) {
|
||||||
interval = IntervalOption.OPTION_1;
|
if (interval == null) {
|
||||||
}
|
interval = IntervalOption.OPTION_1;
|
||||||
|
}
|
||||||
|
|
||||||
float distanceInterval = metricUnits ? (float) (interval.getValue() * UnitConversions.KM_TO_M) : (float) (interval.getValue() * UnitConversions.MI_TO_M);
|
intervalsLiveData = new MutableLiveData<>();
|
||||||
return new IntervalStatistics(trackPoints, distanceInterval);
|
distanceInterval = metricUnits ? (float) (interval.getValue() * UnitConversions.KM_TO_M) : (float) (interval.getValue() * UnitConversions.MI_TO_M);
|
||||||
|
loadIntervalStatistics();
|
||||||
|
}
|
||||||
|
return intervalsLiveData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void loadIntervalStatistics() {
|
||||||
|
IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, distanceInterval);
|
||||||
|
intervalsLiveData.postValue(intervalStatistics.getIntervalList());
|
||||||
|
}
|
||||||
|
|
||||||
public void add(TrackPoint trackPoint) {
|
public void add(TrackPoint trackPoint) {
|
||||||
synchronized (trackPoints) {
|
synchronized (trackPoints) {
|
||||||
trackPoints.add(trackPoint);
|
trackPoints.add(trackPoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onNewTrackPoints() {
|
||||||
|
synchronized (trackPoints) {
|
||||||
|
if (intervalsLiveData != null) {
|
||||||
|
loadIntervalStatistics();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
synchronized (trackPoints) {
|
synchronized (trackPoints) {
|
||||||
trackPoints.clear();
|
trackPoints.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void upload(boolean metricUnits, @Nullable IntervalOption interval) {
|
||||||
|
synchronized (trackPoints) {
|
||||||
|
if (interval == null) {
|
||||||
|
interval = IntervalOption.OPTION_1;
|
||||||
|
}
|
||||||
|
|
||||||
|
distanceInterval = metricUnits ? (float) (interval.getValue() * UnitConversions.KM_TO_M) : (float) (interval.getValue() * UnitConversions.MI_TO_M);
|
||||||
|
loadIntervalStatistics();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Intervals length this view model support.
|
* Intervals length this view model support.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -57,7 +57,7 @@
|
|||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:background="@color/stats_separator" />
|
android:background="@color/stats_separator" />
|
||||||
|
|
||||||
<ListView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/interval_list"
|
android:id="@+id/interval_list"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
|||||||
Reference in New Issue
Block a user