mirror of
https://github.com/CatimaLoyalty/Android.git
synced 2025-12-24 15:47:53 -05:00
Compare commits
761 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7afbe41f95 | ||
|
|
6d4cbee499 | ||
|
|
f16a597b91 | ||
|
|
3ba0649891 | ||
|
|
297ecc371e | ||
|
|
79c8570507 | ||
|
|
8f81d2d141 | ||
|
|
325535af12 | ||
|
|
f7af4169a3 | ||
|
|
e5ec45f877 | ||
|
|
902a4e411b | ||
|
|
03ec2b4492 | ||
|
|
6a2f8a630a | ||
|
|
78e998fad9 | ||
|
|
e6ccef8ad0 | ||
|
|
acbd614b50 | ||
|
|
445e661fac | ||
|
|
d774231c05 | ||
|
|
b5d2a5dbab | ||
|
|
6ab61eb78a | ||
|
|
bbd8bb7d29 | ||
|
|
3e082b598f | ||
|
|
70510ed9d1 | ||
|
|
5da1c67dd6 | ||
|
|
dfa237958e | ||
|
|
127d53e85a | ||
|
|
7cce782143 | ||
|
|
662eb853d7 | ||
|
|
d8e58f8b38 | ||
|
|
4748e24dda | ||
|
|
5af024f482 | ||
|
|
8adc013330 | ||
|
|
4edfe962ef | ||
|
|
d80a71c852 | ||
|
|
299c9f2b4d | ||
|
|
03e3e45f07 | ||
|
|
c35c316788 | ||
|
|
d68f0c60d4 | ||
|
|
5faa28a7e7 | ||
|
|
2fffb89179 | ||
|
|
bd27ccd535 | ||
|
|
3149234f5d | ||
|
|
dcf55c93a8 | ||
|
|
3f3394c1e9 | ||
|
|
2b22dd4a1f | ||
|
|
a70710ca6a | ||
|
|
cd3baf5bd9 | ||
|
|
1138d6387e | ||
|
|
b664fd391a | ||
|
|
11cc65bae3 | ||
|
|
eaafa56503 | ||
|
|
09c6d58639 | ||
|
|
1767a009a7 | ||
|
|
a2e25512ec | ||
|
|
824b931764 | ||
|
|
0cb75fd421 | ||
|
|
36f0d814f6 | ||
|
|
2aa0c46d30 | ||
|
|
ff63dbfdaa | ||
|
|
5cacdbd192 | ||
|
|
c99bf206cc | ||
|
|
f546bb2681 | ||
|
|
bef750e047 | ||
|
|
6998599377 | ||
|
|
11164f9a73 | ||
|
|
b867795a08 | ||
|
|
9998205051 | ||
|
|
7692f19ea6 | ||
|
|
d8361cc288 | ||
|
|
93d0724c60 | ||
|
|
2c03f193f0 | ||
|
|
7028a1f4ca | ||
|
|
32d62dd9ba | ||
|
|
401fc98b4d | ||
|
|
993589427b | ||
|
|
2390568bdf | ||
|
|
50ccf6da46 | ||
|
|
44066e6599 | ||
|
|
fc9c616869 | ||
|
|
f09f35e44b | ||
|
|
f81fb0c673 | ||
|
|
b0b4e80da4 | ||
|
|
923982e449 | ||
|
|
d05c97946d | ||
|
|
504a1fd01b | ||
|
|
5efacec417 | ||
|
|
595d10fc44 | ||
|
|
2128f0a601 | ||
|
|
0b0a45da05 | ||
|
|
94fb4b4411 | ||
|
|
fc7932d3bf | ||
|
|
97a5311593 | ||
|
|
c7db676b2b | ||
|
|
f8c8b3b2e6 | ||
|
|
df4cb5b189 | ||
|
|
d2b2a53109 | ||
|
|
5107f88998 | ||
|
|
78f2e1698f | ||
|
|
6887cd098b | ||
|
|
0881d745f5 | ||
|
|
94b3a07f90 | ||
|
|
dc280b4023 | ||
|
|
edce6d6cd3 | ||
|
|
23dc47a5b9 | ||
|
|
683b4f46d9 | ||
|
|
ac021bce2a | ||
|
|
b0c34cbca0 | ||
|
|
4a0c8270ef | ||
|
|
793f1e2f87 | ||
|
|
0101b6fa25 | ||
|
|
47dff850fb | ||
|
|
fe7503c3ac | ||
|
|
5df7d8a530 | ||
|
|
ee129a3373 | ||
|
|
b979b1fc86 | ||
|
|
361ebb8b80 | ||
|
|
fd49466e0d | ||
|
|
bf8f00f877 | ||
|
|
9f0b7604cb | ||
|
|
6d151e7377 | ||
|
|
6ecc94526e | ||
|
|
1462911ffa | ||
|
|
952f7a933b | ||
|
|
b76c4ef352 | ||
|
|
48048cdb82 | ||
|
|
a555f1c41b | ||
|
|
2ab7ebb2c0 | ||
|
|
e2b0113687 | ||
|
|
dc7e9b541a | ||
|
|
b44ef7dfc0 | ||
|
|
4d1f4a64fa | ||
|
|
fd7e6e4993 | ||
|
|
b749c79a81 | ||
|
|
5d019a8e5b | ||
|
|
4e203aebfe | ||
|
|
fb2bede135 | ||
|
|
5a88909cd2 | ||
|
|
017034a788 | ||
|
|
66ff6f8199 | ||
|
|
ddccbad020 | ||
|
|
578fb068ee | ||
|
|
d624eb3842 | ||
|
|
f46fedda4b | ||
|
|
6e5ac2ca3d | ||
|
|
20de874ea1 | ||
|
|
c8a207083b | ||
|
|
ab7505c67a | ||
|
|
b26050b6bf | ||
|
|
84e9c8efd4 | ||
|
|
2811a14627 | ||
|
|
3b6d4d44b0 | ||
|
|
71bc304c51 | ||
|
|
7b0652ff11 | ||
|
|
61a3054655 | ||
|
|
54c1cc3661 | ||
|
|
ee0f9e04de | ||
|
|
ebe5289d6e | ||
|
|
15ba15c602 | ||
|
|
fd0448efc4 | ||
|
|
43fa2623d4 | ||
|
|
c1b9babf33 | ||
|
|
4c8edf9d9a | ||
|
|
ff4271f51d | ||
|
|
2424ab01cf | ||
|
|
b38dfeeed7 | ||
|
|
7912cd190f | ||
|
|
11962ad930 | ||
|
|
a8e2c65072 | ||
|
|
1b8ca5f467 | ||
|
|
46fff6da5b | ||
|
|
47e75f64ed | ||
|
|
fb95c8c9d4 | ||
|
|
66cc97214c | ||
|
|
f52423ed70 | ||
|
|
29bea052eb | ||
|
|
b683f1fce4 | ||
|
|
2ec55473a5 | ||
|
|
2fe89ffcbc | ||
|
|
7e88a10884 | ||
|
|
f260051160 | ||
|
|
b8dacc2459 | ||
|
|
b311dd99dc | ||
|
|
1d7a9843d8 | ||
|
|
03e59ad00f | ||
|
|
c083af1c76 | ||
|
|
6abb0a2a75 | ||
|
|
e672e7f1a6 | ||
|
|
852a638ea1 | ||
|
|
ec1b642614 | ||
|
|
145dac72af | ||
|
|
7d2679b2a3 | ||
|
|
ffbcd2183b | ||
|
|
b9d646868c | ||
|
|
2ce530a644 | ||
|
|
1c8e9ba1cf | ||
|
|
9a2560dbc2 | ||
|
|
f900c4299d | ||
|
|
f78494f882 | ||
|
|
b55f10b59d | ||
|
|
277ee90421 | ||
|
|
fcdde83038 | ||
|
|
12fb29ecce | ||
|
|
ae7dd63d6b | ||
|
|
48f680f7bd | ||
|
|
e3d4e393b3 | ||
|
|
81f13c255a | ||
|
|
97ce322d0c | ||
|
|
a3e876d9a2 | ||
|
|
234356f8f2 | ||
|
|
d3222f7bdc | ||
|
|
a5658e72c7 | ||
|
|
d1e1fcfabe | ||
|
|
9d6bd0770c | ||
|
|
6f76ee389b | ||
|
|
87aa74f231 | ||
|
|
a0dc80c1a5 | ||
|
|
d8dbe25a64 | ||
|
|
e3bec085df | ||
|
|
4d4b92b33f | ||
|
|
cc58c769cc | ||
|
|
37f08f6fc4 | ||
|
|
73e02e7c06 | ||
|
|
7a1b6fccc2 | ||
|
|
2473e3f91a | ||
|
|
0a6bb2805c | ||
|
|
1eecc6f065 | ||
|
|
b8b4fe4958 | ||
|
|
6dd8b6798b | ||
|
|
69d0a3f4aa | ||
|
|
ce81934890 | ||
|
|
ba06f47e3e | ||
|
|
cfc37d4af6 | ||
|
|
43cd6edda2 | ||
|
|
0bd262d82f | ||
|
|
f09bafa104 | ||
|
|
20e34ee365 | ||
|
|
7a6bd8f661 | ||
|
|
54c3765e36 | ||
|
|
4d7f563b0d | ||
|
|
4e0ecaa7be | ||
|
|
4f41d238eb | ||
|
|
7c3d021427 | ||
|
|
bfde036484 | ||
|
|
84ef4ad030 | ||
|
|
3b85fccd60 | ||
|
|
830d0f6e6a | ||
|
|
1ceede27a3 | ||
|
|
fa33cdaca4 | ||
|
|
f39fbb55a1 | ||
|
|
f3ffa0ab88 | ||
|
|
48e1fcc38e | ||
|
|
5b889c4c0c | ||
|
|
616ca77c39 | ||
|
|
59bf064783 | ||
|
|
220393c445 | ||
|
|
b976c03fb0 | ||
|
|
dad0493666 | ||
|
|
776613c507 | ||
|
|
7570d9d319 | ||
|
|
2e648d1062 | ||
|
|
5ad02ae9cc | ||
|
|
99fc568419 | ||
|
|
5f835716e0 | ||
|
|
1f6fe787eb | ||
|
|
1b08d30611 | ||
|
|
05ad33a756 | ||
|
|
b103366a10 | ||
|
|
343d37d4b8 | ||
|
|
60dc050633 | ||
|
|
96c3583393 | ||
|
|
43656dbcca | ||
|
|
7c38bbe6ab | ||
|
|
c6c6448501 | ||
|
|
7ff9da2d7f | ||
|
|
39893cc66d | ||
|
|
5110618d7d | ||
|
|
a856831ee1 | ||
|
|
c5fc6a4212 | ||
|
|
7708646c68 | ||
|
|
c84dbac020 | ||
|
|
56c2297e02 | ||
|
|
6956805e62 | ||
|
|
7736e30043 | ||
|
|
ace441f072 | ||
|
|
a37e99ce39 | ||
|
|
6b1bdbaa78 | ||
|
|
b4077a2fc3 | ||
|
|
2f660c6fec | ||
|
|
cc0511a2aa | ||
|
|
2f367efa70 | ||
|
|
bff1b54b7e | ||
|
|
010e9556b8 | ||
|
|
7c54c01dfe | ||
|
|
5709c8dbec | ||
|
|
0f434d7fe7 | ||
|
|
a2d7e70431 | ||
|
|
0c0c462372 | ||
|
|
f630539d9a | ||
|
|
7e27ddcc94 | ||
|
|
f9b61c24e9 | ||
|
|
d26a138e3e | ||
|
|
e695a4e993 | ||
|
|
8cae6aed4a | ||
|
|
56b72dd544 | ||
|
|
b935b30e00 | ||
|
|
b9a6c6248d | ||
|
|
09d8703545 | ||
|
|
6e25211f12 | ||
|
|
6f1dc74f66 | ||
|
|
61b1317fc5 | ||
|
|
129ee2d5aa | ||
|
|
2358ea1f89 | ||
|
|
87a172afd5 | ||
|
|
5a64b6996e | ||
|
|
9ba07982bf | ||
|
|
98c41ec003 | ||
|
|
bc9875e7fc | ||
|
|
b8bc7eded3 | ||
|
|
6625deadf7 | ||
|
|
c781e4ff4e | ||
|
|
b5a2508027 | ||
|
|
4916917ac9 | ||
|
|
4f9b1dae04 | ||
|
|
9b841399f7 | ||
|
|
45fdc81aa7 | ||
|
|
fdb28a3e52 | ||
|
|
a151a9334e | ||
|
|
618c7a294d | ||
|
|
f926d61213 | ||
|
|
d572add506 | ||
|
|
012da7397f | ||
|
|
92e4908ea8 | ||
|
|
a051d628ab | ||
|
|
0116346035 | ||
|
|
3049b7b0ae | ||
|
|
0c484d26ee | ||
|
|
55bea5262e | ||
|
|
f64d0dcc16 | ||
|
|
ad23a671a2 | ||
|
|
718e6b433d | ||
|
|
65e2965df2 | ||
|
|
2bf815942c | ||
|
|
b5547c5cdf | ||
|
|
371fd00606 | ||
|
|
4f2a448e7e | ||
|
|
2755641e6b | ||
|
|
3e80066a68 | ||
|
|
2e78f77be6 | ||
|
|
d042d9a7e8 | ||
|
|
c4064a2ed1 | ||
|
|
0ac0fad091 | ||
|
|
7fdad67ea4 | ||
|
|
be21bc1654 | ||
|
|
e65fb4390f | ||
|
|
9f7bd4a0bd | ||
|
|
b43817cbde | ||
|
|
da0e2ebb99 | ||
|
|
aa87f33676 | ||
|
|
4e7d4383e8 | ||
|
|
0702aa1c79 | ||
|
|
cd97f89d90 | ||
|
|
eab6c4d2bd | ||
|
|
20775cbc79 | ||
|
|
90ecb1997f | ||
|
|
64b1483fa5 | ||
|
|
8365a719d8 | ||
|
|
068e793682 | ||
|
|
2e1c72e401 | ||
|
|
3e213c6996 | ||
|
|
ec65abadc3 | ||
|
|
d24b56b5c1 | ||
|
|
b56f4e1653 | ||
|
|
efba8773c5 | ||
|
|
15339a8c44 | ||
|
|
050021de5c | ||
|
|
16cff71cfb | ||
|
|
76226a319f | ||
|
|
eed138a2fb | ||
|
|
fd202fc940 | ||
|
|
5364b57cee | ||
|
|
3136b80f26 | ||
|
|
f3471b082f | ||
|
|
9d24ef7791 | ||
|
|
6223ccd4bc | ||
|
|
a81cc27414 | ||
|
|
78481516b0 | ||
|
|
423b74273e | ||
|
|
83e0a988d9 | ||
|
|
1adea88ff1 | ||
|
|
dda912c8c5 | ||
|
|
f3b31bb306 | ||
|
|
3b31c4e05a | ||
|
|
1155108086 | ||
|
|
73bb7ded09 | ||
|
|
d8655eb631 | ||
|
|
55cddfef91 | ||
|
|
c35dc736f4 | ||
|
|
23b762a500 | ||
|
|
b9b638de45 | ||
|
|
5f57507dda | ||
|
|
852eb6612c | ||
|
|
d96aac7bbf | ||
|
|
9018479959 | ||
|
|
ce8a99e840 | ||
|
|
9236f66bc6 | ||
|
|
37b3964351 | ||
|
|
a60d902fda | ||
|
|
a97ce6f9aa | ||
|
|
a15bce04e8 | ||
|
|
34fa68f852 | ||
|
|
e5fef55fc7 | ||
|
|
c5cca1b14d | ||
|
|
f34ec1cc4b | ||
|
|
95aafa5c99 | ||
|
|
00160b0c45 | ||
|
|
a7e6d56bc9 | ||
|
|
9dc8d5e2f4 | ||
|
|
cf7cf679e0 | ||
|
|
4e7e0d9480 | ||
|
|
fbb3013b20 | ||
|
|
ca37e2aa84 | ||
|
|
e6bf60d457 | ||
|
|
35835fda34 | ||
|
|
a3133de6cf | ||
|
|
40fb10a2ec | ||
|
|
856c735ba3 | ||
|
|
16fb0df55d | ||
|
|
5658acbb07 | ||
|
|
8b5faac0e0 | ||
|
|
10be9b58ce | ||
|
|
832840be08 | ||
|
|
5806156495 | ||
|
|
0be9b908ab | ||
|
|
718d2b7567 | ||
|
|
fed9754908 | ||
|
|
c0e09ca429 | ||
|
|
4a3737b846 | ||
|
|
b4add4a0ea | ||
|
|
5aabbd629e | ||
|
|
a5ef50317d | ||
|
|
48a8cc927a | ||
|
|
cbb97d7d57 | ||
|
|
5eee344035 | ||
|
|
4c7e0c7174 | ||
|
|
37bb9c86f4 | ||
|
|
83ea6ffbf7 | ||
|
|
f4945c2cd2 | ||
|
|
24d9c7825a | ||
|
|
2aa7bb4639 | ||
|
|
110b3bcef3 | ||
|
|
f625efc76d | ||
|
|
22608a9802 | ||
|
|
abf6c1328d | ||
|
|
225058ab2b | ||
|
|
275dc6caa2 | ||
|
|
ceb7b12154 | ||
|
|
849da26198 | ||
|
|
5808c991bb | ||
|
|
e8554cb221 | ||
|
|
46092c060d | ||
|
|
736eebd45c | ||
|
|
f72db53345 | ||
|
|
b8c017259e | ||
|
|
4b4a237b9a | ||
|
|
17034e992f | ||
|
|
bf143038b0 | ||
|
|
73805ad3bb | ||
|
|
7006e35ebf | ||
|
|
fc598018b2 | ||
|
|
d01ec85c32 | ||
|
|
04ee918152 | ||
|
|
943b70647b | ||
|
|
edba5d5dca | ||
|
|
86be5d1994 | ||
|
|
5be2308606 | ||
|
|
a2e81d08d7 | ||
|
|
9f5f0e79f4 | ||
|
|
3f8c4c94ce | ||
|
|
46895c0037 | ||
|
|
65c98a4840 | ||
|
|
67c58c10b2 | ||
|
|
8d82a4616f | ||
|
|
01a272f8e6 | ||
|
|
0235a397cf | ||
|
|
e37ee6f812 | ||
|
|
2b5ea47d9f | ||
|
|
0fcdaf3f60 | ||
|
|
b2cb38b61d | ||
|
|
fd1fe168cc | ||
|
|
9deb80ff9b | ||
|
|
75fdd7390f | ||
|
|
7cd6c17b55 | ||
|
|
c273d8d17f | ||
|
|
c1b02f58ff | ||
|
|
0bac2dfb62 | ||
|
|
4f0288e396 | ||
|
|
55f0399325 | ||
|
|
e756c6e50b | ||
|
|
b8c022dc70 | ||
|
|
d823ddd8aa | ||
|
|
352b42bba0 | ||
|
|
89894131d9 | ||
|
|
5ddcb0a668 | ||
|
|
5d5b1f0a63 | ||
|
|
03e638786b | ||
|
|
7d5b2f8dc8 | ||
|
|
13257d17fc | ||
|
|
5e8a589d9a | ||
|
|
44711043b9 | ||
|
|
80ee8aa860 | ||
|
|
cd6685b974 | ||
|
|
b5f464000b | ||
|
|
27159323d5 | ||
|
|
9551ce8a8b | ||
|
|
525471f749 | ||
|
|
57bedd0300 | ||
|
|
c3345a1a15 | ||
|
|
15f6bd86a1 | ||
|
|
e2ad1c9da2 | ||
|
|
7082669612 | ||
|
|
d595d24769 | ||
|
|
4287d66f6f | ||
|
|
311361d105 | ||
|
|
4a88e39deb | ||
|
|
4511590263 | ||
|
|
d0c30ffa1c | ||
|
|
813c2bff85 | ||
|
|
0c3cf43841 | ||
|
|
6c651f7e3e | ||
|
|
ef7fc92920 | ||
|
|
ccaf70c749 | ||
|
|
2299bf9d86 | ||
|
|
b65f8f32ca | ||
|
|
47695d3a72 | ||
|
|
439c660b2e | ||
|
|
4293d7d4b2 | ||
|
|
e368e66b8a | ||
|
|
992a7f9e84 | ||
|
|
ecf0faf00a | ||
|
|
64178f3fd1 | ||
|
|
dab6588800 | ||
|
|
f71bb32592 | ||
|
|
0c44212d92 | ||
|
|
ce149c91e9 | ||
|
|
05dfd48f57 | ||
|
|
ef955b866d | ||
|
|
a843b5a1b9 | ||
|
|
8305d58ccc | ||
|
|
6af79d5f41 | ||
|
|
3b326d6f9c | ||
|
|
1456e5073e | ||
|
|
6a13dbf66a | ||
|
|
c5b1718e8e | ||
|
|
e2bcc24867 | ||
|
|
c86cc22a93 | ||
|
|
765577ae9e | ||
|
|
d1d15644b5 | ||
|
|
5d41b213db | ||
|
|
aa515b1192 | ||
|
|
068660fe3f | ||
|
|
99ffaf97d1 | ||
|
|
b53999c1cf | ||
|
|
57f87d7dc0 | ||
|
|
760f6a873d | ||
|
|
675bd43ff8 | ||
|
|
07532ce001 | ||
|
|
908a7055c7 | ||
|
|
2be371caed | ||
|
|
f4f3f2e307 | ||
|
|
edbf76c7dc | ||
|
|
f8615f45f0 | ||
|
|
c3bc3e9911 | ||
|
|
8adcca1df2 | ||
|
|
bdde8669ac | ||
|
|
59db2df525 | ||
|
|
3b83ff5b0e | ||
|
|
19fb290a51 | ||
|
|
e1e823d9e0 | ||
|
|
7ed477b670 | ||
|
|
1960fb0b6a | ||
|
|
e913482790 | ||
|
|
2e5bd76d31 | ||
|
|
876ae979da | ||
|
|
d918c15ad6 | ||
|
|
d57cb307c3 | ||
|
|
cbcf1bcd99 | ||
|
|
96bc10583f | ||
|
|
5ca4d29a36 | ||
|
|
2efb666fae | ||
|
|
89dce1068f | ||
|
|
9854125af9 | ||
|
|
6e307ab1f0 | ||
|
|
c4f0d1bef6 | ||
|
|
5201788818 | ||
|
|
8472bc9755 | ||
|
|
ce0f531831 | ||
|
|
18e9c3ccb5 | ||
|
|
19afe8e69c | ||
|
|
8e5bace7cc | ||
|
|
1ca85e9d7b | ||
|
|
eb24af8266 | ||
|
|
12ba01eb87 | ||
|
|
a0e30bdccc | ||
|
|
4663e22128 | ||
|
|
7bd1d16d24 | ||
|
|
fd838cfd43 | ||
|
|
1eca79b4cb | ||
|
|
1d694f5f2c | ||
|
|
96a046b165 | ||
|
|
5ff9c9c469 | ||
|
|
836cdd87bc | ||
|
|
0af6dd4d44 | ||
|
|
034d62a643 | ||
|
|
417224602e | ||
|
|
c711aeae7f | ||
|
|
1f12543e3e | ||
|
|
dc31175b5d | ||
|
|
719b8112eb | ||
|
|
f19a3c507b | ||
|
|
2416ec396a | ||
|
|
d1fe92e967 | ||
|
|
ff5fd49b89 | ||
|
|
1015e0d94e | ||
|
|
d872828e7d | ||
|
|
8f6ad6d1bd | ||
|
|
05fd629ad4 | ||
|
|
efdb0dd6bb | ||
|
|
2e0482beef | ||
|
|
0f25743da4 | ||
|
|
e970bf185a | ||
|
|
60f3547b01 | ||
|
|
ae09db428b | ||
|
|
ebca7ca150 | ||
|
|
b6ef6806a0 | ||
|
|
20a9cb30c4 | ||
|
|
a43112f469 | ||
|
|
2a95b2f530 | ||
|
|
cb979bfbec | ||
|
|
feabe353b0 | ||
|
|
7987932100 | ||
|
|
e496c69e15 | ||
|
|
e0300f8f21 | ||
|
|
64cbcb2ef7 | ||
|
|
7e24f02a73 | ||
|
|
ed89eab782 | ||
|
|
17863e8920 | ||
|
|
6e82e6fc5d | ||
|
|
64b1103f13 | ||
|
|
e4274df941 | ||
|
|
c786b4b5a7 | ||
|
|
95405270cb | ||
|
|
9ef8eb2934 | ||
|
|
4f70b06edb | ||
|
|
5677aa2b38 | ||
|
|
44997e1bbd | ||
|
|
a96c569314 | ||
|
|
5545220d53 | ||
|
|
4f47ee30ba | ||
|
|
4f9f318960 | ||
|
|
5dcfcf0ad0 | ||
|
|
eb809974c4 | ||
|
|
815b037be7 | ||
|
|
3eff150835 | ||
|
|
e191e8bbe6 | ||
|
|
5aaf135325 | ||
|
|
6fd4f617e5 | ||
|
|
621e4ce162 | ||
|
|
bf07e8935f | ||
|
|
e69d6a453b | ||
|
|
579c2fc640 | ||
|
|
ba870481dd | ||
|
|
0c10936e75 | ||
|
|
26cf4250ad | ||
|
|
a8d5f38d5c | ||
|
|
5e1bac4bcf | ||
|
|
6df8b7b1ea | ||
|
|
964c603405 | ||
|
|
f64eea6470 | ||
|
|
aa97ad49d2 | ||
|
|
6221da72f8 | ||
|
|
fe8ec38bf3 | ||
|
|
7324353d74 | ||
|
|
c5f0ee3a66 | ||
|
|
a8b4b25afb | ||
|
|
9744ede674 | ||
|
|
43505d427d | ||
|
|
65e54c63ef | ||
|
|
31b306d432 | ||
|
|
25bd1de09d | ||
|
|
c82e0d82a9 | ||
|
|
6625488d84 | ||
|
|
a1f632ace3 | ||
|
|
b9ea7fd1ed | ||
|
|
8dff8d392e | ||
|
|
b10d50d5a0 | ||
|
|
0b54d87f4f | ||
|
|
a9568d6adb | ||
|
|
18c5aa4707 | ||
|
|
f53c61bbec | ||
|
|
25c9c67ca2 | ||
|
|
2efbf664c9 | ||
|
|
44023969a7 | ||
|
|
0e5216b010 | ||
|
|
f258506936 | ||
|
|
8bad342374 | ||
|
|
476a219bec | ||
|
|
24e19e26ba | ||
|
|
3cb16ae3e9 | ||
|
|
c7ddf957fa | ||
|
|
0de8cd93ad | ||
|
|
614e5bac2d | ||
|
|
e76bd9363c | ||
|
|
97c508c920 | ||
|
|
0f178c1cac | ||
|
|
8eab852e1a | ||
|
|
539f8846d8 | ||
|
|
fb239b6974 | ||
|
|
85b553e17b | ||
|
|
f085f1e9e6 | ||
|
|
56b387c725 | ||
|
|
daf0cdaa71 | ||
|
|
6a7bebdbcd | ||
|
|
7f72c3bcaf | ||
|
|
52bff53756 | ||
|
|
73743b7f1e | ||
|
|
acfcd3d2d2 | ||
|
|
24e8d12b73 | ||
|
|
28068dcddc | ||
|
|
3e5ab76636 | ||
|
|
3dceec8ec0 | ||
|
|
0cc409d087 | ||
|
|
346acfa3f5 | ||
|
|
8d48da431e | ||
|
|
b913fad847 | ||
|
|
c900642f8e | ||
|
|
c7d0da9e20 | ||
|
|
f66b368cc2 | ||
|
|
4dad96472f | ||
|
|
01bb4f0fc4 | ||
|
|
9ae5d74c7c | ||
|
|
5af6d9b61c | ||
|
|
eb89a04c72 | ||
|
|
1019e40987 | ||
|
|
175d860885 | ||
|
|
de8aa6b6fd | ||
|
|
a5a7be02f6 | ||
|
|
14b7f8af81 | ||
|
|
62d01abf92 | ||
|
|
a328fa8f4a | ||
|
|
9e55271db1 | ||
|
|
e09ba941b8 | ||
|
|
7562b662b7 | ||
|
|
ce65163377 | ||
|
|
da445255ec | ||
|
|
fb36aecf42 | ||
|
|
3d624eae97 | ||
|
|
0a05676a87 | ||
|
|
bce6ae0da6 | ||
|
|
2268465d2e | ||
|
|
7a9953cfee | ||
|
|
ecc11c120b |
11
.github/dependabot.yml
vendored
Normal file
11
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "gradle" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "daily"
|
||||
19
.github/workflows/android.yml
vendored
19
.github/workflows/android.yml
vendored
@@ -2,9 +2,13 @@ name: Android CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches:
|
||||
- master
|
||||
- staging
|
||||
- trying
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -13,17 +17,20 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- 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
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
- name: set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
- name: set up JDK 11
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: 1.8
|
||||
distribution: 'adopt'
|
||||
java-version: '11'
|
||||
- name: Build
|
||||
run: ./gradlew assembleRelease
|
||||
- name: Check lint
|
||||
run: ./gradlew lintRelease
|
||||
- name: Run unit tests
|
||||
run: ./gradlew testReleaseUnitTest
|
||||
run: ./gradlew testReleaseUnitTest || ./gradlew testReleaseUnitTest
|
||||
- name: SpotBugs
|
||||
run: ./gradlew spotbugsRelease
|
||||
- name: Archive test results
|
||||
|
||||
23
.github/workflows/autoclose-needs-info.yml
vendored
Normal file
23
.github/workflows/autoclose-needs-info.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: 'Close issues and PRs needing info for too long'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 1 * * *'
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v4
|
||||
with:
|
||||
days-before-stale: -1
|
||||
days-before-close: 90
|
||||
close-issue-message: 'This issue is missing necessary information and cannot be worked on in its current state. It has therefore been closed to keep the issue tracker clean. If you have more information, feel free to reopen it.'
|
||||
close-pr-message: 'This PR is missing necessary information and cannot be merged in its current state. It has therefore been closed to keep the issue tracker clean. If you have more information, feel free to reopen it.'
|
||||
only-labels: 'needs info'
|
||||
stale-issue-label: 'needs info'
|
||||
stale-pr-label: 'needs info'
|
||||
remove-stale-when-updated: false
|
||||
@@ -10,20 +10,23 @@ on:
|
||||
- '**.webp'
|
||||
jobs:
|
||||
build:
|
||||
# Only run on Pull Requests within the same repository, and not from forks.
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
name: calibreapp/image-actions
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@master
|
||||
uses: actions/checkout@v2
|
||||
- name: Compress Images
|
||||
id: calibre
|
||||
uses: calibreapp/image-actions@master
|
||||
uses: calibreapp/image-actions@1.1.0
|
||||
with:
|
||||
githubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
ignorePaths: 'app/src/test'
|
||||
compressOnly: true
|
||||
- name: Create New Pull Request If Needed
|
||||
if: steps.calibre.outputs.markdown != ''
|
||||
uses: peter-evans/create-pull-request@master
|
||||
uses: peter-evans/create-pull-request@v3
|
||||
with:
|
||||
title: Compressed Images
|
||||
branch-suffix: timestamp
|
||||
|
||||
26
.github/workflows/changelog-to-fastlane.yml
vendored
Normal file
26
.github/workflows/changelog-to-fastlane.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Convert CHANGELOG to Fastlane
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
convert_changelog_to_fastlane:
|
||||
runs-on: ubuntu-latest
|
||||
name: Convert CHANGELOG to Fastlane
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
id: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Run converter script
|
||||
run: python .scripts/changelog_to_fastlane.py
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v3
|
||||
with:
|
||||
title: "Update Fastlane changelogs"
|
||||
commit-message: "Update Fastlane changelogs"
|
||||
branch-suffix: timestamp
|
||||
73
.github/workflows/codeql-analysis.yml
vendored
Normal file
73
.github/workflows/codeql-analysis.yml
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches:
|
||||
- master
|
||||
schedule:
|
||||
- cron: '33 1 * * 4'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'java' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
# Learn more:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
25
.github/workflows/contributors-to-file.yml
vendored
Normal file
25
.github/workflows/contributors-to-file.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
name: Write contributors to file
|
||||
on:
|
||||
schedule:
|
||||
- cron: '3 4 * * 0'
|
||||
|
||||
jobs:
|
||||
contributors_to_file:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/master'
|
||||
name: Write contributors to file
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
id: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Update contributors
|
||||
id: update_contributors
|
||||
uses: TheLastProject/contributors-to-file-action@v2
|
||||
with:
|
||||
file_in_repo: app/src/main/res/raw/contributors.txt
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v3
|
||||
with:
|
||||
title: "Update contributors"
|
||||
commit-message: "Update contributors"
|
||||
branch-suffix: timestamp
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,3 +7,4 @@ build/
|
||||
captures/
|
||||
**/release
|
||||
**/debug
|
||||
app/*.log
|
||||
|
||||
28
.scripts/changelog_to_fastlane.py
Normal file
28
.scripts/changelog_to_fastlane.py
Normal file
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
changelogs = {}
|
||||
|
||||
with open('CHANGELOG.md') as changelog:
|
||||
version_code = None
|
||||
text = []
|
||||
|
||||
for line in changelog:
|
||||
if line.startswith("## "):
|
||||
if version_code != None:
|
||||
changelogs[version_code] = text
|
||||
|
||||
text = []
|
||||
match = re.match("## \S* - (\d*).*", line)
|
||||
if not match:
|
||||
raise ValueError(f"Invalid version line: {line}")
|
||||
version_code = match.group(1)
|
||||
elif line:
|
||||
# Turn Markdown [links](to_url) into links (to_url)
|
||||
text.append(re.sub(r'\[(.*?)\]\((.*?)\)', r'\1 (\2)', line))
|
||||
|
||||
for version, description in changelogs.items():
|
||||
with open(os.path.join("fastlane", "metadata", "android", "en-US", "changelogs", f"{version}.txt"), "w") as fastlane_file:
|
||||
fastlane_file.write("".join(description).strip())
|
||||
388
CHANGELOG.md
388
CHANGELOG.md
@@ -1,17 +1,138 @@
|
||||
# Changelog
|
||||
|
||||
## v1.14 (2021-06-07)
|
||||
## Unreleased - 89
|
||||
|
||||
Changes:
|
||||
- Fix swiping between groups not working on an empty group
|
||||
- Allow password-protecting exports
|
||||
- Improve usage of space for QR codes
|
||||
- Save the last used zoom level per card
|
||||
- Fix a crash when swiping right after a tap
|
||||
|
||||
## v2.7.3 - 88 (2021-10-10)
|
||||
|
||||
- Fix incorrect migration making first card become invisible
|
||||
|
||||
## v2.7.2 - 87 (2021-10-09)
|
||||
|
||||
- Fix regression breaking import/export
|
||||
|
||||
## v2.7.1 - 86 (2021-10-07)
|
||||
|
||||
- Improve search with spaces
|
||||
|
||||
## v2.7.0 - 85 (2021-10-05)
|
||||
|
||||
Android 4.4 is no longer supported starting with this release. If you want to use Catima on Android 4.4, please use version 2.6.1.
|
||||
|
||||
- Improved Android 12 support
|
||||
- Improved about screen
|
||||
- Search now ignores accents
|
||||
|
||||
## v2.6.1 - 84 (2021-09-25)
|
||||
|
||||
- Minor bugfixes and improvements
|
||||
|
||||
## v2.6.0 - 83 (2021-09-19)
|
||||
|
||||
- Support for changing the sorting order
|
||||
- Prevent Out Of Memory on scanning large pictures for barcode
|
||||
|
||||
## v2.5.0 - 82 (2021-09-10)
|
||||
|
||||
- Improved support for screen readers
|
||||
- Don't crash when trying to open a video from gallery
|
||||
- Swipe support on loyalty card view screen
|
||||
- Don't reset group on back button press
|
||||
|
||||
## v2.4.0 - 81 (2021-08-29)
|
||||
|
||||
- Improve card list for landscape and tablet display
|
||||
- Add theming colour support (thanks, Subhashish Anand!)
|
||||
- Don't close scan activity on camera error (so manual entry is still possible)
|
||||
- Add all contributors to the about dialog
|
||||
|
||||
## v2.3.0 - 80 (2021-08-19)
|
||||
|
||||
- Fix images not imported from backup
|
||||
- Option to override language
|
||||
|
||||
## v2.2.3 - 79 (2021-08-13)
|
||||
|
||||
- Fix widget creating different-looking shortcut than app shortcuts
|
||||
- Replace default Android black screen with splash screen
|
||||
|
||||
## v2.2.2 - 78 (2021-08-08)
|
||||
|
||||
- Fix crash on rotation in loyalty card edit activity
|
||||
|
||||
## v2.2.1 - 77 (2021-08-07)
|
||||
|
||||
- Improve Stocard importer
|
||||
- Fix importing Catima export with multiline note
|
||||
- Scale card title in acceptable range
|
||||
- Animation improvements
|
||||
|
||||
## v2.2.0 - 76 (2021-08-02)
|
||||
|
||||
- Make links in notes clickable
|
||||
- Pre-select group the user is currently in when creating a new card
|
||||
- Comma-separate group names in loyalty card view
|
||||
- Fix maximize button appearing on no barcode
|
||||
|
||||
## v2.1.0 - 75 (2021-08-01)
|
||||
|
||||
- Fix selected colour in colour changing dialog
|
||||
- Support for deleting multiple cards at once
|
||||
- Fix possible ArithmeticException when resizing image
|
||||
- Fix fullscreen is closed when rotating device
|
||||
|
||||
## v2.0.4 - 74 (2021-07-27)
|
||||
|
||||
- Fix shortcut creation
|
||||
- Generate card-specific shortcut icon
|
||||
- Fix ability to change loyalty card colour
|
||||
|
||||
## v2.0.3 - 73 (2021-07-25)
|
||||
|
||||
- Fix loading photos when editing existing card
|
||||
|
||||
## v2.0.2 - 72 (2021-07-25)
|
||||
|
||||
- Fix inability to configure photos in new loyalty card
|
||||
|
||||
## v2.0.1 - 71 (2021-07-21)
|
||||
|
||||
- Several minor translation and UI fixes
|
||||
- Fix crash in import/sharing loyalty card on Android 6
|
||||
|
||||
## v2.0 - 70 (2021-07-14)
|
||||
|
||||
- BREAKING CHANGE: The backup format changed, see https://github.com/TheLastProject/Catima/wiki/Export-format
|
||||
- BREAKING CHANGE: The URL sharing format changed, see https://github.com/TheLastProject/Catima/wiki/Card-sharing-URL-format
|
||||
- Make it possible to enable or disable the flashlight while scanning
|
||||
- Add UPC-E support
|
||||
- Support adding a front and back photo to each card
|
||||
- Support importing password-protected zip files
|
||||
- Support importing from Stocard (Beta)
|
||||
- Fix useless whitespace in notes from Fidme import
|
||||
- Support new Voucher Vault export format
|
||||
- Fix Floating Action Buttons being behind other UI elements on Android 4
|
||||
- Fix loyalty card viewer appbar top margin
|
||||
|
||||
## v1.14.1 - 69 (2021-06-14)
|
||||
|
||||
- Add missing barcode ID to export
|
||||
- Don't show update barcode dialog if value is the same as card ID
|
||||
- Add Finnish translation
|
||||
|
||||
## v1.14 - 68 (2021-06-07)
|
||||
|
||||
- Support new PDF417 export from Voucher Vault
|
||||
- Support copying multiple barcodes at once
|
||||
- Support sharing multiple loyalty cards at once
|
||||
- Ask to update barcode value if card ID changes
|
||||
|
||||
## v1.13 (2021-04-10)
|
||||
|
||||
Changes:
|
||||
## v1.13 - 67 (2021-04-10)
|
||||
|
||||
- Add option to set a separate barcode value from card ID
|
||||
- Simplify font sizing configuration
|
||||
@@ -20,175 +141,125 @@ Changes:
|
||||
- Always show all barcode types in manual entry
|
||||
- Remove privacy policy first start dialog
|
||||
|
||||
## v1.12 (2021-03-30)
|
||||
|
||||
Changes:
|
||||
## v1.12 - 66 (2021-03-30)
|
||||
|
||||
- Support importing [Fidme](https://play.google.com/store/apps/details?id=fr.snapp.fidme) exports
|
||||
- Allow importing a card from a picture stored in the user's Android gallery
|
||||
- Fix multiline note cutoff
|
||||
- Change "Thank you" text on privacy dialog to "Accept" because Huawei is overly pedantic
|
||||
|
||||
## v1.11 (2021-03-21)
|
||||
|
||||
Changes:
|
||||
## v1.11 - 64 (2021-03-21)
|
||||
|
||||
- Add privacy policy dialog on first start (required by Huawei)
|
||||
|
||||
## v1.10 (2021-03-07)
|
||||
|
||||
Changes:
|
||||
## v1.10 - 63 (2021-03-07)
|
||||
|
||||
- Support importing [Voucher Vault](https://github.com/tim-smart/vouchervault/) exports
|
||||
- Option to keep the screen on while viewing a loyalty card
|
||||
- Option to suspend the lock screen while viewing a loyalty card
|
||||
|
||||
## v1.9.2 (2021-02-24)
|
||||
|
||||
Changes:
|
||||
## v1.9.2 - 62 (2021-02-24)
|
||||
|
||||
- Fix parsing balance for countries using space as separator
|
||||
|
||||
## v1.9.1 (2021-02-23)
|
||||
|
||||
Changes:
|
||||
## v1.9.1 - 61 (2021-02-23)
|
||||
|
||||
- Improve balance parsing logic
|
||||
- Fix currency decimal display on main screen
|
||||
|
||||
## v1.9 (2021-02-22)
|
||||
|
||||
Changes:
|
||||
## v1.9 - 59 (2021-02-22)
|
||||
|
||||
- Add balance support
|
||||
- Reorganize barcode tab of edit view
|
||||
|
||||
## v1.8.1 (2021-02-12)
|
||||
|
||||
Changes:
|
||||
## v1.8.1 - 58 (2021-02-12)
|
||||
|
||||
- Fix Crash on versions before Android 7
|
||||
|
||||
## v1.8 (2021-01-28)
|
||||
|
||||
Changes:
|
||||
## v1.8 - 57 (2021-01-28)
|
||||
|
||||
- Add support for scaling the barcode when moving to top to fit even more small scanners
|
||||
- Fix bottom sheet jumping after switching to fullscreen
|
||||
- Make header in loyalty card view small in landscape mode
|
||||
- Fix cards not staying in group when group gets renamed
|
||||
|
||||
## v1.7.1 (2021-01-18)
|
||||
|
||||
Changes:
|
||||
## v1.7.1 - 56 (2021-01-18)
|
||||
|
||||
- Fix crash on switching to barcode tab in edit view if there is no barcode
|
||||
|
||||
## v1.7.0 (2021-01-18)
|
||||
|
||||
Changes:
|
||||
## v1.7.0 - 55 (2021-01-18)
|
||||
|
||||
- Separate edit UI in tabs to make it feel more spacious
|
||||
- Add expiry field support
|
||||
|
||||
## v1.6.2 (2021-01-04)
|
||||
|
||||
Changes:
|
||||
## v1.6.2 - 54 (2021-01-04)
|
||||
|
||||
- Fix edit button or more info bottom sheet drawing over barcode ID
|
||||
|
||||
## v1.6.1 (2020-12-16)
|
||||
|
||||
Changes:
|
||||
## v1.6.1 - 64 (2020-12-16)
|
||||
|
||||
- Fix regression causing manual barcode entry to not be saved
|
||||
|
||||
## v1.6.0 (2020-12-15)
|
||||
|
||||
Changes:
|
||||
## v1.6.0 - 52 (2020-12-15)
|
||||
|
||||
- Automatically focus text field when creating or editing a group
|
||||
- Fix blurry icons (use SVG everywhere)
|
||||
- Always open camera but add manual scan button to camera view
|
||||
|
||||
## v1.5.1 (2020-12-03)
|
||||
|
||||
Changes:
|
||||
## v1.5.1 - 51 (2020-12-03)
|
||||
|
||||
- Fix bottomsheet background being transparent
|
||||
|
||||
## v1.5.0 (2020-12-03)
|
||||
|
||||
Changes:
|
||||
## v1.5.0 - 50 (2020-12-03)
|
||||
|
||||
- Improve contrast by always using white text on red buttons
|
||||
- Draggable bottom sheet in loyalty card view
|
||||
|
||||
## v1.4.1 (2020-12-01)
|
||||
|
||||
Changes:
|
||||
## v1.4.1 - 49 (2020-12-01)
|
||||
|
||||
- Improved translations
|
||||
- Small UI fixes
|
||||
|
||||
## v1.4.0 (2020-11-28)
|
||||
|
||||
Changes:
|
||||
## v1.4.0 - 48 (2020-11-28)
|
||||
|
||||
- Move About screen into its own activity
|
||||
- Ask user if they want to use their camera or manually enter ID on add/edit card
|
||||
- Make group ordering manual instead of forced alphabetically
|
||||
|
||||
## v1.3.0 (2020-11-22)
|
||||
|
||||
Changes:
|
||||
## v1.3.0 - 47 (2020-11-22)
|
||||
|
||||
- Always show all import/export options and show a toast on actual issues (improves compat with XPrivacyLua)
|
||||
- Ask for confirmation when leaving edit view after making changes without saving
|
||||
|
||||
## v1.2.2 (2020-11-19)
|
||||
|
||||
Changes:
|
||||
## v1.2.2 - 46 (2020-11-19)
|
||||
|
||||
- Remember active group tab between screens and sessions
|
||||
|
||||
## v1.2.1 (2020-11-17)
|
||||
|
||||
Changes:
|
||||
## v1.2.1 - 45 (2020-11-17)
|
||||
|
||||
- Fix home screen swiping triggering during vertical swipes too
|
||||
|
||||
## v1.2.0 (2020-11-17)
|
||||
|
||||
Changes:
|
||||
## v1.2.0 - 44 (2020-11-17)
|
||||
|
||||
- Add swiping between groups on the home screen
|
||||
- Fix crash with cards lacking header colour
|
||||
|
||||
## v1.1.0 (2020-11-11)
|
||||
|
||||
Changes:
|
||||
## v1.1.0 - 43 (2020-11-11)
|
||||
|
||||
- Improved edit UI
|
||||
- Removed header text colour option (now automatically generated based on brightness)
|
||||
- Updated translations
|
||||
|
||||
## v1.0.1 (2020-11-07)
|
||||
|
||||
Changes:
|
||||
## v1.0.1 - 42 (2020-11-07)
|
||||
|
||||
- Fix crash in search with no groups
|
||||
|
||||
## v1.0 (2020-11-06)
|
||||
|
||||
Changes:
|
||||
## v1.0 - 41 (2020-11-06)
|
||||
|
||||
- Added rounded edges to card icons on main overview
|
||||
- Added support for grouping entries
|
||||
|
||||
## v0.29 (2020-10-29)
|
||||
|
||||
Changes:
|
||||
## v0.29 - 40 (2020-10-29)
|
||||
|
||||
- Rebrand to Catima
|
||||
- Removed intro
|
||||
@@ -197,31 +268,23 @@ Changes:
|
||||
- Add favourites support
|
||||
- Fix disabled auto-rotate being ignored
|
||||
|
||||
## v0.28 (2020-03-09)
|
||||
|
||||
Changes:
|
||||
## v0.28 - 39 (2020-03-09)
|
||||
|
||||
- Fix barcode centering when exiting full screen ([#351](https://github.com/brarcher/loyalty-card-locker/pull/351))
|
||||
- Allow backup export location to be selected ([#352](https://github.com/brarcher/loyalty-card-locker/pull/352))
|
||||
- Update translations ([#357](https://github.com/brarcher/loyalty-card-locker/pull/357)) & ([#362](https://github.com/brarcher/loyalty-card-locker/pull/362))
|
||||
|
||||
## v0.27 (2020-01-26)
|
||||
|
||||
Changes:
|
||||
## v0.27 - 38 (2020-01-26)
|
||||
|
||||
- Tapping on a barcode now moves it to the top of the screen ([#348](https://github.com/brarcher/loyalty-card-locker/pull/348))
|
||||
- Add white space around barcodes to improve scanning in dark mode ([#328](https://github.com/brarcher/loyalty-card-locker/issues/328))
|
||||
- Fix swapped import buttons. ([#346](https://github.com/brarcher/loyalty-card-locker/pull/346))
|
||||
|
||||
## v0.26.1 (2020-01-09)
|
||||
|
||||
Changes:
|
||||
## v0.26.1 - 37 (2020-01-09)
|
||||
|
||||
- Fix issue with sharing cards without background color ([#343](https://github.com/brarcher/loyalty-card-locker/pull/343))
|
||||
|
||||
## v0.26 (2020-01-05)
|
||||
|
||||
Changes:
|
||||
## v0.26 - 36 (2020-01-05)
|
||||
|
||||
- Add ability to search for a card ([#320](https://github.com/brarcher/loyalty-card-locker/pull/320))
|
||||
- Add ability to share and receive loyalty cards ([#321](https://github.com/brarcher/loyalty-card-locker/pull/321))
|
||||
@@ -238,57 +301,41 @@ Changes:
|
||||
- Polish
|
||||
- Russian
|
||||
|
||||
## v0.25.4 (2019-10-04)
|
||||
|
||||
Changes:
|
||||
## v0.25.4 - 35 (2019-10-04)
|
||||
|
||||
- Enable app backups
|
||||
- Update French and Slovenian translations
|
||||
|
||||
## v0.25.3 (2019-03-02)
|
||||
|
||||
Changes:
|
||||
## v0.25.3 - 34 (2019-03-02)
|
||||
|
||||
- Update Russian translations
|
||||
|
||||
## v0.25.2 (2019-01-05)
|
||||
|
||||
Changes:
|
||||
## v0.25.2 - 33 (2019-01-05)
|
||||
|
||||
- Update and add translations
|
||||
|
||||
## v0.25.1 (2018-10-14)
|
||||
|
||||
Changes:
|
||||
## v0.25.1 - 32 (2018-10-14)
|
||||
|
||||
- Fix creating new card by manually entering barcode ([issue #272](https://github.com/brarcher/loyalty-card-locker/issues/272))
|
||||
|
||||
## v0.25 (2018-10-07)
|
||||
|
||||
Changes:
|
||||
## v0.25 - 31 (2018-10-07)
|
||||
|
||||
- Sort card list case insensitive ([pull #266](https://github.com/brarcher/loyalty-card-locker/pull/266))
|
||||
- Add setting to lock orientation for all cards ([pull #269](https://github.com/brarcher/loyalty-card-locker/pull/269)
|
||||
|
||||
## v0.24 (2018-07-31)
|
||||
|
||||
Changes:
|
||||
## v0.24 - 30 (2018-07-31)
|
||||
|
||||
- Add a setting to control screen brightness when displaying a barcode ([pull #259](https://github.com/brarcher/loyalty-card-locker/pull/259))
|
||||
- Add Greek translations ([pull #252](https://github.com/brarcher/loyalty-card-locker/pull/252))
|
||||
- Add Slovenian translations ([pull #260](https://github.com/brarcher/loyalty-card-locker/pull/260))
|
||||
- Update translations ([pull #260](https://github.com/brarcher/loyalty-card-locker/pull/260), [pull #254](https://github.com/brarcher/loyalty-card-locker/pull/254))
|
||||
|
||||
## v0.23.4 (2018-05-12)
|
||||
|
||||
Changes:
|
||||
## v0.23.4 - 29 (2018-05-12)
|
||||
|
||||
- Fix Spanish translations ([pull #244](https://github.com/brarcher/loyalty-card-locker/pull/244))
|
||||
- Update translations ([pull #244](https://github.com/brarcher/loyalty-card-locker/pull/244))
|
||||
|
||||
## v0.23.3 (2018-05-05)
|
||||
|
||||
Changes:
|
||||
## v0.23.3 - 28 (2018-05-05)
|
||||
|
||||
- Added translations
|
||||
- Polish ([pull #232](https://github.com/brarcher/loyalty-card-locker/pull/232))
|
||||
@@ -296,87 +343,63 @@ Changes:
|
||||
- Slovak ([pull #232](https://github.com/brarcher/loyalty-card-locker/pull/232))
|
||||
- Updated translations ([pull #239](https://github.com/brarcher/loyalty-card-locker/pull/239))
|
||||
|
||||
## v0.23.2 (2018-03-11)
|
||||
|
||||
Changes:
|
||||
## v0.23.2 - 27 (2018-03-11)
|
||||
|
||||
- Reduce min SDK from 17 to 15. ([pull #226](https://github.com/brarcher/loyalty-card-locker/pull/226))
|
||||
- Remove usage of legacy apache library, used only in unit tests but no longer needed. ([pull #225](https://github.com/brarcher/loyalty-card-locker/pull/225))
|
||||
|
||||
## v0.23.1 (2018-03-07)
|
||||
|
||||
Changes:
|
||||
## v0.23.1 - 26 (2018-03-07)
|
||||
|
||||
- Prevent crash when rendering a barcode exhausts the application's memory. ([pull #219](https://github.com/brarcher/loyalty-card-locker/pull/219))
|
||||
|
||||
## v0.23 (2018-02-28)
|
||||
|
||||
Changes:
|
||||
## v0.23 - 25 (2018-02-28)
|
||||
|
||||
- Reduce space in header when viewing a card. ([pull #213](https://github.com/brarcher/loyalty-card-locker/pull/213))
|
||||
- Disable beep when scanning a barcode. ([pull #216](https://github.com/brarcher/loyalty-card-locker/pull/216))
|
||||
|
||||
## v0.22 (2018-02-19)
|
||||
|
||||
Changes:
|
||||
## v0.22 - 24 (2018-02-19)
|
||||
|
||||
- Update translations. ([pull #208](https://github.com/brarcher/loyalty-card-locker/pull/208))
|
||||
- Barcode rendering updates: ([pull #209](https://github.com/brarcher/loyalty-card-locker/pull/209))
|
||||
- Reload card view activity when screen is rotated, so barcode image is correct size.
|
||||
- Render 1D barcodes in a larger space, allowing them to better fill the screen.
|
||||
|
||||
## v0.21 (2018-02-17)
|
||||
|
||||
Changes:
|
||||
## v0.21 - 23 (2018-02-17)
|
||||
|
||||
- Add quiet space at the start/end of barcodes. ([pull #200](https://github.com/brarcher/loyalty-card-locker/pull/200))
|
||||
- Add options to configure the colors used for the store name font and background. ([pull #203](https://github.com/brarcher/loyalty-card-locker/pull/203))
|
||||
- Add options to adjust font sizes on the card listing page and single card page. ([pull #204](https://github.com/brarcher/loyalty-card-locker/pull/204))
|
||||
|
||||
## v0.20 (2018-02-10)
|
||||
|
||||
Changes:
|
||||
## v0.20 - 22 (2018-02-10)
|
||||
|
||||
- Changes to Card view to display the note, allow the card ID to take multiple lines, and show the store name. ([pull #197](https://github.com/brarcher/loyalty-card-locker/pull/197))
|
||||
|
||||
## v0.19 (2018-02-01)
|
||||
|
||||
Changes:
|
||||
## v0.19 - 21 (2018-02-01)
|
||||
|
||||
- Improved layout for card list. ([pull #188](https://github.com/brarcher/loyalty-card-locker/pull/188))
|
||||
- Improved layout when viewing a card. ([pull #190](https://github.com/brarcher/loyalty-card-locker/pull/190))
|
||||
|
||||
## v0.18.1 (2018-01-24)
|
||||
|
||||
Changes:
|
||||
## v0.18.1 - 20 (2018-01-24)
|
||||
|
||||
- Workaround crash during install on some Android versions (likely Android 5 and below). ([pull #184](https://github.com/brarcher/loyalty-card-locker/pull/184))
|
||||
|
||||
## v0.18 (2018-01-19)
|
||||
|
||||
Changes:
|
||||
## v0.18 - 19 (2018-01-19)
|
||||
|
||||
- Fix crash when importing certain types of corrupted CSV files. ([pull #177](https://github.com/brarcher/loyalty-card-locker/pull/177))
|
||||
- Fix importing backups directly from the file system. ([pull #180](https://github.com/brarcher/loyalty-card-locker/pull/180))
|
||||
- Fix importing backups from certain types of content providers. ([pull #179](https://github.com/brarcher/loyalty-card-locker/pull/179))
|
||||
|
||||
## v0.17 (2018-01-11)
|
||||
|
||||
Changes:
|
||||
## v0.17 - 18 (2018-01-11)
|
||||
|
||||
- Fix issue on Android SDK 24+ where using the file chooser import option would cause a crash. ([pull #170](https://github.com/brarcher/loyalty-card-locker/pull/170))
|
||||
- New icon and color scheme. ([pull #171](https://github.com/brarcher/loyalty-card-locker/pull/171))
|
||||
|
||||
## v0.16 (2017-11-29)
|
||||
|
||||
Changes:
|
||||
## v0.16 - 17 (2017-11-29)
|
||||
|
||||
- Add support for adding loyalty card shortcuts from the launcher/homescreen. ([pull #161](https://github.com/brarcher/loyalty-card-locker/pull/161))
|
||||
- Remove support for adding loyalty card shortcuts from the app itself. This removes the need for the shortcut permission. ([pull #163](https://github.com/brarcher/loyalty-card-locker/pull/163))
|
||||
|
||||
## v0.15 (2017-11-25)
|
||||
|
||||
Changes:
|
||||
## v0.15 - 16 (2017-11-25)
|
||||
|
||||
- Add support for adding shortcuts to home screen when adding or editing a card. ([pull #155](https://github.com/brarcher/loyalty-card-locker/pull/155))
|
||||
- Remove widget, as it was a poor substitute for shortcuts. ([pull #155](https://github.com/brarcher/loyalty-card-locker/pull/155))
|
||||
@@ -384,37 +407,27 @@ Changes:
|
||||
- Report more accurate mime type when exporting backup data. ([pull #156](https://github.com/brarcher/loyalty-card-locker/pull/156))
|
||||
- Fix bug where a card could not be edited. ([pull #155](https://github.com/brarcher/loyalty-card-locker/pull/155))
|
||||
|
||||
## v0.14 (2017-10-26)
|
||||
|
||||
Changes:
|
||||
## v0.14 - 15 (2017-10-26)
|
||||
|
||||
- Add support for app shortcuts (Android 7.1+), where the most recently used cards will appear as shortcuts. ([pull #145](https://github.com/brarcher/loyalty-card-locker/pull/145))
|
||||
- Add a widget which works like a pinned app shortcut, to support devices which run below Android 7.1. ([pull #142](https://github.com/brarcher/loyalty-card-locker/pull/142))
|
||||
|
||||
## v0.13 (2017-07-25)
|
||||
|
||||
Changes:
|
||||
## v0.13 - 14 (2017-07-25)
|
||||
|
||||
- Add screen rotation lock menu option when displaying a card. If locked, the screen will transition to its "natural" orientation and further screen rotation will be blocked. ([pull #128](https://github.com/brarcher/loyalty-card-locker/pull/128))
|
||||
- If a card is selected from the main screen but cannot be loaded, the application fails gracefully and posts a message. ([pull #132](https://github.com/brarcher/loyalty-card-locker/pull/132))
|
||||
- Fix case where layout IDs for intro wizard could not be found. ([pull #128](https://github.com/brarcher/loyalty-card-locker/pull/128))
|
||||
|
||||
## v0.12 (2017-07-16)
|
||||
|
||||
Changes:
|
||||
## v0.12 - 13 (2017-07-16)
|
||||
|
||||
- A change in v0.11 reduced the memory usage of barcode drawing, but affected the barcode dimensions. This is now changed to maintain the barcode dimensions while reducing memory usage. ([pull #126](https://github.com/brarcher/loyalty-card-locker/pull/126))
|
||||
- Update German and French translations. ([pull #122](https://github.com/brarcher/loyalty-card-locker/pull/122), [pull #124](https://github.com/brarcher/loyalty-card-locker/pull/124), [pull #125](https://github.com/brarcher/loyalty-card-locker/pull/125))
|
||||
|
||||
## v0.11.1 (2017-06-29)
|
||||
|
||||
Changes:
|
||||
## v0.11.1 - 12 (2017-06-29)
|
||||
|
||||
- Prevent a crash when rotation the screen in the first run intro wizard.
|
||||
|
||||
## v0.11 (2017-06-26)
|
||||
|
||||
Improvements:
|
||||
## v0.11 - 11 (2017-06-26)
|
||||
|
||||
- When editing a card ID, pre-populate the existing ID to start. ([pull #94](https://github.com/brarcher/loyalty-card-locker/pull/94))
|
||||
- Limit the width of generated barcodes to reduce memory usage and out of memory errors. ([pull #103](https://github.com/brarcher/loyalty-card-locker/pull/103))
|
||||
@@ -422,15 +435,13 @@ Improvements:
|
||||
- Change the color scheme to be softer and compatible with the app icon, and change the layout when viewing a card to be cleaner. ([pull #107](https://github.com/brarcher/loyalty-card-locker/pull/107))
|
||||
- Add an intro wizard which launches on the app's first launch. ([pull #108](https://github.com/brarcher/loyalty-card-locker/pull/108))
|
||||
|
||||
## v0.10 (2017-02-12)
|
||||
|
||||
Improvements:
|
||||
## v0.10 - 10 (2017-02-12)
|
||||
|
||||
- Changed the default import/export filename. ([pull #84](https://github.com/brarcher/loyalty-card-locker/pull/84))
|
||||
- Correct string on the import/export page. ([pull #87](https://github.com/brarcher/loyalty-card-locker/pull/87))
|
||||
- Improve layout of card view page. The text should be easier to read, and is selectable with a long click. ([pull #91](https://github.com/brarcher/loyalty-card-locker/pull/91))
|
||||
|
||||
## v0.9 (2017-01-17)
|
||||
## v0.9 - 9 (2017-01-17)
|
||||
|
||||
The "Locker" part of the name was not intuitive. To help remedy this a new application icon was created by betsythefc which better represents the purpose of the application: to store loyalty cards which use barcodes. Along with this new icon the name of the application has been changed to "Loyalty Card Keychain".
|
||||
|
||||
@@ -440,55 +451,36 @@ Additional features/improvements:
|
||||
- Translations for Lithuanian added. ([pull #62](https://github.com/brarcher/loyalty-card-locker/pull/62))
|
||||
- Translations for French added. ([pull #80](https://github.com/brarcher/loyalty-card-locker/pull/80))
|
||||
|
||||
## v0.8 (2016-11-22)
|
||||
|
||||
New features/improvements:
|
||||
## v0.8 - 8 (2016-11-22)
|
||||
|
||||
- Screen brightness increased to its maximum when displaying a card, to help barcode scanners successfully capture the barcode. ([pull #54](https://github.com/brarcher/loyalty-card-locker/pull/54))
|
||||
- Add a delete confirmation when deleting a card. ([pull #55](https://github.com/brarcher/loyalty-card-locker/pull/55))
|
||||
- Add translations for German ([pull #57](https://github.com/brarcher/loyalty-card-locker/pull/57)) and Czech ([pull #58](https://github.com/brarcher/loyalty-card-locker/pull/58)).
|
||||
- Clarification change for Italian translation. ([pull #66](https://github.com/brarcher/loyalty-card-locker/pull/66))
|
||||
|
||||
## v0.7 (2016-07-14)
|
||||
|
||||
New features/improvements:
|
||||
## v0.7 - 7 (2016-07-14)
|
||||
|
||||
- Long-click of a card brings up option to copy card ID to the clipboard. ([pull #49](https://github.com/brarcher/loyalty-card-locker/issues/49))
|
||||
|
||||
Bug fixes:
|
||||
|
||||
- Back button on Input/Export view now works, moving user to main view
|
||||
|
||||
## v0.6 (2016-05-23)
|
||||
|
||||
New features/improvements:
|
||||
## v0.6 - 6 (2016-05-23)
|
||||
|
||||
- Allow user to enter barcode manually. If a user elects to enter a barcode manually, a list of all valid and supported barcode images is displayed. The user then may select the barcode image which matches what the user wants. [issue #33](https://github.com/brarcher/loyalty-card-locker/issues/33), [pull #44](https://github.com/brarcher/loyalty-card-locker/pull/44)
|
||||
|
||||
Bug fixes:
|
||||
|
||||
- Resolve issue where some displayed barcodes were blurry. ([issue #37](https://github.com/brarcher/loyalty-card-locker/issues/37))
|
||||
|
||||
## v0.5 (2016-05-16)
|
||||
|
||||
New features/improvements:
|
||||
## v0.5 - 5 (2016-05-16)
|
||||
|
||||
- An about dialog can be opened from the main screen, which gives details about the application and project on GitHub ([issue #19](https://github.com/brarcher/loyalty-card-locker/issues/19))
|
||||
- Allow loyalty card information to be imported from/exported to a CSV file in external storage ([issue #36](https://github.com/brarcher/loyalty-card-locker/issues/36), [issue #20](https://github.com/brarcher/loyalty-card-locker/issues/20))
|
||||
|
||||
## v0.4 (2016-04-09)
|
||||
|
||||
New features/improvements:
|
||||
## v0.4 - 4 (2016-04-09)
|
||||
|
||||
- Dutch translation
|
||||
- Allow name field to be editable after adding loyalty card
|
||||
- Add an optional note field
|
||||
|
||||
Bug fixes:
|
||||
|
||||
- Resolve all issues identified by FindBugs and require all FindBugs issues be resolved prior to pull request acceptance
|
||||
|
||||
## v0.3 (2016-02-11)
|
||||
## v0.3 - 3 (2016-02-11)
|
||||
|
||||
- Now officially supports the following list of 1D and 2D barcodes:
|
||||
- AZTEC
|
||||
@@ -505,13 +497,13 @@ Bug fixes:
|
||||
|
||||
- Generated barcodes are larger, easier to scan from a scanning device
|
||||
|
||||
## v0.2 (2016-02-07)
|
||||
## v0.2 - 2 (2016-02-07)
|
||||
|
||||
- Italian translations
|
||||
- Support for all 1D barcode types. (Originally only product 1D barcodes were supported)
|
||||
- Add required camera permission, which was initially missing.
|
||||
|
||||
## v0.1 (2016-01-30)
|
||||
## v0.1 - 1 (2016-01-30)
|
||||
|
||||
- Ability to create/edit/delete loyalty cards
|
||||
- Capture barcode of loyalty card using a camera
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
How to Submit Patches to the Loyalty Card Keychain Project
|
||||
How to Submit Patches to the Catima Project
|
||||
===============================================================================
|
||||
https://github.com/brarcher/budget-watch
|
||||
https://github.com/TheLastProject/Catima
|
||||
|
||||
This document is intended to act as a guide to help you contribute to the
|
||||
Loyalty Card Keychain 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
|
||||
should have a much easier time getting your work merged with the upstream
|
||||
project.
|
||||
@@ -21,16 +21,16 @@ These are the Android lint checker, run using:
|
||||
|
||||
# ./gradlew lintRelease
|
||||
|
||||
and FindBugs, run using:
|
||||
and SpotBugs, run using:
|
||||
|
||||
# ./gradlew findbugs
|
||||
# ./gradlew spotbugsRelease
|
||||
|
||||
The final check is by testing the application on a live device and verifying
|
||||
the basic functionality works as expected.
|
||||
|
||||
## Make Sure Your Code is Tested
|
||||
|
||||
The Loyalty Card Keychain code uses a fair number of unit tests to verify that
|
||||
The Catima code uses a fair number of unit tests to verify that
|
||||
the basic functionality is working. Submissions which add functionality
|
||||
or significantly change the existing code should include additional tests
|
||||
to verify the proper operation of the proposed changes.
|
||||
@@ -85,7 +85,7 @@ your real name, saying:
|
||||
## Submit Patch(es) for Review
|
||||
|
||||
Finally, you will need to submit your patches so that they can be reviewed
|
||||
and potentially merged into the main Loyalty Card Keychain repository. The preferred
|
||||
way to do this is to submit a Pull Request to the Loyalty Card Keychain project.
|
||||
and potentially merged into the main Catima repository. The preferred
|
||||
way to do this is to submit a Pull Request to the Catima project.
|
||||
Changes need to apply cleanly onto the master branch and pass all
|
||||
unit tests and produce no errors during static analysis.
|
||||
|
||||
157
Gemfile.lock
157
Gemfile.lock
@@ -1,58 +1,75 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.2)
|
||||
addressable (2.7.0)
|
||||
CFPropertyList (3.0.3)
|
||||
addressable (2.8.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
artifactory (3.0.15)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.1.0)
|
||||
aws-partitions (1.388.0)
|
||||
aws-sdk-core (3.109.1)
|
||||
aws-eventstream (1.2.0)
|
||||
aws-partitions (1.501.0)
|
||||
aws-sdk-core (3.121.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.239.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
jmespath (~> 1.0)
|
||||
aws-sdk-kms (1.39.0)
|
||||
aws-sdk-core (~> 3, >= 3.109.0)
|
||||
aws-sdk-kms (1.48.0)
|
||||
aws-sdk-core (~> 3, >= 3.120.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.83.1)
|
||||
aws-sdk-core (~> 3, >= 3.109.0)
|
||||
aws-sdk-s3 (1.102.0)
|
||||
aws-sdk-core (~> 3, >= 3.120.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sigv4 (1.2.2)
|
||||
aws-sigv4 (~> 1.4)
|
||||
aws-sigv4 (1.4.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
babosa (1.0.4)
|
||||
claide (1.0.3)
|
||||
colored (1.2)
|
||||
colored2 (3.1.2)
|
||||
commander-fastlane (4.4.6)
|
||||
highline (~> 1.7.2)
|
||||
commander (4.6.0)
|
||||
highline (~> 2.0.0)
|
||||
declarative (0.0.20)
|
||||
declarative-option (0.1.0)
|
||||
digest-crc (0.6.1)
|
||||
rake (~> 13.0)
|
||||
digest-crc (0.6.4)
|
||||
rake (>= 12.0.0, < 14.0.0)
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.7.6)
|
||||
emoji_regex (3.2.0)
|
||||
excon (0.78.0)
|
||||
faraday (1.1.0)
|
||||
emoji_regex (3.2.2)
|
||||
excon (0.85.0)
|
||||
faraday (1.7.2)
|
||||
faraday-em_http (~> 1.0)
|
||||
faraday-em_synchrony (~> 1.0)
|
||||
faraday-excon (~> 1.1)
|
||||
faraday-httpclient (~> 1.0.1)
|
||||
faraday-net_http (~> 1.0)
|
||||
faraday-net_http_persistent (~> 1.1)
|
||||
faraday-patron (~> 1.0)
|
||||
faraday-rack (~> 1.0)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
ruby2_keywords
|
||||
ruby2_keywords (>= 0.0.4)
|
||||
faraday-cookie_jar (0.0.7)
|
||||
faraday (>= 0.8.0)
|
||||
http-cookie (~> 1.0.0)
|
||||
faraday_middleware (1.0.0)
|
||||
faraday-em_http (1.0.0)
|
||||
faraday-em_synchrony (1.0.0)
|
||||
faraday-excon (1.1.0)
|
||||
faraday-httpclient (1.0.1)
|
||||
faraday-net_http (1.0.1)
|
||||
faraday-net_http_persistent (1.2.0)
|
||||
faraday-patron (1.0.0)
|
||||
faraday-rack (1.0.0)
|
||||
faraday_middleware (1.1.0)
|
||||
faraday (~> 1.0)
|
||||
fastimage (2.2.0)
|
||||
fastlane (2.165.0)
|
||||
fastimage (2.2.5)
|
||||
fastlane (2.193.1)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.3, < 3.0.0)
|
||||
addressable (>= 2.8, < 3.0.0)
|
||||
artifactory (~> 3.0)
|
||||
aws-sdk-s3 (~> 1.0)
|
||||
babosa (>= 1.0.3, < 2.0.0)
|
||||
bundler (>= 1.12.0, < 3.0.0)
|
||||
colored
|
||||
commander-fastlane (>= 4.4.6, < 5.0.0)
|
||||
commander (~> 4.6)
|
||||
dotenv (>= 2.1.1, < 3.0.0)
|
||||
emoji_regex (>= 0.1, < 4.0)
|
||||
excon (>= 0.71.0, < 1.0.0)
|
||||
@@ -61,18 +78,20 @@ GEM
|
||||
faraday_middleware (~> 1.0)
|
||||
fastimage (>= 2.1.0, < 3.0.0)
|
||||
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||
google-api-client (>= 0.37.0, < 0.39.0)
|
||||
google-cloud-storage (>= 1.15.0, < 2.0.0)
|
||||
highline (>= 1.7.2, < 2.0.0)
|
||||
google-apis-androidpublisher_v3 (~> 0.3)
|
||||
google-apis-playcustomapp_v1 (~> 0.1)
|
||||
google-cloud-storage (~> 1.31)
|
||||
highline (~> 2.0)
|
||||
json (< 3.0.0)
|
||||
jwt (>= 2.1.0, < 3)
|
||||
mini_magick (>= 4.9.4, < 5.0.0)
|
||||
multipart-post (~> 2.0.0)
|
||||
naturally (~> 2.2)
|
||||
optparse (~> 0.1.1)
|
||||
plist (>= 3.1.0, < 4.0.0)
|
||||
rubyzip (>= 2.0.0, < 3.0.0)
|
||||
security (= 0.1.3)
|
||||
simctl (~> 1.6.3)
|
||||
slack-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-table (>= 1.4.5, < 2.0.0)
|
||||
tty-screen (>= 0.6.3, < 1.0.0)
|
||||
@@ -82,73 +101,85 @@ GEM
|
||||
xcpretty (~> 0.3.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3)
|
||||
gh_inspector (1.1.3)
|
||||
google-api-client (0.38.0)
|
||||
google-apis-androidpublisher_v3 (0.11.0)
|
||||
google-apis-core (>= 0.4, < 2.a)
|
||||
google-apis-core (0.4.1)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (~> 0.9)
|
||||
httpclient (>= 2.8.1, < 3.0)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
httpclient (>= 2.8.1, < 3.a)
|
||||
mini_mime (~> 1.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.0)
|
||||
signet (~> 0.12)
|
||||
google-cloud-core (1.5.0)
|
||||
retriable (>= 2.0, < 4.a)
|
||||
rexml
|
||||
webrick
|
||||
google-apis-iamcredentials_v1 (0.7.0)
|
||||
google-apis-core (>= 0.4, < 2.a)
|
||||
google-apis-playcustomapp_v1 (0.5.0)
|
||||
google-apis-core (>= 0.4, < 2.a)
|
||||
google-apis-storage_v1 (0.6.0)
|
||||
google-apis-core (>= 0.4, < 2.a)
|
||||
google-cloud-core (1.6.0)
|
||||
google-cloud-env (~> 1.0)
|
||||
google-cloud-errors (~> 1.0)
|
||||
google-cloud-env (1.4.0)
|
||||
google-cloud-env (1.5.0)
|
||||
faraday (>= 0.17.3, < 2.0)
|
||||
google-cloud-errors (1.0.1)
|
||||
google-cloud-storage (1.29.1)
|
||||
google-cloud-errors (1.1.0)
|
||||
google-cloud-storage (1.34.1)
|
||||
addressable (~> 2.5)
|
||||
digest-crc (~> 0.4)
|
||||
google-api-client (~> 0.33)
|
||||
google-cloud-core (~> 1.2)
|
||||
googleauth (~> 0.9)
|
||||
google-apis-iamcredentials_v1 (~> 0.1)
|
||||
google-apis-storage_v1 (~> 0.1)
|
||||
google-cloud-core (~> 1.6)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
mini_mime (~> 1.0)
|
||||
googleauth (0.14.0)
|
||||
googleauth (0.17.1)
|
||||
faraday (>= 0.17.3, < 2.0)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
memoist (~> 0.16)
|
||||
multi_json (~> 1.11)
|
||||
os (>= 0.9, < 2.0)
|
||||
signet (~> 0.14)
|
||||
highline (1.7.10)
|
||||
http-cookie (1.0.3)
|
||||
signet (~> 0.15)
|
||||
highline (2.0.3)
|
||||
http-cookie (1.0.4)
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.8.3)
|
||||
jmespath (1.4.0)
|
||||
json (2.3.1)
|
||||
jwt (2.2.2)
|
||||
json (2.5.1)
|
||||
jwt (2.2.3)
|
||||
memoist (0.16.2)
|
||||
mini_magick (4.10.1)
|
||||
mini_mime (1.0.2)
|
||||
mini_magick (4.11.0)
|
||||
mini_mime (1.1.1)
|
||||
multi_json (1.15.0)
|
||||
multipart-post (2.0.0)
|
||||
nanaimo (0.3.0)
|
||||
naturally (2.2.0)
|
||||
naturally (2.2.1)
|
||||
optparse (0.1.1)
|
||||
os (1.1.1)
|
||||
plist (3.5.0)
|
||||
plist (3.6.0)
|
||||
public_suffix (4.0.6)
|
||||
rake (13.0.1)
|
||||
representable (3.0.4)
|
||||
rake (13.0.6)
|
||||
representable (3.1.1)
|
||||
declarative (< 0.1.0)
|
||||
declarative-option (< 0.2.0)
|
||||
trailblazer-option (>= 0.1.1, < 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
retriable (3.1.2)
|
||||
rexml (3.2.5)
|
||||
rouge (2.0.7)
|
||||
ruby2_keywords (0.0.2)
|
||||
rubyzip (2.3.0)
|
||||
ruby2_keywords (0.0.5)
|
||||
rubyzip (2.3.2)
|
||||
security (0.1.3)
|
||||
signet (0.14.0)
|
||||
addressable (~> 2.3)
|
||||
signet (0.16.0)
|
||||
addressable (~> 2.8)
|
||||
faraday (>= 0.17.3, < 2.0)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
multi_json (~> 1.10)
|
||||
simctl (1.6.8)
|
||||
CFPropertyList
|
||||
naturally
|
||||
slack-notifier (2.3.2)
|
||||
terminal-notifier (2.0.0)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
trailblazer-option (0.1.1)
|
||||
tty-cursor (0.7.1)
|
||||
tty-screen (0.8.1)
|
||||
tty-spinner (0.9.3)
|
||||
@@ -156,18 +187,20 @@ GEM
|
||||
uber (0.1.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.7)
|
||||
unf_ext (0.0.8)
|
||||
unicode-display_width (1.7.0)
|
||||
webrick (1.7.0)
|
||||
word_wrap (1.0.0)
|
||||
xcodeproj (1.19.0)
|
||||
xcodeproj (1.21.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.3.0)
|
||||
rexml (~> 3.2.4)
|
||||
xcpretty (0.3.0)
|
||||
rouge (~> 2.0.7)
|
||||
xcpretty-travis-formatter (1.0.0)
|
||||
xcpretty-travis-formatter (1.0.1)
|
||||
xcpretty (~> 0.2, >= 0.0.7)
|
||||
|
||||
PLATFORMS
|
||||
|
||||
@@ -11,17 +11,18 @@ spotbugs {
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion "30.0.3"
|
||||
compileSdkVersion 31
|
||||
buildToolsVersion "31.0.0"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "me.hackerchick.catima"
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 29
|
||||
versionCode 68
|
||||
versionName "1.14"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 31
|
||||
versionCode 89
|
||||
versionName "2.8.0"
|
||||
|
||||
vectorDrawables.useSupportLibrary true
|
||||
multiDexEnabled true
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
@@ -36,8 +37,18 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
bundle {
|
||||
language {
|
||||
enableSplit = false
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
encoding "UTF-8"
|
||||
|
||||
// Flag to enable support for the new language APIs
|
||||
coreLibraryDesugaringEnabled true
|
||||
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
@@ -47,6 +58,12 @@ android {
|
||||
"MissingTranslation", "MissingPrefix"
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -63,29 +80,31 @@ android {
|
||||
|
||||
dependencies {
|
||||
// AndroidX
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'androidx.cardview:cardview:1.0.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
|
||||
implementation 'androidx.exifinterface:exifinterface:1.3.3'
|
||||
implementation 'androidx.preference:preference:1.1.1'
|
||||
implementation 'com.google.android.material:material:1.3.0'
|
||||
implementation 'com.google.android.material:material:1.4.0'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||
|
||||
// Splash Screen
|
||||
implementation 'androidx.core:core-splashscreen:1.0.0-alpha02'
|
||||
|
||||
// Third-party
|
||||
implementation 'com.journeyapps:zxing-android-embedded:4.1.0@aar'
|
||||
// Do not upgrade past 3.3.3! Causes a crash on versions before Android Nougat
|
||||
//noinspection GradleDependency
|
||||
implementation 'com.google.zxing:core:3.3.3'
|
||||
implementation 'org.apache.commons:commons-csv:1.8'
|
||||
implementation 'com.journeyapps:zxing-android-embedded:4.2.0@aar'
|
||||
implementation 'com.google.zxing:core:3.4.1'
|
||||
implementation 'org.apache.commons:commons-csv:1.9.0'
|
||||
implementation 'com.jaredrummler:colorpicker:1.1.0'
|
||||
implementation 'com.google.guava:guava:30.1.1-jre'
|
||||
implementation 'com.github.invissvenska:NumberPickerPreference:1.0.2'
|
||||
implementation 'com.github.invissvenska:NumberPickerPreference:1.0.3'
|
||||
implementation 'net.lingala.zip4j:zip4j:2.9.0'
|
||||
|
||||
// SpotBugs
|
||||
implementation 'io.wcm.tooling.spotbugs:io.wcm.tooling.spotbugs.annotations:1.0.0'
|
||||
|
||||
// Testing
|
||||
testImplementation 'androidx.test:core:1.3.0'
|
||||
testImplementation 'androidx.test:core:1.4.0'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'org.robolectric:robolectric:4.4'
|
||||
testImplementation 'org.robolectric:robolectric:4.6.1'
|
||||
}
|
||||
|
||||
tasks.withType(SpotBugsTask) {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
android:name="android.hardware.camera.autofocus"
|
||||
android:required="false" />
|
||||
|
||||
<uses-sdk tools:overrideLibrary="com.google.zxing.client.android" />
|
||||
<uses-sdk tools:overrideLibrary="com.google.zxing.client.android"/>
|
||||
|
||||
<application
|
||||
android:name=".LoyaltyCardLockerApplication"
|
||||
@@ -29,13 +29,15 @@
|
||||
<activity
|
||||
android:name="protect.card_locker.MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme.NoActionBar">
|
||||
android:theme="@style/Theme.App.Starting"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".AboutActivity"
|
||||
android:label="@string/about"
|
||||
@@ -56,17 +58,29 @@
|
||||
android:theme="@style/AppTheme.NoActionBar"
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:exported="true">
|
||||
<intent-filter android:autoVerify="true" android:label="@string/app_name">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<!-- Main card sharing URIs -->
|
||||
<data android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
|
||||
<data android:host="@string/intent_import_card_from_url_host_catima_app"
|
||||
android:pathPrefix="@string/intent_import_card_from_url_path_prefix_catima_app" />
|
||||
</intent-filter>
|
||||
<intent-filter android:label="@string/app_name">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<!-- Accepts URIs that begin with "https://github.com/brarcher/loyalty-card-locker/” -->
|
||||
<data android:scheme="https"
|
||||
android:host="@string/intent_import_card_from_url_host"
|
||||
android:pathPrefix="@string/intent_import_card_from_url_path_prefix" />
|
||||
<data android:scheme="https"
|
||||
android:host="@string/intent_import_card_from_url_host_old"
|
||||
android:pathPrefix="@string/intent_import_card_from_url_path_prefix_old" />
|
||||
<!-- Old card sharing URIs -->
|
||||
<data android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
|
||||
<data android:host="@string/intent_import_card_from_url_host_thelastproject"
|
||||
android:pathPrefix="@string/intent_import_card_from_url_path_prefix_thelastproject" />
|
||||
<data android:host="@string/intent_import_card_from_url_host_brarcher"
|
||||
android:pathPrefix="@string/intent_import_card_from_url_path_prefix_brarcher" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
@@ -80,7 +94,8 @@
|
||||
android:windowSoftInputMode="stateHidden"/>
|
||||
<activity
|
||||
android:name=".preferences.SettingsActivity"
|
||||
android:label="@string/settings"/>
|
||||
android:label="@string/settings"
|
||||
android:theme="@style/AppTheme.NoActionBar"/>
|
||||
<activity
|
||||
android:name=".ImportExportActivity"
|
||||
android:label="@string/importExport"
|
||||
@@ -88,7 +103,8 @@
|
||||
<activity
|
||||
android:name=".CardShortcutConfigure"
|
||||
android:label="@string/cardShortcut"
|
||||
android:theme="@style/AppTheme.NoActionBar">
|
||||
android:theme="@style/AppTheme.NoActionBar"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.CREATE_SHORTCUT"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
|
||||
@@ -1,30 +1,39 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.core.text.HtmlCompat;
|
||||
|
||||
public class AboutActivity extends AppCompatActivity
|
||||
public class AboutActivity extends CatimaAppCompatActivity implements View.OnClickListener
|
||||
{
|
||||
private static final String TAG = "Catima";
|
||||
ConstraintLayout version_history, translate, license, repo, privacy, error, credits, rate;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
setTitle(R.string.about);
|
||||
setContentView(R.layout.about_activity);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
@@ -34,13 +43,31 @@ public class AboutActivity extends AppCompatActivity
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
StringBuilder contributors = new StringBuilder().append("<br/>");
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(getResources().openRawResource(R.raw.contributors), StandardCharsets.UTF_8));
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
String tmp = reader.readLine();
|
||||
|
||||
if (tmp == null || tmp.isEmpty()) {
|
||||
reader.close();
|
||||
break;
|
||||
}
|
||||
|
||||
contributors.append("<br/>");
|
||||
contributors.append(tmp);
|
||||
}
|
||||
} catch (IOException ignored) {}
|
||||
|
||||
final List<ThirdPartyInfo> USED_LIBRARIES = new ArrayList<>();
|
||||
USED_LIBRARIES.add(new ThirdPartyInfo("Color Picker", "https://github.com/jaredrummler/ColorPicker", "Apache 2.0"));
|
||||
USED_LIBRARIES.add(new ThirdPartyInfo("Commons CSV", "https://commons.apache.org/proper/commons-csv/", "Apache 2.0"));
|
||||
USED_LIBRARIES.add(new ThirdPartyInfo("Guava", "https://github.com/google/guava", "Apache 2.0"));
|
||||
USED_LIBRARIES.add(new ThirdPartyInfo("NumberPickerPreference", "https://github.com/invissvenska/NumberPickerPreference", "GNU LGPL 3.0"));
|
||||
USED_LIBRARIES.add(new ThirdPartyInfo("Zip4j", "https://github.com/srikanth-lingala/zip4j", "Apache 2.0"));
|
||||
USED_LIBRARIES.add(new ThirdPartyInfo("ZXing", "https://github.com/zxing/zxing", "Apache 2.0"));
|
||||
USED_LIBRARIES.add(new ThirdPartyInfo("ZXing Android Embedded", "https://github.com/journeyapps/zxing-android-embedded", "Apache 2.0"));
|
||||
USED_LIBRARIES.add(new ThirdPartyInfo("Color Picker", "https://github.com/jaredrummler/ColorPicker", "Apache 2.0"));
|
||||
USED_LIBRARIES.add(new ThirdPartyInfo("NumberPickerPreference", "https://github.com/invissvenska/NumberPickerPreference", "GNU LGPL 3.0"));
|
||||
|
||||
final List<ThirdPartyInfo> USED_ASSETS = new ArrayList<>();
|
||||
USED_ASSETS.add(new ThirdPartyInfo("Android icons", "https://fonts.google.com/icons?selected=Material+Icons", "Apache 2.0"));
|
||||
@@ -48,13 +75,13 @@ public class AboutActivity extends AppCompatActivity
|
||||
StringBuilder libs = new StringBuilder().append("<br/>");
|
||||
for (ThirdPartyInfo entry : USED_LIBRARIES)
|
||||
{
|
||||
libs.append("<br/><a href=\"").append(entry.url()).append("\">").append(entry.name()).append("</a> (").append(entry.license()).append(")<br/>");
|
||||
libs.append("<br/><a href=\"").append(entry.url()).append("\">").append(entry.name()).append("</a> (").append(entry.license()).append(")");
|
||||
}
|
||||
|
||||
StringBuilder resources = new StringBuilder().append("<br/>");
|
||||
for (ThirdPartyInfo entry : USED_ASSETS)
|
||||
{
|
||||
resources.append("<br/><a href=\"").append(entry.url()).append("\">").append(entry.name()).append("</a> (").append(entry.license()).append(")<br/>");
|
||||
resources.append("<br/><a href=\"").append(entry.url()).append("\">").append(entry.name()).append("</a> (").append(entry.license()).append(")");
|
||||
}
|
||||
|
||||
String appName = getString(R.string.app_name);
|
||||
@@ -71,37 +98,82 @@ public class AboutActivity extends AppCompatActivity
|
||||
Log.w(TAG, "Package name not found", e);
|
||||
}
|
||||
|
||||
TextView copyright = findViewById(R.id.credits_sub);
|
||||
copyright.setText(String.format(getString(R.string.app_copyright_fmt), year));
|
||||
TextView vHistory = findViewById(R.id.version_history_sub);
|
||||
vHistory.setText(String.format(getString(R.string.debug_version_fmt), version));
|
||||
|
||||
setTitle(String.format(getString(R.string.about_title_fmt), appName));
|
||||
|
||||
TextView aboutTextView = findViewById(R.id.aboutText);
|
||||
aboutTextView.setText(HtmlCompat.fromHtml(String.format(getString(R.string.debug_version_fmt), version) +
|
||||
"<br/><br/>" +
|
||||
String.format(getString(R.string.app_revision_fmt),
|
||||
"<a href=\"" + getString(R.string.app_revision_url) + "\">" +
|
||||
"GitHub" +
|
||||
"</a>") +
|
||||
"<br/><br/>" +
|
||||
String.format(getString(R.string.app_copyright_fmt), year) +
|
||||
"<br/><br/>" +
|
||||
getString(R.string.app_copyright_old) +
|
||||
"<br/><br/>" +
|
||||
getString(R.string.app_license) +
|
||||
"<br/><br/>" +
|
||||
String.format(getString(R.string.app_libraries), libs.toString()) +
|
||||
"<br/><br/>" +
|
||||
String.format(getString(R.string.app_resources), resources.toString()), HtmlCompat.FROM_HTML_MODE_COMPACT));
|
||||
aboutTextView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
version_history = findViewById(R.id.version_history);
|
||||
translate = findViewById(R.id.translate);
|
||||
license = findViewById(R.id.license);
|
||||
repo = findViewById(R.id.repo);
|
||||
privacy = findViewById(R.id.privacy);
|
||||
error = findViewById(R.id.report_error);
|
||||
credits = findViewById(R.id.credits);
|
||||
rate = findViewById(R.id.rate);
|
||||
|
||||
version_history.setOnClickListener(this);
|
||||
translate.setOnClickListener(this);
|
||||
license.setOnClickListener(this);
|
||||
repo.setOnClickListener(this);
|
||||
privacy.setOnClickListener(this);
|
||||
error.setOnClickListener(this);
|
||||
rate.setOnClickListener(this);
|
||||
|
||||
StringBuilder contributorInfo = new StringBuilder();
|
||||
contributorInfo.append(HtmlCompat.fromHtml(String.format(getString(R.string.app_contributors), contributors.toString()), HtmlCompat.FROM_HTML_MODE_COMPACT));
|
||||
contributorInfo.append("\n\n");
|
||||
contributorInfo.append(getString(R.string.app_copyright_old));
|
||||
contributorInfo.append("\n\n");
|
||||
contributorInfo.append(HtmlCompat.fromHtml(String.format(getString(R.string.app_libraries), libs.toString()), HtmlCompat.FROM_HTML_MODE_COMPACT));
|
||||
contributorInfo.append("\n\n");
|
||||
contributorInfo.append(HtmlCompat.fromHtml(String.format(getString(R.string.app_resources), resources.toString()), HtmlCompat.FROM_HTML_MODE_COMPACT));
|
||||
|
||||
credits.setOnClickListener(view -> new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.credits)
|
||||
.setMessage(contributorInfo.toString())
|
||||
.setPositiveButton(R.string.ok, (dialogInterface, i) -> {})
|
||||
.show());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item)
|
||||
{
|
||||
int id = item.getItemId();
|
||||
|
||||
if (id == android.R.id.home) {
|
||||
finish();
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
int id = view.getId();
|
||||
|
||||
String url;
|
||||
if (id == R.id.version_history) {
|
||||
url = "https://catima.app/changelog/";
|
||||
} else if (id == R.id.translate) {
|
||||
url = "https://hosted.weblate.org/engage/catima/";
|
||||
} else if (id == R.id.license) {
|
||||
url = "https://github.com/TheLastProject/Catima/blob/master/LICENSE";
|
||||
} else if (id == R.id.repo) {
|
||||
url = "https://github.com/TheLastProject/Catima/";
|
||||
} else if (id == R.id.privacy) {
|
||||
url = "https://catima.app/privacy-policy/";
|
||||
} else if (id == R.id.report_error) {
|
||||
url = "https://github.com/TheLastProject/Catima/issues";
|
||||
} else if (id == R.id.rate) {
|
||||
url = "https://play.google.com/store/apps/details?id=me.hackerchick.catima";
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,31 +1,32 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.MultiFormatWriter;
|
||||
import com.google.zxing.WriterException;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import protect.card_locker.async.CompatCallable;
|
||||
|
||||
/**
|
||||
* This task will generate a barcode and load it into an ImageView.
|
||||
* Only a weak reference of the ImageView is kept, so this class will not
|
||||
* prevent the ImageView from being garbage collected.
|
||||
*/
|
||||
class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
|
||||
{
|
||||
public class BarcodeImageWriterTask implements CompatCallable<Bitmap> {
|
||||
private static final String TAG = "Catima";
|
||||
|
||||
private static final int IS_VALID = 999;
|
||||
private final Context mContext;
|
||||
private boolean isSuccesful;
|
||||
|
||||
// When drawn in a smaller window 1D barcodes for some reason end up
|
||||
@@ -36,16 +37,19 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
|
||||
private final WeakReference<ImageView> imageViewReference;
|
||||
private final WeakReference<TextView> textViewReference;
|
||||
private String cardId;
|
||||
private final BarcodeFormat format;
|
||||
private final CatimaBarcode format;
|
||||
private final int imageHeight;
|
||||
private final int imageWidth;
|
||||
private final boolean showFallback;
|
||||
private final Runnable callback;
|
||||
|
||||
BarcodeImageWriterTask(ImageView imageView, String cardIdString,
|
||||
BarcodeFormat barcodeFormat, TextView textView,
|
||||
boolean showFallback, Runnable callback)
|
||||
{
|
||||
BarcodeImageWriterTask(
|
||||
Context context, ImageView imageView, String cardIdString,
|
||||
CatimaBarcode barcodeFormat, TextView textView,
|
||||
boolean showFallback, Runnable callback
|
||||
) {
|
||||
mContext = context;
|
||||
|
||||
isSuccesful = true;
|
||||
this.callback = callback;
|
||||
|
||||
@@ -58,26 +62,21 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
|
||||
|
||||
final int MAX_WIDTH = getMaxWidth(format);
|
||||
|
||||
if(imageView.getWidth() < MAX_WIDTH)
|
||||
{
|
||||
if (imageView.getWidth() < MAX_WIDTH) {
|
||||
imageHeight = imageView.getHeight();
|
||||
imageWidth = imageView.getWidth();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Scale down the image to reduce the memory needed to produce it
|
||||
imageWidth = MAX_WIDTH;
|
||||
double ratio = (double)MAX_WIDTH / (double)imageView.getWidth();
|
||||
imageHeight = (int)(imageView.getHeight() * ratio);
|
||||
double ratio = (double) MAX_WIDTH / (double) imageView.getWidth();
|
||||
imageHeight = (int) (imageView.getHeight() * ratio);
|
||||
}
|
||||
|
||||
this.showFallback = showFallback;
|
||||
}
|
||||
|
||||
private int getMaxWidth(BarcodeFormat format)
|
||||
{
|
||||
switch(format)
|
||||
{
|
||||
private int getMaxWidth(CatimaBarcode format) {
|
||||
switch (format.format()) {
|
||||
// 2D barcodes
|
||||
case AZTEC:
|
||||
case DATA_MATRIX:
|
||||
@@ -104,10 +103,8 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
|
||||
}
|
||||
}
|
||||
|
||||
private String getFallbackString(BarcodeFormat format)
|
||||
{
|
||||
switch(format)
|
||||
{
|
||||
private String getFallbackString(CatimaBarcode format) {
|
||||
switch (format.format()) {
|
||||
// 2D barcodes
|
||||
case AZTEC:
|
||||
return "AZTEC";
|
||||
@@ -133,28 +130,24 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
|
||||
return "1003";
|
||||
case UPC_A:
|
||||
return "123456789012";
|
||||
case UPC_E:
|
||||
return "0123456";
|
||||
default:
|
||||
throw new IllegalArgumentException("No fallback known for this barcode type");
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap generate()
|
||||
{
|
||||
if (cardId.isEmpty())
|
||||
{
|
||||
private Bitmap generate() {
|
||||
if (cardId.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MultiFormatWriter writer = new MultiFormatWriter();
|
||||
BitMatrix bitMatrix;
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
bitMatrix = writer.encode(cardId, format, imageWidth, imageHeight, null);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
try {
|
||||
try {
|
||||
bitMatrix = writer.encode(cardId, format.format(), imageWidth, imageHeight, null);
|
||||
} catch (Exception e) {
|
||||
// Cast a wider net here and catch any exception, as there are some
|
||||
// cases where an encoder may fail if the data is invalid for the
|
||||
// barcode type. If this happens, we want to fail gracefully.
|
||||
@@ -169,11 +162,9 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
|
||||
|
||||
int[] pixels = new int[bitMatrixWidth * bitMatrixHeight];
|
||||
|
||||
for (int y = 0; y < bitMatrixHeight; y++)
|
||||
{
|
||||
for (int y = 0; y < bitMatrixHeight; y++) {
|
||||
int offset = y * bitMatrixWidth;
|
||||
for (int x = 0; x < bitMatrixWidth; x++)
|
||||
{
|
||||
for (int x = 0; x < bitMatrixWidth; x++) {
|
||||
int color = bitMatrix.get(x, y) ? BLACK : WHITE;
|
||||
pixels[offset + x] = color;
|
||||
}
|
||||
@@ -193,19 +184,14 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
|
||||
int widthScale = imageWidth / bitMatrixHeight;
|
||||
int scalingFactor = Math.min(heightScale, widthScale);
|
||||
|
||||
if(scalingFactor > 1)
|
||||
{
|
||||
if (scalingFactor > 1) {
|
||||
bitmap = Bitmap.createScaledBitmap(bitmap, bitMatrixWidth * scalingFactor, bitMatrixHeight * scalingFactor, false);
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
catch (WriterException e)
|
||||
{
|
||||
} catch (WriterException e) {
|
||||
Log.e(TAG, "Failed to generate barcode of type " + format + ": " + cardId, e);
|
||||
}
|
||||
catch(OutOfMemoryError e)
|
||||
{
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.w(TAG, "Insufficient memory to render barcode, "
|
||||
+ imageWidth + "x" + imageHeight + ", " + format.name()
|
||||
+ ", length=" + cardId.length(), e);
|
||||
@@ -214,40 +200,49 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
|
||||
return null;
|
||||
}
|
||||
|
||||
public Bitmap doInBackground(Void... params)
|
||||
{
|
||||
Bitmap bitmap = generate();
|
||||
public Bitmap doInBackground(Void... params) {
|
||||
// Only do the hard tasks if we've not already been cancelled
|
||||
if (!Thread.currentThread().isInterrupted()) {
|
||||
Bitmap bitmap = generate();
|
||||
|
||||
if (bitmap == null) {
|
||||
isSuccesful = false;
|
||||
if (bitmap == null) {
|
||||
isSuccesful = false;
|
||||
|
||||
if (showFallback) {
|
||||
Log.i(TAG, "Barcode generation failed, generating fallback...");
|
||||
cardId = getFallbackString(format);
|
||||
bitmap = generate();
|
||||
if (showFallback && !Thread.currentThread().isInterrupted()) {
|
||||
Log.i(TAG, "Barcode generation failed, generating fallback...");
|
||||
cardId = getFallbackString(format);
|
||||
bitmap = generate();
|
||||
return bitmap;
|
||||
}
|
||||
} else {
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
// We've been interrupted - create a empty fallback
|
||||
Bitmap.Config config = Bitmap.Config.ARGB_8888;
|
||||
return Bitmap.createBitmap(imageWidth, imageHeight, config);
|
||||
}
|
||||
|
||||
protected void onPostExecute(Bitmap result)
|
||||
{
|
||||
public void onPostExecute(Object castResult) {
|
||||
Bitmap result = (Bitmap) castResult;
|
||||
|
||||
Log.i(TAG, "Finished generating barcode image of type " + format + ": " + cardId);
|
||||
ImageView imageView = imageViewReference.get();
|
||||
if(imageView == null)
|
||||
{
|
||||
if (imageView == null) {
|
||||
// The ImageView no longer exists, nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
String formatPrettyName = format.prettyName();
|
||||
|
||||
imageView.setTag(isSuccesful);
|
||||
|
||||
imageView.setImageBitmap(result);
|
||||
imageView.setContentDescription(mContext.getString(R.string.barcodeImageDescriptionWithType, formatPrettyName));
|
||||
TextView textView = textViewReference.get();
|
||||
|
||||
if(result != null)
|
||||
{
|
||||
if (result != null) {
|
||||
Log.i(TAG, "Displaying barcode");
|
||||
imageView.setVisibility(View.VISIBLE);
|
||||
|
||||
@@ -259,11 +254,9 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
|
||||
|
||||
if (textView != null) {
|
||||
textView.setVisibility(View.VISIBLE);
|
||||
textView.setText(format.name());
|
||||
textView.setText(formatPrettyName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Log.i(TAG, "Barcode generation failed, removing image from display");
|
||||
imageView.setVisibility(View.GONE);
|
||||
if (textView != null) {
|
||||
@@ -275,4 +268,19 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
|
||||
callback.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPreExecute() {
|
||||
// No Action
|
||||
}
|
||||
|
||||
/**
|
||||
* Provided to comply with Callable while keeping the original Syntax of AsyncTask
|
||||
*
|
||||
* @return generated Bitmap
|
||||
*/
|
||||
@Override
|
||||
public Bitmap call() {
|
||||
return doInBackground();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,9 @@ package protect.card_locker;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.MenuItem;
|
||||
@@ -16,115 +15,88 @@ import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
import protect.card_locker.async.TaskHandler;
|
||||
|
||||
/**
|
||||
* This activity is callable and will allow a user to enter
|
||||
* barcode data and generate all barcodes possible for
|
||||
* the data. The user may then select any barcode, where its
|
||||
* data and type will be returned to the caller.
|
||||
*/
|
||||
public class BarcodeSelectorActivity extends AppCompatActivity
|
||||
{
|
||||
public class BarcodeSelectorActivity extends CatimaAppCompatActivity {
|
||||
private static final String TAG = "Catima";
|
||||
|
||||
// Result this activity will return
|
||||
public static final String BARCODE_CONTENTS = "contents";
|
||||
public static final String BARCODE_FORMAT = "format";
|
||||
|
||||
// These are all the barcode types that the zxing library
|
||||
// is able to generate a barcode for, and thus should be
|
||||
// the only barcodes which we should attempt to scan.
|
||||
public static final Collection<String> SUPPORTED_BARCODE_TYPES = Collections.unmodifiableList(
|
||||
Arrays.asList(
|
||||
BarcodeFormat.AZTEC.name(),
|
||||
BarcodeFormat.CODE_39.name(),
|
||||
BarcodeFormat.CODE_128.name(),
|
||||
BarcodeFormat.CODABAR.name(),
|
||||
BarcodeFormat.DATA_MATRIX.name(),
|
||||
BarcodeFormat.EAN_8.name(),
|
||||
BarcodeFormat.EAN_13.name(),
|
||||
BarcodeFormat.ITF.name(),
|
||||
BarcodeFormat.PDF_417.name(),
|
||||
BarcodeFormat.QR_CODE.name(),
|
||||
BarcodeFormat.UPC_A.name()
|
||||
));
|
||||
|
||||
private Map<String, Pair<Integer, Integer>> barcodeViewMap;
|
||||
private LinkedList<AsyncTask> barcodeGeneratorTasks = new LinkedList<>();
|
||||
|
||||
final private TaskHandler mTasks = new TaskHandler();
|
||||
|
||||
private final Handler typingDelayHandler = new Handler(Looper.getMainLooper());
|
||||
public static final Integer INPUT_DELAY = 250;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setTitle(R.string.selectBarcodeTitle);
|
||||
setContentView(R.layout.barcode_selector_activity);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if(actionBar != null)
|
||||
{
|
||||
if (actionBar != null) {
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
barcodeViewMap = ImmutableMap.<String, Pair<Integer, Integer>>builder()
|
||||
.put(BarcodeFormat.AZTEC.name(), new Pair<>(R.id.aztecBarcode, R.id.aztecBarcodeText))
|
||||
.put(BarcodeFormat.CODE_39.name(), new Pair<>(R.id.code39Barcode, R.id.code39BarcodeText))
|
||||
.put(BarcodeFormat.CODE_128.name(), new Pair<>(R.id.code128Barcode, R.id.code128BarcodeText))
|
||||
.put(BarcodeFormat.CODABAR.name(), new Pair<>(R.id.codabarBarcode, R.id.codabarBarcodeText))
|
||||
.put(BarcodeFormat.DATA_MATRIX.name(), new Pair<>(R.id.datamatrixBarcode, R.id.datamatrixBarcodeText))
|
||||
.put(BarcodeFormat.EAN_8.name(), new Pair<>(R.id.ean8Barcode, R.id.ean8BarcodeText))
|
||||
.put(BarcodeFormat.EAN_13.name(), new Pair<>(R.id.ean13Barcode, R.id.ean13BarcodeText))
|
||||
.put(BarcodeFormat.ITF.name(), new Pair<>(R.id.itfBarcode, R.id.itfBarcodeText))
|
||||
.put(BarcodeFormat.PDF_417.name(), new Pair<>(R.id.pdf417Barcode, R.id.pdf417BarcodeText))
|
||||
.put(BarcodeFormat.QR_CODE.name(), new Pair<>(R.id.qrcodeBarcode, R.id.qrcodeBarcodeText))
|
||||
.put(BarcodeFormat.UPC_A.name(), new Pair<>(R.id.upcaBarcode, R.id.upcaBarcodeText))
|
||||
.build();
|
||||
barcodeViewMap = new HashMap<>();
|
||||
barcodeViewMap.put(BarcodeFormat.AZTEC.name(), new Pair<>(R.id.aztecBarcode, R.id.aztecBarcodeText));
|
||||
barcodeViewMap.put(BarcodeFormat.CODE_39.name(), new Pair<>(R.id.code39Barcode, R.id.code39BarcodeText));
|
||||
barcodeViewMap.put(BarcodeFormat.CODE_128.name(), new Pair<>(R.id.code128Barcode, R.id.code128BarcodeText));
|
||||
barcodeViewMap.put(BarcodeFormat.CODABAR.name(), new Pair<>(R.id.codabarBarcode, R.id.codabarBarcodeText));
|
||||
barcodeViewMap.put(BarcodeFormat.DATA_MATRIX.name(), new Pair<>(R.id.datamatrixBarcode, R.id.datamatrixBarcodeText));
|
||||
barcodeViewMap.put(BarcodeFormat.EAN_8.name(), new Pair<>(R.id.ean8Barcode, R.id.ean8BarcodeText));
|
||||
barcodeViewMap.put(BarcodeFormat.EAN_13.name(), new Pair<>(R.id.ean13Barcode, R.id.ean13BarcodeText));
|
||||
barcodeViewMap.put(BarcodeFormat.ITF.name(), new Pair<>(R.id.itfBarcode, R.id.itfBarcodeText));
|
||||
barcodeViewMap.put(BarcodeFormat.PDF_417.name(), new Pair<>(R.id.pdf417Barcode, R.id.pdf417BarcodeText));
|
||||
barcodeViewMap.put(BarcodeFormat.QR_CODE.name(), new Pair<>(R.id.qrcodeBarcode, R.id.qrcodeBarcodeText));
|
||||
barcodeViewMap.put(BarcodeFormat.UPC_A.name(), new Pair<>(R.id.upcaBarcode, R.id.upcaBarcodeText));
|
||||
barcodeViewMap.put(BarcodeFormat.UPC_E.name(), new Pair<>(R.id.upceBarcode, R.id.upceBarcodeText));
|
||||
|
||||
EditText cardId = findViewById(R.id.cardId);
|
||||
cardId.addTextChangedListener(new TextWatcher()
|
||||
{
|
||||
|
||||
cardId.addTextChangedListener(new SimpleTextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after)
|
||||
{
|
||||
// Noting to do
|
||||
}
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
// Delay the input processing so we avoid overload
|
||||
typingDelayHandler.removeCallbacksAndMessages(null);
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count)
|
||||
{
|
||||
Log.d(TAG, "Entered text: " + s);
|
||||
typingDelayHandler.postDelayed(() -> {
|
||||
Log.d(TAG, "Entered text: " + s);
|
||||
|
||||
generateBarcodes(s.toString());
|
||||
runOnUiThread(() -> {
|
||||
generateBarcodes(s.toString());
|
||||
|
||||
View noBarcodeButtonView = findViewById(R.id.noBarcode);
|
||||
setButtonListener(noBarcodeButtonView, s.toString());
|
||||
noBarcodeButtonView.setEnabled(s.length() > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s)
|
||||
{
|
||||
// Noting to do
|
||||
View noBarcodeButtonView = findViewById(R.id.noBarcode);
|
||||
setButtonListener(noBarcodeButtonView, s.toString());
|
||||
noBarcodeButtonView.setEnabled(s.length() > 0);
|
||||
});
|
||||
}, INPUT_DELAY);
|
||||
}
|
||||
});
|
||||
|
||||
final Bundle b = getIntent().getExtras();
|
||||
final String initialCardId = b != null ? b.getString("initialCardId") : null;
|
||||
|
||||
if(initialCardId != null)
|
||||
{
|
||||
if (initialCardId != null) {
|
||||
cardId.setText(initialCardId);
|
||||
} else {
|
||||
generateBarcodes("");
|
||||
@@ -132,101 +104,74 @@ public class BarcodeSelectorActivity extends AppCompatActivity
|
||||
}
|
||||
|
||||
private void generateBarcodes(String value) {
|
||||
// Stop any async tasks which may not have been started yet
|
||||
for(AsyncTask task : barcodeGeneratorTasks)
|
||||
{
|
||||
task.cancel(false);
|
||||
}
|
||||
barcodeGeneratorTasks.clear();
|
||||
// Attempt to stop any async tasks which may not have been started yet
|
||||
// TODO this can be very much optimized by only generating Barcodes visible to the User
|
||||
mTasks.flushTaskList(TaskHandler.TYPE.BARCODE, true, false, false);
|
||||
|
||||
// Update barcodes
|
||||
for(Map.Entry<String, Pair<Integer, Integer>> entry : barcodeViewMap.entrySet())
|
||||
{
|
||||
for (Map.Entry<String, Pair<Integer, Integer>> entry : barcodeViewMap.entrySet()) {
|
||||
ImageView image = findViewById(entry.getValue().first);
|
||||
TextView text = findViewById(entry.getValue().second);
|
||||
createBarcodeOption(image, entry.getKey(), value, text);
|
||||
}
|
||||
}
|
||||
|
||||
private void setButtonListener(final View button, final String cardId)
|
||||
{
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Log.d(TAG, "Selected no barcode");
|
||||
Intent result = new Intent();
|
||||
result.putExtra(BARCODE_FORMAT, "");
|
||||
result.putExtra(BARCODE_CONTENTS, cardId);
|
||||
BarcodeSelectorActivity.this.setResult(RESULT_OK, result);
|
||||
finish();
|
||||
}
|
||||
private void setButtonListener(final View button, final String cardId) {
|
||||
button.setOnClickListener(view -> {
|
||||
Log.d(TAG, "Selected no barcode");
|
||||
Intent result = new Intent();
|
||||
result.putExtra(BARCODE_FORMAT, "");
|
||||
result.putExtra(BARCODE_CONTENTS, cardId);
|
||||
BarcodeSelectorActivity.this.setResult(RESULT_OK, result);
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
private void createBarcodeOption(final ImageView image, final String formatType, final String cardId, final TextView text)
|
||||
{
|
||||
final BarcodeFormat format = BarcodeFormat.valueOf(formatType);
|
||||
if(format == null)
|
||||
{
|
||||
Log.w(TAG, "Unsupported barcode format: " + formatType);
|
||||
return;
|
||||
}
|
||||
private void createBarcodeOption(final ImageView image, final String formatType, final String cardId, final TextView text) {
|
||||
final CatimaBarcode format = CatimaBarcode.fromName(formatType);
|
||||
|
||||
image.setImageBitmap(null);
|
||||
image.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
Log.d(TAG, "Selected barcode type " + formatType);
|
||||
image.setOnClickListener(v -> {
|
||||
Log.d(TAG, "Selected barcode type " + formatType);
|
||||
|
||||
if (!((boolean) image.getTag())) {
|
||||
Toast.makeText(BarcodeSelectorActivity.this, getString(R.string.wrongValueForBarcodeType), Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
Intent result = new Intent();
|
||||
result.putExtra(BARCODE_FORMAT, formatType);
|
||||
result.putExtra(BARCODE_CONTENTS, cardId);
|
||||
BarcodeSelectorActivity.this.setResult(RESULT_OK, result);
|
||||
finish();
|
||||
if (!((boolean) image.getTag())) {
|
||||
Toast.makeText(BarcodeSelectorActivity.this, getString(R.string.wrongValueForBarcodeType), Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
Intent result = new Intent();
|
||||
result.putExtra(BARCODE_FORMAT, formatType);
|
||||
result.putExtra(BARCODE_CONTENTS, cardId);
|
||||
BarcodeSelectorActivity.this.setResult(RESULT_OK, result);
|
||||
finish();
|
||||
});
|
||||
|
||||
if(image.getHeight() == 0)
|
||||
{
|
||||
if (image.getHeight() == 0) {
|
||||
// The size of the ImageView is not yet available as it has not
|
||||
// yet been drawn. Wait for it to be drawn so the size is available.
|
||||
image.getViewTreeObserver().addOnGlobalLayoutListener(
|
||||
new ViewTreeObserver.OnGlobalLayoutListener()
|
||||
{
|
||||
@Override
|
||||
public void onGlobalLayout()
|
||||
{
|
||||
Log.d(TAG, "Global layout finished, type: + " + formatType + ", width: " + image.getWidth());
|
||||
image.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
Log.d(TAG, "Global layout finished, type: + " + formatType + ", width: " + image.getWidth());
|
||||
image.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
|
||||
Log.d(TAG, "Generating barcode for type " + formatType);
|
||||
BarcodeImageWriterTask task = new BarcodeImageWriterTask(image, cardId, format, text, true, null);
|
||||
barcodeGeneratorTasks.add(task);
|
||||
task.execute();
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.d(TAG, "Generating barcode for type " + formatType);
|
||||
|
||||
BarcodeImageWriterTask barcodeWriter = new BarcodeImageWriterTask(getApplicationContext(), image, cardId, format, text, true, null);
|
||||
mTasks.executeTask(TaskHandler.TYPE.BARCODE, barcodeWriter);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Log.d(TAG, "Generating barcode for type " + formatType);
|
||||
BarcodeImageWriterTask task = new BarcodeImageWriterTask(image, cardId, format, text, true, null);
|
||||
barcodeGeneratorTasks.add(task);
|
||||
task.execute();
|
||||
BarcodeImageWriterTask barcodeWriter = new BarcodeImageWriterTask(getApplicationContext(), image, cardId, format, text, true, null);
|
||||
mTasks.executeTask(TaskHandler.TYPE.BARCODE, barcodeWriter);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item)
|
||||
{
|
||||
if (item.getItemId() == android.R.id.home)
|
||||
{
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
setResult(Activity.RESULT_CANCELED);
|
||||
finish();
|
||||
return true;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
@@ -12,6 +10,10 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.content.pm.ShortcutInfoCompat;
|
||||
import androidx.core.content.pm.ShortcutManagerCompat;
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
@@ -47,6 +49,11 @@ public class CardShortcutConfigure extends AppCompatActivity implements LoyaltyC
|
||||
}
|
||||
|
||||
final RecyclerView cardList = findViewById(R.id.list);
|
||||
|
||||
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
|
||||
cardList.setLayoutManager(mLayoutManager);
|
||||
cardList.setItemAnimator(new DefaultItemAnimator());
|
||||
|
||||
cardList.setVisibility(View.VISIBLE);
|
||||
|
||||
Cursor cardCursor = db.getLoyaltyCardCursor();
|
||||
@@ -62,30 +69,13 @@ public class CardShortcutConfigure extends AppCompatActivity implements LoyaltyC
|
||||
|
||||
Log.d(TAG, "Creating shortcut for card " + loyaltyCard.store + "," + loyaltyCard.id);
|
||||
|
||||
Intent shortcutIntent = new Intent(CardShortcutConfigure.this, LoyaltyCardViewActivity.class);
|
||||
shortcutIntent.setAction(Intent.ACTION_MAIN);
|
||||
// Prevent instances of the view activity from piling up; if one exists let this
|
||||
// one replace it.
|
||||
shortcutIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt("id", loyaltyCard.id);
|
||||
bundle.putBoolean("view", true);
|
||||
shortcutIntent.putExtras(bundle);
|
||||
ShortcutInfoCompat shortcut = ShortcutHelper.createShortcutBuilder(CardShortcutConfigure.this, loyaltyCard).build();
|
||||
|
||||
Parcelable icon = Intent.ShortcutIconResource.fromContext(CardShortcutConfigure.this, R.mipmap.ic_launcher);
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
|
||||
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, loyaltyCard.store);
|
||||
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, icon);
|
||||
setResult(RESULT_OK, intent);
|
||||
setResult(RESULT_OK, ShortcutManagerCompat.createShortcutResultIntent(CardShortcutConfigure.this, shortcut));
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIconClicked(int inputPosition) {
|
||||
onClickAction(inputPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRowClicked(int inputPosition) {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Resources;
|
||||
import android.util.TypedValue;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
public class CatimaAppCompatActivity extends AppCompatActivity {
|
||||
|
||||
SharedPreferences pref;
|
||||
HashMap<String, Integer> supportedThemes;
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
// Apply chosen language
|
||||
super.attachBaseContext(Utils.updateBaseContextLocale(base));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources.Theme getTheme() {
|
||||
if (supportedThemes == null) {
|
||||
supportedThemes = new HashMap<>();
|
||||
supportedThemes.put(getString(R.string.settings_key_blue_theme), R.style.AppTheme_blue);
|
||||
supportedThemes.put(getString(R.string.settings_key_brown_theme), R.style.AppTheme_brown);
|
||||
supportedThemes.put(getString(R.string.settings_key_green_theme), R.style.AppTheme_green);
|
||||
supportedThemes.put(getString(R.string.settings_key_grey_theme), R.style.AppTheme_grey);
|
||||
supportedThemes.put(getString(R.string.settings_key_magenta_theme), R.style.AppTheme_magenta);
|
||||
supportedThemes.put(getString(R.string.settings_key_pink_theme), R.style.AppTheme_pink);
|
||||
supportedThemes.put(getString(R.string.settings_key_sky_blue_theme), R.style.AppTheme_sky_blue);
|
||||
supportedThemes.put(getString(R.string.settings_key_violet_theme), R.style.AppTheme_violet);
|
||||
}
|
||||
|
||||
Resources.Theme theme = super.getTheme();
|
||||
pref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
String themeName = pref.getString(getString(R.string.setting_key_theme_color), getString(R.string.settings_key_catima_theme));
|
||||
|
||||
theme.applyStyle(Utils.mapGetOrDefault(supportedThemes, themeName, R.style.AppTheme_NoActionBar), true);
|
||||
|
||||
return theme;
|
||||
}
|
||||
|
||||
public int getThemeColor() {
|
||||
TypedValue typedValue = new TypedValue();
|
||||
Resources.Theme theme = getTheme();
|
||||
theme.resolveAttribute(R.attr.colorPrimary, typedValue, true);
|
||||
return typedValue.data;
|
||||
}
|
||||
}
|
||||
90
app/src/main/java/protect/card_locker/CatimaBarcode.java
Normal file
90
app/src/main/java/protect/card_locker/CatimaBarcode.java
Normal file
@@ -0,0 +1,90 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class CatimaBarcode {
|
||||
public static final List<BarcodeFormat> barcodeFormats = Collections.unmodifiableList(Arrays.asList(
|
||||
BarcodeFormat.AZTEC,
|
||||
BarcodeFormat.CODE_39,
|
||||
BarcodeFormat.CODE_128,
|
||||
BarcodeFormat.CODABAR,
|
||||
BarcodeFormat.DATA_MATRIX,
|
||||
BarcodeFormat.EAN_8,
|
||||
BarcodeFormat.EAN_13,
|
||||
BarcodeFormat.ITF,
|
||||
BarcodeFormat.PDF_417,
|
||||
BarcodeFormat.QR_CODE,
|
||||
BarcodeFormat.UPC_A,
|
||||
BarcodeFormat.UPC_E
|
||||
));
|
||||
|
||||
public static final List<String> barcodePrettyNames = Collections.unmodifiableList(Arrays.asList(
|
||||
"Aztec",
|
||||
"Code 39",
|
||||
"Code 128",
|
||||
"Codabar",
|
||||
"Data Matrix",
|
||||
"EAN 8",
|
||||
"EAN 13",
|
||||
"ITF",
|
||||
"PDF 417",
|
||||
"QR Code",
|
||||
"UPC A",
|
||||
"UPC E"
|
||||
));
|
||||
|
||||
private final BarcodeFormat mBarcodeFormat;
|
||||
|
||||
private CatimaBarcode(BarcodeFormat barcodeFormat) {
|
||||
mBarcodeFormat = barcodeFormat;
|
||||
}
|
||||
|
||||
public static CatimaBarcode fromBarcode(BarcodeFormat barcodeFormat) {
|
||||
return new CatimaBarcode(barcodeFormat);
|
||||
}
|
||||
|
||||
public static CatimaBarcode fromName(String name) {
|
||||
return new CatimaBarcode(BarcodeFormat.valueOf(name));
|
||||
}
|
||||
|
||||
public static CatimaBarcode fromPrettyName(String prettyName) {
|
||||
try {
|
||||
return new CatimaBarcode(barcodeFormats.get(barcodePrettyNames.indexOf(prettyName)));
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
throw new IllegalArgumentException("No barcode type with pretty name " + prettyName + " known!");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isSupported() {
|
||||
return barcodeFormats.contains(mBarcodeFormat);
|
||||
}
|
||||
|
||||
public boolean isSquare(){
|
||||
return mBarcodeFormat == BarcodeFormat.AZTEC
|
||||
|| mBarcodeFormat == BarcodeFormat.DATA_MATRIX
|
||||
|| mBarcodeFormat == BarcodeFormat.MAXICODE
|
||||
|| mBarcodeFormat == BarcodeFormat.QR_CODE;
|
||||
}
|
||||
|
||||
public BarcodeFormat format() {
|
||||
return mBarcodeFormat;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return mBarcodeFormat.name();
|
||||
}
|
||||
|
||||
public String prettyName() {
|
||||
int index = barcodeFormats.indexOf(mBarcodeFormat);
|
||||
|
||||
if (index == -1 || index >= barcodePrettyNames.size()) {
|
||||
return mBarcodeFormat.name();
|
||||
}
|
||||
|
||||
return barcodePrettyNames.get(index);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.journeyapps.barcodescanner.CaptureManager;
|
||||
import com.journeyapps.barcodescanner.DecoratedBarcodeView;
|
||||
|
||||
public class CatimaCaptureManager extends CaptureManager {
|
||||
private Activity activity;
|
||||
|
||||
public CatimaCaptureManager(Activity activity, DecoratedBarcodeView barcodeView) {
|
||||
super(activity, barcodeView);
|
||||
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void displayFrameworkBugMessageAndExit(String message) {
|
||||
// We don't want to exit, as we also have a enter from card image and add manually button here
|
||||
// So we show a toast instead
|
||||
Toast.makeText(activity, message, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
@@ -3,14 +3,17 @@ package protect.card_locker;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@@ -19,7 +22,7 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
{
|
||||
public static final String DATABASE_NAME = "Catima.db";
|
||||
public static final int ORIGINAL_DATABASE_VERSION = 1;
|
||||
public static final int DATABASE_VERSION = 10;
|
||||
public static final int DATABASE_VERSION = 14;
|
||||
|
||||
public static class LoyaltyCardDbGroups
|
||||
{
|
||||
@@ -43,6 +46,8 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
public static final String BARCODE_ID = "barcodeid";
|
||||
public static final String BARCODE_TYPE = "barcodetype";
|
||||
public static final String STAR_STATUS = "starstatus";
|
||||
public static final String LAST_USED = "lastused";
|
||||
public static final String ZOOM_LEVEL = "zoomlevel";
|
||||
}
|
||||
|
||||
public static class LoyaltyCardDbIdsGroups
|
||||
@@ -52,22 +57,45 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
public static final String groupID = "groupId";
|
||||
}
|
||||
|
||||
public static class LoyaltyCardDbFTS
|
||||
{
|
||||
public static final String TABLE = "fts";
|
||||
public static final String ID = "rowid"; // This should NEVER be changed
|
||||
public static final String STORE = "store";
|
||||
public static final String NOTE = "note";
|
||||
}
|
||||
|
||||
public enum LoyaltyCardOrder {
|
||||
Alpha,
|
||||
LastUsed,
|
||||
Expiry
|
||||
}
|
||||
|
||||
public enum LoyaltyCardOrderDirection {
|
||||
Ascending,
|
||||
Descending
|
||||
}
|
||||
|
||||
private Context mContext;
|
||||
|
||||
public DBHelper(Context context)
|
||||
{
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db)
|
||||
{
|
||||
// create table for card groups
|
||||
db.execSQL("create table " + LoyaltyCardDbGroups.TABLE + "(" +
|
||||
db.execSQL("CREATE TABLE " + LoyaltyCardDbGroups.TABLE + "(" +
|
||||
LoyaltyCardDbGroups.ID + " TEXT primary key not null," +
|
||||
LoyaltyCardDbGroups.ORDER + " INTEGER DEFAULT '0')");
|
||||
|
||||
// create table for cards
|
||||
// Balance is TEXT and not REAL to be able to store a BigDecimal without precision loss
|
||||
db.execSQL("create table " + LoyaltyCardDbIds.TABLE + "(" +
|
||||
db.execSQL("CREATE TABLE " + LoyaltyCardDbIds.TABLE + "(" +
|
||||
LoyaltyCardDbIds.ID + " INTEGER primary key autoincrement," +
|
||||
LoyaltyCardDbIds.STORE + " TEXT not null," +
|
||||
LoyaltyCardDbIds.NOTE + " TEXT not null," +
|
||||
@@ -78,26 +106,32 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
LoyaltyCardDbIds.CARD_ID + " TEXT not null," +
|
||||
LoyaltyCardDbIds.BARCODE_ID + " TEXT," +
|
||||
LoyaltyCardDbIds.BARCODE_TYPE + " TEXT," +
|
||||
LoyaltyCardDbIds.STAR_STATUS + " INTEGER DEFAULT '0' )");
|
||||
LoyaltyCardDbIds.STAR_STATUS + " INTEGER DEFAULT '0'," +
|
||||
LoyaltyCardDbIds.LAST_USED + " INTEGER DEFAULT '0', "+
|
||||
LoyaltyCardDbIds.ZOOM_LEVEL + " INTEGER DEFAULT '100' )");
|
||||
|
||||
// create associative table for cards in groups
|
||||
db.execSQL("create table " + LoyaltyCardDbIdsGroups.TABLE + "(" +
|
||||
db.execSQL("CREATE TABLE " + LoyaltyCardDbIdsGroups.TABLE + "(" +
|
||||
LoyaltyCardDbIdsGroups.cardID + " INTEGER," +
|
||||
LoyaltyCardDbIdsGroups.groupID + " TEXT," +
|
||||
"primary key (" + LoyaltyCardDbIdsGroups.cardID + "," + LoyaltyCardDbIdsGroups.groupID +"))");
|
||||
|
||||
// create FTS search table
|
||||
db.execSQL("CREATE VIRTUAL TABLE " + LoyaltyCardDbFTS.TABLE + " USING fts4(" +
|
||||
LoyaltyCardDbFTS.STORE + ", " + LoyaltyCardDbFTS.NOTE + ", " +
|
||||
"tokenize=unicode61);");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
|
||||
{
|
||||
// Upgrade from version 1 to version 2
|
||||
if(oldVersion < 2 && newVersion >= 2)
|
||||
{
|
||||
db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE
|
||||
+ " ADD COLUMN " + LoyaltyCardDbIds.NOTE + " TEXT not null default ''");
|
||||
}
|
||||
|
||||
// Upgrade from version 2 to version 3
|
||||
if(oldVersion < 3 && newVersion >= 3)
|
||||
{
|
||||
db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE
|
||||
@@ -106,26 +140,23 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
+ " ADD COLUMN " + LoyaltyCardDbIds.HEADER_TEXT_COLOR + " INTEGER");
|
||||
}
|
||||
|
||||
// Upgrade from version 3 to version 4
|
||||
if(oldVersion < 4 && newVersion >= 4)
|
||||
{
|
||||
db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE
|
||||
+ " ADD COLUMN " + LoyaltyCardDbIds.STAR_STATUS + " INTEGER DEFAULT '0'");
|
||||
}
|
||||
|
||||
// Upgrade from version 4 to version 5
|
||||
if(oldVersion < 5 && newVersion >= 5)
|
||||
{
|
||||
db.execSQL("create table " + LoyaltyCardDbGroups.TABLE + "(" +
|
||||
db.execSQL("CREATE TABLE " + LoyaltyCardDbGroups.TABLE + "(" +
|
||||
LoyaltyCardDbGroups.ID + " TEXT primary key not null)");
|
||||
|
||||
db.execSQL("create table " + LoyaltyCardDbIdsGroups.TABLE + "(" +
|
||||
db.execSQL("CREATE TABLE " + LoyaltyCardDbIdsGroups.TABLE + "(" +
|
||||
LoyaltyCardDbIdsGroups.cardID + " INTEGER," +
|
||||
LoyaltyCardDbIdsGroups.groupID + " TEXT," +
|
||||
"primary key (" + LoyaltyCardDbIdsGroups.cardID + "," + LoyaltyCardDbIdsGroups.groupID +"))");
|
||||
}
|
||||
|
||||
// Upgrade from version 5 to 6
|
||||
if(oldVersion < 6 && newVersion >= 6)
|
||||
{
|
||||
db.execSQL("ALTER TABLE " + LoyaltyCardDbGroups.TABLE
|
||||
@@ -201,7 +232,7 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
|
||||
db.execSQL("DROP TABLE " + LoyaltyCardDbIds.TABLE);
|
||||
|
||||
db.execSQL("create table " + LoyaltyCardDbIds.TABLE + "(" +
|
||||
db.execSQL("CREATE TABLE " + LoyaltyCardDbIds.TABLE + "(" +
|
||||
LoyaltyCardDbIds.ID + " INTEGER primary key autoincrement," +
|
||||
LoyaltyCardDbIds.STORE + " TEXT not null," +
|
||||
LoyaltyCardDbIds.NOTE + " TEXT not null," +
|
||||
@@ -245,15 +276,110 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
if(oldVersion < 11 && newVersion >= 11)
|
||||
{
|
||||
db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE
|
||||
+ " ADD COLUMN " + LoyaltyCardDbIds.LAST_USED + " INTEGER DEFAULT '0'");
|
||||
}
|
||||
|
||||
if(oldVersion < 12 && newVersion >= 12)
|
||||
{
|
||||
db.execSQL("CREATE VIRTUAL TABLE " + LoyaltyCardDbFTS.TABLE + " USING fts4(" +
|
||||
LoyaltyCardDbFTS.STORE + ", " + LoyaltyCardDbFTS.NOTE + ", " +
|
||||
"tokenize=unicode61);");
|
||||
|
||||
Cursor cursor = db.rawQuery("SELECT * FROM " + LoyaltyCardDbIds.TABLE + ";", null, null);
|
||||
|
||||
cursor.moveToFirst();
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
int id = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.ID));
|
||||
String store = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STORE));
|
||||
String note = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE));
|
||||
insertFTS(db, id, store, note);
|
||||
}
|
||||
}
|
||||
|
||||
if(oldVersion < 13 && newVersion >= 13)
|
||||
{
|
||||
db.execSQL("DELETE FROM " + LoyaltyCardDbFTS.TABLE + ";");
|
||||
|
||||
Cursor cursor = db.rawQuery("SELECT * FROM " + LoyaltyCardDbIds.TABLE + ";", null, null);
|
||||
|
||||
if (cursor.moveToFirst()) {
|
||||
int id = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.ID));
|
||||
String store = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STORE));
|
||||
String note = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE));
|
||||
insertFTS(db, id, store, note);
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
id = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.ID));
|
||||
store = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STORE));
|
||||
note = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE));
|
||||
insertFTS(db, id, store, note);
|
||||
}
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
if(oldVersion < 14 && newVersion >= 14){
|
||||
db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE
|
||||
+ " ADD COLUMN " + LoyaltyCardDbIds.ZOOM_LEVEL + " INTEGER DEFAULT '100' ");
|
||||
}
|
||||
}
|
||||
|
||||
private ContentValues generateFTSContentValues(final int id, final String store, final String note) {
|
||||
// FTS on Android is severely limited and can only search for word starting with a certain string
|
||||
// So for each word, we grab every single substring
|
||||
// This makes it possible to find Décathlon by searching both de and cat, for example
|
||||
|
||||
ContentValues ftsContentValues = new ContentValues();
|
||||
|
||||
StringBuilder storeString = new StringBuilder();
|
||||
for (String word : store.split(" ")) {
|
||||
for (int i = 0; i < word.length(); i++) {
|
||||
storeString.append(word);
|
||||
storeString.append(" ");
|
||||
word = word.substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder noteString = new StringBuilder();
|
||||
for (String word : note.split(" ")) {
|
||||
for (int i = 0; i < word.length(); i++) {
|
||||
noteString.append(word);
|
||||
noteString.append(" ");
|
||||
word = word.substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
ftsContentValues.put(LoyaltyCardDbFTS.ID, id);
|
||||
ftsContentValues.put(LoyaltyCardDbFTS.STORE, storeString.toString());
|
||||
ftsContentValues.put(LoyaltyCardDbFTS.NOTE, noteString.toString());
|
||||
|
||||
return ftsContentValues;
|
||||
}
|
||||
|
||||
private void insertFTS(final SQLiteDatabase db, final int id, final String store, final String note) {
|
||||
db.insert(LoyaltyCardDbFTS.TABLE, null, generateFTSContentValues(id, store, note));
|
||||
}
|
||||
|
||||
private void updateFTS(final SQLiteDatabase db, final int id, final String store, final String note) {
|
||||
db.update(LoyaltyCardDbFTS.TABLE, generateFTSContentValues(id, store, note),
|
||||
whereAttrs(LoyaltyCardDbFTS.ID), withArgs(id));
|
||||
}
|
||||
|
||||
public long insertLoyaltyCard(final String store, final String note, final Date expiry,
|
||||
final BigDecimal balance, final Currency balanceType,
|
||||
final String cardId, final String barcodeId,
|
||||
final BarcodeFormat barcodeType, final Integer headerColor,
|
||||
final int starStatus)
|
||||
final CatimaBarcode barcodeType, final Integer headerColor,
|
||||
final int starStatus, final Long lastUsed)
|
||||
{
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
db.beginTransaction();
|
||||
|
||||
// Card
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(LoyaltyCardDbIds.STORE, store);
|
||||
contentValues.put(LoyaltyCardDbIds.NOTE, note);
|
||||
@@ -262,19 +388,31 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
contentValues.put(LoyaltyCardDbIds.BALANCE_TYPE, balanceType != null ? balanceType.getCurrencyCode() : null);
|
||||
contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId);
|
||||
contentValues.put(LoyaltyCardDbIds.BARCODE_ID, barcodeId);
|
||||
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType != null ? barcodeType.toString() : null);
|
||||
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType != null ? barcodeType.name() : null);
|
||||
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
|
||||
contentValues.put(LoyaltyCardDbIds.STAR_STATUS, starStatus);
|
||||
final long newId = db.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
|
||||
return newId;
|
||||
contentValues.put(LoyaltyCardDbIds.LAST_USED, lastUsed != null ? lastUsed : Utils.getUnixTime());
|
||||
long id = db.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
|
||||
|
||||
// FTS
|
||||
insertFTS(db, (int) id, store, note);
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
public boolean insertLoyaltyCard(final SQLiteDatabase db, final String store,
|
||||
final String note, final Date expiry, final BigDecimal balance,
|
||||
final Currency balanceType, final String cardId,
|
||||
final String barcodeId, final BarcodeFormat barcodeType,
|
||||
final Integer headerColor, final int starStatus)
|
||||
public long insertLoyaltyCard(final SQLiteDatabase db, final String store,
|
||||
final String note, final Date expiry, final BigDecimal balance,
|
||||
final Currency balanceType, final String cardId,
|
||||
final String barcodeId, final CatimaBarcode barcodeType,
|
||||
final Integer headerColor, final int starStatus,
|
||||
final Long lastUsed)
|
||||
{
|
||||
db.beginTransaction();
|
||||
|
||||
// Card
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(LoyaltyCardDbIds.STORE, store);
|
||||
contentValues.put(LoyaltyCardDbIds.NOTE, note);
|
||||
@@ -283,19 +421,31 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
contentValues.put(LoyaltyCardDbIds.BALANCE_TYPE, balanceType != null ? balanceType.getCurrencyCode() : null);
|
||||
contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId);
|
||||
contentValues.put(LoyaltyCardDbIds.BARCODE_ID, barcodeId);
|
||||
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType != null ? barcodeType.toString() : null);
|
||||
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType != null ? barcodeType.name() : null);
|
||||
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
|
||||
contentValues.put(LoyaltyCardDbIds.STAR_STATUS,starStatus);
|
||||
final long newId = db.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
|
||||
return (newId != -1);
|
||||
contentValues.put(LoyaltyCardDbIds.STAR_STATUS, starStatus);
|
||||
contentValues.put(LoyaltyCardDbIds.LAST_USED, lastUsed != null ? lastUsed : Utils.getUnixTime());
|
||||
long id = db.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
|
||||
|
||||
// FTS
|
||||
insertFTS(db, (int) id, store, note);
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
public boolean insertLoyaltyCard(final SQLiteDatabase db, final int id, final String store,
|
||||
final String note, final Date expiry, final BigDecimal balance,
|
||||
final Currency balanceType, final String cardId,
|
||||
final String barcodeId, final BarcodeFormat barcodeType,
|
||||
final Integer headerColor, final int starStatus)
|
||||
public long insertLoyaltyCard(final SQLiteDatabase db, final int id, final String store,
|
||||
final String note, final Date expiry, final BigDecimal balance,
|
||||
final Currency balanceType, final String cardId,
|
||||
final String barcodeId, final CatimaBarcode barcodeType,
|
||||
final Integer headerColor, final int starStatus,
|
||||
final Long lastUsed)
|
||||
{
|
||||
db.beginTransaction();
|
||||
|
||||
// Card
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(LoyaltyCardDbIds.ID, id);
|
||||
contentValues.put(LoyaltyCardDbIds.STORE, store);
|
||||
@@ -305,20 +455,31 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
contentValues.put(LoyaltyCardDbIds.BALANCE_TYPE, balanceType != null ? balanceType.getCurrencyCode() : null);
|
||||
contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId);
|
||||
contentValues.put(LoyaltyCardDbIds.BARCODE_ID, barcodeId);
|
||||
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType != null ? barcodeType.toString() : null);
|
||||
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType != null ? barcodeType.name() : null);
|
||||
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
|
||||
contentValues.put(LoyaltyCardDbIds.STAR_STATUS,starStatus);
|
||||
final long newId = db.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
|
||||
return (newId != -1);
|
||||
contentValues.put(LoyaltyCardDbIds.STAR_STATUS, starStatus);
|
||||
contentValues.put(LoyaltyCardDbIds.LAST_USED, lastUsed != null ? lastUsed : Utils.getUnixTime());
|
||||
db.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
|
||||
|
||||
// FTS
|
||||
insertFTS(db, id, store, note);
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
public boolean updateLoyaltyCard(final int id, final String store, final String note,
|
||||
final Date expiry, final BigDecimal balance,
|
||||
final Currency balanceType, final String cardId,
|
||||
final String barcodeId, final BarcodeFormat barcodeType,
|
||||
final String barcodeId, final CatimaBarcode barcodeType,
|
||||
final Integer headerColor)
|
||||
{
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
db.beginTransaction();
|
||||
|
||||
// Card
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(LoyaltyCardDbIds.STORE, store);
|
||||
contentValues.put(LoyaltyCardDbIds.NOTE, note);
|
||||
@@ -327,11 +488,17 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
contentValues.put(LoyaltyCardDbIds.BALANCE_TYPE, balanceType != null ? balanceType.getCurrencyCode() : null);
|
||||
contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId);
|
||||
contentValues.put(LoyaltyCardDbIds.BARCODE_ID, barcodeId);
|
||||
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType != null ? barcodeType.toString() : null);
|
||||
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType != null ? barcodeType.name() : null);
|
||||
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
|
||||
int rowsUpdated = db.update(LoyaltyCardDbIds.TABLE, contentValues,
|
||||
LoyaltyCardDbIds.ID + "=?",
|
||||
new String[]{Integer.toString(id)});
|
||||
whereAttrs(LoyaltyCardDbIds.ID), withArgs(id));
|
||||
|
||||
// FTS
|
||||
updateFTS(db, id, store, note);
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
|
||||
return (rowsUpdated == 1);
|
||||
}
|
||||
|
||||
@@ -341,23 +508,43 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(LoyaltyCardDbIds.STAR_STATUS,starStatus);
|
||||
int rowsUpdated = db.update(LoyaltyCardDbIds.TABLE, contentValues,
|
||||
LoyaltyCardDbIds.ID + "=?",
|
||||
new String[]{Integer.toString(id)});
|
||||
whereAttrs(LoyaltyCardDbIds.ID),
|
||||
withArgs(id));
|
||||
return (rowsUpdated == 1);
|
||||
}
|
||||
|
||||
public boolean updateLoyaltyCardLastUsed(final int id) {
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(LoyaltyCardDbIds.LAST_USED, System.currentTimeMillis() / 1000);
|
||||
int rowsUpdated = db.update(LoyaltyCardDbIds.TABLE, contentValues,
|
||||
whereAttrs(LoyaltyCardDbIds.ID),
|
||||
withArgs(id));
|
||||
return (rowsUpdated == 1);
|
||||
}
|
||||
|
||||
public boolean updateLoyaltyCardZoomLevel(int loyaltyCardId, int zoomLevel){
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(LoyaltyCardDbIds.ZOOM_LEVEL,zoomLevel);
|
||||
Log.d("updateLoyaltyCardZLevel","Card Id = "+loyaltyCardId+" Zoom level= "+zoomLevel);
|
||||
int rowsUpdated = db.update(LoyaltyCardDbIds.TABLE,contentValues,
|
||||
whereAttrs(LoyaltyCardDbIds.ID),
|
||||
withArgs(loyaltyCardId));
|
||||
Log.d("updateLoyaltyCardZLevel","Rows changed = "+rowsUpdated);
|
||||
return (rowsUpdated == 1);
|
||||
}
|
||||
|
||||
public LoyaltyCard getLoyaltyCard(final int id)
|
||||
{
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
Cursor data = db.rawQuery("select * from " + LoyaltyCardDbIds.TABLE +
|
||||
" where " + LoyaltyCardDbIds.ID + "=?", new String[]{String.format("%d", id)});
|
||||
Cursor data = db.query(LoyaltyCardDbIds.TABLE, null, whereAttrs(LoyaltyCardDbIds.ID), withArgs(id), null, null, null);
|
||||
|
||||
LoyaltyCard card = null;
|
||||
|
||||
if(data.getCount() == 1)
|
||||
{
|
||||
data.moveToFirst();
|
||||
|
||||
card = LoyaltyCard.toLoyaltyCard(data);
|
||||
}
|
||||
|
||||
@@ -372,7 +559,7 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
Cursor data = db.rawQuery("select * from " + LoyaltyCardDbGroups.TABLE + " g " +
|
||||
" LEFT JOIN " + LoyaltyCardDbIdsGroups.TABLE + " ig ON ig." + LoyaltyCardDbIdsGroups.groupID + " = g." + LoyaltyCardDbGroups.ID +
|
||||
" where " + LoyaltyCardDbIdsGroups.cardID + "=?" +
|
||||
" ORDER BY " + LoyaltyCardDbIdsGroups.groupID, new String[]{String.format("%d", id)});
|
||||
" ORDER BY " + LoyaltyCardDbIdsGroups.groupID, withArgs(id));
|
||||
|
||||
List<Group> groups = new ArrayList<>();
|
||||
|
||||
@@ -398,8 +585,8 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
|
||||
// First delete lookup table entries associated with this card
|
||||
db.delete(LoyaltyCardDbIdsGroups.TABLE,
|
||||
LoyaltyCardDbIdsGroups.cardID + " = ? ",
|
||||
new String[]{String.format("%d", id)});
|
||||
whereAttrs(LoyaltyCardDbIdsGroups.cardID),
|
||||
withArgs(id));
|
||||
|
||||
// Then create entries for selected values
|
||||
for (Group group : groups) {
|
||||
@@ -414,8 +601,8 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
{
|
||||
// First delete lookup table entries associated with this card
|
||||
db.delete(LoyaltyCardDbIdsGroups.TABLE,
|
||||
LoyaltyCardDbIdsGroups.cardID + " = ? ",
|
||||
new String[]{String.format("%d", id)});
|
||||
whereAttrs(LoyaltyCardDbIdsGroups.cardID),
|
||||
withArgs(id));
|
||||
|
||||
// Then create entries for selected values
|
||||
for (Group group : groups) {
|
||||
@@ -426,18 +613,31 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
}
|
||||
}
|
||||
|
||||
public boolean deleteLoyaltyCard (final int id)
|
||||
public boolean deleteLoyaltyCard(final int id)
|
||||
{
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
// Delete card
|
||||
int rowsDeleted = db.delete(LoyaltyCardDbIds.TABLE,
|
||||
LoyaltyCardDbIds.ID + " = ? ",
|
||||
new String[]{String.format("%d", id)});
|
||||
whereAttrs(LoyaltyCardDbIds.ID),
|
||||
withArgs(id));
|
||||
|
||||
// And delete lookup table entries associated with this card
|
||||
db.delete(LoyaltyCardDbIdsGroups.TABLE,
|
||||
LoyaltyCardDbIdsGroups.cardID + " = ? ",
|
||||
new String[]{String.format("%d", id)});
|
||||
whereAttrs(LoyaltyCardDbIdsGroups.cardID),
|
||||
withArgs(id));
|
||||
|
||||
// Delete FTS table entries
|
||||
db.delete(LoyaltyCardDbFTS.TABLE,
|
||||
whereAttrs(LoyaltyCardDbFTS.ID),
|
||||
withArgs(id));
|
||||
|
||||
// Also wipe card images associated with this card
|
||||
try {
|
||||
Utils.saveCardImage(mContext, null, id, true);
|
||||
Utils.saveCardImage(mContext, null, id, false);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return (rowsDeleted == 1);
|
||||
}
|
||||
@@ -468,8 +668,18 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
*/
|
||||
public Cursor getLoyaltyCardCursor(final String filter, Group group)
|
||||
{
|
||||
String actualFilter = String.format("%%%s%%", filter);
|
||||
String[] selectionArgs = { actualFilter, actualFilter };
|
||||
return getLoyaltyCardCursor(filter, group, LoyaltyCardOrder.Alpha, LoyaltyCardOrderDirection.Ascending);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a cursor to all loyalty cards with the filter text in either the store or note in a certain group sorted as requested.
|
||||
*
|
||||
* @param filter
|
||||
* @param group
|
||||
* @param order
|
||||
* @return Cursor
|
||||
*/
|
||||
public Cursor getLoyaltyCardCursor(String filter, Group group, LoyaltyCardOrder order, LoyaltyCardOrderDirection direction) {
|
||||
StringBuilder groupFilter = new StringBuilder();
|
||||
String limitString = "";
|
||||
|
||||
@@ -479,11 +689,11 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
List<Integer> allowedIds = getGroupCardIds(group._id);
|
||||
|
||||
// Empty group
|
||||
if (allowedIds.size() > 0) {
|
||||
if (!allowedIds.isEmpty()) {
|
||||
groupFilter.append("AND (");
|
||||
|
||||
for (int i = 0; i < allowedIds.size(); i++) {
|
||||
groupFilter.append(LoyaltyCardDbIds.ID + " = " + allowedIds.get(i));
|
||||
groupFilter.append(LoyaltyCardDbIds.TABLE + "." + LoyaltyCardDbIds.ID + " = ").append(allowedIds.get(i));
|
||||
if (i != allowedIds.size() - 1) {
|
||||
groupFilter.append(" OR ");
|
||||
}
|
||||
@@ -494,50 +704,29 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
}
|
||||
}
|
||||
|
||||
Cursor res = db.rawQuery("select * from " + LoyaltyCardDbIds.TABLE +
|
||||
" WHERE (" + LoyaltyCardDbIds.STORE + " LIKE ? " +
|
||||
" OR " + LoyaltyCardDbIds.NOTE + " LIKE ? )" +
|
||||
String orderField = getFieldForOrder(order);
|
||||
|
||||
return db.rawQuery("SELECT " + LoyaltyCardDbIds.TABLE + ".* FROM " + LoyaltyCardDbIds.TABLE +
|
||||
" JOIN " + LoyaltyCardDbFTS.TABLE +
|
||||
" ON " + LoyaltyCardDbFTS.TABLE + "." + LoyaltyCardDbFTS.ID + " = " + LoyaltyCardDbIds.TABLE + "." + LoyaltyCardDbIds.ID +
|
||||
(filter.trim().isEmpty() ? " " : " AND " + LoyaltyCardDbFTS.TABLE + " MATCH ? ") +
|
||||
groupFilter.toString() +
|
||||
" ORDER BY " + LoyaltyCardDbIds.STAR_STATUS + " DESC," + LoyaltyCardDbIds.STORE + " COLLATE NOCASE ASC " +
|
||||
limitString, selectionArgs, null);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public int getLoyaltyCardCount()
|
||||
{
|
||||
// An empty string will match everything
|
||||
return getLoyaltyCardCount("");
|
||||
" ORDER BY " + LoyaltyCardDbIds.TABLE + "." + LoyaltyCardDbIds.STAR_STATUS + " DESC, " +
|
||||
" (CASE WHEN " + LoyaltyCardDbIds.TABLE + "." + orderField + " IS NULL THEN 1 ELSE 0 END), " +
|
||||
LoyaltyCardDbIds.TABLE + "." + orderField + " COLLATE NOCASE " + getDbDirection(order, direction) + ", " +
|
||||
LoyaltyCardDbIds.TABLE + "." + LoyaltyCardDbIds.STORE + " COLLATE NOCASE ASC " +
|
||||
limitString, filter.trim().isEmpty() ? null : new String[] { TextUtils.join("* ", filter.split(" ")) + '*' }, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of loyalty cards with the filter text in either the store or note.
|
||||
* Returns the amount of loyalty cards.
|
||||
*
|
||||
* @param filter
|
||||
* @return Integer
|
||||
*/
|
||||
public int getLoyaltyCardCount(String filter)
|
||||
public int getLoyaltyCardCount()
|
||||
{
|
||||
String actualFilter = String.format("%%%s%%", filter);
|
||||
String[] selectionArgs = { actualFilter, actualFilter };
|
||||
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
Cursor data = db.rawQuery("SELECT Count(*) FROM " + LoyaltyCardDbIds.TABLE +
|
||||
" WHERE " + LoyaltyCardDbIds.STORE + " LIKE ? " +
|
||||
" OR " + LoyaltyCardDbIds.NOTE + " LIKE ? "
|
||||
, selectionArgs, null);
|
||||
|
||||
int numItems = 0;
|
||||
|
||||
if(data.getCount() == 1)
|
||||
{
|
||||
data.moveToFirst();
|
||||
numItems = data.getInt(0);
|
||||
}
|
||||
|
||||
data.close();
|
||||
|
||||
return numItems;
|
||||
return (int) DatabaseUtils.queryNumEntries(db, LoyaltyCardDbIds.TABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -549,9 +738,8 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
{
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
|
||||
Cursor res = db.rawQuery("select * from " + LoyaltyCardDbGroups.TABLE +
|
||||
return db.rawQuery("select * from " + LoyaltyCardDbGroups.TABLE +
|
||||
" ORDER BY " + LoyaltyCardDbGroups.ORDER + " ASC," + LoyaltyCardDbGroups.ID + " COLLATE NOCASE ASC", null, null);
|
||||
return res;
|
||||
}
|
||||
|
||||
public List<Group> getGroups() {
|
||||
@@ -565,31 +753,27 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
}
|
||||
|
||||
groups.add(Group.toGroup(data));
|
||||
|
||||
while (data.moveToNext()) {
|
||||
groups.add(Group.toGroup(data));
|
||||
}
|
||||
|
||||
data.close();
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
public void reorderGroups(final List<Group> groups)
|
||||
{
|
||||
Integer order = 0;
|
||||
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
ContentValues contentValues;
|
||||
|
||||
for (Group group : groups)
|
||||
{
|
||||
contentValues = new ContentValues();
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(LoyaltyCardDbGroups.ORDER, order);
|
||||
|
||||
db.update(LoyaltyCardDbGroups.TABLE, contentValues,
|
||||
LoyaltyCardDbGroups.ID + "=?",
|
||||
new String[]{group._id});
|
||||
whereAttrs(LoyaltyCardDbGroups.ID),
|
||||
withArgs(group._id));
|
||||
|
||||
order++;
|
||||
}
|
||||
@@ -598,18 +782,15 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
public Group getGroup(final String groupName)
|
||||
{
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
Cursor data = db.rawQuery("select * from " + LoyaltyCardDbGroups.TABLE +
|
||||
" where " + LoyaltyCardDbGroups.ID + "=?", new String[]{groupName});
|
||||
Cursor data = db.query(LoyaltyCardDbGroups.TABLE, null,
|
||||
whereAttrs(LoyaltyCardDbGroups.ID), withArgs(groupName), null, null, null);
|
||||
|
||||
Group group = null;
|
||||
|
||||
if(data.getCount() == 1)
|
||||
{
|
||||
data.moveToFirst();
|
||||
|
||||
group = Group.toGroup(data);
|
||||
}
|
||||
|
||||
data.close();
|
||||
|
||||
return group;
|
||||
@@ -618,28 +799,14 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
public int getGroupCount()
|
||||
{
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
Cursor data = db.rawQuery("SELECT Count(*) FROM " + LoyaltyCardDbGroups.TABLE, null);
|
||||
|
||||
int numItems = 0;
|
||||
|
||||
if(data.getCount() == 1)
|
||||
{
|
||||
data.moveToFirst();
|
||||
numItems = data.getInt(0);
|
||||
}
|
||||
|
||||
data.close();
|
||||
|
||||
return numItems;
|
||||
return (int) DatabaseUtils.queryNumEntries(db, LoyaltyCardDbGroups.TABLE);
|
||||
}
|
||||
|
||||
public List<Integer> getGroupCardIds(final String groupName)
|
||||
{
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
Cursor data = db.rawQuery("SELECT " + LoyaltyCardDbIdsGroups.cardID +
|
||||
" FROM " + LoyaltyCardDbIdsGroups.TABLE +
|
||||
" WHERE " + LoyaltyCardDbIdsGroups.groupID + " =? ", new String[]{groupName});
|
||||
|
||||
Cursor data = db.query(LoyaltyCardDbIdsGroups.TABLE, withArgs(LoyaltyCardDbIdsGroups.cardID),
|
||||
whereAttrs(LoyaltyCardDbIdsGroups.groupID), withArgs(groupName), null, null, null);
|
||||
List<Integer> cardIds = new ArrayList<>();
|
||||
|
||||
if (!data.moveToFirst()) {
|
||||
@@ -665,8 +832,7 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(LoyaltyCardDbGroups.ID, name);
|
||||
contentValues.put(LoyaltyCardDbGroups.ORDER, getGroupCount());
|
||||
final long newId = db.insert(LoyaltyCardDbGroups.TABLE, null, contentValues);
|
||||
return newId;
|
||||
return db.insert(LoyaltyCardDbGroups.TABLE, null, contentValues);
|
||||
}
|
||||
|
||||
public boolean insertGroup(final SQLiteDatabase db, final String name)
|
||||
@@ -675,7 +841,7 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
contentValues.put(LoyaltyCardDbGroups.ID, name);
|
||||
contentValues.put(LoyaltyCardDbGroups.ORDER, getGroupCount());
|
||||
final long newId = db.insert(LoyaltyCardDbGroups.TABLE, null, contentValues);
|
||||
return (newId != -1);
|
||||
return newId != -1;
|
||||
}
|
||||
|
||||
public boolean updateGroup(final String groupName, final String newName)
|
||||
@@ -695,13 +861,13 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
try {
|
||||
// Update group name
|
||||
int groupsChanged = db.update(LoyaltyCardDbGroups.TABLE, groupContentValues,
|
||||
LoyaltyCardDbGroups.ID + "=?",
|
||||
new String[]{groupName});
|
||||
whereAttrs(LoyaltyCardDbGroups.ID),
|
||||
withArgs(groupName));
|
||||
|
||||
// Also update lookup tables
|
||||
db.update(LoyaltyCardDbIdsGroups.TABLE, lookupContentValues,
|
||||
LoyaltyCardDbIdsGroups.groupID + "=?",
|
||||
new String[]{groupName});
|
||||
whereAttrs(LoyaltyCardDbIdsGroups.groupID),
|
||||
withArgs(groupName));
|
||||
|
||||
if (groupsChanged == 1) {
|
||||
db.setTransactionSuccessful();
|
||||
@@ -725,13 +891,13 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
try {
|
||||
// Delete group
|
||||
int groupsDeleted = db.delete(LoyaltyCardDbGroups.TABLE,
|
||||
LoyaltyCardDbGroups.ID + " = ? ",
|
||||
new String[]{groupName});
|
||||
whereAttrs(LoyaltyCardDbGroups.ID),
|
||||
withArgs(groupName));
|
||||
|
||||
// And delete lookup table entries associated with this group
|
||||
db.delete(LoyaltyCardDbIdsGroups.TABLE,
|
||||
LoyaltyCardDbIdsGroups.groupID + " = ? ",
|
||||
new String[]{groupName});
|
||||
whereAttrs(LoyaltyCardDbIdsGroups.groupID),
|
||||
withArgs(groupName));
|
||||
|
||||
if (groupsDeleted == 1) {
|
||||
db.setTransactionSuccessful();
|
||||
@@ -741,6 +907,9 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
// Reorder after delete to ensure no bad order IDs
|
||||
reorderGroups(getGroups());
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -748,21 +917,49 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
{
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
|
||||
Cursor data = db.rawQuery("SELECT Count(*) FROM " + LoyaltyCardDbIdsGroups.TABLE +
|
||||
" where " + LoyaltyCardDbIdsGroups.groupID + "=?",
|
||||
new String[]{groupName});
|
||||
return (int) DatabaseUtils.queryNumEntries(db, LoyaltyCardDbIdsGroups.TABLE,
|
||||
whereAttrs(LoyaltyCardDbIdsGroups.groupID), withArgs(groupName));
|
||||
}
|
||||
|
||||
int numItems = 0;
|
||||
private String whereAttrs(String... attrs) {
|
||||
if (attrs.length == 0) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder whereClause = new StringBuilder(attrs[0]).append("=?");
|
||||
for (int i = 1; i < attrs.length; i++) {
|
||||
whereClause.append(" AND ").append(attrs[i]).append("=?");
|
||||
}
|
||||
return whereClause.toString();
|
||||
}
|
||||
|
||||
if(data.getCount() == 1)
|
||||
{
|
||||
data.moveToFirst();
|
||||
numItems = data.getInt(0);
|
||||
private String[] withArgs(Object... object) {
|
||||
return Arrays.stream(object)
|
||||
.map(String::valueOf)
|
||||
.toArray(String[]::new);
|
||||
}
|
||||
|
||||
private String getFieldForOrder(LoyaltyCardOrder order) {
|
||||
if (order == LoyaltyCardOrder.Alpha) {
|
||||
return LoyaltyCardDbIds.STORE;
|
||||
}
|
||||
|
||||
data.close();
|
||||
if (order == LoyaltyCardOrder.LastUsed) {
|
||||
return LoyaltyCardDbIds.LAST_USED;
|
||||
}
|
||||
|
||||
return numItems;
|
||||
if (order == LoyaltyCardOrder.Expiry) {
|
||||
return LoyaltyCardDbIds.EXPIRY;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unknown order " + order);
|
||||
}
|
||||
|
||||
private String getDbDirection(LoyaltyCardOrder order, LoyaltyCardOrderDirection direction) {
|
||||
if (order == LoyaltyCardOrder.LastUsed) {
|
||||
// We want the default sorting to put the most recently used first
|
||||
return direction == LoyaltyCardOrderDirection.Descending ? "ASC" : "DESC";
|
||||
}
|
||||
|
||||
return direction == LoyaltyCardOrderDirection.Ascending ? "ASC" : "DESC";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,16 +5,18 @@ import android.database.Cursor;
|
||||
public class Group
|
||||
{
|
||||
public final String _id;
|
||||
public final int order;
|
||||
|
||||
public Group(final String _id)
|
||||
{
|
||||
public Group(final String _id, final int order) {
|
||||
this._id = _id;
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public static Group toGroup(Cursor cursor)
|
||||
{
|
||||
String _id = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbGroups.ID));
|
||||
int order = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbGroups.ORDER));
|
||||
|
||||
return new Group(_id);
|
||||
return new Group(_id, order);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,52 +5,94 @@ import android.database.Cursor;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CursorAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.AppCompatImageButton;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import protect.card_locker.preferences.Settings;
|
||||
|
||||
class GroupCursorAdapter extends CursorAdapter
|
||||
class GroupCursorAdapter extends BaseCursorAdapter<GroupCursorAdapter.GroupListItemViewHolder>
|
||||
{
|
||||
Settings settings;
|
||||
DBHelper db;
|
||||
Settings mSettings;
|
||||
private Cursor mCursor;
|
||||
private final Context mContext;
|
||||
private final GroupCursorAdapter.GroupAdapterListener mListener;
|
||||
DBHelper mDb;
|
||||
|
||||
public GroupCursorAdapter(Context context, Cursor cursor)
|
||||
{
|
||||
super(context, cursor, 0);
|
||||
settings = new Settings(context);
|
||||
public GroupCursorAdapter(Context inputContext, Cursor inputCursor, GroupCursorAdapter.GroupAdapterListener inputListener) {
|
||||
super(inputCursor);
|
||||
setHasStableIds(true);
|
||||
mSettings = new Settings(inputContext);
|
||||
mContext = inputContext;
|
||||
mListener = inputListener;
|
||||
mDb = new DBHelper(inputContext);
|
||||
|
||||
db = new DBHelper(context);
|
||||
swapCursor(mCursor);
|
||||
}
|
||||
|
||||
// The newView method is used to inflate a new view and return it,
|
||||
// you don't bind any data to the view at this point.
|
||||
@Override
|
||||
public View newView(Context context, Cursor cursor, ViewGroup parent)
|
||||
{
|
||||
return LayoutInflater.from(context).inflate(R.layout.group_layout, parent, false);
|
||||
public void swapCursor(Cursor inputCursor) {
|
||||
super.swapCursor(inputCursor);
|
||||
mCursor = inputCursor;
|
||||
}
|
||||
|
||||
// The bindView method is used to bind all data to a given view
|
||||
// such as setting the text on a TextView.
|
||||
@NonNull
|
||||
@Override
|
||||
public void bindView(View view, Context context, Cursor cursor)
|
||||
public GroupCursorAdapter.GroupListItemViewHolder onCreateViewHolder(ViewGroup inputParent, int inputViewType)
|
||||
{
|
||||
// Find fields to populate in inflated template
|
||||
TextView nameField = view.findViewById(R.id.name);
|
||||
TextView countField = view.findViewById(R.id.cardCount);
|
||||
View itemView = LayoutInflater.from(inputParent.getContext()).inflate(R.layout.group_layout, inputParent, false);
|
||||
return new GroupListItemViewHolder(itemView);
|
||||
}
|
||||
|
||||
// Extract properties from cursor
|
||||
Group group = Group.toGroup(cursor);
|
||||
public Cursor getCursor()
|
||||
{
|
||||
return mCursor;
|
||||
}
|
||||
|
||||
Integer groupCardCount = db.getGroupCardCount(group._id);
|
||||
public void onBindViewHolder(GroupCursorAdapter.GroupListItemViewHolder inputHolder, Cursor inputCursor) {
|
||||
Group group = Group.toGroup(inputCursor);
|
||||
|
||||
// Populate fields with extracted properties
|
||||
nameField.setText(group._id);
|
||||
inputHolder.mName.setText(group._id);
|
||||
|
||||
countField.setText(context.getResources().getQuantityString(R.plurals.groupCardCount, groupCardCount, groupCardCount));
|
||||
int groupCardCount = mDb.getGroupCardCount(group._id);
|
||||
inputHolder.mCardCount.setText(mContext.getResources().getQuantityString(R.plurals.groupCardCount, groupCardCount, groupCardCount));
|
||||
|
||||
nameField.setTextSize(settings.getFontSizeMax(settings.getMediumFont()));
|
||||
countField.setTextSize(settings.getFontSizeMax(settings.getSmallFont()));
|
||||
inputHolder.mName.setTextSize(mSettings.getFontSizeMax(mSettings.getMediumFont()));
|
||||
inputHolder.mCardCount.setTextSize(mSettings.getFontSizeMax(mSettings.getSmallFont()));
|
||||
|
||||
applyClickEvents(inputHolder);
|
||||
}
|
||||
|
||||
private void applyClickEvents(GroupListItemViewHolder inputHolder)
|
||||
{
|
||||
inputHolder.mMoveDown.setOnClickListener(view -> mListener.onMoveDownButtonClicked(inputHolder.itemView));
|
||||
inputHolder.mMoveUp.setOnClickListener(view -> mListener.onMoveUpButtonClicked(inputHolder.itemView));
|
||||
inputHolder.mEdit.setOnClickListener(view -> mListener.onEditButtonClicked(inputHolder.itemView));
|
||||
inputHolder.mDelete.setOnClickListener(view -> mListener.onDeleteButtonClicked(inputHolder.itemView));
|
||||
}
|
||||
|
||||
public interface GroupAdapterListener
|
||||
{
|
||||
void onMoveDownButtonClicked(View view);
|
||||
void onMoveUpButtonClicked(View view);
|
||||
void onEditButtonClicked(View view);
|
||||
void onDeleteButtonClicked(View view);
|
||||
}
|
||||
|
||||
public static class GroupListItemViewHolder extends RecyclerView.ViewHolder
|
||||
{
|
||||
public TextView mName, mCardCount;
|
||||
public AppCompatImageButton mMoveUp, mMoveDown, mEdit, mDelete;
|
||||
|
||||
public GroupListItemViewHolder(View inputView) {
|
||||
super(inputView);
|
||||
mName = inputView.findViewById(R.id.name);
|
||||
mCardCount = inputView.findViewById(R.id.cardCount);
|
||||
mMoveUp = inputView.findViewById(R.id.moveUp);
|
||||
mMoveDown = inputView.findViewById(R.id.moveDown);
|
||||
mEdit = inputView.findViewById(R.id.edit);
|
||||
mDelete = inputView.findViewById(R.id.delete);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,30 +6,37 @@ import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.text.InputType;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
public class ImportExportActivity extends AppCompatActivity
|
||||
{
|
||||
import protect.card_locker.async.TaskHandler;
|
||||
import protect.card_locker.importexport.DataFormat;
|
||||
import protect.card_locker.importexport.ImportExportResult;
|
||||
|
||||
public class ImportExportActivity extends CatimaAppCompatActivity {
|
||||
private static final String TAG = "Catima";
|
||||
|
||||
private static final int PERMISSIONS_EXTERNAL_STORAGE = 1;
|
||||
@@ -41,17 +48,19 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
private String importAlertTitle;
|
||||
private String importAlertMessage;
|
||||
private DataFormat importDataFormat;
|
||||
private String exportPassword;
|
||||
|
||||
final private TaskHandler mTasks = new TaskHandler();
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setTitle(R.string.importExport);
|
||||
setContentView(R.layout.import_export_activity);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if(actionBar != null)
|
||||
{
|
||||
if (actionBar != null) {
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
@@ -60,28 +69,46 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
|
||||
if (ContextCompat.checkSelfPermission(ImportExportActivity.this,
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ||
|
||||
ContextCompat.checkSelfPermission(ImportExportActivity.this,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
|
||||
{
|
||||
ContextCompat.checkSelfPermission(ImportExportActivity.this,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(ImportExportActivity.this,
|
||||
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
PERMISSIONS_EXTERNAL_STORAGE);
|
||||
}
|
||||
|
||||
// Check that there is a file manager available
|
||||
final Intent intentCreateDocumentAction = new Intent(Intent.ACTION_CREATE_DOCUMENT);
|
||||
intentCreateDocumentAction.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intentCreateDocumentAction.setType("text/csv");
|
||||
intentCreateDocumentAction.putExtra(Intent.EXTRA_TITLE, "Catima.csv");
|
||||
intentCreateDocumentAction.setType("application/zip");
|
||||
intentCreateDocumentAction.putExtra(Intent.EXTRA_TITLE, "catima.zip");
|
||||
|
||||
Button exportButton = findViewById(R.id.exportButton);
|
||||
exportButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
exportButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
chooseFileWithIntent(intentCreateDocumentAction, CHOOSE_EXPORT_LOCATION);
|
||||
public void onClick(View v) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(ImportExportActivity.this);
|
||||
builder.setTitle(R.string.exportPassword);
|
||||
|
||||
FrameLayout container = new FrameLayout(ImportExportActivity.this);
|
||||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
params.leftMargin = 50;
|
||||
params.rightMargin = 50;
|
||||
|
||||
final EditText input = new EditText(ImportExportActivity.this);
|
||||
input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
input.setLayoutParams(params);
|
||||
input.setHint(R.string.exportPasswordHint);
|
||||
|
||||
container.addView(input);
|
||||
builder.setView(container);
|
||||
builder.setPositiveButton(R.string.ok, (dialogInterface, i) -> {
|
||||
exportPassword = input.getText().toString();
|
||||
chooseFileWithIntent(intentCreateDocumentAction, CHOOSE_EXPORT_LOCATION);
|
||||
});
|
||||
builder.setNegativeButton(R.string.cancel, (dialogInterface, i) -> dialogInterface.cancel());
|
||||
builder.show();
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@@ -91,11 +118,9 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
intentGetContentAction.setType("*/*");
|
||||
|
||||
Button importFilesystem = findViewById(R.id.importOptionFilesystemButton);
|
||||
importFilesystem.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
importFilesystem.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
public void onClick(View v) {
|
||||
chooseImportType(intentGetContentAction);
|
||||
}
|
||||
});
|
||||
@@ -104,20 +129,31 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
final Intent intentPickAction = new Intent(Intent.ACTION_PICK);
|
||||
|
||||
Button importApplication = findViewById(R.id.importOptionApplicationButton);
|
||||
importApplication.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
importApplication.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
public void onClick(View v) {
|
||||
chooseImportType(intentPickAction);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void chooseImportType(Intent baseIntent) {
|
||||
List<CharSequence> betaImportOptions = new ArrayList<>();
|
||||
betaImportOptions.add("Fidme");
|
||||
betaImportOptions.add("Stocard");
|
||||
List<CharSequence> importOptions = new ArrayList<>();
|
||||
|
||||
for (String importOption : getResources().getStringArray(R.array.import_types_array)) {
|
||||
if (betaImportOptions.contains(importOption)) {
|
||||
importOption = importOption + " (BETA)";
|
||||
}
|
||||
|
||||
importOptions.add(importOption);
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.chooseImportType)
|
||||
.setItems(R.array.import_types_array, (dialog, which) -> {
|
||||
.setItems(importOptions.toArray(new CharSequence[importOptions.size()]), (dialog, which) -> {
|
||||
switch (which) {
|
||||
// Catima
|
||||
case 0:
|
||||
@@ -137,8 +173,14 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
importAlertMessage = getString(R.string.importLoyaltyCardKeychainMessage);
|
||||
importDataFormat = DataFormat.Catima;
|
||||
break;
|
||||
// Voucher Vault
|
||||
// Stocard
|
||||
case 3:
|
||||
importAlertTitle = getString(R.string.importStocard);
|
||||
importAlertMessage = getString(R.string.importStocardMessage);
|
||||
importDataFormat = DataFormat.Stocard;
|
||||
break;
|
||||
// Voucher Vault
|
||||
case 4:
|
||||
importAlertTitle = getString(R.string.importVoucherVault);
|
||||
importAlertMessage = getString(R.string.importVoucherVaultMessage);
|
||||
importDataFormat = DataFormat.VoucherVault;
|
||||
@@ -151,67 +193,74 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
.setTitle(importAlertTitle)
|
||||
.setMessage(importAlertMessage)
|
||||
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
chooseFileWithIntent(baseIntent, IMPORT);
|
||||
}
|
||||
})
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
chooseFileWithIntent(baseIntent, IMPORT);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void startImport(final InputStream target, final Uri targetUri, final DataFormat dataFormat)
|
||||
{
|
||||
ImportExportTask.TaskCompleteListener listener = new ImportExportTask.TaskCompleteListener()
|
||||
{
|
||||
private void startImport(final InputStream target, final Uri targetUri, final DataFormat dataFormat, final char[] password, final boolean closeWhenDone) {
|
||||
mTasks.flushTaskList(TaskHandler.TYPE.IMPORT, true, false, false);
|
||||
ImportExportTask.TaskCompleteListener listener = new ImportExportTask.TaskCompleteListener() {
|
||||
@Override
|
||||
public void onTaskComplete(boolean success)
|
||||
{
|
||||
onImportComplete(success, targetUri);
|
||||
public void onTaskComplete(ImportExportResult result, DataFormat dataFormat) {
|
||||
onImportComplete(result, targetUri, dataFormat);
|
||||
if (closeWhenDone) {
|
||||
try {
|
||||
target.close();
|
||||
} catch (IOException ioException) {
|
||||
ioException.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
importExporter = new ImportExportTask(ImportExportActivity.this,
|
||||
dataFormat, target, listener);
|
||||
importExporter.execute();
|
||||
dataFormat, target, password, listener);
|
||||
mTasks.executeTask(TaskHandler.TYPE.IMPORT, importExporter);
|
||||
}
|
||||
|
||||
private void startExport(final OutputStream target, final Uri targetUri)
|
||||
{
|
||||
ImportExportTask.TaskCompleteListener listener = new ImportExportTask.TaskCompleteListener()
|
||||
{
|
||||
private void startExport(final OutputStream target, final Uri targetUri, char[] password, final boolean closeWhenDone) {
|
||||
mTasks.flushTaskList(TaskHandler.TYPE.EXPORT, true, false, false);
|
||||
ImportExportTask.TaskCompleteListener listener = new ImportExportTask.TaskCompleteListener() {
|
||||
@Override
|
||||
public void onTaskComplete(boolean success)
|
||||
{
|
||||
onExportComplete(success, targetUri);
|
||||
public void onTaskComplete(ImportExportResult result, DataFormat dataFormat) {
|
||||
onExportComplete(result, targetUri);
|
||||
if (closeWhenDone) {
|
||||
try {
|
||||
target.close();
|
||||
} catch (IOException ioException) {
|
||||
ioException.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
importExporter = new ImportExportTask(ImportExportActivity.this,
|
||||
DataFormat.Catima, target, listener);
|
||||
importExporter.execute();
|
||||
DataFormat.Catima, target, password, listener);
|
||||
mTasks.executeTask(TaskHandler.TYPE.EXPORT, importExporter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults)
|
||||
{
|
||||
if(requestCode == PERMISSIONS_EXTERNAL_STORAGE)
|
||||
{
|
||||
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
|
||||
if (requestCode == PERMISSIONS_EXTERNAL_STORAGE) {
|
||||
// If request is cancelled, the result arrays are empty.
|
||||
boolean success = grantResults.length > 0;
|
||||
|
||||
for(int grant : grantResults)
|
||||
{
|
||||
if(grant != PackageManager.PERMISSION_GRANTED)
|
||||
{
|
||||
for (int grant : grantResults) {
|
||||
if (grant != PackageManager.PERMISSION_GRANTED) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(success == false)
|
||||
{
|
||||
if (!success) {
|
||||
// External storage permission rejected, inform user that
|
||||
// import/export is prevented
|
||||
Toast.makeText(getApplicationContext(), R.string.noExternalStoragePermissionError,
|
||||
@@ -222,22 +271,17 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy()
|
||||
{
|
||||
if(importExporter != null && importExporter.getStatus() != AsyncTask.Status.RUNNING)
|
||||
{
|
||||
importExporter.cancel(true);
|
||||
}
|
||||
protected void onDestroy() {
|
||||
mTasks.flushTaskList(TaskHandler.TYPE.IMPORT, true, false, false);
|
||||
mTasks.flushTaskList(TaskHandler.TYPE.EXPORT, true, false, false);
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item)
|
||||
{
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
|
||||
if(id == android.R.id.home)
|
||||
{
|
||||
if (id == android.R.id.home) {
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
@@ -245,28 +289,46 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void onImportComplete(boolean success, Uri path)
|
||||
{
|
||||
private void retryWithPassword(DataFormat dataFormat, Uri uri) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.passwordRequired);
|
||||
|
||||
final EditText input = new EditText(this);
|
||||
input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
builder.setView(input);
|
||||
|
||||
builder.setPositiveButton(R.string.ok, (dialogInterface, i) -> {
|
||||
activityResultParser(IMPORT, RESULT_OK, uri, input.getText().toString().toCharArray());
|
||||
});
|
||||
builder.setNegativeButton(R.string.cancel, (dialogInterface, i) -> dialogInterface.cancel());
|
||||
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void onImportComplete(ImportExportResult result, Uri path, DataFormat dataFormat) {
|
||||
if (result == ImportExportResult.BadPassword) {
|
||||
retryWithPassword(dataFormat, path);
|
||||
return;
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
|
||||
if(success)
|
||||
{
|
||||
int messageId;
|
||||
|
||||
if (result == ImportExportResult.Success) {
|
||||
builder.setTitle(R.string.importSuccessfulTitle);
|
||||
}
|
||||
else
|
||||
{
|
||||
messageId = R.string.importSuccessful;
|
||||
} else {
|
||||
builder.setTitle(R.string.importFailedTitle);
|
||||
messageId = R.string.importFailed;
|
||||
}
|
||||
|
||||
int messageId = success ? R.string.importSuccessful : R.string.importFailed;
|
||||
final String message = getResources().getString(messageId);
|
||||
|
||||
builder.setMessage(message);
|
||||
builder.setNeutralButton(R.string.ok, new DialogInterface.OnClickListener()
|
||||
{
|
||||
builder.setNeutralButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
@@ -274,135 +336,107 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private void onExportComplete(boolean success, final Uri path)
|
||||
{
|
||||
private void onExportComplete(ImportExportResult result, final Uri path) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
|
||||
if(success)
|
||||
{
|
||||
int messageId;
|
||||
|
||||
if (result == ImportExportResult.Success) {
|
||||
builder.setTitle(R.string.exportSuccessfulTitle);
|
||||
}
|
||||
else
|
||||
{
|
||||
messageId = R.string.exportSuccessful;
|
||||
} else {
|
||||
builder.setTitle(R.string.exportFailedTitle);
|
||||
messageId = R.string.exportFailed;
|
||||
}
|
||||
|
||||
int messageId = success ? R.string.exportSuccessful : R.string.exportFailed;
|
||||
final String message = getResources().getString(messageId);
|
||||
|
||||
builder.setMessage(message);
|
||||
builder.setNeutralButton(R.string.ok, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
builder.setNeutralButton(R.string.ok, (dialog, which) -> dialog.dismiss());
|
||||
|
||||
if(success)
|
||||
{
|
||||
if (result == ImportExportResult.Success) {
|
||||
final CharSequence sendLabel = ImportExportActivity.this.getResources().getText(R.string.sendLabel);
|
||||
|
||||
builder.setPositiveButton(sendLabel, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
Intent sendIntent = new Intent(Intent.ACTION_SEND);
|
||||
sendIntent.putExtra(Intent.EXTRA_STREAM, path);
|
||||
sendIntent.setType("text/csv");
|
||||
builder.setPositiveButton(sendLabel, (dialog, which) -> {
|
||||
Intent sendIntent = new Intent(Intent.ACTION_SEND);
|
||||
sendIntent.putExtra(Intent.EXTRA_STREAM, path);
|
||||
sendIntent.setType("text/csv");
|
||||
|
||||
// set flag to give temporary permission to external app to use the FileProvider
|
||||
sendIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
// set flag to give temporary permission to external app to use the FileProvider
|
||||
sendIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
|
||||
ImportExportActivity.this.startActivity(Intent.createChooser(sendIntent,
|
||||
sendLabel));
|
||||
ImportExportActivity.this.startActivity(Intent.createChooser(sendIntent,
|
||||
sendLabel));
|
||||
|
||||
dialog.dismiss();
|
||||
}
|
||||
dialog.dismiss();
|
||||
});
|
||||
}
|
||||
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private void chooseFileWithIntent(Intent intent, int requestCode)
|
||||
{
|
||||
try
|
||||
{
|
||||
private void chooseFileWithIntent(Intent intent, int requestCode) {
|
||||
try {
|
||||
startActivityForResult(intent, requestCode);
|
||||
}
|
||||
catch (ActivityNotFoundException e)
|
||||
{
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(getApplicationContext(), R.string.failedOpeningFileManager, Toast.LENGTH_LONG).show();
|
||||
Log.e(TAG, "No activity found to handle intent", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (resultCode != RESULT_OK)
|
||||
{
|
||||
private void activityResultParser(int requestCode, int resultCode, Uri uri, char[] password) {
|
||||
if (resultCode != RESULT_OK) {
|
||||
Log.w(TAG, "Failed onActivityResult(), result=" + resultCode);
|
||||
return;
|
||||
}
|
||||
|
||||
Uri uri = data.getData();
|
||||
if(uri == null)
|
||||
{
|
||||
if (uri == null) {
|
||||
Log.e(TAG, "Activity returned a NULL URI");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (requestCode == CHOOSE_EXPORT_LOCATION)
|
||||
{
|
||||
try {
|
||||
if (requestCode == CHOOSE_EXPORT_LOCATION) {
|
||||
|
||||
OutputStream writer;
|
||||
if (uri.getScheme() != null)
|
||||
{
|
||||
if (uri.getScheme() != null) {
|
||||
writer = getContentResolver().openOutputStream(uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
writer = new FileOutputStream(new File(uri.toString()));
|
||||
}
|
||||
|
||||
Log.e(TAG, "Starting file export with: " + uri.toString());
|
||||
startExport(writer, uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
startExport(writer, uri, exportPassword.toCharArray(), true);
|
||||
} else {
|
||||
InputStream reader;
|
||||
if(uri.getScheme() != null)
|
||||
{
|
||||
if (uri.getScheme() != null) {
|
||||
reader = getContentResolver().openInputStream(uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
reader = new FileInputStream(new File(uri.toString()));
|
||||
}
|
||||
|
||||
Log.e(TAG, "Starting file import with: " + uri.toString());
|
||||
|
||||
startImport(reader, uri, importDataFormat);
|
||||
startImport(reader, uri, importDataFormat, password, true);
|
||||
}
|
||||
}
|
||||
catch(FileNotFoundException e)
|
||||
{
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to import/export file: " + uri.toString(), e);
|
||||
if (requestCode == CHOOSE_EXPORT_LOCATION)
|
||||
{
|
||||
onExportComplete(false, uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
onImportComplete(false, uri);
|
||||
if (requestCode == CHOOSE_EXPORT_LOCATION) {
|
||||
onExportComplete(ImportExportResult.GenericFailure, uri);
|
||||
} else {
|
||||
onImportComplete(ImportExportResult.GenericFailure, uri, importDataFormat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (data == null) {
|
||||
Log.e(TAG, "Activity returned NULL data");
|
||||
return;
|
||||
}
|
||||
|
||||
activityResultParser(requestCode, resultCode, data.getData(), null);
|
||||
}
|
||||
}
|
||||
@@ -2,21 +2,23 @@ package protect.card_locker;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import protect.card_locker.async.CompatCallable;
|
||||
import protect.card_locker.importexport.DataFormat;
|
||||
import protect.card_locker.importexport.ImportExportResult;
|
||||
import protect.card_locker.importexport.MultiFormatExporter;
|
||||
import protect.card_locker.importexport.MultiFormatImporter;
|
||||
|
||||
class ImportExportTask extends AsyncTask<Void, Void, Boolean>
|
||||
{
|
||||
public class ImportExportTask implements CompatCallable<ImportExportResult> {
|
||||
private static final String TAG = "Catima";
|
||||
|
||||
private Activity activity;
|
||||
@@ -24,6 +26,7 @@ class ImportExportTask extends AsyncTask<Void, Void, Boolean>
|
||||
private DataFormat format;
|
||||
private OutputStream outputStream;
|
||||
private InputStream inputStream;
|
||||
private char[] password;
|
||||
private TaskCompleteListener listener;
|
||||
|
||||
private ProgressDialog progress;
|
||||
@@ -31,55 +34,47 @@ class ImportExportTask extends AsyncTask<Void, Void, Boolean>
|
||||
/**
|
||||
* Constructor which will setup a task for exporting to the given file
|
||||
*/
|
||||
ImportExportTask(Activity activity, DataFormat format, OutputStream output,
|
||||
TaskCompleteListener listener)
|
||||
{
|
||||
ImportExportTask(Activity activity, DataFormat format, OutputStream output, char[] password,
|
||||
TaskCompleteListener listener) {
|
||||
super();
|
||||
this.activity = activity;
|
||||
this.doImport = false;
|
||||
this.format = format;
|
||||
this.outputStream = output;
|
||||
this.password = password;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor which will setup a task for importing from the given InputStream.
|
||||
*/
|
||||
ImportExportTask(Activity activity, DataFormat format, InputStream input,
|
||||
TaskCompleteListener listener)
|
||||
{
|
||||
ImportExportTask(Activity activity, DataFormat format, InputStream input, char[] password,
|
||||
TaskCompleteListener listener) {
|
||||
super();
|
||||
this.activity = activity;
|
||||
this.doImport = true;
|
||||
this.format = format;
|
||||
this.inputStream = input;
|
||||
this.password = password;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
private boolean performImport(InputStream stream, DBHelper db)
|
||||
{
|
||||
boolean result = false;
|
||||
private ImportExportResult performImport(Context context, InputStream stream, DBHelper db, char[] password) {
|
||||
ImportExportResult importResult = MultiFormatImporter.importData(context, db, stream, format, password);
|
||||
|
||||
Log.i(TAG, "Import result: " + importResult.name());
|
||||
|
||||
result = MultiFormatImporter.importData(db, stream, format);
|
||||
|
||||
Log.i(TAG, "Import result: " + result);
|
||||
|
||||
return result;
|
||||
return importResult;
|
||||
}
|
||||
|
||||
private boolean performExport(OutputStream stream, DBHelper db)
|
||||
{
|
||||
boolean result = false;
|
||||
private ImportExportResult performExport(Context context, OutputStream stream, DBHelper db, char[] password) {
|
||||
ImportExportResult result = ImportExportResult.GenericFailure;
|
||||
|
||||
try
|
||||
{
|
||||
OutputStreamWriter writer = new OutputStreamWriter(stream, Charset.forName("UTF-8"));
|
||||
result = MultiFormatExporter.exportData(db, writer, format);
|
||||
try {
|
||||
OutputStreamWriter writer = new OutputStreamWriter(stream, StandardCharsets.UTF_8);
|
||||
result = MultiFormatExporter.exportData(context, db, stream, format, password);
|
||||
writer.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Unable to export file", e);
|
||||
}
|
||||
|
||||
@@ -88,56 +83,56 @@ class ImportExportTask extends AsyncTask<Void, Void, Boolean>
|
||||
return result;
|
||||
}
|
||||
|
||||
protected void onPreExecute()
|
||||
{
|
||||
public void onPreExecute() {
|
||||
progress = new ProgressDialog(activity);
|
||||
progress.setTitle(doImport ? R.string.importing : R.string.exporting);
|
||||
|
||||
progress.setOnDismissListener(new DialogInterface.OnDismissListener()
|
||||
{
|
||||
progress.setOnDismissListener(new DialogInterface.OnDismissListener() {
|
||||
@Override
|
||||
public void onDismiss(DialogInterface dialog)
|
||||
{
|
||||
ImportExportTask.this.cancel(true);
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
ImportExportTask.this.stop();
|
||||
}
|
||||
});
|
||||
|
||||
progress.show();
|
||||
}
|
||||
|
||||
protected Boolean doInBackground(Void... nothing)
|
||||
{
|
||||
protected ImportExportResult doInBackground(Void... nothing) {
|
||||
final DBHelper db = new DBHelper(activity);
|
||||
boolean result;
|
||||
ImportExportResult result;
|
||||
|
||||
if(doImport)
|
||||
{
|
||||
result = performImport(inputStream, db);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = performExport(outputStream, db);
|
||||
if (doImport) {
|
||||
result = performImport(activity.getApplicationContext(), inputStream, db, password);
|
||||
} else {
|
||||
result = performExport(activity.getApplicationContext(), outputStream, db, password);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected void onPostExecute(Boolean result)
|
||||
{
|
||||
listener.onTaskComplete(result);
|
||||
public void onPostExecute(Object castResult) {
|
||||
listener.onTaskComplete((ImportExportResult) castResult, format);
|
||||
|
||||
progress.dismiss();
|
||||
Log.i(TAG, (doImport ? "Import" : "Export") + " Complete");
|
||||
}
|
||||
|
||||
protected void onCancelled()
|
||||
{
|
||||
protected void onCancelled() {
|
||||
progress.dismiss();
|
||||
Log.i(TAG, (doImport ? "Import" : "Export") + " Cancelled");
|
||||
}
|
||||
interface TaskCompleteListener
|
||||
{
|
||||
void onTaskComplete(boolean success);
|
||||
|
||||
protected void stop() {
|
||||
// Whelp
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImportExportResult call() {
|
||||
return doInBackground();
|
||||
}
|
||||
|
||||
interface TaskCompleteListener {
|
||||
void onTaskComplete(ImportExportResult result, DataFormat format);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,9 +7,14 @@ import android.net.Uri;
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
import java.io.InvalidObjectException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.math.BigDecimal;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class ImportURIHelper {
|
||||
@@ -21,29 +26,34 @@ public class ImportURIHelper {
|
||||
private static final String CARD_ID = DBHelper.LoyaltyCardDbIds.CARD_ID;
|
||||
private static final String BARCODE_ID = DBHelper.LoyaltyCardDbIds.BARCODE_ID;
|
||||
private static final String BARCODE_TYPE = DBHelper.LoyaltyCardDbIds.BARCODE_TYPE;
|
||||
|
||||
private static final String HEADER_COLOR = DBHelper.LoyaltyCardDbIds.HEADER_COLOR;
|
||||
|
||||
private final Context context;
|
||||
private final String host;
|
||||
private final String path;
|
||||
private final String oldHost;
|
||||
private final String oldPath;
|
||||
private final String[] hosts = new String[3];
|
||||
private final String[] paths = new String[3];
|
||||
private final String shareText;
|
||||
private final String shareMultipleText;
|
||||
|
||||
public ImportURIHelper(Context context) {
|
||||
this.context = context;
|
||||
host = context.getResources().getString(R.string.intent_import_card_from_url_host);
|
||||
path = context.getResources().getString(R.string.intent_import_card_from_url_path_prefix);
|
||||
oldHost = "brarcher.github.io";
|
||||
oldPath = "/loyalty-card-locker/share";
|
||||
hosts[0] = context.getResources().getString(R.string.intent_import_card_from_url_host_catima_app);
|
||||
paths[0] = context.getResources().getString(R.string.intent_import_card_from_url_path_prefix_catima_app);
|
||||
hosts[1] = context.getResources().getString(R.string.intent_import_card_from_url_host_thelastproject);
|
||||
paths[1] = context.getResources().getString(R.string.intent_import_card_from_url_path_prefix_thelastproject);
|
||||
hosts[2] = context.getResources().getString(R.string.intent_import_card_from_url_host_brarcher);
|
||||
paths[2] = context.getResources().getString(R.string.intent_import_card_from_url_path_prefix_brarcher);
|
||||
shareText = context.getResources().getString(R.string.intent_import_card_from_url_share_text);
|
||||
shareMultipleText = context.getResources().getString(R.string.intent_import_card_from_url_share_multiple_text);
|
||||
}
|
||||
|
||||
private boolean isImportUri(Uri uri) {
|
||||
return (uri.getHost().equals(host) && uri.getPath().equals(path)) || (uri.getHost().equals(oldHost) && uri.getPath().equals(oldPath));
|
||||
for (int i = 0; i < hosts.length; i++) {
|
||||
if (uri.getHost().equals(hosts[i]) && uri.getPath().equals(paths[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public LoyaltyCard parse(Uri uri) throws InvalidObjectException {
|
||||
@@ -53,83 +63,120 @@ public class ImportURIHelper {
|
||||
|
||||
try {
|
||||
// These values are allowed to be null
|
||||
BarcodeFormat barcodeType = null;
|
||||
CatimaBarcode barcodeType = null;
|
||||
Date expiry = null;
|
||||
BigDecimal balance = new BigDecimal("0");
|
||||
Currency balanceType = null;
|
||||
Integer headerColor = null;
|
||||
|
||||
String store = uri.getQueryParameter(STORE);
|
||||
String note = uri.getQueryParameter(NOTE);
|
||||
String cardId = uri.getQueryParameter(CARD_ID);
|
||||
String barcodeId = uri.getQueryParameter(BARCODE_ID);
|
||||
if (store == null || note == null || cardId == null) throw new InvalidObjectException("Not a valid import URI");
|
||||
// Store everything in a simple key/value hashmap
|
||||
HashMap<String, String> kv = new HashMap<>();
|
||||
|
||||
String unparsedBarcodeType = uri.getQueryParameter(BARCODE_TYPE);
|
||||
if(unparsedBarcodeType != null && !unparsedBarcodeType.equals(""))
|
||||
{
|
||||
barcodeType = BarcodeFormat.valueOf(unparsedBarcodeType);
|
||||
// First, grab all query parameters (backwards compatibility)
|
||||
for (String key : uri.getQueryParameterNames()) {
|
||||
kv.put(key, uri.getQueryParameter(key));
|
||||
}
|
||||
|
||||
String unparsedBalance = uri.getQueryParameter(BALANCE);
|
||||
// Then, parse the new and more private fragment part
|
||||
// Overriding old format entries if they exist
|
||||
String fragment = uri.getFragment();
|
||||
if (fragment != null) {
|
||||
for (String fragmentPart : fragment.split("&")) {
|
||||
String[] fragmentData = fragmentPart.split("=", 2);
|
||||
kv.put(fragmentData[0], URLDecoder.decode(fragmentData[1], StandardCharsets.UTF_8.name()));
|
||||
}
|
||||
}
|
||||
|
||||
// Then use all values we care about
|
||||
String store = kv.get(STORE);
|
||||
String note = kv.get(NOTE);
|
||||
String cardId = kv.get(CARD_ID);
|
||||
String barcodeId = kv.get(BARCODE_ID);
|
||||
if (store == null || note == null || cardId == null) throw new InvalidObjectException("Not a valid import URI: " + uri.toString());
|
||||
|
||||
String unparsedBarcodeType = kv.get(BARCODE_TYPE);
|
||||
if(unparsedBarcodeType != null && !unparsedBarcodeType.equals(""))
|
||||
{
|
||||
barcodeType = CatimaBarcode.fromName(unparsedBarcodeType);
|
||||
}
|
||||
|
||||
String unparsedBalance = kv.get(BALANCE);
|
||||
if(unparsedBalance != null && !unparsedBalance.equals(""))
|
||||
{
|
||||
balance = new BigDecimal(unparsedBalance);
|
||||
}
|
||||
String unparsedBalanceType = uri.getQueryParameter(BALANCE_TYPE);
|
||||
String unparsedBalanceType = kv.get(BALANCE_TYPE);
|
||||
if (unparsedBalanceType != null && !unparsedBalanceType.equals(""))
|
||||
{
|
||||
balanceType = Currency.getInstance(unparsedBalanceType);
|
||||
}
|
||||
String unparsedExpiry = uri.getQueryParameter(EXPIRY);
|
||||
String unparsedExpiry = kv.get(EXPIRY);
|
||||
if(unparsedExpiry != null && !unparsedExpiry.equals(""))
|
||||
{
|
||||
expiry = new Date(Long.parseLong(unparsedExpiry));
|
||||
}
|
||||
|
||||
String unparsedHeaderColor = uri.getQueryParameter(HEADER_COLOR);
|
||||
String unparsedHeaderColor = kv.get(HEADER_COLOR);
|
||||
if(unparsedHeaderColor != null)
|
||||
{
|
||||
headerColor = Integer.parseInt(unparsedHeaderColor);
|
||||
}
|
||||
|
||||
return new LoyaltyCard(-1, store, note, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, 0);
|
||||
} catch (NullPointerException | NumberFormatException ex) {
|
||||
return new LoyaltyCard(-1, store, note, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, 0, Utils.getUnixTime(),100);
|
||||
} catch (NullPointerException | NumberFormatException | UnsupportedEncodingException ex) {
|
||||
throw new InvalidObjectException("Not a valid import URI");
|
||||
}
|
||||
}
|
||||
|
||||
private StringBuilder appendFragment(StringBuilder fragment, String key, String value) throws UnsupportedEncodingException {
|
||||
if (fragment.length() > 0) {
|
||||
fragment.append("&");
|
||||
}
|
||||
|
||||
// Double-encode the value to make sure it can't accidentally contain symbols that'll break the parser
|
||||
fragment.append(key).append("=").append(URLEncoder.encode(value, StandardCharsets.UTF_8.name()));
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
// Protected for usage in tests
|
||||
protected Uri toUri(LoyaltyCard loyaltyCard) {
|
||||
protected Uri toUri(LoyaltyCard loyaltyCard) throws UnsupportedEncodingException {
|
||||
Uri.Builder uriBuilder = new Uri.Builder();
|
||||
uriBuilder.scheme("https");
|
||||
uriBuilder.authority(host);
|
||||
uriBuilder.path(path);
|
||||
uriBuilder.appendQueryParameter(STORE, loyaltyCard.store);
|
||||
uriBuilder.appendQueryParameter(NOTE, loyaltyCard.note);
|
||||
uriBuilder.appendQueryParameter(BALANCE, loyaltyCard.balance.toString());
|
||||
uriBuilder.authority(hosts[0]);
|
||||
uriBuilder.path(paths[0]);
|
||||
|
||||
// Use fragment instead of QueryParameter to not leak this data to the server
|
||||
StringBuilder fragment = new StringBuilder();
|
||||
|
||||
fragment = appendFragment(fragment, STORE, loyaltyCard.store);
|
||||
fragment = appendFragment(fragment, NOTE, loyaltyCard.note);
|
||||
fragment = appendFragment(fragment, BALANCE, loyaltyCard.balance.toString());
|
||||
if (loyaltyCard.balanceType != null) {
|
||||
uriBuilder.appendQueryParameter(BALANCE_TYPE, loyaltyCard.balanceType.getCurrencyCode());
|
||||
fragment = appendFragment(fragment, BALANCE_TYPE, loyaltyCard.balanceType.getCurrencyCode());
|
||||
}
|
||||
if (loyaltyCard.expiry != null) {
|
||||
uriBuilder.appendQueryParameter(EXPIRY, String.valueOf(loyaltyCard.expiry.getTime()));
|
||||
fragment = appendFragment(fragment, EXPIRY, String.valueOf(loyaltyCard.expiry.getTime()));
|
||||
}
|
||||
uriBuilder.appendQueryParameter(CARD_ID, loyaltyCard.cardId);
|
||||
fragment = appendFragment(fragment, CARD_ID, loyaltyCard.cardId);
|
||||
if(loyaltyCard.barcodeId != null) {
|
||||
uriBuilder.appendQueryParameter(BARCODE_ID, loyaltyCard.barcodeId);
|
||||
fragment = appendFragment(fragment, BARCODE_ID, loyaltyCard.barcodeId);
|
||||
}
|
||||
|
||||
if(loyaltyCard.barcodeType != null) {
|
||||
uriBuilder.appendQueryParameter(BARCODE_TYPE, loyaltyCard.barcodeType.toString());
|
||||
fragment = appendFragment(fragment, BARCODE_TYPE, loyaltyCard.barcodeType.name());
|
||||
}
|
||||
if(loyaltyCard.headerColor != null) {
|
||||
uriBuilder.appendQueryParameter(HEADER_COLOR, loyaltyCard.headerColor.toString());
|
||||
fragment = appendFragment(fragment, HEADER_COLOR, loyaltyCard.headerColor.toString());
|
||||
}
|
||||
//StarStatus will not be exported
|
||||
// Star status will not be exported
|
||||
// Front and back pictures are often too big to fit into a message in base64 nicely, not sharing either...
|
||||
|
||||
uriBuilder.fragment(fragment.toString());
|
||||
return uriBuilder.build();
|
||||
}
|
||||
|
||||
public void startShareIntent(List<LoyaltyCard> loyaltyCards) {
|
||||
public void startShareIntent(List<LoyaltyCard> loyaltyCards) throws UnsupportedEncodingException {
|
||||
int loyaltyCardCount = loyaltyCards.size();
|
||||
|
||||
StringBuilder text = new StringBuilder();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.database.Cursor;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Currency;
|
||||
@@ -10,8 +10,7 @@ import java.util.Date;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class LoyaltyCard
|
||||
{
|
||||
public class LoyaltyCard implements Parcelable {
|
||||
public final int id;
|
||||
public final String store;
|
||||
public final String note;
|
||||
@@ -23,17 +22,20 @@ public class LoyaltyCard
|
||||
@Nullable
|
||||
public final String barcodeId;
|
||||
|
||||
public final BarcodeFormat barcodeType;
|
||||
@Nullable
|
||||
public final CatimaBarcode barcodeType;
|
||||
|
||||
@Nullable
|
||||
public final Integer headerColor;
|
||||
|
||||
public final int starStatus;
|
||||
public final long lastUsed;
|
||||
public int zoomLevel;
|
||||
|
||||
public LoyaltyCard(final int id, final String store, final String note, final Date expiry,
|
||||
final BigDecimal balance, final Currency balanceType, final String cardId,
|
||||
final String barcodeId, final BarcodeFormat barcodeType, final Integer headerColor,
|
||||
final int starStatus)
|
||||
@Nullable final String barcodeId, @Nullable final CatimaBarcode barcodeType,
|
||||
@Nullable final Integer headerColor, final int starStatus, final long lastUsed,final int zoomLevel)
|
||||
{
|
||||
this.id = id;
|
||||
this.store = store;
|
||||
@@ -46,6 +48,44 @@ public class LoyaltyCard
|
||||
this.barcodeType = barcodeType;
|
||||
this.headerColor = headerColor;
|
||||
this.starStatus = starStatus;
|
||||
this.lastUsed = lastUsed;
|
||||
this.zoomLevel = zoomLevel;
|
||||
}
|
||||
|
||||
protected LoyaltyCard(Parcel in) {
|
||||
id = in.readInt();
|
||||
store = in.readString();
|
||||
note = in.readString();
|
||||
long tmpExpiry = in.readLong();
|
||||
expiry = tmpExpiry != -1 ? new Date(tmpExpiry) : null;
|
||||
balance = (BigDecimal) in.readValue(BigDecimal.class.getClassLoader());
|
||||
balanceType = (Currency) in.readValue(Currency.class.getClassLoader());
|
||||
cardId = in.readString();
|
||||
barcodeId = in.readString();
|
||||
String tmpBarcodeType = in.readString();
|
||||
barcodeType = !tmpBarcodeType.isEmpty() ? CatimaBarcode.fromName(tmpBarcodeType) : null;
|
||||
int tmpHeaderColor = in.readInt();
|
||||
headerColor = tmpHeaderColor != -1 ? tmpHeaderColor : null;
|
||||
starStatus = in.readInt();
|
||||
lastUsed = in.readLong();
|
||||
zoomLevel = in.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel parcel, int i) {
|
||||
parcel.writeInt(id);
|
||||
parcel.writeString(store);
|
||||
parcel.writeString(note);
|
||||
parcel.writeLong(expiry != null ? expiry.getTime() : -1);
|
||||
parcel.writeValue(balance);
|
||||
parcel.writeValue(balanceType);
|
||||
parcel.writeString(cardId);
|
||||
parcel.writeString(barcodeId);
|
||||
parcel.writeString(barcodeType != null ? barcodeType.name() : "");
|
||||
parcel.writeInt(headerColor != null ? headerColor : -1);
|
||||
parcel.writeInt(starStatus);
|
||||
parcel.writeLong(lastUsed);
|
||||
parcel.writeInt(zoomLevel);
|
||||
}
|
||||
|
||||
public static LoyaltyCard toLoyaltyCard(Cursor cursor)
|
||||
@@ -58,19 +98,21 @@ public class LoyaltyCard
|
||||
String cardId = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID));
|
||||
String barcodeId = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_ID));
|
||||
int starred = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS));
|
||||
long lastUsed = cursor.getLong(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.LAST_USED));
|
||||
int zoomLevel = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.ZOOM_LEVEL));
|
||||
|
||||
int barcodeTypeColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE);
|
||||
int balanceTypeColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE);
|
||||
int headerColorColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_COLOR);
|
||||
|
||||
BarcodeFormat barcodeType = null;
|
||||
CatimaBarcode barcodeType = null;
|
||||
Currency balanceType = null;
|
||||
Date expiry = null;
|
||||
Integer headerColor = null;
|
||||
|
||||
if (cursor.isNull(barcodeTypeColumn) == false)
|
||||
{
|
||||
barcodeType = BarcodeFormat.valueOf(cursor.getString(barcodeTypeColumn));
|
||||
barcodeType = CatimaBarcode.fromName(cursor.getString(barcodeTypeColumn));
|
||||
}
|
||||
|
||||
if (cursor.isNull(balanceTypeColumn) == false)
|
||||
@@ -88,6 +130,23 @@ public class LoyaltyCard
|
||||
headerColor = cursor.getInt(headerColorColumn);
|
||||
}
|
||||
|
||||
return new LoyaltyCard(id, store, note, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, starred);
|
||||
return new LoyaltyCard(id, store, note, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, starred, lastUsed,zoomLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public static final Creator<LoyaltyCard> CREATOR = new Creator<LoyaltyCard>() {
|
||||
@Override
|
||||
public LoyaltyCard createFromParcel(Parcel in) {
|
||||
return new LoyaltyCard(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoyaltyCard[] newArray(int size) {
|
||||
return new LoyaltyCard[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.util.TypedValue;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -14,19 +16,20 @@ import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.core.graphics.BlendModeColorFilterCompat;
|
||||
import androidx.core.graphics.BlendModeCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import protect.card_locker.preferences.Settings;
|
||||
|
||||
public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCursorAdapter.LoyaltyCardListItemViewHolder>
|
||||
{
|
||||
private static int mCurrentSelectedIndex = -1;
|
||||
public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCursorAdapter.LoyaltyCardListItemViewHolder> {
|
||||
private int mCurrentSelectedIndex = -1;
|
||||
private Cursor mCursor;
|
||||
Settings mSettings;
|
||||
boolean mDarkModeEnabled;
|
||||
@@ -36,34 +39,44 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
private SparseBooleanArray mAnimationItemsIndex;
|
||||
private boolean mReverseAllAnimations = false;
|
||||
|
||||
public LoyaltyCardCursorAdapter(Context inputContext, Cursor inputCursor, CardAdapterListener inputListener)
|
||||
{
|
||||
public LoyaltyCardCursorAdapter(Context inputContext, Cursor inputCursor, CardAdapterListener inputListener) {
|
||||
super(inputCursor);
|
||||
setHasStableIds(true);
|
||||
mSettings = new Settings(inputContext);
|
||||
mCursor = inputCursor;
|
||||
mContext = inputContext;
|
||||
mListener = inputListener;
|
||||
mSelectedItems = new SparseBooleanArray();
|
||||
mAnimationItemsIndex = new SparseBooleanArray();
|
||||
|
||||
mDarkModeEnabled = MainActivity.isDarkModeEnabled(inputContext);
|
||||
|
||||
swapCursor(mCursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoyaltyCardListItemViewHolder onCreateViewHolder(ViewGroup inputParent, int inputViewType)
|
||||
{
|
||||
View itemView = LayoutInflater.from(inputParent.getContext()).inflate(R.layout.loyalty_card_layout, inputParent, false);
|
||||
return new LoyaltyCardListItemViewHolder(itemView);
|
||||
public void swapCursor(Cursor inputCursor) {
|
||||
super.swapCursor(inputCursor);
|
||||
mCursor = inputCursor;
|
||||
}
|
||||
|
||||
public Cursor getCursor()
|
||||
{
|
||||
@Override
|
||||
public LoyaltyCardListItemViewHolder onCreateViewHolder(ViewGroup inputParent, int inputViewType) {
|
||||
View itemView = LayoutInflater.from(inputParent.getContext()).inflate(R.layout.loyalty_card_layout, inputParent, false);
|
||||
return new LoyaltyCardListItemViewHolder(itemView, mListener);
|
||||
}
|
||||
|
||||
public Cursor getCursor() {
|
||||
return mCursor;
|
||||
}
|
||||
|
||||
public void onBindViewHolder(LoyaltyCardListItemViewHolder inputHolder, Cursor inputCursor) {
|
||||
// Invisible until we want to show something more
|
||||
inputHolder.mDivider.setVisibility(View.GONE);
|
||||
|
||||
int size = mSettings.getFontSizeMax(mSettings.getSmallFont());
|
||||
|
||||
if (mDarkModeEnabled) {
|
||||
inputHolder.mStarIcon.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP);
|
||||
inputHolder.mStarIcon.setColorFilter(BlendModeColorFilterCompat.createBlendModeColorFilterCompat(Color.WHITE, BlendModeCompat.SRC_ATOP));
|
||||
}
|
||||
|
||||
LoyaltyCard loyaltyCard = LoyaltyCard.toLoyaltyCard(inputCursor);
|
||||
@@ -73,173 +86,164 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
if (!loyaltyCard.note.isEmpty()) {
|
||||
inputHolder.mNoteField.setVisibility(View.VISIBLE);
|
||||
inputHolder.mNoteField.setText(loyaltyCard.note);
|
||||
inputHolder.mNoteField.setTextSize(mSettings.getFontSizeMax(mSettings.getSmallFont()));
|
||||
inputHolder.mNoteField.setTextSize(size);
|
||||
} else {
|
||||
inputHolder.mNoteField.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (!loyaltyCard.balance.equals(new BigDecimal("0"))) {
|
||||
int drawableSize = dpToPx((size*24)/14, mContext);
|
||||
inputHolder.mDivider.setVisibility(View.VISIBLE);
|
||||
inputHolder.mBalanceField.setVisibility(View.VISIBLE);
|
||||
inputHolder.mBalanceField.setText(mContext.getString(R.string.balanceSentence, Utils.formatBalance(mContext, loyaltyCard.balance, loyaltyCard.balanceType)));
|
||||
inputHolder.mBalanceField.setTextSize(mSettings.getFontSizeMax(mSettings.getSmallFont()));
|
||||
Drawable balanceIcon = inputHolder.mBalanceField.getCompoundDrawables()[0];
|
||||
balanceIcon.setBounds(0,0,drawableSize,drawableSize);
|
||||
inputHolder.mBalanceField.setCompoundDrawablesRelative(balanceIcon, null, null, null);
|
||||
if (mDarkModeEnabled) {
|
||||
balanceIcon.setColorFilter(BlendModeColorFilterCompat.createBlendModeColorFilterCompat(Color.WHITE, BlendModeCompat.SRC_ATOP));
|
||||
}
|
||||
inputHolder.mBalanceField.setText(Utils.formatBalance(mContext, loyaltyCard.balance, loyaltyCard.balanceType));
|
||||
inputHolder.mBalanceField.setTextSize(size);
|
||||
} else {
|
||||
inputHolder.mBalanceField.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (loyaltyCard.expiry != null)
|
||||
{
|
||||
if (loyaltyCard.expiry != null) {
|
||||
int drawableSize = dpToPx((size*24)/14, mContext);
|
||||
inputHolder.mDivider.setVisibility(View.VISIBLE);
|
||||
inputHolder.mExpiryField.setVisibility(View.VISIBLE);
|
||||
int expiryString = R.string.expiryStateSentence;
|
||||
if(Utils.hasExpired(loyaltyCard.expiry)) {
|
||||
expiryString = R.string.expiryStateSentenceExpired;
|
||||
inputHolder.mExpiryField.setTextColor(mContext.getResources().getColor(R.color.alert));
|
||||
Drawable expiryIcon = inputHolder.mExpiryField.getCompoundDrawables()[0];
|
||||
expiryIcon.setBounds(0,0, drawableSize, drawableSize);
|
||||
inputHolder.mExpiryField.setCompoundDrawablesRelative(expiryIcon, null, null, null);
|
||||
if (Utils.hasExpired(loyaltyCard.expiry)) {
|
||||
expiryIcon.setColorFilter(BlendModeColorFilterCompat.createBlendModeColorFilterCompat(Color.RED, BlendModeCompat.SRC_ATOP));
|
||||
inputHolder.mExpiryField.setTextColor(Color.RED);
|
||||
} else if (mDarkModeEnabled) {
|
||||
expiryIcon.setColorFilter(BlendModeColorFilterCompat.createBlendModeColorFilterCompat(Color.WHITE, BlendModeCompat.SRC_ATOP));
|
||||
}
|
||||
inputHolder.mExpiryField.setText(mContext.getString(expiryString, DateFormat.getDateInstance(DateFormat.LONG).format(loyaltyCard.expiry)));
|
||||
inputHolder.mExpiryField.setTextSize(mSettings.getFontSizeMax(mSettings.getSmallFont()));
|
||||
inputHolder.mExpiryField.setText(DateFormat.getDateInstance(DateFormat.LONG).format(loyaltyCard.expiry));
|
||||
inputHolder.mExpiryField.setTextSize(size);
|
||||
} else {
|
||||
inputHolder.mExpiryField.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
inputHolder.mStarIcon.setVisibility((loyaltyCard.starStatus != 0) ? View.VISIBLE : View.GONE);
|
||||
inputHolder.mStarIcon.setVisibility(loyaltyCard.starStatus != 0 ? View.VISIBLE : View.GONE);
|
||||
inputHolder.mCardIcon.setImageBitmap(Utils.generateIcon(mContext, loyaltyCard.store, loyaltyCard.headerColor).getLetterTile());
|
||||
int imageSize = dpToPx( (size*46)/14, mContext);
|
||||
inputHolder.mCardIcon.getLayoutParams().height = imageSize;
|
||||
inputHolder.mCardIcon.getLayoutParams().width = imageSize;
|
||||
inputHolder.mStarIcon.getLayoutParams().height = imageSize;
|
||||
inputHolder.mStarIcon.getLayoutParams().width = imageSize;
|
||||
inputHolder.mTickIcon.getLayoutParams().height = imageSize;
|
||||
inputHolder.mTickIcon.getLayoutParams().width = imageSize;
|
||||
|
||||
/* Changing Padding and Mragin of different views according to font size
|
||||
* Views Included:
|
||||
* a) InformationContainer padding
|
||||
* b) Store left padding
|
||||
* c) Divider Margin
|
||||
* d) note top margin
|
||||
* e) row margin
|
||||
* */
|
||||
int marginPaddingSize = dpToPx((size*16)/14, mContext );
|
||||
inputHolder.mInformationContainer.setPadding(marginPaddingSize, marginPaddingSize, marginPaddingSize, marginPaddingSize);
|
||||
inputHolder.mStoreField.setPadding(marginPaddingSize, 0, 0, 0);
|
||||
LinearLayout.LayoutParams lpDivider = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT );
|
||||
lpDivider.setMargins(0, marginPaddingSize, 0, marginPaddingSize);
|
||||
inputHolder.mDivider.setLayoutParams(lpDivider);
|
||||
LinearLayout.LayoutParams lpNoteField = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT );
|
||||
lpNoteField.setMargins(0, marginPaddingSize/2, 0, 0);
|
||||
inputHolder.mNoteField.setLayoutParams(lpNoteField);
|
||||
LinearLayout.LayoutParams lpRow = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT );
|
||||
lpRow.setMargins(marginPaddingSize/2, marginPaddingSize/2, marginPaddingSize/2, marginPaddingSize/2);
|
||||
inputHolder.mRow.setLayoutParams(lpRow);
|
||||
|
||||
inputHolder.itemView.setActivated(mSelectedItems.get(inputCursor.getPosition(), false));
|
||||
applyIconAnimation(inputHolder, inputCursor.getPosition());
|
||||
applyClickEvents(inputHolder, inputCursor.getPosition());
|
||||
|
||||
}
|
||||
|
||||
private void applyClickEvents(LoyaltyCardListItemViewHolder inputHolder, final int inputPosition)
|
||||
{
|
||||
inputHolder.mThumbnailContainer.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View inputView)
|
||||
{
|
||||
mListener.onIconClicked(inputPosition);
|
||||
}
|
||||
private void applyClickEvents(LoyaltyCardListItemViewHolder inputHolder, final int inputPosition) {
|
||||
inputHolder.mRow.setOnClickListener(inputView -> mListener.onRowClicked(inputPosition));
|
||||
inputHolder.mInformationContainer.setOnClickListener(inputView -> mListener.onRowClicked(inputPosition));
|
||||
|
||||
inputHolder.mRow.setOnLongClickListener(inputView -> {
|
||||
mListener.onRowLongClicked(inputPosition);
|
||||
inputView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||
return true;
|
||||
});
|
||||
|
||||
inputHolder.mRow.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View inputView)
|
||||
{
|
||||
mListener.onRowClicked(inputPosition);
|
||||
}
|
||||
});
|
||||
|
||||
inputHolder.mInformationContainer.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View inputView)
|
||||
{
|
||||
mListener.onRowClicked(inputPosition);
|
||||
}
|
||||
});
|
||||
|
||||
inputHolder.mRow.setOnLongClickListener(new View.OnLongClickListener()
|
||||
{
|
||||
@Override
|
||||
public boolean onLongClick(View inputView)
|
||||
{
|
||||
mListener.onRowLongClicked(inputPosition);
|
||||
inputView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
inputHolder.mInformationContainer.setOnLongClickListener(new View.OnLongClickListener()
|
||||
{
|
||||
@Override
|
||||
public boolean onLongClick(View inputView)
|
||||
{
|
||||
mListener.onRowLongClicked(inputPosition);
|
||||
inputView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||
return true;
|
||||
}
|
||||
inputHolder.mInformationContainer.setOnLongClickListener(inputView -> {
|
||||
mListener.onRowLongClicked(inputPosition);
|
||||
inputView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void applyIconAnimation(LoyaltyCardListItemViewHolder inputHolder, int inputPosition)
|
||||
{
|
||||
if (mSelectedItems.get(inputPosition, false))
|
||||
{
|
||||
private void applyIconAnimation(LoyaltyCardListItemViewHolder inputHolder, int inputPosition) {
|
||||
if (mSelectedItems.get(inputPosition, false)) {
|
||||
inputHolder.mThumbnailFrontContainer.setVisibility(View.GONE);
|
||||
resetIconYAxis(inputHolder.mThumbnailBackContainer);
|
||||
inputHolder.mThumbnailBackContainer.setVisibility(View.VISIBLE);
|
||||
inputHolder.mThumbnailBackContainer.setAlpha(1);
|
||||
if (mCurrentSelectedIndex == inputPosition)
|
||||
{
|
||||
if (mCurrentSelectedIndex == inputPosition) {
|
||||
LoyaltyCardAnimator.flipView(mContext, inputHolder.mThumbnailBackContainer, inputHolder.mThumbnailFrontContainer, true);
|
||||
resetCurrentIndex();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
inputHolder.mThumbnailBackContainer.setVisibility(View.GONE);
|
||||
resetIconYAxis(inputHolder.mThumbnailFrontContainer);
|
||||
inputHolder.mThumbnailFrontContainer.setVisibility(View.VISIBLE);
|
||||
inputHolder.mThumbnailFrontContainer.setAlpha(1);
|
||||
if ((mReverseAllAnimations && mAnimationItemsIndex.get(inputPosition, false)) || mCurrentSelectedIndex == inputPosition)
|
||||
{
|
||||
if ((mReverseAllAnimations && mAnimationItemsIndex.get(inputPosition, false)) || mCurrentSelectedIndex == inputPosition) {
|
||||
LoyaltyCardAnimator.flipView(mContext, inputHolder.mThumbnailBackContainer, inputHolder.mThumbnailFrontContainer, false);
|
||||
resetCurrentIndex();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void resetIconYAxis(View inputView)
|
||||
{
|
||||
if (inputView.getRotationY() != 0)
|
||||
{
|
||||
private void resetIconYAxis(View inputView) {
|
||||
if (inputView.getRotationY() != 0) {
|
||||
inputView.setRotationY(0);
|
||||
}
|
||||
}
|
||||
|
||||
public void resetAnimationIndex()
|
||||
{
|
||||
public void resetAnimationIndex() {
|
||||
mReverseAllAnimations = false;
|
||||
mAnimationItemsIndex.clear();
|
||||
}
|
||||
|
||||
@SuppressFBWarnings("ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD")
|
||||
public void toggleSelection(int inputPosition)
|
||||
{
|
||||
|
||||
public void toggleSelection(int inputPosition) {
|
||||
mCurrentSelectedIndex = inputPosition;
|
||||
if (mSelectedItems.get(inputPosition, false))
|
||||
{
|
||||
if (mSelectedItems.get(inputPosition, false)) {
|
||||
mSelectedItems.delete(inputPosition);
|
||||
mAnimationItemsIndex.delete(inputPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
mSelectedItems.put(inputPosition, true);
|
||||
mAnimationItemsIndex.put(inputPosition, true);
|
||||
}
|
||||
notifyItemChanged(inputPosition);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void clearSelections()
|
||||
{
|
||||
public void clearSelections() {
|
||||
mReverseAllAnimations = true;
|
||||
mSelectedItems.clear();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public int getSelectedItemCount()
|
||||
{
|
||||
public int getSelectedItemCount() {
|
||||
return mSelectedItems.size();
|
||||
}
|
||||
|
||||
public ArrayList<LoyaltyCard> getSelectedItems()
|
||||
{
|
||||
public ArrayList<LoyaltyCard> getSelectedItems() {
|
||||
|
||||
ArrayList<LoyaltyCard> result = new ArrayList<>();
|
||||
|
||||
int i;
|
||||
for(i = 0; i < mSelectedItems.size(); i++)
|
||||
{
|
||||
for (i = 0; i < mSelectedItems.size(); i++) {
|
||||
mCursor.moveToPosition(mSelectedItems.keyAt(i));
|
||||
result.add(LoyaltyCard.toLoyaltyCard(mCursor));
|
||||
}
|
||||
@@ -247,33 +251,29 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
return result;
|
||||
}
|
||||
|
||||
private void resetCurrentIndex()
|
||||
{
|
||||
private void resetCurrentIndex() {
|
||||
mCurrentSelectedIndex = -1;
|
||||
}
|
||||
|
||||
public interface CardAdapterListener
|
||||
{
|
||||
void onIconClicked(int inputPosition);
|
||||
public interface CardAdapterListener {
|
||||
void onRowClicked(int inputPosition);
|
||||
|
||||
void onRowLongClicked(int inputPosition);
|
||||
}
|
||||
|
||||
public class LoyaltyCardListItemViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener
|
||||
{
|
||||
public static class LoyaltyCardListItemViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public TextView mStoreField, mNoteField, mBalanceField, mExpiryField;
|
||||
public LinearLayout mInformationContainer;
|
||||
public ImageView mCardIcon, mStarIcon;
|
||||
public CardView mThumbnailContainer;
|
||||
public ConstraintLayout mRow;
|
||||
public ImageView mCardIcon, mStarIcon, mTickIcon;
|
||||
public MaterialCardView mRow;
|
||||
public View mDivider;
|
||||
public RelativeLayout mThumbnailFrontContainer, mThumbnailBackContainer;
|
||||
|
||||
public LoyaltyCardListItemViewHolder(View inputView)
|
||||
{
|
||||
public LoyaltyCardListItemViewHolder(View inputView, CardAdapterListener inputListener) {
|
||||
super(inputView);
|
||||
mThumbnailContainer = inputView.findViewById(R.id.thumbnail_container);
|
||||
mRow = inputView.findViewById(R.id.row);
|
||||
mDivider = inputView.findViewById(R.id.info_divider);
|
||||
mThumbnailFrontContainer = inputView.findViewById(R.id.thumbnail_front);
|
||||
mThumbnailBackContainer = inputView.findViewById(R.id.thumbnail_back);
|
||||
mInformationContainer = inputView.findViewById(R.id.information_container);
|
||||
@@ -283,15 +283,18 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
mExpiryField = inputView.findViewById(R.id.expiry);
|
||||
mCardIcon = inputView.findViewById(R.id.thumbnail);
|
||||
mStarIcon = inputView.findViewById(R.id.star);
|
||||
inputView.setOnLongClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View inputView)
|
||||
{
|
||||
mListener.onRowLongClicked(getAdapterPosition());
|
||||
inputView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||
return true;
|
||||
mTickIcon = inputView.findViewById(R.id.selected_thumbnail);
|
||||
inputView.setOnLongClickListener(view -> {
|
||||
inputListener.onRowClicked(getAdapterPosition());
|
||||
inputView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int dpToPx(int dp, Context mContext){
|
||||
Resources r = mContext.getResources();
|
||||
int px = (int)TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics());
|
||||
return px;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
15
app/src/main/java/protect/card_locker/LoyaltyCardField.java
Normal file
15
app/src/main/java/protect/card_locker/LoyaltyCardField.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package protect.card_locker;
|
||||
|
||||
public enum LoyaltyCardField {
|
||||
id,
|
||||
store,
|
||||
note,
|
||||
expiry,
|
||||
balance,
|
||||
balanceType,
|
||||
cardId,
|
||||
barcodeId,
|
||||
barcodeType,
|
||||
headerColor,
|
||||
starStatus
|
||||
}
|
||||
@@ -3,13 +3,16 @@ package protect.card_locker;
|
||||
import android.app.Application;
|
||||
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
|
||||
import protect.card_locker.preferences.Settings;
|
||||
|
||||
public class LoyaltyCardLockerApplication extends Application {
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
Settings settings = new Settings(getApplicationContext());
|
||||
Settings settings = new Settings(this);
|
||||
AppCompatDelegate.setDefaultNightMode(settings.getTheme());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,21 +3,26 @@ package protect.card_locker;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
@@ -25,38 +30,45 @@ import android.widget.Toast;
|
||||
import com.google.android.material.appbar.AppBarLayout;
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.constraintlayout.widget.Guideline;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
import androidx.core.widget.TextViewCompat;
|
||||
|
||||
import protect.card_locker.async.TaskHandler;
|
||||
import protect.card_locker.preferences.Settings;
|
||||
|
||||
public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
{
|
||||
public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements GestureDetector.OnGestureListener {
|
||||
private static final String TAG = "Catima";
|
||||
|
||||
private GestureDetector mGestureDetector;
|
||||
|
||||
TextView cardIdFieldView;
|
||||
BottomSheetBehavior behavior;
|
||||
View bottomSheet;
|
||||
View bottomSheetContentWrapper;
|
||||
ImageView bottomSheetButton;
|
||||
TextView noteView;
|
||||
TextView groupsView;
|
||||
TextView balanceView;
|
||||
TextView expiryView;
|
||||
TextView storeName;
|
||||
AppCompatTextView storeName;
|
||||
ImageButton maximizeButton;
|
||||
ImageView barcodeImage;
|
||||
ImageView mainImage;
|
||||
LinearLayout dotIndicator;
|
||||
ImageButton minimizeButton;
|
||||
View collapsingToolbarLayout;
|
||||
AppBarLayout appBarLayout;
|
||||
@@ -69,35 +81,131 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
|
||||
String cardIdString;
|
||||
String barcodeIdString;
|
||||
BarcodeFormat format;
|
||||
CatimaBarcode format;
|
||||
|
||||
FloatingActionButton editButton;
|
||||
|
||||
Guideline centerGuideline;
|
||||
SeekBar barcodeScaler;
|
||||
|
||||
Bitmap frontImageBitmap;
|
||||
Bitmap backImageBitmap;
|
||||
|
||||
boolean starred;
|
||||
boolean backgroundNeedsDarkIcons;
|
||||
boolean barcodeIsFullscreen = false;
|
||||
boolean isFullscreen = false;
|
||||
int mainImageIndex = 0;
|
||||
List<ImageType> imageTypes;
|
||||
private ImageView[] dots;
|
||||
boolean isBarcodeSupported = true;
|
||||
|
||||
private void extractIntentFields(Intent intent)
|
||||
{
|
||||
static final String STATE_IMAGEINDEX = "imageIndex";
|
||||
static final String STATE_FULLSCREEN = "isFullscreen";
|
||||
|
||||
final private TaskHandler mTasks = new TaskHandler();
|
||||
|
||||
@Override
|
||||
public boolean onDown(MotionEvent e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShowPress(MotionEvent e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSingleTapUp(MotionEvent e) {
|
||||
if (imageTypes.size() > 1) {
|
||||
Toast.makeText(this, getString(R.string.swipeToSwitchImages), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLongPress(MotionEvent e) {
|
||||
// Also switch on long-press for accessibility
|
||||
setMainImage(true, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||
Log.d(TAG, "On fling");
|
||||
|
||||
if (Math.abs(velocityY) > (0.75 * Math.abs(velocityX))) {
|
||||
// Vertical swipe
|
||||
// Swipe up
|
||||
if (velocityY < -150) {
|
||||
if (!isFullscreen) {
|
||||
setFullscreen(true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Swipe down
|
||||
if (velocityY > 150) {
|
||||
if (isFullscreen) {
|
||||
setFullscreen(false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else if (Math.abs(velocityX) > (0.75 * Math.abs(velocityY))) {
|
||||
// Horizontal swipe
|
||||
// Swipe right
|
||||
if (velocityX < -150) {
|
||||
setMainImage(true, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Swipe left
|
||||
if (velocityX > 150) {
|
||||
setMainImage(false, false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (imageTypes.size() > 1) {
|
||||
Toast.makeText(this, getString(R.string.swipeToSwitchImages), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
enum ImageType {
|
||||
NONE,
|
||||
BARCODE,
|
||||
IMAGE_FRONT,
|
||||
IMAGE_BACK
|
||||
}
|
||||
|
||||
private void extractIntentFields(Intent intent) {
|
||||
final Bundle b = intent.getExtras();
|
||||
loyaltyCardId = b != null ? b.getInt("id") : 0;
|
||||
Log.d(TAG, "View activity: id=" + loyaltyCardId);
|
||||
}
|
||||
|
||||
private Drawable getIcon(int icon, boolean dark)
|
||||
{
|
||||
private Drawable getDotIcon(boolean active) {
|
||||
Drawable unwrappedIcon = AppCompatResources.getDrawable(this, active ? R.drawable.active_dot : R.drawable.inactive_dot);
|
||||
assert unwrappedIcon != null;
|
||||
Drawable wrappedIcon = DrawableCompat.wrap(unwrappedIcon);
|
||||
DrawableCompat.setTint(wrappedIcon, ContextCompat.getColor(getApplicationContext(), R.color.iconColor));
|
||||
|
||||
return wrappedIcon;
|
||||
}
|
||||
|
||||
private Drawable getIcon(int icon, boolean dark) {
|
||||
Drawable unwrappedIcon = AppCompatResources.getDrawable(this, icon);
|
||||
assert unwrappedIcon != null;
|
||||
Drawable wrappedIcon = DrawableCompat.wrap(unwrappedIcon);
|
||||
if(dark)
|
||||
{
|
||||
if (dark) {
|
||||
DrawableCompat.setTint(wrappedIcon, Color.BLACK);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
DrawableCompat.setTintList(wrappedIcon, null);
|
||||
}
|
||||
|
||||
@@ -105,10 +213,14 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
mainImageIndex = savedInstanceState.getInt(STATE_IMAGEINDEX);
|
||||
isFullscreen = savedInstanceState.getBoolean(STATE_FULLSCREEN);
|
||||
}
|
||||
|
||||
settings = new Settings(this);
|
||||
|
||||
extractIntentFields(getIntent());
|
||||
@@ -120,6 +232,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
|
||||
cardIdFieldView = findViewById(R.id.cardIdView);
|
||||
bottomSheet = findViewById(R.id.bottom_sheet);
|
||||
bottomSheetContentWrapper = findViewById(R.id.bottomSheetContentWrapper);
|
||||
bottomSheetButton = findViewById(R.id.bottomSheetButton);
|
||||
noteView = findViewById(R.id.noteView);
|
||||
groupsView = findViewById(R.id.groupsView);
|
||||
@@ -127,15 +240,17 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
expiryView = findViewById(R.id.expiryView);
|
||||
storeName = findViewById(R.id.storeName);
|
||||
maximizeButton = findViewById(R.id.maximizeButton);
|
||||
barcodeImage = findViewById(R.id.barcode);
|
||||
mainImage = findViewById(R.id.mainImage);
|
||||
dotIndicator = findViewById(R.id.dotIndicator);
|
||||
minimizeButton = findViewById(R.id.minimizeButton);
|
||||
collapsingToolbarLayout = findViewById(R.id.collapsingToolbarLayout);
|
||||
appBarLayout = findViewById(R.id.app_bar_layout);
|
||||
|
||||
centerGuideline = findViewById(R.id.centerGuideline);
|
||||
centerGuideline.setGuidelinePercent(0.5f);
|
||||
barcodeScaler = findViewById(R.id.barcodeScaler);
|
||||
barcodeScaler.setProgress(100);
|
||||
maximizeButton.setBackgroundColor(getThemeColor());
|
||||
minimizeButton.setBackgroundColor(getThemeColor());
|
||||
bottomSheetButton.setBackgroundColor(getThemeColor());
|
||||
barcodeScaler.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
@@ -144,8 +259,18 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
float scale = (float) progress / (float) barcodeScaler.getMax();
|
||||
Log.d(TAG, "Scaling to " + scale);
|
||||
|
||||
redrawBarcodeAfterResize();
|
||||
centerGuideline.setGuidelinePercent(0.5f * scale);
|
||||
if(isFullscreen){
|
||||
loyaltyCard.zoomLevel = progress;
|
||||
db.updateLoyaltyCardZoomLevel(loyaltyCardId, loyaltyCard.zoomLevel);
|
||||
}
|
||||
|
||||
if (format != null && format.isSquare()) {
|
||||
centerGuideline.setGuidelinePercent(0.75f * scale);
|
||||
} else {
|
||||
centerGuideline.setGuidelinePercent(0.5f * scale);
|
||||
}
|
||||
|
||||
drawMainImage(mainImageIndex, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -163,16 +288,6 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
|
||||
// Allow making barcode fullscreen on tap
|
||||
maximizeButton.setOnClickListener(v -> setFullscreen(true));
|
||||
barcodeImage.setOnClickListener(view -> {
|
||||
if (barcodeIsFullscreen)
|
||||
{
|
||||
setFullscreen(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
setFullscreen(true);
|
||||
}
|
||||
});
|
||||
minimizeButton.setOnClickListener(v -> setFullscreen(false));
|
||||
|
||||
editButton = findViewById(R.id.fabEdit);
|
||||
@@ -185,6 +300,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
startActivity(intent);
|
||||
finish();
|
||||
});
|
||||
editButton.bringToFront();
|
||||
|
||||
behavior = BottomSheetBehavior.from(bottomSheet);
|
||||
behavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
|
||||
@@ -197,12 +313,18 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
editButton.hide();
|
||||
} else if (newState == BottomSheetBehavior.STATE_COLLAPSED) {
|
||||
bottomSheetButton.setImageResource(R.drawable.ic_baseline_arrow_drop_up_24);
|
||||
editButton.show();
|
||||
if (!isFullscreen) {
|
||||
editButton.show();
|
||||
}
|
||||
|
||||
// Scroll bottomsheet content back to top
|
||||
bottomSheetContentWrapper.setScrollY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSlide(@NonNull View bottomSheet, float slideOffset) { }
|
||||
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
|
||||
}
|
||||
});
|
||||
|
||||
bottomSheetButton.setOnClickListener(v -> {
|
||||
@@ -212,11 +334,33 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
|
||||
}
|
||||
});
|
||||
|
||||
// Fix bottom sheet content sizing
|
||||
ViewTreeObserver viewTreeObserver = bottomSheet.getViewTreeObserver();
|
||||
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
bottomSheet.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
|
||||
int height = displayMetrics.heightPixels;
|
||||
int maxHeight = height - appBarLayout.getHeight() - bottomSheetButton.getHeight();
|
||||
Log.d(TAG, "Button sheet should be " + maxHeight + " pixels high");
|
||||
bottomSheetContentWrapper.setLayoutParams(
|
||||
new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
maxHeight
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
mGestureDetector = new GestureDetector(this, this);
|
||||
View.OnTouchListener gestureTouchListener = (v, event) -> mGestureDetector.onTouchEvent(event);
|
||||
mainImage.setOnTouchListener(gestureTouchListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent)
|
||||
{
|
||||
public void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
|
||||
Log.i(TAG, "Received new intent");
|
||||
@@ -224,8 +368,15 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume()
|
||||
{
|
||||
public void onSaveInstanceState(Bundle savedInstanceState) {
|
||||
savedInstanceState.putInt(STATE_IMAGEINDEX, mainImageIndex);
|
||||
savedInstanceState.putBoolean(STATE_FULLSCREEN, isFullscreen);
|
||||
super.onSaveInstanceState(savedInstanceState);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
Log.i(TAG, "To view card: " + loyaltyCardId);
|
||||
@@ -234,12 +385,10 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
// '1' is the brightest. We attempt to maximize the brightness
|
||||
// to help barcode readers scan the barcode.
|
||||
Window window = getWindow();
|
||||
if(window != null)
|
||||
{
|
||||
if (window != null) {
|
||||
WindowManager.LayoutParams attributes = window.getAttributes();
|
||||
|
||||
if (settings.useMaxBrightnessDisplayingBarcode())
|
||||
{
|
||||
if (settings.useMaxBrightnessDisplayingBarcode()) {
|
||||
attributes.screenBrightness = 1F;
|
||||
}
|
||||
|
||||
@@ -248,7 +397,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
}
|
||||
|
||||
if (settings.getDisableLockscreenWhileViewingCard()) {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD|
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
|
||||
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
|
||||
}
|
||||
|
||||
@@ -256,8 +405,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
}
|
||||
|
||||
loyaltyCard = db.getLoyaltyCard(loyaltyCardId);
|
||||
if(loyaltyCard == null)
|
||||
{
|
||||
if (loyaltyCard == null) {
|
||||
Log.w(TAG, "Could not lookup loyalty card " + loyaltyCardId);
|
||||
Toast.makeText(this, R.string.noCardExistsError, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
@@ -275,76 +423,69 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
settings.getFontSizeMin(settings.getLargeFont()), settings.getFontSizeMax(settings.getLargeFont()),
|
||||
1, TypedValue.COMPLEX_UNIT_SP);
|
||||
|
||||
if(loyaltyCard.note.length() > 0)
|
||||
{
|
||||
if (loyaltyCard.note.length() > 0) {
|
||||
noteView.setVisibility(View.VISIBLE);
|
||||
noteView.setText(loyaltyCard.note);
|
||||
noteView.setTextSize(settings.getFontSizeMax(settings.getMediumFont()));
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
noteView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
List<Group> loyaltyCardGroups = db.getLoyaltyCardGroups(loyaltyCardId);
|
||||
|
||||
if(loyaltyCardGroups.size() > 0) {
|
||||
StringBuilder groupsString = new StringBuilder();
|
||||
if (loyaltyCardGroups.size() > 0) {
|
||||
List<String> groupNames = new ArrayList<>();
|
||||
for (Group group : loyaltyCardGroups) {
|
||||
groupsString.append(group._id);
|
||||
groupsString.append(" ");
|
||||
groupNames.add(group._id);
|
||||
}
|
||||
|
||||
groupsView.setVisibility(View.VISIBLE);
|
||||
groupsView.setText(getString(R.string.groupsList, groupsString.toString()));
|
||||
groupsView.setText(getString(R.string.groupsList, TextUtils.join(", ", groupNames)));
|
||||
groupsView.setTextSize(settings.getFontSizeMax(settings.getMediumFont()));
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
groupsView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if(!loyaltyCard.balance.equals(new BigDecimal(0))) {
|
||||
if (!loyaltyCard.balance.equals(new BigDecimal(0))) {
|
||||
balanceView.setVisibility(View.VISIBLE);
|
||||
balanceView.setText(getString(R.string.balanceSentence, Utils.formatBalance(this, loyaltyCard.balance, loyaltyCard.balanceType)));
|
||||
balanceView.setTextSize(settings.getFontSizeMax(settings.getMediumFont()));
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
balanceView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if(loyaltyCard.expiry != null) {
|
||||
if (loyaltyCard.expiry != null) {
|
||||
expiryView.setVisibility(View.VISIBLE);
|
||||
|
||||
int expiryString = R.string.expiryStateSentence;
|
||||
if(Utils.hasExpired(loyaltyCard.expiry)) {
|
||||
if (Utils.hasExpired(loyaltyCard.expiry)) {
|
||||
expiryString = R.string.expiryStateSentenceExpired;
|
||||
expiryView.setTextColor(getResources().getColor(R.color.alert));
|
||||
expiryView.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.alert));
|
||||
}
|
||||
expiryView.setText(getString(expiryString, DateFormat.getDateInstance(DateFormat.LONG).format(loyaltyCard.expiry)));
|
||||
expiryView.setTextSize(settings.getFontSizeMax(settings.getMediumFont()));
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
expiryView.setVisibility(View.GONE);
|
||||
}
|
||||
expiryView.setTag(loyaltyCard.expiry);
|
||||
|
||||
if (!barcodeIsFullscreen) {
|
||||
if (!isFullscreen) {
|
||||
makeBottomSheetVisibleIfUseful();
|
||||
}
|
||||
|
||||
storeName.setText(loyaltyCard.store);
|
||||
storeName.setTextSize(settings.getFontSizeMax(settings.getLargeFont()));
|
||||
TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(
|
||||
storeName,
|
||||
settings.getFontSizeMin(settings.getLargeFont()),
|
||||
settings.getFontSizeMax(settings.getLargeFont()),
|
||||
1,
|
||||
TypedValue.COMPLEX_UNIT_DIP);
|
||||
|
||||
int backgroundHeaderColor;
|
||||
if(loyaltyCard.headerColor != null)
|
||||
{
|
||||
if (loyaltyCard.headerColor != null) {
|
||||
backgroundHeaderColor = loyaltyCard.headerColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
backgroundHeaderColor = LetterBitmap.getDefaultColor(this, loyaltyCard.store);
|
||||
}
|
||||
|
||||
@@ -352,12 +493,9 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
appBarLayout.setBackgroundColor(backgroundHeaderColor);
|
||||
|
||||
int textColor;
|
||||
if(Utils.needsDarkForeground(backgroundHeaderColor))
|
||||
{
|
||||
if (Utils.needsDarkForeground(backgroundHeaderColor)) {
|
||||
textColor = Color.BLACK;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
textColor = Color.WHITE;
|
||||
}
|
||||
storeName.setTextColor(textColor);
|
||||
@@ -366,72 +504,69 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
// If the background is very bright, we should use dark icons
|
||||
backgroundNeedsDarkIcons = Utils.needsDarkForeground(backgroundHeaderColor);
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if(actionBar != null)
|
||||
{
|
||||
if (actionBar != null) {
|
||||
actionBar.setHomeAsUpIndicator(getIcon(R.drawable.ic_arrow_back_white, backgroundNeedsDarkIcons));
|
||||
}
|
||||
|
||||
// Make notification area light if dark icons are needed
|
||||
if(Build.VERSION.SDK_INT >= 23)
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
window.getDecorView().setSystemUiVisibility(backgroundNeedsDarkIcons ? View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR : 0);
|
||||
}
|
||||
if(Build.VERSION.SDK_INT >= 21)
|
||||
{
|
||||
window.setStatusBarColor(Color.TRANSPARENT);
|
||||
}
|
||||
window.setStatusBarColor(Color.TRANSPARENT);
|
||||
|
||||
// Set shadow colour of store text so even same color on same color would be readable
|
||||
storeName.setShadowLayer(1, 1, 1, backgroundNeedsDarkIcons ? Color.BLACK : Color.WHITE);
|
||||
|
||||
Boolean isBarcodeSupported = true;
|
||||
|
||||
if (format != null && !BarcodeSelectorActivity.SUPPORTED_BARCODE_TYPES.contains(format.name())) {
|
||||
if (format != null && !format.isSupported()) {
|
||||
isBarcodeSupported = false;
|
||||
|
||||
Toast.makeText(this, getString(R.string.unsupportedBarcodeType), Toast.LENGTH_LONG).show();
|
||||
} else if (format == null) {
|
||||
isBarcodeSupported = false;
|
||||
}
|
||||
|
||||
if(format != null && isBarcodeSupported)
|
||||
{
|
||||
if (!barcodeIsFullscreen) {
|
||||
maximizeButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
barcodeImage.setVisibility(View.VISIBLE);
|
||||
if(barcodeImage.getHeight() == 0)
|
||||
{
|
||||
Log.d(TAG, "ImageView size is not known known at start, waiting for load");
|
||||
// The size of the ImageView is not yet available as it has not
|
||||
// yet been drawn. Wait for it to be drawn so the size is available.
|
||||
redrawBarcodeAfterResize();
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.d(TAG, "ImageView size known known, creating barcode");
|
||||
new BarcodeImageWriterTask(
|
||||
barcodeImage,
|
||||
barcodeIdString != null ? barcodeIdString : cardIdString,
|
||||
format,
|
||||
null,
|
||||
false,
|
||||
null)
|
||||
.execute();
|
||||
imageTypes = new ArrayList<>();
|
||||
|
||||
if (isBarcodeSupported) {
|
||||
imageTypes.add(ImageType.BARCODE);
|
||||
}
|
||||
|
||||
frontImageBitmap = Utils.retrieveCardImage(this, loyaltyCard.id, true);
|
||||
backImageBitmap = Utils.retrieveCardImage(this, loyaltyCard.id, false);
|
||||
|
||||
if (frontImageBitmap != null) {
|
||||
imageTypes.add(ImageType.IMAGE_FRONT);
|
||||
}
|
||||
|
||||
if (backImageBitmap != null) {
|
||||
imageTypes.add(ImageType.IMAGE_BACK);
|
||||
}
|
||||
|
||||
dotIndicator.removeAllViews();
|
||||
if (imageTypes.size() >= 2) {
|
||||
dots = new ImageView[imageTypes.size()];
|
||||
|
||||
for (int i = 0; i < imageTypes.size(); i++) {
|
||||
dots[i] = new ImageView(this);
|
||||
dots[i].setImageDrawable(getDotIcon(false));
|
||||
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
params.setMargins(8, 0, 8, 0);
|
||||
|
||||
dotIndicator.addView(dots[i], params);
|
||||
}
|
||||
|
||||
// Force redraw fullscreen state
|
||||
setFullscreen(barcodeIsFullscreen);
|
||||
}
|
||||
else
|
||||
{
|
||||
maximizeButton.setVisibility(View.GONE);
|
||||
barcodeImage.setVisibility(View.GONE);
|
||||
dotIndicator.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
setFullscreen(isFullscreen);
|
||||
|
||||
db.updateLoyaltyCardLastUsed(loyaltyCard.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (barcodeIsFullscreen)
|
||||
{
|
||||
if (isFullscreen) {
|
||||
setFullscreen(false);
|
||||
return;
|
||||
}
|
||||
@@ -440,16 +575,14 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu)
|
||||
{
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.card_view_menu, menu);
|
||||
|
||||
// Always calculate lockscreen icon, it may need a black color
|
||||
boolean lockBarcodeScreenOrientation = settings.getLockBarcodeScreenOrientation();
|
||||
MenuItem item = menu.findItem(R.id.action_lock_unlock);
|
||||
setOrientatonLock(item, lockBarcodeScreenOrientation);
|
||||
if(lockBarcodeScreenOrientation)
|
||||
{
|
||||
if (lockBarcodeScreenOrientation) {
|
||||
item.setVisible(false);
|
||||
}
|
||||
|
||||
@@ -468,8 +601,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
if (starred) {
|
||||
menu.findItem(R.id.action_star_unstar).setIcon(getIcon(R.drawable.ic_starred_white, backgroundNeedsDarkIcons));
|
||||
menu.findItem(R.id.action_star_unstar).setTitle(R.string.unstar);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
menu.findItem(R.id.action_star_unstar).setIcon(getIcon(R.drawable.ic_unstarred_white, backgroundNeedsDarkIcons));
|
||||
menu.findItem(R.id.action_star_unstar).setTitle(R.string.star);
|
||||
}
|
||||
@@ -477,27 +609,27 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item)
|
||||
{
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
|
||||
switch(id)
|
||||
{
|
||||
switch (id) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
break;
|
||||
|
||||
case R.id.action_share:
|
||||
importURIHelper.startShareIntent(Arrays.asList(loyaltyCard));
|
||||
try {
|
||||
importURIHelper.startShareIntent(Arrays.asList(loyaltyCard));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Toast.makeText(LoyaltyCardViewActivity.this, R.string.failedGeneratingShareURL, Toast.LENGTH_LONG).show();
|
||||
e.printStackTrace();
|
||||
}
|
||||
return true;
|
||||
|
||||
case R.id.action_lock_unlock:
|
||||
if(rotationEnabled)
|
||||
{
|
||||
if (rotationEnabled) {
|
||||
setOrientatonLock(item, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
setOrientatonLock(item, false);
|
||||
}
|
||||
rotationEnabled = !rotationEnabled;
|
||||
@@ -513,8 +645,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void setupOrientation()
|
||||
{
|
||||
private void setupOrientation() {
|
||||
Toolbar portraitToolbar = findViewById(R.id.toolbar);
|
||||
Toolbar landscapeToolbar = findViewById(R.id.toolbar_landscape);
|
||||
|
||||
@@ -542,60 +673,110 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
}
|
||||
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if(actionBar != null)
|
||||
{
|
||||
if (actionBar != null) {
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
}
|
||||
private void setOrientatonLock(MenuItem item, boolean lock)
|
||||
{
|
||||
if(lock)
|
||||
{
|
||||
|
||||
private void setOrientatonLock(MenuItem item, boolean lock) {
|
||||
if (lock) {
|
||||
|
||||
item.setIcon(getIcon(R.drawable.ic_lock_outline_white_24dp, backgroundNeedsDarkIcons));
|
||||
item.setTitle(R.string.unlockScreen);
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
item.setIcon(getIcon(R.drawable.ic_lock_open_white_24dp, backgroundNeedsDarkIcons));
|
||||
item.setTitle(R.string.lockScreen);
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
|
||||
}
|
||||
}
|
||||
|
||||
private void makeBottomSheetVisibleIfUseful()
|
||||
{
|
||||
private void makeBottomSheetVisibleIfUseful() {
|
||||
if (noteView.getVisibility() == View.VISIBLE || groupsView.getVisibility() == View.VISIBLE || balanceView.getVisibility() == View.VISIBLE || expiryView.getVisibility() == View.VISIBLE) {
|
||||
bottomSheet.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
bottomSheet.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void redrawBarcodeAfterResize()
|
||||
{
|
||||
private void drawBarcode() {
|
||||
mTasks.flushTaskList(TaskHandler.TYPE.BARCODE, true, false, false);
|
||||
if (format != null) {
|
||||
barcodeImage.getViewTreeObserver().addOnGlobalLayoutListener(
|
||||
BarcodeImageWriterTask barcodeWriter = new BarcodeImageWriterTask(
|
||||
getApplicationContext(),
|
||||
mainImage,
|
||||
barcodeIdString != null ? barcodeIdString : cardIdString,
|
||||
format,
|
||||
null,
|
||||
false,
|
||||
null);
|
||||
mTasks.executeTask(TaskHandler.TYPE.BARCODE, barcodeWriter);
|
||||
}
|
||||
}
|
||||
|
||||
private void redrawBarcodeAfterResize() {
|
||||
if (format != null) {
|
||||
mainImage.getViewTreeObserver().addOnGlobalLayoutListener(
|
||||
new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
barcodeImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
mainImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
|
||||
Log.d(TAG, "ImageView size now known");
|
||||
new BarcodeImageWriterTask(
|
||||
barcodeImage,
|
||||
barcodeIdString != null ? barcodeIdString : cardIdString,
|
||||
format,
|
||||
null,
|
||||
false,
|
||||
null)
|
||||
.execute();
|
||||
drawBarcode();
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void drawMainImage(int index, boolean waitForResize) {
|
||||
if (imageTypes.isEmpty()) {
|
||||
mainImage.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dots != null) {
|
||||
for (int i = 0; i < dots.length; i++) {
|
||||
dots[i].setImageDrawable(getDotIcon(i == index));
|
||||
}
|
||||
}
|
||||
|
||||
ImageType wantedImageType = imageTypes.get(index);
|
||||
|
||||
if (wantedImageType == ImageType.BARCODE) {
|
||||
if (waitForResize) {
|
||||
redrawBarcodeAfterResize();
|
||||
} else {
|
||||
drawBarcode();
|
||||
}
|
||||
mainImage.setBackgroundColor(Color.WHITE);
|
||||
} else if (wantedImageType == ImageType.IMAGE_FRONT) {
|
||||
mainImage.setImageBitmap(frontImageBitmap);
|
||||
mainImage.setBackgroundColor(Color.TRANSPARENT);
|
||||
} else if (wantedImageType == ImageType.IMAGE_BACK) {
|
||||
mainImage.setImageBitmap(backImageBitmap);
|
||||
mainImage.setBackgroundColor(Color.TRANSPARENT);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown image type: " + wantedImageType);
|
||||
}
|
||||
|
||||
mainImage.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void setMainImage(boolean next, boolean overflow) {
|
||||
int newIndex = mainImageIndex + (next ? 1 : -1);
|
||||
|
||||
if (newIndex >= imageTypes.size() && overflow) {
|
||||
newIndex = 0;
|
||||
}
|
||||
|
||||
if (newIndex == -1 || newIndex >= imageTypes.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mainImageIndex = newIndex;
|
||||
|
||||
drawMainImage(newIndex, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -604,14 +785,16 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
* The purpose of this function is to make sure the barcode can be scanned from the phone
|
||||
* by machines which offer no space to insert the complete device.
|
||||
*/
|
||||
private void setFullscreen(boolean enable)
|
||||
{
|
||||
private void setFullscreen(boolean enabled) {
|
||||
isFullscreen = enabled;
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if(enable)
|
||||
{
|
||||
Log.d(TAG, "Move into of fullscreen");
|
||||
// Prepare redraw after size change
|
||||
redrawBarcodeAfterResize();
|
||||
|
||||
if (enabled && !imageTypes.isEmpty()) {
|
||||
Log.d(TAG, "Move into fullscreen");
|
||||
|
||||
drawMainImage(mainImageIndex, true);
|
||||
|
||||
barcodeScaler.setProgress(loyaltyCard.zoomLevel);
|
||||
|
||||
// Hide maximize and show minimize button and scaler
|
||||
maximizeButton.setVisibility(View.GONE);
|
||||
@@ -619,8 +802,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
barcodeScaler.setVisibility(View.VISIBLE);
|
||||
|
||||
// Hide actionbar
|
||||
if(actionBar != null)
|
||||
{
|
||||
if (actionBar != null) {
|
||||
actionBar.hide();
|
||||
}
|
||||
|
||||
@@ -630,7 +812,6 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
// Or the barcode will be centered instead of on top of the screen
|
||||
// Don't ask me why...
|
||||
appBarLayout.setVisibility(View.INVISIBLE);
|
||||
appBarLayout.setPadding(0, 0, 0, 0);
|
||||
collapsingToolbarLayout.setVisibility(View.GONE);
|
||||
findViewById(R.id.toolbar_landscape).setVisibility(View.GONE);
|
||||
|
||||
@@ -646,28 +827,22 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
);
|
||||
|
||||
// Set current state
|
||||
barcodeIsFullscreen = true;
|
||||
}
|
||||
else if(!enable)
|
||||
{
|
||||
} else {
|
||||
Log.d(TAG, "Move out of fullscreen");
|
||||
|
||||
// Reset center guideline
|
||||
barcodeScaler.setProgress(100);
|
||||
|
||||
// Prepare redraw after size change
|
||||
redrawBarcodeAfterResize();
|
||||
drawMainImage(mainImageIndex, true);
|
||||
|
||||
// Show maximize and hide minimize button and scaler
|
||||
maximizeButton.setVisibility(View.VISIBLE);
|
||||
maximizeButton.setVisibility(imageTypes.isEmpty() ? View.GONE : View.VISIBLE);
|
||||
|
||||
minimizeButton.setVisibility(View.GONE);
|
||||
barcodeScaler.setVisibility(View.GONE);
|
||||
|
||||
// Show actionbar
|
||||
if(actionBar != null)
|
||||
{
|
||||
if (actionBar != null) {
|
||||
actionBar.show();
|
||||
}
|
||||
|
||||
@@ -676,7 +851,6 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
appBarLayout.setVisibility(View.VISIBLE);
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
getWindowManager().getDefaultDisplay().getMetrics(metrics);
|
||||
appBarLayout.setPadding(0, (int) Math.ceil(metrics.density * 24), 0, 0);
|
||||
setupOrientation();
|
||||
|
||||
// Show other UI elements
|
||||
@@ -690,9 +864,8 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
& ~View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
& ~View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
);
|
||||
|
||||
// Set current state
|
||||
barcodeIsFullscreen = false;
|
||||
}
|
||||
|
||||
Log.d("setFullScreen","Is full screen enabled? "+enabled+" Zoom Level = "+barcodeScaler.getProgress());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.database.CursorIndexOutOfBoundsException;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
@@ -16,24 +16,27 @@ import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.view.ActionMode;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.splashscreen.SplashScreen;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import protect.card_locker.preferences.SettingsActivity;
|
||||
|
||||
public class MainActivity extends AppCompatActivity implements LoyaltyCardCursorAdapter.CardAdapterListener, GestureDetector.OnGestureListener
|
||||
public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCardCursorAdapter.CardAdapterListener, GestureDetector.OnGestureListener
|
||||
{
|
||||
private static final String TAG = "Catima";
|
||||
|
||||
@@ -43,14 +46,19 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
|
||||
private Menu mMenu;
|
||||
private GestureDetector mGestureDetector;
|
||||
protected String mFilter = "";
|
||||
protected Object mGroup = null;
|
||||
protected DBHelper.LoyaltyCardOrder mOrder = DBHelper.LoyaltyCardOrder.Alpha;
|
||||
protected DBHelper.LoyaltyCardOrderDirection mOrderDirection = DBHelper.LoyaltyCardOrderDirection.Ascending;
|
||||
protected int selectedTab = 0;
|
||||
private RecyclerView mCardList;
|
||||
private View mHelpText;
|
||||
private View mNoMatchingCardsText;
|
||||
private View mNoGroupCardsText;
|
||||
|
||||
private ActionMode.Callback mCurrentActionModeCallback = new ActionMode.Callback()
|
||||
{
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode inputMode, Menu inputMenu)
|
||||
{
|
||||
public boolean onCreateActionMode(ActionMode inputMode, Menu inputMenu) {
|
||||
inputMode.getMenuInflater().inflate(R.menu.card_longclick_menu, inputMenu);
|
||||
return true;
|
||||
}
|
||||
@@ -62,10 +70,8 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode inputMode, MenuItem inputItem)
|
||||
{
|
||||
if (inputItem.getItemId() == R.id.action_copy_to_clipboard)
|
||||
{
|
||||
public boolean onActionItemClicked(ActionMode inputMode, MenuItem inputItem) {
|
||||
if (inputItem.getItemId() == R.id.action_copy_to_clipboard) {
|
||||
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
|
||||
|
||||
String clipboardData;
|
||||
@@ -93,27 +99,65 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
|
||||
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)
|
||||
{
|
||||
} else if (inputItem.getItemId() == R.id.action_share) {
|
||||
final ImportURIHelper importURIHelper = new ImportURIHelper(MainActivity.this);
|
||||
importURIHelper.startShareIntent(mAdapter.getSelectedItems());
|
||||
try {
|
||||
importURIHelper.startShareIntent(mAdapter.getSelectedItems());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Toast.makeText(MainActivity.this, R.string.failedGeneratingShareURL, Toast.LENGTH_LONG).show();
|
||||
e.printStackTrace();
|
||||
}
|
||||
inputMode.finish();
|
||||
return true;
|
||||
}
|
||||
else if(inputItem.getItemId() == R.id.action_edit)
|
||||
{
|
||||
} else if(inputItem.getItemId() == R.id.action_edit) {
|
||||
if (mAdapter.getSelectedItemCount() != 1) {
|
||||
throw new IllegalArgumentException("Cannot edit more than 1 card at a time");
|
||||
}
|
||||
|
||||
Intent intent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt("id", mAdapter.getSelectedItems().get(0).id);
|
||||
bundle.putBoolean("update", true);
|
||||
bundle.putInt(LoyaltyCardEditActivity.BUNDLE_ID, mAdapter.getSelectedItems().get(0).id);
|
||||
bundle.putBoolean(LoyaltyCardEditActivity.BUNDLE_UPDATE, true);
|
||||
intent.putExtras(bundle);
|
||||
startActivity(intent);
|
||||
inputMode.finish();
|
||||
return true;
|
||||
} else if(inputItem.getItemId() == R.id.action_delete) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
|
||||
// The following may seem weird, but it is necessary to give translators enough flexibility.
|
||||
// For example, in Russian, Android's plural quantity "one" actually refers to "any number ending on 1 but not ending in 11".
|
||||
// So while in English the extra non-plural form seems unnecessary duplication, it is necessary to give translators enough flexibility.
|
||||
// In here, we use the plain string when meaning exactly 1, and otherwise use the plural forms
|
||||
if (mAdapter.getSelectedItemCount() == 1) {
|
||||
builder.setTitle(R.string.deleteTitle);
|
||||
builder.setMessage(R.string.deleteConfirmation);
|
||||
} else {
|
||||
builder.setTitle(getResources().getQuantityString(R.plurals.deleteCardsTitle, mAdapter.getSelectedItemCount(), mAdapter.getSelectedItemCount()));
|
||||
builder.setMessage(getResources().getQuantityString(R.plurals.deleteCardsConfirmation, mAdapter.getSelectedItemCount(), mAdapter.getSelectedItemCount()));
|
||||
}
|
||||
|
||||
builder.setPositiveButton(R.string.confirm, (dialog, which) -> {
|
||||
DBHelper db = new DBHelper(MainActivity.this);
|
||||
|
||||
for (LoyaltyCard loyaltyCard : mAdapter.getSelectedItems()) {
|
||||
Log.e(TAG, "Deleting card: " + loyaltyCard.id);
|
||||
|
||||
db.deleteLoyaltyCard(loyaltyCard.id);
|
||||
|
||||
ShortcutHelper.removeShortcut(MainActivity.this, loyaltyCard.id);
|
||||
}
|
||||
|
||||
TabLayout.Tab tab = ((TabLayout) findViewById(R.id.groups)).getTabAt(selectedTab);
|
||||
mGroup = tab != null ? tab.getTag() : null;
|
||||
|
||||
updateLoyaltyCardList();
|
||||
|
||||
dialog.dismiss();
|
||||
});
|
||||
builder.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.show();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -140,25 +184,26 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
|
||||
protected void onCreate(Bundle inputSavedInstanceState)
|
||||
{
|
||||
super.onCreate(inputSavedInstanceState);
|
||||
SplashScreen.installSplashScreen(this);
|
||||
setTitle(R.string.app_name);
|
||||
setContentView(R.layout.main_activity);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
updateLoyaltyCardList(mFilter, null);
|
||||
|
||||
TabLayout groupsTabLayout = findViewById(R.id.groups);
|
||||
groupsTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
|
||||
@Override
|
||||
public void onTabSelected(TabLayout.Tab tab) {
|
||||
selectedTab = tab.getPosition();
|
||||
updateLoyaltyCardList(mFilter, tab.getTag());
|
||||
|
||||
Log.d("onTabSelected","Tab Position "+tab.getPosition());
|
||||
mGroup = tab.getTag();
|
||||
updateLoyaltyCardList();
|
||||
// Store active tab in Shared Preference to restore next app launch
|
||||
SharedPreferences activeTabPref = getApplicationContext().getSharedPreferences(
|
||||
getString(R.string.sharedpreference_active_tab),
|
||||
Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor activeTabPrefEditor = activeTabPref.edit();
|
||||
activeTabPrefEditor.putInt(getString(R.string.sharedpreference_active_tab), selectedTab);
|
||||
activeTabPrefEditor.putInt(getString(R.string.sharedpreference_active_tab), tab.getPosition());
|
||||
activeTabPrefEditor.apply();
|
||||
}
|
||||
|
||||
@@ -175,20 +220,24 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
|
||||
|
||||
mGestureDetector = new GestureDetector(this, this);
|
||||
|
||||
View.OnTouchListener gestureTouchListener = new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(final View v, final MotionEvent event){
|
||||
return mGestureDetector.onTouchEvent(event);
|
||||
}
|
||||
};
|
||||
View.OnTouchListener gestureTouchListener = (v, event) -> mGestureDetector.onTouchEvent(event);
|
||||
|
||||
final View helpText = findViewById(R.id.helpText);
|
||||
final View noMatchingCardsText = findViewById(R.id.noMatchingCardsText);
|
||||
final View list = findViewById(R.id.list);
|
||||
mHelpText = findViewById(R.id.helpText);
|
||||
mNoMatchingCardsText = findViewById(R.id.noMatchingCardsText);
|
||||
mNoGroupCardsText = findViewById(R.id.noGroupCardsText);
|
||||
mCardList = findViewById(R.id.list);
|
||||
|
||||
helpText.setOnTouchListener(gestureTouchListener);
|
||||
noMatchingCardsText.setOnTouchListener(gestureTouchListener);
|
||||
list.setOnTouchListener(gestureTouchListener);
|
||||
mHelpText.setOnTouchListener(gestureTouchListener);
|
||||
mNoMatchingCardsText.setOnTouchListener(gestureTouchListener);
|
||||
mCardList.setOnTouchListener(gestureTouchListener);
|
||||
mNoGroupCardsText.setOnTouchListener(gestureTouchListener);
|
||||
|
||||
mAdapter = new LoyaltyCardCursorAdapter(this, null, this);
|
||||
mCardList.setAdapter(mAdapter);
|
||||
registerForContextMenu(mCardList);
|
||||
|
||||
mGroup = null;
|
||||
updateLoyaltyCardList();
|
||||
|
||||
/*
|
||||
* This was added for Huawei, but Huawei is just too much of a fucking pain.
|
||||
@@ -247,13 +296,20 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
|
||||
TabLayout groupsTabLayout = findViewById(R.id.groups);
|
||||
updateTabGroups(groupsTabLayout);
|
||||
|
||||
// Restore active tab from Shared Preference
|
||||
// Restore settings from Shared Preference
|
||||
SharedPreferences activeTabPref = getApplicationContext().getSharedPreferences(
|
||||
getString(R.string.sharedpreference_active_tab),
|
||||
Context.MODE_PRIVATE);
|
||||
selectedTab = activeTabPref.getInt(getString(R.string.sharedpreference_active_tab), 0);
|
||||
SharedPreferences sortPref = getApplicationContext().getSharedPreferences(
|
||||
getString(R.string.sharedpreference_sort),
|
||||
Context.MODE_PRIVATE);
|
||||
try {
|
||||
mOrder = DBHelper.LoyaltyCardOrder.valueOf(sortPref.getString(getString(R.string.sharedpreference_sort_order), null));
|
||||
mOrderDirection = DBHelper.LoyaltyCardOrderDirection.valueOf(sortPref.getString(getString(R.string.sharedpreference_sort_direction), null));
|
||||
} catch (IllegalArgumentException | NullPointerException ignored) {}
|
||||
|
||||
Object group = null;
|
||||
mGroup = null;
|
||||
|
||||
if (groupsTabLayout.getTabCount() != 0) {
|
||||
TabLayout.Tab tab = groupsTabLayout.getTabAt(selectedTab);
|
||||
@@ -263,20 +319,22 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
|
||||
|
||||
groupsTabLayout.selectTab(tab);
|
||||
assert tab != null;
|
||||
group = tab.getTag();
|
||||
mGroup = tab.getTag();
|
||||
}
|
||||
updateLoyaltyCardList(mFilter, group);
|
||||
updateLoyaltyCardList();
|
||||
// End of active tab logic
|
||||
|
||||
FloatingActionButton addButton = findViewById(R.id.fabAdd);
|
||||
addButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent i = new Intent(getApplicationContext(), ScanActivity.class);
|
||||
startActivityForResult(i, Utils.BARCODE_SCAN);
|
||||
addButton.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(getApplicationContext(), ScanActivity.class);
|
||||
Bundle bundle = new Bundle();
|
||||
if (selectedTab != 0) {
|
||||
bundle.putString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP, groupsTabLayout.getTabAt(selectedTab).getText().toString());
|
||||
}
|
||||
intent.putExtras(bundle);
|
||||
startActivityForResult(intent, Utils.BARCODE_SCAN);
|
||||
});
|
||||
addButton.bringToFront();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -292,7 +350,7 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
|
||||
MenuItem searchItem = mMenu.findItem(R.id.action_search);
|
||||
searchItem.collapseActionView();
|
||||
}
|
||||
recreate();
|
||||
ActivityCompat.recreate(this);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -302,8 +360,12 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
|
||||
if(!barcodeValues.isEmpty()) {
|
||||
Intent newIntent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
|
||||
Bundle newBundle = new Bundle();
|
||||
newBundle.putString("barcodeType", barcodeValues.format());
|
||||
newBundle.putString("cardId", barcodeValues.content());
|
||||
newBundle.putString(LoyaltyCardEditActivity.BUNDLE_BARCODETYPE, barcodeValues.format());
|
||||
newBundle.putString(LoyaltyCardEditActivity.BUNDLE_CARDID, barcodeValues.content());
|
||||
Bundle inputBundle = intent.getExtras();
|
||||
if (inputBundle != null && inputBundle.getString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP) != null) {
|
||||
newBundle.putString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP, inputBundle.getString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP));
|
||||
}
|
||||
newIntent.putExtras(newBundle);
|
||||
startActivity(newIntent);
|
||||
}
|
||||
@@ -312,72 +374,58 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
|
||||
@Override
|
||||
public void onBackPressed()
|
||||
{
|
||||
if (mMenu == null)
|
||||
{
|
||||
super.onBackPressed();
|
||||
return;
|
||||
}
|
||||
if (mMenu != null) {
|
||||
SearchView searchView = (SearchView) mMenu.findItem(R.id.action_search).getActionView();
|
||||
|
||||
SearchView searchView = (SearchView) mMenu.findItem(R.id.action_search).getActionView();
|
||||
|
||||
if (!searchView.isIconified())
|
||||
{
|
||||
searchView.setIconified(true);
|
||||
} else {
|
||||
TabLayout groupsTabLayout = findViewById(R.id.groups);
|
||||
|
||||
if (groupsTabLayout.getVisibility() == View.VISIBLE && selectedTab != 0) {
|
||||
selectedTab = 0;
|
||||
groupsTabLayout.selectTab(groupsTabLayout.getTabAt(0));
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
if (!searchView.isIconified()) {
|
||||
searchView.setIconified(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
private void updateLoyaltyCardList(String filterText, Object tag)
|
||||
{
|
||||
private void updateLoyaltyCardList() {
|
||||
Group group = null;
|
||||
if (tag != null) {
|
||||
group = (Group) tag;
|
||||
if (mGroup != null) {
|
||||
group = (Group) mGroup;
|
||||
}
|
||||
|
||||
mCardList = findViewById(R.id.list);
|
||||
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
|
||||
mCardList.setLayoutManager(mLayoutManager);
|
||||
mCardList.setItemAnimator(new DefaultItemAnimator());
|
||||
|
||||
final TextView helpText = findViewById(R.id.helpText);
|
||||
final TextView noMatchingCardsText = findViewById(R.id.noMatchingCardsText);
|
||||
|
||||
Cursor cardCursor = mDB.getLoyaltyCardCursor(filterText, group);
|
||||
|
||||
mAdapter = new LoyaltyCardCursorAdapter(this, cardCursor, this);
|
||||
mCardList.setAdapter(mAdapter);
|
||||
|
||||
registerForContextMenu(mCardList);
|
||||
mAdapter.swapCursor(mDB.getLoyaltyCardCursor(mFilter, group, mOrder, mOrderDirection));
|
||||
|
||||
if(mDB.getLoyaltyCardCount() > 0)
|
||||
{
|
||||
// We want the cardList to be visible regardless of the filtered match count
|
||||
// to ensure that the noMatchingCardsText doesn't end up being shown below
|
||||
// the keyboard
|
||||
mCardList.setVisibility(View.VISIBLE);
|
||||
helpText.setVisibility(View.GONE);
|
||||
mHelpText.setVisibility(View.GONE);
|
||||
mNoGroupCardsText.setVisibility(View.GONE);
|
||||
if(mAdapter.getItemCount() > 0)
|
||||
{
|
||||
noMatchingCardsText.setVisibility(View.GONE);
|
||||
mCardList.setVisibility(View.VISIBLE);
|
||||
mNoMatchingCardsText.setVisibility(View.GONE);
|
||||
}
|
||||
else
|
||||
{
|
||||
noMatchingCardsText.setVisibility(View.VISIBLE);
|
||||
mCardList.setVisibility(View.GONE);
|
||||
if (!mFilter.isEmpty()) {
|
||||
// Actual Empty Search Result
|
||||
mNoMatchingCardsText.setVisibility(View.VISIBLE);
|
||||
mNoGroupCardsText.setVisibility(View.GONE);
|
||||
} else {
|
||||
// Group Tab with no Group Cards
|
||||
mNoMatchingCardsText.setVisibility(View.GONE);
|
||||
mNoGroupCardsText.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mCardList.setVisibility(View.GONE);
|
||||
helpText.setVisibility(View.VISIBLE);
|
||||
noMatchingCardsText.setVisibility(View.GONE);
|
||||
mHelpText.setVisibility(View.VISIBLE);
|
||||
mNoMatchingCardsText.setVisibility(View.GONE);
|
||||
mNoGroupCardsText.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (mCurrentActionMode != null) {
|
||||
@@ -414,14 +462,6 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
|
||||
groupsTabLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void openPrivacyPolicy() {
|
||||
Intent browserIntent = new Intent(
|
||||
Intent.ACTION_VIEW,
|
||||
Uri.parse("https://thelastproject.github.io/Catima/privacy-policy")
|
||||
);
|
||||
startActivity(browserIntent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu inputMenu)
|
||||
{
|
||||
@@ -436,14 +476,9 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
|
||||
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
|
||||
searchView.setSubmitButtonEnabled(false);
|
||||
|
||||
searchView.setOnCloseListener(new SearchView.OnCloseListener()
|
||||
{
|
||||
@Override
|
||||
public boolean onClose()
|
||||
{
|
||||
invalidateOptionsMenu();
|
||||
return false;
|
||||
}
|
||||
searchView.setOnCloseListener(() -> {
|
||||
invalidateOptionsMenu();
|
||||
return false;
|
||||
});
|
||||
|
||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener()
|
||||
@@ -461,11 +496,9 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
|
||||
|
||||
TabLayout groupsTabLayout = findViewById(R.id.groups);
|
||||
TabLayout.Tab currentTab = groupsTabLayout.getTabAt(groupsTabLayout.getSelectedTabPosition());
|
||||
mGroup = currentTab != null ? currentTab.getTag() : null;
|
||||
|
||||
updateLoyaltyCardList(
|
||||
mFilter,
|
||||
currentTab != null ? currentTab.getTag() : null
|
||||
);
|
||||
updateLoyaltyCardList();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -479,6 +512,47 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
|
||||
{
|
||||
int id = inputItem.getItemId();
|
||||
|
||||
if (id == R.id.action_sort)
|
||||
{
|
||||
TabLayout.Tab tab = ((TabLayout) findViewById(R.id.groups)).getTabAt(selectedTab);
|
||||
AtomicInteger currentIndex = new AtomicInteger();
|
||||
List<DBHelper.LoyaltyCardOrder> loyaltyCardOrders = Arrays.asList(DBHelper.LoyaltyCardOrder.values());
|
||||
for (int i = 0; i < loyaltyCardOrders.size(); i++) {
|
||||
if (mOrder == loyaltyCardOrders.get(i)) {
|
||||
currentIndex.set(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
|
||||
builder.setTitle(R.string.sort_by);
|
||||
|
||||
final View customLayout = getLayoutInflater().inflate(R.layout.sorting_option, null);
|
||||
builder.setView(customLayout);
|
||||
|
||||
CheckBox ch = (CheckBox) customLayout.findViewById(R.id.checkBox_reverse);
|
||||
ch.setChecked(mOrderDirection == DBHelper.LoyaltyCardOrderDirection.Descending);
|
||||
|
||||
builder.setSingleChoiceItems(R.array.sort_types_array, currentIndex.get(), (dialog, which) -> currentIndex.set(which));
|
||||
|
||||
builder.setPositiveButton(R.string.sort, (dialog, which) -> {
|
||||
if(ch.isChecked()) {
|
||||
setSort(loyaltyCardOrders.get(currentIndex.get()), DBHelper.LoyaltyCardOrderDirection.Descending);
|
||||
}
|
||||
else {
|
||||
setSort(loyaltyCardOrders.get(currentIndex.get()), DBHelper.LoyaltyCardOrderDirection.Ascending);
|
||||
}
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
builder.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
|
||||
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.show();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (id == R.id.action_manage_groups)
|
||||
{
|
||||
Intent i = new Intent(getApplicationContext(), ManageGroupsActivity.class);
|
||||
@@ -500,12 +574,6 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
|
||||
return true;
|
||||
}
|
||||
|
||||
if(id == R.id.action_privacy_policy)
|
||||
{
|
||||
openPrivacyPolicy();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (id == R.id.action_about)
|
||||
{
|
||||
Intent i = new Intent(getApplicationContext(), AboutActivity.class);
|
||||
@@ -516,6 +584,24 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
|
||||
return super.onOptionsItemSelected(inputItem);
|
||||
}
|
||||
|
||||
private void setSort(DBHelper.LoyaltyCardOrder order, DBHelper.LoyaltyCardOrderDirection direction) {
|
||||
// Update values
|
||||
mOrder = order;
|
||||
mOrderDirection = direction;
|
||||
|
||||
// Store in Shared Preference to restore next app launch
|
||||
SharedPreferences sortPref = getApplicationContext().getSharedPreferences(
|
||||
getString(R.string.sharedpreference_sort),
|
||||
Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor sortPrefEditor = sortPref.edit();
|
||||
sortPrefEditor.putString(getString(R.string.sharedpreference_sort_order), order.name());
|
||||
sortPrefEditor.putString(getString(R.string.sharedpreference_sort_direction), direction.name());
|
||||
sortPrefEditor.apply();
|
||||
|
||||
// Update card list
|
||||
updateLoyaltyCardList();
|
||||
}
|
||||
|
||||
protected static boolean isDarkModeEnabled(Context inputContext)
|
||||
{
|
||||
Configuration config = inputContext.getResources().getConfiguration();
|
||||
@@ -548,6 +634,12 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||
mGestureDetector.onTouchEvent(ev);
|
||||
return super.dispatchTouchEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||
Log.d(TAG, "On fling");
|
||||
@@ -563,9 +655,10 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
|
||||
}
|
||||
|
||||
Integer currentTab = groupsTabLayout.getSelectedTabPosition();
|
||||
|
||||
Log.d("onFling","Current Tab "+currentTab);
|
||||
// Swipe right
|
||||
if (velocityX < -150) {
|
||||
Log.d("onFling","Right Swipe detected "+velocityX);
|
||||
Integer nextTab = currentTab + 1;
|
||||
|
||||
if (nextTab == groupsTabLayout.getTabCount()) {
|
||||
@@ -579,6 +672,7 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
|
||||
|
||||
// Swipe left
|
||||
if (velocityX > 150) {
|
||||
Log.d("onFling","Left Swipe detected "+velocityX);
|
||||
Integer nextTab = currentTab - 1;
|
||||
|
||||
if (nextTab < 0) {
|
||||
@@ -616,7 +710,7 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
|
||||
if (count == 0) {
|
||||
mCurrentActionMode.finish();
|
||||
} else {
|
||||
mCurrentActionMode.setTitle("Selected: " + count + " Cards");
|
||||
mCurrentActionMode.setTitle(getResources().getQuantityString(R.plurals.selectedCardCount, count, count));
|
||||
|
||||
MenuItem editItem = mCurrentActionMode.getMenu().findItem(R.id.action_edit);
|
||||
if (count == 1) {
|
||||
@@ -631,21 +725,10 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIconClicked(int inputPosition)
|
||||
{
|
||||
if (mCurrentActionMode == null)
|
||||
{
|
||||
mCurrentActionMode = startSupportActionMode(mCurrentActionModeCallback);
|
||||
}
|
||||
|
||||
toggleSelection(inputPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRowClicked(int inputPosition)
|
||||
{
|
||||
|
||||
if (mAdapter.getSelectedItemCount() > 0)
|
||||
{
|
||||
enableActionMode(inputPosition);
|
||||
@@ -654,7 +737,21 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
|
||||
{
|
||||
Cursor selected = mAdapter.getCursor();
|
||||
selected.moveToPosition(inputPosition);
|
||||
LoyaltyCard loyaltyCard = LoyaltyCard.toLoyaltyCard(selected);
|
||||
// FIXME
|
||||
//
|
||||
// There is a really nasty edge case that can happen when someone taps a card but right
|
||||
// after it swipes (very small window, hard to reproduce). The cursor gets replaced and
|
||||
// may not have a card at the ID number that is returned from onRowClicked.
|
||||
//
|
||||
// The proper fix, obviously, would involve makes sure an onFling can't happen while a
|
||||
// click is being processed. Sadly, I have not yet found a way to make that possible.
|
||||
LoyaltyCard loyaltyCard;
|
||||
try {
|
||||
loyaltyCard = LoyaltyCard.toLoyaltyCard(selected);
|
||||
} catch (CursorIndexOutOfBoundsException e) {
|
||||
Log.w(TAG, "Prevented crash from tap + swipe on ID " + inputPosition + ": " + e);
|
||||
return;
|
||||
}
|
||||
|
||||
Intent i = new Intent(this, LoyaltyCardViewActivity.class);
|
||||
i.setAction("");
|
||||
@@ -662,9 +759,9 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
|
||||
b.putInt("id", loyaltyCard.id);
|
||||
i.putExtras(b);
|
||||
|
||||
ShortcutHelper.updateShortcuts(MainActivity.this, loyaltyCard, i);
|
||||
ShortcutHelper.updateShortcuts(MainActivity.this, loyaltyCard);
|
||||
|
||||
startActivityForResult(i, Utils.MAIN_REQUEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.text.InputType;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
@@ -20,19 +16,25 @@ import java.util.List;
|
||||
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class ManageGroupsActivity extends AppCompatActivity
|
||||
public class ManageGroupsActivity extends CatimaAppCompatActivity implements GroupCursorAdapter.GroupAdapterListener
|
||||
{
|
||||
private static final String TAG = "Catima";
|
||||
|
||||
private final DBHelper db = new DBHelper(this);
|
||||
private final DBHelper mDb = new DBHelper(this);
|
||||
private TextView mHelpText;
|
||||
private RecyclerView mGroupList;
|
||||
GroupCursorAdapter mAdapter;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
setTitle(R.string.groups);
|
||||
setContentView(R.layout.manage_groups_activity);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
@@ -41,23 +43,28 @@ public class ManageGroupsActivity extends AppCompatActivity
|
||||
{
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
updateGroupList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
updateGroupList();
|
||||
|
||||
FloatingActionButton addButton = findViewById(R.id.fabAdd);
|
||||
addButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
createGroup();
|
||||
}
|
||||
});
|
||||
addButton.setOnClickListener(v -> createGroup());
|
||||
addButton.bringToFront();
|
||||
|
||||
mGroupList = findViewById(R.id.list);
|
||||
mHelpText = findViewById(R.id.helpText);
|
||||
|
||||
// Init group list
|
||||
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
|
||||
mGroupList.setLayoutManager(mLayoutManager);
|
||||
mGroupList.setItemAnimator(new DefaultItemAnimator());
|
||||
|
||||
mAdapter = new GroupCursorAdapter(this, null, this);
|
||||
mGroupList.setAdapter(mAdapter);
|
||||
|
||||
updateGroupList();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -67,27 +74,17 @@ public class ManageGroupsActivity extends AppCompatActivity
|
||||
|
||||
private void updateGroupList()
|
||||
{
|
||||
final ListView groupList = findViewById(R.id.list);
|
||||
final TextView helpText = findViewById(R.id.helpText);
|
||||
final DBHelper db = new DBHelper(this);
|
||||
mAdapter.swapCursor(mDb.getGroupCursor());
|
||||
|
||||
if(db.getGroupCount() > 0)
|
||||
{
|
||||
groupList.setVisibility(View.VISIBLE);
|
||||
helpText.setVisibility(View.GONE);
|
||||
}
|
||||
else
|
||||
{
|
||||
groupList.setVisibility(View.GONE);
|
||||
helpText.setVisibility(View.VISIBLE);
|
||||
if (mDb.getGroupCount() == 0) {
|
||||
mGroupList.setVisibility(View.GONE);
|
||||
mHelpText.setVisibility(View.VISIBLE);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Cursor groupCursor = db.getGroupCursor();
|
||||
|
||||
final GroupCursorAdapter adapter = new GroupCursorAdapter(this, groupCursor);
|
||||
groupList.setAdapter(adapter);
|
||||
|
||||
registerForContextMenu(groupList);
|
||||
mGroupList.setVisibility(View.VISIBLE);
|
||||
mHelpText.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void invalidateHomescreenActiveTab()
|
||||
@@ -112,121 +109,35 @@ public class ManageGroupsActivity extends AppCompatActivity
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
public void moveGroupUp(View view) {
|
||||
moveGroup(view, true);
|
||||
}
|
||||
|
||||
public void moveGroupDown(View view) {
|
||||
moveGroup(view, false);
|
||||
}
|
||||
|
||||
public void editGroup(View view) {
|
||||
final String groupName = getGroupname(view);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.enter_group_name);
|
||||
final EditText input = new EditText(this);
|
||||
input.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
input.setText(groupName);
|
||||
builder.setView(input);
|
||||
|
||||
builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
db.updateGroup(groupName, input.getText().toString());
|
||||
updateGroupList();
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.cancel();
|
||||
}
|
||||
});
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.show();
|
||||
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
|
||||
input.requestFocus();
|
||||
}
|
||||
|
||||
public void deleteGroup(View view) {
|
||||
final String groupName = getGroupname(view);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.deleteConfirmationGroup);
|
||||
builder.setMessage(groupName);
|
||||
|
||||
builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
db.deleteGroup(groupName);
|
||||
updateGroupList();
|
||||
// Delete may change ordering, so invalidate
|
||||
invalidateHomescreenActiveTab();
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.cancel();
|
||||
}
|
||||
});
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void createGroup() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this,R.style.AlertDialogTheme);
|
||||
builder.setTitle(R.string.enter_group_name);
|
||||
final EditText input = new EditText(this);
|
||||
input.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
builder.setView(input);
|
||||
|
||||
builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
db.insertGroup(input.getText().toString());
|
||||
updateGroupList();
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.cancel();
|
||||
}
|
||||
builder.setPositiveButton(getString(R.string.ok), (dialog, which) -> {
|
||||
mDb.insertGroup(input.getText().toString());
|
||||
updateGroupList();
|
||||
});
|
||||
builder.setNegativeButton(getString(R.string.cancel), (dialog, which) -> dialog.cancel());
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.show();
|
||||
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
|
||||
input.requestFocus();
|
||||
}
|
||||
|
||||
private String getGroupname(View view) {
|
||||
LinearLayout parentRow = (LinearLayout) view.getParent().getParent();
|
||||
TextView groupNameTextView = parentRow.findViewById(R.id.name);
|
||||
private String getGroupName(View view) {
|
||||
TextView groupNameTextView = view.findViewById(R.id.name);
|
||||
return (String) groupNameTextView.getText();
|
||||
}
|
||||
|
||||
private void moveGroup(View view, boolean up) {
|
||||
final String groupName = getGroupname(view);
|
||||
List<Group> groups = mDb.getGroups();
|
||||
final String groupName = getGroupName(view);
|
||||
|
||||
List<Group> groups = db.getGroups();
|
||||
|
||||
int currentIndex = -1;
|
||||
Integer newIndex;
|
||||
|
||||
// Get current index in group list
|
||||
for (int i = 0; i < groups.size(); i++) {
|
||||
if (groups.get(i)._id.equals(groupName)) {
|
||||
currentIndex = i;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentIndex == -1) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
int currentIndex = mDb.getGroup(groupName).order;
|
||||
int newIndex;
|
||||
|
||||
// Reinsert group in correct position
|
||||
if (up) {
|
||||
@@ -244,7 +155,7 @@ public class ManageGroupsActivity extends AppCompatActivity
|
||||
groups.add(newIndex, group);
|
||||
|
||||
// Update database
|
||||
db.reorderGroups(groups);
|
||||
mDb.reorderGroups(groups);
|
||||
|
||||
// Update UI
|
||||
updateGroupList();
|
||||
@@ -252,4 +163,56 @@ public class ManageGroupsActivity extends AppCompatActivity
|
||||
// Ordering may have changed, so invalidate
|
||||
invalidateHomescreenActiveTab();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMoveDownButtonClicked(View view) {
|
||||
moveGroup(view, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMoveUpButtonClicked(View view) {
|
||||
moveGroup(view, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEditButtonClicked(View view) {
|
||||
final String groupName = getGroupName(view);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.enter_group_name);
|
||||
final EditText input = new EditText(this);
|
||||
input.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
input.setText(groupName);
|
||||
builder.setView(input);
|
||||
|
||||
builder.setPositiveButton(getString(R.string.ok), (dialog, which) -> {
|
||||
String newGroupName = input.getText().toString();
|
||||
mDb.updateGroup(groupName, newGroupName);
|
||||
updateGroupList();
|
||||
});
|
||||
builder.setNegativeButton(getString(R.string.cancel), (dialog, which) -> dialog.cancel());
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.show();
|
||||
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
|
||||
input.requestFocus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeleteButtonClicked(View view) {
|
||||
final String groupName = getGroupName(view);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.deleteConfirmationGroup);
|
||||
builder.setMessage(groupName);
|
||||
|
||||
builder.setPositiveButton(getString(R.string.ok), (dialog, which) -> {
|
||||
mDb.deleteGroup(groupName);
|
||||
updateGroupList();
|
||||
// Delete may change ordering, so invalidate
|
||||
invalidateHomescreenActiveTab();
|
||||
});
|
||||
builder.setNegativeButton(getString(R.string.cancel), (dialog, which) -> dialog.cancel());
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.show();
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,14 @@ package protect.card_locker;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.zxing.ResultPoint;
|
||||
import com.google.zxing.client.android.Intents;
|
||||
@@ -18,7 +21,6 @@ import com.journeyapps.barcodescanner.DecoratedBarcodeView;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
/**
|
||||
@@ -27,23 +29,27 @@ import androidx.appcompat.widget.Toolbar;
|
||||
* Based on https://github.com/journeyapps/zxing-android-embedded/blob/0fdfbce9fb3285e985bad9971c5f7c0a7a334e7b/sample/src/main/java/example/zxing/CustomScannerActivity.java
|
||||
* originally licensed under Apache 2.0
|
||||
*/
|
||||
public class ScanActivity extends AppCompatActivity {
|
||||
public class ScanActivity extends CatimaAppCompatActivity {
|
||||
private static final String TAG = "Catima";
|
||||
|
||||
private CaptureManager capture;
|
||||
private DecoratedBarcodeView barcodeScannerView;
|
||||
|
||||
private String cardId;
|
||||
private String addGroup;
|
||||
private boolean torch = false;
|
||||
|
||||
private void extractIntentFields(Intent intent) {
|
||||
final Bundle b = intent.getExtras();
|
||||
cardId = b != null ? b.getString("cardId") : null;
|
||||
cardId = b != null ? b.getString(LoyaltyCardEditActivity.BUNDLE_CARDID) : null;
|
||||
addGroup = b != null ? b.getString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP) : null;
|
||||
Log.d(TAG, "Scan activity: id=" + cardId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setTitle(R.string.scanCardBarcode);
|
||||
setContentView(R.layout.scan_activity);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
@@ -55,11 +61,14 @@ public class ScanActivity extends AppCompatActivity {
|
||||
|
||||
extractIntentFields(getIntent());
|
||||
|
||||
findViewById(R.id.add_from_image).setOnClickListener(this::addFromImage);
|
||||
findViewById(R.id.add_manually).setOnClickListener(this::addManually);
|
||||
|
||||
barcodeScannerView = findViewById(R.id.zxing_barcode_scanner);
|
||||
|
||||
// Even though we do the actual decoding with the barcodeScannerView
|
||||
// CaptureManager needs to be running to show the camera and scanning bar
|
||||
capture = new CaptureManager(this, barcodeScannerView);
|
||||
capture = new CatimaCaptureManager(this, barcodeScannerView);
|
||||
Intent captureIntent = new Intent();
|
||||
Bundle captureIntentBundle = new Bundle();
|
||||
captureIntentBundle.putBoolean(Intents.Scan.BEEP_ENABLED, false);
|
||||
@@ -72,7 +81,10 @@ public class ScanActivity extends AppCompatActivity {
|
||||
Intent scanResult = new Intent();
|
||||
Bundle scanResultBundle = new Bundle();
|
||||
scanResultBundle.putString(BarcodeSelectorActivity.BARCODE_CONTENTS, result.getText());
|
||||
scanResultBundle.putString(BarcodeSelectorActivity.BARCODE_FORMAT, result.getBarcodeFormat().toString());
|
||||
scanResultBundle.putString(BarcodeSelectorActivity.BARCODE_FORMAT, result.getBarcodeFormat().name());
|
||||
if (addGroup != null) {
|
||||
scanResultBundle.putString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP, addGroup);
|
||||
}
|
||||
scanResult.putExtras(scanResultBundle);
|
||||
ScanActivity.this.setResult(RESULT_OK, scanResult);
|
||||
finish();
|
||||
@@ -114,6 +126,18 @@ public class ScanActivity extends AppCompatActivity {
|
||||
return barcodeScannerView.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu)
|
||||
{
|
||||
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH)) {
|
||||
getMenuInflater().inflate(R.menu.scan_menu, menu);
|
||||
}
|
||||
|
||||
barcodeScannerView.setTorchOff();
|
||||
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item)
|
||||
{
|
||||
@@ -122,6 +146,18 @@ public class ScanActivity extends AppCompatActivity {
|
||||
setResult(Activity.RESULT_CANCELED);
|
||||
finish();
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.action_toggle_flashlight) {
|
||||
if (torch) {
|
||||
torch = false;
|
||||
barcodeScannerView.setTorchOff();
|
||||
item.setTitle(R.string.turn_flashlight_on);
|
||||
item.setIcon(R.drawable.ic_flashlight_off_white_24dp);
|
||||
} else {
|
||||
torch = true;
|
||||
barcodeScannerView.setTorchOn();
|
||||
item.setTitle(R.string.turn_flashlight_off);
|
||||
item.setIcon(R.drawable.ic_flashlight_on_white_24dp);
|
||||
}
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
@@ -132,13 +168,23 @@ public class ScanActivity extends AppCompatActivity {
|
||||
{
|
||||
super.onActivityResult(requestCode, resultCode, intent);
|
||||
|
||||
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent, this);
|
||||
BarcodeValues barcodeValues;
|
||||
|
||||
try {
|
||||
barcodeValues = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent, this);
|
||||
} catch (NullPointerException e) {
|
||||
Toast.makeText(this, R.string.errorReadingImage, Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!barcodeValues.isEmpty()) {
|
||||
Intent manualResult = new Intent();
|
||||
Bundle manualResultBundle = new Bundle();
|
||||
manualResultBundle.putString(BarcodeSelectorActivity.BARCODE_CONTENTS, barcodeValues.content());
|
||||
manualResultBundle.putString(BarcodeSelectorActivity.BARCODE_FORMAT, barcodeValues.format());
|
||||
if (addGroup != null) {
|
||||
manualResultBundle.putString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP, addGroup);
|
||||
}
|
||||
manualResult.putExtras(manualResultBundle);
|
||||
ScanActivity.this.setResult(RESULT_OK, manualResult);
|
||||
finish();
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.pm.ShortcutManager;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Build;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.core.content.pm.ShortcutInfoCompat;
|
||||
import androidx.core.content.pm.ShortcutManagerCompat;
|
||||
import androidx.core.graphics.drawable.IconCompat;
|
||||
|
||||
class ShortcutHelper
|
||||
{
|
||||
// Android documentation says that no more than 5 shortcuts
|
||||
@@ -29,125 +30,113 @@ class ShortcutHelper
|
||||
* card exceeds the max number of shortcuts, then the least recently
|
||||
* used card shortcut is discarded.
|
||||
*/
|
||||
@TargetApi(25)
|
||||
static void updateShortcuts(Context context, LoyaltyCard card, Intent intent)
|
||||
static void updateShortcuts(Context context, LoyaltyCard card)
|
||||
{
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N_MR1)
|
||||
LinkedList<ShortcutInfoCompat> list = new LinkedList<>(ShortcutManagerCompat.getDynamicShortcuts(context));
|
||||
|
||||
DBHelper dbHelper = new DBHelper(context);
|
||||
|
||||
String shortcutId = Integer.toString(card.id);
|
||||
|
||||
// Sort the shortcuts by rank, so working with the relative order will be easier.
|
||||
// This sorts so that the lowest rank is first.
|
||||
Collections.sort(list, Comparator.comparingInt(ShortcutInfoCompat::getRank));
|
||||
|
||||
Integer foundIndex = null;
|
||||
|
||||
for(int index = 0; index < list.size(); index++)
|
||||
{
|
||||
ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class);
|
||||
LinkedList<ShortcutInfo> list = new LinkedList<>(shortcutManager.getDynamicShortcuts());
|
||||
|
||||
String shortcutId = Integer.toString(card.id);
|
||||
|
||||
// Sort the shortcuts by rank, so working with the relative order will be easier.
|
||||
// This sorts so that the lowest rank is first.
|
||||
Collections.sort(list, new Comparator<ShortcutInfo>()
|
||||
if(list.get(index).getId().equals(shortcutId))
|
||||
{
|
||||
@Override
|
||||
public int compare(ShortcutInfo o1, ShortcutInfo o2)
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1)
|
||||
{
|
||||
return o1.getRank() - o2.getRank();
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
// Found the item already
|
||||
foundIndex = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Integer foundIndex = null;
|
||||
|
||||
for(int index = 0; index < list.size(); index++)
|
||||
if(foundIndex != null)
|
||||
{
|
||||
// If the item is already found, then the list needs to be
|
||||
// reordered, so that the selected item now has the lowest
|
||||
// rank, thus letting it survive longer.
|
||||
ShortcutInfoCompat found = list.remove(foundIndex.intValue());
|
||||
list.addFirst(found);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The item is new to the list. First, we need to trim the list
|
||||
// until it is able to accept a new item, then the item is
|
||||
// inserted.
|
||||
while(list.size() >= MAX_SHORTCUTS)
|
||||
{
|
||||
if(list.get(index).getId().equals(shortcutId))
|
||||
{
|
||||
// Found the item already
|
||||
foundIndex = index;
|
||||
break;
|
||||
}
|
||||
list.pollLast();
|
||||
}
|
||||
|
||||
if(foundIndex != null)
|
||||
{
|
||||
// If the item is already found, then the list needs to be
|
||||
// reordered, so that the selected item now has the lowest
|
||||
// rank, thus letting it survive longer.
|
||||
ShortcutInfo found = list.remove(foundIndex.intValue());
|
||||
list.addFirst(found);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The item is new to the list. First, we need to trim the list
|
||||
// until it is able to accept a new item, then the item is
|
||||
// inserted.
|
||||
while(list.size() >= MAX_SHORTCUTS)
|
||||
{
|
||||
list.pollLast();
|
||||
}
|
||||
ShortcutInfoCompat shortcut = createShortcutBuilder(context, card).build();
|
||||
|
||||
ShortcutInfo shortcut = new ShortcutInfo.Builder(context, Integer.toString(card.id))
|
||||
.setShortLabel(card.store)
|
||||
.setLongLabel(card.store)
|
||||
.setIntent(intent)
|
||||
.build();
|
||||
list.addFirst(shortcut);
|
||||
}
|
||||
|
||||
list.addFirst(shortcut);
|
||||
}
|
||||
LinkedList<ShortcutInfoCompat> finalList = new LinkedList<>();
|
||||
|
||||
LinkedList<ShortcutInfo> finalList = new LinkedList<>();
|
||||
// The ranks are now updated; the order in the list is the rank.
|
||||
for(int index = 0; index < list.size(); index++)
|
||||
{
|
||||
ShortcutInfoCompat prevShortcut = list.get(index);
|
||||
|
||||
// The ranks are now updated; the order in the list is the rank.
|
||||
for(int index = 0; index < list.size(); index++)
|
||||
{
|
||||
ShortcutInfo prevShortcut = list.get(index);
|
||||
LoyaltyCard loyaltyCard = dbHelper.getLoyaltyCard(Integer.parseInt(prevShortcut.getId()));
|
||||
|
||||
Intent shortcutIntent = prevShortcut.getIntent();
|
||||
|
||||
// Prevent instances of the view activity from piling up; if one exists let this
|
||||
// one replace it.
|
||||
shortcutIntent.setFlags(shortcutIntent.getFlags() | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
|
||||
ShortcutInfo updatedShortcut = new ShortcutInfo.Builder(context, prevShortcut.getId())
|
||||
.setShortLabel(prevShortcut.getShortLabel())
|
||||
.setLongLabel(prevShortcut.getLongLabel())
|
||||
.setIntent(shortcutIntent)
|
||||
.setIcon(Icon.createWithResource(context, R.drawable.circle))
|
||||
ShortcutInfoCompat updatedShortcut = createShortcutBuilder(context, loyaltyCard)
|
||||
.setRank(index)
|
||||
.build();
|
||||
|
||||
finalList.addLast(updatedShortcut);
|
||||
}
|
||||
|
||||
shortcutManager.setDynamicShortcuts(finalList);
|
||||
finalList.addLast(updatedShortcut);
|
||||
}
|
||||
|
||||
ShortcutManagerCompat.setDynamicShortcuts(context, finalList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the given card id from the app shortcuts, if such a
|
||||
* shortcut exists.
|
||||
*/
|
||||
@TargetApi(25)
|
||||
static void removeShortcut(Context context, int cardId)
|
||||
{
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N_MR1)
|
||||
List<ShortcutInfoCompat> list = ShortcutManagerCompat.getDynamicShortcuts(context);
|
||||
|
||||
String shortcutId = Integer.toString(cardId);
|
||||
|
||||
for(int index = 0; index < list.size(); index++)
|
||||
{
|
||||
ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class);
|
||||
List<ShortcutInfo> list = shortcutManager.getDynamicShortcuts();
|
||||
|
||||
String shortcutId = Integer.toString(cardId);
|
||||
|
||||
for(int index = 0; index < list.size(); index++)
|
||||
if(list.get(index).getId().equals(shortcutId))
|
||||
{
|
||||
if(list.get(index).getId().equals(shortcutId))
|
||||
{
|
||||
list.remove(index);
|
||||
break;
|
||||
}
|
||||
list.remove(index);
|
||||
break;
|
||||
}
|
||||
|
||||
shortcutManager.setDynamicShortcuts(list);
|
||||
}
|
||||
|
||||
ShortcutManagerCompat.setDynamicShortcuts(context, list);
|
||||
}
|
||||
|
||||
static ShortcutInfoCompat.Builder createShortcutBuilder(Context context, LoyaltyCard loyaltyCard) {
|
||||
Intent intent = new Intent(context, LoyaltyCardViewActivity.class);
|
||||
intent.setAction(Intent.ACTION_MAIN);
|
||||
// Prevent instances of the view activity from piling up; if one exists let this
|
||||
// one replace it.
|
||||
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
final Bundle bundle = new Bundle();
|
||||
bundle.putInt("id", loyaltyCard.id);
|
||||
bundle.putBoolean("view", true);
|
||||
intent.putExtras(bundle);
|
||||
|
||||
Bitmap iconBitmap = Utils.generateIcon(context, loyaltyCard, true).getLetterTile();
|
||||
|
||||
IconCompat icon = IconCompat.createWithAdaptiveBitmap(iconBitmap);
|
||||
|
||||
return new ShortcutInfoCompat.Builder(context, Integer.toString(loyaltyCard.id))
|
||||
.setShortLabel(loyaltyCard.store)
|
||||
.setLongLabel(loyaltyCard.store)
|
||||
.setIntent(intent)
|
||||
.setIcon(icon);
|
||||
}
|
||||
}
|
||||
|
||||
15
app/src/main/java/protect/card_locker/SimpleTextWatcher.java
Normal file
15
app/src/main/java/protect/card_locker/SimpleTextWatcher.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
|
||||
public class SimpleTextWatcher implements TextWatcher {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) { }
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) { }
|
||||
}
|
||||
@@ -3,8 +3,15 @@ package protect.card_locker;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ImageDecoder;
|
||||
import android.graphics.Matrix;
|
||||
import android.os.Build;
|
||||
import android.os.LocaleList;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
@@ -17,6 +24,10 @@ import com.google.zxing.RGBLuminanceSource;
|
||||
import com.google.zxing.Result;
|
||||
import com.google.zxing.common.HybridBinarizer;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.text.NumberFormat;
|
||||
@@ -24,8 +35,12 @@ import java.util.Calendar;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.exifinterface.media.ExifInterface;
|
||||
import protect.card_locker.preferences.Settings;
|
||||
|
||||
public class Utils {
|
||||
private static final String TAG = "Catima";
|
||||
@@ -35,9 +50,19 @@ public class Utils {
|
||||
public static final int SELECT_BARCODE_REQUEST = 2;
|
||||
public static final int BARCODE_SCAN = 3;
|
||||
public static final int BARCODE_IMPORT_FROM_IMAGE_FILE = 4;
|
||||
public static final int CARD_IMAGE_FROM_CAMERA_FRONT = 5;
|
||||
public static final int CARD_IMAGE_FROM_CAMERA_BACK = 6;
|
||||
public static final int CARD_IMAGE_FROM_FILE_FRONT = 7;
|
||||
public static final int CARD_IMAGE_FROM_FILE_BACK = 8;
|
||||
|
||||
static final double LUMINANCE_MIDPOINT = 0.5;
|
||||
|
||||
static final int BITMAP_SIZE_BIG = 512;
|
||||
|
||||
static public LetterBitmap generateIcon(Context context, LoyaltyCard loyaltyCard, boolean forShortcut) {
|
||||
return generateIcon(context, loyaltyCard.store, loyaltyCard.headerColor, forShortcut);
|
||||
}
|
||||
|
||||
static public LetterBitmap generateIcon(Context context, String store, Integer backgroundColor) {
|
||||
return generateIcon(context, store, backgroundColor, false);
|
||||
}
|
||||
@@ -81,7 +106,12 @@ public class Utils {
|
||||
|
||||
Bitmap bitmap;
|
||||
try {
|
||||
bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), intent.getData());
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
ImageDecoder.Source image_source = ImageDecoder.createSource(context.getContentResolver(), intent.getData());
|
||||
bitmap = ImageDecoder.decodeBitmap(image_source, (decoder, info, source) -> decoder.setMutableRequired(true));
|
||||
} else {
|
||||
bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), intent.getData());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error getting data from image file");
|
||||
e.printStackTrace();
|
||||
@@ -122,6 +152,21 @@ public class Utils {
|
||||
}
|
||||
|
||||
static public BarcodeValues getBarcodeFromBitmap(Bitmap bitmap) {
|
||||
// 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++) {
|
||||
try {
|
||||
return Utils.getBarcodeFromBitmapReal(bitmap);
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.w(TAG, "Ran OOM in getBarcodeFromBitmap! 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);
|
||||
}
|
||||
}
|
||||
|
||||
// Give up
|
||||
return new BarcodeValues(null, null);
|
||||
}
|
||||
|
||||
static private BarcodeValues getBarcodeFromBitmapReal(Bitmap bitmap) {
|
||||
// In order to decode it, the Bitmap must first be converted into a pixel array...
|
||||
int[] intArray = new int[bitmap.getWidth() * bitmap.getHeight()];
|
||||
bitmap.getPixels(intArray, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
|
||||
@@ -208,4 +253,152 @@ public class Utils {
|
||||
// Parse as BigDecimal
|
||||
return new BigDecimal(value);
|
||||
}
|
||||
|
||||
static public byte[] bitmapToByteArray(Bitmap bitmap) {
|
||||
if (bitmap == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);
|
||||
return bos.toByteArray();
|
||||
}
|
||||
|
||||
static public Bitmap resizeBitmap(Bitmap bitmap) {
|
||||
if (bitmap == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
double maxSize = BITMAP_SIZE_BIG;
|
||||
|
||||
double width = bitmap.getWidth();
|
||||
double height = bitmap.getHeight();
|
||||
|
||||
if (height > width) {
|
||||
double scale = height / maxSize;
|
||||
height = maxSize;
|
||||
width = width / scale;
|
||||
} else if (width > height) {
|
||||
double scale = width / maxSize;
|
||||
width = maxSize;
|
||||
height = height / scale;
|
||||
} else {
|
||||
height = maxSize;
|
||||
width = maxSize;
|
||||
}
|
||||
|
||||
return Bitmap.createScaledBitmap(bitmap, (int) Math.round(width), (int) Math.round(height), true);
|
||||
}
|
||||
|
||||
static public Bitmap rotateBitmap(Bitmap bitmap, ExifInterface exifInterface) {
|
||||
switch (exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)) {
|
||||
case ExifInterface.ORIENTATION_ROTATE_90:
|
||||
return rotateBitmap(bitmap, 90f);
|
||||
case ExifInterface.ORIENTATION_ROTATE_180:
|
||||
return rotateBitmap(bitmap, 180f);
|
||||
case ExifInterface.ORIENTATION_ROTATE_270:
|
||||
return rotateBitmap(bitmap, 270f);
|
||||
default:
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
|
||||
static public Bitmap rotateBitmap(Bitmap bitmap, float rotation) {
|
||||
if (rotation == 0) {
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.postRotate(rotation);
|
||||
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
||||
}
|
||||
|
||||
static public String getCardImageFileName(int loyaltyCardId, boolean front) {
|
||||
StringBuilder cardImageFileNameBuilder = new StringBuilder();
|
||||
|
||||
cardImageFileNameBuilder.append("card_");
|
||||
cardImageFileNameBuilder.append(loyaltyCardId);
|
||||
cardImageFileNameBuilder.append("_");
|
||||
if (front) {
|
||||
cardImageFileNameBuilder.append("front");
|
||||
} else {
|
||||
cardImageFileNameBuilder.append("back");
|
||||
}
|
||||
cardImageFileNameBuilder.append(".png");
|
||||
|
||||
return cardImageFileNameBuilder.toString();
|
||||
}
|
||||
|
||||
static public void saveCardImage(Context context, Bitmap bitmap, String fileName) throws FileNotFoundException {
|
||||
if (bitmap == null) {
|
||||
context.deleteFile(fileName);
|
||||
return;
|
||||
}
|
||||
|
||||
FileOutputStream out = context.openFileOutput(fileName, Context.MODE_PRIVATE);
|
||||
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
|
||||
}
|
||||
|
||||
static public void saveCardImage(Context context, Bitmap bitmap, int loyaltyCardId, boolean front) throws FileNotFoundException {
|
||||
saveCardImage(context, bitmap, getCardImageFileName(loyaltyCardId, front));
|
||||
}
|
||||
|
||||
static public Bitmap retrieveCardImage(Context context, String fileName) {
|
||||
FileInputStream in;
|
||||
try {
|
||||
in = context.openFileInput(fileName);
|
||||
} catch (FileNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return BitmapFactory.decodeStream(in);
|
||||
}
|
||||
|
||||
static public Bitmap retrieveCardImage(Context context, int loyaltyCardId, boolean front) {
|
||||
return retrieveCardImage(context, getCardImageFileName(loyaltyCardId, front));
|
||||
}
|
||||
|
||||
static public <T,U> U mapGetOrDefault(Map<T,U> map, T key, U defaultValue) {
|
||||
U value = map.get(key);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static public Locale stringToLocale(String localeString) {
|
||||
String[] localeParts = localeString.split("-");
|
||||
if (localeParts.length == 1) {
|
||||
return new Locale(localeParts[0]);
|
||||
}
|
||||
|
||||
if (localeParts[1].startsWith("r")) {
|
||||
localeParts[1] = localeParts[1].substring(1);
|
||||
}
|
||||
return new Locale(localeParts[0], localeParts[1]);
|
||||
}
|
||||
|
||||
static public Context updateBaseContextLocale(Context context) {
|
||||
Settings settings = new Settings(context);
|
||||
|
||||
Locale chosenLocale = settings.getLocale();
|
||||
|
||||
Resources res = context.getResources();
|
||||
Configuration configuration = res.getConfiguration();
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||
configuration.locale = chosenLocale != null ? chosenLocale : Locale.getDefault();
|
||||
res.updateConfiguration(configuration, res.getDisplayMetrics());
|
||||
return context;
|
||||
}
|
||||
|
||||
LocaleList localeList = chosenLocale != null ? new LocaleList(chosenLocale) : LocaleList.getDefault();
|
||||
LocaleList.setDefault(localeList);
|
||||
configuration.setLocales(localeList);
|
||||
return context.createConfigurationContext(configuration);
|
||||
}
|
||||
|
||||
static public long getUnixTime() {
|
||||
return System.currentTimeMillis() / 1000;
|
||||
}
|
||||
}
|
||||
|
||||
35
app/src/main/java/protect/card_locker/ZipUtils.java
Normal file
35
app/src/main/java/protect/card_locker/ZipUtils.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
|
||||
import net.lingala.zip4j.io.inputstream.ZipInputStream;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class ZipUtils {
|
||||
static public String read(ZipInputStream zipInputStream) throws IOException {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
Reader reader = new BufferedReader(new InputStreamReader(zipInputStream, StandardCharsets.UTF_8));
|
||||
int c;
|
||||
while ((c = reader.read()) != -1) {
|
||||
stringBuilder.append((char) c);
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
static public Bitmap readImage(ZipInputStream zipInputStream) {
|
||||
return BitmapFactory.decodeStream(zipInputStream);
|
||||
}
|
||||
|
||||
static public JSONObject readJSON(ZipInputStream zipInputStream) throws IOException, JSONException {
|
||||
return new JSONObject(read(zipInputStream));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package protect.card_locker.async;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public interface CompatCallable<T> extends Callable<T> {
|
||||
void onPostExecute(Object result);
|
||||
void onPreExecute();
|
||||
}
|
||||
174
app/src/main/java/protect/card_locker/async/TaskHandler.java
Normal file
174
app/src/main/java/protect/card_locker/async/TaskHandler.java
Normal file
@@ -0,0 +1,174 @@
|
||||
package protect.card_locker.async;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* AsyncTask has been deprecated so this provides very rudimentary compatibility without
|
||||
* needing to redo too many Parts.
|
||||
*
|
||||
* However this is a much, much more cooperative Behaviour than before so
|
||||
* the callers need to ensure we do NOT rely on forced cancellation and feed less into the
|
||||
* ThreadPools so we don't OOM/Overload the Users device
|
||||
*
|
||||
* This assumes single-threaded callers.
|
||||
*/
|
||||
public class TaskHandler {
|
||||
|
||||
public enum TYPE {
|
||||
BARCODE,
|
||||
IMPORT,
|
||||
EXPORT
|
||||
}
|
||||
|
||||
HashMap<TYPE, ThreadPoolExecutor> executors = generateExecutors();
|
||||
|
||||
final private HashMap<TYPE, LinkedList<Future<?>>> taskList = new HashMap<>();
|
||||
|
||||
private final Handler uiHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
private HashMap<TYPE, ThreadPoolExecutor> generateExecutors() {
|
||||
HashMap<TYPE, ThreadPoolExecutor> initExecutors = new HashMap<>();
|
||||
for (TYPE type : TYPE.values()) {
|
||||
replaceExecutor(initExecutors, type, false, false);
|
||||
}
|
||||
return initExecutors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces (or initializes) an Executor with a clean (new) one
|
||||
* @param executors Map Reference
|
||||
* @param type Which Queue
|
||||
* @param flushOld attempt shutdown
|
||||
* @param waitOnOld wait for Termination
|
||||
*/
|
||||
private void replaceExecutor(HashMap<TYPE, ThreadPoolExecutor> executors, TYPE type, Boolean flushOld, Boolean waitOnOld) {
|
||||
ThreadPoolExecutor oldExecutor = executors.get(type);
|
||||
if (oldExecutor != null) {
|
||||
if (flushOld) {
|
||||
oldExecutor.shutdownNow();
|
||||
}
|
||||
if (waitOnOld) {
|
||||
try {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
oldExecutor.awaitTermination(5, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
executors.put(type, (ThreadPoolExecutor) Executors.newCachedThreadPool());
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a Pseudo-AsyncTask for execution
|
||||
*
|
||||
* @param type Queue
|
||||
* @param callable PseudoAsyncTask
|
||||
*/
|
||||
public void executeTask(TYPE type, CompatCallable<?> callable) {
|
||||
Runnable runner = () -> {
|
||||
try {
|
||||
// Run on the UI Thread
|
||||
uiHandler.post(callable::onPreExecute);
|
||||
|
||||
// Background
|
||||
final Object result = callable.call();
|
||||
|
||||
// Post results on UI Thread so we can show them
|
||||
uiHandler.post(() -> {
|
||||
callable.onPostExecute(result);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
};
|
||||
|
||||
LinkedList<Future<?>> list = taskList.get(type);
|
||||
|
||||
if (list == null) {
|
||||
list = new LinkedList<>();
|
||||
}
|
||||
|
||||
ThreadPoolExecutor executor = executors.get(type);
|
||||
|
||||
if (executor != null) {
|
||||
Future<?> task = executor.submit(runner);
|
||||
// Test Queue Cancellation:
|
||||
// task.cancel(true);
|
||||
list.push(task);
|
||||
taskList.put(type, list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This will attempt to cancel a currently running list of Tasks
|
||||
* Useful to ignore scheduled tasks - but not able to hard-stop tasks that are running
|
||||
*
|
||||
* @param type Which Queue to target
|
||||
* @param forceCancel attempt to close the Queue and force-replace it after
|
||||
* @param waitForFinish wait and return after the old executor finished. Times out after 5s
|
||||
* @param waitForCurrentlyRunning wait before cancelling tasks. Useful for tests.
|
||||
*/
|
||||
public void flushTaskList(TYPE type, Boolean forceCancel, Boolean waitForFinish, Boolean waitForCurrentlyRunning) {
|
||||
// Only used for Testing
|
||||
if (waitForCurrentlyRunning) {
|
||||
ThreadPoolExecutor oldExecutor = executors.get(type);
|
||||
|
||||
if (oldExecutor != null) {
|
||||
try {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
oldExecutor.awaitTermination(5, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to cancel known Tasks and clean the List
|
||||
LinkedList<Future<?>> tasks = taskList.get(type);
|
||||
if (tasks != null) {
|
||||
for (Future<?> task : tasks) {
|
||||
if (!task.isDone() || !task.isCancelled()) {
|
||||
// Interrupt any Task we can
|
||||
task.cancel(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
tasks = new LinkedList<>();
|
||||
taskList.put(type, tasks);
|
||||
|
||||
if (forceCancel || waitForFinish) {
|
||||
ThreadPoolExecutor oldExecutor = executors.get(type);
|
||||
|
||||
if (oldExecutor != null) {
|
||||
if (forceCancel) {
|
||||
if (waitForFinish) {
|
||||
try {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
oldExecutor.awaitTermination(5, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
oldExecutor.shutdownNow();
|
||||
replaceExecutor(executors, type, true, false);
|
||||
} else {
|
||||
try {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
oldExecutor.awaitTermination(5, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import net.lingala.zip4j.io.outputstream.ZipOutputStream;
|
||||
import net.lingala.zip4j.model.ZipParameters;
|
||||
import net.lingala.zip4j.model.enums.EncryptionMethod;
|
||||
import net.lingala.zip4j.util.InternalZipConstants;
|
||||
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.CSVPrinter;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.Group;
|
||||
import protect.card_locker.LoyaltyCard;
|
||||
import protect.card_locker.Utils;
|
||||
|
||||
/**
|
||||
* Class for exporting the database into CSV (Comma Separate Values)
|
||||
* format.
|
||||
*/
|
||||
public class CatimaExporter implements Exporter
|
||||
{
|
||||
public void exportData(Context context, DBHelper db, OutputStream output,char[] password) throws IOException, InterruptedException
|
||||
{
|
||||
// Necessary vars
|
||||
int readLen;
|
||||
byte[] readBuffer = new byte[InternalZipConstants.BUFF_SIZE];
|
||||
|
||||
// Create zip output stream
|
||||
ZipOutputStream zipOutputStream;
|
||||
|
||||
if(password!=null && password.length>0){
|
||||
zipOutputStream = new ZipOutputStream(output,password);
|
||||
}
|
||||
else{
|
||||
zipOutputStream = new ZipOutputStream(output);
|
||||
}
|
||||
|
||||
// Generate CSV
|
||||
ByteArrayOutputStream catimaOutputStream = new ByteArrayOutputStream();
|
||||
OutputStreamWriter catimaOutputStreamWriter = new OutputStreamWriter(catimaOutputStream, StandardCharsets.UTF_8);
|
||||
writeCSV(db, catimaOutputStreamWriter);
|
||||
|
||||
// Add CSV to zip file
|
||||
ZipParameters csvZipParameters = createZipParameters("catima.csv",password);
|
||||
zipOutputStream.putNextEntry(csvZipParameters);
|
||||
InputStream csvInputStream = new ByteArrayInputStream(catimaOutputStream.toByteArray());
|
||||
while ((readLen = csvInputStream.read(readBuffer)) != -1) {
|
||||
zipOutputStream.write(readBuffer, 0, readLen);
|
||||
}
|
||||
zipOutputStream.closeEntry();
|
||||
|
||||
// Loop over all cards again
|
||||
Cursor cardCursor = db.getLoyaltyCardCursor();
|
||||
while(cardCursor.moveToNext())
|
||||
{
|
||||
// For each card
|
||||
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cardCursor);
|
||||
|
||||
// Prepare looping over both front and back image
|
||||
boolean[] frontValues = new boolean[2];
|
||||
frontValues[0] = true;
|
||||
frontValues[1] = false;
|
||||
|
||||
// For each image
|
||||
for (boolean front : frontValues) {
|
||||
// If it exists, add to the .zip file
|
||||
Bitmap image = Utils.retrieveCardImage(context, card.id, front);
|
||||
if (image != null) {
|
||||
ZipParameters imageZipParameters = createZipParameters(Utils.getCardImageFileName(card.id, front),password);
|
||||
zipOutputStream.putNextEntry(imageZipParameters);
|
||||
InputStream imageInputStream = new ByteArrayInputStream(Utils.bitmapToByteArray(image));
|
||||
while ((readLen = imageInputStream.read(readBuffer)) != -1) {
|
||||
zipOutputStream.write(readBuffer, 0, readLen);
|
||||
}
|
||||
zipOutputStream.closeEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zipOutputStream.close();
|
||||
}
|
||||
|
||||
private ZipParameters createZipParameters(String fileName, char[] password){
|
||||
ZipParameters zipParameters = new ZipParameters();
|
||||
zipParameters.setFileNameInZip(fileName);
|
||||
if(password!=null && password.length>0){
|
||||
zipParameters.setEncryptFiles(true);
|
||||
zipParameters.setEncryptionMethod(EncryptionMethod.AES);
|
||||
}
|
||||
return zipParameters;
|
||||
}
|
||||
|
||||
private void writeCSV(DBHelper db, OutputStreamWriter output) throws IOException, InterruptedException {
|
||||
CSVPrinter printer = new CSVPrinter(output, CSVFormat.RFC4180);
|
||||
|
||||
// Print the version
|
||||
printer.printRecord("2");
|
||||
|
||||
printer.println();
|
||||
|
||||
// Print the header for groups
|
||||
printer.printRecord(DBHelper.LoyaltyCardDbGroups.ID);
|
||||
|
||||
Cursor groupCursor = db.getGroupCursor();
|
||||
|
||||
while(groupCursor.moveToNext())
|
||||
{
|
||||
Group group = Group.toGroup(groupCursor);
|
||||
|
||||
printer.printRecord(group._id);
|
||||
|
||||
if(Thread.currentThread().isInterrupted())
|
||||
{
|
||||
throw new InterruptedException();
|
||||
}
|
||||
}
|
||||
|
||||
groupCursor.close();
|
||||
|
||||
// Print an empty line
|
||||
printer.println();
|
||||
|
||||
// Print the header for cards
|
||||
printer.printRecord(DBHelper.LoyaltyCardDbIds.ID,
|
||||
DBHelper.LoyaltyCardDbIds.STORE,
|
||||
DBHelper.LoyaltyCardDbIds.NOTE,
|
||||
DBHelper.LoyaltyCardDbIds.EXPIRY,
|
||||
DBHelper.LoyaltyCardDbIds.BALANCE,
|
||||
DBHelper.LoyaltyCardDbIds.BALANCE_TYPE,
|
||||
DBHelper.LoyaltyCardDbIds.CARD_ID,
|
||||
DBHelper.LoyaltyCardDbIds.BARCODE_ID,
|
||||
DBHelper.LoyaltyCardDbIds.BARCODE_TYPE,
|
||||
DBHelper.LoyaltyCardDbIds.HEADER_COLOR,
|
||||
DBHelper.LoyaltyCardDbIds.STAR_STATUS,
|
||||
DBHelper.LoyaltyCardDbIds.LAST_USED);
|
||||
|
||||
Cursor cardCursor = db.getLoyaltyCardCursor();
|
||||
|
||||
while(cardCursor.moveToNext())
|
||||
{
|
||||
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cardCursor);
|
||||
|
||||
printer.printRecord(card.id,
|
||||
card.store,
|
||||
card.note,
|
||||
card.expiry != null ? card.expiry.getTime() : "",
|
||||
card.balance,
|
||||
card.balanceType,
|
||||
card.cardId,
|
||||
card.barcodeId,
|
||||
card.barcodeType != null ? card.barcodeType.name() : "",
|
||||
card.headerColor,
|
||||
card.starStatus,
|
||||
card.lastUsed);
|
||||
|
||||
if(Thread.currentThread().isInterrupted())
|
||||
{
|
||||
throw new InterruptedException();
|
||||
}
|
||||
}
|
||||
|
||||
cardCursor.close();
|
||||
|
||||
// Print an empty line
|
||||
printer.println();
|
||||
|
||||
// Print the header for card group mappings
|
||||
printer.printRecord(DBHelper.LoyaltyCardDbIdsGroups.cardID,
|
||||
DBHelper.LoyaltyCardDbIdsGroups.groupID);
|
||||
|
||||
Cursor cardCursor2 = db.getLoyaltyCardCursor();
|
||||
|
||||
while(cardCursor2.moveToNext())
|
||||
{
|
||||
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cardCursor2);
|
||||
|
||||
for (Group group : db.getLoyaltyCardGroups(card.id)) {
|
||||
printer.printRecord(card.id, group._id);
|
||||
}
|
||||
|
||||
if(Thread.currentThread().isInterrupted())
|
||||
{
|
||||
throw new InterruptedException();
|
||||
}
|
||||
}
|
||||
|
||||
cardCursor2.close();
|
||||
|
||||
printer.close();
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,38 @@
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
import net.lingala.zip4j.io.inputstream.ZipInputStream;
|
||||
import net.lingala.zip4j.model.LocalFileHeader;
|
||||
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.CSVParser;
|
||||
import org.apache.commons.csv.CSVRecord;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.StringReader;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import protect.card_locker.CatimaBarcode;
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.FormatException;
|
||||
import protect.card_locker.Group;
|
||||
import protect.card_locker.Utils;
|
||||
import protect.card_locker.ZipUtils;
|
||||
|
||||
/**
|
||||
* Class for importing a database from CSV (Comma Separate Values)
|
||||
@@ -30,10 +41,39 @@ import protect.card_locker.Group;
|
||||
* The database's loyalty cards are expected to appear in the CSV data.
|
||||
* A header is expected for the each table showing the names of the columns.
|
||||
*/
|
||||
public class CsvDatabaseImporter implements DatabaseImporter
|
||||
public class CatimaImporter implements Importer
|
||||
{
|
||||
public void importData(DBHelper db, InputStream input) throws IOException, FormatException, InterruptedException
|
||||
{
|
||||
public void importData(Context context, DBHelper db, InputStream input, char[] password) throws IOException, FormatException, InterruptedException {
|
||||
InputStream bufferedInputStream = new BufferedInputStream(input);
|
||||
bufferedInputStream.mark(100);
|
||||
|
||||
// First, check if this is a zip file
|
||||
ZipInputStream zipInputStream = new ZipInputStream(bufferedInputStream,password);
|
||||
|
||||
boolean isZipFile = false;
|
||||
|
||||
LocalFileHeader localFileHeader;
|
||||
while ((localFileHeader = zipInputStream.getNextEntry()) != null) {
|
||||
isZipFile = true;
|
||||
|
||||
String fileName = Uri.parse(localFileHeader.getFileName()).getLastPathSegment();
|
||||
if (fileName.equals("catima.csv")) {
|
||||
importCSV(context, db, new ByteArrayInputStream(ZipUtils.read(zipInputStream).getBytes(StandardCharsets.UTF_8)));
|
||||
} else if (fileName.endsWith(".png")) {
|
||||
Utils.saveCardImage(context, ZipUtils.readImage(zipInputStream), fileName);
|
||||
} else {
|
||||
throw new FormatException("Unexpected file in import: " + fileName);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isZipFile) {
|
||||
// This is not a zip file, try importing as bare CSV
|
||||
bufferedInputStream.reset();
|
||||
importCSV(context, db, bufferedInputStream);
|
||||
}
|
||||
}
|
||||
|
||||
public void importCSV(Context context, DBHelper db, InputStream input) throws IOException, FormatException, InterruptedException {
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
|
||||
|
||||
bufferedReader.mark(100);
|
||||
@@ -50,10 +90,10 @@ public class CsvDatabaseImporter implements DatabaseImporter
|
||||
|
||||
switch (version) {
|
||||
case 1:
|
||||
parseV1(db, bufferedReader);
|
||||
parseV1(context, db, bufferedReader);
|
||||
break;
|
||||
case 2:
|
||||
parseV2(db, bufferedReader);
|
||||
parseV2(context, db, bufferedReader);
|
||||
break;
|
||||
default:
|
||||
throw new FormatException(String.format("No code to parse version %s", version));
|
||||
@@ -62,9 +102,9 @@ public class CsvDatabaseImporter implements DatabaseImporter
|
||||
bufferedReader.close();
|
||||
}
|
||||
|
||||
public void parseV1(DBHelper db, BufferedReader input) throws IOException, FormatException, InterruptedException
|
||||
public void parseV1(Context context, DBHelper db, BufferedReader input) throws IOException, FormatException, InterruptedException
|
||||
{
|
||||
final CSVParser parser = new CSVParser(input, CSVFormat.RFC4180.withHeader());
|
||||
final CSVParser parser = new CSVParser(input, CSVFormat.RFC4180.builder().setHeader().build());
|
||||
|
||||
SQLiteDatabase database = db.getWritableDatabase();
|
||||
database.beginTransaction();
|
||||
@@ -73,7 +113,7 @@ public class CsvDatabaseImporter implements DatabaseImporter
|
||||
{
|
||||
for (CSVRecord record : parser)
|
||||
{
|
||||
importLoyaltyCard(database, db, record);
|
||||
importLoyaltyCard(context, database, db, record);
|
||||
|
||||
if(Thread.currentThread().isInterrupted())
|
||||
{
|
||||
@@ -95,7 +135,7 @@ public class CsvDatabaseImporter implements DatabaseImporter
|
||||
}
|
||||
}
|
||||
|
||||
public void parseV2(DBHelper db, BufferedReader input) throws IOException, FormatException, InterruptedException
|
||||
public void parseV2(Context context, DBHelper db, BufferedReader input) throws IOException, FormatException, InterruptedException
|
||||
{
|
||||
SQLiteDatabase database = db.getWritableDatabase();
|
||||
database.beginTransaction();
|
||||
@@ -108,18 +148,36 @@ public class CsvDatabaseImporter implements DatabaseImporter
|
||||
String tmp = input.readLine();
|
||||
|
||||
if (tmp == null || tmp.isEmpty()) {
|
||||
boolean sectionParsed = false;
|
||||
|
||||
switch (part) {
|
||||
case 0:
|
||||
// This is the version info, ignore
|
||||
sectionParsed = true;
|
||||
break;
|
||||
case 1:
|
||||
parseV2Groups(db, database, stringPart);
|
||||
try {
|
||||
parseV2Groups(db, database, stringPart);
|
||||
sectionParsed = true;
|
||||
} catch (FormatException e) {
|
||||
// We may have a multiline field, try again
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
parseV2Cards(db, database, stringPart);
|
||||
try {
|
||||
parseV2Cards(context, db, database, stringPart);
|
||||
sectionParsed = true;
|
||||
} catch (FormatException e) {
|
||||
// We may have a multiline field, try again
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
parseV2CardGroups(db, database, stringPart);
|
||||
try {
|
||||
parseV2CardGroups(db, database, stringPart);
|
||||
sectionParsed = true;
|
||||
} catch (FormatException e) {
|
||||
// We may have a multiline field, try again
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new FormatException("Issue parsing CSV data, too many parts for v2 parsing");
|
||||
@@ -129,8 +187,12 @@ public class CsvDatabaseImporter implements DatabaseImporter
|
||||
break;
|
||||
}
|
||||
|
||||
part += 1;
|
||||
stringPart = "";
|
||||
if (sectionParsed) {
|
||||
part += 1;
|
||||
stringPart = "";
|
||||
} else {
|
||||
stringPart += tmp + "\n";
|
||||
}
|
||||
} else {
|
||||
stringPart += tmp + "\n";
|
||||
}
|
||||
@@ -147,11 +209,13 @@ public class CsvDatabaseImporter implements DatabaseImporter
|
||||
public void parseV2Groups(DBHelper db, SQLiteDatabase database, String data) throws IOException, FormatException, InterruptedException
|
||||
{
|
||||
// Parse groups
|
||||
final CSVParser groupParser = new CSVParser(new StringReader(data), CSVFormat.RFC4180.withHeader());
|
||||
final CSVParser groupParser = new CSVParser(new StringReader(data), CSVFormat.RFC4180.builder().setHeader().build());
|
||||
|
||||
List<CSVRecord> records = new ArrayList<>();
|
||||
|
||||
try {
|
||||
for (CSVRecord record : groupParser) {
|
||||
importGroup(database, db, record);
|
||||
records.add(record);
|
||||
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new InterruptedException();
|
||||
@@ -162,16 +226,22 @@ public class CsvDatabaseImporter implements DatabaseImporter
|
||||
} finally {
|
||||
groupParser.close();
|
||||
}
|
||||
|
||||
for (CSVRecord record : records) {
|
||||
importGroup(database, db, record);
|
||||
}
|
||||
}
|
||||
|
||||
public void parseV2Cards(DBHelper db, SQLiteDatabase database, String data) throws IOException, FormatException, InterruptedException
|
||||
public void parseV2Cards(Context context, DBHelper db, SQLiteDatabase database, String data) throws IOException, FormatException, InterruptedException
|
||||
{
|
||||
// Parse cards
|
||||
final CSVParser cardParser = new CSVParser(new StringReader(data), CSVFormat.RFC4180.withHeader());
|
||||
final CSVParser cardParser = new CSVParser(new StringReader(data), CSVFormat.RFC4180.builder().setHeader().build());
|
||||
|
||||
List<CSVRecord> records = new ArrayList<>();
|
||||
|
||||
try {
|
||||
for (CSVRecord record : cardParser) {
|
||||
importLoyaltyCard(database, db, record);
|
||||
records.add(record);
|
||||
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new InterruptedException();
|
||||
@@ -182,16 +252,22 @@ public class CsvDatabaseImporter implements DatabaseImporter
|
||||
} finally {
|
||||
cardParser.close();
|
||||
}
|
||||
|
||||
for (CSVRecord record : records) {
|
||||
importLoyaltyCard(context, database, db, record);
|
||||
}
|
||||
}
|
||||
|
||||
public void parseV2CardGroups(DBHelper db, SQLiteDatabase database, String data) throws IOException, FormatException, InterruptedException
|
||||
{
|
||||
// Parse card group mappings
|
||||
final CSVParser cardGroupParser = new CSVParser(new StringReader(data), CSVFormat.RFC4180.withHeader());
|
||||
final CSVParser cardGroupParser = new CSVParser(new StringReader(data), CSVFormat.RFC4180.builder().setHeader().build());
|
||||
|
||||
List<CSVRecord> records = new ArrayList<>();
|
||||
|
||||
try {
|
||||
for (CSVRecord record : cardGroupParser) {
|
||||
importCardGroupMapping(database, db, record);
|
||||
records.add(record);
|
||||
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new InterruptedException();
|
||||
@@ -202,13 +278,17 @@ public class CsvDatabaseImporter implements DatabaseImporter
|
||||
} finally {
|
||||
cardGroupParser.close();
|
||||
}
|
||||
|
||||
for (CSVRecord record : records) {
|
||||
importCardGroupMapping(database, db, record);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a single loyalty card into the database using the given
|
||||
* session.
|
||||
*/
|
||||
private void importLoyaltyCard(SQLiteDatabase database, DBHelper helper, CSVRecord record)
|
||||
private void importLoyaltyCard(Context context, SQLiteDatabase database, DBHelper helper, CSVRecord record)
|
||||
throws IOException, FormatException
|
||||
{
|
||||
int id = CSVHelpers.extractInt(DBHelper.LoyaltyCardDbIds.ID, record, false);
|
||||
@@ -252,11 +332,11 @@ public class CsvDatabaseImporter implements DatabaseImporter
|
||||
barcodeId = null;
|
||||
}
|
||||
|
||||
BarcodeFormat barcodeType = null;
|
||||
CatimaBarcode barcodeType = null;
|
||||
String unparsedBarcodeType = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE, record, "");
|
||||
if(!unparsedBarcodeType.isEmpty())
|
||||
{
|
||||
barcodeType = BarcodeFormat.valueOf(unparsedBarcodeType);
|
||||
barcodeType = CatimaBarcode.fromName(unparsedBarcodeType);
|
||||
}
|
||||
|
||||
Integer headerColor = null;
|
||||
@@ -269,12 +349,21 @@ public class CsvDatabaseImporter implements DatabaseImporter
|
||||
int starStatus = 0;
|
||||
try {
|
||||
starStatus = CSVHelpers.extractInt(DBHelper.LoyaltyCardDbIds.STAR_STATUS, record, false);
|
||||
} catch (FormatException _e ) {
|
||||
} catch (FormatException _e) {
|
||||
// This field did not exist in versions 0.28 and before
|
||||
// We catch this exception so we can still import old backups
|
||||
}
|
||||
if (starStatus != 1) starStatus = 0;
|
||||
helper.insertLoyaltyCard(database, id, store, note, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, starStatus);
|
||||
|
||||
Long lastUsed = 0L;
|
||||
try {
|
||||
lastUsed = CSVHelpers.extractLong(DBHelper.LoyaltyCardDbIds.LAST_USED, record, false);
|
||||
} catch (FormatException _e) {
|
||||
// This field did not exist in versions 2.5.0 and before
|
||||
// We catch this exception so we can still import old backups
|
||||
}
|
||||
|
||||
helper.insertLoyaltyCard(database, id, store, note, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, starStatus, lastUsed);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1,116 +0,0 @@
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import android.database.Cursor;
|
||||
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.CSVPrinter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.Group;
|
||||
import protect.card_locker.LoyaltyCard;
|
||||
|
||||
/**
|
||||
* Class for exporting the database into CSV (Comma Separate Values)
|
||||
* format.
|
||||
*/
|
||||
public class CsvDatabaseExporter implements DatabaseExporter
|
||||
{
|
||||
public void exportData(DBHelper db, OutputStreamWriter output) throws IOException, InterruptedException
|
||||
{
|
||||
CSVPrinter printer = new CSVPrinter(output, CSVFormat.RFC4180);
|
||||
|
||||
// Print the version
|
||||
printer.printRecord("2");
|
||||
|
||||
printer.println();
|
||||
|
||||
// Print the header for groups
|
||||
printer.printRecord(DBHelper.LoyaltyCardDbGroups.ID);
|
||||
|
||||
Cursor groupCursor = db.getGroupCursor();
|
||||
|
||||
while(groupCursor.moveToNext())
|
||||
{
|
||||
Group group = Group.toGroup(groupCursor);
|
||||
|
||||
printer.printRecord(group._id);
|
||||
|
||||
if(Thread.currentThread().isInterrupted())
|
||||
{
|
||||
throw new InterruptedException();
|
||||
}
|
||||
}
|
||||
|
||||
groupCursor.close();
|
||||
|
||||
// Print an empty line
|
||||
printer.println();
|
||||
|
||||
// Print the header for cards
|
||||
printer.printRecord(DBHelper.LoyaltyCardDbIds.ID,
|
||||
DBHelper.LoyaltyCardDbIds.STORE,
|
||||
DBHelper.LoyaltyCardDbIds.NOTE,
|
||||
DBHelper.LoyaltyCardDbIds.EXPIRY,
|
||||
DBHelper.LoyaltyCardDbIds.BALANCE,
|
||||
DBHelper.LoyaltyCardDbIds.BALANCE_TYPE,
|
||||
DBHelper.LoyaltyCardDbIds.CARD_ID,
|
||||
DBHelper.LoyaltyCardDbIds.HEADER_COLOR,
|
||||
DBHelper.LoyaltyCardDbIds.BARCODE_TYPE,
|
||||
DBHelper.LoyaltyCardDbIds.STAR_STATUS);
|
||||
|
||||
Cursor cardCursor = db.getLoyaltyCardCursor();
|
||||
|
||||
while(cardCursor.moveToNext())
|
||||
{
|
||||
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cardCursor);
|
||||
|
||||
printer.printRecord(card.id,
|
||||
card.store,
|
||||
card.note,
|
||||
card.expiry != null ? card.expiry.getTime() : "",
|
||||
card.balance,
|
||||
card.balanceType,
|
||||
card.cardId,
|
||||
card.headerColor,
|
||||
card.barcodeType,
|
||||
card.starStatus);
|
||||
|
||||
if(Thread.currentThread().isInterrupted())
|
||||
{
|
||||
throw new InterruptedException();
|
||||
}
|
||||
}
|
||||
|
||||
cardCursor.close();
|
||||
|
||||
// Print an empty line
|
||||
printer.println();
|
||||
|
||||
// Print the header for card group mappings
|
||||
printer.printRecord(DBHelper.LoyaltyCardDbIdsGroups.cardID,
|
||||
DBHelper.LoyaltyCardDbIdsGroups.groupID);
|
||||
|
||||
Cursor cardCursor2 = db.getLoyaltyCardCursor();
|
||||
|
||||
while(cardCursor2.moveToNext())
|
||||
{
|
||||
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cardCursor2);
|
||||
|
||||
for (Group group : db.getLoyaltyCardGroups(card.id)) {
|
||||
printer.printRecord(card.id, group._id);
|
||||
}
|
||||
|
||||
if(Thread.currentThread().isInterrupted())
|
||||
{
|
||||
throw new InterruptedException();
|
||||
}
|
||||
}
|
||||
|
||||
cardCursor2.close();
|
||||
|
||||
printer.close();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
package protect.card_locker;
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
public enum DataFormat
|
||||
{
|
||||
Catima,
|
||||
Fidme,
|
||||
Stocard,
|
||||
VoucherVault
|
||||
;
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import protect.card_locker.DBHelper;
|
||||
|
||||
@@ -9,11 +11,11 @@ import protect.card_locker.DBHelper;
|
||||
* Interface for a class which can export the contents of the database
|
||||
* in a given format.
|
||||
*/
|
||||
public interface DatabaseExporter
|
||||
public interface Exporter
|
||||
{
|
||||
/**
|
||||
* Export the database to the output stream in a given format.
|
||||
* @throws IOException
|
||||
*/
|
||||
void exportData(DBHelper db, OutputStreamWriter output) throws IOException, InterruptedException;
|
||||
void exportData(Context context, DBHelper db, OutputStream output,char[] password) throws IOException, InterruptedException;
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
import net.lingala.zip4j.io.inputstream.ZipInputStream;
|
||||
import net.lingala.zip4j.model.LocalFileHeader;
|
||||
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.CSVParser;
|
||||
import org.apache.commons.csv.CSVRecord;
|
||||
@@ -15,11 +19,11 @@ import java.io.StringReader;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.ParseException;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import protect.card_locker.CatimaBarcode;
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.FormatException;
|
||||
import protect.card_locker.Utils;
|
||||
|
||||
/**
|
||||
* Class for importing a database from CSV (Comma Separate Values)
|
||||
@@ -28,20 +32,19 @@ import protect.card_locker.FormatException;
|
||||
* The database's loyalty cards are expected to appear in the CSV data.
|
||||
* A header is expected for the each table showing the names of the columns.
|
||||
*/
|
||||
public class FidmeImporter implements DatabaseImporter
|
||||
{
|
||||
public void importData(DBHelper db, InputStream input) throws IOException, FormatException, JSONException, ParseException {
|
||||
public class FidmeImporter implements Importer {
|
||||
public void importData(Context context, DBHelper db, InputStream input, char[] password) throws IOException, FormatException, JSONException, ParseException {
|
||||
// We actually retrieve a .zip file
|
||||
ZipInputStream zipInputStream = new ZipInputStream(input);
|
||||
ZipInputStream zipInputStream = new ZipInputStream(input, password);
|
||||
|
||||
StringBuilder loyaltyCards = new StringBuilder();
|
||||
byte[] buffer = new byte[1024];
|
||||
int read = 0;
|
||||
|
||||
ZipEntry zipEntry;
|
||||
LocalFileHeader localFileHeader;
|
||||
|
||||
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
|
||||
if (zipEntry.getName().equals("loyalty_programs.csv")) {
|
||||
while ((localFileHeader = zipInputStream.getNextEntry()) != null) {
|
||||
if (localFileHeader.getFileName().equals("loyalty_programs.csv")) {
|
||||
while ((read = zipInputStream.read(buffer, 0, 1024)) >= 0) {
|
||||
loyaltyCards.append(new String(buffer, 0, read, StandardCharsets.UTF_8));
|
||||
}
|
||||
@@ -55,7 +58,7 @@ public class FidmeImporter implements DatabaseImporter
|
||||
SQLiteDatabase database = db.getWritableDatabase();
|
||||
database.beginTransaction();
|
||||
|
||||
final CSVParser fidmeParser = new CSVParser(new StringReader(loyaltyCards.toString()), CSVFormat.RFC4180.withDelimiter(';').withHeader());
|
||||
final CSVParser fidmeParser = new CSVParser(new StringReader(loyaltyCards.toString()), CSVFormat.RFC4180.builder().setDelimiter(';').setHeader().build());
|
||||
|
||||
try {
|
||||
for (CSVRecord record : fidmeParser) {
|
||||
@@ -83,8 +86,7 @@ public class FidmeImporter implements DatabaseImporter
|
||||
* session.
|
||||
*/
|
||||
private void importLoyaltyCard(SQLiteDatabase database, DBHelper helper, CSVRecord record)
|
||||
throws IOException, FormatException
|
||||
{
|
||||
throws IOException, FormatException {
|
||||
// A loyalty card export from Fidme contains the following fields:
|
||||
// Retailer (store name)
|
||||
// Program (program name)
|
||||
@@ -96,40 +98,40 @@ public class FidmeImporter implements DatabaseImporter
|
||||
// The store is called Retailer
|
||||
String store = CSVHelpers.extractString("Retailer", record, "");
|
||||
|
||||
if (store.isEmpty())
|
||||
{
|
||||
if (store.isEmpty()) {
|
||||
throw new FormatException("No store listed, but is required");
|
||||
}
|
||||
|
||||
// There seems to be no note field in the CSV? So let's combine other fields instead...
|
||||
String program = CSVHelpers.extractString("Program", record, "");
|
||||
String addedAt = CSVHelpers.extractString("Added At", record, "");
|
||||
String firstName = CSVHelpers.extractString("Firstname", record, "");
|
||||
String lastName = CSVHelpers.extractString("Lastname", record, "");
|
||||
String program = CSVHelpers.extractString("Program", record, "").trim();
|
||||
String addedAt = CSVHelpers.extractString("Added At", record, "").trim();
|
||||
String firstName = CSVHelpers.extractString("Firstname", record, "").trim();
|
||||
String lastName = CSVHelpers.extractString("Lastname", record, "").trim();
|
||||
|
||||
String combinedName = String.format("%s %s", firstName, lastName);
|
||||
String combinedName = String.format("%s %s", firstName, lastName).trim();
|
||||
|
||||
StringBuilder noteBuilder = new StringBuilder();
|
||||
if (!program.isEmpty()) noteBuilder.append(program).append('\n');
|
||||
if (!addedAt.isEmpty()) noteBuilder.append(addedAt).append('\n');
|
||||
if (!combinedName.isEmpty()) noteBuilder.append(combinedName).append('\n');
|
||||
String note = noteBuilder.toString();
|
||||
String note = noteBuilder.toString().trim();
|
||||
|
||||
// The ID is called reference
|
||||
String cardId = CSVHelpers.extractString("Reference", record, "");
|
||||
if(cardId.isEmpty())
|
||||
{
|
||||
if (cardId.isEmpty()) {
|
||||
throw new FormatException("No card ID listed, but is required");
|
||||
}
|
||||
|
||||
// Sadly, Fidme exports don't contain the card type
|
||||
// I guess they have an online DB of all the different companies and what type they use
|
||||
// TODO: Hook this into our own loyalty card DB if we ever get one
|
||||
BarcodeFormat barcodeType = null;
|
||||
CatimaBarcode barcodeType = null;
|
||||
|
||||
// No favourite data in the export either
|
||||
int starStatus = 0;
|
||||
|
||||
helper.insertLoyaltyCard(database, store, note, null, BigDecimal.valueOf(0), null, cardId, null, barcodeType, null, starStatus);
|
||||
// TODO: Front and back image
|
||||
|
||||
helper.insertLoyaltyCard(database, store, note, null, BigDecimal.valueOf(0), null, cardId, null, barcodeType, null, starStatus, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
public enum ImportExportResult
|
||||
{
|
||||
Success,
|
||||
GenericFailure,
|
||||
BadPassword
|
||||
;
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -13,7 +15,7 @@ import protect.card_locker.FormatException;
|
||||
* Interface for a class which can import the contents of a stream
|
||||
* into the database.
|
||||
*/
|
||||
public interface DatabaseImporter
|
||||
public interface Importer
|
||||
{
|
||||
/**
|
||||
* Import data from the input stream in a given format into
|
||||
@@ -21,5 +23,5 @@ public interface DatabaseImporter
|
||||
* @throws IOException
|
||||
* @throws FormatException
|
||||
*/
|
||||
void importData(DBHelper db, InputStream input) throws IOException, FormatException, InterruptedException, JSONException, ParseException;
|
||||
void importData(Context context, DBHelper db, InputStream input, char[] password) throws IOException, FormatException, InterruptedException, JSONException, ParseException;
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.DataFormat;
|
||||
|
||||
public class MultiFormatExporter
|
||||
{
|
||||
@@ -18,18 +18,18 @@ public class MultiFormatExporter
|
||||
*
|
||||
* The output stream is closed on success.
|
||||
*
|
||||
* @return true if the database was successfully exported,
|
||||
* false otherwise. If false, partial data may have been
|
||||
* @return ImportExportResult.Success if the database was successfully exported,
|
||||
* another ImportExportResult otherwise. If not Success, partial data may have been
|
||||
* written to the output stream, and it should be discarded.
|
||||
*/
|
||||
public static boolean exportData(DBHelper db, OutputStreamWriter output, DataFormat format)
|
||||
public static ImportExportResult exportData(Context context, DBHelper db, OutputStream output, DataFormat format,char[] password)
|
||||
{
|
||||
DatabaseExporter exporter = null;
|
||||
Exporter exporter = null;
|
||||
|
||||
switch(format)
|
||||
{
|
||||
case Catima:
|
||||
exporter = new CsvDatabaseExporter();
|
||||
exporter = new CatimaExporter();
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Failed to export data, unknown format " + format.name());
|
||||
@@ -40,8 +40,8 @@ public class MultiFormatExporter
|
||||
{
|
||||
try
|
||||
{
|
||||
exporter.exportData(db, output);
|
||||
return true;
|
||||
exporter.exportData(context, db, output,password);
|
||||
return ImportExportResult.Success;
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
@@ -52,12 +52,12 @@ public class MultiFormatExporter
|
||||
Log.e(TAG, "Failed to export data", e);
|
||||
}
|
||||
|
||||
return false;
|
||||
return ImportExportResult.GenericFailure;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.e(TAG, "Unsupported data format exported: " + format.name());
|
||||
return false;
|
||||
return ImportExportResult.GenericFailure;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -9,7 +12,6 @@ import java.io.InputStream;
|
||||
import java.text.ParseException;
|
||||
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.DataFormat;
|
||||
import protect.card_locker.FormatException;
|
||||
|
||||
public class MultiFormatImporter
|
||||
@@ -23,22 +25,25 @@ public class MultiFormatImporter
|
||||
* The input stream is not closed, and doing so is the
|
||||
* responsibility of the caller.
|
||||
*
|
||||
* @return true if the database was successfully imported,
|
||||
* false otherwise. If false, no data was written to
|
||||
* @return ImportExportResult.Success if the database was successfully imported,
|
||||
* or another result otherwise. If no Success, no data was written to
|
||||
* the database.
|
||||
*/
|
||||
public static boolean importData(DBHelper db, InputStream input, DataFormat format)
|
||||
public static ImportExportResult importData(Context context, DBHelper db, InputStream input, DataFormat format, char[] password)
|
||||
{
|
||||
DatabaseImporter importer = null;
|
||||
Importer importer = null;
|
||||
|
||||
switch(format)
|
||||
{
|
||||
case Catima:
|
||||
importer = new CsvDatabaseImporter();
|
||||
importer = new CatimaImporter();
|
||||
break;
|
||||
case Fidme:
|
||||
importer = new FidmeImporter();
|
||||
break;
|
||||
case Stocard:
|
||||
importer = new StocardImporter();
|
||||
break;
|
||||
case VoucherVault:
|
||||
importer = new VoucherVaultImporter();
|
||||
break;
|
||||
@@ -48,10 +53,14 @@ public class MultiFormatImporter
|
||||
{
|
||||
try
|
||||
{
|
||||
importer.importData(db, input);
|
||||
return true;
|
||||
importer.importData(context, db, input, password);
|
||||
return ImportExportResult.Success;
|
||||
}
|
||||
catch(IOException | FormatException | InterruptedException | JSONException | ParseException e)
|
||||
catch(ZipException e)
|
||||
{
|
||||
return ImportExportResult.BadPassword;
|
||||
}
|
||||
catch(IOException | FormatException | InterruptedException | JSONException | ParseException | NullPointerException e)
|
||||
{
|
||||
Log.e(TAG, "Failed to import data", e);
|
||||
}
|
||||
@@ -61,6 +70,7 @@ public class MultiFormatImporter
|
||||
{
|
||||
Log.e(TAG, "Unsupported data format imported: " + format.name());
|
||||
}
|
||||
return false;
|
||||
|
||||
return ImportExportResult.GenericFailure;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
import net.lingala.zip4j.io.inputstream.ZipInputStream;
|
||||
import net.lingala.zip4j.model.LocalFileHeader;
|
||||
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.CSVParser;
|
||||
import org.apache.commons.csv.CSVRecord;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.ParseException;
|
||||
import java.util.HashMap;
|
||||
|
||||
import protect.card_locker.CatimaBarcode;
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.FormatException;
|
||||
import protect.card_locker.R;
|
||||
import protect.card_locker.Utils;
|
||||
import protect.card_locker.ZipUtils;
|
||||
|
||||
/**
|
||||
* Class for importing a database from CSV (Comma Separate Values)
|
||||
* formatted data.
|
||||
*
|
||||
* The database's loyalty cards are expected to appear in the CSV data.
|
||||
* A header is expected for the each table showing the names of the columns.
|
||||
*/
|
||||
public class StocardImporter implements Importer
|
||||
{
|
||||
public void importData(Context context, DBHelper db, InputStream input, char[] password) throws IOException, FormatException, JSONException, ParseException {
|
||||
HashMap<String, HashMap<String, Object>> loyaltyCardHashMap = new HashMap<>();
|
||||
HashMap<String, HashMap<String, String>> providers = new HashMap<>();
|
||||
|
||||
final CSVParser parser = new CSVParser(new InputStreamReader(context.getResources().openRawResource(R.raw.stocard_stores), StandardCharsets.UTF_8), CSVFormat.RFC4180.builder().setHeader().build());
|
||||
|
||||
try
|
||||
{
|
||||
for (CSVRecord record : parser)
|
||||
{
|
||||
HashMap<String, String> recordData = new HashMap<>();
|
||||
recordData.put("name", record.get("name"));
|
||||
recordData.put("barcodeFormat", record.get("barcodeFormat"));
|
||||
|
||||
providers.put(record.get("_id"), recordData);
|
||||
}
|
||||
|
||||
parser.close();
|
||||
} catch(IllegalArgumentException|IllegalStateException e) {
|
||||
throw new FormatException("Issue parsing CSV data", e);
|
||||
}
|
||||
|
||||
ZipInputStream zipInputStream = new ZipInputStream(input, password);
|
||||
|
||||
String[] providersFileName = null;
|
||||
String[] cardBaseName = null;
|
||||
String cardName = "";
|
||||
LocalFileHeader localFileHeader;
|
||||
while ((localFileHeader = zipInputStream.getNextEntry()) != null) {
|
||||
String fileName = localFileHeader.getFileName();
|
||||
String[] nameParts = fileName.split("/");
|
||||
|
||||
if (providersFileName == null) {
|
||||
providersFileName = new String[] {
|
||||
nameParts[0],
|
||||
"sync",
|
||||
"data",
|
||||
"users",
|
||||
nameParts[0],
|
||||
"analytics-properties.json"
|
||||
};
|
||||
cardBaseName = new String[] {
|
||||
nameParts[0],
|
||||
"sync",
|
||||
"data",
|
||||
"users",
|
||||
nameParts[0],
|
||||
"loyalty-cards"
|
||||
};
|
||||
}
|
||||
|
||||
if (startsWith(nameParts, cardBaseName, 1)) {
|
||||
// Extract cardName
|
||||
cardName = nameParts[cardBaseName.length].split("\\.", 2)[0];
|
||||
|
||||
// This is the card itself
|
||||
if (nameParts.length == cardBaseName.length + 1) {
|
||||
// Ignore the .txt file
|
||||
if (fileName.endsWith(".json")) {
|
||||
JSONObject jsonObject = ZipUtils.readJSON(zipInputStream);
|
||||
|
||||
loyaltyCardHashMap = appendToLoyaltyCardHashMap(
|
||||
loyaltyCardHashMap,
|
||||
cardName,
|
||||
"cardId",
|
||||
jsonObject.getString("input_id")
|
||||
);
|
||||
loyaltyCardHashMap = appendToLoyaltyCardHashMap(
|
||||
loyaltyCardHashMap,
|
||||
cardName,
|
||||
"_providerId",
|
||||
jsonObject
|
||||
.getJSONObject("input_provider_reference")
|
||||
.getString("identifier")
|
||||
.substring("/loyalty-card-providers/".length())
|
||||
);
|
||||
|
||||
if (jsonObject.has("input_barcode_format")) {
|
||||
loyaltyCardHashMap = appendToLoyaltyCardHashMap(
|
||||
loyaltyCardHashMap,
|
||||
cardName,
|
||||
"barcodeType",
|
||||
jsonObject.getString("input_barcode_format")
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (fileName.endsWith("notes/default.json")) {
|
||||
loyaltyCardHashMap = appendToLoyaltyCardHashMap(
|
||||
loyaltyCardHashMap,
|
||||
cardName,
|
||||
"note",
|
||||
ZipUtils.readJSON(zipInputStream)
|
||||
.getString("content")
|
||||
);
|
||||
} else if (fileName.endsWith("/images/front.png")) {
|
||||
loyaltyCardHashMap = appendToLoyaltyCardHashMap(
|
||||
loyaltyCardHashMap,
|
||||
cardName,
|
||||
"frontImage",
|
||||
ZipUtils.readImage(zipInputStream)
|
||||
);
|
||||
} else if (fileName.endsWith("/images/back.png")) {
|
||||
loyaltyCardHashMap = appendToLoyaltyCardHashMap(
|
||||
loyaltyCardHashMap,
|
||||
cardName,
|
||||
"backImage",
|
||||
ZipUtils.readImage(zipInputStream)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (loyaltyCardHashMap.keySet().size() == 0) {
|
||||
throw new FormatException("Couldn't find any loyalty cards in this Stocard export.");
|
||||
}
|
||||
|
||||
SQLiteDatabase database = db.getWritableDatabase();
|
||||
database.beginTransaction();
|
||||
|
||||
for (HashMap<String, Object> loyaltyCardData : loyaltyCardHashMap.values()) {
|
||||
String providerId = (String) loyaltyCardData.get("_providerId");
|
||||
HashMap<String, String> providerData = providers.get(providerId);
|
||||
|
||||
String store = providerData != null ? providerData.get("name") : providerId;
|
||||
String note = (String) Utils.mapGetOrDefault(loyaltyCardData, "note", "");
|
||||
String cardId = (String) loyaltyCardData.get("cardId");
|
||||
String barcodeTypeString = (String) Utils.mapGetOrDefault(loyaltyCardData, "barcodeType", providerData != null ? providerData.get("barcodeFormat") : null);
|
||||
CatimaBarcode barcodeType = null;
|
||||
if (barcodeTypeString != null) {
|
||||
if (barcodeTypeString.equals("RSS_DATABAR_EXPANDED")) {
|
||||
barcodeType = CatimaBarcode.fromBarcode(BarcodeFormat.RSS_EXPANDED);
|
||||
} else {
|
||||
barcodeType = CatimaBarcode.fromName(barcodeTypeString);
|
||||
}
|
||||
}
|
||||
|
||||
long loyaltyCardInternalId = db.insertLoyaltyCard(database, store, note, null, BigDecimal.valueOf(0), null, cardId, null, barcodeType, null, 0, null);
|
||||
|
||||
if (loyaltyCardData.containsKey("frontImage")) {
|
||||
Utils.saveCardImage(context, (Bitmap) loyaltyCardData.get("frontImage"), (int) loyaltyCardInternalId, true);
|
||||
}
|
||||
if (loyaltyCardData.containsKey("backImage")) {
|
||||
Utils.saveCardImage(context, (Bitmap) loyaltyCardData.get("backImage"), (int) loyaltyCardInternalId, false);
|
||||
}
|
||||
}
|
||||
|
||||
database.setTransactionSuccessful();
|
||||
database.endTransaction();
|
||||
database.close();
|
||||
|
||||
zipInputStream.close();
|
||||
}
|
||||
|
||||
private boolean startsWith(String[] full, String[] start, int minExtraLength) {
|
||||
if (full.length - minExtraLength < start.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < start.length; i++) {
|
||||
if (!start[i].contentEquals(full[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private HashMap<String, HashMap<String, Object>> appendToLoyaltyCardHashMap(HashMap<String, HashMap<String, Object>> loyaltyCardHashMap, String cardID, String key, Object value) {
|
||||
HashMap<String, Object> loyaltyCardData = loyaltyCardHashMap.get(cardID);
|
||||
if (loyaltyCardData == null) {
|
||||
loyaltyCardData = new HashMap<>();
|
||||
}
|
||||
|
||||
loyaltyCardData.put(key, value);
|
||||
loyaltyCardHashMap.put(cardID, loyaltyCardData);
|
||||
|
||||
return loyaltyCardHashMap;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Color;
|
||||
|
||||
@@ -22,8 +23,10 @@ import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import protect.card_locker.CatimaBarcode;
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.FormatException;
|
||||
import protect.card_locker.Utils;
|
||||
|
||||
/**
|
||||
* Class for importing a database from CSV (Comma Separate Values)
|
||||
@@ -32,9 +35,9 @@ import protect.card_locker.FormatException;
|
||||
* The database's loyalty cards are expected to appear in the CSV data.
|
||||
* A header is expected for the each table showing the names of the columns.
|
||||
*/
|
||||
public class VoucherVaultImporter implements DatabaseImporter
|
||||
public class VoucherVaultImporter implements Importer
|
||||
{
|
||||
public void importData(DBHelper db, InputStream input) throws IOException, FormatException, JSONException, ParseException {
|
||||
public void importData(Context context, DBHelper db, InputStream input, char[] password) throws IOException, FormatException, JSONException, ParseException {
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@@ -60,35 +63,37 @@ public class VoucherVaultImporter implements DatabaseImporter
|
||||
expiry = dateFormat.parse(jsonCard.getString("expires"));
|
||||
}
|
||||
|
||||
BigDecimal balance;
|
||||
if (!jsonCard.isNull("balance")) {
|
||||
BigDecimal balance = new BigDecimal("0");
|
||||
if (jsonCard.has("balanceMilliunits")) {
|
||||
if (!jsonCard.isNull("balanceMilliunits")) {
|
||||
balance = new BigDecimal(String.valueOf(jsonCard.getInt("balanceMilliunits") / 1000.0));
|
||||
}
|
||||
} else if (!jsonCard.isNull("balance")) {
|
||||
balance = new BigDecimal(String.valueOf(jsonCard.getDouble("balance")));
|
||||
} else {
|
||||
balance = new BigDecimal("0");
|
||||
}
|
||||
|
||||
Currency balanceType = Currency.getInstance("USD");
|
||||
|
||||
String cardId = jsonCard.getString("code");
|
||||
|
||||
BarcodeFormat barcodeType = null;
|
||||
CatimaBarcode barcodeType = null;
|
||||
|
||||
String codeTypeFromJSON = jsonCard.getString("codeType");
|
||||
switch (codeTypeFromJSON) {
|
||||
case "CODE128":
|
||||
barcodeType = BarcodeFormat.CODE_128;
|
||||
barcodeType = CatimaBarcode.fromBarcode(BarcodeFormat.CODE_128);
|
||||
break;
|
||||
case "CODE39":
|
||||
barcodeType = BarcodeFormat.CODE_39;
|
||||
barcodeType = CatimaBarcode.fromBarcode(BarcodeFormat.CODE_39);
|
||||
break;
|
||||
case "EAN13":
|
||||
barcodeType = BarcodeFormat.EAN_13;
|
||||
barcodeType = CatimaBarcode.fromBarcode(BarcodeFormat.EAN_13);
|
||||
break;
|
||||
case "PDF417":
|
||||
barcodeType = BarcodeFormat.PDF_417;
|
||||
barcodeType = CatimaBarcode.fromBarcode(BarcodeFormat.PDF_417);
|
||||
break;
|
||||
case "QR":
|
||||
barcodeType = BarcodeFormat.QR_CODE;
|
||||
barcodeType = CatimaBarcode.fromBarcode(BarcodeFormat.QR_CODE);
|
||||
break;
|
||||
case "TEXT":
|
||||
break;
|
||||
@@ -122,10 +127,10 @@ public class VoucherVaultImporter implements DatabaseImporter
|
||||
headerColor = Color.YELLOW;
|
||||
break;
|
||||
default:
|
||||
throw new FormatException("Unknown colour type foun: " + colorFromJSON);
|
||||
throw new FormatException("Unknown colour type found: " + colorFromJSON);
|
||||
}
|
||||
|
||||
db.insertLoyaltyCard(store, "", expiry, balance, balanceType, cardId, null, barcodeType, headerColor, 0);
|
||||
db.insertLoyaltyCard(store, "", expiry, balance, balanceType, cardId, null, barcodeType, headerColor, 0, Utils.getUnixTime());
|
||||
}
|
||||
|
||||
database.setTransactionSuccessful();
|
||||
|
||||
@@ -3,11 +3,14 @@ package protect.card_locker.preferences;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import androidx.annotation.IntegerRes;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import protect.card_locker.R;
|
||||
import protect.card_locker.Utils;
|
||||
|
||||
public class Settings
|
||||
{
|
||||
@@ -45,6 +48,17 @@ public class Settings
|
||||
return settings.getBoolean(getResString(keyId), defaultValue);
|
||||
}
|
||||
|
||||
public Locale getLocale()
|
||||
{
|
||||
String value = getString(R.string.settings_key_locale, "");
|
||||
|
||||
if (value.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Utils.stringToLocale(value);
|
||||
}
|
||||
|
||||
public int getTheme()
|
||||
{
|
||||
String value = getString(R.string.settings_key_theme, getResString(R.string.settings_key_system_theme));
|
||||
|
||||
@@ -1,27 +1,39 @@
|
||||
package protect.card_locker.preferences;
|
||||
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import nl.invissvenska.numberpickerpreference.NumberDialogPreference;
|
||||
import nl.invissvenska.numberpickerpreference.NumberPickerPreferenceDialogFragment;
|
||||
import protect.card_locker.CatimaAppCompatActivity;
|
||||
import protect.card_locker.R;
|
||||
import protect.card_locker.Utils;
|
||||
|
||||
public class SettingsActivity extends AppCompatActivity
|
||||
public class SettingsActivity extends CatimaAppCompatActivity
|
||||
{
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
setTitle(R.string.settings);
|
||||
setContentView(R.layout.settings_activity);
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if(actionBar != null)
|
||||
{
|
||||
@@ -29,8 +41,9 @@ public class SettingsActivity extends AppCompatActivity
|
||||
}
|
||||
|
||||
// Display the fragment as the main content.
|
||||
SettingsFragment fragment = new SettingsFragment();
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.settings_container, new SettingsFragment())
|
||||
.replace(R.id.settings_container, fragment)
|
||||
.commit();
|
||||
}
|
||||
|
||||
@@ -51,46 +64,68 @@ public class SettingsActivity extends AppCompatActivity
|
||||
public static class SettingsFragment extends PreferenceFragmentCompat
|
||||
{
|
||||
private static final String DIALOG_FRAGMENT_TAG = "SettingsFragment";
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey)
|
||||
{
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
// Load the preferences from an XML resource
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
|
||||
// Show pretty names
|
||||
ListPreference localePreference = findPreference(getResources().getString(R.string.settings_key_locale));
|
||||
assert localePreference != null;
|
||||
CharSequence[] entryValues = localePreference.getEntryValues();
|
||||
List<CharSequence> entries = new ArrayList<>();
|
||||
for (CharSequence entry : entryValues) {
|
||||
if (entry.length() == 0) {
|
||||
entries.add(getResources().getString(R.string.settings_system_locale));
|
||||
} else {
|
||||
Locale entryLocale = Utils.stringToLocale(entry.toString());
|
||||
entries.add(entryLocale.getDisplayName(entryLocale));
|
||||
}
|
||||
}
|
||||
|
||||
localePreference.setEntries(entries.toArray(new CharSequence[entryValues.length]));
|
||||
|
||||
Preference themePreference = findPreference(getResources().getString(R.string.settings_key_theme));
|
||||
assert themePreference != null;
|
||||
themePreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
|
||||
{
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object o)
|
||||
{
|
||||
if(o.toString().equals(getResources().getString(R.string.settings_key_light_theme)))
|
||||
{
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
|
||||
}
|
||||
else if(o.toString().equals(getResources().getString(R.string.settings_key_dark_theme)))
|
||||
{
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
|
||||
}
|
||||
else
|
||||
{
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
|
||||
}
|
||||
|
||||
FragmentActivity activity = getActivity();
|
||||
if (activity != null) {
|
||||
activity.recreate();
|
||||
}
|
||||
return true;
|
||||
themePreference.setOnPreferenceChangeListener((preference, o) -> {
|
||||
if (o.toString().equals(getResources().getString(R.string.settings_key_light_theme))) {
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
|
||||
} else if (o.toString().equals(getResources().getString(R.string.settings_key_dark_theme))) {
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
|
||||
} else {
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
|
||||
}
|
||||
|
||||
FragmentActivity activity = getActivity();
|
||||
if (activity != null) {
|
||||
ActivityCompat.recreate(activity);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
Preference colorPreference = findPreference(getResources().getString(R.string.setting_key_theme_color));
|
||||
assert colorPreference != null;
|
||||
colorPreference.setOnPreferenceChangeListener((preference, o) -> {
|
||||
FragmentActivity activity = getActivity();
|
||||
if (activity != null) {
|
||||
ActivityCompat.recreate(activity);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
localePreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
// Refresh the activity
|
||||
Intent intent = new Intent(getContext(), SettingsActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
getContext().startActivity(intent);
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayPreferenceDialog(Preference preference)
|
||||
{
|
||||
if (preference instanceof NumberDialogPreference)
|
||||
{
|
||||
public void onDisplayPreferenceDialog(Preference preference) {
|
||||
if (preference instanceof NumberDialogPreference) {
|
||||
NumberDialogPreference dialogPreference = (NumberDialogPreference) preference;
|
||||
DialogFragment dialogFragment = NumberPickerPreferenceDialogFragment
|
||||
.newInstance(
|
||||
@@ -102,9 +137,7 @@ public class SettingsActivity extends AppCompatActivity
|
||||
);
|
||||
dialogFragment.setTargetFragment(this, 0);
|
||||
dialogFragment.show(getParentFragmentManager(), DIALOG_FRAGMENT_TAG);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
super.onDisplayPreferenceDialog(preference);
|
||||
}
|
||||
}
|
||||
|
||||
9
app/src/main/res/drawable/active_dot.xml
Normal file
9
app/src/main/res/drawable/active_dot.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval" android:useLevel="true"
|
||||
android:dither="true">
|
||||
|
||||
<size android:height="12dip" android:width="12dip"/>
|
||||
|
||||
<solid android:color="@android:color/white"/>
|
||||
</shape>
|
||||
@@ -1,66 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="192dp"
|
||||
android:height="192dp"
|
||||
android:viewportWidth="50.8"
|
||||
android:viewportHeight="50.8">
|
||||
<path
|
||||
android:pathData="M14.3354,20.1954l20.7318,-9.2304l5.7612,12.9398l-20.7318,9.2304z"
|
||||
android:strokeLineJoin="miter"
|
||||
android:strokeWidth="0.529167"
|
||||
android:fillColor="#f0f0f0"
|
||||
android:strokeColor="#c80000"/>
|
||||
<path
|
||||
android:pathData="M14.8755,10.9648l23.2041,10.3311l-6.8874,15.4694l-23.2041,-10.3311z"
|
||||
android:strokeLineJoin="miter"
|
||||
android:strokeWidth="0.529167"
|
||||
android:fillColor="#f0f0f0"
|
||||
android:strokeColor="#c80000"/>
|
||||
<path
|
||||
android:pathData="M16.5599,16.1348l26.5459,7.6119l-4.5489,15.8639l-26.5459,-7.6119z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="1.5875"
|
||||
android:fillColor="#c80000"
|
||||
android:strokeColor="#c80000"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M12.011,15.4955h27.6157v16.5032h-27.6157z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="1.5875"
|
||||
android:fillColor="#ff0000"
|
||||
android:strokeColor="#ff0000"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M7.8471,23.7471a4.3659,8.5899 0,1 0,8.7317 0a4.3659,8.5899 0,1 0,-8.7317 0z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="0.91078"
|
||||
android:fillColor="#ff0000"
|
||||
android:strokeColor="#ff0000"/>
|
||||
<path
|
||||
android:pathData="m24.4983,25.781a1.6711,1.6711 0,0 1,-1.3809 1.6457,1.6711 1.6711,0 0,1 -1.8605,-1.0741"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="0.529167"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#f0f0f0"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="m27.7991,26.333a1.6711,1.6711 0,0 1,-1.8605 1.0741,1.6711 1.6711,0 0,1 -1.3809,-1.6457"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="0.529167"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#f0f0f0"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="m16.0606,22.271 l2.6458,-2.6458 2.6458,2.6458"
|
||||
android:strokeLineJoin="miter"
|
||||
android:strokeWidth="0.529167"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#f0f0f0"
|
||||
android:strokeLineCap="butt"/>
|
||||
<path
|
||||
android:pathData="m27.7023,22.271 l2.6458,-2.6458 2.6458,2.6458"
|
||||
android:strokeLineJoin="miter"
|
||||
android:strokeWidth="0.529167"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#f0f0f0"
|
||||
android:strokeLineCap="butt"/>
|
||||
</vector>
|
||||
13
app/src/main/res/drawable/ic_baseline_access_time_24.xml
Normal file
13
app/src/main/res/drawable/ic_baseline_access_time_24.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<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="@android:color/white"
|
||||
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"/>
|
||||
</vector>
|
||||
@@ -1,5 +0,0 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M20,3h-1L19,1h-2v2L7,3L7,1L5,1v2L4,3c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,5c0,-1.1 -0.9,-2 -2,-2zM20,21L4,21L4,8h16v13z"/>
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/ic_baseline_payments_24.xml
Normal file
10
app/src/main/res/drawable/ic_baseline_payments_24.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<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="@android:color/white"
|
||||
android:pathData="M19,14L19,6c0,-1.1 -0.9,-2 -2,-2L3,4c-1.1,0 -2,0.9 -2,2v8c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM10,13c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM23,7v11c0,1.1 -0.9,2 -2,2L4,20v-2h17L21,7h2z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_baseline_sort_24.xml
Normal file
5
app/src/main/res/drawable/ic_baseline_sort_24.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:tint="#FFFFFF" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M3,18h6v-2L3,16v2zM3,6v2h18L21,6L3,6zM3,13h12v-2L3,11v2z"/>
|
||||
</vector>
|
||||
15
app/src/main/res/drawable/ic_flashlight_off_white_24dp.xml
Normal file
15
app/src/main/res/drawable/ic_flashlight_off_white_24dp.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M18,5l0,-3l-12,0l0,1.17l1.83,1.83z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
<path
|
||||
android:pathData="M16,11l2,-3l0,-1l-8.17,0l6.17,6.17z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
<path
|
||||
android:pathData="M2.81,2.81L1.39,4.22L8,10.83V22h8v-3.17l3.78,3.78l1.41,-1.41L2.81,2.81z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
</vector>
|
||||
12
app/src/main/res/drawable/ic_flashlight_on_white_24dp.xml
Normal file
12
app/src/main/res/drawable/ic_flashlight_on_white_24dp.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M6,2h12v3h-12z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
<path
|
||||
android:pathData="M6,7v1l2,3v11h8V11l2,-3V7H6zM12,15.5c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5s1.5,0.67 1.5,1.5S12.83,15.5 12,15.5z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/inactive_dot.xml
Normal file
9
app/src/main/res/drawable/inactive_dot.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval" android:useLevel="true"
|
||||
android:dither="true">
|
||||
|
||||
<size android:height="8dip" android:width="8dip"/>
|
||||
|
||||
<solid android:color="@android:color/white"/>
|
||||
</shape>
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@android:color/darker_gray"/>
|
||||
<size android:height="1dp"/>
|
||||
</shape>
|
||||
@@ -22,21 +22,333 @@
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<LinearLayout
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="?attr/actionBarSize"
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/aboutText"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:linksClickable="true"
|
||||
android:singleLine="false"
|
||||
android:focusable="true" />
|
||||
android:orientation="vertical">
|
||||
|
||||
</LinearLayout>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/version_history"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/version_history_main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:padding="2dp"
|
||||
android:text="@string/version_history"
|
||||
android:textColor="@color/colorSecondaryText"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/version_history_sub"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="2dp"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/version_history_main" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:text="@string/arrow"
|
||||
android:textColor="@color/colorSecondaryText"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/credits"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/credits_main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:padding="2dp"
|
||||
android:text="@string/credits"
|
||||
android:textColor="@color/colorSecondaryText"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/credits_sub"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="2dp"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/credits_main" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:text="@string/arrow"
|
||||
android:textColor="@color/colorSecondaryText"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/translate"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/translate_main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:padding="2dp"
|
||||
android:text="@string/help_translate_this_app"
|
||||
android:textColor="@color/colorSecondaryText"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:text="@string/arrow"
|
||||
android:textColor="@color/colorSecondaryText"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/license"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/license_main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:padding="2dp"
|
||||
android:text="@string/license"
|
||||
android:textColor="@color/colorSecondaryText"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/license_sub"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="2dp"
|
||||
android:text="@string/app_license"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/license_main"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:text="@string/arrow"
|
||||
android:textColor="@color/colorSecondaryText"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/repo"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/repo_main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:padding="2dp"
|
||||
android:text="@string/source_repository"
|
||||
android:textColor="@color/colorSecondaryText"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/repo_sub"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="2dp"
|
||||
android:text="@string/on_github"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/repo_main" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:text="@string/arrow"
|
||||
android:textColor="@color/colorSecondaryText"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/privacy"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/privacy_main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:padding="2dp"
|
||||
android:text="@string/privacy_policy"
|
||||
android:textColor="@color/colorSecondaryText"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/privacy_sub"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="2dp"
|
||||
android:text="@string/and_data_usage"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/privacy_main" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:text="@string/arrow"
|
||||
android:textColor="@color/colorSecondaryText"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/rate"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rate_main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:padding="2dp"
|
||||
android:text="@string/rate_this_app"
|
||||
android:textColor="@color/colorSecondaryText"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rate_sub"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="2dp"
|
||||
android:text="@string/on_google_play"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/rate_main" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:text="@string/arrow"
|
||||
android:textColor="@color/colorSecondaryText"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:padding="8dp"
|
||||
android:id="@+id/report_error"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/report_error_main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:padding="2dp"
|
||||
android:text="@string/report_error"
|
||||
android:textColor="@color/colorSecondaryText"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/report_error_sub"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@id/report_error_main"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:textSize="16sp"
|
||||
android:text="@string/on_github"
|
||||
android:padding="2dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:text="@string/arrow"
|
||||
android:textColor="@color/colorSecondaryText"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
@@ -82,7 +82,6 @@
|
||||
android:layout_height="@dimen/barcode_disp_height"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:id="@+id/aztecBarcode"
|
||||
android:contentDescription="@string/barcodeImageDescription"
|
||||
android:layout_weight="1.0"/>
|
||||
<TextView
|
||||
android:id="@+id/aztecBarcodeText"
|
||||
@@ -103,7 +102,6 @@
|
||||
android:layout_height="@dimen/barcode_disp_height"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:id="@+id/code39Barcode"
|
||||
android:contentDescription="@string/barcodeImageDescription"
|
||||
android:layout_weight="1.0"/>
|
||||
<TextView
|
||||
android:id="@+id/code39BarcodeText"
|
||||
@@ -124,7 +122,6 @@
|
||||
android:layout_height="@dimen/barcode_disp_height"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:id="@+id/code128Barcode"
|
||||
android:contentDescription="@string/barcodeImageDescription"
|
||||
android:layout_weight="1.0"/>
|
||||
<TextView
|
||||
android:id="@+id/code128BarcodeText"
|
||||
@@ -145,7 +142,6 @@
|
||||
android:layout_height="@dimen/barcode_disp_height"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:id="@+id/codabarBarcode"
|
||||
android:contentDescription="@string/barcodeImageDescription"
|
||||
android:layout_weight="1.0"/>
|
||||
<TextView
|
||||
android:id="@+id/codabarBarcodeText"
|
||||
@@ -166,7 +162,6 @@
|
||||
android:layout_height="@dimen/barcode_disp_height"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:id="@+id/datamatrixBarcode"
|
||||
android:contentDescription="@string/barcodeImageDescription"
|
||||
android:layout_weight="1.0"/>
|
||||
<TextView
|
||||
android:id="@+id/datamatrixBarcodeText"
|
||||
@@ -187,7 +182,6 @@
|
||||
android:layout_height="@dimen/barcode_disp_height"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:id="@+id/ean8Barcode"
|
||||
android:contentDescription="@string/barcodeImageDescription"
|
||||
android:layout_weight="1.0"/>
|
||||
<TextView
|
||||
android:id="@+id/ean8BarcodeText"
|
||||
@@ -208,7 +202,6 @@
|
||||
android:layout_height="@dimen/barcode_disp_height"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:id="@+id/ean13Barcode"
|
||||
android:contentDescription="@string/barcodeImageDescription"
|
||||
android:layout_weight="1.0"/>
|
||||
<TextView
|
||||
android:id="@+id/ean13BarcodeText"
|
||||
@@ -229,7 +222,6 @@
|
||||
android:layout_height="@dimen/barcode_disp_height"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:id="@+id/itfBarcode"
|
||||
android:contentDescription="@string/barcodeImageDescription"
|
||||
android:layout_weight="1.0"/>
|
||||
<TextView
|
||||
android:id="@+id/itfBarcodeText"
|
||||
@@ -250,7 +242,6 @@
|
||||
android:layout_height="@dimen/barcode_disp_height"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:id="@+id/pdf417Barcode"
|
||||
android:contentDescription="@string/barcodeImageDescription"
|
||||
android:layout_weight="1.0"/>
|
||||
<TextView
|
||||
android:id="@+id/pdf417BarcodeText"
|
||||
@@ -271,7 +262,6 @@
|
||||
android:layout_height="@dimen/barcode_disp_height"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:id="@+id/qrcodeBarcode"
|
||||
android:contentDescription="@string/barcodeImageDescription"
|
||||
android:layout_weight="1.0"/>
|
||||
<TextView
|
||||
android:id="@+id/qrcodeBarcodeText"
|
||||
@@ -292,7 +282,6 @@
|
||||
android:layout_height="@dimen/barcode_disp_height"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:id="@+id/upcaBarcode"
|
||||
android:contentDescription="@string/barcodeImageDescription"
|
||||
android:layout_weight="1.0"/>
|
||||
<TextView
|
||||
android:id="@+id/upcaBarcodeText"
|
||||
@@ -304,6 +293,26 @@
|
||||
android:textStyle="bold"
|
||||
android:layout_weight="1.0" />
|
||||
</LinearLayout>
|
||||
<LinearLayout android:orientation="vertical"
|
||||
android:padding="10.0dp"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/barcode_disp_height"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:id="@+id/upceBarcode"
|
||||
android:layout_weight="1.0"/>
|
||||
<TextView
|
||||
android:id="@+id/upceBarcodeText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="10.0dip"
|
||||
android:gravity="center"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
android:textStyle="bold"
|
||||
android:layout_weight="1.0" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
@@ -26,12 +26,24 @@
|
||||
android:gravity="center"
|
||||
android:text="@string/noMatchingGiftCards"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<TextView
|
||||
style="@style/AppTheme.TextView.NoData"
|
||||
android:id="@+id/noGroupCardsText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:text="@string/noGroupCards"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list"
|
||||
app:layoutManager="androidx.recyclerview.widget.StaggeredGridLayoutManager"
|
||||
app:spanCount="@integer/main_view_card_columns"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scrollbars="vertical"
|
||||
android:visibility="gone" />
|
||||
android:background="@color/mainLoyaltyCardBackground"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
@@ -30,14 +30,12 @@
|
||||
android:id="@+id/add_from_image"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="addFromImage"
|
||||
android:text="@string/addFromImage" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/add_manually"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="addManually"
|
||||
android:text="@string/addManually" />
|
||||
</LinearLayout>
|
||||
</merge>
|
||||
@@ -4,7 +4,7 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:baselineAligned="false"
|
||||
android:padding="@dimen/activity_margin">
|
||||
@@ -44,8 +44,7 @@
|
||||
android:layout_weight="1"
|
||||
app:srcCompat="@drawable/ic_baseline_arrow_drop_up_24"
|
||||
android:contentDescription="@string/moveUp"
|
||||
app:tint="@color/iconColor"
|
||||
android:onClick="moveGroupUp"/>
|
||||
app:tint="@color/iconColor"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/moveDown"
|
||||
@@ -54,8 +53,7 @@
|
||||
android:layout_weight="1"
|
||||
app:srcCompat="@drawable/ic_baseline_arrow_drop_down_24"
|
||||
android:contentDescription="@string/moveDown"
|
||||
app:tint="@color/iconColor"
|
||||
android:onClick="moveGroupDown"/>
|
||||
app:tint="@color/iconColor"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/edit"
|
||||
@@ -64,8 +62,7 @@
|
||||
android:layout_weight="1"
|
||||
app:srcCompat="@drawable/ic_mode_edit_white_24dp"
|
||||
android:contentDescription="@string/edit"
|
||||
app:tint="@color/iconColor"
|
||||
android:onClick="editGroup" />
|
||||
app:tint="@color/iconColor"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/delete"
|
||||
@@ -74,8 +71,7 @@
|
||||
android:layout_weight="1"
|
||||
app:srcCompat="@drawable/ic_delete_white_24dp"
|
||||
android:contentDescription="@string/delete"
|
||||
app:tint="@color/iconColor"
|
||||
android:onClick="deleteGroup"/>
|
||||
app:tint="@color/iconColor"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -17,9 +17,10 @@
|
||||
android:text="@string/noGroups"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<ListView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list"
|
||||
android:visibility="gone"/>
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scrollbars="vertical"
|
||||
android:visibility="gone" />
|
||||
</RelativeLayout>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true">
|
||||
<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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fabSave"
|
||||
@@ -38,6 +40,10 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/barcode"/>
|
||||
<com.google.android.material.tabs.TabItem
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/photos"/>
|
||||
</com.google.android.material.tabs.TabLayout>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
@@ -62,7 +68,7 @@
|
||||
android:paddingTop="@dimen/inputPadding"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="@dimen/cardThumbnailSize"
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
android:layout_marginEnd="@dimen/activity_margin"
|
||||
@@ -78,7 +84,7 @@
|
||||
android:contentDescription="@string/thumbnailDescription"
|
||||
android:src="@mipmap/ic_launcher"/>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/storeNameField"
|
||||
@@ -307,7 +313,6 @@
|
||||
android:padding="10.0dp"
|
||||
android:background="#ffffff"
|
||||
android:id="@+id/barcode"
|
||||
android:contentDescription="@string/barcodeImageDescription"
|
||||
android:layout_weight="1.0"/>
|
||||
</LinearLayout>
|
||||
|
||||
@@ -326,6 +331,82 @@
|
||||
android:layout_weight="1.0"/>
|
||||
</LinearLayout>
|
||||
</TableLayout>
|
||||
|
||||
<TableLayout
|
||||
android:id="@+id/picturesPart"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<!-- Front image -->
|
||||
<LinearLayout
|
||||
android:id="@+id/frontImageHolder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="@dimen/inputPadding"
|
||||
android:paddingTop="@dimen/inputPadding">
|
||||
|
||||
<!-- Front image -->
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/activity_margin"
|
||||
android:layout_marginTop="@dimen/activity_margin"
|
||||
android:layout_marginEnd="@dimen/activity_margin"
|
||||
android:layout_marginBottom="@dimen/activity_margin"
|
||||
android:paddingHorizontal="@dimen/inputPadding"
|
||||
app:cardCornerRadius="4dp"
|
||||
app:cardElevation="0dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/frontImage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:adjustViewBounds="true"
|
||||
android:minHeight="50dp"
|
||||
android:contentDescription="@string/frontImageDescription"
|
||||
android:scaleType="fitCenter"
|
||||
app:srcCompat="@drawable/ic_camera_white" />
|
||||
|
||||
</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 -->
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/activity_margin"
|
||||
android:layout_marginTop="@dimen/activity_margin"
|
||||
android:layout_marginEnd="@dimen/activity_margin"
|
||||
android:layout_marginBottom="@dimen/activity_margin"
|
||||
android:paddingHorizontal="@dimen/inputPadding"
|
||||
app:cardCornerRadius="4dp"
|
||||
app:cardElevation="0dp">
|
||||
|
||||
<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" />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
</LinearLayout>
|
||||
</TableLayout>
|
||||
</TableLayout>
|
||||
</ScrollView>
|
||||
|
||||
|
||||
@@ -1,118 +1,154 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/row"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="0.5dp"
|
||||
android:background="@drawable/list_row"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:padding="@dimen/activity_margin">
|
||||
android:layout_margin="8dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/information_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/activity_margin"
|
||||
android:layout_marginLeft="@dimen/activity_margin"
|
||||
android:layout_toEndOf="@+id/thumbnail_container"
|
||||
android:layout_toRightOf="@+id/thumbnail_container"
|
||||
android:layout_weight="1"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/thumbnail_container"
|
||||
app:layout_constraintEnd_toStartOf="@+id/star"
|
||||
app:layout_constraintStart_toEndOf="@+id/thumbnail_container"
|
||||
app:layout_constraintTop_toTopOf="@+id/thumbnail_container">
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/store"
|
||||
android:layout_width="wrap_content"
|
||||
<LinearLayout
|
||||
android:id="@+id/information_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/storeNameTextSize"
|
||||
android:textStyle="bold" />
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/note"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:textSize="@dimen/noteTextSize" />
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/balance"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:textSize="@dimen/noteTextSize" />
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/thumbnail_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:cardCornerRadius="4dp"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/expiry"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:textSize="@dimen/noteTextSize" />
|
||||
<RelativeLayout
|
||||
android:id="@+id/thumbnail_front"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignTop="@+id/storeContainer"
|
||||
android:layout_alignBottom="@+id/storeContainer"
|
||||
android:layout_gravity="center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/thumbnail"
|
||||
android:scaleType="centerCrop"
|
||||
android:layout_width="@dimen/cardThumbnailSize"
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
android:layout_centerVertical="true"
|
||||
android:contentDescription="@string/thumbnailDescription"
|
||||
android:src="@mipmap/ic_launcher" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/thumbnail_back"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/selected_thumbnail"
|
||||
android:layout_width="@dimen/cardThumbnailSize"
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
android:layout_centerInParent="true"
|
||||
android:contentDescription="@string/thumbnailDescription"
|
||||
android:scaleType="centerCrop"
|
||||
app:srcCompat="@drawable/ic_done" />
|
||||
|
||||
</RelativeLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/storeContainer"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_toEndOf="@+id/thumbnail_container">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/store"
|
||||
android:paddingStart="16dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="left"
|
||||
android:textAppearance="?attr/textAppearanceHeadline6" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/star"
|
||||
android:layout_width="@dimen/cardThumbnailSize"
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
android:layout_centerVertical="true"
|
||||
android:scaleType="centerCrop"
|
||||
app:srcCompat="@drawable/ic_starred_white"
|
||||
android:contentDescription="@string/starImage"
|
||||
app:tint="?attr/colorControlNormal"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:visibility="gone" />
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/note"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textAppearance="?attr/textAppearanceBody2"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:visibility="gone" />
|
||||
|
||||
<View android:id="@+id/info_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:background="?android:attr/dividerVertical"
|
||||
android:visibility="gone" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
<TextView
|
||||
android:id="@+id/balance"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingEnd="8dp"
|
||||
android:textAppearance="?attr/textAppearanceBody2"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
app:drawableLeftCompat="@drawable/ic_baseline_payments_24"
|
||||
android:drawablePadding="4dp"
|
||||
android:layout_alignParentStart="true"
|
||||
android:visibility="gone" />
|
||||
<TextView
|
||||
android:id="@+id/expiry"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:gravity="center_vertical"
|
||||
android:textAppearance="?attr/textAppearanceBody2"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
app:drawableLeftCompat="@drawable/ic_baseline_access_time_24"
|
||||
android:drawablePadding="4dp"
|
||||
android:layout_toEndOf="@+id/balance"
|
||||
android:visibility="gone" />
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/thumbnail_container"
|
||||
android:layout_width="@dimen/cardThumbnailSize"
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
app:cardCornerRadius="4dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/thumbnail_front"
|
||||
android:layout_width="@dimen/cardThumbnailSize"
|
||||
android:layout_height="@dimen/cardThumbnailSize">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/thumbnail"
|
||||
android:layout_width="@dimen/cardThumbnailSize"
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
android:contentDescription="@string/thumbnailDescription"
|
||||
android:src="@mipmap/ic_launcher" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/thumbnail_back"
|
||||
android:layout_width="@dimen/cardThumbnailSize"
|
||||
android:layout_height="@dimen/cardThumbnailSize">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/cardThumbnailSize"
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:contentDescription="@string/thumbnailDescription"
|
||||
app:srcCompat="@drawable/ic_done" />
|
||||
|
||||
</RelativeLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/star"
|
||||
android:layout_width="@dimen/cardThumbnailSize"
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
android:layout_marginLeft="@dimen/activity_margin"
|
||||
app:srcCompat="@drawable/ic_starred_white"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="@dimen/activity_margin"
|
||||
android:contentDescription="@string/starImage"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:tint="@android:color/black" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
@@ -45,6 +45,7 @@
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/maximizeButton"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="15.0dip"
|
||||
@@ -53,26 +54,25 @@
|
||||
android:padding="0dp"
|
||||
app:srcCompat="@drawable/ic_baseline_arrow_drop_up_24"
|
||||
android:contentDescription="@string/moveBarcodeToTopOfScreen"
|
||||
android:tint="@android:color/white"
|
||||
android:background="@color/colorPrimary"
|
||||
app:tint="#ffffff"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/barcode"
|
||||
app:layout_constraintBottom_toTopOf="@+id/mainImage"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/barcode"
|
||||
android:layout_width="0dp"
|
||||
android:id="@+id/mainImage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginStart="15.0dip"
|
||||
android:layout_marginEnd="15.0dip"
|
||||
android:padding="10dp"
|
||||
android:background="#ffffff"
|
||||
app:layout_constraintBottom_toTopOf="@+id/centerGuideline"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/maximizeButton"
|
||||
android:contentDescription="@string/barcodeImageDescription"/>
|
||||
app:layout_constraintTop_toBottomOf="@+id/maximizeButton"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/minimizeButton"
|
||||
@@ -81,15 +81,31 @@
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="15.0dip"
|
||||
android:layout_marginEnd="15.0dip"
|
||||
android:layout_marginTop="10dp"
|
||||
android:padding="0dp"
|
||||
app:srcCompat="@drawable/ic_baseline_arrow_drop_down_24"
|
||||
android:contentDescription="@string/moveBarcodeToCenterOfScreen"
|
||||
android:tint="@android:color/white"
|
||||
android:background="@color/colorPrimary"
|
||||
app:layout_constraintTop_toBottomOf="@+id/barcode"
|
||||
app:tint="#ffffff"
|
||||
app:layout_constraintTop_toBottomOf="@+id/mainImage"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/dotIndicator"
|
||||
android:visibility="gone"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginStart="15.0dip"
|
||||
android:layout_marginEnd="15.0dip"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/minimizeButton"/>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/barcodeScaler"
|
||||
android:visibility="gone"
|
||||
@@ -106,12 +122,16 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/cardIdView"
|
||||
android:enabled="true"
|
||||
android:textIsSelectable="true"
|
||||
android:focusable="true"
|
||||
android:longClickable="true"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginLeft="10.0dip"
|
||||
android:layout_marginRight="10.0dip"
|
||||
android:paddingBottom="80dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/minimizeButton"
|
||||
app:layout_constraintTop_toBottomOf="@+id/dotIndicator"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -134,60 +154,85 @@
|
||||
android:id="@+id/bottom_sheet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/inputBackground"
|
||||
android:fitsSystemWindows="false"
|
||||
android:orientation="vertical"
|
||||
android:padding="20dp"
|
||||
android:visibility="gone"
|
||||
app:behavior_hideable="false"
|
||||
app:behavior_peekHeight="104dp"
|
||||
app:behavior_peekHeight="80dp"
|
||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
|
||||
tools:visibility="visible"
|
||||
android:fitsSystemWindows="true">
|
||||
tools:visibility="visible">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/bottomSheetButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="80dp"
|
||||
android:background="@color/colorPrimary"
|
||||
android:scaleType="fitCenter"
|
||||
android:layout_gravity="top|start"
|
||||
android:tint="@android:color/white"
|
||||
android:scaleType="fitCenter"
|
||||
android:contentDescription="@string/toggleMoreInfo"
|
||||
app:tint="#ffffff"
|
||||
app:srcCompat="@drawable/ic_baseline_arrow_drop_up_24" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/noteView"
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/bottomSheetContentWrapper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/inputBackground"
|
||||
android:gravity="center"
|
||||
android:padding="20dp"
|
||||
android:textSize="@dimen/singleCardNoteTextSizeMin" />
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/groupsView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/inputBackground"
|
||||
android:gravity="center"
|
||||
android:padding="20dp"
|
||||
android:textSize="@dimen/singleCardNoteTextSizeMin" />
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/balanceView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/inputBackground"
|
||||
android:gravity="center"
|
||||
android:padding="20dp"
|
||||
android:textSize="@dimen/singleCardNoteTextSizeMin" />
|
||||
<TextView
|
||||
android:id="@+id/noteView"
|
||||
android:enabled="true"
|
||||
android:textIsSelectable="true"
|
||||
android:focusable="true"
|
||||
android:longClickable="true"
|
||||
android:autoLink="all"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:padding="20dp"
|
||||
android:textSize="@dimen/singleCardNoteTextSizeMin" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/expiryView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/inputBackground"
|
||||
android:gravity="center"
|
||||
android:padding="20dp"
|
||||
android:textSize="@dimen/singleCardNoteTextSizeMin" />
|
||||
<TextView
|
||||
android:id="@+id/groupsView"
|
||||
android:enabled="true"
|
||||
android:textIsSelectable="true"
|
||||
android:focusable="true"
|
||||
android:longClickable="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:padding="20dp"
|
||||
android:textSize="@dimen/singleCardNoteTextSizeMin" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/balanceView"
|
||||
android:enabled="true"
|
||||
android:textIsSelectable="true"
|
||||
android:focusable="true"
|
||||
android:longClickable="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:padding="20dp"
|
||||
android:textSize="@dimen/singleCardNoteTextSizeMin" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/expiryView"
|
||||
android:enabled="true"
|
||||
android:textIsSelectable="true"
|
||||
android:focusable="true"
|
||||
android:longClickable="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:padding="20dp"
|
||||
android:textSize="@dimen/singleCardNoteTextSizeMin" />
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
@@ -197,7 +242,6 @@
|
||||
android:clipToPadding="false"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="24dp"
|
||||
android:weightSum="1.0"
|
||||
android:fitsSystemWindows="true">
|
||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
@@ -212,7 +256,7 @@
|
||||
app:expandedTitleMarginEnd="64dp"
|
||||
app:contentScrim="?colorPrimary"
|
||||
app:expandedTitleGravity="top">
|
||||
<TextView
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/storeName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -243,6 +287,7 @@
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="?actionBarSize"
|
||||
app:contentInsetStart="72.0dip"
|
||||
app:layout_collapseMode="pin" />
|
||||
app:layout_collapseMode="pin"
|
||||
android:paddingTop="6dp" />
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
@@ -2,8 +2,23 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay"/>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/settings_container"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
21
app/src/main/res/layout/sorting_option.xml
Normal file
21
app/src/main/res/layout/sorting_option.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkBox_reverse"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:paddingLeft="20dp"
|
||||
android:text="@string/reverse"
|
||||
android:textSize="19sp"/>
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -7,20 +7,27 @@
|
||||
android:title="@string/copy_to_clipboard"
|
||||
android:icon="@drawable/ic_copy"
|
||||
android:titleCondensed="@string/copy_to_clipboard"
|
||||
app:showAsAction="always"/>
|
||||
app:showAsAction="ifRoom"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_share"
|
||||
android:title="@string/share"
|
||||
android:icon="@drawable/ic_share"
|
||||
android:titleCondensed="@string/share"
|
||||
app:showAsAction="always"/>
|
||||
app:showAsAction="ifRoom"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_edit"
|
||||
android:icon="@drawable/ic_edit"
|
||||
android:title="@string/editCardTitle"
|
||||
android:titleCondensed="@string/editCardTitle"
|
||||
app:showAsAction="always"/>
|
||||
app:showAsAction="ifRoom"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_delete"
|
||||
android:icon="@drawable/ic_delete_white_24dp"
|
||||
android:title="@string/delete"
|
||||
android:titleCondensed="@string/delete"
|
||||
app:showAsAction="ifRoom"/>
|
||||
|
||||
</menu>
|
||||
@@ -8,24 +8,25 @@
|
||||
android:icon="@drawable/ic_search_white"
|
||||
app:actionViewClass="androidx.appcompat.widget.SearchView"
|
||||
app:showAsAction="always|collapseActionView"/>
|
||||
<item
|
||||
android:id="@+id/action_sort"
|
||||
android:title="@string/sort"
|
||||
android:icon="@drawable/ic_baseline_sort_24"
|
||||
app:showAsAction="always"/>
|
||||
<item
|
||||
android:id="@+id/action_manage_groups"
|
||||
android:icon="@drawable/ic_folder_white"
|
||||
android:title="@string/groups"
|
||||
app:showAsAction="always"/>
|
||||
app:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/action_import_export"
|
||||
android:icon="@drawable/ic_import_export_white_24dp"
|
||||
android:title="@string/importExport"
|
||||
app:showAsAction="ifRoom"/>
|
||||
app:showAsAction="never"/>
|
||||
<item
|
||||
android:id="@+id/action_settings"
|
||||
android:title="@string/settings"
|
||||
app:showAsAction="never"/>
|
||||
<item
|
||||
android:id="@+id/action_privacy_policy"
|
||||
android:title="@string/privacy_policy"
|
||||
app:showAsAction="never"/>
|
||||
<item
|
||||
android:id="@+id/action_about"
|
||||
android:title="@string/about"
|
||||
|
||||
9
app/src/main/res/menu/scan_menu.xml
Normal file
9
app/src/main/res/menu/scan_menu.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/action_toggle_flashlight"
|
||||
android:icon="@drawable/ic_flashlight_off_white_24dp"
|
||||
android:title="@string/turn_flashlight_on"
|
||||
app:showAsAction="always"/>
|
||||
</menu>
|
||||
74
app/src/main/res/raw/contributors.txt
Normal file
74
app/src/main/res/raw/contributors.txt
Normal file
@@ -0,0 +1,74 @@
|
||||
Sylvia van Os
|
||||
Branden Archer
|
||||
J. Lavoie
|
||||
Allan Nordhøy
|
||||
Heimen Stoffels
|
||||
solokot
|
||||
mondstern
|
||||
Taco
|
||||
IllusiveMan196
|
||||
Petr Novák
|
||||
Oğuz Ersen
|
||||
Gediminas Murauskas
|
||||
Joel A
|
||||
StoyanDimitrov
|
||||
Samantaz Fox
|
||||
Nyatsuki
|
||||
arno-github
|
||||
Ankit Tiwari
|
||||
Sergio Paredes
|
||||
arshbeerSingh
|
||||
huuhaa
|
||||
Michael Moroni
|
||||
Olivia (Zoe)
|
||||
betsythefc
|
||||
waffshappen
|
||||
Miha Frangež
|
||||
K. Herbert
|
||||
Quentin PAGÈS
|
||||
String E. Fighter
|
||||
Yurical
|
||||
Adolfo Jayme-Barrientos
|
||||
Alessandro Mandelli
|
||||
KovalevArtem
|
||||
Clonewayx
|
||||
D. Domig
|
||||
Diego
|
||||
Jane Kong
|
||||
Lukas Grassauer
|
||||
Michalis
|
||||
schirinowski
|
||||
Thomas Bertels
|
||||
inesre
|
||||
lgasp
|
||||
phlostically
|
||||
Aditya Das
|
||||
Kevin Sicong Jiang
|
||||
Airat
|
||||
BMN
|
||||
Biren
|
||||
Flav
|
||||
Franciszek Stefan
|
||||
Izzy
|
||||
Karol Kosek
|
||||
bittin
|
||||
Maciej Błędkowski
|
||||
Mattia
|
||||
Michael Gangolf
|
||||
pbeckmann
|
||||
Peer Beckmann
|
||||
QuangDNguyen2211
|
||||
Rohan Babbar
|
||||
Rose Liverman
|
||||
Simone Dotto
|
||||
Still Hsu
|
||||
Subhashish Anand
|
||||
Tymofii Lytvynenko
|
||||
Tjipke van der Heide
|
||||
avikkundu
|
||||
opsik
|
||||
psa-jforestier
|
||||
Robin
|
||||
sergio
|
||||
Marcus
|
||||
techwebpd
|
||||
7
app/src/main/res/raw/readme.md
Normal file
7
app/src/main/res/raw/readme.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# stocard_stores.csv
|
||||
|
||||
stocard_stores.csv was created by extracting /data/data/de.stocard/de.stocard.stocard/databases/stores on a rooted devices and running the following command over it:
|
||||
|
||||
sqlite3 -header -csv stores "select _id,name,barcodeFormat from stores" > stocard_stores.csv
|
||||
|
||||
Only used for data portability reasons (ensuring importing works). Do NOT copy this anywhere else or use it for any purpose other than ensuring we can import a GDPR-provided export. We want to make sure this stays under fair use.
|
||||
4249
app/src/main/res/raw/stocard_stores.csv
Normal file
4249
app/src/main/res/raw/stocard_stores.csv
Normal file
File diff suppressed because it is too large
Load Diff
213
app/src/main/res/values-bg/strings.xml
Normal file
213
app/src/main/res/values-bg/strings.xml
Normal file
@@ -0,0 +1,213 @@
|
||||
<?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">
|
||||
<string name="action_add">Добавяне</string>
|
||||
<string name="action_search">Търсене</string>
|
||||
<string name="leaveWithoutSaveTitle">Изход</string>
|
||||
<string name="confirm">Потвърждаване</string>
|
||||
<string name="delete">Премахване</string>
|
||||
<string name="edit">Редактиране</string>
|
||||
<string name="save">Запазване</string>
|
||||
<string name="cancel">Отказ</string>
|
||||
<string name="unstar">Премахва от любими</string>
|
||||
<string name="star">Добавя към любими</string>
|
||||
<string name="noBarcode">Без щрихкод</string>
|
||||
<string name="barcodeNoBarcode">Картата няма щрихкод</string>
|
||||
<string name="barcodeType">Вид на щрихкод</string>
|
||||
<string name="cardId">Идентификатор на карта</string>
|
||||
<string name="note">Бележка</string>
|
||||
<string name="storeName">Наименование</string>
|
||||
<string name="noMatchingGiftCards">Няма резултати. Променете критериите за търсене.</string>
|
||||
<string name="noGiftCards">Докоснете бутона +, за да добавите карта или внесете от менюто ⋮.</string>
|
||||
<string name="all">Всички</string>
|
||||
<plurals name="groupCardCount">
|
||||
<item quantity="one"><xliff:g>%d</xliff:g> карта</item>
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> карти</item>
|
||||
</plurals>
|
||||
<string name="failedOpeningFileManager">Инсталирайте приложение за управление на файлове.</string>
|
||||
<string name="app_license">Свободен софтуер с авторски права, лицензиран под GPLv3+</string>
|
||||
<string name="frontImageDescription">Снимка на предната страна на карта</string>
|
||||
<string name="backImageDescription">Снимка на задната страна на карта</string>
|
||||
<string name="parsingBalanceFailed"><xliff:g>%s</xliff:g> не изглежда истинска наличност.</string>
|
||||
<string name="no">Не</string>
|
||||
<string name="yes">Да</string>
|
||||
<string name="setBackImage">Снимка на задната страна</string>
|
||||
<string name="setFrontImage">Снимка на предната страна</string>
|
||||
<string name="photos">Снимки</string>
|
||||
<string name="importOptionApplicationExplanation">Изберете файл чрез друго приложение.</string>
|
||||
<string name="noExternalStoragePermissionError">Дайте разрешение за достъп до хранилището, за да работи внасянето и изнасянето</string>
|
||||
<string name="noCardExistsError">Картата не е намерена</string>
|
||||
<string name="updateBarcodeQuestionText">Идентификаторът е променен. Желаете ли с неговата стойност да бъде променен и щрихкода\?</string>
|
||||
<string name="updateBarcodeQuestionTitle">Обновяване на щрихкода\?</string>
|
||||
<string name="noCardIdError">Не е въведен идентификатор</string>
|
||||
<string name="noCardsMessage">Добавете карта</string>
|
||||
<string name="cardShortcut">Пряк път до карта</string>
|
||||
<string name="addCardTitle">Добавяне на карта</string>
|
||||
<string name="removeImage">Премахване на изображение</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="wrongValueForBarcodeType">Стойността е невалидна за избрания вид щрихкод</string>
|
||||
<string name="setBarcodeId">Задаване на стойност</string>
|
||||
<string name="sameAsCardId">Същата като идентификатора</string>
|
||||
<string name="barcodeId">Стойност на щрихкода</string>
|
||||
<string name="importLoyaltyCardKeychain">Внасяне от Loyalty Card Keychain</string>
|
||||
<string name="importFidmeMessage">Изберете файла <i>fidme-export-request-xxxxxx.zip</i>, предварително изнесен от FidMe и ръчно изберете вида на щрихкодовете.
|
||||
\nСъздайте такъв файл от Data Protection в менюто на профила във FidMe и изберете „Extract my data“.</string>
|
||||
<string name="importFidme">Внасяне от FidMe</string>
|
||||
<string name="exportOptionExplanation">Данните ще бъдат запазени на място по ваш избор.</string>
|
||||
<string name="accept">Приемане</string>
|
||||
<string name="privacy_policy_popup_text">Политика за личните данни (необходима от някои магазини за приложения):
|
||||
\n
|
||||
\nНЕ СЕ СЪБИРАТ ИЗОБЩО НИКАКВИ ДАННИ, което може да бъде потвърдено, защото приложението е със свободен код.</string>
|
||||
<string name="privacy_policy">Политика за личните данни</string>
|
||||
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
|
||||
<string name="turn_flashlight_off">Изключва светкавицата</string>
|
||||
<string name="turn_flashlight_on">Включва светкавицата</string>
|
||||
<string name="passwordRequired">Въведете паролата</string>
|
||||
<string name="points">Точки</string>
|
||||
<string name="currency">Валута</string>
|
||||
<string name="balance">Наличност</string>
|
||||
<string name="errorReadingImage">Изображението е нечетимо</string>
|
||||
<string name="noBarcodeFound">Не е намерен щрихкод</string>
|
||||
<string name="moveBarcodeToCenterOfScreen">Премества щрихкода в центъра на екрана</string>
|
||||
<string name="moveBarcodeToTopOfScreen">Премества щрихкода най-горе на екрана</string>
|
||||
<string name="never">Не изтича</string>
|
||||
<string name="chooseExpiryDate">Дата на изтичане</string>
|
||||
<string name="expiryDate">Валидност</string>
|
||||
<string name="editBarcode">Редактиране на щрихкод</string>
|
||||
<string name="barcode">Щрихкод</string>
|
||||
<string name="card">Карта</string>
|
||||
<string name="groupsList">Списъци: <xliff:g>%s</xliff:g></string>
|
||||
<string name="expiryStateSentence">Валидност: <xliff:g>%s</xliff:g></string>
|
||||
<string name="expiryStateSentenceExpired">Изтекла: <xliff:g>%s</xliff:g></string>
|
||||
<string name="balancePoints"><xliff:g>%s</xliff:g> точки</string>
|
||||
<string name="balanceSentence">Наличност: <xliff:g>%s</xliff:g></string>
|
||||
<string name="noGroups">Докоснете бутона +, за да добавите списък.</string>
|
||||
<string name="noStoreError">Не е въведено наименование</string>
|
||||
<string name="groups">Списъци</string>
|
||||
<string name="enter_group_name">Въведете име на списък</string>
|
||||
<string name="intent_import_card_from_url_share_text">Искам да споделя тази карта с вас</string>
|
||||
<string name="settings_display_barcode_max_brightness">Увеличаване на яркостта при видим щрихкод</string>
|
||||
<string name="settings_lock_barcode_orientation">Спиране на завъртането на щрихкода</string>
|
||||
<string name="settings_keep_screen_on">Поддържане на екрана включен</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Предотвратяване на заключване на екрана</string>
|
||||
<string name="settings_max_font_size_scale">Максимален размер на шрифта</string>
|
||||
<string name="settings_dark_theme">Тъмна</string>
|
||||
<string name="settings_light_theme">Светла</string>
|
||||
<string name="settings_system_theme">Системна</string>
|
||||
<string name="settings_theme">Тема</string>
|
||||
<string name="settings_category_title_ui">Потребителски интерфейс</string>
|
||||
<string name="settings">Настройки</string>
|
||||
<string name="starImage">Звезда за любимо</string>
|
||||
<string name="thumbnailDescription">Миниатюра на картата</string>
|
||||
<string name="copy_to_clipboard_toast">Идентификаторът на картата е копиран в междинната памет</string>
|
||||
<string name="enterBarcodeInstructions">Въведете идентификатор на картата или като изберете вида на щрихкода или докоснете бутона „Картата няма щрихкод“.</string>
|
||||
<string name="selectBarcodeTitle">Избиране на щрихкод</string>
|
||||
<string name="importOptionApplicationButton">Избиране чрез приложение</string>
|
||||
<string name="importing">Внасяне…</string>
|
||||
<string name="exporting">Изнасяне…</string>
|
||||
<string name="exportFailed">Картите не могат да бъдат изнесени</string>
|
||||
<string name="exportFailedTitle">Грешка при изнасяне</string>
|
||||
<string name="importFailed">Картите не могат да бъдат внесени</string>
|
||||
<string name="importFailedTitle">Грешка при внасяне</string>
|
||||
<string name="exportSuccessfulTitle">Резултат от изнасяне</string>
|
||||
<string name="importSuccessfulTitle">Резултат от внасяне</string>
|
||||
<string name="importExportHelp">Резервните копия на картите ви дават възможност да ги преместите на друго устройство.</string>
|
||||
<string name="exportName">Изнасяне</string>
|
||||
<string name="importExport">Внасяне/изнасяне</string>
|
||||
<string name="sendLabel">Изпращане…</string>
|
||||
<string name="scanCardBarcode">Снемане на щрихкод от карта</string>
|
||||
<string name="editCardTitle">Редактиране на карта</string>
|
||||
<string name="share">Споделя</string>
|
||||
<string name="copy_to_clipboard">Копира идентификатора в междинната памет</string>
|
||||
<string name="ok">Добре</string>
|
||||
<string name="importSuccessful">Картите са внесени успешно</string>
|
||||
<string name="chooseImportType">От къде ще внесете\?</string>
|
||||
<string name="importCatimaMessage">Изберете файла <i>catima.zip</i>, предварително изнесен от Catima.
|
||||
\nСъздайте такъв файл от меню Внасяне/изнасяне от друго устройство с Catima като изберете Изнасяне.</string>
|
||||
<string name="importOptionApplicationTitle">От друго приложение</string>
|
||||
<string name="importOptionFilesystemButton">Избиране от файлова система</string>
|
||||
<string name="importOptionFilesystemExplanation">Изберете определен файл от файловата система.</string>
|
||||
<string name="app_resources">Свободни ресурси: <xliff:g id="app_resources_list">%s</xliff:g></string>
|
||||
<string name="app_libraries">Свободни библиотеки: <xliff:g id="app_libraries_list">%s</xliff:g></string>
|
||||
<string name="app_revision_fmt">Компилация: <xliff:g id="app_revision_url">%s</xliff:g></string>
|
||||
<string name="debug_version_fmt">Издание: <xliff:g id="version">%s</xliff:g></string>
|
||||
<string name="about_title_fmt">Относно <xliff:g id="app_name">%s</xliff:g></string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Всички права запазени © 2019–<xliff:g>%d</xliff:g> Sylvia van Os</string>
|
||||
<string name="app_copyright_old">На основата на Loyalty Card Keychain
|
||||
\nвсички права запазени © 2016–2020 Branden Archer</string>
|
||||
<string name="about">Относно</string>
|
||||
<string name="importOptionFilesystemTitle">Внасяне от файловата система</string>
|
||||
<string name="importCatima">Внасяне от Catima</string>
|
||||
<string name="exportSuccessful">Картите са изнесени успешно</string>
|
||||
<string name="unlockScreen">Разрешава автоматичното завъртане</string>
|
||||
<string name="lockScreen">Спира автоматичното завъртане</string>
|
||||
<plurals name="selectedCardCount">
|
||||
<item quantity="one"><xliff:g>%d</xliff:g> избрана карта</item>
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> избрани карти</item>
|
||||
</plurals>
|
||||
<string name="deleteConfirmationGroup">Изтриване на група\?</string>
|
||||
<string name="moveDown">Преместване надолу</string>
|
||||
<string name="moveUp">Преместване нагоре</string>
|
||||
<string name="addFromImage">Избор от галерията</string>
|
||||
<string name="addManually">Ръчно въвеждане</string>
|
||||
<string name="leaveWithoutSaveConfirmation">Оставяте промените незапазени\?</string>
|
||||
<string name="unsupportedBarcodeType">Щрихкод от този вид не може да бъде показан. Може да бъде поддържан в следващо издание.</string>
|
||||
<string name="importStocard">Внасяне от Stocard</string>
|
||||
<string name="importVoucherVault">Внасяне от Voucher Vault</string>
|
||||
<string name="importVoucherVaultMessage">Изберете файла <i>vouchervault.json</i>, предварително изнесен от Voucher Vault.
|
||||
\nСъздайте такъв файл от меню „Export“ във Voucher Vault.</string>
|
||||
<string name="importStocardMessage">Изберете файла <i>***-sync.zip</i>, предварително изнесен от Stocard.
|
||||
\nПолучете го като изпратите писмо на support@stocardapp.com с искане за изнасяне вашите данни.</string>
|
||||
<string name="importLoyaltyCardKeychainMessage">Изберете файла <i>LoyaltyCardKeychain.csv</i>, предварително изнесен от Loyalty Card Keychain.
|
||||
\nСъздайте такъв файл от меню Внасяне/изнасяне от друго устройство с Loyalty Card Keychain като изберете Изнасяне.</string>
|
||||
<string name="failedParsingImportUriError">Препратката не може да бъде анализирана за внасяне</string>
|
||||
<string name="card_ids_copied">[не превеждай този низ, https://github.com/TheLastProject/Catima/issues/278]</string>
|
||||
<string name="failedGeneratingShareURL">Грешка при създаване на адрес за споделяне. Изпратете доклад за дефект.</string>
|
||||
<string name="deleteTitle">Изтрийте картата</string>
|
||||
<plurals name="deleteCardsTitle">
|
||||
<item quantity="one">Изтриване на <xliff:g>%d</xliff:g> карта</item>
|
||||
<item quantity="other">Изтриване на <xliff:g>%d</xliff:g> карти</item>
|
||||
</plurals>
|
||||
<string name="deleteConfirmation">Потвърдете премахване на карта\?</string>
|
||||
<plurals name="deleteCardsConfirmation">
|
||||
<item quantity="one">Изтриване на тази <xliff:g>%d</xliff:g> карта завинаги\?</item>
|
||||
<item quantity="other">Изтриване на тези <xliff:g>%d</xliff:g> карти за постоянно\?</item>
|
||||
</plurals>
|
||||
<string name="app_contributors">Осъществено от: <xliff:g id="app_contributors">%s</xliff:g></string>
|
||||
<string name="settings_brown_theme">Браун</string>
|
||||
<string name="settings_grey_theme">Сив</string>
|
||||
<string name="settings_green_theme">Зелен</string>
|
||||
<string name="settings_sky_blue_theme">Небесно синьо</string>
|
||||
<string name="settings_blue_theme">Синьо</string>
|
||||
<string name="settings_violet_theme">Виолет</string>
|
||||
<string name="settings_magenta_theme">Магента</string>
|
||||
<string name="settings_pink_theme">Розов</string>
|
||||
<string name="settings_catima_theme">Catima</string>
|
||||
<string name="settings_theme_color">Цвят на темата</string>
|
||||
<string name="settings_system_locale">Система</string>
|
||||
<string name="settings_locale">Език</string>
|
||||
<string name="noGroupCards">Тази група не съдържа карти</string>
|
||||
<string name="toggleMoreInfo">Превключване на повече информация</string>
|
||||
<string name="barcodeImageDescriptionWithType">Изображение на щрихкод на карта от вида <xliff:g>%s</xliff:g></string>
|
||||
<string name="swipeToSwitchImages">Плъзване или задържане за смяна на изображения</string>
|
||||
<string name="sort_by">Сортиране по</string>
|
||||
<string name="reverse">Наобратно</string>
|
||||
<string name="sort_by_balance">Наличност</string>
|
||||
<string name="sort_by_expiry">Валидност</string>
|
||||
<string name="sort_by_most_recently_used">Последно използване</string>
|
||||
<string name="sort_by_name">Наименование</string>
|
||||
<string name="sort">Сортиране</string>
|
||||
<string name="credits">Заслуги</string>
|
||||
<string name="license">Лиценз</string>
|
||||
<string name="source_repository">Хранилище на изходния код</string>
|
||||
<string name="on_github">в GitHub</string>
|
||||
<string name="rate_this_app">Оценете приложението</string>
|
||||
<string name="report_error">Докладване на грешка</string>
|
||||
<string name="version_history">История на изданията</string>
|
||||
<string name="on_google_play">в Google Play</string>
|
||||
<string name="and_data_usage">и използване на данни</string>
|
||||
<string name="help_translate_this_app">Помогнете за превода на приложението</string>
|
||||
<string name="exportPasswordHint">Въведете парола</string>
|
||||
<string name="exportPassword">Задаване на парола за защита на изнесеното (по избор)</string>
|
||||
</resources>
|
||||
90
app/src/main/res/values-bn-rIN/strings.xml
Normal file
90
app/src/main/res/values-bn-rIN/strings.xml
Normal file
@@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="save">সংরক্ষণ</string>
|
||||
<string name="cancel">বাতিল</string>
|
||||
<string name="unstar">তারা মুক্ত</string>
|
||||
<string name="star">তারা</string>
|
||||
<string name="barcodeNoBarcode">বারকোড না বারকোড</string>
|
||||
<string name="barcodeType">বারকোড ধরন</string>
|
||||
<string name="note">বিঃদ্রঃ</string>
|
||||
<string name="storeName">দোকানের নাম</string>
|
||||
<string name="noMatchingGiftCards">কোন ম্যাচিং উপহার কার্ড নেই</string>
|
||||
<string name="noGiftCards">উপহার কার্ড নেই</string>
|
||||
<string name="action_add">কর্ম যোগ</string>
|
||||
<string name="all">সব</string>
|
||||
<string name="noGroupCards">গোষ্ঠী কার্ড নেই</string>
|
||||
<string name="noGroups">গোষ্ঠীগুলি নেই</string>
|
||||
<string name="groups">গোষ্ঠীগুলি</string>
|
||||
<string name="enter_group_name">গোষ্ঠী নাম লিখুন </string>
|
||||
<string name="exportSuccessful">রপ্তানি সফল</string>
|
||||
<string name="importSuccessful">আগম সফল</string>
|
||||
<string name="intent_import_card_from_url_share_text">url শেয়ার টেক্সট থেকে ইন্টেন্ট ইম্পোর্ট কার্ড</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card"> কার্ড দেখা কালিন লকস্ক্রিন নিষ্ক্রিয়</string>
|
||||
<string name="settings_keep_screen_on">সেটিংস পর্দা খোলা রাখুন</string>
|
||||
<string name="settings_lock_barcode_orientation"> তালাবদ্ধ বার কোড অভিযোজন</string>
|
||||
<string name="settings_max_font_size_scale">সর্বোচ্চ হরফ আকার</string>
|
||||
<string name="settings_light_theme">সাদাটে থিম</string>
|
||||
<string name="settings_system_theme">যন্ত্রর থিম</string>
|
||||
<string name="settings_theme">থিম</string>
|
||||
<string name="settings_category_title_ui">বিভাগ শিরোনাম</string>
|
||||
<string name="starImage">তারা ছবি</string>
|
||||
<string name="importCatima">ক্যাতিনা আগম</string>
|
||||
<string name="importLoyaltyCardKeychain">আমদানি লয়্যালটি কার্ড কীচেন</string>
|
||||
<string name="importStocard">স্টো কার্ড আমদানি করুন</string>
|
||||
<string name="importVoucherVault">আমদানি ভাউচার ভল্ট</string>
|
||||
<string name="barcodeId">বারকোড আইডি</string>
|
||||
<string name="sameAsCardId">কার্ড আইডির মতো</string>
|
||||
<string name="setBarcodeId">বারকোড আইডি সেট করুন</string>
|
||||
<string name="unsupportedBarcodeType">অসমর্থিত বারকোড টাইপ</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="frontImageDescription">সামনের চিত্রের বর্ণনা</string>
|
||||
<string name="backImageDescription">পিছনের চিত্রের বর্ণনা</string>
|
||||
<string name="photos">ছবি</string>
|
||||
<string name="setFrontImage">সদর ছবি স্থাপন</string>
|
||||
<string name="setBackImage">পিছনের ছবি স্থাপন</string>
|
||||
<string name="removeImage">ছবি অপসারণ</string>
|
||||
<string name="takePhoto">ছবি নেত্তয়া</string>
|
||||
<string name="updateBarcodeQuestionTitle">হালনাগাদ বারকোড প্রশ্ন শিরোনাম</string>
|
||||
<string name="updateBarcodeQuestionText">হালনাগাদ বারকোড প্রশ্ন টেক্সট </string>
|
||||
<string name="yes">হাঁ</string>
|
||||
<string name="no">না</string>
|
||||
<string name="passwordRequired">পাসওয়ার্ড প্রয়োজন</string>
|
||||
<string name="failedGeneratingShareURL">শেয়ার ইউআরএল তৈরি করতে ব্যর্থ হয়েছে</string>
|
||||
<string name="turn_flashlight_on">টর্চলাইট চালু করুন</string>
|
||||
<string name="turn_flashlight_off">টর্চলাইট বন্ধ করুন</string>
|
||||
<string name="settings_locale">লোকেল</string>
|
||||
<string name="settings_system_locale">সিস্টেম লোকেল</string>
|
||||
<string name="settings_theme_color">থিম রঙ</string>
|
||||
<string name="settings_catima_theme">কটিমা থিম</string>
|
||||
<string name="settings_pink_theme">গোলাপী থিম</string>
|
||||
<string name="settings_magenta_theme">ম্যাজেন্টা থিম</string>
|
||||
<string name="settings_violet_theme">ভায়োলেট থিম</string>
|
||||
<string name="settings_blue_theme">নীল থিম</string>
|
||||
<string name="settings_sky_blue_theme">আকাশী নীল থিম</string>
|
||||
<string name="settings_green_theme">সবুজ থিম</string>
|
||||
<string name="settings_brown_theme">বাদামী থিম</string>
|
||||
<string name="sort">সাজান</string>
|
||||
<string name="toggleMoreInfo">আরও তথ্য টগল করুন</string>
|
||||
<string name="swipeToSwitchImages">ছবি পরিবর্তন করতে সোয়াইপ করুন</string>
|
||||
<string name="sort_by_name">নামের দ্বারা সাজান</string>
|
||||
<string name="sort_by_most_recently_used">সর্বাধিক সম্প্রতি ব্যবহৃত দ্বারা সাজান</string>
|
||||
<string name="sort_by_expiry">মেয়াদ শেষ করে সাজান</string>
|
||||
<string name="reverse">বিপরীত</string>
|
||||
<string name="sort_by">ক্রমানুসার</string>
|
||||
<string name="noCardExistsError">কোন কার্ড নেই ত্রুটি</string>
|
||||
<string name="noStoreError">স্টোরেজ ত্রুটি নেই</string>
|
||||
<string name="card_ids_copied">কার্ড আইডি কপি করা হয়েছে</string>
|
||||
<string name="noCardsMessage">কোন কার্ড বার্তা নেই</string>
|
||||
<string name="addCardTitle">কার্ডের শিরোনাম যোগ করুন</string>
|
||||
<string name="editCardTitle">কার্ডের শিরোনাম সম্পাদনা করুন</string>
|
||||
<string name="sendLabel">লেবেল পাঠান</string>
|
||||
<string name="share">ভাগ</string>
|
||||
<string name="copy_to_clipboard">নকল করুন ক্লিপবোর্ড এ</string>
|
||||
<string name="deleteConfirmation">নিশ্চিতকরণ মুছে দিন</string>
|
||||
<string name="unlockScreen">পর্দা আনলক করুন</string>
|
||||
<string name="confirm">নিশ্চিত করুন</string>
|
||||
<string name="delete">মুছে ফেলুন</string>
|
||||
<string name="edit">সম্পাদনা</string>
|
||||
</resources>
|
||||
90
app/src/main/res/values-bs/strings.xml
Normal file
90
app/src/main/res/values-bs/strings.xml
Normal file
@@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="save">Snimi</string>
|
||||
<string name="cancel">Odustani</string>
|
||||
<string name="unstar">Ukloni sve omiljene</string>
|
||||
<string name="star">Omiljene</string>
|
||||
<string name="barcodeNoBarcode">Ova kartica nema barkode</string>
|
||||
<string name="barcodeType">Barcode tip</string>
|
||||
<string name="note">Bilježnica</string>
|
||||
<string name="storeName">Ime</string>
|
||||
<string name="noMatchingGiftCards">Nisam našao ništa. Pokušaj promijeniti pretragu.</string>
|
||||
<string name="noGiftCards">Kliknite + Plus dugme da dodate kartu ili uvozite nešto iz menija prvo.</string>
|
||||
<string name="action_add">Dodaj</string>
|
||||
<string name="all">Sve</string>
|
||||
<string name="noGroupCards">Ova grupa ne sadrži nikakve karte</string>
|
||||
<string name="noGroups">Kliknite + Plus dugme da dodate grupe za kategorizaciju prvo.</string>
|
||||
<string name="groups">Grupe</string>
|
||||
<string name="enter_group_name">Unesite ime grupe</string>
|
||||
<string name="exportSuccessful">Podaci o kartama izvožen</string>
|
||||
<string name="importSuccessful">Uvezeni podaci o kartama</string>
|
||||
<string name="intent_import_card_from_url_share_text">Želim podijeliti čestitku s tobom</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Spriječi zaključavanje ekrana</string>
|
||||
<string name="settings_keep_screen_on">Zadrži ekran</string>
|
||||
<string name="settings_lock_barcode_orientation">Zaključaj barcode orjentacija</string>
|
||||
<string name="settings_max_font_size_scale">Max. veliäťina fonta</string>
|
||||
<string name="settings_light_theme">Svjetlo</string>
|
||||
<string name="settings_system_theme">Sistem</string>
|
||||
<string name="settings_theme">Tema</string>
|
||||
<string name="settings_category_title_ui">Korisničko okruženje</string>
|
||||
<string name="starImage">Omiljena zvijezda</string>
|
||||
<string name="importCatima">Uvezi iz Catima</string>
|
||||
<string name="importLoyaltyCardKeychain">Uvezi iz Loyalty Card Keychain</string>
|
||||
<string name="importStocard">Uvezi iz Stokarda</string>
|
||||
<string name="importVoucherVault">Uvezi iz trezora vaučer</string>
|
||||
<string name="barcodeId">Barcode vrijednost</string>
|
||||
<string name="sameAsCardId">Isto kao i kartica</string>
|
||||
<string name="setBarcodeId">Postavi vrijednost za bar kod</string>
|
||||
<string name="unsupportedBarcodeType">Ovaj bar kod još nije prikazan. Ona može biti podržana u kasnijoj verziji app.</string>
|
||||
<string name="wrongValueForBarcodeType">Izabrana vrijednost nije izvršna</string>
|
||||
<string name="copy_to_clipboard_multiple_toast">IDs kartica kopiran u clipboard</string>
|
||||
<string name="intent_import_card_from_url_share_multiple_text">Želim podijeliti karte s tobom</string>
|
||||
<string name="frontImageDescription">Slika kartice</string>
|
||||
<string name="backImageDescription">Slika pozadine kartice</string>
|
||||
<string name="photos">Slike</string>
|
||||
<string name="setFrontImage">Postavi naslovnu sliku</string>
|
||||
<string name="setBackImage">Vrati sliku</string>
|
||||
<string name="removeImage">Ukloni sliku</string>
|
||||
<string name="takePhoto">Slikaj</string>
|
||||
<string name="updateBarcodeQuestionTitle">Ažurirati bar kod vrijednost\?</string>
|
||||
<string name="updateBarcodeQuestionText">Promijenio si auto. Da li želite ažurirati bar kod koristiti istu vrijednost\?</string>
|
||||
<string name="yes">Da</string>
|
||||
<string name="no">Ne.</string>
|
||||
<string name="passwordRequired">Upišite šifru</string>
|
||||
<string name="failedGeneratingShareURL">Ne mogu generisati oštriji URL. Molim vas, prijavite ovo.</string>
|
||||
<string name="turn_flashlight_on">Upali lampu</string>
|
||||
<string name="turn_flashlight_off">Ugasi lampu</string>
|
||||
<string name="settings_locale">Jezik</string>
|
||||
<string name="settings_system_locale">Sistem</string>
|
||||
<string name="settings_theme_color">Boja teme</string>
|
||||
<string name="settings_catima_theme">Catima</string>
|
||||
<string name="settings_pink_theme">Ružičasto</string>
|
||||
<string name="settings_magenta_theme">Ljubičasto</string>
|
||||
<string name="settings_violet_theme">Ljubičasto</string>
|
||||
<string name="settings_blue_theme">Plavo</string>
|
||||
<string name="settings_sky_blue_theme">Nebo plavo</string>
|
||||
<string name="settings_green_theme">Zeleno</string>
|
||||
<string name="settings_brown_theme">Braun</string>
|
||||
<string name="sort">Poništi sortiranje</string>
|
||||
<string name="toggleMoreInfo">Uključi savjete</string>
|
||||
<string name="swipeToSwitchImages">Swipe ili long press za prebacivanje slika</string>
|
||||
<string name="sort_by_name">Ime</string>
|
||||
<string name="sort_by_most_recently_used">Nedavno Korišten</string>
|
||||
<string name="sort_by_expiry">Sajam</string>
|
||||
<string name="reverse">Rikverc</string>
|
||||
<string name="sort_by">Sortiraj</string>
|
||||
<string name="noCardExistsError">Nisam mogao pronaći karticu</string>
|
||||
<string name="noStoreError">Nije uneseno ime</string>
|
||||
<string name="card_ids_copied">Kopiran ID kartice(s)</string>
|
||||
<string name="noCardsMessage">Dodaj prvo kartu</string>
|
||||
<string name="addCardTitle">Dodaj Kartu</string>
|
||||
<string name="editCardTitle">Izmijeni Karticu</string>
|
||||
<string name="sendLabel">Pošalji…</string>
|
||||
<string name="share">Podijeli</string>
|
||||
<string name="copy_to_clipboard">Kopiraj ID u clipboard</string>
|
||||
<string name="deleteConfirmation">Izbriši trajno ovu karticu\?</string>
|
||||
<string name="unlockScreen">Odblokalna Rotacija</string>
|
||||
<string name="confirm">Potvrdi</string>
|
||||
<string name="delete">Obriši</string>
|
||||
<string name="edit">Izmijeni</string>
|
||||
</resources>
|
||||
@@ -1,10 +1,8 @@
|
||||
<resources
|
||||
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
|
||||
<?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">
|
||||
<string name="action_add">Přidat</string>
|
||||
|
||||
<string name="noGiftCards">ZAtím némáte žádné věrnostní karty. Klikněte na tlačítko "+" (plus) nahoře a začněte.\n\nLoyalty Card Locker umožňuje nosit své věrnostní karty v telefonu, takže jsou vždy na dosah.</string>
|
||||
<string name="storeName">Obchod</string>
|
||||
<string name="noGiftCards">Klepnutím na tlačítko + plus přidáte kartu nebo ji nejprve importujete z nabídky⋮.</string>
|
||||
<string name="storeName">Název</string>
|
||||
<string name="note">Poznámka</string>
|
||||
<string name="cardId">ID karty</string>
|
||||
<string name="cancel">Zrušit</string>
|
||||
@@ -12,43 +10,208 @@
|
||||
<string name="edit">Editovat</string>
|
||||
<string name="delete">Smazat</string>
|
||||
<string name="confirm">Potvrdit</string>
|
||||
<string name="deleteTitle">Odstzranit věrnostní kartu</string>
|
||||
<string name="deleteConfirmation">Opravdu chcete smazat tuto věrnostní kartu?</string>
|
||||
<string name="ok">Ano</string>
|
||||
<string name="copy_to_clipboard">Kopírovat ID do schránky</string>
|
||||
<string name="sendLabel">Odeslat…</string>
|
||||
<string name="sendLabel">Odeslat…</string>
|
||||
<string name="editCardTitle">Editovat věrnostní kartu</string>
|
||||
<string name="addCardTitle">Přidat věrnostní kartu</string>
|
||||
<string name="scanCardBarcode">Oskenujte kód karty</string>
|
||||
<string name="barcodeImageDescription">Obrázek kódu karty</string>
|
||||
|
||||
<string name="noStoreError">Nebyl zadán Obchod</string>
|
||||
<string name="noCardIdError">Nebylo zadáno ID karty</string>
|
||||
<string name="scanCardBarcode">Skenování Čárový Kód Karty</string>
|
||||
<string name="noStoreError">Zadáno žádné jméno</string>
|
||||
<string name="noCardIdError">Žádné ID karty zadáno</string>
|
||||
<string name="importExport">Import/Export</string>
|
||||
<string name="exportName">Export</string>
|
||||
<string name="importExportHelp">Zálohování dat vám umožní přesunout vaše uložené karty na jiné zařízení.</string>
|
||||
<string name="importSuccessfulTitle">Import proběhl úspěšně</string>
|
||||
<string name="importExportHelp">Zálohování karet vám umožní přesunout je do jiného zařízení.</string>
|
||||
<string name="importSuccessfulTitle">Dovážit</string>
|
||||
<string name="importFailedTitle">Import selhal</string>
|
||||
<string name="importFailed">Import selhal</string>
|
||||
<string name="exportSuccessfulTitle">Export proběhl úspěšně</string>
|
||||
<string name="importFailed">Nelze importovat karty</string>
|
||||
<string name="exportSuccessfulTitle">Exportovat</string>
|
||||
<string name="exportFailedTitle">Export selhal</string>
|
||||
<string name="exportFailed">Export selhal</string>
|
||||
<string name="importing">Importuji…</string>
|
||||
<string name="exporting">Exportuji…</string>
|
||||
<string name="noExternalStoragePermissionError">Nelze importovat nebo exportovat karty bez přístupu k externímu uložišti</string>
|
||||
<string name="exportFailed">Nelze exportovat karty</string>
|
||||
<string name="importing">Importuji…</string>
|
||||
<string name="exporting">Exportuji…</string>
|
||||
<string name="noExternalStoragePermissionError">Udělit povolení externího úložiště pro import nebo export karet jako první</string>
|
||||
<string name="importOptionFilesystemTitle">Import ze souborového systému</string>
|
||||
<string name="importOptionFilesystemExplanation">Vyberte konkrétní soubor v uložišti.</string>
|
||||
<string name="importOptionFilesystemButton">Ze souborového systému</string>
|
||||
<string name="importOptionApplicationTitle">Použít externí aplikaci</string>
|
||||
<string name="importOptionApplicationExplanation">K otevření souboru použije externí aplikaci jako Dropbox, Google Drive, nebo vámi preferovaný prohlížeč souborů.</string>
|
||||
<string name="importOptionApplicationButton">Použít externí aplikaci</string>
|
||||
|
||||
<string name="importOptionApplicationTitle">Použijte jinou aplikaci</string>
|
||||
<string name="importOptionApplicationExplanation">K otevření souboru použijte libovolnou aplikaci nebo svého oblíbeného správce souborů.</string>
|
||||
<string name="importOptionApplicationButton">Použijte jinou aplikaci</string>
|
||||
<string name="about">O aplikaci</string>
|
||||
<string name="app_license">Licensed under the GPLv3.</string>
|
||||
<string name="app_license">Copylefted libre software, licencovaný GPLv3+</string>
|
||||
<string name="about_title_fmt">O aplikaci <xliff:g id="app_name">%s</xliff:g></string>
|
||||
<string name="debug_version_fmt">Verze: <xliff:g id="version">%s</xliff:g></string>
|
||||
<string name="app_revision_fmt">Revizní informace: <xliff:g id="app_revision_url">%s</xliff:g></string>
|
||||
<string name="app_revision_fmt">Informace o revizi: <xliff:g id="app_revision_url">%s</xliff:g></string>
|
||||
<string name="selectBarcodeTitle">Vyberte čárový kód</string>
|
||||
<string name="copy_to_clipboard_toast">ID karty zkopírováno do schránky</string>
|
||||
|
||||
</resources>
|
||||
<string name="copy_to_clipboard_toast">ID karty zkopírované do schránky</string>
|
||||
<string name="deleteTitle">Smazat kartu</string>
|
||||
<string name="deleteConfirmation">Opravdu chcete smazat tuto věrnostní kartu?</string>
|
||||
<string name="moveBarcodeToCenterOfScreen">Střed čárového kódu na obrazovce</string>
|
||||
<string name="moveBarcodeToTopOfScreen">Přesuňte čárový kód do horní části obrazovky</string>
|
||||
<string name="chooseExpiryDate">Zvolte datum vypršení platnosti</string>
|
||||
<string name="never">Nikdy</string>
|
||||
<string name="expiryDate">Platnost</string>
|
||||
<string name="editBarcode">Upravit čárový kód</string>
|
||||
<string name="barcode">Kód</string>
|
||||
<string name="app_resources">Zdroje třetích stran Libre: <xliff:g id="app_resources_list">%s</xliff:g></string>
|
||||
<string name="app_libraries">Libre knihovny třetích stran: <xliff:g id="app_libraries_list">%s</xliff:g></string>
|
||||
<string name="app_copyright_old">Na základě Loyalty Card Keychain
|
||||
\ncopyright © 2016–2020 Branden Archer</string>
|
||||
<string name="exportOptionExplanation">Data budou zapsána na místo podle vašeho výběru.</string>
|
||||
<string name="failedParsingImportUriError">Nelze analyzovat import URI</string>
|
||||
<string name="noCardExistsError">Nelze najít kartu</string>
|
||||
<string name="noCardsMessage">Nejprve přidejte kartu</string>
|
||||
<string name="cardShortcut">Zástupce Karty</string>
|
||||
<string name="share">Podíl</string>
|
||||
<string name="unlockScreen">Odblokovat Rotaci</string>
|
||||
<string name="lockScreen">Otáčení Bloku</string>
|
||||
<string name="unstar">Odebrat z oblíbených</string>
|
||||
<string name="star">Přidat do oblíbených</string>
|
||||
<string name="noBarcode">Žádný čárový kód</string>
|
||||
<string name="barcodeNoBarcode">Tato karta nemá čárový kód</string>
|
||||
<string name="barcodeType">Typ čárového kódu</string>
|
||||
<string name="noMatchingGiftCards">Nic jsem nenašel. Zkuste změnit vyhledávání.</string>
|
||||
<string name="action_search">Vyhledávání</string>
|
||||
<string name="thumbnailDescription">Miniatura karty</string>
|
||||
<string name="card_ids_copied">Zkopírované ID karty(karet)</string>
|
||||
<plurals name="deleteCardsConfirmation">
|
||||
<item quantity="one">Opravdu chcete kartu <xliff:g>%d</xliff:g> trvale odstranit\?</item>
|
||||
<item quantity="few">Opravdu chcete karty <xliff:g>%d</xliff:g> trvale odstranit\?</item>
|
||||
<item quantity="other">Opravdu chcete karty <xliff:g>%d</xliff:g> trvale odstranit\?</item>
|
||||
</plurals>
|
||||
<plurals name="deleteCardsTitle">
|
||||
<item quantity="one">Odstranit kartu <xliff:g>%d</xliff:g></item>
|
||||
<item quantity="few">Odstranit karty <xliff:g>%d</xliff:g></item>
|
||||
<item quantity="other">Odstranit karty <xliff:g>%d</xliff:g></item>
|
||||
</plurals>
|
||||
<string name="importSuccessful">Data karty importována</string>
|
||||
<string name="intent_import_card_from_url_share_text">Chci s Vámi sdílet kartu</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Bránit uzamykání obrazovky</string>
|
||||
<string name="settings_keep_screen_on">Udržovat obrazovku zapnutou</string>
|
||||
<string name="settings_lock_barcode_orientation">Zamknout orientaci čárového kódu</string>
|
||||
<string name="settings_max_font_size_scale">Maximální velikost písma</string>
|
||||
<string name="settings_dark_theme">Tmavý</string>
|
||||
<string name="settings_light_theme">Světlý</string>
|
||||
<string name="settings_system_theme">Systém</string>
|
||||
<string name="settings_theme">Vzhled</string>
|
||||
<string name="settings_category_title_ui">Uživatelské rozhraní</string>
|
||||
<string name="settings">Nastavení</string>
|
||||
<string name="card">Karta</string>
|
||||
<string name="balancePoints"><xliff:g>%s</xliff:g> bodů</string>
|
||||
<string name="balanceSentence">Zůstatek: <xliff:g>%s</xliff:g></string>
|
||||
<string name="expiryStateSentenceExpired">Platnost vypršela: <xliff:g>%s</xliff:g></string>
|
||||
<string name="expiryStateSentence">Platí do: <xliff:g>%s</xliff:g></string>
|
||||
<string name="moveDown">Přesunout dolů</string>
|
||||
<string name="moveUp">Přesunout nahoru</string>
|
||||
<string name="enterBarcodeInstructions">Zadejte ID karty a níže vyberte typ čárového kódu nebo \"Tato karta nemá čárový kód\".</string>
|
||||
<string name="settings_brown_theme">Hnědá</string>
|
||||
<string name="settings_grey_theme">Šedá</string>
|
||||
<string name="settings_green_theme">Zelená</string>
|
||||
<string name="settings_sky_blue_theme">Azurová</string>
|
||||
<string name="settings_blue_theme">Modrá</string>
|
||||
<string name="settings_violet_theme">Fialová</string>
|
||||
<string name="settings_magenta_theme">Purpurová</string>
|
||||
<string name="settings_pink_theme">Růžová</string>
|
||||
<string name="settings_catima_theme">Catima</string>
|
||||
<string name="settings_theme_color">Barva motivu</string>
|
||||
<string name="settings_system_locale">Systém</string>
|
||||
<string name="settings_locale">Jazyk</string>
|
||||
<string name="turn_flashlight_off">Vypnout světlo</string>
|
||||
<string name="turn_flashlight_on">Zapnout světlo</string>
|
||||
<string name="failedGeneratingShareURL">Nepodařilo se vygenerovat sdílenou adresu URL. Nahlaste to prosím.</string>
|
||||
<string name="passwordRequired">Zadejte prosím heslo</string>
|
||||
<string name="no">Ne</string>
|
||||
<string name="yes">Ano</string>
|
||||
<string name="updateBarcodeQuestionText">Změnili jste ID karty. Chcete také aktualizovat čárový kód, aby používal stejnou hodnotu\?</string>
|
||||
<string name="updateBarcodeQuestionTitle">Aktualizovat hodnotu čárového kódu\?</string>
|
||||
<string name="takePhoto">Pořídit fotku</string>
|
||||
<string name="removeImage">Odstranit obrázek</string>
|
||||
<string name="setBackImage">Nastavit obrázek zadní strany</string>
|
||||
<string name="setFrontImage">Nastavit obrázek přední strany</string>
|
||||
<string name="photos">Fotky</string>
|
||||
<string name="backImageDescription">Obrázek zadní strany karty</string>
|
||||
<string name="frontImageDescription">Obrázek přední strany karty</string>
|
||||
<string name="intent_import_card_from_url_share_multiple_text">Chci s Vámi sdílet kartu</string>
|
||||
<string name="copy_to_clipboard_multiple_toast">ID karty zkopírováno do schránky</string>
|
||||
<string name="wrongValueForBarcodeType">Hodnota není platná pro vybraný typ čárového kódu</string>
|
||||
<string name="unsupportedBarcodeType">Tento typ čárového kódu zatím nelze zobrazit. Možná bude podporován v pozdější verzi aplikace.</string>
|
||||
<string name="barcodeId">Hodnota čárového kódu</string>
|
||||
<string name="setBarcodeId">Nastavení hodnoty čárového kódu</string>
|
||||
<string name="sameAsCardId">Stejné jako ID karty</string>
|
||||
<string name="importVoucherVaultMessage">Vyberte svůj <i>vouchervault.json</i> export z Voucher Vault, který chcete importovat.
|
||||
\nVytvořte jej tak, že nejprve stisknete tlačítko Exportovat v aplikaci Voucher Vault.</string>
|
||||
<string name="importVoucherVault">Import z Voucher Vault</string>
|
||||
<string name="importStocardMessage">Vyberte svůj <i>***-sync.zip</i> export z aplikace Stocard, který chcete importovat.
|
||||
\nZískejte ji zasláním e-mailu na adresu support@stocardapp.com s žádostí o export vašich dat.</string>
|
||||
<string name="importStocard">Import ze Stocard</string>
|
||||
<string name="importLoyaltyCardKeychainMessage">Vyberte export <i>LoyaltyCardKeychain.csv</i> z klíčenky věrnostních karet, který chcete importovat.
|
||||
\nVytvořte jej z nabídky Import/Export v Loyalty Card Keychain tak, že tam nejprve stisknete tlačítko Exportovat.</string>
|
||||
<string name="importLoyaltyCardKeychain">Import z Loyalty Card Keychain</string>
|
||||
<string name="importFidmeMessage">Vyberte svůj <i>fidme-export-request-xxxxxx.zip</i> export z FidMe k importu a poté vyberte typy čárových kódů ručně.
|
||||
\nVytvořte jej ze svého profilu FidMe tak, že nejprve zvolíte možnost Ochrana dat a poté stisknete tlačítko Extract moje data.</string>
|
||||
<string name="importFidme">Import z FidMe</string>
|
||||
<string name="importCatimaMessage">Vyberte export <i>catima.zip</i> z programu Catima, který chcete importovat.
|
||||
\nVytvořte jej z nabídky Import/Export jiné aplikace Catima tak, že v ní nejprve stisknete tlačítko Exportovat.</string>
|
||||
<string name="importCatima">Import z Catima</string>
|
||||
<string name="accept">Přijmout</string>
|
||||
<string name="privacy_policy_popup_text">Oznámení o zásadách ochrany osobních údajů (vyžadováno některými obchody s aplikacemi):
|
||||
\n
|
||||
\nNejsou shromažďovány žádné údaje, což může potvrdit každý, protože naše aplikace je libre software.</string>
|
||||
<string name="privacy_policy">Zásady soukromí</string>
|
||||
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
|
||||
<string name="chooseImportType">Importovat data z\?</string>
|
||||
<string name="parsingBalanceFailed"><xliff:g>%s</xliff:g> se nezdá být platným zůstatkem.</string>
|
||||
<string name="points">Body</string>
|
||||
<string name="currency">Měna</string>
|
||||
<string name="balance">Zůstatek</string>
|
||||
<string name="errorReadingImage">Obrázek se nepodařilo přečíst</string>
|
||||
<string name="noBarcodeFound">Čarový kód nenalezen</string>
|
||||
<string name="groupsList">Skupiny: <xliff:g>%s</xliff:g></string>
|
||||
<string name="addFromImage">Výběr obrázku z galerie</string>
|
||||
<string name="addManually">Ruční zadání ID karty</string>
|
||||
<string name="leaveWithoutSaveConfirmation">Ukončit bez uložení\?</string>
|
||||
<string name="leaveWithoutSaveTitle">Ukončit</string>
|
||||
<string name="failedOpeningFileManager">Nejprve si nainstalujte správce souborů.</string>
|
||||
<string name="deleteConfirmationGroup">Smazat skupinu\?</string>
|
||||
<string name="all">Všechny</string>
|
||||
<plurals name="groupCardCount">
|
||||
<item quantity="one"><xliff:g>%d</xliff:g> karta</item>
|
||||
<item quantity="few"><xliff:g>%d</xliff:g> karty</item>
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> karet</item>
|
||||
</plurals>
|
||||
<string name="noGroups">Kliknutím na tlačítko + plus nejprve přidejte skupiny pro kategorizaci.</string>
|
||||
<string name="groups">Skupiny</string>
|
||||
<string name="enter_group_name">Zadejte název skupiny</string>
|
||||
<string name="exportSuccessful">Data karty exportována</string>
|
||||
<string name="settings_display_barcode_max_brightness">Rozjasněné zobrazení čárového kódu</string>
|
||||
<string name="starImage">Oblíbená hvězda</string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Copyright © 2019–<xliff:g>%d</xliff:g> Sylvia van Os</string>
|
||||
<plurals name="selectedCardCount">
|
||||
<item quantity="one">Vybrána <xliff:g>%d</xliff:g> karta</item>
|
||||
<item quantity="few">Vybrány <xliff:g>%d</xliff:g> karty</item>
|
||||
<item quantity="other">Vybráno <xliff:g>%d</xliff:g> karet</item>
|
||||
</plurals>
|
||||
<string name="app_contributors">Přispěli: <xliff:g id="app_contributors">%s</xliff:g></string>
|
||||
<string name="noGroupCards">Tato skupina neobsahuje žádné karty</string>
|
||||
<string name="sort_by">Seřadit podle</string>
|
||||
<string name="reverse">Obrátit</string>
|
||||
<string name="sort_by_balance">Zůstatek</string>
|
||||
<string name="sort_by_expiry">Vypršení</string>
|
||||
<string name="sort_by_most_recently_used">Naposledy použité</string>
|
||||
<string name="sort_by_name">Název</string>
|
||||
<string name="swipeToSwitchImages">Přejetím nebo dlouhým stisknutím přepínáte obrázky</string>
|
||||
<string name="toggleMoreInfo">Přepnout zobrazení dalších informací</string>
|
||||
<string name="sort">Seřadit</string>
|
||||
<string name="barcodeImageDescriptionWithType">Obrázek čárového kódu karty typu <xliff:g>%s</xliff:g></string>
|
||||
<string name="version_history">Historie verzí</string>
|
||||
<string name="rate_this_app">Ohodnoťte tuto aplikaci</string>
|
||||
<string name="and_data_usage">a využití dat</string>
|
||||
<string name="credits">Kredity</string>
|
||||
<string name="on_github">na GitHubu</string>
|
||||
<string name="source_repository">Úložiště zdrojů</string>
|
||||
<string name="license">Licence</string>
|
||||
<string name="help_translate_this_app">Pomozte s překladem této aplikace</string>
|
||||
<string name="report_error">Nahlásit chybu</string>
|
||||
<string name="on_google_play">na Google Play</string>
|
||||
<string name="exportPassword">Nastavení hesla pro ochranu exportu (volitelné)</string>
|
||||
<string name="exportPasswordHint">Zadejte heslo</string>
|
||||
</resources>
|
||||
96
app/src/main/res/values-da/strings.xml
Normal file
96
app/src/main/res/values-da/strings.xml
Normal file
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="scanCardBarcode">Scanne kortets stregkode</string>
|
||||
<string name="addCardTitle">Tilføj kort</string>
|
||||
<string name="editCardTitle">Rediger kort</string>
|
||||
<string name="sendLabel">Afsend…</string>
|
||||
<string name="share">Aktie</string>
|
||||
<string name="copy_to_clipboard">Kopier ID til udklipsholder</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="deleteConfirmation">Slete dette kort permanent\?</string>
|
||||
<plurals name="deleteCardsTitle">
|
||||
<item quantity="one">Streichen <xliff:g>%d</xliff:g> kort</item>
|
||||
<item quantity="other">Streichen <xliff:g>%d</xliff:g> korts</item>
|
||||
</plurals>
|
||||
<string name="deleteTitle">Karte streichen</string>
|
||||
<string name="unlockScreen">Afbloker rotation</string>
|
||||
<string name="lockScreen">Blokrotation</string>
|
||||
<string name="confirm">Bekræft</string>
|
||||
<string name="delete">Slet</string>
|
||||
<string name="edit">Rediger</string>
|
||||
<string name="save">Gem</string>
|
||||
<string name="cancel">Annuller</string>
|
||||
<string name="unstar">Fjern fra favoritter</string>
|
||||
<string name="star">Føj til favoritter</string>
|
||||
<string name="noBarcode">Ingen stregkode</string>
|
||||
<string name="barcodeNoBarcode">Dette kort har ingen stregkode</string>
|
||||
<string name="barcodeType">Stregkode type</string>
|
||||
<string name="cardId">Kort ID</string>
|
||||
<string name="note">Bemærk</string>
|
||||
<string name="storeName">Navn</string>
|
||||
<string name="noMatchingGiftCards">Jeg fandt ikke noget. Prøv at ændre din søgning.</string>
|
||||
<string name="noGiftCards">Klik på + plus-knappen for at tilføje et kort, eller importer først nogle kort fra ⋮-menuen.</string>
|
||||
<plurals name="selectedCardCount">
|
||||
<item quantity="one"><xliff:g>%d</xliff:g> valgt kort</item>
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> valgte kort</item>
|
||||
</plurals>
|
||||
<string name="action_add">Tilføj</string>
|
||||
<string name="action_search">Søg</string>
|
||||
<string name="importExport">Import/eksport</string>
|
||||
<string name="exportName">Eksport</string>
|
||||
<string name="importExportHelp">Hvis du sikkerhedskopierer dine kort, kan du flytte dem til en anden enhed.</string>
|
||||
<string name="importSuccessfulTitle">Importeret</string>
|
||||
<string name="importFailedTitle">Import mislykkedes</string>
|
||||
<string name="importFailed">Kunne ikke importere kort</string>
|
||||
<string name="exportSuccessfulTitle">Eksporteret</string>
|
||||
<string name="exportFailedTitle">Eksport mislykkedes</string>
|
||||
<string name="exportFailed">Kunne ikke eksportere kort</string>
|
||||
<string name="importing">Importere…</string>
|
||||
<string name="exporting">Eksportere…</string>
|
||||
<string name="settings_dark_theme">Mørk</string>
|
||||
<string name="settings_light_theme">Lys</string>
|
||||
<string name="settings_theme">Tema</string>
|
||||
<string name="settings_category_title_ui">Brugergrænseflade</string>
|
||||
<string name="settings">Indstillinger</string>
|
||||
<string name="starImage">Favorit stjerne</string>
|
||||
<string name="thumbnailDescription">Miniaturebillede til kort</string>
|
||||
<string name="copy_to_clipboard_toast">Kort-ID kopieret til udklipsholderen</string>
|
||||
<string name="enterBarcodeInstructions">Indtast kortets ID, og vælg enten kortets stregkodetype nedenfor, eller \"Dette kort har ingen stregkode\".</string>
|
||||
<string name="selectBarcodeTitle">Vælg stregkode</string>
|
||||
<string name="app_copyright_old">Baseret på Loyalty Card Keychain
|
||||
\ncopyright © 2016-2020 Branden Archer.</string>
|
||||
<string name="about">Om</string>
|
||||
<string name="importOptionApplicationButton">Brug en anden app</string>
|
||||
<string name="importOptionApplicationExplanation">Brug en hvilken som helst app eller din foretrukne filhåndtering til at åbne en fil.</string>
|
||||
<string name="importOptionApplicationTitle">Brug en anden app</string>
|
||||
<string name="noCardsMessage">Tilføj først et kort</string>
|
||||
<string name="cardShortcut">Kort genvej</string>
|
||||
<string name="importOptionFilesystemButton">Fra filsystemet</string>
|
||||
<string name="importOptionFilesystemExplanation">Vælg en bestemt fil fra filsystemet.</string>
|
||||
<string name="importOptionFilesystemTitle">Import fra filsystem</string>
|
||||
<string name="exportOptionExplanation">Dataene skrives til en placering efter eget valg.</string>
|
||||
<string name="noExternalStoragePermissionError">Giv først tilladelse til ekstern lagring til at importere eller eksportere kort</string>
|
||||
<string name="failedParsingImportUriError">Kunne ikke analysere import-URI\'en</string>
|
||||
<string name="noCardExistsError">Kunne ikke finde kort</string>
|
||||
<string name="noCardIdError">Der er ikke angivet noget kort-ID</string>
|
||||
<string name="noStoreError">Intet navn angivet</string>
|
||||
<string name="deleteConfirmationGroup">Slet gruppe\?</string>
|
||||
<string name="all">Alle</string>
|
||||
<string name="noGroupCards">Denne gruppe indeholder ikke nogen kort</string>
|
||||
<string name="noGroups">Klik på + plus-knappen for først at tilføje grupper til kategorisering.</string>
|
||||
<string name="groups">Grupper</string>
|
||||
<string name="enter_group_name">Indtast gruppenavn</string>
|
||||
<string name="exportSuccessful">Eksporterede kortdata</string>
|
||||
<string name="importSuccessful">Kortdata importeret</string>
|
||||
<string name="intent_import_card_from_url_share_text">Jeg vil dele et kort med jer</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Forebyg låseskærm</string>
|
||||
<string name="settings_keep_screen_on">LHold skærm tændt</string>
|
||||
<string name="settings_lock_barcode_orientation">Lås stregkode-orientering</string>
|
||||
<string name="moveUp">Bevæg dig opad</string>
|
||||
<string name="leaveWithoutSaveConfirmation">Forlade uden at gemme\?</string>
|
||||
<string name="settings_display_barcode_max_brightness">Lysere stregkodevisning</string>
|
||||
<string name="failedOpeningFileManager">Installer først en filhåndteringsprogram.</string>
|
||||
<string name="moveDown">Bevæger sig nedad</string>
|
||||
<string name="leaveWithoutSaveTitle">Afslut</string>
|
||||
<string name="addManually">Indtast kort-ID manuelt</string>
|
||||
</resources>
|
||||
@@ -2,8 +2,8 @@
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" xmlns:tools="http://schemas.android.com/tools">
|
||||
<string name="action_search">Suchen</string>
|
||||
<string name="action_add">Hinzufügen</string>
|
||||
<string name="noGiftCards">Klicken Sie auf das Pluszeichen +, um eine Karte hinzuzufügen, oder importieren Sie zunächst einige aus dem ⋮ Menü.</string>
|
||||
<string name="noMatchingGiftCards">Nichts gefunden. Versuchen Sie, Ihre Suche zu ändern.</string>
|
||||
<string name="noGiftCards">Füge eine Karte mit + hinzu oder importiere welche über das ⋮ Menü.</string>
|
||||
<string name="noMatchingGiftCards">Nichts gefunden. Versuche, deine Suche zu ändern.</string>
|
||||
<string name="storeName">Name</string>
|
||||
<string name="note">Notiz</string>
|
||||
<string name="cardId">Kartennummer</string>
|
||||
@@ -12,27 +12,24 @@
|
||||
<string name="edit">Bearbeiten</string>
|
||||
<string name="delete">Löschen</string>
|
||||
<string name="confirm">Bestätigen</string>
|
||||
<string name="lockScreen">Rotation blockieren</string>
|
||||
<string name="unlockScreen">Rotation zulassen</string>
|
||||
<string name="lockScreen">Rotation sperren</string>
|
||||
<string name="unlockScreen">Rotation erlauben</string>
|
||||
<string name="star">Zu den Favoriten hinzufügen</string>
|
||||
<string name="unstar">Aus der Favoritenliste entfernen</string>
|
||||
<string name="deleteTitle">Karte entfernen</string>
|
||||
<string name="deleteConfirmation">Diese Karte löschen\?</string>
|
||||
<string name="unstar">Aus den Favoriten entfernen</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="copy_to_clipboard">Nummer in die Zwischenablage kopieren</string>
|
||||
<string name="copy_to_clipboard">Kartennummer in die Zwischenablage kopieren</string>
|
||||
<string name="sendLabel">Senden …</string>
|
||||
<string name="editCardTitle">Kundenkarte bearbeiten</string>
|
||||
<string name="addCardTitle">Neue Kundenkarte</string>
|
||||
<string name="scanCardBarcode">Strichcode scannen</string>
|
||||
<string name="editCardTitle">Karte bearbeiten</string>
|
||||
<string name="addCardTitle">Neue Karte</string>
|
||||
<string name="scanCardBarcode">Barcode scannen</string>
|
||||
<string name="cardShortcut">Shortcut zu einer Karte</string>
|
||||
<string name="noCardsMessage">Fügen Sie zuerst eine Karte hinzu</string>
|
||||
<string name="barcodeImageDescription">Bild des Strichcodes</string>
|
||||
<string name="noCardsMessage">Füge zuerst eine Karte hinzu</string>
|
||||
<string name="noStoreError">Kein Name eingegeben</string>
|
||||
<string name="noCardIdError">Keine Kartennummer angegeben</string>
|
||||
<string name="noCardExistsError">Karte konnte nicht gefunden werden</string>
|
||||
<string name="importExport">Import/Export</string>
|
||||
<string name="exportName">Exportieren</string>
|
||||
<string name="importExportHelp">Durch das Sichern Ihrer Karten können Sie sie auf ein anderes Gerät verschieben.</string>
|
||||
<string name="exportName">Export</string>
|
||||
<string name="importExportHelp">Durch das Sichern deiner Karten kannst du sie auf ein anderes Gerät übertragen.</string>
|
||||
<string name="importSuccessfulTitle">Importiert</string>
|
||||
<string name="importFailedTitle">Import fehlgeschlagen</string>
|
||||
<string name="importFailed">Karten konnten nicht importiert werden</string>
|
||||
@@ -41,51 +38,52 @@
|
||||
<string name="exportFailed">Karten konnten nicht exportiert werden</string>
|
||||
<string name="importing">Importiere…</string>
|
||||
<string name="exporting">Exportiere…</string>
|
||||
<string name="noExternalStoragePermissionError">Erteilen Sie zuerst die Erlaubnis zur externen Speicherung, um Karten zu importieren oder zu exportieren</string>
|
||||
<string name="importOptionFilesystemTitle">Importiere aus Dateisystem</string>
|
||||
<string name="importOptionFilesystemExplanation">Wähle eine Datei aus dem Speicher aus.</string>
|
||||
<string name="importOptionFilesystemButton">Aus Dateisystem</string>
|
||||
<string name="importOptionApplicationTitle">Andere Anwendung verwenden</string>
|
||||
<string name="importOptionApplicationExplanation">Verwenden Sie eine beliebige Anwendung oder Ihren bevorzugten Dateiverwaltungsprogramm, um eine Datei zu öffnen.</string>
|
||||
<string name="importOptionApplicationButton">Andere Anwendung verwenden</string>
|
||||
<string name="noExternalStoragePermissionError">Erlaube Speicherzugriff um Karten zu importieren oder exportieren</string>
|
||||
<string name="importOptionFilesystemTitle">Importiere aus dem Dateisystem</string>
|
||||
<string name="importOptionFilesystemExplanation">Wähle eine Datei vom Dateisystem aus.</string>
|
||||
<string name="importOptionFilesystemButton">Wähle vom Dateisystem</string>
|
||||
<string name="importOptionApplicationTitle">Andere Anwendungen</string>
|
||||
<string name="importOptionApplicationExplanation">Beliebige Anwendung oder deinen bevorzugten Dateimanager zur Dateiauswahl verwenden.</string>
|
||||
<string name="importOptionApplicationButton">Aus anderer Anwendung</string>
|
||||
<string name="about">Über</string>
|
||||
<string name="app_license">Freie Software, lizensiert unter der GPLv3+.</string>
|
||||
<string name="app_license">Freie Software, lizensiert unter der GPLv3+</string>
|
||||
<string name="about_title_fmt">Über <xliff:g id="app_name">%s</xliff:g></string>
|
||||
<string name="debug_version_fmt">Version: <xliff:g id="version">%s</xliff:g></string>
|
||||
<string name="app_revision_fmt">Informationen zu dieser Version: <xliff:g id="app_revision_url">%s</xliff:g></string>
|
||||
<string name="selectBarcodeTitle">Strichcode auswählen</string>
|
||||
<string name="copy_to_clipboard_toast">Nummer in die Zwischenablage kopiert</string>
|
||||
<string name="app_revision_fmt">Revisionsinfo: <xliff:g id="app_revision_url">%s</xliff:g></string>
|
||||
<string name="selectBarcodeTitle">Barcode auswählen</string>
|
||||
<string name="copy_to_clipboard_toast">Kartennummer in die Zwischenablage kopiert</string>
|
||||
<string name="thumbnailDescription">Vorschaubild für die Karte</string>
|
||||
<string name="settings">Einstellungen</string>
|
||||
<string name="settings_category_title_ui">Benutzeroberfläche</string>
|
||||
<string name="settings_display_barcode_max_brightness">Helligkeit bei Strichcode Ansicht erhöhen</string>
|
||||
<string name="settings_lock_barcode_orientation">Strichcodeausrichtung sperren</string>
|
||||
<string name="settings_display_barcode_max_brightness">Barcodeansicht aufhellen</string>
|
||||
<string name="settings_lock_barcode_orientation">Barcoderotation sperren</string>
|
||||
<string name="exportSuccessful">Kartendaten exportiert</string>
|
||||
<string name="importSuccessful">Kartendaten importiert</string>
|
||||
<string name="intent_import_card_from_url_share_text">Ich will eine Karte mit Ihnen teilen</string>
|
||||
<string name="intent_import_card_from_url_share_text">Ich würde gerne diese Karte mit dir teilen</string>
|
||||
<string name="settings_dark_theme">Dunkel</string>
|
||||
<string name="settings_light_theme">Hell</string>
|
||||
<string name="settings_system_theme">System</string>
|
||||
<string name="settings_theme">Design</string>
|
||||
<string name="enterBarcodeInstructions">Geben Sie die Karten-ID ein und wählen Sie unten entweder den Strichcodetyp oder „Diese Karte hat keinen Strichcode“.</string>
|
||||
<string name="settings_theme">Farbschema</string>
|
||||
<string name="enterBarcodeInstructions">Gib die Kartennummer ein und wähle entweder den Barcode-Typ unten oder wähle \"Diese Karte hat keinen Barcode\" aus.</string>
|
||||
<string name="app_copyright_old">Basierend auf Loyalty Card Keychain
|
||||
\nCopyright © 2016-2020 Branden Archer.</string>
|
||||
<string name="exportOptionExplanation">Die Daten werden an einen Ort Ihrer Wahl geschrieben.</string>
|
||||
<string name="failedParsingImportUriError">Der Import-URI konnte nicht analysiert werden</string>
|
||||
\nCopyright © 2016-2020 Branden Archer</string>
|
||||
<string name="exportOptionExplanation">Die Daten werden an einen Ort deiner Wahl geschrieben.</string>
|
||||
<string name="failedParsingImportUriError">Die Import-URI konnte nicht verarbeitet werden</string>
|
||||
<string name="share">Teilen</string>
|
||||
<string name="barcodeNoBarcode">Diese Karte hat keinen Strichcode</string>
|
||||
<string name="barcodeType">Strichcode-Typ</string>
|
||||
<string name="barcodeNoBarcode">Diese Karte hat keinen Barcode</string>
|
||||
<string name="barcodeType">Barcodetyp</string>
|
||||
<string name="starImage">Favoritenstern</string>
|
||||
<string name="deleteConfirmationGroup">Gruppe löschen\?</string>
|
||||
<string name="deleteConfirmationGroup">Gruppe löschen?</string>
|
||||
<string name="all">Alle</string>
|
||||
<string name="noGroups">Klicken Sie auf das Pluszeichen +, um zunächst Gruppen zur Kategorisierung hinzuzufügen.</string>
|
||||
<string name="noGroups">Klicke auf das Pluszeichen + um eine Gruppe hinzuzufügen.</string>
|
||||
<string name="noGroupCards">Diese Gruppe hat noch keine Karten</string>
|
||||
<string name="groups">Gruppen</string>
|
||||
<string name="enter_group_name">Geben Sie den Gruppennamen ein</string>
|
||||
<string name="enter_group_name">Gib einen Gruppennamen ein</string>
|
||||
<string name="leaveWithoutSaveConfirmation">Beenden ohne zu speichern\?</string>
|
||||
<string name="leaveWithoutSaveTitle">Beenden</string>
|
||||
<string name="failedOpeningFileManager">Installieren Sie zunächst ein Dateiverwaltungsprogramm.</string>
|
||||
<string name="noBarcode">Kein Strichcode</string>
|
||||
<string name="addManually">Die Karten-ID manuell eingeben</string>
|
||||
<string name="failedOpeningFileManager">Installiere zuerst einen Dateimanager.</string>
|
||||
<string name="noBarcode">Kein Barcode</string>
|
||||
<string name="addManually">Kartennummer manuell eingeben</string>
|
||||
<string name="moveDown">Nach unten verschieben</string>
|
||||
<string name="moveUp">Nach oben verschieben</string>
|
||||
<plurals name="groupCardCount">
|
||||
@@ -94,17 +92,17 @@
|
||||
</plurals>
|
||||
<string name="groupsList">Gruppen: <xliff:g>%s</xliff:g></string>
|
||||
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
|
||||
<string name="chooseImportType">Daten importieren aus\?</string>
|
||||
<string name="chooseImportType">Daten importieren aus?</string>
|
||||
<string name="parsingBalanceFailed"><xliff:g>%s</xliff:g> scheint kein gültiges Guthaben zu sein.</string>
|
||||
<string name="points">Punkte</string>
|
||||
<string name="currency">Währung</string>
|
||||
<string name="balance">Guthaben</string>
|
||||
<string name="moveBarcodeToCenterOfScreen">Strichcode auf dem Bildschirm zentrieren</string>
|
||||
<string name="moveBarcodeToTopOfScreen">Strichcode auf dem Bildschirm nach oben schieben</string>
|
||||
<string name="moveBarcodeToCenterOfScreen">Barcode auf dem Bildschirm zentrieren</string>
|
||||
<string name="moveBarcodeToTopOfScreen">Barcode auf dem Bildschirm oben fixieren</string>
|
||||
<string name="chooseExpiryDate">Ablaufdatum wählen</string>
|
||||
<string name="never">Nie</string>
|
||||
<string name="expiryDate">Ablaufdatum</string>
|
||||
<string name="editBarcode">Strichcode bearbeiten</string>
|
||||
<string name="editBarcode">Barcode ändern</string>
|
||||
<string name="barcode">Strichcode</string>
|
||||
<string name="card">Karte</string>
|
||||
<string name="balancePoints"><xliff:g>%s</xliff:g> Punkte</string>
|
||||
@@ -112,38 +110,104 @@
|
||||
<string name="expiryStateSentenceExpired">Abgelaufen: <xliff:g>%s</xliff:g></string>
|
||||
<string name="expiryStateSentence">Läuft ab: <xliff:g>%s</xliff:g></string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Sperrbildschirm verhindern</string>
|
||||
<string name="settings_keep_screen_on">Bildschirm eingeschaltet halten</string>
|
||||
<string name="settings_keep_screen_on">Bildschirm aktiv halten</string>
|
||||
<string name="privacy_policy_popup_text">Hinweis zum Datenschutz (oft gefordert):
|
||||
\n
|
||||
\nKEINE DATEN WERDEN GESAMMELT, was jeder bestätigen kann, da unsere Anwendung eine freie Software ist.</string>
|
||||
<string name="accept">Annehmen</string>
|
||||
<string name="privacy_policy">Datenschutzrichtlinie</string>
|
||||
<string name="importVoucherVaultMessage">Suchen Sie eine Datei, die wahrscheinlich <i>vouchervault.json</i> heißt, um sie zu importieren.
|
||||
\nOder erstellen Sie sie, indem Sie zuerst in Voucher Vault auf Export drücken.</string>
|
||||
<string name="importVoucherVaultMessage">Wählen Sie Ihren <i>vouchervault.json</i>-Export aus Voucher Vault zum Importieren aus.
|
||||
\nErstellen Sie ihn, indem Sie zuerst auf Export in Voucher Vault drücken.</string>
|
||||
<string name="importVoucherVault">Aus Voucher Vault importieren</string>
|
||||
<string name="importLoyaltyCardKeychainMessage">Suchen Sie eine Datei, die höchstwahrscheinlich <i>LoyaltyCardKeychain.csv</i> heißt, um sie zu importieren.
|
||||
\nOder erstellen Sie sie über das Menü Import/Export in Loyalty Card Keychain, indem Sie zuerst auf Export drücken.</string>
|
||||
<string name="importLoyaltyCardKeychainMessage">Wählen Sie Ihren <i>LoyaltyCardKeychain.csv</i>-Export aus Loyalty Card Keychain zum Importieren aus.
|
||||
\nErstellen Sie ihn über das Menü Import/Export in Loyalty Card Keychain, indem Sie dort zuerst auf Export drücken.</string>
|
||||
<string name="importLoyaltyCardKeychain">Aus Loyalty Card Keychain importieren</string>
|
||||
<string name="importFidmeMessage">Suchen Sie eine Datei, die wahrscheinlich <i>fidme-export-request-xxxxxx.zip</i> heißt, um sie zu importieren, und wählen Sie anschließend die Strichcodetypen manuell aus.
|
||||
\nOder erstellen Sie sie aus Ihrem FidMe-Profil, indem Sie Datenschutz wählen und dann zuerst auf Extrahiere meine Daten drücken.</string>
|
||||
<string name="importFidmeMessage">Wählen Sie Ihren <i>fidme-export-request-xxxxxx.zip</i>-Export aus FidMe zum Importieren aus und wählen Sie anschließend die Strichcodetypen manuell aus.
|
||||
\nOder erstellen Sie ihn aus Ihrem FidMe-Profil, indem Sie Datenschutz wählen und dann zuerst auf Meine Daten extrahieren drücken.</string>
|
||||
<string name="importFidme">Aus FidMe importieren</string>
|
||||
<string name="importCatimaMessage">Suchen Sie eine Datei, die wahrscheinlich <i>Catima.csv</i> heißt, um sie zu importieren.
|
||||
\nOder erstellen Sie sie aus dem Import/Export-Menü einer anderen Catima-Anwendung, indem Sie dort zuerst Export drücken.</string>
|
||||
<string name="importCatimaMessage">Wählen Sie Ihren <i>catima.zip</i>-Export aus Catima zum Importieren aus.
|
||||
\nErstellen Sie ihn aus dem Import/Export-Menü einer anderen Catima-Anwendung, indem Sie dort zuerst Export drücken.</string>
|
||||
<string name="importCatima">Aus Catima importieren</string>
|
||||
<string name="setBarcodeId">Strichcodewert setzen</string>
|
||||
<string name="sameAsCardId">Gleich wie Karten-ID</string>
|
||||
<string name="barcodeId">Strichcodewert</string>
|
||||
<string name="errorReadingImage">Das Bild konnte nicht gelesen werden</string>
|
||||
<string name="noBarcodeFound">Kein Strichcode gefunden</string>
|
||||
<string name="addFromImage">Bild aus der Galerie auswählen</string>
|
||||
<string name="settings_max_font_size_scale">Max. Schriftgröße</string>
|
||||
<string name="unsupportedBarcodeType">Dieser Strichcodetyp kann noch nicht angezeigt werden. Er wird möglicherweise in einer neueren Version der Anwendung unterstützt.</string>
|
||||
<string name="wrongValueForBarcodeType">Der Wert ist für den gewählten Strichcodetyp nicht gültig</string>
|
||||
<string name="setBarcodeId">Manuell eingeben</string>
|
||||
<string name="sameAsCardId">Entspricht Kartennummer</string>
|
||||
<string name="barcodeId">Barcodewert</string>
|
||||
<string name="errorReadingImage">Bildverarbeitung fehlgeschlagen</string>
|
||||
<string name="noBarcodeFound">Kein Barcode erkannt</string>
|
||||
<string name="addFromImage">Bild aus der Galerie wählen</string>
|
||||
<string name="settings_max_font_size_scale">Maximale Schriftgröße</string>
|
||||
<string name="unsupportedBarcodeType">Dieser Barcodetyp kann noch nicht angezeigt werden. Wir hoffen das Format in einer zukünftigen Version zu unterstützen.</string>
|
||||
<string name="wrongValueForBarcodeType">Der Wert ist für den gewählten Barcodetyp leider nicht gültig</string>
|
||||
<string name="app_resources">Freie Ressourcen von Drittanbietern: <xliff:g id="app_resources_list">%s</xliff:g></string>
|
||||
<string name="app_libraries">Freie Bibliotheken von Drittanbietern: <xliff:g id="app_libraries_list">%s</xliff:g></string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Copyright © 2019–<xliff:g>%d</xliff:g> Sylvia van Os.</string>
|
||||
<string name="intent_import_card_from_url_share_multiple_text">Ich möchte Karten mit dir teilen</string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Copyright © 2019–<xliff:g>%d</xliff:g> Sylvia van Os</string>
|
||||
<string name="intent_import_card_from_url_share_multiple_text">Ich möchte diese Karten mit dir teilen</string>
|
||||
<string name="copy_to_clipboard_multiple_toast">Kartennummern in die Zwischenablage kopiert</string>
|
||||
<string name="card_ids_copied">Kartennummer(n) kopiert</string>
|
||||
<string name="card_selected">"Ausgewählt: "</string>
|
||||
<string name="no">Nein</string>
|
||||
<string name="yes">Ja</string>
|
||||
<string name="updateBarcodeQuestionText">Kartennummer geändert. Möchtest du auch den Barcode auf den gleichen Wert ändern?</string>
|
||||
<string name="updateBarcodeQuestionTitle">Barcodewert aktualisieren?</string>
|
||||
<string name="takePhoto">Foto aufnehmen</string>
|
||||
<string name="removeImage">Bild entfernen</string>
|
||||
<string name="setBackImage">Kartenrückseite</string>
|
||||
<string name="setFrontImage">Kartenvorderseite</string>
|
||||
<string name="photos">Fotos</string>
|
||||
<string name="frontImageDescription">Bild der Kartenvorderseite</string>
|
||||
<string name="backImageDescription">Bild der Kartenrückseite</string>
|
||||
<string name="passwordRequired">Bitte gib das Passwort ein</string>
|
||||
<string name="importStocardMessage">Wählen Sie Ihren <i>***-sync.zip</i>-Export aus Stocard zum Importieren aus.
|
||||
\nSie erhalten ihn, indem Sie eine E-Mail an support@stocardapp.com senden und um einen Export Ihrer Daten bitten.</string>
|
||||
<string name="importStocard">Von Stocard importieren</string>
|
||||
<string name="turn_flashlight_off">Licht ausschalten</string>
|
||||
<string name="turn_flashlight_on">Licht einschalten</string>
|
||||
<string name="failedGeneratingShareURL">URL konnte nicht erstellt werden. Bitte melde das an uns.</string>
|
||||
<plurals name="selectedCardCount">
|
||||
<item quantity="one"><xliff:g>%d</xliff:g> Karte ausgewählt</item>
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> Karten ausgewählt</item>
|
||||
</plurals>
|
||||
<string name="deleteTitle">Karte löschen</string>
|
||||
<string name="deleteConfirmation">Diese Karte wirklich löschen?</string>
|
||||
<plurals name="deleteCardsConfirmation">
|
||||
<item quantity="one">Diese <xliff:g>%d</xliff:g> Karte endgültig löschen\?</item>
|
||||
<item quantity="other">Diese <xliff:g>%d</xliff:g> Karten endgültig löschen\?</item>
|
||||
</plurals>
|
||||
<plurals name="deleteCardsTitle">
|
||||
<item quantity="one"><xliff:g>%d</xliff:g> Karte löschen</item>
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> Karten löschen</item>
|
||||
</plurals>
|
||||
<string name="settings_system_locale">System</string>
|
||||
<string name="settings_locale">Sprache</string>
|
||||
<string name="settings_brown_theme">Braun</string>
|
||||
<string name="settings_grey_theme">Grau</string>
|
||||
<string name="settings_green_theme">Grün</string>
|
||||
<string name="settings_sky_blue_theme">Himmelblau</string>
|
||||
<string name="settings_blue_theme">Blau</string>
|
||||
<string name="settings_violet_theme">Violett</string>
|
||||
<string name="settings_magenta_theme">Magenta</string>
|
||||
<string name="settings_pink_theme">Rosa</string>
|
||||
<string name="settings_catima_theme">Catima</string>
|
||||
<string name="settings_theme_color">Designfarbe</string>
|
||||
<string name="app_contributors">Ermöglicht durch: <xliff:g id="app_contributors">%s</xliff:g></string>
|
||||
<string name="toggleMoreInfo">Umschalten, um weitere Informationen anzuzeigen</string>
|
||||
<string name="barcodeImageDescriptionWithType">Bild des Kartenstrichcodes des Typs <xliff:g>%s</xliff:g></string>
|
||||
<string name="swipeToSwitchImages">Wischen oder langes Drücken zum Wechseln der Bilder</string>
|
||||
<string name="sort_by">Sortieren nach</string>
|
||||
<string name="sort_by_balance">Kontostand</string>
|
||||
<string name="sort_by_expiry">Ablaufdatum</string>
|
||||
<string name="sort_by_most_recently_used">Zuletzt verwendet</string>
|
||||
<string name="sort_by_name">Name</string>
|
||||
<string name="sort">Sortieren</string>
|
||||
<string name="reverse">Umgekehrt</string>
|
||||
<string name="version_history">Versionshistorie</string>
|
||||
<string name="credits">Dank an</string>
|
||||
<string name="help_translate_this_app">Hilfe bei der Übersetzung</string>
|
||||
<string name="and_data_usage">und Datennutzung</string>
|
||||
<string name="rate_this_app">Bewerte die Anwendung</string>
|
||||
<string name="on_google_play">auf Google Play</string>
|
||||
<string name="license">Lizenz</string>
|
||||
<string name="source_repository">Quellcode</string>
|
||||
<string name="on_github">auf GitHub</string>
|
||||
<string name="report_error">Fehler melden</string>
|
||||
<string name="exportPassword">Legen Sie ein Passwort fest, um Ihren Export zu schützen (optional)</string>
|
||||
<string name="exportPasswordHint">Passwort eingeben</string>
|
||||
</resources>
|
||||
@@ -12,8 +12,6 @@
|
||||
<string name="confirm">Επιβεβαίωση</string>
|
||||
<string name="lockScreen">Αποκλεισμός Περιστροφής</string>
|
||||
<string name="unlockScreen">Περιστροφή</string>
|
||||
<string name="deleteTitle">Αφαίρεση Κάρτας</string>
|
||||
<string name="deleteConfirmation">Παρακαλώ επιβεβαιώστε ότι θέλετε να διαγράψετε αυτή την κάρτα.</string>
|
||||
<string name="ok">Εντάξει</string>
|
||||
<string name="copy_to_clipboard">Αντιγραφή κωδικού στο πρόχειρο</string>
|
||||
<string name="sendLabel">Αποστολή…</string>
|
||||
@@ -22,7 +20,6 @@
|
||||
<string name="scanCardBarcode">Σαρώστε τον κωδικό της κάρτας</string>
|
||||
<string name="cardShortcut">Συντόμευση Κάρτας</string>
|
||||
<string name="noCardsMessage">Δεν υπάρχουν κάρτες. προσθέστε μία πρώτα</string>
|
||||
<string name="barcodeImageDescription">Εικόνα του barcode της κάρτας</string>
|
||||
<string name="noStoreError">Δεν δώσατε κατάστημα</string>
|
||||
<string name="noCardIdError">Δεν δώσατε κωδικό κάρτας</string>
|
||||
<string name="noCardExistsError">Δεν ήταν δυνατό να εντοπιστεί κάρτα</string>
|
||||
@@ -58,4 +55,6 @@
|
||||
<string name="settings_light_theme">Φωτεινό</string>
|
||||
<string name="settings_system_theme">Σύστημα</string>
|
||||
<string name="barcode">Γραμμικός κώδικας</string>
|
||||
<string name="deleteTitle">Αφαίρεση Κάρτας</string>
|
||||
<string name="deleteConfirmation">Παρακαλώ επιβεβαιώστε ότι θέλετε να διαγράψετε αυτή την κάρτα.</string>
|
||||
</resources>
|
||||
91
app/src/main/res/values-eo/strings.xml
Normal file
91
app/src/main/res/values-eo/strings.xml
Normal file
@@ -0,0 +1,91 @@
|
||||
<?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">
|
||||
<string name="barcodeNoBarcode">Strekokodo mankas al karto</string>
|
||||
<string name="delete">Forigi</string>
|
||||
<string name="noBarcode">Sen strekokodo</string>
|
||||
<string name="barcodeType">Tipo de strekokodo</string>
|
||||
<string name="cardId">Identigilo de karto</string>
|
||||
<string name="settings_category_title_ui">Fasado</string>
|
||||
<string name="settings">Agordoj</string>
|
||||
<string name="selectBarcodeTitle">Elekti strekokodon</string>
|
||||
<string name="debug_version_fmt">Versio: <xliff:g id="version">%s</xliff:g></string>
|
||||
<string name="about_title_fmt">Pri <xliff:g id="app_name">%s</xliff:g></string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Kopirajto © 2019–<xliff:g>%d</xliff:g> Sylvia van Os.</string>
|
||||
<string name="importOptionFilesystemButton">El dosiersistemo</string>
|
||||
<string name="importOptionFilesystemTitle">Enporti el dosiersistemo</string>
|
||||
<string name="exportFailedTitle">Elportado malsukcesis</string>
|
||||
<string name="exportSuccessfulTitle">Elportado sukcesis</string>
|
||||
<string name="importFailedTitle">Enportado malsukcesis</string>
|
||||
<string name="importSuccessfulTitle">Enportado sukcesis</string>
|
||||
<string name="exporting">Elportante…</string>
|
||||
<string name="importing">Enportante…</string>
|
||||
<string name="exportName">Elporti</string>
|
||||
<string name="importExport">Enporti/elporti</string>
|
||||
<string name="addCardTitle">Aldoni karton</string>
|
||||
<string name="editCardTitle">Redakti karton</string>
|
||||
<string name="sendLabel">Sendi…</string>
|
||||
<string name="takePhoto">Foti</string>
|
||||
<string name="no">Ne</string>
|
||||
<string name="yes">Jes</string>
|
||||
<string name="photos">Fotoj</string>
|
||||
<string name="points">Poentoj</string>
|
||||
<string name="currency">Valuto</string>
|
||||
<string name="editBarcode">Redakti strekokodon</string>
|
||||
<string name="barcode">Strekokodo</string>
|
||||
<string name="card">Karto</string>
|
||||
<string name="never">Neniam</string>
|
||||
<string name="groupsList">Grupoj: <xliff:g>%s</xliff:g></string>
|
||||
<string name="groups">Grupoj</string>
|
||||
<string name="settings_dark_theme">Malhela</string>
|
||||
<string name="settings_light_theme">Hela</string>
|
||||
<string name="settings_system_theme">Sistema</string>
|
||||
<string name="settings_theme">Etoso</string>
|
||||
<string name="about">Pri</string>
|
||||
<string name="note">Noto</string>
|
||||
<string name="storeName">Nomo</string>
|
||||
<string name="confirm">Konfirmi</string>
|
||||
<string name="ok">Bone</string>
|
||||
<string name="edit">Redakti</string>
|
||||
<string name="save">Konservi</string>
|
||||
<string name="cancel">Nuligi</string>
|
||||
<string name="action_add">Aldoni</string>
|
||||
<string name="action_search">Serĉi</string>
|
||||
<string name="deleteConfirmation">Ĉu forigi ĉi tiun karton\?</string>
|
||||
<string name="deleteTitle">Forigi karton</string>
|
||||
<string name="settings_lock_barcode_orientation">Seruro barcode orientiĝo</string>
|
||||
<string name="settings_display_barcode_max_brightness">Heligi barcode vido</string>
|
||||
<string name="settings_max_font_size_scale">Max. tiparo grandeco</string>
|
||||
<string name="starImage">Preferata stelo</string>
|
||||
<string name="thumbnailDescription">Bildeto por karto</string>
|
||||
<string name="app_copyright_old">Bazita sur Lojaleco Karto Keychain
|
||||
\nkopirajto © 2016-2020 Branden Archer.</string>
|
||||
<string name="importOptionApplicationButton">Uzi alian app</string>
|
||||
<string name="importOptionApplicationExplanation">Uzi ajna app aŭ via preferata dosiera # mana\? ero por malfermi dosieron.</string>
|
||||
<string name="importOptionApplicationTitle">Uzi alian app</string>
|
||||
<string name="importOptionFilesystemExplanation">Elektu specifa dosiero de la dosiersistemo.</string>
|
||||
<string name="exportOptionExplanation">La datumoj estos skribita al loko de via elekto.</string>
|
||||
<string name="noExternalStoragePermissionError">Grant ekstera stokado permeso de importado aŭ eksportado kartoj unua</string>
|
||||
<string name="exportFailed">Ne povis eksporti kartoj</string>
|
||||
<string name="importFailed">Ne povis importi kartoj</string>
|
||||
<string name="importExportHelp">Subtenanta supre vian kartoj permesas vin movi ilin al alia aparato.</string>
|
||||
<string name="failedParsingImportUriError">Ne eblis analizi la importado URI</string>
|
||||
<string name="noCardExistsError">Ne eblis trovi karto</string>
|
||||
<string name="noCardIdError">Neniu karto ID eniris</string>
|
||||
<string name="noStoreError">Neniu eniris nomo</string>
|
||||
<string name="noCardsMessage">Aldoni karto unua</string>
|
||||
<string name="cardShortcut">Karto Mallongirejo</string>
|
||||
<string name="scanCardBarcode">Scintigrafio Barcode Card</string>
|
||||
<string name="share">Interŝanĝado</string>
|
||||
<string name="unlockScreen">Malbloki Rotacio</string>
|
||||
<string name="lockScreen">Bloko Rotacio</string>
|
||||
<string name="star">Aldoni al miaj plej ŝatataj</string>
|
||||
<string name="copy_to_clipboard_toast">Card ID kopiita al la tondujo</string>
|
||||
<string name="settings_keep_screen_on">Teni sur ekrano</string>
|
||||
<string name="importSuccessful">Karto datumo importitaj</string>
|
||||
<string name="enter_group_name">Eniri nomo de la grupo</string>
|
||||
<string name="noGroups">Klaki la + plus butonon por aldoni grupoj por categorization unua.</string>
|
||||
<string name="all">Ĉiuj</string>
|
||||
<string name="intent_import_card_from_url_share_text">Mi deziras dividi karto kun vi</string>
|
||||
<string name="exportSuccessful">Karto datumo eksportita</string>
|
||||
<string name="noGroupCards">Ĉi tiu grupo ne enhavas ajnan kartoj</string>
|
||||
</resources>
|
||||
@@ -12,8 +12,6 @@
|
||||
<string name="confirm">Confirmar</string>
|
||||
<string name="lockScreen">Bloquear giro</string>
|
||||
<string name="unlockScreen">Desbloquear giro</string>
|
||||
<string name="deleteTitle">Eliminar tarjeta</string>
|
||||
<string name="deleteConfirmation">¿Quiere eliminar esta tarjeta\?</string>
|
||||
<string name="ok">Aceptar</string>
|
||||
<string name="copy_to_clipboard">Copiar id. en portapapeles</string>
|
||||
<string name="sendLabel">Enviar…</string>
|
||||
@@ -22,13 +20,12 @@
|
||||
<string name="scanCardBarcode">Escanear código de barras de la tarjeta</string>
|
||||
<string name="cardShortcut">Atajo de tarjeta</string>
|
||||
<string name="noCardsMessage">Añada una tarjeta primero</string>
|
||||
<string name="barcodeImageDescription">Imagen del código de barras de la tarjeta</string>
|
||||
<string name="noStoreError">No se proporcionó ningún nombre</string>
|
||||
<string name="noCardIdError">Id. de tarjeta no especificado</string>
|
||||
<string name="noCardExistsError">No se ha podido encontrar la tarjeta</string>
|
||||
<string name="importExport">Importar/exportar</string>
|
||||
<string name="exportName">Exportar</string>
|
||||
<string name="importExportHelp">La copia de respaldo le permite transferir sus tarjetas a otro dispositivo.</string>
|
||||
<string name="importExportHelp">El respaldo le permite transferir sus tarjetas a otro dispositivo.</string>
|
||||
<string name="importSuccessfulTitle">Importado</string>
|
||||
<string name="importFailedTitle">Falló la importación</string>
|
||||
<string name="importFailed">No se han podido importar tarjetas</string>
|
||||
@@ -48,18 +45,18 @@
|
||||
<string name="app_license">Programa libre con «copyleft», disponible en virtud de la licencia GPLv3+.</string>
|
||||
<string name="about_title_fmt">Acerca de <xliff:g id="app_name">%s</xliff:g></string>
|
||||
<string name="debug_version_fmt">Versión: <xliff:g id="version">%s</xliff:g></string>
|
||||
<string name="settings">Configuración</string>
|
||||
<string name="settings">Ajustes</string>
|
||||
<string name="settings_category_title_ui">Interfaz de usuario</string>
|
||||
<string name="settings_display_barcode_max_brightness">Iluminar vista del código de barras</string>
|
||||
<string name="exportSuccessful">Datos de las tarjetas exportados</string>
|
||||
<string name="importSuccessful">Datos de las tarjetas importados</string>
|
||||
<string name="intent_import_card_from_url_share_text">Quiero compartirle una tarjeta</string>
|
||||
<string name="settings_lock_barcode_orientation">Bloquear orientación del código de barras</string>
|
||||
<string name="intent_import_card_from_url_share_text">Quiero compartirte una tarjeta</string>
|
||||
<string name="settings_lock_barcode_orientation">Bloquear giro en el código de barras</string>
|
||||
<string name="settings_dark_theme">Oscuro</string>
|
||||
<string name="settings_light_theme">Claro</string>
|
||||
<string name="settings_system_theme">Sistema</string>
|
||||
<string name="settings_theme">Tema</string>
|
||||
<string name="enterBarcodeInstructions">Introduzca el identificador de tarjeta y seleccione la imagen que represente el código de barras que se utilizará, o bien, elija «Esta tarjeta no tiene código de barras» para no utilizar ninguno.</string>
|
||||
<string name="enterBarcodeInstructions">Introduzca el identificador de tarjeta y seleccione el código de barras que se utilizará, o de lo contrario, elija «Esta tarjeta no tiene código de barras».</string>
|
||||
<string name="app_copyright_old">Basado en Loyalty Card Keychain
|
||||
\nderechos de autor © 2016-2020 de Branden Archer.</string>
|
||||
<string name="exportOptionExplanation">Los datos se guardarán en la ubicación que elija.</string>
|
||||
@@ -73,7 +70,7 @@
|
||||
<string name="noGroups">Primero pulse en el botón «+» para añadir grupos de categorización.</string>
|
||||
<string name="starImage">Favorito</string>
|
||||
<string name="thumbnailDescription">Miniatura de la tarjeta</string>
|
||||
<string name="copy_to_clipboard_toast">Se copió el identificador de tarjeta en el portapapeles</string>
|
||||
<string name="copy_to_clipboard_toast">Se copió la id. de tarjeta en el portapapeles</string>
|
||||
<string name="selectBarcodeTitle">Seleccionar el código de barras</string>
|
||||
<string name="unstar">Eliminar de favoritos</string>
|
||||
<string name="noBarcode">Sin código de barras</string>
|
||||
@@ -94,8 +91,8 @@
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> tarjetas</item>
|
||||
</plurals>
|
||||
<string name="points">Puntos</string>
|
||||
<string name="moveBarcodeToCenterOfScreen">Centrar el código de barras en la pantalla</string>
|
||||
<string name="moveBarcodeToTopOfScreen">Mover el código de barras a la zona superior de la pantalla</string>
|
||||
<string name="moveBarcodeToCenterOfScreen">Centre el código de barras en la pantalla</string>
|
||||
<string name="moveBarcodeToTopOfScreen">Mueva el código de barras a la zona superior de la pantalla</string>
|
||||
<string name="chooseExpiryDate">Elegir fecha de caducidad</string>
|
||||
<string name="never">Nunca</string>
|
||||
<string name="expiryDate">Fecha de caducidad</string>
|
||||
@@ -108,4 +105,87 @@
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Derechos de autor © 2019-<xliff:g>%d</xliff:g> de Sylvia van Os.</string>
|
||||
<string name="app_resources">Recursos de terceros libres: <xliff:g id="app_resources_list">%s</xliff:g></string>
|
||||
<string name="app_libraries">Bibliotecas de terceros libres: <xliff:g id="app_libraries_list">%s</xliff:g></string>
|
||||
<string name="settings_max_font_size_scale">Tam. de fuente máximo</string>
|
||||
<string name="importCatimaMessage">Seleccione su <i>catima.zip</i> exportado desde Catima para importarlo.
|
||||
\nO créalo primero desde el menú Importar/Exportar de otra app de Catima al presionar Exportar desde allí.</string>
|
||||
<string name="importFidmeMessage">Seleccione su <i>fidme-export-request-xxxxxx.zip</i> exportado desde FidMe para importar, y luego escoja los tipos de códigos de barras manualmente.
|
||||
\nO créalo primero desde tu perfil de FidMe eligiendo Protección de datos y pulsa Extraer mis datos.</string>
|
||||
<string name="importLoyaltyCardKeychainMessage">Seleccione su <i>LoyaltyCardKeychain.csv</i> exportado desde Loyalty Card Keychain para importarlo.
|
||||
\nO créalo primero desde el menú Importar/Exportar en Loyalty Card Keychain pulsando Exportar desde allí.</string>
|
||||
<string name="importStocardMessage">Seleccione su exportación <i>*-sync.zip</i> de Stocard para importarla.
|
||||
\nO consígalo enviando un correo electrónico a support@stocardapp.com solicitando una exportación de sus datos.</string>
|
||||
<string name="importVoucherVaultMessage">Seleccione su <i>vouchervault.json</i> exportado desde Voucher Vault para importarlo.
|
||||
\nO créalo pulsando primero Exportar en Voucher Vault.</string>
|
||||
<string name="failedGeneratingShareURL">No se ha podido generar una URL compartible. Por favor, informe de ello.</string>
|
||||
<string name="passwordRequired">Por favor, introduzca la contraseña</string>
|
||||
<string name="updateBarcodeQuestionText">Ha cambiado la Id. de la tarjeta. ¿Desea actualizar también el código de barras para usar el mismo valor\?</string>
|
||||
<string name="intent_import_card_from_url_share_multiple_text">Quiero compartirte algunas tarjetas</string>
|
||||
<string name="setBackImage">Establecer imagen anversa</string>
|
||||
<string name="card_ids_copied">Id. de tarjetas copiadas</string>
|
||||
<string name="turn_flashlight_off">Apagar linterna</string>
|
||||
<string name="turn_flashlight_on">Encender linterna</string>
|
||||
<string name="no">No</string>
|
||||
<string name="yes">Sí</string>
|
||||
<string name="updateBarcodeQuestionTitle">¿Actualizar valor de código de barras\?</string>
|
||||
<string name="takePhoto">Tomar una foto</string>
|
||||
<string name="removeImage">Quitar imagen</string>
|
||||
<string name="setFrontImage">Establecer imagen frontal</string>
|
||||
<string name="photos">Fotos</string>
|
||||
<string name="backImageDescription">Imagen del reverso de la tarjeta</string>
|
||||
<string name="frontImageDescription">Imagen frontal de la tarjeta</string>
|
||||
<string name="copy_to_clipboard_multiple_toast">ID de tarjetas copiadas al portapapeles</string>
|
||||
<string name="wrongValueForBarcodeType">El valor no es válido para el tipo de código de barras seleccionado</string>
|
||||
<string name="unsupportedBarcodeType">Este tipo de código de barras todavía no se puede visualizar. Es posible que se admita en una futura versión de la aplicación.</string>
|
||||
<string name="setBarcodeId">Establecer valor de código de barra</string>
|
||||
<string name="sameAsCardId">Igual que la ID de tarjeta</string>
|
||||
<string name="barcodeId">Valor de código de barra</string>
|
||||
<string name="importVoucherVault">Importar desde Voucher Vault</string>
|
||||
<string name="importStocard">Importar desde Stocard</string>
|
||||
<string name="importLoyaltyCardKeychain">Importar desde Loyalty Card Keychain</string>
|
||||
<string name="importFidme">Importar desde FidMe</string>
|
||||
<string name="importCatima">Importar desde Catima</string>
|
||||
<string name="accept">Aceptar</string>
|
||||
<string name="privacy_policy_popup_text">Aviso de política de privacidad (requerido por algunas tiendas de apps):
|
||||
\n
|
||||
\nNINGÚN DATO SE RECOPILA, cualquiera puede confirmar ya que nuestra aplicación es software libre.</string>
|
||||
<string name="privacy_policy">Política de privacidad</string>
|
||||
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
|
||||
<string name="chooseImportType">¿De dónde importar datos\?</string>
|
||||
<string name="parsingBalanceFailed"><xliff:g>%s</xliff:g> no tendría un saldo válido.</string>
|
||||
<string name="currency">Moneda</string>
|
||||
<string name="balance">Saldo</string>
|
||||
<string name="errorReadingImage">No se pudo leer la imagen</string>
|
||||
<string name="noBarcodeFound">No se encontró código de barras</string>
|
||||
<string name="balanceSentence">Saldo: <xliff:g>%s</xliff:g></string>
|
||||
<string name="addFromImage">Selecciona imagen desde galería</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Evitar bloqueo de pantalla</string>
|
||||
<string name="settings_keep_screen_on">Mantener pantalla encendida</string>
|
||||
<plurals name="selectedCardCount">
|
||||
<item quantity="one"><xliff:g>%d</xliff:g> tarjeta seleccionada</item>
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> tarjetas seleccionadas</item>
|
||||
</plurals>
|
||||
<string name="deleteTitle">Eliminar la tarjeta</string>
|
||||
<string name="deleteConfirmation">¿Quiere eliminar esta tarjeta\?</string>
|
||||
<plurals name="deleteCardsConfirmation">
|
||||
<item quantity="one">¿Borrar esta tarjeta <xliff:g>%d</xliff:g> permanentemente\?</item>
|
||||
<item quantity="other">¿Borrar estas tarjetas <xliff:g>%d</xliff:g> permanentemente\?</item>
|
||||
</plurals>
|
||||
<plurals name="deleteCardsTitle">
|
||||
<item quantity="one">Borrar <xliff:g>%d</xliff:g> tarjeta</item>
|
||||
<item quantity="other">Borrar <xliff:g>%d</xliff:g> tarjetas</item>
|
||||
</plurals>
|
||||
<string name="app_contributors">Hecho posible por: <xliff:g id="app_contributors">%s</xliff:g></string>
|
||||
<string name="settings_brown_theme">Marrón</string>
|
||||
<string name="settings_grey_theme">Gris</string>
|
||||
<string name="settings_green_theme">Verde</string>
|
||||
<string name="settings_sky_blue_theme">Azul cielo</string>
|
||||
<string name="settings_blue_theme">Azul</string>
|
||||
<string name="settings_violet_theme">Violeta</string>
|
||||
<string name="settings_magenta_theme">Magenta</string>
|
||||
<string name="settings_pink_theme">Rosa</string>
|
||||
<string name="settings_theme_color">Color del tema</string>
|
||||
<string name="settings_catima_theme">Catima</string>
|
||||
<string name="settings_system_locale">Sistema</string>
|
||||
<string name="settings_locale">Lengua</string>
|
||||
<string name="noGroupCards">Este grupo no contiene ninguna tarjeta</string>
|
||||
</resources>
|
||||
191
app/src/main/res/values-fi/strings.xml
Normal file
191
app/src/main/res/values-fi/strings.xml
Normal file
@@ -0,0 +1,191 @@
|
||||
<?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">
|
||||
<string name="noExternalStoragePermissionError">Salli käyttöoikeus ulkoisen tallennustilan käyttöön voidaksesi tuoda tai viedä kortteja</string>
|
||||
<string name="no">Ei</string>
|
||||
<string name="yes">Kyllä</string>
|
||||
<string name="updateBarcodeQuestionText">Vaihdoit kortin ID-tunnuksen. Haluatko päivittää myös viivakoodin käyttämään samaa arvoa\?</string>
|
||||
<string name="updateBarcodeQuestionTitle">Päivitä viivakoodin arvo\?</string>
|
||||
<string name="intent_import_card_from_url_share_multiple_text">Haluan jakaa joitain kortteja kanssasi</string>
|
||||
<string name="copy_to_clipboard_multiple_toast">Kortin tunnukset kopioidaan leikepöydälle</string>
|
||||
<string name="wrongValueForBarcodeType">Arvo ei ole kelvollinen valitulle viivakoodityypille</string>
|
||||
<string name="unsupportedBarcodeType">Tätä viivakoodityyppiä ei voi vielä näyttää. Sitä saatetaan tukea sovelluksen myöhemmässä versiossa.</string>
|
||||
<string name="setBarcodeId">Aseta viivakoodin arvo</string>
|
||||
<string name="sameAsCardId">Sama kuin kortin ID-tunnus</string>
|
||||
<string name="barcodeId">Viivakoodin arvo</string>
|
||||
<string name="importVoucherVaultMessage">Valitse tuotava <i>vouchervault.json</i>-vienti Voucher Vaultista.
|
||||
\nTai luo se painamalla ensin Vie Voucher Vaultissa.</string>
|
||||
<string name="importVoucherVault">Tuo Voucher Vault varmuuskopiotiedostosta</string>
|
||||
<string name="importLoyaltyCardKeychainMessage">Valitse <i>LoyaltyCardKeychain.csv</i>-vientitietosi Loyalty Card Keychainista tuotavaksi.
|
||||
\nTai luo se Loyalty Card Keychainin Import/Export-valikosta painamalla ensin Export (Vie).</string>
|
||||
<string name="importLoyaltyCardKeychain">Tuo Loyalty Card Keychain varmuuskopiotiedostosta</string>
|
||||
<string name="importFidmeMessage">Valitse <i>fidme-export-request-xxxxxxxx.zip</i>-vienti FidMe:stä tuotavaksi ja valitse viivakoodityypit manuaalisesti sen jälkeen.
|
||||
\nTai luo se FidMe-profiilistasi valitsemalla Tietosuoja ja painamalla ensin Extract my data.</string>
|
||||
<string name="importFidme">Tuo FidMe varmuuskopiotiedostosta</string>
|
||||
<string name="importCatimaMessage">Valitse tuotava <i>catima.zip</i>-vienti Catimasta.
|
||||
\nTai luo se jonkin toisen Catima-sovelluksen Tuo/Vie-valikosta painamalla siellä ensin Vie.</string>
|
||||
<string name="importCatima">Tuo Catima varmuuskopiotiedostosta</string>
|
||||
<string name="accept">Hyväksy</string>
|
||||
<string name="privacy_policy_popup_text">Tietosuojaseloste (joidenkin sovelluskauppojen vaatimus):
|
||||
\n
|
||||
\nMITÄÄN TIETOJA EI KERÄTÄ LAINKAAN, minkä kuka tahansa voi vahvistaa, koska sovelluksemma on vapaa ohjelmisto.</string>
|
||||
<string name="privacy_policy">Tietosuojakäytäntö</string>
|
||||
<string name="app_loyalty_card_keychain">Kanta-asiakaskortin avainnippu</string>
|
||||
<string name="chooseImportType">Tuo tietoja kohteesta\?</string>
|
||||
<string name="parsingBalanceFailed"><xliff:g>%s</xliff:g> ei vaikuta olevan kelvollinen saldo.</string>
|
||||
<string name="points">Pisteet</string>
|
||||
<string name="currency">Valuutta</string>
|
||||
<string name="balance">Saldo</string>
|
||||
<string name="errorReadingImage">Kuvaa ei voitu lukea</string>
|
||||
<string name="noBarcodeFound">Viivakoodia ei löytynyt</string>
|
||||
<string name="moveBarcodeToCenterOfScreen">Keskitä viivakoodi näytölle</string>
|
||||
<string name="moveBarcodeToTopOfScreen">Siirrä viivakoodi näytön yläosaan</string>
|
||||
<string name="chooseExpiryDate">Valitse viimeinen voimassaolopäivä</string>
|
||||
<string name="never">Ei koskaan</string>
|
||||
<string name="expiryDate">Viimeinen voimassaolopäivä</string>
|
||||
<string name="editBarcode">Muokkaa viivakoodia</string>
|
||||
<string name="barcode">Viivakoodi</string>
|
||||
<string name="card">Kortti</string>
|
||||
<string name="balancePoints"><xliff:g>%s</xliff:g> pistettä</string>
|
||||
<string name="balanceSentence">Saldo: <xliff:g>%s</xliff:g></string>
|
||||
<string name="expiryStateSentenceExpired">Vanhentunut: <xliff:g>%s</xliff:g></string>
|
||||
<string name="expiryStateSentence">Vanhenee: <xliff:g>%s</xliff:g></string>
|
||||
<string name="groupsList">Ryhmät: <xliff:g>%s</xliff:g></string>
|
||||
<string name="addFromImage">Valitse kuva galleriasta</string>
|
||||
<string name="addManually">Anna kortin ID-tunnus manuaalisesti</string>
|
||||
<string name="leaveWithoutSaveConfirmation">Poistu tallentamatta\?</string>
|
||||
<string name="leaveWithoutSaveTitle">Poistu</string>
|
||||
<string name="moveDown">Siirrä alaspäin</string>
|
||||
<string name="moveUp">Siirrä ylöspäin</string>
|
||||
<string name="failedOpeningFileManager">Asenna ensin tiedostonhallintaohjelma.</string>
|
||||
<string name="deleteConfirmationGroup">Poista ryhmä\?</string>
|
||||
<string name="all">Kaikki</string>
|
||||
<plurals name="groupCardCount">
|
||||
<item quantity="one"><xliff:g>%d</xliff:g> kortti</item>
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> kortit</item>
|
||||
</plurals>
|
||||
<string name="noGroups">Napsauta + plus-painiketta lisätäksesi ensin ryhmät luokittelua varten.</string>
|
||||
<string name="groups">Ryhmät</string>
|
||||
<string name="enter_group_name">Anna ryhmän nimi</string>
|
||||
<string name="exportSuccessful">Korttitietojen vienti valmis</string>
|
||||
<string name="importSuccessful">Korttitietojen tuonti valmis</string>
|
||||
<string name="intent_import_card_from_url_share_text">Haluan jakaa kortin kanssasi</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Estä lukitusnäyttö</string>
|
||||
<string name="settings_keep_screen_on">Pidä näyttö päällä</string>
|
||||
<string name="settings_lock_barcode_orientation">Lukitse viivakoodin suunta</string>
|
||||
<string name="settings_display_barcode_max_brightness">Kirkasta viivakoodinäkymää</string>
|
||||
<string name="settings_max_font_size_scale">Fontin enimmäiskoko</string>
|
||||
<string name="settings_dark_theme">Tumma</string>
|
||||
<string name="settings_light_theme">Vaalea</string>
|
||||
<string name="settings_system_theme">Järjestelmän oletus</string>
|
||||
<string name="settings_theme">Teema</string>
|
||||
<string name="settings_category_title_ui">Käyttöliittymä</string>
|
||||
<string name="settings">Asetukset</string>
|
||||
<string name="starImage">Suosikki tähti</string>
|
||||
<string name="thumbnailDescription">Kortin pikkukuva</string>
|
||||
<string name="copy_to_clipboard_toast">Kortin ID-tunnus kopioitu leikepöydälle</string>
|
||||
<string name="enterBarcodeInstructions">Syötä kortin ID-tunnus ja valitse sen viivakoodityyppi, tai valitse \"Tällä kortilla ei ole viivakoodia\".</string>
|
||||
<string name="selectBarcodeTitle">Valitse viivakoodi</string>
|
||||
<string name="app_resources">Vapaat kolmannen osapuolen resurssit: <xliff:g id="app_resources_list">%s</xliff:g></string>
|
||||
<string name="app_libraries">Kolmannen osapuolen Libre-kirjastot: <xliff:g id="app_libraries_list">%s</xliff:g></string>
|
||||
<string name="app_revision_fmt">Muutostiedot: <xliff:g id="app_revision_url">%s</xliff:g></string>
|
||||
<string name="debug_version_fmt">Versio: <xliff:g id="version">%s</xliff:g></string>
|
||||
<string name="about_title_fmt">Tietoja <xliff:g id="app_name">%s</xliff:g></string>
|
||||
<string name="app_license">Copyleft (käyttäjänoikeus) - vapaa ohjelmisto, lisenssi GPLv3+.</string>
|
||||
<string name="app_copyright_old">Perustuu Loyalty Card Keychain sovellukseen
|
||||
\ntekijänoikeus © 2016–2020 Branden Archer.</string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Tekijänoikeus © 2019–<xliff:g>%d</xliff:g> Sylvia van Os.</string>
|
||||
<string name="about">Tietoja</string>
|
||||
<string name="importOptionApplicationButton">Käytä toista sovellusta</string>
|
||||
<string name="importOptionApplicationExplanation">Käytä mitä tahansa sovellusta tai suosikkitiedostonhallintaasi tiedoston avaamiseen.</string>
|
||||
<string name="importOptionApplicationTitle">Käytä toista sovellusta</string>
|
||||
<string name="importOptionFilesystemButton">Tiedostojärjestelmästä</string>
|
||||
<string name="importOptionFilesystemExplanation">Valitse tietty tiedosto tiedostojärjestelmästä.</string>
|
||||
<string name="importOptionFilesystemTitle">Tuo tiedostojärjestelmästä</string>
|
||||
<string name="exportOptionExplanation">Tiedot kirjoitetaan valitsemaasi sijaintiin.</string>
|
||||
<string name="exporting">Viedään…</string>
|
||||
<string name="importing">Tuodaan…</string>
|
||||
<string name="exportFailed">Kortteja ei voitu viedä</string>
|
||||
<string name="exportFailedTitle">Vienti epäonnistui</string>
|
||||
<string name="exportSuccessfulTitle">Vienti valmis</string>
|
||||
<string name="importFailed">Kortteja ei voitu tuoda</string>
|
||||
<string name="importFailedTitle">Tuonti epäonnistui</string>
|
||||
<string name="importSuccessfulTitle">Tuonti valmis</string>
|
||||
<string name="importExportHelp">Varmuuskopioimalla korttisi, voit siirtää ne toiseen laitteeseen.</string>
|
||||
<string name="exportName">Vie</string>
|
||||
<string name="importExport">Tuo/Vie</string>
|
||||
<string name="failedParsingImportUriError">Tuonnin URI: n jäsentäminen epäonnistui</string>
|
||||
<string name="noCardExistsError">Korttia ei löytynyt</string>
|
||||
<string name="noCardIdError">Kortin ID-tunnusta ei annettu</string>
|
||||
<string name="noStoreError">Nimeä ei annettu</string>
|
||||
<string name="card_ids_copied">Kopioidut korttitunnukset</string>
|
||||
<string name="noCardsMessage">Lisää ensin kortti</string>
|
||||
<string name="cardShortcut">Kortin pikakuvake</string>
|
||||
<string name="scanCardBarcode">Skannaa kortin viivakoodi</string>
|
||||
<string name="addCardTitle">Lisää kortti</string>
|
||||
<string name="editCardTitle">Muokkaa korttia</string>
|
||||
<string name="sendLabel">Lähetä…</string>
|
||||
<string name="share">Jaa</string>
|
||||
<string name="copy_to_clipboard">Kopioi ID-tunnus leikepöydälle</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="unlockScreen">Poista kierron esto</string>
|
||||
<string name="lockScreen">Estä kierto</string>
|
||||
<string name="confirm">Vahvista</string>
|
||||
<string name="delete">Poista</string>
|
||||
<string name="edit">Muokkaa</string>
|
||||
<string name="save">Tallenna</string>
|
||||
<string name="cancel">Peruuta</string>
|
||||
<string name="unstar">Poista suosikeista</string>
|
||||
<string name="star">Lisää suosikkeihin</string>
|
||||
<string name="noBarcode">Ei viivakoodia</string>
|
||||
<string name="barcodeNoBarcode">Tällä kortilla ei ole viivakoodia</string>
|
||||
<string name="barcodeType">Viivakoodin tyyppi</string>
|
||||
<string name="cardId">Kortin ID-tunnus</string>
|
||||
<string name="note">Lisätieto</string>
|
||||
<string name="storeName">Nimi</string>
|
||||
<string name="noMatchingGiftCards">Ei hakutuloksia, kokeile toisella hakutermillä.</string>
|
||||
<string name="noGiftCards">Lisää ensin kortti napsauttamalla + plus-painiketta, tai mene ⋮ valikkoon tuodaksesi varmuuskopiosta.</string>
|
||||
<string name="action_add">Lisää</string>
|
||||
<string name="action_search">Hae</string>
|
||||
<string name="takePhoto">Ota valokuva</string>
|
||||
<string name="removeImage">Poista kuva</string>
|
||||
<string name="setBackImage">Aseta takakuva</string>
|
||||
<string name="setFrontImage">Aseta etukuva</string>
|
||||
<string name="photos">Valokuvat</string>
|
||||
<string name="backImageDescription">Kortin takakuva</string>
|
||||
<string name="frontImageDescription">Kortin etukuva</string>
|
||||
<string name="deleteConfirmation">Poista tämä kortti\?</string>
|
||||
<string name="deleteTitle">Poista kortti</string>
|
||||
<plurals name="deleteCardsConfirmation">
|
||||
<item quantity="one">Poista tämä <xliff:g>%d</xliff:g> kortti pysyvästi\?</item>
|
||||
<item quantity="other">Poista tämä <xliff:g>%d</xliff:g> kortit pysyvästi\?</item>
|
||||
</plurals>
|
||||
<plurals name="deleteCardsTitle">
|
||||
<item quantity="one">Poista <xliff:g>%d</xliff:g>kortti</item>
|
||||
<item quantity="other">Poista <xliff:g>%d</xliff:g>kortit</item>
|
||||
</plurals>
|
||||
<plurals name="selectedCardCount">
|
||||
<item quantity="one"><xliff:g>%d</xliff:g> Kortti valittu</item>
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> kortit valitut</item>
|
||||
</plurals>
|
||||
<string name="importStocard">Tuo Stocardista</string>
|
||||
<string name="importStocardMessage">Valitse tuotava <i>***-sync.zip</i>-vienti Stocardista.
|
||||
\nTai hanki se lähettämällä sähköpostia osoitteeseen support@stocardapp.com ja pyytämällä tietojesi vientiä.</string>
|
||||
<string name="passwordRequired">Ole hyvä ja syötä salasana</string>
|
||||
<string name="failedGeneratingShareURL">Ei pystynyt luomaan jaettavaa URL-osoitetta. Ilmoita tästä.</string>
|
||||
<string name="turn_flashlight_on">Sytytä taskulamppu</string>
|
||||
<string name="turn_flashlight_off">Sammuta salamavalo</string>
|
||||
<string name="app_contributors">Mahdollistanut: <xliff:g id="app_contributors">%s</xliff:g></string>
|
||||
<string name="settings_brown_theme">Ruskea</string>
|
||||
<string name="settings_grey_theme">Harmaa</string>
|
||||
<string name="settings_green_theme">Vihreä</string>
|
||||
<string name="settings_sky_blue_theme">Taivaan sininen</string>
|
||||
<string name="settings_blue_theme">Siniset</string>
|
||||
<string name="settings_violet_theme">Violetti</string>
|
||||
<string name="settings_magenta_theme">Magenta</string>
|
||||
<string name="settings_pink_theme">Pinkki</string>
|
||||
<string name="settings_theme_color">Teeman väri</string>
|
||||
<string name="settings_catima_theme">Catima</string>
|
||||
<string name="settings_system_locale">Järjestelmä</string>
|
||||
<string name="settings_locale">Kieli</string>
|
||||
<string name="noGroupCards">Tämä ryhmä ei sisällä kortteja</string>
|
||||
</resources>
|
||||
@@ -1,7 +1,7 @@
|
||||
<?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">
|
||||
<string name="action_add">Ajouter</string>
|
||||
<string name="noGiftCards">Cliquez sur le bouton + plus pour ajouter une carte, ou importez-en d’abord depuis le menu ⋮</string>
|
||||
<string name="noGiftCards">Cliquez sur le bouton + plus pour ajouter une carte, ou importez les depuis le menu ⋮</string>
|
||||
<string name="storeName">Nom</string>
|
||||
<string name="note">Notes</string>
|
||||
<string name="cardId">Numéro</string>
|
||||
@@ -12,8 +12,6 @@
|
||||
<string name="confirm">Confirmer</string>
|
||||
<string name="lockScreen">Désactiver la rotation</string>
|
||||
<string name="unlockScreen">Activer la rotation</string>
|
||||
<string name="deleteTitle">Supprimer la carte de fidélité</string>
|
||||
<string name="deleteConfirmation">Supprimer cette carte \?</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="copy_to_clipboard">Copier le numéro dans le presse-papier</string>
|
||||
<string name="sendLabel">Envoyer…</string>
|
||||
@@ -22,7 +20,6 @@
|
||||
<string name="scanCardBarcode">Scanner le code-barres</string>
|
||||
<string name="cardShortcut">Raccourci de carte</string>
|
||||
<string name="noCardsMessage">Ajoutez d\'abord une carte</string>
|
||||
<string name="barcodeImageDescription">Image du code-barres</string>
|
||||
<string name="noStoreError">Aucun nom saisi</string>
|
||||
<string name="noCardIdError">Aucun numéro de carte saisi</string>
|
||||
<string name="noCardExistsError">Aucune carte trouvée</string>
|
||||
@@ -45,7 +42,7 @@
|
||||
<string name="importOptionApplicationExplanation">Utilisez le gestionnaire de fichiers de votre choix pour importer un fichier.</string>
|
||||
<string name="importOptionApplicationButton">Utiliser une autre application</string>
|
||||
<string name="about">À propos</string>
|
||||
<string name="app_license">Logiciel libre à copyleft, sous licence GPLv3+.</string>
|
||||
<string name="app_license">Logiciel libre à copyleft, sous licence GPLv3+</string>
|
||||
<string name="about_title_fmt">À propos de <xliff:g id="app_name">%s</xliff:g></string>
|
||||
<string name="debug_version_fmt">Version : <xliff:g id="version">%s</xliff:g></string>
|
||||
<string name="app_revision_fmt">Notes de version : <xliff:g id="app_revision_url">%s</xliff:g></string>
|
||||
@@ -65,7 +62,7 @@
|
||||
<string name="settings_theme">Thème</string>
|
||||
<string name="enterBarcodeInstructions">Entrez l’identifiant de la carte et choisissez le type de code-barres ci-dessous, ou « Cette carte n’a pas de code-barres ».</string>
|
||||
<string name="app_copyright_old">Basé sur Loyalty Card Keychain
|
||||
\ncopyright © 2016-2020 Branden Archer.</string>
|
||||
\ncopyright © 2016-2020 Branden Archer</string>
|
||||
<string name="exportOptionExplanation">Les données seront exportées vers l\'emplacement de votre choix.</string>
|
||||
<string name="failedParsingImportUriError">Impossible d\'analyser l\'URI d\'importation</string>
|
||||
<string name="share">Partager</string>
|
||||
@@ -100,7 +97,7 @@
|
||||
<string name="privacy_policy">Politique de confidentialité</string>
|
||||
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
|
||||
<string name="chooseImportType">Importer les données depuis \?</string>
|
||||
<string name="parsingBalanceFailed"><xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> ne semble pas être un solde valide.</string>
|
||||
<string name="parsingBalanceFailed"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g> ne semble pas être un solde valide.</string>
|
||||
<string name="points">Points</string>
|
||||
<string name="currency">Monnaie</string>
|
||||
<string name="balance">Solde</string>
|
||||
@@ -112,23 +109,23 @@
|
||||
<string name="editBarcode">Modifier le code-barres</string>
|
||||
<string name="barcode">Code-barres</string>
|
||||
<string name="card">Carte</string>
|
||||
<string name="balancePoints"><xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> points</string>
|
||||
<string name="balanceSentence">Solde : <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g></string>
|
||||
<string name="expiryStateSentenceExpired">Expiré : <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g></string>
|
||||
<string name="expiryStateSentence">Expire : <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g></string>
|
||||
<string name="balancePoints"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g> points</string>
|
||||
<string name="balanceSentence">Solde : <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
|
||||
<string name="expiryStateSentenceExpired">Expiré : <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
|
||||
<string name="expiryStateSentence">Expire : <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Empêcher le verrouillage de l’écran</string>
|
||||
<string name="settings_keep_screen_on">Garder l’écran allumé</string>
|
||||
<string name="importVoucherVaultMessage">Trouvez un fichier probablement nommé <i>vouchervault.json</i> à importer.
|
||||
\nOu créez-le en appuyant d’abord sur Exporter dans Voucher Vault.</string>
|
||||
<string name="importVoucherVaultMessage">Sélectionnez votre exportation <i>vouchervault.json</i> de Voucher Vault à importer.
|
||||
\nCréez-la en appuyant d’abord sur Exporter dans Voucher Vault.</string>
|
||||
<string name="importVoucherVault">Importer depuis Voucher Vault</string>
|
||||
<string name="importLoyaltyCardKeychainMessage">Trouvez un fichier probablement nommé <i>LoyaltyCardKeychain.csv</i> à importer.
|
||||
\nOu créez-le à partir du menu Importer/Exporter du Loyalty Card Keychain en appuyant d’abord sur Exporter.</string>
|
||||
<string name="importLoyaltyCardKeychainMessage">Sélectionnez votre exportation <i>LoyaltyCardKeychain.csv</i> à partir de Loyalty Card Keychain pour l’importer.
|
||||
\nCréez-la à partir du menu Importer/Exporter du Loyalty Card Keychain en appuyant d’abord sur Exporter.</string>
|
||||
<string name="importLoyaltyCardKeychain">Importer depuis Loyalty Card Keychain</string>
|
||||
<string name="importFidmeMessage">Trouvez un fichier probablement nommé <i>fidme-export-request-xxxxxx.zip</i> pour l’importer, et sélectionnez ensuite manuellement les types de codes-barres.
|
||||
\nVous pouvez aussi le créer à partir de votre profil FidMe en choisissant Protection des données, puis en cliquant sur Extraire mes données.</string>
|
||||
<string name="importFidmeMessage">Sélectionnez votre exportation <i>fidme-export-request-xxxxxx.zip</i> de FidMe pour l’importer, puis sélectionnez manuellement les types de codes-barres.
|
||||
\nCréez-la à partir de votre profil FidMe en choisissant Protection des données, puis en cliquant sur Extraire mes données d’abord.</string>
|
||||
<string name="importFidme">Importer depuis FidMe</string>
|
||||
<string name="importCatimaMessage">Trouvez un fichier probablement nommé <i>Catima.csv</i> à importer.
|
||||
\nOu créez-le à partir du menu Importer/Exporter d’une autre application Catima en appuyant d’abord sur Exporter.</string>
|
||||
<string name="importCatimaMessage">Sélectionnez votre exportation <i>catima.zip</i> depuis Catima à importer.
|
||||
\nCréez-la à partir du menu Importer/Exporter d’une autre application Catima en appuyant d’abord sur Exporter.</string>
|
||||
<string name="importCatima">Importer depuis Catima</string>
|
||||
<string name="addFromImage">Sélectionner dans la galerie</string>
|
||||
<string name="errorReadingImage">Impossible de lire l\'image</string>
|
||||
@@ -137,13 +134,80 @@
|
||||
<string name="sameAsCardId">Identique à l’identifiant de la carte</string>
|
||||
<string name="barcodeId">Valeur du code-barres</string>
|
||||
<string name="settings_max_font_size_scale">Taille max. de la police</string>
|
||||
<string name="unsupportedBarcodeType">Ce type de code-barres ne peut pas encore être affiché. Il sera peut-être pris en charge dans une version plus récente de l\'application.</string>
|
||||
<string name="unsupportedBarcodeType">Ce type de code-barres ne peut pas encore être affiché. Il sera peut-être pris en charge dans une version ultérieure de l’application.</string>
|
||||
<string name="wrongValueForBarcodeType">La valeur n\'est pas valide pour le type de code-barres sélectionné</string>
|
||||
<string name="app_resources">Ressources libres tierces : <xliff:g id="app_resources_list">%s</xliff:g></string>
|
||||
<string name="app_libraries">Bibliothèques libres tierces : <xliff:g id="app_libraries_list">%s</xliff:g></string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Copyright © 2019–<xliff:g>%d</xliff:g> Sylvia van Os.</string>
|
||||
<string name="app_resources">Ressources tierces libres : <xliff:g id="app_resources_list">%s</xliff:g></string>
|
||||
<string name="app_libraries">Bibliothèques tierces libres : <xliff:g id="app_libraries_list">%s</xliff:g></string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Copyright © 2019–<xliff:g>%d</xliff:g> Sylvia van Os</string>
|
||||
<string name="intent_import_card_from_url_share_multiple_text">Je veux partager des cartes avec vous</string>
|
||||
<string name="copy_to_clipboard_multiple_toast">Nméros des cartes copiés dans le presse-papier</string>
|
||||
<string name="copy_to_clipboard_multiple_toast">Identifiants des cartes copiés dans le presse-papiers</string>
|
||||
<string name="card_ids_copied">Num. de la carte copié(s)</string>
|
||||
<string name="card_selected">"Sélectionnée : "</string>
|
||||
<string name="updateBarcodeQuestionText">Vous avez changé l\'identifiant de la carte. Voulez-vous également mettre à jour le code-barres pour utiliser la même valeur \?</string>
|
||||
<string name="no">Non</string>
|
||||
<string name="yes">Oui</string>
|
||||
<string name="updateBarcodeQuestionTitle">Mettre à jour la valeur du code-barres \?</string>
|
||||
<string name="takePhoto">Prendre une photo</string>
|
||||
<string name="removeImage">Retirer l’image</string>
|
||||
<string name="setBackImage">Définir l’image verso</string>
|
||||
<string name="setFrontImage">Définir l’image recto</string>
|
||||
<string name="photos">Photos</string>
|
||||
<string name="backImageDescription">Image verso de la carte</string>
|
||||
<string name="frontImageDescription">Image recto de la carte</string>
|
||||
<string name="passwordRequired">Veuillez entrer le mot de passe</string>
|
||||
<string name="importStocardMessage">Sélectionnez votre exportation <i>***-sync.zip</i> de Stocard pour l’importer.
|
||||
\nVous pouvez l’obtenir en envoyant un courriel à support@stocardapp.com pour demander une exportation de vos données.</string>
|
||||
<string name="importStocard">Importer depuis Stocard</string>
|
||||
<string name="turn_flashlight_off">Éteindre la lampe de poche</string>
|
||||
<string name="turn_flashlight_on">Allumer la lampe de poche</string>
|
||||
<string name="failedGeneratingShareURL">Impossible de générer une URL partageable. Veuillez signaler ceci.</string>
|
||||
<plurals name="selectedCardCount">
|
||||
<item quantity="one"><xliff:g>%d</xliff:g> carte sélectionnée</item>
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> cartes sélectionnées</item>
|
||||
</plurals>
|
||||
<string name="deleteTitle">Supprimer la carte</string>
|
||||
<string name="deleteConfirmation">Supprimer cette carte \?</string>
|
||||
<plurals name="deleteCardsConfirmation">
|
||||
<item quantity="one">Supprimer cette <xliff:g>%d</xliff:g> carte définitivement \?</item>
|
||||
<item quantity="other">Supprimer ces <xliff:g>%d</xliff:g> cartes définitivement \?</item>
|
||||
</plurals>
|
||||
<plurals name="deleteCardsTitle">
|
||||
<item quantity="one">Supprimer <xliff:g>%d</xliff:g> carte</item>
|
||||
<item quantity="other">Supprimer <xliff:g>%d</xliff:g> cartes</item>
|
||||
</plurals>
|
||||
<string name="settings_system_locale">Système</string>
|
||||
<string name="settings_locale">Langue</string>
|
||||
<string name="settings_brown_theme">Marron</string>
|
||||
<string name="settings_grey_theme">Gris</string>
|
||||
<string name="settings_green_theme">Vert</string>
|
||||
<string name="settings_sky_blue_theme">Bleu ciel</string>
|
||||
<string name="settings_blue_theme">Bleu</string>
|
||||
<string name="settings_violet_theme">Violet</string>
|
||||
<string name="settings_magenta_theme">Magenta</string>
|
||||
<string name="settings_pink_theme">Rose</string>
|
||||
<string name="settings_catima_theme">Catima</string>
|
||||
<string name="settings_theme_color">Couleur du thème</string>
|
||||
<string name="app_contributors">Rendu possible par : <xliff:g id="app_contributors">%s</xliff:g></string>
|
||||
<string name="noGroupCards">Ce groupe ne contient pas de cartes</string>
|
||||
<string name="toggleMoreInfo">Activer/désactiver l\'affichage de plus d\'infos</string>
|
||||
<string name="barcodeImageDescriptionWithType">Image du code-barres de la carte de type <xliff:g>%s</xliff:g></string>
|
||||
<string name="swipeToSwitchImages">Balayez ou appuyez longuement pour changer d\'image</string>
|
||||
<string name="sort">Trier</string>
|
||||
<string name="sort_by">Trier par</string>
|
||||
<string name="reverse">Inversé</string>
|
||||
<string name="sort_by_expiry">Date d\'expiration</string>
|
||||
<string name="sort_by_most_recently_used">Les plus récemment utilisées</string>
|
||||
<string name="sort_by_name">Nom</string>
|
||||
<string name="sort_by_balance">Solde</string>
|
||||
<string name="report_error">Signaler une erreur</string>
|
||||
<string name="on_google_play">sur Google Play</string>
|
||||
<string name="rate_this_app">Notez cette appli</string>
|
||||
<string name="and_data_usage">utilisation des données</string>
|
||||
<string name="on_github">sur GitHub</string>
|
||||
<string name="source_repository">Dépôt source</string>
|
||||
<string name="license">Licence</string>
|
||||
<string name="help_translate_this_app">Aidez à traduire cette appli</string>
|
||||
<string name="credits">Contributeurs</string>
|
||||
<string name="version_history">Historique des versions</string>
|
||||
<string name="exportPassword">Définissez un mot de passe pour protéger vos exportations (facultatif)</string>
|
||||
<string name="exportPasswordHint">Entrez le mot de passe</string>
|
||||
</resources>
|
||||
190
app/src/main/res/values-in-rID/strings.xml
Normal file
190
app/src/main/res/values-in-rID/strings.xml
Normal file
@@ -0,0 +1,190 @@
|
||||
<?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">
|
||||
<string name="app_revision_url">https://github.com/TheLastProject/Catima/releases</string>
|
||||
<string name="storeName">Nama</string>
|
||||
<string name="note">Keterangan</string>
|
||||
<string name="delete">Hapus</string>
|
||||
<string name="edit">Ubah</string>
|
||||
<string name="save">Simpan</string>
|
||||
<string name="deleteTitle">Hapus kartu</string>
|
||||
<string name="cardId">Kartu ID</string>
|
||||
<string name="barcodeType">Tipe barcode</string>
|
||||
<string name="star">Tambahkan ke favorit</string>
|
||||
<string name="unstar">Hapus dari favorit</string>
|
||||
<string name="action_add">Tambah</string>
|
||||
<string name="action_search">Cari</string>
|
||||
<string name="sort_by_name">Nama</string>
|
||||
<string name="sort_by_balance">Saldo</string>
|
||||
<string name="sort_by">Sortir dengan</string>
|
||||
<string name="sort">Sortir</string>
|
||||
<string name="credits">Kredit</string>
|
||||
<string name="license">Lisensi</string>
|
||||
<string name="settings">Pengaturan</string>
|
||||
<string name="settings_system_theme">Sistem</string>
|
||||
<string name="selectBarcodeTitle">Pilih Barcode</string>
|
||||
<string name="deleteConfirmation">Hapus kartu ini secara permanen?</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="share">Bagikan</string>
|
||||
<string name="editCardTitle">Ubah Kartu</string>
|
||||
<string name="addCardTitle">Tambah Kartu</string>
|
||||
<string name="scanCardBarcode">Pindai Kartu Barcode</string>
|
||||
<string name="barcodeNoBarcode">Kartu ini tidak memiliki barcode</string>
|
||||
<string name="cancel">Batalkan</string>
|
||||
<string name="importExport">Import/Ekspor</string>
|
||||
<string name="settings_category_title_ui">Tampilan Pengguna</string>
|
||||
<string name="settings_theme">Tema</string>
|
||||
<string name="all">Semua</string>
|
||||
<string name="leaveWithoutSaveTitle">Keluar</string>
|
||||
<string name="card">Kartu</string>
|
||||
<string name="barcode">Barcode</string>
|
||||
<string name="chooseExpiryDate">Pilih masa berlaku</string>
|
||||
<string name="noBarcodeFound">Barcode tidak ditemukan</string>
|
||||
<string name="errorReadingImage">TIdak dapat membaca gambar</string>
|
||||
<string name="balance">Saldo</string>
|
||||
<string name="currency">Mata uang</string>
|
||||
<string name="chooseImportType">Impor data dari?</string>
|
||||
<string name="accept">Terima</string>
|
||||
<string name="importCatima">Impor dari Catima</string>
|
||||
<string name="importFidme">Impor dari FidMe</string>
|
||||
<string name="barcodeId">Nilai barcode</string>
|
||||
<string name="sameAsCardId">Sama denga kartu ID</string>
|
||||
<string name="setBarcodeId">Tentukan nilai barcode</string>
|
||||
<string name="photos">Foto</string>
|
||||
<string name="setFrontImage">Atur gambar bagian depan</string>
|
||||
<string name="report_error">Lapor Kesalahan</string>
|
||||
<string name="rate_this_app">Beri nilai pada aplikasi ini</string>
|
||||
<string name="sort_by_expiry">Masa berlaku</string>
|
||||
<string name="sort_by_most_recently_used">Paling banyak digunakan</string>
|
||||
<string name="settings_catima_theme">Catima</string>
|
||||
<string name="settings_pink_theme">Merah Muda</string>
|
||||
<string name="settings_blue_theme">Biru</string>
|
||||
<string name="settings_green_theme">Hijau</string>
|
||||
<string name="settings_sky_blue_theme">Biru Langit</string>
|
||||
<string name="settings_grey_theme">Abu-abu</string>
|
||||
<string name="settings_brown_theme">Cokelat</string>
|
||||
<string name="settings_violet_theme">Ungu</string>
|
||||
<string name="settings_magenta_theme">Magenta</string>
|
||||
<string name="settings_theme_color">Warna tema</string>
|
||||
<string name="settings_system_locale">Sistem</string>
|
||||
<string name="settings_locale">Bahasa</string>
|
||||
<string name="turn_flashlight_on">Hidupkan lampu flash</string>
|
||||
<string name="turn_flashlight_off">Matikan lampu flash</string>
|
||||
<string name="exportPasswordHint">Masukan kata sandi</string>
|
||||
<string name="yes">Ya</string>
|
||||
<string name="no">Tidak</string>
|
||||
<string name="takePhoto">Ambil foto</string>
|
||||
<string name="removeImage">Hapus gambar</string>
|
||||
<string name="setBackImage">Atur gambar bagian belakang</string>
|
||||
<string name="intent_import_card_from_url_share_multiple_text">Saya ingin berbagi kartu dengan anda</string>
|
||||
<string name="noGiftCards">Klik tanda + tombol tambah untuk menambahkan kartu, atau mengimpor beberapa kartu melalui menu ⋮ terlebih dahulu.</string>
|
||||
<string name="noMatchingGiftCards">Tidak menemukan apapun. Coba untuk mengubah pencarian anda.</string>
|
||||
<string name="noBarcode">Bukan barcode</string>
|
||||
<string name="confirm">Konfirmasi</string>
|
||||
<string name="copy_to_clipboard">Salin ID</string>
|
||||
<string name="sendLabel">Kirim…</string>
|
||||
<string name="noCardsMessage">Tambah kartu terlebih dahulu</string>
|
||||
<string name="noStoreError">Nama masih kosong</string>
|
||||
<string name="noCardIdError">Kartu ID masih kosong</string>
|
||||
<string name="noCardExistsError">Tidak dapat menemukan kartu</string>
|
||||
<string name="failedParsingImportUriError">Tidak dapat menguraikan alamat impor situs web</string>
|
||||
<string name="exportName">Ekspor</string>
|
||||
<string name="importSuccessfulTitle">Sudah diimpor</string>
|
||||
<string name="importFailedTitle">Impor gagal</string>
|
||||
<string name="importFailed">Tidak dapat mengimpor kartu</string>
|
||||
<string name="exportSuccessfulTitle">Sudah diekspor</string>
|
||||
<string name="exportFailedTitle">Ekspor gagal</string>
|
||||
<string name="exportFailed">Tidak dapat mengekspor kartu</string>
|
||||
<string name="importing">Mengimpor…</string>
|
||||
<string name="exporting">Mengekspor…</string>
|
||||
<string name="noExternalStoragePermissionError">Berikan izin penyimpanan eksternal untuk mengimpor atau mengekspor kartu terlebih dahulu</string>
|
||||
<string name="exportOptionExplanation">The data will be written to a location of your choice.</string>
|
||||
<string name="importOptionFilesystemTitle">Impor dari sistem</string>
|
||||
<string name="importOptionFilesystemExplanation">Pilih file dari sistem</string>
|
||||
<string name="importOptionFilesystemButton">Dari sistem</string>
|
||||
<string name="importOptionApplicationTitle">Gunakan aplikasi lain</string>
|
||||
<string name="importOptionApplicationExplanation">Gunakan aplikasi lain atau pengelola file favorit anda untuk membuka file.</string>
|
||||
<string name="importOptionApplicationButton">Gunakan aplikasi lain</string>
|
||||
<string name="about">Tentang</string>
|
||||
<string name="app_copyright_fmt">Hak Cipta © 2019–<xliff:g>%d</xliff:g> Sylvia van Os</string>
|
||||
<string name="app_copyright_old">Berdasarkan Loyalty Card Keychain hak cipta © 2016–2020 Branden Archer</string>
|
||||
<string name="app_license">Perangkat lunak bebas copyleft, berlisensi GPLv3+</string>
|
||||
<string name="about_title_fmt">Tentang <xliff:g id="app_name">%s</xliff:g></string>
|
||||
<string name="debug_version_fmt">Versi: <xliff:g id="version">%s</xliff:g></string>
|
||||
<string name="app_revision_fmt">Info Revisi: <xliff:g id="app_revision_url">%s</xliff:g></string>
|
||||
<string name="app_libraries">Pustaka pihak ketiga gratis: <xliff:g id="app_libraries_list">%s</xliff:g></string>
|
||||
<string name="app_resources">Sumber daya pihak ketiga gratis: <xliff:g id="app_resources_list">%s</xliff:g></string>
|
||||
<string name="enterBarcodeInstructions">Masukkan ID kartu, dan pilih jenis barcodenya di bawah atau \"Kartu ini tidak memiliki barcode\".</string>
|
||||
<string name="copy_to_clipboard_toast">ID kartu telah disalin</string>
|
||||
<string name="thumbnailDescription">Gambar tampilan untuk kartu</string>
|
||||
<string name="starImage">Favorit</string>
|
||||
<string name="settings_light_theme">Terang</string>
|
||||
<string name="settings_dark_theme">Gelap</string>
|
||||
<string name="settings_max_font_size_scale">Ukuran maksimal huruf</string>
|
||||
<string name="settings_display_barcode_max_brightness">Terangkan tampilan barcode</string>
|
||||
<string name="settings_lock_barcode_orientation">Kunci orientasi barcode</string>
|
||||
<string name="settings_keep_screen_on">Biarkan layar menyala</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Mencegah layar menyala</string>
|
||||
<string name="intent_import_card_from_url_share_text">Saya ingin berbagi kartu dengan anda</string>
|
||||
<string name="importSuccessful">Data kartu terimpor</string>
|
||||
<string name="exportSuccessful">Data kartu terekspor</string>
|
||||
<string name="enter_group_name">Masukan nama grup</string>
|
||||
<string name="groups">Grup</string>
|
||||
<string name="noGroups">Klik pada tombol tambah + untuk menambahkan grup atau kategori terlebih dahulu.</string>
|
||||
<string name="noGroupCards">Grup ini tidak memilik kartu</string>
|
||||
<string name="deleteConfirmationGroup">Hapus grup?</string>
|
||||
<string name="failedOpeningFileManager">Instal aplikasi pengelola file terlebih dahulu.</string>
|
||||
<string name="moveUp">Pindah ke atas</string>
|
||||
<string name="moveDown">Pindah ke bawah</string>
|
||||
<string name="leaveWithoutSaveConfirmation">Keluar tanpa menyimpan?</string>
|
||||
<string name="addManually">Masukan ID kartu</string>
|
||||
<string name="addFromImage">Pilih gambar dari galeri</string>
|
||||
<string name="groupsList">Grup: <xliff:g>%s</xliff:g></string>
|
||||
<string name="expiryStateSentence">Masa ber: <xliff:g>%s</xliff:g></string>
|
||||
<string name="expiryStateSentenceExpired">Kadaluwarsa: <xliff:g>%s</xliff:g></string>
|
||||
<string name="balanceSentence">Saldo: <xliff:g>%s</xliff:g></string>
|
||||
<string name="balancePoints"><xliff:g>%s</xliff:g> poin</string>
|
||||
<string name="editBarcode">Ubah barcode</string>
|
||||
<string name="expiryDate">Tanggal masa berlaku</string>
|
||||
<string name="never">Tidak pernah</string>
|
||||
<string name="moveBarcodeToTopOfScreen">Pindah barcode ke bagian paling depan</string>
|
||||
<string name="moveBarcodeToCenterOfScreen">Pusatkan barcode pada layar</string>
|
||||
<string name="points">Poin</string>
|
||||
<string name="parsingBalanceFailed"><xliff:g>%s</xliff:g> sepertinya bukan saldo yang valid.</string>
|
||||
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
|
||||
<string name="privacy_policy">Kebijakan Privasi</string>
|
||||
<string name="privacy_policy_popup_text">Pemberitahuan kebijakan privasi (diperlukan oleh beberapa toko aplikasi): TIDAK ADA DATA YANG DIKUMPULKAN SAMA SEKALI, yang dapat dikonfirmasi oleh siapa pun karena aplikasi kami adalah perangkat lunak gratis.</string>
|
||||
<string name="importCatimaMessage">Pilih ekspor <i>catima.zip</i> Anda dari Catima untuk diimpor. Buat dari menu Impor/Ekspor aplikasi Catima lain dengan menekan Ekspor di sana terlebih dahulu.</string>
|
||||
<string name="importFidmeMessage">Pilih ekspor <i>fidme-export-request-xxxxxx.zip</i> Anda dari FidMe untuk diimpor, dan pilih jenis barcode secara manual setelahnya. Buat dari profil FidMe Anda dengan memilih Perlindungan Data lalu tekan Ekstrak data saya terlebih dahulu.</string>
|
||||
<string name="importLoyaltyCardKeychain">Impor dari Loyalty Card Keychain</string>
|
||||
<string name="importLoyaltyCardKeychainMessage">Pilih ekspor <i>LoyaltyCardKeychain.csv</i> Anda dari Loyalty Card Keychain untuk diimpor. Buat dari menu Import/Export di Loyalty Card Keychain dengan menekan Export terlebih dahulu.</string>
|
||||
<string name="importStocard">Impor dari Stocard</string>
|
||||
<string name="importStocardMessage">Pilih ekspor <i>***-sync.zip</i> Anda dari Stocard untuk diimpor. Dapatkan dengan mengirim email ke support@stocardapp.com untuk meminta ekspor data Anda.</string>
|
||||
<string name="importVoucherVault">Impor dari Voucher Vault</string>
|
||||
<string name="importVoucherVaultMessage">Pilih ekspor <i>vouchervault.json</i> Anda dari Vault Voucher untuk diimpor. Buat dengan menekan Ekspor di Vault Voucher terlebih dahulu.</string>
|
||||
<string name="unsupportedBarcodeType">Jenis barcode ini belum dapat ditampilkan. Ini mungkin didukung di versi aplikasi yang lebih baru.</string>
|
||||
<string name="wrongValueForBarcodeType">Nilai tidak berlaku untuk jenis barcode yang dipilih</string>
|
||||
<string name="copy_to_clipboard_multiple_toast">ID kartu telah disalin</string>
|
||||
<string name="frontImageDescription">Gambar depan kartu</string>
|
||||
<string name="backImageDescription">Gambar belakang kartu</string>
|
||||
<string name="updateBarcodeQuestionTitle">Perbarui barcode?</string>
|
||||
<string name="updateBarcodeQuestionText">Anda mengubah ID kartu. Apakah Anda juga ingin memperbarui barcode untuk menggunakan nilai yang sama?</string>
|
||||
<string name="passwordRequired">Silahkan masukan kata sandi</string>
|
||||
<string name="exportPassword">Tetapkan kata sandi untuk melindungi ekspor anda (opsional)</string>
|
||||
<string name="failedGeneratingShareURL">Tidak dapat membuat alamat berbagi. Mohon laporkan ini.</string>
|
||||
<string name="app_contributors">Pengembangan dibantu oleh: <xliff:g id="app_contributors">%s</xliff:g></string>
|
||||
<string name="toggleMoreInfo">Tampilkan info selengkapnya</string>
|
||||
<string name="swipeToSwitchImages">Geser atau tekan yang lama untuk mengganti gambar</string>
|
||||
<string name="reverse">Ubah urutan</string>
|
||||
<string name="version_history">Riwayat Versi</string>
|
||||
<string name="help_translate_this_app">Bantu terjemahkan aplikasi ini</string>
|
||||
<string name="source_repository">Sumber Repositori</string>
|
||||
<string name="on_github">di GitHub</string>
|
||||
<string name="and_data_usage">dan penggunaan data</string>
|
||||
<string name="on_google_play">di Google Play</string>
|
||||
<string name="lockScreen">Blokir rotasi</string>
|
||||
<string name="unlockScreen">Buka blokir rotasi</string>
|
||||
<string name="cardShortcut">Pintasan kartu</string>
|
||||
<string name="card_ids_copied">ID kartu yang tersalin</string>
|
||||
<string name="barcodeImageDescriptionWithType">Gambar dari jenis barcode <xliff:g>%s</xliff:g></string>
|
||||
<string name="importExportHelp">Mencadangkan kartu memungkinkan anda memindahkannya ke perangkat lain.</string>
|
||||
</resources>
|
||||
28
app/src/main/res/values-is/strings.xml
Normal file
28
app/src/main/res/values-is/strings.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="action_add">Bæta</string>
|
||||
<string name="noBarcode">Nei strikamerkið</string>
|
||||
<string name="action_search">Leita að</string>
|
||||
<string name="delete">Eyða</string>
|
||||
<string name="unlockScreen">Opna Snúningur</string>
|
||||
<string name="noGiftCards">Smelltu á + plús takka til að bæta kort, eða að flytja inn sumir frá ⋮ matseðill fyrst.</string>
|
||||
<string name="note">Athugið</string>
|
||||
<string name="barcodeType">Strikamerkið tegund</string>
|
||||
<string name="cancel">Hætta</string>
|
||||
<string name="noMatchingGiftCards">Vissi ekki að finna neitt. Reyna að breyta leita.</string>
|
||||
<string name="storeName">Nafnið</string>
|
||||
<string name="barcodeNoBarcode">Þetta kort hefur ekki strikamerkið</string>
|
||||
<string name="star">Bæta við eftirlæti</string>
|
||||
<string name="unstar">Fjarlægja frá eftirlæti</string>
|
||||
<string name="save">Sparaðu</string>
|
||||
<string name="edit">Breyta</string>
|
||||
<string name="confirm">Staðfesta</string>
|
||||
<string name="lockScreen">Blokk Snúningur</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="sendLabel">Sendu…</string>
|
||||
<string name="deleteConfirmation">Eyða þetta kort til frambúðar\?</string>
|
||||
<string name="share">Deila</string>
|
||||
<string name="editCardTitle">Breyta Kort</string>
|
||||
<string name="addCardTitle">Bæta Kort</string>
|
||||
<string name="scanCardBarcode">Skanna Kort Strikamerkið</string>
|
||||
</resources>
|
||||
@@ -15,8 +15,6 @@
|
||||
<string name="confirm">Conferma</string>
|
||||
<string name="lockScreen">Blocca rotazione</string>
|
||||
<string name="unlockScreen">Sblocca rotazione</string>
|
||||
<string name="deleteTitle">Rimuovi carta fedeltà</string>
|
||||
<string name="deleteConfirmation">Eliminare questa carta\?</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="copy_to_clipboard">Copia ID negli appunti</string>
|
||||
<string name="share">Condividi</string>
|
||||
@@ -26,7 +24,6 @@
|
||||
<string name="scanCardBarcode">Scansiona il codice carta</string>
|
||||
<string name="cardShortcut">Scorciatoia per la carta</string>
|
||||
<string name="noCardsMessage">Aggiungi prima una carta</string>
|
||||
<string name="barcodeImageDescription">Immagine del codice della carta</string>
|
||||
<string name="noStoreError">Nessun nome inserito</string>
|
||||
<string name="noCardIdError">Nessun codice carta inserito</string>
|
||||
<string name="noCardExistsError">Impossibile trovare la carta</string>
|
||||
@@ -43,14 +40,14 @@
|
||||
<string name="importing">Importazione in corso…</string>
|
||||
<string name="exporting">Esportazione in corso…</string>
|
||||
<string name="noExternalStoragePermissionError">Concedi l\'autorizzazione all\'archiviazione esterna per importare o esportare prima le carte</string>
|
||||
<string name="importOptionFilesystemTitle">Importa dal file system</string>
|
||||
<string name="importOptionFilesystemExplanation">Scegli un file dal file system.</string>
|
||||
<string name="importOptionFilesystemButton">Dal file system</string>
|
||||
<string name="importOptionFilesystemTitle">Importa dall\'archivio</string>
|
||||
<string name="importOptionFilesystemExplanation">Scegli un file dall\'archivio.</string>
|
||||
<string name="importOptionFilesystemButton">Dall\'archivio</string>
|
||||
<string name="importOptionApplicationTitle">Usa un’altra app</string>
|
||||
<string name="importOptionApplicationExplanation">Usa qualsiasi app o il tuo gestore di file preferito per aprire un file.</string>
|
||||
<string name="importOptionApplicationButton">Usa un’altra app</string>
|
||||
<string name="about">Informazioni</string>
|
||||
<string name="app_license">Software libero con copyleft, licenza GPLv3+.</string>
|
||||
<string name="app_license">Software libero con copyleft, licenza GPLv3+</string>
|
||||
<string name="about_title_fmt">Informazioni su <xliff:g id="app_name">%s</xliff:g></string>
|
||||
<string name="debug_version_fmt">Versione: <xliff:g id="version">%s</xliff:g></string>
|
||||
<string name="app_revision_fmt">Informazioni sulla revisione: <xliff:g id="app_revision_url"> %s </xliff:g></string>
|
||||
@@ -70,7 +67,7 @@
|
||||
<string name="exportSuccessful">Dati della carta importati</string>
|
||||
<string name="importSuccessful">Dati della carta importati</string>
|
||||
<string name="app_copyright_old">Basato su Loyalty Card Keychain
|
||||
\ncopyright © 2016–2020 Branden Archer.</string>
|
||||
\ncopyright © 2016–2020 Branden Archer</string>
|
||||
<string name="exportOptionExplanation">I dati verranno scritti in una posizione a tua scelta.</string>
|
||||
<string name="barcodeType">Tipo di codice a barre</string>
|
||||
<string name="unstar">Rimuovi dai preferiti</string>
|
||||
@@ -93,7 +90,7 @@
|
||||
<item quantity="one"><xliff:g>%d</xliff:g> carta</item>
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> carte</item>
|
||||
</plurals>
|
||||
<string name="parsingBalanceFailed"><xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> non sembra un saldo corretto.</string>
|
||||
<string name="parsingBalanceFailed"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g> non sembra un saldo corretto.</string>
|
||||
<string name="points">Punti</string>
|
||||
<string name="currency">Valuta</string>
|
||||
<string name="balance">Saldo</string>
|
||||
@@ -118,17 +115,17 @@
|
||||
\n
|
||||
\nNESSUN DATO VIENE RACCOLTO, cosa che chiunque può confermare dato che la nostra applicazione è un software libero.</string>
|
||||
<string name="privacy_policy">Informativa sulla riservatezza</string>
|
||||
<string name="importVoucherVaultMessage">Trova un file probabilmente chiamato <i>vouchervault.json</i> da importare.
|
||||
\nOppure crealo premendo prima Esporta in Voucher Vault.</string>
|
||||
<string name="importVoucherVaultMessage">Seleziona la tua esportazione <i>vouchervault.json</i> da Voucher Vault da importare.
|
||||
\nCrealo premendo prima Esporta in Voucher Vault.</string>
|
||||
<string name="importVoucherVault">Importa da Voucher Vault</string>
|
||||
<string name="importLoyaltyCardKeychainMessage">Trova un file probabilmente chiamato <i>LoyaltyCardKeychain.csv</i> da importare.
|
||||
\nOppure crealo dal menù Importa/Esporta nel Loyalty Card Keychain premendo prima Esporta.</string>
|
||||
<string name="importLoyaltyCardKeychainMessage">Seleziona la tua esportazione <i>LoyaltyCardKeychain.csv</i> da Loyalty Card Keychain per importarla.
|
||||
\nCreatelo dal menù Importazione/Esportazione in Loyalty Card Keychain premendo prima su Esporta.</string>
|
||||
<string name="importLoyaltyCardKeychain">Importa da Loyalty Card Keychain</string>
|
||||
<string name="importFidmeMessage">Trova un file probabilmente chiamato <i>fidme-export-request-xxxxxx.zip</i> da importare, e poi seleziona i tipi di codice a barre manualmente in seguito.
|
||||
\nOppure crearlo dal tuo profilo FidMe scegliendo Protezione dati e poi premendo Estrai i miei dati prima.</string>
|
||||
<string name="importFidmeMessage">Seleziona la tua esportazione <i>fidme-export-request-xxxxxx.zip</i> da FidMe per importare, e seleziona i tipi di codice a barre manualmente dopo.
|
||||
\nCrearlo dal tuo profilo FidMe scegliendo Protezione Dati e poi premendo Estrai i miei dati prima.</string>
|
||||
<string name="importFidme">Importa da FidMe</string>
|
||||
<string name="importCatimaMessage">Trova un file probabilmente chiamato <i>Catima.csv</i> da importare.
|
||||
\nOppure crealo dal menù Importa/Esporta di un’altra applicazione Catima premendo prima Esporta.</string>
|
||||
<string name="importCatimaMessage">Seleziona la tua esportazione <i>catima.zip</i> da Catima per importarla.
|
||||
\nCrealo dal menù Importazione/Esportazione di un\'altra applicazione Catima premendo prima Esporta.</string>
|
||||
<string name="importCatima">Importa da Catima</string>
|
||||
<string name="setBarcodeId">Imposta il valore del codice a barre</string>
|
||||
<string name="sameAsCardId">Uguale all\'ID della carta</string>
|
||||
@@ -137,13 +134,80 @@
|
||||
<string name="noBarcodeFound">Nessun codice a barre trovato</string>
|
||||
<string name="addFromImage">Seleziona immagine dalla galleria</string>
|
||||
<string name="settings_max_font_size_scale">Dimensione mass. caratteri</string>
|
||||
<string name="unsupportedBarcodeType">Questo tipo di codice a barre non può ancora essere visualizzato. Potrebbe essere supportato in una versione più recente dell\'applicazione.</string>
|
||||
<string name="unsupportedBarcodeType">Questo tipo di codice a barre non può ancora essere visualizzato. Potrebbe essere supportato in una versione successiva dell\'applicazione.</string>
|
||||
<string name="wrongValueForBarcodeType">Il valore non è valido per il tipo di codice a barre selezionato</string>
|
||||
<string name="app_resources">Risorse libre di terze parti: <xliff:g id="app_resources_list"> %s </xliff:g></string>
|
||||
<string name="app_libraries">Librerie libre di terze parti: <xliff:g id="app_libraries_list"> %s </xliff:g></string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Copyright © 2019–<xliff:g>%d</xliff:g> Sylvia van Os.</string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Copyright © 2019–<xliff:g>%d</xliff:g> Sylvia van Os</string>
|
||||
<string name="intent_import_card_from_url_share_multiple_text">Voglio condividere alcune carte con te</string>
|
||||
<string name="copy_to_clipboard_multiple_toast">Numeri delle carte copiati negli appunti</string>
|
||||
<string name="card_ids_copied">Numero/i della carta copiato/i</string>
|
||||
<string name="card_selected">"Selezionata: "</string>
|
||||
<string name="no">No</string>
|
||||
<string name="yes">Sì</string>
|
||||
<string name="updateBarcodeQuestionText">Hai cambiato l\'ID della carta. Vuoi anche aggiornare il codice a barre per usare lo stesso valore\?</string>
|
||||
<string name="updateBarcodeQuestionTitle">Aggiornare il valore del codice a barre\?</string>
|
||||
<string name="takePhoto">Scatta una foto</string>
|
||||
<string name="removeImage">Rimuovi l’immagine</string>
|
||||
<string name="setBackImage">Imposta immagine posteriore</string>
|
||||
<string name="setFrontImage">Imposta immagine frontale</string>
|
||||
<string name="photos">Foto</string>
|
||||
<string name="backImageDescription">Immagine posteriore della carta</string>
|
||||
<string name="frontImageDescription">Immagine frontale della carta</string>
|
||||
<string name="passwordRequired">Si prega di inserire la password</string>
|
||||
<string name="importStocardMessage">Seleziona la tua esportazione <i>***-sync.zip</i> da Stocard per importare.
|
||||
\nOttienilo inviando un\'e-mail a support@stocardapp.com chiedendo un\'esportazione dei tuoi dati.</string>
|
||||
<string name="importStocard">Importa da Stocard</string>
|
||||
<string name="turn_flashlight_off">Spegni la torcia</string>
|
||||
<string name="turn_flashlight_on">Accendi la torcia</string>
|
||||
<string name="failedGeneratingShareURL">Impossibile generare un URL condivisibile. Si prega di segnalarlo.</string>
|
||||
<plurals name="selectedCardCount">
|
||||
<item quantity="one"><xliff:g>%d</xliff:g> carta selezionata</item>
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> carte selezionate</item>
|
||||
</plurals>
|
||||
<string name="deleteTitle">Rimuovi la carta</string>
|
||||
<string name="deleteConfirmation">Eliminare questa carta\?</string>
|
||||
<plurals name="deleteCardsConfirmation">
|
||||
<item quantity="one">Eliminare definitivamente questa scheda <xliff:g>%d</xliff:g>\?</item>
|
||||
<item quantity="other">Eliminare definitivamente queste schede <xliff:g>%d</xliff:g>\?</item>
|
||||
</plurals>
|
||||
<plurals name="deleteCardsTitle">
|
||||
<item quantity="one">Elimina <xliff:g>%d</xliff:g> carta</item>
|
||||
<item quantity="other">Elimina <xliff:g>%d</xliff:g> carte</item>
|
||||
</plurals>
|
||||
<string name="settings_system_locale">Sistema</string>
|
||||
<string name="settings_locale">Lingua</string>
|
||||
<string name="settings_brown_theme">Marrone</string>
|
||||
<string name="settings_grey_theme">Grigio</string>
|
||||
<string name="settings_green_theme">Verde</string>
|
||||
<string name="settings_sky_blue_theme">Azzurro</string>
|
||||
<string name="settings_blue_theme">Blu</string>
|
||||
<string name="settings_violet_theme">Viola</string>
|
||||
<string name="settings_magenta_theme">Magenta</string>
|
||||
<string name="settings_pink_theme">Rosa</string>
|
||||
<string name="settings_catima_theme">Catima</string>
|
||||
<string name="settings_theme_color">Colore del tema</string>
|
||||
<string name="app_contributors">Reso possibile da: <xliff:g id="app_contributors">%s</xliff:g></string>
|
||||
<string name="noGroupCards">Questo gruppo non contiene carte</string>
|
||||
<string name="toggleMoreInfo">Attiva/disattiva la visualizzazione di altre informazioni</string>
|
||||
<string name="barcodeImageDescriptionWithType">Immagine del codice a barre della carta del tipo <xliff:g>%s</xliff:g></string>
|
||||
<string name="swipeToSwitchImages">Scorri o premi a lungo per cambiare immagine</string>
|
||||
<string name="sort_by">Ordina per</string>
|
||||
<string name="reverse">Inverti</string>
|
||||
<string name="sort_by_balance">Saldo</string>
|
||||
<string name="sort_by_expiry">Scadenza</string>
|
||||
<string name="sort_by_most_recently_used">Usate più di recente</string>
|
||||
<string name="sort_by_name">Nome</string>
|
||||
<string name="sort">Ordina</string>
|
||||
<string name="license">Licenza</string>
|
||||
<string name="source_repository">Repository fonti</string>
|
||||
<string name="on_github">su GitHub</string>
|
||||
<string name="exportPasswordHint">Inserisci la password</string>
|
||||
<string name="on_google_play">su Google Play</string>
|
||||
<string name="exportPassword">Imposta una password per proteggere la tua esportazione (opzionale)</string>
|
||||
<string name="version_history">Cronologia delle versioni</string>
|
||||
<string name="credits">Crediti</string>
|
||||
<string name="help_translate_this_app">Aiuta a tradurre questa app</string>
|
||||
<string name="and_data_usage">e utilizzo dei dati</string>
|
||||
<string name="rate_this_app">Valuta questa applicazione</string>
|
||||
<string name="report_error">Segnala un errore</string>
|
||||
</resources>
|
||||
@@ -3,15 +3,15 @@
|
||||
<string name="wrongValueForBarcodeType">選択したバーコード形式ではこの番号は使用できません</string>
|
||||
<string name="unsupportedBarcodeType">このバーコード形式は表示できません。将来のアップデートにより対応するかもしれません。</string>
|
||||
<string name="setBarcodeId">バーコード番号を設定</string>
|
||||
<string name="importLoyaltyCardKeychainMessage">インポートするには <i>LoyaltyCardKeychain.csv</i> のような名前のファイルを選択してください。
|
||||
\nまたは、あらかじめ Loyalty Card Keychainアプリからファイルをエクスポートしてください。</string>
|
||||
<string name="importLoyaltyCardKeychain">Loyalty Card Keychain からインポート</string>
|
||||
<string name="importFidmeMessage">インポートするには <i>fidme-export-request-xxxxxx.zip</i> のような名前のファイルを選択してください。そのあと手動でバーコード形式を選択してください。
|
||||
\nまたは 、あらかじめFidMeからファイルを作成してください。</string>
|
||||
<string name="importFidme">FidMe からインポート</string>
|
||||
<string name="importCatimaMessage">インポートするには <i>Catima.csv</i> のような名前のファイルを選択してください。
|
||||
\nまたは、あらかじめ他のCatima アプリからファイルをエクスポートしてください。</string>
|
||||
<string name="importCatima">Catima からインポート</string>
|
||||
<string name="importLoyaltyCardKeychainMessage">インポートするにはLoyalty Card Keychainでエクスポートした <i>LoyaltyCardKeychain.csv</i>ファイルを選択してください。
|
||||
\nファイルがない場合、 Loyalty Card Keychainアプリからファイルをエクスポートしてください。</string>
|
||||
<string name="importLoyaltyCardKeychain">Loyalty Card Keychainからインポート</string>
|
||||
<string name="importFidmeMessage">インポートするにはFindMeでエクスポートした <i>fidme-export-request-xxxxxx.zip</i>ファイルを選択してください。そのあと手動でバーコード形式を選択してください。
|
||||
\nファイルがない場合、FidMeでファイルを作成してください。</string>
|
||||
<string name="importFidme">FidMeからインポート</string>
|
||||
<string name="importCatimaMessage">インポートするにはCatimaでエクスポートした<i>Catima.zip</i>ファイルを選択してください。
|
||||
\nファイルがない場合、他のCatimaアプリでファイルをエクスポートしてください。</string>
|
||||
<string name="importCatima">Catimaからインポート</string>
|
||||
<string name="accept">承認</string>
|
||||
<string name="privacy_policy_popup_text">プライバシーポリシーの案内:
|
||||
\n
|
||||
@@ -35,7 +35,7 @@
|
||||
<string name="card">カード</string>
|
||||
<string name="balancePoints"><xliff:g>%s</xliff:g> ポイント</string>
|
||||
<string name="balanceSentence">残高: <xliff:g>%s</xliff:g></string>
|
||||
<string name="expiryStateSentenceExpired">期限: <xliff:g>%s</xliff:g></string>
|
||||
<string name="expiryStateSentenceExpired">期限切れ: <xliff:g>%s</xliff:g></string>
|
||||
<string name="expiryStateSentence">期限: <xliff:g>%s</xliff:g></string>
|
||||
<string name="groupsList">グループ: <xliff:g>%s</xliff:g></string>
|
||||
<string name="addFromImage">ギャラリーから画像を選択</string>
|
||||
@@ -55,7 +55,7 @@
|
||||
<string name="enter_group_name">グループ名を入力</string>
|
||||
<string name="exportSuccessful">カードのデータがエクスポートされました</string>
|
||||
<string name="importSuccessful">カードのデータがインポートされました</string>
|
||||
<string name="intent_import_card_from_url_share_text">あなたとカードを共有したいです</string>
|
||||
<string name="intent_import_card_from_url_share_text">カード共有をしましょう</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">バーコード表示中は画面をロックしない</string>
|
||||
<string name="settings_keep_screen_on">バーコード表示中は画面を消灯しない</string>
|
||||
<string name="settings_lock_barcode_orientation">バーコード表示画面を自動回転しない</string>
|
||||
@@ -65,22 +65,22 @@
|
||||
<string name="settings_light_theme">ライト</string>
|
||||
<string name="settings_system_theme">システムに従う</string>
|
||||
<string name="settings_theme">テーマ</string>
|
||||
<string name="settings_category_title_ui">ユーザーインターフェイス</string>
|
||||
<string name="settings_category_title_ui">外観</string>
|
||||
<string name="settings">設定</string>
|
||||
<string name="starImage">お気に入りのスター</string>
|
||||
<string name="thumbnailDescription">カードのサムネイル</string>
|
||||
<string name="copy_to_clipboard_toast">カード番号をクリップボードにコピーしました</string>
|
||||
<string name="enterBarcodeInstructions">カード番号を入力し、バーコード形式を選択してください。</string>
|
||||
<string name="selectBarcodeTitle">バーコード選択</string>
|
||||
<string name="app_libraries">Third-party libre libraries: <xliff:g id="app_libraries_list">%s</xliff:g></string>
|
||||
<string name="app_libraries">Libre third-party libraries: <xliff:g id="app_libraries_list">%s</xliff:g></string>
|
||||
<string name="app_revision_fmt">Revision Info: <xliff:g id="app_revision_url">%s</xliff:g></string>
|
||||
<string name="debug_version_fmt">Version: <xliff:g id="version">%s</xliff:g></string>
|
||||
<string name="about_title_fmt">About <xliff:g id="app_name">%s</xliff:g></string>
|
||||
<string name="app_license">Copylefted libre software, licensed GPLv3+.</string>
|
||||
<string name="app_license">Copylefted libre software, licensed GPLv3+</string>
|
||||
<string name="app_copyright_old">Based on Loyalty Card Keychain
|
||||
\ncopyright © 2016–2020 Branden Archer.</string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Copyright © 2019–<xliff:g>%d</xliff:g> Sylvia van Os.</string>
|
||||
<string name="app_resources">Third-party libre resources: <xliff:g id="app_resources_list">%s</xliff:g></string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Copyright © 2019–<xliff:g>%d</xliff:g> Sylvia van Os</string>
|
||||
<string name="app_resources">Libre third-party resources: <xliff:g id="app_resources_list">%s</xliff:g></string>
|
||||
<string name="about">Catimaについて</string>
|
||||
<string name="importOptionApplicationButton">外部のアプリを使う</string>
|
||||
<string name="importOptionApplicationExplanation">任意のアプリやお気に入りのファイルマネージャーからファイルを開く。</string>
|
||||
@@ -93,24 +93,23 @@
|
||||
<string name="exporting">エクスポート中…</string>
|
||||
<string name="importing">インポート中…</string>
|
||||
<string name="exportFailed">カードをエクスポートできませんでした</string>
|
||||
<string name="exportFailedTitle">エクスポート失敗</string>
|
||||
<string name="exportFailedTitle">エクスポートに失敗しました</string>
|
||||
<string name="exportSuccessfulTitle">エクスポートしました</string>
|
||||
<string name="sameAsCardId">カードの表記と同一</string>
|
||||
<string name="sameAsCardId">カード番号に合わせる</string>
|
||||
<string name="barcodeId">バーコード番号</string>
|
||||
<string name="importVoucherVaultMessage">インポートするには <i>vouchervault.json</i> のような名前のファイルを選択してください。
|
||||
\nまたは、あらかじめVoucher Vault でファイルを作成してください。</string>
|
||||
<string name="importVoucherVault">Voucher Vault からインポート</string>
|
||||
<string name="importVoucherVaultMessage">Voucher Vaultでエクスポートした <i>vouchervault.json</i>ファイルを選択してください。
|
||||
\nファイルがない場合、Voucher Vaultでファイルをエクスポートしてください。</string>
|
||||
<string name="importVoucherVault">Voucher Vaultからインポート</string>
|
||||
<string name="importFailed">カードをインポートできません</string>
|
||||
<string name="importFailedTitle">インポート失敗</string>
|
||||
<string name="importFailedTitle">インポートに失敗しました</string>
|
||||
<string name="importSuccessfulTitle">インポートしました</string>
|
||||
<string name="importExportHelp">カードをバックアップすると、カードを他のデバイスに移すことができます。</string>
|
||||
<string name="importExportHelp">カードをバックアップすると、他のデバイスにカードを移すことができます。</string>
|
||||
<string name="exportName">エクスポート</string>
|
||||
<string name="importExport">インポート/エクスポート</string>
|
||||
<string name="failedParsingImportUriError">インポートURIを解析できません</string>
|
||||
<string name="noCardExistsError">カードが見つかりません</string>
|
||||
<string name="noCardIdError">カード番号が入力されていません</string>
|
||||
<string name="noStoreError">名前が入力されていません</string>
|
||||
<string name="barcodeImageDescription">バーコードの画像</string>
|
||||
<string name="noCardsMessage">カードを追加</string>
|
||||
<string name="cardShortcut">カードのショートカット</string>
|
||||
<string name="scanCardBarcode">カードのバーコードをスキャン</string>
|
||||
@@ -118,22 +117,20 @@
|
||||
<string name="editCardTitle">カードの編集</string>
|
||||
<string name="sendLabel">送信先を選択…</string>
|
||||
<string name="share">共有</string>
|
||||
<string name="copy_to_clipboard">カード番号をクリップボードにコピー</string>
|
||||
<string name="ok">はい</string>
|
||||
<string name="deleteConfirmation">選択したカードを削除しますか?</string>
|
||||
<string name="deleteTitle">カードを削除</string>
|
||||
<string name="copy_to_clipboard">カード番号をクリップボードにコピーする</string>
|
||||
<string name="ok">確定</string>
|
||||
<string name="unlockScreen">自動回転を無効にしない</string>
|
||||
<string name="lockScreen">自動回転を無効にする</string>
|
||||
<string name="confirm">確認</string>
|
||||
<string name="delete">削除</string>
|
||||
<string name="edit">編集</string>
|
||||
<string name="save">保存</string>
|
||||
<string name="cancel">取り消し</string>
|
||||
<string name="cancel">取消</string>
|
||||
<string name="unstar">お気に入りから削除</string>
|
||||
<string name="star">お気に入りに追加</string>
|
||||
<string name="noBarcode">バーコードなし</string>
|
||||
<string name="barcodeNoBarcode">バーコード指定なし</string>
|
||||
<string name="barcodeType">形式</string>
|
||||
<string name="barcodeType">バーコード形式</string>
|
||||
<string name="cardId">カード番号</string>
|
||||
<string name="note">メモ</string>
|
||||
<string name="storeName">名前</string>
|
||||
@@ -141,8 +138,72 @@
|
||||
<string name="noGiftCards">まず初めに+ボタンを押してカードを追加するか、メニューから以前のカードをインポートしてください。</string>
|
||||
<string name="action_add">追加</string>
|
||||
<string name="action_search">検索</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="copy_to_clipboard_multiple_toast">カード番号をクリップボードにコピーしました</string>
|
||||
<string name="card_ids_copied">コピーしたカード</string>
|
||||
<string name="card_selected">"選択: "</string>
|
||||
<string name="card_ids_copied">カード番号をコピーしました</string>
|
||||
<string name="turn_flashlight_off">ライトをオフにする</string>
|
||||
<string name="turn_flashlight_on">ライトをオンにする</string>
|
||||
<string name="failedGeneratingShareURL">共有URLの生成を生成できませんでした。バグを報告してください。</string>
|
||||
<string name="passwordRequired">パスワードを入力してください</string>
|
||||
<string name="no">いいえ</string>
|
||||
<string name="yes">はい</string>
|
||||
<string name="updateBarcodeQuestionText">カード番号を変更しました。バーコード番号も同じ値に変更しますか?</string>
|
||||
<string name="updateBarcodeQuestionTitle">バーコードの番号を変更しますか?</string>
|
||||
<string name="takePhoto">写真を撮影する</string>
|
||||
<string name="removeImage">画像を削除</string>
|
||||
<string name="setBackImage">裏面の画像を設定</string>
|
||||
<string name="setFrontImage">オモテ面の画像を設定</string>
|
||||
<string name="photos">フォト</string>
|
||||
<string name="backImageDescription">カードの裏面</string>
|
||||
<string name="frontImageDescription">カードのオモテ面</string>
|
||||
<string name="importStocardMessage">Stocardでエクスポートした<i>***-sync.zip</i>ファイルを選択してください。
|
||||
\nファイルがない場合、e-mailing support@stocardapp.comにデータのエクスポートを要求してください。</string>
|
||||
<string name="importStocard">Stocardからインポート</string>
|
||||
<plurals name="selectedCardCount">
|
||||
<item quantity="other">選択済み: <xliff:g>%d</xliff:g> 枚</item>
|
||||
</plurals>
|
||||
<string name="deleteConfirmation">このカードを削除しますか?</string>
|
||||
<string name="deleteTitle">カードの削除</string>
|
||||
<plurals name="deleteCardsConfirmation">
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> 枚のカードを削除しますか?</item>
|
||||
</plurals>
|
||||
<plurals name="deleteCardsTitle">
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> 枚のカードの削除</item>
|
||||
</plurals>
|
||||
<string name="barcodeImageDescriptionWithType">バーコード形式の画像 <xliff:g>%s</xliff:g></string>
|
||||
<string name="toggleMoreInfo">詳細を表示する</string>
|
||||
<string name="app_contributors">Made possible by: <xliff:g id="app_contributors">%s</xliff:g></string>
|
||||
<string name="settings_brown_theme">Brown</string>
|
||||
<string name="settings_grey_theme">Gray</string>
|
||||
<string name="settings_green_theme">Green</string>
|
||||
<string name="settings_sky_blue_theme">Sky Blue</string>
|
||||
<string name="settings_blue_theme">Blue</string>
|
||||
<string name="settings_violet_theme">Violet</string>
|
||||
<string name="settings_magenta_theme">Magenta</string>
|
||||
<string name="settings_pink_theme">Pink</string>
|
||||
<string name="settings_catima_theme">Catima</string>
|
||||
<string name="settings_theme_color">テーマカラー</string>
|
||||
<string name="settings_system_locale">システムに従う</string>
|
||||
<string name="settings_locale">言語</string>
|
||||
<string name="noGroupCards">このグループにはカードがありません</string>
|
||||
<string name="swipeToSwitchImages">画像を切り替えるには長押し、またはスワイプ</string>
|
||||
<string name="sort_by">並び替え</string>
|
||||
<string name="sort_by_balance">残高</string>
|
||||
<string name="sort_by_expiry">期限</string>
|
||||
<string name="sort_by_most_recently_used">最近使用したカード</string>
|
||||
<string name="sort_by_name">名前</string>
|
||||
<string name="sort">並び替え</string>
|
||||
<string name="rate_this_app">このアプリを評価する</string>
|
||||
<string name="on_github">GitHub</string>
|
||||
<string name="source_repository">ソースリポジトリ</string>
|
||||
<string name="exportPassword">パスワードを設定してエクスポートしたファイルを保護する(任意)</string>
|
||||
<string name="exportPasswordHint">パスワードを入力してください</string>
|
||||
<string name="version_history">更新履歴</string>
|
||||
<string name="credits">貢献者</string>
|
||||
<string name="help_translate_this_app">このアプリの翻訳を手伝う</string>
|
||||
<string name="license">ライセンス</string>
|
||||
<string name="on_google_play">Google Play</string>
|
||||
<string name="report_error">エラーを報告する</string>
|
||||
<string name="reverse">逆順</string>
|
||||
<string name="and_data_usage">and data usage</string>
|
||||
</resources>
|
||||
@@ -15,7 +15,7 @@
|
||||
<string name="settings_category_title_ui">사용자 인터페이스</string>
|
||||
<string name="settings">설정</string>
|
||||
<string name="enterBarcodeInstructions">카드 ID를 입력하고 카드에서 사용하는 바코드 이미지를 선택하세요. 바코드를 사용하지 않는 경우 “이 카드는 바코드가 없음”을 선택하세요.</string>
|
||||
<string name="app_revision_fmt">리비전 정보: <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\" id=\"app_revision_url\">%s</xliff:g></string>
|
||||
<string name="app_revision_fmt">리비전 정보: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" id="app_revision_url">%s</xliff:g></string>
|
||||
<string name="selectBarcodeTitle">바코드 선택</string>
|
||||
<string name="about">정보</string>
|
||||
<string name="exporting">내보내는 중…</string>
|
||||
@@ -31,8 +31,6 @@
|
||||
<string name="share">공유</string>
|
||||
<string name="copy_to_clipboard">ID를 클립보드에 복사</string>
|
||||
<string name="ok">확인</string>
|
||||
<string name="deleteConfirmation">정말 이 카드를 삭제하시겠습니까\?</string>
|
||||
<string name="deleteTitle">카드 제거</string>
|
||||
<string name="unlockScreen">회전 잠금 해제</string>
|
||||
<string name="lockScreen">회전 잠금</string>
|
||||
<string name="confirm">확인</string>
|
||||
@@ -65,7 +63,6 @@
|
||||
<string name="importFailedTitle">가져오기 실패</string>
|
||||
<string name="importSuccessfulTitle">가져오기 완료</string>
|
||||
<string name="noCardIdError">카드 ID를 입력하지 않음</string>
|
||||
<string name="barcodeImageDescription">카드 바코드 이미지</string>
|
||||
<string name="storeName">매장</string>
|
||||
<string name="thumbnailDescription">카드 섬네일</string>
|
||||
<string name="importOptionApplicationButton">외부 앱 사용</string>
|
||||
@@ -78,4 +75,6 @@
|
||||
<string name="starImage">즐겨찾기 별</string>
|
||||
<string name="settings_display_barcode_max_brightness">바코드를 표시할 때 화면 밝기 높이기</string>
|
||||
<string name="barcode">바코드</string>
|
||||
</resources>
|
||||
<string name="deleteConfirmation">정말 이 카드를 삭제하시겠습니까\?</string>
|
||||
<string name="deleteTitle">카드 제거</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
<resources
|
||||
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
|
||||
<?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">
|
||||
<string name="action_add">Pridėti</string>
|
||||
|
||||
<string name="noGiftCards">Šiuo metu neturite nė vienos įvestos lojalumo kortelės. Paspauskite "+" (pliuso) pliuso mygtuką, kad pradėtumėte.\n\nLoyalty Card Locker leidžia Jums visada nešiotis lojalumo kortelių informaciją savo telefone ar planšetėje, taip jos visada pasiekiamos.</string>
|
||||
<string name="storeName">Parduotuvė</string>
|
||||
<string name="noGiftCards">Norėdami pridėti kortelę, spustelėkite mygtuką + plius arba pirmiausia importuokite kortelę iš ⋮ meniu.</string>
|
||||
<string name="storeName">Pavadinimas</string>
|
||||
<string name="note">Užrašas</string>
|
||||
<string name="cardId">Kortelės ID</string>
|
||||
<string name="cancel">Atšaukti</string>
|
||||
@@ -12,30 +10,208 @@
|
||||
<string name="edit">Redaguoti</string>
|
||||
<string name="delete">Ištrinti</string>
|
||||
<string name="confirm">Patvirtinti</string>
|
||||
<string name="deleteTitle">Panaikinti lojalumo kortelę</string>
|
||||
<string name="deleteConfirmation">Prašome patvirtinti jog Jūs norite panaikinti šią lojalumo kortelę.</string>
|
||||
<string name="ok">Gerai</string>
|
||||
<string name="copy_to_clipboard">Nukopijuoti ID į iškarpinę</string>
|
||||
<string name="editCardTitle">Redaguoti lojalumo kortelę</string>
|
||||
<string name="addCardTitle">Pridėti lojalumo kortelę</string>
|
||||
<string name="scanCardBarcode">Nuskanuokite kortelės brūkšninį kodą</string>
|
||||
<string name="barcodeImageDescription">Kortelės brūkšninio kodo paveikslėlis</string>
|
||||
|
||||
<string name="noStoreError">Parduotuvė neįvesta</string>
|
||||
<string name="scanCardBarcode">Skenuoti kortelės brūkšninį kodą</string>
|
||||
<string name="noStoreError">Neįvestas pavadinimas</string>
|
||||
<string name="noCardIdError">Neįvestas kortelės ID</string>
|
||||
<string name="importExport">Importuoti/Exportuoti</string>
|
||||
<string name="exportName">Exportuoti</string>
|
||||
<string name="importFailed">Nepavyko importuoti</string>
|
||||
<string name="exportFailed">Nepavyko eksportuoti</string>
|
||||
<string name="importing">Importuoja…</string>
|
||||
<string name="exporting">Eksportuoja…</string>
|
||||
<string name="noExternalStoragePermissionError">Negalima importuoti/eksportuoti kortelių be išorinės atminties leidimo</string>
|
||||
<string name="importFailed">Nepavyko importuoti kortelių</string>
|
||||
<string name="exportFailed">Nepavyko eksportuoti kortelių</string>
|
||||
<string name="importing">Importuoja…</string>
|
||||
<string name="exporting">Eksportuoja…</string>
|
||||
<string name="noExternalStoragePermissionError">Pirmiausia suteikite išorinės saugyklos leidimą, kad galėtumėte importuoti arba eksportuoti korteles</string>
|
||||
<string name="about">Apie</string>
|
||||
<string name="app_license">Licenzijuota pagal GPLv3.</string>
|
||||
<string name="app_license">Copylefted libre programinė įranga, licencijuota GPLv3+</string>
|
||||
<string name="about_title_fmt">Apie <xliff:g id="app_name">%s</xliff:g></string>
|
||||
<string name="debug_version_fmt">Versija: <xliff:g id="version">%s</xliff:g></string>
|
||||
<string name="app_revision_fmt">Revizijos informacija: <xliff:g id="app_revision_url">%s</xliff:g></string>
|
||||
<string name="app_revision_fmt">Revizijos info: <xliff:g id="app_revision_url">%s</xliff:g></string>
|
||||
<string name="selectBarcodeTitle">Pasirinkite brūkšninį kodą</string>
|
||||
<string name="copy_to_clipboard_toast">Kortelės ID nukopijuota į iškarpinę</string>
|
||||
|
||||
</resources>
|
||||
<string name="card_ids_copied">Nukopijuotos kortelės ID</string>
|
||||
<string name="noCardsMessage">Pirmiausia pridėkite kortelę</string>
|
||||
<string name="sendLabel">Siųsti…</string>
|
||||
<string name="unlockScreen">Atblokuoti pasukimą</string>
|
||||
<string name="lockScreen">Blokuoti pasukimą</string>
|
||||
<string name="unstar">Pašalinti iš mėgstamiausių</string>
|
||||
<string name="star">Pridėti prie mėgstamiausių</string>
|
||||
<string name="noBarcode">Nėra brūkšninio kodo</string>
|
||||
<string name="barcodeNoBarcode">Ši kortelė neturi brūkšninio kodo</string>
|
||||
<string name="barcodeType">Brūkšninio kodo tipas</string>
|
||||
<string name="noMatchingGiftCards">Nieko nerasta. Pabandykite pakeisti paiešką.</string>
|
||||
<string name="action_search">Ieškoti</string>
|
||||
<string name="cardShortcut">Kortelės sparčioji nuoroda</string>
|
||||
<string name="importVoucherVaultMessage">Pasirinkite savo <i>vouchervault.json</i> eksportą iš Voucher Vault, kurį norite importuoti.
|
||||
\nSukurkite jį pirmiausia paspausdami Eksportuoti Voucher Vault.</string>
|
||||
<string name="importVoucherVault">Importuoti iš Voucher Vault</string>
|
||||
<string name="importLoyaltyCardKeychainMessage">Pasirinkite savo <i> LoyaltyCardKeychain.csv</i> eksportą iš Loyalty Card Keychain, kurį norite importuoti.
|
||||
\nSukurkite jį iš Loyalty Card Keychain meniu Importavimas/Eksportavimas, pirmiausia paspausdami Eksportuoti.</string>
|
||||
<string name="importLoyaltyCardKeychain">Importuoti iš Loyalty Card Keychain</string>
|
||||
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
|
||||
<string name="parsingBalanceFailed">Panašu, kad <xliff:g> %s </xliff:g> reikšmė nėra tinkama.</string>
|
||||
<string name="moveBarcodeToCenterOfScreen">Centruoti brūkšninį kodą ekrane</string>
|
||||
<string name="moveBarcodeToTopOfScreen">Perkelti brūkšninį kodą į ekrano viršų</string>
|
||||
<string name="settings_display_barcode_max_brightness">Šviesinti brūkšninio kodo rodinį</string>
|
||||
<string name="failedParsingImportUriError">Nepavyko iššifruoti importo URI</string>
|
||||
<string name="turn_flashlight_off">Išjunkti žibintuvėlį</string>
|
||||
<string name="turn_flashlight_on">Įjunkti žibintuvėlį</string>
|
||||
<string name="failedGeneratingShareURL">Nepavyko sugeneruoti bendrinamo URL. Praneškite apie tai.</string>
|
||||
<string name="passwordRequired">Įveskite slaptažodį</string>
|
||||
<string name="no">Ne</string>
|
||||
<string name="yes">Taip</string>
|
||||
<string name="updateBarcodeQuestionText">Pakeitėte kortelės ID. Ar norite atnaujinti ir brūkšninį kodą, kad būtų naudojama ta pati reikšmė\?</string>
|
||||
<string name="updateBarcodeQuestionTitle">Atnaujinti brūkšninio kodo reikšmę\?</string>
|
||||
<string name="takePhoto">Nufotografuoti</string>
|
||||
<string name="removeImage">Pašalinti vaizdą</string>
|
||||
<string name="setBackImage">Nustatyti galinį vaizdą</string>
|
||||
<string name="setFrontImage">Nustatyti priekinį vaizdą</string>
|
||||
<string name="photos">Nuotraukos</string>
|
||||
<string name="backImageDescription">Kortelės galinis vaizdas</string>
|
||||
<string name="frontImageDescription">Kortelės priekinis vaizdas</string>
|
||||
<string name="intent_import_card_from_url_share_multiple_text">Noriu su jumis pasidalyti keliomis kortelėmis</string>
|
||||
<string name="copy_to_clipboard_multiple_toast">Kortelės ID nukopijuotas į iškarpinę</string>
|
||||
<string name="wrongValueForBarcodeType">Vertė netinkama pasirinktam brūkšninio kodo tipui</string>
|
||||
<string name="unsupportedBarcodeType">Šio brūkšninio kodo tipo dar negalima rodyti. Galbūt jis bus palaikomas vėlesnėje programėlės versijoje.</string>
|
||||
<string name="setBarcodeId">Nustatyti brūkšninio kodo reikšmę</string>
|
||||
<string name="sameAsCardId">Tokia pat kaip kortelės ID</string>
|
||||
<string name="barcodeId">Brūkšninio kodo reikšmė</string>
|
||||
<string name="importStocardMessage">Pasirinkite <i>***-sync.zip</i> eksportą iš Stocard, kad galėtumėte importuoti.
|
||||
\nGaukite susisiekę el. paštu support@stocardapp.com, prašydami eksportuoti jūsų duomenis.</string>
|
||||
<string name="importStocard">Importuoti iš Stocard</string>
|
||||
<string name="importFidmeMessage">Pasirinkite <i>fidme-export-request-xxxxxx.zip</i> eksportą iš FidMe, kurį norite importuoti, ir po to brūkšninių kodų tipus pasirinkite rankiniu būdu.
|
||||
\nSukurkite jį iš savo FidMe profilio, pasirinkę Duomenų apsauga ir pirmiausia paspaudę Išgauti mano duomenis.</string>
|
||||
<string name="importFidme">Importuoti iš FidMe</string>
|
||||
<string name="importCatimaMessage">Norėdami importuoti, pasirinkite savo <i> catima.zip </i> eksportavimą iš Catima.
|
||||
\nSukurkite ją iš kitos Catima programos importavimo / eksportavimo meniu, pirmiausia ten paspausdami Eksportuoti.</string>
|
||||
<string name="importCatima">Importuoti iš Catima</string>
|
||||
<string name="accept">Priimti</string>
|
||||
<string name="privacy_policy_popup_text">Privatumo politikos pranešimas (kurio reikalaujama kai kuriose programėlių parduotuvėse):
|
||||
\n
|
||||
\nJOKIE DUOMENYS NĖRA RENKAMI, o tai gali patvirtinti bet kas, nes mūsų programėlė yra libre programinė įranga.</string>
|
||||
<string name="privacy_policy">Privatumo politika</string>
|
||||
<string name="chooseImportType">Importuoti duomenis iš\?</string>
|
||||
<string name="points">Taškai</string>
|
||||
<string name="currency">Valiuta</string>
|
||||
<string name="balance">Balansas</string>
|
||||
<string name="errorReadingImage">Nepavyko nuskaityti paveikslėlio</string>
|
||||
<string name="noBarcodeFound">Nerastas joks brūkšninis kodas</string>
|
||||
<string name="chooseExpiryDate">Pasirinkite galiojimo datą</string>
|
||||
<string name="never">Niekada</string>
|
||||
<string name="expiryDate">Galiojimo data</string>
|
||||
<string name="editBarcode">Redaguoti brūkšninį kodą</string>
|
||||
<string name="barcode">Brūkšninis kodas</string>
|
||||
<string name="card">Kortelė</string>
|
||||
<string name="balancePoints"><xliff:g>%s</xliff:g> taškai</string>
|
||||
<string name="balanceSentence">Balansas: <xliff:g>%s</xliff:g></string>
|
||||
<string name="expiryStateSentenceExpired">Galiojimas baigėsi: <xliff:g>%s</xliff:g></string>
|
||||
<string name="expiryStateSentence">Nustoja galioti: <xliff:g>%s</xliff:g></string>
|
||||
<string name="groupsList">Grupės: <xliff:g> %s </xliff:g></string>
|
||||
<string name="addFromImage">Pasirinkti vaizdą iš galerijos</string>
|
||||
<string name="addManually">Rankiniu būdu įvesti kortelės ID</string>
|
||||
<string name="leaveWithoutSaveConfirmation">Išeiti neišsaugojus\?</string>
|
||||
<string name="leaveWithoutSaveTitle">Išeiti</string>
|
||||
<string name="moveDown">Judėti žemyn</string>
|
||||
<string name="moveUp">Judėti aukštyn</string>
|
||||
<string name="failedOpeningFileManager">Pirmiausia įdiekite failų tvarkyklę.</string>
|
||||
<string name="deleteConfirmationGroup">Ištrinti grupę\?</string>
|
||||
<string name="all">Visos</string>
|
||||
<plurals name="groupCardCount">
|
||||
<item quantity="one"><xliff:g>%d</xliff:g> kortelę</item>
|
||||
<item quantity="few"><xliff:g>%d</xliff:g> kortelės</item>
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> kortelių</item>
|
||||
</plurals>
|
||||
<string name="noGroups">Spustelėkite + pliuso mygtuką, kad pirmiausia pridėtumėte grupes kategorizavimui.</string>
|
||||
<string name="groups">Grupės</string>
|
||||
<string name="enter_group_name">Įvesti grupės pavadinimą</string>
|
||||
<string name="exportSuccessful">Kortelės duomenys eksportuoti</string>
|
||||
<string name="importSuccessful">Kortelės duomenys importuoti</string>
|
||||
<string name="intent_import_card_from_url_share_text">Noriu pasidalyti su jumis kortele</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Neleisti užrakinti ekrano</string>
|
||||
<string name="settings_keep_screen_on">Laikyti ekraną įjungtą</string>
|
||||
<string name="settings_lock_barcode_orientation">Užrakinti brūkšninio kodo orientaciją</string>
|
||||
<string name="settings_max_font_size_scale">Didžiausias šrifto dydis</string>
|
||||
<string name="settings_dark_theme">Tamsi</string>
|
||||
<string name="settings_light_theme">Šviesi</string>
|
||||
<string name="settings_system_theme">Sistema</string>
|
||||
<string name="settings_theme">Tema</string>
|
||||
<string name="settings_category_title_ui">Vartotojo sąsaja</string>
|
||||
<string name="settings">Nustatymai</string>
|
||||
<string name="starImage">Mėgstamiausia žvaigždė</string>
|
||||
<string name="thumbnailDescription">Kortelės miniatiūra</string>
|
||||
<string name="enterBarcodeInstructions">Įveskite kortelės ID ir toliau pasirinkite jos brūkšninio kodo tipą arba \"Ši kortelė neturi brūkšninio kodo\".</string>
|
||||
<string name="app_resources">Libre trečiųjų šalių ištekliai: <xliff:g id="app_resources_list">%s</xliff:g></string>
|
||||
<string name="app_libraries">Libre trečiųjų šalių bibliotekos: <xliff:g id="app_libraries_list">%s</xliff:g></string>
|
||||
<string name="app_copyright_old">Paremta Loyalty Card Keychain
|
||||
\nautorinės teisės © 2016–2020 Branden Archer</string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Autorinės teisės © 2019–<xliff:g>%d</xliff:g> Sylvia van Os</string>
|
||||
<string name="importOptionApplicationButton">Naudoti kitą programą</string>
|
||||
<string name="importOptionApplicationExplanation">Norėdami atidaryti failą, naudokite bet kurią programą arba mėgstamą failų tvarkyklę.</string>
|
||||
<string name="importOptionApplicationTitle">Naudoti kitą programą</string>
|
||||
<string name="importOptionFilesystemButton">Iš failų sistemos</string>
|
||||
<string name="importOptionFilesystemExplanation">Pasirinkite konkretų failą iš failų sistemos.</string>
|
||||
<string name="importOptionFilesystemTitle">Importuoti iš failų sistemos</string>
|
||||
<string name="exportOptionExplanation">Duomenys bus įrašyti į jūsų pasirinktą vietą.</string>
|
||||
<string name="exportFailedTitle">Eksportuoti nepavyko</string>
|
||||
<string name="exportSuccessfulTitle">Eksportuota</string>
|
||||
<string name="importFailedTitle">Importuoti nepavyko</string>
|
||||
<string name="importSuccessfulTitle">Importuota</string>
|
||||
<string name="importExportHelp">Atsarginių kopijų darymas leidžia perkelti korteles į kitą įrenginį.</string>
|
||||
<string name="noCardExistsError">Nepavyko rasti kortelės</string>
|
||||
<string name="share">Dalintis</string>
|
||||
<plurals name="selectedCardCount">
|
||||
<item quantity="one">Pasirinkta: <xliff:g>%d</xliff:g> kortelė</item>
|
||||
<item quantity="few">Pasirinktos: <xliff:g>%d</xliff:g> kortelės</item>
|
||||
<item quantity="other">Pasirinkta: <xliff:g>%d</xliff:g> kortelių</item>
|
||||
</plurals>
|
||||
<string name="deleteTitle">Panaikinti lojalumo kortelę</string>
|
||||
<string name="deleteConfirmation">Ištrinti šią kortelę\?</string>
|
||||
<plurals name="deleteCardsConfirmation">
|
||||
<item quantity="one">Ištrinti šią <xliff:g>%d</xliff:g> kortelę visam laikui\?</item>
|
||||
<item quantity="few">Ištrinti šias <xliff:g>%d</xliff:g> korteles visam laikui\?</item>
|
||||
<item quantity="other">Ištrinti šias <xliff:g>%d</xliff:g> kortelių visam laikui\?</item>
|
||||
</plurals>
|
||||
<plurals name="deleteCardsTitle">
|
||||
<item quantity="one">Ištrinti <xliff:g>%d</xliff:g> kortelę</item>
|
||||
<item quantity="few">Ištrinti <xliff:g>%d</xliff:g> korteles</item>
|
||||
<item quantity="other">Ištrinti <xliff:g>%d</xliff:g> kortelių</item>
|
||||
</plurals>
|
||||
<string name="settings_system_locale">Sistemos</string>
|
||||
<string name="settings_locale">Kalba</string>
|
||||
<string name="settings_brown_theme">Ruda</string>
|
||||
<string name="settings_grey_theme">Pilka</string>
|
||||
<string name="settings_green_theme">Žalia</string>
|
||||
<string name="settings_sky_blue_theme">Dangaus mėlynumo</string>
|
||||
<string name="settings_blue_theme">Mėlyna</string>
|
||||
<string name="settings_violet_theme">Violetinė</string>
|
||||
<string name="settings_magenta_theme">Rausvai raudona</string>
|
||||
<string name="settings_pink_theme">Rožinė</string>
|
||||
<string name="settings_catima_theme">Catima</string>
|
||||
<string name="settings_theme_color">Temos spalva</string>
|
||||
<string name="app_contributors">Tapo įmanoma su pagalba: <xliff:g id="app_contributors">%s</xliff:g></string>
|
||||
<string name="noGroupCards">Šioje grupėje nėra jokių kortelių</string>
|
||||
<string name="toggleMoreInfo">Perjungti išsamios informacijos rodymą</string>
|
||||
<string name="barcodeImageDescriptionWithType"><xliff:g>%s</xliff:g> tipo kortelės brūkšninio kodo vaizdas</string>
|
||||
<string name="swipeToSwitchImages">Perbraukite arba ilgai palaikykite paspaudę, kad perjungtumėte vaizdus</string>
|
||||
<string name="sort_by">Rikiuoti pagal</string>
|
||||
<string name="reverse">Atvirkštinis</string>
|
||||
<string name="sort_by_balance">Balansas</string>
|
||||
<string name="sort_by_expiry">Galiojimo pabaiga</string>
|
||||
<string name="sort_by_most_recently_used">Vėliausiai naudota</string>
|
||||
<string name="sort_by_name">Pavadinimas</string>
|
||||
<string name="sort">Rikiuoti</string>
|
||||
<string name="credits">Padėkos</string>
|
||||
<string name="source_repository">Šaltinio repozitorija</string>
|
||||
<string name="and_data_usage">ir duomenų naudojimas</string>
|
||||
<string name="rate_this_app">Įvertinkite šią programėlę</string>
|
||||
<string name="version_history">Versijų istorija</string>
|
||||
<string name="license">Licencija</string>
|
||||
<string name="on_github">GitHub</string>
|
||||
<string name="on_google_play">Google Play</string>
|
||||
<string name="help_translate_this_app">Padėkite išversti šią programėlę</string>
|
||||
<string name="report_error">Pranešti apie klaidą</string>
|
||||
<string name="exportPasswordHint">Įveskite slaptažodį</string>
|
||||
<string name="exportPassword">Nustatykite slaptažodį, kad apsaugotumėte eksportavimą (neprivaloma)</string>
|
||||
</resources>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user