mirror of
https://github.com/CatimaLoyalty/Android.git
synced 2025-12-24 15:47:53 -05:00
Compare commits
680 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c2e810319 | ||
|
|
88d27c0385 | ||
|
|
2f1f6ed452 | ||
|
|
1ac816000d | ||
|
|
aa069e93b1 | ||
|
|
2f0084a73b | ||
|
|
68624f8911 | ||
|
|
5d7c503465 | ||
|
|
9015b17de6 | ||
|
|
a5f7265bd6 | ||
|
|
b03d579ee7 | ||
|
|
f4511aa82e | ||
|
|
69da7e1e2c | ||
|
|
b3de8428f8 | ||
|
|
7edf97774b | ||
|
|
fddad366d6 | ||
|
|
fcb03791b1 | ||
|
|
e25b0ee251 | ||
|
|
c5b5b0e979 | ||
|
|
347e975d9b | ||
|
|
8890788c3d | ||
|
|
9f9da82e18 | ||
|
|
ab8490798b | ||
|
|
c4b9bf77d9 | ||
|
|
a1a653b939 | ||
|
|
f9cf64a2ea | ||
|
|
83e9a01b8d | ||
|
|
1d776914ee | ||
|
|
bfc103dca9 | ||
|
|
0281f15b36 | ||
|
|
e1929de4b3 | ||
|
|
81699dbed9 | ||
|
|
5c42342070 | ||
|
|
f1c21fc7fe | ||
|
|
7c418dc416 | ||
|
|
3610e9dfb2 | ||
|
|
c5793da52c | ||
|
|
deef5ced0c | ||
|
|
7861f6cd8c | ||
|
|
4ad73a754e | ||
|
|
d3487cc535 | ||
|
|
677283c872 | ||
|
|
d39b732b77 | ||
|
|
5242290a8f | ||
|
|
9d07c1a29c | ||
|
|
548b1b1e8e | ||
|
|
8318d2e7c3 | ||
|
|
99e836a8f1 | ||
|
|
3586d27249 | ||
|
|
5904ba2af7 | ||
|
|
48b89af11a | ||
|
|
0b0a5cc5fa | ||
|
|
098f987d66 | ||
|
|
0083150fa9 | ||
|
|
1c4b4f1f25 | ||
|
|
b7f596568d | ||
|
|
2df7b9e8c6 | ||
|
|
c37e5f4076 | ||
|
|
8840390b3a | ||
|
|
a8a8ac59b0 | ||
|
|
a2e37a82b7 | ||
|
|
1b52cafea6 | ||
|
|
706bf36584 | ||
|
|
05f3b77a73 | ||
|
|
00b0502a6a | ||
|
|
ed755c35b4 | ||
|
|
3101688abb | ||
|
|
98c9c9ce15 | ||
|
|
3040cbf7a7 | ||
|
|
806b232d60 | ||
|
|
982e012940 | ||
|
|
e2cff0f11e | ||
|
|
e678bae95c | ||
|
|
c3758f2a23 | ||
|
|
4852e61c99 | ||
|
|
74d55c8b73 | ||
|
|
89670b5176 | ||
|
|
6840f39e35 | ||
|
|
635fb0105b | ||
|
|
9ad50041b5 | ||
|
|
0887a16555 | ||
|
|
ea690d19dc | ||
|
|
40f7b6e417 | ||
|
|
25b3e18618 | ||
|
|
aa28a52855 | ||
|
|
fe87fe1981 | ||
|
|
6f66b95506 | ||
|
|
a80ab32f07 | ||
|
|
de6248a5d6 | ||
|
|
cfbaeb1ffc | ||
|
|
749a4ddb29 | ||
|
|
4c070c3137 | ||
|
|
5cb8cf3a14 | ||
|
|
a1410a588e | ||
|
|
b2b40809ad | ||
|
|
c092a04e9c | ||
|
|
5bd3a309cc | ||
|
|
4bf6f6cd5f | ||
|
|
1872bea0c3 | ||
|
|
665ef5f712 | ||
|
|
9fbcb23465 | ||
|
|
25c35acd6d | ||
|
|
35747b7d9f | ||
|
|
eea5cdfdd0 | ||
|
|
e0c28830ab | ||
|
|
e714d98ae6 | ||
|
|
bbfba92de0 | ||
|
|
efda8f9f10 | ||
|
|
041472b44b | ||
|
|
92f79e9d3e | ||
|
|
f932d8f6e4 | ||
|
|
5e3399cd32 | ||
|
|
b736f31dc2 | ||
|
|
ff285430c0 | ||
|
|
504c1ac516 | ||
|
|
c4746fe2b9 | ||
|
|
99383d4fc6 | ||
|
|
71cd16caac | ||
|
|
6a04077cec | ||
|
|
5664ff5631 | ||
|
|
48764e266e | ||
|
|
435cfd2839 | ||
|
|
34639f2a2e | ||
|
|
ce9c3bffe6 | ||
|
|
d0d15393f6 | ||
|
|
e9eaf51e40 | ||
|
|
de8a843414 | ||
|
|
0ad4c683b7 | ||
|
|
fdbff0f942 | ||
|
|
a02d9fd995 | ||
|
|
d7f43f8a2b | ||
|
|
3cbd69c6c7 | ||
|
|
1cff0e29e4 | ||
|
|
e718ffb77d | ||
|
|
c2d711650f | ||
|
|
f205045916 | ||
|
|
6ed6e683b5 | ||
|
|
1612bed309 | ||
|
|
ac8a2c445c | ||
|
|
f98203fc5d | ||
|
|
82298d0d50 | ||
|
|
916515f8b2 | ||
|
|
b5e6d40857 | ||
|
|
efef397870 | ||
|
|
377438b6ae | ||
|
|
27443db945 | ||
|
|
420886bc3f | ||
|
|
86a01d36db | ||
|
|
329293fa7c | ||
|
|
26bbea7377 | ||
|
|
50385c6e0e | ||
|
|
43c45c9c0c | ||
|
|
17cdc58b19 | ||
|
|
4960b77ca3 | ||
|
|
a7c6ac695e | ||
|
|
63dfe27b25 | ||
|
|
2ec44046a8 | ||
|
|
0363bc67e4 | ||
|
|
affa434679 | ||
|
|
ded151830e | ||
|
|
e1b604c1d2 | ||
|
|
45d9353b54 | ||
|
|
ceceda560a | ||
|
|
088c25dbad | ||
|
|
54fc1e9106 | ||
|
|
1ce4216584 | ||
|
|
78d0bc7bb7 | ||
|
|
fc4bb134a9 | ||
|
|
cecfcd4714 | ||
|
|
c3364d4592 | ||
|
|
df29abbebd | ||
|
|
d4f30f798b | ||
|
|
61578a6016 | ||
|
|
13695b5c37 | ||
|
|
97d1dcc7eb | ||
|
|
5efea8c1e8 | ||
|
|
c2607cc6a2 | ||
|
|
6c70e77a75 | ||
|
|
d67c896ffe | ||
|
|
c821040c25 | ||
|
|
946a56e921 | ||
|
|
b1ea2c4687 | ||
|
|
745dd109fb | ||
|
|
9f047f3dbd | ||
|
|
68afe746eb | ||
|
|
ebd3b0f7f5 | ||
|
|
163de3653e | ||
|
|
1507694bae | ||
|
|
6d134009be | ||
|
|
c1d2a30931 | ||
|
|
e5cb6fa1a7 | ||
|
|
2188280ada | ||
|
|
fdc8cb0adf | ||
|
|
b79286d1a6 | ||
|
|
d9f0ea8346 | ||
|
|
a262120bc3 | ||
|
|
446131a87d | ||
|
|
3466ba6591 | ||
|
|
5b3c6559b4 | ||
|
|
4680b53f86 | ||
|
|
8423ea9b59 | ||
|
|
1d8b2d82ed | ||
|
|
784bd299e8 | ||
|
|
c8df7a7a2e | ||
|
|
cec2058f49 | ||
|
|
c910a96005 | ||
|
|
f3c7a75963 | ||
|
|
04e0b9ba34 | ||
|
|
1b8e9e4d7c | ||
|
|
7a556e320b | ||
|
|
db706a02d3 | ||
|
|
8e452ed6bd | ||
|
|
a9e574a9b4 | ||
|
|
f24d11403c | ||
|
|
e1c658ac2a | ||
|
|
a5901700b4 | ||
|
|
bf4cd7a961 | ||
|
|
79ca5196d0 | ||
|
|
b24d641aa6 | ||
|
|
84051555b6 | ||
|
|
48ff57d0fc | ||
|
|
3a6828b34b | ||
|
|
efd36d061f | ||
|
|
a5ef8ae914 | ||
|
|
37ce61646b | ||
|
|
5e59315017 | ||
|
|
fbed1348aa | ||
|
|
438e9f09a3 | ||
|
|
aee9176518 | ||
|
|
70f7768cee | ||
|
|
bf7e35c361 | ||
|
|
71a6fb2f56 | ||
|
|
8d799fce26 | ||
|
|
ae6e98b876 | ||
|
|
44d8597fa5 | ||
|
|
de47b7b1d2 | ||
|
|
631d65708e | ||
|
|
62ffcbc948 | ||
|
|
14ef3086a4 | ||
|
|
011b0f7ecc | ||
|
|
89eaac12d5 | ||
|
|
2fbe5a821c | ||
|
|
5516acecce | ||
|
|
6163de3086 | ||
|
|
4658896304 | ||
|
|
e0d9e2b2dd | ||
|
|
7dd2291376 | ||
|
|
bc69b82029 | ||
|
|
1502ac1923 | ||
|
|
0552acae26 | ||
|
|
18e30d2726 | ||
|
|
1646db24df | ||
|
|
6a6a8fff2b | ||
|
|
a0f8f6e6b5 | ||
|
|
3dda3aa1a5 | ||
|
|
ac6bffcc67 | ||
|
|
93fac2e726 | ||
|
|
9b724ebcc7 | ||
|
|
540ef6d345 | ||
|
|
64fa790836 | ||
|
|
2c18b0dd19 | ||
|
|
f570f2746a | ||
|
|
4c9c717792 | ||
|
|
db4408108a | ||
|
|
f75df30493 | ||
|
|
0f914414af | ||
|
|
3468293f5f | ||
|
|
c9b8b2df9e | ||
|
|
3d138c9504 | ||
|
|
72b95988e5 | ||
|
|
188c147173 | ||
|
|
6224a13d17 | ||
|
|
f393b9b618 | ||
|
|
6fa6cdfadf | ||
|
|
bc7f44f60a | ||
|
|
5579784b0a | ||
|
|
a20f8b58f8 | ||
|
|
fc8d0ac1aa | ||
|
|
edf953cdad | ||
|
|
7290c4aa28 | ||
|
|
f28e614c7f | ||
|
|
616b23d3a8 | ||
|
|
5fda774626 | ||
|
|
30941a9e16 | ||
|
|
9289d3cf0b | ||
|
|
0ecc36a83c | ||
|
|
d3b8569ef7 | ||
|
|
19722af65f | ||
|
|
329be8abbb | ||
|
|
9aa6acccda | ||
|
|
ba05c6872b | ||
|
|
ca1fc214ff | ||
|
|
2ebb3e1837 | ||
|
|
019c5e232d | ||
|
|
dda527a72f | ||
|
|
df0bbe773f | ||
|
|
a320e1c776 | ||
|
|
607669a74c | ||
|
|
f50dfeac9e | ||
|
|
ef46f6a00b | ||
|
|
eafedb52ad | ||
|
|
423384fae6 | ||
|
|
09e96eba53 | ||
|
|
4b0a4a7db9 | ||
|
|
2371738a15 | ||
|
|
e22d11f8e9 | ||
|
|
917e60a7f5 | ||
|
|
4d09f66ac6 | ||
|
|
622aa96464 | ||
|
|
9887e19ec9 | ||
|
|
a308634868 | ||
|
|
44c3c8b32f | ||
|
|
69835ff0c9 | ||
|
|
8270bc6c03 | ||
|
|
9e8ea3384e | ||
|
|
c8b258cd62 | ||
|
|
f23c73a67a | ||
|
|
8ba3295ba1 | ||
|
|
6e3d61304f | ||
|
|
9b7b3c4b9f | ||
|
|
b993221af5 | ||
|
|
86c029fe92 | ||
|
|
fbe857a5cf | ||
|
|
1717560c05 | ||
|
|
02f85cd49a | ||
|
|
69a6afe611 | ||
|
|
94fa6d19c4 | ||
|
|
211ee9ce69 | ||
|
|
e818be3d50 | ||
|
|
6620369616 | ||
|
|
9d9ce1d11f | ||
|
|
76ba3c333e | ||
|
|
0429086c57 | ||
|
|
4a4b534813 | ||
|
|
8cfae80ea8 | ||
|
|
517a9cf37a | ||
|
|
242f4b7ad9 | ||
|
|
ce1a210650 | ||
|
|
9d369a8e6f | ||
|
|
240f73f743 | ||
|
|
3cf1579bb0 | ||
|
|
171fb83249 | ||
|
|
603b02911b | ||
|
|
ab5eb62bc1 | ||
|
|
40b2b5c96f | ||
|
|
6efe0f6c54 | ||
|
|
00a78c6030 | ||
|
|
0db46614a8 | ||
|
|
32d139f756 | ||
|
|
c2c8b780c8 | ||
|
|
057a58a3d6 | ||
|
|
551fbed8ad | ||
|
|
468c86b5ea | ||
|
|
50cb3146bf | ||
|
|
0853bd88a4 | ||
|
|
3bbe8e9524 | ||
|
|
ebc2e07fc4 | ||
|
|
ad368e68eb | ||
|
|
a1d4bb746f | ||
|
|
44473ae921 | ||
|
|
2c3d9f714c | ||
|
|
13121e9869 | ||
|
|
14116324fb | ||
|
|
d207197055 | ||
|
|
a36775c6fd | ||
|
|
cae95d2577 | ||
|
|
1c95a767c0 | ||
|
|
26fd3e3a58 | ||
|
|
e79be213fc | ||
|
|
2010b9a25c | ||
|
|
355c2f9ceb | ||
|
|
43daeec5c3 | ||
|
|
5ddf77dd63 | ||
|
|
64fb91d644 | ||
|
|
4921d9f4b0 | ||
|
|
8f9dd584b1 | ||
|
|
04f2cffeb3 | ||
|
|
8ca899b7d1 | ||
|
|
c0f3e814a0 | ||
|
|
1eb8d03b74 | ||
|
|
ec0dc6bd4c | ||
|
|
cd7b3d5970 | ||
|
|
55843899da | ||
|
|
d639013bea | ||
|
|
45785b66e7 | ||
|
|
6a20366cc9 | ||
|
|
4856e9e971 | ||
|
|
4141856287 | ||
|
|
12196fc9b1 | ||
|
|
c73e7924ad | ||
|
|
acf83087f4 | ||
|
|
8ef507c011 | ||
|
|
d36f82df05 | ||
|
|
5f50e86234 | ||
|
|
733c9885e8 | ||
|
|
4b7acefece | ||
|
|
a6b950e2e1 | ||
|
|
c21f8098ef | ||
|
|
3ccce45f17 | ||
|
|
f096f63352 | ||
|
|
babd6ff9bb | ||
|
|
3c5dea6fbf | ||
|
|
3bf7d827a1 | ||
|
|
eea03dd516 | ||
|
|
92ceb7ee50 | ||
|
|
aba5dbfb38 | ||
|
|
1ba4839544 | ||
|
|
cca68cfcc5 | ||
|
|
d2043c331a | ||
|
|
b57bc7d7b6 | ||
|
|
23fb6d9c6a | ||
|
|
2498ee0354 | ||
|
|
eb38d0a8aa | ||
|
|
cb0deaae27 | ||
|
|
8220c8b32e | ||
|
|
8458a2e79a | ||
|
|
d436a20b23 | ||
|
|
c24f3b6ece | ||
|
|
d5cb1a79d2 | ||
|
|
7040dbf997 | ||
|
|
d1685f2557 | ||
|
|
36237d9b3e | ||
|
|
f968aba926 | ||
|
|
2d7960ddc4 | ||
|
|
4cdd168b8c | ||
|
|
9de54872f3 | ||
|
|
f8f0edd8d3 | ||
|
|
3703f3d1ac | ||
|
|
3bbd16e3a9 | ||
|
|
a2947d1f58 | ||
|
|
4b94dd8b48 | ||
|
|
62376769cd | ||
|
|
e057a24526 | ||
|
|
25cc1a1160 | ||
|
|
d7ed4aeff8 | ||
|
|
58ebb7c7bf | ||
|
|
be2c586820 | ||
|
|
8a0b2d9460 | ||
|
|
4b1583ce4c | ||
|
|
83c611f4ae | ||
|
|
0a7082bed5 | ||
|
|
94c84c60f5 | ||
|
|
4ce7f475f4 | ||
|
|
75057d3c41 | ||
|
|
62fce7231d | ||
|
|
d201d2e98f | ||
|
|
36ff4ca9ae | ||
|
|
a7a775bc01 | ||
|
|
d217ac8752 | ||
|
|
d376569ba1 | ||
|
|
1a09b1bd5e | ||
|
|
ec1f4f8fbd | ||
|
|
3883617a34 | ||
|
|
2e7c8f39a7 | ||
|
|
7fc2bdc46e | ||
|
|
c141a8986c | ||
|
|
70ad884aa3 | ||
|
|
2bd8aed853 | ||
|
|
78f5908c33 | ||
|
|
2f43febe95 | ||
|
|
17d23de518 | ||
|
|
af831cfdbd | ||
|
|
4476646317 | ||
|
|
958dc29689 | ||
|
|
21216f3aa6 | ||
|
|
808dd73221 | ||
|
|
e3bd82f4a2 | ||
|
|
2cbb105c21 | ||
|
|
40c364a5e8 | ||
|
|
8c484078b4 | ||
|
|
f93f3b912d | ||
|
|
934ef22a8c | ||
|
|
732679f3f8 | ||
|
|
2e7cbcbac9 | ||
|
|
eb5161a382 | ||
|
|
b765d77145 | ||
|
|
ad344c7d12 | ||
|
|
d84ce6ff82 | ||
|
|
d4e388cf5d | ||
|
|
34fcedfa28 | ||
|
|
c75eed56f0 | ||
|
|
8ce0ce82ef | ||
|
|
304754f991 | ||
|
|
ab6e90d9f6 | ||
|
|
9df22f18f0 | ||
|
|
2eeec777d7 | ||
|
|
1d41039de2 | ||
|
|
112b4a034c | ||
|
|
8c06427976 | ||
|
|
486a2b887e | ||
|
|
3f3f289f62 | ||
|
|
bf2b28065d | ||
|
|
d00d25ad00 | ||
|
|
f1371daecb | ||
|
|
2fecc524f0 | ||
|
|
f5beffcad5 | ||
|
|
b78bdb3ced | ||
|
|
f16b3ca119 | ||
|
|
f2e820f7bb | ||
|
|
cf38357f3e | ||
|
|
9862a9912f | ||
|
|
990968a67b | ||
|
|
3331cf9ccf | ||
|
|
8a45222783 | ||
|
|
d92efc020b | ||
|
|
deb1edb7a6 | ||
|
|
3265a9eb4d | ||
|
|
c0ed86818f | ||
|
|
67d8a242b5 | ||
|
|
d12a4bf75d | ||
|
|
e86db3887b | ||
|
|
ce8488e515 | ||
|
|
5ef9aad5a7 | ||
|
|
d9fd3fd001 | ||
|
|
f9f3efe1e2 | ||
|
|
c5b9399fc4 | ||
|
|
19ad303508 | ||
|
|
d5b9bb9ca1 | ||
|
|
c50661f1bd | ||
|
|
afd8d2e675 | ||
|
|
be8765aab1 | ||
|
|
5656751a14 | ||
|
|
12b81f7fe5 | ||
|
|
3ff64d9abc | ||
|
|
0a39a8bb5b | ||
|
|
64f68d5c19 | ||
|
|
2975c46f96 | ||
|
|
f6762df1cf | ||
|
|
1aa0585909 | ||
|
|
1cb20eeab3 | ||
|
|
c8acee2fbd | ||
|
|
ff35773eed | ||
|
|
48403399ab | ||
|
|
96dafeab70 | ||
|
|
d10e358e7d | ||
|
|
71eb8c53bc | ||
|
|
186c2d1d56 | ||
|
|
2efa35504f | ||
|
|
ca1996da53 | ||
|
|
b8d0724446 | ||
|
|
c9a08e4f5b | ||
|
|
4920b67d0c | ||
|
|
e8eb3fce5c | ||
|
|
aaccae647a | ||
|
|
df4e851bfe | ||
|
|
05c85dac14 | ||
|
|
86d953bb74 | ||
|
|
1ecc39bea5 | ||
|
|
4c9182eb0b | ||
|
|
62fafaff95 | ||
|
|
aa0fdcc6b6 | ||
|
|
6c33dba401 | ||
|
|
c07d9805e4 | ||
|
|
941a123db0 | ||
|
|
2e58421a80 | ||
|
|
ae28e02cce | ||
|
|
e5d8dc8d59 | ||
|
|
b85de0e089 | ||
|
|
4264155837 | ||
|
|
d2a500824a | ||
|
|
6a91d59050 | ||
|
|
451c49d05f | ||
|
|
b4ec4b74ca | ||
|
|
6c254315af | ||
|
|
dfaf61722f | ||
|
|
2dbebea884 | ||
|
|
d7da942481 | ||
|
|
6b4a01c954 | ||
|
|
aee7728f56 | ||
|
|
6fbbe38542 | ||
|
|
651744b770 | ||
|
|
a189ac22ce | ||
|
|
f4d71e4525 | ||
|
|
45ec062499 | ||
|
|
82d8e84d78 | ||
|
|
fa25bff46f | ||
|
|
5648061a31 | ||
|
|
9cd2f66b40 | ||
|
|
90b7b43faf | ||
|
|
cfe5865d35 | ||
|
|
243898c441 | ||
|
|
0afc6f3976 | ||
|
|
fa95ea6a62 | ||
|
|
abb559afd6 | ||
|
|
61e7d1b154 | ||
|
|
da594a8287 | ||
|
|
15f5c4a67b | ||
|
|
19f7af1e42 | ||
|
|
b5b994642a | ||
|
|
45da445866 | ||
|
|
55b1ad1c55 | ||
|
|
fcd6f075f6 | ||
|
|
3d2f119fdb | ||
|
|
05f4add5cd | ||
|
|
eb71d5f717 | ||
|
|
a64603d64e | ||
|
|
e686bb36ce | ||
|
|
351a805046 | ||
|
|
466ac404bc | ||
|
|
b5c0be8ed5 | ||
|
|
f963b2fd4c | ||
|
|
c26ae45cdc | ||
|
|
addb0896c0 | ||
|
|
c75bef4f01 | ||
|
|
f633fb39aa | ||
|
|
49200fff48 | ||
|
|
f4bd6a3f59 | ||
|
|
18021e7653 | ||
|
|
de364a4cc4 | ||
|
|
3ba42aad6d | ||
|
|
63fad8bae1 | ||
|
|
ad418bc9bd | ||
|
|
b0263d8eb5 | ||
|
|
97d3969f93 | ||
|
|
1c85172206 | ||
|
|
711fa7ef81 | ||
|
|
2220a8e3c6 | ||
|
|
cd1bd31e23 | ||
|
|
f812194b1a | ||
|
|
79c26cfed7 | ||
|
|
85ea10303a | ||
|
|
4196ad8d61 | ||
|
|
90be6a418b | ||
|
|
2e2563cfc0 | ||
|
|
983c207019 | ||
|
|
6a5983b7bf | ||
|
|
b34e69d5d5 | ||
|
|
f1cd9ac935 | ||
|
|
5eb56c934b | ||
|
|
f9b24d4b1b | ||
|
|
537b2cba82 | ||
|
|
d788a274a8 | ||
|
|
57c383c064 | ||
|
|
49a8828007 | ||
|
|
d56a31160a | ||
|
|
bb78f9c12a | ||
|
|
00c005afce | ||
|
|
c008767bd6 | ||
|
|
5e15555ad4 | ||
|
|
916d12c504 | ||
|
|
854958e364 | ||
|
|
d295dd40fd | ||
|
|
069a3c99ef | ||
|
|
f1e7e5494d | ||
|
|
189fe7d101 | ||
|
|
9a9ff77d1c | ||
|
|
b8c1eaf88e | ||
|
|
e525a32511 | ||
|
|
de9f108534 | ||
|
|
c5f0d03a1c | ||
|
|
e592452d02 | ||
|
|
d3d2b37001 | ||
|
|
3dbe4f8327 | ||
|
|
3d81c1be08 | ||
|
|
333874e5e1 | ||
|
|
69a53a8408 | ||
|
|
8dc479793b | ||
|
|
a2d48a236b | ||
|
|
67f390f594 | ||
|
|
1a5109e036 | ||
|
|
ff42ead6b7 | ||
|
|
2f8d8e79c1 | ||
|
|
efa6ac1f6c | ||
|
|
4fc56068e1 | ||
|
|
7a0253ddcd | ||
|
|
ac3647695b | ||
|
|
fc902db170 | ||
|
|
b7230ba2a7 | ||
|
|
f5b71beb4b | ||
|
|
380a12e926 | ||
|
|
f92f84ee5c | ||
|
|
e1eb049d05 | ||
|
|
e03ca71728 | ||
|
|
9f3633b2ef | ||
|
|
0b53bdf6eb | ||
|
|
2536767437 | ||
|
|
1624d56edb | ||
|
|
7f1b1484ac | ||
|
|
ab1e6eed0c | ||
|
|
ba5cf81bb4 |
7
.github/workflows/autoclose-needs-info.yml
vendored
7
.github/workflows/autoclose-needs-info.yml
vendored
@@ -17,7 +17,8 @@ jobs:
|
||||
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'
|
||||
only-labels: 'state: needs info'
|
||||
stale-issue-label: 'state: needs info'
|
||||
stale-pr-label: 'state: needs info'
|
||||
remove-stale-when-updated: false
|
||||
enable-statistics: true
|
||||
|
||||
93
CHANGELOG.md
93
CHANGELOG.md
@@ -1,6 +1,97 @@
|
||||
# Changelog
|
||||
|
||||
## Unreleased - 91
|
||||
## v2.17.1 - 109
|
||||
|
||||
- Fix incorrect text colour on "No barcode" button
|
||||
|
||||
## v2.17.0 - 108
|
||||
|
||||
- Add card duplication feature
|
||||
- Don't allow choosing expiry before 1970 (they never worked anyway)
|
||||
- Add support for archiving cards
|
||||
- Move delete from edit to view
|
||||
- Remove rotation lock icon in favour of a new rotation lock setting
|
||||
|
||||
## v2.16.3 - 107 (2022-04-15)
|
||||
|
||||
- Stocard import fixes
|
||||
|
||||
## v2.16.2 - 106 (2022-03-31)
|
||||
|
||||
- Fix some character sequences being shown as a single character
|
||||
|
||||
## v2.16.1 - 105 (2022-03-25)
|
||||
|
||||
- Fix gray block appearing on invalid value for barcode
|
||||
- Stocard import fixes
|
||||
|
||||
## v2.16.0 - 104 (2022-03-09)
|
||||
|
||||
- Save card detail expansion state
|
||||
- Minor UI fixes
|
||||
|
||||
## v2.15.2 - 103 (2022-02-11)
|
||||
|
||||
- Fix manual language selection not applying everywhere
|
||||
- Fix crash in edit view on regionless locale
|
||||
|
||||
## v2.15.1 - 102 (2022-02-10)
|
||||
|
||||
- Various minor fixes
|
||||
- Fix crash when using Norwegian translation
|
||||
|
||||
## v2.15.0 - 101 (2022-02-06)
|
||||
|
||||
- Fix cropper not using theme colour
|
||||
- Fix minor theming issues
|
||||
- Add pure black dark theme for OLED screens
|
||||
|
||||
## v2.14.1 - 100 (2022-01-15)
|
||||
|
||||
- Hide search, expand and sort icons until there is at least 1 card
|
||||
- Various theming fixes
|
||||
|
||||
## v2.14.0 - 99 (2022-01-14)
|
||||
|
||||
- Material You redesign
|
||||
|
||||
## v2.13.1 - 98 (2022-01-09)
|
||||
|
||||
- Fix various TalkBack-related bugs
|
||||
|
||||
## v2.13.0 - 97 (2022-01-03)
|
||||
|
||||
- Fixed pressing the save button multiple times creating multiple entries
|
||||
- Lower card header size when hiding details to fit even more cards
|
||||
- Restructure edit screen
|
||||
- Improve star icon contrast in main view
|
||||
|
||||
## v2.12.0 - 96 (2021-12-23)
|
||||
|
||||
- Add CODE 93 support
|
||||
- Various minor bugfixes and improvements
|
||||
|
||||
## v2.11.2 - 95 (2021-12-04)
|
||||
|
||||
- Fix crash on sharing card
|
||||
|
||||
## v2.11.1 - 94 (2021-11-30)
|
||||
|
||||
- Fix blurriness of main screen letter icons
|
||||
- Fix icons sometimes disappearing after selection
|
||||
- Fix status bar icons possibly being invisible on Android 5
|
||||
|
||||
## v2.11.0 - 93 (2021-11-28)
|
||||
|
||||
- Add Catima to [Quick Access Device Controls](https://developer.android.com/guide/topics/ui/device-control)
|
||||
- Fix some groups not showing up correctly in group management screen
|
||||
|
||||
## v2.10.0 - 92 (2021-11-20)
|
||||
|
||||
- New main screen layout
|
||||
- Fix bottomsheet sizing issues when switching in and out of fullscreen
|
||||
|
||||
## v2.9.0 - 91 (2021-11-14)
|
||||
|
||||
- Improved group management support
|
||||
- Support cropping images
|
||||
|
||||
3
FUNDING.yml
Normal file
3
FUNDING.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
github: TheLastProject
|
||||
custom:
|
||||
- "https://paypal.me/sylviavanos"
|
||||
97
Gemfile.lock
97
Gemfile.lock
@@ -1,29 +1,30 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.3)
|
||||
CFPropertyList (3.0.5)
|
||||
rexml
|
||||
addressable (2.8.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
artifactory (3.0.15)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.2.0)
|
||||
aws-partitions (1.501.0)
|
||||
aws-sdk-core (3.121.0)
|
||||
aws-partitions (1.597.0)
|
||||
aws-sdk-core (3.131.1)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.239.0)
|
||||
aws-partitions (~> 1, >= 1.525.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
jmespath (~> 1.0)
|
||||
aws-sdk-kms (1.48.0)
|
||||
aws-sdk-core (~> 3, >= 3.120.0)
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.57.0)
|
||||
aws-sdk-core (~> 3, >= 3.127.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.102.0)
|
||||
aws-sdk-core (~> 3, >= 3.120.0)
|
||||
aws-sdk-s3 (1.114.0)
|
||||
aws-sdk-core (~> 3, >= 3.127.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.4)
|
||||
aws-sigv4 (1.4.0)
|
||||
aws-sigv4 (1.5.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
babosa (1.0.4)
|
||||
claide (1.0.3)
|
||||
claide (1.1.0)
|
||||
colored (1.2)
|
||||
colored2 (3.1.2)
|
||||
commander (4.6.0)
|
||||
@@ -34,18 +35,19 @@ GEM
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.7.6)
|
||||
emoji_regex (3.2.2)
|
||||
excon (0.85.0)
|
||||
faraday (1.7.2)
|
||||
emoji_regex (3.2.3)
|
||||
excon (0.92.3)
|
||||
faraday (1.10.0)
|
||||
faraday-em_http (~> 1.0)
|
||||
faraday-em_synchrony (~> 1.0)
|
||||
faraday-excon (~> 1.1)
|
||||
faraday-httpclient (~> 1.0.1)
|
||||
faraday-httpclient (~> 1.0)
|
||||
faraday-multipart (~> 1.0)
|
||||
faraday-net_http (~> 1.0)
|
||||
faraday-net_http_persistent (~> 1.1)
|
||||
faraday-net_http_persistent (~> 1.0)
|
||||
faraday-patron (~> 1.0)
|
||||
faraday-rack (~> 1.0)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday-retry (~> 1.0)
|
||||
ruby2_keywords (>= 0.0.4)
|
||||
faraday-cookie_jar (0.0.7)
|
||||
faraday (>= 0.8.0)
|
||||
@@ -54,14 +56,17 @@ GEM
|
||||
faraday-em_synchrony (1.0.0)
|
||||
faraday-excon (1.1.0)
|
||||
faraday-httpclient (1.0.1)
|
||||
faraday-multipart (1.0.4)
|
||||
multipart-post (~> 2)
|
||||
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-retry (1.0.3)
|
||||
faraday_middleware (1.2.0)
|
||||
faraday (~> 1.0)
|
||||
fastimage (2.2.5)
|
||||
fastlane (2.193.1)
|
||||
fastimage (2.2.6)
|
||||
fastlane (2.206.2)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.8, < 3.0.0)
|
||||
artifactory (~> 3.0)
|
||||
@@ -101,9 +106,9 @@ GEM
|
||||
xcpretty (~> 0.3.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3)
|
||||
gh_inspector (1.1.3)
|
||||
google-apis-androidpublisher_v3 (0.11.0)
|
||||
google-apis-androidpublisher_v3 (0.21.0)
|
||||
google-apis-core (>= 0.4, < 2.a)
|
||||
google-apis-core (0.4.1)
|
||||
google-apis-core (0.5.0)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
httpclient (>= 2.8.1, < 3.a)
|
||||
@@ -112,53 +117,53 @@ GEM
|
||||
retriable (>= 2.0, < 4.a)
|
||||
rexml
|
||||
webrick
|
||||
google-apis-iamcredentials_v1 (0.7.0)
|
||||
google-apis-iamcredentials_v1 (0.10.0)
|
||||
google-apis-core (>= 0.4, < 2.a)
|
||||
google-apis-playcustomapp_v1 (0.5.0)
|
||||
google-apis-playcustomapp_v1 (0.7.0)
|
||||
google-apis-core (>= 0.4, < 2.a)
|
||||
google-apis-storage_v1 (0.6.0)
|
||||
google-apis-storage_v1 (0.14.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.5.0)
|
||||
faraday (>= 0.17.3, < 2.0)
|
||||
google-cloud-errors (1.1.0)
|
||||
google-cloud-storage (1.34.1)
|
||||
addressable (~> 2.5)
|
||||
google-cloud-env (1.6.0)
|
||||
faraday (>= 0.17.3, < 3.0)
|
||||
google-cloud-errors (1.2.0)
|
||||
google-cloud-storage (1.36.2)
|
||||
addressable (~> 2.8)
|
||||
digest-crc (~> 0.4)
|
||||
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.17.1)
|
||||
faraday (>= 0.17.3, < 2.0)
|
||||
googleauth (1.1.3)
|
||||
faraday (>= 0.17.3, < 3.a)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
memoist (~> 0.16)
|
||||
multi_json (~> 1.11)
|
||||
os (>= 0.9, < 2.0)
|
||||
signet (~> 0.15)
|
||||
signet (>= 0.16, < 2.a)
|
||||
highline (2.0.3)
|
||||
http-cookie (1.0.4)
|
||||
http-cookie (1.0.5)
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.8.3)
|
||||
jmespath (1.4.0)
|
||||
json (2.5.1)
|
||||
jwt (2.2.3)
|
||||
jmespath (1.6.1)
|
||||
json (2.6.2)
|
||||
jwt (2.4.1)
|
||||
memoist (0.16.2)
|
||||
mini_magick (4.11.0)
|
||||
mini_mime (1.1.1)
|
||||
mini_mime (1.1.2)
|
||||
multi_json (1.15.0)
|
||||
multipart-post (2.0.0)
|
||||
nanaimo (0.3.0)
|
||||
naturally (2.2.1)
|
||||
optparse (0.1.1)
|
||||
os (1.1.1)
|
||||
os (1.1.4)
|
||||
plist (3.6.0)
|
||||
public_suffix (4.0.6)
|
||||
public_suffix (4.0.7)
|
||||
rake (13.0.6)
|
||||
representable (3.1.1)
|
||||
representable (3.2.0)
|
||||
declarative (< 0.1.0)
|
||||
trailblazer-option (>= 0.1.1, < 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
@@ -168,9 +173,9 @@ GEM
|
||||
ruby2_keywords (0.0.5)
|
||||
rubyzip (2.3.2)
|
||||
security (0.1.3)
|
||||
signet (0.16.0)
|
||||
signet (0.16.1)
|
||||
addressable (~> 2.8)
|
||||
faraday (>= 0.17.3, < 2.0)
|
||||
faraday (>= 0.17.5, < 3.0)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
multi_json (~> 1.10)
|
||||
simctl (1.6.8)
|
||||
@@ -179,7 +184,7 @@ GEM
|
||||
terminal-notifier (2.0.0)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
trailblazer-option (0.1.1)
|
||||
trailblazer-option (0.1.2)
|
||||
tty-cursor (0.7.1)
|
||||
tty-screen (0.8.1)
|
||||
tty-spinner (0.9.3)
|
||||
@@ -187,8 +192,8 @@ GEM
|
||||
uber (0.1.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.8)
|
||||
unicode-display_width (1.7.0)
|
||||
unf_ext (0.0.8.2)
|
||||
unicode-display_width (1.8.0)
|
||||
webrick (1.7.0)
|
||||
word_wrap (1.0.0)
|
||||
xcodeproj (1.21.0)
|
||||
|
||||
@@ -18,8 +18,8 @@ android {
|
||||
applicationId "me.hackerchick.catima"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 31
|
||||
versionCode 91
|
||||
versionName "2.9.0"
|
||||
versionCode 109
|
||||
versionName "2.17.1"
|
||||
|
||||
vectorDrawables.useSupportLibrary true
|
||||
multiDexEnabled true
|
||||
@@ -49,8 +49,8 @@ android {
|
||||
// Flag to enable support for the new language APIs
|
||||
coreLibraryDesugaringEnabled true
|
||||
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
@@ -80,25 +80,25 @@ android {
|
||||
|
||||
dependencies {
|
||||
// AndroidX
|
||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
|
||||
implementation 'androidx.appcompat:appcompat:1.4.2'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
|
||||
implementation 'androidx.exifinterface:exifinterface:1.3.3'
|
||||
implementation 'androidx.palette:palette:1.0.0'
|
||||
implementation 'androidx.preference:preference:1.1.1'
|
||||
implementation 'com.google.android.material:material:1.4.0'
|
||||
implementation 'com.github.yalantis:ucrop:2.2.7'
|
||||
implementation 'androidx.preference:preference:1.2.0'
|
||||
implementation 'com.google.android.material:material:1.6.1'
|
||||
implementation 'com.github.yalantis:ucrop:2.2.8'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||
|
||||
// Splash Screen
|
||||
implementation 'androidx.core:core-splashscreen:1.0.0-alpha02'
|
||||
implementation 'androidx.core:core-splashscreen:1.0.0-rc01'
|
||||
|
||||
// Third-party
|
||||
implementation 'com.journeyapps:zxing-android-embedded:4.3.0@aar'
|
||||
implementation 'com.google.zxing:core:3.4.1'
|
||||
implementation 'com.google.zxing:core:3.5.0'
|
||||
implementation 'org.apache.commons:commons-csv:1.9.0'
|
||||
implementation 'com.jaredrummler:colorpicker:1.1.0'
|
||||
implementation 'com.github.invissvenska:NumberPickerPreference:1.0.3'
|
||||
implementation 'net.lingala.zip4j:zip4j:2.9.0'
|
||||
implementation 'com.github.invissvenska:NumberPickerPreference:1.0.4'
|
||||
implementation 'net.lingala.zip4j:zip4j:2.11.1'
|
||||
|
||||
// SpotBugs
|
||||
implementation 'io.wcm.tooling.spotbugs:io.wcm.tooling.spotbugs.annotations:1.0.0'
|
||||
@@ -106,7 +106,7 @@ dependencies {
|
||||
// Testing
|
||||
testImplementation 'androidx.test:core:1.4.0'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'org.robolectric:robolectric:4.6.1'
|
||||
testImplementation 'org.robolectric:robolectric:4.8.1'
|
||||
}
|
||||
|
||||
tasks.withType(SpotBugsTask) {
|
||||
|
||||
@@ -37,11 +37,11 @@
|
||||
<activity
|
||||
android:name=".AboutActivity"
|
||||
android:label="@string/about"
|
||||
android:theme="@style/AppTheme.NoActionBar"></activity>
|
||||
android:theme="@style/AppTheme.NoActionBar" />
|
||||
<activity
|
||||
android:name=".ManageGroupsActivity"
|
||||
android:label="@string/groups"
|
||||
android:theme="@style/AppTheme.NoActionBar"></activity>
|
||||
android:theme="@style/AppTheme.NoActionBar" />
|
||||
<activity
|
||||
android:name=".ManageGroupActivity"
|
||||
android:label="@string/group_edit"
|
||||
@@ -116,8 +116,8 @@
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="com.yalantis.ucrop.UCropActivity"
|
||||
android:theme="@style/Theme.AppCompat.NoActionBar"/>
|
||||
android:name=".UCropWrapper"
|
||||
android:theme="@style/AppTheme.NoActionBar" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
@@ -128,6 +128,11 @@
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_provider_paths" />
|
||||
</provider>
|
||||
<service android:name=".CardsOnPowerScreenService" android:label="@string/app_name"
|
||||
android:permission="android.permission.BIND_CONTROLS" android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.controls.ControlsProviderService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -151,13 +151,13 @@ public class AboutActivity extends CatimaAppCompatActivity implements View.OnCli
|
||||
} 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";
|
||||
url = "https://github.com/CatimaLoyalty/Android/blob/master/LICENSE";
|
||||
} else if (id == R.id.repo) {
|
||||
url = "https://github.com/TheLastProject/Catima/";
|
||||
url = "https://github.com/CatimaLoyalty/Android/";
|
||||
} 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";
|
||||
url = "https://github.com/CatimaLoyalty/Android/issues";
|
||||
} else if (id == R.id.rate) {
|
||||
url = "https://play.google.com/store/apps/details?id=me.hackerchick.catima";
|
||||
} else {
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
@@ -46,7 +47,7 @@ public class BarcodeImageWriterTask implements CompatCallable<Bitmap> {
|
||||
BarcodeImageWriterTask(
|
||||
Context context, ImageView imageView, String cardIdString,
|
||||
CatimaBarcode barcodeFormat, TextView textView,
|
||||
boolean showFallback, Runnable callback
|
||||
boolean showFallback, Runnable callback, boolean roundCornerPadding
|
||||
) {
|
||||
mContext = context;
|
||||
|
||||
@@ -60,18 +61,32 @@ public class BarcodeImageWriterTask implements CompatCallable<Bitmap> {
|
||||
cardId = cardIdString;
|
||||
format = barcodeFormat;
|
||||
|
||||
int padding = 0;
|
||||
// Some barcodes already have internal whitespace and shouldn't get extra padding
|
||||
// TODO: Get rid of this hack by somehow detecting this extra whitespace
|
||||
if (roundCornerPadding && !barcodeFormat.hasInternalPadding()) {
|
||||
padding = Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, context.getResources().getDisplayMetrics()));
|
||||
}
|
||||
|
||||
final int MAX_WIDTH = getMaxWidth(format);
|
||||
|
||||
int tempImageHeight;
|
||||
int tempImageWidth;
|
||||
|
||||
if (imageView.getWidth() < MAX_WIDTH) {
|
||||
imageHeight = imageView.getHeight();
|
||||
imageWidth = imageView.getWidth();
|
||||
tempImageHeight = imageView.getHeight();
|
||||
tempImageWidth = imageView.getWidth();
|
||||
} else {
|
||||
// Scale down the image to reduce the memory needed to produce it
|
||||
imageWidth = MAX_WIDTH;
|
||||
tempImageWidth = MAX_WIDTH;
|
||||
double ratio = (double) MAX_WIDTH / (double) imageView.getWidth();
|
||||
imageHeight = (int) (imageView.getHeight() * ratio);
|
||||
tempImageHeight = (int) (imageView.getHeight() * ratio);
|
||||
}
|
||||
|
||||
// Ensure space for padding if wanted
|
||||
imageWidth = tempImageWidth;
|
||||
imageHeight = tempImageHeight - padding;
|
||||
|
||||
this.showFallback = showFallback;
|
||||
}
|
||||
|
||||
@@ -120,6 +135,8 @@ public class BarcodeImageWriterTask implements CompatCallable<Bitmap> {
|
||||
return "C0C";
|
||||
case CODE_39:
|
||||
return "CODE_39";
|
||||
case CODE_93:
|
||||
return "CODE_93";
|
||||
case CODE_128:
|
||||
return "CODE_128";
|
||||
case EAN_8:
|
||||
|
||||
@@ -6,23 +6,18 @@ import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import protect.card_locker.async.TaskHandler;
|
||||
|
||||
/**
|
||||
* This activity is callable and will allow a user to enter
|
||||
@@ -30,20 +25,18 @@ import protect.card_locker.async.TaskHandler;
|
||||
* the data. The user may then select any barcode, where its
|
||||
* data and type will be returned to the caller.
|
||||
*/
|
||||
public class BarcodeSelectorActivity extends CatimaAppCompatActivity {
|
||||
public class BarcodeSelectorActivity extends CatimaAppCompatActivity implements BarcodeSelectorAdapter.BarcodeSelectorListener {
|
||||
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";
|
||||
|
||||
private Map<String, Pair<Integer, Integer>> barcodeViewMap;
|
||||
|
||||
final private TaskHandler mTasks = new TaskHandler();
|
||||
|
||||
private final Handler typingDelayHandler = new Handler(Looper.getMainLooper());
|
||||
public static final Integer INPUT_DELAY = 250;
|
||||
|
||||
private BarcodeSelectorAdapter mAdapter;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -56,21 +49,10 @@ public class BarcodeSelectorActivity extends CatimaAppCompatActivity {
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
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);
|
||||
ListView mBarcodeList = findViewById(R.id.barcodes);
|
||||
mAdapter = new BarcodeSelectorAdapter(this, new ArrayList<>(), this);
|
||||
mBarcodeList.setAdapter(mAdapter);
|
||||
|
||||
cardId.addTextChangedListener(new SimpleTextWatcher() {
|
||||
@Override
|
||||
@@ -103,16 +85,13 @@ public class BarcodeSelectorActivity extends CatimaAppCompatActivity {
|
||||
}
|
||||
|
||||
private void generateBarcodes(String value) {
|
||||
// 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()) {
|
||||
ImageView image = findViewById(entry.getValue().first);
|
||||
TextView text = findViewById(entry.getValue().second);
|
||||
createBarcodeOption(image, entry.getKey(), value, text);
|
||||
ArrayList<CatimaBarcodeWithValue> barcodes = new ArrayList<>();
|
||||
for (BarcodeFormat barcodeFormat : CatimaBarcode.barcodeFormats) {
|
||||
CatimaBarcode catimaBarcode = CatimaBarcode.fromBarcode(barcodeFormat);
|
||||
barcodes.add(new CatimaBarcodeWithValue(catimaBarcode, value));
|
||||
}
|
||||
mAdapter.setBarcodes(barcodes);
|
||||
}
|
||||
|
||||
private void setButtonListener(final View button, final String cardId) {
|
||||
@@ -126,48 +105,6 @@ public class BarcodeSelectorActivity extends CatimaAppCompatActivity {
|
||||
});
|
||||
}
|
||||
|
||||
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(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 (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);
|
||||
|
||||
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 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) {
|
||||
@@ -178,4 +115,26 @@ public class BarcodeSelectorActivity extends CatimaAppCompatActivity {
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRowClicked(int inputPosition, View view) {
|
||||
CatimaBarcodeWithValue barcodeWithValue = mAdapter.getItem(inputPosition);
|
||||
CatimaBarcode catimaBarcode = barcodeWithValue.catimaBarcode();
|
||||
|
||||
if (!mAdapter.isValid(view)) {
|
||||
Toast.makeText(this, getString(R.string.wrongValueForBarcodeType), Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
String barcodeFormat = catimaBarcode.format().name();
|
||||
String value = barcodeWithValue.value();
|
||||
|
||||
Log.d(TAG, "Selected barcode type " + barcodeFormat);
|
||||
|
||||
Intent result = new Intent();
|
||||
result.putExtra(BARCODE_FORMAT, barcodeFormat);
|
||||
result.putExtra(BARCODE_CONTENTS, value);
|
||||
BarcodeSelectorActivity.this.setResult(RESULT_OK, result);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import protect.card_locker.async.TaskHandler;
|
||||
|
||||
public class BarcodeSelectorAdapter extends ArrayAdapter<CatimaBarcodeWithValue> {
|
||||
private static final String TAG = "Catima";
|
||||
|
||||
private final TaskHandler mTasks = new TaskHandler();
|
||||
private final BarcodeSelectorListener mListener;
|
||||
|
||||
private static class ViewHolder {
|
||||
ImageView image;
|
||||
TextView text;
|
||||
}
|
||||
|
||||
public interface BarcodeSelectorListener {
|
||||
void onRowClicked(int inputPosition, View view);
|
||||
}
|
||||
|
||||
public BarcodeSelectorAdapter(Context context, ArrayList<CatimaBarcodeWithValue> barcodes, BarcodeSelectorListener barcodeSelectorListener) {
|
||||
super(context, 0, barcodes);
|
||||
mListener = barcodeSelectorListener;
|
||||
}
|
||||
|
||||
public void setBarcodes(ArrayList<CatimaBarcodeWithValue> barcodes) {
|
||||
clear();
|
||||
addAll(barcodes);
|
||||
notifyDataSetChanged();
|
||||
mTasks.flushTaskList(TaskHandler.TYPE.BARCODE, true, false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
CatimaBarcodeWithValue catimaBarcodeWithValue = getItem(position);
|
||||
CatimaBarcode catimaBarcode = catimaBarcodeWithValue.catimaBarcode();
|
||||
String value = catimaBarcodeWithValue.value();
|
||||
|
||||
ViewHolder viewHolder;
|
||||
if (convertView == null) {
|
||||
viewHolder = new ViewHolder();
|
||||
LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||
convertView = inflater.inflate(R.layout.barcode_layout, parent, false);
|
||||
viewHolder.image = convertView.findViewById(R.id.barcodeImage);
|
||||
viewHolder.text = convertView.findViewById(R.id.barcodeName);
|
||||
convertView.setTag(viewHolder);
|
||||
} else {
|
||||
viewHolder = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
|
||||
createBarcodeOption(viewHolder.image, catimaBarcode.format().name(), value, viewHolder.text);
|
||||
|
||||
View finalConvertView = convertView;
|
||||
convertView.setOnClickListener(view -> mListener.onRowClicked(position, finalConvertView));
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
public boolean isValid(View view) {
|
||||
ViewHolder viewHolder = (ViewHolder) view.getTag();
|
||||
return viewHolder.image.getTag() != null && (boolean) viewHolder.image.getTag();
|
||||
}
|
||||
|
||||
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.setClipToOutline(true);
|
||||
|
||||
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);
|
||||
|
||||
Log.d(TAG, "Generating barcode for type " + formatType);
|
||||
|
||||
BarcodeImageWriterTask barcodeWriter = new BarcodeImageWriterTask(getContext(), image, cardId, format, text, true, null, true);
|
||||
mTasks.executeTask(TaskHandler.TYPE.BARCODE, barcodeWriter);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Log.d(TAG, "Generating barcode for type " + formatType);
|
||||
BarcodeImageWriterTask barcodeWriter = new BarcodeImageWriterTask(getContext(), image, cardId, format, text, true, null, true);
|
||||
mTasks.executeTask(TaskHandler.TYPE.BARCODE, barcodeWriter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,17 @@ import android.database.Cursor;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public abstract class BaseCursorAdapter<V extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<V> {
|
||||
private Cursor mCursor;
|
||||
public Cursor mCursor;
|
||||
private boolean mDataValid;
|
||||
private int mRowIDColumn;
|
||||
|
||||
public BaseCursorAdapter(Cursor inputCursor) {
|
||||
private String mRowIDColumnName;
|
||||
|
||||
public BaseCursorAdapter(Cursor inputCursor, String rowIDColumnName) {
|
||||
setHasStableIds(true);
|
||||
|
||||
mRowIDColumnName = rowIDColumnName;
|
||||
|
||||
swapCursor(inputCursor);
|
||||
}
|
||||
|
||||
@@ -58,6 +63,7 @@ public abstract class BaseCursorAdapter<V extends RecyclerView.ViewHolder> exten
|
||||
|
||||
if (inputCursor != null) {
|
||||
mCursor = inputCursor;
|
||||
mRowIDColumn = mCursor.getColumnIndex(mRowIDColumnName);
|
||||
mDataValid = true;
|
||||
notifyDataSetChanged();
|
||||
} else {
|
||||
|
||||
@@ -1,68 +1,69 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.Toast;
|
||||
|
||||
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.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* The configuration screen for creating a shortcut.
|
||||
*/
|
||||
public class CardShortcutConfigure extends AppCompatActivity implements LoyaltyCardCursorAdapter.CardAdapterListener {
|
||||
public class CardShortcutConfigure extends CatimaAppCompatActivity implements LoyaltyCardCursorAdapter.CardAdapterListener {
|
||||
static final String TAG = "Catima";
|
||||
final DBHelper mDb = new DBHelper(this);
|
||||
private SQLiteDatabase mDatabase;
|
||||
private LoyaltyCardCursorAdapter mAdapter;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
|
||||
mDatabase = new DBHelper(this).getReadableDatabase();
|
||||
|
||||
// Set the result to CANCELED. This will cause nothing to happen if the
|
||||
// aback button is pressed.
|
||||
setResult(RESULT_CANCELED);
|
||||
|
||||
setContentView(R.layout.main_activity);
|
||||
setContentView(R.layout.simple_toolbar_list_activity);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
toolbar.setVisibility(View.GONE);
|
||||
|
||||
// Hide new button because it won't work here anyway
|
||||
FloatingActionButton newFab = findViewById(R.id.fabAdd);
|
||||
newFab.setVisibility(View.GONE);
|
||||
|
||||
final DBHelper db = new DBHelper(this);
|
||||
toolbar.setTitle(R.string.shortcutSelectCard);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
// If there are no cards, bail
|
||||
if (db.getLoyaltyCardCount() == 0) {
|
||||
int cardCount = DBHelper.getLoyaltyCardCount(mDatabase);
|
||||
if (cardCount == 0) {
|
||||
Toast.makeText(this, R.string.noCardsMessage, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
}
|
||||
|
||||
// If all cards are archived, bail
|
||||
if (DBHelper.getArchivedCardsCount(mDatabase) == cardCount) {
|
||||
Toast.makeText(this, R.string.noUnarchivedCardsMessage, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
}
|
||||
|
||||
final RecyclerView cardList = findViewById(R.id.list);
|
||||
GridLayoutManager layoutManager = (GridLayoutManager) cardList.getLayoutManager();
|
||||
if (layoutManager != null) {
|
||||
layoutManager.setSpanCount(getResources().getInteger(R.integer.main_view_card_columns));
|
||||
}
|
||||
|
||||
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
|
||||
cardList.setLayoutManager(mLayoutManager);
|
||||
cardList.setItemAnimator(new DefaultItemAnimator());
|
||||
|
||||
cardList.setVisibility(View.VISIBLE);
|
||||
|
||||
Cursor cardCursor = db.getLoyaltyCardCursor();
|
||||
|
||||
final LoyaltyCardCursorAdapter adapter = new LoyaltyCardCursorAdapter(this, cardCursor, this);
|
||||
cardList.setAdapter(adapter);
|
||||
Cursor cardCursor = DBHelper.getLoyaltyCardCursor(mDatabase, DBHelper.LoyaltyCardArchiveFilter.Unarchived);
|
||||
mAdapter = new LoyaltyCardCursorAdapter(this, cardCursor, this);
|
||||
cardList.setAdapter(mAdapter);
|
||||
}
|
||||
|
||||
private void onClickAction(int position) {
|
||||
Cursor selected = mDb.getLoyaltyCardCursor();
|
||||
Cursor selected = DBHelper.getLoyaltyCardCursor(mDatabase, DBHelper.LoyaltyCardArchiveFilter.Unarchived);
|
||||
selected.moveToPosition(position);
|
||||
LoyaltyCard loyaltyCard = LoyaltyCard.toLoyaltyCard(selected);
|
||||
|
||||
@@ -75,6 +76,26 @@ public class CardShortcutConfigure extends AppCompatActivity implements LoyaltyC
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu inputMenu) {
|
||||
getMenuInflater().inflate(R.menu.card_details_menu, inputMenu);
|
||||
Utils.updateMenuCardDetailsButtonState(inputMenu.findItem(R.id.action_unfold), mAdapter.showingDetails());
|
||||
return super.onCreateOptionsMenu(inputMenu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem inputItem) {
|
||||
int id = inputItem.getItemId();
|
||||
|
||||
if (id == R.id.action_unfold) {
|
||||
mAdapter.showDetails(!mAdapter.showingDetails());
|
||||
invalidateOptionsMenu();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(inputItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRowClicked(int inputPosition) {
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Build;
|
||||
import android.service.controls.Control;
|
||||
import android.service.controls.ControlsProviderService;
|
||||
import android.service.controls.DeviceTypes;
|
||||
import android.service.controls.actions.ControlAction;
|
||||
import android.service.controls.templates.StatelessTemplate;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Flow;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
public class CardsOnPowerScreenService extends ControlsProviderService {
|
||||
|
||||
public static final String PREFIX = "catima-";
|
||||
static final String TAG = "Catima";
|
||||
private SQLiteDatabase mDatabase;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
mDatabase = new DBHelper(this).getReadableDatabase();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Flow.Publisher<Control> createPublisherForAllAvailable() {
|
||||
Cursor loyaltyCardCursor = DBHelper.getLoyaltyCardCursor(mDatabase, DBHelper.LoyaltyCardArchiveFilter.Unarchived);
|
||||
return subscriber -> {
|
||||
while (loyaltyCardCursor.moveToNext()) {
|
||||
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(loyaltyCardCursor);
|
||||
Intent openIntent = new Intent(this, LoyaltyCardViewActivity.class)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.putExtra("id", card.id);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(getBaseContext(), card.id, openIntent, PendingIntent.FLAG_IMMUTABLE);
|
||||
subscriber.onNext(
|
||||
new Control.StatelessBuilder(PREFIX + card.id, pendingIntent)
|
||||
.setControlId(PREFIX + card.id)
|
||||
.setTitle(card.store)
|
||||
.setDeviceType(DeviceTypes.TYPE_GENERIC_OPEN_CLOSE)
|
||||
.setSubtitle(card.note)
|
||||
.setCustomIcon(Icon.createWithBitmap(getIcon(this, card)))
|
||||
.build()
|
||||
);
|
||||
}
|
||||
subscriber.onComplete();
|
||||
};
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Flow.Publisher<Control> createPublisherFor(@NonNull List<String> controlIds) {
|
||||
return subscriber -> {
|
||||
subscriber.onSubscribe(new NoOpSubscription());
|
||||
for (String controlId : controlIds) {
|
||||
Control control;
|
||||
|
||||
try {
|
||||
Integer cardId = this.controlIdToCardId(controlId);
|
||||
LoyaltyCard card = DBHelper.getLoyaltyCard(mDatabase, cardId);
|
||||
Intent openIntent = new Intent(this, LoyaltyCardViewActivity.class)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.putExtra("id", card.id);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(getBaseContext(), card.id, openIntent, PendingIntent.FLAG_IMMUTABLE);
|
||||
control = new Control.StatefulBuilder(controlId, pendingIntent)
|
||||
.setTitle(card.store)
|
||||
.setDeviceType(DeviceTypes.TYPE_GENERIC_OPEN_CLOSE)
|
||||
.setSubtitle(card.note)
|
||||
.setStatus(Control.STATUS_OK)
|
||||
.setControlTemplate(new StatelessTemplate(controlId))
|
||||
.setCustomIcon(Icon.createWithBitmap(getIcon(this, card)))
|
||||
.build();
|
||||
} catch (NullPointerException ignored) {
|
||||
Intent mainScreenIntent = new Intent(this, MainActivity.class)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(getBaseContext(), -1, mainScreenIntent, PendingIntent.FLAG_IMMUTABLE);
|
||||
control = new Control.StatefulBuilder(controlId, pendingIntent)
|
||||
.setStatus(Control.STATUS_NOT_FOUND)
|
||||
.build();
|
||||
}
|
||||
Log.d(TAG, "Dispatching widget " + controlId);
|
||||
subscriber.onNext(control);
|
||||
}
|
||||
subscriber.onComplete();
|
||||
};
|
||||
}
|
||||
|
||||
private Bitmap getIcon(Context context, LoyaltyCard loyaltyCard) {
|
||||
Bitmap cardIcon = Utils.retrieveCardImage(context, loyaltyCard.id, ImageLocationType.icon);
|
||||
|
||||
if (cardIcon != null) {
|
||||
return cardIcon;
|
||||
}
|
||||
|
||||
return Utils.generateIcon(this, loyaltyCard.store, loyaltyCard.headerColor).getLetterTile();
|
||||
}
|
||||
|
||||
private Integer controlIdToCardId(String controlId) {
|
||||
if (controlId == null)
|
||||
return null;
|
||||
if (!controlId.startsWith(PREFIX)) {
|
||||
Log.w(TAG, "Unsupported control ID format: " + controlId);
|
||||
return null;
|
||||
}
|
||||
controlId = controlId.substring(PREFIX.length());
|
||||
try {
|
||||
return Integer.parseInt(controlId);
|
||||
} catch (RuntimeException ex) {
|
||||
Log.e(TAG, "Unsupported control ID format. Expected numeric after prefix, found: " + controlId);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performControlAction(@NonNull String controlId, @NonNull ControlAction action, @NonNull Consumer<Integer> consumer) {
|
||||
consumer.accept(ControlAction.RESPONSE_OK);
|
||||
Intent openIntent = new Intent(this, LoyaltyCardViewActivity.class)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.putExtra("id", controlIdToCardId(controlId));
|
||||
startActivity(openIntent);
|
||||
|
||||
closePowerScreenOnAndroid11();
|
||||
}
|
||||
|
||||
@SuppressLint({"MissingPermission", "deprecation"})
|
||||
private void closePowerScreenOnAndroid11() {
|
||||
// Android 12 will auto-close the power screen, but earlier versions won't
|
||||
// Lint complains about this but on Android 11 the permission is not needed
|
||||
// On Android 12, we don't need it, and Google will probably get angry if we ask for it
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
|
||||
sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A no-op subscription.
|
||||
* <p>
|
||||
* Flow.Subscriptions are made to last during time and receive periodic updates.
|
||||
* Our app does not require sending periodic updates of loyalty cards, so we are just ignoring anything in the subscription
|
||||
* Also, our db is quick enough to respond that the Publisher is immediately sending and completing data.
|
||||
* This facility is overkill, but if we don't call onSubscribe the service won't work
|
||||
*/
|
||||
private static class NoOpSubscription implements Flow.Subscription {
|
||||
@Override
|
||||
public void request(long l) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,15 @@
|
||||
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 android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
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
|
||||
@@ -22,32 +17,28 @@ public class CatimaAppCompatActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
@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);
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
// XXX splash screen activity has to do this after installing splash screen before view inflate
|
||||
if (!this.getClass().getSimpleName().equals(MainActivity.class.getSimpleName())) {
|
||||
Utils.patchColors(this);
|
||||
}
|
||||
|
||||
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;
|
||||
@Override
|
||||
protected void onPostCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onPostCreate(savedInstanceState);
|
||||
// material 3 designer does not consider status bar colors
|
||||
// XXX changing this in onCreate causes issues with the splash screen activity, so doing this here
|
||||
boolean darkMode = Utils.isDarkModeEnabled(this);
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
getWindow().setStatusBarColor(Color.TRANSPARENT);
|
||||
getWindow().getDecorView().setSystemUiVisibility(darkMode ? 0 : View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
|
||||
} else {
|
||||
// icons are always white back then
|
||||
getWindow().setStatusBarColor(darkMode ? Color.TRANSPARENT : Color.argb(127, 0, 0, 0));
|
||||
}
|
||||
// XXX android 9 and below has a nasty rendering bug if the theme was patched earlier
|
||||
Utils.postPatchColors(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ public class CatimaBarcode {
|
||||
public static final List<BarcodeFormat> barcodeFormats = Collections.unmodifiableList(Arrays.asList(
|
||||
BarcodeFormat.AZTEC,
|
||||
BarcodeFormat.CODE_39,
|
||||
BarcodeFormat.CODE_93,
|
||||
BarcodeFormat.CODE_128,
|
||||
BarcodeFormat.CODABAR,
|
||||
BarcodeFormat.DATA_MATRIX,
|
||||
@@ -25,6 +26,7 @@ public class CatimaBarcode {
|
||||
public static final List<String> barcodePrettyNames = Collections.unmodifiableList(Arrays.asList(
|
||||
"Aztec",
|
||||
"Code 39",
|
||||
"Code 93",
|
||||
"Code 128",
|
||||
"Codabar",
|
||||
"Data Matrix",
|
||||
@@ -70,6 +72,11 @@ public class CatimaBarcode {
|
||||
|| mBarcodeFormat == BarcodeFormat.QR_CODE;
|
||||
}
|
||||
|
||||
public boolean hasInternalPadding() {
|
||||
return mBarcodeFormat == BarcodeFormat.PDF_417
|
||||
|| mBarcodeFormat == BarcodeFormat.QR_CODE;
|
||||
}
|
||||
|
||||
public BarcodeFormat format() {
|
||||
return mBarcodeFormat;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package protect.card_locker;
|
||||
|
||||
public class CatimaBarcodeWithValue {
|
||||
private final CatimaBarcode mCatimaBarcode;
|
||||
private final String mValue;
|
||||
|
||||
public CatimaBarcodeWithValue(CatimaBarcode catimaBarcode, String value) {
|
||||
mCatimaBarcode = catimaBarcode;
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public CatimaBarcode catimaBarcode() {
|
||||
return mCatimaBarcode;
|
||||
}
|
||||
|
||||
public String value() {
|
||||
return mValue;
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,25 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.journeyapps.barcodescanner.CaptureManager;
|
||||
import com.journeyapps.barcodescanner.DecoratedBarcodeView;
|
||||
|
||||
public class CatimaCaptureManager extends CaptureManager {
|
||||
private Activity activity;
|
||||
private final Context mContext;
|
||||
|
||||
public CatimaCaptureManager(Activity activity, DecoratedBarcodeView barcodeView) {
|
||||
super(activity, barcodeView);
|
||||
|
||||
this.activity = activity;
|
||||
mContext = activity.getApplicationContext();
|
||||
}
|
||||
|
||||
@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();
|
||||
Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import java.util.List;
|
||||
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 = 14;
|
||||
public static final int DATABASE_VERSION = 15;
|
||||
|
||||
public static class LoyaltyCardDbGroups {
|
||||
public static final String TABLE = "groups";
|
||||
@@ -45,6 +45,7 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
public static final String STAR_STATUS = "starstatus";
|
||||
public static final String LAST_USED = "lastused";
|
||||
public static final String ZOOM_LEVEL = "zoomlevel";
|
||||
public static final String ARCHIVE_STATUS = "archive";
|
||||
}
|
||||
|
||||
public static class LoyaltyCardDbIdsGroups {
|
||||
@@ -71,12 +72,14 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
Descending
|
||||
}
|
||||
|
||||
private Context mContext;
|
||||
public enum LoyaltyCardArchiveFilter {
|
||||
All,
|
||||
Archived,
|
||||
Unarchived
|
||||
}
|
||||
|
||||
public DBHelper(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -101,7 +104,8 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
LoyaltyCardDbIds.BARCODE_TYPE + " TEXT," +
|
||||
LoyaltyCardDbIds.STAR_STATUS + " INTEGER DEFAULT '0'," +
|
||||
LoyaltyCardDbIds.LAST_USED + " INTEGER DEFAULT '0', " +
|
||||
LoyaltyCardDbIds.ZOOM_LEVEL + " INTEGER DEFAULT '100' )");
|
||||
LoyaltyCardDbIds.ZOOM_LEVEL + " INTEGER DEFAULT '100', " +
|
||||
LoyaltyCardDbIds.ARCHIVE_STATUS + " INTEGER DEFAULT '0' )");
|
||||
|
||||
// create associative table for cards in groups
|
||||
db.execSQL("CREATE TABLE " + LoyaltyCardDbIdsGroups.TABLE + "(" +
|
||||
@@ -115,7 +119,6 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
"tokenize=unicode61);");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
if (oldVersion < 2 && newVersion >= 2) {
|
||||
@@ -307,9 +310,13 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE
|
||||
+ " ADD COLUMN " + LoyaltyCardDbIds.ZOOM_LEVEL + " INTEGER DEFAULT '100' ");
|
||||
}
|
||||
if (oldVersion < 15 && newVersion >= 15) {
|
||||
db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE
|
||||
+ " ADD COLUMN " + LoyaltyCardDbIds.ARCHIVE_STATUS + " INTEGER DEFAULT '0' ");
|
||||
}
|
||||
}
|
||||
|
||||
private ContentValues generateFTSContentValues(final int id, final String store, final String note) {
|
||||
private static 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
|
||||
@@ -341,22 +348,21 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
return ftsContentValues;
|
||||
}
|
||||
|
||||
private void insertFTS(final SQLiteDatabase db, final int id, final String store, final String note) {
|
||||
private static 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) {
|
||||
private static 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 CatimaBarcode barcodeType, final Integer headerColor,
|
||||
final int starStatus, final Long lastUsed) {
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
db.beginTransaction();
|
||||
public static long insertLoyaltyCard(
|
||||
final SQLiteDatabase database, 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,final int archiveStatus) {
|
||||
database.beginTransaction();
|
||||
|
||||
// Card
|
||||
ContentValues contentValues = new ContentValues();
|
||||
@@ -371,56 +377,24 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
|
||||
contentValues.put(LoyaltyCardDbIds.STAR_STATUS, starStatus);
|
||||
contentValues.put(LoyaltyCardDbIds.LAST_USED, lastUsed != null ? lastUsed : Utils.getUnixTime());
|
||||
long id = db.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
|
||||
contentValues.put(LoyaltyCardDbIds.ARCHIVE_STATUS, archiveStatus);
|
||||
long id = database.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
|
||||
|
||||
// FTS
|
||||
insertFTS(db, (int) id, store, note);
|
||||
insertFTS(database, (int) id, store, note);
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
database.setTransactionSuccessful();
|
||||
database.endTransaction();
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
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);
|
||||
contentValues.put(LoyaltyCardDbIds.EXPIRY, expiry != null ? expiry.getTime() : null);
|
||||
contentValues.put(LoyaltyCardDbIds.BALANCE, balance.toString());
|
||||
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.name() : null);
|
||||
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
|
||||
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 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();
|
||||
public static long insertLoyaltyCard(
|
||||
final SQLiteDatabase database, 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, final int archiveStatus) {
|
||||
database.beginTransaction();
|
||||
|
||||
// Card
|
||||
ContentValues contentValues = new ContentValues();
|
||||
@@ -436,24 +410,24 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
|
||||
contentValues.put(LoyaltyCardDbIds.STAR_STATUS, starStatus);
|
||||
contentValues.put(LoyaltyCardDbIds.LAST_USED, lastUsed != null ? lastUsed : Utils.getUnixTime());
|
||||
db.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
|
||||
contentValues.put(LoyaltyCardDbIds.ARCHIVE_STATUS, archiveStatus);
|
||||
database.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
|
||||
|
||||
// FTS
|
||||
insertFTS(db, id, store, note);
|
||||
insertFTS(database, id, store, note);
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
database.setTransactionSuccessful();
|
||||
database.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 CatimaBarcode barcodeType,
|
||||
final Integer headerColor) {
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
db.beginTransaction();
|
||||
public static boolean updateLoyaltyCard(
|
||||
SQLiteDatabase database, 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) {
|
||||
database.beginTransaction();
|
||||
|
||||
// Card
|
||||
ContentValues contentValues = new ContentValues();
|
||||
@@ -466,53 +440,58 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
contentValues.put(LoyaltyCardDbIds.BARCODE_ID, barcodeId);
|
||||
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType != null ? barcodeType.name() : null);
|
||||
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
|
||||
int rowsUpdated = db.update(LoyaltyCardDbIds.TABLE, contentValues,
|
||||
int rowsUpdated = database.update(LoyaltyCardDbIds.TABLE, contentValues,
|
||||
whereAttrs(LoyaltyCardDbIds.ID), withArgs(id));
|
||||
|
||||
// FTS
|
||||
updateFTS(db, id, store, note);
|
||||
updateFTS(database, id, store, note);
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
database.setTransactionSuccessful();
|
||||
database.endTransaction();
|
||||
|
||||
return (rowsUpdated == 1);
|
||||
}
|
||||
|
||||
public boolean updateLoyaltyCardStarStatus(final int id, final int starStatus) {
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
public static boolean updateLoyaltyCardArchiveStatus(SQLiteDatabase database, final int id, final int archiveStatus) {
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(LoyaltyCardDbIds.ARCHIVE_STATUS, archiveStatus);
|
||||
int rowsUpdated = database.update(LoyaltyCardDbIds.TABLE, contentValues,
|
||||
whereAttrs(LoyaltyCardDbIds.ID),
|
||||
withArgs(id));
|
||||
return (rowsUpdated == 1);
|
||||
}
|
||||
|
||||
public static boolean updateLoyaltyCardStarStatus(SQLiteDatabase database, final int id, final int starStatus) {
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(LoyaltyCardDbIds.STAR_STATUS, starStatus);
|
||||
int rowsUpdated = db.update(LoyaltyCardDbIds.TABLE, contentValues,
|
||||
int rowsUpdated = database.update(LoyaltyCardDbIds.TABLE, contentValues,
|
||||
whereAttrs(LoyaltyCardDbIds.ID),
|
||||
withArgs(id));
|
||||
return (rowsUpdated == 1);
|
||||
}
|
||||
|
||||
public boolean updateLoyaltyCardLastUsed(final int id) {
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
public static boolean updateLoyaltyCardLastUsed(SQLiteDatabase database, final int id) {
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(LoyaltyCardDbIds.LAST_USED, System.currentTimeMillis() / 1000);
|
||||
int rowsUpdated = db.update(LoyaltyCardDbIds.TABLE, contentValues,
|
||||
int rowsUpdated = database.update(LoyaltyCardDbIds.TABLE, contentValues,
|
||||
whereAttrs(LoyaltyCardDbIds.ID),
|
||||
withArgs(id));
|
||||
return (rowsUpdated == 1);
|
||||
}
|
||||
|
||||
public boolean updateLoyaltyCardZoomLevel(int loyaltyCardId, int zoomLevel) {
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
public static boolean updateLoyaltyCardZoomLevel(SQLiteDatabase database, int loyaltyCardId, int zoomLevel) {
|
||||
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,
|
||||
int rowsUpdated = database.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.query(LoyaltyCardDbIds.TABLE, null, whereAttrs(LoyaltyCardDbIds.ID), withArgs(id), null, null, null);
|
||||
public static LoyaltyCard getLoyaltyCard(SQLiteDatabase database, final int id) {
|
||||
Cursor data = database.query(LoyaltyCardDbIds.TABLE, null, whereAttrs(LoyaltyCardDbIds.ID), withArgs(id), null, null, null);
|
||||
|
||||
LoyaltyCard card = null;
|
||||
|
||||
@@ -526,9 +505,8 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
return card;
|
||||
}
|
||||
|
||||
public List<Group> getLoyaltyCardGroups(final int id) {
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
Cursor data = db.rawQuery("select * from " + LoyaltyCardDbGroups.TABLE + " g " +
|
||||
public static List<Group> getLoyaltyCardGroups(SQLiteDatabase database, final int id) {
|
||||
Cursor data = database.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, withArgs(id));
|
||||
@@ -551,11 +529,9 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
return groups;
|
||||
}
|
||||
|
||||
public void setLoyaltyCardGroups(final int id, List<Group> groups) {
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
|
||||
public static void setLoyaltyCardGroups(SQLiteDatabase database, final int id, List<Group> groups) {
|
||||
// First delete lookup table entries associated with this card
|
||||
db.delete(LoyaltyCardDbIdsGroups.TABLE,
|
||||
database.delete(LoyaltyCardDbIdsGroups.TABLE,
|
||||
whereAttrs(LoyaltyCardDbIdsGroups.cardID),
|
||||
withArgs(id));
|
||||
|
||||
@@ -564,46 +540,30 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(LoyaltyCardDbIdsGroups.cardID, id);
|
||||
contentValues.put(LoyaltyCardDbIdsGroups.groupID, group._id);
|
||||
db.insert(LoyaltyCardDbIdsGroups.TABLE, null, contentValues);
|
||||
database.insert(LoyaltyCardDbIdsGroups.TABLE, null, contentValues);
|
||||
}
|
||||
}
|
||||
|
||||
public void setLoyaltyCardGroups(final SQLiteDatabase db, final int id, List<Group> groups) {
|
||||
// First delete lookup table entries associated with this card
|
||||
db.delete(LoyaltyCardDbIdsGroups.TABLE,
|
||||
whereAttrs(LoyaltyCardDbIdsGroups.cardID),
|
||||
withArgs(id));
|
||||
|
||||
// Then create entries for selected values
|
||||
for (Group group : groups) {
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(LoyaltyCardDbIdsGroups.cardID, id);
|
||||
contentValues.put(LoyaltyCardDbIdsGroups.groupID, group._id);
|
||||
db.insert(LoyaltyCardDbIdsGroups.TABLE, null, contentValues);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean deleteLoyaltyCard(final int id) {
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
public static boolean deleteLoyaltyCard(SQLiteDatabase database, Context context, final int id) {
|
||||
// Delete card
|
||||
int rowsDeleted = db.delete(LoyaltyCardDbIds.TABLE,
|
||||
int rowsDeleted = database.delete(LoyaltyCardDbIds.TABLE,
|
||||
whereAttrs(LoyaltyCardDbIds.ID),
|
||||
withArgs(id));
|
||||
|
||||
// And delete lookup table entries associated with this card
|
||||
db.delete(LoyaltyCardDbIdsGroups.TABLE,
|
||||
database.delete(LoyaltyCardDbIdsGroups.TABLE,
|
||||
whereAttrs(LoyaltyCardDbIdsGroups.cardID),
|
||||
withArgs(id));
|
||||
|
||||
// Delete FTS table entries
|
||||
db.delete(LoyaltyCardDbFTS.TABLE,
|
||||
database.delete(LoyaltyCardDbFTS.TABLE,
|
||||
whereAttrs(LoyaltyCardDbFTS.ID),
|
||||
withArgs(id));
|
||||
|
||||
// Also wipe card images associated with this card
|
||||
for (ImageLocationType imageLocationType : ImageLocationType.values()) {
|
||||
try {
|
||||
Utils.saveCardImage(mContext, null, id, imageLocationType);
|
||||
Utils.saveCardImage(context, null, id, imageLocationType);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -612,9 +572,19 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
return (rowsDeleted == 1);
|
||||
}
|
||||
|
||||
public Cursor getLoyaltyCardCursor() {
|
||||
public static int getArchivedCardsCount(SQLiteDatabase database) {
|
||||
return (int) DatabaseUtils.queryNumEntries(database, LoyaltyCardDbIds.TABLE,
|
||||
whereAttrs(LoyaltyCardDbIds.ARCHIVE_STATUS), withArgs(1));
|
||||
}
|
||||
|
||||
public static Cursor getLoyaltyCardCursor(SQLiteDatabase database) {
|
||||
// An empty string will match everything
|
||||
return getLoyaltyCardCursor("");
|
||||
return getLoyaltyCardCursor(database, LoyaltyCardArchiveFilter.All);
|
||||
}
|
||||
|
||||
public static Cursor getLoyaltyCardCursor(SQLiteDatabase database, LoyaltyCardArchiveFilter archiveFilter) {
|
||||
// An empty string will match everything
|
||||
return getLoyaltyCardCursor(database, "", archiveFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -623,8 +593,8 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
* @param filter
|
||||
* @return Cursor
|
||||
*/
|
||||
public Cursor getLoyaltyCardCursor(final String filter) {
|
||||
return getLoyaltyCardCursor(filter, null);
|
||||
public static Cursor getLoyaltyCardCursor(SQLiteDatabase database, final String filter, LoyaltyCardArchiveFilter archiveFilter) {
|
||||
return getLoyaltyCardCursor(database, filter, null, archiveFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -634,8 +604,8 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
* @param group
|
||||
* @return Cursor
|
||||
*/
|
||||
public Cursor getLoyaltyCardCursor(final String filter, Group group) {
|
||||
return getLoyaltyCardCursor(filter, group, LoyaltyCardOrder.Alpha, LoyaltyCardOrderDirection.Ascending);
|
||||
public static Cursor getLoyaltyCardCursor(SQLiteDatabase database, final String filter, Group group, LoyaltyCardArchiveFilter archiveFilter) {
|
||||
return getLoyaltyCardCursor(database, filter, group, LoyaltyCardOrder.Alpha, LoyaltyCardOrderDirection.Ascending, archiveFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -646,14 +616,12 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
* @param order
|
||||
* @return Cursor
|
||||
*/
|
||||
public Cursor getLoyaltyCardCursor(String filter, Group group, LoyaltyCardOrder order, LoyaltyCardOrderDirection direction) {
|
||||
public static Cursor getLoyaltyCardCursor(SQLiteDatabase database, String filter, Group group, LoyaltyCardOrder order, LoyaltyCardOrderDirection direction, LoyaltyCardArchiveFilter archiveFilter) {
|
||||
StringBuilder groupFilter = new StringBuilder();
|
||||
String limitString = "";
|
||||
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
|
||||
if (group != null) {
|
||||
List<Integer> allowedIds = getGroupCardIds(group._id);
|
||||
List<Integer> allowedIds = getGroupCardIds(database, group._id);
|
||||
|
||||
// Empty group
|
||||
if (!allowedIds.isEmpty()) {
|
||||
@@ -671,14 +639,21 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
}
|
||||
}
|
||||
|
||||
String archiveFilterString = "";
|
||||
if (archiveFilter != LoyaltyCardArchiveFilter.All) {
|
||||
archiveFilterString = " AND " + LoyaltyCardDbIds.TABLE + "." + LoyaltyCardDbIds.ARCHIVE_STATUS + " = " + (archiveFilter.equals(LoyaltyCardArchiveFilter.Unarchived) ? 0 : 1);
|
||||
}
|
||||
|
||||
String orderField = getFieldForOrder(order);
|
||||
|
||||
return db.rawQuery("SELECT " + LoyaltyCardDbIds.TABLE + ".* FROM " + LoyaltyCardDbIds.TABLE +
|
||||
return database.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.TABLE + "." + LoyaltyCardDbIds.STAR_STATUS + " DESC, " +
|
||||
archiveFilterString +
|
||||
" ORDER BY " + LoyaltyCardDbIds.TABLE + "." + LoyaltyCardDbIds.ARCHIVE_STATUS + " ASC, " +
|
||||
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 " +
|
||||
@@ -690,9 +665,8 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
*
|
||||
* @return Integer
|
||||
*/
|
||||
public int getLoyaltyCardCount() {
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
return (int) DatabaseUtils.queryNumEntries(db, LoyaltyCardDbIds.TABLE);
|
||||
public static int getLoyaltyCardCount(SQLiteDatabase database) {
|
||||
return (int) DatabaseUtils.queryNumEntries(database, LoyaltyCardDbIds.TABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -700,15 +674,13 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
*
|
||||
* @return Cursor
|
||||
*/
|
||||
public Cursor getGroupCursor() {
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
|
||||
return db.rawQuery("select * from " + LoyaltyCardDbGroups.TABLE +
|
||||
public static Cursor getGroupCursor(SQLiteDatabase database) {
|
||||
return database.rawQuery("select * from " + LoyaltyCardDbGroups.TABLE +
|
||||
" ORDER BY " + LoyaltyCardDbGroups.ORDER + " ASC," + LoyaltyCardDbGroups.ID + " COLLATE NOCASE ASC", null, null);
|
||||
}
|
||||
|
||||
public List<Group> getGroups() {
|
||||
Cursor data = getGroupCursor();
|
||||
public static List<Group> getGroups(SQLiteDatabase database) {
|
||||
Cursor data = getGroupCursor(database);
|
||||
|
||||
List<Group> groups = new ArrayList<>();
|
||||
|
||||
@@ -726,15 +698,14 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
return groups;
|
||||
}
|
||||
|
||||
public void reorderGroups(final List<Group> groups) {
|
||||
public static void reorderGroups(SQLiteDatabase database, final List<Group> groups) {
|
||||
Integer order = 0;
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
|
||||
for (Group group : groups) {
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(LoyaltyCardDbGroups.ORDER, order);
|
||||
|
||||
db.update(LoyaltyCardDbGroups.TABLE, contentValues,
|
||||
database.update(LoyaltyCardDbGroups.TABLE, contentValues,
|
||||
whereAttrs(LoyaltyCardDbGroups.ID),
|
||||
withArgs(group._id));
|
||||
|
||||
@@ -742,9 +713,8 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
}
|
||||
}
|
||||
|
||||
public Group getGroup(final String groupName) {
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
Cursor data = db.query(LoyaltyCardDbGroups.TABLE, null,
|
||||
public static Group getGroup(SQLiteDatabase database, final String groupName) {
|
||||
Cursor data = database.query(LoyaltyCardDbGroups.TABLE, null,
|
||||
whereAttrs(LoyaltyCardDbGroups.ID), withArgs(groupName), null, null, null);
|
||||
|
||||
Group group = null;
|
||||
@@ -757,14 +727,12 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
return group;
|
||||
}
|
||||
|
||||
public int getGroupCount() {
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
return (int) DatabaseUtils.queryNumEntries(db, LoyaltyCardDbGroups.TABLE);
|
||||
public static int getGroupCount(SQLiteDatabase database) {
|
||||
return (int) DatabaseUtils.queryNumEntries(database, LoyaltyCardDbGroups.TABLE);
|
||||
}
|
||||
|
||||
public List<Integer> getGroupCardIds(final String groupName) {
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
Cursor data = db.query(LoyaltyCardDbIdsGroups.TABLE, withArgs(LoyaltyCardDbIdsGroups.cardID),
|
||||
public static List<Integer> getGroupCardIds(SQLiteDatabase database, final String groupName) {
|
||||
Cursor data = database.query(LoyaltyCardDbIdsGroups.TABLE, withArgs(LoyaltyCardDbIdsGroups.cardID),
|
||||
whereAttrs(LoyaltyCardDbIdsGroups.groupID), withArgs(groupName), null, null, null);
|
||||
List<Integer> cardIds = new ArrayList<>();
|
||||
|
||||
@@ -783,99 +751,85 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
return cardIds;
|
||||
}
|
||||
|
||||
public long insertGroup(final String name) {
|
||||
public static long insertGroup(SQLiteDatabase database, final String name) {
|
||||
if (name.isEmpty()) return -1;
|
||||
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(LoyaltyCardDbGroups.ID, name);
|
||||
contentValues.put(LoyaltyCardDbGroups.ORDER, getGroupCount());
|
||||
return db.insert(LoyaltyCardDbGroups.TABLE, null, contentValues);
|
||||
contentValues.put(LoyaltyCardDbGroups.ORDER, getGroupCount(database));
|
||||
return database.insert(LoyaltyCardDbGroups.TABLE, null, contentValues);
|
||||
}
|
||||
|
||||
public boolean insertGroup(final SQLiteDatabase db, final String name) {
|
||||
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 != -1;
|
||||
}
|
||||
|
||||
public boolean updateGroup(final String groupName, final String newName) {
|
||||
public static boolean updateGroup(SQLiteDatabase database, final String groupName, final String newName) {
|
||||
if (newName.isEmpty()) return false;
|
||||
|
||||
boolean success = false;
|
||||
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
ContentValues groupContentValues = new ContentValues();
|
||||
groupContentValues.put(LoyaltyCardDbGroups.ID, newName);
|
||||
|
||||
ContentValues lookupContentValues = new ContentValues();
|
||||
lookupContentValues.put(LoyaltyCardDbIdsGroups.groupID, newName);
|
||||
|
||||
db.beginTransaction();
|
||||
database.beginTransaction();
|
||||
try {
|
||||
// Update group name
|
||||
int groupsChanged = db.update(LoyaltyCardDbGroups.TABLE, groupContentValues,
|
||||
int groupsChanged = database.update(LoyaltyCardDbGroups.TABLE, groupContentValues,
|
||||
whereAttrs(LoyaltyCardDbGroups.ID),
|
||||
withArgs(groupName));
|
||||
|
||||
// Also update lookup tables
|
||||
db.update(LoyaltyCardDbIdsGroups.TABLE, lookupContentValues,
|
||||
database.update(LoyaltyCardDbIdsGroups.TABLE, lookupContentValues,
|
||||
whereAttrs(LoyaltyCardDbIdsGroups.groupID),
|
||||
withArgs(groupName));
|
||||
|
||||
if (groupsChanged == 1) {
|
||||
db.setTransactionSuccessful();
|
||||
database.setTransactionSuccessful();
|
||||
success = true;
|
||||
}
|
||||
} catch (SQLiteException e) {
|
||||
} catch (SQLiteException ignored) {
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
database.endTransaction();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public boolean deleteGroup(final String groupName) {
|
||||
public static boolean deleteGroup(SQLiteDatabase database, final String groupName) {
|
||||
boolean success = false;
|
||||
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
|
||||
db.beginTransaction();
|
||||
database.beginTransaction();
|
||||
try {
|
||||
// Delete group
|
||||
int groupsDeleted = db.delete(LoyaltyCardDbGroups.TABLE,
|
||||
int groupsDeleted = database.delete(LoyaltyCardDbGroups.TABLE,
|
||||
whereAttrs(LoyaltyCardDbGroups.ID),
|
||||
withArgs(groupName));
|
||||
|
||||
// And delete lookup table entries associated with this group
|
||||
db.delete(LoyaltyCardDbIdsGroups.TABLE,
|
||||
database.delete(LoyaltyCardDbIdsGroups.TABLE,
|
||||
whereAttrs(LoyaltyCardDbIdsGroups.groupID),
|
||||
withArgs(groupName));
|
||||
|
||||
if (groupsDeleted == 1) {
|
||||
db.setTransactionSuccessful();
|
||||
database.setTransactionSuccessful();
|
||||
success = true;
|
||||
}
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
database.endTransaction();
|
||||
}
|
||||
|
||||
// Reorder after delete to ensure no bad order IDs
|
||||
reorderGroups(getGroups());
|
||||
reorderGroups(database, getGroups(database));
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public int getGroupCardCount(final String groupName) {
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
|
||||
return (int) DatabaseUtils.queryNumEntries(db, LoyaltyCardDbIdsGroups.TABLE,
|
||||
public static int getGroupCardCount(SQLiteDatabase database, final String groupName) {
|
||||
return (int) DatabaseUtils.queryNumEntries(database, LoyaltyCardDbIdsGroups.TABLE,
|
||||
whereAttrs(LoyaltyCardDbIdsGroups.groupID), withArgs(groupName));
|
||||
}
|
||||
|
||||
private String whereAttrs(String... attrs) {
|
||||
static private String whereAttrs(String... attrs) {
|
||||
if (attrs.length == 0) {
|
||||
return null;
|
||||
}
|
||||
@@ -886,13 +840,13 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
return whereClause.toString();
|
||||
}
|
||||
|
||||
private String[] withArgs(Object... object) {
|
||||
static private String[] withArgs(Object... object) {
|
||||
return Arrays.stream(object)
|
||||
.map(String::valueOf)
|
||||
.toArray(String[]::new);
|
||||
}
|
||||
|
||||
private String getFieldForOrder(LoyaltyCardOrder order) {
|
||||
private static String getFieldForOrder(LoyaltyCardOrder order) {
|
||||
if (order == LoyaltyCardOrder.Alpha) {
|
||||
return LoyaltyCardDbIds.STORE;
|
||||
}
|
||||
@@ -908,7 +862,7 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
throw new IllegalArgumentException("Unknown order " + order);
|
||||
}
|
||||
|
||||
private String getDbDirection(LoyaltyCardOrder order, LoyaltyCardOrderDirection direction) {
|
||||
private static 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";
|
||||
|
||||
@@ -2,6 +2,7 @@ package protect.card_locker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -12,28 +13,21 @@ import androidx.appcompat.widget.AppCompatImageButton;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import protect.card_locker.preferences.Settings;
|
||||
|
||||
class GroupCursorAdapter extends BaseCursorAdapter<GroupCursorAdapter.GroupListItemViewHolder> {
|
||||
public class GroupCursorAdapter extends BaseCursorAdapter<GroupCursorAdapter.GroupListItemViewHolder> {
|
||||
Settings mSettings;
|
||||
private Cursor mCursor;
|
||||
private final Context mContext;
|
||||
private final GroupCursorAdapter.GroupAdapterListener mListener;
|
||||
DBHelper mDb;
|
||||
public final Context mContext;
|
||||
private final GroupAdapterListener mListener;
|
||||
SQLiteDatabase mDatabase;
|
||||
|
||||
public GroupCursorAdapter(Context inputContext, Cursor inputCursor, GroupCursorAdapter.GroupAdapterListener inputListener) {
|
||||
super(inputCursor);
|
||||
public GroupCursorAdapter(Context inputContext, Cursor inputCursor, GroupAdapterListener inputListener) {
|
||||
super(inputCursor, DBHelper.LoyaltyCardDbGroups.ORDER);
|
||||
setHasStableIds(true);
|
||||
mSettings = new Settings(inputContext);
|
||||
mContext = inputContext;
|
||||
mListener = inputListener;
|
||||
mDb = new DBHelper(inputContext);
|
||||
mDatabase = new DBHelper(inputContext).getReadableDatabase();
|
||||
|
||||
swapCursor(mCursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void swapCursor(Cursor inputCursor) {
|
||||
super.swapCursor(inputCursor);
|
||||
mCursor = inputCursor;
|
||||
swapCursor(inputCursor);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -43,16 +37,12 @@ class GroupCursorAdapter extends BaseCursorAdapter<GroupCursorAdapter.GroupListI
|
||||
return new GroupListItemViewHolder(itemView);
|
||||
}
|
||||
|
||||
public Cursor getCursor() {
|
||||
return mCursor;
|
||||
}
|
||||
|
||||
public void onBindViewHolder(GroupCursorAdapter.GroupListItemViewHolder inputHolder, Cursor inputCursor) {
|
||||
public void onBindViewHolder(GroupListItemViewHolder inputHolder, Cursor inputCursor) {
|
||||
Group group = Group.toGroup(inputCursor);
|
||||
|
||||
inputHolder.mName.setText(group._id);
|
||||
|
||||
int groupCardCount = mDb.getGroupCardCount(group._id);
|
||||
int groupCardCount = DBHelper.getGroupCardCount(mDatabase, group._id);
|
||||
inputHolder.mCardCount.setText(mContext.getResources().getQuantityString(R.plurals.groupCardCount, groupCardCount, groupCardCount));
|
||||
|
||||
inputHolder.mName.setTextSize(mSettings.getFontSizeMax(mSettings.getMediumFont()));
|
||||
|
||||
@@ -10,14 +10,20 @@ 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 androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -26,21 +32,15 @@ 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.widget.Toolbar;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import protect.card_locker.async.TaskHandler;
|
||||
import protect.card_locker.importexport.DataFormat;
|
||||
import protect.card_locker.importexport.ImportExportResult;
|
||||
import protect.card_locker.importexport.ImportExportResultType;
|
||||
|
||||
public class ImportExportActivity extends CatimaAppCompatActivity {
|
||||
private static final String TAG = "Catima";
|
||||
|
||||
private static final int PERMISSIONS_EXTERNAL_STORAGE = 1;
|
||||
private static final int CHOOSE_EXPORT_LOCATION = 2;
|
||||
private static final int IMPORT = 3;
|
||||
|
||||
private ImportExportTask importExporter;
|
||||
|
||||
@@ -49,6 +49,10 @@ public class ImportExportActivity extends CatimaAppCompatActivity {
|
||||
private DataFormat importDataFormat;
|
||||
private String exportPassword;
|
||||
|
||||
private ActivityResultLauncher<Intent> fileCreateLauncher;
|
||||
private ActivityResultLauncher<String> fileOpenLauncher;
|
||||
private ActivityResultLauncher<Intent> filePickerLauncher;
|
||||
|
||||
final private TaskHandler mTasks = new TaskHandler();
|
||||
|
||||
@Override
|
||||
@@ -76,6 +80,49 @@ public class ImportExportActivity extends CatimaAppCompatActivity {
|
||||
PERMISSIONS_EXTERNAL_STORAGE);
|
||||
}
|
||||
|
||||
// would use ActivityResultContracts.CreateDocument() but mime type cannot be set
|
||||
fileCreateLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
|
||||
Intent intent = result.getData();
|
||||
if (intent == null) {
|
||||
Log.e(TAG, "Activity returned NULL data");
|
||||
return;
|
||||
}
|
||||
Uri uri = intent.getData();
|
||||
if (uri == null) {
|
||||
Log.e(TAG, "Activity returned NULL uri");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
OutputStream writer = getContentResolver().openOutputStream(uri);
|
||||
Log.e(TAG, "Starting file export with: " + result.toString());
|
||||
startExport(writer, uri, exportPassword.toCharArray(), true);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to export file: " + result.toString(), e);
|
||||
onExportComplete(new ImportExportResult(ImportExportResultType.GenericFailure, result.toString()), uri);
|
||||
}
|
||||
|
||||
});
|
||||
fileOpenLauncher = registerForActivityResult(new ActivityResultContracts.GetContent(), result -> {
|
||||
if (result == null) {
|
||||
Log.e(TAG, "Activity returned NULL data");
|
||||
return;
|
||||
}
|
||||
openFileForImport(result, null);
|
||||
});
|
||||
filePickerLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
|
||||
Intent intent = result.getData();
|
||||
if (intent == null) {
|
||||
Log.e(TAG, "Activity returned NULL data");
|
||||
return;
|
||||
}
|
||||
Uri uri = intent.getData();
|
||||
if (uri == null) {
|
||||
Log.e(TAG, "Activity returned NULL uri");
|
||||
return;
|
||||
}
|
||||
openFileForImport(intent.getData(), null);
|
||||
});
|
||||
|
||||
// Check that there is a file manager available
|
||||
final Intent intentCreateDocumentAction = new Intent(Intent.ACTION_CREATE_DOCUMENT);
|
||||
intentCreateDocumentAction.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
@@ -83,60 +130,57 @@ public class ImportExportActivity extends CatimaAppCompatActivity {
|
||||
intentCreateDocumentAction.putExtra(Intent.EXTRA_TITLE, "catima.zip");
|
||||
|
||||
Button exportButton = findViewById(R.id.exportButton);
|
||||
exportButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(ImportExportActivity.this);
|
||||
builder.setTitle(R.string.exportPassword);
|
||||
exportButton.setOnClickListener(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;
|
||||
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);
|
||||
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();
|
||||
container.addView(input);
|
||||
builder.setView(container);
|
||||
builder.setPositiveButton(R.string.ok, (dialogInterface, i) -> {
|
||||
exportPassword = input.getText().toString();
|
||||
try {
|
||||
fileCreateLauncher.launch(intentCreateDocumentAction);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(getApplicationContext(), R.string.failedOpeningFileManager, Toast.LENGTH_LONG).show();
|
||||
Log.e(TAG, "No activity found to handle intent", e);
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(R.string.cancel, (dialogInterface, i) -> dialogInterface.cancel());
|
||||
builder.show();
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// Check that there is a file manager available
|
||||
final Intent intentGetContentAction = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
intentGetContentAction.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intentGetContentAction.setType("*/*");
|
||||
|
||||
Button importFilesystem = findViewById(R.id.importOptionFilesystemButton);
|
||||
importFilesystem.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
chooseImportType(intentGetContentAction);
|
||||
}
|
||||
});
|
||||
importFilesystem.setOnClickListener(v -> chooseImportType(false));
|
||||
|
||||
// Check that there is an app that data can be imported from
|
||||
final Intent intentPickAction = new Intent(Intent.ACTION_PICK);
|
||||
|
||||
Button importApplication = findViewById(R.id.importOptionApplicationButton);
|
||||
importApplication.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
chooseImportType(intentPickAction);
|
||||
}
|
||||
});
|
||||
importApplication.setOnClickListener(v -> chooseImportType(true));
|
||||
}
|
||||
|
||||
private void chooseImportType(Intent baseIntent) {
|
||||
private void openFileForImport(Uri uri, char[] password) {
|
||||
try {
|
||||
InputStream reader = getContentResolver().openInputStream(uri);
|
||||
Log.e(TAG, "Starting file import with: " + uri.toString());
|
||||
startImport(reader, uri, importDataFormat, password, true);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to import file: " + uri.toString(), e);
|
||||
onImportComplete(new ImportExportResult(ImportExportResultType.GenericFailure, e.toString()), uri, importDataFormat);
|
||||
}
|
||||
}
|
||||
|
||||
private void chooseImportType(boolean choosePicker) {
|
||||
List<CharSequence> betaImportOptions = new ArrayList<>();
|
||||
betaImportOptions.add("Fidme");
|
||||
betaImportOptions.add("Stocard");
|
||||
@@ -194,7 +238,17 @@ public class ImportExportActivity extends CatimaAppCompatActivity {
|
||||
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
chooseFileWithIntent(baseIntent, IMPORT);
|
||||
try {
|
||||
if (choosePicker) {
|
||||
final Intent intentPickAction = new Intent(Intent.ACTION_PICK);
|
||||
filePickerLauncher.launch(intentPickAction);
|
||||
} else {
|
||||
fileOpenLauncher.launch("*/*");
|
||||
}
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(getApplicationContext(), R.string.failedOpeningFileManager, Toast.LENGTH_LONG).show();
|
||||
Log.e(TAG, "No activity found to handle intent", e);
|
||||
}
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
@@ -297,63 +351,58 @@ public class ImportExportActivity extends CatimaAppCompatActivity {
|
||||
builder.setView(input);
|
||||
|
||||
builder.setPositiveButton(R.string.ok, (dialogInterface, i) -> {
|
||||
activityResultParser(IMPORT, RESULT_OK, uri, input.getText().toString().toCharArray());
|
||||
openFileForImport(uri, input.getText().toString().toCharArray());
|
||||
});
|
||||
builder.setNegativeButton(R.string.cancel, (dialogInterface, i) -> dialogInterface.cancel());
|
||||
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private String buildResultDialogMessage(ImportExportResult result, boolean isImport) {
|
||||
int messageId;
|
||||
|
||||
if (result.resultType() == ImportExportResultType.Success) {
|
||||
messageId = isImport ? R.string.importSuccessful : R.string.exportSuccessful;
|
||||
} else {
|
||||
messageId = isImport ? R.string.importFailed : R.string.exportFailed;
|
||||
}
|
||||
|
||||
StringBuilder messageBuilder = new StringBuilder(getResources().getString(messageId));
|
||||
if (result.developerDetails() != null) {
|
||||
messageBuilder.append("\n\n");
|
||||
messageBuilder.append(getResources().getString(R.string.include_if_asking_support));
|
||||
messageBuilder.append("\n\n");
|
||||
messageBuilder.append(result.developerDetails());
|
||||
}
|
||||
|
||||
return messageBuilder.toString();
|
||||
}
|
||||
|
||||
private void onImportComplete(ImportExportResult result, Uri path, DataFormat dataFormat) {
|
||||
if (result == ImportExportResult.BadPassword) {
|
||||
ImportExportResultType resultType = result.resultType();
|
||||
|
||||
if (resultType == ImportExportResultType.BadPassword) {
|
||||
retryWithPassword(dataFormat, path);
|
||||
return;
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
|
||||
int messageId;
|
||||
|
||||
if (result == ImportExportResult.Success) {
|
||||
builder.setTitle(R.string.importSuccessfulTitle);
|
||||
messageId = R.string.importSuccessful;
|
||||
} else {
|
||||
builder.setTitle(R.string.importFailedTitle);
|
||||
messageId = R.string.importFailed;
|
||||
}
|
||||
|
||||
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.setTitle(resultType == ImportExportResultType.Success ? R.string.importSuccessfulTitle : R.string.importFailedTitle);
|
||||
builder.setMessage(buildResultDialogMessage(result, true));
|
||||
builder.setNeutralButton(R.string.ok, (dialog, which) -> dialog.dismiss());
|
||||
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private void onExportComplete(ImportExportResult result, final Uri path) {
|
||||
ImportExportResultType resultType = result.resultType();
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
|
||||
int messageId;
|
||||
|
||||
if (result == ImportExportResult.Success) {
|
||||
builder.setTitle(R.string.exportSuccessfulTitle);
|
||||
messageId = R.string.exportSuccessful;
|
||||
} else {
|
||||
builder.setTitle(R.string.exportFailedTitle);
|
||||
messageId = R.string.exportFailed;
|
||||
}
|
||||
|
||||
final String message = getResources().getString(messageId);
|
||||
|
||||
builder.setMessage(message);
|
||||
builder.setTitle(resultType == ImportExportResultType.Success ? R.string.exportSuccessfulTitle : R.string.exportFailedTitle);
|
||||
builder.setMessage(buildResultDialogMessage(result, false));
|
||||
builder.setNeutralButton(R.string.ok, (dialog, which) -> dialog.dismiss());
|
||||
|
||||
if (result == ImportExportResult.Success) {
|
||||
if (resultType == ImportExportResultType.Success) {
|
||||
final CharSequence sendLabel = ImportExportActivity.this.getResources().getText(R.string.sendLabel);
|
||||
|
||||
builder.setPositiveButton(sendLabel, (dialog, which) -> {
|
||||
@@ -373,69 +422,4 @@ public class ImportExportActivity extends CatimaAppCompatActivity {
|
||||
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private void chooseFileWithIntent(Intent intent, int requestCode) {
|
||||
try {
|
||||
startActivityForResult(intent, requestCode);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(getApplicationContext(), R.string.failedOpeningFileManager, Toast.LENGTH_LONG).show();
|
||||
Log.e(TAG, "No activity found to handle intent", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void activityResultParser(int requestCode, int resultCode, Uri uri, char[] password) {
|
||||
if (resultCode != RESULT_OK) {
|
||||
Log.w(TAG, "Failed onActivityResult(), result=" + resultCode);
|
||||
return;
|
||||
}
|
||||
|
||||
if (uri == null) {
|
||||
Log.e(TAG, "Activity returned a NULL URI");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (requestCode == CHOOSE_EXPORT_LOCATION) {
|
||||
|
||||
OutputStream writer;
|
||||
if (uri.getScheme() != null) {
|
||||
writer = getContentResolver().openOutputStream(uri);
|
||||
} else {
|
||||
writer = new FileOutputStream(new File(uri.toString()));
|
||||
}
|
||||
Log.e(TAG, "Starting file export with: " + uri.toString());
|
||||
startExport(writer, uri, exportPassword.toCharArray(), true);
|
||||
} else {
|
||||
InputStream reader;
|
||||
if (uri.getScheme() != null) {
|
||||
reader = getContentResolver().openInputStream(uri);
|
||||
} else {
|
||||
reader = new FileInputStream(new File(uri.toString()));
|
||||
}
|
||||
|
||||
Log.e(TAG, "Starting file import with: " + uri.toString());
|
||||
|
||||
startImport(reader, uri, importDataFormat, password, true);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to import/export file: " + uri.toString(), e);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -15,6 +16,7 @@ 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.ImportExportResultType;
|
||||
import protect.card_locker.importexport.MultiFormatExporter;
|
||||
import protect.card_locker.importexport.MultiFormatImporter;
|
||||
|
||||
@@ -59,22 +61,23 @@ public class ImportExportTask implements CompatCallable<ImportExportResult> {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
private ImportExportResult performImport(Context context, InputStream stream, DBHelper db, char[] password) {
|
||||
ImportExportResult importResult = MultiFormatImporter.importData(context, db, stream, format, password);
|
||||
private ImportExportResult performImport(Context context, InputStream stream, SQLiteDatabase database, char[] password) {
|
||||
ImportExportResult importResult = MultiFormatImporter.importData(context, database, stream, format, password);
|
||||
|
||||
Log.i(TAG, "Import result: " + importResult.name());
|
||||
Log.i(TAG, "Import result: " + importResult);
|
||||
|
||||
return importResult;
|
||||
}
|
||||
|
||||
private ImportExportResult performExport(Context context, OutputStream stream, DBHelper db, char[] password) {
|
||||
ImportExportResult result = ImportExportResult.GenericFailure;
|
||||
private ImportExportResult performExport(Context context, OutputStream stream, SQLiteDatabase database, char[] password) {
|
||||
ImportExportResult result;
|
||||
|
||||
try {
|
||||
OutputStreamWriter writer = new OutputStreamWriter(stream, StandardCharsets.UTF_8);
|
||||
result = MultiFormatExporter.exportData(context, db, stream, format, password);
|
||||
result = MultiFormatExporter.exportData(context, database, stream, format, password);
|
||||
writer.close();
|
||||
} catch (IOException e) {
|
||||
result = new ImportExportResult(ImportExportResultType.GenericFailure, e.toString());
|
||||
Log.e(TAG, "Unable to export file", e);
|
||||
}
|
||||
|
||||
@@ -98,15 +101,17 @@ public class ImportExportTask implements CompatCallable<ImportExportResult> {
|
||||
}
|
||||
|
||||
protected ImportExportResult doInBackground(Void... nothing) {
|
||||
final DBHelper db = new DBHelper(activity);
|
||||
final SQLiteDatabase database = new DBHelper(activity).getWritableDatabase();
|
||||
ImportExportResult result;
|
||||
|
||||
if (doImport) {
|
||||
result = performImport(activity.getApplicationContext(), inputStream, db, password);
|
||||
result = performImport(activity.getApplicationContext(), inputStream, database, password);
|
||||
} else {
|
||||
result = performExport(activity.getApplicationContext(), outputStream, db, password);
|
||||
result = performExport(activity.getApplicationContext(), outputStream, database, password);
|
||||
}
|
||||
|
||||
database.close();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ public class ImportURIHelper {
|
||||
private final String shareMultipleText;
|
||||
|
||||
public ImportURIHelper(Context context) {
|
||||
this.context = context;
|
||||
this.context = context.getApplicationContext();
|
||||
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);
|
||||
@@ -116,7 +116,7 @@ public class ImportURIHelper {
|
||||
headerColor = Integer.parseInt(unparsedHeaderColor);
|
||||
}
|
||||
|
||||
return new LoyaltyCard(-1, store, note, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, 0, Utils.getUnixTime(), 100);
|
||||
return new LoyaltyCard(-1, store, note, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, 0, Utils.getUnixTime(), 100,0);
|
||||
} catch (NullPointerException | NumberFormatException | UnsupportedEncodingException ex) {
|
||||
throw new InvalidObjectException("Not a valid import URI");
|
||||
}
|
||||
@@ -197,6 +197,7 @@ public class ImportURIHelper {
|
||||
sendIntent.setType("text/plain");
|
||||
|
||||
Intent shareIntent = Intent.createChooser(sendIntent, null);
|
||||
shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(shareIntent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Typeface;
|
||||
import android.text.TextPaint;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.graphics.PaintCompat;
|
||||
|
||||
/**
|
||||
* Original from https://github.com/andOTP/andOTP/blob/master/app/src/main/java/org/shadowice/flocke/andotp/Utilities/LetterBitmap.java
|
||||
@@ -48,7 +51,6 @@ class LetterBitmap {
|
||||
public LetterBitmap(Context context, String displayName, String key, int tileLetterFontSize,
|
||||
int width, int height, Integer backgroundColor, Integer textColor) {
|
||||
TextPaint paint = new TextPaint();
|
||||
paint.setTypeface(Typeface.create("sans-serif-light", Typeface.BOLD));
|
||||
|
||||
if (textColor != null) {
|
||||
paint.setColor(textColor);
|
||||
@@ -58,6 +60,8 @@ class LetterBitmap {
|
||||
|
||||
paint.setTextAlign(Paint.Align.CENTER);
|
||||
paint.setAntiAlias(true);
|
||||
paint.setTextSize(tileLetterFontSize);
|
||||
paint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
|
||||
|
||||
if (backgroundColor == null) {
|
||||
mColor = getDefaultColor(context, key);
|
||||
@@ -66,22 +70,31 @@ class LetterBitmap {
|
||||
}
|
||||
|
||||
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
String firstChar = displayName.substring(0, 1);
|
||||
String firstChar = displayName.substring(0, 1).toUpperCase();
|
||||
int firstCharEnd = 2;
|
||||
while (firstCharEnd <= displayName.length()) {
|
||||
// Test for the longest render-able string
|
||||
// But ignore containing only a-Z0-9 to not render things like ffi as a single character
|
||||
String test = displayName.substring(0, firstCharEnd);
|
||||
if (!isAlphabetical(test) && PaintCompat.hasGlyph(paint, test)) {
|
||||
firstChar = test;
|
||||
}
|
||||
firstCharEnd++;
|
||||
}
|
||||
|
||||
Log.d("LetterBitmap", "using sequence " + firstChar + " to render first char which has length " + firstChar.length());
|
||||
|
||||
final Canvas c = new Canvas();
|
||||
c.setBitmap(mBitmap);
|
||||
c.drawColor(mColor);
|
||||
|
||||
char[] firstCharArray = new char[1];
|
||||
firstCharArray[0] = firstChar.toUpperCase().charAt(0);
|
||||
paint.setTextSize(tileLetterFontSize);
|
||||
|
||||
// The bounds that enclose the letter
|
||||
Rect bounds = new Rect();
|
||||
paint.getTextBounds(firstChar, 0, firstChar.length(), bounds);
|
||||
c.drawText(firstChar,
|
||||
0, firstChar.length(),
|
||||
width / 2.0f, (height - (bounds.bottom + bounds.top)) / 2.0f
|
||||
, paint);
|
||||
|
||||
paint.getTextBounds(firstCharArray, 0, 1, bounds);
|
||||
c.drawText(firstCharArray, 0, 1, width / 2.0f, height / 2.0f
|
||||
+ (bounds.bottom - bounds.top) / 2.0f, paint);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,6 +125,10 @@ class LetterBitmap {
|
||||
return colors.getColor(color, Color.BLACK);
|
||||
}
|
||||
|
||||
private static boolean isAlphabetical(String string) {
|
||||
return string.matches("[a-zA-Z0-9]*");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the color which the letter tile will use if no default
|
||||
* color is provided.
|
||||
|
||||
@@ -29,13 +29,14 @@ public class LoyaltyCard implements Parcelable {
|
||||
public final Integer headerColor;
|
||||
|
||||
public final int starStatus;
|
||||
public final int archiveStatus;
|
||||
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,
|
||||
@Nullable final String barcodeId, @Nullable final CatimaBarcode barcodeType,
|
||||
@Nullable final Integer headerColor, final int starStatus, final long lastUsed, final int zoomLevel) {
|
||||
@Nullable final Integer headerColor, final int starStatus, final long lastUsed, final int zoomLevel,final int archiveStatus) {
|
||||
this.id = id;
|
||||
this.store = store;
|
||||
this.note = note;
|
||||
@@ -49,6 +50,7 @@ public class LoyaltyCard implements Parcelable {
|
||||
this.starStatus = starStatus;
|
||||
this.lastUsed = lastUsed;
|
||||
this.zoomLevel = zoomLevel;
|
||||
this.archiveStatus = archiveStatus;
|
||||
}
|
||||
|
||||
protected LoyaltyCard(Parcel in) {
|
||||
@@ -68,6 +70,7 @@ public class LoyaltyCard implements Parcelable {
|
||||
starStatus = in.readInt();
|
||||
lastUsed = in.readLong();
|
||||
zoomLevel = in.readInt();
|
||||
archiveStatus = in.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -85,6 +88,7 @@ public class LoyaltyCard implements Parcelable {
|
||||
parcel.writeInt(starStatus);
|
||||
parcel.writeLong(lastUsed);
|
||||
parcel.writeInt(zoomLevel);
|
||||
parcel.writeInt(archiveStatus);
|
||||
}
|
||||
|
||||
public static LoyaltyCard toLoyaltyCard(Cursor cursor) {
|
||||
@@ -98,6 +102,7 @@ public class LoyaltyCard implements Parcelable {
|
||||
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 archived = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.ARCHIVE_STATUS));
|
||||
|
||||
int barcodeTypeColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE);
|
||||
int balanceTypeColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE);
|
||||
@@ -124,7 +129,7 @@ public class LoyaltyCard implements Parcelable {
|
||||
headerColor = cursor.getInt(headerColorColumn);
|
||||
}
|
||||
|
||||
return new LoyaltyCard(id, store, note, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, starred, lastUsed, zoomLevel);
|
||||
return new LoyaltyCard(id, store, note, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, starred, lastUsed, zoomLevel,archived);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.animation.AnimatorInflater;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
public class LoyaltyCardAnimator {
|
||||
|
||||
private static AnimatorSet selectedViewIn, defaultViewOut, selectedViewOut, defaultViewIn;
|
||||
|
||||
public static void flipView(Context inputContext, final View inputSelectedView, final View inputDefaultView, boolean inputItemSelected) {
|
||||
|
||||
selectedViewIn = (AnimatorSet) AnimatorInflater.loadAnimator(inputContext, R.animator.flip_left_in);
|
||||
defaultViewOut = (AnimatorSet) AnimatorInflater.loadAnimator(inputContext, R.animator.flip_right_out);
|
||||
selectedViewOut = (AnimatorSet) AnimatorInflater.loadAnimator(inputContext, R.animator.flip_left_out);
|
||||
defaultViewIn = (AnimatorSet) AnimatorInflater.loadAnimator(inputContext, R.animator.flip_right_in);
|
||||
|
||||
final AnimatorSet showFrontAnim = new AnimatorSet();
|
||||
final AnimatorSet showBackAnim = new AnimatorSet();
|
||||
|
||||
selectedViewIn.setTarget(inputSelectedView);
|
||||
defaultViewOut.setTarget(inputDefaultView);
|
||||
showFrontAnim.playTogether(selectedViewIn, defaultViewOut);
|
||||
|
||||
selectedViewOut.setTarget(inputSelectedView);
|
||||
defaultViewIn.setTarget(inputDefaultView);
|
||||
showBackAnim.playTogether(defaultViewIn, selectedViewOut);
|
||||
|
||||
if (inputItemSelected) {
|
||||
showFrontAnim.start();
|
||||
} else {
|
||||
showBackAnim.start();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.util.TypedValue;
|
||||
@@ -13,36 +13,38 @@ import android.view.HapticFeedbackConstants;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewOutlineProvider;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.BlendModeColorFilterCompat;
|
||||
import androidx.core.graphics.BlendModeCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
|
||||
import androidx.core.graphics.BlendModeColorFilterCompat;
|
||||
import androidx.core.graphics.BlendModeCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import protect.card_locker.preferences.Settings;
|
||||
|
||||
public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCursorAdapter.LoyaltyCardListItemViewHolder> {
|
||||
private int mCurrentSelectedIndex = -1;
|
||||
private Cursor mCursor;
|
||||
Settings mSettings;
|
||||
boolean mDarkModeEnabled;
|
||||
private Context mContext;
|
||||
private CardAdapterListener mListener;
|
||||
public final Context mContext;
|
||||
private final CardAdapterListener mListener;
|
||||
protected SparseBooleanArray mSelectedItems;
|
||||
protected SparseBooleanArray mAnimationItemsIndex;
|
||||
private boolean mReverseAllAnimations = false;
|
||||
private boolean mShowDetails;
|
||||
|
||||
public LoyaltyCardCursorAdapter(Context inputContext, Cursor inputCursor, CardAdapterListener inputListener) {
|
||||
super(inputCursor);
|
||||
super(inputCursor, DBHelper.LoyaltyCardDbIds.ID);
|
||||
setHasStableIds(true);
|
||||
mSettings = new Settings(inputContext);
|
||||
mContext = inputContext;
|
||||
@@ -52,13 +54,34 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
|
||||
mDarkModeEnabled = Utils.isDarkModeEnabled(inputContext);
|
||||
|
||||
swapCursor(mCursor);
|
||||
refreshState();
|
||||
|
||||
swapCursor(inputCursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void swapCursor(Cursor inputCursor) {
|
||||
super.swapCursor(inputCursor);
|
||||
mCursor = inputCursor;
|
||||
public void refreshState() {
|
||||
// Retrieve user details preference
|
||||
SharedPreferences cardDetailsPref = mContext.getSharedPreferences(
|
||||
mContext.getString(R.string.sharedpreference_card_details),
|
||||
Context.MODE_PRIVATE);
|
||||
mShowDetails = cardDetailsPref.getBoolean(mContext.getString(R.string.sharedpreference_card_details_show), true);
|
||||
}
|
||||
|
||||
public void showDetails(boolean show) {
|
||||
mShowDetails = show;
|
||||
notifyDataSetChanged();
|
||||
|
||||
// Store in Shared Preference to restore next adapter launch
|
||||
SharedPreferences cardDetailsPref = mContext.getSharedPreferences(
|
||||
mContext.getString(R.string.sharedpreference_card_details),
|
||||
Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor cardDetailsPrefEditor = cardDetailsPref.edit();
|
||||
cardDetailsPrefEditor.putBoolean(mContext.getString(R.string.sharedpreference_card_details_show), show);
|
||||
cardDetailsPrefEditor.apply();
|
||||
}
|
||||
|
||||
public boolean showingDetails() {
|
||||
return mShowDetails;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -67,176 +90,100 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
return new LoyaltyCardListItemViewHolder(itemView, mListener);
|
||||
}
|
||||
|
||||
public Cursor getCursor() {
|
||||
return mCursor;
|
||||
public LoyaltyCard getCard(int position) {
|
||||
mCursor.moveToPosition(position);
|
||||
return LoyaltyCard.toLoyaltyCard(mCursor);
|
||||
}
|
||||
|
||||
public void onBindViewHolder(LoyaltyCardListItemViewHolder inputHolder, Cursor inputCursor) {
|
||||
// Invisible until we want to show something more
|
||||
inputHolder.mDivider.setVisibility(View.GONE);
|
||||
|
||||
// remove outline shadow
|
||||
inputHolder.mThumbnailContainer.setOutlineProvider(new ViewOutlineProvider() {
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline) {
|
||||
ViewOutlineProvider.BACKGROUND.getOutline(view, outline);
|
||||
outline.setAlpha(0f);
|
||||
}
|
||||
});
|
||||
|
||||
int size = mSettings.getFontSizeMax(mSettings.getSmallFont());
|
||||
|
||||
if (mDarkModeEnabled) {
|
||||
inputHolder.mStarIcon.setColorFilter(BlendModeColorFilterCompat.createBlendModeColorFilterCompat(Color.WHITE, BlendModeCompat.SRC_ATOP));
|
||||
}
|
||||
|
||||
LoyaltyCard loyaltyCard = LoyaltyCard.toLoyaltyCard(inputCursor);
|
||||
|
||||
inputHolder.mStoreField.setText(loyaltyCard.store);
|
||||
inputHolder.mStoreField.setTextSize(mSettings.getFontSizeMax(mSettings.getMediumFont()));
|
||||
if (!loyaltyCard.note.isEmpty()) {
|
||||
inputHolder.mNoteField.setVisibility(View.VISIBLE);
|
||||
inputHolder.mNoteField.setText(loyaltyCard.note);
|
||||
inputHolder.mNoteField.setTextSize(size);
|
||||
inputHolder.setStoreField(loyaltyCard.store);
|
||||
if (mShowDetails && !loyaltyCard.note.isEmpty()) {
|
||||
inputHolder.setNoteField(loyaltyCard.note);
|
||||
} else {
|
||||
inputHolder.mNoteField.setVisibility(View.GONE);
|
||||
inputHolder.setNoteField(null);
|
||||
}
|
||||
|
||||
if (!loyaltyCard.balance.equals(new BigDecimal("0"))) {
|
||||
int drawableSize = dpToPx((size * 24) / 14, mContext);
|
||||
inputHolder.mDivider.setVisibility(View.VISIBLE);
|
||||
inputHolder.mBalanceField.setVisibility(View.VISIBLE);
|
||||
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);
|
||||
if (mShowDetails && !loyaltyCard.balance.equals(new BigDecimal("0"))) {
|
||||
inputHolder.setBalanceField(loyaltyCard.balance, loyaltyCard.balanceType);
|
||||
} else {
|
||||
inputHolder.mBalanceField.setVisibility(View.GONE);
|
||||
inputHolder.setBalanceField(null, null);
|
||||
}
|
||||
|
||||
if (loyaltyCard.expiry != null) {
|
||||
int drawableSize = dpToPx((size * 24) / 14, mContext);
|
||||
inputHolder.mDivider.setVisibility(View.VISIBLE);
|
||||
inputHolder.mExpiryField.setVisibility(View.VISIBLE);
|
||||
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(DateFormat.getDateInstance(DateFormat.LONG).format(loyaltyCard.expiry));
|
||||
inputHolder.mExpiryField.setTextSize(size);
|
||||
if (mShowDetails && loyaltyCard.expiry != null) {
|
||||
inputHolder.setExpiryField(loyaltyCard.expiry);
|
||||
} else {
|
||||
inputHolder.mExpiryField.setVisibility(View.GONE);
|
||||
inputHolder.setExpiryField(null);
|
||||
}
|
||||
|
||||
setHeaderHeight(inputHolder, mShowDetails);
|
||||
Bitmap cardIcon = Utils.retrieveCardImage(mContext, loyaltyCard.id, ImageLocationType.icon);
|
||||
if (cardIcon != null) {
|
||||
inputHolder.mCardIcon.setImageBitmap(cardIcon);
|
||||
inputHolder.mCardIcon.setBackgroundColor(Color.TRANSPARENT);
|
||||
inputHolder.mCardIcon.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||
} else {
|
||||
inputHolder.mCardIcon.setImageBitmap(Utils.generateIcon(mContext, loyaltyCard.store, loyaltyCard.headerColor).getLetterTile());
|
||||
if (loyaltyCard.headerColor != null) {
|
||||
inputHolder.mCardIcon.setBackgroundColor(loyaltyCard.headerColor);
|
||||
}
|
||||
inputHolder.mCardIcon.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
||||
}
|
||||
inputHolder.setIconBackgroundColor(loyaltyCard.headerColor != null ? loyaltyCard.headerColor : R.attr.colorPrimary);
|
||||
|
||||
inputHolder.mStarIcon.setVisibility(loyaltyCard.starStatus != 0 ? View.VISIBLE : View.GONE);
|
||||
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.toggleCardStateIcon(loyaltyCard.starStatus != 0, loyaltyCard.archiveStatus != 0, itemSelected(inputCursor.getPosition()));
|
||||
|
||||
inputHolder.itemView.setActivated(mSelectedItems.get(inputCursor.getPosition(), false));
|
||||
applyIconAnimation(inputHolder, inputCursor.getPosition());
|
||||
applyClickEvents(inputHolder, inputCursor.getPosition());
|
||||
|
||||
// Force redraw to fix size not shrinking after data change
|
||||
inputHolder.mRow.requestLayout();
|
||||
}
|
||||
|
||||
private void setHeaderHeight(LoyaltyCardListItemViewHolder inputHolder, boolean expanded) {
|
||||
int iconHeight;
|
||||
if (expanded) {
|
||||
iconHeight = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
} else {
|
||||
iconHeight = (int) mContext.getResources().getDimension(R.dimen.cardThumbnailSize);
|
||||
}
|
||||
|
||||
inputHolder.mIconLayout.getLayoutParams().height = expanded ? 0 : iconHeight;
|
||||
inputHolder.mCardIcon.getLayoutParams().height = iconHeight;
|
||||
inputHolder.mTickIcon.getLayoutParams().height = iconHeight;
|
||||
}
|
||||
|
||||
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.mInformationContainer.setOnLongClickListener(inputView -> {
|
||||
mListener.onRowLongClicked(inputPosition);
|
||||
inputView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||
return true;
|
||||
});
|
||||
private boolean itemSelected(int inputPosition) {
|
||||
return 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 (itemSelected(inputPosition)) {
|
||||
inputHolder.mCardIcon.setVisibility(View.GONE);
|
||||
inputHolder.mTickIcon.setVisibility(View.VISIBLE);
|
||||
if (mCurrentSelectedIndex == inputPosition) {
|
||||
LoyaltyCardAnimator.flipView(mContext, inputHolder.mThumbnailBackContainer, inputHolder.mThumbnailFrontContainer, true);
|
||||
resetCurrentIndex();
|
||||
}
|
||||
} else {
|
||||
inputHolder.mThumbnailBackContainer.setVisibility(View.GONE);
|
||||
resetIconYAxis(inputHolder.mThumbnailFrontContainer);
|
||||
inputHolder.mThumbnailFrontContainer.setVisibility(View.VISIBLE);
|
||||
inputHolder.mThumbnailFrontContainer.setAlpha(1);
|
||||
inputHolder.mTickIcon.setVisibility(View.GONE);
|
||||
inputHolder.mCardIcon.setVisibility(View.VISIBLE);
|
||||
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) {
|
||||
inputView.setRotationY(0);
|
||||
}
|
||||
}
|
||||
|
||||
public void resetAnimationIndex() {
|
||||
mReverseAllAnimations = false;
|
||||
mAnimationItemsIndex.clear();
|
||||
}
|
||||
|
||||
|
||||
public void toggleSelection(int inputPosition) {
|
||||
mCurrentSelectedIndex = inputPosition;
|
||||
if (mSelectedItems.get(inputPosition, false)) {
|
||||
@@ -282,30 +229,33 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
void onRowLongClicked(int inputPosition);
|
||||
}
|
||||
|
||||
public static class LoyaltyCardListItemViewHolder extends RecyclerView.ViewHolder {
|
||||
public class LoyaltyCardListItemViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public TextView mStoreField, mNoteField, mBalanceField, mExpiryField;
|
||||
public LinearLayout mInformationContainer;
|
||||
public ImageView mCardIcon, mStarIcon, mTickIcon;
|
||||
public MaterialCardView mRow;
|
||||
public ImageView mCardIcon, mStarBackground, mStarBorder, mTickIcon, mArchivedBackground;
|
||||
public MaterialCardView mRow, mIconLayout;
|
||||
public ConstraintLayout mStar, mArchived;
|
||||
public View mDivider;
|
||||
public RelativeLayout mThumbnailFrontContainer, mThumbnailBackContainer;
|
||||
public MaterialCardView mThumbnailContainer;
|
||||
|
||||
public LoyaltyCardListItemViewHolder(View inputView, CardAdapterListener inputListener) {
|
||||
private int mIconBackgroundColor;
|
||||
|
||||
|
||||
|
||||
protected LoyaltyCardListItemViewHolder(View inputView, CardAdapterListener inputListener) {
|
||||
super(inputView);
|
||||
mRow = inputView.findViewById(R.id.row);
|
||||
mDivider = inputView.findViewById(R.id.info_divider);
|
||||
mThumbnailContainer = inputView.findViewById(R.id.thumbnail_container);
|
||||
mThumbnailFrontContainer = inputView.findViewById(R.id.thumbnail_front);
|
||||
mThumbnailBackContainer = inputView.findViewById(R.id.thumbnail_back);
|
||||
mInformationContainer = inputView.findViewById(R.id.information_container);
|
||||
mStoreField = inputView.findViewById(R.id.store);
|
||||
mNoteField = inputView.findViewById(R.id.note);
|
||||
mBalanceField = inputView.findViewById(R.id.balance);
|
||||
mExpiryField = inputView.findViewById(R.id.expiry);
|
||||
mIconLayout = inputView.findViewById(R.id.icon_layout);
|
||||
mCardIcon = inputView.findViewById(R.id.thumbnail);
|
||||
mStarIcon = inputView.findViewById(R.id.star);
|
||||
mStar = inputView.findViewById(R.id.star);
|
||||
mStarBackground = inputView.findViewById(R.id.star_background);
|
||||
mStarBorder = inputView.findViewById(R.id.star_border);
|
||||
mArchived = inputView.findViewById(R.id.archivedIcon);
|
||||
mArchivedBackground = inputView.findViewById(R.id.archive_background);
|
||||
mTickIcon = inputView.findViewById(R.id.selected_thumbnail);
|
||||
inputView.setOnLongClickListener(view -> {
|
||||
inputListener.onRowClicked(getAdapterPosition());
|
||||
@@ -313,6 +263,114 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public void setStoreField(String text) {
|
||||
mStoreField.setText(text);
|
||||
mStoreField.setTextSize(mSettings.getFontSizeMax(mSettings.getMediumFont()));
|
||||
mStoreField.requestLayout();
|
||||
}
|
||||
|
||||
public void setNoteField(String text) {
|
||||
if (text == null) {
|
||||
mNoteField.setVisibility(View.GONE);
|
||||
} else {
|
||||
mNoteField.setVisibility(View.VISIBLE);
|
||||
mNoteField.setText(text);
|
||||
mNoteField.setTextSize(mSettings.getFontSizeMax(mSettings.getSmallFont()));
|
||||
}
|
||||
mNoteField.requestLayout();
|
||||
}
|
||||
|
||||
public void setBalanceField(BigDecimal balance, Currency balanceType) {
|
||||
if (balance == null) {
|
||||
mBalanceField.setVisibility(View.GONE);
|
||||
} else {
|
||||
int size = mSettings.getFontSizeMax(mSettings.getSmallFont());
|
||||
int drawableSize = dpToPx((size * 24) / 14, mContext);
|
||||
mDivider.setVisibility(View.VISIBLE);
|
||||
mBalanceField.setVisibility(View.VISIBLE);
|
||||
Drawable balanceIcon = mBalanceField.getCompoundDrawables()[0];
|
||||
balanceIcon.setBounds(0, 0, drawableSize, drawableSize);
|
||||
mBalanceField.setCompoundDrawablesRelative(balanceIcon, null, null, null);
|
||||
if (mDarkModeEnabled) {
|
||||
balanceIcon.setColorFilter(BlendModeColorFilterCompat.createBlendModeColorFilterCompat(Color.WHITE, BlendModeCompat.SRC_ATOP));
|
||||
}
|
||||
mBalanceField.setText(Utils.formatBalance(mContext, balance, balanceType));
|
||||
mBalanceField.setTextSize(size);
|
||||
}
|
||||
mBalanceField.requestLayout();
|
||||
}
|
||||
|
||||
public void setExpiryField(Date expiry) {
|
||||
if (expiry == null) {
|
||||
mExpiryField.setVisibility(View.GONE);
|
||||
} else {
|
||||
int size = mSettings.getFontSizeMax(mSettings.getSmallFont());
|
||||
int drawableSize = dpToPx((size * 24) / 14, mContext);
|
||||
mDivider.setVisibility(View.VISIBLE);
|
||||
mExpiryField.setVisibility(View.VISIBLE);
|
||||
Drawable expiryIcon = mExpiryField.getCompoundDrawables()[0];
|
||||
expiryIcon.setBounds(0, 0, drawableSize, drawableSize);
|
||||
mExpiryField.setCompoundDrawablesRelative(expiryIcon, null, null, null);
|
||||
if (Utils.hasExpired(expiry)) {
|
||||
expiryIcon.setColorFilter(BlendModeColorFilterCompat.createBlendModeColorFilterCompat(Color.RED, BlendModeCompat.SRC_ATOP));
|
||||
mExpiryField.setTextColor(Color.RED);
|
||||
} else if (mDarkModeEnabled) {
|
||||
expiryIcon.setColorFilter(BlendModeColorFilterCompat.createBlendModeColorFilterCompat(Color.WHITE, BlendModeCompat.SRC_ATOP));
|
||||
}
|
||||
mExpiryField.setText(DateFormat.getDateInstance(DateFormat.LONG).format(expiry));
|
||||
mExpiryField.setTextSize(size);
|
||||
}
|
||||
mExpiryField.requestLayout();
|
||||
}
|
||||
|
||||
public void toggleCardStateIcon(boolean enableStar, boolean enableArchive, boolean colorByTheme) {
|
||||
/* the below code does not work in android 5! hence the change of drawable instead
|
||||
boolean needDarkForeground = Utils.needsDarkForeground(mIconBackgroundColor);
|
||||
Drawable borderDrawable = mStarBorder.getDrawable().mutate();
|
||||
Drawable backgroundDrawable = mStarBackground.getDrawable().mutate();
|
||||
DrawableCompat.setTint(borderDrawable, needsDarkForeground ? Color.BLACK : Color.WHITE);
|
||||
DrawableCompat.setTint(backgroundDrawable, needsDarkForeground ? Color.BLACK : Color.WHITE);
|
||||
mStarBorder.setImageDrawable(borderDrawable);
|
||||
mStarBackground.setImageDrawable(backgroundDrawable);
|
||||
*/
|
||||
boolean dark = Utils.needsDarkForeground(mIconBackgroundColor);
|
||||
if (colorByTheme) {
|
||||
dark = !mDarkModeEnabled;
|
||||
}
|
||||
|
||||
if (dark) {
|
||||
mStarBorder.setImageResource(R.drawable.ic_unstarred_white);
|
||||
mStarBackground.setImageResource(R.drawable.ic_starred_black);
|
||||
mArchivedBackground.setImageResource(R.drawable.ic_baseline_archive_24_black);
|
||||
} else {
|
||||
mStarBorder.setImageResource(R.drawable.ic_unstarred_black);
|
||||
mStarBackground.setImageResource(R.drawable.ic_starred_white);
|
||||
mArchivedBackground.setImageResource(R.drawable.ic_baseline_archive_24);
|
||||
}
|
||||
|
||||
if (enableStar) {
|
||||
mStar.setVisibility(View.VISIBLE);
|
||||
} else{
|
||||
mStar.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (enableArchive) {
|
||||
mArchived.setVisibility(View.VISIBLE);
|
||||
} else{
|
||||
mArchived.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
mStarBorder.invalidate();
|
||||
mStarBackground.invalidate();
|
||||
mArchivedBackground.invalidate();
|
||||
|
||||
}
|
||||
|
||||
public void setIconBackgroundColor(int color) {
|
||||
mIconBackgroundColor = color;
|
||||
mCardIcon.setBackgroundColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
public int dpToPx(int dp, Context mContext) {
|
||||
|
||||
@@ -9,6 +9,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.TypedArray;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
@@ -35,6 +36,7 @@ import android.widget.Toast;
|
||||
|
||||
import com.google.android.material.chip.Chip;
|
||||
import com.google.android.material.chip.ChipGroup;
|
||||
import com.google.android.material.color.MaterialColors;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
@@ -65,14 +67,16 @@ import java.util.concurrent.Callable;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.content.FileProvider;
|
||||
import androidx.exifinterface.media.ExifInterface;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.palette.graphics.Palette;
|
||||
|
||||
import protect.card_locker.async.TaskHandler;
|
||||
|
||||
public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
@@ -104,6 +108,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
private static final int PERMISSION_REQUEST_CAMERA_IMAGE_ICON = 102;
|
||||
|
||||
public static final String BUNDLE_ID = "id";
|
||||
public static final String BUNDLE_DUPLICATE_ID = "duplicateId";
|
||||
public static final String BUNDLE_UPDATE = "update";
|
||||
public static final String BUNDLE_CARDID = "cardId";
|
||||
public static final String BUNDLE_BARCODEID = "barcodeId";
|
||||
@@ -132,8 +137,11 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
|
||||
Button enterButton;
|
||||
|
||||
Toolbar toolbar;
|
||||
|
||||
int loyaltyCardId;
|
||||
boolean updateLoyaltyCard;
|
||||
boolean duplicateFromLoyaltyCardId;
|
||||
String cardId;
|
||||
String barcodeId;
|
||||
String barcodeType;
|
||||
@@ -141,7 +149,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
|
||||
Uri importLoyaltyCardUri = null;
|
||||
|
||||
DBHelper db;
|
||||
SQLiteDatabase mDatabase;
|
||||
ImportURIHelper importUriHelper;
|
||||
|
||||
boolean hasChanged = false;
|
||||
@@ -151,7 +159,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
AlertDialog confirmExitDialog = null;
|
||||
|
||||
boolean validBalance = true;
|
||||
Runnable warnOnInvalidBarcodeType;
|
||||
Runnable barcodeImageGenerationFinishedCallback;
|
||||
|
||||
HashMap<String, Currency> currencies = new HashMap<>();
|
||||
|
||||
@@ -176,6 +184,16 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
|
||||
final private TaskHandler mTasks = new TaskHandler();
|
||||
|
||||
// store system locale for Build.VERSION.SDK_INT < Build.VERSION_CODES.N
|
||||
private Locale mSystemLocale;
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
// store system locale
|
||||
mSystemLocale = Locale.getDefault();
|
||||
super.attachBaseContext(base);
|
||||
}
|
||||
|
||||
private static LoyaltyCard updateTempState(LoyaltyCard loyaltyCard, LoyaltyCardField fieldName, Object value) {
|
||||
return new LoyaltyCard(
|
||||
(int) (fieldName == LoyaltyCardField.id ? value : loyaltyCard.id),
|
||||
@@ -189,13 +207,17 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
(CatimaBarcode) (fieldName == LoyaltyCardField.barcodeType ? value : loyaltyCard.barcodeType),
|
||||
(Integer) (fieldName == LoyaltyCardField.headerColor ? value : loyaltyCard.headerColor),
|
||||
(int) (fieldName == LoyaltyCardField.starStatus ? value : loyaltyCard.starStatus),
|
||||
Utils.getUnixTime(), 100
|
||||
Utils.getUnixTime(), 100, (int) (fieldName == LoyaltyCardField.archiveStatus ? value : loyaltyCard.archiveStatus)
|
||||
);
|
||||
}
|
||||
|
||||
private void updateTempState(LoyaltyCardField fieldName, Object value) {
|
||||
tempLoyaltyCard = updateTempState(tempLoyaltyCard, fieldName, value);
|
||||
|
||||
if (initDone && (fieldName == LoyaltyCardField.cardId || fieldName == LoyaltyCardField.barcodeId || fieldName == LoyaltyCardField.barcodeType)) {
|
||||
generateBarcode();
|
||||
}
|
||||
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
@@ -203,6 +225,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
final Bundle b = intent.getExtras();
|
||||
loyaltyCardId = b != null ? b.getInt(BUNDLE_ID) : 0;
|
||||
updateLoyaltyCard = b != null && b.getBoolean(BUNDLE_UPDATE, false);
|
||||
duplicateFromLoyaltyCardId = b != null && b.getBoolean(BUNDLE_DUPLICATE_ID, false);
|
||||
|
||||
cardId = b != null ? b.getString(BUNDLE_CARDID) : null;
|
||||
barcodeId = b != null ? b.getString(BUNDLE_BARCODEID) : null;
|
||||
@@ -211,7 +234,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
|
||||
importLoyaltyCardUri = intent.getData();
|
||||
|
||||
Log.d(TAG, "View activity: id=" + loyaltyCardId
|
||||
Log.d(TAG, "Edit activity: id=" + loyaltyCardId
|
||||
+ ", updateLoyaltyCard=" + updateLoyaltyCard);
|
||||
}
|
||||
|
||||
@@ -274,16 +297,17 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.loyalty_card_edit_activity);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
mDatabase = new DBHelper(this).getWritableDatabase();
|
||||
|
||||
extractIntentFields(getIntent());
|
||||
|
||||
db = new DBHelper(this);
|
||||
importUriHelper = new ImportURIHelper(this);
|
||||
|
||||
for (Currency currency : Currency.getAvailableCurrencies()) {
|
||||
@@ -302,6 +326,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
barcodeIdField = findViewById(R.id.barcodeIdField);
|
||||
barcodeTypeField = findViewById(R.id.barcodeTypeField);
|
||||
barcodeImage = findViewById(R.id.barcode);
|
||||
barcodeImage.setClipToOutline(true);
|
||||
barcodeImageLayout = findViewById(R.id.barcodeLayout);
|
||||
barcodeCaptureLayout = findViewById(R.id.barcodeCaptureLayout);
|
||||
cardImageFrontHolder = findViewById(R.id.frontImageHolder);
|
||||
@@ -310,11 +335,10 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
cardImageBack = findViewById(R.id.backImage);
|
||||
|
||||
enterButton = findViewById(R.id.enterButton);
|
||||
cardImageFront.setBackgroundColor(getThemeColor());
|
||||
cardImageBack.setBackgroundColor(getThemeColor());
|
||||
|
||||
warnOnInvalidBarcodeType = () -> {
|
||||
barcodeImageGenerationFinishedCallback = () -> {
|
||||
if (!(boolean) barcodeImage.getTag()) {
|
||||
barcodeImageLayout.setVisibility(View.GONE);
|
||||
Toast.makeText(LoyaltyCardEditActivity.this, getString(R.string.wrongValueForBarcodeType), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
};
|
||||
@@ -426,16 +450,12 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
LocaleList locales = getApplicationContext().getResources().getConfiguration().getLocales();
|
||||
|
||||
for (int i = locales.size() - 1; i > 0; i--) {
|
||||
for (int i = locales.size() - 1; i >= 0; i--) {
|
||||
Locale locale = locales.get(i);
|
||||
String currencySymbol = Currency.getInstance(locale).getSymbol();
|
||||
currencyList.remove(currencySymbol);
|
||||
currencyList.add(0, currencySymbol);
|
||||
currencyPrioritizeLocaleSymbols(currencyList, locale);
|
||||
}
|
||||
} else {
|
||||
String currencySymbol = Currency.getInstance(getApplicationContext().getResources().getConfiguration().locale).getSymbol();
|
||||
currencyList.remove(currencySymbol);
|
||||
currencyList.add(0, currencySymbol);
|
||||
currencyPrioritizeLocaleSymbols(currencyList, mSystemLocale);
|
||||
}
|
||||
|
||||
currencyList.add(0, getString(R.string.points));
|
||||
@@ -511,8 +531,6 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
} else {
|
||||
updateTempState(LoyaltyCardField.barcodeId, s.toString());
|
||||
}
|
||||
|
||||
generateOrHideBarcode();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -543,8 +561,6 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
}
|
||||
|
||||
generateOrHideBarcode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -656,33 +672,43 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
});
|
||||
|
||||
mCropperOptions = new UCrop.Options();
|
||||
setCropperTheme();
|
||||
}
|
||||
|
||||
// ucrop 2.2.6 initial aspect ratio is glitched when 0x0 is used as the initial ratio option
|
||||
// https://github.com/Yalantis/uCrop/blob/281c8e6438d81f464d836fc6b500517144af264a/ucrop/src/main/java/com/yalantis/ucrop/UCropActivity.java#L264
|
||||
// so source width height has to be provided for now, depending on whether future versions of ucrop will support 0x0 as the default option
|
||||
private void setCropperOptions(boolean oneByOneDefault, float sourceWidth, float sourceHeight) {
|
||||
private void setCropperOptions(boolean cardShapeDefault, float sourceWidth, float sourceHeight) {
|
||||
mCropperOptions.setCompressionFormat(TEMP_CROP_IMAGE_FORMAT);
|
||||
mCropperOptions.setFreeStyleCropEnabled(true);
|
||||
mCropperOptions.setHideBottomControls(false);
|
||||
// default aspect ratio workaround
|
||||
int selectedByDefault = 1;
|
||||
if (oneByOneDefault) {
|
||||
selectedByDefault = 0;
|
||||
if (cardShapeDefault) {
|
||||
selectedByDefault = 2;
|
||||
}
|
||||
mCropperOptions.setAspectRatioOptions(selectedByDefault,
|
||||
new AspectRatio(null, 1, 1),
|
||||
new AspectRatio(getResources().getString(R.string.ucrop_label_original).toUpperCase(), sourceWidth, sourceHeight),
|
||||
new AspectRatio(getResources().getString(R.string.card).toUpperCase(), 85.6f, 53.98f)
|
||||
);
|
||||
}
|
||||
|
||||
private void setCropperTheme() {
|
||||
mCropperOptions.setToolbarColor(getResources().getColor(R.color.colorPrimary));
|
||||
mCropperOptions.setStatusBarColor(getResources().getColor(R.color.colorPrimaryDark));
|
||||
mCropperOptions.setToolbarWidgetColor(Color.WHITE);
|
||||
mCropperOptions.setActiveControlsWidgetColor(getResources().getColor(R.color.colorPrimary));
|
||||
// Fix theming
|
||||
|
||||
int colorPrimary = MaterialColors.getColor(this, R.attr.colorPrimary, ContextCompat.getColor(this, R.color.md_theme_light_primary));
|
||||
int colorOnPrimary = MaterialColors.getColor(this, R.attr.colorOnPrimary, ContextCompat.getColor(this, R.color.md_theme_light_onPrimary));
|
||||
int colorSurface = MaterialColors.getColor(this, R.attr.colorSurface, ContextCompat.getColor(this, R.color.md_theme_light_surface));
|
||||
int colorOnSurface = MaterialColors.getColor(this, R.attr.colorOnSurface, ContextCompat.getColor(this, R.color.md_theme_light_onSurface));
|
||||
int colorBackground = MaterialColors.getColor(this, android.R.attr.colorBackground, ContextCompat.getColor(this, R.color.md_theme_light_onSurface));
|
||||
mCropperOptions.setToolbarColor(colorSurface);
|
||||
mCropperOptions.setStatusBarColor(colorSurface);
|
||||
mCropperOptions.setToolbarWidgetColor(colorOnSurface);
|
||||
mCropperOptions.setRootViewBackgroundColor(colorBackground);
|
||||
// set tool tip to be the darker of primary color
|
||||
if (Utils.isDarkModeEnabled(this)) {
|
||||
mCropperOptions.setActiveControlsWidgetColor(colorOnPrimary);
|
||||
} else {
|
||||
mCropperOptions.setActiveControlsWidgetColor(colorPrimary);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -726,10 +752,9 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
|
||||
onResuming = true;
|
||||
|
||||
|
||||
if (tempLoyaltyCard == null) {
|
||||
if (updateLoyaltyCard) {
|
||||
tempLoyaltyCard = db.getLoyaltyCard(loyaltyCardId);
|
||||
if (updateLoyaltyCard || duplicateFromLoyaltyCardId) {
|
||||
tempLoyaltyCard = DBHelper.getLoyaltyCard(mDatabase, loyaltyCardId);
|
||||
if (tempLoyaltyCard == null) {
|
||||
Log.w(TAG, "Could not lookup loyalty card " + loyaltyCardId);
|
||||
Toast.makeText(this, R.string.noCardExistsError, Toast.LENGTH_LONG).show();
|
||||
@@ -746,7 +771,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
}
|
||||
} else {
|
||||
// New card, use default values
|
||||
tempLoyaltyCard = new LoyaltyCard(-1, "", "", null, new BigDecimal("0"), null, "", null, null, null, 0, Utils.getUnixTime(), 100);
|
||||
tempLoyaltyCard = new LoyaltyCard(-1, "", "", null, new BigDecimal("0"), null, "", null, null, null, 0, Utils.getUnixTime(), 100,0);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -754,7 +779,11 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
if (!initDone) {
|
||||
if (updateLoyaltyCard) {
|
||||
setTitle(R.string.editCardTitle);
|
||||
} else {
|
||||
setTitle(R.string.addCardTitle);
|
||||
}
|
||||
|
||||
if (updateLoyaltyCard || duplicateFromLoyaltyCardId) {
|
||||
if (!mFrontImageUnsaved && !croppedFrontImage() && !mFrontImageRemoved) {
|
||||
setCardImage(cardImageFront, Utils.retrieveCardImage(this, tempLoyaltyCard.id, ImageLocationType.front), true);
|
||||
}
|
||||
@@ -792,9 +821,9 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
barcodeTypeField.setText(tempLoyaltyCard.barcodeType != null ? tempLoyaltyCard.barcodeType.prettyName() : getString(R.string.noBarcode));
|
||||
|
||||
if (groupsChips.getChildCount() == 0) {
|
||||
List<Group> existingGroups = db.getGroups();
|
||||
List<Group> existingGroups = DBHelper.getGroups(mDatabase);
|
||||
|
||||
List<Group> loyaltyCardGroups = db.getLoyaltyCardGroups(loyaltyCardId);
|
||||
List<Group> loyaltyCardGroups = DBHelper.getLoyaltyCardGroups(mDatabase, loyaltyCardId);
|
||||
|
||||
if (existingGroups.isEmpty()) {
|
||||
groupsChips.setVisibility(View.GONE);
|
||||
@@ -802,7 +831,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
groupsChips.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
for (Group group : db.getGroups()) {
|
||||
for (Group group : DBHelper.getGroups(mDatabase)) {
|
||||
Chip chip = (Chip) getLayoutInflater().inflate(R.layout.layout_chip_choice, groupsChips, false);
|
||||
chip.setText(group._id);
|
||||
chip.setTag(group);
|
||||
@@ -832,10 +861,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
// Generate random header color
|
||||
if (tempLoyaltyCard.headerColor == null) {
|
||||
// Select a random color to start out with.
|
||||
TypedArray colors = getResources().obtainTypedArray(R.array.letter_tile_colors);
|
||||
final int color = (int) (Math.random() * colors.length());
|
||||
updateTempState(LoyaltyCardField.headerColor, colors.getColor(color, Color.BLACK));
|
||||
colors.recycle();
|
||||
updateTempState(LoyaltyCardField.headerColor, Utils.getRandomHeaderColor(this));
|
||||
}
|
||||
|
||||
// It can't be null because we set it in updateTempState but SpotBugs insists it can be
|
||||
@@ -876,7 +902,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
hasChanged = hadChanges;
|
||||
}
|
||||
|
||||
generateOrHideBarcode();
|
||||
generateBarcode();
|
||||
|
||||
enterButton.setOnClickListener(new EditCardIdAndBarcode());
|
||||
barcodeImage.setOnClickListener(new EditCardIdAndBarcode());
|
||||
@@ -893,11 +919,11 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
onResuming = false;
|
||||
}
|
||||
|
||||
protected void setColorFromIcon(){
|
||||
protected void setColorFromIcon() {
|
||||
Object icon = thumbnail.getTag();
|
||||
if(icon != null && (icon instanceof Bitmap)){
|
||||
updateTempState(LoyaltyCardField.headerColor, new Palette.Builder((Bitmap)icon).generate().getDominantColor(tempLoyaltyCard.headerColor != null ? tempLoyaltyCard.headerColor : getResources().getColor(R.color.colorPrimary)));
|
||||
}else{
|
||||
if (icon != null && (icon instanceof Bitmap)) {
|
||||
updateTempState(LoyaltyCardField.headerColor, Utils.getHeaderColorFromImage((Bitmap) icon, tempLoyaltyCard.headerColor != null ? tempLoyaltyCard.headerColor : R.attr.colorPrimary));
|
||||
} else {
|
||||
Log.d("setColorFromIcon", "attempting header color change from icon but icon does not exist");
|
||||
}
|
||||
}
|
||||
@@ -1035,8 +1061,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
|
||||
class ChooseCardImage implements View.OnClickListener {
|
||||
@Override
|
||||
public void onClick(View v) throws NoSuchElementException
|
||||
{
|
||||
public void onClick(View v) throws NoSuchElementException {
|
||||
ImageView targetView;
|
||||
|
||||
if (v.getId() == R.id.frontImageHolder) {
|
||||
@@ -1074,11 +1099,9 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
}
|
||||
|
||||
ColorPickerDialog dialog = dialogBuilder.create();
|
||||
dialog.setColorPickerDialogListener(new ColorPickerDialogListener()
|
||||
{
|
||||
dialog.setColorPickerDialogListener(new ColorPickerDialogListener() {
|
||||
@Override
|
||||
public void onColorSelected(int dialogId, int color)
|
||||
{
|
||||
public void onColorSelected(int dialogId, int color) {
|
||||
updateTempState(LoyaltyCardField.headerColor, color);
|
||||
|
||||
// Unset image if set
|
||||
@@ -1088,8 +1111,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDialogDismissed(int dialogId)
|
||||
{
|
||||
public void onDialogDismissed(int dialogId) {
|
||||
// Nothing to do, no change made
|
||||
}
|
||||
});
|
||||
@@ -1212,7 +1234,16 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
int day = c.get(Calendar.DAY_OF_MONTH);
|
||||
|
||||
// Create a new instance of DatePickerDialog and return it
|
||||
return new DatePickerDialog(getActivity(), this, year, month, day);
|
||||
DatePickerDialog datePickerDialog = new DatePickerDialog(getActivity(), this, year, month, day);
|
||||
datePickerDialog.getDatePicker().setMinDate(getMinDateOfDatePicker());
|
||||
return datePickerDialog;
|
||||
}
|
||||
|
||||
private long getMinDateOfDatePicker()
|
||||
{
|
||||
Calendar minDateCalendar = Calendar.getInstance();
|
||||
minDateCalendar.set(1970, 0, 1);
|
||||
return minDateCalendar.getTimeInMillis();
|
||||
}
|
||||
|
||||
public void onDateSet(DatePicker view, int year, int month, int day) {
|
||||
@@ -1234,6 +1265,11 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
}
|
||||
|
||||
private void doSave() {
|
||||
if (isFinishing()) {
|
||||
// If we are done saving, ignore any queued up save button presses
|
||||
return;
|
||||
}
|
||||
|
||||
if (tempStoredOldBarcodeValue != null) {
|
||||
askBarcodeChange(this::doSave);
|
||||
return;
|
||||
@@ -1262,7 +1298,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
}
|
||||
|
||||
if (updateLoyaltyCard) { //update of "starStatus" not necessary, since it cannot be changed in this activity (only in ViewActivity)
|
||||
db.updateLoyaltyCard(loyaltyCardId, tempLoyaltyCard.store, tempLoyaltyCard.note, tempLoyaltyCard.expiry, tempLoyaltyCard.balance, tempLoyaltyCard.balanceType, tempLoyaltyCard.cardId, tempLoyaltyCard.barcodeId, tempLoyaltyCard.barcodeType, tempLoyaltyCard.headerColor);
|
||||
DBHelper.updateLoyaltyCard(mDatabase, loyaltyCardId, tempLoyaltyCard.store, tempLoyaltyCard.note, tempLoyaltyCard.expiry, tempLoyaltyCard.balance, tempLoyaltyCard.balanceType, tempLoyaltyCard.cardId, tempLoyaltyCard.barcodeId, tempLoyaltyCard.barcodeType, tempLoyaltyCard.headerColor);
|
||||
try {
|
||||
Utils.saveCardImage(this, (Bitmap) cardImageFront.getTag(), loyaltyCardId, ImageLocationType.front);
|
||||
Utils.saveCardImage(this, (Bitmap) cardImageBack.getTag(), loyaltyCardId, ImageLocationType.back);
|
||||
@@ -1272,7 +1308,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
}
|
||||
Log.i(TAG, "Updated " + loyaltyCardId + " to " + cardId);
|
||||
} else {
|
||||
loyaltyCardId = (int) db.insertLoyaltyCard(tempLoyaltyCard.store, tempLoyaltyCard.note, tempLoyaltyCard.expiry, tempLoyaltyCard.balance, tempLoyaltyCard.balanceType, tempLoyaltyCard.cardId, tempLoyaltyCard.barcodeId, tempLoyaltyCard.barcodeType, tempLoyaltyCard.headerColor, 0, tempLoyaltyCard.lastUsed);
|
||||
loyaltyCardId = (int) DBHelper.insertLoyaltyCard(mDatabase, tempLoyaltyCard.store, tempLoyaltyCard.note, tempLoyaltyCard.expiry, tempLoyaltyCard.balance, tempLoyaltyCard.balanceType, tempLoyaltyCard.cardId, tempLoyaltyCard.barcodeId, tempLoyaltyCard.barcodeType, tempLoyaltyCard.headerColor, 0, tempLoyaltyCard.lastUsed,0);
|
||||
try {
|
||||
Utils.saveCardImage(this, (Bitmap) cardImageFront.getTag(), loyaltyCardId, ImageLocationType.front);
|
||||
Utils.saveCardImage(this, (Bitmap) cardImageBack.getTag(), loyaltyCardId, ImageLocationType.back);
|
||||
@@ -1282,20 +1318,20 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
db.setLoyaltyCardGroups(loyaltyCardId, selectedGroups);
|
||||
DBHelper.setLoyaltyCardGroups(mDatabase, loyaltyCardId, selectedGroups);
|
||||
|
||||
ShortcutHelper.updateShortcuts(this, db.getLoyaltyCard(loyaltyCardId));
|
||||
ShortcutHelper.updateShortcuts(this, DBHelper.getLoyaltyCard(mDatabase, loyaltyCardId));
|
||||
|
||||
if(duplicateFromLoyaltyCardId){
|
||||
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
if (updateLoyaltyCard) {
|
||||
getMenuInflater().inflate(R.menu.card_update_menu, menu);
|
||||
} else {
|
||||
getMenuInflater().inflate(R.menu.card_add_menu, menu);
|
||||
}
|
||||
getMenuInflater().inflate(R.menu.card_add_menu, menu);
|
||||
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
@@ -1304,31 +1340,9 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
|
||||
switch (id) {
|
||||
case android.R.id.home:
|
||||
askBeforeQuitIfChanged();
|
||||
break;
|
||||
|
||||
case R.id.action_delete:
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.deleteTitle);
|
||||
builder.setMessage(R.string.deleteConfirmation);
|
||||
builder.setPositiveButton(R.string.confirm, (dialog, which) -> {
|
||||
Log.e(TAG, "Deleting card: " + loyaltyCardId);
|
||||
|
||||
DBHelper db = new DBHelper(LoyaltyCardEditActivity.this);
|
||||
db.deleteLoyaltyCard(loyaltyCardId);
|
||||
|
||||
ShortcutHelper.removeShortcut(LoyaltyCardEditActivity.this, loyaltyCardId);
|
||||
|
||||
finish();
|
||||
dialog.dismiss();
|
||||
});
|
||||
builder.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.show();
|
||||
|
||||
return true;
|
||||
if (id == android.R.id.home) {
|
||||
askBeforeQuitIfChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
@@ -1352,9 +1366,9 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
mCropperOptions.setToolbarTitle(getResources().getString(R.string.setIcon));
|
||||
}
|
||||
|
||||
if(requestedIcon()) {
|
||||
setCropperOptions(true,0f, 0f);
|
||||
}else{
|
||||
if (requestedIcon()) {
|
||||
setCropperOptions(true, 0f, 0f);
|
||||
} else {
|
||||
// sniff the input image for width and height to work around a ucrop bug
|
||||
Bitmap image = null;
|
||||
try {
|
||||
@@ -1365,7 +1379,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
}
|
||||
if (image == null) {
|
||||
Log.d("cropper", "failed loading bitmap for initial width and height for ucrop " + sourceUri.toString());
|
||||
setCropperOptions(true,0f, 0f);
|
||||
setCropperOptions(true, 0f, 0f);
|
||||
} else {
|
||||
try {
|
||||
Bitmap imageRotated = Utils.rotateBitmap(image, new ExifInterface(getContentResolver().openInputStream(sourceUri)));
|
||||
@@ -1381,36 +1395,40 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
}
|
||||
}
|
||||
}
|
||||
mCropperLauncher.launch(
|
||||
UCrop.of(
|
||||
sourceUri,
|
||||
destUri
|
||||
).withOptions(mCropperOptions)
|
||||
.getIntent(this)
|
||||
);
|
||||
Intent ucropIntent = UCrop.of(
|
||||
sourceUri,
|
||||
destUri
|
||||
).withOptions(mCropperOptions)
|
||||
.getIntent(this);
|
||||
ucropIntent.setClass(this, UCropWrapper.class);
|
||||
for (int i = 0; i < toolbar.getChildCount(); i++) {
|
||||
// send toolbar font details to ucrop wrapper
|
||||
View child = toolbar.getChildAt(i);
|
||||
if (child instanceof AppCompatTextView) {
|
||||
AppCompatTextView childTextView = (AppCompatTextView) child;
|
||||
ucropIntent.putExtra(UCropWrapper.UCROP_TOOLBAR_TYPEFACE_STYLE, childTextView.getTypeface().getStyle());
|
||||
break;
|
||||
}
|
||||
}
|
||||
mCropperLauncher.launch(ucropIntent);
|
||||
}
|
||||
|
||||
private void showBarcode() {
|
||||
barcodeImageLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
private void generateBarcode() {
|
||||
if (tempLoyaltyCard == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
private void hideBarcode() {
|
||||
barcodeImageLayout.setVisibility(View.GONE);
|
||||
}
|
||||
mTasks.flushTaskList(TaskHandler.TYPE.BARCODE, true, false, false);
|
||||
|
||||
private void generateOrHideBarcode() {
|
||||
String cardIdString = tempLoyaltyCard.barcodeId != null ? tempLoyaltyCard.barcodeId : tempLoyaltyCard.cardId;
|
||||
CatimaBarcode barcodeFormat = tempLoyaltyCard.barcodeType;
|
||||
|
||||
if (barcodeFormat == null || cardIdString.isEmpty() || !barcodeFormat.isSupported()) {
|
||||
hideBarcode();
|
||||
} else {
|
||||
generateBarcode(cardIdString, barcodeFormat);
|
||||
if (cardIdString == null || barcodeFormat == null) {
|
||||
barcodeImageLayout.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void generateBarcode(String cardIdString, CatimaBarcode barcodeFormat) {
|
||||
mTasks.flushTaskList(TaskHandler.TYPE.BARCODE, true, false, false);
|
||||
barcodeImageLayout.setVisibility(View.VISIBLE);
|
||||
|
||||
if (barcodeImage.getHeight() == 0) {
|
||||
Log.d(TAG, "ImageView size is not known known at start, waiting for load");
|
||||
@@ -1423,17 +1441,15 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
barcodeImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
|
||||
Log.d(TAG, "ImageView size now known");
|
||||
BarcodeImageWriterTask barcodeWriter = new BarcodeImageWriterTask(getApplicationContext(), barcodeImage, cardIdString, barcodeFormat, null, false, warnOnInvalidBarcodeType);
|
||||
BarcodeImageWriterTask barcodeWriter = new BarcodeImageWriterTask(getApplicationContext(), barcodeImage, cardIdString, barcodeFormat, null, false, barcodeImageGenerationFinishedCallback, true);
|
||||
mTasks.executeTask(TaskHandler.TYPE.BARCODE, barcodeWriter);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Log.d(TAG, "ImageView size known known, creating barcode");
|
||||
BarcodeImageWriterTask barcodeWriter = new BarcodeImageWriterTask(getApplicationContext(), barcodeImage, cardIdString, barcodeFormat, null, false, warnOnInvalidBarcodeType);
|
||||
BarcodeImageWriterTask barcodeWriter = new BarcodeImageWriterTask(getApplicationContext(), barcodeImage, cardIdString, barcodeFormat, null, false, barcodeImageGenerationFinishedCallback, true);
|
||||
mTasks.executeTask(TaskHandler.TYPE.BARCODE, barcodeWriter);
|
||||
}
|
||||
|
||||
showBarcode();
|
||||
}
|
||||
|
||||
private void generateIcon(String store) {
|
||||
@@ -1463,32 +1479,36 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
}
|
||||
|
||||
View cardPart = findViewById(R.id.cardPart);
|
||||
View barcodePart = findViewById(R.id.barcodePart);
|
||||
View optionsPart = findViewById(R.id.optionsPart);
|
||||
View picturesPart = findViewById(R.id.picturesPart);
|
||||
|
||||
if (getString(R.string.card).equals(part)) {
|
||||
cardPart.setVisibility(View.VISIBLE);
|
||||
barcodePart.setVisibility(View.GONE);
|
||||
picturesPart.setVisibility(View.GONE);
|
||||
|
||||
// Explicitly hide barcode (fixes blurriness on redraw)
|
||||
hideBarcode();
|
||||
} else if (getString(R.string.barcode).equals(part)) {
|
||||
cardPart.setVisibility(View.GONE);
|
||||
barcodePart.setVisibility(View.VISIBLE);
|
||||
optionsPart.setVisibility(View.GONE);
|
||||
picturesPart.setVisibility(View.GONE);
|
||||
|
||||
// Redraw barcode due to size change (Visibility.GONE sets it to 0)
|
||||
generateOrHideBarcode();
|
||||
generateBarcode();
|
||||
} else if (getString(R.string.options).equals(part)) {
|
||||
cardPart.setVisibility(View.GONE);
|
||||
optionsPart.setVisibility(View.VISIBLE);
|
||||
picturesPart.setVisibility(View.GONE);
|
||||
} else if (getString(R.string.photos).equals(part)) {
|
||||
cardPart.setVisibility(View.GONE);
|
||||
barcodePart.setVisibility(View.GONE);
|
||||
optionsPart.setVisibility(View.GONE);
|
||||
picturesPart.setVisibility(View.VISIBLE);
|
||||
|
||||
// Explicitly hide barcode (fixes blurriness on redraw)
|
||||
hideBarcode();
|
||||
} else {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
private void currencyPrioritizeLocaleSymbols(ArrayList<String> currencyList, Locale locale) {
|
||||
try {
|
||||
String currencySymbol = Currency.getInstance(locale).getSymbol();
|
||||
currencyList.remove(currencySymbol);
|
||||
currencyList.add(0, currencySymbol);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.d(TAG, "Could not get currency data for locale info: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,5 +11,6 @@ public enum LoyaltyCardField {
|
||||
barcodeId,
|
||||
barcodeType,
|
||||
headerColor,
|
||||
starStatus
|
||||
starStatus,
|
||||
archiveStatus
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@ package protect.card_locker;
|
||||
import android.app.Application;
|
||||
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
|
||||
import com.google.android.material.color.DynamicColors;
|
||||
|
||||
import protect.card_locker.preferences.Settings;
|
||||
|
||||
public class LoyaltyCardLockerApplication extends Application {
|
||||
|
||||
@@ -2,7 +2,9 @@ package protect.card_locker;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.Configuration;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Outline;
|
||||
@@ -31,6 +33,7 @@ import android.widget.Toast;
|
||||
|
||||
import com.google.android.material.appbar.AppBarLayout;
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
import com.google.android.material.color.MaterialColors;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
@@ -42,15 +45,19 @@ import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.constraintlayout.widget.Guideline;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
import androidx.core.widget.NestedScrollView;
|
||||
import androidx.core.widget.TextViewCompat;
|
||||
|
||||
import protect.card_locker.async.TaskHandler;
|
||||
import protect.card_locker.preferences.Settings;
|
||||
|
||||
@@ -59,6 +66,8 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
|
||||
private GestureDetector mGestureDetector;
|
||||
|
||||
CoordinatorLayout coordinatorLayout;
|
||||
ConstraintLayout mainLayout;
|
||||
TextView cardIdFieldView;
|
||||
BottomSheetBehavior behavior;
|
||||
LinearLayout bottomSheet;
|
||||
@@ -81,7 +90,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
int loyaltyCardId;
|
||||
LoyaltyCard loyaltyCard;
|
||||
boolean rotationEnabled;
|
||||
DBHelper db;
|
||||
SQLiteDatabase database;
|
||||
ImportURIHelper importURIHelper;
|
||||
Settings settings;
|
||||
|
||||
@@ -114,6 +123,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
private final int HEADER_FILTER_ALPHA = 127;
|
||||
|
||||
final private TaskHandler mTasks = new TaskHandler();
|
||||
Runnable barcodeImageGenerationFinishedCallback;
|
||||
|
||||
@Override
|
||||
public boolean onDown(MotionEvent e) {
|
||||
@@ -242,21 +252,35 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
settings = new Settings(this);
|
||||
|
||||
String cardOrientation = settings.getCardViewOrientation();
|
||||
if (cardOrientation.equals(getString(R.string.settings_key_lock_on_opening_orientation))) {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
|
||||
} else if (cardOrientation.equals(getString(R.string.settings_key_portrait_orientation))) {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
} else if (cardOrientation.equals(getString(R.string.settings_key_landscape_orientation))) {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
||||
} else {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
|
||||
}
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
mainImageIndex = savedInstanceState.getInt(STATE_IMAGEINDEX);
|
||||
isFullscreen = savedInstanceState.getBoolean(STATE_FULLSCREEN);
|
||||
bottomSheetState = savedInstanceState.getInt(STATE_BOTTOMSHEET);
|
||||
}
|
||||
|
||||
settings = new Settings(this);
|
||||
|
||||
extractIntentFields(getIntent());
|
||||
|
||||
setContentView(R.layout.loyalty_card_view_layout);
|
||||
|
||||
db = new DBHelper(this);
|
||||
database = new DBHelper(this).getWritableDatabase();
|
||||
importURIHelper = new ImportURIHelper(this);
|
||||
|
||||
coordinatorLayout = findViewById(R.id.coordinator_layout);
|
||||
mainLayout = findViewById(R.id.mainLayout);
|
||||
cardIdFieldView = findViewById(R.id.cardIdView);
|
||||
bottomSheet = findViewById(R.id.bottom_sheet);
|
||||
bottomSheetContentWrapper = findViewById(R.id.bottomSheetContentWrapper);
|
||||
@@ -268,6 +292,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
storeName = findViewById(R.id.storeName);
|
||||
maximizeButton = findViewById(R.id.maximizeButton);
|
||||
mainImage = findViewById(R.id.mainImage);
|
||||
mainImage.setClipToOutline(true);
|
||||
dotIndicator = findViewById(R.id.dotIndicator);
|
||||
minimizeButton = findViewById(R.id.minimizeButton);
|
||||
collapsingToolbarLayout = findViewById(R.id.collapsingToolbarLayout);
|
||||
@@ -275,11 +300,21 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
iconImage = findViewById(R.id.icon_image);
|
||||
landscapeToolbar = findViewById(R.id.toolbar_landscape);
|
||||
|
||||
barcodeImageGenerationFinishedCallback = () -> {
|
||||
if (!(boolean) mainImage.getTag()) {
|
||||
mainImage.setVisibility(View.GONE);
|
||||
imageTypes.remove(ImageType.BARCODE);
|
||||
|
||||
// Redraw UI
|
||||
setDotIndicator(Utils.isDarkModeEnabled(LoyaltyCardViewActivity.this));
|
||||
setFullscreen(isFullscreen);
|
||||
|
||||
Toast.makeText(LoyaltyCardViewActivity.this, getString(R.string.wrongValueForBarcodeType), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
};
|
||||
|
||||
centerGuideline = findViewById(R.id.centerGuideline);
|
||||
barcodeScaler = findViewById(R.id.barcodeScaler);
|
||||
maximizeButton.setBackgroundColor(getThemeColor());
|
||||
minimizeButton.setBackgroundColor(getThemeColor());
|
||||
bottomSheetButton.setBackgroundColor(getThemeColor());
|
||||
barcodeScaler.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
@@ -293,11 +328,11 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
Log.d(TAG, "Scaling to " + scale);
|
||||
|
||||
loyaltyCard.zoomLevel = progress;
|
||||
db.updateLoyaltyCardZoomLevel(loyaltyCardId, loyaltyCard.zoomLevel);
|
||||
DBHelper.updateLoyaltyCardZoomLevel(database, loyaltyCardId, loyaltyCard.zoomLevel);
|
||||
|
||||
setCenterGuideline(loyaltyCard.zoomLevel);
|
||||
|
||||
drawMainImage(mainImageIndex, true);
|
||||
drawMainImage(mainImageIndex, true, isFullscreen);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -369,14 +404,18 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
mainImage.setOnTouchListener(gestureTouchListener);
|
||||
}
|
||||
|
||||
private void changeUiToBottomSheetState(int newState){
|
||||
private void changeUiToBottomSheetState(int newState) {
|
||||
if (newState == BottomSheetBehavior.STATE_DRAGGING) {
|
||||
editButton.hide();
|
||||
} else if (newState == BottomSheetBehavior.STATE_EXPANDED) {
|
||||
bottomSheetButton.setImageResource(R.drawable.ic_baseline_arrow_drop_down_24);
|
||||
bottomSheetButton.setContentDescription(getString(R.string.hideMoreInfo));
|
||||
mainLayout.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||
editButton.hide();
|
||||
} else if (newState == BottomSheetBehavior.STATE_COLLAPSED) {
|
||||
bottomSheetButton.setImageResource(R.drawable.ic_baseline_arrow_drop_up_24);
|
||||
bottomSheetButton.setContentDescription(getString(R.string.showMoreInfo));
|
||||
mainLayout.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
|
||||
if (!isFullscreen) {
|
||||
editButton.show();
|
||||
}
|
||||
@@ -387,9 +426,9 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
bottomSheetState = newState;
|
||||
}
|
||||
|
||||
private void adjustLayoutHeights(){
|
||||
private void adjustLayoutHeights() {
|
||||
// use getLayoutParams instead of getHeight when heights are pre-determined in xml! getHeight could return 0 if a View is not inflated
|
||||
if(iconImage.getLayoutParams().height != appBarLayout.getHeight()) {
|
||||
if (appBarLayout.getHeight() != 0 && iconImage.getLayoutParams().height != appBarLayout.getHeight()) {
|
||||
Log.d("adjustLayoutHeights", "setting imageIcon height from: " + iconImage.getLayoutParams().height + " to: " + appBarLayout.getHeight());
|
||||
iconImage.setLayoutParams(new CoordinatorLayout.LayoutParams(
|
||||
CoordinatorLayout.LayoutParams.MATCH_PARENT, appBarLayout.getHeight())
|
||||
@@ -406,17 +445,13 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
// bottomSheetContentWrapper.getChildAt(i).invalidate();
|
||||
// }
|
||||
// since it's basically allergic to getting enlarged then shrunk again, and setting it at all when fullscreen makes no sense
|
||||
if(!isFullscreen){
|
||||
if (!isFullscreen) {
|
||||
Log.d("adjustLayoutHeights", "setting bottomSheet height from: " + params.height + " to: " + bottomSheetHeight);
|
||||
bottomSheetContentWrapper.setLayoutParams(
|
||||
new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, bottomSheetHeight)
|
||||
);
|
||||
}
|
||||
}
|
||||
// XXX
|
||||
// from android 5 - 9 a padding magically grows on top of bottomSheet after leaving fullscreen
|
||||
// I suspect that it's a bug with going into immersive mode then out, along with coordinator layout bottom sheet
|
||||
Log.d("adjustLayoutHeights", "padding top: " + bottomSheet.getPaddingTop() + " padding bottom: " + bottomSheet.getPaddingBottom());
|
||||
}
|
||||
|
||||
|
||||
@@ -466,7 +501,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
window.setAttributes(attributes);
|
||||
}
|
||||
|
||||
loyaltyCard = db.getLoyaltyCard(loyaltyCardId);
|
||||
loyaltyCard = DBHelper.getLoyaltyCard(database, loyaltyCardId);
|
||||
if (loyaltyCard == null) {
|
||||
Log.w(TAG, "Could not lookup loyalty card " + loyaltyCardId);
|
||||
Toast.makeText(this, R.string.noCardExistsError, Toast.LENGTH_LONG).show();
|
||||
@@ -493,7 +528,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
noteView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
List<Group> loyaltyCardGroups = db.getLoyaltyCardGroups(loyaltyCardId);
|
||||
List<Group> loyaltyCardGroups = DBHelper.getLoyaltyCardGroups(database, loyaltyCardId);
|
||||
|
||||
if (loyaltyCardGroups.size() > 0) {
|
||||
List<String> groupNames = new ArrayList<>();
|
||||
@@ -522,7 +557,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
int expiryString = R.string.expiryStateSentence;
|
||||
if (Utils.hasExpired(loyaltyCard.expiry)) {
|
||||
expiryString = R.string.expiryStateSentenceExpired;
|
||||
expiryView.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.alert));
|
||||
expiryView.setTextColor(Color.RED);
|
||||
}
|
||||
expiryView.setText(getString(expiryString, DateFormat.getDateInstance(DateFormat.LONG).format(loyaltyCard.expiry)));
|
||||
expiryView.setTextSize(settings.getFontSizeMax(settings.getMediumFont()));
|
||||
@@ -560,15 +595,39 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
storeName.setTextColor(textColor);
|
||||
landscapeToolbar.setTitleTextColor(textColor);
|
||||
|
||||
// Also apply colours to UI elements
|
||||
int darkenedColor = ColorUtils.blendARGB(backgroundHeaderColor, Color.BLACK, 0.1f);
|
||||
barcodeScaler.setProgressTintList(ColorStateList.valueOf(darkenedColor));
|
||||
barcodeScaler.setThumbTintList(ColorStateList.valueOf(darkenedColor));
|
||||
maximizeButton.setBackgroundColor(darkenedColor);
|
||||
minimizeButton.setBackgroundColor(darkenedColor);
|
||||
bottomSheetButton.setBackgroundColor(darkenedColor);
|
||||
maximizeButton.setColorFilter(textColor);
|
||||
minimizeButton.setColorFilter(textColor);
|
||||
bottomSheetButton.setColorFilter(textColor);
|
||||
int complementaryColor = Utils.getComplementaryColor(darkenedColor);
|
||||
editButton.setBackgroundTintList(ColorStateList.valueOf(complementaryColor));
|
||||
Drawable editButtonIcon = editButton.getDrawable();
|
||||
editButtonIcon.mutate();
|
||||
int colorPrimary = MaterialColors.getColor(this, R.attr.colorPrimary, ContextCompat.getColor(this, R.color.md_theme_light_primary));
|
||||
int colorOnPrimary = MaterialColors.getColor(this, R.attr.colorOnPrimary, ContextCompat.getColor(this, R.color.md_theme_light_onPrimary));
|
||||
boolean darkMode = Utils.isDarkModeEnabled(this);
|
||||
if (Utils.needsDarkForeground(complementaryColor)) {
|
||||
editButtonIcon.setTint(darkMode ? colorOnPrimary : colorPrimary);
|
||||
} else {
|
||||
editButtonIcon.setTint(darkMode ? colorPrimary : colorOnPrimary);
|
||||
}
|
||||
editButton.setImageDrawable(editButtonIcon);
|
||||
|
||||
Bitmap icon = Utils.retrieveCardImage(this, loyaltyCard.id, ImageLocationType.icon);
|
||||
if (icon != null){
|
||||
if (icon != null) {
|
||||
int backgroundAlphaColor = Utils.needsDarkForeground(backgroundHeaderColor) ? Color.WHITE : Color.BLACK;
|
||||
Log.d("onResume", "setting icon image");
|
||||
iconImage.setImageBitmap(icon);
|
||||
int backgroundWithAlpha = Color.argb(HEADER_FILTER_ALPHA, Color.red(backgroundAlphaColor), Color.green(backgroundAlphaColor), Color.blue(backgroundAlphaColor));
|
||||
// for images that has alpha
|
||||
appBarLayout.setBackgroundColor(backgroundWithAlpha);
|
||||
}else{
|
||||
} else {
|
||||
Bitmap plain = Bitmap.createBitmap(new int[]{backgroundHeaderColor}, 1, 1, Bitmap.Config.ARGB_8888);
|
||||
iconImage.setImageBitmap(plain);
|
||||
appBarLayout.setBackgroundColor(Color.TRANSPARENT);
|
||||
@@ -584,8 +643,11 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
// Make notification area light if dark icons are needed
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
window.getDecorView().setSystemUiVisibility(backgroundNeedsDarkIcons ? View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR : 0);
|
||||
window.setStatusBarColor(Color.TRANSPARENT);
|
||||
} else {
|
||||
// Darken statusbar if icons won't be visible otherwise
|
||||
window.setStatusBarColor(backgroundNeedsDarkIcons ? ColorUtils.blendARGB(backgroundHeaderColor, Color.BLACK, 0.15f) : 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);
|
||||
@@ -614,30 +676,14 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
imageTypes.add(ImageType.IMAGE_BACK);
|
||||
}
|
||||
|
||||
dotIndicator.removeAllViews();
|
||||
if (imageTypes.size() >= 2) {
|
||||
dots = new ImageView[imageTypes.size()];
|
||||
boolean darkMode = Utils.isDarkModeEnabled(getApplicationContext());
|
||||
|
||||
for (int i = 0; i < imageTypes.size(); i++) {
|
||||
dots[i] = new ImageView(this);
|
||||
dots[i].setImageDrawable(getDotIcon(false, darkMode));
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
dotIndicator.setVisibility(View.VISIBLE);
|
||||
}
|
||||
setDotIndicator(darkMode);
|
||||
|
||||
setFullscreen(isFullscreen);
|
||||
|
||||
// restore bottomSheet UI states from changing orientation
|
||||
changeUiToBottomSheetState(bottomSheetState);
|
||||
|
||||
db.updateLoyaltyCardLastUsed(loyaltyCard.id);
|
||||
DBHelper.updateLoyaltyCardLastUsed(database, loyaltyCard.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -653,18 +699,19 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
@Override
|
||||
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) {
|
||||
item.setVisible(false);
|
||||
}
|
||||
|
||||
loyaltyCard = db.getLoyaltyCard(loyaltyCardId);
|
||||
loyaltyCard = DBHelper.getLoyaltyCard(database, loyaltyCardId);
|
||||
starred = loyaltyCard.starStatus != 0;
|
||||
|
||||
if(loyaltyCard.archiveStatus != 0){
|
||||
menu.findItem(R.id.action_unarchive).setVisible(true);
|
||||
menu.findItem(R.id.action_archive).setVisible(false);
|
||||
}
|
||||
else{
|
||||
menu.findItem(R.id.action_unarchive).setVisible(false);
|
||||
menu.findItem(R.id.action_archive).setVisible(true);
|
||||
}
|
||||
|
||||
menu.findItem(R.id.action_overflow).setIcon(getIcon(R.drawable.ic_overflow_menu, backgroundNeedsDarkIcons));
|
||||
menu.findItem(R.id.action_share).setIcon(getIcon(R.drawable.ic_share_white, backgroundNeedsDarkIcons));
|
||||
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
@@ -702,20 +749,53 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
}
|
||||
return true;
|
||||
|
||||
case R.id.action_lock_unlock:
|
||||
if (rotationEnabled) {
|
||||
setOrientatonLock(item, true);
|
||||
} else {
|
||||
setOrientatonLock(item, false);
|
||||
}
|
||||
rotationEnabled = !rotationEnabled;
|
||||
case R.id.action_duplicate:
|
||||
loyaltyCard = DBHelper.getLoyaltyCard(database, loyaltyCardId);
|
||||
Intent intent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt("id", loyaltyCardId);
|
||||
bundle.putBoolean("duplicateId", true);
|
||||
intent.putExtras(bundle);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
|
||||
case R.id.action_star_unstar:
|
||||
starred = !starred;
|
||||
db.updateLoyaltyCardStarStatus(loyaltyCardId, starred ? 1 : 0);
|
||||
DBHelper.updateLoyaltyCardStarStatus(database, loyaltyCardId, starred ? 1 : 0);
|
||||
invalidateOptionsMenu();
|
||||
return true;
|
||||
|
||||
case R.id.action_archive:
|
||||
DBHelper.updateLoyaltyCardArchiveStatus(database, loyaltyCardId, 1);
|
||||
Toast.makeText(LoyaltyCardViewActivity.this, R.string.archived, Toast.LENGTH_LONG).show();
|
||||
invalidateOptionsMenu();
|
||||
return true;
|
||||
|
||||
case R.id.action_unarchive:
|
||||
DBHelper.updateLoyaltyCardArchiveStatus(database, loyaltyCardId, 0);
|
||||
Toast.makeText(LoyaltyCardViewActivity.this, R.string.unarchived, Toast.LENGTH_LONG).show();
|
||||
invalidateOptionsMenu();
|
||||
return true;
|
||||
|
||||
case R.id.action_delete:
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.deleteTitle);
|
||||
builder.setMessage(R.string.deleteConfirmation);
|
||||
builder.setPositiveButton(R.string.confirm, (dialog, which) -> {
|
||||
Log.e(TAG, "Deleting card: " + loyaltyCardId);
|
||||
|
||||
DBHelper.deleteLoyaltyCard(database, LoyaltyCardViewActivity.this, loyaltyCardId);
|
||||
|
||||
ShortcutHelper.removeShortcut(LoyaltyCardViewActivity.this, loyaltyCardId);
|
||||
|
||||
finish();
|
||||
dialog.dismiss();
|
||||
});
|
||||
builder.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.show();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
@@ -727,7 +807,6 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
int orientation = getResources().getConfiguration().orientation;
|
||||
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
Log.d(TAG, "Detected landscape mode");
|
||||
|
||||
setTitle(loyaltyCard.store);
|
||||
|
||||
collapsingToolbarLayout.setVisibility(View.GONE);
|
||||
@@ -753,19 +832,6 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
item.setIcon(getIcon(R.drawable.ic_lock_open_white_24dp, backgroundNeedsDarkIcons));
|
||||
item.setTitle(R.string.lockScreen);
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
|
||||
}
|
||||
}
|
||||
|
||||
private void makeBottomSheetVisibleIfUseful() {
|
||||
if (noteView.getVisibility() == View.VISIBLE || groupsView.getVisibility() == View.VISIBLE || balanceView.getVisibility() == View.VISIBLE || expiryView.getVisibility() == View.VISIBLE) {
|
||||
bottomSheet.setVisibility(View.VISIBLE);
|
||||
@@ -774,7 +840,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
}
|
||||
}
|
||||
|
||||
private void drawBarcode() {
|
||||
private void drawBarcode(boolean addPadding) {
|
||||
mTasks.flushTaskList(TaskHandler.TYPE.BARCODE, true, false, false);
|
||||
if (format != null) {
|
||||
BarcodeImageWriterTask barcodeWriter = new BarcodeImageWriterTask(
|
||||
@@ -784,12 +850,13 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
format,
|
||||
null,
|
||||
false,
|
||||
null);
|
||||
barcodeImageGenerationFinishedCallback,
|
||||
addPadding);
|
||||
mTasks.executeTask(TaskHandler.TYPE.BARCODE, barcodeWriter);
|
||||
}
|
||||
}
|
||||
|
||||
private void redrawBarcodeAfterResize() {
|
||||
private void redrawBarcodeAfterResize(boolean addPadding) {
|
||||
if (format != null) {
|
||||
mainImage.getViewTreeObserver().addOnGlobalLayoutListener(
|
||||
new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@@ -798,13 +865,13 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
mainImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
|
||||
Log.d(TAG, "ImageView size now known");
|
||||
drawBarcode();
|
||||
drawBarcode(addPadding);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void drawMainImage(int index, boolean waitForResize) {
|
||||
private void drawMainImage(int index, boolean waitForResize, boolean isFullscreen) {
|
||||
if (imageTypes.isEmpty()) {
|
||||
mainImage.setVisibility(View.GONE);
|
||||
return;
|
||||
@@ -820,18 +887,28 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
ImageType wantedImageType = imageTypes.get(index);
|
||||
|
||||
if (wantedImageType == ImageType.BARCODE) {
|
||||
if (waitForResize) {
|
||||
redrawBarcodeAfterResize();
|
||||
// Use border in non-fullscreen mode
|
||||
if (!isFullscreen) {
|
||||
mainImage.setBackground(AppCompatResources.getDrawable(this, R.drawable.round_outline));
|
||||
} else {
|
||||
drawBarcode();
|
||||
mainImage.setBackgroundColor(Color.WHITE);
|
||||
}
|
||||
mainImage.setBackgroundColor(Color.WHITE);
|
||||
|
||||
if (waitForResize) {
|
||||
redrawBarcodeAfterResize(!isFullscreen);
|
||||
} else {
|
||||
drawBarcode(!isFullscreen);
|
||||
}
|
||||
|
||||
mainImage.setContentDescription(getString(R.string.barcodeImageDescriptionWithType, format.prettyName()));
|
||||
} else if (wantedImageType == ImageType.IMAGE_FRONT) {
|
||||
mainImage.setImageBitmap(frontImageBitmap);
|
||||
mainImage.setBackgroundColor(Color.TRANSPARENT);
|
||||
mainImage.setContentDescription(getString(R.string.frontImageDescription));
|
||||
} else if (wantedImageType == ImageType.IMAGE_BACK) {
|
||||
mainImage.setImageBitmap(backImageBitmap);
|
||||
mainImage.setBackgroundColor(Color.TRANSPARENT);
|
||||
mainImage.setContentDescription(getString(R.string.backImageDescription));
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown image type: " + wantedImageType);
|
||||
}
|
||||
@@ -852,7 +929,26 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
|
||||
mainImageIndex = newIndex;
|
||||
|
||||
drawMainImage(newIndex, false);
|
||||
drawMainImage(newIndex, false, isFullscreen);
|
||||
}
|
||||
|
||||
private void setDotIndicator(boolean darkMode) {
|
||||
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, darkMode));
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
dotIndicator.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -868,7 +964,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
if (enabled && !imageTypes.isEmpty()) {
|
||||
Log.d(TAG, "Move into fullscreen");
|
||||
|
||||
drawMainImage(mainImageIndex, true);
|
||||
drawMainImage(mainImageIndex, true, isFullscreen);
|
||||
|
||||
barcodeScaler.setProgress(loyaltyCard.zoomLevel);
|
||||
setCenterGuideline(loyaltyCard.zoomLevel);
|
||||
@@ -889,6 +985,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
// Or the barcode will be centered instead of on top of the screen
|
||||
// Don't ask me why...
|
||||
appBarLayout.setVisibility(View.INVISIBLE);
|
||||
iconImage.setVisibility(View.INVISIBLE);
|
||||
collapsingToolbarLayout.setVisibility(View.GONE);
|
||||
landscapeToolbar.setVisibility(View.GONE);
|
||||
|
||||
@@ -898,6 +995,8 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||
editButton.hide();
|
||||
|
||||
// android 5-9, avoid padding growing on top of bottomSheet
|
||||
coordinatorLayout.removeView(bottomSheet);
|
||||
|
||||
// Set Android to fullscreen mode
|
||||
getWindow().getDecorView().setSystemUiVisibility(
|
||||
@@ -911,7 +1010,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
// Reset center guideline
|
||||
setCenterGuideline(100);
|
||||
|
||||
drawMainImage(mainImageIndex, true);
|
||||
drawMainImage(mainImageIndex, true, isFullscreen);
|
||||
|
||||
// Show maximize and hide minimize button and scaler
|
||||
maximizeButton.setVisibility(imageTypes.isEmpty() ? View.GONE : View.VISIBLE);
|
||||
@@ -928,6 +1027,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
// And restore 24dp paddingTop for appBarLayout
|
||||
appBarLayout.setVisibility(View.VISIBLE);
|
||||
setupOrientation();
|
||||
iconImage.setVisibility(View.VISIBLE);
|
||||
|
||||
// Show other UI elements
|
||||
cardIdFieldView.setVisibility(View.VISIBLE);
|
||||
@@ -940,6 +1040,11 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
& ~View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
& ~View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
);
|
||||
|
||||
// android 5-9, avoid padding growing on top of bottomSheet
|
||||
if (bottomSheet.getParent() != coordinatorLayout) {
|
||||
coordinatorLayout.addView(bottomSheet);
|
||||
}
|
||||
}
|
||||
|
||||
Log.d("setFullScreen", "Is full screen enabled? " + enabled + " Zoom Level = " + barcodeScaler.getProgress());
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.SearchManager;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.database.CursorIndexOutOfBoundsException;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
@@ -18,6 +19,16 @@ import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.view.ActionMode;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.splashscreen.SplashScreen;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
|
||||
@@ -26,22 +37,18 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.view.ActionMode;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.splashscreen.SplashScreen;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import protect.card_locker.preferences.SettingsActivity;
|
||||
|
||||
public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCardCursorAdapter.CardAdapterListener, GestureDetector.OnGestureListener {
|
||||
private static final String TAG = "Catima";
|
||||
public static final String RESTART_ACTIVITY_INTENT = "restart_activity_intent";
|
||||
|
||||
private final DBHelper mDB = new DBHelper(this);
|
||||
private SQLiteDatabase mDatabase;
|
||||
private LoyaltyCardCursorAdapter mAdapter;
|
||||
private ActionMode mCurrentActionMode;
|
||||
private Menu mMenu;
|
||||
private SearchView mSearchView;
|
||||
private GestureDetector mGestureDetector;
|
||||
private int mLoyaltyCardCount = 0;
|
||||
protected String mFilter = "";
|
||||
protected Object mGroup = null;
|
||||
protected DBHelper.LoyaltyCardOrder mOrder = DBHelper.LoyaltyCardOrder.Alpha;
|
||||
@@ -52,6 +59,12 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
private View mNoMatchingCardsText;
|
||||
private View mNoGroupCardsText;
|
||||
|
||||
private boolean mArchiveMode;
|
||||
public static final String BUNDLE_ARCHIVE_MODE = "archiveMode";
|
||||
|
||||
private ActivityResultLauncher<Intent> mBarcodeScannerLauncher;
|
||||
private ActivityResultLauncher<Intent> mSettingsLauncher;
|
||||
|
||||
private ActionMode.Callback mCurrentActionModeCallback = new ActionMode.Callback() {
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode inputMode, Menu inputMenu) {
|
||||
@@ -132,12 +145,10 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
}
|
||||
|
||||
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);
|
||||
Log.d(TAG, "Deleting card: " + loyaltyCard.id);
|
||||
|
||||
db.deleteLoyaltyCard(loyaltyCard.id);
|
||||
DBHelper.deleteLoyaltyCard(mDatabase, MainActivity.this, loyaltyCard.id);
|
||||
|
||||
ShortcutHelper.removeShortcut(MainActivity.this, loyaltyCard.id);
|
||||
}
|
||||
@@ -145,7 +156,7 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
TabLayout.Tab tab = ((TabLayout) findViewById(R.id.groups)).getTabAt(selectedTab);
|
||||
mGroup = tab != null ? tab.getTag() : null;
|
||||
|
||||
updateLoyaltyCardList();
|
||||
updateLoyaltyCardList(true);
|
||||
|
||||
dialog.dismiss();
|
||||
});
|
||||
@@ -155,6 +166,44 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
|
||||
return true;
|
||||
}
|
||||
else if(inputItem.getItemId() == R.id.action_archive){
|
||||
for (LoyaltyCard loyaltyCard : mAdapter.getSelectedItems()) {
|
||||
Log.d(TAG, "Archiving card: " + loyaltyCard.id);
|
||||
DBHelper.updateLoyaltyCardArchiveStatus(mDatabase, loyaltyCard.id,1);
|
||||
updateLoyaltyCardList(false);
|
||||
inputMode.finish();
|
||||
invalidateOptionsMenu();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if(inputItem.getItemId() == R.id.action_unarchive){
|
||||
for (LoyaltyCard loyaltyCard : mAdapter.getSelectedItems()) {
|
||||
Log.d(TAG, "Unarchiving card: " + loyaltyCard.id);
|
||||
DBHelper.updateLoyaltyCardArchiveStatus(mDatabase, loyaltyCard.id,0);
|
||||
updateLoyaltyCardList(false);
|
||||
inputMode.finish();
|
||||
invalidateOptionsMenu();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if(inputItem.getItemId() == R.id.action_star){
|
||||
for (LoyaltyCard loyaltyCard : mAdapter.getSelectedItems()) {
|
||||
Log.d(TAG, "Starring card: " + loyaltyCard.id);
|
||||
DBHelper.updateLoyaltyCardStarStatus(mDatabase, loyaltyCard.id, 1);
|
||||
updateLoyaltyCardList(false);
|
||||
inputMode.finish();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if(inputItem.getItemId() == R.id.action_unstar){
|
||||
for (LoyaltyCard loyaltyCard : mAdapter.getSelectedItems()) {
|
||||
Log.d(TAG, "Unstarring card: " + loyaltyCard.id);
|
||||
DBHelper.updateLoyaltyCardStarStatus(mDatabase, loyaltyCard.id, 0);
|
||||
updateLoyaltyCardList(false);
|
||||
inputMode.finish();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -163,24 +212,38 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
public void onDestroyActionMode(ActionMode inputMode) {
|
||||
mAdapter.clearSelections();
|
||||
mCurrentActionMode = null;
|
||||
mCardList.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mAdapter.resetAnimationIndex();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle inputSavedInstanceState) {
|
||||
extractIntentFields(getIntent());
|
||||
super.onCreate(inputSavedInstanceState);
|
||||
SplashScreen.installSplashScreen(this);
|
||||
setTitle(R.string.app_name);
|
||||
setContentView(R.layout.main_activity);
|
||||
if(!mArchiveMode) {
|
||||
setTitle(R.string.app_name);
|
||||
setContentView(R.layout.main_activity);
|
||||
}
|
||||
else{
|
||||
setTitle(R.string.archiveList);
|
||||
setContentView(R.layout.archive_activity);
|
||||
}
|
||||
// XXX color patching has to be done again after setting splash screen
|
||||
Utils.patchColors(this);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
if(mArchiveMode){
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
mDatabase = new DBHelper(this).getWritableDatabase();
|
||||
|
||||
|
||||
|
||||
TabLayout groupsTabLayout = findViewById(R.id.groups);
|
||||
groupsTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
|
||||
@Override
|
||||
@@ -188,7 +251,7 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
selectedTab = tab.getPosition();
|
||||
Log.d("onTabSelected", "Tab Position " + tab.getPosition());
|
||||
mGroup = tab.getTag();
|
||||
updateLoyaltyCardList();
|
||||
updateLoyaltyCardList(false);
|
||||
// Store active tab in Shared Preference to restore next app launch
|
||||
SharedPreferences activeTabPref = getApplicationContext().getSharedPreferences(
|
||||
getString(R.string.sharedpreference_active_tab),
|
||||
@@ -228,7 +291,7 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
registerForContextMenu(mCardList);
|
||||
|
||||
mGroup = null;
|
||||
updateLoyaltyCardList();
|
||||
updateLoyaltyCardList(true);
|
||||
|
||||
/*
|
||||
* This was added for Huawei, but Huawei is just too much of a fucking pain.
|
||||
@@ -260,23 +323,48 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
.show();
|
||||
}
|
||||
*/
|
||||
|
||||
mBarcodeScannerLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
|
||||
Intent intent = result.getData();
|
||||
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(Utils.BARCODE_SCAN, result.getResultCode(), intent, this);
|
||||
|
||||
if (!barcodeValues.isEmpty()) {
|
||||
Intent newIntent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
|
||||
Bundle newBundle = new Bundle();
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
mSettingsLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
|
||||
if (result.getResultCode() == Activity.RESULT_OK) {
|
||||
Intent intent = result.getData();
|
||||
if (intent != null && intent.getBooleanExtra(RESTART_ACTIVITY_INTENT, false)) {
|
||||
recreate();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
mAdapter.refreshState();
|
||||
|
||||
if (mCurrentActionMode != null) {
|
||||
mAdapter.clearSelections();
|
||||
mCurrentActionMode.finish();
|
||||
}
|
||||
|
||||
if (mMenu != null) {
|
||||
SearchView searchView = (SearchView) mMenu.findItem(R.id.action_search).getActionView();
|
||||
|
||||
if (!searchView.isIconified()) {
|
||||
mFilter = searchView.getQuery().toString();
|
||||
}
|
||||
if (mSearchView != null && !mSearchView.isIconified()) {
|
||||
mFilter = mSearchView.getQuery().toString();
|
||||
}
|
||||
|
||||
// Start of active tab logic
|
||||
@@ -309,83 +397,67 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
assert tab != null;
|
||||
mGroup = tab.getTag();
|
||||
}
|
||||
updateLoyaltyCardList();
|
||||
updateLoyaltyCardList(true);
|
||||
// End of active tab logic
|
||||
|
||||
FloatingActionButton addButton = findViewById(R.id.fabAdd);
|
||||
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();
|
||||
}
|
||||
if (!mArchiveMode) {
|
||||
FloatingActionButton addButton = findViewById(R.id.fabAdd);
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
super.onActivityResult(requestCode, resultCode, intent);
|
||||
|
||||
if (requestCode == Utils.MAIN_REQUEST) {
|
||||
// We're coming back from another view so clear the search
|
||||
// We only do this now to prevent a flash of all entries right after picking one
|
||||
mFilter = "";
|
||||
if (mMenu != null) {
|
||||
MenuItem searchItem = mMenu.findItem(R.id.action_search);
|
||||
searchItem.collapseActionView();
|
||||
}
|
||||
updateLoyaltyCardList();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent, this);
|
||||
|
||||
if (!barcodeValues.isEmpty()) {
|
||||
Intent newIntent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
|
||||
Bundle newBundle = new Bundle();
|
||||
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);
|
||||
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);
|
||||
mBarcodeScannerLauncher.launch(intent);
|
||||
});
|
||||
addButton.bringToFront();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (mMenu != null) {
|
||||
SearchView searchView = (SearchView) mMenu.findItem(R.id.action_search).getActionView();
|
||||
|
||||
if (!searchView.isIconified()) {
|
||||
searchView.setIconified(true);
|
||||
return;
|
||||
}
|
||||
if (!mSearchView.isIconified()) {
|
||||
mSearchView.setIconified(true);
|
||||
return;
|
||||
}
|
||||
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
private void updateLoyaltyCardList() {
|
||||
private void displayCardSetupOptions(Menu menu, boolean shouldShow) {
|
||||
for (int id : new int[]{R.id.action_search, R.id.action_unfold, R.id.action_sort}) {
|
||||
menu.findItem(id).setVisible(shouldShow);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateLoyaltyCardCount() {
|
||||
mLoyaltyCardCount = DBHelper.getLoyaltyCardCount(mDatabase);
|
||||
}
|
||||
|
||||
private void updateLoyaltyCardList(boolean updateCount) {
|
||||
Group group = null;
|
||||
if (mGroup != null) {
|
||||
group = (Group) mGroup;
|
||||
}
|
||||
|
||||
mAdapter.swapCursor(mDB.getLoyaltyCardCursor(mFilter, group, mOrder, mOrderDirection));
|
||||
mAdapter.swapCursor(DBHelper.getLoyaltyCardCursor(mDatabase, mFilter, group, mOrder, mOrderDirection, mArchiveMode ? DBHelper.LoyaltyCardArchiveFilter.Archived : DBHelper.LoyaltyCardArchiveFilter.Unarchived));
|
||||
|
||||
if (mDB.getLoyaltyCardCount() > 0) {
|
||||
if (updateCount) {
|
||||
updateLoyaltyCardCount();
|
||||
// Update menu icons if necessary
|
||||
invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
if (mLoyaltyCardCount > 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
|
||||
mHelpText.setVisibility(View.GONE);
|
||||
mNoGroupCardsText.setVisibility(View.GONE);
|
||||
|
||||
if (mAdapter.getItemCount() > 0) {
|
||||
mCardList.setVisibility(View.VISIBLE);
|
||||
mNoMatchingCardsText.setVisibility(View.GONE);
|
||||
@@ -404,6 +476,7 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
} else {
|
||||
mCardList.setVisibility(View.GONE);
|
||||
mHelpText.setVisibility(View.VISIBLE);
|
||||
|
||||
mNoMatchingCardsText.setVisibility(View.GONE);
|
||||
mNoGroupCardsText.setVisibility(View.GONE);
|
||||
}
|
||||
@@ -413,10 +486,13 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
}
|
||||
}
|
||||
|
||||
public void updateTabGroups(TabLayout groupsTabLayout) {
|
||||
final DBHelper db = new DBHelper(this);
|
||||
private void extractIntentFields(Intent intent) {
|
||||
final Bundle b = intent.getExtras();
|
||||
mArchiveMode = b != null && b.getBoolean(BUNDLE_ARCHIVE_MODE, false);
|
||||
}
|
||||
|
||||
List<Group> newGroups = db.getGroups();
|
||||
public void updateTabGroups(TabLayout groupsTabLayout) {
|
||||
List<Group> newGroups = DBHelper.getGroups(mDatabase);
|
||||
|
||||
if (newGroups.size() == 0) {
|
||||
groupsTabLayout.removeAllTabs();
|
||||
@@ -439,26 +515,32 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
}
|
||||
|
||||
groupsTabLayout.setVisibility(View.VISIBLE);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu inputMenu) {
|
||||
this.mMenu = inputMenu;
|
||||
if(!mArchiveMode)
|
||||
getMenuInflater().inflate(R.menu.main_menu, inputMenu);
|
||||
else{
|
||||
getMenuInflater().inflate(R.menu.archive_menu, inputMenu);
|
||||
}
|
||||
|
||||
getMenuInflater().inflate(R.menu.main_menu, inputMenu);
|
||||
Utils.updateMenuCardDetailsButtonState(inputMenu.findItem(R.id.action_unfold), mAdapter.showingDetails());
|
||||
displayCardSetupOptions(inputMenu, mLoyaltyCardCount > 0);
|
||||
|
||||
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
|
||||
if (searchManager != null) {
|
||||
SearchView searchView = (SearchView) inputMenu.findItem(R.id.action_search).getActionView();
|
||||
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
|
||||
searchView.setSubmitButtonEnabled(false);
|
||||
mSearchView = (SearchView) inputMenu.findItem(R.id.action_search).getActionView();
|
||||
mSearchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
|
||||
mSearchView.setSubmitButtonEnabled(false);
|
||||
|
||||
searchView.setOnCloseListener(() -> {
|
||||
mSearchView.setOnCloseListener(() -> {
|
||||
invalidateOptionsMenu();
|
||||
return false;
|
||||
});
|
||||
|
||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||
mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
return false;
|
||||
@@ -472,12 +554,20 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
TabLayout.Tab currentTab = groupsTabLayout.getTabAt(groupsTabLayout.getSelectedTabPosition());
|
||||
mGroup = currentTab != null ? currentTab.getTag() : null;
|
||||
|
||||
updateLoyaltyCardList();
|
||||
updateLoyaltyCardList(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
if(!mArchiveMode) {
|
||||
if (DBHelper.getArchivedCardsCount(mDatabase) == 0) {
|
||||
inputMenu.findItem(R.id.action_archived).setVisible(false);
|
||||
} else {
|
||||
inputMenu.findItem(R.id.action_archived).setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
return super.onCreateOptionsMenu(inputMenu);
|
||||
}
|
||||
|
||||
@@ -485,8 +575,18 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
public boolean onOptionsItemSelected(MenuItem inputItem) {
|
||||
int id = inputItem.getItemId();
|
||||
|
||||
if (id == android.R.id.home) {
|
||||
onBackPressed();
|
||||
}
|
||||
|
||||
if (id == R.id.action_unfold) {
|
||||
mAdapter.showDetails(!mAdapter.showingDetails());
|
||||
invalidateOptionsMenu();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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++) {
|
||||
@@ -502,17 +602,21 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
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);
|
||||
CheckBox showReversed = (CheckBox) customLayout.findViewById(R.id.checkBox_reverse);
|
||||
|
||||
|
||||
showReversed.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);
|
||||
}
|
||||
|
||||
setSort(
|
||||
loyaltyCardOrders.get(currentIndex.get()),
|
||||
showReversed.isChecked() ? DBHelper.LoyaltyCardOrderDirection.Descending : DBHelper.LoyaltyCardOrderDirection.Ascending
|
||||
);
|
||||
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
@@ -526,28 +630,38 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
|
||||
if (id == R.id.action_manage_groups) {
|
||||
Intent i = new Intent(getApplicationContext(), ManageGroupsActivity.class);
|
||||
startActivityForResult(i, Utils.MAIN_REQUEST);
|
||||
startActivity(i);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (id == R.id.action_import_export) {
|
||||
Intent i = new Intent(getApplicationContext(), ImportExportActivity.class);
|
||||
startActivityForResult(i, Utils.MAIN_REQUEST);
|
||||
startActivity(i);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (id == R.id.action_settings) {
|
||||
Intent i = new Intent(getApplicationContext(), SettingsActivity.class);
|
||||
startActivityForResult(i, Utils.MAIN_REQUEST);
|
||||
mSettingsLauncher.launch(i);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (id == R.id.action_about) {
|
||||
Intent i = new Intent(getApplicationContext(), AboutActivity.class);
|
||||
startActivityForResult(i, Utils.MAIN_REQUEST);
|
||||
startActivity(i);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(id == R.id.action_archived){
|
||||
Intent i = new Intent(getApplicationContext(), MainActivity.class);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putBoolean("archiveMode", true);
|
||||
i.putExtras(bundle);
|
||||
startActivity(i);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return super.onOptionsItemSelected(inputItem);
|
||||
}
|
||||
|
||||
@@ -566,7 +680,7 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
sortPrefEditor.apply();
|
||||
|
||||
// Update card list
|
||||
updateLoyaltyCardList();
|
||||
updateLoyaltyCardList(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -669,10 +783,47 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
mCurrentActionMode.setTitle(getResources().getQuantityString(R.plurals.selectedCardCount, count, count));
|
||||
|
||||
MenuItem editItem = mCurrentActionMode.getMenu().findItem(R.id.action_edit);
|
||||
MenuItem archiveItem = mCurrentActionMode.getMenu().findItem(R.id.action_archive);
|
||||
MenuItem unarchiveItem = mCurrentActionMode.getMenu().findItem(R.id.action_unarchive);
|
||||
MenuItem starItem = mCurrentActionMode.getMenu().findItem(R.id.action_star);
|
||||
MenuItem unstarItem = mCurrentActionMode.getMenu().findItem(R.id.action_unstar);
|
||||
|
||||
boolean hasStarred = false;
|
||||
boolean hasUnstarred = false;
|
||||
|
||||
if(!mArchiveMode) {
|
||||
unarchiveItem.setVisible(false);
|
||||
archiveItem.setVisible(true);
|
||||
}
|
||||
else{
|
||||
unarchiveItem.setVisible(true);
|
||||
archiveItem.setVisible(false);
|
||||
}
|
||||
|
||||
for (LoyaltyCard loyaltyCard : mAdapter.getSelectedItems()) {
|
||||
|
||||
if (loyaltyCard.starStatus == 1) {
|
||||
hasStarred = true;
|
||||
} else {
|
||||
hasUnstarred = true;
|
||||
}
|
||||
|
||||
if (hasStarred && hasUnstarred) {
|
||||
hasStarred = true;
|
||||
hasUnstarred = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 1) {
|
||||
starItem.setVisible(!hasStarred);
|
||||
unstarItem.setVisible(!hasUnstarred);
|
||||
editItem.setVisible(true);
|
||||
editItem.setEnabled(true);
|
||||
} else {
|
||||
starItem.setVisible(hasUnstarred);
|
||||
unstarItem.setVisible(hasStarred);
|
||||
|
||||
editItem.setVisible(false);
|
||||
editItem.setEnabled(false);
|
||||
}
|
||||
@@ -687,8 +838,6 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
if (mAdapter.getSelectedItemCount() > 0) {
|
||||
enableActionMode(inputPosition);
|
||||
} else {
|
||||
Cursor selected = mAdapter.getCursor();
|
||||
selected.moveToPosition(inputPosition);
|
||||
// FIXME
|
||||
//
|
||||
// There is a really nasty edge case that can happen when someone taps a card but right
|
||||
@@ -699,7 +848,7 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
// click is being processed. Sadly, I have not yet found a way to make that possible.
|
||||
LoyaltyCard loyaltyCard;
|
||||
try {
|
||||
loyaltyCard = LoyaltyCard.toLoyaltyCard(selected);
|
||||
loyaltyCard = mAdapter.getCard(inputPosition);
|
||||
} catch (CursorIndexOutOfBoundsException e) {
|
||||
Log.w(TAG, "Prevented crash from tap + swipe on ID " + inputPosition + ": " + e);
|
||||
return;
|
||||
@@ -713,7 +862,7 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
|
||||
ShortcutHelper.updateShortcuts(MainActivity.this, loyaltyCard);
|
||||
|
||||
startActivityForResult(i, Utils.MAIN_REQUEST);
|
||||
startActivity(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package protect.card_locker;
|
||||
|
||||
|
||||
import android.content.Intent;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
@@ -25,7 +27,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class ManageGroupActivity extends CatimaAppCompatActivity implements ManageGroupCursorAdapter.CardAdapterListener {
|
||||
|
||||
private final DBHelper mDB = new DBHelper(this);
|
||||
private SQLiteDatabase mDatabase;
|
||||
private ManageGroupCursorAdapter mAdapter;
|
||||
|
||||
private final String SAVE_INSTANCE_ADAPTER_STATE = "adapterState";
|
||||
@@ -45,6 +47,8 @@ public class ManageGroupActivity extends CatimaAppCompatActivity implements Mana
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
mDatabase = new DBHelper(this).getWritableDatabase();
|
||||
|
||||
mHelpText = findViewById(R.id.helpText);
|
||||
mCardList = findViewById(R.id.list);
|
||||
FloatingActionButton saveButton = findViewById(R.id.fabSave);
|
||||
@@ -70,7 +74,7 @@ public class ManageGroupActivity extends CatimaAppCompatActivity implements Mana
|
||||
return;
|
||||
}
|
||||
if (!mGroup._id.equals(currentGroupName)) {
|
||||
if (mDB.getGroup(currentGroupName) != null) {
|
||||
if (DBHelper.getGroup(mDatabase, currentGroupName) != null) {
|
||||
mGroupNameNotInUse = false;
|
||||
mGroupNameText.setError(getResources().getText(R.string.group_name_already_in_use));
|
||||
} else {
|
||||
@@ -86,7 +90,7 @@ public class ManageGroupActivity extends CatimaAppCompatActivity implements Mana
|
||||
throw (new IllegalArgumentException("this activity expects a group loaded into it's intent"));
|
||||
}
|
||||
Log.d("groupId", "groupId: " + groupId);
|
||||
mGroup = mDB.getGroup(groupId);
|
||||
mGroup = DBHelper.getGroup(mDatabase, groupId);
|
||||
if (mGroup == null) {
|
||||
throw (new IllegalArgumentException("cannot load group " + groupId + " from database"));
|
||||
}
|
||||
@@ -123,7 +127,7 @@ public class ManageGroupActivity extends CatimaAppCompatActivity implements Mana
|
||||
|
||||
mAdapter.commitToDatabase();
|
||||
if (!currentGroupName.equals(mGroup._id)) {
|
||||
mDB.updateGroup(mGroup._id, currentGroupName);
|
||||
DBHelper.updateGroup(mDatabase, mGroup._id, currentGroupName);
|
||||
}
|
||||
Toast.makeText(getApplicationContext(), R.string.group_updated, Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
@@ -153,6 +157,26 @@ public class ManageGroupActivity extends CatimaAppCompatActivity implements Mana
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu inputMenu) {
|
||||
getMenuInflater().inflate(R.menu.card_details_menu, inputMenu);
|
||||
Utils.updateMenuCardDetailsButtonState(inputMenu.findItem(R.id.action_unfold), mAdapter.showingDetails());
|
||||
return super.onCreateOptionsMenu(inputMenu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem inputItem) {
|
||||
int id = inputItem.getItemId();
|
||||
|
||||
if (id == R.id.action_unfold) {
|
||||
mAdapter.showDetails(!mAdapter.showingDetails());
|
||||
invalidateOptionsMenu();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(inputItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
@@ -163,9 +187,9 @@ public class ManageGroupActivity extends CatimaAppCompatActivity implements Mana
|
||||
}
|
||||
|
||||
private void updateLoyaltyCardList() {
|
||||
mAdapter.swapCursor(mDB.getLoyaltyCardCursor());
|
||||
mAdapter.swapCursor(DBHelper.getLoyaltyCardCursor(mDatabase));
|
||||
|
||||
if (mAdapter.getCountFromCursor() == 0) {
|
||||
if (mAdapter.getItemCount() == 0) {
|
||||
mCardList.setVisibility(View.GONE);
|
||||
mHelpText.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
@@ -205,7 +229,7 @@ public class ManageGroupActivity extends CatimaAppCompatActivity implements Mana
|
||||
|
||||
@Override
|
||||
public void onRowLongClicked(int inputPosition) {
|
||||
// do nothing for now
|
||||
mAdapter.toggleSelection(inputPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,24 +2,27 @@ package protect.card_locker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class ManageGroupCursorAdapter extends LoyaltyCardCursorAdapter {
|
||||
private HashMap<Integer, Integer> mIndexCardMap;
|
||||
private HashMap<Integer, Boolean> mInGroupOverlay;
|
||||
private HashMap<Integer, Boolean> mIsLoyaltyCardInGroupCache;
|
||||
private HashMap<Integer, List<Group>> mGetGroupCache;
|
||||
final private Group mGroup;
|
||||
final private DBHelper mDb;
|
||||
final private SQLiteDatabase mDatabase;
|
||||
|
||||
public ManageGroupCursorAdapter(Context inputContext, Cursor inputCursor, CardAdapterListener inputListener, Group group) {
|
||||
super(inputContext, inputCursor, inputListener);
|
||||
mGroup = new Group(group._id, group.order);
|
||||
mInGroupOverlay = new HashMap<>();
|
||||
mDb = new DBHelper(inputContext);
|
||||
mDatabase = new DBHelper(inputContext).getWritableDatabase();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -47,7 +50,7 @@ public class ManageGroupCursorAdapter extends LoyaltyCardCursorAdapter {
|
||||
if (cache != null) {
|
||||
return cache;
|
||||
}
|
||||
List<Group> groups = mDb.getLoyaltyCardGroups(cardId);
|
||||
List<Group> groups = DBHelper.getLoyaltyCardGroups(mDatabase, cardId);
|
||||
mGetGroupCache.put(cardId, groups);
|
||||
return groups;
|
||||
}
|
||||
@@ -94,7 +97,7 @@ public class ManageGroupCursorAdapter extends LoyaltyCardCursorAdapter {
|
||||
} else {
|
||||
groups.remove(mGroup);
|
||||
}
|
||||
mDb.setLoyaltyCardGroups(cardId, groups);
|
||||
DBHelper.setLoyaltyCardGroups(mDatabase, cardId, groups);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,8 +108,4 @@ public class ManageGroupCursorAdapter extends LoyaltyCardCursorAdapter {
|
||||
public HashMap<Integer, Boolean> exportInGroupState() {
|
||||
return new HashMap<>(mInGroupOverlay);
|
||||
}
|
||||
|
||||
public int getCountFromCursor() {
|
||||
return super.getCursor().getCount();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package protect.card_locker;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.os.Bundle;
|
||||
import android.text.InputType;
|
||||
import android.view.MenuItem;
|
||||
@@ -26,7 +27,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
public class ManageGroupsActivity extends CatimaAppCompatActivity implements GroupCursorAdapter.GroupAdapterListener {
|
||||
private static final String TAG = "Catima";
|
||||
|
||||
private final DBHelper mDb = new DBHelper(this);
|
||||
private SQLiteDatabase mDatabase;
|
||||
private TextView mHelpText;
|
||||
private RecyclerView mGroupList;
|
||||
GroupCursorAdapter mAdapter;
|
||||
@@ -42,6 +43,8 @@ public class ManageGroupsActivity extends CatimaAppCompatActivity implements Gro
|
||||
if (actionBar != null) {
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
mDatabase = new DBHelper(this).getWritableDatabase();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,9 +75,9 @@ public class ManageGroupsActivity extends CatimaAppCompatActivity implements Gro
|
||||
}
|
||||
|
||||
private void updateGroupList() {
|
||||
mAdapter.swapCursor(mDb.getGroupCursor());
|
||||
mAdapter.swapCursor(DBHelper.getGroupCursor(mDatabase));
|
||||
|
||||
if (mDb.getGroupCount() == 0) {
|
||||
if (DBHelper.getGroupCount(mDatabase) == 0) {
|
||||
mGroupList.setVisibility(View.GONE);
|
||||
mHelpText.setVisibility(View.VISIBLE);
|
||||
|
||||
@@ -106,7 +109,7 @@ public class ManageGroupsActivity extends CatimaAppCompatActivity implements Gro
|
||||
}
|
||||
|
||||
private void createGroup() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.AlertDialogTheme);
|
||||
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);
|
||||
@@ -118,11 +121,11 @@ public class ManageGroupsActivity extends CatimaAppCompatActivity implements Gro
|
||||
Toast.makeText(getApplicationContext(), R.string.group_name_is_empty, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
if (mDb.getGroup(inputString) != null) {
|
||||
if (DBHelper.getGroup(mDatabase, inputString) != null) {
|
||||
Toast.makeText(getApplicationContext(), R.string.group_name_already_in_use, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
mDb.insertGroup(inputString);
|
||||
DBHelper.insertGroup(mDatabase, inputString);
|
||||
updateGroupList();
|
||||
});
|
||||
builder.setNegativeButton(getString(R.string.cancel), (dialog, which) -> dialog.cancel());
|
||||
@@ -138,10 +141,10 @@ public class ManageGroupsActivity extends CatimaAppCompatActivity implements Gro
|
||||
}
|
||||
|
||||
private void moveGroup(View view, boolean up) {
|
||||
List<Group> groups = mDb.getGroups();
|
||||
List<Group> groups = DBHelper.getGroups(mDatabase);
|
||||
final String groupName = getGroupName(view);
|
||||
|
||||
int currentIndex = mDb.getGroup(groupName).order;
|
||||
int currentIndex = DBHelper.getGroup(mDatabase, groupName).order;
|
||||
int newIndex;
|
||||
|
||||
// Reinsert group in correct position
|
||||
@@ -160,7 +163,7 @@ public class ManageGroupsActivity extends CatimaAppCompatActivity implements Gro
|
||||
groups.add(newIndex, group);
|
||||
|
||||
// Update database
|
||||
mDb.reorderGroups(groups);
|
||||
DBHelper.reorderGroups(mDatabase, groups);
|
||||
|
||||
// Update UI
|
||||
updateGroupList();
|
||||
@@ -195,7 +198,7 @@ public class ManageGroupsActivity extends CatimaAppCompatActivity implements Gro
|
||||
builder.setMessage(groupName);
|
||||
|
||||
builder.setPositiveButton(getString(R.string.ok), (dialog, which) -> {
|
||||
mDb.deleteGroup(groupName);
|
||||
DBHelper.deleteGroup(mDatabase, groupName);
|
||||
updateGroupList();
|
||||
// Delete may change ordering, so invalidate
|
||||
invalidateHomescreenActiveTab();
|
||||
|
||||
@@ -20,6 +20,8 @@ import com.journeyapps.barcodescanner.DecoratedBarcodeView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
@@ -39,6 +41,10 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
||||
private String addGroup;
|
||||
private boolean torch = false;
|
||||
|
||||
private ActivityResultLauncher<Intent> manualAddLauncher;
|
||||
// can't use the pre-made contract because that launches the file manager for image type instead of gallery
|
||||
private ActivityResultLauncher<Intent> photoPickerLauncher;
|
||||
|
||||
private void extractIntentFields(Intent intent) {
|
||||
final Bundle b = intent.getExtras();
|
||||
cardId = b != null ? b.getString(LoyaltyCardEditActivity.BUNDLE_CARDID) : null;
|
||||
@@ -60,6 +66,8 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
||||
|
||||
extractIntentFields(getIntent());
|
||||
|
||||
manualAddLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> handleActivityResult(Utils.SELECT_BARCODE_REQUEST, result.getResultCode(), result.getData()));
|
||||
photoPickerLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> handleActivityResult(Utils.BARCODE_IMPORT_FROM_IMAGE_FILE, result.getResultCode(), result.getData()));
|
||||
findViewById(R.id.add_from_image).setOnClickListener(this::addFromImage);
|
||||
findViewById(R.id.add_manually).setOnClickListener(this::addManually);
|
||||
|
||||
@@ -159,8 +167,7 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
private void handleActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
super.onActivityResult(requestCode, resultCode, intent);
|
||||
|
||||
BarcodeValues barcodeValues;
|
||||
@@ -193,12 +200,12 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
||||
b.putString("initialCardId", cardId);
|
||||
i.putExtras(b);
|
||||
}
|
||||
startActivityForResult(i, Utils.SELECT_BARCODE_REQUEST);
|
||||
manualAddLauncher.launch(i);
|
||||
}
|
||||
|
||||
public void addFromImage(View view) {
|
||||
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
|
||||
photoPickerIntent.setType("image/*");
|
||||
startActivityForResult(photoPickerIntent, Utils.BARCODE_IMPORT_FROM_IMAGE_FILE);
|
||||
photoPickerLauncher.launch(photoPickerIntent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,14 @@ package protect.card_locker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.util.Arrays;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
@@ -18,8 +20,6 @@ import androidx.core.content.pm.ShortcutManagerCompat;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.core.graphics.drawable.IconCompat;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
class ShortcutHelper {
|
||||
// Android documentation says that no more than 5 shortcuts
|
||||
// are supported. However, that may be too many, as not all
|
||||
@@ -46,7 +46,7 @@ class ShortcutHelper {
|
||||
static void updateShortcuts(Context context, LoyaltyCard card) {
|
||||
LinkedList<ShortcutInfoCompat> list = new LinkedList<>(ShortcutManagerCompat.getDynamicShortcuts(context));
|
||||
|
||||
DBHelper dbHelper = new DBHelper(context);
|
||||
SQLiteDatabase database = new DBHelper(context).getReadableDatabase();
|
||||
|
||||
String shortcutId = Integer.toString(card.id);
|
||||
|
||||
@@ -89,7 +89,7 @@ class ShortcutHelper {
|
||||
for (int index = 0; index < list.size(); index++) {
|
||||
ShortcutInfoCompat prevShortcut = list.get(index);
|
||||
|
||||
LoyaltyCard loyaltyCard = dbHelper.getLoyaltyCard(Integer.parseInt(prevShortcut.getId()));
|
||||
LoyaltyCard loyaltyCard = DBHelper.getLoyaltyCard(database, Integer.parseInt(prevShortcut.getId()));
|
||||
|
||||
ShortcutInfoCompat updatedShortcut = createShortcutBuilder(context, loyaltyCard)
|
||||
.setRank(index)
|
||||
@@ -120,7 +120,8 @@ class ShortcutHelper {
|
||||
ShortcutManagerCompat.setDynamicShortcuts(context, list);
|
||||
}
|
||||
|
||||
static @NotNull Bitmap createAdaptiveBitmap(@NotNull Bitmap in, int paddingColor){
|
||||
static @NotNull
|
||||
Bitmap createAdaptiveBitmap(@NotNull Bitmap in, int paddingColor) {
|
||||
Bitmap ret = Bitmap.createBitmap(ADAPTIVE_BITMAP_SIZE, ADAPTIVE_BITMAP_SIZE, Bitmap.Config.ARGB_8888);
|
||||
Canvas output = new Canvas(ret);
|
||||
output.drawColor(ColorUtils.compositeColors(PADDING_COLOR_OVERLAY, paddingColor));
|
||||
@@ -143,7 +144,7 @@ class ShortcutHelper {
|
||||
Bitmap iconBitmap = Utils.retrieveCardImage(context, loyaltyCard.id, ImageLocationType.icon);
|
||||
if (iconBitmap == null) {
|
||||
iconBitmap = Utils.generateIcon(context, loyaltyCard, true).getLetterTile();
|
||||
}else{
|
||||
} else {
|
||||
iconBitmap = createAdaptiveBitmap(iconBitmap, loyaltyCard.headerColor == null ? PADDING_COLOR : loyaltyCard.headerColor);
|
||||
}
|
||||
|
||||
|
||||
79
app/src/main/java/protect/card_locker/UCropWrapper.java
Normal file
79
app/src/main/java/protect/card_locker/UCropWrapper.java
Normal file
@@ -0,0 +1,79 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
|
||||
import com.google.android.material.color.MaterialColors;
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
import com.yalantis.ucrop.UCropActivity;
|
||||
|
||||
public class UCropWrapper extends UCropActivity {
|
||||
public static final String UCROP_TOOLBAR_TYPEFACE_STYLE = "ucop_toolbar_typeface_style";
|
||||
|
||||
@Override
|
||||
protected void onPostCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onPostCreate(savedInstanceState);
|
||||
boolean darkMode = Utils.isDarkModeEnabled(this);
|
||||
// setup status bar to look like the rest of the app
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
getWindow().getDecorView().setSystemUiVisibility(darkMode ? 0 : View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
|
||||
} else {
|
||||
// icons are always white back then
|
||||
if (!darkMode) {
|
||||
getWindow().setStatusBarColor(ColorUtils.compositeColors(Color.argb(127, 0, 0, 0), getWindow().getStatusBarColor()));
|
||||
}
|
||||
}
|
||||
|
||||
// find and check views that we wish to color modify
|
||||
// for when we update ucrop or switch to another cropper
|
||||
View check = findViewById(com.yalantis.ucrop.R.id.wrapper_controls);
|
||||
if (check instanceof FrameLayout) {
|
||||
FrameLayout controls = (FrameLayout) check;
|
||||
check = findViewById(com.yalantis.ucrop.R.id.wrapper_states);
|
||||
if (check instanceof LinearLayout) {
|
||||
LinearLayout states = (LinearLayout) check;
|
||||
for (int i = 0; i < controls.getChildCount(); i++) {
|
||||
check = controls.getChildAt(i);
|
||||
if (check instanceof AppCompatImageView) {
|
||||
AppCompatImageView controlsBackgroundImage = (AppCompatImageView) check;
|
||||
// everything gathered and are as expected, now perform color patching
|
||||
Utils.patchColors(this);
|
||||
int colorSurface = MaterialColors.getColor(this, R.attr.colorSurface, ContextCompat.getColor(this, R.color.md_theme_light_surface));
|
||||
int colorOnSurface = MaterialColors.getColor(this, R.attr.colorOnSurface, ContextCompat.getColor(this, R.color.md_theme_light_onSurface));
|
||||
|
||||
Drawable controlsBackgroundImageDrawable = controlsBackgroundImage.getBackground();
|
||||
controlsBackgroundImageDrawable.mutate();
|
||||
controlsBackgroundImageDrawable.setTint(darkMode ? colorOnSurface : colorSurface);
|
||||
controlsBackgroundImage.setBackgroundDrawable(controlsBackgroundImageDrawable);
|
||||
|
||||
states.setBackgroundColor(darkMode ? colorSurface : colorOnSurface);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// change toolbar font
|
||||
check = findViewById(com.yalantis.ucrop.R.id.toolbar_title);
|
||||
if (check instanceof MaterialTextView) {
|
||||
MaterialTextView toolbarTextview = (MaterialTextView) check;
|
||||
Intent intent = getIntent();
|
||||
int style = intent.getIntExtra(UCROP_TOOLBAR_TYPEFACE_STYLE, -1);
|
||||
if (style != -1) {
|
||||
toolbarTextview.setTypeface(Typeface.defaultFromStyle(style));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
@@ -14,8 +15,12 @@ import android.os.Build;
|
||||
import android.os.LocaleList;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.material.color.DynamicColors;
|
||||
import com.google.zxing.BinaryBitmap;
|
||||
import com.google.zxing.LuminanceSource;
|
||||
import com.google.zxing.MultiFormatReader;
|
||||
@@ -39,9 +44,12 @@ import java.util.GregorianCalendar;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.exifinterface.media.ExifInterface;
|
||||
|
||||
import androidx.palette.graphics.Palette;
|
||||
import protect.card_locker.preferences.Settings;
|
||||
|
||||
public class Utils {
|
||||
@@ -84,7 +92,7 @@ public class Utils {
|
||||
tileLetterFontSize = context.getResources().getDimensionPixelSize(R.dimen.tileLetterFontSize);
|
||||
}
|
||||
|
||||
int pixelSize = context.getResources().getDimensionPixelSize(R.dimen.cardThumbnailSize);
|
||||
int pixelSize = context.getResources().getDimensionPixelSize(R.dimen.tileLetterImageSize);
|
||||
|
||||
if (backgroundColor == null) {
|
||||
backgroundColor = LetterBitmap.getDefaultColor(context, store);
|
||||
@@ -206,7 +214,7 @@ public class Utils {
|
||||
|
||||
if (currency == null) {
|
||||
numberFormat.setMaximumFractionDigits(0);
|
||||
return context.getString(R.string.balancePoints, numberFormat.format(value));
|
||||
return context.getResources().getQuantityString(R.plurals.balancePoints, value.intValue(), numberFormat.format(value));
|
||||
}
|
||||
|
||||
NumberFormat currencyFormat = NumberFormat.getCurrencyInstance();
|
||||
@@ -448,4 +456,82 @@ public class Utils {
|
||||
return loadImage(context.getCacheDir() + "/" + name);
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/59324801/8378787
|
||||
public static int getComplementaryColor(int color) {
|
||||
int R = color & 255;
|
||||
int G = (color >> 8) & 255;
|
||||
int B = (color >> 16) & 255;
|
||||
int A = (color >> 24) & 255;
|
||||
R = 255 - R;
|
||||
G = 255 - G;
|
||||
B = 255 - B;
|
||||
return R + (G << 8) + (B << 16) + (A << 24);
|
||||
}
|
||||
|
||||
// replace colors in the current theme
|
||||
public static void patchColors(AppCompatActivity activity) {
|
||||
Settings settings = new Settings(activity);
|
||||
String color = settings.getColor();
|
||||
|
||||
Resources.Theme theme = activity.getTheme();
|
||||
Resources resources = activity.getResources();
|
||||
if (color.equals(resources.getString(R.string.settings_key_pink_theme))) {
|
||||
theme.applyStyle(R.style.pink, true);
|
||||
} else if (color.equals(resources.getString(R.string.settings_key_magenta_theme))) {
|
||||
theme.applyStyle(R.style.magenta, true);
|
||||
} else if (color.equals(resources.getString(R.string.settings_key_violet_theme))) {
|
||||
theme.applyStyle(R.style.violet, true);
|
||||
} else if (color.equals(resources.getString(R.string.settings_key_blue_theme))) {
|
||||
theme.applyStyle(R.style.blue, true);
|
||||
} else if (color.equals(resources.getString(R.string.settings_key_sky_blue_theme))) {
|
||||
theme.applyStyle(R.style.skyblue, true);
|
||||
} else if (color.equals(resources.getString(R.string.settings_key_green_theme))) {
|
||||
theme.applyStyle(R.style.green, true);
|
||||
} else if (color.equals(resources.getString(R.string.settings_key_brown_theme))) {
|
||||
theme.applyStyle(R.style.brown, true);
|
||||
} else if (color.equals(resources.getString(R.string.settings_key_catima_theme))) {
|
||||
// catima theme is AppTheme itself, no dynamic colors nor applyStyle
|
||||
} else {
|
||||
// final catch all in case of invalid theme value from older versions
|
||||
// also handles R.string.settings_key_system_theme
|
||||
DynamicColors.applyIfAvailable(activity);
|
||||
}
|
||||
|
||||
if (isDarkModeEnabled(activity) && settings.getOledDark()) {
|
||||
theme.applyStyle(R.style.DarkBackground, true);
|
||||
}
|
||||
}
|
||||
|
||||
// XXX android 9 and below has issues with patched theme where the background becomes a
|
||||
// rendering mess
|
||||
// use after views are inflated
|
||||
public static void postPatchColors(AppCompatActivity activity) {
|
||||
TypedValue typedValue = new TypedValue();
|
||||
activity.getTheme().resolveAttribute(android.R.attr.colorBackground, typedValue, true);
|
||||
activity.findViewById(android.R.id.content).setBackgroundColor(typedValue.data);
|
||||
}
|
||||
|
||||
public static void updateMenuCardDetailsButtonState(MenuItem item, boolean currentlyExpanded) {
|
||||
if (currentlyExpanded) {
|
||||
item.setIcon(R.drawable.ic_baseline_unfold_less_24);
|
||||
item.setTitle(R.string.action_hide_details);
|
||||
} else {
|
||||
item.setIcon(R.drawable.ic_baseline_unfold_more_24);
|
||||
item.setTitle(R.string.action_show_details);
|
||||
}
|
||||
}
|
||||
|
||||
public static int getHeaderColorFromImage(Bitmap image, int fallback) {
|
||||
if (image == null) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
return new Palette.Builder(image).generate().getDominantColor(R.attr.colorPrimary);
|
||||
}
|
||||
|
||||
public static int getRandomHeaderColor(Context context) {
|
||||
TypedArray colors = context.getResources().obtainTypedArray(R.array.letter_tile_colors);
|
||||
final int color = (int) (Math.random() * colors.length());
|
||||
return colors.getColor(color, Color.BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package protect.card_locker.importexport;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import net.lingala.zip4j.io.outputstream.ZipOutputStream;
|
||||
@@ -31,7 +32,7 @@ import protect.card_locker.Utils;
|
||||
* format.
|
||||
*/
|
||||
public class CatimaExporter implements Exporter {
|
||||
public void exportData(Context context, DBHelper db, OutputStream output, char[] password) throws IOException, InterruptedException {
|
||||
public void exportData(Context context, SQLiteDatabase database, OutputStream output, char[] password) throws IOException, InterruptedException {
|
||||
// Necessary vars
|
||||
int readLen;
|
||||
byte[] readBuffer = new byte[InternalZipConstants.BUFF_SIZE];
|
||||
@@ -48,7 +49,7 @@ public class CatimaExporter implements Exporter {
|
||||
// Generate CSV
|
||||
ByteArrayOutputStream catimaOutputStream = new ByteArrayOutputStream();
|
||||
OutputStreamWriter catimaOutputStreamWriter = new OutputStreamWriter(catimaOutputStream, StandardCharsets.UTF_8);
|
||||
writeCSV(db, catimaOutputStreamWriter);
|
||||
writeCSV(database, catimaOutputStreamWriter);
|
||||
|
||||
// Add CSV to zip file
|
||||
ZipParameters csvZipParameters = createZipParameters("catima.csv", password);
|
||||
@@ -60,7 +61,7 @@ public class CatimaExporter implements Exporter {
|
||||
zipOutputStream.closeEntry();
|
||||
|
||||
// Loop over all cards again
|
||||
Cursor cardCursor = db.getLoyaltyCardCursor();
|
||||
Cursor cardCursor = DBHelper.getLoyaltyCardCursor(database);
|
||||
while (cardCursor.moveToNext()) {
|
||||
// For each card
|
||||
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cardCursor);
|
||||
@@ -70,7 +71,7 @@ public class CatimaExporter implements Exporter {
|
||||
// If it exists, add to the .zip file
|
||||
Bitmap image = Utils.retrieveCardImage(context, card.id, imageLocationType);
|
||||
if (image != null) {
|
||||
ZipParameters imageZipParameters = createZipParameters(Utils.getCardImageFileName(card.id, imageLocationType) ,password);
|
||||
ZipParameters imageZipParameters = createZipParameters(Utils.getCardImageFileName(card.id, imageLocationType), password);
|
||||
zipOutputStream.putNextEntry(imageZipParameters);
|
||||
InputStream imageInputStream = new ByteArrayInputStream(Utils.bitmapToByteArray(image));
|
||||
while ((readLen = imageInputStream.read(readBuffer)) != -1) {
|
||||
@@ -94,7 +95,7 @@ public class CatimaExporter implements Exporter {
|
||||
return zipParameters;
|
||||
}
|
||||
|
||||
private void writeCSV(DBHelper db, OutputStreamWriter output) throws IOException, InterruptedException {
|
||||
private void writeCSV(SQLiteDatabase database, OutputStreamWriter output) throws IOException, InterruptedException {
|
||||
CSVPrinter printer = new CSVPrinter(output, CSVFormat.RFC4180);
|
||||
|
||||
// Print the version
|
||||
@@ -105,7 +106,7 @@ public class CatimaExporter implements Exporter {
|
||||
// Print the header for groups
|
||||
printer.printRecord(DBHelper.LoyaltyCardDbGroups.ID);
|
||||
|
||||
Cursor groupCursor = db.getGroupCursor();
|
||||
Cursor groupCursor = DBHelper.getGroupCursor(database);
|
||||
|
||||
while (groupCursor.moveToNext()) {
|
||||
Group group = Group.toGroup(groupCursor);
|
||||
@@ -134,9 +135,10 @@ public class CatimaExporter implements Exporter {
|
||||
DBHelper.LoyaltyCardDbIds.BARCODE_TYPE,
|
||||
DBHelper.LoyaltyCardDbIds.HEADER_COLOR,
|
||||
DBHelper.LoyaltyCardDbIds.STAR_STATUS,
|
||||
DBHelper.LoyaltyCardDbIds.LAST_USED);
|
||||
DBHelper.LoyaltyCardDbIds.LAST_USED,
|
||||
DBHelper.LoyaltyCardDbIds.ARCHIVE_STATUS);
|
||||
|
||||
Cursor cardCursor = db.getLoyaltyCardCursor();
|
||||
Cursor cardCursor = DBHelper.getLoyaltyCardCursor(database);
|
||||
|
||||
while (cardCursor.moveToNext()) {
|
||||
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cardCursor);
|
||||
@@ -152,7 +154,8 @@ public class CatimaExporter implements Exporter {
|
||||
card.barcodeType != null ? card.barcodeType.name() : "",
|
||||
card.headerColor,
|
||||
card.starStatus,
|
||||
card.lastUsed);
|
||||
card.lastUsed,
|
||||
card.archiveStatus);
|
||||
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new InterruptedException();
|
||||
@@ -168,12 +171,12 @@ public class CatimaExporter implements Exporter {
|
||||
printer.printRecord(DBHelper.LoyaltyCardDbIdsGroups.cardID,
|
||||
DBHelper.LoyaltyCardDbIdsGroups.groupID);
|
||||
|
||||
Cursor cardCursor2 = db.getLoyaltyCardCursor();
|
||||
Cursor cardCursor2 = DBHelper.getLoyaltyCardCursor(database);
|
||||
|
||||
while (cardCursor2.moveToNext()) {
|
||||
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cardCursor2);
|
||||
|
||||
for (Group group : db.getLoyaltyCardGroups(card.id)) {
|
||||
for (Group group : DBHelper.getLoyaltyCardGroups(database, card.id)) {
|
||||
printer.printRecord(card.id, group._id);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ import protect.card_locker.ZipUtils;
|
||||
* A header is expected for the each table showing the names of the columns.
|
||||
*/
|
||||
public class CatimaImporter implements Importer {
|
||||
public void importData(Context context, DBHelper db, InputStream input, char[] password) throws IOException, FormatException, InterruptedException {
|
||||
public void importData(Context context, SQLiteDatabase database, InputStream input, char[] password) throws IOException, FormatException, InterruptedException {
|
||||
InputStream bufferedInputStream = new BufferedInputStream(input);
|
||||
bufferedInputStream.mark(100);
|
||||
|
||||
@@ -55,7 +55,7 @@ public class CatimaImporter implements Importer {
|
||||
|
||||
String fileName = Uri.parse(localFileHeader.getFileName()).getLastPathSegment();
|
||||
if (fileName.equals("catima.csv")) {
|
||||
importCSV(context, db, new ByteArrayInputStream(ZipUtils.read(zipInputStream).getBytes(StandardCharsets.UTF_8)));
|
||||
importCSV(context, database, new ByteArrayInputStream(ZipUtils.read(zipInputStream).getBytes(StandardCharsets.UTF_8)));
|
||||
} else if (fileName.endsWith(".png")) {
|
||||
Utils.saveCardImage(context, ZipUtils.readImage(zipInputStream), fileName);
|
||||
} else {
|
||||
@@ -66,11 +66,11 @@ public class CatimaImporter implements Importer {
|
||||
if (!isZipFile) {
|
||||
// This is not a zip file, try importing as bare CSV
|
||||
bufferedInputStream.reset();
|
||||
importCSV(context, db, bufferedInputStream);
|
||||
importCSV(context, database, bufferedInputStream);
|
||||
}
|
||||
}
|
||||
|
||||
public void importCSV(Context context, DBHelper db, InputStream input) throws IOException, FormatException, InterruptedException {
|
||||
public void importCSV(Context context, SQLiteDatabase database, InputStream input) throws IOException, FormatException, InterruptedException {
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
|
||||
|
||||
bufferedReader.mark(100);
|
||||
@@ -87,10 +87,10 @@ public class CatimaImporter implements Importer {
|
||||
|
||||
switch (version) {
|
||||
case 1:
|
||||
parseV1(context, db, bufferedReader);
|
||||
parseV1(context, database, bufferedReader);
|
||||
break;
|
||||
case 2:
|
||||
parseV2(context, db, bufferedReader);
|
||||
parseV2(context, database, bufferedReader);
|
||||
break;
|
||||
default:
|
||||
throw new FormatException(String.format("No code to parse version %s", version));
|
||||
@@ -99,15 +99,12 @@ public class CatimaImporter implements Importer {
|
||||
bufferedReader.close();
|
||||
}
|
||||
|
||||
public void parseV1(Context context, DBHelper db, BufferedReader input) throws IOException, FormatException, InterruptedException {
|
||||
public void parseV1(Context context, SQLiteDatabase database, BufferedReader input) throws IOException, FormatException, InterruptedException {
|
||||
final CSVParser parser = new CSVParser(input, CSVFormat.RFC4180.builder().setHeader().build());
|
||||
|
||||
SQLiteDatabase database = db.getWritableDatabase();
|
||||
database.beginTransaction();
|
||||
|
||||
try {
|
||||
for (CSVRecord record : parser) {
|
||||
importLoyaltyCard(context, database, db, record);
|
||||
importLoyaltyCard(context, database, record);
|
||||
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new InterruptedException();
|
||||
@@ -115,19 +112,12 @@ public class CatimaImporter implements Importer {
|
||||
}
|
||||
|
||||
parser.close();
|
||||
database.setTransactionSuccessful();
|
||||
} catch (IllegalArgumentException | IllegalStateException e) {
|
||||
throw new FormatException("Issue parsing CSV data", e);
|
||||
} finally {
|
||||
database.endTransaction();
|
||||
database.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void parseV2(Context context, DBHelper db, BufferedReader input) throws IOException, FormatException, InterruptedException {
|
||||
SQLiteDatabase database = db.getWritableDatabase();
|
||||
database.beginTransaction();
|
||||
|
||||
public void parseV2(Context context, SQLiteDatabase database, BufferedReader input) throws IOException, FormatException, InterruptedException {
|
||||
Integer part = 0;
|
||||
String stringPart = "";
|
||||
|
||||
@@ -145,7 +135,7 @@ public class CatimaImporter implements Importer {
|
||||
break;
|
||||
case 1:
|
||||
try {
|
||||
parseV2Groups(db, database, stringPart);
|
||||
parseV2Groups(database, stringPart);
|
||||
sectionParsed = true;
|
||||
} catch (FormatException e) {
|
||||
// We may have a multiline field, try again
|
||||
@@ -153,7 +143,7 @@ public class CatimaImporter implements Importer {
|
||||
break;
|
||||
case 2:
|
||||
try {
|
||||
parseV2Cards(context, db, database, stringPart);
|
||||
parseV2Cards(context, database, stringPart);
|
||||
sectionParsed = true;
|
||||
} catch (FormatException e) {
|
||||
// We may have a multiline field, try again
|
||||
@@ -161,7 +151,7 @@ public class CatimaImporter implements Importer {
|
||||
break;
|
||||
case 3:
|
||||
try {
|
||||
parseV2CardGroups(db, database, stringPart);
|
||||
parseV2CardGroups(database, stringPart);
|
||||
sectionParsed = true;
|
||||
} catch (FormatException e) {
|
||||
// We may have a multiline field, try again
|
||||
@@ -185,16 +175,12 @@ public class CatimaImporter implements Importer {
|
||||
stringPart += tmp + "\n";
|
||||
}
|
||||
}
|
||||
database.setTransactionSuccessful();
|
||||
} catch (FormatException e) {
|
||||
throw new FormatException("Issue parsing CSV data", e);
|
||||
} finally {
|
||||
database.endTransaction();
|
||||
database.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void parseV2Groups(DBHelper db, SQLiteDatabase database, String data) throws IOException, FormatException, InterruptedException {
|
||||
public void parseV2Groups(SQLiteDatabase database, String data) throws IOException, FormatException, InterruptedException {
|
||||
// Parse groups
|
||||
final CSVParser groupParser = new CSVParser(new StringReader(data), CSVFormat.RFC4180.builder().setHeader().build());
|
||||
|
||||
@@ -215,11 +201,11 @@ public class CatimaImporter implements Importer {
|
||||
}
|
||||
|
||||
for (CSVRecord record : records) {
|
||||
importGroup(database, db, record);
|
||||
importGroup(database, record);
|
||||
}
|
||||
}
|
||||
|
||||
public void parseV2Cards(Context context, DBHelper db, SQLiteDatabase database, String data) throws IOException, FormatException, InterruptedException {
|
||||
public void parseV2Cards(Context context, SQLiteDatabase database, String data) throws IOException, FormatException, InterruptedException {
|
||||
// Parse cards
|
||||
final CSVParser cardParser = new CSVParser(new StringReader(data), CSVFormat.RFC4180.builder().setHeader().build());
|
||||
|
||||
@@ -240,11 +226,11 @@ public class CatimaImporter implements Importer {
|
||||
}
|
||||
|
||||
for (CSVRecord record : records) {
|
||||
importLoyaltyCard(context, database, db, record);
|
||||
importLoyaltyCard(context, database, record);
|
||||
}
|
||||
}
|
||||
|
||||
public void parseV2CardGroups(DBHelper db, SQLiteDatabase database, String data) throws IOException, FormatException, InterruptedException {
|
||||
public void parseV2CardGroups(SQLiteDatabase database, String data) throws IOException, FormatException, InterruptedException {
|
||||
// Parse card group mappings
|
||||
final CSVParser cardGroupParser = new CSVParser(new StringReader(data), CSVFormat.RFC4180.builder().setHeader().build());
|
||||
|
||||
@@ -265,7 +251,7 @@ public class CatimaImporter implements Importer {
|
||||
}
|
||||
|
||||
for (CSVRecord record : records) {
|
||||
importCardGroupMapping(database, db, record);
|
||||
importCardGroupMapping(database, record);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,7 +259,7 @@ public class CatimaImporter implements Importer {
|
||||
* Import a single loyalty card into the database using the given
|
||||
* session.
|
||||
*/
|
||||
private void importLoyaltyCard(Context context, SQLiteDatabase database, DBHelper helper, CSVRecord record)
|
||||
private void importLoyaltyCard(Context context, SQLiteDatabase database, CSVRecord record)
|
||||
throws IOException, FormatException {
|
||||
int id = CSVHelpers.extractInt(DBHelper.LoyaltyCardDbIds.ID, record, false);
|
||||
|
||||
@@ -335,6 +321,15 @@ public class CatimaImporter implements Importer {
|
||||
}
|
||||
if (starStatus != 1) starStatus = 0;
|
||||
|
||||
int archiveStatus = 0;
|
||||
try {
|
||||
archiveStatus = CSVHelpers.extractInt(DBHelper.LoyaltyCardDbIds.ARCHIVE_STATUS, record, false);
|
||||
} catch (FormatException _e) {
|
||||
// This field did not exist in versions 2.16.3 and before
|
||||
// We catch this exception so we can still import old backups
|
||||
}
|
||||
if (archiveStatus != 1) archiveStatus = 0;
|
||||
|
||||
Long lastUsed = 0L;
|
||||
try {
|
||||
lastUsed = CSVHelpers.extractLong(DBHelper.LoyaltyCardDbIds.LAST_USED, record, false);
|
||||
@@ -343,31 +338,29 @@ public class CatimaImporter implements Importer {
|
||||
// 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);
|
||||
DBHelper.insertLoyaltyCard(database, id, store, note, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, starStatus, lastUsed,archiveStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a single group into the database using the given
|
||||
* session.
|
||||
*/
|
||||
private void importGroup(SQLiteDatabase database, DBHelper helper, CSVRecord record)
|
||||
throws IOException, FormatException {
|
||||
private void importGroup(SQLiteDatabase database, CSVRecord record) throws FormatException {
|
||||
String id = CSVHelpers.extractString(DBHelper.LoyaltyCardDbGroups.ID, record, null);
|
||||
|
||||
helper.insertGroup(database, id);
|
||||
DBHelper.insertGroup(database, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a single card to group mapping into the database using the given
|
||||
* session.
|
||||
*/
|
||||
private void importCardGroupMapping(SQLiteDatabase database, DBHelper helper, CSVRecord record)
|
||||
throws IOException, FormatException {
|
||||
private void importCardGroupMapping(SQLiteDatabase database, CSVRecord record) throws FormatException {
|
||||
Integer cardId = CSVHelpers.extractInt(DBHelper.LoyaltyCardDbIdsGroups.cardID, record, false);
|
||||
String groupId = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIdsGroups.groupID, record, null);
|
||||
|
||||
List<Group> cardGroups = helper.getLoyaltyCardGroups(cardId);
|
||||
cardGroups.add(helper.getGroup(groupId));
|
||||
helper.setLoyaltyCardGroups(database, cardId, cardGroups);
|
||||
List<Group> cardGroups = DBHelper.getLoyaltyCardGroups(database, cardId);
|
||||
cardGroups.add(DBHelper.getGroup(database, groupId));
|
||||
DBHelper.setLoyaltyCardGroups(database, cardId, cardGroups);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import protect.card_locker.DBHelper;
|
||||
|
||||
/**
|
||||
* Interface for a class which can export the contents of the database
|
||||
* in a given format.
|
||||
@@ -17,5 +16,5 @@ public interface Exporter {
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
void exportData(Context context, DBHelper db, OutputStream output, char[] password) throws IOException, InterruptedException;
|
||||
void exportData(Context context, SQLiteDatabase database, OutputStream output, char[] password) throws IOException, InterruptedException;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.text.ParseException;
|
||||
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)
|
||||
@@ -30,7 +31,7 @@ import protect.card_locker.FormatException;
|
||||
* A header is expected for the each table showing the names of the columns.
|
||||
*/
|
||||
public class FidmeImporter implements Importer {
|
||||
public void importData(Context context, DBHelper db, InputStream input, char[] password) throws IOException, FormatException, JSONException, ParseException {
|
||||
public void importData(Context context, SQLiteDatabase database, InputStream input, char[] password) throws IOException, FormatException, JSONException, ParseException {
|
||||
// We actually retrieve a .zip file
|
||||
ZipInputStream zipInputStream = new ZipInputStream(input, password);
|
||||
|
||||
@@ -52,14 +53,11 @@ public class FidmeImporter implements Importer {
|
||||
throw new FormatException("Couldn't find loyalty_programs.csv in zip file or it is empty");
|
||||
}
|
||||
|
||||
SQLiteDatabase database = db.getWritableDatabase();
|
||||
database.beginTransaction();
|
||||
|
||||
final CSVParser fidmeParser = new CSVParser(new StringReader(loyaltyCards.toString()), CSVFormat.RFC4180.builder().setDelimiter(';').setHeader().build());
|
||||
|
||||
try {
|
||||
for (CSVRecord record : fidmeParser) {
|
||||
importLoyaltyCard(database, db, record);
|
||||
importLoyaltyCard(context, database, record);
|
||||
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new InterruptedException();
|
||||
@@ -71,10 +69,6 @@ public class FidmeImporter implements Importer {
|
||||
fidmeParser.close();
|
||||
}
|
||||
|
||||
database.setTransactionSuccessful();
|
||||
database.endTransaction();
|
||||
database.close();
|
||||
|
||||
zipInputStream.close();
|
||||
}
|
||||
|
||||
@@ -82,8 +76,8 @@ public class FidmeImporter implements Importer {
|
||||
* Import a single loyalty card into the database using the given
|
||||
* session.
|
||||
*/
|
||||
private void importLoyaltyCard(SQLiteDatabase database, DBHelper helper, CSVRecord record)
|
||||
throws IOException, FormatException {
|
||||
private void importLoyaltyCard(Context context, SQLiteDatabase database, CSVRecord record)
|
||||
throws FormatException {
|
||||
// A loyalty card export from Fidme contains the following fields:
|
||||
// Retailer (store name)
|
||||
// Program (program name)
|
||||
@@ -124,11 +118,13 @@ public class FidmeImporter implements Importer {
|
||||
// TODO: Hook this into our own loyalty card DB if we ever get one
|
||||
CatimaBarcode barcodeType = null;
|
||||
|
||||
// No favourite data in the export either
|
||||
// No favourite data or colour in the export either
|
||||
int starStatus = 0;
|
||||
int archiveStatus = 0;
|
||||
int headerColor = Utils.getRandomHeaderColor(context);
|
||||
|
||||
// TODO: Front and back image
|
||||
|
||||
helper.insertLoyaltyCard(database, store, note, null, BigDecimal.valueOf(0), null, cardId, null, barcodeType, null, starStatus, null);
|
||||
DBHelper.insertLoyaltyCard(database, store, note, null, BigDecimal.valueOf(0), null, cardId, null, barcodeType, headerColor, starStatus, null,archiveStatus);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,23 @@
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
public enum ImportExportResult {
|
||||
Success,
|
||||
GenericFailure,
|
||||
BadPassword;
|
||||
public class ImportExportResult {
|
||||
private ImportExportResultType resultType;
|
||||
private String developerDetails;
|
||||
|
||||
public ImportExportResult(ImportExportResultType resultType) {
|
||||
this(resultType, null);
|
||||
}
|
||||
|
||||
public ImportExportResult(ImportExportResultType resultType, String developerDetails) {
|
||||
this.resultType = resultType;
|
||||
this.developerDetails = developerDetails;
|
||||
}
|
||||
|
||||
public ImportExportResultType resultType() {
|
||||
return resultType;
|
||||
}
|
||||
|
||||
public String developerDetails() {
|
||||
return developerDetails;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
public enum ImportExportResultType {
|
||||
Success,
|
||||
GenericFailure,
|
||||
BadPassword;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
@@ -8,7 +9,6 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.text.ParseException;
|
||||
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.FormatException;
|
||||
|
||||
/**
|
||||
@@ -23,5 +23,5 @@ public interface Importer {
|
||||
* @throws IOException
|
||||
* @throws FormatException
|
||||
*/
|
||||
void importData(Context context, DBHelper db, InputStream input, char[] password) throws IOException, FormatException, InterruptedException, JSONException, ParseException;
|
||||
void importData(Context context, SQLiteDatabase database, InputStream input, char[] password) throws IOException, FormatException, InterruptedException, JSONException, ParseException;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import protect.card_locker.DBHelper;
|
||||
|
||||
public class MultiFormatExporter {
|
||||
private static final String TAG = "Catima";
|
||||
|
||||
@@ -21,7 +20,7 @@ public class MultiFormatExporter {
|
||||
* another ImportExportResult otherwise. If not Success, partial data may have been
|
||||
* written to the output stream, and it should be discarded.
|
||||
*/
|
||||
public static ImportExportResult exportData(Context context, DBHelper db, OutputStream output, DataFormat format, char[] password) {
|
||||
public static ImportExportResult exportData(Context context, SQLiteDatabase database, OutputStream output, DataFormat format, char[] password) {
|
||||
Exporter exporter = null;
|
||||
|
||||
switch (format) {
|
||||
@@ -33,20 +32,20 @@ public class MultiFormatExporter {
|
||||
break;
|
||||
}
|
||||
|
||||
String error;
|
||||
if (exporter != null) {
|
||||
try {
|
||||
exporter.exportData(context, db, output, password);
|
||||
return ImportExportResult.Success;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to export data", e);
|
||||
} catch (InterruptedException e) {
|
||||
exporter.exportData(context, database, output, password);
|
||||
return new ImportExportResult(ImportExportResultType.Success);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to export data", e);
|
||||
error = e.toString();
|
||||
}
|
||||
|
||||
return ImportExportResult.GenericFailure;
|
||||
} else {
|
||||
Log.e(TAG, "Unsupported data format exported: " + format.name());
|
||||
return ImportExportResult.GenericFailure;
|
||||
error = "Unsupported data format exported: " + format.name();
|
||||
Log.e(TAG, error);
|
||||
}
|
||||
|
||||
return new ImportExportResult(ImportExportResultType.GenericFailure, error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.util.Log;
|
||||
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
@@ -11,7 +12,6 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.text.ParseException;
|
||||
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.FormatException;
|
||||
|
||||
public class MultiFormatImporter {
|
||||
@@ -28,7 +28,7 @@ public class MultiFormatImporter {
|
||||
* or another result otherwise. If no Success, no data was written to
|
||||
* the database.
|
||||
*/
|
||||
public static ImportExportResult importData(Context context, DBHelper db, InputStream input, DataFormat format, char[] password) {
|
||||
public static ImportExportResult importData(Context context, SQLiteDatabase database, InputStream input, DataFormat format, char[] password) {
|
||||
Importer importer = null;
|
||||
|
||||
switch (format) {
|
||||
@@ -46,20 +46,31 @@ public class MultiFormatImporter {
|
||||
break;
|
||||
}
|
||||
|
||||
String error = null;
|
||||
if (importer != null) {
|
||||
database.beginTransaction();
|
||||
try {
|
||||
importer.importData(context, db, input, password);
|
||||
return ImportExportResult.Success;
|
||||
importer.importData(context, database, input, password);
|
||||
database.setTransactionSuccessful();
|
||||
return new ImportExportResult(ImportExportResultType.Success);
|
||||
} catch (ZipException e) {
|
||||
return ImportExportResult.BadPassword;
|
||||
} catch (IOException | FormatException | InterruptedException | JSONException | ParseException | NullPointerException e) {
|
||||
if (e.getType().equals(ZipException.Type.WRONG_PASSWORD)) {
|
||||
return new ImportExportResult(ImportExportResultType.BadPassword);
|
||||
} else {
|
||||
Log.e(TAG, "Failed to import data", e);
|
||||
error = e.toString();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to import data", e);
|
||||
error = e.toString();
|
||||
} finally {
|
||||
database.endTransaction();
|
||||
}
|
||||
|
||||
} else {
|
||||
Log.e(TAG, "Unsupported data format imported: " + format.name());
|
||||
error = "Unsupported data format imported: " + format.name();
|
||||
Log.e(TAG, error);
|
||||
}
|
||||
|
||||
return ImportExportResult.GenericFailure;
|
||||
return new ImportExportResult(ImportExportResultType.GenericFailure, error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package protect.card_locker.importexport;
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
@@ -39,15 +40,17 @@ import protect.card_locker.ZipUtils;
|
||||
* 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 {
|
||||
private static final String TAG = "Catima";
|
||||
|
||||
public void importData(Context context, SQLiteDatabase database, 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<>();
|
||||
HashMap<String, HashMap<String, Object>> 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<>();
|
||||
HashMap<String, Object> recordData = new HashMap<>();
|
||||
recordData.put("name", record.get("name"));
|
||||
recordData.put("barcodeFormat", record.get("barcodeFormat"));
|
||||
|
||||
@@ -62,6 +65,8 @@ public class StocardImporter implements Importer {
|
||||
ZipInputStream zipInputStream = new ZipInputStream(input, password);
|
||||
|
||||
String[] providersFileName = null;
|
||||
String[] customProvidersBaseName = null;
|
||||
String customProviderId = "";
|
||||
String[] cardBaseName = null;
|
||||
String cardName = "";
|
||||
LocalFileHeader localFileHeader;
|
||||
@@ -78,6 +83,14 @@ public class StocardImporter implements Importer {
|
||||
nameParts[0],
|
||||
"analytics-properties.json"
|
||||
};
|
||||
customProvidersBaseName = new String[]{
|
||||
nameParts[0],
|
||||
"sync",
|
||||
"data",
|
||||
"users",
|
||||
nameParts[0],
|
||||
"loyalty-card-custom-providers"
|
||||
};
|
||||
cardBaseName = new String[]{
|
||||
nameParts[0],
|
||||
"sync",
|
||||
@@ -88,6 +101,33 @@ public class StocardImporter implements Importer {
|
||||
};
|
||||
}
|
||||
|
||||
if (startsWith(nameParts, customProvidersBaseName, 1)) {
|
||||
// Extract providerId
|
||||
customProviderId = nameParts[customProvidersBaseName.length].split("\\.", 2)[0];
|
||||
|
||||
// Name file
|
||||
if (nameParts.length == customProvidersBaseName.length + 1) {
|
||||
// Ignore the .txt file
|
||||
if (fileName.endsWith(".json")) {
|
||||
JSONObject jsonObject = ZipUtils.readJSON(zipInputStream);
|
||||
|
||||
providers = appendToHashMap(
|
||||
providers,
|
||||
customProviderId,
|
||||
"name",
|
||||
jsonObject.getString("name")
|
||||
);
|
||||
}
|
||||
} else if (fileName.endsWith("logo.png")) {
|
||||
providers = appendToHashMap(
|
||||
providers,
|
||||
customProviderId,
|
||||
"logo",
|
||||
ZipUtils.readImage(zipInputStream)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (startsWith(nameParts, cardBaseName, 1)) {
|
||||
// Extract cardName
|
||||
cardName = nameParts[cardBaseName.length].split("\\.", 2)[0];
|
||||
@@ -98,24 +138,33 @@ public class StocardImporter implements Importer {
|
||||
if (fileName.endsWith(".json")) {
|
||||
JSONObject jsonObject = ZipUtils.readJSON(zipInputStream);
|
||||
|
||||
loyaltyCardHashMap = appendToLoyaltyCardHashMap(
|
||||
loyaltyCardHashMap = appendToHashMap(
|
||||
loyaltyCardHashMap,
|
||||
cardName,
|
||||
"cardId",
|
||||
jsonObject.getString("input_id")
|
||||
);
|
||||
loyaltyCardHashMap = appendToLoyaltyCardHashMap(
|
||||
|
||||
// Provider ID can be either custom or not, extract whatever version is relevant
|
||||
String customProviderPrefix = "/users/" + nameParts[0] + "/loyalty-card-custom-providers/";
|
||||
String providerId = jsonObject
|
||||
.getJSONObject("input_provider_reference")
|
||||
.getString("identifier");
|
||||
if (providerId.startsWith(customProviderPrefix)) {
|
||||
providerId = providerId.substring(customProviderPrefix.length());
|
||||
} else {
|
||||
providerId = providerId.substring("/loyalty-card-providers/".length());
|
||||
}
|
||||
|
||||
loyaltyCardHashMap = appendToHashMap(
|
||||
loyaltyCardHashMap,
|
||||
cardName,
|
||||
"_providerId",
|
||||
jsonObject
|
||||
.getJSONObject("input_provider_reference")
|
||||
.getString("identifier")
|
||||
.substring("/loyalty-card-providers/".length())
|
||||
providerId
|
||||
);
|
||||
|
||||
if (jsonObject.has("input_barcode_format")) {
|
||||
loyaltyCardHashMap = appendToLoyaltyCardHashMap(
|
||||
loyaltyCardHashMap = appendToHashMap(
|
||||
loyaltyCardHashMap,
|
||||
cardName,
|
||||
"barcodeType",
|
||||
@@ -124,7 +173,7 @@ public class StocardImporter implements Importer {
|
||||
}
|
||||
}
|
||||
} else if (fileName.endsWith("notes/default.json")) {
|
||||
loyaltyCardHashMap = appendToLoyaltyCardHashMap(
|
||||
loyaltyCardHashMap = appendToHashMap(
|
||||
loyaltyCardHashMap,
|
||||
cardName,
|
||||
"note",
|
||||
@@ -132,14 +181,14 @@ public class StocardImporter implements Importer {
|
||||
.getString("content")
|
||||
);
|
||||
} else if (fileName.endsWith("/images/front.png")) {
|
||||
loyaltyCardHashMap = appendToLoyaltyCardHashMap(
|
||||
loyaltyCardHashMap = appendToHashMap(
|
||||
loyaltyCardHashMap,
|
||||
cardName,
|
||||
"frontImage",
|
||||
ZipUtils.readImage(zipInputStream)
|
||||
);
|
||||
} else if (fileName.endsWith("/images/back.png")) {
|
||||
loyaltyCardHashMap = appendToLoyaltyCardHashMap(
|
||||
loyaltyCardHashMap = appendToHashMap(
|
||||
loyaltyCardHashMap,
|
||||
cardName,
|
||||
"backImage",
|
||||
@@ -153,27 +202,43 @@ public class StocardImporter implements Importer {
|
||||
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;
|
||||
if (providerId == null) {
|
||||
Log.d(TAG, "Missing providerId for card " + loyaltyCardData + ", ignoring...");
|
||||
continue;
|
||||
}
|
||||
|
||||
HashMap<String, Object> providerData = providers.get(providerId);
|
||||
|
||||
String store = providerData != null ? providerData.get("name").toString() : 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 != null && !barcodeTypeString.isEmpty()) {
|
||||
if (barcodeTypeString.equals("RSS_DATABAR_EXPANDED")) {
|
||||
barcodeType = CatimaBarcode.fromBarcode(BarcodeFormat.RSS_EXPANDED);
|
||||
} else if (barcodeTypeString.equals("GS1_128")) {
|
||||
barcodeType = CatimaBarcode.fromBarcode(BarcodeFormat.CODE_128);
|
||||
} else {
|
||||
barcodeType = CatimaBarcode.fromName(barcodeTypeString);
|
||||
}
|
||||
}
|
||||
|
||||
long loyaltyCardInternalId = db.insertLoyaltyCard(database, store, note, null, BigDecimal.valueOf(0), null, cardId, null, barcodeType, null, 0, null);
|
||||
int headerColor = Utils.getRandomHeaderColor(context);
|
||||
Bitmap cardIcon = null;
|
||||
if (providerData != null && providerData.containsKey("logo")) {
|
||||
cardIcon = (Bitmap) providerData.get("logo");
|
||||
headerColor = Utils.getHeaderColorFromImage(cardIcon, headerColor);
|
||||
}
|
||||
|
||||
long loyaltyCardInternalId = DBHelper.insertLoyaltyCard(database, store, note, null, BigDecimal.valueOf(0), null, cardId, null, barcodeType, headerColor, 0, null,0);
|
||||
|
||||
if (cardIcon != null) {
|
||||
Utils.saveCardImage(context, cardIcon, (int) loyaltyCardInternalId, ImageLocationType.icon);
|
||||
}
|
||||
|
||||
if (loyaltyCardData.containsKey("frontImage")) {
|
||||
Utils.saveCardImage(context, (Bitmap) loyaltyCardData.get("frontImage"), (int) loyaltyCardInternalId, ImageLocationType.front);
|
||||
@@ -183,10 +248,6 @@ public class StocardImporter implements Importer {
|
||||
}
|
||||
}
|
||||
|
||||
database.setTransactionSuccessful();
|
||||
database.endTransaction();
|
||||
database.close();
|
||||
|
||||
zipInputStream.close();
|
||||
}
|
||||
|
||||
@@ -204,7 +265,7 @@ public class StocardImporter implements Importer {
|
||||
return true;
|
||||
}
|
||||
|
||||
private HashMap<String, HashMap<String, Object>> appendToLoyaltyCardHashMap(HashMap<String, HashMap<String, Object>> loyaltyCardHashMap, String cardID, String key, Object value) {
|
||||
private HashMap<String, HashMap<String, Object>> appendToHashMap(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<>();
|
||||
|
||||
@@ -36,7 +36,7 @@ import protect.card_locker.Utils;
|
||||
* A header is expected for the each table showing the names of the columns.
|
||||
*/
|
||||
public class VoucherVaultImporter implements Importer {
|
||||
public void importData(Context context, DBHelper db, InputStream input, char[] password) throws IOException, FormatException, JSONException, ParseException {
|
||||
public void importData(Context context, SQLiteDatabase database, InputStream input, char[] password) throws IOException, FormatException, JSONException, ParseException {
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@@ -46,9 +46,6 @@ public class VoucherVaultImporter implements Importer {
|
||||
}
|
||||
JSONArray jsonArray = new JSONArray(sb.toString());
|
||||
|
||||
SQLiteDatabase database = db.getWritableDatabase();
|
||||
database.beginTransaction();
|
||||
|
||||
// See https://github.com/tim-smart/vouchervault/issues/4#issuecomment-788226503 for more info
|
||||
for (int i = 0; i < jsonArray.length(); i++) {
|
||||
JSONObject jsonCard = jsonArray.getJSONObject(i);
|
||||
@@ -129,13 +126,9 @@ public class VoucherVaultImporter implements Importer {
|
||||
throw new FormatException("Unknown colour type found: " + colorFromJSON);
|
||||
}
|
||||
|
||||
db.insertLoyaltyCard(store, "", expiry, balance, balanceType, cardId, null, barcodeType, headerColor, 0, Utils.getUnixTime());
|
||||
DBHelper.insertLoyaltyCard(database, store, "", expiry, balance, balanceType, cardId, null, barcodeType, headerColor, 0, Utils.getUnixTime(),0);
|
||||
}
|
||||
|
||||
database.setTransactionSuccessful();
|
||||
database.endTransaction();
|
||||
database.close();
|
||||
|
||||
bufferedReader.close();
|
||||
}
|
||||
}
|
||||
@@ -9,36 +9,37 @@ 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 {
|
||||
private Context context;
|
||||
private SharedPreferences settings;
|
||||
private final Context mContext;
|
||||
private SharedPreferences mSettings;
|
||||
|
||||
public Settings(Context context) {
|
||||
this.context = context;
|
||||
this.settings = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
mContext = context.getApplicationContext();
|
||||
mSettings = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
}
|
||||
|
||||
private String getResString(@StringRes int resId) {
|
||||
return context.getString(resId);
|
||||
return mContext.getString(resId);
|
||||
}
|
||||
|
||||
private int getResInt(@IntegerRes int resId) {
|
||||
return context.getResources().getInteger(resId);
|
||||
return mContext.getResources().getInteger(resId);
|
||||
}
|
||||
|
||||
private String getString(@StringRes int keyId, String defaultValue) {
|
||||
return settings.getString(getResString(keyId), defaultValue);
|
||||
return mSettings.getString(getResString(keyId), defaultValue);
|
||||
}
|
||||
|
||||
private int getInt(@StringRes int keyId, @IntegerRes int defaultId) {
|
||||
return settings.getInt(getResString(keyId), getResInt(defaultId));
|
||||
return mSettings.getInt(getResString(keyId), getResInt(defaultId));
|
||||
}
|
||||
|
||||
private boolean getBoolean(@StringRes int keyId, boolean defaultValue) {
|
||||
return settings.getBoolean(getResString(keyId), defaultValue);
|
||||
return mSettings.getBoolean(getResString(keyId), defaultValue);
|
||||
}
|
||||
|
||||
public Locale getLocale() {
|
||||
@@ -91,8 +92,8 @@ public class Settings {
|
||||
return getBoolean(R.string.settings_key_display_barcode_max_brightness, true);
|
||||
}
|
||||
|
||||
public boolean getLockBarcodeScreenOrientation() {
|
||||
return getBoolean(R.string.settings_key_lock_barcode_orientation, false);
|
||||
public String getCardViewOrientation() {
|
||||
return getString(R.string.settings_key_card_orientation, getResString(R.string.settings_key_follow_system_orientation));
|
||||
}
|
||||
|
||||
public boolean getKeepScreenOn() {
|
||||
@@ -102,4 +103,12 @@ public class Settings {
|
||||
public boolean getDisableLockscreenWhileViewingCard() {
|
||||
return getBoolean(R.string.settings_key_disable_lockscreen_while_viewing_card, true);
|
||||
}
|
||||
|
||||
public boolean getOledDark() {
|
||||
return getBoolean(R.string.settings_key_oled_dark, false);
|
||||
}
|
||||
|
||||
public String getColor() {
|
||||
return getString(R.string.setting_key_theme_color, mContext.getResources().getString(R.string.settings_key_system_theme));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package protect.card_locker.preferences;
|
||||
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
@@ -9,22 +10,29 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
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 com.google.android.material.color.DynamicColors;
|
||||
|
||||
import nl.invissvenska.numberpickerpreference.NumberDialogPreference;
|
||||
import nl.invissvenska.numberpickerpreference.NumberPickerPreferenceDialogFragment;
|
||||
import protect.card_locker.CatimaAppCompatActivity;
|
||||
import protect.card_locker.MainActivity;
|
||||
import protect.card_locker.R;
|
||||
import protect.card_locker.Utils;
|
||||
|
||||
public class SettingsActivity extends CatimaAppCompatActivity {
|
||||
|
||||
private final static String RELOAD_MAIN_STATE = "mReloadMain";
|
||||
private SettingsFragment fragment;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -38,10 +46,21 @@ public class SettingsActivity extends CatimaAppCompatActivity {
|
||||
}
|
||||
|
||||
// Display the fragment as the main content.
|
||||
SettingsFragment fragment = new SettingsFragment();
|
||||
fragment = new SettingsFragment();
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.settings_container, fragment)
|
||||
.commit();
|
||||
|
||||
// restore reload main state
|
||||
if (savedInstanceState != null) {
|
||||
fragment.mReloadMain = savedInstanceState.getBoolean(RELOAD_MAIN_STATE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putBoolean(RELOAD_MAIN_STATE, fragment.mReloadMain);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -49,16 +68,34 @@ public class SettingsActivity extends CatimaAppCompatActivity {
|
||||
int id = item.getItemId();
|
||||
|
||||
if (id == android.R.id.home) {
|
||||
finish();
|
||||
finishSettingsActivity();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
finishSettingsActivity();
|
||||
}
|
||||
|
||||
private void finishSettingsActivity() {
|
||||
if (fragment.mReloadMain) {
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(MainActivity.RESTART_ACTIVITY_INTENT, true);
|
||||
setResult(Activity.RESULT_OK, intent);
|
||||
} else {
|
||||
setResult(Activity.RESULT_OK);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
|
||||
public static class SettingsFragment extends PreferenceFragmentCompat {
|
||||
private static final String DIALOG_FRAGMENT_TAG = "SettingsFragment";
|
||||
|
||||
public boolean mReloadMain;
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
// Load the preferences from an XML resource
|
||||
@@ -91,30 +128,39 @@ public class SettingsActivity extends CatimaAppCompatActivity {
|
||||
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));
|
||||
localePreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
refreshActivity(true);
|
||||
return true;
|
||||
});
|
||||
|
||||
Preference oledDarkPreference = findPreference(getResources().getString(R.string.settings_key_oled_dark));
|
||||
assert oledDarkPreference != null;
|
||||
oledDarkPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
refreshActivity(true);
|
||||
return true;
|
||||
});
|
||||
|
||||
ListPreference 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);
|
||||
}
|
||||
refreshActivity(true);
|
||||
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);
|
||||
if (!DynamicColors.isDynamicColorAvailable()) {
|
||||
colorPreference.setEntryValues(R.array.color_values_no_dynamic);
|
||||
colorPreference.setEntries(R.array.color_value_strings_no_dynamic);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
private void refreshActivity(boolean reloadMain) {
|
||||
mReloadMain = reloadMain || mReloadMain;
|
||||
Activity activity = getActivity();
|
||||
if (activity != null) {
|
||||
activity.recreate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<objectAnimator
|
||||
android:duration="0"
|
||||
android:propertyName="alpha"
|
||||
android:valueFrom="1.0"
|
||||
android:valueTo="0.0" />
|
||||
|
||||
<objectAnimator
|
||||
android:duration="@integer/full_rotation_duration"
|
||||
android:interpolator="@android:interpolator/accelerate_decelerate"
|
||||
android:propertyName="rotationY"
|
||||
android:valueFrom="-180"
|
||||
android:valueTo="0" />
|
||||
|
||||
<objectAnimator
|
||||
android:duration="1"
|
||||
android:propertyName="alpha"
|
||||
android:startOffset="@integer/half_rotation_duration"
|
||||
android:valueFrom="0.0"
|
||||
android:valueTo="1.0" />
|
||||
</set>
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<objectAnimator
|
||||
android:duration="@integer/full_rotation_duration"
|
||||
android:interpolator="@android:interpolator/accelerate_decelerate"
|
||||
android:propertyName="rotationY"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="180" />
|
||||
|
||||
<objectAnimator
|
||||
android:duration="1"
|
||||
android:propertyName="alpha"
|
||||
android:startOffset="@integer/half_rotation_duration"
|
||||
android:valueFrom="1.0"
|
||||
android:valueTo="0.0" />
|
||||
</set>
|
||||
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<objectAnimator
|
||||
android:duration="0"
|
||||
android:propertyName="alpha"
|
||||
android:valueFrom="1.0"
|
||||
android:valueTo="0.0" />
|
||||
|
||||
<objectAnimator
|
||||
android:duration="@integer/full_rotation_duration"
|
||||
android:interpolator="@android:interpolator/accelerate_decelerate"
|
||||
android:propertyName="rotationY"
|
||||
android:valueFrom="180"
|
||||
android:valueTo="0" />
|
||||
|
||||
<objectAnimator
|
||||
android:duration="1"
|
||||
android:propertyName="alpha"
|
||||
android:startOffset="@integer/half_rotation_duration"
|
||||
android:valueFrom="0.0"
|
||||
android:valueTo="1.0" />
|
||||
</set>
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<objectAnimator
|
||||
android:duration="@integer/full_rotation_duration"
|
||||
android:interpolator="@android:interpolator/accelerate_decelerate"
|
||||
android:propertyName="rotationY"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="-180" />
|
||||
|
||||
<objectAnimator
|
||||
android:duration="1"
|
||||
android:propertyName="alpha"
|
||||
android:startOffset="@integer/half_rotation_duration"
|
||||
android:valueFrom="1.0"
|
||||
android:valueTo="0.0" />
|
||||
</set>
|
||||
@@ -1,4 +1,4 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
|
||||
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="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
|
||||
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,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
|
||||
|
||||
10
app/src/main/res/drawable/ic_baseline_archive_24.xml
Normal file
10
app/src/main/res/drawable/ic_baseline_archive_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="@android:color/white">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M20.54,5.23l-1.39,-1.68C18.88,3.21 18.47,3 18,3H6c-0.47,0 -0.88,0.21 -1.16,0.55L3.46,5.23C3.17,5.57 3,6.02 3,6.5V19c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V6.5c0,-0.48 -0.17,-0.93 -0.46,-1.27zM12,17.5L6.5,12H10v-2h4v2h3.5L12,17.5zM5.12,5l0.81,-1h12l0.94,1H5.12z"/>
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/ic_baseline_archive_24_black.xml
Normal file
10
app/src/main/res/drawable/ic_baseline_archive_24_black.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="@android:color/black">
|
||||
<path
|
||||
android:fillColor="@android:color/black"
|
||||
android:pathData="M20.54,5.23l-1.39,-1.68C18.88,3.21 18.47,3 18,3H6c-0.47,0 -0.88,0.21 -1.16,0.55L3.46,5.23C3.17,5.57 3,6.02 3,6.5V19c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V6.5c0,-0.48 -0.17,-0.93 -0.46,-1.27zM12,17.5L6.5,12H10v-2h4v2h3.5L12,17.5zM5.12,5l0.81,-1h12l0.94,1H5.12z"/>
|
||||
</vector>
|
||||
@@ -1,4 +1,4 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
|
||||
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="M7,10l5,5 5,-5z"/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
|
||||
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="M7,14l5,-5 5,5z"/>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:tint="#FFFFFF" android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal" 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>
|
||||
|
||||
5
app/src/main/res/drawable/ic_baseline_unfold_less_24.xml
Normal file
5
app/src/main/res/drawable/ic_baseline_unfold_less_24.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
|
||||
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="M7.41,18.59L8.83,20 12,16.83 15.17,20l1.41,-1.41L12,14l-4.59,4.59zM16.59,5.41L15.17,4 12,7.17 8.83,4 7.41,5.41 12,10l4.59,-4.59z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_baseline_unfold_more_24.xml
Normal file
5
app/src/main/res/drawable/ic_baseline_unfold_more_24.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
|
||||
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="M12,5.83L15.17,9l1.41,-1.41L12,3 7.41,7.59 8.83,9 12,5.83zM12,18.17L8.83,15l-1.41,1.41L12,21l4.59,-4.59L15.17,15 12,18.17z"/>
|
||||
</vector>
|
||||
@@ -2,7 +2,8 @@
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z"/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
|
||||
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="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="@color/colorPrimary"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/>
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:pathData="M18,5l0,-3l-12,0l0,1.17l1.83,1.83z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:pathData="M6,2h12v3h-12z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
|
||||
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="M10,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2h-8l-2,-2z"/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
|
||||
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="M9,3L5,6.99h3L8,14h2L10,6.99h3L9,3zM16,17.01L16,10h-2v7.01h-3L15,21l4,-3.99h-3z"/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
|
||||
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="M12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6h1.9c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM18,20L6,20L6,10h12v10z"/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
|
||||
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="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z"/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
|
||||
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,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
|
||||
|
||||
10
app/src/main/res/drawable/ic_overflow_menu.xml
Normal file
10
app/src/main/res/drawable/ic_overflow_menu.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="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
|
||||
</vector>
|
||||
@@ -1,4 +1,4 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
|
||||
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="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
|
||||
|
||||
5
app/src/main/res/drawable/ic_starred_black.xml
Normal file
5
app/src/main/res/drawable/ic_starred_black.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#000000"
|
||||
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="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_unstarred_black.xml
Normal file
5
app/src/main/res/drawable/ic_unstarred_black.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#000000"
|
||||
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="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
|
||||
</vector>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@color/listItemHighlight" android:state_activated="true" />
|
||||
</selector>
|
||||
5
app/src/main/res/drawable/round_outline.xml
Normal file
5
app/src/main/res/drawable/round_outline.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#FFFFFF" />
|
||||
<corners android:radius="10dp" />
|
||||
</shape>
|
||||
@@ -1,4 +1,4 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
|
||||
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="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z"/>
|
||||
|
||||
@@ -10,15 +10,13 @@
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay"/>
|
||||
style="?attr/toolbarStyle" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
@@ -46,7 +44,6 @@
|
||||
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" />
|
||||
@@ -61,12 +58,12 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/version_history_main" />
|
||||
|
||||
<TextView
|
||||
android:importantForAccessibility="no"
|
||||
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"
|
||||
@@ -86,7 +83,6 @@
|
||||
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" />
|
||||
@@ -101,12 +97,12 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/credits_main" />
|
||||
|
||||
<TextView
|
||||
android:importantForAccessibility="no"
|
||||
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"
|
||||
@@ -126,18 +122,28 @@
|
||||
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:id="@+id/translate_sub"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="2dp"
|
||||
android:text="@string/translate_platform"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/translate_main"/>
|
||||
|
||||
<TextView
|
||||
android:importantForAccessibility="no"
|
||||
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"
|
||||
@@ -157,7 +163,6 @@
|
||||
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" />
|
||||
@@ -174,12 +179,12 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/license_main"/>
|
||||
|
||||
<TextView
|
||||
android:importantForAccessibility="no"
|
||||
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"
|
||||
@@ -199,7 +204,6 @@
|
||||
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" />
|
||||
@@ -215,12 +219,12 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/repo_main" />
|
||||
|
||||
<TextView
|
||||
android:importantForAccessibility="no"
|
||||
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"
|
||||
@@ -240,7 +244,6 @@
|
||||
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" />
|
||||
@@ -256,12 +259,12 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/privacy_main" />
|
||||
|
||||
<TextView
|
||||
android:importantForAccessibility="no"
|
||||
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"
|
||||
@@ -281,7 +284,6 @@
|
||||
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" />
|
||||
@@ -297,12 +299,12 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/rate_main" />
|
||||
|
||||
<TextView
|
||||
android:importantForAccessibility="no"
|
||||
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"
|
||||
@@ -322,7 +324,6 @@
|
||||
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" />
|
||||
@@ -338,12 +339,12 @@
|
||||
android:padding="2dp"/>
|
||||
|
||||
<TextView
|
||||
android:importantForAccessibility="no"
|
||||
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"
|
||||
|
||||
@@ -22,15 +22,13 @@
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay" />
|
||||
style="?attr/toolbarStyle" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/groups"
|
||||
|
||||
32
app/src/main/res/layout/archive_activity.xml
Normal file
32
app/src/main/res/layout/archive_activity.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?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"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context="protect.card_locker.MainActivity">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
style="?attr/toolbarStyle" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/groups"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:tabMode="scrollable"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<include layout="@layout/content_main"/>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
24
app/src/main/res/layout/barcode_layout.xml
Normal file
24
app/src/main/res/layout/barcode_layout.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout android:orientation="vertical"
|
||||
android:padding="10.0dp"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<ImageView
|
||||
android:background="@drawable/round_outline"
|
||||
android:importantForAccessibility="no"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/barcode_disp_height"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:id="@+id/barcodeImage"
|
||||
android:layout_weight="1.0"/>
|
||||
<TextView
|
||||
android:id="@+id/barcodeName"
|
||||
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>
|
||||
@@ -1,318 +1,88 @@
|
||||
<?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="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay" />
|
||||
|
||||
style="?attr/toolbarStyle" />
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<ScrollView
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
android:id="@+id/scrollView">
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:id="@+id/explanationText"
|
||||
android:textSize="20sp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:paddingStart="20.0dip"
|
||||
android:paddingEnd="20.0dip"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/barcodeIdLayout"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:text="@string/enterBarcodeInstructions" />
|
||||
<LinearLayout
|
||||
android:id="@+id/barcodeIdLayout"
|
||||
android:orientation="horizontal"
|
||||
android:padding="10.0dip"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@+id/explanationText"
|
||||
app:layout_constraintBottom_toTopOf="@+id/noBarcode"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
<TextView android:textSize="16.0sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:paddingStart="20.0dip"
|
||||
android:paddingEnd="20.0dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:labelFor="@+id/cardId"
|
||||
android:text="@string/cardId" />
|
||||
|
||||
<LinearLayout android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<LinearLayout android:orientation="horizontal"
|
||||
android:padding="10.0dip"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView android:textSize="20sp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:paddingStart="20.0dip"
|
||||
android:paddingEnd="20.0dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/enterBarcodeInstructions" />
|
||||
</LinearLayout>
|
||||
<LinearLayout android:orientation="horizontal"
|
||||
android:padding="10.0dip"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/barcodeIdLayout">
|
||||
<TextView android:textSize="16.0sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:paddingStart="20.0dip"
|
||||
android:paddingEnd="20.0dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:labelFor="@+id/cardId"
|
||||
android:text="@string/cardId" />
|
||||
<EditText android:id="@+id/cardId"
|
||||
android:hint="AB1234"
|
||||
android:importantForAutofill="no"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="text"/>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
<EditText
|
||||
android:id="@+id/cardId"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/barcodesLayout"/>
|
||||
<Button
|
||||
android:id="@+id/noBarcode"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/barcodeNoBarcode"
|
||||
android:textColor="#FFFFFF"
|
||||
android:enabled="false" />
|
||||
<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/aztecBarcode"
|
||||
android:layout_weight="1.0"/>
|
||||
<TextView
|
||||
android:id="@+id/aztecBarcodeText"
|
||||
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 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/code39Barcode"
|
||||
android:layout_weight="1.0"/>
|
||||
<TextView
|
||||
android:id="@+id/code39BarcodeText"
|
||||
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 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/code128Barcode"
|
||||
android:layout_weight="1.0"/>
|
||||
<TextView
|
||||
android:id="@+id/code128BarcodeText"
|
||||
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 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/codabarBarcode"
|
||||
android:layout_weight="1.0"/>
|
||||
<TextView
|
||||
android:id="@+id/codabarBarcodeText"
|
||||
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 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/datamatrixBarcode"
|
||||
android:layout_weight="1.0"/>
|
||||
<TextView
|
||||
android:id="@+id/datamatrixBarcodeText"
|
||||
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 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/ean8Barcode"
|
||||
android:layout_weight="1.0"/>
|
||||
<TextView
|
||||
android:id="@+id/ean8BarcodeText"
|
||||
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 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/ean13Barcode"
|
||||
android:layout_weight="1.0"/>
|
||||
<TextView
|
||||
android:id="@+id/ean13BarcodeText"
|
||||
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 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/itfBarcode"
|
||||
android:layout_weight="1.0"/>
|
||||
<TextView
|
||||
android:id="@+id/itfBarcodeText"
|
||||
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 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/pdf417Barcode"
|
||||
android:layout_weight="1.0"/>
|
||||
<TextView
|
||||
android:id="@+id/pdf417BarcodeText"
|
||||
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 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/qrcodeBarcode"
|
||||
android:layout_weight="1.0"/>
|
||||
<TextView
|
||||
android:id="@+id/qrcodeBarcodeText"
|
||||
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 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/upcaBarcode"
|
||||
android:layout_weight="1.0"/>
|
||||
<TextView
|
||||
android:id="@+id/upcaBarcodeText"
|
||||
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 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>
|
||||
android:hint="AB1234"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="text"
|
||||
android:minHeight="48dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
<Button
|
||||
android:id="@+id/noBarcode"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@+id/barcodeIdLayout"
|
||||
app:layout_constraintBottom_toTopOf="@+id/barcodes"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:text="@string/barcodeNoBarcode"
|
||||
android:enabled="false" />
|
||||
<ListView
|
||||
android:id="@+id/barcodes"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="fill_parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/noBarcode"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:scrollbars="vertical" />
|
||||
</LinearLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
@@ -38,12 +38,13 @@
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list"
|
||||
app:layoutManager="androidx.recyclerview.widget.StaggeredGridLayoutManager"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:spanCount="@integer/main_view_card_columns"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="80dp"
|
||||
android:clipToPadding="false"
|
||||
android:scrollbars="vertical"
|
||||
android:background="@color/mainLoyaltyCardBackground"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
@@ -43,8 +43,7 @@
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
android:layout_weight="1"
|
||||
app:srcCompat="@drawable/ic_baseline_arrow_drop_up_24"
|
||||
android:contentDescription="@string/moveUp"
|
||||
app:tint="@color/iconColor"/>
|
||||
android:contentDescription="@string/moveUp"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/moveDown"
|
||||
@@ -52,8 +51,7 @@
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
android:layout_weight="1"
|
||||
app:srcCompat="@drawable/ic_baseline_arrow_drop_down_24"
|
||||
android:contentDescription="@string/moveDown"
|
||||
app:tint="@color/iconColor"/>
|
||||
android:contentDescription="@string/moveDown"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/edit"
|
||||
@@ -61,8 +59,7 @@
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
android:layout_weight="1"
|
||||
app:srcCompat="@drawable/ic_mode_edit_white_24dp"
|
||||
android:contentDescription="@string/edit"
|
||||
app:tint="@color/iconColor"/>
|
||||
android:contentDescription="@string/edit"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/delete"
|
||||
@@ -70,8 +67,7 @@
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
android:layout_weight="1"
|
||||
app:srcCompat="@drawable/ic_delete_white_24dp"
|
||||
android:contentDescription="@string/delete"
|
||||
app:tint="@color/iconColor"/>
|
||||
android:contentDescription="@string/delete"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
android:id="@+id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="80dp"
|
||||
android:clipToPadding="false"
|
||||
android:scrollbars="vertical"
|
||||
android:visibility="gone" />
|
||||
</RelativeLayout>
|
||||
|
||||
@@ -7,15 +7,13 @@
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay" />
|
||||
style="?attr/toolbarStyle" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingRight="8dp"
|
||||
style="@style/Widget.MaterialComponents.Chip.Choice"
|
||||
style="@style/Widget.MaterialComponents.Chip.Filter"
|
||||
app:checkedIconVisible="true"
|
||||
android:textAppearance="?android:attr/textAppearance" />
|
||||
android:textAppearance="?android:attr/textAppearance" />
|
||||
|
||||
@@ -18,15 +18,13 @@
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay" />
|
||||
style="?attr/toolbarStyle" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabs"
|
||||
@@ -39,7 +37,7 @@
|
||||
<com.google.android.material.tabs.TabItem
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/barcode"/>
|
||||
android:text="@string/options"/>
|
||||
<com.google.android.material.tabs.TabItem
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -50,7 +48,6 @@
|
||||
|
||||
<ScrollView android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/inputContrastBackground"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
||||
<TableLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -76,8 +73,7 @@
|
||||
app:cardCornerRadius="4dp"
|
||||
android:paddingHorizontal="@dimen/inputPadding"
|
||||
app:cardElevation="0dp"
|
||||
app:cardBackgroundColor="@android:color/transparent"
|
||||
android:outlineProvider="none">
|
||||
app:cardBackgroundColor="@android:color/transparent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/thumbnail"
|
||||
@@ -128,6 +124,95 @@
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Barcode ID -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="@dimen/inputPadding"
|
||||
android:paddingTop="@dimen/inputPadding"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<!-- Barcode ID -->
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/barcodeIdView"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.0"
|
||||
android:hint="@string/barcodeId"
|
||||
android:labelFor="@+id/barcodeIdView">
|
||||
|
||||
<AutoCompleteTextView
|
||||
android:id="@+id/barcodeIdField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="none"/>
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Barcode type -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="@dimen/inputPadding"
|
||||
android:paddingTop="@dimen/inputPadding"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<!-- Barcode type -->
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/barcodeTypeView"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.0"
|
||||
android:hint="@string/barcodeType"
|
||||
android:labelFor="@+id/barcodeTypeField">
|
||||
|
||||
<AutoCompleteTextView
|
||||
android:id="@+id/barcodeTypeField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="none"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Barcode -->
|
||||
<LinearLayout android:orientation="horizontal"
|
||||
android:layout_marginTop="10.0dp"
|
||||
android:layout_marginStart="10.0dp"
|
||||
android:layout_marginEnd="10.0dp"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/barcodeLayout">
|
||||
<ImageView
|
||||
android:background="@drawable/round_outline"
|
||||
android:importantForAccessibility="no"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/barcode_disp_height"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:id="@+id/barcode"
|
||||
android:layout_weight="1.0"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Buttons -->
|
||||
<LinearLayout android:orientation="horizontal"
|
||||
android:padding="10.0dip"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/barcodeCaptureLayout">
|
||||
<Button android:id="@+id/enterButton"
|
||||
android:layout_margin="@dimen/inputMargin"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/editBarcode"
|
||||
android:layout_weight="1.0"/>
|
||||
</LinearLayout>
|
||||
</TableLayout>
|
||||
<TableLayout
|
||||
android:id="@+id/optionsPart"
|
||||
android:visibility="gone">
|
||||
|
||||
<!-- Note -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -161,6 +246,7 @@
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:contentDescription="@string/groups"
|
||||
android:id="@+id/groupChips"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
@@ -240,99 +326,6 @@
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
</LinearLayout>
|
||||
</TableLayout>
|
||||
<TableLayout
|
||||
android:id="@+id/barcodePart"
|
||||
android:visibility="gone">
|
||||
|
||||
<!-- Barcode ID -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="@dimen/inputPadding"
|
||||
android:paddingTop="@dimen/inputPadding"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<!-- Barcode ID -->
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/barcodeIdView"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.0"
|
||||
android:hint="@string/barcodeId"
|
||||
android:labelFor="@+id/barcodeIdView">
|
||||
|
||||
<AutoCompleteTextView
|
||||
android:id="@+id/barcodeIdField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="none"/>
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Barcode type -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="@dimen/inputPadding"
|
||||
android:paddingTop="@dimen/inputPadding"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<!-- Barcode type -->
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/barcodeTypeView"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.0"
|
||||
android:hint="@string/barcodeType"
|
||||
android:labelFor="@+id/barcodeTypeField">
|
||||
|
||||
<AutoCompleteTextView
|
||||
android:id="@+id/barcodeTypeField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="none"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Barcode -->
|
||||
<View
|
||||
android:layout_height="@dimen/inputBorderThickness"
|
||||
android:layout_width="match_parent" />
|
||||
|
||||
<LinearLayout android:orientation="horizontal"
|
||||
android:padding="10.0dip"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/barcodeLayout">
|
||||
<ImageView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/barcode_disp_height"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:padding="10.0dp"
|
||||
android:background="#ffffff"
|
||||
android:id="@+id/barcode"
|
||||
android:layout_weight="1.0"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Buttons -->
|
||||
<LinearLayout android:orientation="horizontal"
|
||||
android:padding="10.0dip"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/barcodeCaptureLayout">
|
||||
<Button android:id="@+id/enterButton"
|
||||
android:layout_margin="@dimen/inputMargin"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/editBarcode"
|
||||
android:textColor="#FFFFFF"
|
||||
android:layout_weight="1.0"/>
|
||||
</LinearLayout>
|
||||
</TableLayout>
|
||||
|
||||
<TableLayout
|
||||
android:id="@+id/picturesPart"
|
||||
@@ -369,7 +362,8 @@
|
||||
android:minHeight="50dp"
|
||||
android:contentDescription="@string/frontImageDescription"
|
||||
android:scaleType="fitCenter"
|
||||
app:srcCompat="@drawable/ic_camera_white" />
|
||||
app:srcCompat="@drawable/ic_camera_white"
|
||||
android:background="?attr/colorPrimary" />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
</LinearLayout>
|
||||
@@ -404,7 +398,8 @@
|
||||
android:minHeight="50dp"
|
||||
android:contentDescription="@string/backImageDescription"
|
||||
android:scaleType="fitCenter"
|
||||
app:srcCompat="@drawable/ic_camera_white" />
|
||||
app:srcCompat="@drawable/ic_camera_white"
|
||||
android:background="?attr/colorPrimary" />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
</LinearLayout>
|
||||
|
||||
@@ -1,146 +1,204 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
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"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/row"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp">
|
||||
|
||||
<LinearLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:layout_marginBottom="4dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/information_container"
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/icon_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
android:layout_height="0dp"
|
||||
android:outlineProvider="none"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:strokeWidth="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/store"
|
||||
app:layout_constraintDimensionRatio="85.6f:53.98f"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<RelativeLayout
|
||||
<ImageView
|
||||
android:importantForAccessibility="no"
|
||||
android:id="@+id/thumbnail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
android:layout_height="match_parent"
|
||||
android:contentDescription="@string/thumbnailDescription"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@mipmap/ic_launcher"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<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"
|
||||
app:cardBackgroundColor="@android:color/transparent">
|
||||
<ImageView
|
||||
android:importantForAccessibility="no"
|
||||
android:id="@+id/selected_thumbnail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:contentDescription="@string/thumbnailDescription"
|
||||
android:scaleType="fitCenter"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_done" />
|
||||
|
||||
<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="fitCenter"
|
||||
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>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/store"
|
||||
android:paddingStart="16dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="left"
|
||||
android:layout_toRightOf="@+id/thumbnail_container"
|
||||
android:layout_toLeftOf="@+id/star"
|
||||
android:textAppearance="?attr/textAppearanceHeadline6" />
|
||||
|
||||
<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"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/star"
|
||||
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" />
|
||||
android:layout_gravity="end"
|
||||
tools:ignore="ExtraText">
|
||||
|
||||
<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" />
|
||||
<ImageView
|
||||
android:id="@+id/star_background"
|
||||
android:layout_width="@dimen/cardThumbnailSize"
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
android:layout_gravity="end"
|
||||
android:alpha="0.5"
|
||||
android:contentDescription="@string/starred"
|
||||
android:elevation="4dp"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_starred_white"
|
||||
tools:ignore="ImageContrastCheck" />
|
||||
|
||||
<RelativeLayout
|
||||
<ImageView
|
||||
android:importantForAccessibility="no"
|
||||
android:id="@+id/star_border"
|
||||
android:layout_width="@dimen/cardThumbnailSize"
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
android:layout_gravity="end"
|
||||
android:alpha="0.5"
|
||||
android:contentDescription="@string/starImage"
|
||||
android:elevation="4dp"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_unstarred_black"
|
||||
tools:ignore="ImageContrastCheck" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/archivedIcon"
|
||||
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>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
android:layout_height="149dp"
|
||||
android:layout_gravity="end"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/thumbnail"
|
||||
tools:layout_editor_absoluteX="355dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/archive_background"
|
||||
android:layout_width="41dp"
|
||||
android:layout_height="44dp"
|
||||
android:layout_gravity="end"
|
||||
android:alpha="0.5"
|
||||
android:contentDescription="@string/archived"
|
||||
android:elevation="4dp"
|
||||
android:rotationX="2"
|
||||
android:visibility="visible"
|
||||
app:srcCompat="@drawable/ic_baseline_archive_24"
|
||||
tools:ignore="ImageContrastCheck,MissingConstraints"
|
||||
tools:layout_editor_absoluteX="0dp"
|
||||
tools:layout_editor_absoluteY="-1dp" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/store"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:textAppearance="?attr/textAppearanceHeadline1"
|
||||
app:layout_constraintTop_toBottomOf="@+id/icon_layout"
|
||||
app:layout_constraintBottom_toTopOf="@+id/note"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:text="Example store"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/note"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:textAppearance="?attr/textAppearanceBody2"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
app:layout_constraintTop_toBottomOf="@+id/store"
|
||||
app:layout_constraintBottom_toTopOf="@+id/info_divider"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:text="Example note"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/info_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="?android:attr/dividerVertical"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
app:layout_constraintTop_toBottomOf="@+id/note"
|
||||
app:layout_constraintBottom_toTopOf="@+id/balance"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/balance"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:textAppearance="?attr/textAppearanceBody2"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
app:drawableLeftCompat="@drawable/ic_baseline_payments_24"
|
||||
android:drawablePadding="4dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toBottomOf="@+id/info_divider"
|
||||
app:layout_constraintBottom_toTopOf="@+id/expiry"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:visibility="visible"
|
||||
tools:text="525 points"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/expiry"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:textAppearance="?attr/textAppearanceBody2"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
app:drawableLeftCompat="@drawable/ic_baseline_access_time_24"
|
||||
android:drawablePadding="4dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toBottomOf="@+id/balance"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:visibility="visible"
|
||||
tools:text="Tomorrow"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/mainLayout"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
@@ -54,11 +55,12 @@
|
||||
android:padding="0dp"
|
||||
app:srcCompat="@drawable/ic_baseline_arrow_drop_up_24"
|
||||
android:contentDescription="@string/moveBarcodeToTopOfScreen"
|
||||
app:tint="#ffffff"
|
||||
app:tint="?attr/colorOnPrimary"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/mainImage"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:background="?attr/colorPrimary" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/mainImage"
|
||||
@@ -85,10 +87,11 @@
|
||||
android:padding="0dp"
|
||||
app:srcCompat="@drawable/ic_baseline_arrow_drop_down_24"
|
||||
android:contentDescription="@string/moveBarcodeToCenterOfScreen"
|
||||
app:tint="#ffffff"
|
||||
app:tint="?attr/colorOnPrimary"
|
||||
app:layout_constraintTop_toBottomOf="@+id/mainImage"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:background="?attr/colorPrimary" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/dotIndicator"
|
||||
@@ -107,6 +110,7 @@
|
||||
app:layout_constraintTop_toBottomOf="@+id/minimizeButton"/>
|
||||
|
||||
<SeekBar
|
||||
android:contentDescription="@string/set_scale"
|
||||
android:id="@+id/barcodeScaler"
|
||||
android:visibility="gone"
|
||||
android:max="100"
|
||||
@@ -154,7 +158,7 @@
|
||||
android:id="@+id/bottom_sheet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/inputBackground"
|
||||
android:background="?android:colorBackground"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="0px"
|
||||
android:visibility="gone"
|
||||
@@ -168,10 +172,11 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="80dp"
|
||||
android:layout_gravity="top|start"
|
||||
android:contentDescription="@string/toggleMoreInfo"
|
||||
android:contentDescription="@string/showMoreInfo"
|
||||
android:scaleType="fitCenter"
|
||||
app:srcCompat="@drawable/ic_baseline_arrow_drop_up_24"
|
||||
app:tint="#ffffff" />
|
||||
app:tint="?attr/colorOnPrimary"
|
||||
android:background="?attr/colorPrimary" />
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/bottomSheetContentWrapper"
|
||||
@@ -247,14 +252,13 @@
|
||||
android:weightSum="1.0"
|
||||
>
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar_landscape"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="?actionBarSize"
|
||||
android:background="@android:color/transparent"
|
||||
android:fitsSystemWindows="false"
|
||||
android:paddingTop="0dp"
|
||||
android:theme="@style/CardView.ActionBarTheme"
|
||||
android:visibility="gone"
|
||||
app:contentInsetStart="72.0dip"
|
||||
app:layout_collapseMode="pin" />
|
||||
@@ -290,7 +294,6 @@
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="?actionBarSize"
|
||||
android:background="@android:color/transparent"
|
||||
android:theme="@style/CardView.ActionBarTheme"
|
||||
app:contentInsetStart="72.0dip"
|
||||
app:layout_collapseMode="pin" />
|
||||
|
||||
|
||||
@@ -19,15 +19,13 @@
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay"/>
|
||||
style="?attr/toolbarStyle" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/groups"
|
||||
|
||||
@@ -19,15 +19,13 @@
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay"/>
|
||||
style="?attr/toolbarStyle" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
||||
@@ -9,15 +9,13 @@
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay" />
|
||||
style="?attr/toolbarStyle" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
@@ -25,6 +23,7 @@
|
||||
android:id="@+id/zxing_barcode_scanner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="?attr/actionBarSize"
|
||||
app:zxing_scanner_layout="@layout/custom_barcode_scanner">
|
||||
</com.journeyapps.barcodescanner.DecoratedBarcodeView>
|
||||
</RelativeLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical">
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context="protect.card_locker.preferences.SettingsActivity">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
style="?attr/toolbarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay"/>
|
||||
android:layout_height="?attr/actionBarSize" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/settings_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="?attr/actionBarSize" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
34
app/src/main/res/layout/simple_toolbar_list_activity.xml
Normal file
34
app/src/main/res/layout/simple_toolbar_list_activity.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<?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">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
style="?attr/toolbarStyle" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:spanCount="1"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent" />
|
||||
</RelativeLayout>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
24
app/src/main/res/menu/archive_menu.xml
Normal file
24
app/src/main/res/menu/archive_menu.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<menu 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"
|
||||
tools:context="protect.card_locker.MainActivity">
|
||||
<item
|
||||
android:id="@+id/action_search"
|
||||
android:title="@string/action_search"
|
||||
android:icon="@drawable/ic_search_white"
|
||||
app:actionViewClass="androidx.appcompat.widget.SearchView"
|
||||
app:showAsAction="always|collapseActionView"
|
||||
android:visible="false"/>
|
||||
<item
|
||||
android:id="@+id/action_unfold"
|
||||
android:title="@string/action_hide_details"
|
||||
android:icon="@drawable/ic_baseline_unfold_less_24"
|
||||
app:showAsAction="always"
|
||||
android:visible="false"/>
|
||||
<item
|
||||
android:id="@+id/action_sort"
|
||||
android:title="@string/sort"
|
||||
android:icon="@drawable/ic_baseline_sort_24"
|
||||
app:showAsAction="always"
|
||||
android:visible="false"/>
|
||||
</menu>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user