From 216d40c2c5465749c3b3fc401d4f28c3b26476fe Mon Sep 17 00:00:00 2001 From: EiSiMo Date: Mon, 13 Apr 2026 19:05:17 +0200 Subject: [PATCH] Custom icons, improved GPS, history layout, address fixes - Replace placeholder SVG icons with custom PNG icons (80x80 padded) - AffineTransform for pixel-perfect icon centering in menu ring - Colored circles behind icons with icon drawn at 60% size - GPS: always wait full timeout (30s), keep best quality fix - History: show address, PLZ+city, coordinates on separate lines - Geocoding: fall back to "name" field for street-level results, include PLZ+city in cached address data - Address distance threshold raised to 70m Co-Authored-By: Claude Opus 4.6 (1M context) --- resources/drawables/drawables.xml | 22 +++---- resources/drawables/icon_arrest.png | Bin 0 -> 1422 bytes resources/drawables/icon_arrival.png | Bin 0 -> 1016 bytes resources/drawables/icon_delete.png | Bin 0 -> 1086 bytes resources/drawables/icon_end.png | Bin 0 -> 670 bytes resources/drawables/icon_event.png | Bin 0 -> 676 bytes resources/drawables/icon_evidence.png | Bin 0 -> 2218 bytes resources/drawables/icon_force.png | Bin 0 -> 1364 bytes resources/drawables/icon_history.png | Bin 0 -> 1479 bytes resources/drawables/icon_sighting.png | Bin 0 -> 1362 bytes resources/drawables/icon_start.png | Bin 0 -> 765 bytes resources/drawables/launcher_icon.png | Bin 0 -> 2343 bytes source/Config.mc | 3 +- source/Event.mc | 12 ++-- source/GeocodingService.mc | 51 ++++++++-------- source/GpsService.mc | 19 +++--- source/HistoryView.mc | 85 +++++++++++++------------- source/MenuView.mc | 17 ++++-- 18 files changed, 108 insertions(+), 101 deletions(-) create mode 100644 resources/drawables/icon_arrest.png create mode 100644 resources/drawables/icon_arrival.png create mode 100644 resources/drawables/icon_delete.png create mode 100644 resources/drawables/icon_end.png create mode 100644 resources/drawables/icon_event.png create mode 100644 resources/drawables/icon_evidence.png create mode 100644 resources/drawables/icon_force.png create mode 100644 resources/drawables/icon_history.png create mode 100644 resources/drawables/icon_sighting.png create mode 100644 resources/drawables/icon_start.png create mode 100644 resources/drawables/launcher_icon.png diff --git a/resources/drawables/drawables.xml b/resources/drawables/drawables.xml index d0febbd..4923670 100644 --- a/resources/drawables/drawables.xml +++ b/resources/drawables/drawables.xml @@ -1,13 +1,13 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/resources/drawables/icon_arrest.png b/resources/drawables/icon_arrest.png new file mode 100644 index 0000000000000000000000000000000000000000..1a6f58cbf37d6eb9b531f8b5160a5e3a421c59dd GIT binary patch literal 1422 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 literal 0 HcmV?d00001 diff --git a/resources/drawables/icon_delete.png b/resources/drawables/icon_delete.png new file mode 100644 index 0000000000000000000000000000000000000000..639faf0144fc21e6535a0be5d704032602107f88 GIT binary patch literal 1086 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r50wh%=tki&%Lb6AYF9SmrkloP2!0-zw)bN6V zq11qZ;Z*_ygVhWM2JwP9y8>;15==?n?k)@rt9q4M1MG8=m zy~NYkmHib9uYkGEA&KUHKus?^T^vI)?!BF9>o096alD>WzjxIV)=m!|o&e4^z1Um4 zp|6gZC%g>$$f&jAm!MW?Ql|PdBcn|QF0C6Bj<9%NJ#b~&B9ASe3XY0Tp59rQ^<=lV zmi?Ud6Se;9xNdyB`F~xGeg9+T_QMYgEM)lFm8F8$6!x`WnsnSU+lMzw%3+%Cd=uMT zBf+gdT~ZoP?_sVha&p&}P3q%JX#L*2HGY;&msIy1-X*SC8lRT1Yk%Mr71@1<^Y5f2 zao3s0FIq{6U77n>@OzD_+<6J+C>wrVF;{Mh=c<*H_Qhw(6eiscTCDTA)$jgW1y9+1 zn+~mrZS*!-)A4(jhZF>S^c^#YeIGtWq*9hrB4 zF-a{Tvcn?LX0lgWaQP|sc~PP>6^@&4?n+xSbIUG1+jA=-bMgy3mg%0{A*#J?<&3rV zD-Zl$^Z7#gie0?UHIG=QFT1KLmN_}qY>`ca{FbxY-hUD%7cE%g*SKn0xQOPWEt90? z@8q)yT_)Loz)Up$-eca+dlp^PwM=Ma<=gdBI?nj~?E_t_kEA-fY9{QL^?G8uTP|q9 zfkgB8q}fK6ul7CnvXhP$@kr>ZFQ0s2SMaUn|9t;06yD{WnzR@z{xo|{--8+=={GeX?ue+dIHy$#DgGwxcpt3=aaXlZ3)0nzaPQO!c21`W6kC7HRY z#U+Wk1!#IqO|49zmK@!*gbk=i5~L?MKdq!Zu_%?Hyu4g5GcUV1Ik6yBFTW^#_B$IX PpdtoOS3j3^P6;15==?n?k)@rt9q4TC3R0J&o=k zn4-A1O8lRo{QVJ#c2zf+;~> znoFkyy?Sq!ZM5|um+ymfzrJ?AE;z;P&vPT~pw#62xr`4#SEZbuHTgNOahyUyE^pUo zruEmBSm+ld_jY}byRk9tXhrh3$SrM0E7%`(exG0Q`OMF!rZ*S+PxsA|tJFQW>A3vH zC%x=@^_~4Dbxh6cy|~@+a;NC~JAru-b0U5oymY#^TafqMUa{vVT^1)-PCc!XWcYqV z*ZN~oOcy6icQSc)w1ZcEPthYoX<_?GZF`I!?Rc=n z-#f8u>3bhwFsqihMwFx^mZVxG7o`Fz1|tI_GhG7{T|?s#Lo+L5ATriAFt9Q((3h&- zgrXrgKP5A*5>11Vft8^dL_^almtdd<4Y&;@nYpROC5gEOXnIUdtxQZHdX8>d!UohM v3DOgspH@mmtT}V`<;yxP!WTttDnm{r-UW|wtoX^ literal 0 HcmV?d00001 diff --git a/resources/drawables/icon_event.png b/resources/drawables/icon_event.png new file mode 100644 index 0000000000000000000000000000000000000000..b4788bf1cac403a9c8a01baea9a5430f77758680 GIT binary patch literal 676 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r50wh%=tki&%Lb6AYF9SmrkloP2!0-zw)bN6V zq11qZ;Z*_ygVhWM2JwP9y8>;15==?n?k)@rt9q4fE|4?I?T7JSy>zMipuVx(< z|2X%E_y*tUP8&p6Zfn0^3!8OPQSKnA{d| z^|i66N=S2h8JG!8NicAq_eNZX{p_h73SaM?nYCoH`kK9>(PcIpw(jhlGpQ@w-(>Fm ztLHQJTo0LICf)pW``)(F9Gz|9M>DGU+zq$<`EWX>IO+4UFQ3k=tUqL*VO+j=U4DS= zC-+G_Ty`H%Sf-!JsOP!#e9jS7uHCJ znYF&%m-PIknxcge&`~)m8V7H91T-~nVN?!eEq`PEbKcy*RAse#5wo9%U6$3$XahaZ zq4-bfeOir5#7x&b4`5)cmbgZgq$HN4S|t~y0x1R~10yqC0~1|C;}AnLD`Oxs(Kax! zGB9|x*A*0j2o1UUDVb@NXc~-wN=+de>O0~+oZULGeQ&TGws3k`? zEnx%dkp$@p&QB{TPb^AhC@(M9%goCzPEIUH)ypqRpZ(583aE&|)78&qol`;+0DgS( Ae*gdg literal 0 HcmV?d00001 diff --git a/resources/drawables/icon_evidence.png b/resources/drawables/icon_evidence.png new file mode 100644 index 0000000000000000000000000000000000000000..f9e13a999756a7173b0d2a91abc6134bba5ffd13 GIT binary patch literal 2218 zcmZ9NXHe7W7RLWTkkFK-6zOd#0TT!<5;U}c2vQ>gLTHglNlXF}To40SMCly?5$PpJ zSUMu2Qbdt19qA=h6-0D#Z=AieGj~6n^StMI&u`v2^L{w7wl-*f9tj=*0QfB}Ozl}} z`zyd8R#p^4D6>S?+rr)&0K!=3$Y=og&axuc03a9&08A_ZAhG~Jl$hIMXUMu>_p(Bp z0zgH)f)=adCR$t}1HkcDzk;osQ0BujxhR&_W?W1VzknX(rC8KYmg8t?igNUu-I@pw zNM9B|wkv}ym8mN>7Ib+I63n_UpoWnpYxBg-yJZh|LV}b}#&~3V#RYYN-)9BDcv~Pe zb4grm*CCsYNS?4yEvH$M-=TmA#0ewda>2@~J^yI_5AEpzk*&dVA=L*@$+N5B`5V*y zo@X{}0`%IjJAdjc7*~gBgU=^p7__ z3d1J?^H%9&d&}OTUMiS%r#@rm^|8m8c!_$6IvH~5M5P-4pG^6zjUOvHYHTiB4HLMX z^3gK>=KE<9*HW}f-*8-Yr?#h04#~7KGVrmC@~@8{r1c`Udmu_U*C&XCpigCt^%5$h zLVt#9TT8Q^-SQ|N?w+l+7(e2nwf|0^cPrtNQ?q{fQtixR8v)5rN!kmICVdS9+PW2A z8DaZIn84I)5ct%9K9lc+fjpQwEqfLTQh%Zn(&Q#|sJRyz=#nV-y7Vb#3W;s`UGZuV zTBI*`PoPQ!RB3Hm&C~CH!EXISSr;ZJpw=nge5U-CwY#mHs4)E$C>@!RFgTbDC<~o5EHXmEq9w=5StHhuRQ10h za`b(!nbdFT3MLJqH-&Qp7`mu|Ih1cXD3$&iw3-vx@Wt9T#r{TyWrn^YcoGsqLafKv z$eS<9;5tq7{A*q5HGOP-lXduz7nuGmg4c-uOX{CUqvn_QD1)bioZ$6JcO##xL8!ah zPS5(iOVBAMkl15Qy}OhF^{M1VA^H?6}s0);?{z3Ai^Z=Vm@(!tZ+SO>S2 z>z7;Ki73Hd;aXo$4LWV7#s8kjL$mg%1I|i+cU2$9ijXvhG}b;_q+NI4|}E|p!Q zjGsF3Q1F21+|Nxbj5uySA?sXuo6xm;@k=gu^_cP+DWBbT`;9kbXT0)F>U+*8q$>FM z!y^yEMsOM$Z}2v`_ajslyvW8VnMK-wBDWO zNzVG-A+t06`J5dbwNM2_&+v`G6y=J#7sllD&ez(8$_G)n#{;~yxt|olTxJrs{SUhU zy6jB@wL{45nsZ8tQ9pgux;NV{pDZ+ffX#FmHrdrg?KUZ{FhxeP_;6tdx8cvmF70>t z=+JD)c1t>_#>yeonU4~vQgvIucv)va`Id(O++|(*;3g)Vl5O>Z9aUp47a<3hKKuUB z(U91NLWlbz)H({A$WV#H-z+sCp%QqX$$@8KLW`S%@T|M|)|wQsgqR>_YQ9` zgv*GIyNv97iSUdq+MKSUp(c36J4p={_4$!a&gk%+yKhbEak-rCW5tOwJ+Hif-nZKh z&XEuJbZe%`;*KUE@{`{#8&BTQFg_J2C1$mMM3Xcb^whhb7q5Vc%x`weJkaP?|0>Re z7l%v~n>2Wm>ZTq<56vcGJqF^6*eQOdjE-@Fs927}^ z&XhZ3VwTookk!!1u$kBL8-6=zVtWu@>WDAb_pIo!AFv)3+hu{?T-vG8wT&EX&gy7E zJ9*HTx+6SYC|-DS-g@g>1{>34SkrsA7`i)c9iPtvJsRp5vs3-6-cs%^aRrAhQ0oQl_LrSW;KGJ&wMtPoy75Q#}VR_dP|GNH399zGces@TcKMCqsLS~n{IAxkBACC6QW+tInlPg$ep0U#YXO(I7%zgVNst{z3)k@oKX02Q$ikUOT$CKiV@Fw}P z1ki$N>1sgX8Zd1~m@WdYkI>duhe8ohsMwl2?f-y4f)5@S`forFp#%RL$O*aM!U9zO zd4Ylt^d(a~1Bw4)baW8#|1q7lMaNi-_%FtRfTK`7NxlG;O4Y;%`IEgpiN2Zy(zTpz RLkU(BU} literal 0 HcmV?d00001 diff --git a/resources/drawables/icon_force.png b/resources/drawables/icon_force.png new file mode 100644 index 0000000000000000000000000000000000000000..beefd850108c86ef6fb4fb2350796eddac852c12 GIT binary patch literal 1364 zcmZ9MdpOez7{`AuEo9w}Sf<-iawge+Ml5QnaycuhRZMvlIT<@H zB__9Xl+GI3BJJd8$c!m+ol8V?x?pwI(|JzM)APLV`+YCZ=Xw8nUk={e4XS6X2LJ#R z=k7vKEB=e?XshFP7{*L3TSMImUI6fmI!`$d0E_BY$^-zML;}E65CC9u0l+ARC-iYp z4>Us#y14+r?F>^;UFpWS`!N6@-sFpGlr<$zshcaAI4|tVDQ)Q5J-eD<>`wroZHjYo zB8Cmj^wPt+sW8c#_JW08ij;fAmYSysE5Ew1Esf(;_yA`o<(fI&&;lk6@2xDVRq_RsX7a8p>kSgNkQl-7y#OL}z)$G!~@x?gR z=l3U%zW3tei=8j%Z9owDcoN~T+|Dwl={JSzq(_3nDyw8bJ9v8OzEs6Ksw*86G!><0}6`J`urAmYqRRFS2#5#GMASq#yW$X`r9K9YF z37Hq;9otmnW=CgXLK6zNn9LWQx`dWsX)LCe!R>c~7DIB7P;WuizwM&r#npWc`6h-s zJmk@WTmB;^qJc38Y@AruvmAvW@2DHKC)mS=H`V=WK2NEXyN(8Bh3BDLfAEQ}FkKCo z4IL{rXMm3Dc4bCZujbw;Jlt7^VWV0)kIeh=4Q?*?{&Y@cT(H@JKSyor)aXvHihEJv zlRMMNt+|Z7Y9F8QNA?-cgH(%Z=aEr?G_N;RSDV+darrCftq5HX&=&9%+J^$Fh!uOilxlECl_z6nVa7n7^%|=eRMu7 zN?$uroCxh7RF!~76GuO8S5CmQ4G@G-(urw)XI$lL$ZUiCWOLV$jP=ZnjDDPP>c^hk z^Phw_tMFF>#T?!Jn`Sn)%@A25aCvZfz2^*G@b|?MM%9#04*J6K&<@EnXm7;*5u7s5 zCd55UAqL@xBnLNK=^r=9t~y&npXXC^KoPuH1pUr)wVzmj@ayuMU1(Cs#*XL3EEmB(UvG{BFYwHtv0j;5{W?~ry8+ezS1{<9!&|S zC4C2gdoVWEUx9m+L|q)RsWh41mLWyHq;|o%F}vaGv~)KOO}Efr_oav3CGxb699k=d``ZRXj&kzDM)m1vK8Fm7nd~FWy;+-27n*uU}NcVowZeh zzHrr3ZX^leh^=q=X}17V+K%DymDKywQtM7j`%ewdm8CFbDSamc!gFxTqp39H9PxKo znv;f(<||vLvL|qiQ@)ItUf7wF7LQ!Dsp&9-<#8P2Jyz~RWhtw>7>oNm7C||zB#_=6 zPAvDR%+Z6!iuA5@MaP>TxRd?ADKchGlvu+b$Sh$`=vVNUtf9<^lMc`1 zYlf-k9Xs0UGj~>x4_O#gT7m}@-^L9HM5(>|X zZ0oX8-vq@JI()Zo+|0a!&SHkExeqWiWY0OjoNdFQyOiMcTIstk-)=N(DJ%MsNs#pp z^TCRUBvvK?CtW=AdI}Py|4dDiNNM68M{Ue>Uk*Q2WL;Sl-Bj7*J~BI?`VB)>St-ku zRM6XTH%Nd5nTW~sS1m`=Hw6|eH?bhu&q7wRs`#&)`MtqlPhIl1omNjt@yM=`&!!FV zsh~(lLiJcX6LA7E_U6h|0DB zOw8#W)s?Xpnf_u^Kd29yUI16aI;|4|~a1tu)y#4Al7bWOcwtQJf zjnA=;v$AC0TSCq$lEU}QW~jwFl|t(GJJgO``i1uCak_mqCP*@yT_bkCCcqohS)~%V$e>S~OAT6&uWPie;)SnPcO=CChoYMl_Wa)uH?we_x4dXVo+86!dPlpJN-bN>J=rfXVSnHrCM&9L-h%9 znqE~!v#t}kR@#&5pLgGA|G`F(kY%g8rjBu$Y9e!FQ9;XKxd+A0sCD$(*uVmEX)vu^ zOp%|1u=Dqu2KHj{6^oj1CM9j{gdPo1l=!{{llmY?cRTe{mp&Mg|dx*vOc# z7$g#9_@BuTy-t9~$bV+sql1aDSbPwOjg8e0jS43OU}J*xqwyi-A50Z^kAQ=%vrVg& GU&=qwWt0;D literal 0 HcmV?d00001 diff --git a/resources/drawables/icon_sighting.png b/resources/drawables/icon_sighting.png new file mode 100644 index 0000000000000000000000000000000000000000..b51c0de3e2a68182bfe6cb8fa4387e1f418efb9b GIT binary patch literal 1362 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r50wh%=tki&%Lb6AYF9SmrkloP2!0-zw)bN6V zq11qZ;Z*_ygVhWM2JwP9y8>;15==?n?k)@rt9q4M1MG8=m zy~NYkmHib9uYkGEA&KUH3=GVFJY5_^GVZ;dQJ--oRpxm7Zfors{r>r<&HV4R`_K2EFJ5pl!$hjrZ84CV z6)nDh^$RAg6$>T=3aMRFm^x+hd5%ZV{_MCqXIXpVF3ZESObX8zHQKf9bP^UdcaGz& zR|?sEh^={T1OE(@h{gZ+A5l2V#>nJo?0Ra~#EBxW-SyamuU#>mrKEUFEA{T&B;hZC zw!2;SMIQNa^H3aD@Vhn*qIL~lvd(N@vom*z5 zcZvT9f2TG34!4cQ_mc7f$-^^d*FBWK zJ%!c(bm-R0ntO{mcgi%ZE{Uk}`Ly{@$wXP3kSrr%>Q!HoM8*FT+5fANy?net_ucib$xcUbz;^F7s# z>rYhPJZZ$;UvBaw4zca1gp&~427+V(pVfL2oo94So-Sl6*=d$Be zPnQY)A+Z(9s~bP;ih4Zj3CoPMwOYx^ZLx{Q&8yN@`gUqOUJ<1o|J6xu%BQtex%)Rv z))Es)5#0J_v3sNxPxh*hU#=hCePvUJ0{4#DceiMsu2j0m(GvQ~lxxzL3tm&!Ngn_H zxFy}C#WHUL-$mCcW>vhQmCK*HS!l2Nd2o}8#B+h8nS$F3Z#Q#Dk z@;GnZObj$R`&84)r)y*0y=UKiE~gcr`o3{euFZiWrynyPNbSGm)ctnawi#BhA{<+4 zgkqix-T$B(J5&Aazx)n`r6R%!Q|f-rJfooG(8AGrr1RqWh-;Fj&@2a}icf!%S9D~Q zs?x4K4=fr~OI#yLQW8s2t&)pUffR$0fsvW6fr+l6afqRrl`#;RXd4(<85q3U>k2Av z5E^pxQ!>*k(KHxZS{YkHGz16K@c=bwz-=hW%uOvWNz5%k(_?CCWdgP2=%yuXKs}Nm uJ;C{DCFO}lsSM@i<$9TU*~Q6;1*v-ZMd`EO*+>BuF?hQAxvX;15==?n?k)@rt9q4#6$`I`xy~Vp=6?(fjLSV;978hhy}h|#`$(b8@sH_?bi^ck#8MZYDYI_AnDLSS z1N)8ji={R4tXz$DdQP0w>M}7X;=}`yuG6L8w|S^r-GBf5_s^;K&PVT`f8KUkcCZsEv ztP%|nST|##xyc$JgKy>%ZIgAP0Ts*7XvltKVtv?|+Lp}5bx61P%TXSY7UN|v`y@54 z+)TDPGC`e{P5P6`0R`uIVlQe>_%_6~-w}C{d*W>4yfz!@3x_A9vs=j(Sv#~l=W%PS zYrCTsVAsCKK5c=*9D^B8)*s&chsRLR+#vB`!}2w%m1}G{);V=n>OE%Jx6!qaS?Z+R zckY}G{(VMQpSagBo!8uODtVXlzJ_v<$VX~_9pw+Co6UHBp~+@V#sTL~r+hW7H8(cT`xJ>jsQP z)e_f;l9a@fRIB8oR3OD*WME{bYha>lXdGf_W@QXS#@Yr3Rt5(8Qq`MKH00)|WTsW3 zX)ppRwS;Kcp<$u|)Sv;kp(HamwYVfPw*XC#si~ET2}IA)O-tB-dL%)5g7ec#$`gxH l8OqDc^)mCai<1)zQuXqS(r3T3kpe1W@O1TaS?83{1OUDZ9%=vp literal 0 HcmV?d00001 diff --git a/resources/drawables/launcher_icon.png b/resources/drawables/launcher_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..6f8b27661d99e8b63ffe52c6a78b750dd25e4d87 GIT binary patch literal 2343 zcmZ9Oc{J1u8^>p1Fqm=gMTn`;l)T2+Gq~xFC5J7FSQ zmXam)i=-P0*~WIK#WL2JjHPRPb?!OuIqyBs`99xszUTRz=db6S=k_T_TUodY90UT% z+MTdEz3b*bBnjK?xJ_>OuI=|daoPa{ia~=wjO!rK-@6fG1_Zi{1%YP0cBisIpgolQ zCg&d@%!goW1={&h?3NPNZX!iF;T{13A=-Wj+!0plw;M`O?HsHnX2oRX^fbh_O!>RX zH#@82XM-jda%mN3-4qgEK7Bt%10PSaMv31sWUBbw4R)>`5#u;KA_b42UF=WLIh8(l ztWYC{BtwrRm^HIrp~8pl$}!ff?;8vv72SJSd=jsoopxlBxN*u7W3Vy3uP$&gr*Zjc z;NqCJ@n*VR!0LR*->V)Q^Br7@Yv4+5I+L1Em$}29T3dW-k{Yz}YdW)KK|1oMuHmik z&ji9h#!?B-W4UX#@=;di^iS_-SShHdD7%%37Og7@ zrn&|lYS1v=u7HSznR(7iWaA=#l87|l+>ti_LfCssSw3M|c2H$3q_D&474LLbK!Yeb z$t`{V`@H5`P$%R*QT27vlhMWYcmRMjv_i5L1?)A=pHZJ}Tk!y=#C#^{wur`(Ge5q3oEvN)%0%J&>``l8#_ zI~fbwdvz7eXW&Q=K0GTrtr7M-*ASFK3Mb`BRO<2XYL?FQenATpX>OZgaTj);b~jv? zF~3!;X&`dU`gQvF5W4Ru(&&5zwCX06n7cjM2Stg9tSjgPZ#m!UEI;=$%WV{+x^n;c z#9@rRdz1MU-F=DALQ(G%Six;3U&X7e0B_{T=f0a(F^WI+U7l#hNN)D_C5xxW)& z2iwh&-Zccb=$_&yuk6GRCnQ?D7B%UiJ|bv6cp$*bOt2KQQQP_qcK#X1ID$JIPcX0u z@)k{*BV&lCl~c8f62(+^2qoZ-xDtj0!25(BO>j-Rl(Y#~KWT!~RK%2^bC8#}Y0^TJ)1+8<)P4Bn3 zGzuMx5*8DVMe}Xi5`0x#(iysp1m;=0a09;>J#wX>v6GK8d8r;}qdgfhmm*{=E;+2DD+2b>N+>M=^g_ies?JxN%!2P0>Wc>T5= zQ~Ak_m&W_VyV6whJEYDTuw`60XWFj_jctE@$=Q%}QI+Q2)_5On^{~u*a$E=u5J?YR zrGRBt2e=|l=<9{>sehF!ODh40H<9cSW;0&R^;rTIu`(Wv554wLgc*9ZgONF#@Kc z(gq4)K6uvE8F4t|jc5-(9}^6hOv#_u7Slc??~@aH(c7;Aq6@;j^}r>v4!K9iuwSOh zmE79y-`N6uE!yR1eGj3mo8WZ_530bMI9gqI-)l;mA8|ysJ$oY9=S*3d12EXXBwk~0 z%Y2#!9RkIQ^V9%NTTi({-kw8!Q`-ft+s<3>WEA2$85totp}&rKkR^9l3i~vlc{(We56Y40T0%dbLVXbxtc&GN46CTWpWX(z62> z^(nEfg?o)bVQ+)zlOf7yHi=tR;V}*#rjLt~9OOMmQrv}6OG@ue&#OzdCOe};#WNdE zlueeeNT#k(kAJkl)JAAIjp~IxelUO@%%e&Lh_mTCRRzXDph@JJ zagKY_;{fL19(4=lj}sEcHPN4{m=_olap&N`UvVEGUl_ZkjLMD` zp`Lq}$}VM&4`Vp$F58`0xdF`!hZ>lOuhKf-Qd7Mx3fC-~*m7;(IX2hoAukTU=u;Xm zO-P&r4+wfRXuh!g6rng0IV;!r>P}IXuQ{Eqg<-C-i+jh*Mrh}{7sCg}2QNMu$~=iae~10y zbz9>&AJZ9bs*{C>U%cjwkw-!tyYpLzTXaPY{nN)YC%T6AiD~kgSMj{FT$>Utxc;TG z-g~TRxQ{KXwKQzZ>A_j`Y`4%4g9V;4NXp8a&_nFN%dU@|xAy4XQz9QX3p};^5FDgh z|4#MuruyT2!~J&+q>t4%*1;O;=oy^VGsYPjSS${Ub&zuN{6Byk=0^&M{x@KR y(=+%7m lon, "acc" => accuracy }; - if (address != null) { - d["addr"] = address; - } + if (address != null) { d["addr"] = address; } + if (zip != null) { d["zip"] = zip; } return d; } @@ -44,9 +45,8 @@ class Event { d["lon"] as Float or Null, d["acc"] as Float or Null ); - if (d.hasKey("addr")) { - evt.address = d["addr"] as String or Null; - } + if (d.hasKey("addr")) { evt.address = d["addr"] as String or Null; } + if (d.hasKey("zip")) { evt.zip = d["zip"] as String or Null; } return evt; } } diff --git a/source/GeocodingService.mc b/source/GeocodingService.mc index 9ca0bb1..8afee70 100644 --- a/source/GeocodingService.mc +++ b/source/GeocodingService.mc @@ -2,15 +2,15 @@ import Toybox.Communications; import Toybox.Lang; // Reverse-geocodes a lat/lon pair via Photon API. Calls back with -// a formatted address string or null on failure. +// a Dictionary { "addr" => String, "zip" => String } or null. class GeocodingService { - private var _callback as Method(address as String or Null) as Void; + private var _callback as Method(result as Dictionary or Null) as Void; private var _lat as Float; private var _lon as Float; function initialize(lat as Float, lon as Float, - callback as Method(address as String or Null) as Void) { + callback as Method(result as Dictionary or Null) as Void) { _callback = callback; _lat = lat; _lon = lon; @@ -28,16 +28,13 @@ class GeocodingService { } function _onResponse(responseCode as Number, data as Dictionary or String or Null) as Void { - System.println("GEO: responseCode=" + responseCode); if (responseCode != 200 || data == null || !(data instanceof Dictionary)) { - System.println("GEO: bad response, data=" + data); _callback.invoke(null); return; } var features = data["features"]; if (features == null || !(features instanceof Array) || features.size() == 0) { - System.println("GEO: no features"); _callback.invoke(null); return; } @@ -45,7 +42,6 @@ class GeocodingService { var feature = features[0] as Dictionary; var props = feature["properties"] as Dictionary; var geometry = feature["geometry"] as Dictionary; - System.println("GEO: props=" + props); // Haversine check: only use address if result is within threshold. if (geometry != null) { @@ -54,31 +50,38 @@ class GeocodingService { var rLon = (coords[0] as Double).toFloat(); var rLat = (coords[1] as Double).toFloat(); var dist = Haversine.distance(_lat, _lon, rLat, rLon); - System.println("GEO: dist=" + dist + " max=" + Config.ADDRESS_MAX_DISTANCE_M); if (dist > Config.ADDRESS_MAX_DISTANCE_M) { - System.println("GEO: too far, rejecting"); _callback.invoke(null); return; } } } - var addr = _formatAddress(props); - System.println("GEO: address=" + addr); - _callback.invoke(addr); - } - - private function _formatAddress(props as Dictionary) as String { var street = props["street"]; - var number = props["housenumber"]; - - var parts = ""; - if (street != null) { - parts = street as String; - if (number != null) { - parts = parts + " " + number; - } + if (street == null) { street = props["name"]; } + if (street == null) { + _callback.invoke(null); + return; } - return parts.equals("") ? "Unbekannt" : parts; + + var addr = street as String; + var number = props["housenumber"]; + if (number != null) { + addr = addr + " " + number; + } + + var result = { "addr" => addr } as Dictionary; + var zip = props["postcode"]; + var city = props["city"]; + if (zip != null || city != null) { + var zipCity = ""; + if (zip != null) { zipCity = zip as String; } + if (city != null) { + if (!zipCity.equals("")) { zipCity = zipCity + " "; } + zipCity = zipCity + city; + } + result["zip"] = zipCity; + } + _callback.invoke(result); } } diff --git a/source/GpsService.mc b/source/GpsService.mc index 6631f70..7101556 100644 --- a/source/GpsService.mc +++ b/source/GpsService.mc @@ -47,17 +47,16 @@ class GpsService { if (info == null || info.position == null) { return; } var degrees = info.position.toDegrees(); - var acc = (info.accuracy != null) ? info.accuracy : null; + var quality = (info.accuracy != null) ? info.accuracy : 0; - _bestFix = { - "lat" => degrees[0].toFloat(), - "lon" => degrees[1].toFloat(), - "acc" => (acc != null) ? acc.toFloat() : null - }; - - // Good enough → stop early. - if (acc != null && acc <= Config.GPS_TARGET_ACCURACY_M) { - _finish(_bestFix); + // Always keep the best quality fix. + if (_bestFix == null || quality >= (_bestFix["q"] as Number)) { + _bestFix = { + "lat" => degrees[0].toFloat(), + "lon" => degrees[1].toFloat(), + "acc" => quality.toFloat(), + "q" => quality + }; } } diff --git a/source/HistoryView.mc b/source/HistoryView.mc index 881c8d3..ea39d05 100644 --- a/source/HistoryView.mc +++ b/source/HistoryView.mc @@ -64,7 +64,7 @@ class HistoryView extends WatchUi.View { var evt = _events[_index]; // Use cached address from event if available. if (evt.address != null) { - _addressCache[_index] = evt.address; + _addressCache[_index] = { "addr" => evt.address, "zip" => evt.zip }; return; } if (evt.lat == null || evt.lon == null) { return; } @@ -76,13 +76,14 @@ class HistoryView extends WatchUi.View { _geocoder.start(); } - function _onAddress(address as String or Null) as Void { - _addressCache[_index] = address; + function _onAddress(result as Dictionary or Null) as Void { + _addressCache[_index] = result; _addressLoading = false; - // Persist address in the event so the glance can show it. - if (address != null && _index < _events.size()) { + // Persist in the event so the glance can show it. + if (result != null && _index < _events.size()) { var evt = _events[_index]; - evt.address = address; + evt.address = result["addr"] as String or Null; + evt.zip = result["zip"] as String or Null; EventStore.updateAt(_index, evt); } WatchUi.requestUpdate(); @@ -134,51 +135,51 @@ class HistoryView extends WatchUi.View { private function _drawMiddleSection(dc as Dc, cx as Number, topH as Number, botY as Number) as Void { var evt = _events[_index]; - var midY = topH + (botY - topH) / 2; + var fontDetail = Graphics.FONT_XTINY; + var lineH = dc.getFontHeight(fontDetail); + var sectionMid = topH + (botY - topH) / 2; - // Event type label. - var label = _eventLabel(evt.type); - var color = _eventColor(evt.type); - dc.setColor(color, Config.COLOR_BG); - var fontLabel = Graphics.FONT_SMALL; - var lineH = dc.getFontHeight(fontLabel); - dc.drawText(cx, midY - lineH * 2, fontLabel, label, - Graphics.TEXT_JUSTIFY_CENTER | Graphics.TEXT_JUSTIFY_VCENTER); + // Collect lines to draw. + var lines = [] as Array; // [text, color] - // Date + time. - dc.setColor(Config.COLOR_FG, Config.COLOR_BG); - var fontDetail = Graphics.FONT_TINY; - var dateStr = _formatTimestamp(evt.timestamp); - dc.drawText(cx, midY - lineH / 2, fontDetail, dateStr, - Graphics.TEXT_JUSTIFY_CENTER | Graphics.TEXT_JUSTIFY_VCENTER); + // 1: Event type (colored). + lines.add([_eventLabel(evt.type), _eventColor(evt.type)]); - // Address / coordinates / status. - var locStr = _locationString(evt); - dc.drawText(cx, midY + lineH / 2, fontDetail, locStr, - Graphics.TEXT_JUSTIFY_CENTER | Graphics.TEXT_JUSTIFY_VCENTER); + // 2: Date + time. + lines.add([_formatTimestamp(evt.timestamp), Config.COLOR_FG]); - // Counter (e.g. "3/7"). - dc.setColor(0x888888, Config.COLOR_BG); - dc.drawText(cx, midY + lineH * 3 / 2, Graphics.FONT_XTINY, - (_index + 1) + "/" + _events.size(), - Graphics.TEXT_JUSTIFY_CENTER | Graphics.TEXT_JUSTIFY_VCENTER); - } - - private function _locationString(evt as Event) as String { + // 3: Address or status. if (evt.lat == null || evt.lon == null) { - return WatchUi.loadResource(Rez.Strings.history_no_gps) as String; - } - if (_addressCache.hasKey(_index)) { - var cached = _addressCache[_index]; + lines.add([WatchUi.loadResource(Rez.Strings.history_no_gps) as String, Config.COLOR_FG]); + } else if (_addressCache.hasKey(_index)) { + var cached = _addressCache[_index] as Dictionary or Null; if (cached != null) { - return cached as String; + lines.add([cached["addr"] as String, Config.COLOR_FG]); + var zip = cached["zip"] as String or Null; + if (zip != null) { + lines.add([zip, 0x888888]); + } } - return _formatCoords(evt.lat as Float, evt.lon as Float); + } else if (_addressLoading) { + lines.add([WatchUi.loadResource(Rez.Strings.history_loading_address) as String, 0x888888]); } - if (_addressLoading) { - return WatchUi.loadResource(Rez.Strings.history_loading_address) as String; + + // 4: Coordinates (always, if available). + if (evt.lat != null && evt.lon != null) { + lines.add([_formatCoords(evt.lat as Float, evt.lon as Float), 0x888888]); + } + + // 5: Counter. + lines.add([(_index + 1) + "/" + _events.size(), 0x666666]); + + // Draw centered vertically. + var totalH = lines.size() * lineH; + var startY = sectionMid - totalH / 2 + lineH / 2; + for (var i = 0; i < lines.size(); i++) { + dc.setColor(lines[i][1] as Number, Config.COLOR_BG); + dc.drawText(cx, startY + i * lineH, fontDetail, lines[i][0] as String, + Graphics.TEXT_JUSTIFY_CENTER | Graphics.TEXT_JUSTIFY_VCENTER); } - return _formatCoords(evt.lat as Float, evt.lon as Float); } private function _formatCoords(lat as Float, lon as Float) as String { diff --git a/source/MenuView.mc b/source/MenuView.mc index adbb1fd..a11bc09 100644 --- a/source/MenuView.mc +++ b/source/MenuView.mc @@ -67,6 +67,11 @@ class MenuView extends WatchUi.View { var isSelected = (i == _selectedIndex); var targetSize = isSelected ? selSize : baseSize; + // Colored circle behind icon. + var color = _items[i][:color] as Number; + dc.setColor(color, Config.COLOR_BG); + dc.fillCircle(x, y, targetSize / 2); + var iconId = _items[i][:icon]; var bmp = _bitmaps[iconId] as BitmapResource; if (bmp != null) { @@ -91,12 +96,12 @@ class MenuView extends WatchUi.View { cx as Number, cy as Number, targetSize as Number) as Void { var bmpW = bmp.getWidth(); + var bmpH = bmp.getHeight(); var scale = targetSize.toFloat() / bmpW.toFloat(); - dc.drawBitmap2( - cx - targetSize / 2, - cy - targetSize / 2, - bmp, - { :scaleX => scale, :scaleY => scale } - ); + var tf = new Graphics.AffineTransform(); + tf.translate(cx.toFloat(), cy.toFloat()); + tf.scale(scale, scale); + tf.translate(-bmpW.toFloat() / 2.0, -bmpH.toFloat() / 2.0); + dc.drawBitmap2(0, 0, bmp, { :transform => tf }); } }