mirror of
https://github.com/CatimaLoyalty/Android.git
synced 2026-01-04 13:08:04 -05:00
Compare commits
701 Commits
badAttempt
...
pdfImport
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8519e12aa7 | ||
|
|
1429abd94d | ||
|
|
0e873b9ea5 | ||
|
|
cbc8886241 | ||
|
|
6c7bfb4c50 | ||
|
|
6ed5e52890 | ||
|
|
bc1bd88d75 | ||
|
|
09e1201086 | ||
|
|
df829a4956 | ||
|
|
46dc163e06 | ||
|
|
e70b2bdbbd | ||
|
|
c1f8e930bb | ||
|
|
c8dd1d8509 | ||
|
|
e50adc39c5 | ||
|
|
09948b6a21 | ||
|
|
f50e80d24d | ||
|
|
777512aad7 | ||
|
|
423fd272d4 | ||
|
|
969256f130 | ||
|
|
702d4af8d9 | ||
|
|
02696c1328 | ||
|
|
dcffdf3329 | ||
|
|
4110b5ab48 | ||
|
|
dea96069b0 | ||
|
|
04c5cb08f1 | ||
|
|
b37f8943c7 | ||
|
|
54d4f8b70b | ||
|
|
658cc43f86 | ||
|
|
958abe5ed7 | ||
|
|
fa99c2ef7c | ||
|
|
2b38f474fd | ||
|
|
5643814e65 | ||
|
|
59b588b797 | ||
|
|
757321930d | ||
|
|
03a5a382e9 | ||
|
|
66a91db8f0 | ||
|
|
013dd020c1 | ||
|
|
948e469c05 | ||
|
|
d4aff16da8 | ||
|
|
efa17689b6 | ||
|
|
57ab82b2f0 | ||
|
|
4052cec9ba | ||
|
|
3fc1057999 | ||
|
|
8458beaab0 | ||
|
|
c04c505368 | ||
|
|
2ee913c833 | ||
|
|
09074cbc9e | ||
|
|
29b79a010b | ||
|
|
992f2e2e44 | ||
|
|
7cca7154c5 | ||
|
|
daa7f7a7e2 | ||
|
|
21c1580513 | ||
|
|
9c700eb8d0 | ||
|
|
be21ba515a | ||
|
|
671b0b1228 | ||
|
|
dd455bfabd | ||
|
|
b3294112aa | ||
|
|
baae7ab358 | ||
|
|
d14d5ef83e | ||
|
|
9b92882fc5 | ||
|
|
ebfe0d0b6c | ||
|
|
83a35e02ce | ||
|
|
13ae1f5014 | ||
|
|
4327c45cd3 | ||
|
|
3f4076bc7d | ||
|
|
1ae09c3dff | ||
|
|
daa8127f47 | ||
|
|
aadb5334c2 | ||
|
|
b5b897f7b9 | ||
|
|
273035b209 | ||
|
|
6867408d14 | ||
|
|
bf17d12b08 | ||
|
|
523a602442 | ||
|
|
103beef323 | ||
|
|
e700afac60 | ||
|
|
4e60bc2029 | ||
|
|
a233db799d | ||
|
|
c4958ff40d | ||
|
|
02329e1176 | ||
|
|
480bf8489e | ||
|
|
6762c342c9 | ||
|
|
d760e17095 | ||
|
|
3b6d1e1764 | ||
|
|
9250b2b3dc | ||
|
|
0c1cdc98e8 | ||
|
|
a90f000d46 | ||
|
|
90fe400069 | ||
|
|
de7bc3d7ea | ||
|
|
46b1040108 | ||
|
|
d42908e163 | ||
|
|
6468491e04 | ||
|
|
1e58cfb5f3 | ||
|
|
f01c388f0c | ||
|
|
0e5c84e8f2 | ||
|
|
ca733a5404 | ||
|
|
59a28ebd62 | ||
|
|
d48247f107 | ||
|
|
d084d5f67b | ||
|
|
d11483dcf2 | ||
|
|
2386ee30f7 | ||
|
|
9899d80719 | ||
|
|
e809392191 | ||
|
|
94d9010731 | ||
|
|
b160b1dca8 | ||
|
|
6e2fa6fd22 | ||
|
|
2b0a126d3c | ||
|
|
d8297db010 | ||
|
|
fd7b72d6e5 | ||
|
|
45471910e6 | ||
|
|
bb36804a46 | ||
|
|
0e2991cb7f | ||
|
|
efbcd00b55 | ||
|
|
27e252f68a | ||
|
|
d2cdced416 | ||
|
|
b150ace903 | ||
|
|
7697855e0d | ||
|
|
08f4cb32a5 | ||
|
|
eef026ff4f | ||
|
|
3fac41e8b1 | ||
|
|
5c01f638be | ||
|
|
9e2d41b821 | ||
|
|
472f837f44 | ||
|
|
f2d1b9b5c9 | ||
|
|
621bfa3f99 | ||
|
|
f64703d683 | ||
|
|
75d1fba8f8 | ||
|
|
308a2bbaf3 | ||
|
|
4ba752a10e | ||
|
|
df826d76e5 | ||
|
|
f70138cd28 | ||
|
|
ea81304dd5 | ||
|
|
683ef8cc49 | ||
|
|
f9db51c8d7 | ||
|
|
e5de170887 | ||
|
|
c45761f069 | ||
|
|
65937be5b2 | ||
|
|
b249dd141d | ||
|
|
f67548d18b | ||
|
|
a8b8c10f0f | ||
|
|
3cd0aab25e | ||
|
|
4dcafe0abe | ||
|
|
0de1fcbe9b | ||
|
|
2b8214e144 | ||
|
|
0300aaa850 | ||
|
|
2605372e5d | ||
|
|
72b09506dc | ||
|
|
14b42e8980 | ||
|
|
7c25bb9b79 | ||
|
|
f3b2e1e1ac | ||
|
|
ef4ad0e197 | ||
|
|
34d8331c50 | ||
|
|
8430dff21c | ||
|
|
ba87ee739e | ||
|
|
41503f912f | ||
|
|
fd396b8fc7 | ||
|
|
2abc8b8f64 | ||
|
|
160e61ead4 | ||
|
|
c2e8577674 | ||
|
|
66b8f35994 | ||
|
|
b141009350 | ||
|
|
0e8dd2fd64 | ||
|
|
988336cfec | ||
|
|
5e3668820b | ||
|
|
81c919448e | ||
|
|
23ca0802cb | ||
|
|
40c2337054 | ||
|
|
7db8b1602d | ||
|
|
32b35382a6 | ||
|
|
3ec6cbe416 | ||
|
|
90322f75ce | ||
|
|
534c4001c1 | ||
|
|
e21030aed3 | ||
|
|
d6d12174d5 | ||
|
|
27e78dbadd | ||
|
|
0d520b48e1 | ||
|
|
d7a49013ec | ||
|
|
fc974e2038 | ||
|
|
7144f60340 | ||
|
|
883af7fd10 | ||
|
|
fcb2be604c | ||
|
|
d24366a3ba | ||
|
|
a0aa19f26f | ||
|
|
589f009a65 | ||
|
|
e849fd379e | ||
|
|
e28c5e877b | ||
|
|
ae90b46c05 | ||
|
|
a8a1158b4b | ||
|
|
8789be5340 | ||
|
|
93af3dbc36 | ||
|
|
96c111f0c8 | ||
|
|
89dca33ce8 | ||
|
|
f9f1149bb5 | ||
|
|
cfd5fbe3ae | ||
|
|
028905df42 | ||
|
|
3dd74fd674 | ||
|
|
0af9549d02 | ||
|
|
a367d24125 | ||
|
|
dfa0c36af5 | ||
|
|
66fcad7658 | ||
|
|
370ec55df1 | ||
|
|
b03ead95ea | ||
|
|
90e6463688 | ||
|
|
80cfb516c0 | ||
|
|
989cf31897 | ||
|
|
caf9e163df | ||
|
|
8363115faf | ||
|
|
48cc286b14 | ||
|
|
07e7b56c9f | ||
|
|
4cac19acb9 | ||
|
|
ed1db31d62 | ||
|
|
2a2a711375 | ||
|
|
c96cef5298 | ||
|
|
8506c05081 | ||
|
|
9adc6d0704 | ||
|
|
ca62d542ef | ||
|
|
84c343e41f | ||
|
|
d12283e75d | ||
|
|
0297304751 | ||
|
|
966846c507 | ||
|
|
48de759eb0 | ||
|
|
4e23fb4195 | ||
|
|
1cfd04ba67 | ||
|
|
c6f1e0cffe | ||
|
|
027d153747 | ||
|
|
bd1376e6bf | ||
|
|
7fa8ae8697 | ||
|
|
0b01604c4e | ||
|
|
d37dca7e7d | ||
|
|
5eb2753168 | ||
|
|
502e2e5e21 | ||
|
|
bbb2c0d81f | ||
|
|
59e62c2805 | ||
|
|
15d9a16ead | ||
|
|
e99cba7fcb | ||
|
|
781b6ecc48 | ||
|
|
ec8536976f | ||
|
|
2f9101e9f2 | ||
|
|
0cc2fd44ac | ||
|
|
6db4cbab9b | ||
|
|
0b2111b6f0 | ||
|
|
b3f118ef15 | ||
|
|
db8a652e5b | ||
|
|
d7a4f81eba | ||
|
|
d0c0694662 | ||
|
|
0fdf79449c | ||
|
|
cbf8b2d5f0 | ||
|
|
e194cca5f1 | ||
|
|
5d450852e1 | ||
|
|
ce86811d7c | ||
|
|
9fb3b765e7 | ||
|
|
d02dac2c41 | ||
|
|
243536d0d1 | ||
|
|
b258ba8654 | ||
|
|
5ec87a7a3a | ||
|
|
c84692db35 | ||
|
|
efe2a720f4 | ||
|
|
8b876f51fc | ||
|
|
033de00ad4 | ||
|
|
81f0e769b8 | ||
|
|
dd14533c6c | ||
|
|
f7c50caeb1 | ||
|
|
b00aa74bfe | ||
|
|
944089536f | ||
|
|
266732f744 | ||
|
|
33ee23a0e1 | ||
|
|
b064851254 | ||
|
|
19782fdb9c | ||
|
|
eb7658c35a | ||
|
|
fff35d7a74 | ||
|
|
ed5506554a | ||
|
|
dac7e8cb46 | ||
|
|
5ad27b1b53 | ||
|
|
ca3c29f5e4 | ||
|
|
935ef20955 | ||
|
|
86ec70ee72 | ||
|
|
2303caf1be | ||
|
|
ee1e5c0d0e | ||
|
|
4bdb5a86ad | ||
|
|
92ac59a783 | ||
|
|
89ea86e795 | ||
|
|
ffe6236c2d | ||
|
|
c057e03e97 | ||
|
|
372834f60f | ||
|
|
1265c6d801 | ||
|
|
2ae0ddd9e7 | ||
|
|
fba6f3a111 | ||
|
|
357b005b15 | ||
|
|
8df25a301b | ||
|
|
71c7252458 | ||
|
|
41f8fc2094 | ||
|
|
b5752967a2 | ||
|
|
5ee14c2fe8 | ||
|
|
a34321c680 | ||
|
|
56ed2f454d | ||
|
|
40ac0d83c5 | ||
|
|
b8de73b5b3 | ||
|
|
547af33f40 | ||
|
|
f1b525396c | ||
|
|
50890c4f31 | ||
|
|
3322133d1e | ||
|
|
5b6c6b4466 | ||
|
|
5f2402dd15 | ||
|
|
b34533a92a | ||
|
|
c521fae4ee | ||
|
|
ce47efecb0 | ||
|
|
133da20f93 | ||
|
|
cee8074232 | ||
|
|
6f78e2b404 | ||
|
|
b05b46f10a | ||
|
|
1188e58fb0 | ||
|
|
575150af9e | ||
|
|
d82e0bebb6 | ||
|
|
72131a6b2d | ||
|
|
b89a13ce70 | ||
|
|
389a53b2cc | ||
|
|
be0f534f66 | ||
|
|
36eee3686c | ||
|
|
5c76cb01bf | ||
|
|
e57cd9eea3 | ||
|
|
e795ec6907 | ||
|
|
5e69f18b88 | ||
|
|
6bb6d54b6a | ||
|
|
86f049d9a4 | ||
|
|
5cb02ae0f2 | ||
|
|
88380878e0 | ||
|
|
59c7341aad | ||
|
|
e3596adae4 | ||
|
|
8d697c3414 | ||
|
|
c1b0fead33 | ||
|
|
228b9ecc4d | ||
|
|
7c03469e91 | ||
|
|
c922e0645d | ||
|
|
8015cc65b8 | ||
|
|
85240fa3d5 | ||
|
|
3ed418166d | ||
|
|
b4a173d352 | ||
|
|
bd57e11f16 | ||
|
|
d65141ee92 | ||
|
|
fc12efff5e | ||
|
|
bd0879923c | ||
|
|
d2df924ba5 | ||
|
|
613c92e8c4 | ||
|
|
d8429bf305 | ||
|
|
1c8926887b | ||
|
|
7f03f371e0 | ||
|
|
273a39abb7 | ||
|
|
562b984029 | ||
|
|
74e498c5ab | ||
|
|
4c825c703b | ||
|
|
c54af65033 | ||
|
|
f80dbe9073 | ||
|
|
e9a48dca41 | ||
|
|
157c428919 | ||
|
|
a67aec366a | ||
|
|
ee12482f6e | ||
|
|
c2383fbb40 | ||
|
|
65ab14897f | ||
|
|
87ecb3c1b9 | ||
|
|
9dfbc92e92 | ||
|
|
c65303b08b | ||
|
|
fd8c960c58 | ||
|
|
7511ea0c2d | ||
|
|
4810a3cee1 | ||
|
|
a230d9f877 | ||
|
|
18e699445c | ||
|
|
4a5106c38c | ||
|
|
3afde7049e | ||
|
|
82a3ae16a6 | ||
|
|
31995f4b8b | ||
|
|
b7c444fd8f | ||
|
|
58481c842b | ||
|
|
5847ff4b7c | ||
|
|
056c255aa6 | ||
|
|
9c99b9d0ad | ||
|
|
d4a9197e60 | ||
|
|
57d7176ac9 | ||
|
|
029fa9b5e8 | ||
|
|
c38a89b48f | ||
|
|
21958442bb | ||
|
|
f92ae736b4 | ||
|
|
548714f778 | ||
|
|
1d61a7c0eb | ||
|
|
6c5a06ce5b | ||
|
|
1d4e47b832 | ||
|
|
cd9ef1c231 | ||
|
|
a1011e0668 | ||
|
|
9e7d51c36b | ||
|
|
0e826a654b | ||
|
|
40778dd494 | ||
|
|
d7b983f1ca | ||
|
|
206f6385ae | ||
|
|
18cba2d702 | ||
|
|
847850eeff | ||
|
|
107bf5a0f5 | ||
|
|
d3edde4a43 | ||
|
|
963f42628f | ||
|
|
7d83cf1dfd | ||
|
|
5bb2df98cd | ||
|
|
2796a15353 | ||
|
|
55595159be | ||
|
|
5cab0e3932 | ||
|
|
4d6c08fc73 | ||
|
|
0c147830ee | ||
|
|
b5a3a4c735 | ||
|
|
f867e8cb93 | ||
|
|
524af89c96 | ||
|
|
3df4d4783f | ||
|
|
d0e7187273 | ||
|
|
7819460377 | ||
|
|
4396214e7a | ||
|
|
697c6f3f39 | ||
|
|
0239e5a89e | ||
|
|
532fae7de5 | ||
|
|
135c4498d8 | ||
|
|
7f0e5d9c59 | ||
|
|
cf4eeafce0 | ||
|
|
8597580d93 | ||
|
|
e3643328f0 | ||
|
|
e76c8219bb | ||
|
|
650ad23e59 | ||
|
|
abc66a0c08 | ||
|
|
701e1ee5fa | ||
|
|
8031a02003 | ||
|
|
f383758c7d | ||
|
|
eeb7eddff2 | ||
|
|
1df27e2d4a | ||
|
|
66ffe63a62 | ||
|
|
a963ed20d3 | ||
|
|
c057e7a420 | ||
|
|
10bab6349a | ||
|
|
e89b612c93 | ||
|
|
2d99c61d78 | ||
|
|
ed2a816656 | ||
|
|
a73064ae46 | ||
|
|
b07ac08b69 | ||
|
|
c5793caaf3 | ||
|
|
22fadb1b15 | ||
|
|
5cd2cbb1a2 | ||
|
|
20ec9f5cc3 | ||
|
|
93db0669ff | ||
|
|
7c2eebb20c | ||
|
|
e76a63c34b | ||
|
|
80cab9ba59 | ||
|
|
c26ddd9342 | ||
|
|
78ec649af3 | ||
|
|
1ae61cc0b7 | ||
|
|
5616158ff7 | ||
|
|
554a60cf95 | ||
|
|
737a6272a9 | ||
|
|
dc7f3573fe | ||
|
|
4003faa17f | ||
|
|
4aecb05e17 | ||
|
|
f58eafd2b6 | ||
|
|
1061bc691a | ||
|
|
734813c29e | ||
|
|
2cdd0f6678 | ||
|
|
c65b30189f | ||
|
|
93db35396a | ||
|
|
3d6cf5883e | ||
|
|
3c316d1878 | ||
|
|
be6d3a460d | ||
|
|
feb93ccb48 | ||
|
|
6ad0dd1f24 | ||
|
|
1c936b4b2e | ||
|
|
3c201a9c29 | ||
|
|
5ccdc349ff | ||
|
|
a842f01e32 | ||
|
|
b3b5960725 | ||
|
|
781318397c | ||
|
|
e7e5cdac22 | ||
|
|
11bce01405 | ||
|
|
ed84b0fcb9 | ||
|
|
7f82bc9822 | ||
|
|
2d40ac0111 | ||
|
|
81ffee2e3b | ||
|
|
133e1695f6 | ||
|
|
78a8a65ae2 | ||
|
|
8e28b43ecb | ||
|
|
20062db01e | ||
|
|
42e5a15455 | ||
|
|
e61c20c66f | ||
|
|
8e4ca5fa78 | ||
|
|
8e08e863d2 | ||
|
|
6ad16c4e86 | ||
|
|
42fa077099 | ||
|
|
079d16020a | ||
|
|
fab6a42069 | ||
|
|
c461ce133e | ||
|
|
d8f3e1f1ef | ||
|
|
107b576db1 | ||
|
|
e553d50cb7 | ||
|
|
ff83a4a4f3 | ||
|
|
58ad83caef | ||
|
|
380d961ea4 | ||
|
|
ca4a336b6a | ||
|
|
5205a69041 | ||
|
|
013f3d2d4c | ||
|
|
ca3f7a7c28 | ||
|
|
b9813c9ccb | ||
|
|
7255571f36 | ||
|
|
a9e6e9d75c | ||
|
|
4bcdbc62c6 | ||
|
|
c91e92b80b | ||
|
|
ca2f2c349e | ||
|
|
420179458d | ||
|
|
20ffc23487 | ||
|
|
8de5646d07 | ||
|
|
16dbe1572a | ||
|
|
18f68cdb24 | ||
|
|
147cf544a3 | ||
|
|
a4763d8ed0 | ||
|
|
382d86dce6 | ||
|
|
2a2d05dd01 | ||
|
|
09e6617808 | ||
|
|
f9190ed915 | ||
|
|
ea7ca81f1c | ||
|
|
68e5c36909 | ||
|
|
4e167c79c1 | ||
|
|
9361487af7 | ||
|
|
c80107b870 | ||
|
|
52a1cc6549 | ||
|
|
5f34d54b42 | ||
|
|
ec4265504b | ||
|
|
3f2b140d1f | ||
|
|
4af3695301 | ||
|
|
f5368a4aab | ||
|
|
f5059b95d3 | ||
|
|
85fa0afaa8 | ||
|
|
3bcccba6d4 | ||
|
|
188c2e25a2 | ||
|
|
162622631f | ||
|
|
946a426b32 | ||
|
|
354a0f7fc0 | ||
|
|
6015a944ff | ||
|
|
e30320603a | ||
|
|
acd644025e | ||
|
|
b3e26f532d | ||
|
|
3a64b4652f | ||
|
|
56f691ca53 | ||
|
|
57c770c9ba | ||
|
|
53c3c85e57 | ||
|
|
9a02d1bfbc | ||
|
|
ffbb138986 | ||
|
|
181e9f082c | ||
|
|
912a4cb955 | ||
|
|
50a1e77500 | ||
|
|
aef32d2b95 | ||
|
|
d091ccb593 | ||
|
|
6fbbae9f0a | ||
|
|
176aa3880e | ||
|
|
fcf64c86a6 | ||
|
|
b5f6742b12 | ||
|
|
26ae92dc84 | ||
|
|
c65cdc93a7 | ||
|
|
bd85590d39 | ||
|
|
e3942b1737 | ||
|
|
2dc7fd9a45 | ||
|
|
2b7f8b6bf7 | ||
|
|
db0619718c | ||
|
|
dd19ea6322 | ||
|
|
5d656f9681 | ||
|
|
c2c50664ea | ||
|
|
058eb7dd8b | ||
|
|
7a381f3683 | ||
|
|
96b6ca4b3b | ||
|
|
21815db47d | ||
|
|
72f62b9211 | ||
|
|
0e61a4584e | ||
|
|
ca0ad17e8e | ||
|
|
20c4e03343 | ||
|
|
4ec4d8a3a6 | ||
|
|
34d1e94ba4 | ||
|
|
3ff6a2c269 | ||
|
|
2327c0a940 | ||
|
|
520629c075 | ||
|
|
48446cdac6 | ||
|
|
0e61908265 | ||
|
|
ead67942f1 | ||
|
|
43f36a1c99 | ||
|
|
bd7de108b5 | ||
|
|
ef9a490d0b | ||
|
|
4c26434f41 | ||
|
|
ffd2eccdbe | ||
|
|
214ec7d7fb | ||
|
|
269e0a1c2a | ||
|
|
21c9a63819 | ||
|
|
0121541a9a | ||
|
|
48be5390ec | ||
|
|
ca62ce8bcd | ||
|
|
4ab6d0c5ab | ||
|
|
0c12533a8e | ||
|
|
b983498bbc | ||
|
|
54871a1479 | ||
|
|
5fb911b884 | ||
|
|
8f05433ba3 | ||
|
|
55b5285e62 | ||
|
|
4a920e3f83 | ||
|
|
1979006f2c | ||
|
|
a2ac7f7c41 | ||
|
|
533930771b | ||
|
|
4beceeebf2 | ||
|
|
f17f788d0b | ||
|
|
f6c82035f8 | ||
|
|
fe9fbbfd57 | ||
|
|
e2cce91360 | ||
|
|
9981a64bcd | ||
|
|
757a942ecf | ||
|
|
6954090744 | ||
|
|
24e18473bd | ||
|
|
207e83a582 | ||
|
|
6ad1d9497c | ||
|
|
f728866645 | ||
|
|
f6f904ae39 | ||
|
|
4160909a32 | ||
|
|
c990e40d0c | ||
|
|
d4e0b6d3cf | ||
|
|
0197ae2f58 | ||
|
|
dcb94f242f | ||
|
|
ab66304c72 | ||
|
|
ee7d9e2405 | ||
|
|
146a4676d5 | ||
|
|
27da7913cb | ||
|
|
cb1cf607f5 | ||
|
|
acf94600f8 | ||
|
|
a41124cc7b | ||
|
|
0d0564b295 | ||
|
|
ba7a849c18 | ||
|
|
b3b347cd9b | ||
|
|
2efdb5b9bb | ||
|
|
c7961feb27 | ||
|
|
43c092915a | ||
|
|
720fbb4eae | ||
|
|
0b888380e5 | ||
|
|
8b4fc8c76b | ||
|
|
b9ade35828 | ||
|
|
c990992f8b | ||
|
|
4a7907f991 | ||
|
|
6f3bae96ff | ||
|
|
1a1ea44902 | ||
|
|
4c9a560b53 | ||
|
|
5cb822b295 | ||
|
|
df7b2ee4d0 | ||
|
|
6871c48c8e | ||
|
|
497458f04c | ||
|
|
a196969a93 | ||
|
|
76b65b45bc | ||
|
|
eb9935bb83 | ||
|
|
98c595696d | ||
|
|
d484e83f47 | ||
|
|
19093b462c | ||
|
|
8488352e77 | ||
|
|
38522564cf | ||
|
|
e8e15b5432 | ||
|
|
a032fc06a3 | ||
|
|
a9d55f129b | ||
|
|
84b35aa464 | ||
|
|
54342c4ee0 | ||
|
|
b89050b03c | ||
|
|
37eac1b9b7 | ||
|
|
e4b287b3c2 | ||
|
|
7cef3c4a53 | ||
|
|
6b39470890 | ||
|
|
5f184db48d | ||
|
|
96c430dd8c | ||
|
|
be662f2e67 | ||
|
|
3fdd4559b6 | ||
|
|
ecf9c0d1e2 | ||
|
|
67903c21d4 | ||
|
|
5924c8616c | ||
|
|
0f2f430b7f | ||
|
|
4a99bad8be | ||
|
|
9964fa5943 | ||
|
|
b6860a8634 | ||
|
|
cc0ef2dc0e | ||
|
|
351504dc5f | ||
|
|
b2494d8fdc | ||
|
|
aea1ea0cbf | ||
|
|
ff9f22e4bd | ||
|
|
fb13e0ab30 | ||
|
|
443685c26e | ||
|
|
bd362f1ac0 | ||
|
|
b23606ed3b | ||
|
|
2344b6367f | ||
|
|
394f15bed8 | ||
|
|
83d7422b9c | ||
|
|
a7246cf786 | ||
|
|
7abcd5b0cd | ||
|
|
a2b224df9b | ||
|
|
4542da4c38 | ||
|
|
3c92c53164 | ||
|
|
b326778219 | ||
|
|
81e16d95ac | ||
|
|
ef18bbdf7c | ||
|
|
6562a0177b | ||
|
|
275a427355 | ||
|
|
ce6a79f03d | ||
|
|
dc792c8425 | ||
|
|
680d2173d1 | ||
|
|
e95a20b971 | ||
|
|
56de06abac | ||
|
|
d66176e628 |
20
.github/dependabot.yml
vendored
20
.github/dependabot.yml
vendored
@@ -2,9 +2,29 @@ version: 2
|
|||||||
updates:
|
updates:
|
||||||
- package-ecosystem: "gradle"
|
- package-ecosystem: "gradle"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
|
registries:
|
||||||
|
- google
|
||||||
|
- gradlePluginPortal
|
||||||
|
- jitpack
|
||||||
|
- mavenCentral
|
||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "daily"
|
||||||
- package-ecosystem: "github-actions"
|
- package-ecosystem: "github-actions"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
|
|
||||||
|
# Workaround for https://github.com/dependabot/dependabot-core/issues/6888
|
||||||
|
registries:
|
||||||
|
google:
|
||||||
|
type: maven-repository
|
||||||
|
url: "https://dl.google.com/dl/android/maven2/"
|
||||||
|
gradlePluginPortal:
|
||||||
|
type: maven-repository
|
||||||
|
url: "https://plugins.gradle.org/m2/"
|
||||||
|
jitpack:
|
||||||
|
type: maven-repository
|
||||||
|
url: "https://jitpack.io/"
|
||||||
|
mavenCentral:
|
||||||
|
type: maven-repository
|
||||||
|
url: "https://repo1.maven.org/maven2/"
|
||||||
|
|||||||
6
.github/workflows/android.yml
vendored
6
.github/workflows/android.yml
vendored
@@ -29,10 +29,10 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.0.0
|
- uses: actions/checkout@v4.1.1
|
||||||
- name: Fail on bad translations
|
- name: Fail on bad translations
|
||||||
run: if grep -ri "<xliff" app/src/main/res/values*/strings.xml; then echo "Invalidly escaped translations found"; exit 1; fi
|
run: if grep -ri "<xliff" app/src/main/res/values*/strings.xml; then echo "Invalidly escaped translations found"; exit 1; fi
|
||||||
- uses: gradle/wrapper-validation-action@v1
|
- uses: gradle/wrapper-validation-action@v2
|
||||||
- name: set up OpenJDK 17
|
- name: set up OpenJDK 17
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
@@ -48,7 +48,7 @@ jobs:
|
|||||||
run: ./gradlew spotbugsRelease
|
run: ./gradlew spotbugsRelease
|
||||||
- name: Archive test results
|
- name: Archive test results
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v3.1.3
|
uses: actions/upload-artifact@v4.3.1
|
||||||
with:
|
with:
|
||||||
name: test-results
|
name: test-results
|
||||||
path: app/build/reports
|
path: app/build/reports
|
||||||
|
|||||||
6
.github/workflows/changelog-to-fastlane.yml
vendored
6
.github/workflows/changelog-to-fastlane.yml
vendored
@@ -27,15 +27,15 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
id: checkout
|
id: checkout
|
||||||
uses: actions/checkout@v4.0.0
|
uses: actions/checkout@v4.1.1
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v4.7.0
|
uses: actions/setup-python@v5.0.0
|
||||||
with:
|
with:
|
||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
- name: Run converter script
|
- name: Run converter script
|
||||||
run: python .scripts/changelog_to_fastlane.py
|
run: python .scripts/changelog_to_fastlane.py
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@v5.0.2
|
uses: peter-evans/create-pull-request@v6.0.2
|
||||||
with:
|
with:
|
||||||
title: "Update Fastlane changelogs"
|
title: "Update Fastlane changelogs"
|
||||||
commit-message: "Update Fastlane changelogs"
|
commit-message: "Update Fastlane changelogs"
|
||||||
|
|||||||
7
.github/workflows/contributors-to-file.yml
vendored
7
.github/workflows/contributors-to-file.yml
vendored
@@ -25,14 +25,15 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
id: checkout
|
id: checkout
|
||||||
uses: actions/checkout@v4.0.0
|
uses: actions/checkout@v4.1.1
|
||||||
- name: Update contributors
|
- name: Update contributors
|
||||||
id: update_contributors
|
id: update_contributors
|
||||||
uses: TheLastProject/contributors-to-file-action@v3.0.1
|
uses: TheLastProject/contributors-to-file-action@v3.2.0
|
||||||
with:
|
with:
|
||||||
file_in_repo: app/src/main/res/raw/contributors.txt
|
file_in_repo: app/src/main/res/raw/contributors.txt
|
||||||
|
min_commit_count: 5
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@v5.0.2
|
uses: peter-evans/create-pull-request@v6.0.2
|
||||||
with:
|
with:
|
||||||
title: "Update contributors"
|
title: "Update contributors"
|
||||||
commit-message: "Update contributors"
|
commit-message: "Update contributors"
|
||||||
|
|||||||
38
.github/workflows/generate-feature-graphic.yml
vendored
38
.github/workflows/generate-feature-graphic.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
|||||||
generate-feature-graphic:
|
generate-feature-graphic:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.0.0
|
- uses: actions/checkout@v4.1.1
|
||||||
- name: Install requirements
|
- name: Install requirements
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
@@ -36,41 +36,9 @@ jobs:
|
|||||||
find .scripts/generate_feature_graphic/fonts -name '*.ttf' -exec cp {} "$HOME/.fonts" \;
|
find .scripts/generate_feature_graphic/fonts -name '*.ttf' -exec cp {} "$HOME/.fonts" \;
|
||||||
fc-cache
|
fc-cache
|
||||||
- name: Generate featureGraphic.png for each language
|
- name: Generate featureGraphic.png for each language
|
||||||
run: |
|
run: .scripts/generate_feature_graphic/generate_feature_graphic.sh
|
||||||
for lang in fastlane/metadata/android/*; do
|
|
||||||
pushd "$lang"
|
|
||||||
# Place temporary copy for editing if needed
|
|
||||||
cp ../../../../.scripts/generate_feature_graphic/featureGraphic.svg featureGraphic.svg
|
|
||||||
# Extract text after 'Catima - '
|
|
||||||
export subtext="$(grep -oP '(?<=Catima \S ).*' title.txt || true)"
|
|
||||||
# If there is subtext, change the .svg accordingly
|
|
||||||
if [ -n "$subtext" ]; then
|
|
||||||
perl -pi -e 's/Loyalty Card Wallet/$ENV{subtext}/' featureGraphic.svg
|
|
||||||
# Set correct font for language if needed (Lexend Deca has limited support)
|
|
||||||
# We specifically need the Serif version because of the 200 weight
|
|
||||||
case "$(basename "$lang")" in
|
|
||||||
bg|el-GR|ru-RU|uk) sed -i "s/Lexend Deca/Noto Serif/" featureGraphic.svg ;;
|
|
||||||
ja-JP) sed -i "s/Lexend Deca/Noto Serif CJK JP/" featureGraphic.svg ;;
|
|
||||||
ko) sed -i "s/Lexend Deca/Noto Serif CJK KR/" featureGraphic.svg ;;
|
|
||||||
zh-CN) sed -i "s/Lexend Deca/Noto Serif CJK SC/" featureGraphic.svg ;;
|
|
||||||
zh-TW) sed -i "s/Lexend Deca/Noto Serif CJK TC/" featureGraphic.svg ;;
|
|
||||||
*) ;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
# Ensure images directory exists
|
|
||||||
mkdir -p images
|
|
||||||
# Generate .png
|
|
||||||
convert featureGraphic.svg images/featureGraphic.png
|
|
||||||
# Optimize .png
|
|
||||||
optipng images/featureGraphic.png
|
|
||||||
# Remove metadata (timestamps) from .png
|
|
||||||
mat2 --inplace images/featureGraphic.png
|
|
||||||
# Remove temporary .svg
|
|
||||||
rm featureGraphic.svg
|
|
||||||
popd
|
|
||||||
done
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@v5.0.2
|
uses: peter-evans/create-pull-request@v6.0.2
|
||||||
with:
|
with:
|
||||||
title: "Update feature graphic"
|
title: "Update feature graphic"
|
||||||
commit-message: "Update feature graphic"
|
commit-message: "Update feature graphic"
|
||||||
|
|||||||
33
.github/workflows/gradle-update.yml
vendored
Normal file
33
.github/workflows/gradle-update.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
name: Gradle update
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: '3 6 * * *'
|
||||||
|
permissions:
|
||||||
|
actions: none
|
||||||
|
checks: none
|
||||||
|
contents: write
|
||||||
|
deployments: none
|
||||||
|
discussions: none
|
||||||
|
id-token: none
|
||||||
|
issues: none
|
||||||
|
packages: none
|
||||||
|
pages: none
|
||||||
|
pull-requests: write
|
||||||
|
repository-projects: none
|
||||||
|
security-events: none
|
||||||
|
statuses: none
|
||||||
|
jobs:
|
||||||
|
gradle-update:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4.1.1
|
||||||
|
- uses: obfusk/gradle-update-action@v2.0.0
|
||||||
|
id: gradle-update
|
||||||
|
- uses: gradle/wrapper-validation-action@v2
|
||||||
|
- name: Create Pull Request
|
||||||
|
uses: peter-evans/create-pull-request@v6.0.2
|
||||||
|
with:
|
||||||
|
title: "Update Gradle to ${{ steps.gradle-update.outputs.version }}"
|
||||||
|
commit-message: "Update Gradle to ${{ steps.gradle-update.outputs.version }}"
|
||||||
|
branch-suffix: timestamp
|
||||||
7
.github/workflows/update-locales.yml
vendored
7
.github/workflows/update-locales.yml
vendored
@@ -5,6 +5,7 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
paths:
|
paths:
|
||||||
|
- app/src/main/res/values-*/strings.xml
|
||||||
- app/src/main/res/values/settings.xml
|
- app/src/main/res/values/settings.xml
|
||||||
permissions:
|
permissions:
|
||||||
actions: none
|
actions: none
|
||||||
@@ -24,11 +25,13 @@ jobs:
|
|||||||
update-locales:
|
update-locales:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.0.0
|
- uses: actions/checkout@v4.1.1
|
||||||
|
- name: Add new locales
|
||||||
|
run: .scripts/new-locales.py
|
||||||
- name: Update locales
|
- name: Update locales
|
||||||
run: .scripts/locales.py
|
run: .scripts/locales.py
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@v5.0.2
|
uses: peter-evans/create-pull-request@v6.0.2
|
||||||
with:
|
with:
|
||||||
title: "Update locales"
|
title: "Update locales"
|
||||||
commit-message: "Update locales"
|
commit-message: "Update locales"
|
||||||
|
|||||||
26
.gitignore
vendored
26
.gitignore
vendored
@@ -1,13 +1,25 @@
|
|||||||
|
# Android Studio generated (superseded/unused rules commented out)
|
||||||
*.iml
|
*.iml
|
||||||
.gradle
|
.gradle
|
||||||
local.properties
|
/local.properties
|
||||||
.idea/
|
#/.idea/caches
|
||||||
|
#/.idea/libraries
|
||||||
|
#/.idea/modules.xml
|
||||||
|
#/.idea/workspace.xml
|
||||||
|
#/.idea/navEditor.xml
|
||||||
|
#/.idea/assetWizardSettings.xml
|
||||||
.DS_Store
|
.DS_Store
|
||||||
build/
|
/build
|
||||||
captures/
|
/captures
|
||||||
**/release
|
.externalNativeBuild
|
||||||
**/debug
|
.cxx
|
||||||
app/*.log
|
#local.properties
|
||||||
|
|
||||||
|
# Android extras
|
||||||
|
/app/*.log
|
||||||
|
/app/build
|
||||||
|
/app/release
|
||||||
|
/.idea
|
||||||
|
|
||||||
# Bundle
|
# Bundle
|
||||||
/.bundle/
|
/.bundle/
|
||||||
|
|||||||
55
.scripts/generate_feature_graphic/generate_feature_graphic.sh
Executable file
55
.scripts/generate_feature_graphic/generate_feature_graphic.sh
Executable file
@@ -0,0 +1,55 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
script_location="$(dirname "$(readlink -f "$0")")"
|
||||||
|
|
||||||
|
for lang in "$script_location/../../fastlane/metadata/android/"*; do
|
||||||
|
pushd "$lang"
|
||||||
|
# Place temporary copy for editing if needed
|
||||||
|
cp "$script_location/featureGraphic.svg" featureGraphic.svg
|
||||||
|
if grep -q — title.txt; then
|
||||||
|
# Try splitting title.txt on — (em dash)
|
||||||
|
IFS='—' read -r appname subtext < title.txt
|
||||||
|
elif grep -q – title.txt; then
|
||||||
|
# No result, try splitting title.txt on – (en dash)
|
||||||
|
IFS='–' read -r appname subtext < title.txt
|
||||||
|
elif grep -q - title.txt; then
|
||||||
|
# No result, try splitting on - (dash)
|
||||||
|
IFS='-' read -r appname subtext < title.txt
|
||||||
|
else
|
||||||
|
# No result, use the full title as app name and default subtext
|
||||||
|
appname=$(< title.txt)
|
||||||
|
subtext="Loyalty Card Wallet"
|
||||||
|
fi
|
||||||
|
export appname=${appname%% }
|
||||||
|
export subtext=${subtext## }
|
||||||
|
# If the appname isn't Catima or there is subtext, change the .svg accordingly
|
||||||
|
if [ "$appname" != "Catima" ] || [ -n "$subtext" ]; then
|
||||||
|
perl -pi -e 's/Catima/$ENV{appname}/' featureGraphic.svg
|
||||||
|
perl -pi -e 's/Loyalty Card Wallet/$ENV{subtext}/' featureGraphic.svg
|
||||||
|
# Set correct font or font size for language if needed
|
||||||
|
# (Lexend Deca has limited support and some characters are big)
|
||||||
|
# We specifically need the Serif version because of the 200 weight
|
||||||
|
case "$(basename "$lang")" in
|
||||||
|
bg|el-GR|ru-RU|uk) sed -i "s/Lexend Deca/Noto Serif/" featureGraphic.svg ;;
|
||||||
|
hi-IN) sed -i -e "s/Yesteryear/Noto Serif Devanagari/" -e "s/Lexend Deca/Noto Serif Devanagari/" featureGraphic.svg ;;
|
||||||
|
ja-JP) sed -i "s/Lexend Deca/Noto Serif CJK JP/" featureGraphic.svg ;;
|
||||||
|
ko) sed -i "s/Lexend Deca/Noto Serif CJK KR/" featureGraphic.svg ;;
|
||||||
|
kn-IN) sed -i -e 's/font-size="150"/font-size="100"/' -e 's/y="285.511"/y="235.511"/' featureGraphic.svg ;;
|
||||||
|
zh-CN) sed -i "s/Lexend Deca/Noto Serif CJK SC/" featureGraphic.svg ;;
|
||||||
|
zh-TW) sed -i "s/Lexend Deca/Noto Serif CJK TC/" featureGraphic.svg ;;
|
||||||
|
*) ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
# Ensure images directory exists
|
||||||
|
mkdir -p images
|
||||||
|
# Generate .png
|
||||||
|
convert featureGraphic.svg images/featureGraphic.png
|
||||||
|
# Optimize .png
|
||||||
|
optipng images/featureGraphic.png
|
||||||
|
# Remove metadata (timestamps) from .png
|
||||||
|
mat2 --inplace images/featureGraphic.png
|
||||||
|
# Remove temporary .svg
|
||||||
|
rm featureGraphic.svg
|
||||||
|
popd
|
||||||
|
done
|
||||||
@@ -19,8 +19,8 @@ res = ", ".join(f'"{loc}"' for loc in locales)
|
|||||||
sed = [
|
sed = [
|
||||||
"sed",
|
"sed",
|
||||||
"-i",
|
"-i",
|
||||||
f"s/resourceConfigurations .*/resourceConfigurations += [{res}]/",
|
f"s/resourceConfigurations .*/resourceConfigurations += listOf({res})/",
|
||||||
"app/build.gradle"
|
"app/build.gradle.kts"
|
||||||
]
|
]
|
||||||
subprocess.run(sed, check=True)
|
subprocess.run(sed, check=True)
|
||||||
|
|
||||||
|
|||||||
120
.scripts/new-locales.py
Executable file
120
.scripts/new-locales.py
Executable file
@@ -0,0 +1,120 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import glob
|
||||||
|
import re
|
||||||
|
|
||||||
|
from typing import Iterator, List, Tuple
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
MIN_PERCENT = 90
|
||||||
|
NOT_LANGS = ("night", "w600dp")
|
||||||
|
REPLACE_CODES = {
|
||||||
|
"el": "el-rGR",
|
||||||
|
"id": "in-rID",
|
||||||
|
"ro": "ro-rRO",
|
||||||
|
"zh_Hans": "zh-rCN",
|
||||||
|
"zh_Hant": "zh-rTW",
|
||||||
|
}
|
||||||
|
STATS_URL = "https://hosted.weblate.org/api/components/catima/catima/statistics/"
|
||||||
|
|
||||||
|
|
||||||
|
def get_weblate_langs() -> List[Tuple[str, int]]:
|
||||||
|
r = requests.get(STATS_URL, timeout=5)
|
||||||
|
r.raise_for_status()
|
||||||
|
results = []
|
||||||
|
for lang in r.json()["results"]:
|
||||||
|
if lang["code"] != "en":
|
||||||
|
code = REPLACE_CODES.get(lang["code"], lang["code"]).replace("_", "-r")
|
||||||
|
results.append((code, round(lang["translated_percent"])))
|
||||||
|
return sorted(results)
|
||||||
|
|
||||||
|
|
||||||
|
def get_dir_langs() -> List[str]:
|
||||||
|
results = []
|
||||||
|
for d in glob.glob("app/src/main/res/values-*"):
|
||||||
|
code = d.split("-", 1)[1]
|
||||||
|
if code not in NOT_LANGS:
|
||||||
|
results.append(code)
|
||||||
|
return sorted(results)
|
||||||
|
|
||||||
|
|
||||||
|
def get_xml_langs() -> List[Tuple[str, bool]]:
|
||||||
|
results = []
|
||||||
|
in_section = False
|
||||||
|
with open("app/src/main/res/values/settings.xml") as fh:
|
||||||
|
for line in fh:
|
||||||
|
if not in_section and 'name="locale_values"' in line:
|
||||||
|
in_section = True
|
||||||
|
elif in_section:
|
||||||
|
if "string-array" in line:
|
||||||
|
break
|
||||||
|
disabled = "<!--" in line
|
||||||
|
if m := re.search(r">(.*)<", line):
|
||||||
|
if m[1] != "en":
|
||||||
|
results.append((m[1], disabled))
|
||||||
|
return sorted(results)
|
||||||
|
|
||||||
|
|
||||||
|
def update_xml_langs(langs: List[Tuple[str, bool]]) -> None:
|
||||||
|
lines: List[str] = []
|
||||||
|
in_section = False
|
||||||
|
with open("app/src/main/res/values/settings.xml") as fh:
|
||||||
|
for line in fh:
|
||||||
|
if not in_section and 'name="locale_values"' in line:
|
||||||
|
in_section = True
|
||||||
|
elif in_section:
|
||||||
|
if "string-array" in line:
|
||||||
|
in_section = False
|
||||||
|
lines.extend(_lang_lines(langs))
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
lines.append(line)
|
||||||
|
with open("app/src/main/res/values/settings.xml", "w") as fh:
|
||||||
|
for line in lines:
|
||||||
|
fh.write(line)
|
||||||
|
|
||||||
|
|
||||||
|
def _lang_lines(langs: List[Tuple[str, bool]]) -> Iterator[str]:
|
||||||
|
yield " <item />\n"
|
||||||
|
for lang, disabled in sorted(langs + [("en", False)]):
|
||||||
|
if disabled:
|
||||||
|
yield f" <!-- <item>{lang}</item> -->\n"
|
||||||
|
else:
|
||||||
|
yield f" <item>{lang}</item>\n"
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
web_langs = get_weblate_langs()
|
||||||
|
dir_langs = get_dir_langs()
|
||||||
|
xml_langs = get_xml_langs()
|
||||||
|
|
||||||
|
web_codes = set(code for code, _ in web_langs)
|
||||||
|
dir_codes = set(dir_langs)
|
||||||
|
xml_codes = set(code for code, _ in xml_langs)
|
||||||
|
|
||||||
|
if diff := web_codes - dir_codes:
|
||||||
|
print(f"WARNING: Weblate codes w/o dir: {diff}")
|
||||||
|
if diff := xml_codes - dir_codes:
|
||||||
|
print(f"WARNING: XML codes w/o dir: {diff}")
|
||||||
|
|
||||||
|
percentages = dict(web_langs)
|
||||||
|
all_langs = xml_langs[:]
|
||||||
|
|
||||||
|
# add new langs as disabled
|
||||||
|
for code in dir_codes - xml_codes:
|
||||||
|
all_langs.append((code, True))
|
||||||
|
|
||||||
|
# enable disabled langs if they are at least MIN_PERCENT translated now
|
||||||
|
updated_langs = sorted(
|
||||||
|
(code, percentages[code] < MIN_PERCENT if disabled else disabled)
|
||||||
|
for code, disabled in all_langs
|
||||||
|
)
|
||||||
|
|
||||||
|
if updated_langs != xml_langs:
|
||||||
|
print("Updating...")
|
||||||
|
update_xml_langs(updated_langs)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
18
CHANGELOG.md
18
CHANGELOG.md
@@ -1,9 +1,25 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## Unreleased - 132
|
## Unreleased - 134
|
||||||
|
|
||||||
|
- Support for scanning PDF files for barcodes
|
||||||
|
- Support for image files with multiple barcodes
|
||||||
|
|
||||||
|
## v2.28.0 - 133 (2024-03-08)
|
||||||
|
|
||||||
|
- Target Android 14
|
||||||
|
- Open card icon in gallery on touch
|
||||||
|
- Improve design of Photos tab in edit view
|
||||||
|
- Update spending screen to also support receiving
|
||||||
|
|
||||||
|
## v2.27.0 - 132 (2024-01-30)
|
||||||
|
|
||||||
- Refine "Add card" workflow
|
- Refine "Add card" workflow
|
||||||
- Validation flow improvements
|
- Validation flow improvements
|
||||||
|
- Fix edge case causing invalid UI state when toggling showing archive
|
||||||
|
- Use theme or card colour for navigation bar (Android 8.1+)
|
||||||
|
- Updated validity and expiry date selector
|
||||||
|
- Add option to always rotate (ignoring system settings)
|
||||||
|
|
||||||
## v2.26.0 - 131 (2023-09-14)
|
## v2.26.0 - 131 (2023-09-14)
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
How to Submit Patches to the Catima Project
|
# How to Submit Patches to the Catima Project
|
||||||
===============================================================================
|
|
||||||
https://github.com/TheLastProject/Catima
|
|
||||||
|
|
||||||
This document is intended to act as a guide to help you contribute to the
|
This document is intended to act as a guide to help you contribute to the
|
||||||
Catima project. It is not perfect, and there will always be exceptions
|
Catima project. It is not perfect, and there will always be exceptions
|
||||||
to the rules described here, but by following the instructions below you
|
to the rules described here, but by following the instructions below you
|
||||||
should have a much easier time getting your work merged with the upstream
|
should have a much easier time getting your work merged with the upstream
|
||||||
project.
|
project.
|
||||||
|
|
||||||
|
When contributing, you certify that you agree to and have the rights to submit
|
||||||
|
your contribution under the project's license and understand that git will
|
||||||
|
store your name and email address in project history indefinitely.
|
||||||
|
|
||||||
## Translation Changes
|
## Translation Changes
|
||||||
|
|
||||||
Translation changes are managed through [Weblate](https://hosted.weblate.org/projects/catima/).
|
Translation changes are managed through [Weblate](https://hosted.weblate.org/projects/catima/).
|
||||||
@@ -57,44 +59,6 @@ if you can describe/include a reproducer for the problem in the description as
|
|||||||
well as instructions on how to test for the bug and verify that it has been
|
well as instructions on how to test for the bug and verify that it has been
|
||||||
fixed.
|
fixed.
|
||||||
|
|
||||||
### Sign Your Work
|
|
||||||
|
|
||||||
The sign-off is a simple line at the end of the patch description, which
|
|
||||||
certifies that you wrote it or otherwise have the right to pass it on as an
|
|
||||||
open-source patch. The "Developer's Certificate of Origin" pledge is taken
|
|
||||||
from the Linux Kernel and the rules are pretty simple:
|
|
||||||
|
|
||||||
Developer's Certificate of Origin 1.1
|
|
||||||
|
|
||||||
By making a contribution to this project, I certify that:
|
|
||||||
|
|
||||||
(a) The contribution was created in whole or in part by me and I
|
|
||||||
have the right to submit it under the open source license
|
|
||||||
indicated in the file; or
|
|
||||||
|
|
||||||
(b) The contribution is based upon previous work that, to the best
|
|
||||||
of my knowledge, is covered under an appropriate open source
|
|
||||||
license and I have the right under that license to submit that
|
|
||||||
work with modifications, whether created in whole or in part
|
|
||||||
by me, under the same open source license (unless I am
|
|
||||||
permitted to submit under a different license), as indicated
|
|
||||||
in the file; or
|
|
||||||
|
|
||||||
(c) The contribution was provided directly to me by some other
|
|
||||||
person who certified (a), (b) or (c) and I have not modified
|
|
||||||
it.
|
|
||||||
|
|
||||||
(d) I understand and agree that this project and the contribution
|
|
||||||
are public and that a record of the contribution (including all
|
|
||||||
personal information I submit with it, including my sign-off) is
|
|
||||||
maintained indefinitely and may be redistributed consistent with
|
|
||||||
this project or the open source license(s) involved.
|
|
||||||
|
|
||||||
... then you just add a line to the bottom of your patch description, with
|
|
||||||
your real name, saying:
|
|
||||||
|
|
||||||
Signed-off-by: Random J Developer <random@developer.example.org>
|
|
||||||
|
|
||||||
### Submit Patch(es) for Review
|
### Submit Patch(es) for Review
|
||||||
|
|
||||||
Finally, you will need to submit your patches so that they can be reviewed
|
Finally, you will need to submit your patches so that they can be reviewed
|
||||||
|
|||||||
75
Gemfile.lock
75
Gemfile.lock
@@ -3,25 +3,25 @@ GEM
|
|||||||
specs:
|
specs:
|
||||||
CFPropertyList (3.0.6)
|
CFPropertyList (3.0.6)
|
||||||
rexml
|
rexml
|
||||||
addressable (2.8.5)
|
addressable (2.8.6)
|
||||||
public_suffix (>= 2.0.2, < 6.0)
|
public_suffix (>= 2.0.2, < 6.0)
|
||||||
artifactory (3.0.15)
|
artifactory (3.0.15)
|
||||||
atomos (0.1.3)
|
atomos (0.1.3)
|
||||||
aws-eventstream (1.2.0)
|
aws-eventstream (1.3.0)
|
||||||
aws-partitions (1.824.0)
|
aws-partitions (1.884.0)
|
||||||
aws-sdk-core (3.181.1)
|
aws-sdk-core (3.191.0)
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
aws-eventstream (~> 1, >= 1.3.0)
|
||||||
aws-partitions (~> 1, >= 1.651.0)
|
aws-partitions (~> 1, >= 1.651.0)
|
||||||
aws-sigv4 (~> 1.5)
|
aws-sigv4 (~> 1.8)
|
||||||
jmespath (~> 1, >= 1.6.1)
|
jmespath (~> 1, >= 1.6.1)
|
||||||
aws-sdk-kms (1.71.0)
|
aws-sdk-kms (1.77.0)
|
||||||
aws-sdk-core (~> 3, >= 3.177.0)
|
aws-sdk-core (~> 3, >= 3.191.0)
|
||||||
aws-sigv4 (~> 1.1)
|
aws-sigv4 (~> 1.1)
|
||||||
aws-sdk-s3 (1.134.0)
|
aws-sdk-s3 (1.143.0)
|
||||||
aws-sdk-core (~> 3, >= 3.181.0)
|
aws-sdk-core (~> 3, >= 3.191.0)
|
||||||
aws-sdk-kms (~> 1)
|
aws-sdk-kms (~> 1)
|
||||||
aws-sigv4 (~> 1.6)
|
aws-sigv4 (~> 1.8)
|
||||||
aws-sigv4 (1.6.0)
|
aws-sigv4 (1.8.0)
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
babosa (1.0.4)
|
babosa (1.0.4)
|
||||||
claide (1.1.0)
|
claide (1.1.0)
|
||||||
@@ -32,11 +32,10 @@ GEM
|
|||||||
declarative (0.0.20)
|
declarative (0.0.20)
|
||||||
digest-crc (0.6.5)
|
digest-crc (0.6.5)
|
||||||
rake (>= 12.0.0, < 14.0.0)
|
rake (>= 12.0.0, < 14.0.0)
|
||||||
domain_name (0.5.20190701)
|
domain_name (0.6.20240107)
|
||||||
unf (>= 0.0.5, < 1.0.0)
|
|
||||||
dotenv (2.8.1)
|
dotenv (2.8.1)
|
||||||
emoji_regex (3.2.3)
|
emoji_regex (3.2.3)
|
||||||
excon (0.103.0)
|
excon (0.109.0)
|
||||||
faraday (1.10.3)
|
faraday (1.10.3)
|
||||||
faraday-em_http (~> 1.0)
|
faraday-em_http (~> 1.0)
|
||||||
faraday-em_synchrony (~> 1.0)
|
faraday-em_synchrony (~> 1.0)
|
||||||
@@ -65,8 +64,8 @@ GEM
|
|||||||
faraday-retry (1.0.3)
|
faraday-retry (1.0.3)
|
||||||
faraday_middleware (1.2.0)
|
faraday_middleware (1.2.0)
|
||||||
faraday (~> 1.0)
|
faraday (~> 1.0)
|
||||||
fastimage (2.2.7)
|
fastimage (2.3.0)
|
||||||
fastlane (2.215.1)
|
fastlane (2.219.0)
|
||||||
CFPropertyList (>= 2.3, < 4.0.0)
|
CFPropertyList (>= 2.3, < 4.0.0)
|
||||||
addressable (>= 2.8, < 3.0.0)
|
addressable (>= 2.8, < 3.0.0)
|
||||||
artifactory (~> 3.0)
|
artifactory (~> 3.0)
|
||||||
@@ -85,6 +84,7 @@ GEM
|
|||||||
gh_inspector (>= 1.1.2, < 2.0.0)
|
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||||
google-apis-androidpublisher_v3 (~> 0.3)
|
google-apis-androidpublisher_v3 (~> 0.3)
|
||||||
google-apis-playcustomapp_v1 (~> 0.1)
|
google-apis-playcustomapp_v1 (~> 0.1)
|
||||||
|
google-cloud-env (>= 1.6.0, < 2.0.0)
|
||||||
google-cloud-storage (~> 1.31)
|
google-cloud-storage (~> 1.31)
|
||||||
highline (~> 2.0)
|
highline (~> 2.0)
|
||||||
http-cookie (~> 1.0.5)
|
http-cookie (~> 1.0.5)
|
||||||
@@ -93,7 +93,7 @@ GEM
|
|||||||
mini_magick (>= 4.9.4, < 5.0.0)
|
mini_magick (>= 4.9.4, < 5.0.0)
|
||||||
multipart-post (>= 2.0.0, < 3.0.0)
|
multipart-post (>= 2.0.0, < 3.0.0)
|
||||||
naturally (~> 2.2)
|
naturally (~> 2.2)
|
||||||
optparse (~> 0.1.1)
|
optparse (>= 0.1.1)
|
||||||
plist (>= 3.1.0, < 4.0.0)
|
plist (>= 3.1.0, < 4.0.0)
|
||||||
rubyzip (>= 2.0.0, < 3.0.0)
|
rubyzip (>= 2.0.0, < 3.0.0)
|
||||||
security (= 0.1.3)
|
security (= 0.1.3)
|
||||||
@@ -107,9 +107,9 @@ GEM
|
|||||||
xcpretty (~> 0.3.0)
|
xcpretty (~> 0.3.0)
|
||||||
xcpretty-travis-formatter (>= 0.0.3)
|
xcpretty-travis-formatter (>= 0.0.3)
|
||||||
gh_inspector (1.1.3)
|
gh_inspector (1.1.3)
|
||||||
google-apis-androidpublisher_v3 (0.49.0)
|
google-apis-androidpublisher_v3 (0.54.0)
|
||||||
google-apis-core (>= 0.11.0, < 2.a)
|
google-apis-core (>= 0.11.0, < 2.a)
|
||||||
google-apis-core (0.11.1)
|
google-apis-core (0.11.3)
|
||||||
addressable (~> 2.5, >= 2.5.1)
|
addressable (~> 2.5, >= 2.5.1)
|
||||||
googleauth (>= 0.16.2, < 2.a)
|
googleauth (>= 0.16.2, < 2.a)
|
||||||
httpclient (>= 2.8.1, < 3.a)
|
httpclient (>= 2.8.1, < 3.a)
|
||||||
@@ -117,28 +117,27 @@ GEM
|
|||||||
representable (~> 3.0)
|
representable (~> 3.0)
|
||||||
retriable (>= 2.0, < 4.a)
|
retriable (>= 2.0, < 4.a)
|
||||||
rexml
|
rexml
|
||||||
webrick
|
|
||||||
google-apis-iamcredentials_v1 (0.17.0)
|
google-apis-iamcredentials_v1 (0.17.0)
|
||||||
google-apis-core (>= 0.11.0, < 2.a)
|
google-apis-core (>= 0.11.0, < 2.a)
|
||||||
google-apis-playcustomapp_v1 (0.13.0)
|
google-apis-playcustomapp_v1 (0.13.0)
|
||||||
google-apis-core (>= 0.11.0, < 2.a)
|
google-apis-core (>= 0.11.0, < 2.a)
|
||||||
google-apis-storage_v1 (0.19.0)
|
google-apis-storage_v1 (0.31.0)
|
||||||
google-apis-core (>= 0.9.0, < 2.a)
|
google-apis-core (>= 0.11.0, < 2.a)
|
||||||
google-cloud-core (1.6.0)
|
google-cloud-core (1.6.1)
|
||||||
google-cloud-env (~> 1.0)
|
google-cloud-env (>= 1.0, < 3.a)
|
||||||
google-cloud-errors (~> 1.0)
|
google-cloud-errors (~> 1.0)
|
||||||
google-cloud-env (1.6.0)
|
google-cloud-env (1.6.0)
|
||||||
faraday (>= 0.17.3, < 3.0)
|
faraday (>= 0.17.3, < 3.0)
|
||||||
google-cloud-errors (1.3.1)
|
google-cloud-errors (1.3.1)
|
||||||
google-cloud-storage (1.44.0)
|
google-cloud-storage (1.47.0)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
digest-crc (~> 0.4)
|
digest-crc (~> 0.4)
|
||||||
google-apis-iamcredentials_v1 (~> 0.1)
|
google-apis-iamcredentials_v1 (~> 0.1)
|
||||||
google-apis-storage_v1 (~> 0.19.0)
|
google-apis-storage_v1 (~> 0.31.0)
|
||||||
google-cloud-core (~> 1.6)
|
google-cloud-core (~> 1.6)
|
||||||
googleauth (>= 0.16.2, < 2.a)
|
googleauth (>= 0.16.2, < 2.a)
|
||||||
mini_mime (~> 1.0)
|
mini_mime (~> 1.0)
|
||||||
googleauth (1.8.0)
|
googleauth (1.8.1)
|
||||||
faraday (>= 0.17.3, < 3.a)
|
faraday (>= 0.17.3, < 3.a)
|
||||||
jwt (>= 1.4, < 3.0)
|
jwt (>= 1.4, < 3.0)
|
||||||
multi_json (~> 1.11)
|
multi_json (~> 1.11)
|
||||||
@@ -149,7 +148,7 @@ GEM
|
|||||||
domain_name (~> 0.5)
|
domain_name (~> 0.5)
|
||||||
httpclient (2.8.3)
|
httpclient (2.8.3)
|
||||||
jmespath (1.6.2)
|
jmespath (1.6.2)
|
||||||
json (2.6.3)
|
json (2.7.1)
|
||||||
jwt (2.7.1)
|
jwt (2.7.1)
|
||||||
mini_magick (4.12.0)
|
mini_magick (4.12.0)
|
||||||
mini_mime (1.1.5)
|
mini_mime (1.1.5)
|
||||||
@@ -157,11 +156,11 @@ GEM
|
|||||||
multipart-post (2.3.0)
|
multipart-post (2.3.0)
|
||||||
nanaimo (0.3.0)
|
nanaimo (0.3.0)
|
||||||
naturally (2.2.1)
|
naturally (2.2.1)
|
||||||
optparse (0.1.1)
|
optparse (0.4.0)
|
||||||
os (1.1.4)
|
os (1.1.4)
|
||||||
plist (3.7.0)
|
plist (3.7.1)
|
||||||
public_suffix (5.0.3)
|
public_suffix (5.0.4)
|
||||||
rake (13.0.6)
|
rake (13.1.0)
|
||||||
representable (3.2.0)
|
representable (3.2.0)
|
||||||
declarative (< 0.1.0)
|
declarative (< 0.1.0)
|
||||||
trailblazer-option (>= 0.1.1, < 0.2.0)
|
trailblazer-option (>= 0.1.1, < 0.2.0)
|
||||||
@@ -185,17 +184,13 @@ GEM
|
|||||||
unicode-display_width (>= 1.1.1, < 3)
|
unicode-display_width (>= 1.1.1, < 3)
|
||||||
trailblazer-option (0.1.2)
|
trailblazer-option (0.1.2)
|
||||||
tty-cursor (0.7.1)
|
tty-cursor (0.7.1)
|
||||||
tty-screen (0.8.1)
|
tty-screen (0.8.2)
|
||||||
tty-spinner (0.9.3)
|
tty-spinner (0.9.3)
|
||||||
tty-cursor (~> 0.7)
|
tty-cursor (~> 0.7)
|
||||||
uber (0.1.0)
|
uber (0.1.0)
|
||||||
unf (0.1.4)
|
unicode-display_width (2.5.0)
|
||||||
unf_ext
|
|
||||||
unf_ext (0.0.8.2)
|
|
||||||
unicode-display_width (2.4.2)
|
|
||||||
webrick (1.8.1)
|
|
||||||
word_wrap (1.0.0)
|
word_wrap (1.0.0)
|
||||||
xcodeproj (1.22.0)
|
xcodeproj (1.24.0)
|
||||||
CFPropertyList (>= 2.3.3, < 4.0)
|
CFPropertyList (>= 2.3.3, < 4.0)
|
||||||
atomos (~> 0.1.3)
|
atomos (~> 0.1.3)
|
||||||
claide (>= 1.0.2, < 2.0)
|
claide (>= 1.0.2, < 2.0)
|
||||||
|
|||||||
146
app/build.gradle
146
app/build.gradle
@@ -1,146 +0,0 @@
|
|||||||
import com.github.spotbugs.snom.SpotBugsTask
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
id 'com.android.application'
|
|
||||||
id 'com.github.spotbugs'
|
|
||||||
}
|
|
||||||
|
|
||||||
spotbugs {
|
|
||||||
ignoreFailures = false
|
|
||||||
effort = 'max'
|
|
||||||
excludeFilter = file("./config/spotbugs/exclude.xml")
|
|
||||||
reportsDir = file("$buildDir/reports/spotbugs/")
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
compileSdk 33
|
|
||||||
|
|
||||||
defaultConfig {
|
|
||||||
applicationId "me.hackerchick.catima"
|
|
||||||
minSdk 21
|
|
||||||
targetSdk 33
|
|
||||||
versionCode 131
|
|
||||||
versionName "2.26.0"
|
|
||||||
|
|
||||||
vectorDrawables.useSupportLibrary true
|
|
||||||
multiDexEnabled true
|
|
||||||
|
|
||||||
resourceConfigurations += ["ar", "bg", "bn", "bn-rIN", "bs", "cs", "da", "de", "el-rGR", "en", "eo", "es", "es-rAR", "fi", "fr", "he-rIL", "hi", "hr", "hu", "in-rID", "is", "it", "ja", "ko", "lt", "lv", "nb-rNO", "nl", "oc", "pl", "pt", "ro-rRO", "ru", "sk", "sl", "sv", "tr", "uk", "zh-rTW", "zh-rCN"]
|
|
||||||
}
|
|
||||||
|
|
||||||
buildTypes {
|
|
||||||
release {
|
|
||||||
minifyEnabled true
|
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
|
||||||
resValue "string", "app_name", "Catima"
|
|
||||||
}
|
|
||||||
debug {
|
|
||||||
applicationIdSuffix ".debug"
|
|
||||||
resValue "string", "app_name", "Catima Debug"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buildFeatures {
|
|
||||||
viewBinding true
|
|
||||||
}
|
|
||||||
|
|
||||||
bundle {
|
|
||||||
language {
|
|
||||||
enableSplit = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compileOptions {
|
|
||||||
encoding "UTF-8"
|
|
||||||
|
|
||||||
// Flag to enable support for the new language APIs
|
|
||||||
coreLibraryDesugaringEnabled true
|
|
||||||
|
|
||||||
sourceCompatibility JavaVersion.VERSION_11
|
|
||||||
targetCompatibility JavaVersion.VERSION_11
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
test {
|
|
||||||
resources.srcDirs += ['src/test/res']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Starting with Android Studio 3 Robolectric is unable to find resources.
|
|
||||||
// The following allows it to find the resources.
|
|
||||||
testOptions {
|
|
||||||
unitTests {
|
|
||||||
all {
|
|
||||||
testLogging {
|
|
||||||
events 'started', 'passed', 'skipped', 'failed'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
includeAndroidResources true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lint {
|
|
||||||
lintConfig file('lint.xml')
|
|
||||||
}
|
|
||||||
namespace 'protect.card_locker'
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
// AndroidX
|
|
||||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
|
||||||
implementation 'androidx.exifinterface:exifinterface:1.3.6'
|
|
||||||
implementation 'androidx.palette:palette:1.0.0'
|
|
||||||
implementation 'androidx.preference:preference:1.2.0'
|
|
||||||
implementation 'com.google.android.material:material:1.9.0'
|
|
||||||
implementation 'com.github.yalantis:ucrop:2.2.8'
|
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
|
|
||||||
|
|
||||||
// Splash Screen
|
|
||||||
implementation 'androidx.core:core-splashscreen:1.0.1'
|
|
||||||
|
|
||||||
// Third-party
|
|
||||||
implementation 'com.journeyapps:zxing-android-embedded:4.3.0@aar'
|
|
||||||
implementation 'com.google.zxing:core:3.5.2'
|
|
||||||
implementation 'org.apache.commons:commons-csv:1.9.0'
|
|
||||||
implementation 'com.jaredrummler:colorpicker:1.1.0'
|
|
||||||
implementation 'net.lingala.zip4j:zip4j:2.11.5'
|
|
||||||
|
|
||||||
// SpotBugs
|
|
||||||
implementation 'io.wcm.tooling.spotbugs:io.wcm.tooling.spotbugs.annotations:1.0.0'
|
|
||||||
|
|
||||||
// Testing
|
|
||||||
testImplementation 'androidx.test:core:1.5.0'
|
|
||||||
testImplementation 'junit:junit:4.13.2'
|
|
||||||
testImplementation 'org.robolectric:robolectric:4.10.3'
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.withType(SpotBugsTask) {
|
|
||||||
|
|
||||||
description 'Run spotbugs'
|
|
||||||
group 'verification'
|
|
||||||
|
|
||||||
//classes = fileTree('build/intermediates/javac/debug/compileDebugJavaWithJavac/classes')
|
|
||||||
//source = fileTree('src/main/java')
|
|
||||||
//classpath = files()
|
|
||||||
|
|
||||||
reports {
|
|
||||||
xml.enabled = false
|
|
||||||
html.enabled = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.register('copyRawResFiles', Copy) {
|
|
||||||
from layout.projectDirectory.file("../CHANGELOG.md"),
|
|
||||||
layout.projectDirectory.file("../PRIVACY.md")
|
|
||||||
into layout.projectDirectory.dir("src/main/res/raw")
|
|
||||||
rename { String fileName -> fileName.toLowerCase() }
|
|
||||||
}
|
|
||||||
|
|
||||||
project.afterEvaluate {
|
|
||||||
tasks.each { task ->
|
|
||||||
if (task != copyRawResFiles) {
|
|
||||||
task.dependsOn(copyRawResFiles)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
145
app/build.gradle.kts
Normal file
145
app/build.gradle.kts
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
import com.android.build.gradle.internal.tasks.factory.dependsOn
|
||||||
|
import com.github.spotbugs.snom.SpotBugsTask
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("com.android.application")
|
||||||
|
id("com.github.spotbugs")
|
||||||
|
}
|
||||||
|
|
||||||
|
spotbugs {
|
||||||
|
ignoreFailures.set(false)
|
||||||
|
setEffort("max")
|
||||||
|
excludeFilter.set(file("./config/spotbugs/exclude.xml"))
|
||||||
|
reportsDir.set(layout.buildDirectory.file("reports/spotbugs/").get().asFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "protect.card_locker"
|
||||||
|
compileSdk = 34
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId = "me.hackerchick.catima"
|
||||||
|
minSdk = 21
|
||||||
|
targetSdk = 34
|
||||||
|
versionCode = 133
|
||||||
|
versionName = "2.28.0"
|
||||||
|
|
||||||
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
multiDexEnabled = true
|
||||||
|
|
||||||
|
resourceConfigurations += listOf("ar", "bg", "bn", "bn-rIN", "bs", "cs", "da", "de", "el-rGR", "en", "eo", "es", "es-rAR", "fi", "fr", "he-rIL", "hi", "hr", "hu", "in-rID", "is", "it", "ja", "ko", "lt", "lv", "nb-rNO", "nl", "oc", "pl", "pt-rPT", "ro-rRO", "ru", "sk", "sl", "sv", "tr", "uk", "vi", "zh-rCN", "zh-rTW")
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
isMinifyEnabled = true
|
||||||
|
proguardFiles(
|
||||||
|
getDefaultProguardFile("proguard-android.txt"),
|
||||||
|
"proguard-rules.pro"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
debug {
|
||||||
|
applicationIdSuffix = ".debug"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildFeatures {
|
||||||
|
buildConfig = true
|
||||||
|
viewBinding = true
|
||||||
|
}
|
||||||
|
|
||||||
|
bundle {
|
||||||
|
language {
|
||||||
|
enableSplit = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
encoding = "UTF-8"
|
||||||
|
|
||||||
|
// Flag to enable support for the new language APIs
|
||||||
|
isCoreLibraryDesugaringEnabled = true
|
||||||
|
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
getByName("test") {
|
||||||
|
resources.srcDirs("src/test/res")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Starting with Android Studio 3 Robolectric is unable to find resources.
|
||||||
|
// The following allows it to find the resources.
|
||||||
|
testOptions.unitTests.isIncludeAndroidResources = true
|
||||||
|
tasks.withType<Test>().configureEach {
|
||||||
|
testLogging {
|
||||||
|
events("started", "passed", "skipped", "failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lint {
|
||||||
|
lintConfig = file("lint.xml")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
// AndroidX
|
||||||
|
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||||
|
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||||
|
implementation("androidx.exifinterface:exifinterface:1.3.7")
|
||||||
|
implementation("androidx.palette:palette:1.0.0")
|
||||||
|
implementation("androidx.preference:preference:1.2.1")
|
||||||
|
implementation("com.google.android.material:material:1.11.0")
|
||||||
|
implementation("com.github.yalantis:ucrop:2.2.8")
|
||||||
|
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4")
|
||||||
|
|
||||||
|
// Splash Screen
|
||||||
|
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||||
|
|
||||||
|
// Third-party
|
||||||
|
implementation("com.journeyapps:zxing-android-embedded:4.3.0@aar")
|
||||||
|
implementation("com.google.zxing:core:3.5.3")
|
||||||
|
implementation("org.apache.commons:commons-csv:1.9.0")
|
||||||
|
implementation("com.jaredrummler:colorpicker:1.1.0")
|
||||||
|
implementation("net.lingala.zip4j:zip4j:2.11.5")
|
||||||
|
|
||||||
|
// SpotBugs
|
||||||
|
implementation("io.wcm.tooling.spotbugs:io.wcm.tooling.spotbugs.annotations:1.0.0")
|
||||||
|
|
||||||
|
// Testing
|
||||||
|
testImplementation("androidx.test:core:1.5.0")
|
||||||
|
testImplementation("junit:junit:4.13.2")
|
||||||
|
testImplementation("org.robolectric:robolectric:4.11.1")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType<SpotBugsTask>().configureEach {
|
||||||
|
description = "Run spotbugs"
|
||||||
|
group = "verification"
|
||||||
|
|
||||||
|
//classes = fileTree("build/intermediates/javac/debug/compileDebugJavaWithJavac/classes")
|
||||||
|
//source = fileTree("src/main/java")
|
||||||
|
//classpath = files()
|
||||||
|
|
||||||
|
reports.maybeCreate("xml").required.set(false)
|
||||||
|
reports.maybeCreate("html").required.set(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("copyRawResFiles", Copy::class) {
|
||||||
|
from(
|
||||||
|
layout.projectDirectory.file("../CHANGELOG.md"),
|
||||||
|
layout.projectDirectory.file("../PRIVACY.md")
|
||||||
|
)
|
||||||
|
into(layout.projectDirectory.dir("src/main/res/raw"))
|
||||||
|
rename { it.lowercase() }
|
||||||
|
}.also {
|
||||||
|
tasks.preBuild.dependsOn(it)
|
||||||
|
tasks.getByName<Delete>("clean") {
|
||||||
|
val filesNamesToDelete = listOf("CHANGELOG", "PRIVACY")
|
||||||
|
filesNamesToDelete.forEach { fileName ->
|
||||||
|
delete(layout.projectDirectory.file("src/main/res/raw/${fileName.lowercase()}.md"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
app/proguard-rules.pro
vendored
2
app/proguard-rules.pro
vendored
@@ -2,7 +2,7 @@
|
|||||||
# By default, the flags in this file are appended to flags specified
|
# By default, the flags in this file are appended to flags specified
|
||||||
# in /Users/brarcher/Library/Android/sdk/tools/proguard/proguard-android.txt
|
# in /Users/brarcher/Library/Android/sdk/tools/proguard/proguard-android.txt
|
||||||
# You can edit the include path and order by changing the proguardFiles
|
# You can edit the include path and order by changing the proguardFiles
|
||||||
# directive in build.gradle.
|
# directive in build.gradle.kts.
|
||||||
#
|
#
|
||||||
# For more details, see
|
# For more details, see
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|||||||
2
app/src/debug/res/values-ar/strings.xml
Normal file
2
app/src/debug/res/values-ar/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
2
app/src/debug/res/values-ast/strings.xml
Normal file
2
app/src/debug/res/values-ast/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
2
app/src/debug/res/values-bg/strings.xml
Normal file
2
app/src/debug/res/values-bg/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
2
app/src/debug/res/values-bn-rIN/strings.xml
Normal file
2
app/src/debug/res/values-bn-rIN/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
2
app/src/debug/res/values-bn/strings.xml
Normal file
2
app/src/debug/res/values-bn/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
2
app/src/debug/res/values-bs/strings.xml
Normal file
2
app/src/debug/res/values-bs/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
2
app/src/debug/res/values-ca/strings.xml
Normal file
2
app/src/debug/res/values-ca/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
4
app/src/debug/res/values-cs/strings.xml
Normal file
4
app/src/debug/res/values-cs/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Catima Debug</string>
|
||||||
|
</resources>
|
||||||
2
app/src/debug/res/values-cy/strings.xml
Normal file
2
app/src/debug/res/values-cy/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
2
app/src/debug/res/values-da/strings.xml
Normal file
2
app/src/debug/res/values-da/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
4
app/src/debug/res/values-de/strings.xml
Normal file
4
app/src/debug/res/values-de/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Catima Debug</string>
|
||||||
|
</resources>
|
||||||
2
app/src/debug/res/values-el/strings.xml
Normal file
2
app/src/debug/res/values-el/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
2
app/src/debug/res/values-eo/strings.xml
Normal file
2
app/src/debug/res/values-eo/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
2
app/src/debug/res/values-es-rAR/strings.xml
Normal file
2
app/src/debug/res/values-es-rAR/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
4
app/src/debug/res/values-es/strings.xml
Normal file
4
app/src/debug/res/values-es/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Depuración de Catima</string>
|
||||||
|
</resources>
|
||||||
2
app/src/debug/res/values-fi/strings.xml
Normal file
2
app/src/debug/res/values-fi/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
4
app/src/debug/res/values-fr/strings.xml
Normal file
4
app/src/debug/res/values-fr/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Débogage de Catima</string>
|
||||||
|
</resources>
|
||||||
2
app/src/debug/res/values-he-rIL/strings.xml
Normal file
2
app/src/debug/res/values-he-rIL/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
4
app/src/debug/res/values-hi/strings.xml
Normal file
4
app/src/debug/res/values-hi/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">कैटिमा डीबग</string>
|
||||||
|
</resources>
|
||||||
4
app/src/debug/res/values-hr/strings.xml
Normal file
4
app/src/debug/res/values-hr/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Catima Debug</string>
|
||||||
|
</resources>
|
||||||
4
app/src/debug/res/values-hu/strings.xml
Normal file
4
app/src/debug/res/values-hu/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Catima Debug</string>
|
||||||
|
</resources>
|
||||||
4
app/src/debug/res/values-in/strings.xml
Normal file
4
app/src/debug/res/values-in/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Catima Debug</string>
|
||||||
|
</resources>
|
||||||
2
app/src/debug/res/values-is/strings.xml
Normal file
2
app/src/debug/res/values-is/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
4
app/src/debug/res/values-it/strings.xml
Normal file
4
app/src/debug/res/values-it/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Catima Debug</string>
|
||||||
|
</resources>
|
||||||
2
app/src/debug/res/values-ja/strings.xml
Normal file
2
app/src/debug/res/values-ja/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
4
app/src/debug/res/values-kn/strings.xml
Normal file
4
app/src/debug/res/values-kn/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">ಕ್ಯಾಟಿಮಾ ಡೀಬಗ್</string>
|
||||||
|
</resources>
|
||||||
4
app/src/debug/res/values-ko/strings.xml
Normal file
4
app/src/debug/res/values-ko/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Catima 디버그</string>
|
||||||
|
</resources>
|
||||||
2
app/src/debug/res/values-lb/strings.xml
Normal file
2
app/src/debug/res/values-lb/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
2
app/src/debug/res/values-lt/strings.xml
Normal file
2
app/src/debug/res/values-lt/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
2
app/src/debug/res/values-lv/strings.xml
Normal file
2
app/src/debug/res/values-lv/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
2
app/src/debug/res/values-ml/strings.xml
Normal file
2
app/src/debug/res/values-ml/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
2
app/src/debug/res/values-mr/strings.xml
Normal file
2
app/src/debug/res/values-mr/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
4
app/src/debug/res/values-nb-rNO/strings.xml
Normal file
4
app/src/debug/res/values-nb-rNO/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Catima-avlusing</string>
|
||||||
|
</resources>
|
||||||
4
app/src/debug/res/values-nl/strings.xml
Normal file
4
app/src/debug/res/values-nl/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Catima-foutopsporing</string>
|
||||||
|
</resources>
|
||||||
2
app/src/debug/res/values-oc/strings.xml
Normal file
2
app/src/debug/res/values-oc/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
4
app/src/debug/res/values-pl/strings.xml
Normal file
4
app/src/debug/res/values-pl/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Catima Debug</string>
|
||||||
|
</resources>
|
||||||
4
app/src/debug/res/values-pt-rPT/strings.xml
Normal file
4
app/src/debug/res/values-pt-rPT/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Depuração Catima</string>
|
||||||
|
</resources>
|
||||||
4
app/src/debug/res/values-ro/strings.xml
Normal file
4
app/src/debug/res/values-ro/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Depanare Catima</string>
|
||||||
|
</resources>
|
||||||
4
app/src/debug/res/values-ru/strings.xml
Normal file
4
app/src/debug/res/values-ru/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Отладка Catima</string>
|
||||||
|
</resources>
|
||||||
4
app/src/debug/res/values-sk/strings.xml
Normal file
4
app/src/debug/res/values-sk/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Catima Debug</string>
|
||||||
|
</resources>
|
||||||
2
app/src/debug/res/values-sl/strings.xml
Normal file
2
app/src/debug/res/values-sl/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
2
app/src/debug/res/values-sv/strings.xml
Normal file
2
app/src/debug/res/values-sv/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
4
app/src/debug/res/values-tr/strings.xml
Normal file
4
app/src/debug/res/values-tr/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Catima Hata Ayaklama</string>
|
||||||
|
</resources>
|
||||||
4
app/src/debug/res/values-uk/strings.xml
Normal file
4
app/src/debug/res/values-uk/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Catima Debug</string>
|
||||||
|
</resources>
|
||||||
4
app/src/debug/res/values-vi/strings.xml
Normal file
4
app/src/debug/res/values-vi/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Gỡ lỗi Catima</string>
|
||||||
|
</resources>
|
||||||
4
app/src/debug/res/values-zh-rCN/strings.xml
Normal file
4
app/src/debug/res/values-zh-rCN/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Catima 调试</string>
|
||||||
|
</resources>
|
||||||
2
app/src/debug/res/values-zh-rTW/strings.xml
Normal file
2
app/src/debug/res/values-zh-rTW/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
4
app/src/debug/res/values/strings.xml
Normal file
4
app/src/debug/res/values/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="app_name">Catima Debug</string>
|
||||||
|
</resources>
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
<application
|
<application
|
||||||
android:name=".LoyaltyCardLockerApplication"
|
android:name=".LoyaltyCardLockerApplication"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
android:enableOnBackInvokedCallback="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
@@ -32,7 +33,6 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:label="@string/app_name"
|
|
||||||
android:theme="@style/Theme.App.Starting">
|
android:theme="@style/Theme.App.Starting">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
@@ -44,6 +44,7 @@
|
|||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<data android:mimeType="image/*" />
|
<data android:mimeType="image/*" />
|
||||||
|
<data android:mimeType="application/pdf" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
@@ -111,10 +112,12 @@
|
|||||||
android:name=".preferences.SettingsActivity"
|
android:name=".preferences.SettingsActivity"
|
||||||
android:label="@string/settings"
|
android:label="@string/settings"
|
||||||
android:theme="@style/AppTheme.NoActionBar" />
|
android:theme="@style/AppTheme.NoActionBar" />
|
||||||
|
<!-- FIXME: locked screenOrientation is a workaround for https://github.com/CatimaLoyalty/Android/issues/1715, remove when https://github.com/CatimaLoyalty/Android/issues/513 is fixed -->
|
||||||
<activity
|
<activity
|
||||||
android:name=".ImportExportActivity"
|
android:name=".ImportExportActivity"
|
||||||
android:label="@string/importExport"
|
android:label="@string/importExport"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
android:screenOrientation="locked"
|
||||||
android:theme="@style/AppTheme.NoActionBar">
|
android:theme="@style/AppTheme.NoActionBar">
|
||||||
|
|
||||||
<!-- ZIP Intent Filter -->
|
<!-- ZIP Intent Filter -->
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import android.view.View;
|
|||||||
import android.widget.ScrollView;
|
import android.widget.ScrollView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
|
||||||
|
|
||||||
import protect.card_locker.databinding.AboutActivityBinding;
|
import protect.card_locker.databinding.AboutActivityBinding;
|
||||||
|
|
||||||
public class AboutActivity extends CatimaAppCompatActivity {
|
public class AboutActivity extends CatimaAppCompatActivity {
|
||||||
@@ -43,7 +43,7 @@ public class AboutActivity extends CatimaAppCompatActivity {
|
|||||||
binding.privacy.setTag("https://catima.app/privacy-policy/");
|
binding.privacy.setTag("https://catima.app/privacy-policy/");
|
||||||
binding.reportError.setTag("https://github.com/CatimaLoyalty/Android/issues");
|
binding.reportError.setTag("https://github.com/CatimaLoyalty/Android/issues");
|
||||||
binding.rate.setTag("https://play.google.com/store/apps/details?id=me.hackerchick.catima");
|
binding.rate.setTag("https://play.google.com/store/apps/details?id=me.hackerchick.catima");
|
||||||
binding.donate.setTag("https://catima.app/contribute/#donating");
|
binding.donate.setTag("https://catima.app/donate");
|
||||||
|
|
||||||
boolean installedFromGooglePlay = Utils.installedFromGooglePlay(this);
|
boolean installedFromGooglePlay = Utils.installedFromGooglePlay(this);
|
||||||
// Hide Google Play rate button if not on Google Play
|
// Hide Google Play rate button if not on Google Play
|
||||||
@@ -98,11 +98,7 @@ public class AboutActivity extends CatimaAppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void showCredits() {
|
private void showCredits() {
|
||||||
new MaterialAlertDialogBuilder(this)
|
showHTML(R.string.credits, content.getContributorInfo(), null);
|
||||||
.setTitle(R.string.credits)
|
|
||||||
.setMessage(content.getContributorInfo())
|
|
||||||
.setPositiveButton(R.string.ok, null)
|
|
||||||
.show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showHistory(View view) {
|
private void showHistory(View view) {
|
||||||
@@ -117,7 +113,7 @@ public class AboutActivity extends CatimaAppCompatActivity {
|
|||||||
showHTML(R.string.privacy_policy, content.getPrivacyInfo(), view);
|
showHTML(R.string.privacy_policy, content.getPrivacyInfo(), view);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showHTML(@StringRes int title, final Spanned text, View view) {
|
private void showHTML(@StringRes int title, final Spanned text, @Nullable View view) {
|
||||||
int dialogContentPadding = getResources().getDimensionPixelSize(R.dimen.alert_dialog_content_padding);
|
int dialogContentPadding = getResources().getDimensionPixelSize(R.dimen.alert_dialog_content_padding);
|
||||||
TextView textView = new TextView(this);
|
TextView textView = new TextView(this);
|
||||||
textView.setText(text);
|
textView.setText(text);
|
||||||
@@ -125,12 +121,21 @@ public class AboutActivity extends CatimaAppCompatActivity {
|
|||||||
ScrollView scrollView = new ScrollView(this);
|
ScrollView scrollView = new ScrollView(this);
|
||||||
scrollView.addView(textView);
|
scrollView.addView(textView);
|
||||||
scrollView.setPadding(dialogContentPadding, dialogContentPadding / 2, dialogContentPadding, 0);
|
scrollView.setPadding(dialogContentPadding, dialogContentPadding / 2, dialogContentPadding, 0);
|
||||||
new MaterialAlertDialogBuilder(this)
|
|
||||||
|
// Create dialog
|
||||||
|
MaterialAlertDialogBuilder materialAlertDialogBuilder = new MaterialAlertDialogBuilder(this);
|
||||||
|
materialAlertDialogBuilder
|
||||||
.setTitle(title)
|
.setTitle(title)
|
||||||
.setView(scrollView)
|
.setView(scrollView)
|
||||||
.setPositiveButton(R.string.ok, null)
|
.setPositiveButton(R.string.ok, null);
|
||||||
.setNeutralButton(R.string.view_online, (dialog, which) -> openExternalBrowser(view))
|
|
||||||
.show();
|
// Add View online button if an URL is linked to this view
|
||||||
|
if (view != null && view.getTag() != null) {
|
||||||
|
materialAlertDialogBuilder.setNeutralButton(R.string.view_online, (dialog, which) -> openExternalBrowser(view));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show dialog
|
||||||
|
materialAlertDialogBuilder.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openExternalBrowser(View view) {
|
private void openExternalBrowser(View view) {
|
||||||
|
|||||||
@@ -129,19 +129,19 @@ public class AboutContent {
|
|||||||
return result.toString();
|
return result.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getContributorInfo() {
|
public Spanned getContributorInfo() {
|
||||||
StringBuilder contributorInfo = new StringBuilder();
|
StringBuilder contributorInfo = new StringBuilder();
|
||||||
contributorInfo.append(getCopyright());
|
contributorInfo.append(getCopyright());
|
||||||
contributorInfo.append("\n\n");
|
contributorInfo.append("<br/><br/>");
|
||||||
contributorInfo.append(context.getString(R.string.app_copyright_old));
|
contributorInfo.append(context.getString(R.string.app_copyright_old));
|
||||||
contributorInfo.append("\n\n");
|
contributorInfo.append("<br/><br/>");
|
||||||
contributorInfo.append(HtmlCompat.fromHtml(String.format(context.getString(R.string.app_contributors), getContributors()), HtmlCompat.FROM_HTML_MODE_COMPACT));
|
contributorInfo.append(String.format(context.getString(R.string.app_contributors), getContributors()));
|
||||||
contributorInfo.append("\n\n");
|
contributorInfo.append("<br/><br/>");
|
||||||
contributorInfo.append(HtmlCompat.fromHtml(String.format(context.getString(R.string.app_libraries), getThirdPartyLibraries()), HtmlCompat.FROM_HTML_MODE_COMPACT));
|
contributorInfo.append(String.format(context.getString(R.string.app_libraries), getThirdPartyLibraries()));
|
||||||
contributorInfo.append("\n\n");
|
contributorInfo.append("<br/><br/>");
|
||||||
contributorInfo.append(HtmlCompat.fromHtml(String.format(context.getString(R.string.app_resources), getUsedThirdPartyAssets()), HtmlCompat.FROM_HTML_MODE_COMPACT));
|
contributorInfo.append(String.format(context.getString(R.string.app_resources), getUsedThirdPartyAssets()));
|
||||||
|
|
||||||
return contributorInfo.toString();
|
return HtmlCompat.fromHtml(contributorInfo.toString(), HtmlCompat.FROM_HTML_MODE_COMPACT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Spanned getHistoryInfo() {
|
public Spanned getHistoryInfo() {
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ import android.widget.EditText;
|
|||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
|
||||||
import com.google.zxing.BarcodeFormat;
|
import com.google.zxing.BarcodeFormat;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import androidx.appcompat.widget.Toolbar;
|
|
||||||
|
|
||||||
import protect.card_locker.databinding.BarcodeSelectorActivityBinding;
|
import protect.card_locker.databinding.BarcodeSelectorActivityBinding;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,12 +3,17 @@ package protect.card_locker;
|
|||||||
public class BarcodeValues {
|
public class BarcodeValues {
|
||||||
private final String mFormat;
|
private final String mFormat;
|
||||||
private final String mContent;
|
private final String mContent;
|
||||||
|
private String mNote;
|
||||||
|
|
||||||
public BarcodeValues(String format, String content) {
|
public BarcodeValues(String format, String content) {
|
||||||
mFormat = format;
|
mFormat = format;
|
||||||
mContent = content;
|
mContent = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setNote(String note) {
|
||||||
|
mNote = note;
|
||||||
|
}
|
||||||
|
|
||||||
public String format() {
|
public String format() {
|
||||||
return mFormat;
|
return mFormat;
|
||||||
}
|
}
|
||||||
@@ -17,7 +22,5 @@ public class BarcodeValues {
|
|||||||
return mContent;
|
return mContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEmpty() {
|
public String note() { return mNote; }
|
||||||
return mFormat == null && mContent == null;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package protect.card_locker;
|
||||||
|
|
||||||
|
public interface BarcodeValuesListDisambiguatorCallback {
|
||||||
|
void onUserChoseBarcode(BarcodeValues barcodeValues);
|
||||||
|
void onUserDismissedSelector();
|
||||||
|
}
|
||||||
@@ -15,13 +15,13 @@ import android.service.controls.actions.ControlAction;
|
|||||||
import android.service.controls.templates.StatelessTemplate;
|
import android.service.controls.templates.StatelessTemplate;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Flow;
|
import java.util.concurrent.Flow;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.R)
|
@RequiresApi(Build.VERSION_CODES.R)
|
||||||
public class CardsOnPowerScreenService extends ControlsProviderService {
|
public class CardsOnPowerScreenService extends ControlsProviderService {
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import android.graphics.Color;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.Window;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@@ -13,6 +14,8 @@ import androidx.appcompat.app.AppCompatActivity;
|
|||||||
import androidx.core.view.WindowInsetsControllerCompat;
|
import androidx.core.view.WindowInsetsControllerCompat;
|
||||||
|
|
||||||
public class CatimaAppCompatActivity extends AppCompatActivity {
|
public class CatimaAppCompatActivity extends AppCompatActivity {
|
||||||
|
protected boolean activityOverridesNavBarColor = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void attachBaseContext(Context base) {
|
protected void attachBaseContext(Context base) {
|
||||||
// Apply chosen language
|
// Apply chosen language
|
||||||
@@ -30,20 +33,31 @@ public class CatimaAppCompatActivity extends AppCompatActivity {
|
|||||||
super.onPostCreate(savedInstanceState);
|
super.onPostCreate(savedInstanceState);
|
||||||
// material 3 designer does not consider status bar colors
|
// material 3 designer does not consider status bar colors
|
||||||
// XXX changing this in onCreate causes issues with the splash screen activity, so doing this here
|
// XXX changing this in onCreate causes issues with the splash screen activity, so doing this here
|
||||||
boolean darkMode = Utils.isDarkModeEnabled(this);
|
Window window = getWindow();
|
||||||
if (Build.VERSION.SDK_INT >= 23) {
|
if (window != null) {
|
||||||
View decorView = getWindow().getDecorView();
|
boolean darkMode = Utils.isDarkModeEnabled(this);
|
||||||
WindowInsetsControllerCompat wic = new WindowInsetsControllerCompat(getWindow(), decorView);
|
if (Build.VERSION.SDK_INT >= 23) {
|
||||||
wic.setAppearanceLightStatusBars(!darkMode);
|
View decorView = window.getDecorView();
|
||||||
getWindow().setStatusBarColor(Color.TRANSPARENT);
|
WindowInsetsControllerCompat wic = new WindowInsetsControllerCompat(window, decorView);
|
||||||
} else {
|
wic.setAppearanceLightStatusBars(!darkMode);
|
||||||
// icons are always white back then
|
window.setStatusBarColor(Color.TRANSPARENT);
|
||||||
getWindow().setStatusBarColor(darkMode ? Color.TRANSPARENT : Color.argb(127, 0, 0, 0));
|
} else {
|
||||||
|
// icons are always white back then
|
||||||
|
window.setStatusBarColor(darkMode ? Color.TRANSPARENT : Color.argb(127, 0, 0, 0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// XXX android 9 and below has a nasty rendering bug if the theme was patched earlier
|
// XXX android 9 and below has a nasty rendering bug if the theme was patched earlier
|
||||||
Utils.postPatchColors(this);
|
Utils.postPatchColors(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
if (!activityOverridesNavBarColor) {
|
||||||
|
Utils.setNavigationBarColor(this, null, Utils.resolveBackgroundColor(this), !Utils.isDarkModeEnabled(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void enableToolbarBackButton() {
|
protected void enableToolbarBackButton() {
|
||||||
ActionBar actionBar = getSupportActionBar();
|
ActionBar actionBar = getSupportActionBar();
|
||||||
if (actionBar != null) {
|
if (actionBar != null) {
|
||||||
|
|||||||
@@ -8,14 +8,12 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import protect.card_locker.databinding.GroupLayoutBinding;
|
import protect.card_locker.databinding.GroupLayoutBinding;
|
||||||
import protect.card_locker.preferences.Settings;
|
|
||||||
|
|
||||||
public class GroupCursorAdapter extends BaseCursorAdapter<GroupCursorAdapter.GroupListItemViewHolder> {
|
public class GroupCursorAdapter extends BaseCursorAdapter<GroupCursorAdapter.GroupListItemViewHolder> {
|
||||||
public final Context mContext;
|
public final Context mContext;
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
package protect.card_locker;
|
package protect.card_locker;
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
@@ -14,25 +12,24 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
import com.google.android.material.textfield.TextInputLayout;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import androidx.activity.result.ActivityResultLauncher;
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.appcompat.app.ActionBar;
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
|
||||||
import androidx.appcompat.widget.Toolbar;
|
|
||||||
import androidx.core.app.ActivityCompat;
|
|
||||||
import androidx.core.content.ContextCompat;
|
|
||||||
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
|
||||||
|
|
||||||
import protect.card_locker.async.TaskHandler;
|
import protect.card_locker.async.TaskHandler;
|
||||||
import protect.card_locker.databinding.ImportExportActivityBinding;
|
import protect.card_locker.databinding.ImportExportActivityBinding;
|
||||||
import protect.card_locker.importexport.DataFormat;
|
import protect.card_locker.importexport.DataFormat;
|
||||||
@@ -126,16 +123,19 @@ public class ImportExportActivity extends CatimaAppCompatActivity {
|
|||||||
builder.setTitle(R.string.exportPassword);
|
builder.setTitle(R.string.exportPassword);
|
||||||
|
|
||||||
FrameLayout container = new FrameLayout(ImportExportActivity.this);
|
FrameLayout container = new FrameLayout(ImportExportActivity.this);
|
||||||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
|
||||||
params.leftMargin = 50;
|
final TextInputLayout textInputLayout = new TextInputLayout(ImportExportActivity.this);
|
||||||
params.rightMargin = 50;
|
textInputLayout.setEndIconMode(TextInputLayout.END_ICON_PASSWORD_TOGGLE);
|
||||||
|
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
params.setMargins(50, 10, 50, 0);
|
||||||
|
textInputLayout.setLayoutParams(params);
|
||||||
|
|
||||||
final EditText input = new EditText(ImportExportActivity.this);
|
final EditText input = new EditText(ImportExportActivity.this);
|
||||||
input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||||
input.setLayoutParams(params);
|
|
||||||
input.setHint(R.string.exportPasswordHint);
|
input.setHint(R.string.exportPasswordHint);
|
||||||
|
|
||||||
container.addView(input);
|
textInputLayout.addView(input);
|
||||||
|
container.addView(textInputLayout);
|
||||||
builder.setView(container);
|
builder.setView(container);
|
||||||
builder.setPositiveButton(R.string.ok, (dialogInterface, i) -> {
|
builder.setPositiveButton(R.string.ok, (dialogInterface, i) -> {
|
||||||
exportPassword = input.getText().toString();
|
exportPassword = input.getText().toString();
|
||||||
@@ -148,7 +148,6 @@ public class ImportExportActivity extends CatimaAppCompatActivity {
|
|||||||
});
|
});
|
||||||
builder.setNegativeButton(R.string.cancel, (dialogInterface, i) -> dialogInterface.cancel());
|
builder.setNegativeButton(R.string.cancel, (dialogInterface, i) -> dialogInterface.cancel());
|
||||||
builder.show();
|
builder.show();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check that there is a file manager available
|
// Check that there is a file manager available
|
||||||
@@ -320,9 +319,21 @@ public class ImportExportActivity extends CatimaAppCompatActivity {
|
|||||||
AlertDialog.Builder builder = new MaterialAlertDialogBuilder(this);
|
AlertDialog.Builder builder = new MaterialAlertDialogBuilder(this);
|
||||||
builder.setTitle(R.string.passwordRequired);
|
builder.setTitle(R.string.passwordRequired);
|
||||||
|
|
||||||
final EditText input = new EditText(this);
|
FrameLayout container = new FrameLayout(ImportExportActivity.this);
|
||||||
|
|
||||||
|
final TextInputLayout textInputLayout = new TextInputLayout(ImportExportActivity.this);
|
||||||
|
textInputLayout.setEndIconMode(TextInputLayout.END_ICON_PASSWORD_TOGGLE);
|
||||||
|
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
params.setMargins(50, 10, 50, 0);
|
||||||
|
textInputLayout.setLayoutParams(params);
|
||||||
|
|
||||||
|
final EditText input = new EditText(ImportExportActivity.this);
|
||||||
input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||||
builder.setView(input);
|
input.setHint(R.string.exportPasswordHint);
|
||||||
|
|
||||||
|
textInputLayout.addView(input);
|
||||||
|
container.addView(textInputLayout);
|
||||||
|
builder.setView(container);
|
||||||
|
|
||||||
builder.setPositiveButton(R.string.ok, (dialogInterface, i) -> {
|
builder.setPositiveButton(R.string.ok, (dialogInterface, i) -> {
|
||||||
openFileForImport(uri, input.getText().toString().toCharArray());
|
openFileForImport(uri, input.getText().toString().toCharArray());
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import android.database.Cursor;
|
|||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.Currency;
|
import java.util.Currency;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
public class LoyaltyCard implements Parcelable {
|
public class LoyaltyCard implements Parcelable {
|
||||||
public final int id;
|
public final int id;
|
||||||
public final String store;
|
public final String store;
|
||||||
|
|||||||
@@ -15,13 +15,6 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.google.android.material.card.MaterialCardView;
|
|
||||||
import com.google.android.material.color.MaterialColors;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.text.DateFormat;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
@@ -29,6 +22,13 @@ import androidx.core.graphics.BlendModeColorFilterCompat;
|
|||||||
import androidx.core.graphics.BlendModeCompat;
|
import androidx.core.graphics.BlendModeCompat;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.google.android.material.card.MaterialCardView;
|
||||||
|
import com.google.android.material.color.MaterialColors;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import protect.card_locker.databinding.LoyaltyCardLayoutBinding;
|
import protect.card_locker.databinding.LoyaltyCardLayoutBinding;
|
||||||
|
|
||||||
public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCursorAdapter.LoyaltyCardListItemViewHolder> {
|
public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCursorAdapter.LoyaltyCardListItemViewHolder> {
|
||||||
@@ -123,8 +123,7 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
|||||||
}
|
}
|
||||||
|
|
||||||
inputHolder.mCardIcon.setContentDescription(loyaltyCard.store);
|
inputHolder.mCardIcon.setContentDescription(loyaltyCard.store);
|
||||||
Utils.setIconOrTextWithBackground(mContext, loyaltyCard, icon, inputHolder.mCardIcon, inputHolder.mCardText);
|
inputHolder.mIconBackgroundColor = Utils.setIconOrTextWithBackground(mContext, loyaltyCard, icon, inputHolder.mCardIcon, inputHolder.mCardText);
|
||||||
inputHolder.setIconBackgroundColor(Utils.getHeaderColor(mContext, loyaltyCard));
|
|
||||||
|
|
||||||
inputHolder.toggleCardStateIcon(loyaltyCard.starStatus != 0, loyaltyCard.archiveStatus != 0, itemSelected(inputCursor.getPosition()));
|
inputHolder.toggleCardStateIcon(loyaltyCard.starStatus != 0, loyaltyCard.archiveStatus != 0, itemSelected(inputCursor.getPosition()));
|
||||||
|
|
||||||
@@ -339,11 +338,6 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
|||||||
mArchivedBackground.invalidate();
|
mArchivedBackground.invalidate();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setIconBackgroundColor(int color) {
|
|
||||||
mIconBackgroundColor = color;
|
|
||||||
mCardIcon.setBackgroundColor(color);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int dpToPx(int dp, Context mContext) {
|
public int dpToPx(int dp, Context mContext) {
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ package protect.card_locker;
|
|||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.DatePickerDialog;
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -29,33 +27,34 @@ import android.view.WindowManager;
|
|||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
import android.widget.AutoCompleteTextView;
|
import android.widget.AutoCompleteTextView;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.DatePicker;
|
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.activity.OnBackPressedCallback;
|
||||||
import androidx.activity.result.ActivityResultLauncher;
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
import androidx.activity.result.contract.ActivityResultContracts;
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
|
||||||
import androidx.appcompat.widget.AppCompatTextView;
|
import androidx.appcompat.widget.AppCompatTextView;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.core.content.FileProvider;
|
import androidx.core.content.FileProvider;
|
||||||
import androidx.exifinterface.media.ExifInterface;
|
import androidx.exifinterface.media.ExifInterface;
|
||||||
import androidx.fragment.app.DialogFragment;
|
|
||||||
|
|
||||||
import com.google.android.material.chip.Chip;
|
import com.google.android.material.chip.Chip;
|
||||||
import com.google.android.material.chip.ChipGroup;
|
import com.google.android.material.chip.ChipGroup;
|
||||||
import com.google.android.material.color.MaterialColors;
|
import com.google.android.material.color.MaterialColors;
|
||||||
|
import com.google.android.material.datepicker.CalendarConstraints;
|
||||||
|
import com.google.android.material.datepicker.DateValidatorPointBackward;
|
||||||
|
import com.google.android.material.datepicker.DateValidatorPointForward;
|
||||||
|
import com.google.android.material.datepicker.MaterialDatePicker;
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
|
||||||
import com.google.android.material.tabs.TabLayout;
|
import com.google.android.material.tabs.TabLayout;
|
||||||
import com.jaredrummler.android.colorpicker.ColorPickerDialog;
|
import com.jaredrummler.android.colorpicker.ColorPickerDialog;
|
||||||
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener;
|
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener;
|
||||||
@@ -74,7 +73,6 @@ import java.util.Calendar;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Currency;
|
import java.util.Currency;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.GregorianCalendar;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@@ -94,6 +92,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
|||||||
|
|
||||||
private final String STATE_TAB_INDEX = "savedTab";
|
private final String STATE_TAB_INDEX = "savedTab";
|
||||||
private final String STATE_TEMP_CARD = "tempLoyaltyCard";
|
private final String STATE_TEMP_CARD = "tempLoyaltyCard";
|
||||||
|
private final String STATE_TEMP_CARD_FIELD = "tempLoyaltyCardField";
|
||||||
private final String STATE_REQUESTED_IMAGE = "requestedImage";
|
private final String STATE_REQUESTED_IMAGE = "requestedImage";
|
||||||
private final String STATE_FRONT_IMAGE_UNSAVED = "frontImageUnsaved";
|
private final String STATE_FRONT_IMAGE_UNSAVED = "frontImageUnsaved";
|
||||||
private final String STATE_BACK_IMAGE_UNSAVED = "backImageUnsaved";
|
private final String STATE_BACK_IMAGE_UNSAVED = "backImageUnsaved";
|
||||||
@@ -105,6 +104,9 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
|||||||
private final String STATE_ICON_REMOVED = "iconRemoved";
|
private final String STATE_ICON_REMOVED = "iconRemoved";
|
||||||
private final String STATE_OPEN_SET_ICON_MENU = "openSetIconMenu";
|
private final String STATE_OPEN_SET_ICON_MENU = "openSetIconMenu";
|
||||||
|
|
||||||
|
private static final String PICK_DATE_REQUEST_KEY = "pick_date_request";
|
||||||
|
private static final String NEWLY_PICKED_DATE_ARGUMENT_KEY = "newly_picked_date";
|
||||||
|
|
||||||
private final String TEMP_CAMERA_IMAGE_NAME = LoyaltyCardEditActivity.class.getSimpleName() + "_camera_image.jpg";
|
private final String TEMP_CAMERA_IMAGE_NAME = LoyaltyCardEditActivity.class.getSimpleName() + "_camera_image.jpg";
|
||||||
private final String TEMP_CROP_IMAGE_NAME = LoyaltyCardEditActivity.class.getSimpleName() + "_crop_image.png";
|
private final String TEMP_CROP_IMAGE_NAME = LoyaltyCardEditActivity.class.getSimpleName() + "_crop_image.png";
|
||||||
private final Bitmap.CompressFormat TEMP_CROP_IMAGE_FORMAT = Bitmap.CompressFormat.PNG;
|
private final Bitmap.CompressFormat TEMP_CROP_IMAGE_FORMAT = Bitmap.CompressFormat.PNG;
|
||||||
@@ -182,6 +184,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
|||||||
HashMap<String, String> currencySymbols = new HashMap<>();
|
HashMap<String, String> currencySymbols = new HashMap<>();
|
||||||
|
|
||||||
LoyaltyCard tempLoyaltyCard;
|
LoyaltyCard tempLoyaltyCard;
|
||||||
|
LoyaltyCardField tempLoyaltyCardField;
|
||||||
|
|
||||||
ActivityResultLauncher<Uri> mPhotoTakerLauncher;
|
ActivityResultLauncher<Uri> mPhotoTakerLauncher;
|
||||||
ActivityResultLauncher<Intent> mPhotoPickerLauncher;
|
ActivityResultLauncher<Intent> mPhotoPickerLauncher;
|
||||||
@@ -267,6 +270,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
|||||||
tabs = binding.tabs;
|
tabs = binding.tabs;
|
||||||
savedInstanceState.putInt(STATE_TAB_INDEX, tabs.getSelectedTabPosition());
|
savedInstanceState.putInt(STATE_TAB_INDEX, tabs.getSelectedTabPosition());
|
||||||
savedInstanceState.putParcelable(STATE_TEMP_CARD, tempLoyaltyCard);
|
savedInstanceState.putParcelable(STATE_TEMP_CARD, tempLoyaltyCard);
|
||||||
|
savedInstanceState.putSerializable(STATE_TEMP_CARD_FIELD, tempLoyaltyCardField);
|
||||||
savedInstanceState.putInt(STATE_REQUESTED_IMAGE, mRequestedImage);
|
savedInstanceState.putInt(STATE_REQUESTED_IMAGE, mRequestedImage);
|
||||||
|
|
||||||
Object cardImageFrontObj = cardImageFront.getTag();
|
Object cardImageFrontObj = cardImageFront.getTag();
|
||||||
@@ -302,6 +306,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
|||||||
public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
|
public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
|
||||||
onRestoring = true;
|
onRestoring = true;
|
||||||
tempLoyaltyCard = savedInstanceState.getParcelable(STATE_TEMP_CARD);
|
tempLoyaltyCard = savedInstanceState.getParcelable(STATE_TEMP_CARD);
|
||||||
|
tempLoyaltyCardField = (LoyaltyCardField) savedInstanceState.getSerializable(STATE_TEMP_CARD_FIELD);
|
||||||
super.onRestoreInstanceState(savedInstanceState);
|
super.onRestoreInstanceState(savedInstanceState);
|
||||||
tabs = binding.tabs;
|
tabs = binding.tabs;
|
||||||
tabs.selectTab(tabs.getTabAt(savedInstanceState.getInt(STATE_TAB_INDEX)));
|
tabs.selectTab(tabs.getTabAt(savedInstanceState.getInt(STATE_TAB_INDEX)));
|
||||||
@@ -387,20 +392,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
|||||||
|
|
||||||
addDateFieldTextChangedListener(expiryField, R.string.never, R.string.chooseExpiryDate, LoyaltyCardField.expiry);
|
addDateFieldTextChangedListener(expiryField, R.string.never, R.string.chooseExpiryDate, LoyaltyCardField.expiry);
|
||||||
|
|
||||||
DatePickerFragment.registerDatePickListener(this, (textFieldToEdit, newDate) -> {
|
setMaterialDatePickerResultListener();
|
||||||
switch (textFieldToEdit) {
|
|
||||||
case validFrom:
|
|
||||||
formatDateField(this, validFromField, newDate);
|
|
||||||
updateTempState(LoyaltyCardField.validFrom, newDate);
|
|
||||||
break;
|
|
||||||
case expiry:
|
|
||||||
formatDateField(this, expiryField, newDate);
|
|
||||||
updateTempState(LoyaltyCardField.expiry, newDate);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new AssertionError("Unexpected field: " + textFieldToEdit);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
balanceField.setOnFocusChangeListener((v, hasFocus) -> {
|
balanceField.setOnFocusChangeListener((v, hasFocus) -> {
|
||||||
if (!hasFocus && !onResuming && !onRestoring) {
|
if (!hasFocus && !onResuming && !onRestoring) {
|
||||||
@@ -654,11 +646,22 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
|||||||
Log.d("barcode card id editor", "barcode and card id editor picker returned without an intent");
|
Log.d("barcode card id editor", "barcode and card id editor picker returned without an intent");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(Utils.BARCODE_SCAN, result.getResultCode(), intent, getApplicationContext());
|
|
||||||
|
|
||||||
cardId = barcodeValues.content();
|
List<BarcodeValues> barcodeValuesList = Utils.parseSetBarcodeActivityResult(Utils.BARCODE_SCAN, result.getResultCode(), intent, getApplicationContext());
|
||||||
barcodeType = barcodeValues.format();
|
|
||||||
barcodeId = "";
|
Utils.makeUserChooseBarcodeFromList(this, barcodeValuesList, new BarcodeValuesListDisambiguatorCallback() {
|
||||||
|
@Override
|
||||||
|
public void onUserChoseBarcode(BarcodeValues barcodeValues) {
|
||||||
|
cardId = barcodeValues.content();
|
||||||
|
barcodeType = barcodeValues.format();
|
||||||
|
barcodeId = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUserDismissedSelector() {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -705,6 +708,13 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
|||||||
});
|
});
|
||||||
|
|
||||||
mCropperOptions = new UCrop.Options();
|
mCropperOptions = new UCrop.Options();
|
||||||
|
|
||||||
|
getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
|
||||||
|
@Override
|
||||||
|
public void handleOnBackPressed() {
|
||||||
|
askBeforeQuitIfChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ucrop 2.2.6 initial aspect ratio is glitched when 0x0 is used as the initial ratio option
|
// ucrop 2.2.6 initial aspect ratio is glitched when 0x0 is used as the initial ratio option
|
||||||
@@ -778,7 +788,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
|||||||
|
|
||||||
@SuppressLint("DefaultLocale")
|
@SuppressLint("DefaultLocale")
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
||||||
Log.i(TAG, "To view card: " + loyaltyCardId);
|
Log.i(TAG, "To view card: " + loyaltyCardId);
|
||||||
@@ -1016,14 +1026,14 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
|||||||
if (!lastValue.toString().equals(getString(chooseDateOptionStringId))) {
|
if (!lastValue.toString().equals(getString(chooseDateOptionStringId))) {
|
||||||
dateField.setText(lastValue);
|
dateField.setText(lastValue);
|
||||||
}
|
}
|
||||||
DialogFragment datePickerFragment = DatePickerFragment.newInstance(
|
showDatePicker(
|
||||||
loyaltyCardField,
|
loyaltyCardField,
|
||||||
(Date) dateField.getTag(),
|
(Date) dateField.getTag(),
|
||||||
// if the expiry date is being set, set date picker's minDate to the 'valid from' date
|
// if the expiry date is being set, set date picker's minDate to the 'valid from' date
|
||||||
loyaltyCardField == LoyaltyCardField.expiry ? (Date) validFromField.getTag() : null,
|
loyaltyCardField == LoyaltyCardField.expiry ? (Date) validFromField.getTag() : null,
|
||||||
// if the 'valid from' date is being set, set date picker's maxDate to the expiry date
|
// if the 'valid from' date is being set, set date picker's maxDate to the expiry date
|
||||||
loyaltyCardField == LoyaltyCardField.validFrom ? (Date) expiryField.getTag() : null);
|
loyaltyCardField == LoyaltyCardField.validFrom ? (Date) expiryField.getTag() : null
|
||||||
datePickerFragment.show(getSupportFragmentManager(), "datePicker");
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1064,11 +1074,6 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBackPressed() {
|
|
||||||
askBeforeQuitIfChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
@@ -1379,103 +1384,106 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
|||||||
// Nothing to do, no change made
|
// Nothing to do, no change made
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class DatePickerFragment extends DialogFragment
|
private void showDatePicker(
|
||||||
implements DatePickerDialog.OnDateSetListener {
|
LoyaltyCardField loyaltyCardField,
|
||||||
|
@Nullable Date selectedDate,
|
||||||
|
@Nullable Date minDate,
|
||||||
|
@Nullable Date maxDate
|
||||||
|
) {
|
||||||
|
// Create a new instance of MaterialDatePicker and return it
|
||||||
|
long startDate = minDate != null ? minDate.getTime() : getDefaultMinDateOfDatePicker();
|
||||||
|
long endDate = maxDate != null ? maxDate.getTime() : getDefaultMaxDateOfDatePicker();
|
||||||
|
|
||||||
public interface OnDatePickListener {
|
CalendarConstraints.DateValidator dateValidator;
|
||||||
void onDatePicked(@NonNull LoyaltyCardField textFieldToEdit, @NonNull Date newDate);
|
switch (loyaltyCardField) {
|
||||||
|
case validFrom:
|
||||||
|
dateValidator = DateValidatorPointBackward.before(endDate);
|
||||||
|
break;
|
||||||
|
case expiry:
|
||||||
|
dateValidator = DateValidatorPointForward.from(startDate);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new AssertionError("Unexpected field: " + loyaltyCardField);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String TEXT_FIELD_TO_EDIT_ARGUMENT_KEY = "text_field_to_edit";
|
CalendarConstraints calendarConstraints = new CalendarConstraints.Builder()
|
||||||
private static final String CURRENT_DATE_ARGUMENT_KEY = "current_date";
|
.setValidator(dateValidator)
|
||||||
private static final String MIN_DATE_ARGUMENT_KEY = "min_date";
|
.setStart(startDate)
|
||||||
private static final String MAX_DATE_ARGUMENT_KEY = "max_date";
|
.setEnd(endDate)
|
||||||
private static final String PICK_DATE_REQUEST_KEY = "pick_date_request";
|
.build();
|
||||||
private static final String NEWLY_PICKED_DATE_ARGUMENT_KEY = "newly_picked_date";
|
|
||||||
|
|
||||||
LoyaltyCardField textFieldEdit;
|
// Use the selected date as the default date in the picker
|
||||||
@Nullable
|
final Calendar calendar = Calendar.getInstance();
|
||||||
Date minDate;
|
if (selectedDate != null) {
|
||||||
@Nullable
|
calendar.setTime(selectedDate);
|
||||||
Date maxDate;
|
|
||||||
|
|
||||||
public static DatePickerFragment newInstance(@NonNull LoyaltyCardField textField, @Nullable Date currentDate, @Nullable Date minDate, @Nullable Date maxDate) {
|
|
||||||
Bundle args = new Bundle();
|
|
||||||
args.putSerializable(TEXT_FIELD_TO_EDIT_ARGUMENT_KEY, textField);
|
|
||||||
args.putSerializable(CURRENT_DATE_ARGUMENT_KEY, currentDate);
|
|
||||||
args.putSerializable(MIN_DATE_ARGUMENT_KEY, minDate);
|
|
||||||
args.putSerializable(MAX_DATE_ARGUMENT_KEY, maxDate);
|
|
||||||
DatePickerFragment fragment = new DatePickerFragment();
|
|
||||||
fragment.setArguments(args);
|
|
||||||
return fragment;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void registerDatePickListener(@NonNull AppCompatActivity activity, @NonNull OnDatePickListener listener) {
|
MaterialDatePicker<Long> materialDatePicker = MaterialDatePicker.Builder.datePicker()
|
||||||
activity.getSupportFragmentManager().setFragmentResultListener(
|
.setSelection(calendar.getTimeInMillis())
|
||||||
PICK_DATE_REQUEST_KEY,
|
.setCalendarConstraints(calendarConstraints)
|
||||||
activity,
|
.build();
|
||||||
(requestKey, result) -> listener.onDatePicked(
|
|
||||||
(LoyaltyCardField) Objects.requireNonNull(result.getSerializable(TEXT_FIELD_TO_EDIT_ARGUMENT_KEY)),
|
|
||||||
(Date) Objects.requireNonNull(result.getSerializable(NEWLY_PICKED_DATE_ARGUMENT_KEY))));
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
// Required to handle configuration changes
|
||||||
@Override
|
// See https://github.com/material-components/material-components-android/issues/1688
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
tempLoyaltyCardField = loyaltyCardField;
|
||||||
Bundle args = requireArguments();
|
getSupportFragmentManager().addFragmentOnAttachListener((fragmentManager, fragment) -> {
|
||||||
textFieldEdit = (LoyaltyCardField) args.getSerializable(TEXT_FIELD_TO_EDIT_ARGUMENT_KEY);
|
if (fragment instanceof MaterialDatePicker && Objects.equals(fragment.getTag(), PICK_DATE_REQUEST_KEY)) {
|
||||||
minDate = (Date) args.getSerializable(MIN_DATE_ARGUMENT_KEY);
|
((MaterialDatePicker<Long>) fragment).addOnPositiveButtonClickListener(selection -> {
|
||||||
maxDate = (Date) args.getSerializable(MAX_DATE_ARGUMENT_KEY);
|
Bundle args = new Bundle();
|
||||||
// Use the current date as the default date in the picker
|
args.putLong(NEWLY_PICKED_DATE_ARGUMENT_KEY, selection);
|
||||||
final Calendar c = Calendar.getInstance();
|
getSupportFragmentManager().setFragmentResult(PICK_DATE_REQUEST_KEY, args);
|
||||||
|
});
|
||||||
Date date = (Date) args.getSerializable(CURRENT_DATE_ARGUMENT_KEY);
|
|
||||||
if (date != null) {
|
|
||||||
c.setTime(date);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
int year = c.get(Calendar.YEAR);
|
materialDatePicker.show(getSupportFragmentManager(), PICK_DATE_REQUEST_KEY);
|
||||||
int month = c.get(Calendar.MONTH);
|
}
|
||||||
int day = c.get(Calendar.DAY_OF_MONTH);
|
|
||||||
|
|
||||||
// Create a new instance of DatePickerDialog and return it
|
// Required to handle configuration changes
|
||||||
DatePickerDialog datePickerDialog = new DatePickerDialog(getActivity(), this, year, month, day);
|
// See https://github.com/material-components/material-components-android/issues/1688
|
||||||
datePickerDialog.getDatePicker().setMinDate(minDate != null ? minDate.getTime() : getDefaultMinDateOfDatePicker());
|
private void setMaterialDatePickerResultListener() {
|
||||||
datePickerDialog.getDatePicker().setMaxDate(maxDate != null ? maxDate.getTime() : getDefaultMaxDateOfDatePicker());
|
MaterialDatePicker<Long> fragment = (MaterialDatePicker<Long>) getSupportFragmentManager().findFragmentByTag(PICK_DATE_REQUEST_KEY);
|
||||||
return datePickerDialog;
|
if (fragment != null) {
|
||||||
|
fragment.addOnPositiveButtonClickListener(selection -> {
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putLong(NEWLY_PICKED_DATE_ARGUMENT_KEY, selection);
|
||||||
|
getSupportFragmentManager().setFragmentResult(PICK_DATE_REQUEST_KEY, args);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private long getDefaultMinDateOfDatePicker() {
|
getSupportFragmentManager().setFragmentResultListener(
|
||||||
Calendar minDateCalendar = Calendar.getInstance();
|
PICK_DATE_REQUEST_KEY,
|
||||||
minDateCalendar.set(1970, 0, 1);
|
this,
|
||||||
return minDateCalendar.getTimeInMillis();
|
(requestKey, result) -> {
|
||||||
}
|
long selection = result.getLong(NEWLY_PICKED_DATE_ARGUMENT_KEY);
|
||||||
|
|
||||||
private long getDefaultMaxDateOfDatePicker() {
|
Date newDate = new Date(selection);
|
||||||
Calendar maxDateCalendar = Calendar.getInstance();
|
switch (tempLoyaltyCardField) {
|
||||||
maxDateCalendar.set(2100, 11, 31);
|
case validFrom:
|
||||||
return maxDateCalendar.getTimeInMillis();
|
formatDateField(LoyaltyCardEditActivity.this, validFromField, newDate);
|
||||||
}
|
updateTempState(LoyaltyCardField.validFrom, newDate);
|
||||||
|
break;
|
||||||
|
case expiry:
|
||||||
|
formatDateField(LoyaltyCardEditActivity.this, expiryField, newDate);
|
||||||
|
updateTempState(LoyaltyCardField.expiry, newDate);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new AssertionError("Unexpected field: " + tempLoyaltyCardField);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public void onDateSet(DatePicker view, int year, int month, int day) {
|
private long getDefaultMinDateOfDatePicker() {
|
||||||
Calendar c = new GregorianCalendar();
|
Calendar minDateCalendar = Calendar.getInstance();
|
||||||
c.set(Calendar.YEAR, year);
|
minDateCalendar.set(1970, 0, 1);
|
||||||
c.set(Calendar.MONTH, month);
|
return minDateCalendar.getTimeInMillis();
|
||||||
c.set(Calendar.DAY_OF_MONTH, day);
|
}
|
||||||
c.set(Calendar.HOUR_OF_DAY, 0);
|
|
||||||
c.set(Calendar.MINUTE, 0);
|
|
||||||
c.set(Calendar.SECOND, 0);
|
|
||||||
c.set(Calendar.MILLISECOND, 0);
|
|
||||||
|
|
||||||
long unixTime = c.getTimeInMillis();
|
private long getDefaultMaxDateOfDatePicker() {
|
||||||
|
Calendar maxDateCalendar = Calendar.getInstance();
|
||||||
Date date = new Date(unixTime);
|
maxDateCalendar.set(2100, 11, 31);
|
||||||
|
return maxDateCalendar.getTimeInMillis();
|
||||||
Bundle result = new Bundle();
|
|
||||||
result.putSerializable(TEXT_FIELD_TO_EDIT_ARGUMENT_KEY, textFieldEdit);
|
|
||||||
result.putSerializable(NEWLY_PICKED_DATE_ARGUMENT_KEY, date);
|
|
||||||
getParentFragmentManager().setFragmentResult(PICK_DATE_REQUEST_KEY, result);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doSave() {
|
private void doSave() {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package protect.card_locker;
|
|||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatDelegate;
|
import androidx.appcompat.app.AppCompatDelegate;
|
||||||
|
|
||||||
import protect.card_locker.preferences.Settings;
|
import protect.card_locker.preferences.Settings;
|
||||||
|
|
||||||
public class LoyaltyCardLockerApplication extends Application {
|
public class LoyaltyCardLockerApplication extends Application {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import android.text.method.DigitsKeyListener;
|
|||||||
import android.text.style.ForegroundColorSpan;
|
import android.text.style.ForegroundColorSpan;
|
||||||
import android.text.util.Linkify;
|
import android.text.util.Linkify;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.TypedValue;
|
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -30,6 +29,7 @@ import android.view.Window;
|
|||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
import android.view.WindowInsetsController;
|
import android.view.WindowInsetsController;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
import android.widget.EditText;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
@@ -38,19 +38,17 @@ import android.widget.SeekBar;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.activity.OnBackPressedCallback;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.content.res.AppCompatResources;
|
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import androidx.core.content.FileProvider;
|
import androidx.core.content.FileProvider;
|
||||||
import androidx.core.graphics.BlendModeColorFilterCompat;
|
import androidx.core.graphics.BlendModeColorFilterCompat;
|
||||||
import androidx.core.graphics.BlendModeCompat;
|
import androidx.core.graphics.BlendModeCompat;
|
||||||
import androidx.core.graphics.ColorUtils;
|
import androidx.core.graphics.ColorUtils;
|
||||||
import androidx.core.graphics.drawable.DrawableCompat;
|
|
||||||
import androidx.core.view.ViewCompat;
|
import androidx.core.view.ViewCompat;
|
||||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
|
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
|
||||||
import androidx.core.widget.TextViewCompat;
|
|
||||||
|
|
||||||
import com.google.android.material.color.MaterialColors;
|
import com.google.android.material.color.MaterialColors;
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
@@ -112,22 +110,25 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImageType imageType = imageTypes.get(mainImageIndex);
|
||||||
|
|
||||||
// If the barcode is shown, switch to fullscreen layout
|
// If the barcode is shown, switch to fullscreen layout
|
||||||
if (imageTypes.get(mainImageIndex) == ImageType.BARCODE) {
|
if (imageType == ImageType.BARCODE) {
|
||||||
setFullscreen(true);
|
setFullscreen(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is an image, open it in the gallery.
|
// If this is an image, open it in the gallery.
|
||||||
openCurrentMainImageInGallery();
|
openImageInGallery(imageType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openCurrentMainImageInGallery() {
|
private void openImageInGallery(ImageType imageType) {
|
||||||
ImageType wantedImageType = imageTypes.get(mainImageIndex);
|
|
||||||
|
|
||||||
File file = null;
|
File file = null;
|
||||||
|
|
||||||
switch (wantedImageType) {
|
switch (imageType) {
|
||||||
|
case ICON:
|
||||||
|
file = Utils.retrieveCardImageAsFile(this, loyaltyCardId, ImageLocationType.icon);
|
||||||
|
break;
|
||||||
case IMAGE_FRONT:
|
case IMAGE_FRONT:
|
||||||
file = Utils.retrieveCardImageAsFile(this, loyaltyCardId, ImageLocationType.front);
|
file = Utils.retrieveCardImageAsFile(this, loyaltyCardId, ImageLocationType.front);
|
||||||
break;
|
break;
|
||||||
@@ -175,6 +176,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
|||||||
|
|
||||||
enum ImageType {
|
enum ImageType {
|
||||||
NONE,
|
NONE,
|
||||||
|
ICON,
|
||||||
BARCODE,
|
BARCODE,
|
||||||
IMAGE_FRONT,
|
IMAGE_FRONT,
|
||||||
IMAGE_BACK
|
IMAGE_BACK
|
||||||
@@ -228,7 +230,9 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
|||||||
settings = new Settings(this);
|
settings = new Settings(this);
|
||||||
|
|
||||||
String cardOrientation = settings.getCardViewOrientation();
|
String cardOrientation = settings.getCardViewOrientation();
|
||||||
if (cardOrientation.equals(getString(R.string.settings_key_lock_on_opening_orientation))) {
|
if (cardOrientation.equals(getString(R.string.settings_key_follow_sensor_orientation))) {
|
||||||
|
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
|
||||||
|
} else if (cardOrientation.equals(getString(R.string.settings_key_lock_on_opening_orientation))) {
|
||||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
|
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
|
||||||
} else if (cardOrientation.equals(getString(R.string.settings_key_portrait_orientation))) {
|
} else if (cardOrientation.equals(getString(R.string.settings_key_portrait_orientation))) {
|
||||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||||
@@ -300,7 +304,13 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
|||||||
binding.bottomAppBarNextButton.setOnClickListener(view -> prevNextCard(true));
|
binding.bottomAppBarNextButton.setOnClickListener(view -> prevNextCard(true));
|
||||||
binding.bottomAppBarUpdateBalanceButton.setOnClickListener(view -> showBalanceUpdateDialog());
|
binding.bottomAppBarUpdateBalanceButton.setOnClickListener(view -> showBalanceUpdateDialog());
|
||||||
|
|
||||||
binding.iconContainer.setOnClickListener(view -> Toast.makeText(LoyaltyCardViewActivity.this, R.string.icon_header_click_text, Toast.LENGTH_LONG).show());
|
binding.iconContainer.setOnClickListener(view -> {
|
||||||
|
if (Utils.retrieveCardImage(this, loyaltyCard.id, ImageLocationType.icon) != null) {
|
||||||
|
openImageInGallery(ImageType.ICON);
|
||||||
|
} else {
|
||||||
|
Toast.makeText(LoyaltyCardViewActivity.this, R.string.icon_header_click_text, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
binding.iconContainer.setOnLongClickListener(view -> {
|
binding.iconContainer.setOnLongClickListener(view -> {
|
||||||
Intent intent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
|
Intent intent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
@@ -322,6 +332,17 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
binding.fullscreenImage.setOnClickListener(view -> onMainImageTap());
|
binding.fullscreenImage.setOnClickListener(view -> onMainImageTap());
|
||||||
|
|
||||||
|
getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
|
||||||
|
@Override
|
||||||
|
public void handleOnBackPressed() {
|
||||||
|
if (isFullscreen) {
|
||||||
|
setFullscreen(false);
|
||||||
|
} else {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private SpannableStringBuilder padSpannableString(SpannableStringBuilder spannableStringBuilder) {
|
private SpannableStringBuilder padSpannableString(SpannableStringBuilder spannableStringBuilder) {
|
||||||
@@ -400,7 +421,11 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
|||||||
|
|
||||||
private void showBalanceUpdateDialog() {
|
private void showBalanceUpdateDialog() {
|
||||||
AlertDialog.Builder builder = new MaterialAlertDialogBuilder(this);
|
AlertDialog.Builder builder = new MaterialAlertDialogBuilder(this);
|
||||||
|
|
||||||
|
// Header
|
||||||
builder.setTitle(R.string.updateBalanceTitle);
|
builder.setTitle(R.string.updateBalanceTitle);
|
||||||
|
|
||||||
|
// Layout
|
||||||
FrameLayout container = new FrameLayout(this);
|
FrameLayout container = new FrameLayout(this);
|
||||||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
|
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
@@ -418,61 +443,91 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
|||||||
currentTextview.setText(getString(R.string.currentBalanceSentence, Utils.formatBalance(this, loyaltyCard.balance, loyaltyCard.balanceType)));
|
currentTextview.setText(getString(R.string.currentBalanceSentence, Utils.formatBalance(this, loyaltyCard.balance, loyaltyCard.balanceType)));
|
||||||
layout.addView(currentTextview);
|
layout.addView(currentTextview);
|
||||||
|
|
||||||
TextView updateTextView = new TextView(this);
|
|
||||||
updateTextView.setText(getString(R.string.newBalanceSentence, Utils.formatBalance(this, loyaltyCard.balance, loyaltyCard.balanceType)));
|
|
||||||
layout.addView(updateTextView);
|
|
||||||
|
|
||||||
final TextInputEditText input = new TextInputEditText(this);
|
final TextInputEditText input = new TextInputEditText(this);
|
||||||
Context dialogContext = this;
|
|
||||||
input.setInputType(InputType.TYPE_CLASS_NUMBER);
|
input.setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||||
input.setKeyListener(DigitsKeyListener.getInstance("0123456789,."));
|
input.setKeyListener(DigitsKeyListener.getInstance("0123456789,."));
|
||||||
input.setHint(R.string.updateBalanceHint);
|
input.setHint(R.string.updateBalanceHint);
|
||||||
input.addTextChangedListener(new SimpleTextWatcher() {
|
|
||||||
@Override
|
|
||||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
|
||||||
BigDecimal newBalance;
|
|
||||||
try {
|
|
||||||
newBalance = calculateNewBalance(loyaltyCard.balance, loyaltyCard.balanceType, s.toString());
|
|
||||||
} catch (ParseException e) {
|
|
||||||
input.setTag(null);
|
|
||||||
updateTextView.setText(getString(R.string.newBalanceSentence, Utils.formatBalance(dialogContext, loyaltyCard.balance, loyaltyCard.balanceType)));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save new balance into this element
|
|
||||||
input.setTag(newBalance);
|
|
||||||
updateTextView.setText(getString(R.string.newBalanceSentence, Utils.formatBalance(dialogContext, newBalance, loyaltyCard.balanceType)));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
layout.addView(input);
|
layout.addView(input);
|
||||||
layout.setLayoutParams(params);
|
layout.setLayoutParams(params);
|
||||||
container.addView(layout);
|
container.addView(layout);
|
||||||
|
|
||||||
|
// Set layout
|
||||||
builder.setView(container);
|
builder.setView(container);
|
||||||
builder.setPositiveButton(R.string.ok, (dialogInterface, i) -> {
|
|
||||||
// Grab calculated balance from input field
|
// Buttons
|
||||||
BigDecimal newBalance = (BigDecimal) input.getTag();
|
builder.setPositiveButton(R.string.spend, (dialogInterface, i) -> {
|
||||||
if (newBalance == null) {
|
// Calculate and update balance
|
||||||
return;
|
try {
|
||||||
|
BigDecimal balanceChange = Utils.parseBalance(input.getText().toString(), loyaltyCard.balanceType);
|
||||||
|
BigDecimal newBalance = loyaltyCard.balance.subtract(balanceChange).max(new BigDecimal(0));
|
||||||
|
DBHelper.updateLoyaltyCardBalance(database, loyaltyCardId, newBalance);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
Toast.makeText(getApplicationContext(), R.string.amountParsingFailed, Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actually update balance
|
// Reload state
|
||||||
DBHelper.updateLoyaltyCardBalance(database, loyaltyCardId, newBalance);
|
|
||||||
// Reload UI
|
|
||||||
this.onResume();
|
this.onResume();
|
||||||
|
|
||||||
|
// Show new balance
|
||||||
|
Toast.makeText(getApplicationContext(), getString(R.string.newBalanceSentence, Utils.formatBalance(this, loyaltyCard.balance, loyaltyCard.balanceType)), Toast.LENGTH_LONG).show();
|
||||||
});
|
});
|
||||||
builder.setNegativeButton(getString(R.string.cancel), (dialog, which) -> dialog.cancel());
|
builder.setNegativeButton(R.string.receive, (dialogInterface, i) -> {
|
||||||
|
// Calculate and update balance
|
||||||
|
try {
|
||||||
|
BigDecimal balanceChange = Utils.parseBalance(input.getText().toString(), loyaltyCard.balanceType);
|
||||||
|
BigDecimal newBalance = loyaltyCard.balance.add(balanceChange);
|
||||||
|
DBHelper.updateLoyaltyCardBalance(database, loyaltyCardId, newBalance);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
Toast.makeText(getApplicationContext(), R.string.amountParsingFailed, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reload state
|
||||||
|
this.onResume();
|
||||||
|
|
||||||
|
// Show new balance
|
||||||
|
Toast.makeText(getApplicationContext(), getString(R.string.newBalanceSentence, Utils.formatBalance(this, loyaltyCard.balance, loyaltyCard.balanceType)), Toast.LENGTH_LONG).show();
|
||||||
|
});
|
||||||
|
builder.setNeutralButton(getString(R.string.cancel), (dialog, which) -> dialog.cancel());
|
||||||
AlertDialog dialog = builder.create();
|
AlertDialog dialog = builder.create();
|
||||||
|
|
||||||
|
// Now that the dialog exists, we can bind something that affects the buttons
|
||||||
|
input.addTextChangedListener(new SimpleTextWatcher() {
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
BigDecimal balanceChange;
|
||||||
|
|
||||||
|
try {
|
||||||
|
balanceChange = Utils.parseBalance(s.toString(), loyaltyCard.balanceType);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
input.setError(getString(R.string.amountParsingFailed));
|
||||||
|
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
|
||||||
|
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setEnabled(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.setError(null);
|
||||||
|
if (balanceChange.equals(new BigDecimal(0))) {
|
||||||
|
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
|
||||||
|
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setEnabled(false);
|
||||||
|
} else {
|
||||||
|
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true);
|
||||||
|
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
dialog.show();
|
dialog.show();
|
||||||
|
|
||||||
|
// Disable buttons (must be done **after** dialog is shown to prevent crash
|
||||||
|
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
|
||||||
|
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setEnabled(false);
|
||||||
|
|
||||||
|
// Set focus on input field
|
||||||
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
|
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
|
||||||
input.requestFocus();
|
input.requestFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private BigDecimal calculateNewBalance(BigDecimal currentBalance, Currency currency, String unparsedSubtraction) throws ParseException {
|
|
||||||
BigDecimal subtraction = Utils.parseBalance(unparsedSubtraction, currency);
|
|
||||||
return currentBalance.subtract(subtraction).max(new BigDecimal(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setBottomAppBarButtonState() {
|
private void setBottomAppBarButtonState() {
|
||||||
if (!loyaltyCard.note.isEmpty() || !loyaltyCardGroups.isEmpty() || hasBalance(loyaltyCard) || loyaltyCard.validFrom != null || loyaltyCard.expiry != null) {
|
if (!loyaltyCard.note.isEmpty() || !loyaltyCardGroups.isEmpty() || hasBalance(loyaltyCard) || loyaltyCard.validFrom != null || loyaltyCard.expiry != null) {
|
||||||
binding.bottomAppBarInfoButton.setVisibility(View.VISIBLE);
|
binding.bottomAppBarInfoButton.setVisibility(View.VISIBLE);
|
||||||
@@ -544,7 +599,8 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
protected void onResume() {
|
||||||
|
activityOverridesNavBarColor = true;
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
||||||
Log.i(TAG, "To view card: " + loyaltyCardId);
|
Log.i(TAG, "To view card: " + loyaltyCardId);
|
||||||
@@ -622,7 +678,11 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
|||||||
int darkenedColor = ColorUtils.blendARGB(backgroundHeaderColor, Color.BLACK, 0.1f);
|
int darkenedColor = ColorUtils.blendARGB(backgroundHeaderColor, Color.BLACK, 0.1f);
|
||||||
binding.barcodeScaler.setProgressTintList(ColorStateList.valueOf(darkenedColor));
|
binding.barcodeScaler.setProgressTintList(ColorStateList.valueOf(darkenedColor));
|
||||||
binding.barcodeScaler.setThumbTintList(ColorStateList.valueOf(darkenedColor));
|
binding.barcodeScaler.setThumbTintList(ColorStateList.valueOf(darkenedColor));
|
||||||
|
|
||||||
|
// Set bottomAppBar and system navigation bar color
|
||||||
binding.bottomAppBar.setBackgroundColor(darkenedColor);
|
binding.bottomAppBar.setBackgroundColor(darkenedColor);
|
||||||
|
Utils.setNavigationBarColor(null, window, darkenedColor, Utils.needsDarkForeground(darkenedColor));
|
||||||
|
|
||||||
int complementaryColor = Utils.getComplementaryColor(darkenedColor);
|
int complementaryColor = Utils.getComplementaryColor(darkenedColor);
|
||||||
binding.fabEdit.setBackgroundTintList(ColorStateList.valueOf(complementaryColor));
|
binding.fabEdit.setBackgroundTintList(ColorStateList.valueOf(complementaryColor));
|
||||||
Drawable editButtonIcon = binding.fabEdit.getDrawable();
|
Drawable editButtonIcon = binding.fabEdit.getDrawable();
|
||||||
@@ -676,6 +736,8 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
|||||||
DBHelper.updateLoyaltyCardLastUsed(database, loyaltyCard.id);
|
DBHelper.updateLoyaltyCardLastUsed(database, loyaltyCard.id);
|
||||||
|
|
||||||
invalidateOptionsMenu();
|
invalidateOptionsMenu();
|
||||||
|
|
||||||
|
ShortcutHelper.updateShortcuts(this, loyaltyCard);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setStateBasedOnImageTypes() {
|
private void setStateBasedOnImageTypes() {
|
||||||
@@ -704,16 +766,6 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
|||||||
imageButton.setColorFilter(BlendModeColorFilterCompat.createBlendModeColorFilterCompat(backgroundNeedsDarkIcons ? Color.BLACK : Color.WHITE, BlendModeCompat.SRC_ATOP));
|
imageButton.setColorFilter(BlendModeColorFilterCompat.createBlendModeColorFilterCompat(backgroundNeedsDarkIcons ? Color.BLACK : Color.WHITE, BlendModeCompat.SRC_ATOP));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBackPressed() {
|
|
||||||
if (isFullscreen) {
|
|
||||||
setFullscreen(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
super.onBackPressed();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
getMenuInflater().inflate(R.menu.card_view_menu, menu);
|
getMenuInflater().inflate(R.menu.card_view_menu, menu);
|
||||||
@@ -785,6 +837,8 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
|||||||
DBHelper.updateLoyaltyCardArchiveStatus(database, loyaltyCardId, 1);
|
DBHelper.updateLoyaltyCardArchiveStatus(database, loyaltyCardId, 1);
|
||||||
Toast.makeText(LoyaltyCardViewActivity.this, R.string.archived, Toast.LENGTH_LONG).show();
|
Toast.makeText(LoyaltyCardViewActivity.this, R.string.archived, Toast.LENGTH_LONG).show();
|
||||||
|
|
||||||
|
ShortcutHelper.removeShortcut(LoyaltyCardViewActivity.this, loyaltyCardId);
|
||||||
|
|
||||||
// Re-init loyaltyCard with new data from DB
|
// Re-init loyaltyCard with new data from DB
|
||||||
onResume();
|
onResume();
|
||||||
invalidateOptionsMenu();
|
invalidateOptionsMenu();
|
||||||
@@ -1055,10 +1109,14 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
|||||||
|
|
||||||
// Set Android to fullscreen mode
|
// Set Android to fullscreen mode
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
getWindow().setDecorFitsSystemWindows(false);
|
Window window = getWindow();
|
||||||
if (getWindow().getInsetsController() != null) {
|
if (window != null) {
|
||||||
getWindow().getInsetsController().hide(WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars());
|
window.setDecorFitsSystemWindows(false);
|
||||||
getWindow().getInsetsController().setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
|
WindowInsetsController wic = window.getInsetsController();
|
||||||
|
if (wic != null) {
|
||||||
|
wic.hide(WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars());
|
||||||
|
wic.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setFullscreenModeSdkLessThan30();
|
setFullscreenModeSdkLessThan30();
|
||||||
@@ -1085,10 +1143,14 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
|||||||
|
|
||||||
// Unset fullscreen mode
|
// Unset fullscreen mode
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
getWindow().setDecorFitsSystemWindows(true);
|
Window window = getWindow();
|
||||||
if (getWindow().getInsetsController() != null) {
|
if (window != null) {
|
||||||
getWindow().getInsetsController().show(WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars());
|
window.setDecorFitsSystemWindows(true);
|
||||||
getWindow().getInsetsController().setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_DEFAULT);
|
WindowInsetsController wic = window.getInsetsController();
|
||||||
|
if (wic != null) {
|
||||||
|
wic.show(WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars());
|
||||||
|
wic.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_DEFAULT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
unsetFullscreenModeSdkLessThan30();
|
unsetFullscreenModeSdkLessThan30();
|
||||||
@@ -1100,19 +1162,25 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
|||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
private void unsetFullscreenModeSdkLessThan30() {
|
private void unsetFullscreenModeSdkLessThan30() {
|
||||||
getWindow().getDecorView().setSystemUiVisibility(
|
Window window = getWindow();
|
||||||
getWindow().getDecorView().getSystemUiVisibility()
|
if (window != null) {
|
||||||
& ~View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
window.getDecorView().setSystemUiVisibility(
|
||||||
& ~View.SYSTEM_UI_FLAG_FULLSCREEN
|
window.getDecorView().getSystemUiVisibility()
|
||||||
);
|
& ~View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||||
|
& ~View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
private void setFullscreenModeSdkLessThan30() {
|
private void setFullscreenModeSdkLessThan30() {
|
||||||
getWindow().getDecorView().setSystemUiVisibility(
|
Window window = getWindow();
|
||||||
getWindow().getDecorView().getSystemUiVisibility()
|
if (window != null) {
|
||||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
window.getDecorView().setSystemUiVisibility(
|
||||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
window.getDecorView().getSystemUiVisibility()
|
||||||
);
|
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||||
|
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,15 +2,11 @@ package protect.card_locker;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.SearchManager;
|
import android.app.SearchManager;
|
||||||
import android.content.ClipData;
|
|
||||||
import android.content.ClipboardManager;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.database.CursorIndexOutOfBoundsException;
|
import android.database.CursorIndexOutOfBoundsException;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -22,6 +18,7 @@ import android.view.View;
|
|||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.activity.OnBackPressedCallback;
|
||||||
import androidx.activity.result.ActivityResultLauncher;
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
import androidx.activity.result.contract.ActivityResultContracts;
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
@@ -34,7 +31,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
|||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
import com.google.android.material.tabs.TabLayout;
|
import com.google.android.material.tabs.TabLayout;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -70,7 +66,7 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
|||||||
private View mNoGroupCardsText;
|
private View mNoGroupCardsText;
|
||||||
private TabLayout groupsTabLayout;
|
private TabLayout groupsTabLayout;
|
||||||
|
|
||||||
private Runnable mSwapLoyaltyCardListCursor;
|
private Runnable mUpdateLoyaltyCardListRunnable;
|
||||||
|
|
||||||
private ActivityResultLauncher<Intent> mBarcodeScannerLauncher;
|
private ActivityResultLauncher<Intent> mBarcodeScannerLauncher;
|
||||||
private ActivityResultLauncher<Intent> mSettingsLauncher;
|
private ActivityResultLauncher<Intent> mSettingsLauncher;
|
||||||
@@ -89,35 +85,7 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onActionItemClicked(ActionMode inputMode, MenuItem inputItem) {
|
public boolean onActionItemClicked(ActionMode inputMode, MenuItem inputItem) {
|
||||||
if (inputItem.getItemId() == R.id.action_copy_to_clipboard) {
|
if (inputItem.getItemId() == R.id.action_share) {
|
||||||
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
|
|
||||||
|
|
||||||
String clipboardData;
|
|
||||||
int cardCount = mAdapter.getSelectedItemCount();
|
|
||||||
|
|
||||||
if (cardCount == 1) {
|
|
||||||
clipboardData = mAdapter.getSelectedItems().get(0).cardId;
|
|
||||||
} else {
|
|
||||||
StringBuilder cardIds = new StringBuilder();
|
|
||||||
|
|
||||||
for (int i = 0; i < cardCount; i++) {
|
|
||||||
LoyaltyCard loyaltyCard = mAdapter.getSelectedItems().get(i);
|
|
||||||
|
|
||||||
cardIds.append(loyaltyCard.store + ": " + loyaltyCard.cardId);
|
|
||||||
if (i < (cardCount - 1)) {
|
|
||||||
cardIds.append("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clipboardData = cardIds.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
ClipData clip = ClipData.newPlainText(getString(R.string.card_ids_copied), clipboardData);
|
|
||||||
clipboard.setPrimaryClip(clip);
|
|
||||||
Toast.makeText(MainActivity.this, cardCount > 1 ? R.string.copy_to_clipboard_multiple_toast : R.string.copy_to_clipboard_toast, Toast.LENGTH_LONG).show();
|
|
||||||
inputMode.finish();
|
|
||||||
return true;
|
|
||||||
} else if (inputItem.getItemId() == R.id.action_share) {
|
|
||||||
final ImportURIHelper importURIHelper = new ImportURIHelper(MainActivity.this);
|
final ImportURIHelper importURIHelper = new ImportURIHelper(MainActivity.this);
|
||||||
try {
|
try {
|
||||||
importURIHelper.startShareIntent(mAdapter.getSelectedItems());
|
importURIHelper.startShareIntent(mAdapter.getSelectedItems());
|
||||||
@@ -179,6 +147,7 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
|||||||
for (LoyaltyCard loyaltyCard : mAdapter.getSelectedItems()) {
|
for (LoyaltyCard loyaltyCard : mAdapter.getSelectedItems()) {
|
||||||
Log.d(TAG, "Archiving card: " + loyaltyCard.id);
|
Log.d(TAG, "Archiving card: " + loyaltyCard.id);
|
||||||
DBHelper.updateLoyaltyCardArchiveStatus(mDatabase, loyaltyCard.id, 1);
|
DBHelper.updateLoyaltyCardArchiveStatus(mDatabase, loyaltyCard.id, 1);
|
||||||
|
ShortcutHelper.removeShortcut(MainActivity.this, loyaltyCard.id);
|
||||||
updateLoyaltyCardList(false);
|
updateLoyaltyCardList(false);
|
||||||
inputMode.finish();
|
inputMode.finish();
|
||||||
invalidateOptionsMenu();
|
invalidateOptionsMenu();
|
||||||
@@ -223,12 +192,13 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle inputSavedInstanceState) {
|
protected void onCreate(Bundle inputSavedInstanceState) {
|
||||||
extractIntentFields(getIntent());
|
|
||||||
SplashScreen.installSplashScreen(this);
|
SplashScreen.installSplashScreen(this);
|
||||||
super.onCreate(inputSavedInstanceState);
|
super.onCreate(inputSavedInstanceState);
|
||||||
|
|
||||||
|
// We should extract the share intent after we called the super.onCreate as it may need to spawn a dialog window and the app needs to be initialized to not crash
|
||||||
|
extractIntentFields(getIntent());
|
||||||
|
|
||||||
binding = MainActivityBinding.inflate(getLayoutInflater());
|
binding = MainActivityBinding.inflate(getLayoutInflater());
|
||||||
setTitle(R.string.app_name);
|
|
||||||
setContentView(binding.getRoot());
|
setContentView(binding.getRoot());
|
||||||
setSupportActionBar(binding.toolbar);
|
setSupportActionBar(binding.toolbar);
|
||||||
groupsTabLayout = binding.groups;
|
groupsTabLayout = binding.groups;
|
||||||
@@ -236,13 +206,8 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
|||||||
|
|
||||||
mDatabase = new DBHelper(this).getWritableDatabase();
|
mDatabase = new DBHelper(this).getWritableDatabase();
|
||||||
|
|
||||||
mSwapLoyaltyCardListCursor = () -> {
|
mUpdateLoyaltyCardListRunnable = () -> {
|
||||||
Group group = null;
|
updateLoyaltyCardList(false);
|
||||||
if (mGroup != null) {
|
|
||||||
group = (Group) mGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
mAdapter.swapCursor(DBHelper.getLoyaltyCardCursor(mDatabase, mFilter, group, mOrder, mOrderDirection, mAdapter.showingArchivedCards() ? DBHelper.LoyaltyCardArchiveFilter.All : DBHelper.LoyaltyCardArchiveFilter.Unarchived));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
groupsTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
|
groupsTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
|
||||||
@@ -277,7 +242,7 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
|||||||
mNoGroupCardsText = contentMainBinding.noGroupCardsText;
|
mNoGroupCardsText = contentMainBinding.noGroupCardsText;
|
||||||
mCardList = contentMainBinding.list;
|
mCardList = contentMainBinding.list;
|
||||||
|
|
||||||
mAdapter = new LoyaltyCardCursorAdapter(this, null, this, mSwapLoyaltyCardListCursor);
|
mAdapter = new LoyaltyCardCursorAdapter(this, null, this, mUpdateLoyaltyCardListRunnable);
|
||||||
mCardList.setAdapter(mAdapter);
|
mCardList.setAdapter(mAdapter);
|
||||||
registerForContextMenu(mCardList);
|
registerForContextMenu(mCardList);
|
||||||
|
|
||||||
@@ -322,11 +287,11 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
|||||||
}
|
}
|
||||||
|
|
||||||
Intent intent = result.getData();
|
Intent intent = result.getData();
|
||||||
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(Utils.BARCODE_SCAN, result.getResultCode(), intent, this);
|
List<BarcodeValues> barcodeValuesList = Utils.parseSetBarcodeActivityResult(Utils.BARCODE_SCAN, result.getResultCode(), intent, this);
|
||||||
|
|
||||||
Bundle inputBundle = intent.getExtras();
|
Bundle inputBundle = intent.getExtras();
|
||||||
String group = inputBundle != null ? inputBundle.getString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP) : null;
|
String group = inputBundle != null ? inputBundle.getString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP) : null;
|
||||||
processBarcodeValues(barcodeValues, group);
|
processBarcodeValuesList(barcodeValuesList, group, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
mSettingsLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
|
mSettingsLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
|
||||||
@@ -337,6 +302,17 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
|
||||||
|
@Override
|
||||||
|
public void handleOnBackPressed() {
|
||||||
|
if (mSearchView != null && !mSearchView.isIconified()) {
|
||||||
|
mSearchView.setIconified(true);
|
||||||
|
} else {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -411,16 +387,6 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
|||||||
addButton.bringToFront();
|
addButton.bringToFront();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBackPressed() {
|
|
||||||
if (mSearchView != null && !mSearchView.isIconified()) {
|
|
||||||
mSearchView.setIconified(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
super.onBackPressed();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void displayCardSetupOptions(Menu menu, boolean shouldShow) {
|
private void displayCardSetupOptions(Menu menu, boolean shouldShow) {
|
||||||
for (int id : new int[]{R.id.action_search, R.id.action_display_options, R.id.action_sort}) {
|
for (int id : new int[]{R.id.action_search, R.id.action_display_options, R.id.action_sort}) {
|
||||||
menu.findItem(id).setVisible(shouldShow);
|
menu.findItem(id).setVisible(shouldShow);
|
||||||
@@ -432,7 +398,12 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateLoyaltyCardList(boolean updateCount) {
|
private void updateLoyaltyCardList(boolean updateCount) {
|
||||||
mSwapLoyaltyCardListCursor.run();
|
Group group = null;
|
||||||
|
if (mGroup != null) {
|
||||||
|
group = (Group) mGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
mAdapter.swapCursor(DBHelper.getLoyaltyCardCursor(mDatabase, mFilter, group, mOrder, mOrderDirection, mAdapter.showingArchivedCards() ? DBHelper.LoyaltyCardArchiveFilter.All : DBHelper.LoyaltyCardArchiveFilter.Unarchived));
|
||||||
|
|
||||||
if (updateCount) {
|
if (updateCount) {
|
||||||
updateLoyaltyCardCount();
|
updateLoyaltyCardCount();
|
||||||
@@ -475,63 +446,57 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processBarcodeValues(BarcodeValues barcodeValues, String group) {
|
private void processBarcodeValuesList(List<BarcodeValues> barcodeValuesList, String group, boolean closeAppOnNoBarcode) {
|
||||||
if (barcodeValues.isEmpty()) {
|
if (barcodeValuesList.isEmpty()) {
|
||||||
throw new IllegalArgumentException("barcodesValues may not be empty");
|
throw new IllegalArgumentException("barcodesValues may not be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
Intent newIntent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
|
Utils.makeUserChooseBarcodeFromList(MainActivity.this, barcodeValuesList, new BarcodeValuesListDisambiguatorCallback() {
|
||||||
Bundle newBundle = new Bundle();
|
@Override
|
||||||
newBundle.putString(LoyaltyCardEditActivity.BUNDLE_BARCODETYPE, barcodeValues.format());
|
public void onUserChoseBarcode(BarcodeValues barcodeValues) {
|
||||||
newBundle.putString(LoyaltyCardEditActivity.BUNDLE_CARDID, barcodeValues.content());
|
Intent newIntent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
|
||||||
if (group != null) {
|
Bundle newBundle = new Bundle();
|
||||||
newBundle.putString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP, group);
|
newBundle.putString(LoyaltyCardEditActivity.BUNDLE_BARCODETYPE, barcodeValues.format());
|
||||||
}
|
newBundle.putString(LoyaltyCardEditActivity.BUNDLE_CARDID, barcodeValues.content());
|
||||||
newIntent.putExtras(newBundle);
|
if (group != null) {
|
||||||
startActivity(newIntent);
|
newBundle.putString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP, group);
|
||||||
|
}
|
||||||
|
newIntent.putExtras(newBundle);
|
||||||
|
startActivity(newIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUserDismissedSelector() {
|
||||||
|
if (closeAppOnNoBarcode) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onSharedIntent(Intent intent) {
|
private void onSharedIntent(Intent intent) {
|
||||||
String receivedAction = intent.getAction();
|
String receivedAction = intent.getAction();
|
||||||
String receivedType = intent.getType();
|
String receivedType = intent.getType();
|
||||||
|
|
||||||
// Check if an image was shared to us
|
// Check if an image or file was shared to us
|
||||||
if (Intent.ACTION_SEND.equals(receivedAction)) {
|
if (Intent.ACTION_SEND.equals(receivedAction)) {
|
||||||
if (!receivedType.startsWith("image/")) {
|
List<BarcodeValues> barcodeValuesList;
|
||||||
|
|
||||||
|
if (receivedType.startsWith("image/")) {
|
||||||
|
barcodeValuesList = Utils.retrieveBarcodesFromImage(this, intent.getParcelableExtra(Intent.EXTRA_STREAM));
|
||||||
|
} else if (receivedType.equals("application/pdf")) {
|
||||||
|
barcodeValuesList = Utils.retrieveBarcodesFromPdf(this, intent.getParcelableExtra(Intent.EXTRA_STREAM));
|
||||||
|
} else {
|
||||||
Log.e(TAG, "Wrong mime-type");
|
Log.e(TAG, "Wrong mime-type");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BarcodeValues barcodeValues;
|
if (barcodeValuesList.isEmpty()) {
|
||||||
Bitmap bitmap;
|
|
||||||
|
|
||||||
Uri data = intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
|
||||||
if (data == null) {
|
|
||||||
Toast.makeText(this, R.string.errorReadingImage, Toast.LENGTH_LONG).show();
|
|
||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
processBarcodeValuesList(barcodeValuesList, null, true);
|
||||||
bitmap = Utils.retrieveImageFromUri(this, data);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(TAG, "Error getting data from image file");
|
|
||||||
e.printStackTrace();
|
|
||||||
Toast.makeText(this, R.string.errorReadingImage, Toast.LENGTH_LONG).show();
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
barcodeValues = Utils.getBarcodeFromBitmap(bitmap);
|
|
||||||
|
|
||||||
if (barcodeValues.isEmpty()) {
|
|
||||||
Log.i(TAG, "No barcode found in image file");
|
|
||||||
Toast.makeText(this, R.string.noBarcodeFound, Toast.LENGTH_LONG).show();
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
processBarcodeValues(barcodeValues, null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -611,7 +576,7 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
|||||||
int id = inputItem.getItemId();
|
int id = inputItem.getItemId();
|
||||||
|
|
||||||
if (id == android.R.id.home) {
|
if (id == android.R.id.home) {
|
||||||
onBackPressed();
|
getOnBackPressedDispatcher().onBackPressed();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id == R.id.action_display_options) {
|
if (id == R.id.action_display_options) {
|
||||||
@@ -827,8 +792,6 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
|||||||
b.putIntegerArrayList("cardList", cardList);
|
b.putIntegerArrayList("cardList", cardList);
|
||||||
intent.putExtras(b);
|
intent.putExtras(b);
|
||||||
|
|
||||||
ShortcutHelper.updateShortcuts(MainActivity.this, loyaltyCard);
|
|
||||||
|
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ import android.widget.EditText;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.activity.OnBackPressedCallback;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
|
||||||
@@ -20,11 +26,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
|
||||||
import androidx.appcompat.widget.Toolbar;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
import protect.card_locker.databinding.ActivityManageGroupBinding;
|
import protect.card_locker.databinding.ActivityManageGroupBinding;
|
||||||
|
|
||||||
public class ManageGroupActivity extends CatimaAppCompatActivity implements ManageGroupCursorAdapter.CardAdapterListener {
|
public class ManageGroupActivity extends CatimaAppCompatActivity implements ManageGroupCursorAdapter.CardAdapterListener {
|
||||||
@@ -133,6 +134,13 @@ public class ManageGroupActivity extends CatimaAppCompatActivity implements Mana
|
|||||||
// this setText is here because content_main.xml is reused from main activity
|
// this setText is here because content_main.xml is reused from main activity
|
||||||
noGroupCardsText.setText(getResources().getText(R.string.noGiftCardsGroup));
|
noGroupCardsText.setText(getResources().getText(R.string.noGiftCardsGroup));
|
||||||
updateLoyaltyCardList();
|
updateLoyaltyCardList();
|
||||||
|
|
||||||
|
getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
|
||||||
|
@Override
|
||||||
|
public void handleOnBackPressed() {
|
||||||
|
leaveWithoutSaving();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArrayList<Integer> adapterStateToIntegerArray(HashMap<Integer, Boolean> adapterState) {
|
private ArrayList<Integer> adapterStateToIntegerArray(HashMap<Integer, Boolean> adapterState) {
|
||||||
@@ -210,14 +218,9 @@ public class ManageGroupActivity extends CatimaAppCompatActivity implements Mana
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBackPressed() {
|
|
||||||
leaveWithoutSaving();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onSupportNavigateUp() {
|
public boolean onSupportNavigateUp() {
|
||||||
onBackPressed();
|
getOnBackPressedDispatcher().onBackPressed();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,17 +14,17 @@ import android.widget.EditText;
|
|||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import androidx.recyclerview.widget.DefaultItemAnimator;
|
import androidx.recyclerview.widget.DefaultItemAnimator;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import protect.card_locker.databinding.ManageGroupsActivityBinding;
|
import protect.card_locker.databinding.ManageGroupsActivityBinding;
|
||||||
|
|
||||||
public class ManageGroupsActivity extends CatimaAppCompatActivity implements GroupCursorAdapter.GroupAdapterListener {
|
public class ManageGroupsActivity extends CatimaAppCompatActivity implements GroupCursorAdapter.GroupAdapterListener {
|
||||||
@@ -71,11 +71,6 @@ public class ManageGroupsActivity extends CatimaAppCompatActivity implements Gro
|
|||||||
updateGroupList();
|
updateGroupList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBackPressed() {
|
|
||||||
super.onBackPressed();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateGroupList() {
|
private void updateGroupList() {
|
||||||
mAdapter.swapCursor(DBHelper.getGroupCursor(mDatabase));
|
mAdapter.swapCursor(DBHelper.getGroupCursor(mDatabase));
|
||||||
|
|
||||||
@@ -248,4 +243,4 @@ public class ManageGroupsActivity extends CatimaAppCompatActivity implements Gro
|
|||||||
AlertDialog dialog = builder.create();
|
AlertDialog dialog = builder.create();
|
||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import androidx.activity.result.contract.ActivityResultContracts;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
@@ -63,6 +62,7 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
|||||||
private static final int COMPAT_SCALE_FACTOR_DIP = 320;
|
private static final int COMPAT_SCALE_FACTOR_DIP = 320;
|
||||||
|
|
||||||
private static final int PERMISSION_SCAN_ADD_FROM_IMAGE = 100;
|
private static final int PERMISSION_SCAN_ADD_FROM_IMAGE = 100;
|
||||||
|
private static final int PERMISSION_SCAN_ADD_FROM_PDF = 101;
|
||||||
|
|
||||||
private CaptureManager capture;
|
private CaptureManager capture;
|
||||||
private DecoratedBarcodeView barcodeScannerView;
|
private DecoratedBarcodeView barcodeScannerView;
|
||||||
@@ -74,6 +74,7 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
|||||||
private ActivityResultLauncher<Intent> manualAddLauncher;
|
private ActivityResultLauncher<Intent> manualAddLauncher;
|
||||||
// can't use the pre-made contract because that launches the file manager for image type instead of gallery
|
// can't use the pre-made contract because that launches the file manager for image type instead of gallery
|
||||||
private ActivityResultLauncher<Intent> photoPickerLauncher;
|
private ActivityResultLauncher<Intent> photoPickerLauncher;
|
||||||
|
private ActivityResultLauncher<Intent> pdfPickerLauncher;
|
||||||
|
|
||||||
static final String STATE_SCANNER_ACTIVE = "scannerActive";
|
static final String STATE_SCANNER_ACTIVE = "scannerActive";
|
||||||
private boolean mScannerActive = true;
|
private boolean mScannerActive = true;
|
||||||
@@ -100,6 +101,7 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
|||||||
|
|
||||||
manualAddLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> handleActivityResult(Utils.SELECT_BARCODE_REQUEST, result.getResultCode(), result.getData()));
|
manualAddLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> handleActivityResult(Utils.SELECT_BARCODE_REQUEST, result.getResultCode(), result.getData()));
|
||||||
photoPickerLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> handleActivityResult(Utils.BARCODE_IMPORT_FROM_IMAGE_FILE, result.getResultCode(), result.getData()));
|
photoPickerLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> handleActivityResult(Utils.BARCODE_IMPORT_FROM_IMAGE_FILE, result.getResultCode(), result.getData()));
|
||||||
|
pdfPickerLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> handleActivityResult(Utils.BARCODE_IMPORT_FROM_PDF_FILE, result.getResultCode(), result.getData()));
|
||||||
customBarcodeScannerBinding.fabOtherOptions.setOnClickListener(view -> {
|
customBarcodeScannerBinding.fabOtherOptions.setOnClickListener(view -> {
|
||||||
setScannerActive(false);
|
setScannerActive(false);
|
||||||
|
|
||||||
@@ -109,7 +111,8 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
|||||||
new CharSequence[]{
|
new CharSequence[]{
|
||||||
getString(R.string.addWithoutBarcode),
|
getString(R.string.addWithoutBarcode),
|
||||||
getString(R.string.addManually),
|
getString(R.string.addManually),
|
||||||
getString(R.string.addFromImage)
|
getString(R.string.addFromImage),
|
||||||
|
getString(R.string.addFromPdfFile)
|
||||||
},
|
},
|
||||||
(dialogInterface, i) -> {
|
(dialogInterface, i) -> {
|
||||||
switch (i) {
|
switch (i) {
|
||||||
@@ -122,6 +125,9 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
|||||||
case 2:
|
case 2:
|
||||||
addFromImage();
|
addFromImage();
|
||||||
break;
|
break;
|
||||||
|
case 3:
|
||||||
|
addFromPdfFile();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown 'Add a card in a different way' dialog option");
|
throw new IllegalArgumentException("Unknown 'Add a card in a different way' dialog option");
|
||||||
}
|
}
|
||||||
@@ -269,14 +275,24 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
|||||||
private void handleActivityResult(int requestCode, int resultCode, Intent intent) {
|
private void handleActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||||
super.onActivityResult(requestCode, resultCode, intent);
|
super.onActivityResult(requestCode, resultCode, intent);
|
||||||
|
|
||||||
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent, this);
|
List<BarcodeValues> barcodeValuesList = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent, this);
|
||||||
|
|
||||||
if (barcodeValues.isEmpty()) {
|
if (barcodeValuesList.isEmpty()) {
|
||||||
setScannerActive(true);
|
setScannerActive(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
returnResult(barcodeValues.content(), barcodeValues.format());
|
Utils.makeUserChooseBarcodeFromList(this, barcodeValuesList, new BarcodeValuesListDisambiguatorCallback() {
|
||||||
|
@Override
|
||||||
|
public void onUserChoseBarcode(BarcodeValues barcodeValues) {
|
||||||
|
returnResult(barcodeValues.content(), barcodeValues.format());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUserDismissedSelector() {
|
||||||
|
setScannerActive(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addWithoutBarcode() {
|
private void addWithoutBarcode() {
|
||||||
@@ -344,32 +360,44 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void addManually() {
|
public void addManually() {
|
||||||
Intent i = new Intent(getApplicationContext(), BarcodeSelectorActivity.class);
|
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(ScanActivity.this);
|
||||||
if (cardId != null) {
|
builder.setTitle(R.string.add_manually_warning_title);
|
||||||
final Bundle b = new Bundle();
|
builder.setMessage(R.string.add_manually_warning_message);
|
||||||
b.putString("initialCardId", cardId);
|
builder.setPositiveButton(R.string.continue_, (dialog, which) -> {
|
||||||
i.putExtras(b);
|
Intent i = new Intent(getApplicationContext(), BarcodeSelectorActivity.class);
|
||||||
}
|
if (cardId != null) {
|
||||||
manualAddLauncher.launch(i);
|
final Bundle b = new Bundle();
|
||||||
|
b.putString("initialCardId", cardId);
|
||||||
|
i.putExtras(b);
|
||||||
|
}
|
||||||
|
manualAddLauncher.launch(i);
|
||||||
|
});
|
||||||
|
builder.setNegativeButton(R.string.cancel, (dialog, which) -> setScannerActive(true));
|
||||||
|
builder.setOnCancelListener(dialog -> setScannerActive(true));
|
||||||
|
builder.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addFromImage() {
|
public void addFromImage() {
|
||||||
PermissionUtils.requestStorageReadPermission(this, PERMISSION_SCAN_ADD_FROM_IMAGE);
|
PermissionUtils.requestStorageReadPermission(this, PERMISSION_SCAN_ADD_FROM_IMAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addFromImageAfterPermission() {
|
public void addFromPdfFile() {
|
||||||
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
|
PermissionUtils.requestStorageReadPermission(this, PERMISSION_SCAN_ADD_FROM_PDF);
|
||||||
photoPickerIntent.setType("image/*");
|
}
|
||||||
Intent contentIntent = new Intent(Intent.ACTION_GET_CONTENT);
|
|
||||||
contentIntent.setType("image/*");
|
|
||||||
|
|
||||||
Intent chooserIntent = Intent.createChooser(photoPickerIntent, getString(R.string.addFromImage));
|
private void addFromImageOrFileAfterPermission(String mimeType, ActivityResultLauncher<Intent> launcher, int chooserText, int errorMessage) {
|
||||||
|
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
|
||||||
|
photoPickerIntent.setType(mimeType);
|
||||||
|
Intent contentIntent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||||
|
contentIntent.setType(mimeType);
|
||||||
|
|
||||||
|
Intent chooserIntent = Intent.createChooser(photoPickerIntent, getString(chooserText));
|
||||||
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { contentIntent });
|
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { contentIntent });
|
||||||
try {
|
try {
|
||||||
photoPickerLauncher.launch(chooserIntent);
|
launcher.launch(chooserIntent);
|
||||||
} catch (ActivityNotFoundException e) {
|
} catch (ActivityNotFoundException e) {
|
||||||
setScannerActive(true);
|
setScannerActive(true);
|
||||||
Toast.makeText(getApplicationContext(), R.string.failedLaunchingPhotoPicker, Toast.LENGTH_LONG).show();
|
Toast.makeText(getApplicationContext(), errorMessage, Toast.LENGTH_LONG).show();
|
||||||
Log.e(TAG, "No activity found to handle intent", e);
|
Log.e(TAG, "No activity found to handle intent", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -417,9 +445,13 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
|||||||
|
|
||||||
if (requestCode == CaptureManager.getCameraPermissionReqCode()) {
|
if (requestCode == CaptureManager.getCameraPermissionReqCode()) {
|
||||||
showCameraPermissionMissingText(!granted);
|
showCameraPermissionMissingText(!granted);
|
||||||
} else if (requestCode == PERMISSION_SCAN_ADD_FROM_IMAGE) {
|
} else if (requestCode == PERMISSION_SCAN_ADD_FROM_IMAGE || requestCode == PERMISSION_SCAN_ADD_FROM_PDF) {
|
||||||
if (granted) {
|
if (granted) {
|
||||||
addFromImageAfterPermission();
|
if (requestCode == PERMISSION_SCAN_ADD_FROM_IMAGE) {
|
||||||
|
addFromImageOrFileAfterPermission("image/*", photoPickerLauncher, R.string.addFromImage, R.string.failedLaunchingPhotoPicker);
|
||||||
|
} else {
|
||||||
|
addFromImageOrFileAfterPermission("application/pdf", pdfPickerLauncher, R.string.addFromPdfFile, R.string.failedLaunchingFileManager);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setScannerActive(true);
|
setScannerActive(true);
|
||||||
Toast.makeText(this, R.string.storageReadPermissionRequired, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, R.string.storageReadPermissionRequired, Toast.LENGTH_LONG).show();
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ import android.graphics.Canvas;
|
|||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.core.content.pm.ShortcutInfoCompat;
|
||||||
|
import androidx.core.content.pm.ShortcutManagerCompat;
|
||||||
|
import androidx.core.graphics.ColorUtils;
|
||||||
|
import androidx.core.graphics.drawable.IconCompat;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -15,11 +20,6 @@ import java.util.Comparator;
|
|||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import androidx.core.content.pm.ShortcutInfoCompat;
|
|
||||||
import androidx.core.content.pm.ShortcutManagerCompat;
|
|
||||||
import androidx.core.graphics.ColorUtils;
|
|
||||||
import androidx.core.graphics.drawable.IconCompat;
|
|
||||||
|
|
||||||
class ShortcutHelper {
|
class ShortcutHelper {
|
||||||
// Android documentation says that no more than 5 shortcuts
|
// Android documentation says that no more than 5 shortcuts
|
||||||
// are supported. However, that may be too many, as not all
|
// are supported. However, that may be too many, as not all
|
||||||
@@ -43,6 +43,11 @@ class ShortcutHelper {
|
|||||||
* used card shortcut is discarded.
|
* used card shortcut is discarded.
|
||||||
*/
|
*/
|
||||||
static void updateShortcuts(Context context, LoyaltyCard card) {
|
static void updateShortcuts(Context context, LoyaltyCard card) {
|
||||||
|
if (card.archiveStatus == 1) {
|
||||||
|
// Don't add archived card to menu
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
LinkedList<ShortcutInfoCompat> list = new LinkedList<>(ShortcutManagerCompat.getDynamicShortcuts(context));
|
LinkedList<ShortcutInfoCompat> list = new LinkedList<>(ShortcutManagerCompat.getDynamicShortcuts(context));
|
||||||
|
|
||||||
SQLiteDatabase database = new DBHelper(context).getReadableDatabase();
|
SQLiteDatabase database = new DBHelper(context).getReadableDatabase();
|
||||||
@@ -108,18 +113,7 @@ class ShortcutHelper {
|
|||||||
* shortcut exists.
|
* shortcut exists.
|
||||||
*/
|
*/
|
||||||
static void removeShortcut(Context context, int cardId) {
|
static void removeShortcut(Context context, int cardId) {
|
||||||
List<ShortcutInfoCompat> list = ShortcutManagerCompat.getDynamicShortcuts(context);
|
ShortcutManagerCompat.removeDynamicShortcuts(context, Collections.singletonList(Integer.toString(cardId)));
|
||||||
|
|
||||||
String shortcutId = Integer.toString(cardId);
|
|
||||||
|
|
||||||
for (int index = 0; index < list.size(); index++) {
|
|
||||||
if (list.get(index).getId().equals(shortcutId)) {
|
|
||||||
list.remove(index);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShortcutManagerCompat.setDynamicShortcuts(context, list);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static @NotNull
|
static @NotNull
|
||||||
|
|||||||
@@ -7,19 +7,20 @@ import android.graphics.drawable.Drawable;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.Window;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
import com.google.android.material.color.MaterialColors;
|
|
||||||
import com.google.android.material.textview.MaterialTextView;
|
|
||||||
import com.yalantis.ucrop.UCropActivity;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.widget.AppCompatImageView;
|
import androidx.appcompat.widget.AppCompatImageView;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.core.graphics.ColorUtils;
|
import androidx.core.graphics.ColorUtils;
|
||||||
import androidx.core.view.WindowInsetsControllerCompat;
|
import androidx.core.view.WindowInsetsControllerCompat;
|
||||||
|
|
||||||
|
import com.google.android.material.color.MaterialColors;
|
||||||
|
import com.google.android.material.textview.MaterialTextView;
|
||||||
|
import com.yalantis.ucrop.UCropActivity;
|
||||||
|
|
||||||
public class UCropWrapper extends UCropActivity {
|
public class UCropWrapper extends UCropActivity {
|
||||||
public static final String UCROP_TOOLBAR_TYPEFACE_STYLE = "ucop_toolbar_typeface_style";
|
public static final String UCROP_TOOLBAR_TYPEFACE_STYLE = "ucop_toolbar_typeface_style";
|
||||||
|
|
||||||
@@ -27,15 +28,18 @@ public class UCropWrapper extends UCropActivity {
|
|||||||
protected void onPostCreate(@Nullable Bundle savedInstanceState) {
|
protected void onPostCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onPostCreate(savedInstanceState);
|
super.onPostCreate(savedInstanceState);
|
||||||
boolean darkMode = Utils.isDarkModeEnabled(this);
|
boolean darkMode = Utils.isDarkModeEnabled(this);
|
||||||
|
Window window = getWindow();
|
||||||
// setup status bar to look like the rest of the app
|
// setup status bar to look like the rest of the app
|
||||||
if (Build.VERSION.SDK_INT >= 23) {
|
if (Build.VERSION.SDK_INT >= 23) {
|
||||||
View decorView = getWindow().getDecorView();
|
if (window != null) {
|
||||||
WindowInsetsControllerCompat wic = new WindowInsetsControllerCompat(getWindow(), decorView);
|
View decorView = window.getDecorView();
|
||||||
wic.setAppearanceLightStatusBars(!darkMode);
|
WindowInsetsControllerCompat wic = new WindowInsetsControllerCompat(window, decorView);
|
||||||
|
wic.setAppearanceLightStatusBars(!darkMode);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// icons are always white back then
|
// icons are always white back then
|
||||||
if (!darkMode) {
|
if (window != null && !darkMode) {
|
||||||
getWindow().setStatusBarColor(ColorUtils.compositeColors(Color.argb(127, 0, 0, 0), getWindow().getStatusBarColor()));
|
window.setStatusBarColor(ColorUtils.compositeColors(Color.argb(127, 0, 0, 0), window.getStatusBarColor()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,10 @@ import android.graphics.BitmapFactory;
|
|||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.ImageDecoder;
|
import android.graphics.ImageDecoder;
|
||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
|
import android.graphics.pdf.PdfRenderer;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.provider.MediaStore;
|
import android.provider.MediaStore;
|
||||||
import android.text.Layout;
|
import android.text.Layout;
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
@@ -22,20 +24,24 @@ import android.util.Log;
|
|||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.Window;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RawRes;
|
import androidx.annotation.RawRes;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.appcompat.app.AppCompatDelegate;
|
import androidx.appcompat.app.AppCompatDelegate;
|
||||||
import androidx.core.graphics.ColorUtils;
|
import androidx.core.graphics.ColorUtils;
|
||||||
import androidx.core.os.LocaleListCompat;
|
import androidx.core.os.LocaleListCompat;
|
||||||
|
import androidx.core.view.WindowInsetsControllerCompat;
|
||||||
import androidx.exifinterface.media.ExifInterface;
|
import androidx.exifinterface.media.ExifInterface;
|
||||||
import androidx.palette.graphics.Palette;
|
import androidx.palette.graphics.Palette;
|
||||||
|
|
||||||
import com.google.android.material.color.DynamicColors;
|
import com.google.android.material.color.DynamicColors;
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
import com.google.zxing.BinaryBitmap;
|
import com.google.zxing.BinaryBitmap;
|
||||||
import com.google.zxing.LuminanceSource;
|
import com.google.zxing.LuminanceSource;
|
||||||
import com.google.zxing.MultiFormatReader;
|
import com.google.zxing.MultiFormatReader;
|
||||||
@@ -43,6 +49,8 @@ import com.google.zxing.NotFoundException;
|
|||||||
import com.google.zxing.RGBLuminanceSource;
|
import com.google.zxing.RGBLuminanceSource;
|
||||||
import com.google.zxing.Result;
|
import com.google.zxing.Result;
|
||||||
import com.google.zxing.common.HybridBinarizer;
|
import com.google.zxing.common.HybridBinarizer;
|
||||||
|
import com.google.zxing.multi.GenericMultipleBarcodeReader;
|
||||||
|
import com.google.zxing.multi.MultipleBarcodeReader;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@@ -61,6 +69,7 @@ import java.text.NumberFormat;
|
|||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Currency;
|
import java.util.Currency;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
@@ -80,12 +89,13 @@ public class Utils {
|
|||||||
public static final int SELECT_BARCODE_REQUEST = 2;
|
public static final int SELECT_BARCODE_REQUEST = 2;
|
||||||
public static final int BARCODE_SCAN = 3;
|
public static final int BARCODE_SCAN = 3;
|
||||||
public static final int BARCODE_IMPORT_FROM_IMAGE_FILE = 4;
|
public static final int BARCODE_IMPORT_FROM_IMAGE_FILE = 4;
|
||||||
public static final int CARD_IMAGE_FROM_CAMERA_FRONT = 5;
|
public static final int BARCODE_IMPORT_FROM_PDF_FILE = 5;
|
||||||
public static final int CARD_IMAGE_FROM_CAMERA_BACK = 6;
|
public static final int CARD_IMAGE_FROM_CAMERA_FRONT = 6;
|
||||||
public static final int CARD_IMAGE_FROM_CAMERA_ICON = 7;
|
public static final int CARD_IMAGE_FROM_CAMERA_BACK = 7;
|
||||||
public static final int CARD_IMAGE_FROM_FILE_FRONT = 8;
|
public static final int CARD_IMAGE_FROM_CAMERA_ICON = 8;
|
||||||
public static final int CARD_IMAGE_FROM_FILE_BACK = 9;
|
public static final int CARD_IMAGE_FROM_FILE_FRONT = 9;
|
||||||
public static final int CARD_IMAGE_FROM_FILE_ICON = 10;
|
public static final int CARD_IMAGE_FROM_FILE_BACK = 10;
|
||||||
|
public static final int CARD_IMAGE_FROM_FILE_ICON = 11;
|
||||||
|
|
||||||
public static final String CARD_IMAGE_FILENAME_REGEX = "^(card_)(\\d+)(_(?:front|back|icon)\\.png)$";
|
public static final String CARD_IMAGE_FILENAME_REGEX = "^(card_)(\\d+)(_(?:front|back|icon)\\.png)$";
|
||||||
|
|
||||||
@@ -128,6 +138,80 @@ public class Utils {
|
|||||||
return ColorUtils.calculateLuminance(backgroundColor) > LUMINANCE_MIDPOINT;
|
return ColorUtils.calculateLuminance(backgroundColor) > LUMINANCE_MIDPOINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static public List<BarcodeValues> retrieveBarcodesFromImage(Context context, Uri uri) {
|
||||||
|
Log.i(TAG, "Received image file with possible barcode");
|
||||||
|
|
||||||
|
if (uri == null) {
|
||||||
|
Log.e(TAG, "Uri did not contain any data");
|
||||||
|
Toast.makeText(context, R.string.errorReadingImage, Toast.LENGTH_LONG).show();
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap bitmap;
|
||||||
|
try {
|
||||||
|
bitmap = retrieveImageFromUri(context, uri);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Error getting data from image file");
|
||||||
|
e.printStackTrace();
|
||||||
|
Toast.makeText(context, R.string.errorReadingImage, Toast.LENGTH_LONG).show();
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<BarcodeValues> barcodesFromBitmap = getBarcodesFromBitmap(bitmap);
|
||||||
|
|
||||||
|
if (barcodesFromBitmap.isEmpty()) {
|
||||||
|
Log.i(TAG, "No barcode found in image file");
|
||||||
|
Toast.makeText(context, R.string.noBarcodeFound, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
return barcodesFromBitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public List<BarcodeValues> retrieveBarcodesFromPdf(Context context, Uri uri) {
|
||||||
|
Log.i(TAG, "Received PDF file with possible barcode");
|
||||||
|
|
||||||
|
if (uri == null) {
|
||||||
|
Log.e(TAG, "Uri did not contain any data");
|
||||||
|
Toast.makeText(context, R.string.errorReadingFile, Toast.LENGTH_LONG).show();
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
ParcelFileDescriptor parcelFileDescriptor;
|
||||||
|
PdfRenderer renderer;
|
||||||
|
try {
|
||||||
|
parcelFileDescriptor = context.getContentResolver().openFileDescriptor(uri, "r");
|
||||||
|
renderer = new PdfRenderer(parcelFileDescriptor);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Could not read file in uri");
|
||||||
|
Toast.makeText(context, R.string.errorReadingFile, Toast.LENGTH_LONG).show();
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop over all pages to find barcodes
|
||||||
|
List<BarcodeValues> barcodesFromPdfPages = new ArrayList<>();
|
||||||
|
Bitmap renderedPage;
|
||||||
|
for (int i = 0; i < renderer.getPageCount(); i++) {
|
||||||
|
PdfRenderer.Page page = renderer.openPage(i);
|
||||||
|
renderedPage = Bitmap.createBitmap(page.getWidth(), page.getHeight(), Bitmap.Config.ARGB_8888);
|
||||||
|
page.render(renderedPage, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
|
||||||
|
page.close();
|
||||||
|
|
||||||
|
List<BarcodeValues> barcodesFromPage = getBarcodesFromBitmap(renderedPage);
|
||||||
|
for (BarcodeValues barcodeValues : barcodesFromPage) {
|
||||||
|
barcodeValues.setNote(String.format(context.getString(R.string.pageWithNumber), i+1));
|
||||||
|
barcodesFromPdfPages.add(barcodeValues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
renderer.close();
|
||||||
|
|
||||||
|
if (barcodesFromPdfPages.isEmpty()) {
|
||||||
|
Log.i(TAG, "No barcode found in pdf file");
|
||||||
|
Toast.makeText(context, R.string.noBarcodeFound, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
return barcodesFromPdfPages;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the Barcode format and content based on the result of an activity.
|
* Returns the Barcode format and content based on the result of an activity.
|
||||||
* It shows toasts to notify the end-user as needed itself and will return an empty
|
* It shows toasts to notify the end-user as needed itself and will return an empty
|
||||||
@@ -139,45 +223,20 @@ public class Utils {
|
|||||||
* @param context
|
* @param context
|
||||||
* @return BarcodeValues
|
* @return BarcodeValues
|
||||||
*/
|
*/
|
||||||
static public BarcodeValues parseSetBarcodeActivityResult(int requestCode, int resultCode, Intent intent, Context context) {
|
static public List<BarcodeValues> parseSetBarcodeActivityResult(int requestCode, int resultCode, Intent intent, Context context) {
|
||||||
String contents;
|
String contents;
|
||||||
String format;
|
String format;
|
||||||
|
|
||||||
if (resultCode != Activity.RESULT_OK) {
|
if (resultCode != Activity.RESULT_OK) {
|
||||||
return new BarcodeValues(null, null);
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (requestCode == Utils.BARCODE_IMPORT_FROM_IMAGE_FILE) {
|
if (requestCode == Utils.BARCODE_IMPORT_FROM_IMAGE_FILE) {
|
||||||
Log.i(TAG, "Received image file with possible barcode");
|
return retrieveBarcodesFromImage(context, intent.getData());
|
||||||
|
}
|
||||||
|
|
||||||
Uri data = intent.getData();
|
if (requestCode == Utils.BARCODE_IMPORT_FROM_PDF_FILE) {
|
||||||
if (data == null) {
|
return retrieveBarcodesFromPdf(context, intent.getData());
|
||||||
Log.e(TAG, "Intent did not contain any data");
|
|
||||||
Toast.makeText(context, R.string.errorReadingImage, Toast.LENGTH_LONG).show();
|
|
||||||
return new BarcodeValues(null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
Bitmap bitmap;
|
|
||||||
try {
|
|
||||||
bitmap = retrieveImageFromUri(context, data);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(TAG, "Error getting data from image file");
|
|
||||||
e.printStackTrace();
|
|
||||||
Toast.makeText(context, R.string.errorReadingImage, Toast.LENGTH_LONG).show();
|
|
||||||
return new BarcodeValues(null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
BarcodeValues barcodeFromBitmap = getBarcodeFromBitmap(bitmap);
|
|
||||||
|
|
||||||
if (barcodeFromBitmap.isEmpty()) {
|
|
||||||
Log.i(TAG, "No barcode found in image file");
|
|
||||||
Toast.makeText(context, R.string.noBarcodeFound, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(TAG, "Read barcode id: " + barcodeFromBitmap.content());
|
|
||||||
Log.i(TAG, "Read format: " + barcodeFromBitmap.format());
|
|
||||||
|
|
||||||
return barcodeFromBitmap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (requestCode == Utils.BARCODE_SCAN || requestCode == Utils.SELECT_BARCODE_REQUEST) {
|
if (requestCode == Utils.BARCODE_SCAN || requestCode == Utils.SELECT_BARCODE_REQUEST) {
|
||||||
@@ -193,7 +252,7 @@ public class Utils {
|
|||||||
Log.i(TAG, "Read barcode id: " + contents);
|
Log.i(TAG, "Read barcode id: " + contents);
|
||||||
Log.i(TAG, "Read format: " + format);
|
Log.i(TAG, "Read format: " + format);
|
||||||
|
|
||||||
return new BarcodeValues(format, contents);
|
return Collections.singletonList(new BarcodeValues(format, contents));
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new UnsupportedOperationException("Unknown request code for parseSetBarcodeActivityResult");
|
throw new UnsupportedOperationException("Unknown request code for parseSetBarcodeActivityResult");
|
||||||
@@ -213,22 +272,22 @@ public class Utils {
|
|||||||
return MediaStore.Images.Media.getBitmap(context.getContentResolver(), data);
|
return MediaStore.Images.Media.getBitmap(context.getContentResolver(), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static public BarcodeValues getBarcodeFromBitmap(Bitmap bitmap) {
|
static public List<BarcodeValues> getBarcodesFromBitmap(Bitmap bitmap) {
|
||||||
// This function is vulnerable to OOM, so we try again with a smaller bitmap is we get OOM
|
// This function is vulnerable to OOM, so we try again with a smaller bitmap is we get OOM
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
try {
|
try {
|
||||||
return Utils.getBarcodeFromBitmapReal(bitmap);
|
return Utils.getBarcodesFromBitmapReal(bitmap);
|
||||||
} catch (OutOfMemoryError e) {
|
} catch (OutOfMemoryError e) {
|
||||||
Log.w(TAG, "Ran OOM in getBarcodeFromBitmap! Trying again with smaller picture! Retry " + i + " of 10.");
|
Log.w(TAG, "Ran OOM in getBarcodesFromBitmap! Trying again with smaller picture! Retry " + i + " of 10.");
|
||||||
bitmap = Bitmap.createScaledBitmap(bitmap, (int) Math.round(0.75 * bitmap.getWidth()), (int) Math.round(0.75 * bitmap.getHeight()), false);
|
bitmap = Bitmap.createScaledBitmap(bitmap, (int) Math.round(0.75 * bitmap.getWidth()), (int) Math.round(0.75 * bitmap.getHeight()), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Give up
|
// Give up
|
||||||
return new BarcodeValues(null, null);
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
static private BarcodeValues getBarcodeFromBitmapReal(Bitmap bitmap) {
|
static private List<BarcodeValues> getBarcodesFromBitmapReal(Bitmap bitmap) {
|
||||||
// In order to decode it, the Bitmap must first be converted into a pixel array...
|
// In order to decode it, the Bitmap must first be converted into a pixel array...
|
||||||
int[] intArray = new int[bitmap.getWidth() * bitmap.getHeight()];
|
int[] intArray = new int[bitmap.getWidth() * bitmap.getHeight()];
|
||||||
bitmap.getPixels(intArray, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
|
bitmap.getPixels(intArray, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
|
||||||
@@ -237,15 +296,63 @@ public class Utils {
|
|||||||
LuminanceSource source = new RGBLuminanceSource(bitmap.getWidth(), bitmap.getHeight(), intArray);
|
LuminanceSource source = new RGBLuminanceSource(bitmap.getWidth(), bitmap.getHeight(), intArray);
|
||||||
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
|
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
|
||||||
|
|
||||||
|
List<BarcodeValues> barcodeValuesList = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
Result barcodeResult = new MultiFormatReader().decode(binaryBitmap);
|
MultiFormatReader multiFormatReader = new MultiFormatReader();
|
||||||
|
MultipleBarcodeReader multipleBarcodeReader = new GenericMultipleBarcodeReader(multiFormatReader);
|
||||||
|
|
||||||
return new BarcodeValues(barcodeResult.getBarcodeFormat().name(), barcodeResult.getText());
|
Result[] barcodeResults = multipleBarcodeReader.decodeMultiple(binaryBitmap);
|
||||||
|
|
||||||
|
for (Result barcodeResult : barcodeResults) {
|
||||||
|
Log.i(TAG, "Read barcode id: " + barcodeResult.getText());
|
||||||
|
Log.i(TAG, "Read format: " + barcodeResult.getBarcodeFormat().name());
|
||||||
|
|
||||||
|
barcodeValuesList.add(new BarcodeValues(barcodeResult.getBarcodeFormat().name(), barcodeResult.getText()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return barcodeValuesList;
|
||||||
} catch (NotFoundException e) {
|
} catch (NotFoundException e) {
|
||||||
return new BarcodeValues(null, null);
|
return barcodeValuesList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static public void makeUserChooseBarcodeFromList(Context context, List<BarcodeValues> barcodeValuesList, BarcodeValuesListDisambiguatorCallback callback) {
|
||||||
|
// If there is only one choice, consider it chosen
|
||||||
|
if (barcodeValuesList.size() == 1) {
|
||||||
|
callback.onUserChoseBarcode(barcodeValuesList.get(0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ask user to choose a barcode
|
||||||
|
// TODO: This should contain an image of the barcode in question to help users understand the choice they're making
|
||||||
|
CharSequence[] barcodeDescriptions = new CharSequence[barcodeValuesList.size()];
|
||||||
|
for (int i = 0; i < barcodeValuesList.size(); i++) {
|
||||||
|
BarcodeValues barcodeValues = barcodeValuesList.get(i);
|
||||||
|
CatimaBarcode catimaBarcode = CatimaBarcode.fromName(barcodeValues.format());
|
||||||
|
|
||||||
|
String barcodeContent = barcodeValues.content();
|
||||||
|
// Shorten overly long barcodes
|
||||||
|
if (barcodeContent.length() > 22) {
|
||||||
|
barcodeContent = barcodeContent.substring(0, 20) + "…";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (barcodeValues.note() != null) {
|
||||||
|
barcodeDescriptions[i] = String.format("%s: %s (%s)", barcodeValues.note(), catimaBarcode.prettyName(), barcodeContent);
|
||||||
|
} else {
|
||||||
|
barcodeDescriptions[i] = String.format("%s (%s)", catimaBarcode.prettyName(), barcodeContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context);
|
||||||
|
builder.setTitle(context.getString(R.string.multipleBarcodesFoundPleaseChooseOne));
|
||||||
|
builder.setItems(
|
||||||
|
barcodeDescriptions,
|
||||||
|
(dialogInterface, i) -> callback.onUserChoseBarcode(barcodeValuesList.get(i))
|
||||||
|
);
|
||||||
|
builder.setOnCancelListener(dialogInterface -> callback.onUserDismissedSelector());
|
||||||
|
builder.show();
|
||||||
|
}
|
||||||
|
|
||||||
static public Boolean isNotYetValid(Date validFromDate) {
|
static public Boolean isNotYetValid(Date validFromDate) {
|
||||||
// The note in `hasExpired` does not apply here, since the bug was fixed before this feature was added.
|
// The note in `hasExpired` does not apply here, since the bug was fixed before this feature was added.
|
||||||
return validFromDate.after(getStartOfToday().getTime());
|
return validFromDate.after(getStartOfToday().getTime());
|
||||||
@@ -663,13 +770,31 @@ public class Utils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX android 9 and below has issues with patched theme where the background becomes a
|
// Force correct color
|
||||||
// rendering mess
|
// Fixes OLED dark mode in MainActivity
|
||||||
// use after views are inflated
|
|
||||||
public static void postPatchColors(AppCompatActivity activity) {
|
public static void postPatchColors(AppCompatActivity activity) {
|
||||||
|
activity.findViewById(android.R.id.content).setBackgroundColor(resolveBackgroundColor(activity));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Either pass an Activity on which to call getWindow() or an existing Window (may be null) returned by that function.
|
||||||
|
public static void setNavigationBarColor(@Nullable AppCompatActivity activity, @Nullable Window window, int color, boolean useLightBars) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||||
|
if (window == null && activity != null) {
|
||||||
|
window = activity.getWindow();
|
||||||
|
}
|
||||||
|
if (window != null) {
|
||||||
|
View decorView = window.getDecorView();
|
||||||
|
WindowInsetsControllerCompat wic = new WindowInsetsControllerCompat(window, decorView);
|
||||||
|
wic.setAppearanceLightNavigationBars(useLightBars);
|
||||||
|
window.setNavigationBarColor(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int resolveBackgroundColor(AppCompatActivity activity) {
|
||||||
TypedValue typedValue = new TypedValue();
|
TypedValue typedValue = new TypedValue();
|
||||||
activity.getTheme().resolveAttribute(android.R.attr.colorBackground, typedValue, true);
|
activity.getTheme().resolveAttribute(android.R.attr.colorBackground, typedValue, true);
|
||||||
activity.findViewById(android.R.id.content).setBackgroundColor(typedValue.data);
|
return typedValue.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getHeaderColorFromImage(Bitmap image, int fallback) {
|
public static int getHeaderColorFromImage(Bitmap image, int fallback) {
|
||||||
@@ -727,23 +852,30 @@ public class Utils {
|
|||||||
.replaceAll("(?<!href=\")\\b(https?://[\\w@#%&+=:?/.-]*[\\w@#%&+=:?/-])", "<a href=\"$1\">$1</a>");
|
.replaceAll("(?<!href=\")\\b(https?://[\\w@#%&+=:?/.-]*[\\w@#%&+=:?/-])", "<a href=\"$1\">$1</a>");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setIconOrTextWithBackground(Context context, LoyaltyCard loyaltyCard, Bitmap icon, ImageView backgroundOrIcon, TextView textWhenNoImage) {
|
/**
|
||||||
if (icon != null) {
|
* Sets an icon or text with background on the given ImageView and/or TextView, including background colour.
|
||||||
Log.d("onResume", "setting icon image");
|
*
|
||||||
textWhenNoImage.setVisibility(View.GONE);
|
* @param context Android context
|
||||||
|
* @param loyaltyCard Loyalty Card
|
||||||
|
* @param icon Bitmap of the icon to set, or null
|
||||||
|
* @param backgroundOrIcon ImageView to draw the icon and background on to
|
||||||
|
* @param textWhenNoImage TextView to write the loyalty card name into if icon is null
|
||||||
|
* @return background colour
|
||||||
|
*/
|
||||||
|
public static int setIconOrTextWithBackground(Context context, LoyaltyCard loyaltyCard, Bitmap icon, ImageView backgroundOrIcon, TextView textWhenNoImage) {
|
||||||
|
int headerColor = getHeaderColor(context, loyaltyCard);
|
||||||
|
backgroundOrIcon.setImageBitmap(icon);
|
||||||
|
backgroundOrIcon.setBackgroundColor(headerColor);
|
||||||
|
|
||||||
backgroundOrIcon.setImageBitmap(icon);
|
if (icon != null) {
|
||||||
backgroundOrIcon.setBackgroundColor(Color.TRANSPARENT);
|
textWhenNoImage.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
textWhenNoImage.setVisibility(View.VISIBLE);
|
textWhenNoImage.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
int headerColor = getHeaderColor(context, loyaltyCard);
|
|
||||||
|
|
||||||
backgroundOrIcon.setImageBitmap(null);
|
|
||||||
backgroundOrIcon.setBackgroundColor(headerColor);
|
|
||||||
textWhenNoImage.setText(loyaltyCard.store);
|
textWhenNoImage.setText(loyaltyCard.store);
|
||||||
textWhenNoImage.setTextColor(Utils.needsDarkForeground(headerColor) ? Color.BLACK : Color.WHITE);
|
textWhenNoImage.setTextColor(Utils.needsDarkForeground(headerColor) ? Color.BLACK : Color.WHITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return headerColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean installedFromGooglePlay(Context context) {
|
public static boolean installedFromGooglePlay(Context context) {
|
||||||
|
|||||||
@@ -3,12 +3,13 @@ package protect.card_locker.preferences;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import androidx.annotation.IntegerRes;
|
import androidx.annotation.IntegerRes;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.appcompat.app.AppCompatDelegate;
|
import androidx.appcompat.app.AppCompatDelegate;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import protect.card_locker.R;
|
import protect.card_locker.R;
|
||||||
import protect.card_locker.Utils;
|
import protect.card_locker.Utils;
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package protect.card_locker.preferences;
|
package protect.card_locker.preferences;
|
||||||
|
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
|
||||||
|
import androidx.activity.OnBackPressedCallback;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AppCompatDelegate;
|
import androidx.appcompat.app.AppCompatDelegate;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
@@ -56,6 +56,13 @@ public class SettingsActivity extends CatimaAppCompatActivity {
|
|||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
fragment.mReloadMain = savedInstanceState.getBoolean(RELOAD_MAIN_STATE);
|
fragment.mReloadMain = savedInstanceState.getBoolean(RELOAD_MAIN_STATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
|
||||||
|
@Override
|
||||||
|
public void handleOnBackPressed() {
|
||||||
|
finishSettingsActivity();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -76,11 +83,6 @@ public class SettingsActivity extends CatimaAppCompatActivity {
|
|||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBackPressed() {
|
|
||||||
finishSettingsActivity();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void finishSettingsActivity() {
|
private void finishSettingsActivity() {
|
||||||
if (fragment.mReloadMain) {
|
if (fragment.mReloadMain) {
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:tint="?attr/colorControlNormal">
|
|
||||||
<path
|
|
||||||
android:fillColor="#FFFFFFFF"
|
|
||||||
android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -369,75 +369,106 @@
|
|||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible">
|
tools:visibility="visible">
|
||||||
|
|
||||||
<!-- Front image -->
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/frontImageHolder"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:paddingHorizontal="@dimen/inputPadding"
|
android:baselineAligned="false">
|
||||||
android:paddingTop="@dimen/inputPadding">
|
|
||||||
|
|
||||||
<!-- Front image -->
|
<!-- Front image -->
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:layout_width="match_parent"
|
android:id="@+id/frontImageHolder"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical"
|
android:orientation="horizontal"
|
||||||
android:layout_marginStart="@dimen/activity_margin"
|
android:layout_margin="5dp"
|
||||||
android:layout_marginTop="@dimen/activity_margin"
|
style="?attr/materialCardViewElevatedStyle">
|
||||||
android:layout_marginEnd="@dimen/activity_margin"
|
|
||||||
android:layout_marginBottom="@dimen/activity_margin"
|
|
||||||
android:paddingHorizontal="@dimen/inputPadding"
|
|
||||||
app:cardCornerRadius="4dp"
|
|
||||||
app:cardElevation="0dp">
|
|
||||||
|
|
||||||
<ImageView
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/frontImage"
|
android:id="@+id/frontImageConstraint"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:adjustViewBounds="true"
|
|
||||||
android:minHeight="50dp"
|
|
||||||
android:contentDescription="@string/frontImageDescription"
|
|
||||||
android:scaleType="fitCenter"
|
|
||||||
app:srcCompat="@drawable/ic_camera_white"
|
|
||||||
android:background="?attr/colorPrimary" />
|
|
||||||
|
|
||||||
|
<!-- Back image -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/frontImage"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:minHeight="50dp"
|
||||||
|
android:contentDescription="@string/backImageDescription"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
app:srcCompat="@drawable/ic_camera_white"
|
||||||
|
android:background="?attr/colorPrimary"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/frontImageDescription"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/frontImageDescription"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
android:text="@string/frontImageDescription"
|
||||||
|
android:textAppearance="?attr/textAppearanceHeadlineSmall"
|
||||||
|
android:gravity="center"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/frontImage"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"/>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<!-- Back image -->
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/backImageHolder"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:paddingHorizontal="@dimen/inputPadding"
|
|
||||||
android:paddingTop="@dimen/inputPadding">
|
|
||||||
|
|
||||||
<!-- Back image -->
|
<!-- Back image -->
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:layout_width="match_parent"
|
android:layout_weight="1"
|
||||||
|
android:id="@+id/backImageHolder"
|
||||||
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical"
|
android:orientation="horizontal"
|
||||||
android:layout_marginStart="@dimen/activity_margin"
|
android:layout_margin="5dp"
|
||||||
android:layout_marginTop="@dimen/activity_margin"
|
style="?attr/materialCardViewElevatedStyle">
|
||||||
android:layout_marginEnd="@dimen/activity_margin"
|
|
||||||
android:layout_marginBottom="@dimen/activity_margin"
|
|
||||||
android:paddingHorizontal="@dimen/inputPadding"
|
|
||||||
app:cardCornerRadius="4dp"
|
|
||||||
app:cardElevation="0dp">
|
|
||||||
|
|
||||||
<ImageView
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/backImage"
|
android:id="@+id/backImageConstraint"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:adjustViewBounds="true"
|
|
||||||
android:minHeight="50dp"
|
|
||||||
android:contentDescription="@string/backImageDescription"
|
|
||||||
android:scaleType="fitCenter"
|
|
||||||
app:srcCompat="@drawable/ic_camera_white"
|
|
||||||
android:background="?attr/colorPrimary" />
|
|
||||||
|
|
||||||
|
<!-- Back image -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/backImage"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:minHeight="50dp"
|
||||||
|
android:contentDescription="@string/backImageDescription"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
app:srcCompat="@drawable/ic_camera_white"
|
||||||
|
android:background="?attr/colorPrimary"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/backImageDescription"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/backImageDescription"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
android:text="@string/backImageDescription"
|
||||||
|
android:textAppearance="?attr/textAppearanceHeadlineSmall"
|
||||||
|
android:gravity="center"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/backImage"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"/>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</TableLayout>
|
</TableLayout>
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:id="@+id/coordinator_layout"
|
android:id="@+id/coordinator_layout"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="fill_parent"
|
android:layout_height="fill_parent"
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
|||||||
@@ -9,13 +9,6 @@
|
|||||||
android:titleCondensed="@string/editCardTitle"
|
android:titleCondensed="@string/editCardTitle"
|
||||||
app:showAsAction="ifRoom"/>
|
app:showAsAction="ifRoom"/>
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_copy_to_clipboard"
|
|
||||||
android:icon="@drawable/ic_copy"
|
|
||||||
android:title="@string/copy_to_clipboard"
|
|
||||||
android:titleCondensed="@string/copy_to_clipboard"
|
|
||||||
app:showAsAction="ifRoom"/>
|
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_share"
|
android:id="@+id/action_share"
|
||||||
android:icon="@drawable/ic_share"
|
android:icon="@drawable/ic_share"
|
||||||
|
|||||||
@@ -1,283 +1,58 @@
|
|||||||
Sylvia van Os
|
Sylvia van Os
|
||||||
Branden Archer
|
Branden Archer
|
||||||
J. Lavoie
|
J. Lavoie
|
||||||
Allan Nordhøy
|
|
||||||
solokot
|
solokot
|
||||||
|
Allan Nordhøy
|
||||||
Heimen Stoffels
|
Heimen Stoffels
|
||||||
Oğuz Ersen
|
Oğuz Ersen
|
||||||
FC (Fay) Stegerman
|
FC (Fay) Stegerman
|
||||||
Katharine Chui
|
Katharine Chui
|
||||||
SlavekB
|
SlavekB
|
||||||
mondstern
|
|
||||||
StoyanDimitrov
|
StoyanDimitrov
|
||||||
|
mondstern
|
||||||
IllusiveMan196
|
IllusiveMan196
|
||||||
Altonss
|
Altonss
|
||||||
Michael Moroni
|
Michael Moroni
|
||||||
Gediminas Murauskas
|
GM
|
||||||
|
Eric
|
||||||
Petr Novák
|
Petr Novák
|
||||||
Joel A
|
Joel A
|
||||||
laralem
|
laralem
|
||||||
Taco
|
Taco
|
||||||
pfaffenrodt
|
pfaffenrodt
|
||||||
Eric
|
|
||||||
Nyatsuki
|
|
||||||
HudobniVolk
|
|
||||||
Samantaz Fox
|
|
||||||
arno-github
|
|
||||||
Ankit Tiwari
|
|
||||||
Sergio Paredes
|
|
||||||
Clxff H3r4ld0
|
|
||||||
Aayush Gupta
|
Aayush Gupta
|
||||||
|
HudobniVolk
|
||||||
|
Nyatsuki
|
||||||
|
Samantaz Fox
|
||||||
|
Giovanni Donisi
|
||||||
|
Jiri Grönroos
|
||||||
|
arno-github
|
||||||
|
Cliff Heraldo
|
||||||
|
Sergio Paredes
|
||||||
|
Ankit Tiwari
|
||||||
|
Milan Šalka
|
||||||
|
mdvhimself
|
||||||
Balázs Meskó
|
Balázs Meskó
|
||||||
|
Milo Ivir
|
||||||
|
Skrripy
|
||||||
huuhaa
|
huuhaa
|
||||||
|
大王叫我来巡山
|
||||||
Projjal Moitra
|
Projjal Moitra
|
||||||
Quentin PAGÈS
|
Quentin PAGÈS
|
||||||
Giovanni Donisi
|
ikanakova
|
||||||
Alexander Ivanov
|
|
||||||
arshbeerSingh
|
|
||||||
Denis Shilin
|
|
||||||
Freddo espresso
|
|
||||||
Ziad OUALHADJ
|
|
||||||
Silvério Santos
|
|
||||||
Miha Frangež
|
|
||||||
Eryk Michalak
|
|
||||||
Arnis Jaundžeikars
|
|
||||||
Dan
|
|
||||||
sr093906
|
|
||||||
mdvhimself
|
|
||||||
Jiri Grönroos
|
|
||||||
Katarzyna
|
|
||||||
echo r"0xX4H" | rev
|
|
||||||
Magnitudee
|
|
||||||
Olivia (Zoe)
|
|
||||||
betsythefc
|
|
||||||
waffshappen
|
waffshappen
|
||||||
Robin
|
ngocanhtve
|
||||||
ati3
|
Ziad OUALHADJ
|
||||||
enolp
|
Scrambled777
|
||||||
Evgeniy Khramov
|
Robin Liu
|
||||||
Jane Kong
|
Denis Shilin
|
||||||
Jean Mareilles
|
Alexander Ivanov
|
||||||
Jean-Luc Tibaux
|
Miha Frangež
|
||||||
José Rebelo
|
Silvério Santos
|
||||||
K. Herbert
|
Virginie
|
||||||
Lisa A.
|
|
||||||
Mawuena M. KODZO A.
|
|
||||||
rudy3
|
|
||||||
Reza
|
|
||||||
Still / Azaka
|
|
||||||
String E. Fighter
|
|
||||||
Tapu
|
|
||||||
Yurical
|
|
||||||
rr-vesp
|
|
||||||
yangyangdaji
|
|
||||||
丛林意志
|
|
||||||
alajemba-vik
|
|
||||||
/usr/local/ΕΨΗΕΛΩΝ
|
|
||||||
Adolfo Jayme-Barrientos
|
|
||||||
Alessandro Mandelli
|
|
||||||
KovalevArtem
|
|
||||||
Artem M.
|
|
||||||
Astrohops1
|
|
||||||
BMN
|
|
||||||
balaraz
|
|
||||||
BootVirtual
|
|
||||||
Bottan Hermawan
|
|
||||||
zChiip
|
|
||||||
Clonewayx
|
|
||||||
D. Domig
|
|
||||||
Danylo Lystopadov
|
|
||||||
Diego
|
|
||||||
Eudes-alencar
|
|
||||||
Fede Pujol
|
|
||||||
FineFindus
|
|
||||||
francescbassas
|
|
||||||
Jason Li
|
|
||||||
Jesse Davids
|
|
||||||
Kamborio
|
|
||||||
Kis Dominik
|
|
||||||
Lukas Grassauer
|
|
||||||
Luna Jernberg
|
|
||||||
Marnick L'Eau
|
|
||||||
Michalis
|
|
||||||
Michał
|
|
||||||
Milo Ivir
|
|
||||||
Mohamed A. Salah
|
|
||||||
Yatoku
|
|
||||||
the7thNightmare
|
|
||||||
Quang Trung
|
|
||||||
Rishi Agarwal
|
|
||||||
Rosdyana Kusuma
|
|
||||||
Sabri Ünal
|
|
||||||
umoenks
|
|
||||||
Simon Rusinov
|
|
||||||
Siriusmart
|
|
||||||
Mritunjay
|
|
||||||
Tarik Dzambic
|
|
||||||
Thomas Bertels
|
|
||||||
Thomas Cruveilher
|
|
||||||
Tian Jiale
|
|
||||||
Tong Liu
|
|
||||||
Tymofii Lytvynenko
|
|
||||||
Wanath
|
|
||||||
Younes Bouhouche
|
|
||||||
Runner
|
|
||||||
ce i moa
|
|
||||||
enescan201
|
|
||||||
Frablock
|
|
||||||
inesre
|
|
||||||
lgasp
|
|
||||||
notlin4
|
|
||||||
phlostically
|
|
||||||
pokeghost
|
|
||||||
sal0max
|
|
||||||
Ágata Leuck
|
|
||||||
BmBKun
|
|
||||||
NamHyeonjeong
|
|
||||||
Aditya Das
|
|
||||||
asier123123131
|
|
||||||
Kevin Sicong Jiang
|
|
||||||
Tomer Ben-Rachel
|
|
||||||
Tom Sawyer
|
|
||||||
tfuxu
|
|
||||||
Ahmed Saleh
|
|
||||||
Airat
|
|
||||||
Tapwaterisokey
|
|
||||||
Alexandra-Ioana Moroz
|
|
||||||
sNiXx
|
|
||||||
Angela Enogieru
|
|
||||||
Animesh Chatterjee
|
|
||||||
Artūras Kalenda
|
|
||||||
Ashish Yadav
|
|
||||||
Aya Elsaadany
|
|
||||||
Aya
|
|
||||||
Biren
|
|
||||||
Booc Sylvan
|
|
||||||
Brage Nesteby Reitan
|
|
||||||
Cap Amr Karam
|
|
||||||
Carlo Maria Cuoghi Barbagli
|
|
||||||
ChaoticNeutralCzech
|
|
||||||
ChengCheng
|
|
||||||
CherryMonster222
|
|
||||||
Colgrave
|
|
||||||
djcand
|
|
||||||
Mylou53
|
|
||||||
Daniel Sych
|
|
||||||
danieluhrinyi
|
|
||||||
Daniele Tricoli
|
|
||||||
Kasina Dheeraj
|
|
||||||
Donno
|
|
||||||
Reihan
|
|
||||||
Erik Spjelkavik
|
|
||||||
Flav
|
|
||||||
Franciszek Stefan
|
|
||||||
Gael Caraballo
|
|
||||||
Giacomo Alessandroni
|
|
||||||
Govind S Nair
|
|
||||||
Grzegorz
|
|
||||||
gneiss15
|
|
||||||
Hamustra Scans
|
|
||||||
helzubair
|
|
||||||
HowITsDone
|
|
||||||
Hubert Maciejewicz
|
|
||||||
Izzy
|
|
||||||
Jacek
|
|
||||||
Jacopo Gennaro Esposito
|
|
||||||
Jean-Baptiste
|
|
||||||
Kung-chih
|
|
||||||
Karvjorm
|
|
||||||
polar
|
|
||||||
krkk
|
|
||||||
Kristoffer Grundström
|
|
||||||
Laura Ferraz
|
|
||||||
Lionel HANNEQUIN
|
|
||||||
Lucas da Costa
|
|
||||||
almir992
|
|
||||||
Manan Jhaveri
|
|
||||||
Marco
|
|
||||||
BRBsoup
|
|
||||||
Mateo Gomez
|
|
||||||
Mattia
|
|
||||||
Md. Al-Amin
|
|
||||||
Michael Gangolf
|
Michael Gangolf
|
||||||
Milan Šalka
|
rudy3
|
||||||
3DN1M
|
Kim Seohyun
|
||||||
Minecraft boom
|
Govind S Nair
|
||||||
Mobashir Raihan
|
Freddo espresso
|
||||||
Moi Toi
|
arshbeerSingh
|
||||||
DiCeYMaYo
|
|
||||||
DivideEtImpera
|
|
||||||
Nicolas
|
|
||||||
Nosnahc
|
|
||||||
osamaqw
|
|
||||||
pa4k
|
|
||||||
pbeckmann
|
|
||||||
Peer Beckmann
|
|
||||||
vandman
|
|
||||||
Piotr Strebski
|
|
||||||
Piotr Zet
|
|
||||||
Poorva Patidar
|
|
||||||
Quang Nguyen
|
|
||||||
Ratnesh
|
|
||||||
Rohan Babbar
|
|
||||||
Ronak Upadhyay
|
|
||||||
Rose Liverman
|
|
||||||
SKULD
|
|
||||||
Sabrina
|
|
||||||
Salem Malus
|
|
||||||
Samarth Asthan
|
|
||||||
tatyhub
|
|
||||||
Shailendra Maurya
|
|
||||||
SilverFS
|
|
||||||
Simone Dotto
|
|
||||||
Subhashish Anand
|
|
||||||
Subhradeep Bera
|
|
||||||
Swayam Khare
|
|
||||||
SziaTomi
|
|
||||||
Mehedi Hasan
|
|
||||||
Tim Trek
|
|
||||||
Titas Pažereckas
|
|
||||||
atakujonc
|
|
||||||
tkraljevic
|
|
||||||
Tony C
|
|
||||||
Vancha March
|
|
||||||
tyap-lyap-ivprod
|
|
||||||
Waldemar Stoczkowski
|
|
||||||
Wiktor Kwapisiewicz
|
|
||||||
Yevgeny M
|
|
||||||
Yusril A
|
|
||||||
ahmed-awad26
|
|
||||||
Avik Kundu
|
|
||||||
ayuyydev
|
|
||||||
diksha-2911
|
|
||||||
essys
|
|
||||||
gbonaspetti
|
|
||||||
gittyboy-cell
|
|
||||||
huang ivan
|
|
||||||
lassr8
|
|
||||||
liva
|
|
||||||
lucafont2
|
|
||||||
mtrmirez
|
|
||||||
michaelpratana
|
|
||||||
opsik
|
|
||||||
pesta007
|
|
||||||
polarhun
|
|
||||||
pooyanazari
|
|
||||||
psa-jforestier
|
|
||||||
z369369
|
|
||||||
sergio
|
|
||||||
skauVictor
|
|
||||||
080502
|
|
||||||
Marcus
|
|
||||||
techwebpd
|
|
||||||
tjw123hh
|
|
||||||
Truestorybaby
|
|
||||||
tygyh
|
|
||||||
unstartdev
|
|
||||||
wmilan 17
|
|
||||||
MeH762
|
|
||||||
يوسف لطفي
|
|
||||||
しいたけ
|
|
||||||
元气
|
|
||||||
JaeBeom An
|
|
||||||
JungHee Lee
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
<string name="confirm">تأكيد</string>
|
<string name="confirm">تأكيد</string>
|
||||||
<string name="deleteConfirmation">مسح هذة البطاقة نهائيا؟</string>
|
<string name="deleteConfirmation">مسح هذة البطاقة نهائيا؟</string>
|
||||||
<string name="ok">حسنا</string>
|
<string name="ok">حسنا</string>
|
||||||
<string name="copy_to_clipboard">نسخ البطاقة الشخصية الى الحافظة</string>
|
|
||||||
<string name="share">شارك</string>
|
<string name="share">شارك</string>
|
||||||
<string name="sendLabel">ارسل…</string>
|
<string name="sendLabel">ارسل…</string>
|
||||||
<string name="editCardTitle">عدل البطاقة</string>
|
<string name="editCardTitle">عدل البطاقة</string>
|
||||||
@@ -26,7 +25,6 @@
|
|||||||
<string name="scanCardBarcode">مسح باركود</string>
|
<string name="scanCardBarcode">مسح باركود</string>
|
||||||
<string name="cardShortcut">اختصار البطاقة</string>
|
<string name="cardShortcut">اختصار البطاقة</string>
|
||||||
<string name="noCardsMessage">اضف بطاقة أولا</string>
|
<string name="noCardsMessage">اضف بطاقة أولا</string>
|
||||||
<string name="card_ids_copied">البطاقة(ات) الشخصية المنسوخة</string>
|
|
||||||
<string name="barcodeImageDescriptionWithType">صورة <xliff:g>%s</xliff:g> باركود</string>
|
<string name="barcodeImageDescriptionWithType">صورة <xliff:g>%s</xliff:g> باركود</string>
|
||||||
<string name="noCardExistsError">لا يمكن العثور على هذه البطاقة</string>
|
<string name="noCardExistsError">لا يمكن العثور على هذه البطاقة</string>
|
||||||
<string name="failedParsingImportUriError">لا يمكن تحليل الرابط المستورد</string>
|
<string name="failedParsingImportUriError">لا يمكن تحليل الرابط المستورد</string>
|
||||||
@@ -47,7 +45,6 @@
|
|||||||
<string name="app_license">البرمجيات الحرة متروكة الحقوق, ترخيص +GPLv3</string>
|
<string name="app_license">البرمجيات الحرة متروكة الحقوق, ترخيص +GPLv3</string>
|
||||||
<string name="app_libraries">مكتبات الطرف الثالث الحرة: <xliff:g id="app_libraries_list">%s</xliff:g></string>
|
<string name="app_libraries">مكتبات الطرف الثالث الحرة: <xliff:g id="app_libraries_list">%s</xliff:g></string>
|
||||||
<string name="selectBarcodeTitle">اختار الباركود</string>
|
<string name="selectBarcodeTitle">اختار الباركود</string>
|
||||||
<string name="copy_to_clipboard_toast">تم نسخ بطاقة الهوية إلى الحافظة</string>
|
|
||||||
<string name="thumbnailDescription">صورة مصغرة</string>
|
<string name="thumbnailDescription">صورة مصغرة</string>
|
||||||
<string name="starImage">نجم مفضل</string>
|
<string name="starImage">نجم مفضل</string>
|
||||||
<string name="settings">اعدادات</string>
|
<string name="settings">اعدادات</string>
|
||||||
@@ -95,7 +92,6 @@
|
|||||||
<string name="setBarcodeId">قم بتعيين قيمة الباركود</string>
|
<string name="setBarcodeId">قم بتعيين قيمة الباركود</string>
|
||||||
<string name="unsupportedBarcodeType">لا يمكن عرض نوع الباركود هذا. قد يكون مدعومًا في إصدار أحدث من التطبيق.</string>
|
<string name="unsupportedBarcodeType">لا يمكن عرض نوع الباركود هذا. قد يكون مدعومًا في إصدار أحدث من التطبيق.</string>
|
||||||
<string name="wrongValueForBarcodeType">القيمة غير صالحة لنوع الباركود المحدد</string>
|
<string name="wrongValueForBarcodeType">القيمة غير صالحة لنوع الباركود المحدد</string>
|
||||||
<string name="copy_to_clipboard_multiple_toast">تم نسخ بطاقات الهوية إلى الحافظة</string>
|
|
||||||
<string name="intent_import_card_from_url_share_multiple_text">أريد مشاركة بعض البطاقات معك</string>
|
<string name="intent_import_card_from_url_share_multiple_text">أريد مشاركة بعض البطاقات معك</string>
|
||||||
<string name="frontImageDescription">الصورة الأمامية</string>
|
<string name="frontImageDescription">الصورة الأمامية</string>
|
||||||
<string name="backImageDescription">الصورة الخلفية</string>
|
<string name="backImageDescription">الصورة الخلفية</string>
|
||||||
@@ -177,9 +173,9 @@
|
|||||||
<item quantity="zero">مسح <xliff:g>%d</xliff:g> بطاقة</item>
|
<item quantity="zero">مسح <xliff:g>%d</xliff:g> بطاقة</item>
|
||||||
<item quantity="one">مسح <xliff:g>%d</xliff:g> بطاقة</item>
|
<item quantity="one">مسح <xliff:g>%d</xliff:g> بطاقة</item>
|
||||||
<item quantity="two">مسح <xliff:g>%d</xliff:g> بطاقتين</item>
|
<item quantity="two">مسح <xliff:g>%d</xliff:g> بطاقتين</item>
|
||||||
<item quantity="few">مسح <xliff:g>%d</xliff:g> بطاقات</item>
|
<item quantity="few">مسح <xliff:g>%d</xliff:g> بطائق</item>
|
||||||
<item quantity="many">مسح <xliff:g>%d</xliff:g> بطاقات</item>
|
<item quantity="many">مسح <xliff:g>%d</xliff:g> بطاقة</item>
|
||||||
<item quantity="other">مسح <xliff:g>%d</xliff:g> بطاقات</item>
|
<item quantity="other">مسح <xliff:g>%d</xliff:g> بطاقة</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<plurals name="deleteCardsConfirmation">
|
<plurals name="deleteCardsConfirmation">
|
||||||
<item quantity="zero">مسح هذه <xliff:g>%d</xliff:g> البطاقة نهائيا؟</item>
|
<item quantity="zero">مسح هذه <xliff:g>%d</xliff:g> البطاقة نهائيا؟</item>
|
||||||
@@ -203,7 +199,7 @@
|
|||||||
<string name="groupsList">مجموعات: <xliff:g>%s</xliff:g></string>
|
<string name="groupsList">مجموعات: <xliff:g>%s</xliff:g></string>
|
||||||
<string name="settings_disable_lockscreen_while_viewing_card">منع قفل الشاشة</string>
|
<string name="settings_disable_lockscreen_while_viewing_card">منع قفل الشاشة</string>
|
||||||
<string name="leaveWithoutSaveTitle">خروج</string>
|
<string name="leaveWithoutSaveTitle">خروج</string>
|
||||||
<string name="editGroup">مجموعة التعديل: <xliff:g>%s</xliff:g></string>
|
<string name="editGroup">تعديل المجموعه: <xliff:g>%s</xliff:g></string>
|
||||||
<plurals name="groupCardCount">
|
<plurals name="groupCardCount">
|
||||||
<item quantity="zero"><xliff:g>%d</xliff:g> بطاقة</item>
|
<item quantity="zero"><xliff:g>%d</xliff:g> بطاقة</item>
|
||||||
<item quantity="one"><xliff:g>%d</xliff:g> بطاقة</item>
|
<item quantity="one"><xliff:g>%d</xliff:g> بطاقة</item>
|
||||||
@@ -255,7 +251,7 @@
|
|||||||
<string name="barcodeLongPressMessage">يمكن فتح صور فقط في تطبيق معرض الصور</string>
|
<string name="barcodeLongPressMessage">يمكن فتح صور فقط في تطبيق معرض الصور</string>
|
||||||
<string name="failedToOpenUrl">ثبت متصفح ويب أولاً</string>
|
<string name="failedToOpenUrl">ثبت متصفح ويب أولاً</string>
|
||||||
<string name="welcome">مرحبا بك في كاتيما</string>
|
<string name="welcome">مرحبا بك في كاتيما</string>
|
||||||
<string name="updateBalanceTitle">كم أنفقت؟</string>
|
<string name="updateBalanceTitle">كم أنفقت أو استلمت؟</string>
|
||||||
<string name="currentBalanceSentence">الرصيد الحالي: <xliff:g> %s </xliff:g></string>
|
<string name="currentBalanceSentence">الرصيد الحالي: <xliff:g> %s </xliff:g></string>
|
||||||
<plurals name="viewArchivedCardsWithCount">
|
<plurals name="viewArchivedCardsWithCount">
|
||||||
<item quantity="zero">عرض الأرشيف (<xliff:g>%1$d</xliff:g> بطاقة)</item>
|
<item quantity="zero">عرض الأرشيف (<xliff:g>%1$d</xliff:g> بطاقة)</item>
|
||||||
@@ -306,4 +302,20 @@
|
|||||||
<string name="settings_category_title_privacy">خصوصية</string>
|
<string name="settings_category_title_privacy">خصوصية</string>
|
||||||
<string name="show_balance">إظهار التوازن</string>
|
<string name="show_balance">إظهار التوازن</string>
|
||||||
<string name="settings_keep_screen_on_summary">تعطيل مهلة الشاشة أثناء عرض البطاقة</string>
|
<string name="settings_keep_screen_on_summary">تعطيل مهلة الشاشة أثناء عرض البطاقة</string>
|
||||||
|
<string name="balanceParsingFailed">رصيد غير صالح</string>
|
||||||
|
<string name="card_id_must_not_be_empty">يجب ألا يكون معرف البطاقة فارغا</string>
|
||||||
|
<string name="add_a_card_in_a_different_way">أضف بطاقة بطريقة مختلفة</string>
|
||||||
|
<string name="manually_enter_barcode_instructions">أدخل رقم الهوية أو النص الموجود على بطاقتك واضغط على الرمز الشريطي الذي يشبه الموجود على بطاقتك.</string>
|
||||||
|
<string name="action_more_options">خيارات أخرى</string>
|
||||||
|
<string name="enter_card_id">أدخل رقم الهوية أو النص الموجود على بطاقتك</string>
|
||||||
|
<string name="addWithoutBarcode">إضافة بدون باركود</string>
|
||||||
|
<string name="field_must_not_be_empty">يجب ألا يكون الحقل فارغا</string>
|
||||||
|
<string name="app_name">كاتيما</string>
|
||||||
|
<string name="settings_follow_sensor_orientation">التدوير دائمًا ( تجاهل إعدادات النظام)</string>
|
||||||
|
<string name="add_manually_warning_title">الفحص موصى به</string>
|
||||||
|
<string name="continue_">استمر</string>
|
||||||
|
<string name="spend">انفق</string>
|
||||||
|
<string name="receive">استلم</string>
|
||||||
|
<string name="amountParsingFailed">كمية غير صحيحة</string>
|
||||||
|
<string name="add_manually_warning_message">في بعض المتاجر قيمة الباركود تختلف عن الرقم الموجود على البطاقة. لهذا السبب إدخال الباركود يدوياً لن ينجح دائماً. من المستحسن فحص الباركود بكاميرا بدلا من ذالك. هل انت مُصِر على الاستكمال؟</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
<string name="storeName">Nome</string>
|
<string name="storeName">Nome</string>
|
||||||
<string name="note">Nota</string>
|
<string name="note">Nota</string>
|
||||||
<string name="noMatchingGiftCards">Nun hai nengún resultáu. Prueba a camudar la busca.</string>
|
<string name="noMatchingGiftCards">Nun hai nengún resultáu. Prueba a camudar la busca.</string>
|
||||||
@@ -24,10 +24,6 @@
|
|||||||
<string name="save">Guardar</string>
|
<string name="save">Guardar</string>
|
||||||
<string name="edit">Editar</string>
|
<string name="edit">Editar</string>
|
||||||
<string name="delete">Desaniciar</string>
|
<string name="delete">Desaniciar</string>
|
||||||
<plurals name="deleteCardsTitle">
|
|
||||||
<item quantity="one"></item>
|
|
||||||
<item quantity="other"></item>
|
|
||||||
</plurals>
|
|
||||||
<string name="unstar">Quitar de Favoritos</string>
|
<string name="unstar">Quitar de Favoritos</string>
|
||||||
<string name="cancel">Encaboxar</string>
|
<string name="cancel">Encaboxar</string>
|
||||||
<string name="importFailed">Nun se pudo facer la importación</string>
|
<string name="importFailed">Nun se pudo facer la importación</string>
|
||||||
|
|||||||
@@ -40,9 +40,8 @@
|
|||||||
<string name="addCardTitle">Добавяне на карта</string>
|
<string name="addCardTitle">Добавяне на карта</string>
|
||||||
<string name="removeImage">Премахване на изображение</string>
|
<string name="removeImage">Премахване на изображение</string>
|
||||||
<string name="takePhoto">Снимане</string>
|
<string name="takePhoto">Снимане</string>
|
||||||
<string name="copy_to_clipboard_multiple_toast">Номерата са копирани в междинната памет</string>
|
|
||||||
<string name="intent_import_card_from_url_share_multiple_text">Искам да споделя тези карти с вас</string>
|
<string name="intent_import_card_from_url_share_multiple_text">Искам да споделя тези карти с вас</string>
|
||||||
<string name="wrongValueForBarcodeType">Неприемлива стойност за избрания вид щрихкод</string>
|
<string name="wrongValueForBarcodeType">Стойноста е неприемлива за избрания щрихкод</string>
|
||||||
<string name="setBarcodeId">Задаване на стойност</string>
|
<string name="setBarcodeId">Задаване на стойност</string>
|
||||||
<string name="sameAsCardId">Като номера</string>
|
<string name="sameAsCardId">Като номера</string>
|
||||||
<string name="barcodeId">Стойност на щрихкода</string>
|
<string name="barcodeId">Стойност на щрихкода</string>
|
||||||
@@ -89,7 +88,6 @@
|
|||||||
<string name="settings">Настройки</string>
|
<string name="settings">Настройки</string>
|
||||||
<string name="starImage">Звезда за любимо</string>
|
<string name="starImage">Звезда за любимо</string>
|
||||||
<string name="thumbnailDescription">Миниатюра</string>
|
<string name="thumbnailDescription">Миниатюра</string>
|
||||||
<string name="copy_to_clipboard_toast">Номерът е копиран в междинната памет</string>
|
|
||||||
<string name="selectBarcodeTitle">Избиране на щрихкод</string>
|
<string name="selectBarcodeTitle">Избиране на щрихкод</string>
|
||||||
<string name="importOptionApplicationButton">Избиране чрез приложение</string>
|
<string name="importOptionApplicationButton">Избиране чрез приложение</string>
|
||||||
<string name="importing">Внасяне…</string>
|
<string name="importing">Внасяне…</string>
|
||||||
@@ -107,7 +105,6 @@
|
|||||||
<string name="scanCardBarcode">Снемане на щрихкод</string>
|
<string name="scanCardBarcode">Снемане на щрихкод</string>
|
||||||
<string name="editCardTitle">Редактиране на карта</string>
|
<string name="editCardTitle">Редактиране на карта</string>
|
||||||
<string name="share">Споделя</string>
|
<string name="share">Споделя</string>
|
||||||
<string name="copy_to_clipboard">Копира номера в междинната памет</string>
|
|
||||||
<string name="ok">Добре</string>
|
<string name="ok">Добре</string>
|
||||||
<string name="importSuccessful">Данните са внесени</string>
|
<string name="importSuccessful">Данните са внесени</string>
|
||||||
<string name="chooseImportType">Внасяне на данни на</string>
|
<string name="chooseImportType">Внасяне на данни на</string>
|
||||||
@@ -196,7 +193,7 @@
|
|||||||
<string name="group_name_already_in_use">Има списък с това име</string>
|
<string name="group_name_already_in_use">Има списък с това име</string>
|
||||||
<string name="group_updated">Промените са запазени</string>
|
<string name="group_updated">Промените са запазени</string>
|
||||||
<string name="selectColor">Избиране на цвят</string>
|
<string name="selectColor">Избиране на цвят</string>
|
||||||
<string name="group_name_is_empty">Името на списъка не може да е празно</string>
|
<string name="group_name_is_empty">Името на списъка не трябва да е празно</string>
|
||||||
<string name="group_edit">Редактиране на списък</string>
|
<string name="group_edit">Редактиране на списък</string>
|
||||||
<string name="noGiftCardsGroup">Създайте карти и ги зачислите към списък от тук.</string>
|
<string name="noGiftCardsGroup">Създайте карти и ги зачислите към списък от тук.</string>
|
||||||
<string name="translate_platform">в Weblate</string>
|
<string name="translate_platform">в Weblate</string>
|
||||||
@@ -204,7 +201,6 @@
|
|||||||
<string name="starred">Със звезда</string>
|
<string name="starred">Със звезда</string>
|
||||||
<string name="showMoreInfo">Показване на информация</string>
|
<string name="showMoreInfo">Показване на информация</string>
|
||||||
<string name="options">Настройки</string>
|
<string name="options">Настройки</string>
|
||||||
<string name="card_ids_copied">Номерата са копирани</string>
|
|
||||||
<plurals name="balancePoints">
|
<plurals name="balancePoints">
|
||||||
<item quantity="one"><xliff:g>%s</xliff:g> точка</item>
|
<item quantity="one"><xliff:g>%s</xliff:g> точка</item>
|
||||||
<item quantity="other"><xliff:g>%s</xliff:g> точки</item>
|
<item quantity="other"><xliff:g>%s</xliff:g> точки</item>
|
||||||
@@ -237,7 +233,7 @@
|
|||||||
<string name="barcodeLongPressMessage">В приложението галерия могат да бъдат отваряни само изображения</string>
|
<string name="barcodeLongPressMessage">В приложението галерия могат да бъдат отваряни само изображения</string>
|
||||||
<string name="failedToRetrieveImageFile">Не е възможно извличане на изображение</string>
|
<string name="failedToRetrieveImageFile">Не е възможно извличане на изображение</string>
|
||||||
<string name="noCameraPermissionDirectToSystemSetting">За да сканирате щрихкодове с Catima е необходим достъп до камерата. За да промените разрешението докоснете тук.</string>
|
<string name="noCameraPermissionDirectToSystemSetting">За да сканирате щрихкодове с Catima е необходим достъп до камерата. За да промените разрешението докоснете тук.</string>
|
||||||
<string name="updateBalanceTitle">Каква е промяната\?</string>
|
<string name="updateBalanceTitle">Колко е похарчено или получено?</string>
|
||||||
<string name="updateBalanceHint">Въведете стойност</string>
|
<string name="updateBalanceHint">Въведете стойност</string>
|
||||||
<string name="newBalanceSentence">Нов баланс: <xliff:g>%s</xliff:g></string>
|
<string name="newBalanceSentence">Нов баланс: <xliff:g>%s</xliff:g></string>
|
||||||
<string name="cameraPermissionDeniedTitle">Камерата е недостъпна</string>
|
<string name="cameraPermissionDeniedTitle">Камерата е недостъпна</string>
|
||||||
@@ -278,4 +274,20 @@
|
|||||||
<string name="show_archived_cards">Архивирани карти</string>
|
<string name="show_archived_cards">Архивирани карти</string>
|
||||||
<string name="view_online">Преглед</string>
|
<string name="view_online">Преглед</string>
|
||||||
<string name="app_copyright_short">Всички права запазени © Силвия ван Ос и сътрудници</string>
|
<string name="app_copyright_short">Всички права запазени © Силвия ван Ос и сътрудници</string>
|
||||||
|
<string name="card_id_must_not_be_empty">Номера на картата не трябва да бъде празен</string>
|
||||||
|
<string name="balanceParsingFailed">Неприемлив баланс</string>
|
||||||
|
<string name="add_a_card_in_a_different_way">Добавяне на карта по друг начин</string>
|
||||||
|
<string name="manually_enter_barcode_instructions">Въведете номера или текста и изберете щрихкода, който прилича на този от картата.</string>
|
||||||
|
<string name="action_more_options">Повече</string>
|
||||||
|
<string name="enter_card_id">Въведете номера или текста от картата</string>
|
||||||
|
<string name="addWithoutBarcode">Добавяне на карта без щрихкод</string>
|
||||||
|
<string name="field_must_not_be_empty">Полето не трябва да е празно</string>
|
||||||
|
<string name="app_name">Catima</string>
|
||||||
|
<string name="settings_follow_sensor_orientation">Винаги да се завърта (пренебрегва системната настройка)</string>
|
||||||
|
<string name="continue_">Продължаване</string>
|
||||||
|
<string name="add_manually_warning_title">Препоръчително е да сканирате</string>
|
||||||
|
<string name="add_manually_warning_message">Стойностите от щрихкода и отбелязаните на картата числа в някои случаи се различават. По тази причина е при ръчно въвеждане картата може да не работи. Силно препоръчително е да сканирате щрихкода с камерата. Желаете ли да продължите въпреки това?</string>
|
||||||
|
<string name="amountParsingFailed">Неприемлива сума</string>
|
||||||
|
<string name="spend">Похарчено</string>
|
||||||
|
<string name="receive">Получено</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -33,7 +33,6 @@
|
|||||||
<string name="setBarcodeId">বারকোড আইডি সেট করুন</string>
|
<string name="setBarcodeId">বারকোড আইডি সেট করুন</string>
|
||||||
<string name="unsupportedBarcodeType">এই বারকোডের টাইপটি এখন দেখানো যাচ্ছে না। অ্যাপের পরের সংস্করণে হয়ত এটি সমর্থন করা যেতে পারে।</string>
|
<string name="unsupportedBarcodeType">এই বারকোডের টাইপটি এখন দেখানো যাচ্ছে না। অ্যাপের পরের সংস্করণে হয়ত এটি সমর্থন করা যেতে পারে।</string>
|
||||||
<string name="wrongValueForBarcodeType">বারকোড টাইপের জন্য ভুল মান</string>
|
<string name="wrongValueForBarcodeType">বারকোড টাইপের জন্য ভুল মান</string>
|
||||||
<string name="copy_to_clipboard_multiple_toast">আইডিগুলি ক্লিপবোর্ডে কপি হল</string>
|
|
||||||
<string name="intent_import_card_from_url_share_multiple_text">url থেকে ইন্টেন্ট ইম্পোর্ট কার্ড একাধিক টেক্সট শেয়ার করে</string>
|
<string name="intent_import_card_from_url_share_multiple_text">url থেকে ইন্টেন্ট ইম্পোর্ট কার্ড একাধিক টেক্সট শেয়ার করে</string>
|
||||||
<string name="frontImageDescription">সামনের চিত্র</string>
|
<string name="frontImageDescription">সামনের চিত্র</string>
|
||||||
<string name="backImageDescription">পিছনের চিত্র</string>
|
<string name="backImageDescription">পিছনের চিত্র</string>
|
||||||
@@ -68,13 +67,11 @@
|
|||||||
<string name="reverse">...উল্টো ক্রমে</string>
|
<string name="reverse">...উল্টো ক্রমে</string>
|
||||||
<string name="sort_by">ক্রমানুসার</string>
|
<string name="sort_by">ক্রমানুসার</string>
|
||||||
<string name="noCardExistsError">কার্ডটি খুঁজে পাওয়া গেল না</string>
|
<string name="noCardExistsError">কার্ডটি খুঁজে পাওয়া গেল না</string>
|
||||||
<string name="card_ids_copied">আইডি কপি করা হয়েছে</string>
|
|
||||||
<string name="noCardsMessage">কোন কার্ড বার্তা নেই</string>
|
<string name="noCardsMessage">কোন কার্ড বার্তা নেই</string>
|
||||||
<string name="addCardTitle">কার্ডের শিরোনাম যোগ করুন</string>
|
<string name="addCardTitle">কার্ডের শিরোনাম যোগ করুন</string>
|
||||||
<string name="editCardTitle">কার্ডের শিরোনাম সম্পাদনা করুন</string>
|
<string name="editCardTitle">কার্ডের শিরোনাম সম্পাদনা করুন</string>
|
||||||
<string name="sendLabel">পাঠান…</string>
|
<string name="sendLabel">পাঠান…</string>
|
||||||
<string name="share">ভাগ</string>
|
<string name="share">ভাগ</string>
|
||||||
<string name="copy_to_clipboard">নকল করুন ক্লিপবোর্ড এ</string>
|
|
||||||
<string name="deleteConfirmation">এই কার্ডটি চিরকালের জন্য মুছে দেবো\?</string>
|
<string name="deleteConfirmation">এই কার্ডটি চিরকালের জন্য মুছে দেবো\?</string>
|
||||||
<string name="confirm">নিশ্চিত করুন</string>
|
<string name="confirm">নিশ্চিত করুন</string>
|
||||||
<string name="delete">মুছে ফেলুন</string>
|
<string name="delete">মুছে ফেলুন</string>
|
||||||
@@ -125,7 +122,6 @@
|
|||||||
<string name="importExport">আমদানি/রপ্তানি</string>
|
<string name="importExport">আমদানি/রপ্তানি</string>
|
||||||
<string name="cardShortcut">কার্ড শর্টকাট</string>
|
<string name="cardShortcut">কার্ড শর্টকাট</string>
|
||||||
<string name="exportFailed">রপ্তানি করা যাচ্ছে না</string>
|
<string name="exportFailed">রপ্তানি করা যাচ্ছে না</string>
|
||||||
<string name="copy_to_clipboard_toast">আইডি ক্লিপবোর্ডে নকল করা হল</string>
|
|
||||||
<string name="importExportHelp">নিজের ডেটা অন্য কোথাও সংরক্ষণ করে রাখলে পরে সেটা অন্য ডিভাইসে সরিয়ে নিতে পারবেন।</string>
|
<string name="importExportHelp">নিজের ডেটা অন্য কোথাও সংরক্ষণ করে রাখলে পরে সেটা অন্য ডিভাইসে সরিয়ে নিতে পারবেন।</string>
|
||||||
<string name="importFailed">আমদানি করা গেল না</string>
|
<string name="importFailed">আমদানি করা গেল না</string>
|
||||||
<string name="noGiftCardsGroup">কিছু কার্ড বানান আর এই গ্রুপে স্থির করুন।</string>
|
<string name="noGiftCardsGroup">কিছু কার্ড বানান আর এই গ্রুপে স্থির করুন।</string>
|
||||||
@@ -247,7 +243,7 @@
|
|||||||
<string name="unarchive">সংরক্ষণাগারমুক্ত করুন</string>
|
<string name="unarchive">সংরক্ষণাগারমুক্ত করুন</string>
|
||||||
<string name="archived">কার্ড সংরক্ষণাগারভুক্ত</string>
|
<string name="archived">কার্ড সংরক্ষণাগারভুক্ত</string>
|
||||||
<string name="welcome">Catima-তে স্বাগতম</string>
|
<string name="welcome">Catima-তে স্বাগতম</string>
|
||||||
<string name="updateBalanceTitle">আপনি কত খরচ করেছেন\?</string>
|
<string name="updateBalanceTitle">আপনি কত খরচ করেছেন?</string>
|
||||||
<string name="updateBalanceHint">পরিমান লিখুন</string>
|
<string name="updateBalanceHint">পরিমান লিখুন</string>
|
||||||
<string name="currentBalanceSentence">বর্তমান ব্যালেন্স: <xliff:g>%s</xliff:g></string>
|
<string name="currentBalanceSentence">বর্তমান ব্যালেন্স: <xliff:g>%s</xliff:g></string>
|
||||||
<string name="show_name_below_image_thumbnail">ছবির থাম্বনেইল এর নিচে নামটি দেখান</string>
|
<string name="show_name_below_image_thumbnail">ছবির থাম্বনেইল এর নিচে নামটি দেখান</string>
|
||||||
|
|||||||
@@ -40,7 +40,6 @@
|
|||||||
<item quantity="one">এই <xliff:g>%d</xliff:g> কার্ডটি স্থায়ীভাবে মুছবেন\?</item>
|
<item quantity="one">এই <xliff:g>%d</xliff:g> কার্ডটি স্থায়ীভাবে মুছবেন\?</item>
|
||||||
<item quantity="other">এই <xliff:g>%d</xliff:g> কার্ডগুলিকে স্থায়ীভাবে মুছবেন\?</item>
|
<item quantity="other">এই <xliff:g>%d</xliff:g> কার্ডগুলিকে স্থায়ীভাবে মুছবেন\?</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="copy_to_clipboard">ক্লিপবোর্ডে আইডি কপি করুন</string>
|
|
||||||
<string name="share">শেয়ার করুন</string>
|
<string name="share">শেয়ার করুন</string>
|
||||||
<string name="editCardTitle">কার্ড সম্পাদনা করুন</string>
|
<string name="editCardTitle">কার্ড সম্পাদনা করুন</string>
|
||||||
<string name="addCardTitle">কার্ড যোগ করুন</string>
|
<string name="addCardTitle">কার্ড যোগ করুন</string>
|
||||||
@@ -53,5 +52,28 @@
|
|||||||
<string name="noGiftCards">একটি কার্ড যোগ করতে + প্লাস বোতামে ক্লিক করুন বা ⋮ মেনু থেকে আমদানি করুন।</string>
|
<string name="noGiftCards">একটি কার্ড যোগ করতে + প্লাস বোতামে ক্লিক করুন বা ⋮ মেনু থেকে আমদানি করুন।</string>
|
||||||
<string name="cardShortcut">কার্ড শর্টকাট</string>
|
<string name="cardShortcut">কার্ড শর্টকাট</string>
|
||||||
<string name="noCardsMessage">প্রথমে একটি কার্ড যোগ করুন</string>
|
<string name="noCardsMessage">প্রথমে একটি কার্ড যোগ করুন</string>
|
||||||
<string name="card_ids_copied">আইডি কপি করা হয়েছে</string>
|
<string name="noCardExistsError">কার্ডটি খুঁজে পাওয়া যায়নি</string>
|
||||||
|
<string name="barcodeImageDescriptionWithType">ছবি <xliff:g>%s</xliff:g> বারকোড</string>
|
||||||
|
<string name="cameraPermissionDeniedTitle">ক্যামেরাটি ব্যবহার করা যাচ্ছে না</string>
|
||||||
|
<string name="failedParsingImportUriError">দেওয়া URL-টি প্রক্রিয়া করা যাচ্ছে না</string>
|
||||||
|
<string name="exporting">রপ্তানি করা হচ্ছে…</string>
|
||||||
|
<string name="noCameraPermissionDirectToSystemSetting">বারকোড স্ক্যান করার জন্য, ক্যাটিমা কে ক্যামেরাটি ব্যবহার করার অনুমতি দিতে হবে। এইখানে টাচ করে আপনার অনুমতি সেটিংস পালটে নিন।</string>
|
||||||
|
<string name="settings_dark_theme">অন্ধকার</string>
|
||||||
|
<string name="exportOptionExplanation">ডেটাটি আপনার পছন্দের জায়গায় রাখা হবে।</string>
|
||||||
|
<string name="importFailed">আমদানি করা গেল না</string>
|
||||||
|
<string name="permissionReadCardsDescription">সমস্ত ক্যাটিমা কার্ডস এবং তার তথ্য পড়ুন, নোট্স আর ছবি সহ</string>
|
||||||
|
<string name="importFailedTitle">আমদানি ব্যর্থ</string>
|
||||||
|
<string name="importExportHelp">নিজের ডেটা সংরক্ষণ করে রাখলে পরে সেটা অন্য ডিভাইসে সরিয়ে নিতে পারবেন।</string>
|
||||||
|
<string name="importExport">আমদানি/রপ্তানি</string>
|
||||||
|
<string name="importing">আমদানি করা হচ্ছে…</string>
|
||||||
|
<string name="exportFailed">রপ্তানি করা যাচ্ছে না</string>
|
||||||
|
<string name="exportName">আমদানি/রপ্তানি</string>
|
||||||
|
<string name="cameraPermissionRequired">এই কাজটির জন্য ক্যামেরা ব্যবহার করার অনুমতি লাগবে…</string>
|
||||||
|
<string name="importSuccessfulTitle">আমদানি শেষ</string>
|
||||||
|
<string name="exportSuccessfulTitle">রপ্তানি শেষ</string>
|
||||||
|
<string name="permissionReadCardsLabel">কাটিমা কার্ডস পড়ুন</string>
|
||||||
|
<string name="storageReadPermissionRequired">এই কাজটির জন্য ফোনের স্টোরেজ দেখার অনুমতি লাগবে…</string>
|
||||||
|
<string name="exportFailedTitle">রপ্তানি ব্যর্থ</string>
|
||||||
|
<string name="settings_card_orientation">বারকোড অভিমুখ (ওরিয়েন্টেশন)</string>
|
||||||
|
<string name="app_name">ক্যাটিমা</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user