forked from upstream-mirrors/OpenTracks
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cb71c40350 | |||
| 22bde4af3c | |||
| ecfcc180b9 | |||
| 9c1af67e7e | |||
| 873422540f | |||
| 6cf0b26058 | |||
| a94b227767 | |||
| c820a76102 | |||
| e61b065f2b | |||
| 24d8e976da | |||
| 79e45f7aea | |||
| 72f7b12da6 | |||
| 9973dd05bd | |||
| 41072a8981 | |||
| b1dcdf326c | |||
| c271dd32ac | |||
| 5dfa867034 | |||
| ada0f88538 | |||
| 4df0e2a65c | |||
| 9e7e8c60a6 | |||
| 1075021e21 | |||
| b5acfa022d | |||
| 6fe38453b3 |
@@ -7,7 +7,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2.4.0
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-java@v2
|
||||
with:
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2.4.0
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-java@v2
|
||||
with:
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2.4.0
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-java@v2
|
||||
with:
|
||||
|
||||
@@ -42,6 +42,7 @@ captures/
|
||||
.idea/dictionaries
|
||||
.idea/libraries
|
||||
.idea/caches
|
||||
.idea/misc.xml
|
||||
|
||||
|
||||
# IntelliJ IDEA
|
||||
|
||||
Generated
-24
@@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DesignSurface">
|
||||
<option name="filePathToZoomLevelMap">
|
||||
<map>
|
||||
<entry key="src/main/res/drawable/ic_activity_bike_24dp.xml" value="0.1765" />
|
||||
<entry key="src/main/res/drawable/ic_activity_sailing_24dp.xml" value="0.1935" />
|
||||
<entry key="src/main/res/drawable/ic_activity_snowboarding_24dp.xml" value="0.1935" />
|
||||
<entry key="src/main/res/drawable/ic_activity_swimming_24dp.xml" value="0.1935" />
|
||||
<entry key="src/main/res/drawable/ic_activity_swimming_open_24dp.xml" value="0.1935" />
|
||||
<entry key="src/main/res/drawable/ic_activity_workout_24dp.xml" value="0.1935" />
|
||||
<entry key="src/main/res/menu/aggregated_statistics.xml" value="0.10208333333333333" />
|
||||
<entry key="src/main/res/xml/settings_gps.xml" value="0.22604166666666667" />
|
||||
<entry key="src/main/res/xml/settings_sensors.xml" value="0.2101851851851852" />
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
||||
+4
-4
@@ -6,7 +6,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.1.1'
|
||||
classpath 'com.android.tools.build:gradle:7.1.2'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,8 +59,8 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId "de.dennisguse.opentracks"
|
||||
versionCode 4752
|
||||
versionName "v3.25.0"
|
||||
versionCode 4775
|
||||
versionName "v3.26.1"
|
||||
|
||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||
|
||||
@@ -130,7 +130,7 @@ dependencies {
|
||||
androidTestImplementation 'androidx.test:rules:1.4.0'
|
||||
androidTestImplementation 'androidx.test:runner:1.4.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
androidTestImplementation 'org.mockito:mockito-android:4.3.1'
|
||||
androidTestImplementation 'org.mockito:mockito-android:4.4.0'
|
||||
|
||||
androidTestUtil 'androidx.test:orchestrator:1.4.1'
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
v3.25.1: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Automatic export: hopefully prevent silent failures
|
||||
- Automatic export: prevent crash
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
v3.26.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Voice announcements are configurable
|
||||
- Voice announcements of heart rate
|
||||
|
||||
Bugfix:
|
||||
- Crash due to race condition
|
||||
@@ -0,0 +1,4 @@
|
||||
v3.26.1: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Contextual action bar didn't overlap toolbar in night theme.
|
||||
@@ -1 +1 @@
|
||||
OpenTracks - Suivi d'activités sportives respectueux de votre vie privée.
|
||||
Appli de suivi des activités sportives respectueuse de votre vie privée.
|
||||
|
||||
@@ -13,22 +13,20 @@ import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.anything;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.childAtPosition;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.veryLongTouch;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.withListSize;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Instrumentation;
|
||||
import android.content.Context;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
import android.widget.ListView;
|
||||
|
||||
import androidx.test.espresso.Espresso;
|
||||
import androidx.test.espresso.IdlingRegistry;
|
||||
import androidx.test.espresso.IdlingResource;
|
||||
import androidx.test.espresso.UiController;
|
||||
import androidx.test.espresso.ViewAction;
|
||||
import androidx.test.espresso.ViewInteraction;
|
||||
import androidx.test.espresso.action.MotionEvents;
|
||||
import androidx.test.espresso.assertion.ViewAssertions;
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@@ -37,7 +35,6 @@ import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.TypeSafeMatcher;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@@ -65,12 +62,12 @@ public class EspressoDeleteTrackTest {
|
||||
Instrumentation instrumentation
|
||||
= InstrumentationRegistry.getInstrumentation();
|
||||
idlingResource = new MyIdlingResource(instrumentation.getTargetContext());
|
||||
Espresso.registerIdlingResources(idlingResource);
|
||||
IdlingRegistry.getInstance().register(idlingResource);
|
||||
}
|
||||
|
||||
@After
|
||||
public void unregisterIntentServiceIdlingResource() {
|
||||
Espresso.unregisterIdlingResources(idlingResource);
|
||||
IdlingRegistry.getInstance().unregister(idlingResource);
|
||||
}
|
||||
|
||||
@Ignore("Test fails permanently")
|
||||
@@ -140,87 +137,6 @@ public class EspressoDeleteTrackTest {
|
||||
onView(withId(R.id.track_list)).check(ViewAssertions.matches(withListSize(countBefore - 1)));
|
||||
}
|
||||
|
||||
private static Matcher<View> childAtPosition(
|
||||
final Matcher<View> parentMatcher, final int position) {
|
||||
|
||||
return new TypeSafeMatcher<>() {
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("Child at position " + position + " in parent ");
|
||||
parentMatcher.describeTo(description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matchesSafely(View view) {
|
||||
ViewParent parent = view.getParent();
|
||||
return parent instanceof ViewGroup && parentMatcher.matches(parent)
|
||||
&& view.equals(((ViewGroup) parent).getChildAt(position));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Matcher<View> withListSize (final int size) {
|
||||
return new TypeSafeMatcher<>() {
|
||||
@Override
|
||||
public boolean matchesSafely(final View view) {
|
||||
return ((ListView) view).getCount() == size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(final Description description) {
|
||||
description.appendText("ListView should have " + size + " items");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static ViewAction waitFor(final long duration_ms) {
|
||||
return new ViewAction() {
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Wait for milliseconds.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Matcher<View> getConstraints() {
|
||||
return isDisplayed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void perform(UiController uiController, final View view) {
|
||||
uiController.loopMainThreadForAtLeast(duration_ms);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static ViewAction veryLongTouch(final int duration_ms) {
|
||||
return new ViewAction() {
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Perform long touch.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Matcher<View> getConstraints() {
|
||||
return isDisplayed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void perform(UiController uiController, final View view) {
|
||||
// Get view absolute position
|
||||
int[] location = new int[2];
|
||||
view.getLocationOnScreen(location);
|
||||
|
||||
// Offset coordinates by view position
|
||||
float[] coordinates = new float[]{location[0] + 1, location[1] + 1};
|
||||
|
||||
// Send down event, pause, and send up
|
||||
MotionEvent down = MotionEvents.sendDown(uiController, coordinates, new float[]{1f, 1f}).down;
|
||||
uiController.loopMainThreadForAtLeast(duration_ms);
|
||||
MotionEvents.sendUp(uiController, down, coordinates);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private int numberOfItemsListView() {
|
||||
final int[] counts = new int[1];
|
||||
|
||||
+11
-144
@@ -2,52 +2,43 @@ package de.dennisguse.opentracks;
|
||||
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.Espresso.openContextualActionModeOverflowMenu;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
|
||||
import static androidx.test.espresso.action.ViewActions.replaceText;
|
||||
import static androidx.test.espresso.action.ViewActions.scrollTo;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.childAtPosition;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.veryLongTouch;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
|
||||
import androidx.test.espresso.PerformException;
|
||||
import androidx.test.espresso.UiController;
|
||||
import androidx.test.espresso.ViewAction;
|
||||
import androidx.test.espresso.ViewInteraction;
|
||||
import androidx.test.espresso.action.MotionEvents;
|
||||
import androidx.test.espresso.util.HumanReadables;
|
||||
import androidx.test.espresso.util.TreeIterables;
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.TypeSafeMatcher;
|
||||
import org.hamcrest.core.IsInstanceOf;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.Locale;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class EspressoEditTrackRecordingTest {
|
||||
|
||||
@Rule
|
||||
public final LocaleRule mLocaleRule = new LocaleRule(Locale.ENGLISH);
|
||||
|
||||
@Rule
|
||||
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
||||
|
||||
@@ -71,26 +62,10 @@ public class EspressoEditTrackRecordingTest {
|
||||
trackControllerRecordButton.perform(waitFor(15000));
|
||||
|
||||
// open menu
|
||||
ViewInteraction overflowMenuButton = onView(
|
||||
allOf(withContentDescription("More options"),
|
||||
childAtPosition(
|
||||
childAtPosition(
|
||||
withId(R.id.toolbar),
|
||||
2),
|
||||
2),
|
||||
isDisplayed()));
|
||||
overflowMenuButton.perform(click());
|
||||
openContextualActionModeOverflowMenu();
|
||||
|
||||
// click on edit
|
||||
ViewInteraction appCompatTextView = onView(
|
||||
allOf(withId(R.id.title), withText("Edit"),
|
||||
childAtPosition(
|
||||
childAtPosition(
|
||||
withId(R.id.content),
|
||||
0),
|
||||
0),
|
||||
isDisplayed()));
|
||||
appCompatTextView.perform(click());
|
||||
// Click the item.
|
||||
onView(withText(R.string.menu_edit)).perform(click());
|
||||
|
||||
// change name for "New Name"
|
||||
ViewInteraction textInputEditText = onView(
|
||||
@@ -147,112 +122,4 @@ public class EspressoEditTrackRecordingTest {
|
||||
}
|
||||
}
|
||||
|
||||
private static Matcher<View> childAtPosition(
|
||||
final Matcher<View> parentMatcher, final int position) {
|
||||
|
||||
return new TypeSafeMatcher<>() {
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("Child at position " + position + " in parent ");
|
||||
parentMatcher.describeTo(description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matchesSafely(View view) {
|
||||
ViewParent parent = view.getParent();
|
||||
return parent instanceof ViewGroup && parentMatcher.matches(parent)
|
||||
&& view.equals(((ViewGroup) parent).getChildAt(position));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static ViewAction waitId(final int viewId, final long millis) {
|
||||
return new ViewAction() {
|
||||
@Override
|
||||
public Matcher<View> getConstraints() {
|
||||
return isRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "wait for a specific view with id <" + viewId + "> during " + millis + " millis.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void perform(final UiController uiController, final View view) {
|
||||
uiController.loopMainThreadUntilIdle();
|
||||
final long startTime = System.currentTimeMillis();
|
||||
final long endTime = startTime + millis;
|
||||
final Matcher<View> viewMatcher = withId(viewId);
|
||||
|
||||
do {
|
||||
for (View child : TreeIterables.breadthFirstViewTraversal(view)) {
|
||||
// found view with required ID
|
||||
if (viewMatcher.matches(child)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uiController.loopMainThreadForAtLeast(50);
|
||||
}
|
||||
while (System.currentTimeMillis() < endTime);
|
||||
|
||||
// timeout happens
|
||||
throw new PerformException.Builder()
|
||||
.withActionDescription(this.getDescription())
|
||||
.withViewDescription(HumanReadables.describe(view))
|
||||
.withCause(new TimeoutException())
|
||||
.build();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static ViewAction waitFor(final long duration_ms) {
|
||||
return new ViewAction() {
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Wait for milliseconds.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Matcher<View> getConstraints() {
|
||||
return isDisplayed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void perform(UiController uiController, final View view) {
|
||||
uiController.loopMainThreadForAtLeast(duration_ms);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static ViewAction veryLongTouch(final int duration_ms) {
|
||||
return new ViewAction() {
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Perform long touch.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Matcher<View> getConstraints() {
|
||||
return isDisplayed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void perform(UiController uiController, final View view) {
|
||||
// Get view absolute position
|
||||
int[] location = new int[2];
|
||||
view.getLocationOnScreen(location);
|
||||
|
||||
// Offset coordinates by view position
|
||||
float[] coordinates = new float[]{location[0] + 1, location[1] + 1};
|
||||
|
||||
// Send down event, pause, and send up
|
||||
MotionEvent down = MotionEvents.sendDown(uiController, coordinates, new float[]{1f, 1f}).down;
|
||||
uiController.loopMainThreadForAtLeast(duration_ms);
|
||||
MotionEvents.sendUp(uiController, down, coordinates);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import static androidx.test.espresso.Espresso.onData;
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static org.hamcrest.Matchers.anything;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.selectTabAtIndex;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
@@ -12,21 +22,11 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static androidx.test.espresso.Espresso.onData;
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static org.hamcrest.Matchers.anything;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class EspressoUITest {
|
||||
@@ -136,43 +136,4 @@ public class EspressoUITest {
|
||||
};
|
||||
}
|
||||
|
||||
private static ViewAction waitFor(final long duration_ms) {
|
||||
return new ViewAction() {
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Wait for milliseconds.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Matcher<View> getConstraints() {
|
||||
return isDisplayed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void perform(UiController uiController, final View view) {
|
||||
uiController.loopMainThreadForAtLeast(duration_ms);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static ViewAction selectTabAtIndex(final int index) {
|
||||
return new ViewAction() {
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Selecting tab.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Matcher<View> getConstraints() {
|
||||
return isDisplayed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void perform(UiController uiController, View view) {
|
||||
TabLayout tabLayout = (TabLayout) view;
|
||||
tabLayout.getTabAt(index).select();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class LocaleRule implements TestRule {
|
||||
|
||||
private final Locale[] mLocales;
|
||||
private Locale mDeviceLocale;
|
||||
|
||||
|
||||
public LocaleRule(Locale... locales) {
|
||||
assert locales != null && locales.length > 0;
|
||||
mLocales = locales;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement apply(Statement base, Description description) {
|
||||
return new Statement() {
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
try {
|
||||
mDeviceLocale = Locale.getDefault();
|
||||
for (Locale locale : mLocales) {
|
||||
setLocale(locale);
|
||||
base.evaluate();
|
||||
}
|
||||
} finally {
|
||||
if (mDeviceLocale != null) {
|
||||
setLocale(mDeviceLocale);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void setLocale(Locale locale) {
|
||||
Resources resources = ApplicationProvider.getApplicationContext().getResources();
|
||||
Locale.setDefault(locale);
|
||||
Configuration config = resources.getConfiguration();
|
||||
config.setLocale(locale);
|
||||
DisplayMetrics displayMetrics = resources.getDisplayMetrics();
|
||||
resources.updateConfiguration(config, displayMetrics);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -125,7 +125,7 @@ public class TestDataUtil {
|
||||
trackPoint.setAltitude(i * ALTITUDE_INTERVAL);
|
||||
trackPoint.setSpeed(Speed.of(5f + (i / 10f)));
|
||||
|
||||
trackPoint.setHeartRate(100f + i);
|
||||
trackPoint.setHeartRate(100f + i % 80);
|
||||
trackPoint.setCadence(300f + i);
|
||||
trackPoint.setPower(400f + i);
|
||||
trackPoint.setAltitudeGain(ALTITUDE_GAIN);
|
||||
|
||||
+1
-1
@@ -215,7 +215,7 @@ public class TrackRecordingServiceTestStateMachine {
|
||||
|
||||
// when
|
||||
service.pauseCurrentTrack();
|
||||
Thread.sleep(1000);
|
||||
Thread.sleep(2000);
|
||||
|
||||
// then
|
||||
assertEquals(TrackRecordingService.STATUS_DEFAULT, service.getRecordingStatusObservable().getValue());
|
||||
|
||||
+52
-77
@@ -8,32 +8,40 @@ import android.util.Pair;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Locale;
|
||||
|
||||
import de.dennisguse.opentracks.LocaleRule;
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.stats.SensorStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.ui.intervals.IntervalStatistics;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class VoiceAnnouncementUtilsTest {
|
||||
|
||||
@Rule
|
||||
public final LocaleRule mLocaleRule = new LocaleRule(Locale.ENGLISH);
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
contentProviderUtils = new ContentProviderUtils(context);
|
||||
PreferencesUtils.setVoiceAnnounceHeartRate(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -46,10 +54,10 @@ public class VoiceAnnouncementUtilsTest {
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, true, true, null);
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, true, true, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("total distance 20.00 kilometers in 1 hour 5 minutes 10 seconds at 18.4 kilometers per hour", announcement);
|
||||
assertEquals("Total distance 20.00 kilometers. 1 hour 5 minutes 10 seconds. Speed 18.4 kilometers per hour.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -63,22 +71,15 @@ public class VoiceAnnouncementUtilsTest {
|
||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||
assertEquals(trackPointIterator.getCount(), numberOfPoints);
|
||||
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000));
|
||||
intervalStatistics.addTrackPoints(trackPointIterator);
|
||||
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||
}
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, true, true, lastInterval);
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, true, true, lastInterval, null).toString();
|
||||
|
||||
// then
|
||||
Assert.assertEquals(
|
||||
"total distance " +
|
||||
StringUtils.getDistanceParts(context, stats.getTotalDistance(), true).first +
|
||||
" kilometers in " + buildAndGetTimeText(stats.getTotalTime(), false) + " at " +
|
||||
StringUtils.getSpeedParts(context, stats.getAverageMovingSpeed(), true, true).first +
|
||||
" kilometers per hour Lap speed of " +
|
||||
StringUtils.getSpeedParts(context, lastInterval.getSpeed(), true, true).first +
|
||||
" kilometers per hour",
|
||||
announcement);
|
||||
assertEquals("Total distance 14.21 kilometers. 16 minutes 39 seconds. Speed 51.2 kilometers per hour. Lap speed 51.2 kilometers per hour.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -91,10 +92,10 @@ public class VoiceAnnouncementUtilsTest {
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, true, false, null);
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, true, false, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("total distance 20.00 kilometers in 1 hour 5 minutes 10 seconds at 3 minutes 15 seconds per kilometer", announcement);
|
||||
assertEquals("Total distance 20.00 kilometers. 1 hour 5 minutes 10 seconds. Pace 3 minutes 15 seconds per kilometer.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -108,22 +109,15 @@ public class VoiceAnnouncementUtilsTest {
|
||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||
assertEquals(trackPointIterator.getCount(), numberOfPoints);
|
||||
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000));
|
||||
intervalStatistics.addTrackPoints(trackPointIterator);
|
||||
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||
}
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, true, false, lastInterval);
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, true, false, lastInterval, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals(
|
||||
"total distance " +
|
||||
StringUtils.getDistanceParts(context, stats.getTotalDistance(), true).first +
|
||||
" kilometers in " + buildAndGetTimeText(stats.getTotalTime(), false) + " at " +
|
||||
buildAndGetTimeText(stats.getAverageMovingSpeed().toPace(true), true) +
|
||||
" per kilometer Lap time of " +
|
||||
buildAndGetTimeText(lastInterval.getSpeed().toPace(true), true) +
|
||||
" per kilometer",
|
||||
announcement);
|
||||
assertEquals("Total distance 14.21 kilometers. 16 minutes 39 seconds. Pace 1 minute 10 seconds per kilometer. Lap time 1 minute 10 seconds per kilometer.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -136,10 +130,10 @@ public class VoiceAnnouncementUtilsTest {
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, false, true, null);
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, false, true, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("total distance 12.43 miles in 1 hour 5 minutes 10 seconds at 11.4 miles per hour", announcement);
|
||||
assertEquals("Total distance 12.43 miles. 1 hour 5 minutes 10 seconds. Speed 11.4 miles per hour.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -153,22 +147,15 @@ public class VoiceAnnouncementUtilsTest {
|
||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||
assertEquals(trackPointIterator.getCount(), numberOfPoints);
|
||||
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000));
|
||||
intervalStatistics.addTrackPoints(trackPointIterator);
|
||||
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||
}
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, false, true, lastInterval);
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, false, true, lastInterval, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals(
|
||||
"total distance " +
|
||||
StringUtils.getDistanceParts(context, stats.getTotalDistance(), false).first +
|
||||
" miles in " + buildAndGetTimeText(stats.getTotalTime(), false) + " at " +
|
||||
StringUtils.getSpeedParts(context, stats.getAverageMovingSpeed(), false, true).first +
|
||||
" miles per hour Lap speed of " +
|
||||
StringUtils.getSpeedParts(context, lastInterval.getSpeed(), false, true).first +
|
||||
" miles per hour",
|
||||
announcement);
|
||||
assertEquals("Total distance 8.83 miles. 16 minutes 39 seconds. Speed 31.8 miles per hour. Lap speed 31.8 miles per hour.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -181,10 +168,10 @@ public class VoiceAnnouncementUtilsTest {
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, false, false, null);
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, false, false, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("total distance 12.43 miles in 1 hour 5 minutes 10 seconds at 5 minutes 15 seconds per mile", announcement);
|
||||
assertEquals("Total distance 12.43 miles. 1 hour 5 minutes 10 seconds. Pace 5 minutes 15 seconds per mile.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -198,51 +185,39 @@ public class VoiceAnnouncementUtilsTest {
|
||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||
assertEquals(trackPointIterator.getCount(), numberOfPoints);
|
||||
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000));
|
||||
intervalStatistics.addTrackPoints(trackPointIterator);
|
||||
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||
}
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, false, false, lastInterval);
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, false, false, lastInterval, null).toString();
|
||||
|
||||
// then
|
||||
//assertEquals("total distance 12.43 miles in 1 hour 5 minutes 10 seconds at 5 minutes 15 seconds per mile Lap time of 1 minute 53 seconds per mile", announcement);
|
||||
assertEquals(
|
||||
"total distance " +
|
||||
StringUtils.getDistanceParts(context, stats.getTotalDistance(), false).first +
|
||||
" miles in " + buildAndGetTimeText(stats.getTotalTime(), false) + " at " +
|
||||
buildAndGetTimeText(stats.getAverageMovingSpeed().toPace(false), true) +
|
||||
" per mile Lap time of " +
|
||||
buildAndGetTimeText(lastInterval.getSpeed().toPace(false), true) +
|
||||
" per mile",
|
||||
announcement);
|
||||
assertEquals("Total distance 8.83 miles. 16 minutes 39 seconds. Pace 1 minute 53 seconds per mile. Lap time 1 minute 53 seconds per mile.", announcement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an returns the text representing the duration's time.
|
||||
*
|
||||
* @param duration Duration object.
|
||||
* @param showFromMinutes show minutes tough it's 0.
|
||||
* @return text representing the duratin's time.
|
||||
*/
|
||||
private String buildAndGetTimeText(Duration duration, boolean showFromMinutes) {
|
||||
long hours = Math.abs(duration.getSeconds()) / 3600;
|
||||
long minutes = (Math.abs(duration.getSeconds()) % 3600) / 60;
|
||||
long seconds = Math.abs(duration.getSeconds()) % 60;
|
||||
String hUnit = hours > 1 || hours == 0 ? "hours" : "hour";
|
||||
String mUnit = minutes > 1 || minutes == 0 ? "minutes" : "minute";
|
||||
String sUnit = seconds > 1 || seconds == 0 ? "seconds" : "second";
|
||||
@Test
|
||||
public void getAnnouncement_heart_rate_and_sensor_statistics() {
|
||||
PreferencesUtils.setVoiceAnnounceHeartRate(true);
|
||||
|
||||
String res = hours > 0 ? hours + " " + hUnit : "";
|
||||
if (hours > 0) {
|
||||
res += minutes > 0 || showFromMinutes ? " " + minutes + " " + mUnit : "";
|
||||
} else {
|
||||
res += minutes > 0 || showFromMinutes ? minutes + " " + mUnit : "";
|
||||
int numberOfPoints = 1000;
|
||||
Pair<Track.Id, TrackStatistics> trackWithStats = TestDataUtil.buildTrackWithTrackPoints(contentProviderUtils, numberOfPoints);
|
||||
Track.Id trackId = trackWithStats.first;
|
||||
TrackStatistics stats = trackWithStats.second;
|
||||
IntervalStatistics.Interval lastInterval;
|
||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||
assertEquals(trackPointIterator.getCount(), numberOfPoints);
|
||||
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000));
|
||||
intervalStatistics.addTrackPoints(trackPointIterator);
|
||||
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||
}
|
||||
if (hours > 0 || minutes > 0 || showFromMinutes) {
|
||||
res += " " + seconds + " " + sUnit;
|
||||
} else {
|
||||
res += seconds + " " + sUnit;
|
||||
}
|
||||
return res;
|
||||
|
||||
SensorStatistics sensorStatistics = new SensorStatistics(HeartRate.of(180f), HeartRate.of(180f), null, null, null);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, true, true, lastInterval, sensorStatistics).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 14.21 kilometers. 16 minutes 39 seconds. Speed 51.2 kilometers per hour. Lap speed 51.2 kilometers per hour. Average heart rate 180 bpm. Current heart rate 133 bpm.", announcement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -31,7 +32,9 @@ import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Locale;
|
||||
|
||||
import de.dennisguse.opentracks.LocaleRule;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
@@ -47,6 +50,9 @@ import de.dennisguse.opentracks.util.StringUtils;
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class DescriptionGeneratorTest {
|
||||
|
||||
@Rule
|
||||
public final LocaleRule mLocaleRule = new LocaleRule(Locale.ENGLISH);
|
||||
|
||||
private static final Instant START_TIME = Instant.ofEpochMilli(1288721514000L);
|
||||
private DescriptionGenerator descriptionGenerator;
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
|
||||
/**
|
||||
@@ -67,6 +68,7 @@ public class TrackStatisticsTest {
|
||||
assertEquals(0.0, statistics.getMaxSpeed().toMPS(), 0.0);
|
||||
assertEquals(0.0, statistics.getAverageSpeed().toMPS(), 0.0);
|
||||
assertEquals(0.0, statistics.getAverageMovingSpeed().toMPS(), 0.0);
|
||||
assertNull(statistics.getAverageHeartRate());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -91,6 +93,8 @@ public class TrackStatisticsTest {
|
||||
statistics.setMinAltitude(1200.0); // Resulting min altitude
|
||||
statistics2.setMaxAltitude(3575.0); // Resulting max altitude
|
||||
statistics2.setMinAltitude(2800.0);
|
||||
statistics.setAverageHeartRate(HeartRate.of(100f));
|
||||
statistics2.setAverageHeartRate(HeartRate.of(200f));
|
||||
|
||||
// when
|
||||
statistics.merge(statistics2);
|
||||
@@ -105,6 +109,7 @@ public class TrackStatisticsTest {
|
||||
assertEquals(Speed.of(statistics.getTotalDistance(), statistics.getMovingTime()).toMPS(), statistics.getMaxSpeed().toMPS(), 0.001);
|
||||
assertEquals(1200.0, statistics.getMinAltitude(), 0.001);
|
||||
assertEquals(3575.0, statistics.getMaxAltitude(), 0.001);
|
||||
assertEquals(150.0, statistics.getAverageHeartRate().getBPM(), 0.001);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -40,6 +40,7 @@ public class TrackStatisticsUpdaterTest {
|
||||
|
||||
assertNull(statistics.getTotalAltitudeGain());
|
||||
assertNull(statistics.getTotalAltitudeLoss());
|
||||
assertNull(statistics.getAverageHeartRate());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -66,6 +67,7 @@ public class TrackStatisticsUpdaterTest {
|
||||
|
||||
assertNull(statistics.getTotalAltitudeGain());
|
||||
assertNull(statistics.getTotalAltitudeLoss());
|
||||
assertNull(statistics.getAverageHeartRate());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -91,6 +93,7 @@ public class TrackStatisticsUpdaterTest {
|
||||
assertEquals(14.226, statistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(14.226, statistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||
assertEquals(9.84, statistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(106.85f, statistics.getAverageHeartRate().getBPM(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+3
-1
@@ -9,6 +9,7 @@ import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
||||
|
||||
import android.util.Pair;
|
||||
import android.view.View;
|
||||
@@ -73,7 +74,7 @@ public class EspressoAggregatedFilterTest {
|
||||
withParent(allOf(withId(R.id.filter_items),
|
||||
withParent(IsInstanceOf.<View>instanceOf(android.view.ViewGroup.class)))),
|
||||
isDisplayed()));
|
||||
checkBox.check(matches(isDisplayed()));
|
||||
checkBox.perform(waitFor(2000));
|
||||
|
||||
// check there's an edit text for "from date"
|
||||
onView(withId(R.id.filter_date_edit_text_from)).check(matches(isDisplayed()));
|
||||
@@ -81,4 +82,5 @@ public class EspressoAggregatedFilterTest {
|
||||
// check there's an edit text for "to date"
|
||||
onView(withId(R.id.filter_date_edit_text_to)).check(matches(isDisplayed()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-57
@@ -4,31 +4,22 @@ package de.dennisguse.opentracks.ui.customRecordingLayout;
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.assertThat;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.veryLongTouch;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.test.espresso.NoMatchingViewException;
|
||||
import androidx.test.espresso.UiController;
|
||||
import androidx.test.espresso.ViewAction;
|
||||
import androidx.test.espresso.ViewAssertion;
|
||||
import androidx.test.espresso.ViewInteraction;
|
||||
import androidx.test.espresso.action.MotionEvents;
|
||||
import androidx.test.espresso.matcher.ViewMatchers;
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.TypeSafeMatcher;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -82,51 +73,4 @@ public class EspressoCustomLayoutTest {
|
||||
}
|
||||
}
|
||||
|
||||
private static Matcher<View> childAtPosition(
|
||||
final Matcher<View> parentMatcher, final int position) {
|
||||
|
||||
return new TypeSafeMatcher<>() {
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("Child at position " + position + " in parent ");
|
||||
parentMatcher.describeTo(description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matchesSafely(View view) {
|
||||
ViewParent parent = view.getParent();
|
||||
return parent instanceof ViewGroup && parentMatcher.matches(parent)
|
||||
&& view.equals(((ViewGroup) parent).getChildAt(position));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static ViewAction veryLongTouch(final int duration_ms) {
|
||||
return new ViewAction() {
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Perform long touch.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Matcher<View> getConstraints() {
|
||||
return isDisplayed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void perform(UiController uiController, final View view) {
|
||||
// Get view absolute position
|
||||
int[] location = new int[2];
|
||||
view.getLocationOnScreen(location);
|
||||
|
||||
// Offset coordinates by view position
|
||||
float[] coordinates = new float[]{location[0] + 1, location[1] + 1};
|
||||
|
||||
// Send down event, pause, and send up
|
||||
MotionEvent down = MotionEvents.sendDown(uiController, coordinates, new float[]{1f, 1f}).down;
|
||||
uiController.loopMainThreadForAtLeast(duration_ms);
|
||||
MotionEvents.sendUp(uiController, down, coordinates);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
package de.dennisguse.opentracks.util;
|
||||
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.util.TreeIterables.breadthFirstViewTraversal;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
import android.widget.ListView;
|
||||
|
||||
import androidx.test.espresso.PerformException;
|
||||
import androidx.test.espresso.UiController;
|
||||
import androidx.test.espresso.ViewAction;
|
||||
import androidx.test.espresso.action.MotionEvents;
|
||||
import androidx.test.espresso.util.HumanReadables;
|
||||
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.TypeSafeMatcher;
|
||||
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
public class EspressoUtils {
|
||||
|
||||
public static Matcher<View> childAtPosition(
|
||||
final Matcher<View> parentMatcher, final int position) {
|
||||
|
||||
return new TypeSafeMatcher<>() {
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("Child at position " + position + " in parent ");
|
||||
parentMatcher.describeTo(description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matchesSafely(View view) {
|
||||
ViewParent parent = view.getParent();
|
||||
return parent instanceof ViewGroup && parentMatcher.matches(parent)
|
||||
&& view.equals(((ViewGroup) parent).getChildAt(position));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Matcher<View> withListSize (final int size) {
|
||||
return new TypeSafeMatcher<>() {
|
||||
@Override
|
||||
public boolean matchesSafely(final View view) {
|
||||
return ((ListView) view).getCount() == size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(final Description description) {
|
||||
description.appendText("ListView should have " + size + " items");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static ViewAction waitFor(final long duration_ms) {
|
||||
return new ViewAction() {
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Wait for milliseconds.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Matcher<View> getConstraints() {
|
||||
return isDisplayed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void perform(UiController uiController, final View view) {
|
||||
uiController.loopMainThreadForAtLeast(duration_ms);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static ViewAction veryLongTouch(final int duration_ms) {
|
||||
return new ViewAction() {
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Perform long touch.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Matcher<View> getConstraints() {
|
||||
return isDisplayed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void perform(UiController uiController, final View view) {
|
||||
// Get view absolute position
|
||||
int[] location = new int[2];
|
||||
view.getLocationOnScreen(location);
|
||||
|
||||
// Offset coordinates by view position
|
||||
float[] coordinates = new float[]{location[0] + 1, location[1] + 1};
|
||||
|
||||
// Send down event, pause, and send up
|
||||
MotionEvent down = MotionEvents.sendDown(uiController, coordinates, new float[]{1f, 1f}).down;
|
||||
uiController.loopMainThreadForAtLeast(duration_ms);
|
||||
MotionEvents.sendUp(uiController, down, coordinates);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static ViewAction waitId(final int viewId, final long millis) {
|
||||
return new ViewAction() {
|
||||
@Override
|
||||
public Matcher<View> getConstraints() {
|
||||
return isRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "wait for a specific view with id <" + viewId + "> during " + millis + " millis.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void perform(final UiController uiController, final View view) {
|
||||
uiController.loopMainThreadUntilIdle();
|
||||
final long startTime = System.currentTimeMillis();
|
||||
final long endTime = startTime + millis;
|
||||
final Matcher<View> viewMatcher = withId(viewId);
|
||||
|
||||
do {
|
||||
for (View child : breadthFirstViewTraversal(view)) {
|
||||
// found view with required ID
|
||||
if (viewMatcher.matches(child)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uiController.loopMainThreadForAtLeast(50);
|
||||
}
|
||||
while (System.currentTimeMillis() < endTime);
|
||||
|
||||
// timeout happens
|
||||
throw new PerformException.Builder()
|
||||
.withActionDescription(this.getDescription())
|
||||
.withViewDescription(HumanReadables.describe(view))
|
||||
.withCause(new TimeoutException())
|
||||
.build();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static ViewAction selectTabAtIndex(final int index) {
|
||||
return new ViewAction() {
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Selecting tab.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Matcher<View> getConstraints() {
|
||||
return isDisplayed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void perform(UiController uiController, View view) {
|
||||
TabLayout tabLayout = (TabLayout) view;
|
||||
tabLayout.getTabAt(index).select();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import android.content.Context;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -45,6 +46,7 @@ import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import de.dennisguse.opentracks.LocaleRule;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
|
||||
@@ -56,6 +58,9 @@ import de.dennisguse.opentracks.data.models.Speed;
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class StringUtilsTest {
|
||||
|
||||
@Rule
|
||||
public final LocaleRule mLocaleRule = new LocaleRule(Locale.ENGLISH);
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,11 +23,14 @@ import android.content.Context;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.Locale;
|
||||
|
||||
import de.dennisguse.opentracks.LocaleRule;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
@@ -40,6 +43,9 @@ import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TrackNameUtilsTest {
|
||||
|
||||
@Rule
|
||||
public final LocaleRule mLocaleRule = new LocaleRule(Locale.ENGLISH);
|
||||
|
||||
private static final Track.Id TRACK_ID = new Track.Id(1L);
|
||||
private static final OffsetDateTime START_TIME = OffsetDateTime.parse("2022-01-02T10:15:30.1234+01:00");
|
||||
|
||||
|
||||
@@ -120,10 +120,10 @@ public class StatisticsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
|
||||
return;
|
||||
}
|
||||
this.value.setText(statisticData.hasValue() ? statisticData.getValue() : context.getString(R.string.value_unknown));
|
||||
this.value.setTextAppearance(context, statisticData.getField().isPrimary() ? R.style.StatsPrimaryValue : R.style.StatsSecondaryValue);
|
||||
this.value.setTextAppearance(context, statisticData.getField().isPrimary() ? R.style.TextAppearance_OpenTracks_PrimaryValue : R.style.TextAppearance_OpenTracks_SecondaryValue);
|
||||
this.unit.setText(statisticData.getUnit());
|
||||
this.descMain.setText(statisticData.getField().getTitle());
|
||||
this.descMain.setTextAppearance(context, statisticData.getField().isPrimary() ? R.style.StatsPrimaryLabel : R.style.StatsSecondaryLabel);
|
||||
this.descMain.setTextAppearance(context, statisticData.getField().isPrimary() ? R.style.TextAppearance_OpenTracks_PrimaryHeader : R.style.TextAppearance_OpenTracks_SecondaryHeader);
|
||||
if (statisticData.hasDescription()) {
|
||||
this.descSecondary.setVisibility(View.VISIBLE);
|
||||
this.descSecondary.setText(statisticData.getDescription());
|
||||
@@ -147,8 +147,8 @@ public class StatisticsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
|
||||
if (statisticData == null) {
|
||||
return;
|
||||
}
|
||||
this.value.setTextAppearance(context, statisticData.getField().isPrimary() ? R.style.StatsPrimaryValue : R.style.StatsSecondaryValue);
|
||||
this.descMain.setTextAppearance(context, statisticData.getField().isPrimary() ? R.style.StatsPrimaryLabel : R.style.StatsSecondaryLabel);
|
||||
this.value.setTextAppearance(context, statisticData.getField().isPrimary() ? R.style.TextAppearance_OpenTracks_PrimaryValue : R.style.TextAppearance_OpenTracks_SecondaryValue);
|
||||
this.descMain.setTextAppearance(context, statisticData.getField().isPrimary() ? R.style.TextAppearance_OpenTracks_PrimaryHeader : R.style.TextAppearance_OpenTracks_SecondaryHeader);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ class ChooseActivityTypeImageAdapter extends BaseAdapter {
|
||||
}
|
||||
|
||||
if (position == selected) {
|
||||
imageView.setBackgroundColor(ContextCompat.getColor(parent.getContext(), R.color.colorAccent));
|
||||
imageView.setBackgroundColor(ContextCompat.getColor(parent.getContext(), R.color.opentracks_secondary_color));
|
||||
} else {
|
||||
imageView.setBackgroundColor(Color.TRANSPARENT);
|
||||
}
|
||||
|
||||
+2
@@ -32,8 +32,10 @@ public class ExportServiceResultReceiver extends ResultReceiver {
|
||||
switch (resultCode) {
|
||||
case RESULT_CODE_SUCCESS:
|
||||
receiver.onExportSuccess(trackId);
|
||||
break;
|
||||
case RESULT_CODE_ERROR:
|
||||
receiver.onExportError(trackId);
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Unknown resultCode.");
|
||||
}
|
||||
|
||||
+3
-6
@@ -54,10 +54,7 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
||||
|
||||
protected void onActivityResultCustom(@NonNull Intent resultData) {
|
||||
Uri directoryUri = resultData.getData();
|
||||
int takeFlags = resultData.getFlags();
|
||||
takeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
getContentResolver().takePersistableUriPermission(directoryUri, takeFlags);
|
||||
|
||||
IntentUtils.persistDirectoryAccessPermission(this, directoryUri, resultData.getFlags());
|
||||
startActivity(createNextActivityIntent(directoryUri));
|
||||
}
|
||||
|
||||
@@ -111,11 +108,11 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
||||
Uri oldDirectoryUri = PreferencesUtils.getDefaultExportDirectoryUri();
|
||||
Uri newDirectoryUri = resultData.getData();
|
||||
if (oldDirectoryUri != null && !newDirectoryUri.equals(oldDirectoryUri)) {
|
||||
IntentUtils.releaseDirectoryAccessPermission(getApplicationContext(), oldDirectoryUri);
|
||||
IntentUtils.releaseDirectoryAccessPermission(this, oldDirectoryUri);
|
||||
}
|
||||
|
||||
PreferencesUtils.setDefaultExportDirectoryUri(newDirectoryUri);
|
||||
IntentUtils.persistDirectoryAccessPermission(getApplicationContext(), newDirectoryUri, resultData.getFlags());
|
||||
IntentUtils.persistDirectoryAccessPermission(this, newDirectoryUri, resultData.getFlags());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.speech.tts.TextToSpeech;
|
||||
import android.speech.tts.UtteranceProgressListener;
|
||||
import android.text.Spannable;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -34,6 +35,7 @@ import de.dennisguse.opentracks.data.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.stats.SensorStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.ui.intervals.IntervalStatistics;
|
||||
|
||||
@@ -171,8 +173,12 @@ public class VoiceAnnouncement {
|
||||
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.one(isMetricUnits));
|
||||
intervalStatistics.addTrackPoints(trackPointIterator);
|
||||
IntervalStatistics.Interval lastInterval = intervalStatistics.getLastInterval();
|
||||
SensorStatistics sensorStatistics = null;
|
||||
if (track.getId() != null) {
|
||||
sensorStatistics = contentProviderUtils.getSensorStats(track.getId());
|
||||
}
|
||||
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, track.getTrackStatistics(), isMetricUnits, isReportSpeed, lastInterval);
|
||||
Spannable announcement = VoiceAnnouncementUtils.getAnnouncement(context, track.getTrackStatistics(), isMetricUnits, isReportSpeed, lastInterval, sensorStatistics);
|
||||
|
||||
// We don't care about the utterance id. It is supplied here to force onUtteranceCompleted to be called.
|
||||
tts.speak(announcement, TextToSpeech.QUEUE_FLUSH, null, "not used");
|
||||
|
||||
+125
-36
@@ -1,7 +1,14 @@
|
||||
package de.dennisguse.opentracks.services.announcement;
|
||||
|
||||
import android.content.Context;
|
||||
import static android.text.Spanned.SPAN_INCLUSIVE_EXCLUSIVE;
|
||||
import static de.dennisguse.opentracks.settings.PreferencesUtils.shouldVoiceAnnounceHeartRate;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.TtsSpan;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.time.Duration;
|
||||
@@ -9,6 +16,7 @@ import java.time.Duration;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.stats.SensorStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.ui.intervals.IntervalStatistics;
|
||||
|
||||
@@ -17,68 +25,149 @@ class VoiceAnnouncementUtils {
|
||||
private VoiceAnnouncementUtils() {
|
||||
}
|
||||
|
||||
static String getAnnouncement(Context context, TrackStatistics trackStatistics, boolean isMetricUnits, boolean isReportSpeed, @Nullable IntervalStatistics.Interval currentInterval) {
|
||||
static Spannable getAnnouncement(Context context, TrackStatistics trackStatistics, boolean isMetricUnits, boolean isReportSpeed, @Nullable IntervalStatistics.Interval currentInterval, @Nullable SensorStatistics sensorStatistics) {
|
||||
SpannableStringBuilder builder = new SpannableStringBuilder();
|
||||
Distance distance = trackStatistics.getTotalDistance();
|
||||
Speed distancePerTime = trackStatistics.getAverageMovingSpeed();
|
||||
Speed currentDistancePerTime = currentInterval != null ? currentInterval.getSpeed() : null;
|
||||
int perUnitStringId = isMetricUnits ? R.string.voice_per_kilometer : R.string.voice_per_mile;
|
||||
|
||||
int totalDistanceId = isMetricUnits ? R.plurals.voiceTotalDistanceKilometers : R.plurals.voiceTotalDistanceMiles;
|
||||
double distanceInUnit = distance.toKM_Miles(isMetricUnits);
|
||||
String totalDistance = context.getResources().getQuantityString(totalDistanceId, getQuantityCount(distanceInUnit), distanceInUnit);
|
||||
int distanceId = isMetricUnits ? R.plurals.voiceDistanceKilometers : R.plurals.voiceDistanceMiles;
|
||||
|
||||
builder.append(context.getString(R.string.total_distance));
|
||||
long distanceIntegerPart = (long) distanceInUnit;
|
||||
// Extract the decimal part
|
||||
String distanceFractionalPart = String.format("%.2f", (distanceInUnit - distanceIntegerPart)).substring(2);
|
||||
// Units should always be english singular for TTS.
|
||||
// See https://developer.android.com/reference/android/text/style/TtsSpan?hl=en#TYPE_MEASURE
|
||||
appendDecimalUnit(builder, context.getResources().getQuantityString(distanceId, getQuantityCount(distanceInUnit), distanceInUnit), distanceIntegerPart, distanceFractionalPart, isMetricUnits ? "kilometer" : "mile");
|
||||
// Punctuation helps introduce natural pauses in TTS
|
||||
builder.append(".");
|
||||
if (distance.isZero()) {
|
||||
return totalDistance;
|
||||
return builder;
|
||||
}
|
||||
|
||||
// Announce time
|
||||
Duration movingTime = trackStatistics.getMovingTime();
|
||||
if (!movingTime.isZero()) {
|
||||
appendDuration(context, builder, movingTime);
|
||||
builder.append(".");
|
||||
}
|
||||
|
||||
String rate;
|
||||
String currentRate;
|
||||
String currentRateMsg;
|
||||
if (isReportSpeed) {
|
||||
int speedId = isMetricUnits ? R.plurals.voiceSpeedKilometersPerHour : R.plurals.voiceSpeedMilesPerHour;
|
||||
double speedInUnit = distancePerTime.to(isMetricUnits);
|
||||
rate = context.getResources().getQuantityString(speedId, getQuantityCount(speedInUnit), speedInUnit);
|
||||
|
||||
double currentDistancePerTimeInUnit = currentDistancePerTime != null ? currentDistancePerTime.to(isMetricUnits) : 0;
|
||||
currentRate = context.getResources().getQuantityString(speedId, getQuantityCount(currentDistancePerTimeInUnit), currentDistancePerTimeInUnit);
|
||||
currentRateMsg = context.getString(R.string.voice_speed_lap, currentRate);
|
||||
builder.append(" ")
|
||||
.append(context.getString(R.string.speed));
|
||||
long speedIntegerPart = (long) speedInUnit;
|
||||
// Extract the decimal part
|
||||
String speedFractionalPart = String.format("%.1f", (speedInUnit - speedIntegerPart)).substring(2);
|
||||
appendDecimalUnit(builder, context.getResources().getQuantityString(speedId, getQuantityCount(speedInUnit), speedInUnit), speedIntegerPart, speedFractionalPart, isMetricUnits ? "kilometer per hour" : "mile per hour");
|
||||
builder.append(".");
|
||||
|
||||
if (currentDistancePerTime != null) {
|
||||
double currentDistancePerTimeInUnit = currentDistancePerTime.to(isMetricUnits);
|
||||
|
||||
if (currentDistancePerTimeInUnit > 0) {
|
||||
|
||||
builder.append(" ")
|
||||
.append(context.getString(R.string.lap_speed));
|
||||
long currentDistanceIntegerPart = (long) currentDistancePerTimeInUnit;
|
||||
// Extract the decimal part
|
||||
String currentDistanceFractionalPart = String.format("%.1f", (currentDistancePerTimeInUnit - currentDistanceIntegerPart)).substring(2);
|
||||
appendDecimalUnit(builder, context.getResources().getQuantityString(speedId, getQuantityCount(currentDistancePerTimeInUnit), currentDistancePerTimeInUnit), currentDistanceIntegerPart, currentDistanceFractionalPart, isMetricUnits ? "kilometer per hour" : "mile per hour");
|
||||
builder.append(".");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Duration time = distancePerTime.toPace(isMetricUnits);
|
||||
|
||||
int paceId = isMetricUnits ? R.string.voice_pace_per_kilometer : R.string.voice_pace_per_mile;
|
||||
rate = context.getString(paceId, getAnnounceTime(context, time));
|
||||
builder.append(" ")
|
||||
.append(context.getString(R.string.pace));
|
||||
appendDuration(context, builder, time);
|
||||
builder.append(" ")
|
||||
.append(context.getString(perUnitStringId))
|
||||
.append(".");
|
||||
|
||||
Duration currentTime = currentDistancePerTime != null ? currentDistancePerTime.toPace(isMetricUnits) : Duration.ofMillis(0);
|
||||
currentRate = context.getString(paceId, getAnnounceTime(context, currentTime));
|
||||
currentRateMsg = context.getString(R.string.voice_pace_lap, currentRate);
|
||||
if (!currentTime.isZero()) {
|
||||
builder.append(" ")
|
||||
.append(context.getString(R.string.lap_time));
|
||||
appendDuration(context, builder, currentTime);
|
||||
builder.append(" ")
|
||||
.append(context.getString(perUnitStringId))
|
||||
.append(".");
|
||||
}
|
||||
}
|
||||
|
||||
currentRateMsg = currentInterval == null ? "" : " " + currentRateMsg;
|
||||
if (shouldVoiceAnnounceHeartRate()) {
|
||||
if (sensorStatistics != null && sensorStatistics.hasHeartRate()) {
|
||||
int averageHeartRate = Math.round(sensorStatistics.getAvgHeartRate().getBPM());
|
||||
|
||||
return context.getString(R.string.voice_template, totalDistance, getAnnounceTime(context, trackStatistics.getMovingTime()), rate) + currentRateMsg;
|
||||
}
|
||||
builder.append(" ")
|
||||
.append(context.getString(R.string.average_heart_rate));
|
||||
appendCardinal(builder, context.getString(R.string.sensor_state_heart_rate_value, averageHeartRate), averageHeartRate);
|
||||
builder.append(".");
|
||||
}
|
||||
|
||||
//TODO We might need to localize this using strings.xml if order is relevant.
|
||||
private static String getAnnounceTime(Context context, Duration duration) {
|
||||
String result = "";
|
||||
if (currentInterval != null && currentInterval.hasAverageHeartRate()) {
|
||||
int currentHeartRate = Math.round(currentInterval.getAverageHeartRate().getBPM());
|
||||
|
||||
int hours = (int) (duration.toHours());
|
||||
int minutes = (int) (duration.toMinutes() % 60);
|
||||
int seconds = (int) (duration.getSeconds() % 60);
|
||||
|
||||
if (hours != 0) {
|
||||
String hoursText = context.getResources()
|
||||
.getQuantityString(R.plurals.voiceHours, hours, hours);
|
||||
result += hoursText + " ";
|
||||
builder.append(" ")
|
||||
.append(context.getString(R.string.current_heart_rate));
|
||||
appendCardinal(builder, context.getString(R.string.sensor_state_heart_rate_value, currentHeartRate), currentHeartRate);
|
||||
builder.append(".");
|
||||
}
|
||||
}
|
||||
String minutesText = context.getResources()
|
||||
.getQuantityString(R.plurals.voiceMinutes, minutes, minutes);
|
||||
String secondsText = context.getResources()
|
||||
.getQuantityString(R.plurals.voiceSeconds, seconds, seconds);
|
||||
|
||||
return result + minutesText + " " + secondsText;
|
||||
return builder;
|
||||
}
|
||||
|
||||
static int getQuantityCount(double d) {
|
||||
return (int) d;
|
||||
}
|
||||
|
||||
private static void appendDuration(@NonNull Context context, @NonNull SpannableStringBuilder builder, @NonNull Duration duration) {
|
||||
int hours = (int) (duration.toHours());
|
||||
int minutes = (int) (duration.toMinutes() % 60);
|
||||
int seconds = (int) (duration.getSeconds() % 60);
|
||||
|
||||
if (hours > 0) {
|
||||
appendDecimalUnit(builder, context.getResources().getQuantityString(R.plurals.voiceHours, hours, hours), hours, null, "hour");
|
||||
}
|
||||
if (minutes > 0) {
|
||||
appendDecimalUnit(builder, context.getResources().getQuantityString(R.plurals.voiceMinutes, minutes, minutes), minutes, null, "minute");
|
||||
}
|
||||
if (seconds > 0 || duration.isZero()) {
|
||||
appendDecimalUnit(builder, context.getResources().getQuantityString(R.plurals.voiceSeconds, seconds, seconds), seconds, null, "second");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Speaks as: 98.14 [UNIT] - ninety eight point one four [UNIT with correct plural form]
|
||||
*/
|
||||
private static void appendDecimalUnit(@NonNull SpannableStringBuilder builder, @NonNull String localizedText, long integerPart, @Nullable String fractionalPart, @NonNull String unit) {
|
||||
TtsSpan.MeasureBuilder measureBuilder = new TtsSpan.MeasureBuilder()
|
||||
.setUnit(unit);
|
||||
|
||||
if (fractionalPart == null) {
|
||||
measureBuilder.setNumber(integerPart);
|
||||
} else {
|
||||
measureBuilder.setIntegerPart(integerPart)
|
||||
.setFractionalPart(fractionalPart);
|
||||
}
|
||||
|
||||
builder.append(" ")
|
||||
.append(localizedText, measureBuilder.build(), SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Speaks as: 98 - ninety eight
|
||||
*/
|
||||
private static void appendCardinal(@NonNull SpannableStringBuilder builder, @NonNull String localizedText, long number) {
|
||||
builder.append(" ")
|
||||
.append(localizedText, new TtsSpan.CardinalBuilder().setNumber(number).build(), SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -85,8 +85,15 @@ public class TrackPointCreator implements BluetoothRemoteSensorManager.SensorDat
|
||||
Log.w(TAG, "Not started, should not be called.");
|
||||
return null;
|
||||
}
|
||||
SensorDataSet sensorDataSet = remoteSensorManager.fill(trackPoint);
|
||||
altitudeSumManager.fill(trackPoint);
|
||||
SensorDataSet sensorDataSet = null;
|
||||
BluetoothRemoteSensorManager localRemoteSensorManager = remoteSensorManager;
|
||||
if (localRemoteSensorManager != null) {
|
||||
sensorDataSet = localRemoteSensorManager.fill(trackPoint);
|
||||
}
|
||||
AltitudeSumManager localAltitudeSumManager = altitudeSumManager;
|
||||
if (localAltitudeSumManager != null) {
|
||||
localAltitudeSumManager.fill(trackPoint);
|
||||
}
|
||||
|
||||
return sensorDataSet;
|
||||
}
|
||||
|
||||
@@ -172,6 +172,13 @@ public class PreferencesUtils {
|
||||
setString(keyId, resources.getString(valueId));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void setBoolean(int keyId, Boolean value) {
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putBoolean(getKey(keyId), value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
static void setInt(int keyId, int value) {
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putInt(getKey(keyId), value);
|
||||
@@ -316,6 +323,15 @@ public class PreferencesUtils {
|
||||
return getFloat(R.string.voice_speed_rate_key, DEFAULT);
|
||||
}
|
||||
|
||||
public static boolean shouldVoiceAnnounceHeartRate() {
|
||||
return getBoolean(R.string.voice_announce_heart_rate_key, false);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void setVoiceAnnounceHeartRate(boolean value) {
|
||||
setBoolean(R.string.voice_announce_heart_rate_key, value);
|
||||
}
|
||||
|
||||
public static Distance getRecordingDistanceInterval() {
|
||||
return Distance.of(getInt(R.string.recording_distance_interval_key, getRecordingDistanceIntervalDefaultInternal()));
|
||||
}
|
||||
|
||||
+8
-16
@@ -2,8 +2,6 @@ package de.dennisguse.opentracks.settings;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ScrollView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -17,6 +15,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import de.dennisguse.opentracks.AbstractActivity;
|
||||
import de.dennisguse.opentracks.ui.util.ArrayAdapterFilterDisabled;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.databinding.ActivitySettingsCustomLayoutBinding;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
|
||||
@@ -80,23 +79,16 @@ public class SettingsCustomLayoutEditActivity extends AbstractActivity implement
|
||||
itemTouchHelper.attachToRecyclerView(recyclerViewVisible);
|
||||
|
||||
// Spinner with items per row.
|
||||
ArrayAdapter<Integer> spinnerAdapter = new ArrayAdapter<>(this,
|
||||
ArrayAdapterFilterDisabled<Integer> rowsOptionAdapter = new ArrayAdapterFilterDisabled<>(this,
|
||||
android.R.layout.simple_spinner_dropdown_item,
|
||||
IntStream.of(getResources().getIntArray(R.array.stats_custom_layout_fields_columns_per_row)).boxed().toArray(Integer[]::new));
|
||||
viewBinding.spinnerOptions.setAdapter(spinnerAdapter);
|
||||
viewBinding.spinnerOptions.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
numColumns = position + 1;
|
||||
gridLayoutManager.setSpanCount(numColumns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
|
||||
}
|
||||
viewBinding.rowsOptions.setAdapter(rowsOptionAdapter);
|
||||
viewBinding.rowsOptions.setOnItemClickListener((parent, view, position, id) -> {
|
||||
numColumns = position + 1;
|
||||
gridLayoutManager.setSpanCount(numColumns);
|
||||
});
|
||||
viewBinding.spinnerOptions.setSelection(numColumns - 1);
|
||||
|
||||
viewBinding.rowsOptions.setText(rowsOptionAdapter.getItem(numColumns - 1).toString(), false);
|
||||
|
||||
// Recycler view with not visible stats.
|
||||
layoutFieldsHidden = StatisticsUtils.filterVisible(layout, false);
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.time.Instant;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Altitude;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
@@ -56,6 +57,8 @@ public class TrackStatistics {
|
||||
private Speed maxSpeed;
|
||||
private Float totalAltitudeGain_m = null;
|
||||
private Float totalAltitudeLoss_m = null;
|
||||
// The average heart rate seen on this track
|
||||
private HeartRate avgHeartRate = null;
|
||||
|
||||
public TrackStatistics() {
|
||||
reset();
|
||||
@@ -76,6 +79,7 @@ public class TrackStatistics {
|
||||
altitudeExtremities.set(other.altitudeExtremities.getMin(), other.altitudeExtremities.getMax());
|
||||
totalAltitudeGain_m = other.totalAltitudeGain_m;
|
||||
totalAltitudeLoss_m = other.totalAltitudeLoss_m;
|
||||
avgHeartRate = other.avgHeartRate;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -108,6 +112,19 @@ public class TrackStatistics {
|
||||
stopTime = stopTime.isAfter(other.stopTime) ? stopTime : other.stopTime;
|
||||
}
|
||||
|
||||
if (avgHeartRate == null) {
|
||||
avgHeartRate = other.avgHeartRate;
|
||||
} else {
|
||||
if (other.avgHeartRate != null) {
|
||||
// Using total time as weights for the averaging.
|
||||
// Important to do this before total time is updated
|
||||
avgHeartRate = HeartRate.of(
|
||||
(totalTime.getSeconds() * avgHeartRate.getBPM() + other.totalTime.getSeconds() * other.avgHeartRate.getBPM())
|
||||
/ (totalTime.getSeconds() + other.totalTime.getSeconds())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
totalDistance = totalDistance.plus(other.totalDistance);
|
||||
totalTime = totalTime.plus(other.totalTime);
|
||||
movingTime = movingTime.plus(other.movingTime);
|
||||
@@ -230,6 +247,15 @@ public class TrackStatistics {
|
||||
return totalTime.minus(movingTime);
|
||||
}
|
||||
|
||||
public boolean hasAverageHeartRate() {
|
||||
return avgHeartRate != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public HeartRate getAverageHeartRate() {
|
||||
return avgHeartRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the average speed.
|
||||
* This calculation only takes into account the displacement until the last point that was accounted for in statistics.
|
||||
@@ -287,6 +313,12 @@ public class TrackStatistics {
|
||||
}
|
||||
}
|
||||
|
||||
public void setAverageHeartRate(HeartRate heartRate) {
|
||||
if (heartRate != null) {
|
||||
avgHeartRate = heartRate;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasTotalAltitudeGain() {
|
||||
return totalAltitudeGain_m != null;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Altitude;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
|
||||
@@ -64,6 +65,8 @@ public class TrackStatisticsUpdater {
|
||||
|
||||
private final AltitudeRingBuffer altitudeBuffer;
|
||||
private final SpeedRingBuffer speedBuffer;
|
||||
private float averageHeartRateBPM;
|
||||
private Duration totalHeartRateDuration = Duration.ZERO;
|
||||
|
||||
// The current segment's statistics
|
||||
private final TrackStatistics currentSegment;
|
||||
@@ -85,6 +88,7 @@ public class TrackStatisticsUpdater {
|
||||
|
||||
altitudeBuffer = new AltitudeRingBuffer(ALTITUDE_SMOOTHING_FACTOR);
|
||||
speedBuffer = new SpeedRingBuffer(SPEED_SMOOTHING_FACTOR);
|
||||
resetAverageHeartRate();
|
||||
}
|
||||
|
||||
public TrackStatisticsUpdater(TrackStatisticsUpdater toCopy) {
|
||||
@@ -95,6 +99,7 @@ public class TrackStatisticsUpdater {
|
||||
this.speedBuffer = new SpeedRingBuffer(toCopy.speedBuffer);
|
||||
|
||||
this.lastTrackPoint = toCopy.lastTrackPoint;
|
||||
resetAverageHeartRate();
|
||||
}
|
||||
|
||||
public TrackStatistics getTrackStatistics() {
|
||||
@@ -142,6 +147,17 @@ public class TrackStatisticsUpdater {
|
||||
currentSegment.updateAltitudeExtremities(newAverage);
|
||||
}
|
||||
|
||||
// Update heart rate
|
||||
if (trackPoint.hasHeartRate() && lastTrackPoint != null) {
|
||||
Duration trackPointDuration = Duration.between(lastTrackPoint.getTime(), trackPoint.getTime());
|
||||
Duration newTotalDuration = totalHeartRateDuration.plus(trackPointDuration);
|
||||
|
||||
averageHeartRateBPM = (totalHeartRateDuration.toMillis() * averageHeartRateBPM + trackPointDuration.toMillis() * trackPoint.getHeartRate().getBPM()) / newTotalDuration.toMillis();
|
||||
totalHeartRateDuration = newTotalDuration;
|
||||
|
||||
currentSegment.setAverageHeartRate(HeartRate.of(averageHeartRateBPM));
|
||||
}
|
||||
|
||||
// Update total distance
|
||||
if (trackPoint.hasSensorDistance()) {
|
||||
// Sensor-based distance/speed
|
||||
@@ -184,6 +200,12 @@ public class TrackStatisticsUpdater {
|
||||
lastTrackPoint = null;
|
||||
altitudeBuffer.reset();
|
||||
speedBuffer.reset();
|
||||
resetAverageHeartRate();
|
||||
}
|
||||
|
||||
private void resetAverageHeartRate() {
|
||||
averageHeartRateBPM = 0.0f;
|
||||
totalHeartRateDuration = Duration.ZERO;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+9
-8
@@ -5,11 +5,10 @@ import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.DatePicker;
|
||||
import android.widget.GridLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -17,6 +16,8 @@ import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.button.MaterialButtonToggleGroup;
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
|
||||
import java.time.LocalDate;
|
||||
@@ -61,17 +62,17 @@ public class FilterDialogFragment extends DialogFragment {
|
||||
|
||||
LayoutInflater inflater = LayoutInflater.from(getActivity());
|
||||
View layout = inflater.inflate(R.layout.fragment_filter_dialog, null, false);
|
||||
GridLayout itemsLayout = layout.findViewById(R.id.filter_items);
|
||||
MaterialButtonToggleGroup itemsLayout = layout.findViewById(R.id.filter_items);
|
||||
builder.setView(layout);
|
||||
|
||||
for (FilterItem item : filterItems) {
|
||||
View view = inflater.inflate(R.layout.fragment_filter_dialog_item, null);
|
||||
|
||||
CheckBox checkBox = view.findViewById(R.id.filter_dialog_check_button);
|
||||
checkBox.setText(item.value);
|
||||
checkBox.setChecked(item.isChecked);
|
||||
checkBox.setTag(item.id);
|
||||
checkBox.setOnClickListener(v -> item.isChecked = !item.isChecked);
|
||||
MaterialButton button = view.findViewById(R.id.filter_dialog_check_button);
|
||||
button.setText(item.value);
|
||||
button.setChecked(item.isChecked);
|
||||
button.setTag(item.id);
|
||||
button.setOnClickListener(v -> item.isChecked = !item.isChecked);
|
||||
|
||||
itemsLayout.addView(view);
|
||||
}
|
||||
|
||||
+2
-2
@@ -43,8 +43,8 @@ public class SettingsCustomLayoutEditAdapter extends RecyclerView.Adapter<Recycl
|
||||
viewHolder.title.setText(field.getTitle());
|
||||
viewHolder.value.setText(StatisticsUtils.emptyValue(context, field.getKey()));
|
||||
|
||||
viewHolder.title.setTextAppearance(context, field.isVisible() ? (field.isPrimary() ? R.style.StatsPrimaryLabel : R.style.StatsSecondaryLabel) : R.style.StatsLabelHidden);
|
||||
viewHolder.value.setTextAppearance(context, field.isVisible() ? (field.isPrimary() ? R.style.StatsPrimaryValue : R.style.StatsSecondaryValue) : R.style.StatsValueHidden);
|
||||
viewHolder.title.setTextAppearance(context, field.isVisible() ? (field.isPrimary() ? R.style.TextAppearance_OpenTracks_PrimaryHeader : R.style.TextAppearance_OpenTracks_SecondaryHeader) : R.style.TextAppearance_OpenTracks_HiddenHeader);
|
||||
viewHolder.value.setTextAppearance(context, field.isVisible() ? (field.isPrimary() ? R.style.TextAppearance_OpenTracks_PrimaryValue : R.style.TextAppearance_OpenTracks_SecondaryValue) : R.style.TextAppearance_OpenTracks_HiddenValue);
|
||||
viewHolder.statusIcon.setVisibility(field.isVisible() ? View.GONE : View.VISIBLE);
|
||||
viewHolder.statusIcon.setImageDrawable(AppCompatResources.getDrawable(context, R.drawable.ic_baseline_visibility_off_24));
|
||||
viewHolder.moveIcon.setVisibility(field.isVisible() ? View.VISIBLE : View.GONE);
|
||||
|
||||
@@ -8,6 +8,7 @@ import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.data.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
@@ -102,6 +103,7 @@ public class IntervalStatistics {
|
||||
private Duration time = Duration.ofSeconds(0);
|
||||
private Float gain_m;
|
||||
private Float loss_m;
|
||||
private HeartRate avgHeartRate;
|
||||
|
||||
public Interval() {
|
||||
}
|
||||
@@ -117,6 +119,7 @@ public class IntervalStatistics {
|
||||
time = i.time;
|
||||
gain_m = i.gain_m;
|
||||
loss_m = i.loss_m;
|
||||
avgHeartRate = i.avgHeartRate;
|
||||
}
|
||||
|
||||
public Interval(Interval i) {
|
||||
@@ -124,6 +127,7 @@ public class IntervalStatistics {
|
||||
time = i.time;
|
||||
gain_m = i.gain_m;
|
||||
loss_m = i.loss_m;
|
||||
avgHeartRate = i.avgHeartRate;
|
||||
}
|
||||
|
||||
private void adjust(double adjustFactor) {
|
||||
@@ -155,11 +159,20 @@ public class IntervalStatistics {
|
||||
return loss_m;
|
||||
}
|
||||
|
||||
public boolean hasAverageHeartRate() {
|
||||
return avgHeartRate != null;
|
||||
}
|
||||
|
||||
public HeartRate getAverageHeartRate() {
|
||||
return avgHeartRate;
|
||||
}
|
||||
|
||||
private void add(TrackStatistics trackStatistics, @Nullable TrackPoint lastTrackPoint) {
|
||||
distance = distance.plus(trackStatistics.getTotalDistance());
|
||||
time = time.plus(trackStatistics.getTotalTime());
|
||||
gain_m = trackStatistics.hasTotalAltitudeGain() ? trackStatistics.getTotalAltitudeGain() : gain_m;
|
||||
loss_m = trackStatistics.hasTotalAltitudeLoss() ? trackStatistics.getTotalAltitudeLoss() : loss_m;
|
||||
avgHeartRate = trackStatistics.getAverageHeartRate();
|
||||
if (lastTrackPoint == null) {
|
||||
return;
|
||||
}
|
||||
@@ -176,6 +189,7 @@ public class IntervalStatistics {
|
||||
time = trackStatistics.getTotalTime();
|
||||
gain_m = trackStatistics.hasTotalAltitudeGain() ? trackStatistics.getTotalAltitudeGain() : gain_m;
|
||||
loss_m = trackStatistics.hasTotalAltitudeLoss() ? trackStatistics.getTotalAltitudeLoss() : loss_m;
|
||||
avgHeartRate = trackStatistics.getAverageHeartRate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
@@ -33,6 +32,7 @@ public class IntervalsFragment extends Fragment {
|
||||
|
||||
private static final String FROM_TOP_TO_BOTTOM_KEY = "fromTopToBottom";
|
||||
private static final String TRACK_ID_KEY = "trackId";
|
||||
private static final String SELECTED_INTERVAL_KEY = "selectedIntervalKey";
|
||||
|
||||
private IntervalStatisticsModel viewModel;
|
||||
protected IntervalStatisticsAdapter.StackMode stackModeListView;
|
||||
@@ -41,7 +41,7 @@ public class IntervalsFragment extends Fragment {
|
||||
private Track.Id trackId;
|
||||
private boolean metricUnits;
|
||||
private IntervalStatisticsAdapter adapter;
|
||||
private ArrayAdapter<IntervalStatisticsModel.IntervalOption> spinnerAdapter;
|
||||
private ArrayAdapter<IntervalStatisticsModel.IntervalOption> intervalsAdapter;
|
||||
|
||||
private boolean isReportSpeed;
|
||||
|
||||
@@ -50,8 +50,8 @@ public class IntervalsFragment extends Fragment {
|
||||
protected final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
|
||||
if (PreferencesUtils.isKey(R.string.stats_units_key, key) || PreferencesUtils.isKey(R.string.stats_rate_key, key)) {
|
||||
updateIntervals(PreferencesUtils.isMetricUnits(), selectedInterval);
|
||||
if (spinnerAdapter != null) {
|
||||
spinnerAdapter.notifyDataSetChanged();
|
||||
if (intervalsAdapter != null) {
|
||||
intervalsAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -76,6 +76,15 @@ public class IntervalsFragment extends Fragment {
|
||||
super.onCreate(savedInstanceState);
|
||||
stackModeListView = getArguments().getBoolean(FROM_TOP_TO_BOTTOM_KEY, true) ? IntervalStatisticsAdapter.StackMode.STACK_FROM_TOP : IntervalStatisticsAdapter.StackMode.STACK_FROM_BOTTOM;
|
||||
trackId = getArguments().getParcelable(TRACK_ID_KEY);
|
||||
if (savedInstanceState != null) {
|
||||
selectedInterval = (IntervalStatisticsModel.IntervalOption) savedInstanceState.getSerializable(SELECTED_INTERVAL_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putSerializable(SELECTED_INTERVAL_KEY, selectedInterval);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -93,7 +102,7 @@ public class IntervalsFragment extends Fragment {
|
||||
// TODO handle empty view: before we did viewBinding.intervalList.setEmptyView(viewBinding.intervalListEmptyView);
|
||||
viewBinding.intervalList.setAdapter(adapter);
|
||||
|
||||
spinnerAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, IntervalStatisticsModel.IntervalOption.values()) {
|
||||
intervalsAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, IntervalStatisticsModel.IntervalOption.values()) {
|
||||
@NonNull
|
||||
@Override
|
||||
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
|
||||
@@ -112,17 +121,13 @@ public class IntervalsFragment extends Fragment {
|
||||
}
|
||||
};
|
||||
|
||||
viewBinding.spinnerIntervals.setAdapter(spinnerAdapter);
|
||||
viewBinding.spinnerIntervals.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
updateIntervals(metricUnits, IntervalStatisticsModel.IntervalOption.values()[i]);
|
||||
}
|
||||
viewBinding.intervalsDropdown.setAdapter(intervalsAdapter);
|
||||
viewBinding.intervalsDropdown.setOnItemClickListener((parent, view1, position, id) -> updateIntervals(metricUnits, IntervalStatisticsModel.IntervalOption.values()[position]));
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||
}
|
||||
});
|
||||
viewBinding.intervalsDropdown.setText(
|
||||
getContext().getString(R.string.value_integer_kilometer, Integer.parseInt(selectedInterval != null ? selectedInterval.toString() : IntervalStatisticsModel.IntervalOption.values()[0].toString())),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -173,6 +178,7 @@ public class IntervalsFragment extends Fragment {
|
||||
if (viewModel == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
viewBinding.intervalRate.setText(isReportSpeed ? getString(R.string.stats_speed) : getString(R.string.stats_pace));
|
||||
LiveData<List<IntervalStatistics.Interval>> liveData = viewModel.getIntervalStats(trackId, metricUnits, selectedInterval);
|
||||
liveData.observe(getActivity(), intervalList -> adapter.swapData(intervalList, metricUnits, isReportSpeed));
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package de.dennisguse.opentracks.ui.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Filter;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
// https://github.com/material-components/material-components-android/issues/1464
|
||||
public class ArrayAdapterFilterDisabled<T> extends ArrayAdapter<T> {
|
||||
|
||||
public ArrayAdapterFilterDisabled(@NonNull Context context, int resource, @NonNull T[] objects) {
|
||||
super(context, resource, objects);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Filter getFilter() {
|
||||
return new NeverFilter();
|
||||
}
|
||||
|
||||
private class NeverFilter extends Filter {
|
||||
protected FilterResults performFiltering(CharSequence prefix) {
|
||||
return new FilterResults();
|
||||
}
|
||||
|
||||
protected void publishResults(CharSequence constraint, FilterResults results) {
|
||||
if (ArrayAdapterFilterDisabled.this.getCount() > 0) {
|
||||
notifyDataSetChanged();
|
||||
} else {
|
||||
notifyDataSetInvalidated();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package de.dennisguse.opentracks.ui.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.material.textfield.MaterialAutoCompleteTextView;
|
||||
|
||||
// https://github.com/material-components/material-components-android/issues/1464
|
||||
public class AutoCompleteTextViewForDropdown extends MaterialAutoCompleteTextView {
|
||||
|
||||
public AutoCompleteTextViewForDropdown(@NonNull final Context context, @Nullable final AttributeSet attributeSet) {
|
||||
super(context, attributeSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getFreezesText() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -17,11 +17,9 @@
|
||||
package de.dennisguse.opentracks.ui.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
@@ -77,11 +75,9 @@ public class ListItemUtils {
|
||||
TextView timeDistanceTextView = view.findViewById(R.id.list_item_time_distance);
|
||||
String timeDistanceText;
|
||||
if (isRecording) {
|
||||
timeDistanceTextView.setTextColor(context.getResources().getColor(isPaused ? android.R.color.white : R.color.recording_text));
|
||||
timeDistanceText = context.getString(isPaused ? R.string.generic_paused : R.string.generic_recording);
|
||||
} else {
|
||||
// Match list_item_time_distance in list_item.xml
|
||||
timeDistanceTextView.setTextAppearance(context, R.style.TextSmall);
|
||||
timeDistanceText = getTimeDistance(totalTime, totalDistance);
|
||||
if (markerCount > 0) {
|
||||
timeDistanceText += " \u2027";
|
||||
@@ -111,7 +107,7 @@ public class ListItemUtils {
|
||||
String timeValue = null;
|
||||
if (!isRecording) {
|
||||
dateValue = StringUtils.formatDateTodayRelative(context, offsetDateTime);
|
||||
timeValue = offsetDateTime.format(DateTimeFormatter.ofPattern("hh:mm"));
|
||||
timeValue = offsetDateTime.format(DateTimeFormatter.ofPattern("HH:mm"));
|
||||
}
|
||||
setTextView(context, dateTextView, dateValue, hasPhoto);
|
||||
setTextView(context, timeTextView, timeValue, hasPhoto);
|
||||
@@ -132,10 +128,6 @@ public class ListItemUtils {
|
||||
timeDistanceTextView.setSingleLine(true);
|
||||
setTextView(context, categoryDescriptionTextView, categoryDescription, hasPhoto);
|
||||
}
|
||||
|
||||
// Adjust iconImageView layout gravity
|
||||
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) iconImageView.getLayoutParams();
|
||||
params.gravity = timeDistanceTextView.getVisibility() == View.GONE && markerCountIcon.getVisibility() == View.GONE ? Gravity.TOP : Gravity.CENTER_VERTICAL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,14 +42,14 @@ public class ThemeUtils {
|
||||
}
|
||||
|
||||
public static int getFontSizeSmallInPx(Context context) {
|
||||
TypedArray typedArray = context.obtainStyledAttributes(R.style.TextSmall, new int[]{android.R.attr.textSize});
|
||||
TypedArray typedArray = context.obtainStyledAttributes(R.style.TextAppearance_MaterialComponents_Body2, new int[]{android.R.attr.textSize});
|
||||
int fontSize = typedArray.getDimensionPixelSize(0, 12);
|
||||
typedArray.recycle();
|
||||
return fontSize;
|
||||
}
|
||||
|
||||
public static int getFontSizeMediumInPx(Context context) {
|
||||
TypedArray typedArray = context.obtainStyledAttributes(R.style.TextMedium, new int[]{android.R.attr.textSize});
|
||||
TypedArray typedArray = context.obtainStyledAttributes(R.style.TextAppearance_MaterialComponents_Body1, new int[]{android.R.attr.textSize});
|
||||
int fontSize = typedArray.getDimensionPixelSize(0, 15);
|
||||
typedArray.recycle();
|
||||
return fontSize;
|
||||
|
||||
@@ -77,7 +77,7 @@ public class IntentUtils {
|
||||
|
||||
public static void persistDirectoryAccessPermission(Context context, Uri directoryUri, int existingFlags) {
|
||||
int newFlags = existingFlags & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
context.getContentResolver().takePersistableUriPermission(directoryUri, newFlags);
|
||||
context.getApplicationContext().getContentResolver().takePersistableUriPermission(directoryUri, newFlags);
|
||||
}
|
||||
|
||||
public static void releaseDirectoryAccessPermission(Context context, final Uri documentUri) {
|
||||
@@ -85,7 +85,7 @@ public class IntentUtils {
|
||||
return;
|
||||
}
|
||||
|
||||
context.getContentResolver().getPersistedUriPermissions().stream()
|
||||
context.getApplicationContext().getContentResolver().getPersistedUriPermissions().stream()
|
||||
.map(UriPermission::getUri)
|
||||
.filter(documentUri::equals)
|
||||
.forEach(u -> context.getContentResolver().releasePersistableUriPermission(u, 0));
|
||||
@@ -96,7 +96,7 @@ public class IntentUtils {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return DocumentFile.fromTreeUri(context, directoryUri);
|
||||
return DocumentFile.fromTreeUri(context.getApplicationContext(), directoryUri);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Could not decode directory: " + e.getMessage());
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@color/colorAccentTransparent" android:state_activated="true" />
|
||||
<item android:drawable="@color/opentracks_secondary_transparent" android:state_activated="true" />
|
||||
<item android:drawable="@android:color/transparent" />
|
||||
</selector>
|
||||
@@ -38,17 +38,17 @@ limitations under the License.
|
||||
|
||||
<TextView
|
||||
android:id="@+id/controller_total_time"
|
||||
style="@style/TextSmall"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle2"
|
||||
android:layout_centerInParent="true"
|
||||
android:textColor="@android:color/white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/controller_status"
|
||||
style="@style/TextMicro"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle2"
|
||||
android:layout_above="@id/controller_total_time"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:singleLine="true" />
|
||||
|
||||
@@ -89,7 +89,7 @@ limitations under the License.
|
||||
android:layout_weight="24">
|
||||
|
||||
<TextView
|
||||
style="@style/TextLarge"
|
||||
style="?attr/textAppearanceHeadline6"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/track_list_empty_message" />
|
||||
|
||||
@@ -32,54 +32,55 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/about_text_description"
|
||||
style="@style/TextMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:textAppearance="?attr/textAppearanceSubtitle1"
|
||||
android:gravity="center"
|
||||
android:padding="12dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<View style="@style/StatsHorizontalLine" />
|
||||
<View style="@style/HorizontalLine" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/about_text_version_name"
|
||||
style="@style/TextSmall"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?attr/textAppearanceSubtitle1"
|
||||
android:gravity="center"
|
||||
android:padding="6dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/about_text_version_code"
|
||||
style="@style/TextSmall"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?attr/textAppearanceBody1"
|
||||
android:gravity="center"
|
||||
android:padding="6dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/about_app_url"
|
||||
style="@style/TextSmall"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?attr/textAppearanceBody1"
|
||||
android:autoLink="web"
|
||||
android:gravity="center"
|
||||
android:padding="6dp" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextSmall"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?attr/textAppearanceBody1"
|
||||
android:gravity="center"
|
||||
android:linksClickable="true"
|
||||
android:padding="6dp"
|
||||
android:text="@string/about_history" />
|
||||
|
||||
<View style="@style/StatsHorizontalLine" />
|
||||
<View style="@style/HorizontalLine" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?attr/textAppearanceBody1"
|
||||
android:gravity="center"
|
||||
android:padding="12dp"
|
||||
android:text="@string/about_third_party_data" />
|
||||
@@ -87,15 +88,17 @@
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?attr/textAppearanceBody1"
|
||||
android:gravity="center"
|
||||
android:padding="12dp"
|
||||
android:text="@string/third_party_data" />
|
||||
|
||||
<View style="@style/StatsHorizontalLine" />
|
||||
<View style="@style/HorizontalLine" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?attr/textAppearanceSubtitle1"
|
||||
android:gravity="center"
|
||||
android:padding="12dp"
|
||||
android:text="@string/about_libraries" />
|
||||
@@ -103,6 +106,7 @@
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?attr/textAppearanceBody1"
|
||||
android:gravity="center"
|
||||
android:linksClickable="true"
|
||||
android:text="@string/third_party_libraries" />
|
||||
|
||||
@@ -7,27 +7,20 @@
|
||||
|
||||
<include layout="@layout/toolbar" />
|
||||
|
||||
<LinearLayout
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/rows_options_layout"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.ExposedDropdownMenu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:orientation="horizontal">
|
||||
android:hint="@string/settings_recording_customize_layout_select_columns">
|
||||
|
||||
<TextView
|
||||
style="@style/TextMediumPrimary"
|
||||
android:layout_width="wrap_content"
|
||||
<de.dennisguse.opentracks.ui.util.AutoCompleteTextViewForDropdown
|
||||
android:id="@+id/rows_options"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@string/settings_recording_customize_layout_select_columns" />
|
||||
android:inputType="none" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner_options"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center" />
|
||||
|
||||
</LinearLayout>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/scroll_view"
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
android:id="@+id/custom_layout_list_input_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||
app:errorEnabled="true"
|
||||
android:layout_margin="16dp">
|
||||
|
||||
@@ -39,17 +40,22 @@
|
||||
android:id="@+id/custom_layout_list_bottom_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_margin="16dp"
|
||||
android:gravity="end"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/custom_layout_list_cancel_button"
|
||||
style="@style/BottomButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:text="@android:string/cancel" />
|
||||
|
||||
<Button
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/custom_layout_list_ok_button"
|
||||
style="@style/BottomButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:enabled="false"
|
||||
android:text="@android:string/ok" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/aggregated_stats_empty_view"
|
||||
style="@style/TextLarge"
|
||||
style="?attr/textAppearanceHeadline4"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp"
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingBottom="24dp">
|
||||
|
||||
<!-- Guidelines -->
|
||||
@@ -33,8 +36,7 @@
|
||||
android:id="@+id/aggregated_stats_sport_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintEnd_toStartOf="@id/aggregated_stats_type_label"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline1"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:tint="?android:textColorPrimary"
|
||||
@@ -42,10 +44,11 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/aggregated_stats_type_label"
|
||||
style="@style/LargeLabel"
|
||||
android:layout_marginStart="16dp"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="0dp"
|
||||
android:layout_marginBottom="0dp"
|
||||
android:layout_marginStart="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/aggregated_stats_sport_icon"
|
||||
app:layout_constraintStart_toEndOf="@+id/aggregated_stats_sport_icon"
|
||||
app:layout_constraintTop_toTopOf="@+id/aggregated_stats_sport_icon"
|
||||
@@ -53,10 +56,11 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/aggregated_stats_num_tracks"
|
||||
style="@style/LargeLabel"
|
||||
android:layout_marginStart="4dp"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="0dp"
|
||||
android:layout_marginBottom="0dp"
|
||||
android:layout_marginStart="4dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/aggregated_stats_type_label"
|
||||
app:layout_constraintStart_toEndOf="@+id/aggregated_stats_type_label"
|
||||
app:layout_constraintTop_toTopOf="@+id/aggregated_stats_type_label"
|
||||
@@ -65,8 +69,9 @@
|
||||
<!-- Distance -->
|
||||
<TextView
|
||||
android:id="@+id/aggregated_stats_distance"
|
||||
style="@style/StatsPrimaryValue"
|
||||
android:layout_marginTop="16dp"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toStartOf="@id/aggregated_stats_distance_unit"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline1"
|
||||
@@ -76,8 +81,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/aggregated_stats_distance_unit"
|
||||
style="@style/StatsUnit"
|
||||
android:singleLine="true"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryUnit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/aggregated_stats_distance"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline2"
|
||||
app:layout_constraintStart_toEndOf="@+id/aggregated_stats_distance"
|
||||
@@ -86,9 +92,9 @@
|
||||
<!-- Time -->
|
||||
<TextView
|
||||
android:id="@+id/aggregated_stats_time"
|
||||
style="@style/StatsPrimaryValue"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline3"
|
||||
app:layout_constraintStart_toStartOf="@+id/guideline2"
|
||||
app:layout_constraintTop_toBottomOf="@+id/aggregated_stats_sport_icon"
|
||||
@@ -97,8 +103,9 @@
|
||||
<!-- Avg. rate -->
|
||||
<TextView
|
||||
android:id="@+id/aggregated_stats_avg_rate"
|
||||
style="@style/StatsSmallValue"
|
||||
android:layout_marginTop="24dp"
|
||||
style="@style/TextAppearance.OpenTracks.SecondaryValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toStartOf="@+id/aggregated_stats_avg_rate_unit"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline1"
|
||||
@@ -107,7 +114,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/aggregated_stats_avg_rate_unit"
|
||||
style="@style/StatsUnit"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryUnit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/aggregated_stats_avg_rate"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline2"
|
||||
app:layout_constraintStart_toEndOf="@+id/aggregated_stats_avg_rate"
|
||||
@@ -115,8 +124,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/aggregated_stats_avg_rate_label"
|
||||
style="@style/StatsTinyLabel"
|
||||
android:singleLine="true"
|
||||
style="@style/TextAppearance.OpenTracks.SecondaryHeader"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline2"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline1"
|
||||
@@ -126,8 +136,9 @@
|
||||
<!-- Max. rate -->
|
||||
<TextView
|
||||
android:id="@+id/aggregated_stats_max_rate"
|
||||
style="@style/StatsSmallValue"
|
||||
android:layout_marginTop="24dp"
|
||||
style="@style/TextAppearance.OpenTracks.SecondaryValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toStartOf="@+id/aggregated_stats_max_rate_unit"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
@@ -136,7 +147,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/aggregated_stats_max_rate_unit"
|
||||
style="@style/StatsUnit"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryUnit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/aggregated_stats_max_rate"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintStart_toEndOf="@+id/aggregated_stats_max_rate"
|
||||
@@ -144,8 +157,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/aggregated_stats_max_rate_label"
|
||||
style="@style/StatsTinyLabel"
|
||||
android:singleLine="true"
|
||||
style="@style/TextAppearance.OpenTracks.SecondaryHeader"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
|
||||
@@ -39,9 +39,9 @@ limitations under the License.
|
||||
|
||||
<TextView
|
||||
android:id="@+id/controller_total_time"
|
||||
style="@style/TextSmall"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle2"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:gravity="center"
|
||||
@@ -50,9 +50,9 @@ limitations under the License.
|
||||
|
||||
<TextView
|
||||
android:id="@+id/controller_status"
|
||||
style="@style/TextMicro"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle2"
|
||||
android:layout_above="@id/controller_total_time"
|
||||
android:layout_alignParentStart="true"
|
||||
android:gravity="center"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
style="@style/StatsCardView">
|
||||
style="@style/OpenTracksCardView">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -16,7 +16,7 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="Profile/Custom Layout Title"
|
||||
style="@style/LargeLabel"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
||||
android:layout_margin="8dp"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
style="@style/StatsCardView">
|
||||
style="@style/OpenTracksCardView">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -28,9 +28,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/export_progress_done"
|
||||
style="@style/TextMediumPrimary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle1"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="48dp"
|
||||
app:layout_constraintEnd_toStartOf="@id/export_progress_slash"
|
||||
@@ -41,9 +41,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/export_progress_slash"
|
||||
style="@style/TextMediumPrimary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle1"
|
||||
android:text="@string/generic_separator_done_total"
|
||||
app:layout_constraintBottom_toBottomOf="@id/export_progress_done"
|
||||
app:layout_constraintEnd_toStartOf="@id/export_progress_total"
|
||||
@@ -52,9 +52,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/export_progress_total"
|
||||
style="@style/TextMediumPrimary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle1"
|
||||
android:layout_marginEnd="24dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/export_progress_slash"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@@ -62,9 +62,8 @@
|
||||
app:layout_constraintTop_toTopOf="@id/export_progress_slash"
|
||||
tools:text="10" />
|
||||
|
||||
<ProgressBar
|
||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
android:id="@+id/export_progress_bar"
|
||||
style="@style/ProgressBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
@@ -79,7 +78,11 @@
|
||||
<!-- Tracks exported (new tracks) -->
|
||||
<TextView
|
||||
android:id="@+id/export_progress_summary_new_msg"
|
||||
style="@style/TextImportExportSummaryStart"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle1"
|
||||
android:gravity="end"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/export_progress_summary_new_msg"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_vertical_half"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -87,7 +90,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/export_progress_summary_new"
|
||||
style="@style/TextImportExportSummaryEnd"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle1"
|
||||
app:layout_constraintBottom_toBottomOf="@id/export_progress_summary_new_msg"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline_vertical_half"
|
||||
@@ -112,7 +117,11 @@
|
||||
<!-- Tracks overwritten -->
|
||||
<TextView
|
||||
android:id="@+id/export_progress_summary_overwrite_msg"
|
||||
style="@style/TextImportExportSummaryStart"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle1"
|
||||
android:gravity="end"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/export_progress_summary_overwrite_msg"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_vertical_half"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -120,7 +129,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/export_progress_summary_overwrite"
|
||||
style="@style/TextImportExportSummaryEnd"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle1"
|
||||
app:layout_constraintBottom_toBottomOf="@id/export_progress_summary_overwrite_msg"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline_vertical_half"
|
||||
@@ -145,7 +156,11 @@
|
||||
<!-- Tracks skipped -->
|
||||
<TextView
|
||||
android:id="@+id/export_progress_summary_skip_msg"
|
||||
style="@style/TextImportExportSummaryStart"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle1"
|
||||
android:gravity="end"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/export_progress_summary_skip_msg"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_vertical_half"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -153,7 +168,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/export_progress_summary_skip"
|
||||
style="@style/TextImportExportSummaryEnd"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle1"
|
||||
app:layout_constraintBottom_toBottomOf="@id/export_progress_summary_skip_msg"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline_vertical_half"
|
||||
@@ -178,7 +195,11 @@
|
||||
<!-- Tracks errors -->
|
||||
<TextView
|
||||
android:id="@+id/export_progress_summary_errors_msg"
|
||||
style="@style/TextImportExportSummaryStart"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle1"
|
||||
android:gravity="end"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/export_progress_summary_errors_msg"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_vertical_half"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -186,7 +207,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/export_progress_summary_errors"
|
||||
style="@style/TextImportExportSummaryEnd"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle1"
|
||||
app:layout_constraintBottom_toBottomOf="@id/export_progress_summary_errors_msg"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline_vertical_half"
|
||||
@@ -224,9 +247,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/export_progress_alert_msg"
|
||||
style="@style/TextMediumPrimary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle1"
|
||||
android:layout_marginTop="48dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
@@ -276,6 +299,7 @@
|
||||
android:text="@string/generic_overwrite"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,106 +1,106 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical">
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<ScrollView style="@style/ScrollView.WithBottomButtons">
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButtonToggleGroup
|
||||
android:id="@+id/filter_items"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp">
|
||||
android:padding="8dp"
|
||||
android:orientation="vertical"
|
||||
app:singleSelection="false"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/filter_date_from" />
|
||||
|
||||
<GridLayout
|
||||
android:id="@+id/filter_items"
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/filter_date_from"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/generic_from"
|
||||
android:imeOptions="actionNext"
|
||||
android:importantForAutofill="no"
|
||||
android:padding="8dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/filter_items"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/filter_date_picker_from">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/filter_date_edit_text_from"
|
||||
android:drawableStart="@drawable/ic_baseline_date_range_24"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:columnCount="2"
|
||||
android:orientation="horizontal"
|
||||
android:padding="8dp"
|
||||
android:alignmentMode="alignBounds"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/filter_date_from" />
|
||||
android:enabled="true"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:inputType="date"
|
||||
android:selectAllOnFocus="true" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/filter_date_from"
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<DatePicker
|
||||
android:id="@+id/filter_date_picker_from"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingRight="8dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/filter_date_from"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/filter_date_to" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/filter_date_to"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/generic_to"
|
||||
android:imeOptions="actionNext"
|
||||
android:importantForAutofill="no"
|
||||
android:padding="8dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/filter_date_picker_from"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/filter_date_picker_to">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/filter_date_edit_text_to"
|
||||
android:drawableStart="@drawable/ic_baseline_date_range_24"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/generic_from"
|
||||
android:imeOptions="actionNext"
|
||||
android:importantForAutofill="no"
|
||||
android:padding="8dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/filter_items"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/filter_date_picker_from">
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/filter_date_edit_text_from"
|
||||
android:drawableStart="@drawable/ic_baseline_date_range_24"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:enabled="true"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:inputType="date"
|
||||
android:selectAllOnFocus="true" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
android:enabled="true"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:inputType="date"
|
||||
android:selectAllOnFocus="true" />
|
||||
|
||||
<DatePicker
|
||||
android:id="@+id/filter_date_picker_from"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingRight="8dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/filter_date_from"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/filter_date_to" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/filter_date_to"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/generic_to"
|
||||
android:imeOptions="actionNext"
|
||||
android:importantForAutofill="no"
|
||||
android:padding="8dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/filter_date_picker_from"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/filter_date_picker_to">
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/filter_date_edit_text_to"
|
||||
android:drawableStart="@drawable/ic_baseline_date_range_24"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:enabled="true"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:inputType="date"
|
||||
android:selectAllOnFocus="true" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
<DatePicker
|
||||
android:id="@+id/filter_date_picker_to"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingRight="8dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/filter_date_to"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<DatePicker
|
||||
android:id="@+id/filter_date_picker_to"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingRight="8dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/filter_date_to"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
@@ -1,9 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<com.google.android.material.button.MaterialButton xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/filter_dialog_check_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:checkable="true"
|
||||
android:gravity="center"
|
||||
tools:text="Check Button" />
|
||||
@@ -17,129 +17,120 @@
|
||||
android:padding="20dp">
|
||||
|
||||
<TextView
|
||||
style="@style/TextMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/help_feature_title"
|
||||
android:textStyle="bold" />
|
||||
style="?attr/textAppearanceHeadline6"
|
||||
android:text="@string/help_feature_title" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceBody1"
|
||||
android:text="@string/help_feature_content" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceHeadline6"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/help_track_map_title"
|
||||
android:textStyle="bold" />
|
||||
android:text="@string/help_track_map_title" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceBody1"
|
||||
android:linksClickable="true"
|
||||
android:text="@string/help_track_map_content" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceHeadline6"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/help_recording_title"
|
||||
android:textStyle="bold" />
|
||||
android:text="@string/help_recording_title" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceBody1"
|
||||
android:text="@string/help_recording_content" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceHeadline6"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/help_storage_pictures_title"
|
||||
android:textStyle="bold" />
|
||||
android:text="@string/help_storage_pictures_title" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceBody1"
|
||||
android:text="@string/help_storage_pictures_content" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceHeadline6"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/help_track_export_title"
|
||||
android:textStyle="bold" />
|
||||
android:text="@string/help_track_export_title" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceBody1"
|
||||
android:text="@string/help_track_export_content" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/help_data_google_play_services_title"
|
||||
android:textStyle="bold" />
|
||||
style="?attr/textAppearanceHeadline6"
|
||||
android:text="@string/help_data_google_play_services_title" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceBody1"
|
||||
android:text="@android:string/no" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceHeadline6"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/help_data_storage_title"
|
||||
android:textStyle="bold" />
|
||||
android:text="@string/help_data_storage_title" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceBody1"
|
||||
android:text="@string/help_data_storage_content" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceHeadline6"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/help_gps_troubleshooting_title"
|
||||
android:textStyle="bold" />
|
||||
android:text="@string/help_gps_troubleshooting_title" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceBody1"
|
||||
android:linksClickable="true"
|
||||
android:text="@string/help_gps_troubleshooting_content" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceHeadline6"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/help_support_title"
|
||||
android:textStyle="bold" />
|
||||
android:text="@string/help_support_title" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceBody1"
|
||||
android:linksClickable="true"
|
||||
android:text="@string/help_support_content" />
|
||||
</LinearLayout>
|
||||
|
||||
@@ -28,9 +28,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/import_progress_done"
|
||||
style="@style/TextMediumPrimary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle1"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="48dp"
|
||||
app:layout_constraintEnd_toStartOf="@id/import_progress_slash"
|
||||
@@ -41,9 +41,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/import_progress_slash"
|
||||
style="@style/TextMediumPrimary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle1"
|
||||
android:text="@string/generic_separator_done_total"
|
||||
app:layout_constraintBottom_toBottomOf="@id/import_progress_done"
|
||||
app:layout_constraintEnd_toStartOf="@id/import_progress_total"
|
||||
@@ -52,9 +52,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/import_progress_total"
|
||||
style="@style/TextMediumPrimary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle1"
|
||||
android:layout_marginEnd="24dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/import_progress_slash"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@@ -62,9 +62,8 @@
|
||||
app:layout_constraintTop_toTopOf="@id/import_progress_slash"
|
||||
tools:text="10" />
|
||||
|
||||
<ProgressBar
|
||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
android:id="@+id/import_progress_bar"
|
||||
style="@style/ProgressBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
@@ -79,7 +78,11 @@
|
||||
<!-- Files imported correctly -->
|
||||
<TextView
|
||||
android:id="@+id/import_progress_summary_ok_msg"
|
||||
style="@style/TextImportExportSummaryStart"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle1"
|
||||
android:gravity="end"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/import_progress_summary_ok_msg"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_vertical_half"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -87,7 +90,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/import_progress_summary_ok"
|
||||
style="@style/TextImportExportSummaryEnd"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle1"
|
||||
app:layout_constraintBottom_toBottomOf="@id/import_progress_summary_ok_msg"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline_vertical_half"
|
||||
@@ -112,7 +117,11 @@
|
||||
<!-- Already exists files -->
|
||||
<TextView
|
||||
android:id="@+id/import_progress_summary_exists_msg"
|
||||
style="@style/TextImportExportSummaryStart"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle1"
|
||||
android:gravity="end"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/import_progress_summary_exists_msg"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_vertical_half"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -120,7 +129,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/import_progress_summary_exists"
|
||||
style="@style/TextImportExportSummaryEnd"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle1"
|
||||
app:layout_constraintBottom_toBottomOf="@id/import_progress_summary_exists_msg"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline_vertical_half"
|
||||
@@ -145,7 +156,11 @@
|
||||
<!-- Importing error files -->
|
||||
<TextView
|
||||
android:id="@+id/import_progress_summary_errors_msg"
|
||||
style="@style/TextImportExportSummaryStart"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle1"
|
||||
android:gravity="end"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/import_progress_summary_errors_msg"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_vertical_half"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -153,7 +168,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/import_progress_summary_errors"
|
||||
style="@style/TextImportExportSummaryEnd"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle1"
|
||||
app:layout_constraintBottom_toBottomOf="@id/import_progress_summary_errors_msg"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline_vertical_half"
|
||||
@@ -191,9 +208,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/import_progress_alert_msg"
|
||||
style="@style/TextMediumPrimary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceSubtitle1"
|
||||
android:layout_marginTop="48dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
|
||||
@@ -3,49 +3,52 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="8dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/intervals_input_layout"
|
||||
style="?attr/textInputOutlinedExposedDropdownMenuStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:orientation="horizontal">
|
||||
android:layout_marginBottom="8dp"
|
||||
android:hint="@string/stats_split_interval">
|
||||
|
||||
<TextView
|
||||
style="@style/TextMediumPrimary"
|
||||
android:layout_width="wrap_content"
|
||||
<de.dennisguse.opentracks.ui.util.AutoCompleteTextViewForDropdown
|
||||
android:id="@+id/intervals_dropdown"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/stats_split_interval" />
|
||||
android:inputType="none" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner_intervals"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center" />
|
||||
|
||||
</LinearLayout>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/interval_distance"
|
||||
style="@style/IntervalLabel"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
||||
android:text="@string/stats_distance" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/interval_rate"
|
||||
style="@style/IntervalLabel"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
||||
tools:text="Speed" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/interval_altitude"
|
||||
style="@style/IntervalLabel"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
||||
android:text="@string/stats_altitude" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -65,9 +68,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/interval_list_empty_view"
|
||||
style="@style/TextMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
style="?attr/textAppearanceHeadline4"
|
||||
android:gravity="center_horizontal|center_vertical"
|
||||
android:text="@string/interval_list_empty_message" />
|
||||
|
||||
|
||||
@@ -10,18 +10,20 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/interval_item_distance"
|
||||
style="@style/TextMediumPrimary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="center_vertical"
|
||||
style="?attr/textAppearanceBody1"
|
||||
tools:text="1 km" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/interval_item_rate"
|
||||
style="@style/TextMediumPrimary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="center_vertical"
|
||||
style="?attr/textAppearanceBody1"
|
||||
tools:text="15 km/h" />
|
||||
|
||||
<LinearLayout
|
||||
@@ -32,17 +34,19 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/interval_item_gain"
|
||||
style="@style/TextSmall"
|
||||
android:layout_width="match_parent"
|
||||
app:drawableStartCompat="@drawable/ic_arrow_drop_up_24"
|
||||
tools:text="100 m" />
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceBody2"
|
||||
tools:text="100 m"
|
||||
app:drawableStartCompat="@drawable/ic_arrow_drop_up_24" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/interval_item_loss"
|
||||
style="@style/TextSmall"
|
||||
android:layout_width="match_parent"
|
||||
app:drawableStartCompat="@drawable/ic_arrow_drop_down_24"
|
||||
tools:text="100 m" />
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceBody2"
|
||||
tools:text="100 m"
|
||||
app:drawableStartCompat="@drawable/ic_arrow_drop_down_24" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
+132
-138
@@ -1,152 +1,146 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright 2012 Google Inc.
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:background="@drawable/activated_background"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="12dp">
|
||||
|
||||
<!-- Photo -->
|
||||
<ImageView
|
||||
android:id="@+id/list_item_photo"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:contentDescription="@string/image_photo_marker"
|
||||
android:scaleType="centerCrop" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<!-- A gradient on top of the text -->
|
||||
<ImageView
|
||||
android:id="@+id/list_item_text_gradient"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:alpha=".6"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:contentDescription="@string/image_photo_marker"
|
||||
android:src="@drawable/text_gradient" />
|
||||
<!-- Table 1 -->
|
||||
<TableLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:shrinkColumns="1"
|
||||
android:stretchColumns="1">
|
||||
<!-- Table 1: Row 1 -->
|
||||
<TableRow>
|
||||
<!-- Table 1: Row 1: Column 1: (icon) -->
|
||||
<ImageView
|
||||
android:id="@+id/list_item_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:contentDescription="@string/image_track"
|
||||
app:tint="?android:textColorPrimary" />
|
||||
<!-- Table 1: Row 1: Column 2: (Table 2) -->
|
||||
<TableLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:shrinkColumns="0"
|
||||
android:stretchColumns="0">
|
||||
|
||||
<TableRow>
|
||||
<!-- Table 2: Row 1: Column 1: (name) -->
|
||||
<TextView
|
||||
android:id="@+id/list_item_name"
|
||||
style="@style/TextMediumPrimary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_gravity="bottom"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
tools:text="Main text" />
|
||||
<!-- Table 2: Row 1: Column 2: (date) -->
|
||||
<TextView
|
||||
android:id="@+id/list_item_date"
|
||||
style="@style/ListItemDate"
|
||||
tools:text="2020-09-09" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<!-- Need to match the style set in ListItemUtils.setListItem -->
|
||||
<!-- Table 2: Row 2: Column 1: (shared owner, total time, total distance, and marker count) -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/list_item_time_distance"
|
||||
style="@style/TextSmall"
|
||||
android:layout_gravity="bottom"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
tools:text="50:43 (50.00km)" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/list_item_marker_count_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginStart="2dp"
|
||||
android:layout_marginEnd="2dp"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:contentDescription="@string/image_marker_count"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_marker_show_24dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/list_item_marker_count"
|
||||
style="@style/TextSmall"
|
||||
android:layout_gravity="bottom"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
tools:text="2" />
|
||||
</LinearLayout>
|
||||
<!-- Table 2: Row 2: Column 2: (time) -->
|
||||
<TextView
|
||||
android:id="@+id/list_item_time"
|
||||
style="@style/ListItemDate" />
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<!-- Table 1: Row 2: (category and description) -->
|
||||
<TextView
|
||||
android:id="@+id/list_item_category_description"
|
||||
style="@style/TextSmall"
|
||||
android:layout_column="1"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:singleLine="false"
|
||||
tools:text="description" />
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
</FrameLayout>
|
||||
|
||||
<View
|
||||
<!-- A gradient on top of the text -->
|
||||
<ImageView
|
||||
android:id="@+id/list_item_text_gradient"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?android:attr/activatedBackgroundIndicator" />
|
||||
</FrameLayout>
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:alpha=".6"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:contentDescription="@string/image_photo_marker"
|
||||
android:src="@drawable/text_gradient" />
|
||||
|
||||
<!-- Icon -->
|
||||
<ImageView
|
||||
android:id="@+id/list_item_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:contentDescription="@string/image_track"
|
||||
tools:src="@drawable/ic_activity_bike_24dp"/>
|
||||
|
||||
<!-- Name -->
|
||||
<TextView
|
||||
android:id="@+id/list_item_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceBody1"
|
||||
android:singleLine="true"
|
||||
android:layout_marginStart="8dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/list_item_icon"
|
||||
app:layout_constraintEnd_toStartOf="@id/list_item_date"
|
||||
tools:text="Main text" />
|
||||
|
||||
<!-- Date -->
|
||||
<TextView
|
||||
android:id="@+id/list_item_date"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceBodySmall"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/list_item_name"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:gravity="end"
|
||||
android:layout_marginStart="8dp"
|
||||
tools:text="2022-02-17" />
|
||||
|
||||
<!-- Total time and distance -->
|
||||
<TextView
|
||||
android:id="@+id/list_item_time_distance"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceBodySmall"
|
||||
app:layout_constraintTop_toBottomOf="@id/list_item_name"
|
||||
app:layout_constraintStart_toStartOf="@id/list_item_name"
|
||||
tools:text="50:43 (50.00km)" />
|
||||
|
||||
<!-- Marker count icon -->
|
||||
<ImageView
|
||||
android:id="@+id/list_item_marker_count_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@id/list_item_name"
|
||||
app:layout_constraintStart_toEndOf="@id/list_item_time_distance"
|
||||
app:layout_constraintBottom_toBottomOf="@id/list_item_time_distance"
|
||||
android:contentDescription="@string/image_marker_count"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_marker_show_24dp" />
|
||||
|
||||
<!-- Marker count text -->
|
||||
<TextView
|
||||
android:id="@+id/list_item_marker_count"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceBodySmall"
|
||||
app:layout_constraintStart_toEndOf="@id/list_item_marker_count_icon"
|
||||
app:layout_constraintBottom_toBottomOf="@id/list_item_marker_count_icon"
|
||||
android:singleLine="true"
|
||||
tools:text="2" />
|
||||
|
||||
<!-- Time -->
|
||||
<TextView
|
||||
android:id="@+id/list_item_time"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceBodySmall"
|
||||
app:layout_constraintTop_toBottomOf="@id/list_item_name"
|
||||
app:layout_constraintStart_toEndOf="@id/list_item_marker_count"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:gravity="end"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginStart="8dp"
|
||||
tools:text="10:10:10"/>
|
||||
|
||||
<!-- Second row barrier -->
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/list_item_second_row_barrier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="bottom"
|
||||
app:constraint_referenced_ids="list_item_time_distance,list_item_marker_count_icon,list_item_marker_count,list_item_time" />
|
||||
|
||||
<!-- Category and description -->
|
||||
<TextView
|
||||
android:id="@+id/list_item_category_description"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceBodySmall"
|
||||
app:layout_constraintTop_toBottomOf="@id/list_item_second_row_barrier"
|
||||
app:layout_constraintStart_toStartOf="@id/list_item_name"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:maxLines="2"
|
||||
android:ellipsize="end"
|
||||
tools:text="Description" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -15,7 +15,8 @@ limitations under the License.
|
||||
-->
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/marker_detail_marker_photo"
|
||||
@@ -46,20 +47,32 @@ limitations under the License.
|
||||
|
||||
<TextView
|
||||
android:id="@+id/marker_detail_marker_name"
|
||||
style="@style/TextMedium"
|
||||
android:paddingTop="8dp" />
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceBody1"
|
||||
android:paddingTop="8dp"
|
||||
tools:text="Marker Name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/marker_detail_marker_category"
|
||||
style="@style/TextSmall" />
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceBody2"
|
||||
tools:text="Marker Category" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/marker_detail_marker_description"
|
||||
style="@style/TextSmall" />
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceBody2"
|
||||
tools:text="Marker Description" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/marker_detail_marker_location"
|
||||
style="@style/TextSmall" />
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textAppearanceBody2"
|
||||
tools:text="Marker Location" />
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
</FrameLayout>
|
||||
@@ -31,40 +31,48 @@ limitations under the License.
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textInputOutlinedStyle"
|
||||
android:hint="@string/generic_name"
|
||||
android:imeOptions="actionNext"
|
||||
android:importantForAutofill="no">
|
||||
android:importantForAutofill="no"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:drawableStart="@drawable/ic_marker_orange_pushpin_with_shadow"
|
||||
android:id="@+id/marker_edit_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableStart="@drawable/ic_marker_orange_pushpin_with_shadow"
|
||||
android:inputType="textCapWords"
|
||||
android:selectAllOnFocus="true" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textInputOutlinedExposedDropdownMenuStyle"
|
||||
android:imeOptions="actionNext"
|
||||
android:importantForAutofill="no">
|
||||
android:importantForAutofill="no"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<AutoCompleteTextView
|
||||
<com.google.android.material.textfield.MaterialAutoCompleteTextView
|
||||
android:id="@+id/marker_edit_marker_type"
|
||||
style="@style/EditCategory"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/marker_edit_marker_type"
|
||||
android:imeOptions="actionNext"
|
||||
android:selectAllOnFocus="true" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textInputOutlinedStyle"
|
||||
android:hint="@string/generic_description"
|
||||
android:imeOptions="actionNext"
|
||||
android:importantForAutofill="no">
|
||||
android:importantForAutofill="no"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/marker_edit_description"
|
||||
@@ -72,6 +80,7 @@ limitations under the License.
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textCapSentences"
|
||||
android:selectAllOnFocus="true" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<RelativeLayout
|
||||
|
||||
@@ -27,6 +27,10 @@ limitations under the License.
|
||||
|
||||
<TextView
|
||||
android:id="@+id/marker_list_empty"
|
||||
style="@style/ListEmptyTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
style="?attr/textAppearanceHeadline4"
|
||||
android:gravity="center_horizontal|center_vertical"
|
||||
android:text="@string/marker_list_empty_message" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -14,7 +14,10 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
style="@style/ScrollView">
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scrollbarStyle="outsideInset">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -23,18 +26,33 @@ limitations under the License.
|
||||
android:paddingTop="16dp"
|
||||
android:paddingEnd="8dp">
|
||||
|
||||
<AutoCompleteTextView
|
||||
android:id="@+id/activity_type_preference_text_view"
|
||||
style="@style/EditCategory"
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:hint="@string/track_edit_activity_type_hint" />
|
||||
style="?attr/textInputOutlinedStyle"
|
||||
android:imeOptions="actionNext"
|
||||
android:importantForAutofill="no"
|
||||
android:layout_marginEnd="8dp">
|
||||
|
||||
<com.google.android.material.textfield.MaterialAutoCompleteTextView
|
||||
android:id="@+id/activity_type_preference_text_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp"
|
||||
android:hint="@string/track_edit_activity_type_hint"
|
||||
android:imeOptions="actionNext"
|
||||
android:singleLine="true"
|
||||
android:layout_gravity="bottom"
|
||||
android:selectAllOnFocus="true" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/activity_type_preference_spinner"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="bottom" />
|
||||
android:layout_gravity="bottom"
|
||||
tools:src="@drawable/ic_activity_bike_24dp"/>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
@@ -2,13 +2,22 @@
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
style="@style/StatsScrollView">
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scrollbarStyle="outsideOverlay">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<!-- Guidelines -->
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline_top"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_begin="16dp" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline"
|
||||
android:layout_width="0dp"
|
||||
@@ -33,25 +42,29 @@
|
||||
<!-- Activity information: icon, name, description and start datetime -->
|
||||
<ImageView
|
||||
android:id="@+id/stats_activity_type_icon"
|
||||
style="@style/IconStats"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintTop_toTopOf="@id/stats_name_value"
|
||||
app:layout_constraintTop_toTopOf="@id/guideline_top"
|
||||
tools:src="@drawable/ic_activity_bike_24dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_name_value"
|
||||
style="@style/TextLargeStats"
|
||||
style="?attr/textAppearanceHeadline6"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintStart_toEndOf="@id/stats_activity_type_icon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/guideline_top"
|
||||
tools:text="Name of the activity" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_description_value"
|
||||
style="@style/TextMediumStats"
|
||||
style="?attr/textAppearanceBody1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintStart_toStartOf="@id/stats_name_value"
|
||||
@@ -60,8 +73,11 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_start_datetime_value"
|
||||
style="@style/TextSmallStats"
|
||||
style="?attr/textAppearanceBody2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:gravity="end"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline"
|
||||
@@ -79,8 +95,8 @@
|
||||
<!-- Horizontal line -->
|
||||
<View
|
||||
android:id="@+id/stats_information_horizontal_line"
|
||||
style="@style/StatsHorizontalLine"
|
||||
android:layout_marginBottom="8dp"
|
||||
style="@style/HorizontalLine"
|
||||
android:layout_margin="8dp"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_start_datetime_value" />
|
||||
@@ -88,7 +104,9 @@
|
||||
<!-- Distance -->
|
||||
<TextView
|
||||
android:id="@+id/stats_distance_label"
|
||||
style="@style/LargeLabel"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/stats_distance"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
@@ -97,7 +115,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_distance_value"
|
||||
style="@style/StatsPrimaryValue"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
@@ -107,7 +127,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_distance_unit"
|
||||
style="@style/StatsUnit"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryUnit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="@id/stats_distance_value"
|
||||
app:layout_constraintStart_toEndOf="@id/stats_distance_value"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline2"
|
||||
@@ -116,7 +138,9 @@
|
||||
<!-- Moving time -->
|
||||
<TextView
|
||||
android:id="@+id/stats_moving_time_label"
|
||||
style="@style/LargeLabel"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/stats_moving_time"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
@@ -125,7 +149,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_moving_time_value"
|
||||
style="@style/StatsPrimaryValue"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
@@ -135,7 +161,9 @@
|
||||
<!-- Total time -->
|
||||
<TextView
|
||||
android:id="@+id/stats_total_time_label"
|
||||
style="@style/StatsSmallLabel"
|
||||
style="@style/TextAppearance.OpenTracks.SecondaryHeader"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/stats_total_time"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
@@ -144,7 +172,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_total_time_value"
|
||||
style="@style/StatsSmallValue"
|
||||
style="@style/TextAppearance.OpenTracks.SecondaryValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_total_time_label"
|
||||
@@ -161,8 +191,8 @@
|
||||
<!-- Horizontal line -->
|
||||
<View
|
||||
android:id="@+id/stats_speed_horizontal_line"
|
||||
style="@style/StatsHorizontalLine"
|
||||
android:layout_marginBottom="8dp"
|
||||
style="@style/HorizontalLine"
|
||||
android:layout_margin="8dp"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_time_barrier" />
|
||||
@@ -170,7 +200,9 @@
|
||||
<!-- Speed/Pace max -->
|
||||
<TextView
|
||||
android:id="@+id/stats_max_speed_label"
|
||||
style="@style/LargeLabel"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline2"
|
||||
@@ -179,7 +211,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_max_speed_value"
|
||||
style="@style/StatsPrimaryValue"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
@@ -189,7 +223,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_max_speed_unit"
|
||||
style="@style/StatsUnit"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryUnit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="@id/stats_max_speed_value"
|
||||
app:layout_constraintStart_toEndOf="@id/stats_max_speed_value"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline2"
|
||||
@@ -198,7 +234,9 @@
|
||||
<!-- Avg. moving speed/pace -->
|
||||
<TextView
|
||||
android:id="@+id/stats_moving_speed_label"
|
||||
style="@style/LargeLabel"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
@@ -207,7 +245,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_moving_speed_value"
|
||||
style="@style/StatsPrimaryValue"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
@@ -217,7 +257,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_moving_speed_unit"
|
||||
style="@style/StatsUnit"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryUnit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="@id/stats_moving_speed_value"
|
||||
app:layout_constraintStart_toEndOf="@id/stats_moving_speed_value"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
@@ -226,7 +268,9 @@
|
||||
<!-- Average speed/pace -->
|
||||
<TextView
|
||||
android:id="@+id/stats_average_speed_label"
|
||||
style="@style/StatsSmallLabel"
|
||||
style="@style/TextAppearance.OpenTracks.SecondaryHeader"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
@@ -235,7 +279,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_average_speed_value"
|
||||
style="@style/StatsSmallValue"
|
||||
style="@style/TextAppearance.OpenTracks.SecondaryValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
@@ -245,7 +291,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_average_speed_unit"
|
||||
style="@style/StatsUnit"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryUnit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="@id/stats_average_speed_value"
|
||||
app:layout_constraintStart_toEndOf="@id/stats_average_speed_value"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
@@ -262,8 +310,8 @@
|
||||
<!-- Horizontal line -->
|
||||
<View
|
||||
android:id="@+id/stats_altitude_horizontal_line"
|
||||
style="@style/StatsHorizontalLine"
|
||||
android:layout_marginBottom="8dp"
|
||||
style="@style/HorizontalLine"
|
||||
android:layout_margin="8dp"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_speed_barrier" />
|
||||
@@ -277,7 +325,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_altitude_gain_label"
|
||||
style="@style/LargeLabel"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/stats_gain"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
@@ -286,7 +336,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_altitude_gain_value"
|
||||
style="@style/StatsPrimaryValue"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
@@ -296,7 +348,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_altitude_gain_unit"
|
||||
style="@style/StatsUnit"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryUnit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="@id/stats_altitude_gain_value"
|
||||
app:layout_constraintStart_toEndOf="@id/stats_altitude_gain_value"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline2"
|
||||
@@ -304,7 +358,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_altitude_loss_label"
|
||||
style="@style/LargeLabel"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/stats_loss"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
@@ -313,7 +369,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_altitude_loss_value"
|
||||
style="@style/StatsPrimaryValue"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
@@ -323,7 +381,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_altitude_loss_unit"
|
||||
style="@style/StatsUnit"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryUnit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="@id/stats_altitude_loss_value"
|
||||
app:layout_constraintStart_toEndOf="@id/stats_altitude_loss_value"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
@@ -340,8 +400,8 @@
|
||||
<!-- Horizontal line -->
|
||||
<View
|
||||
android:id="@+id/stats_end_horizontal_line"
|
||||
style="@style/StatsHorizontalLine"
|
||||
android:layout_marginBottom="8dp"
|
||||
style="@style/HorizontalLine"
|
||||
android:layout_margin="8dp"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_altitude_barrier" />
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
style="@style/StatsScrollView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
|
||||
<TextClock
|
||||
android:id="@+id/stats_clock"
|
||||
style="@style/StatsPrimaryValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryValue"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -19,7 +21,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_description_main"
|
||||
style="@style/StatsPrimaryLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
||||
android:text="@string/stats_clock"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_value"
|
||||
style="@style/StatsPrimaryValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryValue"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
@@ -20,7 +22,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_unit"
|
||||
style="@style/StatsUnit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryUnit"
|
||||
tools:text="@string/sensor_unit_beats_per_minute"
|
||||
app:layout_constraintStart_toEndOf="@id/stats_value"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@@ -28,7 +32,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_description_main"
|
||||
style="@style/StatsPrimaryLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
||||
tools:text="@string/sensor_state_heart_rate"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@@ -36,7 +42,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_description_secondary"
|
||||
style="@style/StatsTinyValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.OpenTracks.SecondaryHeader"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="16dp"
|
||||
android:paddingTop="16dp">
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_description_main"
|
||||
style="@style/LargeLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
||||
tools:text="@string/sensor_state_heart_rate"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@@ -18,7 +19,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_value"
|
||||
style="@style/StatsPrimaryValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryValue"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_description_main"
|
||||
@@ -28,7 +31,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_unit"
|
||||
style="@style/StatsUnit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryUnit"
|
||||
tools:text="@string/sensor_unit_beats_per_minute"
|
||||
app:layout_constraintStart_toEndOf="@id/stats_value"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@@ -36,7 +41,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_description_secondary"
|
||||
style="@style/StatsTinyValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.OpenTracks.SecondaryHeader"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
style="@style/Widget.MaterialComponents.Toolbar.Surface"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:minHeight="?attr/actionBarSize" />
|
||||
android:layout_height="wrap_content" />
|
||||
</merge>
|
||||
|
||||
@@ -16,6 +16,7 @@ limitations under the License.
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/toolbar" />
|
||||
@@ -31,9 +32,11 @@ limitations under the License.
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textInputOutlinedStyle"
|
||||
android:hint="@string/generic_name"
|
||||
android:imeOptions="actionNext"
|
||||
android:importantForAutofill="no">
|
||||
android:importantForAutofill="no"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/track_edit_name"
|
||||
@@ -41,6 +44,7 @@ limitations under the License.
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textCapWords"
|
||||
android:selectAllOnFocus="true" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<LinearLayout
|
||||
@@ -52,30 +56,39 @@ limitations under the License.
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textInputOutlinedExposedDropdownMenuStyle"
|
||||
android:imeOptions="actionNext"
|
||||
android:importantForAutofill="no">
|
||||
android:importantForAutofill="no"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<AutoCompleteTextView
|
||||
<com.google.android.material.textfield.MaterialAutoCompleteTextView
|
||||
android:id="@+id/track_edit_activity_type"
|
||||
style="@style/EditCategory"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:hint="@string/track_edit_activity_type_hint"
|
||||
android:imeOptions="actionNext"
|
||||
android:selectAllOnFocus="true" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/track_edit_activity_type_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="bottom" />
|
||||
android:layout_gravity="bottom"
|
||||
tools:src="@drawable/ic_activity_bike_24dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/textInputOutlinedStyle"
|
||||
android:hint="@string/generic_description"
|
||||
android:imeOptions="actionNext"
|
||||
android:importantForAutofill="no">
|
||||
android:importantForAutofill="no"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/track_edit_description"
|
||||
@@ -83,6 +96,7 @@ limitations under the License.
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textCapSentences"
|
||||
android:selectAllOnFocus="true" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -69,7 +69,7 @@ limitations under the License.
|
||||
android:layout_weight="5" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextLarge"
|
||||
style="?attr/textAppearanceHeadline6"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="22"
|
||||
|
||||
@@ -473,7 +473,7 @@
|
||||
<string name="settings_sensor_cycling_distance_speed">Distancia y velocidad</string>
|
||||
<string name="settings_sensor_cycling_cadence">Cadencia</string>
|
||||
<string name="track_delete_not_recording">Este recorrido no puede ser eliminado porque se está grabando actualmente.</string>
|
||||
<string name="settings_defaults_title">Unidades y actividad predeterminadas</string>
|
||||
<string name="settings_defaults_title">Unidades y Actividad predeterminadas</string>
|
||||
<string name="export_cannot_write_to_dir">No se puede escribir al directorio de exportación.</string>
|
||||
<string name="export_dir_not_writable">" (¡No se puede escribir!)"</string>
|
||||
<string name="value_float_mile_hour">%1$.1f mi/h</string>
|
||||
@@ -533,4 +533,8 @@
|
||||
<string name="no_compatible_file_manager_installed">No hay un gestor de archivos compatible instalado</string>
|
||||
<string name="settings_running_sensor">Correr</string>
|
||||
<string name="settings_sensor_bluetooth_service_filter_enabled">Mostrar sólo los dispositivos Bluetooth que anuncian los servicios requeridos</string>
|
||||
<string name="settings_api_title">API pública</string>
|
||||
<string name="settings_public_api_dashboard_enabled_title">Transferencia automática de datos</string>
|
||||
<string name="settings_public_api_dashboard_enabled_summary_off">Los datos grabados no se compartirán automáticamente con otras aplicaciones.</string>
|
||||
<string name="settings_public_api_dashboard_enabled_summary_on">Una aplicación que ha iniciado una grabación puede también acceder a los datos grabados.</string>
|
||||
</resources>
|
||||
@@ -450,7 +450,7 @@ limitations under the License.
|
||||
<string name="settings_night_mode_title">Vzhled</string>
|
||||
<string name="settings_night_mode_option_day">Světlý</string>
|
||||
<string name="settings_night_mode_option_night">Tmavý</string>
|
||||
<string name="settings_sensor_wheel_circumference">Obvod kolečka (mm)</string>
|
||||
<string name="settings_sensor_wheel_circumference">Obvod kola (mm)</string>
|
||||
<string name="stats_split_interval">Rozdělit interval</string>
|
||||
<string name="track_detail_intervals_tab">Intervaly</string>
|
||||
<string name="voice_pace_lap">Čas na kolo %1$s</string>
|
||||
@@ -493,7 +493,7 @@ limitations under the License.
|
||||
<string name="export_progress_summary_new_msg">Vyvezeny nové tratě</string>
|
||||
<string name="import_progress_summary_exists_msg">Existovat</string>
|
||||
<string name="import_progress_summary_ok_msg">Importované soubory</string>
|
||||
<string name="generic_open_track">Otevřená Trať</string>
|
||||
<string name="generic_open_track">Otevřená trať</string>
|
||||
<string name="sensor_state_power_avg">Výkon Avg</string>
|
||||
<string name="sensor_state_heart_rate_max">Maximální srdeční frekvence</string>
|
||||
<string name="sensor_state_heart_rate_avg">Avg srdeční frekvence</string>
|
||||
@@ -557,7 +557,7 @@ limitations under the License.
|
||||
<string name="settings_sensors_title">Bluetooth senzory</string>
|
||||
<string name="settings_gps_summary">Časový interval, Interval vzdálenosti, Maximální vzdálenost, Přesnost</string>
|
||||
<string name="settings_ui_summary">Vzhled, Rozložení záznamu, Téma</string>
|
||||
<string name="settings_open_tracks_title">Otevřené stopy</string>
|
||||
<string name="settings_open_tracks_title">O programu</string>
|
||||
<string name="settings_open_tracks_summary">Vše o projektu OpenTracks</string>
|
||||
<string name="settings_defaults_title">Výchozí jednotky a aktivity</string>
|
||||
<string name="settings_ui_title">Uživatelské rozhraní</string>
|
||||
@@ -582,4 +582,32 @@ limitations under the License.
|
||||
<string name="export_dir_not_writable">" (nelze zapisovat!)"</string>
|
||||
<string name="generic_today">Dnes</string>
|
||||
<string name="no_compatible_file_manager_installed">Není nainstalován kompatibilní správce souborů</string>
|
||||
<string name="settings_announcements_report_heart_rate">Hlásit tepovou frekvenci</string>
|
||||
<string name="voice_per_kilometer">za kilometr</string>
|
||||
<string name="lap_time">Čas kola</string>
|
||||
<string name="lap_speed">Rychlost kola</string>
|
||||
<string name="speed">Rychlost</string>
|
||||
<string name="total_distance">Celková vzdálenost</string>
|
||||
<plurals name="voiceDistanceKilometers">
|
||||
<item quantity="one"></item>
|
||||
<item quantity="few"></item>
|
||||
<item quantity="other"></item>
|
||||
</plurals>
|
||||
<string name="average_heart_rate">Průměrný srdeční tep</string>
|
||||
<string name="current_heart_rate">Aktuální srdeční tep</string>
|
||||
<string name="settings_public_api_enabled_summary_on">Ostatní nainstalované aplikace mohou nahrávání spustit nebo zastavit.</string>
|
||||
<string name="voice_per_mile">za míli</string>
|
||||
<string name="activity_type_swimming">plavání</string>
|
||||
<string name="activity_type_swimming_open">plavání ve volné vodě</string>
|
||||
<string name="activity_type_workout">trénink</string>
|
||||
<string name="generic_dismiss">Odmítnout</string>
|
||||
<string name="gps_recording_status">%1$s: %2$s</string>
|
||||
<string name="gps_recording_without_signal">OpenTracks nahrává bez údajů GPS.</string>
|
||||
<string name="settings_public_api_enabled_title">Veřejné API</string>
|
||||
<string name="settings_public_api_enabled_summary_off">Pouze aplikace OpenTracks může spouštět a zastavovat nahrávání.</string>
|
||||
<string name="settings_public_api_disabled_toast">Veřejné rozhraní API služby OpenTracks je zakázáno: lze jej povolit v nastavení.</string>
|
||||
<string name="settings_api_title">Veřejné API</string>
|
||||
<string name="settings_public_api_dashboard_enabled_title">Automatický přenos dat</string>
|
||||
<string name="settings_public_api_dashboard_enabled_summary_on">Aplikace, která zahájila nahrávání, může také přistupovat k nahraným datům.</string>
|
||||
<string name="settings_public_api_dashboard_enabled_summary_off">Zaznamenaná data nebudou automaticky sdílena s jinými aplikacemi.</string>
|
||||
</resources>
|
||||
@@ -569,7 +569,7 @@ limitations under the License.
|
||||
<string name="settings_announcements_summary">Tiempo, Distancia, Velocidad de la voz</string>
|
||||
<string name="settings_sensor_bluetooth_service_filter_enabled">Mostrar sólo los dispositivos Bluetooth que anuncian los servicios requeridos</string>
|
||||
<string name="settings_open_tracks_summary">Todo sobre el proyecto OpenTracks</string>
|
||||
<string name="settings_defaults_title">Unidades y actividad predeterminadas</string>
|
||||
<string name="settings_defaults_title">Unidades y Actividad predeterminadas</string>
|
||||
<string name="settings_defaults_summary">Unidades preferidas, Actividad predeterminada, Nombre predeterminado del recorrido</string>
|
||||
<string name="settings_open_tracks_title">Open Tracks</string>
|
||||
<string name="generic_undo">Deshacer</string>
|
||||
@@ -584,4 +584,8 @@ limitations under the License.
|
||||
<string name="generic_dismiss">Descartar</string>
|
||||
<string name="gps_recording_status">%1$s: %2$s</string>
|
||||
<string name="gps_recording_without_signal">OpenTracks está grabando sin datos GPS.</string>
|
||||
<string name="settings_api_title">API pública</string>
|
||||
<string name="settings_public_api_dashboard_enabled_title">Transferencia automática de datos</string>
|
||||
<string name="settings_public_api_dashboard_enabled_summary_on">Una aplicación que ha iniciado una grabación puede también acceder a los datos grabados.</string>
|
||||
<string name="settings_public_api_dashboard_enabled_summary_off">Los datos grabados no se compartirán automáticamente con otras aplicaciones.</string>
|
||||
</resources>
|
||||
@@ -85,8 +85,8 @@ limitations under the License.
|
||||
<string name="description_activity_type">Aktivitetstype: %1$s</string>
|
||||
<string name="description_average_moving_pace_in_minute">Gjennomsnittlig forflytningstempo: %1$s min/km (%2$s min/mi)</string>
|
||||
<string name="description_average_moving_speed">Gjennomsnittlig forflytningshastighet: %1$.2f km/t (%2$.1f mi/h)</string>
|
||||
<string name="description_average_pace_in_minute">Gjennomsnittstempo: %1$s min/km (%2$s min/mi)</string>
|
||||
<string name="description_average_speed">Gjennomsnittshastighet: %1$.2f km/t (%2$.1f mi/h)</string>
|
||||
<string name="description_average_pace_in_minute">Gjennomsnittlig tempo: %1$s min/km (%2$s min/mi)</string>
|
||||
<string name="description_average_speed">Gjennomsnittlig hastighet: %1$.2f km/t (%2$.1f mi/h)</string>
|
||||
<string name="description_altitude_gain">Total stigning: %1$d m (%2$d ft)</string>
|
||||
<string name="description_altitude_imperial">Høyde (ft)</string>
|
||||
<string name="description_altitude_metric">Høyde (m)</string>
|
||||
@@ -584,4 +584,22 @@ limitations under the License.
|
||||
<string name="settings_public_api_dashboard_enabled_title">Automatisk dataoverføring</string>
|
||||
<string name="settings_public_api_dashboard_enabled_summary_on">Et program som startet et opptak kan også få tilgang til de registrerte dataene.</string>
|
||||
<string name="settings_public_api_dashboard_enabled_summary_off">Registrerte data blir ikke delt automatisk med andre programmer.</string>
|
||||
<string name="settings_announcements_report_heart_rate">Rapporter puls</string>
|
||||
<string name="voice_per_kilometer">per kilometer</string>
|
||||
<string name="voice_per_mile">per engelsk mil</string>
|
||||
<string name="lap_time">Rundetid</string>
|
||||
<string name="pace">Tempo</string>
|
||||
<string name="lap_speed">Rundehastighet</string>
|
||||
<string name="speed">Hastighet</string>
|
||||
<string name="total_distance">Total distanse</string>
|
||||
<plurals name="voiceDistanceMiles">
|
||||
<item quantity="one">1 engelsk mil</item>
|
||||
<item quantity="other">%1$.2f engelske mil</item>
|
||||
</plurals>
|
||||
<string name="average_heart_rate">Gjennomsnittlig puls</string>
|
||||
<plurals name="voiceDistanceKilometers">
|
||||
<item quantity="one">1 kilometer</item>
|
||||
<item quantity="other">%1$.2f kilometer</item>
|
||||
</plurals>
|
||||
<string name="current_heart_rate">Gjeldende puls</string>
|
||||
</resources>
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorAccent">@android:color/holo_orange_dark</color>
|
||||
<color name="colorAccentTransparent">#55ff8800</color>
|
||||
</resources>
|
||||
@@ -0,0 +1,25 @@
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<style name="ThemeCustom" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||
<item name="colorPrimary">@color/opentracks_secondary_color</item>
|
||||
<item name="colorPrimaryVariant">@android:color/black</item>
|
||||
<item name="colorOnPrimary">@android:color/white</item>
|
||||
|
||||
<item name="colorSecondary">@color/opentracks_secondary_color</item>
|
||||
<item name="colorSecondaryVariant">@color/opentracks_secondary_color</item>
|
||||
<item name="colorOnSecondary">@color/opentracks_on_secondary_color</item>
|
||||
|
||||
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
|
||||
|
||||
<item name="windowActionModeOverlay">true</item>
|
||||
|
||||
<item name="android:colorBackground">@android:color/black</item>
|
||||
<item name="colorOnBackground">@android:color/white</item>
|
||||
|
||||
<item name="colorSurface">@color/opentracks_surface_night_background_color</item>
|
||||
<item name="colorOnSurface">@color/opentracks_surface_night_on_background_color</item>
|
||||
|
||||
<item name="android:activatedBackgroundIndicator">@drawable/activated_background</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
@@ -380,7 +380,7 @@ limitations under the License.
|
||||
<string name="generic_overwrite">Ghi đè</string>
|
||||
<string name="menu_voice_rate">Tốc độ giọng nói</string>
|
||||
<string name="open_track_as_GPX">Mở dưới dạng GPX</string>
|
||||
<string name="help_recording_content">OpenTracks sử dụng các bộ cảm biến GPS của điện thoại để ghi lại vị trí liên tục. Nếu các thiết bị GPS báo cáo dữ liệu không chính xác (ví dụ như, vị trí, tốc độ, độ cao), OpenTracks không thể làm bất cứ điều gì về nó.</string>
|
||||
<string name="help_recording_content">Mở bài Hát sử dụng các bộ cảm biến GPS của điện thoại để ghi lại vị trí liên tục. Nếu các thiết bị GPS báo cáo dữ liệu không chính xác (ví dụ như, vị trí, tốc độ, độ cao), OpenTracks không thể làm bất cứ điều gì về nó.</string>
|
||||
<string name="gps_fixed_and_ready">GPS đã xác định và sẵn sàng</string>
|
||||
<string name="generic_skip">Bỏ qua</string>
|
||||
<string name="import_unsupported_format">Định dạng tệp không được hỗ trợ</string>
|
||||
@@ -562,4 +562,34 @@ limitations under the License.
|
||||
<string name="export_dir_not_writable">" (không ghi được!)"</string>
|
||||
<string name="export_cannot_write_to_dir">Không thể viết để xuất khẩu, xuất khẩu.</string>
|
||||
<string name="no_compatible_file_manager_installed">Không có tương thích quản lý tập tin cài đặt</string>
|
||||
<string name="activity_type_swimming">bơi</string>
|
||||
<string name="activity_type_workout">thể dục</string>
|
||||
<string name="settings_public_api_enabled_title">API công khai</string>
|
||||
<string name="settings_public_api_enabled_summary_off">Chỉ OpenTracks có thể bắt đầu và dừng ghi.</string>
|
||||
<string name="settings_public_api_enabled_summary_on">Các ứng dụng khác có thể bắt đầu hoặc dừng ghi.</string>
|
||||
<string name="settings_announcements_report_heart_rate">Báo cáo nhịp tim</string>
|
||||
<string name="settings_api_title">API công khai</string>
|
||||
<string name="generic_dismiss">Bỏ</string>
|
||||
<string name="gps_recording_status">%1$s: %2$s</string>
|
||||
<string name="gps_recording_without_signal">OpenTracks đang ghi lại mà không có dữ liệu GPS.</string>
|
||||
<string name="activity_type_swimming_open">bơi trong mở nước</string>
|
||||
<string name="voice_per_kilometer">mỗi km</string>
|
||||
<string name="total_distance">Tổng khoảng cách</string>
|
||||
<string name="settings_public_api_disabled_toast">Mở bài Hát Công cộng API là vô hiệu hóa nó có thể được kích hoạt trong các thiết lập.</string>
|
||||
<string name="voice_per_mile">mỗi dặm</string>
|
||||
<string name="lap_time">Thời gian lòng</string>
|
||||
<string name="pace">Tốc độ</string>
|
||||
<string name="lap_speed">Vòng tốc độ</string>
|
||||
<string name="speed">Tốc độ</string>
|
||||
<plurals name="voiceDistanceKilometers">
|
||||
<item quantity="other">%1$.2f Tầng km</item>
|
||||
</plurals>
|
||||
<plurals name="voiceDistanceMiles">
|
||||
<item quantity="other">%1$.2f dặm</item>
|
||||
</plurals>
|
||||
<string name="average_heart_rate">Nhịp tim trung bình</string>
|
||||
<string name="current_heart_rate">Hiện tại nhịp tim</string>
|
||||
<string name="settings_public_api_dashboard_enabled_summary_on">Một ứng dụng bắt đầu ghi hình cũng có thể truy cập vào dữ liệu ghi lại.</string>
|
||||
<string name="settings_public_api_dashboard_enabled_summary_off">Ghi lại dữ liệu sẽ không được miễn phí tự động với các ứng dụng khác.</string>
|
||||
<string name="settings_public_api_dashboard_enabled_title">Chuyển dữ liệu tự động</string>
|
||||
</resources>
|
||||
@@ -14,6 +14,21 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<resources>
|
||||
<!-- Brand colors -->
|
||||
<color name="opentracks_primary_color">#344955</color>
|
||||
<color name="opentracks_primary_variant_color">#232f34</color>
|
||||
<color name="opentracks_on_primary_color">@android:color/white</color>
|
||||
<color name="opentracks_secondary_color">#ff8800</color>
|
||||
<color name="opentracks_secondary_transparent">#55ff8800</color>
|
||||
<color name="opentracks_on_secondary_color">@android:color/white</color>
|
||||
|
||||
<color name="opentracks_surface_day_background_color">#f5f5f5</color>
|
||||
<color name="opentracks_surface_day_on_background_color">@android:color/black</color>
|
||||
|
||||
<color name="opentracks_surface_night_background_color">@android:color/black</color>
|
||||
<color name="opentracks_surface_night_on_background_color">@android:color/white</color>
|
||||
|
||||
<!-- Chart colors -->
|
||||
<color name="chart_cadence_border">#FFF128F3</color> <!-- pink -->
|
||||
<color name="chart_cadence_fill">#40F128F3</color> <!-- transparent pink -->
|
||||
<color name="chart_altitude_border">#FF339933</color> <!-- green -->
|
||||
@@ -27,8 +42,10 @@ limitations under the License.
|
||||
<color name="chart_power_border">#FF7A26C0</color> <!-- purple -->
|
||||
<color name="chart_power_fill">#407A26C0</color> <!-- transparent purple -->
|
||||
|
||||
<color name="colorAccent">@android:color/holo_orange_dark</color>
|
||||
<color name="colorAccentTransparent">#55FF8800</color>
|
||||
<!-- Generic colors -->
|
||||
<color name="red_dark">#ffbe161b</color>
|
||||
|
||||
<color name="light_gray">#aaaaaa</color>
|
||||
|
||||
<color name="colorBackgroundDelete">#F44336</color>
|
||||
<color name="colorForegroundDelete">#FEFEFE</color>
|
||||
|
||||
@@ -283,6 +283,7 @@
|
||||
<item>1.2</item>
|
||||
<item>1.3</item>
|
||||
</string-array>
|
||||
<string name="voice_announce_heart_rate_key" translatable="false">voiceAnnounceHeartRate</string>
|
||||
|
||||
<string name="export_trackfileformat_key" translatable="false">exportTrackFileFormat</string>
|
||||
<!-- See TrackFileFormat -->
|
||||
|
||||
@@ -405,6 +405,7 @@ limitations under the License.
|
||||
<string name="settings_announcements">Announcements</string>
|
||||
<string name="settings_announcements_totaltime_frequency">Time interval</string>
|
||||
<string name="settings_announcements_distance_frequency">Distance interval</string>
|
||||
<string name="settings_announcements_report_heart_rate">Report heart rate</string>
|
||||
|
||||
<string name="settings_reset">Reset</string>
|
||||
<!-- Custom Layout -->
|
||||
@@ -574,6 +575,13 @@ limitations under the License.
|
||||
<string name="voice_template">%1$s in %2$s at %3$s</string>
|
||||
<string name="voice_speed_lap">Lap speed of %1$s</string>
|
||||
<string name="voice_pace_lap">Lap time of %1$s</string>
|
||||
<string name="voice_per_kilometer">per kilometer</string>
|
||||
<string name="voice_per_mile">per mile</string>
|
||||
<string name="lap_time">Lap time</string>
|
||||
<string name="pace">Pace</string>
|
||||
<string name="lap_speed">Lap speed</string>
|
||||
<string name="speed">Speed</string>
|
||||
<string name="total_distance">Total distance</string>
|
||||
<plurals name="voiceTotalDistanceKilometers">
|
||||
<item quantity="one">total distance 1 kilometer</item>
|
||||
<item quantity="other">total distance %1$.2f kilometers</item>
|
||||
@@ -582,6 +590,16 @@ limitations under the License.
|
||||
<item quantity="one">total distance 1 mile</item>
|
||||
<item quantity="other">total distance %1$.2f miles</item>
|
||||
</plurals>
|
||||
<plurals name="voiceDistanceKilometers">
|
||||
<item quantity="one">1 kilometer</item>
|
||||
<item quantity="other">%1$.2f kilometers</item>
|
||||
</plurals>
|
||||
<plurals name="voiceDistanceMiles">
|
||||
<item quantity="one">1 mile</item>
|
||||
<item quantity="other">%1$.2f miles</item>
|
||||
</plurals>
|
||||
<string name="average_heart_rate">Average heart rate</string>
|
||||
<string name="current_heart_rate">Current heart rate</string>
|
||||
<!-- Waypoint Type -->
|
||||
<string name="waypoint_type_atm">ATM</string>
|
||||
<string name="waypoint_type_bank">bank</string>
|
||||
|
||||
+14
-274
@@ -15,7 +15,13 @@ limitations under the License.
|
||||
-->
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<style name="BottomButton" parent="Widget.AppCompat.Button.Borderless">
|
||||
<!-- toolbar -->
|
||||
<style name="Widget.OpenTracks.Toolbar.Surface" parent="Widget.MaterialComponents.Toolbar.Surface">
|
||||
<item name="android:background">#ff0000</item>
|
||||
</style>
|
||||
|
||||
<!-- bottom bar -->
|
||||
<style name="BottomButton" parent="Widget.MaterialComponents.Button.TextButton">
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_weight">1</item>
|
||||
<item name="android:layout_width">0dp</item>
|
||||
@@ -36,70 +42,20 @@ limitations under the License.
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
</style>
|
||||
|
||||
<style name="EditCategory" parent="@style/TextMedium">
|
||||
<item name="android:completionThreshold">1</item>
|
||||
<item name="android:gravity">bottom</item>
|
||||
<item name="android:layout_gravity">bottom</item>
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:selectAllOnFocus">true</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
</style>
|
||||
|
||||
<style name="EditDescription" parent="@style/TextMedium">
|
||||
<item name="android:inputType">textAutoCorrect|textCapSentences|textMultiLine</item>
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:maxLines">50</item>
|
||||
<item name="android:selectAllOnFocus">true</item>
|
||||
<item name="android:singleLine">false</item>
|
||||
</style>
|
||||
|
||||
<style name="EditLabel" parent="@style/TextMedium">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
</style>
|
||||
|
||||
<style name="EditLabel.WithPaddingTop">
|
||||
<item name="android:paddingTop">16dp</item>
|
||||
</style>
|
||||
|
||||
<style name="EditNameMarker" parent="@style/TextMedium.WithIcon">
|
||||
<item name="android:inputType">textAutoCorrect|textCapWords</item>
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:selectAllOnFocus">true</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
</style>
|
||||
|
||||
<style name="EditNameTrack" parent="@style/TextMedium">
|
||||
<item name="android:inputType">textAutoCorrect|textCapWords</item>
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:selectAllOnFocus">true</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
</style>
|
||||
|
||||
<style name="ListEmptyTextView" parent="@style/TextLarge">
|
||||
<item name="android:gravity">center</item>
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<item name="android:padding">8dp</item>
|
||||
</style>
|
||||
|
||||
<style name="ListItemDate" parent="@style/TextSmall">
|
||||
<item name="android:gravity">right</item>
|
||||
<item name="android:layout_gravity">bottom</item>
|
||||
<item name="android:paddingLeft">8dp</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
</style>
|
||||
<!-- scroll view -->
|
||||
<style name="ScrollView">
|
||||
<item name="android:layout_height">match_parent</item>
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:scrollbarStyle">outsideInset</item>
|
||||
</style>
|
||||
|
||||
<!-- scroll view with bottom buttons -->
|
||||
<style name="ScrollView.WithBottomButtons">
|
||||
<item name="android:layout_height">0dp</item>
|
||||
<item name="android:layout_weight">1</item>
|
||||
</style>
|
||||
<!-- sensor state value -->
|
||||
<!-- stats horizontal line -->
|
||||
|
||||
<!-- horizontal line -->
|
||||
<style name="HorizontalLine">
|
||||
<item name="android:background">@color/stats_separator</item>
|
||||
<item name="android:layout_height">1dp</item>
|
||||
@@ -107,225 +63,9 @@ limitations under the License.
|
||||
<item name="android:layout_marginBottom">8dp</item>
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
</style>
|
||||
<style name="StatsHorizontalLine" parent="HorizontalLine">
|
||||
<item name="android:layout_margin">8dp</item>
|
||||
</style>
|
||||
<!-- stats large item container -->
|
||||
<style name="StatsLargeItemContainer">
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_weight">1</item>
|
||||
<item name="android:layout_width">0dp</item>
|
||||
<item name="android:orientation">vertical</item>
|
||||
<item name="android:paddingBottom">6dp</item>
|
||||
<item name="android:paddingTop">6dp</item>
|
||||
</style>
|
||||
|
||||
<style name="LargeLabel" parent="@style/TextSmall">
|
||||
<item name="android:textAllCaps">true</item>
|
||||
<item name="android:textColor">?attr/colorAccent</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="android:layout_marginTop">8dp</item>
|
||||
</style>
|
||||
|
||||
<style name="IntervalLabel" parent="@style/LargeLabel">
|
||||
<item name="android:singleLine">true</item>
|
||||
<item name="android:layout_margin">1dp</item>
|
||||
<item name="android:textAlignment">textStart</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_weight">1</item>
|
||||
<item name="android:layout_width">1dp</item>
|
||||
<item name="android:layout_gravity">center</item>
|
||||
</style>
|
||||
|
||||
<style name="StatsPrimaryLabel" parent="@style/TextMicro">
|
||||
<item name="android:gravity">center</item>
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<item name="android:singleLine">false</item>
|
||||
<item name="android:textAllCaps">true</item>
|
||||
<item name="android:textColor">?attr/colorAccent</item>
|
||||
<item name="android:paddingLeft">4dp</item>
|
||||
<item name="android:paddingRight">4dp</item>
|
||||
<item name="android:layout_marginTop">8dp</item>
|
||||
<item name="android:textStyle">normal</item>
|
||||
</style>
|
||||
|
||||
<style name="StatsSecondaryLabel" parent="@style/TextMicro">
|
||||
<item name="android:gravity">center</item>
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<item name="android:singleLine">false</item>
|
||||
<item name="android:textAllCaps">true</item>
|
||||
<item name="android:paddingLeft">4dp</item>
|
||||
<item name="android:paddingRight">4dp</item>
|
||||
<item name="android:layout_marginTop">16dp</item>
|
||||
<item name="android:textStyle">normal</item>
|
||||
</style>
|
||||
|
||||
<style name="StatsLabelHidden" parent="@style/StatsPrimaryLabel">
|
||||
<item name="android:textColor">@android:color/secondary_text_dark</item>
|
||||
</style>
|
||||
|
||||
<style name="StatsPrimaryValue" parent="@style/TextLarge">
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
<item name="android:textSize">36sp</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
|
||||
<style name="StatsSecondaryValue" parent="@style/TextLarge">
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
<item name="android:textSize">28sp</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
|
||||
<style name="StatsValueHidden" parent="@style/StatsPrimaryValue">
|
||||
<item name="android:textColor">@android:color/secondary_text_dark</item>
|
||||
</style>
|
||||
<!-- stats large value container -->
|
||||
<style name="StatsLargeValueContainer">
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_marginTop">-4dp</item>
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
</style>
|
||||
<!-- stats scroll view -->
|
||||
<style name="StatsScrollView" parent="@style/ScrollView">
|
||||
<item name="android:scrollbarStyle">outsideOverlay</item>
|
||||
</style>
|
||||
<!-- stats small group container -->
|
||||
<style name="StatsSmallGroupContainer" parent="@style/StatsLargeItemContainer">
|
||||
<item name="android:layout_gravity">top|center</item>
|
||||
<item name="android:paddingTop">12dp</item>
|
||||
</style>
|
||||
<!-- stats small item container, for placing inside StatsSmallGroupContainer -->
|
||||
<style name="StatsSmallItemContainer" parent="@style/StatsLargeItemContainer">
|
||||
<item name="android:paddingTop">12dp</item>
|
||||
</style>
|
||||
|
||||
<style name="StatsSmallLabel" parent="@style/TextSmall">
|
||||
<item name="android:gravity">center</item>
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<item name="android:singleLine">false</item>
|
||||
<item name="android:textAllCaps">true</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="android:layout_marginTop">16dp</item>
|
||||
</style>
|
||||
|
||||
<style name="StatsSmallValue" parent="@style/TextLarge">
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
</style>
|
||||
|
||||
<style name="StatsTinyValue" parent="@style/TextSmall">
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
</style>
|
||||
|
||||
<style name="StatsTinyLabel" parent="@style/TextSmall">
|
||||
<item name="android:gravity">center</item>
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<item name="android:singleLine">false</item>
|
||||
</style>
|
||||
|
||||
<style name="StatsSmallValueContainer" parent="@style/StatsLargeValueContainer">
|
||||
<item name="android:layout_marginTop">-2dp</item>
|
||||
</style>
|
||||
|
||||
<style name="StatsUnit" parent="@style/TextSmall">
|
||||
<item name="android:layout_gravity">bottom</item>
|
||||
<item name="android:paddingLeft">4dp</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
</style>
|
||||
|
||||
<style name="TextLargeStats" parent="TextAppearance.AppCompat.Large">
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
<item name="android:paddingLeft">8dp</item>
|
||||
<item name="android:paddingRight">4dp</item>
|
||||
<item name="android:layout_marginTop">16dp</item>
|
||||
</style>
|
||||
|
||||
<style name="TextMediumStats" parent="TextAppearance.AppCompat.Medium">
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
<item name="android:paddingLeft">8dp</item>
|
||||
<item name="android:paddingRight">4dp</item>
|
||||
<item name="android:color">@android:color/secondary_text_light</item>
|
||||
</style>
|
||||
|
||||
<style name="TextSmallStats" parent="TextAppearance.AppCompat.Small">
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
<item name="android:paddingLeft">8dp</item>
|
||||
<item name="android:paddingRight">4dp</item>
|
||||
<item name="android:paddingTop">8dp</item>
|
||||
<item name="android:textAlignment">viewEnd</item>
|
||||
<item name="android:color">@android:color/secondary_text_light</item>
|
||||
</style>
|
||||
|
||||
<style name="IconStats">
|
||||
<item name="android:layout_width">48dp</item>
|
||||
<item name="android:layout_height">48dp</item>
|
||||
<item name="tint">?android:textColorPrimary</item>
|
||||
</style>
|
||||
|
||||
<style name="TextMicro" parent="@style/TextSmall">
|
||||
<item name="android:textSize">12sp</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
</style>
|
||||
|
||||
<style name="TextLarge" parent="TextAppearance.AppCompat.Large">
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
</style>
|
||||
|
||||
<style name="TextMedium" parent="TextAppearance.AppCompat.Medium">
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
</style>
|
||||
|
||||
<style name="TextMediumPrimary" parent="@style/TextMedium">
|
||||
<item name="android:textColor">?android:textColorPrimary</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
</style>
|
||||
|
||||
<style name="TextMedium.WithIcon" style="@style/TextMedium">
|
||||
<item name="android:drawablePadding">8dp</item>
|
||||
<item name="android:gravity">center_vertical</item>
|
||||
</style>
|
||||
|
||||
<style name="TextSmall" parent="TextAppearance.AppCompat.Small">
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
</style>
|
||||
|
||||
<style name="TextImportExportSummaryStart" parent="TextMediumPrimary">
|
||||
<item name="android:layout_width">0dp</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_marginTop">12dp</item>
|
||||
<item name="android:layout_marginEnd">8dp</item>
|
||||
<item name="android:layout_marginStart">24dp</item>
|
||||
<item name="android:gravity">end</item>
|
||||
</style>
|
||||
|
||||
<style name="TextImportExportSummaryEnd" parent="TextMediumPrimary">
|
||||
<item name="android:layout_width">0dp</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_marginStart">8dp</item>
|
||||
<item name="android:layout_marginEnd">24dp</item>
|
||||
</style>
|
||||
|
||||
<style name="ProgressBar" parent="@style/Widget.AppCompat.ProgressBar.Horizontal" />
|
||||
|
||||
<!-- Card View for stats -->
|
||||
<style name="StatsCardView">
|
||||
<!-- card view -->
|
||||
<style name="OpenTracksCardView">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="cardElevation">1dp</item>
|
||||
@@ -336,7 +76,7 @@ limitations under the License.
|
||||
|
||||
<!-- Snackbar -->
|
||||
<style name="MaterialSnackbarTheme" parent="@style/Widget.MaterialComponents.Snackbar">
|
||||
<item name="materialThemeOverlay">@style/ThemeOverlay.App.Snackbar</item>
|
||||
<item name="materialThemeOverlay">@style/ThemeOverlay.OpenTracks.Snackbar</item>
|
||||
<item name="actionTextColorAlpha">1</item>
|
||||
|
||||
<item name="backgroundTint">@android:color/holo_red_dark</item>
|
||||
@@ -353,7 +93,7 @@ limitations under the License.
|
||||
<item name="android:textSize">14sp</item>
|
||||
</style>
|
||||
|
||||
<style name="ThemeOverlay.App.Snackbar" parent="">
|
||||
<style name="ThemeOverlay.OpenTracks.Snackbar" parent="">
|
||||
<item name="colorSurface">@android:color/holo_red_dark</item>
|
||||
<item name="colorOnSurface">@android:color/holo_red_dark</item>
|
||||
</style>
|
||||
|
||||
@@ -15,20 +15,41 @@ limitations under the License.
|
||||
-->
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<style name="ThemeCustom" parent="Theme.AppCompat.DayNight.NoActionBar">
|
||||
<!-- <item name="colorPrimary">TODO</item>-->
|
||||
<!-- <item name="colorPrimaryDark">TODO</item>-->
|
||||
<style name="ThemeCustom" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||
<item name="colorPrimary">@color/opentracks_primary_color</item>
|
||||
<item name="colorPrimaryVariant">@color/opentracks_primary_variant_color</item>
|
||||
<item name="colorOnPrimary">@color/opentracks_on_primary_color</item>
|
||||
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
<item name="android:activatedBackgroundIndicator">@drawable/activated_background</item>
|
||||
<item name="colorSecondary">@color/opentracks_secondary_color</item>
|
||||
<item name="colorSecondaryVariant">@color/opentracks_secondary_color</item>
|
||||
<item name="colorOnSecondary">@color/opentracks_on_secondary_color</item>
|
||||
|
||||
<item name="colorError">@color/red_dark</item>
|
||||
<item name="colorOnError">@android:color/white</item>
|
||||
|
||||
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
|
||||
|
||||
<item name="windowActionModeOverlay">true</item>
|
||||
|
||||
<item name="android:colorBackground">@android:color/white</item>
|
||||
<item name="colorOnBackground">@android:color/black</item>
|
||||
|
||||
<item name="colorSurface">@color/opentracks_surface_day_background_color</item>
|
||||
<item name="colorOnSurface">@color/opentracks_surface_day_on_background_color</item>
|
||||
|
||||
<item name="android:activatedBackgroundIndicator">@drawable/activated_background</item>
|
||||
|
||||
<!-- Snackbar -->
|
||||
<item name="snackbarStyle">@style/MaterialSnackbarTheme</item>
|
||||
<item name="snackbarButtonStyle">@style/MaterialSnackbarTextButtonTheme</item>
|
||||
<item name="snackbarTextViewStyle">@style/MaterialSnackbarTextViewTheme</item>
|
||||
|
||||
<!-- Linear progress indicator -->
|
||||
<item name="linearProgressIndicatorStyle">@style/OpenTracksLinearProgressIndicator</item>
|
||||
</style>
|
||||
|
||||
<style name="OpenTracksLinearProgressIndicator" parent="Widget.MaterialComponents.LinearProgressIndicator">
|
||||
<item name="indicatorColor">?attr/colorSecondary</item>
|
||||
</style>
|
||||
|
||||
<style name="SplashTheme" parent="ThemeCustom">
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="TextAppearance.OpenTracks.PrimaryHeader" parent="TextAppearance.MaterialComponents.Subtitle1">
|
||||
<item name="android:textColor">?attr/colorSecondary</item>
|
||||
<item name="android:layout_marginTop">8dp</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="android:textAllCaps">true</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.OpenTracks.SecondaryHeader" parent="TextAppearance.MaterialComponents.Subtitle2">
|
||||
<item name="android:layout_marginTop">8dp</item>
|
||||
<item name="android:textAllCaps">true</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.OpenTracks.HiddenHeader" parent="TextAppearance.MaterialComponents.Subtitle1">
|
||||
<item name="android:textColor">@color/light_gray</item>
|
||||
<item name="android:layout_marginTop">8dp</item>
|
||||
<item name="android:textAllCaps">true</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.OpenTracks.PrimaryValue" parent="TextAppearance.MaterialComponents.Headline4">
|
||||
<item name="android:textColor">?attr/colorOnBackground</item>
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
<item name="android:layout_marginTop">8dp</item>
|
||||
<item name="android:layout_marginBottom">8dp</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.OpenTracks.SecondaryValue" parent="TextAppearance.MaterialComponents.Headline5">
|
||||
<item name="android:textColor">?attr/colorOnBackground</item>
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
<item name="android:layout_marginTop">8dp</item>
|
||||
<item name="android:layout_marginBottom">8dp</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.OpenTracks.HiddenValue" parent="TextAppearance.MaterialComponents.Headline4">
|
||||
<item name="android:textColor">@color/light_gray</item>
|
||||
<item name="android:layout_gravity">center</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
<item name="android:layout_marginTop">8dp</item>
|
||||
<item name="android:layout_marginBottom">8dp</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.OpenTracks.PrimaryUnit" parent="TextAppearance.MaterialComponents.Caption">
|
||||
<item name="android:textColor">?attr/colorOnBackground</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -26,4 +26,9 @@
|
||||
android:title="@string/menu_voice_rate"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<SwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="@string/voice_announce_heart_rate_key"
|
||||
android:title="@string/settings_announcements_report_heart_rate" />
|
||||
|
||||
</PreferenceScreen>
|
||||
Reference in New Issue
Block a user