forked from upstream-mirrors/OpenTracks
Compare commits
115 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3c52a5b38a | |||
| ea53326183 | |||
| 5bea48096c | |||
| c34e9bd7b6 | |||
| edac784b1e | |||
| 87228d2341 | |||
| 00e62b7d6f | |||
| 024e5e0e5f | |||
| 2feb7d1c1e | |||
| 2f29433a09 | |||
| 5ecb058789 | |||
| 820f26cf1d | |||
| d91f59e1ff | |||
| 0b541b7590 | |||
| f3f8075a5e | |||
| e41df73919 | |||
| 0f05100807 | |||
| d16de9789f | |||
| ca809bde6b | |||
| 692650cf99 | |||
| 9bf77c41a0 | |||
| 3986172095 | |||
| 943e05f17d | |||
| 65e7044d49 | |||
| b072635a32 | |||
| 9237693063 | |||
| 4999b54a97 | |||
| 0dca1461e1 | |||
| 2e13054042 | |||
| 3eba204b9d | |||
| 8b8eaa5ea9 | |||
| c0b6b9e5e4 | |||
| da109fb8fe | |||
| fca4206c86 | |||
| a2531c8651 | |||
| 7b4ab81f5c | |||
| 90446639b0 | |||
| 8744724c0f | |||
| 1113cb1764 | |||
| f9466b7347 | |||
| 45be184d76 | |||
| 730ace0b60 | |||
| b662a8bc42 | |||
| a0b4f78977 | |||
| 6334c03dbc | |||
| 57fd2fd160 | |||
| d76131a34f | |||
| 3bae0869c4 | |||
| a164876615 | |||
| 5bbb5f789f | |||
| 6e42857023 | |||
| b883bb3862 | |||
| 9b5ef0707e | |||
| 9c1a44abb4 | |||
| f75ed13e52 | |||
| 5483065d4d | |||
| c1ecebccaa | |||
| 8f130c50ff | |||
| 1da04398f8 | |||
| 8c05be048a | |||
| 77d040d08b | |||
| 07124ad702 | |||
| 9257abb9ec | |||
| fc4e1583b0 | |||
| 4843a2386a | |||
| 65e1ade5d4 | |||
| 2e3c8c8895 | |||
| adef029ce4 | |||
| fd947ece1d | |||
| 08283c6e31 | |||
| 33fff17749 | |||
| 28ceac3ec4 | |||
| 76b4ae69d3 | |||
| fbad0abe40 | |||
| 9df7b8d8dc | |||
| 1fef75e0ab | |||
| 99216a30e4 | |||
| a1f6f11f3f | |||
| 6dfed5ebef | |||
| 60581665b4 | |||
| d07c60fe71 | |||
| 456e985476 | |||
| 2189135add | |||
| d1322a7fb5 | |||
| 84ffb8a642 | |||
| 9638f088fd | |||
| c2cf4edad8 | |||
| 62519cece1 | |||
| 0e3f48b492 | |||
| 9886db98a7 | |||
| c6b27bc7ae | |||
| 6512547f07 | |||
| a7f71d8902 | |||
| ce4845d1b3 | |||
| 4f68b72333 | |||
| a8a7c2488a | |||
| 5f983ec5e6 | |||
| 9253eb5340 | |||
| 7afc5cc66a | |||
| 09fe79771f | |||
| 6baa816156 | |||
| 0d632f8125 | |||
| a81a63a2ad | |||
| 6656c9ab92 | |||
| af6baeab9f | |||
| 9b4d3a4718 | |||
| 942b29aa25 | |||
| 18e18353e7 | |||
| 8c8fe9f22d | |||
| 648c106f7f | |||
| bb86ab927c | |||
| 9150318ef9 | |||
| b98a0a0283 | |||
| 71f1049642 | |||
| 6185154af9 |
Generated
+35
@@ -1,5 +1,40 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<JavaCodeStyleSettings>
|
||||
<option name="IMPORT_LAYOUT_TABLE">
|
||||
<value>
|
||||
<package name="" withSubpackages="true" static="false" module="true" />
|
||||
<package name="android" withSubpackages="true" static="true" />
|
||||
<package name="androidx" withSubpackages="true" static="true" />
|
||||
<package name="com" withSubpackages="true" static="true" />
|
||||
<package name="junit" withSubpackages="true" static="true" />
|
||||
<package name="net" withSubpackages="true" static="true" />
|
||||
<package name="org" withSubpackages="true" static="true" />
|
||||
<package name="java" withSubpackages="true" static="true" />
|
||||
<package name="javax" withSubpackages="true" static="true" />
|
||||
<package name="" withSubpackages="true" static="true" />
|
||||
<emptyLine />
|
||||
<package name="android" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="androidx" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="com" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="junit" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="net" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="org" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="java" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="javax" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
</value>
|
||||
</option>
|
||||
</JavaCodeStyleSettings>
|
||||
<JetCodeStyleSettings>
|
||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||
<value>
|
||||
|
||||
@@ -100,8 +100,8 @@ __Only required permissions:__
|
||||
|
||||
### Public API
|
||||
|
||||
OpenTracks includes an API for starting/stopping recording by another installed application (e.g., [Automate](https://llamalab.com/automate/), [Tasker](https://tasker.joaoapps.com), or [Easer](https://github.com/renyuneyun/Easer)).
|
||||
The API can be invoked by sending an explicit Intent to start an activity.
|
||||
OpenTracks includes an API for starting/stopping recording by another installed application (e.g., [Automate](https://llamalab.com/automate/), [Tasker](https://tasker.joaoapps.com), or [Easer](https://github.com/renyuneyun/Easer)).
|
||||
The API is disabled by default to protect the user's privacy, but it can easily be enabled in the settings. Once enabled, the API can be invoked by sending an explicit Intent to start an activity.
|
||||
|
||||
`Package` (depends on the variant installed):
|
||||
* F-Droid: `de.dennisguse.opentracks`
|
||||
@@ -121,9 +121,17 @@ The API can be invoked by sending an explicit Intent to start an activity.
|
||||
* **Stop a recording:** `de.dennisguse.opentracks.publicapi.StopRecording`
|
||||
* **Create a marker:** `de.dennisguse.opentracks.publicapi.CreateMarker`
|
||||
|
||||
For testing via adb: `adb shell am start -e someParameter someValue -n "package/class"`
|
||||
For testing the API using adb, the general command syntax is:
|
||||
|
||||
The Public API is disabled by default to protect the user's privacy, but it can easily be enabled in the settings.
|
||||
```shell
|
||||
adb shell am start -e someParameter someValue -n "package/class"
|
||||
```
|
||||
|
||||
Depending on the package and class, a complete command could look something like this:
|
||||
|
||||
```shell
|
||||
adb shell am start -n "de.dennisguse.opentracks.playstore/de.dennisguse.opentracks.publicapi.StartRecording"
|
||||
```
|
||||
|
||||
## File formats compatibility with open-source software
|
||||
| | [GPX 1.1](https://www.topografix.com/GPX/1/1/) | [KML 2.3](https://docs.opengeospatial.org/is/12-007r2/12-007r2.html) | [KMZ 2.3](https://docs.opengeospatial.org/is/12-007r2/12-007r2.html) |
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* Amazfit Neo. But first you have to enable "Discoverable" and "Activity heart rate sharing" in the official Zepp app. (after pairing with OpenTracks you can disable them again)
|
||||
* Garmin HRM-Dual (Reference: 010-12883-00)
|
||||
* CYCPLUS H1
|
||||
* Mojawa Haptifit Terra - all you have to do is connect the app to device called "HT"
|
||||
|
||||
## 0x1816: Cycling Cadence and Speed Service
|
||||
|
||||
|
||||
+7
-7
@@ -6,7 +6,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.9.1'
|
||||
classpath 'com.android.tools.build:gradle:8.11.0'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId 'de.dennisguse.opentracks'
|
||||
versionCode 6102
|
||||
versionName "v4.19.0"
|
||||
versionCode 6233
|
||||
versionName "v4.21.0"
|
||||
|
||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||
|
||||
@@ -142,15 +142,15 @@ android {
|
||||
dependencies {
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.7.1'
|
||||
implementation 'androidx.preference:preference:1.2.1'
|
||||
implementation 'androidx.documentfile:documentfile:1.0.1'
|
||||
implementation 'androidx.documentfile:documentfile:1.1.0'
|
||||
implementation 'androidx.gridlayout:gridlayout:1.1.0'
|
||||
implementation 'com.google.android.material:material:1.12.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
|
||||
implementation 'androidx.core:core:1.16.0'
|
||||
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||
implementation 'androidx.mediarouter:mediarouter:1.7.0'
|
||||
implementation 'androidx.mediarouter:mediarouter:1.8.1'
|
||||
implementation 'androidx.core:core-location-altitude:1.0.0-alpha03'
|
||||
|
||||
androidTestImplementation 'androidx.test:core:1.6.1'
|
||||
@@ -158,7 +158,7 @@ dependencies {
|
||||
androidTestImplementation 'androidx.test:rules:1.6.1'
|
||||
androidTestImplementation 'androidx.test:runner:1.6.2'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
|
||||
androidTestImplementation 'org.mockito:mockito-android:5.17.0'
|
||||
androidTestImplementation 'org.mockito:mockito-android:5.18.0'
|
||||
|
||||
androidTestUtil 'androidx.test:orchestrator:1.5.1'
|
||||
}
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="210mm"
|
||||
height="297mm"
|
||||
viewBox="0 0 210 297"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
|
||||
sodipodi:docname="ic_marker_orange_pushpin_with_shadow.svg">
|
||||
<defs
|
||||
id="defs2">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient871"
|
||||
|
||||
x1="91.712517"
|
||||
y1="266.48581"
|
||||
x2="95.902588"
|
||||
y2="266.72339"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="rotate(22.027833,208.46275,168.26892)"
|
||||
|
||||
|
||||
>
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop867" />
|
||||
<stop
|
||||
style="stop-color:#898d8d;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop869" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.49497475"
|
||||
inkscape:cx="298.98541"
|
||||
inkscape:cy="561.0427"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-rotation="0"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1012"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="1080"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Ebene 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<path
|
||||
d="m 108.52826,213.29755 a 56.035402,7.0061195 5.281088 0 0 -22.425508,2.5321 56.035402,7.0061195 5.281088 0 0 -0.233576,0.17697 l -0.206706,-0.0237 c -0.04471,0.11554 -0.07454,0.2314 -0.09612,0.34752 a 56.035402,7.0061195 5.281088 0 0 0.337963,1.58159 c 0.980726,1.70884 3.760629,3.46326 6.583579,5.2188 l -22.744347,15.50009 a 67.064653,11.556784 4.2275244 0 0 -39.967588,5.26867 67.064653,11.556784 4.2275244 0 0 -0.729154,0.57564 67.064653,11.556784 4.2275244 0 0 -2.754353,1.36358 67.064653,11.556784 4.2275244 0 0 38.908736,16.04324 l -32.176847,27.0646 52.228334,-24.74551 a 67.064653,11.556784 4.2275244 0 0 71.411147,-3.8186 67.064653,11.556784 4.2275244 0 0 0.72812,-0.57507 67.064653,11.556784 4.2275244 0 0 2.75539,-1.36415 67.064653,11.556784 4.2275244 0 0 -12.38374,-10.59818 40.033569,5.0733922 5.2502179 0 0 0.0563,-0.0369 40.033569,5.0733922 5.2502179 0 0 0.37672,-0.34325 l 22.45186,-15.30075 c 8.23597,-0.65161 17.65581,-1.47811 22.94433,-3.00957 a 56.035402,7.0061195 5.281088 0 0 2.65514,-1.03772 56.035402,7.0061195 5.281088 0 0 -46.08401,-12.26953 56.035402,7.0061195 5.281088 0 0 -41.6357,-2.54991 z"
|
||||
style="fill:#333333;fill-opacity:0.49421525;stroke:#000000;stroke-width:0.138931px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.265501"
|
||||
id="path839" />
|
||||
<path
|
||||
style="fill:url(#linearGradient871);fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.265501"
|
||||
d="M 70.603317,174.30832 33.024148,288.94657 94.566672,183.19473 Z"
|
||||
id="path865"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<ellipse
|
||||
cx="144.42586"
|
||||
cy="113.06232"
|
||||
rx="70.318817"
|
||||
ry="39.974442"
|
||||
id="ellipse26"
|
||||
style="fill:#d45500;fill-opacity:1;fill-rule:evenodd;stroke-width:0.264583"
|
||||
transform="rotate(22.027833)" />
|
||||
<ellipse
|
||||
style="fill:#ff6600;fill-opacity:1;fill-rule:evenodd;stroke-width:0.264583"
|
||||
id="ellipse30"
|
||||
ry="39.974442"
|
||||
rx="70.318817"
|
||||
cy="105.23625"
|
||||
cx="145.017"
|
||||
transform="rotate(22.027833)" />
|
||||
<path
|
||||
id="rect28"
|
||||
style="fill:#d45500;fill-opacity:1;fill-rule:evenodd;stroke-width:0.264583"
|
||||
d="m 85.662408,24.322061 c -0.890996,8.350369 2.928035,17.133472 6.825491,25.923906 l -23.333902,57.672763 -0.730686,1.80598 0.248621,0.10059 a 42.43035,17.360788 22.027833 0 0 33.303948,30.10067 42.43035,17.360788 22.027833 0 0 44.86507,1.52585 l 0.13892,0.0562 0.24519,-0.60599 a 42.43035,17.360788 22.027833 0 0 0.59472,-1.15581 42.43035,17.360788 22.027833 0 0 0.37668,-1.24514 l 22.45185,-55.492655 c 9.28986,-2.66566 20.09069,-6.13552 24.76397,-13.17485 z" />
|
||||
<ellipse
|
||||
style="fill:#ff6600;fill-opacity:1;fill-rule:evenodd;stroke-width:0.264583"
|
||||
id="path24"
|
||||
ry="23.966745"
|
||||
rx="59.409279"
|
||||
cy="-10.262156"
|
||||
cx="148.14029"
|
||||
transform="rotate(22.027833)" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 5.0 KiB |
@@ -0,0 +1,15 @@
|
||||
الإصدار 3.0.0: OpenTracks
|
||||
|
||||
الإصدار الأول من OpenTracks (أي، تفرع صلب من MyTracks)
|
||||
|
||||
التغييرات:
|
||||
- تغيير العلامة التجارية من MyTracks إلى OpenTracks
|
||||
- إضافة مستشعر معدل ضربات القلب بتقنية Bluetooth LE
|
||||
- إزالة جميع الميزات التي تتطلب الوصول إلى الإنترنت
|
||||
- إزالة تكامل خرائط جوجل
|
||||
- تمت إزالة استخدام خدمات Google Play
|
||||
- تمت إزالة مستشعرات ANT+
|
||||
- تمت إزالة مستشعرات البلوتوث الكلاسيكية
|
||||
- تمت إزالة بروتوبوف
|
||||
- تمت إزالة تقدير السعرات الحرارية وتقدير النشاط
|
||||
- تمت إزالة واجهة برمجة تطبيقات MyTracks Service API (أي لا يوجد تكامل مع تطبيقات أخرى)
|
||||
@@ -0,0 +1,17 @@
|
||||
v3.0.1: OpenTracks
|
||||
|
||||
التغييرات:
|
||||
- علامات المشاركة
|
||||
- إعادة هيكلة الإعدادات
|
||||
- تمت إزالة زر الرجوع إلى شريط العمل
|
||||
- تصدير: تمت إزالة خيار إعدادات حجم الصورة
|
||||
- تصدير: KML كتنسيق قابل للتحديد الآن
|
||||
|
||||
إصلاحات الأخطاء:
|
||||
- أصبح دليل التصدير الآن /OpenTracks/
|
||||
- الوضع الأفقي لا يتعطل
|
||||
- تم إصلاح تصدير KML/KMZ
|
||||
|
||||
المطور:
|
||||
- الاختبار قابل للتنفيذ (لكن العديد منها يفشل)
|
||||
- تمكين التكامل المستمر مع TravisCI (لا توجد اختبارات)
|
||||
@@ -0,0 +1,14 @@
|
||||
v3.1.0: OpenTracks
|
||||
|
||||
التغييرات:
|
||||
- تمت إضافة إظهار المسار في تطبيقات الخرائط (تم اختباره مع OsmAnd وMaps.ME)
|
||||
- تمت الإضافة أثناء التتبع، إظهار إحصائيات المسار دون فتح الجهاز
|
||||
- تمت إزالة الوضع الأفقي لعرض تفاصيل المسار (كان به أخطاء)
|
||||
- تمت إزالة سجل اقتراحات البحث
|
||||
|
||||
إصلاح الخلل:
|
||||
- طلب إذن تخزين لاستيراد الكل/تصدير الكل
|
||||
|
||||
مطور:
|
||||
- الترقية إلى Gradle 5.6
|
||||
- عدم تصغير بنيات الإصدار
|
||||
@@ -0,0 +1,22 @@
|
||||
رفيق تتبع الرياضة الذي يحترم خصوصيتك.
|
||||
|
||||
الرياضة والأنشطة الخارجية تمنح السعادة والمهارة والثقة بالنفس.
|
||||
|
||||
قدّر صحتك من خلال تتبع تمارينك الرياضية.
|
||||
يسجل أثناء الجري أو المشي، ويمنحك كمبيوتر للدراجات بشاشة أكبر لركوب الدراجات.
|
||||
حدد المواقع المثيرة للاهتمام على طول طريقك بالصور.
|
||||
احتفظ بالإحصائيات المسجلة بتفاصيل كبيرة لتحليلها.
|
||||
شارك فقط البيانات التي تريد أن يحصل عليها الآخرون.
|
||||
|
||||
* إعلانات صوتية.
|
||||
* يدعم مستشعرات Bluetooth LE: معدل ضربات القلب والسرعة والمسافة (ركوب الدراجات)، والإيقاع (ركوب الدراجات)، ومقياس الطاقة (ركوب الدراجات).
|
||||
* كسب الارتفاع وفقدان الارتفاع: عبر مستشعر بارومتري.
|
||||
* يظهر الارتفاع بالارتفاع EGM2008 (فوق متوسط مستوى سطح البحر)؛ يتم تصديره على هيئة WGS84.
|
||||
* تصدير البيانات كمسارات إما على هيئة KMZ (بما في ذلك الصور) أو KML أو GPX.
|
||||
* لا يوجد وصول إلى الإنترنت أو أذونات إضافية.
|
||||
* سمة داكنة وفاتحة، مع احترام إعدادات النظام.
|
||||
* لا إعلانات.
|
||||
|
||||
برنامج ليبر / برنامج مجاني / مفتوح المصدر
|
||||
بمعنى أنه يمكنك استخدام ودراسة وتغيير ومشاركة شفرة المصدر.
|
||||
مرخص من أباتشي 2.0
|
||||
@@ -1 +1 @@
|
||||
تعقب الرياضة التي تحترم تماما خصوصيتك.
|
||||
رفيق تعقّب رياضي يحترم خصوصيتك.
|
||||
|
||||
@@ -1 +1 @@
|
||||
OpenTracks
|
||||
المسارات المفتوحة
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
v4.19.0: OpenTracks
|
||||
|
||||
Změny:
|
||||
- Export GPX nebyl kompatibilní (pořadí a obsah rozšíření TrackPointExtension)
|
||||
|
||||
Oprava chyb:
|
||||
- Před verzí 4.18.0 nebylo možné odstranit značky bez obrázků
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.19.1: OpenTracks
|
||||
|
||||
Oprava chyby:
|
||||
- Exportovaným souborům GPX chyběla jedna schemaLocation
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.19.2: OpenTracks
|
||||
|
||||
Oprava chyby:
|
||||
- Opraven pád z důvodu problémových překladů
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.19.3: OpenTracks
|
||||
|
||||
Oprava chyby:
|
||||
- Opraven export CSV pro nadmořskou výšku (byla uříznuta v 1000 m)
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.19.4: OpenTracks
|
||||
|
||||
Oprava chyb:
|
||||
- Edge-to-edge (SDK35): použito na všechny verze Androidu (určitý pokrok, ale stále se na tom pracuje)
|
||||
- KMZ s obrázky nebylo možné exportovat (záhlaví nebylo možné vypočítat; funkce odstraněna)
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.19.5: OpenTracks
|
||||
|
||||
Oprava chyby:
|
||||
- Ikona v launcheru byla někdy příliš malá
|
||||
- Ikona trasy v podrobnostech používá lokalizovaný typ aktivity
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.19.6: OpenTracks
|
||||
|
||||
Změny:
|
||||
- Podpora edge-to-edge by nyní měla fungovat na všech verzích systému Android
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.20.0: OpenTracks
|
||||
|
||||
Změny:
|
||||
- Umožnění změny časového pásma stop
|
||||
- Zjednodušené rozhraní značek
|
||||
|
||||
Oprava chyby:
|
||||
- Edge-2-edge: reakce na virtuální klávesnici
|
||||
@@ -1,10 +1,22 @@
|
||||
OpenTracks – ein GPS-Tracker, der deine Daten für sich behält.
|
||||
Erfasst deine Kilometer, ohne dass es jemand anderes tut.
|
||||
|
||||
Funktionen & Features:
|
||||
* zeichnet Sport und andere Outdooraktivitäten auf
|
||||
* unterstützt das Markieren interessanter Orte, inkl. Foto-Funktion
|
||||
* zeichnet deine Herzfrequenz auf (Bluetooth LE)
|
||||
* nutze beim Radfahren deine Trittfrequenz- und Geschwindigkeitssensoren sowie Leistungsmesser (Bluetooth LE)
|
||||
* exportiert deine Daten als KML oder GPX
|
||||
* funktioniert ganz ohne Internet
|
||||
Sport und Aktivitäten im Freien machen Spaß und bieten Training und mehr Selbstvertrauen.
|
||||
|
||||
Achte auf deine Gesundheit, indem du dein Training im Blick behältst.
|
||||
Die App zeichnet während dem Laufen oder Spazieren mit und kann auch als Fahrradcomputer genutzt werden.
|
||||
Interessante Orte können am Weg entlang markiert und mit Fotos versehen werden.
|
||||
Messdaten können zur Analyse detailreich gespeichert werden.
|
||||
Du entscheidest, welche Daten du mit anderen Menschen teilst.
|
||||
|
||||
* Gesprochene Ansagen
|
||||
* Unterstützt Bluetooth LE Sensoren: Herzschlagfrequenz, sowie Geschwindigkeit, Distanz, Kadenz und Leistungsmessung beim Fahrradfahren.
|
||||
* Höhenmessung mit einem eingebauten Barometer.
|
||||
* Anzeige der Höhenmessung in EGM2008 (über dem durchschnittlichen Meeresspiegel); Export als WGS84.
|
||||
* Export deiner Streckendaten als KMZ (inkl. Fotos), KML oder GPX.
|
||||
* Kein Internet oder zusätzliche Berechtigungen nötig.
|
||||
* Dunkler und heller Modus, sowie automatische Anpassung an das System.
|
||||
* Keine Werbung.
|
||||
|
||||
Freie Software / Open Source
|
||||
Das bedeutet, dass du den Quellcode nutzen, inspizieren, verändern und teilen darfst.
|
||||
Lizensiert unter Apache 2.0
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.19.1: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Exported GPX files missed one schemaLocation
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.19.2: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Fix crash due to faulty translations
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.19.3: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Fix CSV export for altitude (was cut at 1000m)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.19.4: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Edge-to-edge (SDK35): applied to all version of Android (some progress, but still work in progress)
|
||||
- KMZ with pictures could not be exported (heading not computable; feature removed)
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.19.5: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Launcher icon was sometimes shown too small
|
||||
- Track icon on detail page using localized activity type
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.19.6: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Edge-2-edge should now be working on all Android versions
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.20.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Allow to change timezone offset of tracks
|
||||
- Simplified Marker UI
|
||||
|
||||
Bugfix:
|
||||
- Edge-2-edge: react to virtual keyboard
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.21.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Track list supports swipe-to-delete
|
||||
- Re-implemented themes (for DynamicColor and OLED)
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.19.2 : OpenTracks
|
||||
|
||||
Correction de bugs :
|
||||
- Correction d'un crash dû à des traductions défectueuses
|
||||
@@ -0,0 +1,7 @@
|
||||
v4.19.0: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- níor chomhlíon easpórtáil GPX (Ordú agus inneachar TrackPointExtension)
|
||||
|
||||
Ceartúchán Bug:
|
||||
- níorbh fhéidir marcóirí réamh-v4.18.0 gan pictiúir a scriosadh
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.19.1: OpenTracks
|
||||
|
||||
Ceartú fabht:
|
||||
- Chaill comhaid GPX onnmhairithe suíomh scéime amháin
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.19.2: OpenTracks
|
||||
|
||||
Ceartú fabht:
|
||||
- Deisíodh tuairteáil mar gheall ar aistriúcháin lochtacha
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.19.3: OpenTracks
|
||||
|
||||
Ceartú fabht:
|
||||
- Deisíodh easpórtáil CSV le haghaidh airde (gearradh ag 1000m é)
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.19.4: OpenTracks
|
||||
|
||||
Ceartú fabht:
|
||||
- Imeall go himeall (SDK35): curtha i bhfeidhm ar gach leagan de Android (roinnt dul chun cinn, ach obair ar siúl fós)
|
||||
- Níorbh fhéidir KMZ le pictiúir a easpórtáil (níorbh fhéidir an ceannteideal a ríomh; baineadh an ghné)
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.19.5: OpenTracks
|
||||
|
||||
Ceartú fabht:
|
||||
- Taispeánadh deilbhín an lainseálaí róbheag uaireanta
|
||||
- Deilbhín an rianaithe ar an leathanach sonraí ag baint úsáide as cineál gníomhaíochta logánaithe
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.19.6: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- Ba chóir go mbeadh Edge-2-edge ag obair anois ar gach leagan de Android
|
||||
@@ -0,0 +1,4 @@
|
||||
V4.17.8: Opentracks
|
||||
|
||||
மாற்றங்கள்:
|
||||
- விளக்கப்படங்கள்: காட்டப்பட்ட தரவை உள்ளமைக்க முடியும்
|
||||
@@ -0,0 +1,11 @@
|
||||
v4.18.0: ஓபன்ட்ராக்ச்
|
||||
|
||||
மாற்றங்கள்:
|
||||
- சி.பி.எச் முடக்கப்பட்ட எச்சரிக்கையை முடக்கலாம்
|
||||
|
||||
பிழைத்திருத்த:
|
||||
- பாட்டம்அப்ப்பார் உள்ளடக்கத்தை ஒன்றுடன் ஒன்று
|
||||
|
||||
டெவலப்பர்:
|
||||
- ஆண்ட்ராய்டு இருப்பிடத்திற்கு பதிலாக பதிவு நிலையைப் பயன்படுத்தவும்
|
||||
- மறுசீரமைக்கப்பட்ட சென்சார் திரட்டிகள்
|
||||
@@ -0,0 +1,7 @@
|
||||
V4.19.0: Opentracks
|
||||
|
||||
மாற்றங்கள்:
|
||||
- சிபிஎக்ச் ஏற்றுமதி இணக்கமாக இல்லை (டிராக் பாயிண்ட் எக்ச்டென்சன் ஆர்டர் மற்றும் உள்ளடக்கம்)
|
||||
|
||||
பிழைத்திருத்த:
|
||||
- முன்-V4.18.0 படங்கள் இல்லாத குறிப்பான்களை நீக்க முடியவில்லை
|
||||
@@ -0,0 +1,4 @@
|
||||
V4.19.1: Opentracks
|
||||
|
||||
பிழைத்திருத்த:
|
||||
- ஏற்றுமதி செய்யப்பட்ட சிபிஎக்ச் கோப்புகள் ஒரு திட்டத்தை தவறவிட்டன
|
||||
@@ -0,0 +1,4 @@
|
||||
V4.19.2: Opentracks
|
||||
|
||||
பிழைத்திருத்த:
|
||||
- தவறான மொழிபெயர்ப்புகள் காரணமாக விபத்தை சரிசெய்யவும்
|
||||
@@ -0,0 +1,4 @@
|
||||
V4.19.3: Opentracks
|
||||
|
||||
பிழைத்திருத்த:
|
||||
- உயரத்திற்கு காபிம ஏற்றுமதியை சரிசெய்யவும் (1000 மீ.
|
||||
@@ -0,0 +1,5 @@
|
||||
V4.19.4: Opentracks
|
||||
|
||||
பிழைத்திருத்த:
|
||||
-எட்ச்-டு-எட்ச் (SDK35): ஆண்ட்ராய்டு இன் அனைத்து பதிப்பிற்கும் பயன்படுத்தப்பட்டது (சில முன்னேற்றம், ஆனால் இன்னும் முன்னேற்றத்தில் உள்ளது)
|
||||
- படங்களுடன் KMZ ஏற்றுமதி செய்ய முடியவில்லை (தலைப்பு கணக்கிட முடியாதது; நற்பொருத்தம் அகற்றப்பட்டது)
|
||||
@@ -0,0 +1,7 @@
|
||||
v3.2.1: OpenTracks
|
||||
|
||||
Değişiklikler:
|
||||
- Ayarlar artık tek bir sayfada
|
||||
|
||||
Gelişim:
|
||||
- Androidx Compat'a taşınan ayarlar (parçaları kullanır)
|
||||
@@ -0,0 +1,10 @@
|
||||
v4.17.5: Opentracks
|
||||
|
||||
Değişiklikler:
|
||||
- Tekrarlanabilir yapının: işe yarayıp yaramadığını görelim.
|
||||
|
||||
Bugfix:
|
||||
- Tekrarlanabilir yapı PlayStore simgesini kullanır.
|
||||
|
||||
Geliştirici:
|
||||
- RELEASE_BUILD.sh eklendi
|
||||
@@ -0,0 +1,11 @@
|
||||
v4.18.0: Opentracks
|
||||
|
||||
Değişiklikler:
|
||||
- GPS devre dışı bırakılmış uyarısı devre dışı bırakılabilir
|
||||
|
||||
Bugfix:
|
||||
- Üst üste binen BottomAppBar düzeltildi
|
||||
|
||||
Gelişim:
|
||||
- Android konumu yerine kayıt konumunu kullanır
|
||||
- Refactored sensör toplayıcıları
|
||||
@@ -0,0 +1,7 @@
|
||||
v4.19.0: Opentracks
|
||||
|
||||
Değişiklikler:
|
||||
- GPX dışa aktarma uyumlu değildi (TrackPointExtension sıra ve içerik)
|
||||
|
||||
Bugfix:
|
||||
- pre-v4.18.0 öncesi resimsiz işaretçilerin silinmemesi düzeltildi
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.19.2: Opentracks
|
||||
|
||||
Bugfix:
|
||||
- Hatalı çeviriler nedeniyle çökme sorunu düzeltildi
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -86,8 +86,7 @@ done
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
+2
-2
@@ -763,8 +763,8 @@ public class CustomContentProviderUtilsTest {
|
||||
TrackPoint trackPoint = contentProviderUtils.createTrackPoint(cursorMock);
|
||||
|
||||
// then
|
||||
assertEquals(longitude, trackPoint.getLongitude(), 0.01);
|
||||
assertEquals(latitude, trackPoint.getLatitude(), 0.01);
|
||||
assertEquals(latitude, trackPoint.getPosition().latitude(), 0.01);
|
||||
assertEquals(longitude, trackPoint.getPosition().longitude(), 0.01);
|
||||
assertEquals(time, trackPoint.getTime().toEpochMilli());
|
||||
assertEquals(speed, trackPoint.getSpeed().toMPS(), 0.01);
|
||||
assertEquals(HeartRate.of(75f), trackPoint.getHeartRate());
|
||||
|
||||
+63
-40
@@ -18,6 +18,7 @@ import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
import androidx.test.rule.ServiceTestRule;
|
||||
|
||||
import org.junit.After;
|
||||
@@ -45,6 +46,7 @@ import java.util.concurrent.TimeoutException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.TestUtil;
|
||||
import de.dennisguse.opentracks.TimezoneRule;
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
@@ -87,6 +89,9 @@ public class ExportImportTest {
|
||||
@Rule
|
||||
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule();
|
||||
|
||||
//For csv_export_only() as we the timezone is hardcoded in the expectation.
|
||||
@Rule
|
||||
public TimezoneRule timezoneRule = new TimezoneRule(TimeZone.getTimeZone("Europe/Berlin"));
|
||||
@@ -144,7 +149,7 @@ public class ExportImportTest {
|
||||
|
||||
Distance sensorDistance = Distance.of(10); // recording distance interval
|
||||
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3, 14, 10, 13, 15, 10, 1f);
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3.1234567, 14.0014567, 10, 13, 15, 1020.25, 1f);
|
||||
contentProviderUtils.insertMarker(new Marker(trackId, service.getLastStoredTrackPointWithLocation(), "Marker 1", "Marker 1 desc", "Marker 1 category", null, null));
|
||||
|
||||
// A sensor-only TrackPoint
|
||||
@@ -155,11 +160,10 @@ public class ExportImportTest {
|
||||
mockSensorData(trackPointCreator, 15f, null, 67f, 3f, 50f, null);
|
||||
trackPointCreator.setClock("2020-02-02T02:02:15Z");
|
||||
mockSensorData(trackPointCreator, null, null, 68f, 3f, 50f, null);
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:02:16Z");
|
||||
mockSensorData(trackPointCreator, 5f, Distance.of(2), 69f, 3f, 50f, null); // Distance will be added to next TrackPoint
|
||||
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:02:17Z", 3, 14.001, 10, 13, 15, 10, 0f);
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:02:17Z", 3.1234567, 14.0014567, 10, 13, 15, 1020.25, 0f);
|
||||
contentProviderUtils.insertMarker(new Marker(trackId, service.getLastStoredTrackPointWithLocation(), "Marker 2", "Marker 2 desc", "Marker 2 category", null, null));
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:02:18Z");
|
||||
@@ -169,14 +173,14 @@ public class ExportImportTest {
|
||||
trackPointCreator.setClock("2020-02-02T02:03:20Z");
|
||||
service.resumeTrack(trackId);
|
||||
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:03:21Z", 3, 14.002, 10, 13, 15, 10, 0f);
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:03:21Z", 3.1234567, 14.0024567, 10, 13, 15, 999.123, 0f);
|
||||
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:03:22Z", 3, 16, 10, 13, 15, 10, 0f);
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:03:22Z", 3.1234567, 16, 10, 13, 15, 999.123, 0f);
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:03:30Z");
|
||||
service.getTrackRecordingManager().onIdle();
|
||||
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:03:50Z", 3, 16.001, 10, 27, 15, 10, 0f);
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:03:50Z", 3.1234567, 16.001, 10, 27, 15, 999.123, 0f);
|
||||
|
||||
trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet(trackPointCreator);
|
||||
trackPointCreator.setClock("2020-02-02T02:04:00Z");
|
||||
@@ -209,16 +213,16 @@ public class ExportImportTest {
|
||||
assertEquals(Duration.ofSeconds(26), trackStatistics.getMovingTime()); //TODO Likely too low
|
||||
|
||||
// Distance
|
||||
assertEquals(222125.53125, trackStatistics.getTotalDistance().toM(), 0.01); //TODO Too low
|
||||
assertEquals(222049.34375, trackStatistics.getTotalDistance().toM(), 0.01); //TODO Too low
|
||||
|
||||
// Speed
|
||||
assertEquals(8543.29, trackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(3966.52, trackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(8543.28, trackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||
assertEquals(8540.359, trackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(3965.166, trackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(8540.359, trackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||
|
||||
// Altitude
|
||||
assertEquals(10, trackStatistics.getMinAltitude(), 0.01);
|
||||
assertEquals(10, trackStatistics.getMaxAltitude(), 0.01);
|
||||
assertEquals(999.122, trackStatistics.getMinAltitude(), 0.01);
|
||||
assertEquals(1020.25, trackStatistics.getMaxAltitude(), 0.01);
|
||||
|
||||
assertEquals(2, trackStatistics.getTotalAltitudeGain(), 0.01);
|
||||
assertEquals(2, trackStatistics.getTotalAltitudeLoss(), 0.01);
|
||||
@@ -229,8 +233,8 @@ public class ExportImportTest {
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:03Z"),
|
||||
3d, 14d, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
3.123456, 14.001456, Distance.of(10),
|
||||
Altitude.WGS84.of(1020.25), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeLoss(1f)
|
||||
@@ -251,8 +255,8 @@ public class ExportImportTest {
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:17Z"),
|
||||
3d, 14.001, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
3.123456, 14.001456, Distance.of(10),
|
||||
Altitude.WGS84.of(1020.25), null,
|
||||
null,
|
||||
Speed.of(5)))
|
||||
.setSensorDistance(Distance.of(2))
|
||||
@@ -266,16 +270,16 @@ public class ExportImportTest {
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:21Z"),
|
||||
3d, 14.002d, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
3.123456, 14.002456, Distance.of(10),
|
||||
Altitude.WGS84.of(999.1229858398438), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGain(0f),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(Instant.parse("2020-02-02T02:03:22Z"),
|
||||
3d, 16d, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
3.123456, 16d, Distance.of(10),
|
||||
Altitude.WGS84.of(999.1229858398438), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeLoss(0f)
|
||||
@@ -286,8 +290,8 @@ public class ExportImportTest {
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:50Z"),
|
||||
3d, 16.001, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
3.123456, 16.001, Distance.of(10),
|
||||
Altitude.WGS84.of(999.1229858398438), null,
|
||||
null, Speed.of(15)))
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGain(0f),
|
||||
@@ -334,7 +338,26 @@ public class ExportImportTest {
|
||||
|
||||
// Time
|
||||
assertEquals(track.getZoneOffset(), importedTrack.getZoneOffset());
|
||||
assertEquals(track.getTrackStatistics(), importedTrackStatistics);
|
||||
assertEquals(Instant.parse("2020-02-02T02:02:02Z"), importedTrackStatistics.getStartTime());
|
||||
assertEquals(Instant.parse("2020-02-02T02:04:00Z"), importedTrackStatistics.getStopTime());
|
||||
|
||||
assertEquals(Duration.ofSeconds(56), importedTrackStatistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(26), importedTrackStatistics.getMovingTime());
|
||||
|
||||
// Distance
|
||||
assertEquals(222049.421, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
||||
|
||||
// Speed
|
||||
assertEquals(8540.362, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(3965.168, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(8540.362, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||
|
||||
// Altitude
|
||||
assertEquals(999.122, importedTrackStatistics.getMinAltitude(), 0.01);
|
||||
assertEquals(1020.25, importedTrackStatistics.getMaxAltitude(), 0.01);
|
||||
assertEquals(2, importedTrackStatistics.getTotalAltitudeGain(), 0.01);
|
||||
assertEquals(2, importedTrackStatistics.getTotalAltitudeLoss(), 0.01);
|
||||
|
||||
|
||||
// 4. markers
|
||||
assertMarkers();
|
||||
@@ -406,8 +429,8 @@ public class ExportImportTest {
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:03Z"),
|
||||
3d, 14d, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
3.123456, 14.001456d, Distance.of(10),
|
||||
Altitude.WGS84.of(1020.2), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeLoss(1f)
|
||||
@@ -415,8 +438,8 @@ public class ExportImportTest {
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:17Z"),
|
||||
3d, 14.001, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
3.123456, 14.001456, Distance.of(10),
|
||||
Altitude.WGS84.of(1020.2), null,
|
||||
null,
|
||||
Speed.of(5)))
|
||||
.setAltitudeLoss(1f)
|
||||
@@ -428,8 +451,8 @@ public class ExportImportTest {
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:21Z"),
|
||||
3d, 14.002, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
3.123456, 14.002456, Distance.of(10),
|
||||
Altitude.WGS84.of(999.0999755859375), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeLoss(0f)
|
||||
@@ -437,8 +460,8 @@ public class ExportImportTest {
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:22Z"),
|
||||
3d, 16d, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
3.123456, 16d, Distance.of(10),
|
||||
Altitude.WGS84.of(999.0999755859375), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeLoss(0f)
|
||||
@@ -446,8 +469,8 @@ public class ExportImportTest {
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:50Z"),
|
||||
3d, 16.001, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
3.123456, 16.001, Distance.of(10),
|
||||
Altitude.WGS84.of(999.0999755859375), null,
|
||||
null,
|
||||
Speed.of(10)))
|
||||
.setAltitudeLoss(0f)
|
||||
@@ -467,16 +490,16 @@ public class ExportImportTest {
|
||||
assertEquals(Duration.ofSeconds(107), importedTrackStatistics.getMovingTime());
|
||||
|
||||
// Distance
|
||||
assertEquals(222347.85, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
||||
assertEquals(222271.734, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
||||
|
||||
// Speed
|
||||
assertEquals(2078.01, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(2078.01, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(2078.01, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||
assertEquals(2077.305, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(2077.305, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(2077.305, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||
|
||||
// Altitude
|
||||
assertEquals(10, importedTrackStatistics.getMinAltitude(), 0.01);
|
||||
assertEquals(10, importedTrackStatistics.getMaxAltitude(), 0.01);
|
||||
assertEquals(999.099, importedTrackStatistics.getMinAltitude(), 0.01);
|
||||
assertEquals(1020.2, importedTrackStatistics.getMaxAltitude(), 0.01);
|
||||
assertEquals(2, importedTrackStatistics.getTotalAltitudeGain(), 0.01);
|
||||
assertEquals(2, importedTrackStatistics.getTotalAltitudeLoss(), 0.01);
|
||||
|
||||
@@ -614,7 +637,7 @@ public class ExportImportTest {
|
||||
}
|
||||
}
|
||||
|
||||
private void sendLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, float verticalAccuracy, float speed, float altitude, Float altitudeGain) {
|
||||
private void sendLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, float verticalAccuracy, float speed, double altitude, Float altitudeGain) {
|
||||
Location location = new Location("mock");
|
||||
location.setLatitude(latitude);
|
||||
location.setLongitude(longitude);
|
||||
|
||||
+2
-2
@@ -20,8 +20,8 @@ public class TrackPointAssert {
|
||||
|
||||
Assert.assertEquals("has location,", expected.hasLocation(), actual.hasLocation());
|
||||
if (expected.hasLocation()) {
|
||||
Assert.assertEquals("latitude", expected.getLatitude(), actual.getLatitude(), 0.001);
|
||||
Assert.assertEquals("longitude", expected.getLongitude(), actual.getLongitude(), 0.001);
|
||||
Assert.assertEquals("latitude", expected.getPosition().latitude(), actual.getPosition().latitude(), 0.001);
|
||||
Assert.assertEquals("longitude", expected.getPosition().longitude(), actual.getPosition().longitude(), 0.001);
|
||||
}
|
||||
|
||||
Assert.assertEquals("has altitude", expected.hasAltitude(), actual.hasAltitude());
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#time,trackpoint_type,latitude,longitude,altitude,accuracy_horizontal,accuracy_vertical,speed,altitude_gain,altitude_loss,sensor_distance,heartrate,cadence,power
|
||||
"2020-02-02T03:02:02+01:00","SEGMENT_START_MANUAL",,,,,,,,,,,,
|
||||
"2020-02-02T03:02:03+01:00","TRACKPOINT",3,14,10,10,,54,1,1,,,,
|
||||
"2020-02-02T03:02:03+01:00","TRACKPOINT",3.123456,14.001456,1020.2,10,,54,1,1,,,,
|
||||
"2020-02-02T03:02:04+01:00","TRACKPOINT",,,,,,54,1,1,10,66,3,50
|
||||
"2020-02-02T03:02:15+01:00","TRACKPOINT",,,,,,,,,,68,3,50
|
||||
"2020-02-02T03:02:17+01:00","TRACKPOINT",3,14.001,10,10,,18,0,0,2,69,3,50
|
||||
"2020-02-02T03:02:17+01:00","TRACKPOINT",3.123456,14.001456,1020.2,10,,18,0,0,2,69,3,50
|
||||
"2020-02-02T03:02:18+01:00","SEGMENT_END_MANUAL",,,,,,,,,,,,
|
||||
"2020-02-02T03:03:20+01:00","SEGMENT_START_MANUAL",,,,,,,,,,,,
|
||||
"2020-02-02T03:03:21+01:00","TRACKPOINT",3,14.002,10,10,,54,0,0,,,,
|
||||
"2020-02-02T03:03:22+01:00","SEGMENT_START_AUTOMATIC",3,16,10,10,,54,0,0,,,,
|
||||
"2020-02-02T03:03:21+01:00","TRACKPOINT",3.123456,14.002456,999.1,10,,54,0,0,,,,
|
||||
"2020-02-02T03:03:22+01:00","SEGMENT_START_AUTOMATIC",3.123456,16,999.1,10,,54,0,0,,,,
|
||||
"2020-02-02T03:03:30+01:00","IDLE",,,,,,,0,0,,,,
|
||||
"2020-02-02T03:03:50+01:00","TRACKPOINT",3,16.001,10,10,,54,0,0,,,,
|
||||
"2020-02-02T03:03:50+01:00","TRACKPOINT",3.123456,16.001,999.1,10,,54,0,0,,,,
|
||||
"2020-02-02T03:04:00+01:00","SEGMENT_END_MANUAL",,,,,,,,,,,,
|
||||
|
@@ -79,8 +79,7 @@ limitations under the License.
|
||||
android:hasFragileUserData="false"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/OpenTracksTheme">
|
||||
android:supportsRtl="true">
|
||||
|
||||
<activity
|
||||
android:name=".publicapi.StartRecording"
|
||||
|
||||
@@ -3,6 +3,7 @@ package de.dennisguse.opentracks;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import de.dennisguse.opentracks.databinding.AboutBinding;
|
||||
@@ -29,7 +30,8 @@ public class AboutActivity extends AbstractActivity {
|
||||
ViewUtils.makeClickableLinks(findViewById(android.R.id.content));
|
||||
}
|
||||
|
||||
protected View getRootView() {
|
||||
@NonNull
|
||||
protected View createRootView() {
|
||||
viewBinding = AboutBinding.inflate(getLayoutInflater());
|
||||
return viewBinding.getRoot();
|
||||
}
|
||||
|
||||
@@ -19,7 +19,13 @@ package de.dennisguse.opentracks;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.ViewGroupCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
|
||||
import de.dennisguse.opentracks.services.announcement.TTSManager;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
@@ -31,16 +37,29 @@ public abstract class AbstractActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
if (PreferencesUtils.shouldApplyOledTheme()) {
|
||||
setTheme(R.style.OpenTracksThemeOled);
|
||||
}
|
||||
PreferencesUtils.applyTheme(this);
|
||||
|
||||
EdgeToEdge.enable(this);
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Set volume control stream for text to speech
|
||||
setVolumeControlStream(TTSManager.AUDIO_STREAM);
|
||||
|
||||
setContentView(getRootView());
|
||||
View rootView = createRootView();
|
||||
setContentView(rootView);
|
||||
|
||||
edge2edgeApplyInsets(rootView);
|
||||
}
|
||||
|
||||
private void edge2edgeApplyInsets(View rootView) {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(rootView, (view, windowInsets) -> {
|
||||
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout() | WindowInsetsCompat.Type.ime());
|
||||
view.setPadding(insets.left, insets.top, insets.right, insets.bottom);
|
||||
return WindowInsetsCompat.CONSUMED;
|
||||
});
|
||||
// For Android 10 and below.
|
||||
ViewGroupCompat.installCompatInsetsDispatch(rootView);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -49,5 +68,6 @@ public abstract class AbstractActivity extends AppCompatActivity {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected abstract View getRootView();
|
||||
@NonNull
|
||||
protected abstract View createRootView();
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ public abstract class AbstractTrackDeleteActivity extends AbstractActivity imple
|
||||
ConfirmDeleteDialogFragment.showDialog(getSupportFragmentManager(), trackIds);
|
||||
}
|
||||
|
||||
//TODO A callback is better.
|
||||
@Override
|
||||
public void onConfirmDeleteDone(Track.Id... trackIds) {
|
||||
ArrayList<Track.Id> trackIdList = Arrays.stream(trackIds)
|
||||
|
||||
@@ -3,6 +3,7 @@ package de.dennisguse.opentracks;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import de.dennisguse.opentracks.databinding.HelpBinding;
|
||||
@@ -19,8 +20,9 @@ public class HelpActivity extends AbstractActivity {
|
||||
ViewUtils.makeClickableLinks(findViewById(android.R.id.content));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View getRootView() {
|
||||
protected View createRootView() {
|
||||
helpBinding = HelpBinding.inflate(getLayoutInflater());
|
||||
return helpBinding.getRoot();
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@@ -28,8 +30,9 @@ public class ShowErrorActivity extends AbstractActivity {
|
||||
setSupportActionBar(viewBinding.bottomAppBarLayout.bottomAppBar);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View getRootView() {
|
||||
protected View createRootView() {
|
||||
viewBinding = ActivityShowErrorBinding.inflate(getLayoutInflater());
|
||||
return viewBinding.getRoot();
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@ import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.material.color.DynamicColors;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
@@ -34,11 +32,7 @@ public class Startup extends Application {
|
||||
// Set default values of preferences on first start.
|
||||
PreferencesUtils.resetPreferences(this, false);
|
||||
PreferencesUtils.applyDefaultUnit();
|
||||
PreferencesUtils.applyNightMode();
|
||||
|
||||
if (PreferencesUtils.shouldUseDynamicColors()) {
|
||||
DynamicColors.applyToActivitiesIfAvailable(this);
|
||||
}
|
||||
PreferencesUtils.applyNightModeAndDynamicColors(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -23,12 +23,14 @@ import android.widget.ArrayAdapter;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.time.ZoneOffset;
|
||||
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.databinding.TrackEditBinding;
|
||||
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
||||
import de.dennisguse.opentracks.util.TrackUtils;
|
||||
import de.dennisguse.opentracks.ui.ZoneOffsetAdapter;
|
||||
|
||||
/**
|
||||
* An activity that let's the user see and edit the user editable track meta data such as track name, activity type, and track description.
|
||||
@@ -68,19 +70,18 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
|
||||
return;
|
||||
}
|
||||
|
||||
viewBinding.fields.trackEditName.setText(track.getName());
|
||||
|
||||
viewBinding.fields.trackEditActivityType.setText(track.getActivityTypeLocalized());
|
||||
viewBinding.trackEditName.setText(track.getName());
|
||||
|
||||
viewBinding.trackEditActivityTypeLocalized.setText(track.getActivityTypeLocalized());
|
||||
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line, ActivityType.getLocalizedStrings(this));
|
||||
viewBinding.fields.trackEditActivityType.setAdapter(adapter);
|
||||
viewBinding.fields.trackEditActivityType.setOnItemClickListener((parent, view, position, id) -> {
|
||||
String localizedActivityType = (String) viewBinding.fields.trackEditActivityType.getAdapter().getItem(position);
|
||||
viewBinding.trackEditActivityTypeLocalized.setAdapter(adapter);
|
||||
viewBinding.trackEditActivityTypeLocalized.setOnItemClickListener((parent, view, position, id) -> {
|
||||
String localizedActivityType = (String) viewBinding.trackEditActivityTypeLocalized.getAdapter().getItem(position);
|
||||
setActivityTypeIcon(ActivityType.findByLocalizedString(this, localizedActivityType));
|
||||
});
|
||||
viewBinding.fields.trackEditActivityType.setOnFocusChangeListener((v, hasFocus) -> {
|
||||
viewBinding.trackEditActivityTypeLocalized.setOnFocusChangeListener((v, hasFocus) -> {
|
||||
if (!hasFocus) {
|
||||
String localizedActivityType = viewBinding.fields.trackEditActivityType.getText().toString();
|
||||
String localizedActivityType = viewBinding.trackEditActivityTypeLocalized.getText().toString();
|
||||
setActivityTypeIcon(ActivityType.findByLocalizedString(this, localizedActivityType));
|
||||
}
|
||||
});
|
||||
@@ -92,16 +93,23 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
|
||||
if (activityType == null) {
|
||||
activityType = track.getActivityType();
|
||||
}
|
||||
|
||||
setActivityTypeIcon(activityType);
|
||||
viewBinding.fields.trackEditActivityTypeIcon.setOnClickListener(v -> ChooseActivityTypeDialogFragment.showDialog(getSupportFragmentManager(), this, viewBinding.fields.trackEditActivityType.getText().toString()));
|
||||
viewBinding.trackEditActivityTypeIcon.setOnClickListener(v -> ChooseActivityTypeDialogFragment.showDialog(getSupportFragmentManager(), this, viewBinding.trackEditActivityTypeLocalized.getText().toString()));
|
||||
|
||||
viewBinding.fields.trackEditDescription.setText(track.getDescription());
|
||||
viewBinding.trackEditDescription.setText(track.getDescription());
|
||||
|
||||
final ArrayAdapter<ZoneOffset> zoneOffsetAdapter = new ZoneOffsetAdapter(this, android.R.layout.simple_dropdown_item_1line);
|
||||
viewBinding.trackEditTimeOffset.setText(track.getZoneOffset().toString());
|
||||
viewBinding.trackEditTimeOffset.setAdapter(zoneOffsetAdapter);
|
||||
viewBinding.trackEditTimeOffset.setOnItemClickListener((parent, view, position, id) -> track.setZoneOffset(zoneOffsetAdapter.getItem(position)));
|
||||
|
||||
viewBinding.trackEditSave.setOnClickListener(v -> {
|
||||
TrackUtils.updateTrack(TrackEditActivity.this, track, viewBinding.fields.trackEditName.getText().toString(),
|
||||
viewBinding.fields.trackEditActivityType.getText().toString(), viewBinding.fields.trackEditDescription.getText().toString(),
|
||||
contentProviderUtils);
|
||||
track.setName(viewBinding.trackEditName.getText().toString());
|
||||
track.setDescription(viewBinding.trackEditDescription.getText().toString());
|
||||
|
||||
track.setActivityTypeLocalizedAndUpdateActivityType(this, viewBinding.trackEditActivityTypeLocalized.getText().toString());
|
||||
|
||||
contentProviderUtils.updateTrack(track);
|
||||
finish();
|
||||
});
|
||||
|
||||
@@ -115,20 +123,21 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
|
||||
outState.putSerializable(ICON_VALUE_KEY, activityType);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View getRootView() {
|
||||
protected View createRootView() {
|
||||
viewBinding = TrackEditBinding.inflate(getLayoutInflater());
|
||||
return viewBinding.getRoot();
|
||||
}
|
||||
|
||||
private void setActivityTypeIcon(ActivityType activityType) {
|
||||
this.activityType = activityType;
|
||||
viewBinding.fields.trackEditActivityTypeIcon.setImageResource(activityType.getIconDrawableId());
|
||||
viewBinding.trackEditActivityTypeIcon.setImageResource(activityType.getIconDrawableId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChooseActivityTypeDone(ActivityType activityType) {
|
||||
setActivityTypeIcon(activityType);
|
||||
viewBinding.fields.trackEditActivityType.setText(getString(activityType.getLocalizedStringId()));
|
||||
viewBinding.trackEditActivityTypeLocalized.setText(getString(activityType.getLocalizedStringId()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,10 +30,13 @@ import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
|
||||
@@ -59,6 +62,7 @@ import de.dennisguse.opentracks.ui.aggregatedStatistics.AggregatedStatisticsActi
|
||||
import de.dennisguse.opentracks.ui.aggregatedStatistics.ConfirmDeleteDialogFragment;
|
||||
import de.dennisguse.opentracks.ui.markers.MarkerListActivity;
|
||||
import de.dennisguse.opentracks.ui.util.ActivityUtils;
|
||||
import de.dennisguse.opentracks.ui.util.RecyclerViewSwipeDeleteCallback;
|
||||
import de.dennisguse.opentracks.util.IntentDashboardUtils;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.util.PermissionRequester;
|
||||
@@ -167,6 +171,13 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
viewBinding.trackList.setLayoutManager(layoutManager);
|
||||
viewBinding.trackList.setAdapter(adapter);
|
||||
|
||||
ItemTouchHelper itemTouchhelper = new ItemTouchHelper(new RecyclerViewSwipeDeleteCallback(this) {
|
||||
@Override
|
||||
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
|
||||
TrackListActivity.this.deleteTracks(new Track.Id(viewHolder.getItemId()));
|
||||
}
|
||||
});
|
||||
itemTouchhelper.attachToRecyclerView(viewBinding.trackList);
|
||||
viewBinding.trackListFabAction.setOnClickListener((view) -> {
|
||||
if (recordingStatus.isRecording()) {
|
||||
Toast.makeText(TrackListActivity.this, getString(R.string.hold_to_stop), Toast.LENGTH_LONG).show();
|
||||
@@ -242,8 +253,9 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
adapter = null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View getRootView() {
|
||||
protected View createRootView() {
|
||||
viewBinding = TrackListBinding.inflate(getLayoutInflater());
|
||||
|
||||
viewBinding.trackListSearchView.getEditText().setOnEditorActionListener((v, actionId, event) -> {
|
||||
@@ -304,6 +316,12 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
adapter.swapData(tracks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfirmDeleteAbort(Track.Id... trackIds) {
|
||||
// This is not nice, but it will happen rarely (as deletion is not a standard case).
|
||||
adapter.notifyItemRangeChanged(0, adapter.getItemCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDeleteConfirmed() {
|
||||
// Do nothing
|
||||
|
||||
@@ -34,7 +34,6 @@ import com.google.android.material.tabs.TabLayoutMediator;
|
||||
|
||||
import de.dennisguse.opentracks.chart.ChartFragment;
|
||||
import de.dennisguse.opentracks.chart.TrackDataHubInterface;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.TrackDataHub;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.databinding.TrackRecordedBinding;
|
||||
@@ -67,8 +66,6 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
|
||||
|
||||
private static final String CURRENT_TAB_TAG_KEY = "current_tab_tag_key";
|
||||
|
||||
// The following are setFrequency in onCreate.
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
private TrackDataHub trackDataHub;
|
||||
|
||||
private TrackRecordedBinding viewBinding;
|
||||
@@ -85,8 +82,6 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
contentProviderUtils = new ContentProviderUtils(this);
|
||||
|
||||
handleIntent(getIntent());
|
||||
|
||||
trackDataHub = new TrackDataHub(this);
|
||||
@@ -101,7 +96,6 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
|
||||
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindCallback);
|
||||
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
viewBinding.bottomAppBarLayout.bottomAppBar.replaceMenu(R.menu.track_detail);
|
||||
setSupportActionBar(viewBinding.bottomAppBarLayout.bottomAppBar);
|
||||
|
||||
@@ -142,8 +136,9 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
|
||||
outState.putInt(CURRENT_TAB_TAG_KEY, viewBinding.trackDetailActivityViewPager.getCurrentItem());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View getRootView() {
|
||||
protected View createRootView() {
|
||||
viewBinding = TrackRecordedBinding.inflate(getLayoutInflater());
|
||||
return viewBinding.getRoot();
|
||||
}
|
||||
|
||||
@@ -49,7 +49,6 @@ import de.dennisguse.opentracks.ui.markers.MarkerListActivity;
|
||||
import de.dennisguse.opentracks.ui.util.ActivityUtils;
|
||||
import de.dennisguse.opentracks.util.IntentDashboardUtils;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.util.TrackUtils;
|
||||
|
||||
/**
|
||||
* An activity to show the track detail, record a new track or resumes an existing one.
|
||||
@@ -250,8 +249,9 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
trackRecordingServiceConnection = null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View getRootView() {
|
||||
protected View createRootView() {
|
||||
viewBinding = TrackRecordingBinding.inflate(getLayoutInflater());
|
||||
return viewBinding.getRoot();
|
||||
}
|
||||
@@ -326,7 +326,9 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
public void onChooseActivityTypeDone(ActivityType activityType) {
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
String activityTypeLocalized = getString(activityType.getLocalizedStringId());
|
||||
TrackUtils.updateTrack(this, track, null, activityTypeLocalized, null, contentProviderUtils);
|
||||
track.setActivityTypeLocalizedAndUpdateActivityType(this, activityTypeLocalized);
|
||||
|
||||
contentProviderUtils.updateTrack(track);
|
||||
}
|
||||
|
||||
private class CustomFragmentPagerAdapter extends FragmentStateAdapter {
|
||||
|
||||
@@ -7,6 +7,8 @@ import android.util.Pair;
|
||||
import android.view.View;
|
||||
import android.widget.ArrayAdapter;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
||||
@@ -20,7 +22,6 @@ import de.dennisguse.opentracks.ui.aggregatedStatistics.ConfirmDeleteDialogFragm
|
||||
import de.dennisguse.opentracks.util.ExportUtils;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
import de.dennisguse.opentracks.util.TrackUtils;
|
||||
|
||||
public class TrackStoppedActivity extends AbstractTrackDeleteActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller {
|
||||
|
||||
@@ -32,8 +33,6 @@ public class TrackStoppedActivity extends AbstractTrackDeleteActivity implements
|
||||
|
||||
private Track.Id trackId;
|
||||
|
||||
private boolean isDiscarding = false;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -105,13 +104,16 @@ public class TrackStoppedActivity extends AbstractTrackDeleteActivity implements
|
||||
}
|
||||
|
||||
private void storeTrackMetaData(ContentProviderUtils contentProviderUtils, Track track) {
|
||||
TrackUtils.updateTrack(TrackStoppedActivity.this, track, viewBinding.trackEditName.getText().toString(),
|
||||
viewBinding.trackEditActivityType.getText().toString(), viewBinding.trackEditDescription.getText().toString(),
|
||||
contentProviderUtils);
|
||||
track.setName(viewBinding.trackEditName.getText().toString());
|
||||
track.setDescription(viewBinding.trackEditDescription.getText().toString());
|
||||
track.setActivityTypeLocalizedAndUpdateActivityType(this, viewBinding.trackEditActivityType.getText().toString());
|
||||
|
||||
contentProviderUtils.updateTrack(track);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View getRootView() {
|
||||
protected View createRootView() {
|
||||
viewBinding = TrackStoppedBinding.inflate(getLayoutInflater());
|
||||
return viewBinding.getRoot();
|
||||
}
|
||||
@@ -141,7 +143,6 @@ public class TrackStoppedActivity extends AbstractTrackDeleteActivity implements
|
||||
|
||||
@Override
|
||||
protected void onDeleteConfirmed() {
|
||||
isDiscarding = true;
|
||||
viewBinding.loadingLayout.loadingText.setText(getString(R.string.track_discarding));
|
||||
viewBinding.contentLinearLayout.setVisibility(View.GONE);
|
||||
viewBinding.loadingLayout.loadingIndeterminate.setVisibility(View.VISIBLE);
|
||||
|
||||
@@ -784,8 +784,8 @@ public class ContentProviderUtils {
|
||||
values.put(TrackPointsColumns.TYPE, trackPoint.getType().type_db);
|
||||
|
||||
if (trackPoint.hasLocation()) {
|
||||
values.put(TrackPointsColumns.LONGITUDE, (int) (trackPoint.getLongitude() * 1E6));
|
||||
values.put(TrackPointsColumns.LATITUDE, (int) (trackPoint.getLatitude() * 1E6));
|
||||
values.put(TrackPointsColumns.LATITUDE, (int) (trackPoint.getPosition().latitude() * 1E6));
|
||||
values.put(TrackPointsColumns.LONGITUDE, (int) (trackPoint.getPosition().longitude() * 1E6));
|
||||
}
|
||||
values.put(TrackPointsColumns.TIME, trackPoint.getTime().toEpochMilli());
|
||||
if (trackPoint.hasAltitude()) {
|
||||
|
||||
@@ -129,10 +129,12 @@ public enum ActivityType {
|
||||
.orElse(ActivityType.UNKNOWN);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static ActivityType findByLocalizedString(Context context, String localizedActivityType) {
|
||||
return findByLocalizedString(context.getResources(), localizedActivityType);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static ActivityType findByLocalizedString(Resources resources, String localizedActivityType) {
|
||||
return Arrays.stream(ActivityType.values())
|
||||
.filter(it -> resources.getString(it.getLocalizedStringId()).equals(localizedActivityType))
|
||||
|
||||
@@ -132,14 +132,6 @@ public final class Marker {
|
||||
return position;
|
||||
}
|
||||
|
||||
public double getLatitude() {
|
||||
return position.latitude();
|
||||
}
|
||||
|
||||
public double getLongitude() {
|
||||
return position.longitude();
|
||||
}
|
||||
|
||||
public boolean hasAccuracy() {
|
||||
return position.hasHorizontalAccuracy();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package de.dennisguse.opentracks.data.models;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
@@ -48,7 +49,7 @@ public class Track {
|
||||
|
||||
private ActivityType activityType;
|
||||
|
||||
private final ZoneOffset zoneOffset;
|
||||
private ZoneOffset zoneOffset;
|
||||
|
||||
private TrackStatistics trackStatistics = new TrackStatistics();
|
||||
|
||||
@@ -58,7 +59,7 @@ public class Track {
|
||||
}
|
||||
|
||||
public Track(@NonNull ZoneOffset zoneOffset) {
|
||||
this.zoneOffset = zoneOffset;
|
||||
setZoneOffset(zoneOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,10 +114,19 @@ public class Track {
|
||||
this.activityType = activityType;
|
||||
}
|
||||
|
||||
public void setActivityTypeLocalizedAndUpdateActivityType(Context context, String activityTypeLocalized) {
|
||||
setActivityTypeLocalized(activityTypeLocalized);
|
||||
setActivityType(ActivityType.findByLocalizedString(context, activityTypeLocalized));
|
||||
}
|
||||
|
||||
public ZoneOffset getZoneOffset() {
|
||||
return zoneOffset;
|
||||
}
|
||||
|
||||
public void setZoneOffset(@NonNull ZoneOffset zoneOffset) {
|
||||
this.zoneOffset = zoneOffset;
|
||||
}
|
||||
|
||||
public OffsetDateTime getStartTime() {
|
||||
return trackStatistics
|
||||
.getStartTime().atOffset(zoneOffset);
|
||||
|
||||
@@ -147,18 +147,6 @@ public class TrackPoint {
|
||||
return position.hasLocation();
|
||||
}
|
||||
|
||||
//TODO Should be double
|
||||
@Deprecated //Use getPosition()
|
||||
public Double getLatitude() {
|
||||
return position.latitude();
|
||||
}
|
||||
|
||||
//TODO Should be double
|
||||
@Deprecated //Use getPosition()
|
||||
public Double getLongitude() {
|
||||
return position.longitude();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Position getPosition() {
|
||||
return position;
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package de.dennisguse.opentracks.fragments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
@@ -33,7 +32,6 @@ import androidx.fragment.app.Fragment;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.TrackRecordedActivity;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
||||
import de.dennisguse.opentracks.data.models.SpeedFormatter;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
@@ -185,13 +183,7 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
}
|
||||
|
||||
// Set activity type
|
||||
{
|
||||
Context context = getContext();
|
||||
String localizedActivityType = track.getActivityTypeLocalized();
|
||||
int iconDrawableId = ActivityType.findByLocalizedString(context, localizedActivityType)
|
||||
.getIconDrawableId();
|
||||
viewBinding.statsActivityTypeIcon.setImageDrawable(ContextCompat.getDrawable(getContext(), iconDrawableId));
|
||||
}
|
||||
viewBinding.statsActivityTypeIcon.setImageDrawable(ContextCompat.getDrawable(getContext(), track.getActivityType().getIconDrawableId()));
|
||||
|
||||
// Set time and start datetime
|
||||
{
|
||||
|
||||
@@ -47,8 +47,9 @@ public class IntroductionActivity extends AbstractActivity {
|
||||
viewBinding.introductionViewPager.setUserInputEnabled(false);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View getRootView() {
|
||||
protected View createRootView() {
|
||||
viewBinding = IntroductionBinding.inflate(getLayoutInflater());
|
||||
return viewBinding.getRoot();
|
||||
}
|
||||
|
||||
@@ -60,7 +60,6 @@ public class CSVTrackExporter implements TrackExporter {
|
||||
ALTITUDE_FORMAT.setGroupingUsed(false);
|
||||
|
||||
COORDINATE_FORMAT.setMaximumFractionDigits(6);
|
||||
COORDINATE_FORMAT.setMaximumIntegerDigits(3);
|
||||
COORDINATE_FORMAT.setGroupingUsed(false);
|
||||
|
||||
SPEED_FORMAT.setMaximumFractionDigits(2);
|
||||
@@ -92,19 +91,19 @@ public class CSVTrackExporter implements TrackExporter {
|
||||
List<Column> columns = List.of(
|
||||
new Column("time", null),
|
||||
new Column("trackpoint_type", t -> quote(t.getType().name())),
|
||||
new Column("latitude", t -> t.hasLocation() ? COORDINATE_FORMAT.format(t.getLatitude()) : ""),
|
||||
new Column("longitude", t -> t.hasLocation() ? COORDINATE_FORMAT.format(t.getLongitude()) : ""),
|
||||
new Column("altitude", t -> t.hasAltitude() ? COORDINATE_FORMAT.format(t.getAltitude().toM()) : ""),
|
||||
new Column("latitude", t -> t.hasLocation() ? COORDINATE_FORMAT.format(t.getPosition().latitude()) : ""),
|
||||
new Column("longitude", t -> t.hasLocation() ? COORDINATE_FORMAT.format(t.getPosition().longitude()) : ""),
|
||||
new Column("altitude", t -> t.hasAltitude() ? ALTITUDE_FORMAT.format(t.getAltitude().toM()) : ""),
|
||||
new Column("accuracy_horizontal", t -> t.hasHorizontalAccuracy() ? DISTANCE_FORMAT.format(t.getHorizontalAccuracy().toM()) : ""),
|
||||
new Column("accuracy_vertical", t -> t.hasVerticalAccuracy() ? DISTANCE_FORMAT.format(t.getVerticalAccuracy().toM()) : ""),
|
||||
|
||||
new Column("speed", t -> t.hasSpeed() ? SPEED_FORMAT.format(t.getSpeed().toKMH()) : ""),
|
||||
new Column("altitude_gain", t -> t.hasAltitudeGain() ? DISTANCE_FORMAT.format(t.getAltitudeGain()) : ""),
|
||||
new Column("altitude_loss", t -> t.hasAltitudeLoss() ? DISTANCE_FORMAT.format(t.getAltitudeLoss()) : ""),
|
||||
new Column("altitude_gain", t -> t.hasAltitudeGain() ? ALTITUDE_FORMAT.format(t.getAltitudeGain()) : ""),
|
||||
new Column("altitude_loss", t -> t.hasAltitudeLoss() ? ALTITUDE_FORMAT.format(t.getAltitudeLoss()) : ""),
|
||||
new Column("sensor_distance", t -> t.hasSensorDistance() ? DISTANCE_FORMAT.format(t.getSensorDistance().toM()) : ""),
|
||||
new Column("heartrate", t -> t.hasHeartRate() ? HEARTRATE_FORMAT.format(t.getHeartRate().getBPM()) : ""),
|
||||
new Column("cadence", t -> t.hasCadence() ? CADENCE_FORMAT.format(t.getCadence().getRPM()) : ""),
|
||||
new Column("power", t -> t.hasPower() ? ALTITUDE_FORMAT.format(t.getPower().getW()) : ""));
|
||||
new Column("power", t -> t.hasPower() ? POWER_FORMAT.format(t.getPower().getW()) : ""));
|
||||
|
||||
try {
|
||||
prepare(outputStream);
|
||||
@@ -136,7 +135,7 @@ public class CSVTrackExporter implements TrackExporter {
|
||||
while (trackPointIterator.hasNext()) {
|
||||
if (Thread.interrupted()) throw new InterruptedException();
|
||||
|
||||
writeTrackPoint(columns, trackPointIterator.next());
|
||||
writeTrackPoint(columns, trackPointIterator.next());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -95,8 +94,6 @@ public class ExportActivity extends AppCompatActivity implements ExportService.E
|
||||
private int trackExportSkippedCount;
|
||||
private int trackExportTotalCount;
|
||||
|
||||
boolean doubleBackToCancel = false;
|
||||
|
||||
private ExportActivityBinding viewBinding;
|
||||
|
||||
private ArrayList<String> trackErrors = new ArrayList<>();
|
||||
@@ -214,19 +211,6 @@ public class ExportActivity extends AppCompatActivity implements ExportService.E
|
||||
exportTasks.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (doubleBackToCancel || getTotalDone() == trackExportTotalCount) {
|
||||
super.onBackPressed();
|
||||
return;
|
||||
}
|
||||
|
||||
this.doubleBackToCancel = true;
|
||||
Toast.makeText(this, getString(R.string.generic_click_twice_cancel), Toast.LENGTH_SHORT).show();
|
||||
|
||||
new Handler().postDelayed(() -> doubleBackToCancel = false, 2000);
|
||||
}
|
||||
|
||||
private void createExportTasks(boolean allInOneFile) {
|
||||
List<Track> tracks = contentProviderUtils.getTracks();
|
||||
exportTasks = new ArrayList<>();
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.ResultReceiver;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.JobIntentService;
|
||||
@@ -17,6 +18,8 @@ import de.dennisguse.opentracks.util.ExportUtils;
|
||||
|
||||
public class ExportService extends JobIntentService {
|
||||
|
||||
private static final String TAG = ExportService.class.getSimpleName();
|
||||
|
||||
private static final int JOB_ID = 1;
|
||||
|
||||
private static final String EXTRA_RECEIVER = "extra_receiver";
|
||||
@@ -55,6 +58,9 @@ public class ExportService extends JobIntentService {
|
||||
ExportUtils.exportTrack(this, directoryFile, exportTask);
|
||||
resultReceiver.send(ExportServiceResultReceiver.RESULT_CODE_SUCCESS, bundle);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Export failed: " + e);
|
||||
e.printStackTrace(); //TODO Remove
|
||||
|
||||
bundle.putString(ExportServiceResultReceiver.EXTRA_EXPORT_ERROR_MESSAGE, e.getMessage());
|
||||
resultReceiver.send(ExportServiceResultReceiver.RESULT_CODE_ERROR, bundle);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ 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.Marker;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
@@ -253,7 +254,7 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
}
|
||||
|
||||
private void writeMarker(ZoneOffset zoneOffset, Marker marker) {
|
||||
printWriter.println("<wpt " + formatLocation(marker.getLatitude(), marker.getLongitude()) + ">");
|
||||
printWriter.println("<wpt " + formatLocation(marker.getPosition()) + ">");
|
||||
if (marker.hasAltitude()) {
|
||||
printWriter.println("<ele>" + ALTITUDE_FORMAT.format(marker.getAltitude().toM()) + "</ele>");
|
||||
}
|
||||
@@ -311,7 +312,7 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
private Distance writeTrackPoint(ZoneOffset zoneOffset, TrackPoint trackPoint, List<TrackPoint> sensorPoints, Distance trackDistance) {
|
||||
Distance cumulativeDistance;
|
||||
|
||||
printWriter.println("<trkpt " + formatLocation(trackPoint.getLatitude(), trackPoint.getLongitude()) + ">");
|
||||
printWriter.println("<trkpt " + formatLocation(trackPoint.getPosition()) + ">");
|
||||
|
||||
if (trackPoint.hasAltitude()) {
|
||||
printWriter.println("<ele>" + ALTITUDE_FORMAT.format(trackPoint.getAltitude().toM()) + "</ele>");
|
||||
@@ -395,7 +396,7 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private String formatLocation(double latitude, double longitude) {
|
||||
return "lat=\"" + COORDINATE_FORMAT.format(latitude) + "\" lon=\"" + COORDINATE_FORMAT.format(longitude) + "\"";
|
||||
private String formatLocation(Position position) {
|
||||
return "lat=\"" + COORDINATE_FORMAT.format(position.latitude()) + "\" lon=\"" + COORDINATE_FORMAT.format(position.longitude()) + "\"";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
@@ -274,8 +273,7 @@ public class KMLTrackExporter implements TrackExporter {
|
||||
private void writeMarker(Marker marker, ZoneOffset zoneOffset) {
|
||||
boolean existsPhoto = MarkerUtils.buildInternalPhotoFile(context, marker.getTrackId(), marker.getPhotoUrl()) != null;
|
||||
if (marker.hasPhoto() && exportPhotos && existsPhoto) {
|
||||
float heading = getHeading(marker.getTrackId(), marker.getPosition());
|
||||
writePhotoOverlay(marker, heading, zoneOffset);
|
||||
writePhotoOverlay(marker, zoneOffset);
|
||||
} else {
|
||||
writePlacemark(marker.getName(), marker.getCategory(), marker.getDescription(), marker.getPosition(), marker.getTime(), zoneOffset);
|
||||
}
|
||||
@@ -429,15 +427,14 @@ public class KMLTrackExporter implements TrackExporter {
|
||||
}
|
||||
}
|
||||
|
||||
private void writePhotoOverlay(Marker marker, float heading, ZoneOffset zoneOffset) {
|
||||
private void writePhotoOverlay(Marker marker, ZoneOffset zoneOffset) {
|
||||
printWriter.println("<PhotoOverlay>");
|
||||
printWriter.println("<name>" + StringUtils.formatCData(marker.getName()) + "</name>");
|
||||
printWriter.println("<description>" + StringUtils.formatCData(marker.getDescription()) + "</description>");
|
||||
printWriter.print("<Camera>");
|
||||
printWriter.print("<longitude>" + marker.getLongitude() + "</longitude>");
|
||||
printWriter.print("<latitude>" + marker.getLatitude() + "</latitude>");
|
||||
printWriter.print("<longitude>" + marker.getPosition().longitude() + "</longitude>");
|
||||
printWriter.print("<latitude>" + marker.getPosition().latitude() + "</latitude>");
|
||||
printWriter.print("<altitude>20</altitude>");
|
||||
printWriter.print("<heading>" + heading + "</heading>");
|
||||
printWriter.print("<tilt>90</tilt>");
|
||||
printWriter.println("</Camera>");
|
||||
printWriter.println("<TimeStamp><when>" + getTime(zoneOffset, marker.getTime()) + "</when></TimeStamp>");
|
||||
@@ -468,26 +465,6 @@ public class KMLTrackExporter implements TrackExporter {
|
||||
return StringUtils.formatDateTimeIso8601(instant, zoneOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: check if this is a useful feature (likely not).
|
||||
* Gets the heading to a location.
|
||||
*/
|
||||
private float getHeading(Track.Id trackId, Position position) {
|
||||
TrackPoint.Id trackPointId = contentProviderUtils.getTrackPointId(trackId, position);
|
||||
if (trackPointId == null) {
|
||||
return position.bearing();
|
||||
}
|
||||
TrackPoint viewLocation = contentProviderUtils.getLastValidTrackPoint(trackId);
|
||||
if (viewLocation != null) {
|
||||
Optional<Float> bearing = viewLocation.bearingTo(position);
|
||||
if (bearing.isPresent()) {
|
||||
return bearing.get();
|
||||
}
|
||||
}
|
||||
|
||||
return position.bearing();
|
||||
}
|
||||
|
||||
private static String getCoordinates(Position position, String separator) {
|
||||
String result = position.longitude() + separator + position.latitude();
|
||||
if (position.hasAltitude()) {
|
||||
|
||||
@@ -20,9 +20,7 @@ import android.content.ClipData;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
@@ -56,8 +54,6 @@ public class ImportActivity extends FragmentActivity {
|
||||
|
||||
private ImportActivityBinding viewBinding;
|
||||
|
||||
boolean doubleBackToCancel = false;
|
||||
|
||||
private ArrayList<Uri> documentUris = new ArrayList<>();
|
||||
private boolean isDirectory;
|
||||
|
||||
@@ -122,21 +118,6 @@ public class ImportActivity extends FragmentActivity {
|
||||
outState.putBoolean(BUNDLE_IS_DIRECTORY, isDirectory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (doubleBackToCancel || (summary != null && getTotalDone() == summary.getTotalCount())) {
|
||||
super.onBackPressed();
|
||||
viewModel.cancel();
|
||||
getViewModelStore().clear();
|
||||
return;
|
||||
}
|
||||
|
||||
this.doubleBackToCancel = true;
|
||||
Toast.makeText(this, getString(R.string.generic_click_twice_cancel), Toast.LENGTH_SHORT).show();
|
||||
|
||||
new Handler().postDelayed(() -> doubleBackToCancel=false, 2000);
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
viewBinding.importProgressDone.setText("0");
|
||||
viewBinding.importProgressTotal.setText("0");
|
||||
|
||||
@@ -178,10 +178,10 @@ public class TrackImporter {
|
||||
|
||||
if (current.hasLocation()) {
|
||||
Instant time = current.getTime();
|
||||
if (current.getLatitude() == 100) {
|
||||
if (current.getPosition().latitude() == 100) {
|
||||
//TODO Remove by 31st December 2021.
|
||||
trackPoints.set(i, new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, time));
|
||||
} else if (current.getLatitude() == 200) {
|
||||
} else if (current.getPosition().latitude() == 200) {
|
||||
//TODO Remove by 31st December 2021.
|
||||
trackPoints.set(i, new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, time));
|
||||
//TODO Delete location
|
||||
|
||||
@@ -10,7 +10,6 @@ import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.IntentDashboardUtils;
|
||||
import de.dennisguse.opentracks.util.TrackUtils;
|
||||
|
||||
public class StartRecording extends AbstractAPIActivity {
|
||||
|
||||
@@ -42,12 +41,12 @@ public class StartRecording extends AbstractAPIActivity {
|
||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(this);
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
TrackUtils.updateTrack(this, track,
|
||||
bundle.getString(EXTRA_TRACK_NAME, null),
|
||||
bundle.getString(EXTRA_TRACK_ACTIVITY_TYPE_LOCALIZED, null),
|
||||
ActivityType.findBy(bundle.getString(EXTRA_TRACK_ACTIVITY_TYPE_ID, null)),
|
||||
bundle.getString(EXTRA_TRACK_DESCRIPTION, null),
|
||||
contentProviderUtils);
|
||||
track.setName(bundle.getString(EXTRA_TRACK_NAME, ""));
|
||||
track.setDescription(bundle.getString(EXTRA_TRACK_DESCRIPTION, ""));
|
||||
track.setActivityType(ActivityType.findBy(bundle.getString(EXTRA_TRACK_ACTIVITY_TYPE_ID, null)));
|
||||
track.setActivityTypeLocalized(bundle.getString(EXTRA_TRACK_ACTIVITY_TYPE_LOCALIZED, ""));
|
||||
|
||||
contentProviderUtils.updateTrack(track);
|
||||
}
|
||||
|
||||
private void startDashboardAPI(@NonNull Track.Id trackId, @NonNull Bundle bundle) {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package de.dennisguse.opentracks.settings;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
@@ -30,6 +31,8 @@ import androidx.annotation.VisibleForTesting;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import com.google.android.material.color.DynamicColors;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -338,11 +341,6 @@ public class PreferencesUtils {
|
||||
return getBoolean(R.string.chart_display_heart_rate_key, DEFAULT);
|
||||
}
|
||||
|
||||
public static boolean shouldUseDynamicColors() {
|
||||
final boolean DEFAULT = resources.getBoolean(R.bool.settings_ui_dynamic_colors_default);
|
||||
return getBoolean(R.string.settings_ui_dynamic_colors_key, DEFAULT);
|
||||
}
|
||||
|
||||
public static boolean shouldVoiceAnnouncementOnDeviceSpeaker() {
|
||||
final boolean DEFAULT = resources.getBoolean(R.bool.voice_on_device_speaker_default);
|
||||
return getBoolean(R.string.voice_on_device_speaker_key, DEFAULT);
|
||||
@@ -705,7 +703,7 @@ public class PreferencesUtils {
|
||||
public static TrackFileFormat getExportTrackFileFormat() {
|
||||
final String TRACKFILEFORMAT_NAME_DEFAULT = getString(R.string.export_trackfileformat_default, null);
|
||||
String trackFileFormatName = getString(R.string.export_trackfileformat_key, TRACKFILEFORMAT_NAME_DEFAULT);
|
||||
return Arrays.stream(TrackFileFormat.values())
|
||||
return Arrays.stream(TrackFileFormat.values())
|
||||
.filter(format -> format.getPreferenceId().equals(trackFileFormatName))
|
||||
.findFirst().orElse(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES);
|
||||
}
|
||||
@@ -715,30 +713,62 @@ public class PreferencesUtils {
|
||||
return getBoolean(R.string.import_prevent_reimport_key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return * {@link androidx.appcompat.app.AppCompatDelegate}.MODE_*
|
||||
* * 3: Night OLED friendly
|
||||
*/
|
||||
private static String getUiMode() {
|
||||
final String defaultValue = getKey(R.string.night_mode_default);
|
||||
final String value = getString(R.string.night_mode_key, defaultValue);
|
||||
|
||||
return value;
|
||||
record ThemeConfig(int themeResourceId, int dayNight, boolean dynamicColor) {
|
||||
}
|
||||
|
||||
public static boolean shouldApplyOledTheme() {
|
||||
return resources.getString(R.string.night_mode_night_oled_value)
|
||||
.equals(getUiMode());
|
||||
private static ThemeConfig getThemeMode() {
|
||||
final String defaultValue = getKey(R.string.theme_default);
|
||||
final String value = getString(R.string.theme_key, defaultValue);
|
||||
|
||||
switch (value) {
|
||||
case "0" -> {
|
||||
return new ThemeConfig(R.style.DayNightColorTheme, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM, false);
|
||||
}
|
||||
case "1" -> {
|
||||
return new ThemeConfig(R.style.DayNightDynamicTheme, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM, true);
|
||||
}
|
||||
case "2" -> {
|
||||
return new ThemeConfig(R.style.DayNightColorTheme, AppCompatDelegate.MODE_NIGHT_NO, false);
|
||||
}
|
||||
case "3" -> {
|
||||
return new ThemeConfig(R.style.DayNightDynamicTheme, AppCompatDelegate.MODE_NIGHT_NO, true);
|
||||
}
|
||||
case "4" -> {
|
||||
return new ThemeConfig(R.style.DayNightColorTheme, AppCompatDelegate.MODE_NIGHT_YES, false);
|
||||
}
|
||||
case "5" -> {
|
||||
return new ThemeConfig(R.style.DayNightDynamicTheme, AppCompatDelegate.MODE_NIGHT_YES, true);
|
||||
}
|
||||
case "6" -> {
|
||||
return new ThemeConfig(R.style.NightOledColorTheme, AppCompatDelegate.MODE_NIGHT_YES, false);
|
||||
}
|
||||
case "7" -> {
|
||||
return new ThemeConfig(R.style.NightOledDynamicTheme, AppCompatDelegate.MODE_NIGHT_YES, true);
|
||||
}
|
||||
|
||||
default -> {
|
||||
return new ThemeConfig(R.style.DayNightColorTheme, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void applyTheme(Context context) {
|
||||
context.setTheme(getThemeMode().themeResourceId());
|
||||
}
|
||||
|
||||
public static void applyNightMode() {
|
||||
String uiMode = getUiMode();
|
||||
if (resources.getString(R.string.night_mode_night_oled_value)
|
||||
.equals(uiMode)) {
|
||||
return;
|
||||
}
|
||||
ThemeConfig themeConfig = getThemeMode();
|
||||
|
||||
AppCompatDelegate.setDefaultNightMode(Integer.parseInt(uiMode));
|
||||
AppCompatDelegate.setDefaultNightMode(themeConfig.dayNight);
|
||||
}
|
||||
|
||||
public static void applyNightModeAndDynamicColors(Application application) {
|
||||
ThemeConfig themeConfig = getThemeMode();
|
||||
|
||||
applyNightMode();
|
||||
|
||||
if (application != null && themeConfig.dynamicColor)
|
||||
DynamicColors.applyToActivitiesIfAvailable(application);
|
||||
}
|
||||
|
||||
public static void resetPreferences(Context context, boolean readAgain) {
|
||||
|
||||
@@ -72,8 +72,9 @@ public class SettingsActivity extends AbstractActivity implements ChooseActivity
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View getRootView() {
|
||||
protected View createRootView() {
|
||||
viewBinding = SettingsBinding.inflate(getLayoutInflater());
|
||||
return viewBinding.getRoot();
|
||||
}
|
||||
|
||||
+2
-1
@@ -116,8 +116,9 @@ public class SettingsCustomLayoutEditActivity extends AbstractActivity implement
|
||||
recordingLayoutFieldsVisible = null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View getRootView() {
|
||||
protected View createRootView() {
|
||||
viewBinding = ActivitySettingsCustomLayoutBinding.inflate(getLayoutInflater());
|
||||
return viewBinding.getRoot();
|
||||
}
|
||||
|
||||
+2
-1
@@ -108,8 +108,9 @@ public class SettingsCustomLayoutListActivity extends AbstractActivity implement
|
||||
adapter.reloadLayouts();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View getRootView() {
|
||||
protected View createRootView() {
|
||||
PreferencesUtils.getCustomLayout();
|
||||
viewBinding = ActivitySettingsCustomLayoutListBinding.inflate(getLayoutInflater());
|
||||
return viewBinding.getRoot();
|
||||
|
||||
@@ -2,7 +2,6 @@ package de.dennisguse.opentracks.settings;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
@@ -15,7 +14,7 @@ import de.dennisguse.opentracks.R;
|
||||
public class UserInterfaceSettingsFragment extends PreferenceFragmentCompat {
|
||||
|
||||
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
|
||||
if (PreferencesUtils.isKey(R.string.night_mode_key, key)) {
|
||||
if (PreferencesUtils.isKey(R.string.theme_key, key)) {
|
||||
getActivity().runOnUiThread(() -> {
|
||||
PreferencesUtils.applyNightMode();
|
||||
Toast.makeText(getContext(), R.string.settings_theme_switch_restart, Toast.LENGTH_LONG).show();
|
||||
@@ -33,9 +32,6 @@ public class UserInterfaceSettingsFragment extends PreferenceFragmentCompat {
|
||||
startActivity(intent);
|
||||
return true;
|
||||
});
|
||||
|
||||
Preference dynamicColors = findPreference(getString(R.string.settings_ui_dynamic_colors_key));
|
||||
dynamicColors.setEnabled(Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -35,7 +35,6 @@ import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
import de.dennisguse.opentracks.ui.util.ActivityUtils;
|
||||
import de.dennisguse.opentracks.ui.util.ListItemUtils;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
|
||||
public class TrackListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements ActionMode.Callback {
|
||||
|
||||
@@ -56,6 +55,7 @@ public class TrackListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
|
||||
this.recyclerView = recyclerView;
|
||||
this.recordingStatus = recordingStatus;
|
||||
this.unitSystem = unitSystem;
|
||||
setHasStableIds(true);
|
||||
}
|
||||
|
||||
public void setActionModeCallback(ActivityUtils.ContextualActionModeCallback actionModeCallback) {
|
||||
@@ -77,6 +77,12 @@ public class TrackListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
|
||||
viewHolder.bind(cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
cursor.moveToPosition(position);
|
||||
return cursor.getLong(cursor.getColumnIndexOrThrow(TracksColumns._ID));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
if (cursor == null) {
|
||||
@@ -164,7 +170,6 @@ public class TrackListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
|
||||
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
||||
|
||||
private final TrackListItemBinding viewBinding;
|
||||
private final View view;
|
||||
|
||||
private Track.Id trackId;
|
||||
|
||||
@@ -172,10 +177,9 @@ public class TrackListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
|
||||
super(itemView);
|
||||
|
||||
viewBinding = TrackListItemBinding.bind(itemView);
|
||||
view = itemView;
|
||||
|
||||
view.setOnClickListener(this);
|
||||
view.setOnLongClickListener(this);
|
||||
viewBinding.getRoot().setOnClickListener(this);
|
||||
viewBinding.getRoot().setOnLongClickListener(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -229,20 +233,15 @@ public class TrackListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
|
||||
viewBinding.trackListItemTime.setText(null);
|
||||
}
|
||||
|
||||
//TODO Check if this is needed or a leftover from the MarkerList migration
|
||||
String category = activityType == null ? activityTypeLocalized : null;
|
||||
String categoryDescription = StringUtils.getCategoryDescription(category, description);
|
||||
viewBinding.trackListItemCategoryDescription.setText(categoryDescription);
|
||||
viewBinding.trackListItemCategoryDescription.setVisibility("".equals(categoryDescription) ? View.GONE : View.VISIBLE);
|
||||
|
||||
setSelected(selection.get((int) getId()));
|
||||
}
|
||||
|
||||
public void setSelected(boolean isSelected) {
|
||||
selection.put((int) getId(), isSelected);
|
||||
view.setActivated(isSelected);
|
||||
viewBinding.getRoot().setActivated(isSelected);
|
||||
}
|
||||
|
||||
//TODO Check if this still required.
|
||||
public long getId() {
|
||||
return trackId.id();
|
||||
}
|
||||
@@ -251,7 +250,7 @@ public class TrackListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (selectionMode) {
|
||||
setSelected(!view.isActivated());
|
||||
setSelected(!viewBinding.getRoot().isActivated());
|
||||
actionMode.invalidate();
|
||||
return;
|
||||
}
|
||||
@@ -267,14 +266,14 @@ public class TrackListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
|
||||
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId);
|
||||
ActivityOptions activityOptions = ActivityOptions.makeSceneTransitionAnimation(
|
||||
context,
|
||||
new Pair<>(view.findViewById(R.id.track_list_item_icon), TrackRecordedActivity.VIEW_TRACK_ICON));
|
||||
new Pair<>(viewBinding.getRoot().findViewById(R.id.track_list_item_icon), TrackRecordedActivity.VIEW_TRACK_ICON));
|
||||
context.startActivity(newIntent, activityOptions.toBundle());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
setSelected(!view.isActivated());
|
||||
setSelected(!viewBinding.getRoot().isActivated());
|
||||
if (!selectionMode) {
|
||||
actionMode = context.startSupportActionMode(TrackListAdapter.this);
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package de.dennisguse.opentracks.ui;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.widget.ArrayAdapter;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class ZoneOffsetAdapter extends ArrayAdapter<ZoneOffset> {
|
||||
public ZoneOffsetAdapter(@NonNull Context context, int resource) {
|
||||
super(context, resource, IntStream.range(-12, +12)
|
||||
.boxed()
|
||||
.map(ZoneOffset::ofHours)
|
||||
.toList());
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -83,8 +83,9 @@ public class AggregatedStatisticsActivity extends AbstractActivity implements Fi
|
||||
outState.putBoolean(STATE_ARE_FILTERS_APPLIED, areFiltersApplied);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View getRootView() {
|
||||
protected View createRootView() {
|
||||
viewBinding = AggregatedStatsBinding.inflate(getLayoutInflater());
|
||||
return viewBinding.getRoot();
|
||||
}
|
||||
|
||||
+5
-1
@@ -75,7 +75,8 @@ public class ConfirmDeleteDialogFragment extends DialogFragment {
|
||||
getActivity(),
|
||||
titleId,
|
||||
getString(messageId),
|
||||
(dialog, which) -> caller.onConfirmDeleteDone(trackIds)
|
||||
(dialog, which) -> caller.onConfirmDeleteDone(trackIds),
|
||||
(dialog, which) -> caller.onConfirmDeleteAbort(trackIds)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -86,6 +87,9 @@ public class ConfirmDeleteDialogFragment extends DialogFragment {
|
||||
*/
|
||||
public interface ConfirmDeleteCaller {
|
||||
|
||||
default void onConfirmDeleteAbort(Track.Id... trackIds) {
|
||||
}
|
||||
|
||||
void onConfirmDeleteDone(Track.Id... trackIds);
|
||||
}
|
||||
}
|
||||
@@ -71,13 +71,18 @@ public class DeleteMarkerDialogFragment extends DialogFragment {
|
||||
int titleId = markerIds.length > 1 ? R.string.generic_delete_selected_confirm_title : R.string.marker_delete_one_confirm_title;
|
||||
int messageId = markerIds.length > 1 ? R.string.marker_delete_multiple_confirm_message : R.string.marker_delete_one_confirm_message;
|
||||
return DialogUtils.createConfirmationDialog(
|
||||
fragmentActivity, titleId, getString(messageId), (dialog, which) -> new Thread(() -> {
|
||||
fragmentActivity, titleId, getString(messageId),
|
||||
(dialog, which) -> new Thread(() -> {
|
||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(fragmentActivity);
|
||||
for (Marker.Id markerId : markerIds) {
|
||||
contentProviderUtils.deleteMarker(context, markerId);
|
||||
}
|
||||
caller.onMarkerDeleted();
|
||||
}).start());
|
||||
}
|
||||
).start(),
|
||||
(dialog, which) -> {
|
||||
// None
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,17 +24,15 @@ import android.view.View;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
import androidx.viewpager2.widget.ViewPager2;
|
||||
|
||||
import de.dennisguse.opentracks.AbstractActivity;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.Marker;
|
||||
import de.dennisguse.opentracks.data.tables.MarkerColumns;
|
||||
import de.dennisguse.opentracks.databinding.MarkerDetailActivityBinding;
|
||||
import de.dennisguse.opentracks.ui.markers.DeleteMarkerDialogFragment.DeleteMarkerCaller;
|
||||
|
||||
@@ -53,7 +51,7 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
|
||||
|
||||
private MarkerDetailActivityBinding viewBinding;
|
||||
|
||||
private List<Marker.Id> markerIds;
|
||||
private Cursor cursor;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
@@ -69,40 +67,29 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
|
||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(this);
|
||||
Marker marker = contentProviderUtils.getMarker(markerId);
|
||||
|
||||
markerIds = new ArrayList<>();
|
||||
//TODO only load used data: ID + name
|
||||
cursor = contentProviderUtils.getMarkerCursor(marker.getTrackId(), null, -1);
|
||||
if (cursor == null) {
|
||||
finish();
|
||||
}
|
||||
|
||||
int markerIndex = -1;
|
||||
|
||||
//TODO Load only markerIds, not the whole marker
|
||||
try (Cursor cursor = contentProviderUtils.getMarkerCursor(marker.getTrackId(), null, -1)) {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
for (int i = 0; i < cursor.getCount(); i++) {
|
||||
Marker currentMarker = contentProviderUtils.createMarker(cursor);
|
||||
markerIds.add(currentMarker.getId());
|
||||
if (markerId.equals(currentMarker.getId())) {
|
||||
markerIndex = markerIds.size() - 1;
|
||||
}
|
||||
|
||||
cursor.moveToNext();
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
while (markerIndex == -1 && cursor.moveToNext()) {
|
||||
Marker.Id currentId = new Marker.Id(cursor.getLong(cursor.getColumnIndexOrThrow(MarkerColumns._ID)));
|
||||
if (markerId.equals(currentId)) {
|
||||
markerIndex = cursor.getPosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final MarkerDetailPagerAdapter markerAdapter = new MarkerDetailPagerAdapter(getSupportFragmentManager());
|
||||
|
||||
final MarkerDetailPagerAdapter markerAdapter = new MarkerDetailPagerAdapter(this);
|
||||
viewBinding.makerDetailActivityViewPager.setAdapter(markerAdapter);
|
||||
viewBinding.makerDetailActivityViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
}
|
||||
|
||||
viewBinding.makerDetailActivityViewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
viewBinding.markerDetailToolbar.setTitle(markerAdapter.getPageTitle(position));
|
||||
// viewBinding.bottomAppBarLayout.bottomAppBarTitle.setText(markerAdapter.getPageTitle(position)); TOOD
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
}
|
||||
});
|
||||
viewBinding.makerDetailActivityViewPager.setCurrentItem(markerIndex == -1 ? 0 : markerIndex);
|
||||
@@ -110,43 +97,49 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
|
||||
setSupportActionBar(viewBinding.bottomAppBarLayout.bottomAppBar);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View getRootView() {
|
||||
protected View createRootView() {
|
||||
viewBinding = MarkerDetailActivityBinding.inflate(getLayoutInflater());
|
||||
return viewBinding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
viewBinding = null;
|
||||
|
||||
if (cursor != null) cursor.close();
|
||||
cursor = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMarkerDeleted() {
|
||||
runOnUiThread(this::finish);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marker detail pager adapter.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
private class MarkerDetailPagerAdapter extends FragmentStatePagerAdapter {
|
||||
private class MarkerDetailPagerAdapter extends FragmentStateAdapter {
|
||||
|
||||
MarkerDetailPagerAdapter(FragmentManager fragmentManager) {
|
||||
super(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
|
||||
MarkerDetailPagerAdapter(@NonNull FragmentActivity fa) {
|
||||
super(fa);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Fragment getItem(int position) {
|
||||
return MarkerDetailFragment.newInstance(markerIds.get(position));
|
||||
public Fragment createFragment(int position) {
|
||||
cursor.moveToPosition(position);
|
||||
return MarkerDetailFragment.newInstance(new Marker.Id(cursor.getLong(cursor.getColumnIndexOrThrow(MarkerColumns._ID))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return cursor.getCount();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
return getString(R.string.marker_title, position + 1, getCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return markerIds.size();
|
||||
cursor.moveToPosition(position);
|
||||
return getString(R.string.marker_detail_title, position + 1, getItemCount(), cursor.getString(cursor.getColumnIndexOrThrow(MarkerColumns.NAME)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@ package de.dennisguse.opentracks.ui.markers;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
@@ -26,22 +25,18 @@ import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.Animation.AnimationListener;
|
||||
import android.view.animation.AnimationUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.view.MenuProvider;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import java.time.Duration;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.Marker;
|
||||
import de.dennisguse.opentracks.databinding.MarkerDetailFragmentBinding;
|
||||
import de.dennisguse.opentracks.share.ShareUtils;
|
||||
import de.dennisguse.opentracks.ui.util.ListItemUtils;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
|
||||
@@ -55,43 +50,15 @@ public class MarkerDetailFragment extends Fragment {
|
||||
private static final String TAG = MarkerDetailFragment.class.getSimpleName();
|
||||
private static final String KEY_MARKER_ID = "markerId";
|
||||
|
||||
private static final Duration HIDE_TEXT_DELAY = Duration.ofSeconds(4);
|
||||
|
||||
private MenuItem shareMarkerImageMenuItem;
|
||||
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
private Handler handler;
|
||||
|
||||
private Marker.Id markerId;
|
||||
private Marker marker;
|
||||
|
||||
private MarkerDetailFragmentBinding viewBinding;
|
||||
|
||||
private final Runnable hideText = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Animation animation = AnimationUtils.loadAnimation(getActivity(), R.anim.fadeout);
|
||||
animation.setAnimationListener(new AnimationListener() {
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animation anim) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation anim) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation anim) {
|
||||
viewBinding.markerDetailMarkerTextGradient.setVisibility(View.GONE);
|
||||
viewBinding.markerDetailMarkerInfo.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
viewBinding.markerDetailMarkerTextGradient.startAnimation(animation);
|
||||
viewBinding.markerDetailMarkerInfo.startAnimation(animation);
|
||||
}
|
||||
};
|
||||
|
||||
public static MarkerDetailFragment newInstance(Marker.Id markerId) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(KEY_MARKER_ID, markerId);
|
||||
@@ -111,43 +78,70 @@ public class MarkerDetailFragment extends Fragment {
|
||||
getParentFragmentManager().popBackStack();
|
||||
return;
|
||||
}
|
||||
contentProviderUtils = new ContentProviderUtils(getActivity());
|
||||
handler = new Handler();
|
||||
setHasOptionsMenu(true);
|
||||
contentProviderUtils = new ContentProviderUtils(getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
viewBinding = MarkerDetailFragmentBinding.inflate(inflater, container, false);
|
||||
|
||||
viewBinding.markerDetailMarkerPhoto.setOnClickListener(v -> {
|
||||
handler.removeCallbacks(hideText);
|
||||
int visibility = viewBinding.markerDetailMarkerInfo.getVisibility() == View.GONE ? View.VISIBLE : View.GONE;
|
||||
viewBinding.markerDetailMarkerTextGradient.setVisibility(visibility);
|
||||
viewBinding.markerDetailMarkerInfo.setVisibility(visibility);
|
||||
if (visibility == View.VISIBLE) {
|
||||
handler.postDelayed(hideText, HIDE_TEXT_DELAY.toMillis());
|
||||
requireActivity().addMenuProvider(new MenuProvider() {
|
||||
@Override
|
||||
public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) {
|
||||
menuInflater.inflate(R.menu.marker_detail, menu);
|
||||
shareMarkerImageMenuItem = menu.findItem(R.id.marker_detail_share);
|
||||
updateMarker();
|
||||
updateMenuItems();
|
||||
}
|
||||
});
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemSelected(@NonNull MenuItem item) {
|
||||
FragmentActivity fragmentActivity = getActivity();
|
||||
|
||||
if (item.getItemId() == R.id.marker_detail_show_on_map) {
|
||||
IntentUtils.showCoordinateOnMap(getContext(), marker);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (item.getItemId() == R.id.marker_detail_edit) {
|
||||
Intent intent = IntentUtils.newIntent(fragmentActivity, MarkerEditActivity.class)
|
||||
.putExtra(MarkerEditActivity.EXTRA_MARKER_ID, markerId);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (item.getItemId() == R.id.marker_detail_share) {
|
||||
if (marker.hasPhoto()) {
|
||||
Intent intent = ShareUtils.newShareFileIntent(getContext(), marker.getId());
|
||||
intent = Intent.createChooser(intent, null);
|
||||
startActivity(intent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (item.getItemId() == R.id.marker_detail_delete) {
|
||||
DeleteMarkerDialogFragment.showDialog(getChildFragmentManager(), markerId);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}, getViewLifecycleOwner(), Lifecycle.State.RESUMED);
|
||||
|
||||
return viewBinding.getRoot();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// Need to update the marker in case returning after an edit
|
||||
updateMarker(true);
|
||||
updateMarker();
|
||||
updateUi();
|
||||
updateMenuItems();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
handler.removeCallbacks(hideText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
@@ -166,97 +160,35 @@ public class MarkerDetailFragment extends Fragment {
|
||||
super.setMenuVisibility(menuVisible);
|
||||
// View pager caches the neighboring fragments in the resumed state.
|
||||
// If becoming visible from the resumed state, update the UI to display the text above the image.
|
||||
if (isResumed()) {
|
||||
if (menuVisible) {
|
||||
updateUi();
|
||||
} else {
|
||||
handler.removeCallbacks(hideText);
|
||||
}
|
||||
if (isResumed() && menuVisible) {
|
||||
updateUi();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.marker_detail, menu);
|
||||
shareMarkerImageMenuItem = menu.findItem(R.id.marker_detail_share);
|
||||
updateMarker(false);
|
||||
updateMenuItems();
|
||||
}
|
||||
|
||||
private void updateMenuItems() {
|
||||
if (shareMarkerImageMenuItem != null)
|
||||
shareMarkerImageMenuItem.setVisible(marker.hasPhoto());
|
||||
if (shareMarkerImageMenuItem != null) { // MenuProvider might not yet been initialized.
|
||||
// shareMarkerImageMenuItem.setEnabled(marker.hasPhoto());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
FragmentActivity fragmentActivity = getActivity();
|
||||
|
||||
if (item.getItemId() == R.id.marker_detail_show_on_map) {
|
||||
IntentUtils.showCoordinateOnMap(getContext(), marker);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (item.getItemId() == R.id.marker_detail_edit) {
|
||||
Intent intent = IntentUtils.newIntent(fragmentActivity, MarkerEditActivity.class)
|
||||
.putExtra(MarkerEditActivity.EXTRA_MARKER_ID, markerId);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (item.getItemId() == R.id.marker_detail_share) {
|
||||
if (marker.hasPhoto()) {
|
||||
Intent intent = ShareUtils.newShareFileIntent(getContext(), marker.getId());
|
||||
intent = Intent.createChooser(intent, null);
|
||||
startActivity(intent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (item.getItemId() == R.id.marker_detail_delete) {
|
||||
DeleteMarkerDialogFragment.showDialog(getChildFragmentManager(), markerId);
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void updateMarker(boolean refresh) {
|
||||
if (refresh || marker == null) {
|
||||
marker = contentProviderUtils.getMarker(markerId);
|
||||
if (marker == null) {
|
||||
Log.d(TAG, "marker is null");
|
||||
getParentFragmentManager().popBackStack();
|
||||
}
|
||||
private void updateMarker() {
|
||||
marker = contentProviderUtils.getMarker(markerId);
|
||||
if (marker == null) {
|
||||
Log.d(TAG, "marker is null");
|
||||
getParentFragmentManager().popBackStack();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateUi() {
|
||||
boolean hasPhoto = marker.hasPhoto();
|
||||
if (hasPhoto) {
|
||||
handler.removeCallbacks(hideText);
|
||||
if (marker.hasPhoto()) {
|
||||
viewBinding.markerDetailMarkerPhoto.setImageURI(marker.getPhotoUrl());
|
||||
handler.postDelayed(hideText, HIDE_TEXT_DELAY.toMillis());
|
||||
} else {
|
||||
viewBinding.markerDetailMarkerPhoto.setImageResource(MarkerUtils.ICON_ID);
|
||||
viewBinding.markerDetailMarkerPhoto.setImageDrawable(MarkerUtils.getDefaultPhoto(getContext()));
|
||||
}
|
||||
|
||||
ListItemUtils.setTextView(getActivity(), viewBinding.markerDetailMarkerName, marker.getName(), hasPhoto);
|
||||
viewBinding.markerDetailMarkerCategory.setText(StringUtils.getCategory(marker.getCategory()));
|
||||
viewBinding.markerDetailMarkerDescription.setText(marker.getDescription());
|
||||
|
||||
|
||||
ListItemUtils.setTextView(getActivity(), viewBinding.markerDetailMarkerCategory, StringUtils.getCategory(marker.getCategory()), hasPhoto);
|
||||
|
||||
ListItemUtils.setTextView(getActivity(), viewBinding.markerDetailMarkerDescription, marker.getDescription(), hasPhoto);
|
||||
|
||||
setLocation(hasPhoto);
|
||||
}
|
||||
|
||||
private void setLocation(boolean addShadow) {
|
||||
String value = "[" + getString(R.string.stats_latitude) + " "
|
||||
+ StringUtils.formatCoordinate(getContext(), marker.getLatitude()) + ", "
|
||||
+ getString(R.string.stats_longitude) + " "
|
||||
+ StringUtils.formatCoordinate(getContext(), marker.getLongitude()) + "]";
|
||||
|
||||
ListItemUtils.setTextView(getActivity(), viewBinding.markerDetailMarkerLocation, value, addShadow);
|
||||
viewBinding.markerDetailMarkerLocation.setText(StringUtils.formatCoordinate(getContext(), marker.getPosition()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,8 +85,9 @@ public class MarkerEditActivity extends AbstractActivity {
|
||||
// UI elements
|
||||
private MarkerEditBinding viewBinding;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View getRootView() {
|
||||
protected View createRootView() {
|
||||
viewBinding = MarkerEditBinding.inflate(getLayoutInflater());
|
||||
return viewBinding.getRoot();
|
||||
}
|
||||
|
||||
@@ -145,8 +145,9 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
||||
contentProviderUtils = null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected View getRootView() {
|
||||
protected View createRootView() {
|
||||
viewBinding = MarkerListBinding.inflate(getLayoutInflater());
|
||||
|
||||
viewBinding.markerListSearchView.getEditText().setOnEditorActionListener((v, actionId, event) -> {
|
||||
|
||||
@@ -164,7 +164,6 @@ public class MarkerListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
|
||||
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
||||
|
||||
private final MarkerListItemBinding viewBinding;
|
||||
private final View view;
|
||||
|
||||
private Marker marker;
|
||||
|
||||
@@ -172,23 +171,20 @@ public class MarkerListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
|
||||
super(view);
|
||||
|
||||
viewBinding = MarkerListItemBinding.bind(view);
|
||||
this.view = view;
|
||||
|
||||
view.setOnClickListener(this);
|
||||
view.setOnLongClickListener(this);
|
||||
viewBinding.getRoot().setOnClickListener(this);
|
||||
viewBinding.getRoot().setOnLongClickListener(this);
|
||||
}
|
||||
|
||||
public void bind(Marker marker) {
|
||||
this.marker = marker;
|
||||
|
||||
viewBinding.markerListItemPhoto.setVisibility(marker.hasPhoto() ? View.VISIBLE : View.GONE);
|
||||
viewBinding.markerListItemTextGradient.setVisibility(marker.hasPhoto() ? View.VISIBLE : View.GONE);
|
||||
if (marker.hasPhoto()) {
|
||||
int height = ThemeUtils.getPhotoHeight(context);
|
||||
ViewGroup.LayoutParams params = viewBinding.markerListItemPhoto.getLayoutParams();
|
||||
params.height = height;
|
||||
viewBinding.markerListItemPhoto.setLayoutParams(params);
|
||||
viewBinding.markerListItemTextGradient.setLayoutParams(params);
|
||||
|
||||
asyncLoadPhoto(viewBinding.markerListItemPhoto, marker.getPhotoUrl(), marker.getId());
|
||||
}
|
||||
@@ -208,7 +204,7 @@ public class MarkerListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
|
||||
|
||||
public void setSelected(boolean isSelected) {
|
||||
selection.put((int) getId(), isSelected);
|
||||
view.setActivated(isSelected);
|
||||
viewBinding.getRoot().setActivated(isSelected);
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
|
||||
@@ -26,15 +26,13 @@ public class MarkerUtils {
|
||||
|
||||
private static final String TAG = FileUtils.class.getSimpleName();
|
||||
|
||||
static final int ICON_ID = R.drawable.ic_marker_orange_pushpin_with_shadow;
|
||||
|
||||
private static final String JPEG_EXTENSION = "jpeg";
|
||||
|
||||
private MarkerUtils() {
|
||||
}
|
||||
|
||||
public static Drawable getDefaultPhoto(@NonNull Context context) {
|
||||
return ContextCompat.getDrawable(context, ICON_ID);
|
||||
return ContextCompat.getDrawable(context, R.drawable.ic_marker_show_24dp);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,12 +42,12 @@ public class DialogUtils {
|
||||
* @param message the message
|
||||
* @param okListener the listener when OK is clicked
|
||||
*/
|
||||
public static Dialog createConfirmationDialog(final Context context, int titleId, String message, DialogInterface.OnClickListener okListener) {
|
||||
public static Dialog createConfirmationDialog(final Context context, int titleId, String message, DialogInterface.OnClickListener okListener, DialogInterface.OnClickListener cancelListener) {
|
||||
return new AlertDialog.Builder(context)
|
||||
.setCancelable(true)
|
||||
.setIcon(R.drawable.ic_delete_forever_24dp)
|
||||
.setMessage(message)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setNegativeButton(android.R.string.cancel, cancelListener)
|
||||
.setPositiveButton(android.R.string.ok, okListener)
|
||||
.setTitle(titleId).create();
|
||||
}
|
||||
|
||||
@@ -17,11 +17,8 @@
|
||||
package de.dennisguse.opentracks.ui.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
@@ -89,29 +86,6 @@ public class ListItemUtils {
|
||||
return timeDistanceText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a text view.
|
||||
*
|
||||
* @param context the context
|
||||
* @param textView the text view
|
||||
* @param value the value for the text view
|
||||
* @param addShadow true to add shadow
|
||||
*/
|
||||
public static void setTextView(Context context, TextView textView, String value, boolean addShadow) {
|
||||
if (value == null || value.length() == 0) {
|
||||
textView.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
|
||||
textView.setVisibility(View.VISIBLE);
|
||||
textView.setText(value);
|
||||
if (addShadow) {
|
||||
textView.setShadowLayer(5, 0, 2, ContextCompat.getColor(context, android.R.color.black));
|
||||
} else {
|
||||
textView.setShadowLayer(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setDateAndTime(Context context, TextView dateView, TextView timeView, Instant time, ZoneOffset timeZone) {
|
||||
OffsetDateTime offsetDateTime = OffsetDateTime.ofInstant(time, timeZone);
|
||||
String dateContent = StringUtils.formatDateTodayRelative(context, offsetDateTime);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user