forked from upstream-mirrors/OpenTracks
Compare commits
103 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 |
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>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+5
-5
@@ -6,7 +6,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.10.0'
|
||||
classpath 'com.android.tools.build:gradle:8.11.0'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId 'de.dennisguse.opentracks'
|
||||
versionCode 6130
|
||||
versionName "v4.19.3"
|
||||
versionCode 6233
|
||||
versionName "v4.21.0"
|
||||
|
||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||
|
||||
@@ -142,7 +142,7 @@ 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.1.0'
|
||||
implementation 'androidx.gridlayout:gridlayout:1.1.0'
|
||||
@@ -150,7 +150,7 @@ dependencies {
|
||||
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'
|
||||
|
||||
@@ -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,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());
|
||||
|
||||
+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());
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -91,8 +91,8 @@ 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("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()) : ""),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -7,6 +7,8 @@ import android.util.TypedValue;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.google.android.material.R;
|
||||
|
||||
public class ThemeUtils {
|
||||
|
||||
private ThemeUtils() {
|
||||
@@ -18,7 +20,7 @@ public class ThemeUtils {
|
||||
public static int getBackgroundColor(Context context) {
|
||||
TypedValue typedValue = new TypedValue();
|
||||
Resources.Theme theme = context.getTheme();
|
||||
theme.resolveAttribute(android.R.attr.colorBackground, typedValue, true);
|
||||
theme.resolveAttribute(R.attr.colorSurface, typedValue, true);
|
||||
|
||||
return ContextCompat.getColor(context, typedValue.resourceId);
|
||||
}
|
||||
@@ -26,7 +28,7 @@ public class ThemeUtils {
|
||||
public static int getTextColorPrimary(Context context) {
|
||||
TypedValue typedValue = new TypedValue();
|
||||
Resources.Theme theme = context.getTheme();
|
||||
theme.resolveAttribute(android.R.attr.textColorPrimary, typedValue, true);
|
||||
theme.resolveAttribute(R.attr.colorOnSurface, typedValue, true);
|
||||
|
||||
return ContextCompat.getColor(context, typedValue.resourceId);
|
||||
}
|
||||
@@ -34,20 +36,20 @@ public class ThemeUtils {
|
||||
public static int getTextColorSecondary(Context context) {
|
||||
TypedValue typedValue = new TypedValue();
|
||||
Resources.Theme theme = context.getTheme();
|
||||
theme.resolveAttribute(android.R.attr.textColorSecondary, typedValue, true);
|
||||
theme.resolveAttribute(R.attr.colorSecondary, typedValue, true);
|
||||
|
||||
return ContextCompat.getColor(context, typedValue.resourceId);
|
||||
}
|
||||
|
||||
public static int getFontSizeSmallInPx(Context context) {
|
||||
TypedArray typedArray = context.obtainStyledAttributes(com.google.android.material.R.style.TextAppearance_MaterialComponents_Body2, new int[]{android.R.attr.textSize});
|
||||
TypedArray typedArray = context.obtainStyledAttributes(R.style.TextAppearance_MaterialComponents_Body2, new int[]{android.R.attr.textSize});
|
||||
int fontSize = typedArray.getDimensionPixelSize(0, 12);
|
||||
typedArray.recycle();
|
||||
return fontSize;
|
||||
}
|
||||
|
||||
public static int getFontSizeMediumInPx(Context context) {
|
||||
TypedArray typedArray = context.obtainStyledAttributes(com.google.android.material.R.style.TextAppearance_MaterialComponents_Body1, new int[]{android.R.attr.textSize});
|
||||
TypedArray typedArray = context.obtainStyledAttributes(R.style.TextAppearance_MaterialComponents_Body1, new int[]{android.R.attr.textSize});
|
||||
int fontSize = typedArray.getDimensionPixelSize(0, 15);
|
||||
typedArray.recycle();
|
||||
return fontSize;
|
||||
|
||||
@@ -50,23 +50,14 @@ public class IntentUtils {
|
||||
return new Intent(context, cls).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
}
|
||||
|
||||
public static void showCoordinateOnMap(Context context, Marker marker) {
|
||||
showCoordinateOnMap(context, marker.getLatitude(), marker.getLongitude(), marker.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Send intent to show coordinates on a map (needs an another app).
|
||||
*
|
||||
* @param context the context
|
||||
* @param latitude the latitude
|
||||
* @param longitude the longitude
|
||||
* @param label the label
|
||||
*/
|
||||
private static void showCoordinateOnMap(Context context, double latitude, double longitude, String label) {
|
||||
public static void showCoordinateOnMap(Context context, Marker marker) {
|
||||
//SEE https://developer.android.com/guide/components/intents-common.html#Maps
|
||||
String uri = "geo:0,0?q=" + latitude + "," + longitude;
|
||||
if (label != null && label.length() > 0) {
|
||||
uri += "(" + label + ")";
|
||||
String uri = "geo:0,0?q=" + marker.getPosition().latitude() + "," + marker.getPosition().longitude();
|
||||
if (marker.getName() != null && !marker.getName().isEmpty()) {
|
||||
uri += "(" + marker.getName() + ")";
|
||||
}
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(uri));
|
||||
|
||||
@@ -45,6 +45,7 @@ import de.dennisguse.opentracks.data.models.Cadence;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
import de.dennisguse.opentracks.data.models.Power;
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
|
||||
@@ -142,25 +143,8 @@ public class StringUtils {
|
||||
return df.format(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a coordinate
|
||||
*
|
||||
* @param context the context's object.
|
||||
* @param coordinate the coordinate
|
||||
*/
|
||||
public static String formatCoordinate(Context context, double coordinate) {
|
||||
return context.getString(R.string.location_coordinate, Location.convert(coordinate, Location.FORMAT_DEGREES));
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a complete coordinate (latitude, longitude)
|
||||
*
|
||||
* @param context the context's object.
|
||||
* @param latitude the coordinate's latitude.
|
||||
* @param longitude the coordinate's longitude.
|
||||
*/
|
||||
public static String formatCoordinate(Context context, double latitude, double longitude) {
|
||||
return context.getString(R.string.location_latitude_longitude, Location.convert(latitude, Location.FORMAT_DEGREES), Location.convert(longitude, Location.FORMAT_DEGREES));
|
||||
public static String formatCoordinate(Context context, Position position) {
|
||||
return context.getString(R.string.location_latitude_longitude, Location.convert(position.latitude(), Location.FORMAT_DEGREES), Location.convert(position.longitude(), Location.FORMAT_DEGREES));
|
||||
}
|
||||
|
||||
public static Pair<String, String> getHeartRateParts(Context context, HeartRate heartrate) {
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package de.dennisguse.opentracks.util;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
|
||||
/**
|
||||
* Utilities for updating track.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
//TOOD Refactor ActivityType handling in here!
|
||||
@Deprecated //TODO Refactor: all this should happen somewhere else (ContentProviderUtils?)
|
||||
public class TrackUtils {
|
||||
|
||||
private TrackUtils() {
|
||||
}
|
||||
|
||||
public static void updateTrack(Context context, Track track, String name, String activityTypeLocalized, String description, ContentProviderUtils contentProviderUtils) {
|
||||
updateTrack(context, track, name, activityTypeLocalized, ActivityType.findByLocalizedString(context, activityTypeLocalized), description, contentProviderUtils);
|
||||
}
|
||||
|
||||
public static void updateTrack(Context context, Track track, String name, String activityTypeLocalized, ActivityType activityType, String description, ContentProviderUtils contentProviderUtils) {
|
||||
boolean update = false;
|
||||
if (name != null) {
|
||||
track.setName(name);
|
||||
update = true;
|
||||
}
|
||||
if (activityTypeLocalized != null) {
|
||||
track.setActivityTypeLocalized(activityTypeLocalized);
|
||||
update = true;
|
||||
}
|
||||
if (activityType != null) {
|
||||
track.setActivityType(activityType);
|
||||
} else if (activityTypeLocalized != null) {
|
||||
track.setActivityType(ActivityType.findByLocalizedString(context, activityTypeLocalized)
|
||||
);
|
||||
}
|
||||
if (description != null) {
|
||||
track.setDescription(description);
|
||||
update = true;
|
||||
}
|
||||
if (update) {
|
||||
contentProviderUtils.updateTrack(track);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -265,7 +265,7 @@ public abstract class GenericStatisticsViewHolder extends StatisticViewHolder<St
|
||||
TrackPoint latestTrackPoint = data.latestTrackPoint();
|
||||
String value;
|
||||
if (latestTrackPoint != null && latestTrackPoint.hasLocation()) {
|
||||
value = StringUtils.formatCoordinate(getContext(), latestTrackPoint.getLatitude(), latestTrackPoint.getLongitude());
|
||||
value = StringUtils.formatCoordinate(getContext(), latestTrackPoint.getPosition());
|
||||
} else {
|
||||
value = getContext().getString(R.string.value_unknown);
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background" />
|
||||
<foreground>
|
||||
<inset
|
||||
android:inset="20dp"
|
||||
android:drawable="@drawable/ic_logo_color_24dp" />
|
||||
</foreground>
|
||||
<monochrome>
|
||||
<inset
|
||||
android:inset="20dp"
|
||||
android:drawable="@drawable/ic_logo_24dp" />
|
||||
</monochrome>
|
||||
</adaptive-icon>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user