forked from upstream-mirrors/OpenTracks
Merge pull request #1386 from OpenTracksApp/refactor#1118
Simplify RecordingLayout so we can create custom UIs #1118
This commit is contained in:
@@ -125,6 +125,7 @@ dependencies {
|
|||||||
implementation 'androidx.appcompat:appcompat:1.6.0-rc01'
|
implementation 'androidx.appcompat:appcompat:1.6.0-rc01'
|
||||||
implementation 'androidx.preference:preference:1.2.0'
|
implementation 'androidx.preference:preference:1.2.0'
|
||||||
implementation 'androidx.documentfile:documentfile:1.0.1'
|
implementation 'androidx.documentfile:documentfile:1.0.1'
|
||||||
|
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
||||||
implementation 'com.google.android.material:material:1.7.0'
|
implementation 'com.google.android.material:material:1.7.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
implementation 'androidx.core:core-splashscreen:1.0.0'
|
implementation 'androidx.core:core-splashscreen:1.0.0'
|
||||||
|
|||||||
@@ -7,5 +7,5 @@ Changes:
|
|||||||
Bugfix:
|
Bugfix:
|
||||||
- Speech announcements use MAY_DUCK (again)
|
- Speech announcements use MAY_DUCK (again)
|
||||||
- ChartView shows markers (again)
|
- ChartView shows markers (again)
|
||||||
- Fixed landscape layout of TrackListActivity
|
- Fixed landscape recordingLayout of TrackListActivity
|
||||||
- Request legacy file API behavior (necessary for Android 10)
|
- Request legacy file API behavior (necessary for Android 10)
|
||||||
|
|||||||
+18
-17
@@ -17,7 +17,8 @@ import org.junit.runner.RunWith;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayout;
|
||||||
|
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayoutIO;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class PreferencesOpenHelperTest {
|
public class PreferencesOpenHelperTest {
|
||||||
@@ -38,10 +39,10 @@ public class PreferencesOpenHelperTest {
|
|||||||
PreferencesOpenHelper.newInstance(1).check();
|
PreferencesOpenHelper.newInstance(1).check();
|
||||||
|
|
||||||
// then there should be one layout with old custom layout that has the new CSV value.
|
// then there should be one layout with old custom layout that has the new CSV value.
|
||||||
List<Layout> layouts = PreferencesUtils.getAllCustomLayouts();
|
List<RecordingLayout> recordingLayouts = PreferencesUtils.getAllCustomLayouts();
|
||||||
assertNotNull(layouts);
|
assertNotNull(recordingLayouts);
|
||||||
assertEquals(layouts.size(), 1);
|
assertEquals(recordingLayouts.size(), 1);
|
||||||
assertEquals(layouts.get(0).toCsv(), PreferencesUtils.getCustomLayout().toCsv());
|
assertEquals(recordingLayouts.get(0).toCsv(), PreferencesUtils.getCustomLayout().toCsv());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -63,10 +64,10 @@ public class PreferencesOpenHelperTest {
|
|||||||
+ PreferencesUtils.getLayoutColumnsByDefault() + ";distance,1,1,0;speed,1,1,0;";
|
+ PreferencesUtils.getLayoutColumnsByDefault() + ";distance,1,1,0;speed,1,1,0;";
|
||||||
|
|
||||||
// then there should be one layout with old custom layout that has the new CSV value.
|
// then there should be one layout with old custom layout that has the new CSV value.
|
||||||
List<Layout> layouts = PreferencesUtils.getAllCustomLayouts();
|
List<RecordingLayout> recordingLayouts = PreferencesUtils.getAllCustomLayouts();
|
||||||
assertNotNull(layouts);
|
assertNotNull(recordingLayouts);
|
||||||
assertEquals(layouts.size(), 1);
|
assertEquals(recordingLayouts.size(), 1);
|
||||||
assertEquals(layouts.get(0).toCsv(), updatedOldCustomLayoutCsv);
|
assertEquals(recordingLayouts.get(0).toCsv(), updatedOldCustomLayoutCsv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -87,10 +88,10 @@ public class PreferencesOpenHelperTest {
|
|||||||
String updatedOldCustomLayoutCsv = "whatever;3;distance,1,1,0;speed,1,1,0;";
|
String updatedOldCustomLayoutCsv = "whatever;3;distance,1,1,0;speed,1,1,0;";
|
||||||
|
|
||||||
// then there should be one layout with old custom layout that has the new CSV value.
|
// then there should be one layout with old custom layout that has the new CSV value.
|
||||||
List<Layout> layouts = PreferencesUtils.getAllCustomLayouts();
|
List<RecordingLayout> recordingLayouts = PreferencesUtils.getAllCustomLayouts();
|
||||||
assertNotNull(layouts);
|
assertNotNull(recordingLayouts);
|
||||||
assertEquals(layouts.size(), 1);
|
assertEquals(recordingLayouts.size(), 1);
|
||||||
assertEquals(layouts.get(0).toCsv(), updatedOldCustomLayoutCsv);
|
assertEquals(recordingLayouts.get(0).toCsv(), updatedOldCustomLayoutCsv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -109,10 +110,10 @@ public class PreferencesOpenHelperTest {
|
|||||||
PreferencesOpenHelper.newInstance(1).check();
|
PreferencesOpenHelper.newInstance(1).check();
|
||||||
|
|
||||||
// then custom layout should be equals to default layout.
|
// then custom layout should be equals to default layout.
|
||||||
Layout defaultLayout = Layout.fromCsv(PreferencesUtils.buildDefaultLayout(), resources);
|
RecordingLayout defaultRecordingLayout = RecordingLayoutIO.fromCsv(PreferencesUtils.buildDefaultLayout(), resources);
|
||||||
List<Layout> customLayout = PreferencesUtils.getAllCustomLayouts();
|
List<RecordingLayout> customRecordingLayout = PreferencesUtils.getAllCustomLayouts();
|
||||||
|
|
||||||
assertEquals(customLayout.size(), 1);
|
assertEquals(customRecordingLayout.size(), 1);
|
||||||
assertEquals(defaultLayout.toCsv(), customLayout.get(0).toCsv());
|
assertEquals(defaultRecordingLayout.toCsv(), customRecordingLayout.get(0).toCsv());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ import java.util.List;
|
|||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayout;
|
||||||
|
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayoutIO;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class PreferencesUtilsTest {
|
public class PreferencesUtilsTest {
|
||||||
@@ -85,13 +86,13 @@ public class PreferencesUtilsTest {
|
|||||||
editor.commit();
|
editor.commit();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
List<Layout> layouts = PreferencesUtils.getAllCustomLayouts();
|
List<RecordingLayout> recordingLayouts = PreferencesUtils.getAllCustomLayouts();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(layouts.size(), 1);
|
assertEquals(recordingLayouts.size(), 1);
|
||||||
assertTrue(layouts.get(0).getFields().size() > 0);
|
assertTrue(recordingLayouts.get(0).getFields().size() > 0);
|
||||||
assertEquals(layouts.get(0).getName(), context.getString(R.string.stats_custom_layout_default_layout));
|
assertEquals(recordingLayouts.get(0).getName(), context.getString(R.string.stats_custom_layout_default_layout));
|
||||||
assertTrue(layouts.get(0).getFields().stream().anyMatch(DataField::isVisible));
|
assertTrue(recordingLayouts.get(0).getFields().stream().anyMatch(DataField::isVisible));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -103,12 +104,12 @@ public class PreferencesUtilsTest {
|
|||||||
editor.commit();
|
editor.commit();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
Layout layout = PreferencesUtils.getCustomLayout();
|
RecordingLayout recordingLayout = PreferencesUtils.getCustomLayout();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertTrue(layout.getFields().size() > 0);
|
assertTrue(recordingLayout.getFields().size() > 0);
|
||||||
assertEquals(layout.getName(), context.getString(R.string.stats_custom_layout_default_layout));
|
assertEquals(recordingLayout.getName(), context.getString(R.string.stats_custom_layout_default_layout));
|
||||||
assertTrue(layout.getFields().stream().anyMatch(DataField::isVisible));
|
assertTrue(recordingLayout.getFields().stream().anyMatch(DataField::isVisible));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -126,28 +127,28 @@ public class PreferencesUtilsTest {
|
|||||||
editor.apply();
|
editor.apply();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
Layout layout = PreferencesUtils.getCustomLayout();
|
RecordingLayout recordingLayout = PreferencesUtils.getCustomLayout();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(layout.getFields().size(), 4);
|
assertEquals(recordingLayout.getFields().size(), 4);
|
||||||
assertEquals(layout.getName(), "run");
|
assertEquals(recordingLayout.getName(), "run");
|
||||||
assertEquals(layout.getColumnsPerRow(), 2);
|
assertEquals(recordingLayout.getColumnsPerRow(), 2);
|
||||||
|
|
||||||
assertEquals(layout.getFields().get(0).getKey(), context.getString(R.string.stats_custom_layout_moving_time_key));
|
assertEquals(recordingLayout.getFields().get(0).getKey(), context.getString(R.string.stats_custom_layout_moving_time_key));
|
||||||
assertTrue(layout.getFields().get(0).isVisible());
|
assertTrue(recordingLayout.getFields().get(0).isVisible());
|
||||||
assertTrue(layout.getFields().get(0).isPrimary());
|
assertTrue(recordingLayout.getFields().get(0).isPrimary());
|
||||||
|
|
||||||
assertEquals(layout.getFields().get(1).getKey(), context.getString(R.string.stats_custom_layout_distance_key));
|
assertEquals(recordingLayout.getFields().get(1).getKey(), context.getString(R.string.stats_custom_layout_distance_key));
|
||||||
assertTrue(layout.getFields().get(1).isVisible());
|
assertTrue(recordingLayout.getFields().get(1).isVisible());
|
||||||
assertFalse(layout.getFields().get(1).isPrimary());
|
assertFalse(recordingLayout.getFields().get(1).isPrimary());
|
||||||
|
|
||||||
assertEquals(layout.getFields().get(2).getKey(), context.getString(R.string.stats_custom_layout_average_moving_speed_key));
|
assertEquals(recordingLayout.getFields().get(2).getKey(), context.getString(R.string.stats_custom_layout_average_moving_speed_key));
|
||||||
assertFalse(layout.getFields().get(2).isVisible());
|
assertFalse(recordingLayout.getFields().get(2).isVisible());
|
||||||
assertTrue(layout.getFields().get(2).isPrimary());
|
assertTrue(recordingLayout.getFields().get(2).isPrimary());
|
||||||
|
|
||||||
assertEquals(layout.getFields().get(3).getKey(), context.getString(R.string.stats_custom_layout_speed_key));
|
assertEquals(recordingLayout.getFields().get(3).getKey(), context.getString(R.string.stats_custom_layout_speed_key));
|
||||||
assertFalse(layout.getFields().get(3).isVisible());
|
assertFalse(recordingLayout.getFields().get(3).isVisible());
|
||||||
assertFalse(layout.getFields().get(3).isPrimary());
|
assertFalse(recordingLayout.getFields().get(3).isPrimary());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -165,44 +166,44 @@ public class PreferencesUtilsTest {
|
|||||||
editor.apply();
|
editor.apply();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
Layout layout = PreferencesUtils.getCustomLayout();
|
RecordingLayout recordingLayout = PreferencesUtils.getCustomLayout();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(layout.getFields().size(), 4);
|
assertEquals(recordingLayout.getFields().size(), 4);
|
||||||
assertEquals(layout.getName(), "walking");
|
assertEquals(recordingLayout.getName(), "walking");
|
||||||
assertEquals(layout.getColumnsPerRow(), 2);
|
assertEquals(recordingLayout.getColumnsPerRow(), 2);
|
||||||
|
|
||||||
assertEquals(layout.getFields().get(0).getKey(), context.getString(R.string.stats_custom_layout_moving_time_key));
|
assertEquals(recordingLayout.getFields().get(0).getKey(), context.getString(R.string.stats_custom_layout_moving_time_key));
|
||||||
assertTrue(layout.getFields().get(0).isVisible());
|
assertTrue(recordingLayout.getFields().get(0).isVisible());
|
||||||
assertTrue(layout.getFields().get(0).isPrimary());
|
assertTrue(recordingLayout.getFields().get(0).isPrimary());
|
||||||
|
|
||||||
assertEquals(layout.getFields().get(1).getKey(), context.getString(R.string.stats_custom_layout_distance_key));
|
assertEquals(recordingLayout.getFields().get(1).getKey(), context.getString(R.string.stats_custom_layout_distance_key));
|
||||||
assertTrue(layout.getFields().get(1).isVisible());
|
assertTrue(recordingLayout.getFields().get(1).isVisible());
|
||||||
assertFalse(layout.getFields().get(1).isPrimary());
|
assertFalse(recordingLayout.getFields().get(1).isPrimary());
|
||||||
|
|
||||||
assertEquals(layout.getFields().get(2).getKey(), context.getString(R.string.stats_custom_layout_coordinates_key));
|
assertEquals(recordingLayout.getFields().get(2).getKey(), context.getString(R.string.stats_custom_layout_coordinates_key));
|
||||||
assertFalse(layout.getFields().get(2).isVisible());
|
assertFalse(recordingLayout.getFields().get(2).isVisible());
|
||||||
assertTrue(layout.getFields().get(2).isPrimary());
|
assertTrue(recordingLayout.getFields().get(2).isPrimary());
|
||||||
assertTrue(layout.getFields().get(2).isWide());
|
assertTrue(recordingLayout.getFields().get(2).isWide());
|
||||||
|
|
||||||
assertEquals(layout.getFields().get(3).getKey(), context.getString(R.string.stats_custom_layout_speed_key));
|
assertEquals(recordingLayout.getFields().get(3).getKey(), context.getString(R.string.stats_custom_layout_speed_key));
|
||||||
assertFalse(layout.getFields().get(3).isVisible());
|
assertFalse(recordingLayout.getFields().get(3).isVisible());
|
||||||
assertFalse(layout.getFields().get(3).isPrimary());
|
assertFalse(recordingLayout.getFields().get(3).isPrimary());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetCustomLayout() {
|
public void testSetCustomLayout() {
|
||||||
// given
|
// given
|
||||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
Layout layoutSrc = new Layout("road cycling");
|
RecordingLayout recordingLayoutSrc = new RecordingLayout("road cycling");
|
||||||
layoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_moving_time_key), context.getString(R.string.stats_moving_time), true, true, false));
|
recordingLayoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_moving_time_key), true, true, false));
|
||||||
layoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_distance_key), context.getString(R.string.stats_distance), true, false, false));
|
recordingLayoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_distance_key), true, false, false));
|
||||||
layoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_average_moving_speed_key), context.getString(R.string.stats_average_moving_speed), false, true, false));
|
recordingLayoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_average_moving_speed_key), false, true, false));
|
||||||
layoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_speed_key), context.getString(R.string.stats_speed), false, false, false));
|
recordingLayoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_speed_key), false, false, false));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||||
editor.putString(context.getString(R.string.stats_custom_layouts_key), layoutSrc.toCsv());
|
editor.putString(context.getString(R.string.stats_custom_layouts_key), recordingLayoutSrc.toCsv());
|
||||||
editor.commit();
|
editor.commit();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
@@ -215,13 +216,13 @@ public class PreferencesUtilsTest {
|
|||||||
+ context.getString(R.string.stats_custom_layout_average_moving_speed_key) + ",0,1,0;"
|
+ context.getString(R.string.stats_custom_layout_average_moving_speed_key) + ",0,1,0;"
|
||||||
+ context.getString(R.string.stats_custom_layout_speed_key) + ",0,0,0;");
|
+ context.getString(R.string.stats_custom_layout_speed_key) + ",0,0,0;");
|
||||||
|
|
||||||
Layout layoutDst = PreferencesUtils.getCustomLayout();
|
RecordingLayout recordingLayoutDst = PreferencesUtils.getCustomLayout();
|
||||||
assertEquals(layoutSrc.getName(), layoutDst.getName());
|
assertEquals(recordingLayoutSrc.getName(), recordingLayoutDst.getName());
|
||||||
assertEquals(layoutSrc.getFields().size(), layoutDst.getFields().size());
|
assertEquals(recordingLayoutSrc.getFields().size(), recordingLayoutDst.getFields().size());
|
||||||
for (int i = 0; i < layoutSrc.getFields().size(); i++) {
|
for (int i = 0; i < recordingLayoutSrc.getFields().size(); i++) {
|
||||||
assertEquals(layoutSrc.getFields().get(i).getKey(), layoutDst.getFields().get(i).getKey());
|
assertEquals(recordingLayoutSrc.getFields().get(i).getKey(), recordingLayoutDst.getFields().get(i).getKey());
|
||||||
assertEquals(layoutSrc.getFields().get(i).isVisible(), layoutDst.getFields().get(i).isVisible());
|
assertEquals(recordingLayoutSrc.getFields().get(i).isVisible(), recordingLayoutDst.getFields().get(i).isVisible());
|
||||||
assertEquals(layoutSrc.getFields().get(i).isPrimary(), layoutDst.getFields().get(i).isPrimary());
|
assertEquals(recordingLayoutSrc.getFields().get(i).isPrimary(), recordingLayoutDst.getFields().get(i).isPrimary());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,7 +248,7 @@ public class PreferencesUtilsTest {
|
|||||||
editor.putString(context.getString(R.string.stats_custom_layouts_key), cyclingProfile + "\n" + runningProfile);
|
editor.putString(context.getString(R.string.stats_custom_layouts_key), cyclingProfile + "\n" + runningProfile);
|
||||||
editor.apply();
|
editor.apply();
|
||||||
|
|
||||||
List<Layout> layoutsBefore = PreferencesUtils.getAllCustomLayouts();
|
List<RecordingLayout> layoutsBefore = PreferencesUtils.getAllCustomLayouts();
|
||||||
|
|
||||||
// when cyling profile is updated
|
// when cyling profile is updated
|
||||||
String cyclingProfileUpdated = "cycling;2;"
|
String cyclingProfileUpdated = "cycling;2;"
|
||||||
@@ -256,14 +257,14 @@ public class PreferencesUtilsTest {
|
|||||||
+ context.getString(R.string.stats_custom_layout_average_moving_speed_key) + ",0,0;"
|
+ context.getString(R.string.stats_custom_layout_average_moving_speed_key) + ",0,0;"
|
||||||
+ context.getString(R.string.stats_custom_layout_speed_key) + ",0,0;";
|
+ context.getString(R.string.stats_custom_layout_speed_key) + ",0,0;";
|
||||||
|
|
||||||
List<Layout> layoutsToBeUpdated = new ArrayList<>();
|
List<RecordingLayout> layoutsToBeUpdated = new ArrayList<>();
|
||||||
layoutsToBeUpdated.add(Layout.fromCsv(cyclingProfileUpdated, resources));
|
layoutsToBeUpdated.add(RecordingLayoutIO.fromCsv(cyclingProfileUpdated, resources));
|
||||||
layoutsToBeUpdated.add(Layout.fromCsv(runningProfile, resources));
|
layoutsToBeUpdated.add(RecordingLayoutIO.fromCsv(runningProfile, resources));
|
||||||
|
|
||||||
PreferencesUtils.updateCustomLayouts(layoutsToBeUpdated);
|
PreferencesUtils.updateCustomLayouts(layoutsToBeUpdated);
|
||||||
|
|
||||||
// then only updated profile is modified in the custom layouts
|
// then only updated profile is modified in the custom layouts
|
||||||
List<Layout> layoutsAfter = PreferencesUtils.getAllCustomLayouts();
|
List<RecordingLayout> layoutsAfter = PreferencesUtils.getAllCustomLayouts();
|
||||||
|
|
||||||
assertEquals(layoutsBefore.size(), 2);
|
assertEquals(layoutsBefore.size(), 2);
|
||||||
assertEquals(layoutsAfter.size(), 2);
|
assertEquals(layoutsAfter.size(), 2);
|
||||||
@@ -294,7 +295,7 @@ public class PreferencesUtilsTest {
|
|||||||
editor.putString(context.getString(R.string.stats_custom_layouts_key), cyclingProfile + "\n" + runningProfile);
|
editor.putString(context.getString(R.string.stats_custom_layouts_key), cyclingProfile + "\n" + runningProfile);
|
||||||
editor.apply();
|
editor.apply();
|
||||||
|
|
||||||
List<Layout> layoutsBefore = PreferencesUtils.getAllCustomLayouts();
|
List<RecordingLayout> layoutsBefore = PreferencesUtils.getAllCustomLayouts();
|
||||||
|
|
||||||
// when cyling profile is updated
|
// when cyling profile is updated
|
||||||
String cyclingProfileUpdated = "cycling;2;"
|
String cyclingProfileUpdated = "cycling;2;"
|
||||||
@@ -302,11 +303,11 @@ public class PreferencesUtilsTest {
|
|||||||
+ context.getString(R.string.stats_custom_layout_distance_key) + ",0,0;"
|
+ context.getString(R.string.stats_custom_layout_distance_key) + ",0,0;"
|
||||||
+ context.getString(R.string.stats_custom_layout_average_moving_speed_key) + ",0,0;"
|
+ context.getString(R.string.stats_custom_layout_average_moving_speed_key) + ",0,0;"
|
||||||
+ context.getString(R.string.stats_custom_layout_speed_key) + ",0,0;";
|
+ context.getString(R.string.stats_custom_layout_speed_key) + ",0,0;";
|
||||||
Layout layoutToBeUpdated = Layout.fromCsv(cyclingProfileUpdated, resources);
|
RecordingLayout recordingLayoutToBeUpdated = RecordingLayoutIO.fromCsv(cyclingProfileUpdated, resources);
|
||||||
PreferencesUtils.updateCustomLayout(layoutToBeUpdated);
|
PreferencesUtils.updateCustomLayout(recordingLayoutToBeUpdated);
|
||||||
|
|
||||||
// then only updated profile is modified in the custom layouts
|
// then only updated profile is modified in the custom layouts
|
||||||
List<Layout> layoutsAfter = PreferencesUtils.getAllCustomLayouts();
|
List<RecordingLayout> layoutsAfter = PreferencesUtils.getAllCustomLayouts();
|
||||||
|
|
||||||
assertEquals(layoutsBefore.size(), 2);
|
assertEquals(layoutsBefore.size(), 2);
|
||||||
assertEquals(layoutsAfter.size(), 2);
|
assertEquals(layoutsAfter.size(), 2);
|
||||||
@@ -337,9 +338,9 @@ public class PreferencesUtilsTest {
|
|||||||
editor.apply();
|
editor.apply();
|
||||||
|
|
||||||
// when it gets the custom layout
|
// when it gets the custom layout
|
||||||
Layout layout = PreferencesUtils.getCustomLayout();
|
RecordingLayout recordingLayout = PreferencesUtils.getCustomLayout();
|
||||||
|
|
||||||
// then the first one was returned
|
// then the first one was returned
|
||||||
assertEquals(layout.getName(), "cycling");
|
assertEquals(recordingLayout.getName(), "cycling");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ import org.junit.runner.RunWith;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class CsvLayoutUtilsTest extends TestCase {
|
public class CsvRecordingLayoutUtilsTest extends TestCase {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetCsvLineParts() {
|
public void testGetCsvLineParts() {
|
||||||
+3
-3
@@ -30,7 +30,7 @@ import de.dennisguse.opentracks.settings.PreferencesUtils;
|
|||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class EspressoCustomLayoutTest {
|
public class EspressoCustomRecordingLayoutTest {
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
||||||
@@ -45,9 +45,9 @@ public class EspressoCustomLayoutTest {
|
|||||||
trackControllerRecordButton.perform(click());
|
trackControllerRecordButton.perform(click());
|
||||||
|
|
||||||
// Get custom layout preferences and check all data fields are showed.
|
// Get custom layout preferences and check all data fields are showed.
|
||||||
Layout layout = PreferencesUtils.getCustomLayout();
|
RecordingLayout recordingLayout = PreferencesUtils.getCustomLayout();
|
||||||
|
|
||||||
onView(withId(R.id.stats_recycler_view)).check(new RecyclerViewItemCountAssertion((int) layout.getFields().stream().filter(DataField::isVisible).count()));
|
onView(withId(R.id.recycler_view)).check(new RecyclerViewItemCountAssertion((int) recordingLayout.getFields().stream().filter(DataField::isVisible).count()));
|
||||||
|
|
||||||
// stop recording
|
// stop recording
|
||||||
ViewInteraction trackControllerStopButton = onView(withId(R.id.track_recording_fab_action));
|
ViewInteraction trackControllerStopButton = onView(withId(R.id.track_recording_fab_action));
|
||||||
+39
-43
@@ -17,7 +17,7 @@ import de.dennisguse.opentracks.R;
|
|||||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class LayoutTest extends TestCase {
|
public class RecordingLayoutTest extends TestCase {
|
||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
private final Resources resources = ApplicationProvider.getApplicationContext().getResources();
|
private final Resources resources = ApplicationProvider.getApplicationContext().getResources();
|
||||||
@@ -28,21 +28,17 @@ public class LayoutTest extends TestCase {
|
|||||||
String csv = "running;2;" + context.getString(R.string.stats_custom_layout_speed_key) + ",1,1,0;" + context.getString(R.string.stats_custom_layout_distance_key) + ",1,0,0;" + context.getString(R.string.stats_custom_layout_altitude_key) + ",0,1,0;" + context.getString(R.string.stats_custom_layout_gain_key) + ",0,0,0;";
|
String csv = "running;2;" + context.getString(R.string.stats_custom_layout_speed_key) + ",1,1,0;" + context.getString(R.string.stats_custom_layout_distance_key) + ",1,0,0;" + context.getString(R.string.stats_custom_layout_altitude_key) + ",0,1,0;" + context.getString(R.string.stats_custom_layout_gain_key) + ",0,0,0;";
|
||||||
|
|
||||||
// when create a layout from CSV line
|
// when create a layout from CSV line
|
||||||
Layout layout = Layout.fromCsv(csv, resources);
|
RecordingLayout recordingLayout = RecordingLayoutIO.fromCsv(csv, resources);
|
||||||
List<DataField> dataFieldList = layout.getFields();
|
List<DataField> dataFieldList = recordingLayout.getFields();
|
||||||
|
|
||||||
// then layout and data fields are built correctly
|
// then layout and data fields are built correctly
|
||||||
assertEquals(layout.getName(), "running");
|
assertEquals(recordingLayout.getName(), "running");
|
||||||
assertEquals(layout.getColumnsPerRow(), 2);
|
assertEquals(recordingLayout.getColumnsPerRow(), 2);
|
||||||
assertEquals(dataFieldList.size(), 4);
|
assertEquals(dataFieldList.size(), 4);
|
||||||
assertEquals(dataFieldList.get(0).getKey(), context.getString(R.string.stats_custom_layout_speed_key));
|
assertEquals(dataFieldList.get(0).getKey(), context.getString(R.string.stats_custom_layout_speed_key));
|
||||||
assertEquals(dataFieldList.get(1).getKey(), context.getString(R.string.stats_custom_layout_distance_key));
|
assertEquals(dataFieldList.get(1).getKey(), context.getString(R.string.stats_custom_layout_distance_key));
|
||||||
assertEquals(dataFieldList.get(2).getKey(), context.getString(R.string.stats_custom_layout_altitude_key));
|
assertEquals(dataFieldList.get(2).getKey(), context.getString(R.string.stats_custom_layout_altitude_key));
|
||||||
assertEquals(dataFieldList.get(3).getKey(), context.getString(R.string.stats_custom_layout_gain_key));
|
assertEquals(dataFieldList.get(3).getKey(), context.getString(R.string.stats_custom_layout_gain_key));
|
||||||
assertEquals(dataFieldList.get(0).getTitle(), context.getString(R.string.stats_speed));
|
|
||||||
assertEquals(dataFieldList.get(1).getTitle(), context.getString(R.string.stats_distance));
|
|
||||||
assertEquals(dataFieldList.get(2).getTitle(), context.getString(R.string.stats_altitude));
|
|
||||||
assertEquals(dataFieldList.get(3).getTitle(), context.getString(R.string.stats_gain));
|
|
||||||
assertTrue(dataFieldList.get(0).isVisible());
|
assertTrue(dataFieldList.get(0).isVisible());
|
||||||
assertTrue(dataFieldList.get(0).isPrimary());
|
assertTrue(dataFieldList.get(0).isPrimary());
|
||||||
assertFalse(dataFieldList.get(0).isWide());
|
assertFalse(dataFieldList.get(0).isWide());
|
||||||
@@ -64,13 +60,13 @@ public class LayoutTest extends TestCase {
|
|||||||
String csv1 = "Layout Name;speed,1,1;distance,0,0;";
|
String csv1 = "Layout Name;speed,1,1;distance,0,0;";
|
||||||
|
|
||||||
// when create a layout from CSV line
|
// when create a layout from CSV line
|
||||||
Layout layout = Layout.fromCsv(csv1, resources);
|
RecordingLayout recordingLayout = RecordingLayoutIO.fromCsv(csv1, resources);
|
||||||
List<DataField> dataFieldList = layout.getFields();
|
List<DataField> dataFieldList = recordingLayout.getFields();
|
||||||
|
|
||||||
// then layout and data fields are built correctly
|
// then layout and data fields are built correctly
|
||||||
assertEquals(layout.getName(), PreferencesUtils.getDefaultLayoutName());
|
assertEquals(recordingLayout.getName(), PreferencesUtils.getDefaultLayoutName());
|
||||||
assertEquals(layout.getColumnsPerRow(), PreferencesUtils.getLayoutColumnsByDefault());
|
assertEquals(recordingLayout.getColumnsPerRow(), PreferencesUtils.getLayoutColumnsByDefault());
|
||||||
assertEquals(layout.getFields().size(), 0);
|
assertEquals(recordingLayout.getFields().size(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -79,13 +75,13 @@ public class LayoutTest extends TestCase {
|
|||||||
String csv1 = "Layout Name;2;";
|
String csv1 = "Layout Name;2;";
|
||||||
|
|
||||||
// when create a layout from CSV line
|
// when create a layout from CSV line
|
||||||
Layout layout = Layout.fromCsv(csv1, resources);
|
RecordingLayout recordingLayout = RecordingLayoutIO.fromCsv(csv1, resources);
|
||||||
List<DataField> dataFieldList = layout.getFields();
|
List<DataField> dataFieldList = recordingLayout.getFields();
|
||||||
|
|
||||||
// then layout and data fields are built correctly
|
// then layout and data fields are built correctly
|
||||||
assertEquals(layout.getName(), PreferencesUtils.getDefaultLayoutName());
|
assertEquals(recordingLayout.getName(), PreferencesUtils.getDefaultLayoutName());
|
||||||
assertEquals(layout.getColumnsPerRow(), PreferencesUtils.getLayoutColumnsByDefault());
|
assertEquals(recordingLayout.getColumnsPerRow(), PreferencesUtils.getLayoutColumnsByDefault());
|
||||||
assertEquals(layout.getFields().size(), 0);
|
assertEquals(recordingLayout.getFields().size(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -94,13 +90,13 @@ public class LayoutTest extends TestCase {
|
|||||||
String csv1 = "Layout Name;2;speed,distance,total time;";
|
String csv1 = "Layout Name;2;speed,distance,total time;";
|
||||||
|
|
||||||
// when create a layout from CSV line
|
// when create a layout from CSV line
|
||||||
Layout layout = Layout.fromCsv(csv1, resources);
|
RecordingLayout recordingLayout = RecordingLayoutIO.fromCsv(csv1, resources);
|
||||||
List<DataField> dataFieldList = layout.getFields();
|
List<DataField> dataFieldList = recordingLayout.getFields();
|
||||||
|
|
||||||
// then layout and data fields are built correctly
|
// then layout and data fields are built correctly
|
||||||
assertEquals(layout.getName(), "Layout Name");
|
assertEquals(recordingLayout.getName(), "Layout Name");
|
||||||
assertEquals(layout.getColumnsPerRow(), 2);
|
assertEquals(recordingLayout.getColumnsPerRow(), 2);
|
||||||
assertEquals(layout.getFields().size(), 0);
|
assertEquals(recordingLayout.getFields().size(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -109,27 +105,27 @@ public class LayoutTest extends TestCase {
|
|||||||
String csv1 = "Layout Name;2;speed,1,0;distance;";
|
String csv1 = "Layout Name;2;speed,1,0;distance;";
|
||||||
|
|
||||||
// when create a layout from CSV line
|
// when create a layout from CSV line
|
||||||
Layout layout = Layout.fromCsv(csv1, resources);
|
RecordingLayout recordingLayout = RecordingLayoutIO.fromCsv(csv1, resources);
|
||||||
List<DataField> dataFieldList = layout.getFields();
|
List<DataField> dataFieldList = recordingLayout.getFields();
|
||||||
|
|
||||||
// then layout and data fields are built correctly
|
// then layout and data fields are built correctly
|
||||||
assertEquals(layout.getName(), "Layout Name");
|
assertEquals(recordingLayout.getName(), "Layout Name");
|
||||||
assertEquals(layout.getColumnsPerRow(), 2);
|
assertEquals(recordingLayout.getColumnsPerRow(), 2);
|
||||||
assertEquals(layout.getFields().size(), 1);
|
assertEquals(recordingLayout.getFields().size(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testToCsv() {
|
public void testToCsv() {
|
||||||
// given a layout's object
|
// given a layout's object
|
||||||
Layout layout = new Layout("Test Layout", 2);
|
RecordingLayout recordingLayout = new RecordingLayout("Test Layout", 2);
|
||||||
layout.addField("key1", "Title 1", false, false, false);
|
recordingLayout.addField(new DataField("key1", false, false, false));
|
||||||
layout.addField("key2", "Title 2", false, true, false);
|
recordingLayout.addField(new DataField("key2", false, true, false));
|
||||||
layout.addField("key3", "Title 3", true, false, false);
|
recordingLayout.addField(new DataField("key3", true, false, false));
|
||||||
layout.addField("key4", "Title 4", true, true, false);
|
recordingLayout.addField(new DataField("key4", true, true, false));
|
||||||
layout.addField("key5", "Title 5", true, true, true);
|
recordingLayout.addField(new DataField("key5", true, true, true));
|
||||||
|
|
||||||
// when converts it to CSV
|
// when converts it to CSV
|
||||||
String csv = layout.toCsv();
|
String csv = recordingLayout.toCsv();
|
||||||
|
|
||||||
// then csv is well built
|
// then csv is well built
|
||||||
assertEquals(csv, "Test Layout;2;key1,0,0,0;key2,0,1,0;key3,1,0,0;key4,1,1,0;key5,1,1,1;");
|
assertEquals(csv, "Test Layout;2;key1,0,0,0;key2,0,1,0;key3,1,0,0;key4,1,1,0;key5,1,1,1;");
|
||||||
@@ -138,15 +134,15 @@ public class LayoutTest extends TestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testToCsv_columnsByDefault() {
|
public void testToCsv_columnsByDefault() {
|
||||||
// given a layout's object
|
// given a layout's object
|
||||||
Layout layout = new Layout("Test Layout");
|
RecordingLayout recordingLayout = new RecordingLayout("Test Layout");
|
||||||
layout.addField("key1", "Title 1", false, false, false);
|
recordingLayout.addField(new DataField("key1", false, false, false));
|
||||||
layout.addField("key2", "Title 2", false, true, false);
|
recordingLayout.addField(new DataField("key2", false, true, false));
|
||||||
layout.addField("key3", "Title 3", true, false, false);
|
recordingLayout.addField(new DataField("key3", true, false, false));
|
||||||
layout.addField("key4", "Title 4", true, true, false);
|
recordingLayout.addField(new DataField("key4", true, true, false));
|
||||||
layout.addField("key5", "Title 5", true, true, true);
|
recordingLayout.addField(new DataField("key5", true, true, true));
|
||||||
|
|
||||||
// when converts it to CSV
|
// when converts it to CSV
|
||||||
String csv = layout.toCsv();
|
String csv = recordingLayout.toCsv();
|
||||||
|
|
||||||
// then csv is well built
|
// then csv is well built
|
||||||
assertEquals(csv, "Test Layout;" + PreferencesUtils.getLayoutColumnsByDefault() + ";key1,0,0,0;key2,0,1,0;key3,1,0,0;key4,1,1,0;key5,1,1,1;");
|
assertEquals(csv, "Test Layout;" + PreferencesUtils.getLayoutColumnsByDefault() + ";key1,0,0,0;key2,0,1,0;key3,1,0,0;key4,1,1,0;key5,1,1,1;");
|
||||||
@@ -12,7 +12,7 @@ import org.junit.runner.RunWith;
|
|||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayout;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class StatisticsUtilsTest extends TestCase {
|
public class StatisticsUtilsTest extends TestCase {
|
||||||
@@ -44,16 +44,16 @@ public class StatisticsUtilsTest extends TestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testFilterVisible() {
|
public void testFilterVisible() {
|
||||||
// given
|
// given
|
||||||
Layout layout = new Layout("profile");
|
RecordingLayout recordingLayout = new RecordingLayout("profile");
|
||||||
layout.addField(new DataField("key1", "title1", true, true, false));
|
recordingLayout.addField(new DataField("key1", true, true, false));
|
||||||
layout.addField(new DataField("key2", "title2", false, true, false));
|
recordingLayout.addField(new DataField("key2", false, true, false));
|
||||||
layout.addField(new DataField("key3", "title3", true, true, false));
|
recordingLayout.addField(new DataField("key3", true, true, false));
|
||||||
layout.addField(new DataField("key4", "title4", false, true, false));
|
recordingLayout.addField(new DataField("key4", false, true, false));
|
||||||
layout.addField(new DataField("key5", "title5", true, true, false));
|
recordingLayout.addField(new DataField("key5", true, true, false));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
Layout resultTrue = StatisticsUtils.filterVisible(layout, true);
|
RecordingLayout resultTrue = StatisticsUtils.filterVisible(recordingLayout, true);
|
||||||
Layout resultFalse = StatisticsUtils.filterVisible(layout, false);
|
RecordingLayout resultFalse = StatisticsUtils.filterVisible(recordingLayout, false);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(resultTrue.getFields().size(), 3);
|
assertEquals(resultTrue.getFields().size(), 3);
|
||||||
|
|||||||
-199
@@ -1,199 +0,0 @@
|
|||||||
package de.dennisguse.opentracks.viewmodels;
|
|
||||||
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
|
||||||
import de.dennisguse.opentracks.data.models.Cadence;
|
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
|
||||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
|
||||||
import de.dennisguse.opentracks.data.models.Power;
|
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
|
||||||
import de.dennisguse.opentracks.services.RecordingData;
|
|
||||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
|
||||||
import de.dennisguse.opentracks.stats.SensorStatistics;
|
|
||||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
|
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
|
||||||
public class StatisticDataBuilderTest extends TestCase {
|
|
||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private Track trackMock;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private TrackStatistics trackStatisticsMock;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private RecordingData recordingDataMock;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private SensorStatistics sensorStatisticsMock;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFromRecordingData() {
|
|
||||||
when(trackMock.getTrackStatistics()).thenReturn(trackStatisticsMock);
|
|
||||||
when(trackStatisticsMock.getTotalTime()).thenReturn(Duration.ofMillis(0));
|
|
||||||
when(trackStatisticsMock.getMovingTime()).thenReturn(Duration.ofMillis(0));
|
|
||||||
when(trackStatisticsMock.getTotalDistance()).thenReturn(Distance.of(0));
|
|
||||||
when(recordingDataMock.getTrackStatistics()).thenReturn(trackStatisticsMock);
|
|
||||||
|
|
||||||
// given
|
|
||||||
Layout layout = new Layout(context.getString(R.string.default_activity_default));
|
|
||||||
layout.addField(context.getString(R.string.stats_custom_layout_total_time_key), context.getString(R.string.stats_total_time), true, true, false);
|
|
||||||
layout.addField(context.getString(R.string.stats_custom_layout_moving_time_key), context.getString(R.string.stats_moving_time), true, true, false);
|
|
||||||
layout.addField(context.getString(R.string.stats_custom_layout_distance_key), context.getString(R.string.stats_distance), true, true, false);
|
|
||||||
layout.addField(context.getString(R.string.stats_custom_layout_speed_key), context.getString(R.string.stats_speed), false, true, false);
|
|
||||||
layout.addField(context.getString(R.string.stats_custom_layout_max_speed_key), context.getString(R.string.stats_max_speed), false, true, false);
|
|
||||||
|
|
||||||
// when
|
|
||||||
List<StatisticData> statisticDataList = StatisticDataBuilder.fromRecordingData(context, recordingDataMock, layout, UnitSystem.METRIC);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(statisticDataList.size(), 3);
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_total_time))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_moving_time))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_distance))));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFromRecordingData_allFields() {
|
|
||||||
when(trackMock.getTrackStatistics()).thenReturn(trackStatisticsMock);
|
|
||||||
when(trackStatisticsMock.getTotalTime()).thenReturn(Duration.ofMillis(0));
|
|
||||||
when(trackStatisticsMock.getMovingTime()).thenReturn(Duration.ofMillis(0));
|
|
||||||
when(trackStatisticsMock.getTotalDistance()).thenReturn(Distance.of(0));
|
|
||||||
when(recordingDataMock.getTrackStatistics()).thenReturn(trackStatisticsMock);
|
|
||||||
|
|
||||||
// given
|
|
||||||
Layout layout = new Layout(context.getString(R.string.default_activity_default));
|
|
||||||
layout.addField(context.getString(R.string.stats_custom_layout_total_time_key), context.getString(R.string.stats_total_time), true, true, false);
|
|
||||||
layout.addField(context.getString(R.string.stats_custom_layout_moving_time_key), context.getString(R.string.stats_moving_time), true, true, false);
|
|
||||||
layout.addField(context.getString(R.string.stats_custom_layout_distance_key), context.getString(R.string.stats_distance), true, true, false);
|
|
||||||
layout.addField(context.getString(R.string.stats_custom_layout_speed_key), context.getString(R.string.stats_speed), true, true, false);
|
|
||||||
layout.addField(context.getString(R.string.stats_custom_layout_average_moving_speed_key), context.getString(R.string.stats_average_moving_speed), true, true, false);
|
|
||||||
layout.addField(context.getString(R.string.stats_custom_layout_average_speed_key), context.getString(R.string.stats_average_speed), true, true, false);
|
|
||||||
layout.addField(context.getString(R.string.stats_custom_layout_max_speed_key), context.getString(R.string.stats_max_speed), true, true, false);
|
|
||||||
layout.addField(context.getString(R.string.stats_custom_layout_pace_key), context.getString(R.string.stats_pace), true, true, false);
|
|
||||||
layout.addField(context.getString(R.string.stats_custom_layout_average_moving_pace_key), context.getString(R.string.stats_average_moving_pace), true, true, false);
|
|
||||||
layout.addField(context.getString(R.string.stats_custom_layout_average_pace_key), context.getString(R.string.stats_average_pace), true, true, false);
|
|
||||||
layout.addField(context.getString(R.string.stats_custom_layout_fastest_pace_key), context.getString(R.string.stats_fastest_pace), true, true, false);
|
|
||||||
layout.addField(context.getString(R.string.stats_custom_layout_altitude_key), context.getString(R.string.stats_altitude), true, true, false);
|
|
||||||
layout.addField(context.getString(R.string.stats_custom_layout_gain_key), context.getString(R.string.stats_gain), true, true, false);
|
|
||||||
layout.addField(context.getString(R.string.stats_custom_layout_loss_key), context.getString(R.string.stats_loss), true, true, false);
|
|
||||||
layout.addField(context.getString(R.string.stats_custom_layout_coordinates_key), context.getString(R.string.stats_coordinates), true, true, false);
|
|
||||||
layout.addField(context.getString(R.string.stats_custom_layout_heart_rate_key), context.getString(R.string.stats_sensors_heart_rate), true, true, false);
|
|
||||||
layout.addField(context.getString(R.string.stats_custom_layout_cadence_key), context.getString(R.string.stats_sensors_cadence), true, true, false);
|
|
||||||
layout.addField(context.getString(R.string.stats_custom_layout_power_key), context.getString(R.string.stats_sensors_power), true, true, false);
|
|
||||||
|
|
||||||
// when
|
|
||||||
List<StatisticData> statisticDataList = StatisticDataBuilder.fromRecordingData(context, recordingDataMock, layout, UnitSystem.METRIC);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(statisticDataList.size(), 18);
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_total_time))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_moving_time))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_distance))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_speed))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_average_moving_speed))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_average_speed))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_max_speed))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_pace))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_average_moving_pace))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_average_pace))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_fastest_pace))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_altitude))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_gain))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_loss))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_coordinates))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_sensors_heart_rate))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_sensors_cadence))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_sensors_power))));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFromSensorStatistics_onlyHeartRate() {
|
|
||||||
when(sensorStatisticsMock.hasHeartRate()).thenReturn(true);
|
|
||||||
when(sensorStatisticsMock.getMaxHeartRate()).thenReturn(HeartRate.of(200f));
|
|
||||||
when(sensorStatisticsMock.getAvgHeartRate()).thenReturn(HeartRate.of(150f));
|
|
||||||
when(sensorStatisticsMock.hasCadence()).thenReturn(false);
|
|
||||||
when(sensorStatisticsMock.hasPower()).thenReturn(false);
|
|
||||||
|
|
||||||
// when
|
|
||||||
List<StatisticData> statisticDataList = StatisticDataBuilder.fromSensorStatistics(context, sensorStatisticsMock);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(statisticDataList.size(), 2);
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.sensor_state_heart_rate_max))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.sensor_state_heart_rate_avg))));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFromSensorStatistics_onlyCadence() {
|
|
||||||
when(sensorStatisticsMock.hasHeartRate()).thenReturn(false);
|
|
||||||
when(sensorStatisticsMock.hasCadence()).thenReturn(true);
|
|
||||||
when(sensorStatisticsMock.getAvgCadence()).thenReturn(Cadence.of(90f));
|
|
||||||
when(sensorStatisticsMock.getMaxCadence()).thenReturn(Cadence.of(110f));
|
|
||||||
when(sensorStatisticsMock.hasPower()).thenReturn(false);
|
|
||||||
|
|
||||||
// when
|
|
||||||
List<StatisticData> statisticDataList = StatisticDataBuilder.fromSensorStatistics(context, sensorStatisticsMock);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(statisticDataList.size(), 2);
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.sensor_state_cadence_max))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.sensor_state_cadence_avg))));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFromSensorStatistics_onlyPower() {
|
|
||||||
when(sensorStatisticsMock.hasHeartRate()).thenReturn(false);
|
|
||||||
when(sensorStatisticsMock.hasCadence()).thenReturn(false);
|
|
||||||
when(sensorStatisticsMock.hasPower()).thenReturn(true);
|
|
||||||
when(sensorStatisticsMock.getAvgPower()).thenReturn(Power.of(300f));
|
|
||||||
|
|
||||||
// when
|
|
||||||
List<StatisticData> statisticDataList = StatisticDataBuilder.fromSensorStatistics(context, sensorStatisticsMock);
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.sensor_state_power_avg))));
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(statisticDataList.size(), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFromSensorStatistics() {
|
|
||||||
when(sensorStatisticsMock.hasHeartRate()).thenReturn(true);
|
|
||||||
when(sensorStatisticsMock.getMaxHeartRate()).thenReturn(HeartRate.of(200f));
|
|
||||||
when(sensorStatisticsMock.getAvgHeartRate()).thenReturn(HeartRate.of(150f));
|
|
||||||
when(sensorStatisticsMock.hasCadence()).thenReturn(true);
|
|
||||||
when(sensorStatisticsMock.getAvgCadence()).thenReturn(Cadence.of(90f));
|
|
||||||
when(sensorStatisticsMock.getMaxCadence()).thenReturn(Cadence.of(110f));
|
|
||||||
when(sensorStatisticsMock.hasPower()).thenReturn(true);
|
|
||||||
when(sensorStatisticsMock.getAvgPower()).thenReturn(Power.of(300f));
|
|
||||||
|
|
||||||
// when
|
|
||||||
List<StatisticData> statisticDataList = StatisticDataBuilder.fromSensorStatistics(context, sensorStatisticsMock);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(statisticDataList.size(), 5);
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.sensor_state_heart_rate_max))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.sensor_state_heart_rate_avg))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.sensor_state_cadence_max))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.sensor_state_cadence_avg))));
|
|
||||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.sensor_state_power_avg))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,176 +0,0 @@
|
|||||||
package de.dennisguse.opentracks.adapters;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.TextClock;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.CustomLayoutFieldType;
|
|
||||||
import de.dennisguse.opentracks.viewmodels.StatisticData;
|
|
||||||
|
|
||||||
public class StatisticsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
|
||||||
|
|
||||||
private List<StatisticData> statisticDataList;
|
|
||||||
private final Context context;
|
|
||||||
|
|
||||||
public StatisticsAdapter(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
this.statisticDataList = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
|
||||||
View view;
|
|
||||||
if (viewType == CustomLayoutFieldType.CLOCK.value()) {
|
|
||||||
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.stats_clock_item, parent, false);
|
|
||||||
return new StatisticsAdapter.ViewClockHolder(view);
|
|
||||||
} else {
|
|
||||||
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.stats_generic_item, parent, false);
|
|
||||||
return new StatisticsAdapter.ViewHolder(view);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
|
||||||
StatisticData statisticData = statisticDataList.get(position);
|
|
||||||
if (getItemViewType(position) == CustomLayoutFieldType.CLOCK.value()) {
|
|
||||||
StatisticsAdapter.ViewClockHolder viewHolder = (StatisticsAdapter.ViewClockHolder) holder;
|
|
||||||
viewHolder.setData(statisticData);
|
|
||||||
} else {
|
|
||||||
StatisticsAdapter.ViewHolder viewHolder = (StatisticsAdapter.ViewHolder) holder;
|
|
||||||
viewHolder.setData(statisticData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount() {
|
|
||||||
if (statisticDataList == null) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return statisticDataList.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemViewType(int position) {
|
|
||||||
if (statisticDataList.get(position).getField().getType(context) == CustomLayoutFieldType.CLOCK) {
|
|
||||||
return CustomLayoutFieldType.CLOCK.value();
|
|
||||||
} else {
|
|
||||||
return CustomLayoutFieldType.GENERIC.value();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isItemWide(int position) {
|
|
||||||
return statisticDataList.get(position).getField().isWide();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<StatisticData> swapData(List<StatisticData> data) {
|
|
||||||
if (statisticDataList == data) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
statisticDataList = data;
|
|
||||||
|
|
||||||
if (data != null) {
|
|
||||||
this.notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class WithRecordedLayout extends StatisticsAdapter {
|
|
||||||
public WithRecordedLayout(Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
|
||||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.stats_recorded_item, parent, false);
|
|
||||||
return new StatisticsAdapter.ViewRecordedHolder(view);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ViewHolder extends RecyclerView.ViewHolder {
|
|
||||||
final TextView value;
|
|
||||||
final TextView unit;
|
|
||||||
final TextView descMain;
|
|
||||||
final TextView descSecondary;
|
|
||||||
|
|
||||||
public ViewHolder(@NonNull View itemView) {
|
|
||||||
super(itemView);
|
|
||||||
value = itemView.findViewById(R.id.stats_value);
|
|
||||||
unit = itemView.findViewById(R.id.stats_unit);
|
|
||||||
descMain = itemView.findViewById(R.id.stats_description_main);
|
|
||||||
descSecondary = itemView.findViewById(R.id.stats_description_secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setData(StatisticData statisticData) {
|
|
||||||
if (statisticData == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.value.setText(statisticData.hasValue() ? statisticData.getValue() : context.getString(R.string.value_unknown));
|
|
||||||
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.TextAppearance_OpenTracks_PrimaryHeader : R.style.TextAppearance_OpenTracks_SecondaryHeader);
|
|
||||||
if (statisticData.hasDescription()) {
|
|
||||||
this.descSecondary.setVisibility(View.VISIBLE);
|
|
||||||
this.descSecondary.setText(statisticData.getDescription());
|
|
||||||
} else {
|
|
||||||
this.descSecondary.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ViewClockHolder extends RecyclerView.ViewHolder {
|
|
||||||
final TextClock value;
|
|
||||||
final TextView descMain;
|
|
||||||
|
|
||||||
public ViewClockHolder(@NonNull View itemView) {
|
|
||||||
super(itemView);
|
|
||||||
value = itemView.findViewById(R.id.stats_clock);
|
|
||||||
descMain = itemView.findViewById(R.id.stats_description_main);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setData(StatisticData statisticData) {
|
|
||||||
if (statisticData == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ViewRecordedHolder extends ViewHolder {
|
|
||||||
|
|
||||||
public ViewRecordedHolder(@NonNull View itemView) {
|
|
||||||
super(itemView);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setData(StatisticData statisticData) {
|
|
||||||
if (statisticData == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.value.setText(statisticData.hasValue() ? statisticData.getValue() : context.getString(R.string.value_unknown));
|
|
||||||
this.unit.setText(statisticData.getUnit());
|
|
||||||
this.descMain.setText(statisticData.getField().getTitle());
|
|
||||||
if (statisticData.hasDescription()) {
|
|
||||||
this.descSecondary.setVisibility(View.VISIBLE);
|
|
||||||
this.descSecondary.setText(statisticData.getDescription());
|
|
||||||
} else {
|
|
||||||
this.descSecondary.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -31,11 +31,8 @@ import androidx.fragment.app.Fragment;
|
|||||||
import androidx.recyclerview.widget.GridLayoutManager;
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.TrackRecordedActivity;
|
import de.dennisguse.opentracks.TrackRecordedActivity;
|
||||||
import de.dennisguse.opentracks.adapters.StatisticsAdapter;
|
|
||||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
||||||
import de.dennisguse.opentracks.data.models.SpeedFormatter;
|
import de.dennisguse.opentracks.data.models.SpeedFormatter;
|
||||||
@@ -47,8 +44,6 @@ import de.dennisguse.opentracks.stats.SensorStatistics;
|
|||||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
import de.dennisguse.opentracks.util.StringUtils;
|
import de.dennisguse.opentracks.util.StringUtils;
|
||||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||||
import de.dennisguse.opentracks.viewmodels.StatisticData;
|
|
||||||
import de.dennisguse.opentracks.viewmodels.StatisticDataBuilder;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A fragment to display track statistics to the user for a recorded {@link Track}.
|
* A fragment to display track statistics to the user for a recorded {@link Track}.
|
||||||
@@ -72,7 +67,6 @@ public class StatisticsRecordedFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private SensorStatistics sensorStatistics;
|
private SensorStatistics sensorStatistics;
|
||||||
private StatisticsAdapter.WithRecordedLayout sensorsAdapter;
|
|
||||||
|
|
||||||
private Track.Id trackId;
|
private Track.Id trackId;
|
||||||
@Nullable // Lazily loaded.
|
@Nullable // Lazily loaded.
|
||||||
@@ -113,8 +107,6 @@ public class StatisticsRecordedFragment extends Fragment {
|
|||||||
|
|
||||||
trackId = getArguments().getParcelable(TRACK_ID_KEY);
|
trackId = getArguments().getParcelable(TRACK_ID_KEY);
|
||||||
contentProviderUtils = new ContentProviderUtils(getContext());
|
contentProviderUtils = new ContentProviderUtils(getContext());
|
||||||
|
|
||||||
sensorsAdapter = new StatisticsAdapter.WithRecordedLayout(getContext());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -123,7 +115,7 @@ public class StatisticsRecordedFragment extends Fragment {
|
|||||||
|
|
||||||
RecyclerView sensorsRecyclerView = viewBinding.statsSensorsRecyclerView;
|
RecyclerView sensorsRecyclerView = viewBinding.statsSensorsRecyclerView;
|
||||||
sensorsRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
|
sensorsRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
|
||||||
sensorsRecyclerView.setAdapter(sensorsAdapter);
|
// sensorsRecyclerView.setAdapter(sensorsAdapter);
|
||||||
|
|
||||||
return viewBinding.getRoot();
|
return viewBinding.getRoot();
|
||||||
}
|
}
|
||||||
@@ -261,10 +253,6 @@ public class StatisticsRecordedFragment extends Fragment {
|
|||||||
if (sensorStatistics == null) {
|
if (sensorStatistics == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
//TODO
|
||||||
List<StatisticData> sensorDataList = StatisticDataBuilder.fromSensorStatistics(getContext(), sensorStatistics);
|
|
||||||
if (sensorDataList.size() > 0) {
|
|
||||||
sensorsAdapter.swapData(sensorDataList);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package de.dennisguse.opentracks.fragments;
|
|||||||
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -9,16 +10,14 @@ import android.view.ViewGroup;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.gridlayout.widget.GridLayout;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
|
||||||
import androidx.recyclerview.widget.GridLayoutManager;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.adapters.StatisticsAdapter;
|
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
import de.dennisguse.opentracks.databinding.StatisticsRecordingBinding;
|
import de.dennisguse.opentracks.databinding.StatisticsRecordingBinding;
|
||||||
import de.dennisguse.opentracks.services.RecordingData;
|
import de.dennisguse.opentracks.services.RecordingData;
|
||||||
@@ -26,9 +25,10 @@ import de.dennisguse.opentracks.services.TrackRecordingService;
|
|||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
|
||||||
import de.dennisguse.opentracks.viewmodels.StatisticData;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayout;
|
||||||
import de.dennisguse.opentracks.viewmodels.StatisticsDataModel;
|
import de.dennisguse.opentracks.viewmodels.Mapping;
|
||||||
|
import de.dennisguse.opentracks.viewmodels.StatisticViewHolder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A fragment to display track statistics to the user for a currently recording {@link Track}.
|
* A fragment to display track statistics to the user for a currently recording {@link Track}.
|
||||||
@@ -48,32 +48,22 @@ public class StatisticsRecordingFragment extends Fragment {
|
|||||||
|
|
||||||
private RecordingData recordingData = TrackRecordingService.NOT_RECORDING;
|
private RecordingData recordingData = TrackRecordingService.NOT_RECORDING;
|
||||||
|
|
||||||
private Layout layout;
|
private final List<StatisticViewHolder<?>> viewHolders = new LinkedList<>();
|
||||||
|
|
||||||
|
private RecordingLayout recordingLayout;
|
||||||
|
|
||||||
private StatisticsRecordingBinding viewBinding;
|
private StatisticsRecordingBinding viewBinding;
|
||||||
private StatisticsAdapter statisticsAdapter;
|
|
||||||
private GridLayoutManager gridLayoutManager;
|
|
||||||
private StatisticsDataModel viewModel;
|
|
||||||
private LiveData<List<StatisticData>> statisticsLiveData;
|
|
||||||
|
|
||||||
private UnitSystem unitSystem = UnitSystem.defaultUnitSystem();
|
private UnitSystem unitSystem = UnitSystem.defaultUnitSystem();
|
||||||
|
|
||||||
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
|
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
|
||||||
boolean updateUInecessary = false;
|
|
||||||
|
|
||||||
if (PreferencesUtils.isKey(R.string.stats_units_key, key)) {
|
if (PreferencesUtils.isKey(R.string.stats_units_key, key)) {
|
||||||
updateUInecessary = true;
|
|
||||||
unitSystem = PreferencesUtils.getUnitSystem();
|
unitSystem = PreferencesUtils.getUnitSystem();
|
||||||
|
updateDataOnUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PreferencesUtils.isKey(R.string.stats_custom_layouts_key, key) || PreferencesUtils.isKey(R.string.stats_custom_layout_selected_layout_key, key)) {
|
if (PreferencesUtils.isKey(R.string.stats_custom_layouts_key, key) || PreferencesUtils.isKey(R.string.stats_custom_layout_selected_layout_key, key)) {
|
||||||
updateUInecessary = true;
|
onLayoutChanged(PreferencesUtils.getCustomLayout());
|
||||||
layout = PreferencesUtils.getCustomLayout();
|
|
||||||
gridLayoutManager.setSpanCount(layout.getColumnsPerRow());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key != null && updateUInecessary && isResumed()) {
|
|
||||||
getActivity().runOnUiThread(this::updateUI);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -84,30 +74,11 @@ public class StatisticsRecordingFragment extends Fragment {
|
|||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
|
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
|
||||||
|
|
||||||
statisticsAdapter = new StatisticsAdapter(getContext());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
viewBinding = StatisticsRecordingBinding.inflate(inflater, container, false);
|
viewBinding = StatisticsRecordingBinding.inflate(inflater, container, false);
|
||||||
|
|
||||||
RecyclerView recyclerView = viewBinding.statsRecyclerView;
|
|
||||||
recyclerView.addItemDecoration(new DividerItemDecoration(getContext(), RecyclerView.VERTICAL));
|
|
||||||
layout = PreferencesUtils.getCustomLayout();
|
|
||||||
gridLayoutManager = new GridLayoutManager(getContext(), layout.getColumnsPerRow());
|
|
||||||
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
|
|
||||||
@Override
|
|
||||||
public int getSpanSize(int position) {
|
|
||||||
if (statisticsAdapter.isItemWide(position)) {
|
|
||||||
return layout.getColumnsPerRow();
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
recyclerView.setLayoutManager(gridLayoutManager);
|
|
||||||
recyclerView.setAdapter(statisticsAdapter);
|
|
||||||
|
|
||||||
return viewBinding.getRoot();
|
return viewBinding.getRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,10 +88,6 @@ public class StatisticsRecordingFragment extends Fragment {
|
|||||||
|
|
||||||
PreferencesUtils.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
PreferencesUtils.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
|
|
||||||
viewModel = new ViewModelProvider(getActivity()).get(StatisticsDataModel.class);
|
|
||||||
statisticsLiveData = viewModel.getStatsData();
|
|
||||||
statisticsLiveData.observe(getActivity(), statsDataList -> statisticsAdapter.swapData(statsDataList));
|
|
||||||
|
|
||||||
trackRecordingServiceConnection.startConnection(getContext());
|
trackRecordingServiceConnection.startConnection(getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,6 +100,7 @@ public class StatisticsRecordingFragment extends Fragment {
|
|||||||
@Override
|
@Override
|
||||||
public void onStop() {
|
public void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
|
viewHolders.clear();
|
||||||
trackRecordingServiceConnection.unbind(getContext());
|
trackRecordingServiceConnection.unbind(getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,17 +114,56 @@ public class StatisticsRecordingFragment extends Fragment {
|
|||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
trackRecordingServiceConnection = null;
|
trackRecordingServiceConnection = null;
|
||||||
viewModel = null;
|
|
||||||
|
|
||||||
if (statisticsLiveData != null) {
|
|
||||||
statisticsLiveData.removeObservers(getActivity());
|
|
||||||
}
|
|
||||||
statisticsLiveData = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateUI() {
|
private void onLayoutChanged(@NonNull RecordingLayout newRecordingLayout) {
|
||||||
if (isResumed()) {
|
if (newRecordingLayout.equals(recordingLayout)) {
|
||||||
viewModel.update(recordingData, layout, unitSystem);
|
return;
|
||||||
|
}
|
||||||
|
recordingLayout = newRecordingLayout;
|
||||||
|
|
||||||
|
viewBinding.statsLayout.setColumnCount(recordingLayout.getColumnsPerRow());
|
||||||
|
|
||||||
|
viewBinding.statsLayout.removeAllViews(); //Let's start from scratch
|
||||||
|
viewHolders.clear();
|
||||||
|
|
||||||
|
Map<String, Callable<StatisticViewHolder<?>>> m = Mapping.create(getContext());
|
||||||
|
|
||||||
|
int rowIndex = 0;
|
||||||
|
int columnIndex = 0;
|
||||||
|
for (int i = 0; i < recordingLayout.getFields().size(); i++) {
|
||||||
|
DataField dataField = recordingLayout.getFields().get(i);
|
||||||
|
GridLayout.LayoutParams param = new GridLayout.LayoutParams();
|
||||||
|
param.setGravity(Gravity.FILL_HORIZONTAL);
|
||||||
|
param.width = 0;
|
||||||
|
|
||||||
|
if (dataField.isWide()) {
|
||||||
|
rowIndex++;
|
||||||
|
param.columnSpec = GridLayout.spec(0, recordingLayout.getColumnsPerRow(), 1);
|
||||||
|
param.rowSpec = GridLayout.spec(rowIndex, 1, 1);
|
||||||
|
columnIndex = 0;
|
||||||
|
rowIndex++;
|
||||||
|
} else {
|
||||||
|
if (columnIndex >= recordingLayout.getColumnsPerRow()) {
|
||||||
|
columnIndex = 0;
|
||||||
|
rowIndex++;
|
||||||
|
}
|
||||||
|
param.columnSpec = GridLayout.spec(columnIndex, 1, 1);
|
||||||
|
param.rowSpec = GridLayout.spec(rowIndex, 1, 1);
|
||||||
|
|
||||||
|
columnIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
StatisticViewHolder<?> viewHolder = m.get(dataField.getKey()).call();
|
||||||
|
viewHolder.initialize(getContext(), getLayoutInflater());
|
||||||
|
viewHolder.configureUI(dataField);
|
||||||
|
viewHolders.add(viewHolder);
|
||||||
|
|
||||||
|
viewBinding.statsLayout.addView(viewHolder.getView(), param);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,6 +176,12 @@ public class StatisticsRecordingFragment extends Fragment {
|
|||||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, getString(R.string.stats_rate_key));
|
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, getString(R.string.stats_rate_key));
|
||||||
}
|
}
|
||||||
|
|
||||||
updateUI();
|
updateDataOnUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDataOnUI() {
|
||||||
|
if (isResumed()) {
|
||||||
|
viewHolders.forEach(i -> i.onChanged(unitSystem, recordingData));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,8 @@ import de.dennisguse.opentracks.data.models.Speed;
|
|||||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||||
import de.dennisguse.opentracks.io.file.TrackFilenameGenerator;
|
import de.dennisguse.opentracks.io.file.TrackFilenameGenerator;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.CsvLayoutUtils;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.CsvLayoutUtils;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayout;
|
||||||
|
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayoutIO;
|
||||||
import de.dennisguse.opentracks.util.IntentDashboardUtils;
|
import de.dennisguse.opentracks.util.IntentDashboardUtils;
|
||||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||||
|
|
||||||
@@ -753,35 +754,35 @@ public class PreferencesUtils {
|
|||||||
/**
|
/**
|
||||||
* @return custom layout selected or the first one if any has been selected or the one selected is not exists anymore.
|
* @return custom layout selected or the first one if any has been selected or the one selected is not exists anymore.
|
||||||
*/
|
*/
|
||||||
public static Layout getCustomLayout() {
|
public static RecordingLayout getCustomLayout() {
|
||||||
String csvCustomLayouts = getString(R.string.stats_custom_layouts_key, buildDefaultLayout());
|
String csvCustomLayouts = getString(R.string.stats_custom_layouts_key, buildDefaultLayout());
|
||||||
String[] csvLines = csvCustomLayouts.split(CsvLayoutUtils.LINE_SEPARATOR);
|
String[] csvLines = csvCustomLayouts.split(CsvLayoutUtils.LINE_SEPARATOR);
|
||||||
String layoutSelected = getString(R.string.stats_custom_layout_selected_layout_key, null);
|
String layoutSelected = getString(R.string.stats_custom_layout_selected_layout_key, null);
|
||||||
if (layoutSelected == null) {
|
if (layoutSelected == null) {
|
||||||
return Layout.fromCsv(csvLines[0], resources);
|
return RecordingLayoutIO.fromCsv(csvLines[0], resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String line : csvLines) {
|
for (String line : csvLines) {
|
||||||
Layout layout = Layout.fromCsv(line, resources);
|
RecordingLayout recordingLayout = RecordingLayoutIO.fromCsv(line, resources);
|
||||||
if (layout.sameName(layoutSelected)) {
|
if (recordingLayout.sameName(layoutSelected)) {
|
||||||
return layout;
|
return recordingLayout;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Layout.fromCsv(csvLines[0], resources);
|
return RecordingLayoutIO.fromCsv(csvLines[0], resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void updateCustomLayouts(@NonNull List<Layout> layouts) {
|
public static void updateCustomLayouts(@NonNull List<RecordingLayout> recordingLayouts) {
|
||||||
setString(R.string.stats_custom_layouts_key, layouts.stream().map(Layout::toCsv).collect(Collectors.joining(CsvLayoutUtils.LINE_SEPARATOR)));
|
setString(R.string.stats_custom_layouts_key, RecordingLayoutIO.toCSV(recordingLayouts));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void updateCustomLayout(@NonNull Layout layout) {
|
public static void updateCustomLayout(@NonNull RecordingLayout recordingLayout) {
|
||||||
List<Layout> preferenceLayouts = PreferencesUtils.getAllCustomLayouts();
|
List<RecordingLayout> preferenceRecordingLayouts = PreferencesUtils.getAllCustomLayouts();
|
||||||
Optional<Layout> layoutToBeUpdated = preferenceLayouts.stream().filter(l -> l.sameName(layout)).findFirst();
|
Optional<RecordingLayout> layoutToBeUpdated = preferenceRecordingLayouts.stream().filter(l -> l.sameName(recordingLayout)).findFirst();
|
||||||
if (layoutToBeUpdated.isPresent()) {
|
if (layoutToBeUpdated.isPresent()) {
|
||||||
layoutToBeUpdated.get().replaceAllFields(layout.getFields());
|
layoutToBeUpdated.get().replaceAllFields(recordingLayout.getFields());
|
||||||
layoutToBeUpdated.get().setColumnsPerRow(layout.getColumnsPerRow());
|
layoutToBeUpdated.get().setColumnsPerRow(recordingLayout.getColumnsPerRow());
|
||||||
PreferencesUtils.updateCustomLayouts(preferenceLayouts);
|
PreferencesUtils.updateCustomLayouts(preferenceRecordingLayouts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -795,19 +796,19 @@ public class PreferencesUtils {
|
|||||||
setString(R.string.stats_custom_layout_selected_layout_key, layoutName);
|
setString(R.string.stats_custom_layout_selected_layout_key, layoutName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Layout> getAllCustomLayouts() {
|
public static List<RecordingLayout> getAllCustomLayouts() {
|
||||||
List<Layout> layouts = new ArrayList<>();
|
List<RecordingLayout> recordingLayouts = new ArrayList<>();
|
||||||
String csvCustomLayout = getString(R.string.stats_custom_layouts_key, buildDefaultLayout());
|
String csvCustomLayout = getString(R.string.stats_custom_layouts_key, buildDefaultLayout());
|
||||||
String[] csvLines = csvCustomLayout.split(CsvLayoutUtils.LINE_SEPARATOR);
|
String[] csvLines = csvCustomLayout.split(CsvLayoutUtils.LINE_SEPARATOR);
|
||||||
for (String line : csvLines) {
|
for (String line : csvLines) {
|
||||||
layouts.add(Layout.fromCsv(line, resources));
|
recordingLayouts.add(RecordingLayoutIO.fromCsv(line, resources));
|
||||||
}
|
}
|
||||||
|
|
||||||
return layouts;
|
return recordingLayouts;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<String> getAllCustomLayoutNames() {
|
public static List<String> getAllCustomLayoutNames() {
|
||||||
return getAllCustomLayouts().stream().map(Layout::getName).collect(Collectors.toList());
|
return getAllCustomLayouts().stream().map(RecordingLayout::getName).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void resetCustomLayoutPreferences() {
|
public static void resetCustomLayoutPreferences() {
|
||||||
|
|||||||
+25
-25
@@ -17,7 +17,7 @@ import de.dennisguse.opentracks.AbstractActivity;
|
|||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.databinding.ActivitySettingsCustomLayoutBinding;
|
import de.dennisguse.opentracks.databinding.ActivitySettingsCustomLayoutBinding;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayout;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.SettingsCustomLayoutEditAdapter;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.SettingsCustomLayoutEditAdapter;
|
||||||
import de.dennisguse.opentracks.ui.util.ArrayAdapterFilterDisabled;
|
import de.dennisguse.opentracks.ui.util.ArrayAdapterFilterDisabled;
|
||||||
import de.dennisguse.opentracks.util.StatisticsUtils;
|
import de.dennisguse.opentracks.util.StatisticsUtils;
|
||||||
@@ -30,8 +30,8 @@ public class SettingsCustomLayoutEditActivity extends AbstractActivity implement
|
|||||||
private SettingsCustomLayoutEditAdapter adapterFieldsVisible;
|
private SettingsCustomLayoutEditAdapter adapterFieldsVisible;
|
||||||
private SettingsCustomLayoutEditAdapter adapterFieldsHidden;
|
private SettingsCustomLayoutEditAdapter adapterFieldsHidden;
|
||||||
private String profile;
|
private String profile;
|
||||||
private Layout layoutFieldsVisible;
|
private RecordingLayout recordingLayoutFieldsVisible;
|
||||||
private Layout layoutFieldsHidden;
|
private RecordingLayout recordingLayoutFieldsHidden;
|
||||||
private int numColumns;
|
private int numColumns;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -39,12 +39,12 @@ public class SettingsCustomLayoutEditActivity extends AbstractActivity implement
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
// Recycler view with visible stats.
|
// Recycler view with visible stats.
|
||||||
Layout layout = getIntent().getParcelableExtra(EXTRA_LAYOUT);
|
RecordingLayout recordingLayout = getIntent().getParcelableExtra(EXTRA_LAYOUT);
|
||||||
profile = layout.getName();
|
profile = recordingLayout.getName();
|
||||||
layoutFieldsVisible = StatisticsUtils.filterVisible(layout, true);
|
recordingLayoutFieldsVisible = StatisticsUtils.filterVisible(recordingLayout, true);
|
||||||
adapterFieldsVisible = new SettingsCustomLayoutEditAdapter(this, this, layoutFieldsVisible);
|
adapterFieldsVisible = new SettingsCustomLayoutEditAdapter(this, this, recordingLayoutFieldsVisible);
|
||||||
|
|
||||||
numColumns = layout.getColumnsPerRow();
|
numColumns = recordingLayout.getColumnsPerRow();
|
||||||
RecyclerView recyclerViewVisible = viewBinding.recyclerViewVisible;
|
RecyclerView recyclerViewVisible = viewBinding.recyclerViewVisible;
|
||||||
gridLayoutManager = new GridLayoutManager(this, numColumns);
|
gridLayoutManager = new GridLayoutManager(this, numColumns);
|
||||||
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
|
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
|
||||||
@@ -65,7 +65,7 @@ public class SettingsCustomLayoutEditActivity extends AbstractActivity implement
|
|||||||
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
|
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
|
||||||
int fromPosition = viewHolder.getAdapterPosition();
|
int fromPosition = viewHolder.getAdapterPosition();
|
||||||
int toPosition = target.getAdapterPosition();
|
int toPosition = target.getAdapterPosition();
|
||||||
layoutFieldsVisible = adapterFieldsVisible.move(fromPosition, toPosition);
|
recordingLayoutFieldsVisible = adapterFieldsVisible.move(fromPosition, toPosition);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,8 +90,8 @@ public class SettingsCustomLayoutEditActivity extends AbstractActivity implement
|
|||||||
viewBinding.rowsOptions.setText(rowsOptionAdapter.getItem(numColumns - 1).toString(), false);
|
viewBinding.rowsOptions.setText(rowsOptionAdapter.getItem(numColumns - 1).toString(), false);
|
||||||
|
|
||||||
// Recycler view with not visible stats.
|
// Recycler view with not visible stats.
|
||||||
layoutFieldsHidden = StatisticsUtils.filterVisible(layout, false);
|
recordingLayoutFieldsHidden = StatisticsUtils.filterVisible(recordingLayout, false);
|
||||||
adapterFieldsHidden = new SettingsCustomLayoutEditAdapter(this, this, layoutFieldsHidden);
|
adapterFieldsHidden = new SettingsCustomLayoutEditAdapter(this, this, recordingLayoutFieldsHidden);
|
||||||
RecyclerView recyclerViewNotVisible = viewBinding.recyclerViewNotVisible;
|
RecyclerView recyclerViewNotVisible = viewBinding.recyclerViewNotVisible;
|
||||||
recyclerViewNotVisible.setLayoutManager(new LinearLayoutManager(this));
|
recyclerViewNotVisible.setLayoutManager(new LinearLayoutManager(this));
|
||||||
recyclerViewNotVisible.setAdapter(adapterFieldsHidden);
|
recyclerViewNotVisible.setAdapter(adapterFieldsHidden);
|
||||||
@@ -103,18 +103,18 @@ public class SettingsCustomLayoutEditActivity extends AbstractActivity implement
|
|||||||
@Override
|
@Override
|
||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
if (!layoutFieldsVisible.getFields().isEmpty() || !layoutFieldsHidden.getFields().isEmpty()) {
|
if (!recordingLayoutFieldsVisible.getFields().isEmpty() || !recordingLayoutFieldsHidden.getFields().isEmpty()) {
|
||||||
Layout layout = new Layout(profile, numColumns);
|
RecordingLayout recordingLayout = new RecordingLayout(profile, numColumns);
|
||||||
layout.addFields(layoutFieldsVisible.getFields());
|
recordingLayout.addFields(recordingLayoutFieldsVisible.getFields());
|
||||||
layout.addFields(layoutFieldsHidden.getFields());
|
recordingLayout.addFields(recordingLayoutFieldsHidden.getFields());
|
||||||
PreferencesUtils.updateCustomLayout(layout);
|
PreferencesUtils.updateCustomLayout(recordingLayout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
layoutFieldsVisible = null;
|
recordingLayoutFieldsVisible = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -132,24 +132,24 @@ public class SettingsCustomLayoutEditActivity extends AbstractActivity implement
|
|||||||
if (which == 0) {
|
if (which == 0) {
|
||||||
field.togglePrimary();
|
field.togglePrimary();
|
||||||
} else {
|
} else {
|
||||||
layoutFieldsVisible.removeField(field);
|
recordingLayoutFieldsVisible.removeField(field);
|
||||||
field.toggleVisibility();
|
field.toggleVisibility();
|
||||||
layoutFieldsHidden.addField(field);
|
recordingLayoutFieldsHidden.addField(field);
|
||||||
}
|
}
|
||||||
|
|
||||||
adapterFieldsVisible.swapValues(layoutFieldsVisible);
|
adapterFieldsVisible.swapValues(recordingLayoutFieldsVisible);
|
||||||
adapterFieldsHidden.swapValues(layoutFieldsHidden);
|
adapterFieldsHidden.swapValues(recordingLayoutFieldsHidden);
|
||||||
})
|
})
|
||||||
.create()
|
.create()
|
||||||
.show();
|
.show();
|
||||||
} else {
|
} else {
|
||||||
layoutFieldsHidden.removeField(field);
|
recordingLayoutFieldsHidden.removeField(field);
|
||||||
field.toggleVisibility();
|
field.toggleVisibility();
|
||||||
layoutFieldsVisible.addField(field);
|
recordingLayoutFieldsVisible.addField(field);
|
||||||
viewBinding.scrollView.fullScroll(ScrollView.FOCUS_UP);
|
viewBinding.scrollView.fullScroll(ScrollView.FOCUS_UP);
|
||||||
|
|
||||||
adapterFieldsVisible.swapValues(layoutFieldsVisible);
|
adapterFieldsVisible.swapValues(recordingLayoutFieldsVisible);
|
||||||
adapterFieldsHidden.swapValues(layoutFieldsHidden);
|
adapterFieldsHidden.swapValues(recordingLayoutFieldsHidden);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -23,7 +23,7 @@ import com.google.android.material.textfield.TextInputLayout;
|
|||||||
import de.dennisguse.opentracks.AbstractActivity;
|
import de.dennisguse.opentracks.AbstractActivity;
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.databinding.ActivitySettingsCustomLayoutListBinding;
|
import de.dennisguse.opentracks.databinding.ActivitySettingsCustomLayoutListBinding;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayout;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.SettingsCustomLayoutListAdapter;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.SettingsCustomLayoutListAdapter;
|
||||||
import de.dennisguse.opentracks.ui.util.RecyclerViewSwipeDeleteCallback;
|
import de.dennisguse.opentracks.ui.util.RecyclerViewSwipeDeleteCallback;
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ public class SettingsCustomLayoutListActivity extends AbstractActivity implement
|
|||||||
@Override
|
@Override
|
||||||
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
|
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
|
||||||
final int position = viewHolder.getAdapterPosition();
|
final int position = viewHolder.getAdapterPosition();
|
||||||
final Layout item = adapter.getLayouts().get(position);
|
final RecordingLayout item = adapter.getLayouts().get(position);
|
||||||
|
|
||||||
adapter.removeLayout(position);
|
adapter.removeLayout(position);
|
||||||
|
|
||||||
@@ -150,9 +150,9 @@ public class SettingsCustomLayoutListActivity extends AbstractActivity implement
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSettingsCustomLayoutProfileClicked(@NonNull Layout layout) {
|
public void onSettingsCustomLayoutProfileClicked(@NonNull RecordingLayout recordingLayout) {
|
||||||
Intent intent = new Intent(this, SettingsCustomLayoutEditActivity.class);
|
Intent intent = new Intent(this, SettingsCustomLayoutEditActivity.class);
|
||||||
intent.putExtra(SettingsCustomLayoutEditActivity.EXTRA_LAYOUT, layout);
|
intent.putExtra(SettingsCustomLayoutEditActivity.EXTRA_LAYOUT, recordingLayout);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,19 @@
|
|||||||
package de.dennisguse.opentracks.ui.customRecordingLayout;
|
package de.dennisguse.opentracks.ui.customRecordingLayout;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class DataField implements Parcelable {
|
public class DataField implements Parcelable {
|
||||||
public static final String YES_VALUE = "1";
|
|
||||||
public static final String NOT_VALUE = "0";
|
|
||||||
|
|
||||||
private final String key;
|
private final String key;
|
||||||
private final String title;
|
|
||||||
private boolean isVisible;
|
private boolean isVisible;
|
||||||
private boolean isPrimary;
|
private boolean isPrimary;
|
||||||
private final boolean isWide;
|
private final boolean isWide;
|
||||||
|
|
||||||
public DataField(String key, String title, boolean isVisible, boolean isPrimary, boolean isWide) {
|
public DataField(String key, boolean isVisible, boolean isPrimary, boolean isWide) {
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.title = title;
|
|
||||||
this.isVisible = isVisible;
|
this.isVisible = isVisible;
|
||||||
this.isPrimary = isPrimary;
|
this.isPrimary = isPrimary;
|
||||||
this.isWide = isWide;
|
this.isWide = isWide;
|
||||||
@@ -27,7 +21,6 @@ public class DataField implements Parcelable {
|
|||||||
|
|
||||||
protected DataField(Parcel in) {
|
protected DataField(Parcel in) {
|
||||||
key = in.readString();
|
key = in.readString();
|
||||||
title = in.readString();
|
|
||||||
isVisible = in.readByte() != 0;
|
isVisible = in.readByte() != 0;
|
||||||
isPrimary = in.readByte() != 0;
|
isPrimary = in.readByte() != 0;
|
||||||
isWide = in.readByte() != 0;
|
isWide = in.readByte() != 0;
|
||||||
@@ -49,10 +42,6 @@ public class DataField implements Parcelable {
|
|||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTitle() {
|
|
||||||
return title;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isVisible() {
|
public boolean isVisible() {
|
||||||
return isVisible;
|
return isVisible;
|
||||||
}
|
}
|
||||||
@@ -73,65 +62,6 @@ public class DataField implements Parcelable {
|
|||||||
return isWide;
|
return isWide;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CustomLayoutFieldType getType(Context context) {
|
|
||||||
if (key.equals(context.getString(R.string.stats_custom_layout_clock_key))) {
|
|
||||||
return CustomLayoutFieldType.CLOCK;
|
|
||||||
} else {
|
|
||||||
return CustomLayoutFieldType.GENERIC;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toCsv() {
|
|
||||||
String visible = this.isVisible ? YES_VALUE : NOT_VALUE;
|
|
||||||
String primary = this.isPrimary ? YES_VALUE : NOT_VALUE;
|
|
||||||
String wide = this.isWide ? YES_VALUE : NOT_VALUE;
|
|
||||||
return key + CsvLayoutUtils.PROPERTY_SEPARATOR + visible + CsvLayoutUtils.PROPERTY_SEPARATOR + primary + CsvLayoutUtils.PROPERTY_SEPARATOR + wide;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getTitleByKey(Resources resources, String key) {
|
|
||||||
if (key.equals(resources.getString(R.string.stats_custom_layout_total_time_key))) {
|
|
||||||
return resources.getString(R.string.stats_total_time);
|
|
||||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_moving_time_key))) {
|
|
||||||
return resources.getString(R.string.stats_moving_time);
|
|
||||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_distance_key))) {
|
|
||||||
return resources.getString(R.string.stats_distance);
|
|
||||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_speed_key))) {
|
|
||||||
return resources.getString(R.string.stats_speed);
|
|
||||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_pace_key))) {
|
|
||||||
return resources.getString(R.string.stats_pace);
|
|
||||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_average_moving_speed_key))) {
|
|
||||||
return resources.getString(R.string.stats_average_moving_speed);
|
|
||||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_average_speed_key))) {
|
|
||||||
return resources.getString(R.string.stats_average_speed);
|
|
||||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_max_speed_key))) {
|
|
||||||
return resources.getString(R.string.stats_max_speed);
|
|
||||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_average_moving_pace_key))) {
|
|
||||||
return resources.getString(R.string.stats_average_moving_pace);
|
|
||||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_average_pace_key))) {
|
|
||||||
return resources.getString(R.string.stats_average_pace);
|
|
||||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_fastest_pace_key))) {
|
|
||||||
return resources.getString(R.string.stats_fastest_pace);
|
|
||||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_altitude_key))) {
|
|
||||||
return resources.getString(R.string.stats_altitude);
|
|
||||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_gain_key))) {
|
|
||||||
return resources.getString(R.string.stats_gain);
|
|
||||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_loss_key))) {
|
|
||||||
return resources.getString(R.string.stats_loss);
|
|
||||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_coordinates_key))) {
|
|
||||||
return resources.getString(R.string.stats_coordinates);
|
|
||||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_heart_rate_key))) {
|
|
||||||
return resources.getString(R.string.stats_sensors_heart_rate);
|
|
||||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_cadence_key))) {
|
|
||||||
return resources.getString(R.string.stats_sensors_cadence);
|
|
||||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_power_key))) {
|
|
||||||
return resources.getString(R.string.stats_sensors_power);
|
|
||||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_clock_key))) {
|
|
||||||
return resources.getString(R.string.stats_clock);
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException("It doesn't exists a field with key: " + key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int describeContents() {
|
public int describeContents() {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -140,9 +70,21 @@ public class DataField implements Parcelable {
|
|||||||
@Override
|
@Override
|
||||||
public void writeToParcel(Parcel parcel, int i) {
|
public void writeToParcel(Parcel parcel, int i) {
|
||||||
parcel.writeString(key);
|
parcel.writeString(key);
|
||||||
parcel.writeString(title);
|
|
||||||
parcel.writeByte((byte) (isVisible ? 1 : 0));
|
parcel.writeByte((byte) (isVisible ? 1 : 0));
|
||||||
parcel.writeByte((byte) (isPrimary ? 1 : 0));
|
parcel.writeByte((byte) (isPrimary ? 1 : 0));
|
||||||
parcel.writeByte((byte) (isWide ? 1 : 0));
|
parcel.writeByte((byte) (isWide ? 1 : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
DataField dataField = (DataField) o;
|
||||||
|
return isVisible == dataField.isVisible && isPrimary == dataField.isPrimary && isWide == dataField.isWide && Objects.equals(key, dataField.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(key, isVisible, isPrimary, isWide);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-41
@@ -1,78 +1,50 @@
|
|||||||
package de.dennisguse.opentracks.ui.customRecordingLayout;
|
package de.dennisguse.opentracks.ui.customRecordingLayout;
|
||||||
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
|
||||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||||
|
|
||||||
public class Layout implements Parcelable {
|
public class RecordingLayout implements Parcelable {
|
||||||
private static final String TAG = Layout.class.getSimpleName();
|
|
||||||
|
|
||||||
// User-generated layout's name.
|
// User-generated layout's name.
|
||||||
private final String name;
|
private final String name;
|
||||||
private int columnsPerRow;
|
private int columnsPerRow;
|
||||||
private final List<DataField> dataFields = new ArrayList<>();
|
private final List<DataField> dataFields = new ArrayList<>();
|
||||||
|
|
||||||
public static Layout fromCsv(@NonNull String csvLine, @NonNull Resources resources) {
|
public RecordingLayout(String name) {
|
||||||
List<String> csvParts = CsvLayoutUtils.getCsvLineParts(csvLine);
|
|
||||||
if (csvParts == null) {
|
|
||||||
Log.e(TAG, "Invalid CSV layout. It shouldn't happen: " + csvLine);
|
|
||||||
return new Layout(PreferencesUtils.getDefaultLayoutName());
|
|
||||||
}
|
|
||||||
|
|
||||||
Layout layout = new Layout(csvParts.get(0), Integer.parseInt(csvParts.get(1)));
|
|
||||||
for (int i = 2; i < csvParts.size(); i++) {
|
|
||||||
String[] fieldParts = CsvLayoutUtils.getCsvFieldParts(csvParts.get(i));
|
|
||||||
if (fieldParts == null) {
|
|
||||||
Log.e(TAG, "Invalid CSV layout. It shouldn't happen: " + csvLine);
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
layout.addField(fieldParts[0], DataField.getTitleByKey(resources, fieldParts[0]), fieldParts[1].equals(DataField.YES_VALUE), fieldParts[2].equals(DataField.YES_VALUE), fieldParts[0].equals(resources.getString(R.string.stats_custom_layout_coordinates_key)));
|
|
||||||
}
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Layout(String name) {
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.columnsPerRow = PreferencesUtils.getLayoutColumnsByDefault();
|
this.columnsPerRow = PreferencesUtils.getLayoutColumnsByDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Layout(String name, int columnsPerRow) {
|
public RecordingLayout(String name, int columnsPerRow) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.columnsPerRow = columnsPerRow;
|
this.columnsPerRow = columnsPerRow;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Layout(Parcel in) {
|
protected RecordingLayout(Parcel in) {
|
||||||
name = in.readString();
|
name = in.readString();
|
||||||
columnsPerRow = in.readInt();
|
columnsPerRow = in.readInt();
|
||||||
in.readList(dataFields, DataField.class.getClassLoader());
|
in.readList(dataFields, DataField.class.getClassLoader());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Creator<Layout> CREATOR = new Creator<>() {
|
public static final Creator<RecordingLayout> CREATOR = new Creator<>() {
|
||||||
@Override
|
@Override
|
||||||
public Layout createFromParcel(Parcel in) {
|
public RecordingLayout createFromParcel(Parcel in) {
|
||||||
return new Layout(in);
|
return new RecordingLayout(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Layout[] newArray(int size) {
|
public RecordingLayout[] newArray(int size) {
|
||||||
return new Layout[size];
|
return new RecordingLayout[size];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public void addField(String key, String title, boolean visible, boolean primary, boolean isWide) {
|
|
||||||
dataFields.add(new DataField(key, title, visible, primary, isWide));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addField(DataField dataField) {
|
public void addField(DataField dataField) {
|
||||||
dataFields.add(dataField);
|
dataFields.add(dataField);
|
||||||
}
|
}
|
||||||
@@ -111,8 +83,8 @@ public class Layout implements Parcelable {
|
|||||||
this.columnsPerRow = columnsPerRow;
|
this.columnsPerRow = columnsPerRow;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean sameName(Layout layout) {
|
public boolean sameName(RecordingLayout recordingLayout) {
|
||||||
return this.name.equalsIgnoreCase(layout.getName());
|
return this.name.equalsIgnoreCase(recordingLayout.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean sameName(String name) {
|
public boolean sameName(String name) {
|
||||||
@@ -126,7 +98,7 @@ public class Layout implements Parcelable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return getName() + CsvLayoutUtils.ITEM_SEPARATOR + getColumnsPerRow() + CsvLayoutUtils.ITEM_SEPARATOR
|
return getName() + CsvLayoutUtils.ITEM_SEPARATOR + getColumnsPerRow() + CsvLayoutUtils.ITEM_SEPARATOR
|
||||||
+ fields.stream().map(DataField::toCsv).collect(Collectors.joining(CsvLayoutUtils.ITEM_SEPARATOR))
|
+ fields.stream().map(RecordingLayoutIO::toCsv).collect(Collectors.joining(CsvLayoutUtils.ITEM_SEPARATOR))
|
||||||
+ CsvLayoutUtils.ITEM_SEPARATOR;
|
+ CsvLayoutUtils.ITEM_SEPARATOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,4 +113,17 @@ public class Layout implements Parcelable {
|
|||||||
parcel.writeInt(columnsPerRow);
|
parcel.writeInt(columnsPerRow);
|
||||||
parcel.writeList(dataFields);
|
parcel.writeList(dataFields);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
RecordingLayout recordingLayout = (RecordingLayout) o;
|
||||||
|
return columnsPerRow == recordingLayout.columnsPerRow && Objects.equals(name, recordingLayout.name) && Objects.equals(dataFields, recordingLayout.dataFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(name, columnsPerRow, dataFields);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
package de.dennisguse.opentracks.ui.customRecordingLayout;
|
||||||
|
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.R;
|
||||||
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||||
|
|
||||||
|
public class RecordingLayoutIO {
|
||||||
|
|
||||||
|
private static final String TAG = RecordingLayout.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final String YES_VALUE = "1";
|
||||||
|
private static final String NOT_VALUE = "0";
|
||||||
|
|
||||||
|
public static RecordingLayout fromCsv(@NonNull String csvLine, @NonNull Resources resources) {
|
||||||
|
List<String> csvParts = CsvLayoutUtils.getCsvLineParts(csvLine);
|
||||||
|
if (csvParts == null) {
|
||||||
|
Log.e(TAG, "Invalid CSV layout. It shouldn't happen: " + csvLine);
|
||||||
|
return new RecordingLayout(PreferencesUtils.getDefaultLayoutName());
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordingLayout recordingLayout = new RecordingLayout(csvParts.get(0), Integer.parseInt(csvParts.get(1)));
|
||||||
|
for (int i = 2; i < csvParts.size(); i++) {
|
||||||
|
String[] fieldParts = CsvLayoutUtils.getCsvFieldParts(csvParts.get(i));
|
||||||
|
if (fieldParts == null) {
|
||||||
|
Log.e(TAG, "Invalid CSV layout. It shouldn't happen: " + csvLine);
|
||||||
|
return recordingLayout;
|
||||||
|
}
|
||||||
|
recordingLayout.addField(fromCSV(fieldParts, resources));
|
||||||
|
}
|
||||||
|
return recordingLayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toCSV(List<RecordingLayout> recordingLayouts) {
|
||||||
|
return recordingLayouts.stream().map(RecordingLayout::toCsv).collect(Collectors.joining(CsvLayoutUtils.LINE_SEPARATOR));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DataField fromCSV(String[] fieldParts, @NonNull Resources resources) {
|
||||||
|
return new DataField(
|
||||||
|
fieldParts[0],
|
||||||
|
fieldParts[1].equals(YES_VALUE),
|
||||||
|
fieldParts[2].equals(YES_VALUE),
|
||||||
|
fieldParts[0].equals(resources.getString(R.string.stats_custom_layout_coordinates_key)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static String toCsv(DataField datafield) {
|
||||||
|
String visible = datafield.isVisible() ? YES_VALUE : NOT_VALUE;
|
||||||
|
String primary = datafield.isPrimary() ? YES_VALUE : NOT_VALUE;
|
||||||
|
String wide = datafield.isWide() ? YES_VALUE : NOT_VALUE;
|
||||||
|
return datafield.getKey() + CsvLayoutUtils.PROPERTY_SEPARATOR + visible + CsvLayoutUtils.PROPERTY_SEPARATOR + primary + CsvLayoutUtils.PROPERTY_SEPARATOR + wide;
|
||||||
|
}
|
||||||
|
}
|
||||||
+85
-40
@@ -1,106 +1,151 @@
|
|||||||
package de.dennisguse.opentracks.ui.customRecordingLayout;
|
package de.dennisguse.opentracks.ui.customRecordingLayout;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
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.ImageView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.content.res.AppCompatResources;
|
import androidx.appcompat.content.res.AppCompatResources;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.util.StatisticsUtils;
|
import de.dennisguse.opentracks.data.models.Altitude;
|
||||||
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
|
import de.dennisguse.opentracks.data.models.Speed;
|
||||||
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
|
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||||
|
import de.dennisguse.opentracks.databinding.CustomStatsItemBinding;
|
||||||
|
import de.dennisguse.opentracks.services.RecordingData;
|
||||||
|
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||||
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
|
import de.dennisguse.opentracks.viewmodels.Mapping;
|
||||||
|
import de.dennisguse.opentracks.viewmodels.StatisticViewHolder;
|
||||||
|
|
||||||
public class SettingsCustomLayoutEditAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
public class SettingsCustomLayoutEditAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||||
|
|
||||||
private Layout layout;
|
private static final String TAG = SettingsCustomLayoutEditAdapter.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final RecordingData demoData;
|
||||||
|
|
||||||
|
static {
|
||||||
|
TrackStatistics trackStatistics = new TrackStatistics();
|
||||||
|
trackStatistics.setStartTime(Instant.ofEpochMilli(0));
|
||||||
|
trackStatistics.setMovingTime(Duration.ofMinutes(0));
|
||||||
|
trackStatistics.setTotalTime(Duration.ofMinutes(0));
|
||||||
|
|
||||||
|
trackStatistics.setTotalDistance(Distance.of(0));
|
||||||
|
|
||||||
|
trackStatistics.setTotalAltitudeGain(0f);
|
||||||
|
trackStatistics.setTotalAltitudeLoss(0f);
|
||||||
|
Track track = new Track(ZoneOffset.UTC);
|
||||||
|
track.setTrackStatistics(trackStatistics);
|
||||||
|
|
||||||
|
TrackPoint lastTrackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochMilli(0));
|
||||||
|
lastTrackPoint.setLatitude(0);
|
||||||
|
lastTrackPoint.setLongitude(0);
|
||||||
|
lastTrackPoint.setAltitude(Altitude.EGM2008.of(0));
|
||||||
|
lastTrackPoint.setSpeed(Speed.of(0));
|
||||||
|
|
||||||
|
demoData = new RecordingData(track, lastTrackPoint, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RecordingLayout recordingLayout;
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final SettingsCustomLayoutItemClickListener itemClickListener;
|
private final SettingsCustomLayoutItemClickListener itemClickListener;
|
||||||
|
private final Map<String, Callable<StatisticViewHolder<?>>> mapping;
|
||||||
|
|
||||||
public SettingsCustomLayoutEditAdapter(Context context, SettingsCustomLayoutItemClickListener itemClickListener, Layout layout) {
|
|
||||||
|
public SettingsCustomLayoutEditAdapter(Context context, SettingsCustomLayoutItemClickListener itemClickListener, RecordingLayout recordingLayout) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.itemClickListener = itemClickListener;
|
this.itemClickListener = itemClickListener;
|
||||||
this.layout = layout;
|
this.recordingLayout = recordingLayout;
|
||||||
|
|
||||||
|
mapping = Mapping.create(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
View view = LayoutInflater.from(context).inflate(R.layout.custom_stats_item, parent, false);
|
CustomStatsItemBinding binding = CustomStatsItemBinding.inflate(LayoutInflater.from(context), parent, false);
|
||||||
return new SettingsCustomLayoutEditAdapter.ViewHolder(view);
|
return new SettingsCustomLayoutEditAdapter.ViewHolder(binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||||
SettingsCustomLayoutEditAdapter.ViewHolder viewHolder = (SettingsCustomLayoutEditAdapter.ViewHolder) holder;
|
SettingsCustomLayoutEditAdapter.ViewHolder viewHolder = (SettingsCustomLayoutEditAdapter.ViewHolder) holder;
|
||||||
DataField field = layout.getFields().get(position);
|
DataField field = recordingLayout.getFields().get(position);
|
||||||
viewHolder.itemView.setTag(field.getKey());
|
viewHolder.itemView.setTag(field.getKey());
|
||||||
viewHolder.title.setText(field.getTitle());
|
try {
|
||||||
viewHolder.value.setText(StatisticsUtils.emptyValue(context, field.getKey()));
|
StatisticViewHolder<?> m = mapping.get(field.getKey()).call();
|
||||||
|
m.initialize(context, LayoutInflater.from(context));
|
||||||
|
m.configureUI(field);
|
||||||
|
m.onChanged(UnitSystem.METRIC, demoData);
|
||||||
|
|
||||||
viewHolder.title.setTextAppearance(context, field.isVisible() ? (field.isPrimary() ? R.style.TextAppearance_OpenTracks_PrimaryHeader : R.style.TextAppearance_OpenTracks_SecondaryHeader) : R.style.TextAppearance_OpenTracks_HiddenHeader);
|
viewHolder.viewBinding.statsLayout.removeAllViews(); //TODO this is not really performant
|
||||||
viewHolder.value.setTextAppearance(context, field.isVisible() ? (field.isPrimary() ? R.style.TextAppearance_OpenTracks_PrimaryValue : R.style.TextAppearance_OpenTracks_SecondaryValue) : R.style.TextAppearance_OpenTracks_HiddenValue);
|
viewHolder.viewBinding.statsLayout.addView(m.getView());
|
||||||
viewHolder.statusIcon.setVisibility(field.isVisible() ? View.GONE : View.VISIBLE);
|
} catch (Exception e) {
|
||||||
viewHolder.statusIcon.setImageDrawable(AppCompatResources.getDrawable(context, R.drawable.ic_baseline_visibility_off_24));
|
Log.e(TAG, "Couldn't to instantiate UI for DataField with key " + field.getKey() + " " + e.getMessage());
|
||||||
viewHolder.moveIcon.setVisibility(field.isVisible() ? View.VISIBLE : View.GONE);
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
viewHolder.viewBinding.statsIconShowStatus.setVisibility(field.isVisible() ? View.GONE : View.VISIBLE);
|
||||||
|
viewHolder.viewBinding.statsIconShowStatus.setImageDrawable(AppCompatResources.getDrawable(context, R.drawable.ic_baseline_visibility_off_24));
|
||||||
|
viewHolder.viewBinding.statsIvDragIndicator.setVisibility(field.isVisible() ? View.VISIBLE : View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
public int getItemCount() {
|
||||||
if (layout == null) {
|
if (recordingLayout == null) {
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
return layout.getFields().size();
|
return recordingLayout.getFields().size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isItemWide(int position) {
|
public boolean isItemWide(int position) {
|
||||||
return layout.getFields().get(position).isWide();
|
return recordingLayout.getFields().get(position).isWide();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataField getItem(int position) {
|
public DataField getItem(int position) {
|
||||||
return layout.getFields().get(position);
|
return recordingLayout.getFields().get(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void swapValues(Layout data) {
|
public void swapValues(RecordingLayout data) {
|
||||||
this.layout = data;
|
this.recordingLayout = data;
|
||||||
if (this.layout != null) {
|
if (this.recordingLayout != null) {
|
||||||
this.notifyDataSetChanged();
|
this.notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Layout move(int fromPosition, int toPosition) {
|
public RecordingLayout move(int fromPosition, int toPosition) {
|
||||||
layout.moveField(fromPosition, toPosition);
|
recordingLayout.moveField(fromPosition, toPosition);
|
||||||
notifyItemMoved(fromPosition, toPosition);
|
notifyItemMoved(fromPosition, toPosition);
|
||||||
return layout;
|
return recordingLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
|
private class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
|
||||||
final TextView title;
|
|
||||||
final TextView value;
|
|
||||||
final TextView unit;
|
|
||||||
final ImageView statusIcon;
|
|
||||||
final ImageView moveIcon;
|
|
||||||
|
|
||||||
public ViewHolder(@NonNull View itemView) {
|
CustomStatsItemBinding viewBinding;
|
||||||
super(itemView);
|
|
||||||
title = itemView.findViewById(R.id.stats_description_main);
|
public ViewHolder(@NonNull CustomStatsItemBinding viewBinding) {
|
||||||
value = itemView.findViewById(R.id.stats_value);
|
super(viewBinding.getRoot());
|
||||||
unit = itemView.findViewById(R.id.stats_unit);
|
this.viewBinding = viewBinding;
|
||||||
statusIcon = itemView.findViewById(R.id.stats_icon_show_status);
|
|
||||||
moveIcon = itemView.findViewById(R.id.stats_iv_drag_indicator);
|
viewBinding.getRoot().setOnClickListener(this);
|
||||||
itemView.setOnClickListener(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
String statTitle = (String) view.getTag();
|
String statTitle = (String) view.getTag();
|
||||||
Optional<DataField> optionalField = layout.getFields().stream().filter(f -> f.getKey().equals(statTitle)).findFirst();
|
Optional<DataField> optionalField = recordingLayout.getFields().stream().filter(f -> f.getKey().equals(statTitle)).findFirst();
|
||||||
optionalField.ifPresent(itemClickListener::onSettingsCustomLayoutItemClicked);
|
optionalField.ifPresent(itemClickListener::onSettingsCustomLayoutItemClicked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-17
@@ -17,14 +17,14 @@ import de.dennisguse.opentracks.settings.PreferencesUtils;
|
|||||||
|
|
||||||
public class SettingsCustomLayoutListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
public class SettingsCustomLayoutListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||||
|
|
||||||
private List<Layout> layoutList;
|
private List<RecordingLayout> recordingLayoutList;
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final SettingsCustomLayoutProfileClickListener itemClickListener;
|
private final SettingsCustomLayoutProfileClickListener itemClickListener;
|
||||||
|
|
||||||
public SettingsCustomLayoutListAdapter(Context context, SettingsCustomLayoutProfileClickListener itemClickListener) {
|
public SettingsCustomLayoutListAdapter(Context context, SettingsCustomLayoutProfileClickListener itemClickListener) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.itemClickListener = itemClickListener;
|
this.itemClickListener = itemClickListener;
|
||||||
layoutList = PreferencesUtils.getAllCustomLayouts();
|
recordingLayoutList = PreferencesUtils.getAllCustomLayouts();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -37,37 +37,37 @@ public class SettingsCustomLayoutListAdapter extends RecyclerView.Adapter<Recycl
|
|||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||||
SettingsCustomLayoutListAdapter.ViewHolder viewHolder = (SettingsCustomLayoutListAdapter.ViewHolder) holder;
|
SettingsCustomLayoutListAdapter.ViewHolder viewHolder = (SettingsCustomLayoutListAdapter.ViewHolder) holder;
|
||||||
Layout layout = layoutList.get(position);
|
RecordingLayout recordingLayout = recordingLayoutList.get(position);
|
||||||
viewHolder.itemView.setTag(layout.getName());
|
viewHolder.itemView.setTag(recordingLayout.getName());
|
||||||
viewHolder.title.setText(layout.getName());
|
viewHolder.title.setText(recordingLayout.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
public int getItemCount() {
|
||||||
if (layoutList == null) {
|
if (recordingLayoutList == null) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return layoutList.size();
|
return recordingLayoutList.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Layout> getLayouts() {
|
public List<RecordingLayout> getLayouts() {
|
||||||
return layoutList;
|
return recordingLayoutList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reloadLayouts() {
|
public void reloadLayouts() {
|
||||||
layoutList = PreferencesUtils.getAllCustomLayouts();
|
recordingLayoutList = PreferencesUtils.getAllCustomLayouts();
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeLayout(int position) {
|
public void removeLayout(int position) {
|
||||||
layoutList.remove(position);
|
recordingLayoutList.remove(position);
|
||||||
PreferencesUtils.updateCustomLayouts(layoutList);
|
PreferencesUtils.updateCustomLayouts(recordingLayoutList);
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void restoreItem(Layout layout, int position) {
|
public void restoreItem(RecordingLayout recordingLayout, int position) {
|
||||||
layoutList.add(position, layout);
|
recordingLayoutList.add(position, recordingLayout);
|
||||||
PreferencesUtils.updateCustomLayouts(layoutList);
|
PreferencesUtils.updateCustomLayouts(recordingLayoutList);
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,12 +83,12 @@ public class SettingsCustomLayoutListAdapter extends RecyclerView.Adapter<Recycl
|
|||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
String profile = (String) view.getTag();
|
String profile = (String) view.getTag();
|
||||||
Optional<Layout> optionalLayout = layoutList.stream().filter(layout -> layout.sameName(new Layout(profile))).findFirst();
|
Optional<RecordingLayout> optionalLayout = recordingLayoutList.stream().filter(layout -> layout.sameName(new RecordingLayout(profile))).findFirst();
|
||||||
optionalLayout.ifPresent(itemClickListener::onSettingsCustomLayoutProfileClicked);
|
optionalLayout.ifPresent(itemClickListener::onSettingsCustomLayoutProfileClicked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface SettingsCustomLayoutProfileClickListener {
|
public interface SettingsCustomLayoutProfileClickListener {
|
||||||
void onSettingsCustomLayoutProfileClicked(@NonNull Layout layout);
|
void onSettingsCustomLayoutProfileClicked(@NonNull RecordingLayout recordingLayout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,11 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayout;
|
||||||
|
|
||||||
public class StatisticsUtils {
|
public class StatisticsUtils {
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public static String emptyValue(@NonNull Context context, @NonNull String statTitle) {
|
public static String emptyValue(@NonNull Context context, @NonNull String statTitle) {
|
||||||
if (PreferencesUtils.isKey(R.string.stats_custom_layout_total_time_key, statTitle) || PreferencesUtils.isKey(R.string.stats_custom_layout_moving_time_key, statTitle) || PreferencesUtils.isKey(R.string.stats_custom_layout_pace_key, statTitle) || PreferencesUtils.isKey(R.string.stats_custom_layout_average_moving_pace_key, statTitle) || PreferencesUtils.isKey(R.string.stats_custom_layout_average_pace_key, statTitle) || PreferencesUtils.isKey(R.string.stats_custom_layout_fastest_pace_key, statTitle) || PreferencesUtils.isKey(R.string.stats_custom_layout_clock_key, statTitle)) {
|
if (PreferencesUtils.isKey(R.string.stats_custom_layout_total_time_key, statTitle) || PreferencesUtils.isKey(R.string.stats_custom_layout_moving_time_key, statTitle) || PreferencesUtils.isKey(R.string.stats_custom_layout_pace_key, statTitle) || PreferencesUtils.isKey(R.string.stats_custom_layout_average_moving_pace_key, statTitle) || PreferencesUtils.isKey(R.string.stats_custom_layout_average_pace_key, statTitle) || PreferencesUtils.isKey(R.string.stats_custom_layout_fastest_pace_key, statTitle) || PreferencesUtils.isKey(R.string.stats_custom_layout_clock_key, statTitle)) {
|
||||||
return context.getString(R.string.stats_empty_value_time);
|
return context.getString(R.string.stats_empty_value_time);
|
||||||
@@ -24,9 +25,10 @@ public class StatisticsUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Layout filterVisible(Layout layout, boolean visible) {
|
@Deprecated //Add to Layout?
|
||||||
Layout result = new Layout(layout.getName());
|
public static RecordingLayout filterVisible(RecordingLayout recordingLayout, boolean visible) {
|
||||||
result.addFields(layout.getFields().stream().filter(f -> f.isVisible() == visible).collect(Collectors.toList()));
|
RecordingLayout result = new RecordingLayout(recordingLayout.getName());
|
||||||
|
result.addFields(recordingLayout.getFields().stream().filter(f -> f.isVisible() == visible).collect(Collectors.toList()));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ public class TrackIconUtils {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//TODO Use Map.of()
|
||||||
private static final LinkedHashMap<String, Pair<Integer, Integer>> MAP = new LinkedHashMap<>();
|
private static final LinkedHashMap<String, Pair<Integer, Integer>> MAP = new LinkedHashMap<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package de.dennisguse.opentracks.viewmodels;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.R;
|
||||||
|
import de.dennisguse.opentracks.databinding.StatsClockItemBinding;
|
||||||
|
import de.dennisguse.opentracks.services.RecordingData;
|
||||||
|
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||||
|
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
|
||||||
|
|
||||||
|
public class ClockViewHolder extends StatisticViewHolder<StatsClockItemBinding> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected StatsClockItemBinding createViewBinding(LayoutInflater inflater) {
|
||||||
|
return StatsClockItemBinding.inflate(inflater);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configureUI(DataField dataField) {
|
||||||
|
//TODO Unify with GenericStatisticsViewHolder?
|
||||||
|
getBinding().statsClock.setTextAppearance(getContext(), dataField.isPrimary() ? R.style.TextAppearance_OpenTracks_PrimaryValue : R.style.TextAppearance_OpenTracks_SecondaryValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,322 @@
|
|||||||
|
package de.dennisguse.opentracks.viewmodels;
|
||||||
|
|
||||||
|
import android.util.Pair;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.R;
|
||||||
|
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
||||||
|
import de.dennisguse.opentracks.data.models.Speed;
|
||||||
|
import de.dennisguse.opentracks.data.models.SpeedFormatter;
|
||||||
|
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||||
|
import de.dennisguse.opentracks.databinding.StatsGenericItemBinding;
|
||||||
|
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
||||||
|
import de.dennisguse.opentracks.services.RecordingData;
|
||||||
|
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||||
|
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
|
||||||
|
import de.dennisguse.opentracks.util.StringUtils;
|
||||||
|
|
||||||
|
public abstract class GenericStatisticsViewHolder extends StatisticViewHolder<StatsGenericItemBinding> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected StatsGenericItemBinding createViewBinding(LayoutInflater inflater) {
|
||||||
|
return StatsGenericItemBinding.inflate(inflater);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configureUI(DataField dataField) {
|
||||||
|
getBinding().statsValue.setTextAppearance(getContext(), dataField.isPrimary() ? R.style.TextAppearance_OpenTracks_PrimaryValue : R.style.TextAppearance_OpenTracks_SecondaryValue);
|
||||||
|
getBinding().statsDescriptionMain.setTextAppearance(getContext(), dataField.isPrimary() ? R.style.TextAppearance_OpenTracks_PrimaryHeader : R.style.TextAppearance_OpenTracks_SecondaryHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Distance extends GenericStatisticsViewHolder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||||
|
Pair<String, String> valueAndUnit = DistanceFormatter.Builder()
|
||||||
|
.setUnit(unitSystem)
|
||||||
|
.build(getContext()).getDistanceParts(data.getTrackStatistics().getTotalDistance());
|
||||||
|
|
||||||
|
getBinding().statsValue.setText(valueAndUnit.first);
|
||||||
|
getBinding().statsUnit.setText(valueAndUnit.second);
|
||||||
|
getBinding().statsDescriptionMain.setText(getContext().getString(R.string.stats_distance));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TotalTime extends GenericStatisticsViewHolder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||||
|
Pair<String, String> valueAndUnit = new Pair<>(StringUtils.formatElapsedTime(data.getTrackStatistics().getTotalTime()), null);
|
||||||
|
|
||||||
|
getBinding().statsValue.setText(valueAndUnit.first);
|
||||||
|
getBinding().statsUnit.setText(valueAndUnit.second);
|
||||||
|
getBinding().statsDescriptionMain.setText(getContext().getString(R.string.stats_total_time));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MovingTime extends GenericStatisticsViewHolder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||||
|
String value = StringUtils.formatElapsedTime(data.getTrackStatistics().getMovingTime());
|
||||||
|
|
||||||
|
getBinding().statsValue.setText(value);
|
||||||
|
getBinding().statsDescriptionMain.setText(getContext().getString(R.string.stats_moving_time));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SpeedOrPace extends GenericStatisticsViewHolder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||||
|
// TODO Pace wont work for now
|
||||||
|
boolean reportSpeed = true;
|
||||||
|
SpeedFormatter localSpeedFormatter = SpeedFormatter.Builder()
|
||||||
|
.setUnit(unitSystem)
|
||||||
|
.setReportSpeedOrPace(reportSpeed).build(getContext());
|
||||||
|
|
||||||
|
Pair<String, String> valueAndUnit;
|
||||||
|
|
||||||
|
SensorDataSet sensorDataSet = data.getSensorDataSet();
|
||||||
|
final TrackPoint latestTrackPoint = data.getLatestTrackPoint();
|
||||||
|
if (sensorDataSet != null && sensorDataSet.getSpeed() != null) {
|
||||||
|
valueAndUnit = localSpeedFormatter.getSpeedParts(sensorDataSet.getSpeed().first);
|
||||||
|
getBinding().statsDescriptionMain.setText(sensorDataSet.getSpeed().second);
|
||||||
|
} else {
|
||||||
|
Speed speed = latestTrackPoint != null && latestTrackPoint.hasSpeed() ? latestTrackPoint.getSpeed() : null;
|
||||||
|
valueAndUnit = localSpeedFormatter.getSpeedParts(speed);
|
||||||
|
getBinding().statsDescriptionMain.setText(getContext().getString(R.string.description_speed_source_gps));
|
||||||
|
}
|
||||||
|
|
||||||
|
getBinding().statsValue.setText(valueAndUnit.first);
|
||||||
|
getBinding().statsUnit.setText(valueAndUnit.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AverageMovingSpeed extends GenericStatisticsViewHolder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||||
|
SpeedFormatter speedFormatterSpeed = SpeedFormatter.Builder()
|
||||||
|
.setUnit(unitSystem)
|
||||||
|
.setReportSpeedOrPace(true)
|
||||||
|
.build(getContext());
|
||||||
|
|
||||||
|
Pair<String, String> valueAndUnit = speedFormatterSpeed.getSpeedParts(data.getTrackStatistics().getAverageMovingSpeed());
|
||||||
|
|
||||||
|
getBinding().statsValue.setText(valueAndUnit.first);
|
||||||
|
getBinding().statsUnit.setText(valueAndUnit.second);
|
||||||
|
getBinding().statsDescriptionMain.setText(getContext().getString(R.string.stats_average_moving_speed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AverageSpeed extends GenericStatisticsViewHolder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||||
|
SpeedFormatter speedFormatterSpeed = SpeedFormatter.Builder()
|
||||||
|
.setUnit(unitSystem)
|
||||||
|
.setReportSpeedOrPace(true)
|
||||||
|
.build(getContext());
|
||||||
|
|
||||||
|
Pair<String, String> valueAndUnit = speedFormatterSpeed.getSpeedParts(data.getTrackStatistics().getAverageSpeed());
|
||||||
|
|
||||||
|
getBinding().statsValue.setText(valueAndUnit.first);
|
||||||
|
getBinding().statsUnit.setText(valueAndUnit.second);
|
||||||
|
getBinding().statsDescriptionMain.setText(getContext().getString(R.string.stats_average_speed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MaxSpeed extends GenericStatisticsViewHolder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||||
|
SpeedFormatter speedFormatterSpeed = SpeedFormatter.Builder()
|
||||||
|
.setUnit(unitSystem)
|
||||||
|
.setReportSpeedOrPace(true)
|
||||||
|
.build(getContext());
|
||||||
|
|
||||||
|
Pair<String, String> valueAndUnit = speedFormatterSpeed.getSpeedParts(data.getTrackStatistics().getMaxSpeed());
|
||||||
|
|
||||||
|
getBinding().statsValue.setText(valueAndUnit.first);
|
||||||
|
getBinding().statsUnit.setText(valueAndUnit.second);
|
||||||
|
getBinding().statsDescriptionMain.setText(getContext().getString(R.string.stats_max_speed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AverageMovingPace extends GenericStatisticsViewHolder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||||
|
SpeedFormatter speedFormatterSpeed = SpeedFormatter.Builder()
|
||||||
|
.setUnit(unitSystem)
|
||||||
|
.setReportSpeedOrPace(false)
|
||||||
|
.build(getContext());
|
||||||
|
|
||||||
|
Pair<String, String> valueAndUnit = speedFormatterSpeed.getSpeedParts(data.getTrackStatistics().getAverageMovingSpeed());
|
||||||
|
|
||||||
|
getBinding().statsValue.setText(valueAndUnit.first);
|
||||||
|
getBinding().statsUnit.setText(valueAndUnit.second);
|
||||||
|
getBinding().statsDescriptionMain.setText(getContext().getString(R.string.stats_average_moving_pace));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AveragePace extends GenericStatisticsViewHolder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||||
|
SpeedFormatter speedFormatterSpeed = SpeedFormatter.Builder()
|
||||||
|
.setUnit(unitSystem)
|
||||||
|
.setReportSpeedOrPace(false)
|
||||||
|
.build(getContext());
|
||||||
|
|
||||||
|
Pair<String, String> valueAndUnit = speedFormatterSpeed.getSpeedParts(data.getTrackStatistics().getAverageMovingSpeed());
|
||||||
|
|
||||||
|
getBinding().statsValue.setText(valueAndUnit.first);
|
||||||
|
getBinding().statsUnit.setText(valueAndUnit.second);
|
||||||
|
getBinding().statsDescriptionMain.setText(getContext().getString(R.string.stats_average_pace));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class FastestPace extends GenericStatisticsViewHolder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||||
|
SpeedFormatter speedFormatterSpeed = SpeedFormatter.Builder()
|
||||||
|
.setUnit(unitSystem)
|
||||||
|
.setReportSpeedOrPace(false)
|
||||||
|
.build(getContext());
|
||||||
|
|
||||||
|
Pair<String, String> valueAndUnit = speedFormatterSpeed.getSpeedParts(data.getTrackStatistics().getMaxSpeed());
|
||||||
|
|
||||||
|
getBinding().statsValue.setText(valueAndUnit.first);
|
||||||
|
getBinding().statsUnit.setText(valueAndUnit.second);
|
||||||
|
getBinding().statsDescriptionMain.setText(getContext().getString(R.string.stats_fastest_pace));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Altitude extends GenericStatisticsViewHolder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||||
|
TrackPoint latestTrackPoint = data.getLatestTrackPoint();
|
||||||
|
Float altitude = latestTrackPoint != null && latestTrackPoint.hasAltitude() ? (float) latestTrackPoint.getAltitude().toM() : null;
|
||||||
|
String altitudeReference = latestTrackPoint != null && latestTrackPoint.hasAltitude() ? getContext().getString(latestTrackPoint.getAltitude().getLabelId()) : null;
|
||||||
|
Pair<String, String> valueAndUnit = StringUtils.getAltitudeParts(getContext(), altitude, unitSystem);
|
||||||
|
|
||||||
|
getBinding().statsValue.setText(valueAndUnit.first);
|
||||||
|
getBinding().statsUnit.setText(valueAndUnit.second);
|
||||||
|
getBinding().statsDescriptionMain.setText(R.string.stats_altitude);
|
||||||
|
getBinding().statsDescriptionSecondary.setText(altitudeReference);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Gain extends GenericStatisticsViewHolder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||||
|
|
||||||
|
Pair<String, String> valueAndUnit = StringUtils.getAltitudeParts(getContext(), data.getTrackStatistics().getTotalAltitudeGain(), unitSystem);
|
||||||
|
|
||||||
|
getBinding().statsValue.setText(valueAndUnit.first);
|
||||||
|
getBinding().statsUnit.setText(valueAndUnit.second);
|
||||||
|
getBinding().statsDescriptionMain.setText(R.string.stats_gain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Loss extends GenericStatisticsViewHolder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||||
|
|
||||||
|
Pair<String, String> valueAndUnit = StringUtils.getAltitudeParts(getContext(), data.getTrackStatistics().getTotalAltitudeLoss(), unitSystem);
|
||||||
|
|
||||||
|
getBinding().statsValue.setText(valueAndUnit.first);
|
||||||
|
getBinding().statsUnit.setText(valueAndUnit.second);
|
||||||
|
getBinding().statsDescriptionMain.setText(R.string.stats_loss);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Coordinates extends GenericStatisticsViewHolder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||||
|
TrackPoint latestTrackPoint = data.getLatestTrackPoint();
|
||||||
|
String value;
|
||||||
|
if (latestTrackPoint != null && latestTrackPoint.hasLocation()) {
|
||||||
|
value = StringUtils.formatCoordinate(getContext(), latestTrackPoint.getLatitude(), latestTrackPoint.getLongitude());
|
||||||
|
} else {
|
||||||
|
value = getContext().getString(R.string.value_unknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
getBinding().statsValue.setText(value);
|
||||||
|
getBinding().statsDescriptionMain.setText(R.string.stats_coordinates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SensorHeartRate extends GenericStatisticsViewHolder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||||
|
SensorDataSet sensorDataSet = data.getSensorDataSet();
|
||||||
|
String sensorName = getContext().getString(R.string.value_unknown);
|
||||||
|
|
||||||
|
Pair<String, String> valueAndUnit;
|
||||||
|
if (sensorDataSet != null && sensorDataSet.getHeartRate() != null) {
|
||||||
|
valueAndUnit = StringUtils.getHeartRateParts(getContext(), sensorDataSet.getHeartRate().first);
|
||||||
|
sensorName = sensorDataSet.getHeartRate().second;
|
||||||
|
} else {
|
||||||
|
valueAndUnit = StringUtils.getHeartRateParts(getContext(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
getBinding().statsValue.setText(valueAndUnit.first);
|
||||||
|
getBinding().statsUnit.setText(valueAndUnit.second);
|
||||||
|
getBinding().statsDescriptionMain.setText(R.string.stats_sensors_heart_rate);
|
||||||
|
getBinding().statsDescriptionSecondary.setText(sensorName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SensorCadence extends GenericStatisticsViewHolder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||||
|
SensorDataSet sensorDataSet = data.getSensorDataSet();
|
||||||
|
String sensorName = getContext().getString(R.string.value_unknown);
|
||||||
|
|
||||||
|
Pair<String, String> valueAndUnit;
|
||||||
|
if (sensorDataSet != null && sensorDataSet.getCadence() != null) {
|
||||||
|
valueAndUnit = StringUtils.getCadenceParts(getContext(), sensorDataSet.getCadence().first);
|
||||||
|
sensorName = sensorDataSet.getCadence().second;
|
||||||
|
} else {
|
||||||
|
valueAndUnit = StringUtils.getCadenceParts(getContext(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
getBinding().statsValue.setText(valueAndUnit.first);
|
||||||
|
getBinding().statsUnit.setText(valueAndUnit.second);
|
||||||
|
getBinding().statsDescriptionMain.setText(R.string.stats_sensors_cadence);
|
||||||
|
getBinding().statsDescriptionSecondary.setText(sensorName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SensorPower extends GenericStatisticsViewHolder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||||
|
SensorDataSet sensorDataSet = data.getSensorDataSet();
|
||||||
|
String sensorName = getContext().getString(R.string.value_unknown);
|
||||||
|
|
||||||
|
Pair<String, String> valueAndUnit;
|
||||||
|
if (sensorDataSet != null && sensorDataSet.getCyclingPower() != null) {
|
||||||
|
valueAndUnit = StringUtils.getPowerParts(getContext(), sensorDataSet.getCyclingPower().getValue());
|
||||||
|
sensorName = sensorDataSet.getCyclingPower().getSensorNameOrAddress();
|
||||||
|
} else {
|
||||||
|
valueAndUnit = StringUtils.getCadenceParts(getContext(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
getBinding().statsValue.setText(valueAndUnit.first);
|
||||||
|
getBinding().statsUnit.setText(valueAndUnit.second);
|
||||||
|
getBinding().statsDescriptionMain.setText(R.string.stats_sensors_power);
|
||||||
|
getBinding().statsDescriptionSecondary.setText(sensorName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package de.dennisguse.opentracks.viewmodels;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.R;
|
||||||
|
|
||||||
|
public class Mapping {
|
||||||
|
|
||||||
|
public static Map<String, Callable<StatisticViewHolder<?>>> create(Context context) {
|
||||||
|
HashMap<String, Callable<StatisticViewHolder<?>>> m = new HashMap<>();
|
||||||
|
m.put(context.getString(R.string.stats_custom_layout_total_time_key), GenericStatisticsViewHolder.TotalTime::new);
|
||||||
|
m.put(context.getString(R.string.stats_custom_layout_moving_time_key), GenericStatisticsViewHolder.MovingTime::new);
|
||||||
|
|
||||||
|
m.put(context.getString(R.string.stats_custom_layout_distance_key), GenericStatisticsViewHolder.Distance::new);
|
||||||
|
|
||||||
|
m.put(context.getString(R.string.stats_custom_layout_speed_key), GenericStatisticsViewHolder.SpeedOrPace::new);
|
||||||
|
m.put(context.getString(R.string.stats_custom_layout_pace_key), GenericStatisticsViewHolder.SpeedOrPace::new);
|
||||||
|
m.put(context.getString(R.string.stats_custom_layout_average_moving_speed_key), GenericStatisticsViewHolder.AverageMovingSpeed::new);
|
||||||
|
m.put(context.getString(R.string.stats_custom_layout_average_speed_key), GenericStatisticsViewHolder.AverageSpeed::new);
|
||||||
|
m.put(context.getString(R.string.stats_custom_layout_max_speed_key), GenericStatisticsViewHolder.MaxSpeed::new);
|
||||||
|
m.put(context.getString(R.string.stats_custom_layout_average_moving_pace_key), GenericStatisticsViewHolder.AverageMovingPace::new);
|
||||||
|
m.put(context.getString(R.string.stats_custom_layout_average_pace_key), GenericStatisticsViewHolder.AveragePace::new);
|
||||||
|
m.put(context.getString(R.string.stats_custom_layout_fastest_pace_key), GenericStatisticsViewHolder.FastestPace::new);
|
||||||
|
|
||||||
|
m.put(context.getString(R.string.stats_custom_layout_altitude_key), GenericStatisticsViewHolder.Altitude::new);
|
||||||
|
m.put(context.getString(R.string.stats_custom_layout_gain_key), GenericStatisticsViewHolder.Gain::new);
|
||||||
|
m.put(context.getString(R.string.stats_custom_layout_loss_key), GenericStatisticsViewHolder.Loss::new);
|
||||||
|
m.put(context.getString(R.string.stats_custom_layout_coordinates_key), GenericStatisticsViewHolder.Coordinates::new);
|
||||||
|
|
||||||
|
m.put(context.getString(R.string.stats_custom_layout_heart_rate_key), GenericStatisticsViewHolder.SensorHeartRate::new);
|
||||||
|
m.put(context.getString(R.string.stats_custom_layout_cadence_key), GenericStatisticsViewHolder.SensorCadence::new);
|
||||||
|
m.put(context.getString(R.string.stats_custom_layout_power_key), GenericStatisticsViewHolder.SensorPower::new);
|
||||||
|
m.put(context.getString(R.string.stats_custom_layout_clock_key), ClockViewHolder::new);
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
package de.dennisguse.opentracks.viewmodels;
|
|
||||||
|
|
||||||
import android.util.Pair;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
|
|
||||||
|
|
||||||
public class StatisticData {
|
|
||||||
private final DataField dataField;
|
|
||||||
private final String value;
|
|
||||||
private final String unit;
|
|
||||||
private final String description;
|
|
||||||
|
|
||||||
public StatisticData(@NonNull DataField dataField, @NonNull Pair<String, String> valueAndUnit, String description) {
|
|
||||||
this.dataField = dataField;
|
|
||||||
this.value = valueAndUnit.first;
|
|
||||||
this.unit = valueAndUnit.second;
|
|
||||||
this.description = description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataField getField() {
|
|
||||||
return dataField;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasValue() {
|
|
||||||
return value != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUnit() {
|
|
||||||
return unit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDescription() {
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasDescription() {
|
|
||||||
return description != null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,232 +0,0 @@
|
|||||||
package de.dennisguse.opentracks.viewmodels;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.Pair;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
|
||||||
import de.dennisguse.opentracks.data.models.Cadence;
|
|
||||||
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
|
||||||
import de.dennisguse.opentracks.data.models.Speed;
|
|
||||||
import de.dennisguse.opentracks.data.models.SpeedFormatter;
|
|
||||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
|
||||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
|
||||||
import de.dennisguse.opentracks.services.RecordingData;
|
|
||||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
|
||||||
import de.dennisguse.opentracks.stats.SensorStatistics;
|
|
||||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
|
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
|
|
||||||
import de.dennisguse.opentracks.util.StringUtils;
|
|
||||||
|
|
||||||
public class StatisticDataBuilder {
|
|
||||||
|
|
||||||
public static List<StatisticData> fromRecordingData(@NonNull Context context, @NonNull RecordingData recordingData, @NonNull Layout layout, UnitSystem unitSystem) {
|
|
||||||
List<StatisticData> statisticDataList = layout.getFields().stream()
|
|
||||||
.filter(DataField::isVisible)
|
|
||||||
.map(field -> build(context, recordingData, field.getKey(), field.isPrimary(), unitSystem))
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
statisticDataList.addAll(getSensorStatsDataIfNeeded(context, recordingData, statisticDataList, unitSystem));
|
|
||||||
return statisticDataList;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static StatisticData build(@NonNull Context context, @NonNull RecordingData recordingData, @NonNull String fieldKey, boolean isPrimary, UnitSystem unitSystem) {
|
|
||||||
final TrackPoint latestTrackPoint = recordingData.getLatestTrackPoint();
|
|
||||||
final SensorDataSet sensorDataSet = recordingData.getSensorDataSet();
|
|
||||||
final TrackStatistics trackStatistics = recordingData.getTrackStatistics();
|
|
||||||
|
|
||||||
String title = null;
|
|
||||||
String description = null;
|
|
||||||
Pair<String, String> valueAndUnit = null;
|
|
||||||
boolean isWide = false;
|
|
||||||
|
|
||||||
final String sensorUnknown = context.getString(R.string.value_unknown);
|
|
||||||
|
|
||||||
SpeedFormatter speedFormatterPace = SpeedFormatter.Builder().setUnit(unitSystem).setReportSpeedOrPace(false).build(context);
|
|
||||||
SpeedFormatter speedFormatterSpeed = SpeedFormatter.Builder().setUnit(unitSystem).setReportSpeedOrPace(true).build(context);
|
|
||||||
|
|
||||||
|
|
||||||
if (fieldKey.equals(context.getString(R.string.stats_custom_layout_total_time_key))) {
|
|
||||||
valueAndUnit = new Pair<>(StringUtils.formatElapsedTime(trackStatistics.getTotalTime()), null);
|
|
||||||
title = context.getString(R.string.stats_total_time);
|
|
||||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_moving_time_key))) {
|
|
||||||
valueAndUnit = new Pair<>(StringUtils.formatElapsedTime(trackStatistics.getMovingTime()), null);
|
|
||||||
title = context.getString(R.string.stats_moving_time);
|
|
||||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_distance_key))) {
|
|
||||||
valueAndUnit = DistanceFormatter.Builder()
|
|
||||||
.setUnit(unitSystem)
|
|
||||||
.build(context).getDistanceParts(trackStatistics.getTotalDistance());
|
|
||||||
title = context.getString(R.string.stats_distance);
|
|
||||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_speed_key)) || fieldKey.equals(context.getString(R.string.stats_custom_layout_pace_key))) {
|
|
||||||
boolean reportSpeed = fieldKey.equals("speed");
|
|
||||||
title = reportSpeed ? context.getString(R.string.stats_speed) : context.getString(R.string.stats_pace);
|
|
||||||
|
|
||||||
SpeedFormatter localSpeedFormatter = SpeedFormatter.Builder().setUnit(unitSystem).setReportSpeedOrPace(reportSpeed).build(context);
|
|
||||||
if (sensorDataSet != null && sensorDataSet.getSpeed() != null) {
|
|
||||||
valueAndUnit = localSpeedFormatter.getSpeedParts(sensorDataSet.getSpeed().first);
|
|
||||||
description = sensorDataSet.getSpeed().second;
|
|
||||||
} else {
|
|
||||||
Speed speed = latestTrackPoint != null && latestTrackPoint.hasSpeed() ? latestTrackPoint.getSpeed() : null;
|
|
||||||
valueAndUnit = localSpeedFormatter.getSpeedParts(speed);
|
|
||||||
description = context.getString(R.string.description_speed_source_gps);
|
|
||||||
}
|
|
||||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_average_moving_speed_key))) {
|
|
||||||
valueAndUnit = speedFormatterSpeed.getSpeedParts(trackStatistics.getAverageMovingSpeed());
|
|
||||||
title = context.getString(R.string.stats_average_moving_speed);
|
|
||||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_average_speed_key))) {
|
|
||||||
valueAndUnit = speedFormatterSpeed.getSpeedParts(trackStatistics.getAverageSpeed());
|
|
||||||
title = context.getString(R.string.stats_average_speed);
|
|
||||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_max_speed_key))) {
|
|
||||||
valueAndUnit = speedFormatterSpeed.getSpeedParts(trackStatistics.getMaxSpeed());
|
|
||||||
title = context.getString(R.string.stats_max_speed);
|
|
||||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_average_moving_pace_key))) {
|
|
||||||
valueAndUnit = speedFormatterPace.getSpeedParts(trackStatistics.getAverageMovingSpeed());
|
|
||||||
title = context.getString(R.string.stats_average_moving_pace);
|
|
||||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_average_pace_key))) {
|
|
||||||
valueAndUnit = speedFormatterPace.getSpeedParts(trackStatistics.getAverageSpeed());
|
|
||||||
title = context.getString(R.string.stats_average_pace);
|
|
||||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_fastest_pace_key))) {
|
|
||||||
valueAndUnit = speedFormatterPace.getSpeedParts(trackStatistics.getMaxSpeed());
|
|
||||||
title = context.getString(R.string.stats_fastest_pace);
|
|
||||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_altitude_key))) {
|
|
||||||
Float altitude = latestTrackPoint != null && latestTrackPoint.hasAltitude() ? (float) latestTrackPoint.getAltitude().toM() : null;
|
|
||||||
title = context.getString(R.string.stats_altitude);
|
|
||||||
description = latestTrackPoint != null && latestTrackPoint.hasAltitude() ? context.getString(latestTrackPoint.getAltitude().getLabelId()) : null;
|
|
||||||
valueAndUnit = StringUtils.getAltitudeParts(context, altitude, unitSystem);
|
|
||||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_gain_key))) {
|
|
||||||
valueAndUnit = StringUtils.getAltitudeParts(context, trackStatistics.getTotalAltitudeGain(), unitSystem);
|
|
||||||
title = context.getString(R.string.stats_gain);
|
|
||||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_loss_key))) {
|
|
||||||
valueAndUnit = StringUtils.getAltitudeParts(context, trackStatistics.getTotalAltitudeLoss(), unitSystem);
|
|
||||||
title = context.getString(R.string.stats_loss);
|
|
||||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_coordinates_key))) {
|
|
||||||
title = context.getString(R.string.stats_coordinates);
|
|
||||||
isWide = true;
|
|
||||||
if (latestTrackPoint != null && latestTrackPoint.hasLocation()) {
|
|
||||||
valueAndUnit = new Pair<>(StringUtils.formatCoordinate(context, latestTrackPoint.getLatitude(), latestTrackPoint.getLongitude()), null);
|
|
||||||
} else {
|
|
||||||
valueAndUnit = new Pair<>(context.getString(R.string.value_unknown), null);
|
|
||||||
}
|
|
||||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_heart_rate_key))) {
|
|
||||||
title = context.getString(R.string.stats_sensors_heart_rate);
|
|
||||||
if (sensorDataSet != null && sensorDataSet.getHeartRate() != null) {
|
|
||||||
valueAndUnit = StringUtils.getHeartRateParts(context, sensorDataSet.getHeartRate().first);
|
|
||||||
description = sensorDataSet.getHeartRate().second;
|
|
||||||
} else {
|
|
||||||
valueAndUnit = StringUtils.getHeartRateParts(context, null);
|
|
||||||
description = sensorUnknown;
|
|
||||||
}
|
|
||||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_cadence_key))) {
|
|
||||||
title = context.getString(R.string.stats_sensors_cadence);
|
|
||||||
|
|
||||||
Cadence cadence = null;
|
|
||||||
if (sensorDataSet != null) {
|
|
||||||
Pair<Cadence, String> cadenceData = sensorDataSet.getCadence();
|
|
||||||
if (cadenceData != null) {
|
|
||||||
cadence = cadenceData.first;
|
|
||||||
description = cadenceData.second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
valueAndUnit = StringUtils.getCadenceParts(context, cadence);
|
|
||||||
if (description == null) {
|
|
||||||
description = sensorUnknown;
|
|
||||||
}
|
|
||||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_power_key))) {
|
|
||||||
title = context.getString(R.string.stats_sensors_power);
|
|
||||||
if (sensorDataSet != null && sensorDataSet.getCyclingPower() != null && sensorDataSet.getCyclingPower().hasValue()) {
|
|
||||||
valueAndUnit = StringUtils.getPowerParts(context, sensorDataSet.getCyclingPower().getValue());
|
|
||||||
description = sensorDataSet.getCyclingPower().getSensorNameOrAddress();
|
|
||||||
} else {
|
|
||||||
valueAndUnit = StringUtils.getPowerParts(context, null);
|
|
||||||
description = sensorUnknown;
|
|
||||||
}
|
|
||||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_clock_key))) {
|
|
||||||
title = context.getString(R.string.stats_clock);
|
|
||||||
valueAndUnit = new Pair<>(null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (valueAndUnit == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new StatisticData(new DataField(fieldKey, title, true, isPrimary, isWide), valueAndUnit, description);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a list of StatisticData with sensors configured but not in statisticDataList.
|
|
||||||
*/
|
|
||||||
private static List<StatisticData> getSensorStatsDataIfNeeded(Context context, RecordingData recordingData, List<StatisticData> statisticDataList, UnitSystem unitSystem) {
|
|
||||||
List<StatisticData> sensorDataList = new ArrayList<>();
|
|
||||||
SensorDataSet sensorDataSet = recordingData.getSensorDataSet();
|
|
||||||
if (sensorDataSet == null) {
|
|
||||||
return sensorDataList;
|
|
||||||
}
|
|
||||||
if (statisticDataList.stream().noneMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_sensors_heart_rate))) && sensorDataSet.getHeartRate() != null) {
|
|
||||||
sensorDataList.add(build(context, recordingData, "heart_rate", true, unitSystem));
|
|
||||||
}
|
|
||||||
if (statisticDataList.stream().noneMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_sensors_cadence))) && sensorDataSet.getCadence() != null) {
|
|
||||||
sensorDataList.add(build(context, recordingData, "cadence", true, unitSystem));
|
|
||||||
}
|
|
||||||
if (statisticDataList.stream().noneMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_sensors_power))) && sensorDataSet.getCyclingPower() != null && sensorDataSet.getCyclingPower().hasValue()) {
|
|
||||||
sensorDataList.add(build(context, recordingData, "power", true, unitSystem));
|
|
||||||
}
|
|
||||||
|
|
||||||
return sensorDataList;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<StatisticData> fromSensorStatistics(@NonNull Context context, @NonNull SensorStatistics sensorStatistics) {
|
|
||||||
List<StatisticData> sensorDataList = new ArrayList<>();
|
|
||||||
|
|
||||||
if (sensorStatistics.hasHeartRate()) {
|
|
||||||
sensorDataList.add(
|
|
||||||
new StatisticData(
|
|
||||||
new DataField(context.getString(R.string.stats_custom_layout_heart_rate_key), context.getString(R.string.sensor_state_heart_rate_max), true, true, false),
|
|
||||||
StringUtils.getHeartRateParts(context, sensorStatistics.getMaxHeartRate()),
|
|
||||||
null
|
|
||||||
)
|
|
||||||
);
|
|
||||||
sensorDataList.add(
|
|
||||||
new StatisticData(
|
|
||||||
new DataField(context.getString(R.string.stats_custom_layout_average_heart_rate_key), context.getString(R.string.sensor_state_heart_rate_avg), true, true, false),
|
|
||||||
StringUtils.getHeartRateParts(context, sensorStatistics.getAvgHeartRate()),
|
|
||||||
null
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (sensorStatistics.hasCadence()) {
|
|
||||||
sensorDataList.add(
|
|
||||||
new StatisticData(
|
|
||||||
new DataField(context.getString(R.string.stats_custom_layout_cadence_key), context.getString(R.string.sensor_state_cadence_max), true, true, false),
|
|
||||||
StringUtils.getCadenceParts(context, sensorStatistics.getMaxCadence()),
|
|
||||||
null
|
|
||||||
)
|
|
||||||
);
|
|
||||||
sensorDataList.add(
|
|
||||||
new StatisticData(
|
|
||||||
new DataField(context.getString(R.string.stats_custom_layout_average_cadence_key), context.getString(R.string.sensor_state_cadence_avg), true, true, false),
|
|
||||||
StringUtils.getCadenceParts(context, sensorStatistics.getAvgCadence()),
|
|
||||||
null
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (sensorStatistics.hasPower()) {
|
|
||||||
sensorDataList.add(
|
|
||||||
new StatisticData(
|
|
||||||
new DataField(context.getString(R.string.stats_custom_layout_power_key), context.getString(R.string.sensor_state_power_avg), true, true, false),
|
|
||||||
StringUtils.getPowerParts(context, sensorStatistics.getAvgPower()),
|
|
||||||
null
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sensorDataList;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package de.dennisguse.opentracks.viewmodels;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.viewbinding.ViewBinding;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.services.RecordingData;
|
||||||
|
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||||
|
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
|
||||||
|
|
||||||
|
public abstract class StatisticViewHolder<T extends ViewBinding> {
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
|
||||||
|
private T binding;
|
||||||
|
|
||||||
|
public void initialize(Context context, LayoutInflater inflater) {
|
||||||
|
this.context = context;
|
||||||
|
this.binding = createViewBinding(inflater);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract T createViewBinding(LayoutInflater inflater);
|
||||||
|
|
||||||
|
public abstract void configureUI(DataField dataField);
|
||||||
|
|
||||||
|
public abstract void onChanged(UnitSystem unitSystem, RecordingData data);
|
||||||
|
|
||||||
|
public View getView() {
|
||||||
|
return binding.getRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
T getBinding() {
|
||||||
|
return binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
Context getContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
package de.dennisguse.opentracks.viewmodels;
|
|
||||||
|
|
||||||
import android.app.Application;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.lifecycle.AndroidViewModel;
|
|
||||||
import androidx.lifecycle.MutableLiveData;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.services.RecordingData;
|
|
||||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
|
|
||||||
|
|
||||||
public class StatisticsDataModel extends AndroidViewModel {
|
|
||||||
|
|
||||||
private MutableLiveData<List<StatisticData>> statsData;
|
|
||||||
|
|
||||||
public StatisticsDataModel(@NonNull Application application) {
|
|
||||||
super(application);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MutableLiveData<List<StatisticData>> getStatsData() {
|
|
||||||
if (statsData == null) {
|
|
||||||
statsData = new MutableLiveData<>();
|
|
||||||
}
|
|
||||||
return statsData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void update(RecordingData recordingData, Layout layout, UnitSystem unitSystem) {
|
|
||||||
new Thread(() -> {
|
|
||||||
List<StatisticData> statisticDataList = StatisticDataBuilder.fromRecordingData(getApplication(), recordingData, layout, unitSystem);
|
|
||||||
statsData.postValue(statisticDataList);
|
|
||||||
}).start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -120,6 +120,8 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<include android:id="@+id/bottom_app_bar_layout" layout="@layout/bottomappbar" />
|
<include
|
||||||
|
android:id="@+id/bottom_app_bar_layout"
|
||||||
|
layout="@layout/bottomappbar" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|||||||
@@ -56,6 +56,8 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<include android:id="@+id/bottom_app_bar_layout" layout="@layout/bottomappbar" />
|
<include
|
||||||
|
android:id="@+id/bottom_app_bar_layout"
|
||||||
|
layout="@layout/bottomappbar" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@@ -75,6 +75,8 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<include android:id="@+id/bottom_app_bar_layout" layout="@layout/bottomappbar" />
|
<include
|
||||||
|
android:id="@+id/bottom_app_bar_layout"
|
||||||
|
layout="@layout/bottomappbar" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@@ -21,6 +21,8 @@
|
|||||||
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
<include android:id="@+id/bottom_app_bar_layout" layout="@layout/bottomappbar" />
|
<include
|
||||||
|
android:id="@+id/bottom_app_bar_layout"
|
||||||
|
layout="@layout/bottomappbar" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|||||||
@@ -26,6 +26,8 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<include android:id="@+id/bottom_app_bar_layout" layout="@layout/bottomappbar" />
|
<include
|
||||||
|
android:id="@+id/bottom_app_bar_layout"
|
||||||
|
layout="@layout/bottomappbar" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@@ -22,11 +22,10 @@
|
|||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintBottom_toBottomOf="parent" />
|
app:layout_constraintBottom_toBottomOf="parent" />
|
||||||
|
|
||||||
<include
|
<FrameLayout
|
||||||
android:id="@+id/stats_layout"
|
android:id="@+id/stats_layout"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
layout="@layout/stats_generic_item"
|
|
||||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
||||||
app:layout_constraintStart_toEndOf="@id/stats_iv_drag_indicator"
|
app:layout_constraintStart_toEndOf="@id/stats_iv_drag_indicator"
|
||||||
app:layout_constraintEnd_toStartOf="@id/stats_icon_show_status"
|
app:layout_constraintEnd_toStartOf="@id/stats_icon_show_status"
|
||||||
|
|||||||
@@ -307,6 +307,8 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<include android:id="@+id/bottom_app_bar_layout" layout="@layout/bottomappbar" />
|
<include
|
||||||
|
android:id="@+id/bottom_app_bar_layout"
|
||||||
|
layout="@layout/bottomappbar" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@@ -143,6 +143,8 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<include android:id="@+id/bottom_app_bar_layout" layout="@layout/bottomappbar" />
|
<include
|
||||||
|
android:id="@+id/bottom_app_bar_layout"
|
||||||
|
layout="@layout/bottomappbar" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@@ -251,6 +251,8 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<include android:id="@+id/bottom_app_bar_layout" layout="@layout/bottomappbar" />
|
<include
|
||||||
|
android:id="@+id/bottom_app_bar_layout"
|
||||||
|
layout="@layout/bottomappbar" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@@ -28,6 +28,8 @@ limitations under the License.
|
|||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<include android:id="@+id/bottom_app_bar_layout" layout="@layout/bottomappbar" />
|
<include
|
||||||
|
android:id="@+id/bottom_app_bar_layout"
|
||||||
|
layout="@layout/bottomappbar" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@@ -138,6 +138,8 @@ limitations under the License.
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<include android:id="@+id/bottom_app_bar_layout" layout="@layout/bottomappbar" />
|
<include
|
||||||
|
android:id="@+id/bottom_app_bar_layout"
|
||||||
|
layout="@layout/bottomappbar" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@@ -38,6 +38,8 @@ limitations under the License.
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<include android:id="@+id/bottom_app_bar_layout" layout="@layout/bottomappbar" />
|
<include
|
||||||
|
android:id="@+id/bottom_app_bar_layout"
|
||||||
|
layout="@layout/bottomappbar" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<include android:id="@+id/bottom_app_bar_layout" layout="@layout/bottomappbar" />
|
<include
|
||||||
|
android:id="@+id/bottom_app_bar_layout"
|
||||||
|
layout="@layout/bottomappbar" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@@ -1,16 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.gridlayout.widget.GridLayout
|
||||||
android:id="@+id/stats_recycler_view"
|
android:id="@+id/stats_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintEnd_toStartOf="parent"
|
android:layout_gravity="center_horizontal" />
|
||||||
app:layout_constraintStart_toEndOf="parent"
|
</ScrollView>
|
||||||
app:layout_constraintTop_toBottomOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -46,6 +46,8 @@ limitations under the License.
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<include android:id="@+id/bottom_app_bar_layout" layout="@layout/bottomappbar" />
|
<include
|
||||||
|
android:id="@+id/bottom_app_bar_layout"
|
||||||
|
layout="@layout/bottomappbar" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@@ -36,6 +36,8 @@ limitations under the License.
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<include android:id="@+id/bottom_app_bar_layout" layout="@layout/bottomappbar" />
|
<include
|
||||||
|
android:id="@+id/bottom_app_bar_layout"
|
||||||
|
layout="@layout/bottomappbar" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
Reference in New Issue
Block a user