mirror of
https://github.com/CatimaLoyalty/Android.git
synced 2025-12-24 07:38:00 -05:00
Compare commits
892 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65ac1a2558 | ||
|
|
6e064e1533 | ||
|
|
0d1c3051c8 | ||
|
|
f2885cd96e | ||
|
|
98de0a7acb | ||
|
|
6622d0f4db | ||
|
|
ccc269ab3e | ||
|
|
4008cc2349 | ||
|
|
02bb266762 | ||
|
|
5e64400faf | ||
|
|
da2a444ae8 | ||
|
|
1a22c27326 | ||
|
|
b5a7824179 | ||
|
|
1f84aa9dc7 | ||
|
|
942336e281 | ||
|
|
27cda3a949 | ||
|
|
1d5d105f8a | ||
|
|
5c886d443f | ||
|
|
aa306ad11d | ||
|
|
d1cc0d9aac | ||
|
|
f8e08f76b8 | ||
|
|
27c18fa5ff | ||
|
|
0017e767c9 | ||
|
|
4d742dc9e4 | ||
|
|
96245543e0 | ||
|
|
59767a7c7f | ||
|
|
8766bcbc71 | ||
|
|
47e87736ca | ||
|
|
f8af01de24 | ||
|
|
4b5a1f8009 | ||
|
|
e55773c8d3 | ||
|
|
e83671eee0 | ||
|
|
8b04e36a88 | ||
|
|
76cd06904c | ||
|
|
cac7a60708 | ||
|
|
aef1078e74 | ||
|
|
20c891ee28 | ||
|
|
5bfca6e428 | ||
|
|
5971e48ab6 | ||
|
|
427b4d64ed | ||
|
|
41995b5bdd | ||
|
|
c74e372c76 | ||
|
|
e0650bc6f9 | ||
|
|
0de6a91bab | ||
|
|
54b854eb64 | ||
|
|
47dd3a35ce | ||
|
|
d16f11d9b7 | ||
|
|
ce190ba3f0 | ||
|
|
070419c888 | ||
|
|
5b6489af9f | ||
|
|
84c2c59038 | ||
|
|
8b5efac70a | ||
|
|
f82860ddd9 | ||
|
|
d3af048fd5 | ||
|
|
2b3c908298 | ||
|
|
14f35edb0f | ||
|
|
d0e80f76d6 | ||
|
|
ee155c721c | ||
|
|
8adc43c9a2 | ||
|
|
02d59dc71d | ||
|
|
3edb0f6a5d | ||
|
|
acf9029394 | ||
|
|
10588769b3 | ||
|
|
68a24fae2f | ||
|
|
625ea26b0a | ||
|
|
dc2c73baa6 | ||
|
|
b8db8bffd4 | ||
|
|
6a078e983b | ||
|
|
bdfc74759f | ||
|
|
b13e14c916 | ||
|
|
32adb85c5b | ||
|
|
a51854e5de | ||
|
|
9ce72bbaaa | ||
|
|
544020febf | ||
|
|
9cfc45e495 | ||
|
|
992ed32d7c | ||
|
|
ac4f83b9e0 | ||
|
|
5810d199fc | ||
|
|
8528e5d8f2 | ||
|
|
83d19c30c2 | ||
|
|
646dab336d | ||
|
|
d05e86cd41 | ||
|
|
6d1c5b31f4 | ||
|
|
583cb49949 | ||
|
|
7f5c7b4cd9 | ||
|
|
17350639aa | ||
|
|
edb961085a | ||
|
|
96c5952869 | ||
|
|
2bac1700d1 | ||
|
|
fe2695e6af | ||
|
|
2120eb9574 | ||
|
|
03917a4067 | ||
|
|
bc09a23c84 | ||
|
|
0ed800634c | ||
|
|
ff4ecfe780 | ||
|
|
2691442809 | ||
|
|
18515e2660 | ||
|
|
df5cbaf7ad | ||
|
|
3cacd03ccd | ||
|
|
2ec04dfa9e | ||
|
|
6943956c37 | ||
|
|
26f0f7909e | ||
|
|
2a1682133b | ||
|
|
02897f312e | ||
|
|
7d4d4cf5c0 | ||
|
|
1fcf797bfe | ||
|
|
b94d417157 | ||
|
|
330227d09b | ||
|
|
67f1ffe617 | ||
|
|
220d7a5ea2 | ||
|
|
e781e00256 | ||
|
|
e630333e8d | ||
|
|
7eb827a219 | ||
|
|
e251b4bc01 | ||
|
|
644f9e1f78 | ||
|
|
396d90c499 | ||
|
|
ff6e93ea2c | ||
|
|
fb97617de6 | ||
|
|
ff47ab0a04 | ||
|
|
ff98dae886 | ||
|
|
deae7681f8 | ||
|
|
849c1d8bec | ||
|
|
b88f0e9a82 | ||
|
|
5ea2972ca1 | ||
|
|
08ad6aaa85 | ||
|
|
8ba32a0196 | ||
|
|
68ce3ce01c | ||
|
|
fb47beb380 | ||
|
|
73a1464d5b | ||
|
|
75b1225b38 | ||
|
|
07a4c4b7a4 | ||
|
|
cbf9295225 | ||
|
|
19d7fae814 | ||
|
|
d9db571362 | ||
|
|
91e44d9418 | ||
|
|
e30b95dd6c | ||
|
|
ddf27e619d | ||
|
|
ebbcbf324f | ||
|
|
c161d4d781 | ||
|
|
da01730c73 | ||
|
|
f0d76f1bc2 | ||
|
|
3d5b1d00f0 | ||
|
|
a098839060 | ||
|
|
d3fac95701 | ||
|
|
37590dc5ee | ||
|
|
8dfbc0b5e6 | ||
|
|
0a9b292d12 | ||
|
|
e7d4228c0c | ||
|
|
a1836e5433 | ||
|
|
52e0496e36 | ||
|
|
50e6e28277 | ||
|
|
4cf770837b | ||
|
|
38fb9f7fb8 | ||
|
|
7a6232c8b6 | ||
|
|
13e62f3b38 | ||
|
|
d5590d37e3 | ||
|
|
fd21806456 | ||
|
|
34bbfffdaf | ||
|
|
56bbb4a786 | ||
|
|
2b92d200ca | ||
|
|
d4154e51d3 | ||
|
|
7d69e63dc9 | ||
|
|
0189f13ee6 | ||
|
|
11e32712f3 | ||
|
|
d124894d34 | ||
|
|
84bfcf2b3f | ||
|
|
8e96096353 | ||
|
|
a4739b2001 | ||
|
|
d3f5f33b53 | ||
|
|
11bb1aa126 | ||
|
|
688010cae4 | ||
|
|
150482eb56 | ||
|
|
c5e98c62ec | ||
|
|
7b2f8885b1 | ||
|
|
d4a7c33787 | ||
|
|
0cd245cafc | ||
|
|
7487993537 | ||
|
|
33b7829b88 | ||
|
|
7531853548 | ||
|
|
3b77cf6f8a | ||
|
|
7b0f459337 | ||
|
|
b498ffd66e | ||
|
|
6c8440a95f | ||
|
|
6bb8cc8bc9 | ||
|
|
dad531876a | ||
|
|
205a629071 | ||
|
|
b7704cc55e | ||
|
|
84ddbef585 | ||
|
|
3bed947ba9 | ||
|
|
23d2faf107 | ||
|
|
b1d06a0be1 | ||
|
|
83ad3e2200 | ||
|
|
84dcce6018 | ||
|
|
d447ea7ff8 | ||
|
|
91f8856efe | ||
|
|
77e45626bb | ||
|
|
a61304a72a | ||
|
|
7336075860 | ||
|
|
6dbd70fced | ||
|
|
81c7d8a259 | ||
|
|
a6a899e696 | ||
|
|
4ae314b3a0 | ||
|
|
16ac55fd67 | ||
|
|
c659065986 | ||
|
|
f1a6b5a7a8 | ||
|
|
036de26e2a | ||
|
|
5ca0919546 | ||
|
|
4430df50fe | ||
|
|
4ff7913283 | ||
|
|
0376245dbc | ||
|
|
3052c40359 | ||
|
|
64af34ca71 | ||
|
|
aba95bbd8a | ||
|
|
5de2a40a54 | ||
|
|
c12c5a6cc4 | ||
|
|
8674520d46 | ||
|
|
06d3a5fe54 | ||
|
|
6c485af249 | ||
|
|
f0ed989463 | ||
|
|
05de9d6941 | ||
|
|
2ddebc5970 | ||
|
|
f1265bf84e | ||
|
|
dc4c4be4c7 | ||
|
|
d5100b97f3 | ||
|
|
31dc05f6b2 | ||
|
|
ca7ee9e694 | ||
|
|
7acc2e28cd | ||
|
|
7eff64bf26 | ||
|
|
d5d3cfa4e8 | ||
|
|
2b937eda7c | ||
|
|
118b5a90b6 | ||
|
|
d9c9295220 | ||
|
|
eb4b6f276e | ||
|
|
52e68df515 | ||
|
|
c23973086c | ||
|
|
34be79f3d2 | ||
|
|
a8082748ad | ||
|
|
08a4de919f | ||
|
|
7ce54ec0d3 | ||
|
|
ce7dad0c8e | ||
|
|
e059ed361d | ||
|
|
d12d641913 | ||
|
|
5336b37133 | ||
|
|
1819476a71 | ||
|
|
e2c621ec1f | ||
|
|
854bca27ae | ||
|
|
b1dd9fa38a | ||
|
|
8776ed61e9 | ||
|
|
c35fe166ed | ||
|
|
0126b6d6da | ||
|
|
4ecb877e27 | ||
|
|
1c2e810319 | ||
|
|
4f7f72f213 | ||
|
|
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 | ||
|
|
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
|
||||
|
||||
112
CHANGELOG.md
112
CHANGELOG.md
@@ -1,5 +1,117 @@
|
||||
# Changelog
|
||||
|
||||
## v2.20.0 - 114
|
||||
|
||||
- Add Monochrome icon for Android 13
|
||||
- Improve first launch screen
|
||||
- Fidme import fixes
|
||||
|
||||
## v2.19.0 - 113
|
||||
|
||||
- Add previous and next buttons to the loyalty card view
|
||||
- Fix foreground colour on edit button
|
||||
- Replace floppy disk save icon with checkmark
|
||||
|
||||
## v2.18.2 - 112
|
||||
|
||||
- Make the possibility to set a custom header more visible
|
||||
|
||||
## v2.18.1 - 111
|
||||
|
||||
- Arabic language support
|
||||
- Display archived card count in group overview
|
||||
- Fix balance parsing bugs (made cards not savable in Arabic and other language with non-Western numbers)
|
||||
- Fix custom theme not applying to main screen correctly
|
||||
- Improve display of selected cards
|
||||
- Fix crash when leaving cardview in RTL layouts for cards with expiry or balance
|
||||
- Fix back arrow in card view pointing the wrong way in RTL layouts
|
||||
|
||||
## 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
|
||||
|
||||
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 92
|
||||
versionName "2.10.0"
|
||||
versionCode 114
|
||||
versionName "2.20.0"
|
||||
|
||||
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.4.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
|
||||
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'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||
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.6'
|
||||
|
||||
// Splash Screen
|
||||
implementation 'androidx.core:core-splashscreen:1.0.0-alpha02'
|
||||
implementation 'androidx.core:core-splashscreen:1.0.0'
|
||||
|
||||
// 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.1'
|
||||
implementation 'com.github.invissvenska:NumberPickerPreference:1.0.4'
|
||||
implementation 'net.lingala.zip4j:zip4j:2.11.2'
|
||||
|
||||
// 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.7.1'
|
||||
testImplementation 'org.robolectric:robolectric:4.8.2'
|
||||
}
|
||||
|
||||
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>
|
||||
@@ -1,5 +1,6 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
@@ -9,6 +10,7 @@ import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
@@ -63,6 +65,7 @@ public class AboutActivity extends CatimaAppCompatActivity implements View.OnCli
|
||||
USED_LIBRARIES.add(new ThirdPartyInfo("Color Picker", "https://github.com/jaredrummler/ColorPicker", "Apache 2.0"));
|
||||
USED_LIBRARIES.add(new ThirdPartyInfo("Commons CSV", "https://commons.apache.org/proper/commons-csv/", "Apache 2.0"));
|
||||
USED_LIBRARIES.add(new ThirdPartyInfo("NumberPickerPreference", "https://github.com/invissvenska/NumberPickerPreference", "GNU LGPL 3.0"));
|
||||
USED_LIBRARIES.add(new ThirdPartyInfo("uCrop", "https://github.com/Yalantis/uCrop", "Apache 2.0"));
|
||||
USED_LIBRARIES.add(new ThirdPartyInfo("Zip4j", "https://github.com/srikanth-lingala/zip4j", "Apache 2.0"));
|
||||
USED_LIBRARIES.add(new ThirdPartyInfo("ZXing", "https://github.com/zxing/zxing", "Apache 2.0"));
|
||||
USED_LIBRARIES.add(new ThirdPartyInfo("ZXing Android Embedded", "https://github.com/journeyapps/zxing-android-embedded", "Apache 2.0"));
|
||||
@@ -151,13 +154,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 {
|
||||
@@ -166,7 +169,12 @@ public class AboutActivity extends CatimaAppCompatActivity implements View.OnCli
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
startActivity(intent);
|
||||
try {
|
||||
startActivity(intent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(this, R.string.failedToOpenUrl, Toast.LENGTH_LONG).show();
|
||||
Log.e(TAG, "No activity found to handle intent", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -76,6 +76,7 @@ public class BarcodeSelectorAdapter extends ArrayAdapter<CatimaBarcodeWithValue>
|
||||
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
|
||||
@@ -89,13 +90,13 @@ public class BarcodeSelectorAdapter extends ArrayAdapter<CatimaBarcodeWithValue>
|
||||
|
||||
Log.d(TAG, "Generating barcode for type " + formatType);
|
||||
|
||||
BarcodeImageWriterTask barcodeWriter = new BarcodeImageWriterTask(getContext(), image, cardId, format, text, true, null);
|
||||
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);
|
||||
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,68 @@
|
||||
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 +75,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 java.util.List;
|
||||
import java.util.concurrent.Flow;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
@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,25 @@ 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);
|
||||
}
|
||||
|
||||
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;
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
Utils.patchColors(this);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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, final int starStatus, final Long lastUsed, final int archiveStatus) {
|
||||
database.beginTransaction();
|
||||
|
||||
// Card
|
||||
ContentValues contentValues = new ContentValues();
|
||||
@@ -466,53 +440,62 @@ 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,
|
||||
contentValues.put(LoyaltyCardDbIds.STAR_STATUS, starStatus);
|
||||
contentValues.put(LoyaltyCardDbIds.LAST_USED, lastUsed != null ? lastUsed : Utils.getUnixTime());
|
||||
contentValues.put(LoyaltyCardDbIds.ARCHIVE_STATUS, archiveStatus);
|
||||
|
||||
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 +509,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 +533,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 +544,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 +576,35 @@ 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 int getArchivedCardsCount(SQLiteDatabase database, final String groupName) {
|
||||
Cursor data = database.rawQuery(
|
||||
"select * from " + LoyaltyCardDbIds.TABLE + " c " +
|
||||
" LEFT JOIN " + LoyaltyCardDbIdsGroups.TABLE + " cg " +
|
||||
" ON c." + LoyaltyCardDbIds.ID + " = cg." + LoyaltyCardDbIdsGroups.cardID +
|
||||
" where " + LoyaltyCardDbIds.ARCHIVE_STATUS + " = 1" +
|
||||
" AND " + LoyaltyCardDbIdsGroups.groupID + "= ?",
|
||||
withArgs(groupName)
|
||||
);
|
||||
|
||||
int count = data.getCount();
|
||||
|
||||
data.close();
|
||||
return count;
|
||||
}
|
||||
|
||||
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 +613,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 +624,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 +636,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 +659,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 +685,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 +694,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 +718,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 +733,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 +747,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 +771,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 +860,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 +882,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";
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -12,28 +14,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,18 +38,24 @@ 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);
|
||||
inputHolder.mCardCount.setText(mContext.getResources().getQuantityString(R.plurals.groupCardCount, groupCardCount, groupCardCount));
|
||||
int groupCardCount = DBHelper.getGroupCardCount(mDatabase, group._id);
|
||||
int archivedCardCount = DBHelper.getArchivedCardsCount(mDatabase, group._id);
|
||||
|
||||
Resources resources = mContext.getResources();
|
||||
|
||||
String cardCountText;
|
||||
if (archivedCardCount > 0) {
|
||||
cardCountText = resources.getQuantityString(R.plurals.groupCardCountWithArchived, groupCardCount, groupCardCount, archivedCardCount);
|
||||
} else {
|
||||
cardCountText = resources.getQuantityString(R.plurals.groupCardCount, groupCardCount, groupCardCount);
|
||||
}
|
||||
|
||||
inputHolder.mCardCount.setText(cardCountText);
|
||||
inputHolder.mName.setTextSize(mSettings.getFontSizeMax(mSettings.getMediumFont()));
|
||||
inputHolder.mCardCount.setTextSize(mSettings.getFontSizeMax(mSettings.getSmallFont()));
|
||||
|
||||
|
||||
@@ -10,22 +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 java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
@@ -34,13 +32,12 @@ 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 +46,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 +77,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 +127,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 +235,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 +348,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 +419,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,15 @@ 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 +51,7 @@ public class LoyaltyCard implements Parcelable {
|
||||
this.starStatus = starStatus;
|
||||
this.lastUsed = lastUsed;
|
||||
this.zoomLevel = zoomLevel;
|
||||
this.archiveStatus = archiveStatus;
|
||||
}
|
||||
|
||||
protected LoyaltyCard(Parcel in) {
|
||||
@@ -68,6 +71,7 @@ public class LoyaltyCard implements Parcelable {
|
||||
starStatus = in.readInt();
|
||||
lastUsed = in.readLong();
|
||||
zoomLevel = in.readInt();
|
||||
archiveStatus = in.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -85,6 +89,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 +103,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 +130,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,6 +1,7 @@
|
||||
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;
|
||||
@@ -15,33 +16,33 @@ import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
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.constraintlayout.widget.ConstraintLayout;
|
||||
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 final Context mContext;
|
||||
public final Context mContext;
|
||||
private final CardAdapterListener mListener;
|
||||
protected SparseBooleanArray mSelectedItems;
|
||||
protected SparseBooleanArray mAnimationItemsIndex;
|
||||
private boolean mReverseAllAnimations = false;
|
||||
private boolean mShowDetails = true;
|
||||
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;
|
||||
@@ -51,18 +52,30 @@ 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() {
|
||||
@@ -75,67 +88,37 @@ 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);
|
||||
|
||||
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()));
|
||||
inputHolder.setStoreField(loyaltyCard.store);
|
||||
if (mShowDetails && !loyaltyCard.note.isEmpty()) {
|
||||
inputHolder.mNoteField.setVisibility(View.VISIBLE);
|
||||
inputHolder.mNoteField.setText(loyaltyCard.note);
|
||||
inputHolder.mNoteField.setTextSize(size);
|
||||
inputHolder.setNoteField(loyaltyCard.note);
|
||||
} else {
|
||||
inputHolder.mNoteField.setVisibility(View.GONE);
|
||||
inputHolder.setNoteField(null);
|
||||
}
|
||||
|
||||
if (mShowDetails && !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);
|
||||
inputHolder.setBalanceField(loyaltyCard.balance, loyaltyCard.balanceType);
|
||||
} else {
|
||||
inputHolder.mBalanceField.setVisibility(View.GONE);
|
||||
inputHolder.setBalanceField(null, null);
|
||||
}
|
||||
|
||||
if (mShowDetails && 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);
|
||||
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);
|
||||
@@ -144,9 +127,9 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
inputHolder.mCardIcon.setImageBitmap(Utils.generateIcon(mContext, loyaltyCard.store, loyaltyCard.headerColor).getLetterTile());
|
||||
inputHolder.mCardIcon.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
||||
}
|
||||
inputHolder.mIconLayout.setBackgroundColor(loyaltyCard.headerColor != null ? loyaltyCard.headerColor : ContextCompat.getColor(mContext, R.color.colorPrimary));
|
||||
inputHolder.setIconBackgroundColor(loyaltyCard.headerColor != null ? loyaltyCard.headerColor : R.attr.colorPrimary);
|
||||
|
||||
inputHolder.mStarIcon.setVisibility(loyaltyCard.starStatus != 0 ? View.VISIBLE : View.GONE);
|
||||
inputHolder.toggleCardStateIcon(loyaltyCard.starStatus != 0, loyaltyCard.archiveStatus != 0, itemSelected(inputCursor.getPosition()));
|
||||
|
||||
inputHolder.itemView.setActivated(mSelectedItems.get(inputCursor.getPosition(), false));
|
||||
applyIconAnimation(inputHolder, inputCursor.getPosition());
|
||||
@@ -156,6 +139,19 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
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));
|
||||
|
||||
@@ -166,38 +162,24 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
});
|
||||
}
|
||||
|
||||
private boolean itemSelected(int inputPosition) {
|
||||
return mSelectedItems.get(inputPosition, false);
|
||||
}
|
||||
|
||||
private void applyIconAnimation(LoyaltyCardListItemViewHolder inputHolder, int inputPosition) {
|
||||
if (mSelectedItems.get(inputPosition, false)) {
|
||||
inputHolder.mCardIcon.setVisibility(View.GONE);
|
||||
resetIconYAxis(inputHolder.mTickIcon);
|
||||
if (itemSelected(inputPosition)) {
|
||||
inputHolder.mTickIcon.setVisibility(View.VISIBLE);
|
||||
if (mCurrentSelectedIndex == inputPosition) {
|
||||
LoyaltyCardAnimator.flipView(mContext, inputHolder.mTickIcon, inputHolder.mCardIcon, true);
|
||||
resetCurrentIndex();
|
||||
}
|
||||
} else {
|
||||
inputHolder.mTickIcon.setVisibility(View.GONE);
|
||||
resetIconYAxis(inputHolder.mCardIcon);
|
||||
inputHolder.mCardIcon.setVisibility(View.VISIBLE);
|
||||
if ((mReverseAllAnimations && mAnimationItemsIndex.get(inputPosition, false)) || mCurrentSelectedIndex == inputPosition) {
|
||||
LoyaltyCardAnimator.flipView(mContext, inputHolder.mTickIcon, inputHolder.mCardIcon, 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)) {
|
||||
@@ -243,25 +225,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 ImageView mCardIcon, mStarIcon, mTickIcon;
|
||||
public MaterialCardView mIconLayout, mRow;
|
||||
public ImageView mCardIcon, mStarBackground, mStarBorder, mTickIcon, mArchivedBackground;
|
||||
public MaterialCardView mRow, mIconLayout;
|
||||
public ConstraintLayout mStar, mArchived;
|
||||
public View mDivider;
|
||||
|
||||
public LoyaltyCardListItemViewHolder(View inputView, CardAdapterListener inputListener) {
|
||||
private int mIconBackgroundColor;
|
||||
|
||||
|
||||
|
||||
protected LoyaltyCardListItemViewHolder(View inputView, CardAdapterListener inputListener) {
|
||||
super(inputView);
|
||||
mRow = inputView.findViewById(R.id.row);
|
||||
mIconLayout = inputView.findViewById(R.id.icon_layout);
|
||||
mDivider = inputView.findViewById(R.id.info_divider);
|
||||
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());
|
||||
@@ -269,6 +259,120 @@ 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];
|
||||
if (balanceIcon != null) {
|
||||
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];
|
||||
if (expiryIcon != null) {
|
||||
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));
|
||||
} else if (mDarkModeEnabled) {
|
||||
expiryIcon.setColorFilter(BlendModeColorFilterCompat.createBlendModeColorFilterCompat(Color.WHITE, BlendModeCompat.SRC_ATOP));
|
||||
}
|
||||
}
|
||||
mExpiryField.setText(DateFormat.getDateInstance(DateFormat.LONG).format(expiry));
|
||||
if (Utils.hasExpired(expiry)) {
|
||||
mExpiryField.setTextColor(Color.RED);
|
||||
}
|
||||
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) {
|
||||
|
||||
@@ -5,10 +5,11 @@ import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.DatePickerDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.ActivityNotFoundException;
|
||||
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;
|
||||
@@ -49,6 +51,7 @@ import java.io.IOException;
|
||||
import java.io.InvalidObjectException;
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
@@ -67,12 +70,12 @@ import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
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 +107,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";
|
||||
@@ -113,6 +117,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
TabLayout tabs;
|
||||
|
||||
ImageView thumbnail;
|
||||
ImageView thumbnailEditIcon;
|
||||
EditText storeFieldEdit;
|
||||
EditText noteFieldEdit;
|
||||
ChipGroup groupsChips;
|
||||
@@ -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,19 @@ 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
|
||||
0, // Unimportant, always set to null in doSave so the DB updates it to the current timestamp
|
||||
100, // Unimportant, not updated in doSave, defaults to 100 for new cards
|
||||
(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 +227,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 +236,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 +299,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()) {
|
||||
@@ -292,6 +318,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
|
||||
tabs = findViewById(R.id.tabs);
|
||||
thumbnail = findViewById(R.id.thumbnail);
|
||||
thumbnailEditIcon = findViewById(R.id.thumbnailEditIcon);
|
||||
storeFieldEdit = findViewById(R.id.storeNameEdit);
|
||||
noteFieldEdit = findViewById(R.id.noteEdit);
|
||||
groupsChips = findViewById(R.id.groupChips);
|
||||
@@ -302,6 +329,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 +338,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();
|
||||
}
|
||||
};
|
||||
@@ -377,11 +404,10 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
try {
|
||||
BigDecimal balance = Utils.parseCurrency(s.toString(), Utils.currencyHasDecimals(tempLoyaltyCard.balanceType));
|
||||
BigDecimal balance = Utils.parseBalance(s.toString(), tempLoyaltyCard.balanceType);
|
||||
updateTempState(LoyaltyCardField.balance, balance);
|
||||
validBalance = true;
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
} catch (ParseException e) {
|
||||
validBalance = false;
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -426,16 +452,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 +533,6 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
} else {
|
||||
updateTempState(LoyaltyCardField.barcodeId, s.toString());
|
||||
}
|
||||
|
||||
generateOrHideBarcode();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -543,8 +563,6 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
}
|
||||
|
||||
generateOrHideBarcode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -656,7 +674,6 @@ 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
|
||||
@@ -676,13 +693,24 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
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(ContextCompat.getColor(this, R.color.colorPrimary));
|
||||
mCropperOptions.setStatusBarColor(ContextCompat.getColor(this, R.color.colorPrimaryDark));
|
||||
mCropperOptions.setToolbarWidgetColor(Color.WHITE);
|
||||
mCropperOptions.setActiveControlsWidgetColor(ContextCompat.getColor(this, 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 +754,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 +773,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 +781,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 +823,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 +833,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,16 +863,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();
|
||||
}
|
||||
|
||||
// It can't be null because we set it in updateTempState but SpotBugs insists it can be
|
||||
// NP_NULL_ON_SOME_PATH: Possible null pointer dereference
|
||||
if (tempLoyaltyCard.headerColor != null) {
|
||||
thumbnail.setOnClickListener(new ChooseCardImage());
|
||||
updateTempState(LoyaltyCardField.headerColor, Utils.getRandomHeaderColor(this));
|
||||
}
|
||||
|
||||
// Update from intent
|
||||
@@ -876,7 +898,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
hasChanged = hadChanges;
|
||||
}
|
||||
|
||||
generateOrHideBarcode();
|
||||
generateBarcode();
|
||||
|
||||
enterButton.setOnClickListener(new EditCardIdAndBarcode());
|
||||
barcodeImage.setOnClickListener(new EditCardIdAndBarcode());
|
||||
@@ -890,13 +912,28 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
|
||||
generateIcon(storeFieldEdit.getText().toString());
|
||||
|
||||
// It can't be null because we set it in updateTempState but SpotBugs insists it can be
|
||||
// NP_NULL_ON_SOME_PATH: Possible null pointer dereference and
|
||||
// NP_NULL_PARAM_DEREF: Method call passes null for non-null parameter
|
||||
Integer headerColor = tempLoyaltyCard.headerColor;
|
||||
if (headerColor != null) {
|
||||
thumbnail.setOnClickListener(new ChooseCardImage());
|
||||
thumbnailEditIcon.setBackgroundColor(Utils.needsDarkForeground(headerColor) ? Color.BLACK : Color.WHITE);
|
||||
thumbnailEditIcon.setColorFilter(Utils.needsDarkForeground(headerColor) ? Color.WHITE : Color.BLACK);
|
||||
}
|
||||
|
||||
onResuming = false;
|
||||
}
|
||||
|
||||
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 : ContextCompat.getColor(this, R.color.colorPrimary)));
|
||||
int headerColor = Utils.getHeaderColorFromImage((Bitmap) icon, tempLoyaltyCard.headerColor != null ? tempLoyaltyCard.headerColor : R.attr.colorPrimary);
|
||||
|
||||
updateTempState(LoyaltyCardField.headerColor, headerColor);
|
||||
|
||||
thumbnailEditIcon.setBackgroundColor(Utils.needsDarkForeground(headerColor) ? Color.BLACK : Color.WHITE);
|
||||
thumbnailEditIcon.setColorFilter(Utils.needsDarkForeground(headerColor) ? Color.WHITE : Color.BLACK);
|
||||
} else {
|
||||
Log.d("setColorFromIcon", "attempting header color change from icon but icon does not exist");
|
||||
}
|
||||
@@ -940,16 +977,12 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
|
||||
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
try {
|
||||
if (requestCode == PERMISSION_REQUEST_CAMERA_IMAGE_FRONT) {
|
||||
takePhotoForCard(Utils.CARD_IMAGE_FROM_CAMERA_FRONT);
|
||||
} else if (requestCode == PERMISSION_REQUEST_CAMERA_IMAGE_BACK) {
|
||||
takePhotoForCard(Utils.CARD_IMAGE_FROM_CAMERA_BACK);
|
||||
} else if (requestCode == PERMISSION_REQUEST_CAMERA_IMAGE_ICON) {
|
||||
takePhotoForCard(Utils.CARD_IMAGE_FROM_CAMERA_ICON);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
if (requestCode == PERMISSION_REQUEST_CAMERA_IMAGE_FRONT) {
|
||||
takePhotoForCard(Utils.CARD_IMAGE_FROM_CAMERA_FRONT);
|
||||
} else if (requestCode == PERMISSION_REQUEST_CAMERA_IMAGE_BACK) {
|
||||
takePhotoForCard(Utils.CARD_IMAGE_FROM_CAMERA_BACK);
|
||||
} else if (requestCode == PERMISSION_REQUEST_CAMERA_IMAGE_ICON) {
|
||||
takePhotoForCard(Utils.CARD_IMAGE_FROM_CAMERA_ICON);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1078,6 +1111,9 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
public void onColorSelected(int dialogId, int color) {
|
||||
updateTempState(LoyaltyCardField.headerColor, color);
|
||||
|
||||
thumbnailEditIcon.setBackgroundColor(Utils.needsDarkForeground(color) ? Color.BLACK : Color.WHITE);
|
||||
thumbnailEditIcon.setColorFilter(Utils.needsDarkForeground(color) ? Color.WHITE : Color.BLACK);
|
||||
|
||||
// Unset image if set
|
||||
thumbnail.setTag(null);
|
||||
|
||||
@@ -1145,7 +1181,14 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
|
||||
Intent i = new Intent(Intent.ACTION_PICK);
|
||||
i.setType("image/*");
|
||||
mPhotoPickerLauncher.launch(i);
|
||||
|
||||
try {
|
||||
mPhotoPickerLauncher.launch(i);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(getApplicationContext(), R.string.failedLaunchingPhotoPicker, Toast.LENGTH_LONG).show();
|
||||
Log.e(TAG, "No activity found to handle intent", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
@@ -1175,6 +1218,10 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
callable.call();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
// Rethrow as NoSuchElementException
|
||||
// This isn't really true, but a View.OnClickListener doesn't allow throwing other types
|
||||
throw new NoSuchElementException(e.getMessage());
|
||||
}
|
||||
})
|
||||
.show();
|
||||
@@ -1208,7 +1255,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) {
|
||||
@@ -1230,6 +1286,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;
|
||||
@@ -1257,41 +1318,40 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
|
||||
selectedGroups.add((Group) chip.getTag());
|
||||
}
|
||||
|
||||
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);
|
||||
try {
|
||||
Utils.saveCardImage(this, (Bitmap) cardImageFront.getTag(), loyaltyCardId, ImageLocationType.front);
|
||||
Utils.saveCardImage(this, (Bitmap) cardImageBack.getTag(), loyaltyCardId, ImageLocationType.back);
|
||||
Utils.saveCardImage(this, (Bitmap) thumbnail.getTag(), loyaltyCardId, ImageLocationType.icon);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Log.i(TAG, "Updated " + loyaltyCardId + " to " + cardId);
|
||||
// Both update and new card save with lastUsed set to null
|
||||
// This makes the DBHelper set it to the current date
|
||||
// So that new and edited card are always on top when sorting by recently used
|
||||
if (updateLoyaltyCard) {
|
||||
DBHelper.updateLoyaltyCard(mDatabase, loyaltyCardId, tempLoyaltyCard.store, tempLoyaltyCard.note, tempLoyaltyCard.expiry, tempLoyaltyCard.balance, tempLoyaltyCard.balanceType, tempLoyaltyCard.cardId, tempLoyaltyCard.barcodeId, tempLoyaltyCard.barcodeType, tempLoyaltyCard.headerColor, tempLoyaltyCard.starStatus, null, tempLoyaltyCard.archiveStatus);
|
||||
} 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);
|
||||
try {
|
||||
Utils.saveCardImage(this, (Bitmap) cardImageFront.getTag(), loyaltyCardId, ImageLocationType.front);
|
||||
Utils.saveCardImage(this, (Bitmap) cardImageBack.getTag(), loyaltyCardId, ImageLocationType.back);
|
||||
Utils.saveCardImage(this, (Bitmap) thumbnail.getTag(), loyaltyCardId, ImageLocationType.icon);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
loyaltyCardId = (int) DBHelper.insertLoyaltyCard(mDatabase, tempLoyaltyCard.store, tempLoyaltyCard.note, tempLoyaltyCard.expiry, tempLoyaltyCard.balance, tempLoyaltyCard.balanceType, tempLoyaltyCard.cardId, tempLoyaltyCard.barcodeId, tempLoyaltyCard.barcodeType, tempLoyaltyCard.headerColor, 0, null, 0);
|
||||
}
|
||||
|
||||
db.setLoyaltyCardGroups(loyaltyCardId, selectedGroups);
|
||||
try {
|
||||
Utils.saveCardImage(this, (Bitmap) cardImageFront.getTag(), loyaltyCardId, ImageLocationType.front);
|
||||
Utils.saveCardImage(this, (Bitmap) cardImageBack.getTag(), loyaltyCardId, ImageLocationType.back);
|
||||
Utils.saveCardImage(this, (Bitmap) thumbnail.getTag(), loyaltyCardId, ImageLocationType.icon);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
ShortcutHelper.updateShortcuts(this, db.getLoyaltyCard(loyaltyCardId));
|
||||
Log.i(TAG, "Set " + loyaltyCardId + " to " + cardId + " (update: " + updateLoyaltyCard + ")");
|
||||
|
||||
DBHelper.setLoyaltyCardGroups(mDatabase, loyaltyCardId, selectedGroups);
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -1300,31 +1360,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);
|
||||
@@ -1377,36 +1415,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");
|
||||
@@ -1419,17 +1461,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) {
|
||||
@@ -1459,32 +1499,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
|
||||
}
|
||||
|
||||
@@ -2,14 +2,21 @@ 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;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.text.util.Linkify;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.GestureDetector;
|
||||
@@ -17,7 +24,6 @@ import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewOutlineProvider;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.Window;
|
||||
@@ -29,8 +35,22 @@ import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
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.graphics.BlendModeColorFilterCompat;
|
||||
import androidx.core.graphics.BlendModeCompat;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
import androidx.core.widget.TextViewCompat;
|
||||
|
||||
import com.google.android.material.appbar.AppBarLayout;
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
import com.google.android.material.bottomappbar.BottomAppBar;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
@@ -40,18 +60,6 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.constraintlayout.widget.Guideline;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
import androidx.core.content.ContextCompat;
|
||||
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;
|
||||
|
||||
@@ -61,15 +69,12 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
private GestureDetector mGestureDetector;
|
||||
|
||||
CoordinatorLayout coordinatorLayout;
|
||||
ConstraintLayout mainLayout;
|
||||
TextView cardIdFieldView;
|
||||
BottomSheetBehavior behavior;
|
||||
LinearLayout bottomSheet;
|
||||
NestedScrollView bottomSheetContentWrapper;
|
||||
ImageView bottomSheetButton;
|
||||
TextView noteView;
|
||||
TextView groupsView;
|
||||
TextView balanceView;
|
||||
TextView expiryView;
|
||||
BottomAppBar bottomAppBar;
|
||||
ImageButton bottomAppBarInfoButton;
|
||||
ImageButton bottomAppBarPreviousButton;
|
||||
ImageButton bottomAppBarNextButton;
|
||||
AppCompatTextView storeName;
|
||||
ImageButton maximizeButton;
|
||||
ImageView mainImage;
|
||||
@@ -78,12 +83,16 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
View collapsingToolbarLayout;
|
||||
AppBarLayout appBarLayout;
|
||||
ImageView iconImage;
|
||||
Toolbar portraitToolbar;
|
||||
Toolbar landscapeToolbar;
|
||||
|
||||
int loyaltyCardId;
|
||||
ArrayList<Integer> cardList;
|
||||
|
||||
LoyaltyCard loyaltyCard;
|
||||
List<Group> loyaltyCardGroups;
|
||||
boolean rotationEnabled;
|
||||
DBHelper db;
|
||||
SQLiteDatabase database;
|
||||
ImportURIHelper importURIHelper;
|
||||
Settings settings;
|
||||
|
||||
@@ -107,15 +116,13 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
private ImageView[] dots;
|
||||
boolean isBarcodeSupported = true;
|
||||
|
||||
int bottomSheetState;
|
||||
|
||||
static final String STATE_IMAGEINDEX = "imageIndex";
|
||||
static final String STATE_FULLSCREEN = "isFullscreen";
|
||||
static final String STATE_BOTTOMSHEET = "bottomSheetState";
|
||||
|
||||
private final int HEADER_FILTER_ALPHA = 127;
|
||||
|
||||
final private TaskHandler mTasks = new TaskHandler();
|
||||
Runnable barcodeImageGenerationFinishedCallback;
|
||||
|
||||
@Override
|
||||
public boolean onDown(MotionEvent e) {
|
||||
@@ -200,6 +207,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
private void extractIntentFields(Intent intent) {
|
||||
final Bundle b = intent.getExtras();
|
||||
loyaltyCardId = b != null ? b.getInt("id") : 0;
|
||||
cardList = b != null ? b.getIntegerArrayList("cardList") : null;
|
||||
Log.d(TAG, "View activity: id=" + loyaltyCardId);
|
||||
}
|
||||
|
||||
@@ -220,6 +228,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
Drawable unwrappedIcon = AppCompatResources.getDrawable(this, icon);
|
||||
assert unwrappedIcon != null;
|
||||
Drawable wrappedIcon = DrawableCompat.wrap(unwrappedIcon);
|
||||
wrappedIcon.mutate();
|
||||
if (dark) {
|
||||
DrawableCompat.setTint(wrappedIcon, Color.BLACK);
|
||||
} else {
|
||||
@@ -242,47 +251,87 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
if (savedInstanceState == null) {
|
||||
Bundle incomingIntentExtras = getIntent().getExtras();
|
||||
|
||||
if (incomingIntentExtras == null) {
|
||||
Toast.makeText(this, R.string.noCardExistsError, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
int transitionRight = incomingIntentExtras.getInt("transition_right", -1);
|
||||
if (transitionRight == 1) {
|
||||
// right side transition
|
||||
overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);
|
||||
} else if (transitionRight == 0) {
|
||||
// left side transition
|
||||
overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
bottomSheetButton = findViewById(R.id.bottomSheetButton);
|
||||
noteView = findViewById(R.id.noteView);
|
||||
groupsView = findViewById(R.id.groupsView);
|
||||
balanceView = findViewById(R.id.balanceView);
|
||||
expiryView = findViewById(R.id.expiryView);
|
||||
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);
|
||||
appBarLayout = findViewById(R.id.app_bar_layout);
|
||||
bottomAppBar = findViewById(R.id.bottom_app_bar);
|
||||
iconImage = findViewById(R.id.icon_image);
|
||||
portraitToolbar = findViewById(R.id.toolbar);
|
||||
landscapeToolbar = findViewById(R.id.toolbar_landscape);
|
||||
|
||||
bottomAppBarInfoButton = findViewById(R.id.button_show_info);
|
||||
bottomAppBarPreviousButton = findViewById(R.id.button_previous);
|
||||
bottomAppBarNextButton = findViewById(R.id.button_next);
|
||||
|
||||
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) {
|
||||
@@ -296,11 +345,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
|
||||
@@ -332,33 +381,6 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
});
|
||||
editButton.bringToFront();
|
||||
|
||||
behavior = BottomSheetBehavior.from(bottomSheet);
|
||||
behavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
|
||||
@Override
|
||||
public void onStateChanged(@NonNull View bottomSheet, int newState) {
|
||||
changeUiToBottomSheetState(newState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
|
||||
}
|
||||
});
|
||||
|
||||
bottomSheetButton.setOnClickListener(v -> {
|
||||
if (behavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
|
||||
behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||
} else {
|
||||
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
|
||||
}
|
||||
});
|
||||
|
||||
appBarLayout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
|
||||
@Override
|
||||
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
|
||||
adjustLayoutHeights();
|
||||
}
|
||||
});
|
||||
|
||||
appBarLayout.setOutlineProvider(new ViewOutlineProvider() {
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline) {
|
||||
@@ -367,57 +389,144 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
}
|
||||
});
|
||||
|
||||
bottomAppBarInfoButton.setOnClickListener(view -> showInfoDialog());
|
||||
bottomAppBarPreviousButton.setOnClickListener(view -> prevNextCard(false));
|
||||
bottomAppBarNextButton.setOnClickListener(view -> prevNextCard(true));
|
||||
|
||||
mGestureDetector = new GestureDetector(this, this);
|
||||
View.OnTouchListener gestureTouchListener = (v, event) -> mGestureDetector.onTouchEvent(event);
|
||||
mainImage.setOnTouchListener(gestureTouchListener);
|
||||
}
|
||||
|
||||
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);
|
||||
editButton.hide();
|
||||
} else if (newState == BottomSheetBehavior.STATE_COLLAPSED) {
|
||||
bottomSheetButton.setImageResource(R.drawable.ic_baseline_arrow_drop_up_24);
|
||||
if (!isFullscreen) {
|
||||
editButton.show();
|
||||
}
|
||||
|
||||
// Scroll bottomsheet content back to top
|
||||
bottomSheetContentWrapper.setScrollY(0);
|
||||
}
|
||||
bottomSheetState = newState;
|
||||
}
|
||||
|
||||
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()) {
|
||||
Log.d("adjustLayoutHeights", "setting imageIcon height from: " + iconImage.getLayoutParams().height + " to: " + appBarLayout.getHeight());
|
||||
iconImage.setLayoutParams(new CoordinatorLayout.LayoutParams(
|
||||
CoordinatorLayout.LayoutParams.MATCH_PARENT, appBarLayout.getHeight())
|
||||
);
|
||||
}
|
||||
int bottomSheetHeight = getResources().getDisplayMetrics().heightPixels - appBarLayout.getHeight() - bottomSheetButton.getLayoutParams().height;
|
||||
ViewGroup.LayoutParams params = bottomSheetContentWrapper.getLayoutParams();
|
||||
if (params.height != bottomSheetHeight || params.width != LinearLayout.LayoutParams.MATCH_PARENT) {
|
||||
// XXX android 5 - 9 has so much quirks with setting bottomSheetContent height
|
||||
// just invalidate the wrapper works on 10 onward
|
||||
// bottomSheetContentWrapper.invalidate();
|
||||
// The below worked on android 5 but not 6, reloading the card then it breaks again on 6, entirely random :(
|
||||
// for (int i = 0; i < bottomSheetContentWrapper.getChildCount(); i++) {
|
||||
// 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) {
|
||||
Log.d("adjustLayoutHeights", "setting bottomSheet height from: " + params.height + " to: " + bottomSheetHeight);
|
||||
bottomSheetContentWrapper.setLayoutParams(
|
||||
new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, bottomSheetHeight)
|
||||
appBarLayout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
|
||||
@Override
|
||||
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
|
||||
iconImage.setLayoutParams(new CoordinatorLayout.LayoutParams(
|
||||
CoordinatorLayout.LayoutParams.MATCH_PARENT, appBarLayout.getHeight())
|
||||
);
|
||||
iconImage.setClipBounds(new Rect(left, top, right, bottom));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private SpannableStringBuilder padSpannableString(SpannableStringBuilder spannableStringBuilder) {
|
||||
if (spannableStringBuilder.length() > 0) {
|
||||
spannableStringBuilder.append("\n\n");
|
||||
}
|
||||
|
||||
return spannableStringBuilder;
|
||||
}
|
||||
|
||||
private boolean hasBalance(LoyaltyCard loyaltyCard) {
|
||||
return !loyaltyCard.balance.equals(new BigDecimal(0));
|
||||
}
|
||||
|
||||
private void showInfoDialog() {
|
||||
AlertDialog.Builder infoDialog = new AlertDialog.Builder(this);
|
||||
|
||||
TextView infoTitleView = new TextView(this);
|
||||
infoTitleView.setPadding(20, 20, 20, 20);
|
||||
infoTitleView.setTextSize(settings.getFontSizeMax(settings.getMediumFont()));
|
||||
infoTitleView.setText(loyaltyCard.store);
|
||||
infoDialog.setCustomTitle(infoTitleView);
|
||||
infoDialog.setTitle(loyaltyCard.store);
|
||||
|
||||
TextView infoTextview = new TextView(this);
|
||||
infoTextview.setPadding(20, 0, 20, 0);
|
||||
infoTextview.setAutoLinkMask(Linkify.ALL);
|
||||
infoTextview.setTextIsSelectable(true);
|
||||
|
||||
SpannableStringBuilder infoText = new SpannableStringBuilder();
|
||||
if (!loyaltyCard.note.isEmpty()) {
|
||||
infoText.append(loyaltyCard.note);
|
||||
}
|
||||
|
||||
if (loyaltyCardGroups.size() > 0) {
|
||||
List<String> groupNames = new ArrayList<>();
|
||||
for (Group group : loyaltyCardGroups) {
|
||||
groupNames.add(group._id);
|
||||
}
|
||||
|
||||
padSpannableString(infoText);
|
||||
infoText.append(getString(R.string.groupsList, TextUtils.join(", ", groupNames)));
|
||||
}
|
||||
|
||||
if (hasBalance(loyaltyCard)) {
|
||||
padSpannableString(infoText);
|
||||
infoText.append(getString(R.string.balanceSentence, Utils.formatBalance(this, loyaltyCard.balance, loyaltyCard.balanceType)));
|
||||
}
|
||||
|
||||
if (loyaltyCard.expiry != null) {
|
||||
String formattedExpiry = DateFormat.getDateInstance(DateFormat.LONG).format(loyaltyCard.expiry);
|
||||
|
||||
padSpannableString(infoText);
|
||||
if (Utils.hasExpired(loyaltyCard.expiry)) {
|
||||
int start = infoText.length();
|
||||
|
||||
infoText.append(getString(R.string.expiryStateSentenceExpired, formattedExpiry));
|
||||
infoText.setSpan(new ForegroundColorSpan(Color.RED), start, infoText.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
} else {
|
||||
infoText.append(getString(R.string.expiryStateSentence, formattedExpiry));
|
||||
}
|
||||
}
|
||||
|
||||
infoTextview.setText(infoText);
|
||||
|
||||
infoDialog.setView(infoTextview);
|
||||
infoDialog.setPositiveButton(R.string.ok, (dialogInterface, i) -> dialogInterface.dismiss());
|
||||
infoDialog.create().show();
|
||||
}
|
||||
|
||||
private void setBottomAppBarButtonState() {
|
||||
if (!loyaltyCard.note.isEmpty() || !loyaltyCardGroups.isEmpty() || hasBalance(loyaltyCard) || loyaltyCard.expiry != null) {
|
||||
bottomAppBarInfoButton.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
bottomAppBarInfoButton.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (cardList == null || cardList.size() == 1) {
|
||||
bottomAppBarPreviousButton.setVisibility(View.GONE);
|
||||
bottomAppBarNextButton.setVisibility(View.GONE);
|
||||
} else {
|
||||
bottomAppBarPreviousButton.setVisibility(View.VISIBLE);
|
||||
bottomAppBarNextButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void prevNextCard(boolean next) {
|
||||
// If we're in RTL layout, we want the "left" button to be "next" instead of "previous"
|
||||
// So we swap next around
|
||||
boolean transitionRight = next;
|
||||
if (getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
|
||||
next = !next;
|
||||
}
|
||||
|
||||
int cardListPosition = cardList.indexOf(loyaltyCardId);
|
||||
|
||||
if (next) {
|
||||
if (cardListPosition == cardList.size() - 1) {
|
||||
cardListPosition = 0;
|
||||
} else {
|
||||
cardListPosition = cardListPosition + 1;
|
||||
}
|
||||
} else {
|
||||
if (cardListPosition == 0) {
|
||||
cardListPosition = cardList.size() - 1;
|
||||
} else {
|
||||
cardListPosition = cardListPosition - 1;
|
||||
}
|
||||
}
|
||||
|
||||
loyaltyCardId = cardList.get(cardListPosition);
|
||||
|
||||
// Restart activity with new card id and index
|
||||
Intent intent = getIntent();
|
||||
Bundle b = intent.getExtras();
|
||||
b.putInt("id", loyaltyCardId);
|
||||
b.putInt("transition_right", transitionRight ? 1 : 0);
|
||||
intent.putExtras(b);
|
||||
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
@@ -431,7 +540,6 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
public void onSaveInstanceState(Bundle savedInstanceState) {
|
||||
savedInstanceState.putInt(STATE_IMAGEINDEX, mainImageIndex);
|
||||
savedInstanceState.putBoolean(STATE_FULLSCREEN, isFullscreen);
|
||||
savedInstanceState.putInt(STATE_BOTTOMSHEET, bottomSheetState);
|
||||
super.onSaveInstanceState(savedInstanceState);
|
||||
}
|
||||
|
||||
@@ -465,7 +573,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();
|
||||
@@ -473,6 +581,8 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
return;
|
||||
}
|
||||
|
||||
loyaltyCardGroups = DBHelper.getLoyaltyCardGroups(database, loyaltyCardId);
|
||||
|
||||
setupOrientation();
|
||||
|
||||
format = loyaltyCard.barcodeType;
|
||||
@@ -484,56 +594,6 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
settings.getFontSizeMin(settings.getLargeFont()), settings.getFontSizeMax(settings.getLargeFont()),
|
||||
1, TypedValue.COMPLEX_UNIT_SP);
|
||||
|
||||
if (loyaltyCard.note.length() > 0) {
|
||||
noteView.setVisibility(View.VISIBLE);
|
||||
noteView.setText(loyaltyCard.note);
|
||||
noteView.setTextSize(settings.getFontSizeMax(settings.getMediumFont()));
|
||||
} else {
|
||||
noteView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
List<Group> loyaltyCardGroups = db.getLoyaltyCardGroups(loyaltyCardId);
|
||||
|
||||
if (loyaltyCardGroups.size() > 0) {
|
||||
List<String> groupNames = new ArrayList<>();
|
||||
for (Group group : loyaltyCardGroups) {
|
||||
groupNames.add(group._id);
|
||||
}
|
||||
|
||||
groupsView.setVisibility(View.VISIBLE);
|
||||
groupsView.setText(getString(R.string.groupsList, TextUtils.join(", ", groupNames)));
|
||||
groupsView.setTextSize(settings.getFontSizeMax(settings.getMediumFont()));
|
||||
} else {
|
||||
groupsView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (!loyaltyCard.balance.equals(new BigDecimal(0))) {
|
||||
balanceView.setVisibility(View.VISIBLE);
|
||||
balanceView.setText(getString(R.string.balanceSentence, Utils.formatBalance(this, loyaltyCard.balance, loyaltyCard.balanceType)));
|
||||
balanceView.setTextSize(settings.getFontSizeMax(settings.getMediumFont()));
|
||||
} else {
|
||||
balanceView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (loyaltyCard.expiry != null) {
|
||||
expiryView.setVisibility(View.VISIBLE);
|
||||
|
||||
int expiryString = R.string.expiryStateSentence;
|
||||
if (Utils.hasExpired(loyaltyCard.expiry)) {
|
||||
expiryString = R.string.expiryStateSentenceExpired;
|
||||
expiryView.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.alert));
|
||||
}
|
||||
expiryView.setText(getString(expiryString, DateFormat.getDateInstance(DateFormat.LONG).format(loyaltyCard.expiry)));
|
||||
expiryView.setTextSize(settings.getFontSizeMax(settings.getMediumFont()));
|
||||
} else {
|
||||
expiryView.setVisibility(View.GONE);
|
||||
}
|
||||
expiryView.setTag(loyaltyCard.expiry);
|
||||
|
||||
if (!isFullscreen) {
|
||||
makeBottomSheetVisibleIfUseful();
|
||||
}
|
||||
|
||||
storeName.setText(loyaltyCard.store);
|
||||
storeName.setTextSize(settings.getFontSizeMax(settings.getLargeFont()));
|
||||
TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(
|
||||
@@ -559,6 +619,22 @@ 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);
|
||||
bottomAppBar.setBackgroundColor(darkenedColor);
|
||||
maximizeButton.setColorFilter(textColor);
|
||||
minimizeButton.setColorFilter(textColor);
|
||||
int complementaryColor = Utils.getComplementaryColor(darkenedColor);
|
||||
editButton.setBackgroundTintList(ColorStateList.valueOf(complementaryColor));
|
||||
Drawable editButtonIcon = editButton.getDrawable();
|
||||
editButtonIcon.mutate();
|
||||
editButtonIcon.setTint(Utils.needsDarkForeground(complementaryColor) ? Color.BLACK : Color.WHITE);
|
||||
editButton.setImageDrawable(editButtonIcon);
|
||||
|
||||
Bitmap icon = Utils.retrieveCardImage(this, loyaltyCard.id, ImageLocationType.icon);
|
||||
if (icon != null) {
|
||||
int backgroundAlphaColor = Utils.needsDarkForeground(backgroundHeaderColor) ? Color.WHITE : Color.BLACK;
|
||||
@@ -577,14 +653,22 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
backgroundNeedsDarkIcons = Utils.needsDarkForeground(backgroundHeaderColor);
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.setHomeAsUpIndicator(getIcon(R.drawable.ic_arrow_back_white, backgroundNeedsDarkIcons));
|
||||
actionBar.setHomeAsUpIndicator(getIcon(R.drawable.home_arrow_back_white, backgroundNeedsDarkIcons));
|
||||
}
|
||||
|
||||
fixImageButtonColor(bottomAppBarInfoButton);
|
||||
fixImageButtonColor(bottomAppBarPreviousButton);
|
||||
fixImageButtonColor(bottomAppBarNextButton);
|
||||
setBottomAppBarButtonState();
|
||||
|
||||
// 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);
|
||||
@@ -613,30 +697,15 @@ 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(Utils.isDarkModeEnabled(this));
|
||||
|
||||
setFullscreen(isFullscreen);
|
||||
|
||||
// restore bottomSheet UI states from changing orientation
|
||||
changeUiToBottomSheetState(bottomSheetState);
|
||||
DBHelper.updateLoyaltyCardLastUsed(database, loyaltyCard.id);
|
||||
}
|
||||
|
||||
db.updateLoyaltyCardLastUsed(loyaltyCard.id);
|
||||
private void fixImageButtonColor(ImageButton imageButton) {
|
||||
imageButton.setColorFilter(BlendModeColorFilterCompat.createBlendModeColorFilterCompat(backgroundNeedsDarkIcons ? Color.BLACK : Color.WHITE, BlendModeCompat.SRC_ATOP));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -652,18 +721,17 @@ 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);
|
||||
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);
|
||||
@@ -687,46 +755,78 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
|
||||
switch (id) {
|
||||
case android.R.id.home:
|
||||
if (id == android.R.id.home) {
|
||||
finish();
|
||||
} else if (id == R.id.action_share) {
|
||||
try {
|
||||
importURIHelper.startShareIntent(Arrays.asList(loyaltyCard));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Toast.makeText(LoyaltyCardViewActivity.this, R.string.failedGeneratingShareURL, Toast.LENGTH_LONG).show();
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (id == R.id.action_duplicate) {
|
||||
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;
|
||||
} else if (id == R.id.action_star_unstar) {
|
||||
starred = !starred;
|
||||
DBHelper.updateLoyaltyCardStarStatus(database, loyaltyCardId, starred ? 1 : 0);
|
||||
|
||||
// Re-init loyaltyCard with new data from DB
|
||||
onResume();
|
||||
|
||||
return true;
|
||||
} else if (id == R.id.action_archive) {
|
||||
DBHelper.updateLoyaltyCardArchiveStatus(database, loyaltyCardId, 1);
|
||||
Toast.makeText(LoyaltyCardViewActivity.this, R.string.archived, Toast.LENGTH_LONG).show();
|
||||
|
||||
// Re-init loyaltyCard with new data from DB
|
||||
onResume();
|
||||
|
||||
return true;
|
||||
} else if (id == R.id.action_unarchive) {
|
||||
DBHelper.updateLoyaltyCardArchiveStatus(database, loyaltyCardId, 0);
|
||||
Toast.makeText(LoyaltyCardViewActivity.this, R.string.unarchived, Toast.LENGTH_LONG).show();
|
||||
|
||||
// Re-init loyaltyCard with new data from DB
|
||||
onResume();
|
||||
|
||||
return true;
|
||||
} else if (id == 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();
|
||||
break;
|
||||
dialog.dismiss();
|
||||
});
|
||||
builder.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.show();
|
||||
|
||||
case R.id.action_share:
|
||||
try {
|
||||
importURIHelper.startShareIntent(Arrays.asList(loyaltyCard));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Toast.makeText(LoyaltyCardViewActivity.this, R.string.failedGeneratingShareURL, Toast.LENGTH_LONG).show();
|
||||
e.printStackTrace();
|
||||
}
|
||||
return true;
|
||||
|
||||
case R.id.action_lock_unlock:
|
||||
if (rotationEnabled) {
|
||||
setOrientatonLock(item, true);
|
||||
} else {
|
||||
setOrientatonLock(item, false);
|
||||
}
|
||||
rotationEnabled = !rotationEnabled;
|
||||
return true;
|
||||
|
||||
case R.id.action_star_unstar:
|
||||
starred = !starred;
|
||||
db.updateLoyaltyCardStarStatus(loyaltyCardId, starred ? 1 : 0);
|
||||
invalidateOptionsMenu();
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void setupOrientation() {
|
||||
Toolbar portraitToolbar = findViewById(R.id.toolbar);
|
||||
|
||||
int orientation = getResources().getConfiguration().orientation;
|
||||
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
Log.d(TAG, "Detected landscape mode");
|
||||
|
||||
setTitle(loyaltyCard.store);
|
||||
|
||||
collapsingToolbarLayout.setVisibility(View.GONE);
|
||||
@@ -752,28 +852,7 @@ 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);
|
||||
} else {
|
||||
bottomSheet.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawBarcode() {
|
||||
private void drawBarcode(boolean addPadding) {
|
||||
mTasks.flushTaskList(TaskHandler.TYPE.BARCODE, true, false, false);
|
||||
if (format != null) {
|
||||
BarcodeImageWriterTask barcodeWriter = new BarcodeImageWriterTask(
|
||||
@@ -783,12 +862,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() {
|
||||
@@ -797,13 +877,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;
|
||||
@@ -819,18 +899,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);
|
||||
}
|
||||
@@ -851,7 +941,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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -867,7 +976,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);
|
||||
@@ -883,22 +992,15 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
}
|
||||
|
||||
// Hide toolbars
|
||||
//
|
||||
// Appbar needs to be invisible and have padding removed
|
||||
// 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);
|
||||
|
||||
// Hide other UI elements
|
||||
cardIdFieldView.setVisibility(View.GONE);
|
||||
bottomSheet.setVisibility(View.GONE);
|
||||
behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||
editButton.hide();
|
||||
|
||||
// android 5-9, avoid padding growing on top of bottomSheet
|
||||
coordinatorLayout.removeView(bottomSheet);
|
||||
bottomAppBar.setVisibility(View.GONE);
|
||||
editButton.setVisibility(View.GONE);
|
||||
|
||||
// Set Android to fullscreen mode
|
||||
getWindow().getDecorView().setSystemUiVisibility(
|
||||
@@ -912,7 +1014,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);
|
||||
@@ -926,14 +1028,14 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
}
|
||||
|
||||
// Show appropriate toolbar
|
||||
// And restore 24dp paddingTop for appBarLayout
|
||||
appBarLayout.setVisibility(View.VISIBLE);
|
||||
setupOrientation();
|
||||
iconImage.setVisibility(View.VISIBLE);
|
||||
|
||||
// Show other UI elements
|
||||
cardIdFieldView.setVisibility(View.VISIBLE);
|
||||
makeBottomSheetVisibleIfUseful();
|
||||
editButton.show();
|
||||
editButton.setVisibility(View.VISIBLE);
|
||||
bottomAppBar.setVisibility(View.VISIBLE);
|
||||
|
||||
// Unset fullscreen mode
|
||||
getWindow().getDecorView().setSystemUiVisibility(
|
||||
@@ -941,11 +1043,6 @@ 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;
|
||||
@@ -16,6 +17,7 @@ import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
@@ -26,32 +28,53 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
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;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
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;
|
||||
protected DBHelper.LoyaltyCardOrderDirection mOrderDirection = DBHelper.LoyaltyCardOrderDirection.Ascending;
|
||||
protected int selectedTab = 0;
|
||||
private RecyclerView mCardList;
|
||||
private View mHelpText;
|
||||
private View mHelpSection;
|
||||
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 +155,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 +166,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 +176,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 +222,35 @@ 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) {
|
||||
super.onCreate(inputSavedInstanceState);
|
||||
extractIntentFields(getIntent());
|
||||
SplashScreen.installSplashScreen(this);
|
||||
setTitle(R.string.app_name);
|
||||
setContentView(R.layout.main_activity);
|
||||
super.onCreate(inputSavedInstanceState);
|
||||
if(!mArchiveMode) {
|
||||
setTitle(R.string.app_name);
|
||||
setContentView(R.layout.main_activity);
|
||||
}
|
||||
else{
|
||||
setTitle(R.string.archiveList);
|
||||
setContentView(R.layout.archive_activity);
|
||||
}
|
||||
|
||||
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 +258,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),
|
||||
@@ -213,12 +283,11 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
|
||||
View.OnTouchListener gestureTouchListener = (v, event) -> mGestureDetector.onTouchEvent(event);
|
||||
|
||||
mHelpText = findViewById(R.id.helpText);
|
||||
mHelpSection = findViewById(R.id.helpSection);
|
||||
mNoMatchingCardsText = findViewById(R.id.noMatchingCardsText);
|
||||
mNoGroupCardsText = findViewById(R.id.noGroupCardsText);
|
||||
mCardList = findViewById(R.id.list);
|
||||
|
||||
mHelpText.setOnTouchListener(gestureTouchListener);
|
||||
mNoMatchingCardsText.setOnTouchListener(gestureTouchListener);
|
||||
mCardList.setOnTouchListener(gestureTouchListener);
|
||||
mNoGroupCardsText.setOnTouchListener(gestureTouchListener);
|
||||
@@ -228,7 +297,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 +329,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 +403,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);
|
||||
mHelpSection.setVisibility(View.GONE);
|
||||
mNoGroupCardsText.setVisibility(View.GONE);
|
||||
|
||||
if (mAdapter.getItemCount() > 0) {
|
||||
mCardList.setVisibility(View.VISIBLE);
|
||||
mNoMatchingCardsText.setVisibility(View.GONE);
|
||||
@@ -402,8 +480,15 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (mArchiveMode) {
|
||||
// If an user deletes the last card in archive mode, we should close the activity
|
||||
// This will move us back to the main view
|
||||
finish();
|
||||
}
|
||||
|
||||
mCardList.setVisibility(View.GONE);
|
||||
mHelpText.setVisibility(View.VISIBLE);
|
||||
mHelpSection.setVisibility(View.VISIBLE);
|
||||
|
||||
mNoMatchingCardsText.setVisibility(View.GONE);
|
||||
mNoGroupCardsText.setVisibility(View.GONE);
|
||||
}
|
||||
@@ -413,10 +498,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 +527,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 +566,21 @@ 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,24 +588,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) {
|
||||
boolean shouldShow = !mAdapter.showingDetails();
|
||||
|
||||
if (shouldShow) {
|
||||
inputItem.setIcon(R.drawable.ic_baseline_unfold_less_24);
|
||||
inputItem.setTitle(R.string.action_hide_details);
|
||||
} else {
|
||||
inputItem.setIcon(R.drawable.ic_baseline_unfold_more_24);
|
||||
inputItem.setTitle(R.string.action_show_details);
|
||||
}
|
||||
|
||||
mAdapter.showDetails(shouldShow);
|
||||
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++) {
|
||||
@@ -518,17 +615,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();
|
||||
});
|
||||
|
||||
@@ -542,28 +643,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_archived) {
|
||||
Intent i = new Intent(getApplicationContext(), MainActivity.class);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putBoolean("archiveMode", true);
|
||||
i.putExtras(bundle);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
return super.onOptionsItemSelected(inputItem);
|
||||
}
|
||||
|
||||
@@ -582,7 +693,7 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
sortPrefEditor.apply();
|
||||
|
||||
// Update card list
|
||||
updateLoyaltyCardList();
|
||||
updateLoyaltyCardList(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -685,10 +796,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);
|
||||
}
|
||||
@@ -697,13 +845,12 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onRowClicked(int inputPosition) {
|
||||
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
|
||||
@@ -714,21 +861,28 @@ 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;
|
||||
}
|
||||
|
||||
Intent i = new Intent(this, LoyaltyCardViewActivity.class);
|
||||
i.setAction("");
|
||||
Intent intent = new Intent(this, LoyaltyCardViewActivity.class);
|
||||
intent.setAction("");
|
||||
final Bundle b = new Bundle();
|
||||
b.putInt("id", loyaltyCard.id);
|
||||
i.putExtras(b);
|
||||
|
||||
ArrayList<Integer> cardList = new ArrayList<>();
|
||||
for (int i = 0; i < mAdapter.getItemCount(); i++) {
|
||||
cardList.add(mAdapter.getCard(i).id);
|
||||
}
|
||||
|
||||
b.putIntegerArrayList("cardList", cardList);
|
||||
intent.putExtras(b);
|
||||
|
||||
ShortcutHelper.updateShortcuts(MainActivity.this, loyaltyCard);
|
||||
|
||||
startActivityForResult(i, Utils.MAIN_REQUEST);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
@@ -33,7 +35,7 @@ public class ManageGroupActivity extends CatimaAppCompatActivity implements Mana
|
||||
|
||||
protected Group mGroup = null;
|
||||
private RecyclerView mCardList;
|
||||
private TextView mHelpText;
|
||||
private TextView noGroupCardsText;
|
||||
private EditText mGroupNameText;
|
||||
|
||||
private boolean mGroupNameNotInUse;
|
||||
@@ -45,7 +47,9 @@ public class ManageGroupActivity extends CatimaAppCompatActivity implements Mana
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
mHelpText = findViewById(R.id.helpText);
|
||||
mDatabase = new DBHelper(this).getWritableDatabase();
|
||||
|
||||
noGroupCardsText = findViewById(R.id.noGroupCardsText);
|
||||
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,13 +127,13 @@ 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();
|
||||
});
|
||||
// this setText is here because content_main.xml is reused from main activity
|
||||
mHelpText.setText(getResources().getText(R.string.noGiftCardsGroup));
|
||||
noGroupCardsText.setText(getResources().getText(R.string.noGiftCardsGroup));
|
||||
updateLoyaltyCardList();
|
||||
}
|
||||
|
||||
@@ -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,14 +187,14 @@ 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);
|
||||
noGroupCardsText.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mCardList.setVisibility(View.VISIBLE);
|
||||
mHelpText.setVisibility(View.GONE);
|
||||
noGroupCardsText.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,6 +2,7 @@ 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;
|
||||
@@ -13,13 +14,13 @@ public class ManageGroupCursorAdapter extends LoyaltyCardCursorAdapter {
|
||||
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 +48,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 +95,7 @@ public class ManageGroupCursorAdapter extends LoyaltyCardCursorAdapter {
|
||||
} else {
|
||||
groups.remove(mGroup);
|
||||
}
|
||||
mDb.setLoyaltyCardGroups(cardId, groups);
|
||||
DBHelper.setLoyaltyCardGroups(mDatabase, cardId, groups);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,8 +106,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();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
@@ -20,6 +21,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 +42,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 +67,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 +168,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 +201,17 @@ 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);
|
||||
try {
|
||||
photoPickerLauncher.launch(photoPickerIntent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(getApplicationContext(), R.string.failedLaunchingPhotoPicker, Toast.LENGTH_LONG).show();
|
||||
Log.e(TAG, "No activity found to handle intent", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ 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;
|
||||
@@ -45,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);
|
||||
|
||||
@@ -88,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)
|
||||
|
||||
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 com.google.android.material.color.MaterialColors;
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
import com.yalantis.ucrop.UCropActivity;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
|
||||
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,11 @@ import android.os.Build;
|
||||
import android.os.LocaleList;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
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;
|
||||
@@ -32,6 +36,7 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.ParseException;
|
||||
import java.util.Calendar;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
@@ -39,9 +44,11 @@ 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 +91,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 +213,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();
|
||||
@@ -231,32 +238,19 @@ public class Utils {
|
||||
return numberFormat.format(value);
|
||||
}
|
||||
|
||||
static public Boolean currencyHasDecimals(Currency currency) {
|
||||
static public BigDecimal parseBalance(String value, Currency currency) throws ParseException {
|
||||
NumberFormat numberFormat = NumberFormat.getInstance();
|
||||
|
||||
if (currency == null) {
|
||||
return false;
|
||||
numberFormat.setMaximumFractionDigits(0);
|
||||
} else {
|
||||
numberFormat.setMinimumFractionDigits(currency.getDefaultFractionDigits());
|
||||
numberFormat.setMaximumFractionDigits(currency.getDefaultFractionDigits());
|
||||
}
|
||||
|
||||
return currency.getDefaultFractionDigits() != 0;
|
||||
}
|
||||
Log.d(TAG, numberFormat.parse(value).toString());
|
||||
|
||||
static public BigDecimal parseCurrency(String value, Boolean hasDecimals) throws NumberFormatException {
|
||||
// If there are no decimals expected, remove all separators before parsing
|
||||
if (!hasDecimals) {
|
||||
value = value.replaceAll("[^0-9]", "");
|
||||
return new BigDecimal(value);
|
||||
}
|
||||
|
||||
// There are many ways users can write a currency, so we fix it up a bit
|
||||
// 1. Replace all non-numbers with dots
|
||||
value = value.replaceAll("[^0-9]", ".");
|
||||
|
||||
// 2. Remove all but the last dot
|
||||
while (value.split("\\.").length > 2) {
|
||||
value = value.replaceFirst("\\.", "");
|
||||
}
|
||||
|
||||
// Parse as BigDecimal
|
||||
return new BigDecimal(value);
|
||||
return new BigDecimal(numberFormat.parse(value).toString());
|
||||
}
|
||||
|
||||
static public byte[] bitmapToByteArray(Bitmap bitmap) {
|
||||
@@ -448,4 +442,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);
|
||||
@@ -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)
|
||||
@@ -116,7 +110,10 @@ public class FidmeImporter implements Importer {
|
||||
// The ID is called reference
|
||||
String cardId = CSVHelpers.extractString("Reference", record, "");
|
||||
if (cardId.isEmpty()) {
|
||||
throw new FormatException("No card ID listed, but is required");
|
||||
// Fidme deletes the card id if a card is expired
|
||||
// Because Catima considers the card id a required field, we ignore these expired cards
|
||||
// https://github.com/CatimaLoyalty/Android/issues/1005
|
||||
return;
|
||||
}
|
||||
|
||||
// Sadly, Fidme exports don't contain the card type
|
||||
@@ -124,11 +121,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,11 @@
|
||||
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 +19,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 +31,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,18 +1,12 @@
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.util.Log;
|
||||
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
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 {
|
||||
private static final String TAG = "Catima";
|
||||
@@ -28,7 +22,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 +40,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();
|
||||
}
|
||||
}
|
||||
@@ -13,32 +13,32 @@ 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 +91,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 +102,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,30 +1,37 @@
|
||||
package protect.card_locker.preferences;
|
||||
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import com.google.android.material.color.DynamicColors;
|
||||
|
||||
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 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 +45,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 +67,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 +127,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
|
||||
|
||||
10
app/src/main/res/anim/slide_in_left.xml
Normal file
10
app/src/main/res/anim/slide_in_left.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- https://stackoverflow.com/questions/5151591/android-left-to-right-slide-animation/5151774#5151774 -->
|
||||
<translate
|
||||
android:duration="200"
|
||||
android:fromXDelta="-100%"
|
||||
android:fromYDelta="0%"
|
||||
android:toXDelta="0%"
|
||||
android:toYDelta="0%" />
|
||||
</set>
|
||||
10
app/src/main/res/anim/slide_in_right.xml
Normal file
10
app/src/main/res/anim/slide_in_right.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- https://stackoverflow.com/questions/5151591/android-left-to-right-slide-animation/5151774#5151774 -->
|
||||
<translate
|
||||
android:duration="200"
|
||||
android:fromXDelta="100%"
|
||||
android:fromYDelta="0%"
|
||||
android:toXDelta="0%"
|
||||
android:toYDelta="0%" />
|
||||
</set>
|
||||
10
app/src/main/res/anim/slide_out_left.xml
Normal file
10
app/src/main/res/anim/slide_out_left.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- https://stackoverflow.com/questions/5151591/android-left-to-right-slide-animation/5151774#5151774 -->
|
||||
<translate
|
||||
android:duration="200"
|
||||
android:fromXDelta="0%"
|
||||
android:fromYDelta="0%"
|
||||
android:toXDelta="-100%"
|
||||
android:toYDelta="0%" />
|
||||
</set>
|
||||
10
app/src/main/res/anim/slide_out_right.xml
Normal file
10
app/src/main/res/anim/slide_out_right.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- https://stackoverflow.com/questions/5151591/android-left-to-right-slide-animation/5151774#5151774 -->
|
||||
<translate
|
||||
android:duration="200"
|
||||
android:fromXDelta="0%"
|
||||
android:fromYDelta="0%"
|
||||
android:toXDelta="100%"
|
||||
android:toYDelta="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,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>
|
||||
5
app/src/main/res/drawable/home_arrow_back_white.xml
Normal file
5
app/src/main/res/drawable/home_arrow_back_white.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:autoMirrored="true" 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"/>
|
||||
</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="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||
|
||||
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:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
|
||||
<path android:fillColor="@android:color/white" android:pathData="M15.41,7.41L14,6l-6,6 6,6 1.41,-1.41L10.83,12z"/>
|
||||
</vector>
|
||||
@@ -1,5 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="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"/>
|
||||
<path android:fillColor="@android:color/white" android:pathData="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_baseline_info_24.xml
Normal file
5
app/src/main/res/drawable/ic_baseline_info_24.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/>
|
||||
</vector>
|
||||
@@ -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>
|
||||
|
||||
@@ -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.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"/>
|
||||
|
||||
@@ -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,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"/>
|
||||
|
||||
@@ -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,7 +1,6 @@
|
||||
<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">
|
||||
<path
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
27
app/src/main/res/drawable/ic_launcher_monochrome.xml
Normal file
27
app/src/main/res/drawable/ic_launcher_monochrome.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M53.26,40.92l14.35,-6.39l2.86,6.42"
|
||||
android:strokeAlpha="0.4"
|
||||
android:fillAlpha="0.4"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M36.14,40.95l2.86,-6.42l14.24,6.34"
|
||||
android:strokeAlpha="0.4"
|
||||
android:fillAlpha="0.4"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M40.01,37.17l7.73,3.44H38.48l1.53,-3.44m26.58,0 l1.53,3.44H58.86l7.73,-3.44M39,34.53l-2.86,6.42v1.66H70.47V40.95L67.61,34.53 53.27,40.92l-0.02,-0.05L39,34.53Z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M74.07,51.23l4.93,1.41l-6.44,22.48l-37.61,-10.79l39.13,0l0,-13.11z"
|
||||
android:strokeAlpha="0.7"
|
||||
android:fillAlpha="0.7"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M34.94,40.95C31.66,40.95 29,46.19 29,52.64s2.66,11.69 5.94,11.69L74.07,64.34L74.07,40.95ZM41.21,51.08 L40.15,50.02 44.43,45.74 48.71,50.02 47.65,51.08 44.43,47.86ZM58.02,56.56a3.11,3.11 0,0 1,-2.93 2.05,3.15 3.15,0 0,1 -0.55,-0.05 3.11,3.11 0,0 1,-1.83 -1.04,3.12 3.12,0 0,1 -5.3,-0.96 0.75,0.75 0,0 1,1.41 -0.51,1.62 1.62,0 0,0 3.14,-0.55 0.75,0.75 0,0 1,1.5 0,1.62 1.62,0 0,0 3.14,0.55 0.75,0.75 0,0 1,1.41 0.51ZM64.14,51.08 L60.92,47.86L57.71,51.08l-1.06,-1.06 4.28,-4.28 4.28,4.28Z"/>
|
||||
</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="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>
|
||||
@@ -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"
|
||||
|
||||
@@ -7,14 +7,15 @@
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context="protect.card_locker.ManageGroupActivity">
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fabSave"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end|bottom"
|
||||
app:srcCompat="@drawable/save_24dp"
|
||||
android:layout_margin="16dp"
|
||||
android:contentDescription="@string/save"
|
||||
android:layout_margin="16dp" />
|
||||
app:srcCompat="@drawable/ic_done" />
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@@ -22,15 +23,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>
|
||||
@@ -5,6 +5,8 @@
|
||||
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"
|
||||
|
||||
@@ -2,19 +2,19 @@
|
||||
<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="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
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"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay" />
|
||||
style="?attr/toolbarStyle" />
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<LinearLayout
|
||||
@@ -62,7 +62,8 @@
|
||||
android:hint="AB1234"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="text"
|
||||
android:minHeight="48dp" />
|
||||
android:minHeight="48dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
</LinearLayout>
|
||||
<Button
|
||||
android:id="@+id/noBarcode"
|
||||
@@ -73,7 +74,6 @@
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:text="@string/barcodeNoBarcode"
|
||||
android:textColor="#FFFFFF"
|
||||
android:enabled="false" />
|
||||
<ListView
|
||||
android:id="@+id/barcodes"
|
||||
|
||||
@@ -9,14 +9,33 @@
|
||||
tools:context="protect.card_locker.MainActivity"
|
||||
tools:showIn="@layout/main_activity">
|
||||
|
||||
<TextView
|
||||
style="@style/AppTheme.TextView.NoData"
|
||||
android:id="@+id/helpText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:text="@string/noGiftCards"
|
||||
android:visibility="gone"/>
|
||||
<LinearLayout
|
||||
android:id="@+id/helpSection"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="184dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/ic_launcher_foreground" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.Material3.HeadlineLarge"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/welcome" />
|
||||
|
||||
<TextView
|
||||
style="@style/AppTheme.TextView.NoData"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/noGiftCards"/>
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
style="@style/AppTheme.TextView.NoData"
|
||||
@@ -26,7 +45,7 @@
|
||||
android:gravity="center"
|
||||
android:text="@string/noMatchingGiftCards"
|
||||
android:visibility="gone"/>
|
||||
|
||||
|
||||
<TextView
|
||||
style="@style/AppTheme.TextView.NoData"
|
||||
android:id="@+id/noGroupCardsText"
|
||||
@@ -42,8 +61,9 @@
|
||||
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" />
|
||||
|
||||
@@ -12,21 +12,19 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end|bottom"
|
||||
app:srcCompat="@drawable/save_24dp"
|
||||
android:layout_margin="16dp"
|
||||
android:contentDescription="@string/save"
|
||||
android:layout_margin="16dp" />
|
||||
app:srcCompat="@drawable/ic_done" />
|
||||
|
||||
<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"
|
||||
@@ -86,6 +82,17 @@
|
||||
android:contentDescription="@string/thumbnailDescription"
|
||||
android:src="@mipmap/ic_launcher"/>
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/thumbnailEditIcon"
|
||||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Material3.FloatingActionButton"
|
||||
android:layout_width="@dimen/cardThumbnailEditOverlaySize"
|
||||
android:layout_height="@dimen/cardThumbnailEditOverlaySize"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="4dp"
|
||||
app:contentPadding="4dp"
|
||||
android:alpha="0.8"
|
||||
android:src="@drawable/ic_mode_edit_white_24dp"/>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
@@ -128,6 +135,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 +257,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 +337,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 +373,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 +409,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>
|
||||
|
||||
@@ -16,48 +16,105 @@
|
||||
android:id="@+id/icon_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintDimensionRatio="2:1"
|
||||
android:outlineProvider="none"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/store">
|
||||
app:strokeWidth="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/store"
|
||||
app:layout_constraintDimensionRatio="85.6f:53.98f"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:importantForAccessibility="no"
|
||||
android:id="@+id/thumbnail"
|
||||
android:scaleType="fitCenter"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:contentDescription="@string/thumbnailDescription"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@mipmap/ic_launcher"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:importantForAccessibility="no"
|
||||
android:id="@+id/selected_thumbnail"
|
||||
android:scaleType="fitCenter"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:contentDescription="@string/thumbnailDescription"
|
||||
app:srcCompat="@drawable/ic_done"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:scaleType="fitCenter"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/star"
|
||||
android:layout_width="@dimen/cardThumbnailSize"
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
android:layout_gravity="end"
|
||||
app:srcCompat="@drawable/ic_starred_white"
|
||||
android:contentDescription="@string/starImage"
|
||||
app:tint="?attr/colorControlNormal"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:elevation="4dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
app:srcCompat="@drawable/ic_done"
|
||||
android:background="@color/md_theme_dark_onSelected" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/star"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
tools:ignore="ExtraText">
|
||||
|
||||
<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" />
|
||||
|
||||
<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="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
|
||||
@@ -143,5 +200,6 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:visibility="visible"
|
||||
tools:text="Tomorrow"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
@@ -8,234 +8,6 @@
|
||||
android:layout_height="fill_parent"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fabEdit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end|bottom"
|
||||
app:srcCompat="@drawable/ic_mode_edit_white_24dp"
|
||||
android:contentDescription="@string/edit"
|
||||
android:layout_margin="16dp" />
|
||||
|
||||
<FrameLayout
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/centerGuideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_percent="0.5"/>
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/scalerGuideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_percent="0.75"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/maximizeButton"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="15.0dip"
|
||||
android:layout_marginEnd="15.0dip"
|
||||
android:layout_marginTop="10dp"
|
||||
android:padding="0dp"
|
||||
app:srcCompat="@drawable/ic_baseline_arrow_drop_up_24"
|
||||
android:contentDescription="@string/moveBarcodeToTopOfScreen"
|
||||
app:tint="#ffffff"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/mainImage"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/mainImage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginStart="15.0dip"
|
||||
android:layout_marginEnd="15.0dip"
|
||||
app:layout_constraintBottom_toTopOf="@+id/centerGuideline"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/maximizeButton"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/minimizeButton"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="15.0dip"
|
||||
android:layout_marginEnd="15.0dip"
|
||||
android:layout_marginTop="10dp"
|
||||
android:padding="0dp"
|
||||
app:srcCompat="@drawable/ic_baseline_arrow_drop_down_24"
|
||||
android:contentDescription="@string/moveBarcodeToCenterOfScreen"
|
||||
app:tint="#ffffff"
|
||||
app:layout_constraintTop_toBottomOf="@+id/mainImage"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/dotIndicator"
|
||||
android:visibility="gone"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginStart="15.0dip"
|
||||
android:layout_marginEnd="15.0dip"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/minimizeButton"/>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/barcodeScaler"
|
||||
android:visibility="gone"
|
||||
android:max="100"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/inputPadding"
|
||||
android:layout_marginStart="15.0dip"
|
||||
android:layout_marginEnd="15.0dip"
|
||||
app:layout_constraintTop_toBottomOf="@+id/scalerGuideline"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/cardIdView"
|
||||
android:enabled="true"
|
||||
android:textIsSelectable="true"
|
||||
android:focusable="true"
|
||||
android:longClickable="true"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginLeft="10.0dip"
|
||||
android:layout_marginRight="10.0dip"
|
||||
android:paddingBottom="80dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/dotIndicator"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:textAlignment="center"
|
||||
app:autoSizeTextType="uniform"
|
||||
app:autoSizeMinTextSize="@dimen/singleCardCardIdTextSizeMin"
|
||||
app:autoSizeMaxTextSize="@dimen/singleCardCardIdTextSizeMax"
|
||||
android:ellipsize="end"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/drop_shadow_actionbar"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="5.0dip"
|
||||
android:layout_gravity="top"/>
|
||||
</FrameLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/bottom_sheet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/inputBackground"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="0px"
|
||||
android:visibility="gone"
|
||||
app:behavior_hideable="false"
|
||||
app:behavior_peekHeight="80dp"
|
||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
|
||||
tools:visibility="visible">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/bottomSheetButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="80dp"
|
||||
android:layout_gravity="top|start"
|
||||
android:contentDescription="@string/toggleMoreInfo"
|
||||
android:scaleType="fitCenter"
|
||||
app:srcCompat="@drawable/ic_baseline_arrow_drop_up_24"
|
||||
app:tint="#ffffff" />
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/bottomSheetContentWrapper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/noteView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:autoLink="all"
|
||||
android:enabled="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center"
|
||||
android:longClickable="true"
|
||||
android:padding="20dp"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="@dimen/singleCardNoteTextSizeMin" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/groupsView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:enabled="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center"
|
||||
android:longClickable="true"
|
||||
android:padding="20dp"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="@dimen/singleCardNoteTextSizeMin" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/balanceView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:enabled="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center"
|
||||
android:longClickable="true"
|
||||
android:padding="20dp"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="@dimen/singleCardNoteTextSizeMin" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/expiryView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:enabled="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center"
|
||||
android:longClickable="true"
|
||||
android:padding="20dp"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="@dimen/singleCardNoteTextSizeMin" />
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/app_bar_layout"
|
||||
android:layout_width="fill_parent"
|
||||
@@ -244,17 +16,15 @@
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:fitsSystemWindows="true"
|
||||
android:weightSum="1.0"
|
||||
>
|
||||
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 +60,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" />
|
||||
|
||||
@@ -307,5 +76,202 @@
|
||||
app:srcCompat="@drawable/ic_launcher_foreground"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<FrameLayout
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
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">
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/centerGuideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_percent="0.5"/>
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/scalerGuideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_percent="0.75"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/maximizeButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="15.0dip"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginEnd="15.0dip"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:contentDescription="@string/moveBarcodeToTopOfScreen"
|
||||
android:padding="0dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toTopOf="@+id/mainImage"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_baseline_arrow_drop_up_24"
|
||||
app:tint="?attr/colorOnPrimary"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/mainImage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginStart="15.0dip"
|
||||
android:layout_marginEnd="15.0dip"
|
||||
app:layout_constraintBottom_toTopOf="@+id/centerGuideline"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/maximizeButton"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/minimizeButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="15.0dip"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginEnd="15.0dip"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:contentDescription="@string/moveBarcodeToCenterOfScreen"
|
||||
android:padding="0dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/mainImage"
|
||||
app:srcCompat="@drawable/ic_baseline_arrow_drop_down_24"
|
||||
app:tint="?attr/colorOnPrimary"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/dotIndicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="15.0dip"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginEnd="15.0dip"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/minimizeButton"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/barcodeScaler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/inputPadding"
|
||||
android:layout_marginStart="15.0dip"
|
||||
android:layout_marginEnd="15.0dip"
|
||||
android:contentDescription="@string/set_scale"
|
||||
android:max="100"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/scalerGuideline" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/cardIdView"
|
||||
android:enabled="true"
|
||||
android:textIsSelectable="true"
|
||||
android:focusable="true"
|
||||
android:longClickable="true"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginLeft="10.0dip"
|
||||
android:layout_marginRight="10.0dip"
|
||||
android:paddingBottom="80dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/dotIndicator"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:textAlignment="center"
|
||||
app:autoSizeTextType="uniform"
|
||||
app:autoSizeMinTextSize="@dimen/singleCardCardIdTextSizeMin"
|
||||
app:autoSizeMaxTextSize="@dimen/singleCardCardIdTextSizeMax"
|
||||
android:ellipsize="end"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/drop_shadow_actionbar"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="5.0dip"
|
||||
android:layout_gravity="top"/>
|
||||
</FrameLayout>
|
||||
|
||||
<com.google.android.material.bottomappbar.BottomAppBar
|
||||
android:id="@+id/bottom_app_bar"
|
||||
style="@style/Widget.MaterialComponents.BottomAppBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:contentInsetLeft="0dp"
|
||||
app:contentInsetStart="0dp"
|
||||
app:contentInsetRight="0dp"
|
||||
app:contentInsetEnd="0dp"
|
||||
app:fabAlignmentMode="center">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/button_previous"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="left"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:src="@drawable/ic_baseline_chevron_left_24"
|
||||
android:tooltipText="@string/previousCard"
|
||||
android:visibility="gone" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/button_show_info"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:src="@drawable/ic_baseline_info_24"
|
||||
android:tooltipText="@string/showMoreInfo"
|
||||
android:visibility="gone" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/button_next"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="right"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:src="@drawable/ic_baseline_chevron_right_24"
|
||||
android:tooltipText="@string/nextCard"
|
||||
android:visibility="gone" />
|
||||
|
||||
</com.google.android.material.bottomappbar.BottomAppBar>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fabEdit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/ic_mode_edit_white_24dp"
|
||||
android:contentDescription="@string/edit"
|
||||
app:layout_anchor="@id/bottom_app_bar" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user