Compare commits

...

230 Commits

Author SHA1 Message Date
Sylvia van Os
1a1c028565 Release 1.13 2021-04-10 13:48:13 +02:00
Sylvia van Os
5cd77c3a25 Export V2 test 2021-04-10 13:28:26 +02:00
Sylvia van Os
369631d00c Improve import/export test 2021-04-10 10:14:59 +02:00
Sylvia van Os
350031624c Add V2 import test 2021-04-09 23:01:38 +02:00
Sylvia van Os
846f4d4904 Test barcode type migration 2021-04-07 21:48:19 +02:00
Sylvia van Os
5081eb2dce Lighter grey for non-usable barcode types 2021-04-07 21:19:56 +02:00
Sylvia van Os
e964fda54a Fix tests and barcode ID import 2021-04-07 20:42:11 +02:00
Sylvia van Os
aaa4fc1ef3 Fix bad replace 2021-04-07 18:10:07 +02:00
Sylvia van Os
93331e1a27 Merge pull request #195 from weblate/weblate-catima-catima
Translations update from Weblate
2021-04-07 16:58:28 +02:00
Heimen Stoffels
3251a6266b Translated using Weblate (Dutch)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/nl/
2021-04-07 13:27:23 +02:00
J. Lavoie
d3f8399cbe Translated using Weblate (German)
Currently translated at 66.6% (2 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/de/
2021-04-07 13:27:22 +02:00
J. Lavoie
7c805128a7 Translated using Weblate (French)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/fr/
2021-04-07 13:27:22 +02:00
solokot
a40c4841da Translated using Weblate (Russian)
Currently translated at 100.0% (147 of 147 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-04-07 13:26:58 +02:00
Heimen Stoffels
768ac795ff Translated using Weblate (Dutch)
Currently translated at 100.0% (147 of 147 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-04-07 13:26:58 +02:00
Allan Nordhøy
e8460d52ec Translated using Weblate (Norwegian Bokmål)
Currently translated at 94.5% (139 of 147 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-04-07 13:26:58 +02:00
J. Lavoie
03a7efb52e Translated using Weblate (Italian)
Currently translated at 100.0% (147 of 147 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-04-07 13:26:58 +02:00
J. Lavoie
48b60d8b4d Translated using Weblate (French)
Currently translated at 100.0% (147 of 147 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-04-07 13:26:58 +02:00
J. Lavoie
562b830e5a Translated using Weblate (German)
Currently translated at 100.0% (147 of 147 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-04-07 13:26:57 +02:00
Sylvia van Os
ac810a0c6f Don't force-reset loyalty card type 2021-04-06 22:45:32 +02:00
Sylvia van Os
27a90615a9 Make spotBugs happy 2021-04-06 22:41:11 +02:00
Sylvia van Os
f894427247 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-04-06 22:27:36 +02:00
Sylvia van Os
8b7df8dabe Remove privay policy first start dialog
Just done with Huawei's nonsense.
2021-04-06 22:27:03 +02:00
Sylvia van Os
d66903f972 Merge pull request #193 from weblate/weblate-catima-catima
Translations update from Weblate
2021-04-06 00:39:41 +02:00
Allan Nordhøy
7834a93394 Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/nb_NO/
2021-04-06 00:34:01 +02:00
J. Lavoie
572378de85 Translated using Weblate (Italian)
Currently translated at 100.0% (146 of 146 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-04-06 00:34:00 +02:00
J. Lavoie
26e0c50a13 Translated using Weblate (French)
Currently translated at 100.0% (146 of 146 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-04-06 00:34:00 +02:00
J. Lavoie
cd638a96f3 Translated using Weblate (German)
Currently translated at 100.0% (146 of 146 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-04-06 00:34:00 +02:00
Sylvia van Os
4e043edb64 Show all barcodes and recover from invalid selection 2021-04-06 00:33:35 +02:00
Sylvia van Os
1067d09773 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-04-05 19:44:15 +02:00
Sylvia van Os
d347cdde3e Add license info for third party things in About screen 2021-04-05 19:43:49 +02:00
Sylvia van Os
b704a7492e Merge pull request #192 from weblate/weblate-catima-catima
Translations update from Weblate
2021-04-05 17:43:07 +02:00
Heimen Stoffels
4c28d5d181 Translated using Weblate (Dutch)
Currently translated at 66.6% (2 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/nl/
2021-04-05 17:27:02 +02:00
solokot
47e50de063 Translated using Weblate (Russian)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/ru/
2021-04-05 17:27:02 +02:00
Allan Nordhøy
3777abc2a3 Translated using Weblate (Norwegian Bokmål)
Currently translated at 33.3% (1 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/nb_NO/
2021-04-05 17:27:01 +02:00
solokot
01554381b2 Translated using Weblate (Russian)
Currently translated at 100.0% (146 of 146 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-04-05 17:27:01 +02:00
Heimen Stoffels
09ca9c47ab Translated using Weblate (Dutch)
Currently translated at 100.0% (146 of 146 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-04-05 17:27:01 +02:00
Allan Nordhøy
6751befe5d Translated using Weblate (Norwegian Bokmål)
Currently translated at 94.5% (138 of 146 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-04-05 17:27:00 +02:00
Allan Nordhøy
91661f1059 Translated using Weblate (English)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/en/
2021-04-05 17:26:59 +02:00
Sylvia van Os
afe47f1b84 Use letter icon for shortcuts too 2021-04-05 12:59:12 +02:00
Sylvia van Os
9bcbfc6d81 Hide new FAB in shortcut configure view 2021-04-05 12:20:15 +02:00
Sylvia van Os
89a40a789d Small layout fixes 2021-04-05 00:28:02 +02:00
Allan Nordhøy
e60814d6f3 Fastlane ASO (#187) 2021-04-04 23:08:59 +02:00
Sylvia van Os
ed8028a22b Merge pull request #186 from weblate/weblate-catima-catima
Translations update from Weblate
2021-04-04 14:27:03 +02:00
Allan Nordhøy
bb106f185e Translated using Weblate (Norwegian Bokmål)
Currently translated at 93.8% (137 of 146 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-04-04 14:25:37 +02:00
J. Lavoie
18c4dd4dc9 Translated using Weblate (Italian)
Currently translated at 100.0% (146 of 146 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-04-04 14:25:37 +02:00
J. Lavoie
72672e99c2 Translated using Weblate (French)
Currently translated at 100.0% (146 of 146 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-04-04 14:25:37 +02:00
J. Lavoie
73067d1fe8 Translated using Weblate (German)
Currently translated at 100.0% (146 of 146 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-04-04 14:25:37 +02:00
Sylvia van Os
5e6a4c8184 Merge pull request #184 from comradekingu/patch-1
App strings reworked 2
2021-04-04 14:25:33 +02:00
Sylvia van Os
310228fb5e Merge branch 'master' into patch-1 2021-04-04 14:24:59 +02:00
Allan Nordhøy
5aef382b68 Spelling: fidme-export 2021-04-03 17:16:02 +00:00
Allan Nordhøy
9fa78a4ea8 Spelling: FidMe 2021-04-03 16:13:12 +00:00
Sylvia van Os
400867b03f Merge pull request #185 from weblate/weblate-catima-catima
Translations update from Weblate
2021-04-03 17:47:54 +02:00
solokot
b06c6bc94d Translated using Weblate (Russian)
Currently translated at 100.0% (146 of 146 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-04-03 17:39:05 +02:00
Heimen Stoffels
d6c48bdf6e Translated using Weblate (Dutch)
Currently translated at 100.0% (146 of 146 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-04-03 17:39:05 +02:00
Allan Nordhøy
4d11391f8a Translated using Weblate (Norwegian Bokmål)
Currently translated at 91.0% (133 of 146 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-04-03 17:39:05 +02:00
Sylvia van Os
8b0490fdf3 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-04-03 17:38:48 +02:00
Sylvia van Os
6ec23d976b Fix grammar mistake 2021-04-03 17:38:19 +02:00
Allan Nordhøy
11ed56ee11 App strings reworked 2 2021-04-03 15:26:53 +00:00
Sylvia van Os
4ded73f78e Merge pull request #183 from weblate/weblate-catima-catima
Translations update from Weblate
2021-04-03 15:15:08 +02:00
J. Lavoie
588f8ef677 Translated using Weblate (Korean)
Currently translated at 64.8% (94 of 145 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ko/
2021-04-03 15:12:10 +02:00
solokot
3d70095862 Translated using Weblate (Russian)
Currently translated at 100.0% (145 of 145 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-04-03 15:12:10 +02:00
J. Lavoie
410309ebd2 Translated using Weblate (Polish)
Currently translated at 68.2% (99 of 145 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/pl/
2021-04-03 15:12:10 +02:00
Heimen Stoffels
b9e646a25d Translated using Weblate (Dutch)
Currently translated at 100.0% (145 of 145 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-04-03 15:12:10 +02:00
J. Lavoie
81c1ec9199 Translated using Weblate (Italian)
Currently translated at 100.0% (145 of 145 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-04-03 15:12:10 +02:00
J. Lavoie
4498d08afb Translated using Weblate (French)
Currently translated at 100.0% (145 of 145 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-04-03 15:12:09 +02:00
J. Lavoie
963789db25 Translated using Weblate (German)
Currently translated at 100.0% (145 of 145 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-04-03 15:12:09 +02:00
Sylvia van Os
902fbc505d Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-04-03 15:11:54 +02:00
Sylvia van Os
9889678e53 Add note on unsupported barcode type 2021-04-03 15:11:22 +02:00
Sylvia van Os
ac551ed93f Remove migration guides as the app now tells you in the import screen 2021-04-03 14:36:09 +02:00
Sylvia van Os
ec63931396 Merge pull request #180 from weblate/weblate-catima-catima
Translations update from Weblate
2021-04-03 10:22:08 +02:00
solokot
d29d6ddf4a Translated using Weblate (Russian)
Currently translated at 100.0% (145 of 145 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-04-03 08:39:48 +02:00
J. Lavoie
38aac76144 Translated using Weblate (Russian)
Currently translated at 100.0% (145 of 145 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-04-03 08:39:48 +02:00
Sylvia van Os
30de0a8266 Make spotbugs happy 2021-04-01 18:16:04 +02:00
Sylvia van Os
0016b40256 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-04-01 18:01:49 +02:00
Sylvia van Os
aa3588dbfe Fix unit tests 2021-04-01 18:01:31 +02:00
Sylvia van Os
bf7ddc023d Merge pull request #179 from weblate/weblate-catima-catima
Translations update from Weblate
2021-04-01 17:49:48 +02:00
solokot
e50cf66bca Translated using Weblate (Russian)
Currently translated at 100.0% (151 of 151 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-04-01 17:48:30 +02:00
Heimen Stoffels
85c30185e8 Translated using Weblate (Dutch)
Currently translated at 100.0% (151 of 151 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-04-01 17:48:30 +02:00
Sylvia van Os
5bce259445 Simplify font sizing 2021-04-01 17:48:17 +02:00
Sylvia van Os
8504109399 Merge pull request #178 from TacoTheDank/master
A few miscellaneous improvements
2021-04-01 14:54:52 +02:00
Sylvia van Os
e182857e1b Merge pull request #177 from TheLastProject/feature/differentBarcodeValueThanCardId
Support setting a different barcode value than card ID
2021-04-01 14:34:30 +02:00
Sylvia van Os
981c0b9ca6 Support setting a different barcode value than card ID 2021-04-01 13:26:57 +02:00
TacoTheDank
925f62b633 Use FragmentContainerView for SettingsActivity 2021-03-31 22:56:02 -04:00
TacoTheDank
cceb1207ae Update libraries 2021-03-31 22:30:55 -04:00
TacoTheDank
2e633b19dc Update gradle plugins, rearrange libraries 2021-03-31 22:23:04 -04:00
TacoTheDank
b8b1074a46 Update gradle wrapper 2021-03-31 22:18:57 -04:00
TacoTheDank
bea65793f0 Add wrapper validation action, update upload-artifact to v2 2021-03-31 22:18:47 -04:00
Sylvia van Os
252efabdc6 Increase versioncode for forgotten translation update 2021-03-30 18:51:32 +02:00
Sylvia van Os
018efaeebb Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-03-30 18:46:14 +02:00
Sylvia van Os
56522b42e2 Release 1.12 2021-03-30 18:45:54 +02:00
Sylvia van Os
122a80f29c Merge pull request #176 from weblate/weblate-catima-catima
Translations update from Weblate
2021-03-29 20:58:07 +02:00
Samantaz Fox
9363a31a77 Translated using Weblate (French)
Currently translated at 100.0% (148 of 148 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-03-29 20:57:13 +02:00
Sylvia van Os
21f3c58e32 Merge pull request #175 from weblate/weblate-catima-catima
Translations update from Weblate
2021-03-29 17:43:08 +02:00
solokot
205f7bb59d Translated using Weblate (Russian)
Currently translated at 100.0% (148 of 148 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-03-29 17:26:59 +02:00
Heimen Stoffels
1cc0f11a5d Translated using Weblate (Dutch)
Currently translated at 100.0% (148 of 148 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-03-29 17:26:59 +02:00
Sylvia van Os
d2168f4d91 Merge pull request #174 from weblate/weblate-catima-catima
Translations update from Weblate
2021-03-28 15:57:57 +02:00
Sylvia van Os
ba46e2a0b8 Fix Weblate's incorrect escaping 2021-03-28 15:33:56 +02:00
solokot
6fb102d9b5 Translated using Weblate (Russian)
Currently translated at 100.0% (145 of 145 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-03-28 15:32:04 +02:00
J. Lavoie
83b3d5d31c Translated using Weblate (Spanish)
Currently translated at 86.2% (125 of 145 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/es/
2021-03-28 15:32:04 +02:00
Sylvia van Os
c93b7527ad Merge pull request #173 from TheLastProject/feature/import-local-image
Added ability to import a barcode from a local image
2021-03-28 15:31:59 +02:00
Sylvia van Os
07bfc95bf9 Update CHANGELOG 2021-03-28 15:25:23 +02:00
Sylvia van Os
d34554ac07 Split up code more 2021-03-28 15:20:54 +02:00
Sylvia van Os
63754798ef Fix error toasts not showing 2021-03-26 19:48:17 +01:00
Sylvia van Os
9ca4b7fbe1 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker into feature/import-local-image 2021-03-25 21:35:28 +01:00
Sylvia van Os
4d6021ad8c Merge pull request #171 from weblate/weblate-catima-catima
Translations update from Weblate
2021-03-25 21:32:26 +01:00
Alessandro Mandelli
f76df0d252 Translated using Weblate (Italian)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/it/
2021-03-25 21:29:58 +01:00
solokot
60a172813f Translated using Weblate (Russian)
Currently translated at 100.0% (145 of 145 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-03-25 21:29:58 +01:00
Heimen Stoffels
01025e862a Translated using Weblate (Dutch)
Currently translated at 100.0% (145 of 145 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-03-25 21:29:58 +01:00
Alessandro Mandelli
7e9c7db813 Translated using Weblate (Italian)
Currently translated at 100.0% (145 of 145 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-03-25 21:29:57 +01:00
J. Lavoie
d8b121f503 Translated using Weblate (Italian)
Currently translated at 100.0% (145 of 145 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-03-25 21:29:56 +01:00
J. Lavoie
79a143ebaf Translated using Weblate (French)
Currently translated at 100.0% (145 of 145 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-03-25 21:29:56 +01:00
J. Lavoie
0b78f36784 Translated using Weblate (German)
Currently translated at 100.0% (145 of 145 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-03-25 21:29:55 +01:00
Sylvia van Os
a4c24c6436 Fix multiline note cutoff 2021-03-25 00:00:44 +01:00
Sylvia van Os
f59f9ddec8 Make spotBugs happy 2021-03-24 21:33:36 +01:00
Sylvia van Os
d21f2d12c9 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-03-24 21:17:22 +01:00
Sylvia van Os
30971b7e85 Fix unit tests 2021-03-24 21:17:02 +01:00
Sylvia van Os
7ab9e0f8b0 Merge pull request #169 from weblate/weblate-catima-catima
Translations update from Weblate
2021-03-24 20:52:34 +01:00
J. Lavoie
6e63543cd1 Translated using Weblate (Italian)
Currently translated at 66.6% (2 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/it/
2021-03-24 20:36:16 +01:00
J. Lavoie
d2de5db792 Translated using Weblate (Korean)
Currently translated at 73.7% (101 of 137 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ko/
2021-03-24 20:36:16 +01:00
Heimen Stoffels
a1bb6e3bed Translated using Weblate (Dutch)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/nl/
2021-03-24 20:36:16 +01:00
J. Lavoie
e937fc60a7 Translated using Weblate (German)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/de/
2021-03-24 20:36:16 +01:00
J. Lavoie
1213ee99da Translated using Weblate (French)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/fr/
2021-03-24 20:36:16 +01:00
solokot
7dadce600b Translated using Weblate (Russian)
Currently translated at 100.0% (137 of 137 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-03-24 20:36:16 +01:00
Heimen Stoffels
bb1eae6f79 Translated using Weblate (Dutch)
Currently translated at 100.0% (137 of 137 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-03-24 20:36:16 +01:00
Allan Nordhøy
cf871e9606 Translated using Weblate (Norwegian Bokmål)
Currently translated at 97.8% (134 of 137 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-03-24 20:36:16 +01:00
J. Lavoie
0b92a12694 Translated using Weblate (Italian)
Currently translated at 100.0% (137 of 137 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-03-24 20:36:16 +01:00
J. Lavoie
7636c3648c Translated using Weblate (French)
Currently translated at 100.0% (137 of 137 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-03-24 20:36:16 +01:00
J. Lavoie
f62352cf05 Translated using Weblate (Spanish (Argentina))
Currently translated at 14.5% (20 of 137 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/es_AR/
2021-03-24 20:36:16 +01:00
J. Lavoie
8fbbfb137e Translated using Weblate (Greek)
Currently translated at 44.5% (61 of 137 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/el/
2021-03-24 20:36:16 +01:00
J. Lavoie
afb960257e Translated using Weblate (German)
Currently translated at 100.0% (137 of 137 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-03-24 20:36:16 +01:00
Sylvia van Os
b9e152e3c4 Add Fidme import support 2021-03-24 20:35:58 +01:00
Sylvia van Os
bd1d33867d Comply with Huawei's pedantic nonsense
https://twitter.com/SylvieLorxu/status/1374251557735256065
2021-03-23 21:01:56 +01:00
Sylvia van Os
eba1ed63a6 Release 1.11 2021-03-23 09:35:43 +01:00
Sylvia van Os
74fbdc7a5e Merge pull request #167 from weblate/weblate-catima-catima
Translations update from Weblate
2021-03-21 10:34:44 +01:00
solokot
ef61aaeac6 Translated using Weblate (Russian)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/ru/
2021-03-21 06:30:31 +01:00
Sylvia van Os
b5ee7d7a2d Merge pull request #165 from weblate/weblate-catima-catima
Translations update from Weblate
2021-03-17 18:10:38 +01:00
solokot
f51ad0295a Translated using Weblate (Russian)
Currently translated at 100.0% (138 of 138 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-03-17 15:18:07 +01:00
Sylvia van Os
d0d3289efa Merge remote-tracking branch 'weblate/master' 2021-03-15 19:39:52 +01:00
Sylvia van Os
353d8a7ecd Fix typo 2021-03-15 19:37:45 +01:00
solokot
d221969b5e Translated using Weblate (Russian)
Currently translated at 100.0% (138 of 138 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-03-15 19:34:03 +01:00
Heimen Stoffels
97553e9253 Translated using Weblate (Dutch)
Currently translated at 100.0% (138 of 138 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-03-15 19:34:02 +01:00
Sylvia van Os
11b839dabe Merge pull request #164 from weblate/weblate-catima-catima
Translations update from Weblate
2021-03-15 19:33:57 +01:00
Sylvia van Os
bc5252b2ef Update incorrect Weblate escaping 2021-03-15 19:33:44 +01:00
solokot
5a6b7944b1 Translated using Weblate (Russian)
Currently translated at 100.0% (138 of 138 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-03-15 14:13:24 +01:00
solokot
a9794daca5 Translated using Weblate (Russian)
Currently translated at 100.0% (138 of 138 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-03-15 13:54:15 +01:00
solokot
aee59550e4 Translated using Weblate (Russian)
Currently translated at 100.0% (138 of 138 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-03-15 13:33:14 +01:00
Sylvia van Os
dcc5e5921c Fix unit tests 2021-03-14 21:34:51 +01:00
Sylvia van Os
12df426dea Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-03-14 17:58:36 +01:00
Sylvia van Os
9b4085e955 Show privacy policy dialog on first start 2021-03-14 17:58:20 +01:00
Sylvia van Os
a4f7d4e131 Merge pull request #163 from weblate/weblate-catima-catima
Translations update from Weblate
2021-03-13 17:09:01 +01:00
Sylvia van Os
70b313021c Fix incorrect escaping from Weblate 2021-03-13 17:05:20 +01:00
Schi Ri
d1bdab5c66 Translated using Weblate (German)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/de/
2021-03-12 01:02:56 +01:00
Schi Ri
237405279f Translated using Weblate (German)
Currently translated at 100.0% (134 of 134 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-03-12 01:02:55 +01:00
Sylvia van Os
810fa5f621 Release 1.10 2021-03-07 14:38:38 +01:00
Sylvia van Os
64b709266b Merge pull request #162 from weblate/weblate-catima-catima
Translations update from Weblate
2021-03-07 14:22:02 +01:00
Heimen Stoffels
f9a66ba3a0 Translated using Weblate (Dutch)
Currently translated at 100.0% (134 of 134 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-03-07 14:21:20 +01:00
Allan Nordhøy
bfd36bae9f Translated using Weblate (Norwegian Bokmål)
Currently translated at 98.5% (132 of 134 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-03-07 14:21:20 +01:00
BMN
15116ab673 Translated using Weblate (Italian)
Currently translated at 100.0% (134 of 134 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-03-07 14:21:20 +01:00
Sylvia van Os
d49d019f63 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-03-06 00:13:35 +01:00
Sylvia van Os
ba9d1f3891 Make Loyalty Card Keychain app name translatable
Because, well, the app itself uses different names in different
countries
2021-03-05 23:56:38 +01:00
Sylvia van Os
01596d5637 Merge pull request #160 from weblate/weblate-catima-catima
Translations update from Weblate
2021-03-05 23:50:21 +01:00
Heimen Stoffels
9bd71d5694 Translated using Weblate (Dutch)
Currently translated at 100.0% (129 of 129 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-03-05 23:47:01 +01:00
Allan Nordhøy
6d1e7ee3bb Translated using Weblate (Norwegian Bokmål)
Currently translated at 98.4% (127 of 129 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-03-05 23:47:01 +01:00
Sylvia van Os
0ce71039f1 Split into screen on and lockscreen bypass 2021-03-05 23:44:27 +01:00
Sylvia van Os
a03b89bc93 Merge branch 'master' into feature/keep-screen-on 2021-03-05 23:36:43 +01:00
Sylvia van Os
156b720102 Merge pull request #159 from TheLastProject/feature/voucherVaultImport
Create Voucher Vault import code and test
2021-03-05 09:50:23 +01:00
Sylvia van Os
c13b5dda18 Make Spotbugs happy 2021-03-04 23:45:03 +01:00
Sylvia van Os
ffa39000f7 Add Voucher Vault import to UI 2021-03-04 23:37:53 +01:00
Sylvia van Os
40de4a8dc4 Fix date parsing 2021-03-02 21:10:14 +01:00
Sylvia van Os
db22703ec0 Create Voucher Vault import code and test 2021-03-01 23:02:03 +01:00
Sylvia van Os
8b59ef152a Merge pull request #158 from weblate/weblate-catima-catima
Translations update from Weblate
2021-02-25 19:55:27 +01:00
Sylvia van Os
01bd91ff66 Fix Weblate incorrectly escaping XML 2021-02-25 19:54:46 +01:00
Simone Dotto
b558d2178a Translated using Weblate (Italian)
Currently translated at 100.0% (128 of 128 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-02-25 14:50:29 +01:00
Sylvia van Os
efc8e6ae33 Fix parsing balance for countries using space as separator 2021-02-24 19:17:16 +01:00
Sylvia van Os
aaf481a82c Merge pull request #156 from weblate/weblate-catima-catima
Translations update from Weblate
2021-02-24 12:28:32 +01:00
Sylvia van Os
d03d96b194 Fix Weblate incorrect escaping 2021-02-24 12:28:04 +01:00
inesre
1c92787254 Translated using Weblate (Spanish)
Currently translated at 94.5% (121 of 128 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/es/
2021-02-24 00:50:28 +01:00
Sylvia van Os
bc808d3045 Really fix 2021-02-23 20:55:01 +01:00
Sylvia van Os
b265fadec3 Improve currency parsing logic 2021-02-23 18:49:45 +01:00
Sylvia van Os
d2f5cd05b5 Release 1.9 2021-02-22 23:01:24 +01:00
Sylvia van Os
2dba2c63a8 Merge pull request #153 from weblate/weblate-catima-catima
Translations update from Weblate
2021-02-22 22:00:02 +01:00
Sylvia van Os
3fffcdbd67 Fix incorrect escaping from Weblate 2021-02-22 21:56:20 +01:00
Sylvia van Os
6c53b8e9ca Fix incorrect escaping from Weblate 2021-02-22 21:54:13 +01:00
Heimen Stoffels
8ac2fe575c Translated using Weblate (Dutch)
Currently translated at 100.0% (128 of 128 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-02-22 21:50:28 +01:00
Allan Nordhøy
b4a992abf8 Translated using Weblate (Norwegian Bokmål)
Currently translated at 98.4% (126 of 128 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-02-22 21:50:27 +01:00
Sylvia van Os
45c71f01c2 Simplify balance parsing logic 2021-02-22 20:21:24 +01:00
Sylvia van Os
9f914d4943 Reorganize barcode tab of edit view 2021-02-21 18:36:34 +01:00
Sylvia van Os
3f25f99236 Merge pull request #151 from TheLastProject/feature/balance
WIP: Initial balance support
2021-02-21 18:15:06 +01:00
Sylvia van Os
d54f574558 Update screenshots 2021-02-21 18:10:56 +01:00
Sylvia van Os
0af1935d20 Switch tests to USD 2021-02-21 18:00:06 +01:00
Sylvia van Os
9a37d917f7 Fix test URL 2021-02-20 22:59:51 +01:00
Sylvia van Os
d4f62435ae Add tests for balance 2021-02-20 22:10:23 +01:00
Sylvia van Os
0baf1ba348 Fix tests 2021-02-20 18:15:58 +01:00
Sylvia van Os
4a568d02d3 Various fixes and consistency improvements 2021-02-16 22:22:51 +01:00
Sylvia van Os
1dc30ebaad Reformat balance after focus loss 2021-02-15 22:30:45 +01:00
Sylvia van Os
4fbedbc30c Fix parsing big numbers 2021-02-15 22:02:47 +01:00
Sylvia van Os
1675ebf1dc Set hasChanged when balance was edited 2021-02-15 21:58:22 +01:00
Sylvia van Os
2d6dfdcfdf Initial balance support 2021-02-14 23:32:07 +01:00
Miha Frangež
bacb5b97ec Added ability to import a barcode from a local image 2021-02-14 19:09:33 +01:00
Miha Frange?
940be02851 Added option to keep the screen on 2021-02-14 17:29:51 +01:00
Sylvia van Os
34f8c830fd Release Catima 1.8.1 2021-02-12 18:25:03 +01:00
Sylvia van Os
ee7f5c0498 Fix crash on Android before 7 2021-02-12 12:54:50 +01:00
Sylvia van Os
9a5c3bc661 Merge pull request #145 from weblate/weblate-catima-catima
Translations update from Weblate
2021-02-09 18:11:05 +01:00
Michalis
db9ec7daea Translated using Weblate (Greek)
Currently translated at 47.5% (58 of 122 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/el/
2021-02-09 17:50:33 +01:00
Sylvia van Os
16b683fe9d Merge pull request #144 from weblate/weblate-catima-catima
Translations update from Weblate
2021-02-06 01:44:08 +01:00
Michalis
0e10c644d8 Translated using Weblate (Greek)
Currently translated at 47.5% (58 of 122 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/el/
2021-02-06 01:42:03 +01:00
Sylvia van Os
fd283a5e06 Fix wrong name in privacy policy 2021-02-03 20:35:06 +01:00
Sylvia van Os
e406320f17 Add Huawei AppGallery 2021-02-03 20:29:36 +01:00
Sylvia van Os
40d36ec8fb Release 1.8 2021-01-28 19:45:48 +01:00
Sylvia van Os
99a3849942 Fix missing lookup table update for groups update 2021-01-28 19:31:46 +01:00
Sylvia van Os
26a6a786dc Fix button colours 2021-01-28 17:59:31 +01:00
Sylvia van Os
55a5884852 Fix loyalty card view title colour in landscape mode 2021-01-27 22:21:27 +01:00
Sylvia van Os
c20ce1e555 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-01-27 21:43:59 +01:00
Sylvia van Os
a90899d41e Fix crash on no barcode 2021-01-27 21:42:57 +01:00
Sylvia van Os
413d479ea0 Merge pull request #141 from weblate/weblate-catima-catima
Translations update from Weblate
2021-01-27 21:36:19 +01:00
Heimen Stoffels
8ff452cfd2 Translated using Weblate (Dutch)
Currently translated at 100.0% (122 of 122 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-01-27 21:35:56 +01:00
Allan Nordhøy
54c279ec89 Translated using Weblate (Norwegian Bokmål)
Currently translated at 98.3% (120 of 122 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-01-27 21:35:56 +01:00
Sylvia van Os
000d7722da Fix tests 2021-01-27 21:35:37 +01:00
Sylvia van Os
0bb0df21e4 Make header small in landscape mode 2021-01-27 21:25:34 +01:00
Sylvia van Os
0dcfc11c53 Fix unit tests 2021-01-26 22:50:46 +01:00
Sylvia van Os
58f8a6a929 Fix bottomsheet misplacing after fullscreen change 2021-01-26 22:39:25 +01:00
Sylvia van Os
6a80c633a8 Update CHANGELOG 2021-01-25 23:55:57 +01:00
Sylvia van Os
031bfd94e0 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-01-25 23:54:55 +01:00
Sylvia van Os
45443abaf9 UI hints for barcode to top and scaling 2021-01-25 23:53:33 +01:00
Sylvia van Os
b81da59bcc Merge pull request #139 from weblate/weblate-catima-catima
Translations update from Weblate
2021-01-18 20:36:59 +01:00
Sylvia van Os
86f3530d95 Fix incorrect XML escaping 2021-01-18 20:36:30 +01:00
Sylvia van Os
863a378bf0 Fix incorrect XML escaping 2021-01-18 20:35:39 +01:00
Heimen Stoffels
a9d81b8321 Translated using Weblate (Dutch)
Currently translated at 100.0% (120 of 120 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-01-18 20:32:14 +01:00
Allan Nordhøy
46865f349b Translated using Weblate (Norwegian Bokmål)
Currently translated at 98.3% (118 of 120 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-01-18 20:32:13 +01:00
Sylvia van Os
1d1109d665 Fix crash in edit view 2021-01-18 19:45:45 +01:00
109 changed files with 3876 additions and 1206 deletions

View File

@@ -13,6 +13,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: gradle/wrapper-validation-action@v1
- name: set up JDK 1.8
uses: actions/setup-java@v1
with:
@@ -27,7 +28,7 @@ jobs:
run: ./gradlew spotbugsRelease
- name: Archive test results
if: always()
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v2
with:
name: test-results
path: app/build/reports

View File

@@ -1,6 +1,81 @@
# Changelog
## Unreleased
## v1.13 (2021-04-10)
Changes:
- Add option to set a separate barcode value from card ID
- Simplify font sizing configuration
- Several small UI fixes
- Use letter icon for shortcuts too
- Always show all barcode types in manual entry
- Remove privacy policy first start dialog
## v1.12 (2021-03-30)
Changes:
- Support importing [Fidme](https://play.google.com/store/apps/details?id=fr.snapp.fidme) exports
- Allow importing a card from a picture stored in the user's Android gallery
- Fix multiline note cutoff
- Change "Thank you" text on privacy dialog to "Accept" because Huawei is overly pedantic
## v1.11 (2021-03-21)
Changes:
- Add privacy policy dialog on first start (required by Huawei)
## v1.10 (2021-03-07)
Changes:
- Support importing [Voucher Vault](https://github.com/tim-smart/vouchervault/) exports
- Option to keep the screen on while viewing a loyalty card
- Option to suspend the lock screen while viewing a loyalty card
## v1.9.2 (2021-02-24)
Changes:
- Fix parsing balance for countries using space as separator
## v1.9.1 (2021-02-23)
Changes:
- Improve balance parsing logic
- Fix currency decimal display on main screen
## v1.9 (2021-02-22)
Changes:
- Add balance support
- Reorganize barcode tab of edit view
## v1.8.1 (2021-02-12)
Changes:
- Fix Crash on versions before Android 7
## v1.8 (2021-01-28)
Changes:
- Add support for scaling the barcode when moving to top to fit even more small scanners
- Fix bottom sheet jumping after switching to fullscreen
- Make header in loyalty card view small in landscape mode
- Fix cards not staying in group when group gets renamed
## v1.7.1 (2021-01-18)
Changes:
- Fix crash on switching to barcode tab in edit view if there is no barcode
## v1.7.0 (2021-01-18)
Changes:

View File

@@ -18,8 +18,8 @@ android {
applicationId "me.hackerchick.catima"
minSdkVersion 19
targetSdkVersion 29
versionCode 55
versionName "1.7.0"
versionCode 67
versionName "1.13"
vectorDrawables.useSupportLibrary true
}
@@ -67,20 +67,24 @@ dependencies {
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.preference:preference:1.1.1'
implementation 'com.google.android.material:material:1.2.1'
implementation 'io.wcm.tooling.spotbugs:io.wcm.tooling.spotbugs.annotations:1.0.0'
implementation 'com.google.android.material:material:1.3.0'
// Third-party
implementation 'com.journeyapps:zxing-android-embedded:4.1.0@aar'
implementation 'com.google.zxing:core:3.4.1'
// Do not upgrade past 3.3.3! Causes a crash on versions before Android Nougat
//noinspection GradleDependency
implementation 'com.google.zxing:core:3.3.3'
implementation 'org.apache.commons:commons-csv:1.8'
implementation 'com.jaredrummler:colorpicker:1.1.0'
implementation 'com.google.guava:guava:30.1-jre'
implementation 'com.github.invissvenska:NumberPickerPreference:1.0.1'
implementation 'com.google.guava:guava:30.1.1-jre'
implementation 'com.github.invissvenska:NumberPickerPreference:1.0.2'
// SpotBugs
implementation 'io.wcm.tooling.spotbugs:io.wcm.tooling.spotbugs.annotations:1.0.0'
// Testing
testImplementation 'androidx.test:core:1.0.0'
testImplementation 'junit:junit:4.13.1'
testImplementation 'androidx.test:core:1.3.0'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.robolectric:robolectric:4.4'
}

View File

@@ -49,7 +49,6 @@
<activity
android:name=".LoyaltyCardViewActivity"
android:theme="@style/AppTheme.NoActionBar"
android:label=""
android:windowSoftInputMode="stateHidden"
android:exported="true"/>
<activity

View File

@@ -8,10 +8,9 @@ import android.util.Log;
import android.view.MenuItem;
import android.widget.TextView;
import com.google.common.collect.ImmutableMap;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Map;
import java.util.List;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
@@ -35,30 +34,27 @@ public class AboutActivity extends AppCompatActivity
actionBar.setDisplayHomeAsUpEnabled(true);
}
final Map<String, String> USED_LIBRARIES = new ImmutableMap.Builder<String, String>()
.put("Commons CSV", "https://commons.apache.org/proper/commons-csv/")
.put("Guava", "https://github.com/google/guava")
.put("ZXing", "https://github.com/zxing/zxing")
.put("ZXing Android Embedded", "https://github.com/journeyapps/zxing-android-embedded")
.put("Color Picker", "https://github.com/jaredrummler/ColorPicker")
.put("NumberPickerPreference", "https://github.com/invissvenska/NumberPickerPreference")
.build();
final List<ThirdPartyInfo> USED_LIBRARIES = new ArrayList<>();
USED_LIBRARIES.add(new ThirdPartyInfo("Commons CSV", "https://commons.apache.org/proper/commons-csv/", "Apache 2.0"));
USED_LIBRARIES.add(new ThirdPartyInfo("Guava", "https://github.com/google/guava", "Apache 2.0"));
USED_LIBRARIES.add(new ThirdPartyInfo("ZXing", "https://github.com/zxing/zxing", "Apache 2.0"));
USED_LIBRARIES.add(new ThirdPartyInfo("ZXing Android Embedded", "https://github.com/journeyapps/zxing-android-embedded", "Apache 2.0"));
USED_LIBRARIES.add(new ThirdPartyInfo("Color Picker", "https://github.com/jaredrummler/ColorPicker", "Apache 2.0"));
USED_LIBRARIES.add(new ThirdPartyInfo("NumberPickerPreference", "https://github.com/invissvenska/NumberPickerPreference", "GNU LGPL 3.0"));
final Map<String, String> USED_ASSETS = ImmutableMap.of
(
"Android icons", "https://www.apache.org/licenses/LICENSE-2.0.txt"
);
final List<ThirdPartyInfo> USED_ASSETS = new ArrayList<>();
USED_ASSETS.add(new ThirdPartyInfo("Android icons", "https://fonts.google.com/icons?selected=Material+Icons", "Apache 2.0"));
StringBuilder libs = new StringBuilder().append("<br/>");
for (Map.Entry<String, String> entry : USED_LIBRARIES.entrySet())
for (ThirdPartyInfo entry : USED_LIBRARIES)
{
libs.append("<br/><a href=\"").append(entry.getValue()).append("\">").append(entry.getKey()).append("</a><br/>");
libs.append("<br/><a href=\"").append(entry.url()).append("\">").append(entry.name()).append("</a> (").append(entry.license()).append(")<br/>");
}
StringBuilder resources = new StringBuilder().append("<br/>");
for (Map.Entry<String, String> entry : USED_ASSETS.entrySet())
for (ThirdPartyInfo entry : USED_ASSETS)
{
resources.append("<br/><a href=\"").append(entry.getValue()).append("\">").append(entry.getKey()).append("</a><br/>");
resources.append("<br/><a href=\"").append(entry.url()).append("\">").append(entry.name()).append("</a> (").append(entry.license()).append(")<br/>");
}
String appName = getString(R.string.app_name);

View File

@@ -1,6 +1,8 @@
package protect.card_locker;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.os.AsyncTask;
import android.util.Log;
import android.view.View;
@@ -23,6 +25,9 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
{
private static final String TAG = "Catima";
private static final int IS_VALID = 999;
private boolean isSuccesful;
// When drawn in a smaller window 1D barcodes for some reason end up
// squished, whereas 2D barcodes look fine.
private static final int MAX_WIDTH_1D = 1500;
@@ -30,14 +35,20 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
private final WeakReference<ImageView> imageViewReference;
private final WeakReference<TextView> textViewReference;
private final String cardId;
private String cardId;
private final BarcodeFormat format;
private final int imageHeight;
private final int imageWidth;
private final boolean showFallback;
private final Runnable callback;
BarcodeImageWriterTask(ImageView imageView, String cardIdString,
BarcodeFormat barcodeFormat, TextView textView)
BarcodeFormat barcodeFormat, TextView textView,
boolean showFallback, Runnable callback)
{
isSuccesful = true;
this.callback = callback;
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<>(imageView);
textViewReference = new WeakReference<>(textView);
@@ -59,11 +70,8 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
double ratio = (double)MAX_WIDTH / (double)imageView.getWidth();
imageHeight = (int)(imageView.getHeight() * ratio);
}
}
BarcodeImageWriterTask(ImageView imageView, String cardIdString, BarcodeFormat barcodeFormat)
{
this(imageView, cardIdString, barcodeFormat, null);
this.showFallback = showFallback;
}
private int getMaxWidth(BarcodeFormat format)
@@ -96,7 +104,41 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
}
}
public Bitmap doInBackground(Void... params)
private String getFallbackString(BarcodeFormat format)
{
switch(format)
{
// 2D barcodes
case AZTEC:
return "AZTEC";
case DATA_MATRIX:
return "DATA_MATRIX";
case PDF_417:
return "PDF_417";
case QR_CODE:
return "QR_CODE";
// 1D barcodes:
case CODABAR:
return "C0C";
case CODE_39:
return "CODE_39";
case CODE_128:
return "CODE_128";
case EAN_8:
return "32123456";
case EAN_13:
return "5901234123457";
case ITF:
return "1003";
case UPC_A:
return "123456789012";
default:
throw new IllegalArgumentException("No fallback known for this barcode type");
}
}
private Bitmap generate()
{
if (cardId.isEmpty())
{
@@ -165,13 +207,30 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
catch(OutOfMemoryError e)
{
Log.w(TAG, "Insufficient memory to render barcode, "
+ imageWidth + "x" + imageHeight + ", " + format.name()
+ ", length=" + cardId.length(), e);
+ imageWidth + "x" + imageHeight + ", " + format.name()
+ ", length=" + cardId.length(), e);
}
return null;
}
public Bitmap doInBackground(Void... params)
{
Bitmap bitmap = generate();
if (bitmap == null) {
isSuccesful = false;
if (showFallback) {
Log.i(TAG, "Barcode generation failed, generating fallback...");
cardId = getFallbackString(format);
bitmap = generate();
}
}
return bitmap;
}
protected void onPostExecute(Bitmap result)
{
Log.i(TAG, "Finished generating barcode image of type " + format + ": " + cardId);
@@ -182,6 +241,8 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
return;
}
imageView.setTag(isSuccesful);
imageView.setImageBitmap(result);
TextView textView = textViewReference.get();
@@ -190,6 +251,12 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
Log.i(TAG, "Displaying barcode");
imageView.setVisibility(View.VISIBLE);
if (isSuccesful) {
imageView.setColorFilter(null);
} else {
imageView.setColorFilter(Color.LTGRAY, PorterDuff.Mode.LIGHTEN);
}
if (textView != null) {
textView.setVisibility(View.VISIBLE);
textView.setText(format.name());
@@ -203,5 +270,9 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
textView.setVisibility(View.GONE);
}
}
if (callback != null) {
callback.run();
}
}
}

View File

@@ -17,6 +17,7 @@ import android.view.ViewTreeObserver;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.google.common.collect.ImmutableMap;
import com.google.zxing.BarcodeFormat;
@@ -104,20 +105,7 @@ public class BarcodeSelectorActivity extends AppCompatActivity
{
Log.d(TAG, "Entered text: " + s);
// Stop any async tasks which may not have been started yet
for(AsyncTask task : barcodeGeneratorTasks)
{
task.cancel(false);
}
barcodeGeneratorTasks.clear();
// Update barcodes
for(String key : barcodeViewMap.keySet())
{
ImageView image = findViewById(barcodeViewMap.get(key).first);
TextView text = findViewById(barcodeViewMap.get(key).second);
createBarcodeOption(image, key, s.toString(), text);
}
generateBarcodes(s.toString());
View noBarcodeButtonView = findViewById(R.id.noBarcode);
setButtonListener(noBarcodeButtonView, s.toString());
@@ -137,6 +125,25 @@ public class BarcodeSelectorActivity extends AppCompatActivity
if(initialCardId != null)
{
cardId.setText(initialCardId);
} else {
generateBarcodes("");
}
}
private void generateBarcodes(String value) {
// Stop any async tasks which may not have been started yet
for(AsyncTask task : barcodeGeneratorTasks)
{
task.cancel(false);
}
barcodeGeneratorTasks.clear();
// Update barcodes
for(Map.Entry<String, Pair<Integer, Integer>> entry : barcodeViewMap.entrySet())
{
ImageView image = findViewById(entry.getValue().first);
TextView text = findViewById(entry.getValue().second);
createBarcodeOption(image, entry.getKey(), value, text);
}
}
@@ -171,6 +178,12 @@ public class BarcodeSelectorActivity extends AppCompatActivity
public void onClick(View v)
{
Log.d(TAG, "Selected barcode type " + formatType);
if (!((boolean) image.getTag())) {
Toast.makeText(BarcodeSelectorActivity.this, getString(R.string.wrongValueForBarcodeType), Toast.LENGTH_LONG).show();
return;
}
Intent result = new Intent();
result.putExtra(BARCODE_FORMAT, formatType);
result.putExtra(BARCODE_CONTENTS, cardId);
@@ -193,7 +206,7 @@ public class BarcodeSelectorActivity extends AppCompatActivity
image.getViewTreeObserver().removeOnGlobalLayoutListener(this);
Log.d(TAG, "Generating barcode for type " + formatType);
BarcodeImageWriterTask task = new BarcodeImageWriterTask(image, cardId, format, text);
BarcodeImageWriterTask task = new BarcodeImageWriterTask(image, cardId, format, text, true, null);
barcodeGeneratorTasks.add(task);
task.execute();
}
@@ -202,7 +215,7 @@ public class BarcodeSelectorActivity extends AppCompatActivity
else
{
Log.d(TAG, "Generating barcode for type " + formatType);
BarcodeImageWriterTask task = new BarcodeImageWriterTask(image, cardId, format, text);
BarcodeImageWriterTask task = new BarcodeImageWriterTask(image, cardId, format, text, true, null);
barcodeGeneratorTasks.add(task);
task.execute();
}

View File

@@ -2,16 +2,22 @@ package protect.card_locker;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Parcelable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.pm.ShortcutInfoCompat;
import androidx.core.content.pm.ShortcutManagerCompat;
import androidx.core.graphics.drawable.IconCompat;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
/**
* The configuration screen for creating a shortcut.
*/
@@ -32,6 +38,10 @@ public class CardShortcutConfigure extends AppCompatActivity
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);
// If there are no cards, bail
@@ -69,11 +79,15 @@ public class CardShortcutConfigure extends AppCompatActivity
bundle.putBoolean("view", true);
shortcutIntent.putExtras(bundle);
Parcelable icon = Intent.ShortcutIconResource.fromContext(CardShortcutConfigure.this, R.mipmap.ic_launcher);
Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, loyaltyCard.store);
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, icon);
Bitmap icon = Utils.generateIcon(CardShortcutConfigure.this, loyaltyCard.store, loyaltyCard.headerColor, true).getLetterTile();
ShortcutInfoCompat shortcutInfo = new ShortcutInfoCompat.Builder(CardShortcutConfigure.this, String.valueOf(loyaltyCard.id))
.setIntent(shortcutIntent)
.setIcon(IconCompat.createWithAdaptiveBitmap(icon))
.setShortLabel(loyaltyCard.store)
.build();
Intent intent = ShortcutManagerCompat.createShortcutResultIntent(CardShortcutConfigure.this, shortcutInfo);
setResult(RESULT_OK, intent);
finish();

View File

@@ -4,9 +4,14 @@ import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.graphics.Color;
import com.google.zxing.BarcodeFormat;
import java.math.BigDecimal;
import java.util.Currency;
import java.util.Date;
import java.util.ArrayList;
import java.util.List;
@@ -15,30 +20,33 @@ 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 = 7;
public static final int DATABASE_VERSION = 10;
static class LoyaltyCardDbGroups
public static class LoyaltyCardDbGroups
{
public static final String TABLE = "groups";
public static final String ID = "_id";
public static final String ORDER = "orderId";
}
static class LoyaltyCardDbIds
public static class LoyaltyCardDbIds
{
public static final String TABLE = "cards";
public static final String ID = "_id";
public static final String STORE = "store";
public static final String EXPIRY = "expiry";
public static final String BALANCE = "balance";
public static final String BALANCE_TYPE = "balancetype";
public static final String NOTE = "note";
public static final String HEADER_COLOR = "headercolor";
public static final String HEADER_TEXT_COLOR = "headertextcolor";
public static final String CARD_ID = "cardid";
public static final String BARCODE_ID = "barcodeid";
public static final String BARCODE_TYPE = "barcodetype";
public static final String STAR_STATUS = "starstatus";
}
static class LoyaltyCardDbIdsGroups
public static class LoyaltyCardDbIdsGroups
{
public static final String TABLE = "cardsGroups";
public static final String cardID = "cardId";
@@ -59,15 +67,18 @@ public class DBHelper extends SQLiteOpenHelper
LoyaltyCardDbGroups.ORDER + " INTEGER DEFAULT '0')");
// create table for cards
// Balance is TEXT and not REAL to be able to store a BigDecimal without precision loss
db.execSQL("create table " + LoyaltyCardDbIds.TABLE + "(" +
LoyaltyCardDbIds.ID + " INTEGER primary key autoincrement," +
LoyaltyCardDbIds.STORE + " TEXT not null," +
LoyaltyCardDbIds.NOTE + " TEXT not null," +
LoyaltyCardDbIds.EXPIRY + " INTEGER," +
LoyaltyCardDbIds.BALANCE + " TEXT not null DEFAULT '0'," +
LoyaltyCardDbIds.BALANCE_TYPE + " TEXT," +
LoyaltyCardDbIds.HEADER_COLOR + " INTEGER," +
LoyaltyCardDbIds.HEADER_TEXT_COLOR + " INTEGER," +
LoyaltyCardDbIds.CARD_ID + " TEXT not null," +
LoyaltyCardDbIds.BARCODE_TYPE + " TEXT not null," +
LoyaltyCardDbIds.BARCODE_ID + " TEXT," +
LoyaltyCardDbIds.BARCODE_TYPE + " TEXT," +
LoyaltyCardDbIds.STAR_STATUS + " INTEGER DEFAULT '0' )");
// create associative table for cards in groups
@@ -127,58 +138,198 @@ public class DBHelper extends SQLiteOpenHelper
db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE
+ " ADD COLUMN " + LoyaltyCardDbIds.EXPIRY + " INTEGER");
}
if(oldVersion < 8 && newVersion >= 8)
{
db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE
+ " ADD COLUMN " + LoyaltyCardDbIds.BALANCE + " TEXT not null DEFAULT '0'");
db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE
+ " ADD COLUMN " + LoyaltyCardDbIds.BALANCE_TYPE + " TEXT");
}
if(oldVersion < 9 && newVersion >= 9)
{
db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE
+ " ADD COLUMN " + LoyaltyCardDbIds.BARCODE_ID + " TEXT");
}
if(oldVersion < 10 && newVersion >= 10)
{
// SQLite doesn't support modify column
// So we need to create a temp column to make barcode type nullable
// Let's drop header text colour too while we're at it
// https://www.sqlite.org/faq.html#q11
db.beginTransaction();
db.execSQL("CREATE TEMPORARY TABLE tmp (" +
LoyaltyCardDbIds.ID + " INTEGER primary key autoincrement," +
LoyaltyCardDbIds.STORE + " TEXT not null," +
LoyaltyCardDbIds.NOTE + " TEXT not null," +
LoyaltyCardDbIds.EXPIRY + " INTEGER," +
LoyaltyCardDbIds.BALANCE + " TEXT not null DEFAULT '0'," +
LoyaltyCardDbIds.BALANCE_TYPE + " TEXT," +
LoyaltyCardDbIds.HEADER_COLOR + " INTEGER," +
LoyaltyCardDbIds.CARD_ID + " TEXT not null," +
LoyaltyCardDbIds.BARCODE_ID + " TEXT," +
LoyaltyCardDbIds.BARCODE_TYPE + " TEXT," +
LoyaltyCardDbIds.STAR_STATUS + " INTEGER DEFAULT '0' )");
db.execSQL("INSERT INTO tmp (" +
LoyaltyCardDbIds.ID + " ," +
LoyaltyCardDbIds.STORE + " ," +
LoyaltyCardDbIds.NOTE + " ," +
LoyaltyCardDbIds.EXPIRY + " ," +
LoyaltyCardDbIds.BALANCE + " ," +
LoyaltyCardDbIds.BALANCE_TYPE + " ," +
LoyaltyCardDbIds.HEADER_COLOR + " ," +
LoyaltyCardDbIds.CARD_ID + " ," +
LoyaltyCardDbIds.BARCODE_ID + " ," +
LoyaltyCardDbIds.BARCODE_TYPE + " ," +
LoyaltyCardDbIds.STAR_STATUS + ")" +
" SELECT " +
LoyaltyCardDbIds.ID + " ," +
LoyaltyCardDbIds.STORE + " ," +
LoyaltyCardDbIds.NOTE + " ," +
LoyaltyCardDbIds.EXPIRY + " ," +
LoyaltyCardDbIds.BALANCE + " ," +
LoyaltyCardDbIds.BALANCE_TYPE + " ," +
LoyaltyCardDbIds.HEADER_COLOR + " ," +
LoyaltyCardDbIds.CARD_ID + " ," +
LoyaltyCardDbIds.BARCODE_ID + " ," +
" NULLIF(" + LoyaltyCardDbIds.BARCODE_TYPE + ",'') ," +
LoyaltyCardDbIds.STAR_STATUS +
" FROM " + LoyaltyCardDbIds.TABLE);
db.execSQL("DROP TABLE " + LoyaltyCardDbIds.TABLE);
db.execSQL("create table " + LoyaltyCardDbIds.TABLE + "(" +
LoyaltyCardDbIds.ID + " INTEGER primary key autoincrement," +
LoyaltyCardDbIds.STORE + " TEXT not null," +
LoyaltyCardDbIds.NOTE + " TEXT not null," +
LoyaltyCardDbIds.EXPIRY + " INTEGER," +
LoyaltyCardDbIds.BALANCE + " TEXT not null DEFAULT '0'," +
LoyaltyCardDbIds.BALANCE_TYPE + " TEXT," +
LoyaltyCardDbIds.HEADER_COLOR + " INTEGER," +
LoyaltyCardDbIds.CARD_ID + " TEXT not null," +
LoyaltyCardDbIds.BARCODE_ID + " TEXT," +
LoyaltyCardDbIds.BARCODE_TYPE + " TEXT," +
LoyaltyCardDbIds.STAR_STATUS + " INTEGER DEFAULT '0' )");
db.execSQL("INSERT INTO " + LoyaltyCardDbIds.TABLE + "(" +
LoyaltyCardDbIds.ID + " ," +
LoyaltyCardDbIds.STORE + " ," +
LoyaltyCardDbIds.NOTE + " ," +
LoyaltyCardDbIds.EXPIRY + " ," +
LoyaltyCardDbIds.BALANCE + " ," +
LoyaltyCardDbIds.BALANCE_TYPE + " ," +
LoyaltyCardDbIds.HEADER_COLOR + " ," +
LoyaltyCardDbIds.CARD_ID + " ," +
LoyaltyCardDbIds.BARCODE_ID + " ," +
LoyaltyCardDbIds.BARCODE_TYPE + " ," +
LoyaltyCardDbIds.STAR_STATUS + ")" +
" SELECT " +
LoyaltyCardDbIds.ID + " ," +
LoyaltyCardDbIds.STORE + " ," +
LoyaltyCardDbIds.NOTE + " ," +
LoyaltyCardDbIds.EXPIRY + " ," +
LoyaltyCardDbIds.BALANCE + " ," +
LoyaltyCardDbIds.BALANCE_TYPE + " ," +
LoyaltyCardDbIds.HEADER_COLOR + " ," +
LoyaltyCardDbIds.CARD_ID + " ," +
LoyaltyCardDbIds.BARCODE_ID + " ," +
LoyaltyCardDbIds.BARCODE_TYPE + " ," +
LoyaltyCardDbIds.STAR_STATUS +
" FROM tmp");
db.execSQL("DROP TABLE tmp");
db.setTransactionSuccessful();
db.endTransaction();
}
}
public long insertLoyaltyCard(final String store, final String note, final Date expiry,
final String cardId, final String barcodeType,
final Integer headerColor, final int starStatus)
final BigDecimal balance, final Currency balanceType,
final String cardId, final String barcodeId,
final BarcodeFormat barcodeType, final Integer headerColor,
final int starStatus)
{
SQLiteDatabase db = getWritableDatabase();
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_TYPE, barcodeType);
contentValues.put(LoyaltyCardDbIds.BARCODE_ID, barcodeId);
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType != null ? barcodeType.toString() : null);
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
contentValues.put(LoyaltyCardDbIds.HEADER_TEXT_COLOR, Color.WHITE);
contentValues.put(LoyaltyCardDbIds.STAR_STATUS, starStatus);
final long newId = db.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
return newId;
}
public boolean insertLoyaltyCard(final SQLiteDatabase db, final String store,
final String note, final Date expiry, final BigDecimal balance,
final Currency balanceType, final String cardId,
final String barcodeId, final BarcodeFormat barcodeType,
final Integer headerColor, final int starStatus)
{
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.toString() : null);
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
contentValues.put(LoyaltyCardDbIds.STAR_STATUS,starStatus);
final long newId = db.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
return (newId != -1);
}
public boolean insertLoyaltyCard(final SQLiteDatabase db, final int id, final String store,
final String note, final Date expiry, final String cardId,
final String barcodeType, final Integer headerColor,
final int starStatus)
final String note, final Date expiry, final BigDecimal balance,
final Currency balanceType, final String cardId,
final String barcodeId, final BarcodeFormat barcodeType,
final Integer headerColor, final int starStatus)
{
ContentValues contentValues = new ContentValues();
contentValues.put(LoyaltyCardDbIds.ID, id);
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_TYPE, barcodeType);
contentValues.put(LoyaltyCardDbIds.BARCODE_ID, barcodeId);
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType != null ? barcodeType.toString() : null);
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
contentValues.put(LoyaltyCardDbIds.HEADER_TEXT_COLOR, Color.WHITE);
contentValues.put(LoyaltyCardDbIds.STAR_STATUS,starStatus);
final long newId = db.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
return (newId != -1);
}
public boolean updateLoyaltyCard(final int id, final String store, final String note,
final Date expiry, final String cardId,
final String barcodeType, final Integer headerColor)
final Date expiry, final BigDecimal balance,
final Currency balanceType, final String cardId,
final String barcodeId, final BarcodeFormat barcodeType,
final Integer headerColor)
{
SQLiteDatabase db = getWritableDatabase();
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_TYPE, barcodeType);
contentValues.put(LoyaltyCardDbIds.BARCODE_ID, barcodeId);
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType != null ? barcodeType.toString() : null);
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
contentValues.put(LoyaltyCardDbIds.HEADER_TEXT_COLOR, Color.WHITE);
int rowsUpdated = db.update(LoyaltyCardDbIds.TABLE, contentValues,
LoyaltyCardDbIds.ID + "=?",
new String[]{Integer.toString(id)});
@@ -532,33 +683,66 @@ public class DBHelper extends SQLiteOpenHelper
{
if (newName.isEmpty()) return false;
boolean success = false;
SQLiteDatabase db = getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(LoyaltyCardDbGroups.ID, newName);
ContentValues groupContentValues = new ContentValues();
groupContentValues.put(LoyaltyCardDbGroups.ID, newName);
ContentValues lookupContentValues = new ContentValues();
lookupContentValues.put(LoyaltyCardDbIdsGroups.groupID, newName);
db.beginTransaction();
try {
int rowsUpdated = db.update(LoyaltyCardDbGroups.TABLE, contentValues,
// Update group name
int groupsChanged = db.update(LoyaltyCardDbGroups.TABLE, groupContentValues,
LoyaltyCardDbGroups.ID + "=?",
new String[]{groupName});
return (rowsUpdated == 1);
} catch (android.database.sqlite.SQLiteConstraintException _e) {
return false;
// Also update lookup tables
db.update(LoyaltyCardDbIdsGroups.TABLE, lookupContentValues,
LoyaltyCardDbIdsGroups.groupID + "=?",
new String[]{groupName});
if (groupsChanged == 1) {
db.setTransactionSuccessful();
success = true;
}
} catch (SQLiteException e) {
} finally {
db.endTransaction();
}
return success;
}
public boolean deleteGroup(final String groupName)
{
boolean success = false;
SQLiteDatabase db = getWritableDatabase();
// Delete group
int rowsDeleted = db.delete(LoyaltyCardDbGroups.TABLE,
LoyaltyCardDbGroups.ID + " = ? ",
new String[]{groupName});
// And delete lookup table entries associated with this group
db.delete(LoyaltyCardDbIdsGroups.TABLE,
LoyaltyCardDbIdsGroups.groupID + " = ? ",
new String[]{groupName});
db.beginTransaction();
try {
// Delete group
int groupsDeleted = db.delete(LoyaltyCardDbGroups.TABLE,
LoyaltyCardDbGroups.ID + " = ? ",
new String[]{groupName});
return (rowsDeleted == 1);
// And delete lookup table entries associated with this group
db.delete(LoyaltyCardDbIdsGroups.TABLE,
LoyaltyCardDbIdsGroups.groupID + " = ? ",
new String[]{groupName});
if (groupsDeleted == 1) {
db.setTransactionSuccessful();
success = true;
}
} finally {
db.endTransaction();
}
return success;
}
public int getGroupCardCount(final String groupName)

View File

@@ -2,7 +2,8 @@ package protect.card_locker;
public enum DataFormat
{
CSV,
Catima,
Fidme,
VoucherVault
;
}

View File

@@ -1,19 +0,0 @@
package protect.card_locker;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* Interface for a class which can import the contents of a stream
* into the database.
*/
public interface DatabaseImporter
{
/**
* Import data from the input stream in a given format into
* the database.
* @throws IOException
* @throws FormatException
*/
void importData(DBHelper db, InputStreamReader input) throws IOException, FormatException, InterruptedException;
}

View File

@@ -50,7 +50,7 @@ class GroupCursorAdapter extends CursorAdapter
countField.setText(context.getResources().getQuantityString(R.plurals.groupCardCount, groupCardCount, groupCardCount));
nameField.setTextSize(settings.getCardTitleListFontSize());
countField.setTextSize(settings.getCardNoteListFontSize());
nameField.setTextSize(settings.getFontSizeMax(settings.getMediumFont()));
countField.setTextSize(settings.getFontSizeMax(settings.getSmallFont()));
}
}

View File

@@ -16,6 +16,7 @@ import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.provider.ContactsContract;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
@@ -28,6 +29,7 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.DateFormat;
public class ImportExportActivity extends AppCompatActivity
{
@@ -35,10 +37,14 @@ public class ImportExportActivity extends AppCompatActivity
private static final int PERMISSIONS_EXTERNAL_STORAGE = 1;
private static final int CHOOSE_EXPORT_LOCATION = 2;
private static final int CHOOSE_EXPORTED_FILE = 3;
private static final int IMPORT = 3;
private ImportExportTask importExporter;
private String importAlertTitle;
private String importAlertMessage;
private DataFormat importDataFormat;
@Override
protected void onCreate(Bundle savedInstanceState)
{
@@ -93,7 +99,7 @@ public class ImportExportActivity extends AppCompatActivity
@Override
public void onClick(View v)
{
chooseFileWithIntent(intentGetContentAction, CHOOSE_EXPORTED_FILE);
chooseImportType(intentGetContentAction);
}
});
@@ -106,12 +112,60 @@ public class ImportExportActivity extends AppCompatActivity
@Override
public void onClick(View v)
{
chooseFileWithIntent(intentPickAction, CHOOSE_EXPORTED_FILE);
chooseImportType(intentPickAction);
}
});
}
private void startImport(final InputStream target, final Uri targetUri)
private void chooseImportType(Intent baseIntent) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.chooseImportType)
.setItems(R.array.import_types_array, (dialog, which) -> {
switch (which) {
// Catima
case 0:
importAlertTitle = getString(R.string.importCatima);
importAlertMessage = getString(R.string.importCatimaMessage);
importDataFormat = DataFormat.Catima;
break;
// Fidme
case 1:
importAlertTitle = getString(R.string.importFidme);
importAlertMessage = getString(R.string.importFidmeMessage);
importDataFormat = DataFormat.Fidme;
break;
// Loyalty Card Keychain
case 2:
importAlertTitle = getString(R.string.importLoyaltyCardKeychain);
importAlertMessage = getString(R.string.importLoyaltyCardKeychainMessage);
importDataFormat = DataFormat.Catima;
break;
// Voucher Vault
case 3:
importAlertTitle = getString(R.string.importVoucherVault);
importAlertMessage = getString(R.string.importVoucherVaultMessage);
importDataFormat = DataFormat.VoucherVault;
break;
default:
throw new IllegalArgumentException("Unknown DataFormat");
}
new AlertDialog.Builder(this)
.setTitle(importAlertTitle)
.setMessage(importAlertMessage)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
chooseFileWithIntent(baseIntent, IMPORT);
}
})
.setNegativeButton(R.string.cancel, null)
.show();
});
builder.show();
}
private void startImport(final InputStream target, final Uri targetUri, final DataFormat dataFormat)
{
ImportExportTask.TaskCompleteListener listener = new ImportExportTask.TaskCompleteListener()
{
@@ -123,7 +177,7 @@ public class ImportExportActivity extends AppCompatActivity
};
importExporter = new ImportExportTask(ImportExportActivity.this,
DataFormat.CSV, target, listener);
dataFormat, target, listener);
importExporter.execute();
}
@@ -139,7 +193,7 @@ public class ImportExportActivity extends AppCompatActivity
};
importExporter = new ImportExportTask(ImportExportActivity.this,
DataFormat.CSV, target, listener);
DataFormat.Catima, target, listener);
importExporter.execute();
}
@@ -294,7 +348,7 @@ public class ImportExportActivity extends AppCompatActivity
{
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != RESULT_OK || (requestCode != CHOOSE_EXPORT_LOCATION && requestCode != CHOOSE_EXPORTED_FILE))
if (resultCode != RESULT_OK)
{
Log.w(TAG, "Failed onActivityResult(), result=" + resultCode);
return;
@@ -336,8 +390,9 @@ public class ImportExportActivity extends AppCompatActivity
reader = new FileInputStream(new File(uri.toString()));
}
Log.e(TAG, "Starting file export with: " + uri.toString());
startImport(reader, uri);
Log.e(TAG, "Starting file import with: " + uri.toString());
startImport(reader, uri, importDataFormat);
}
}
catch(FileNotFoundException e)

View File

@@ -13,6 +13,9 @@ import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import protect.card_locker.importexport.MultiFormatExporter;
import protect.card_locker.importexport.MultiFormatImporter;
class ImportExportTask extends AsyncTask<Void, Void, Boolean>
{
private static final String TAG = "Catima";
@@ -58,16 +61,8 @@ class ImportExportTask extends AsyncTask<Void, Void, Boolean>
{
boolean result = false;
try
{
InputStreamReader reader = new InputStreamReader(stream, Charset.forName("UTF-8"));
result = MultiFormatImporter.importData(db, reader, format);
reader.close();
}
catch(IOException e)
{
Log.e(TAG, "Unable to import file", e);
}
result = MultiFormatImporter.importData(db, stream, format);
Log.i(TAG, "Import result: " + result);

View File

@@ -3,14 +3,22 @@ package protect.card_locker;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import com.google.zxing.BarcodeFormat;
import java.io.InvalidObjectException;
import java.math.BigDecimal;
import java.util.Currency;
import java.util.Date;
public class ImportURIHelper {
private static final String STORE = DBHelper.LoyaltyCardDbIds.STORE;
private static final String NOTE = DBHelper.LoyaltyCardDbIds.NOTE;
private static final String EXPIRY = DBHelper.LoyaltyCardDbIds.EXPIRY;
private static final String BALANCE = DBHelper.LoyaltyCardDbIds.BALANCE;
private static final String BALANCE_TYPE = DBHelper.LoyaltyCardDbIds.BALANCE_TYPE;
private static final String CARD_ID = DBHelper.LoyaltyCardDbIds.CARD_ID;
private static final String BARCODE_ID = DBHelper.LoyaltyCardDbIds.BARCODE_ID;
private static final String BARCODE_TYPE = DBHelper.LoyaltyCardDbIds.BARCODE_TYPE;
private static final String HEADER_COLOR = DBHelper.LoyaltyCardDbIds.HEADER_COLOR;
@@ -42,28 +50,47 @@ public class ImportURIHelper {
try {
// These values are allowed to be null
BarcodeFormat barcodeType = null;
Date expiry = null;
BigDecimal balance = new BigDecimal("0");
Currency balanceType = null;
Integer headerColor = null;
Integer headerTextColor = null;
String store = uri.getQueryParameter(STORE);
String note = uri.getQueryParameter(NOTE);
String cardId = uri.getQueryParameter(CARD_ID);
String barcodeType = uri.getQueryParameter(BARCODE_TYPE);
if (store == null || note == null || cardId == null || barcodeType == null) throw new InvalidObjectException("Not a valid import URI");
String barcodeId = uri.getQueryParameter(BARCODE_ID);
if (store == null || note == null || cardId == null) throw new InvalidObjectException("Not a valid import URI");
String unparsedBarcodeType = uri.getQueryParameter(BARCODE_TYPE);
if(unparsedBarcodeType != null && !unparsedBarcodeType.equals(""))
{
barcodeType = BarcodeFormat.valueOf(unparsedBarcodeType);
}
String unparsedBalance = uri.getQueryParameter(BALANCE);
if(unparsedBalance != null && !unparsedBalance.equals(""))
{
balance = new BigDecimal(unparsedBalance);
}
String unparsedBalanceType = uri.getQueryParameter(BALANCE_TYPE);
if (unparsedBalanceType != null && !unparsedBalanceType.equals(""))
{
balanceType = Currency.getInstance(unparsedBalanceType);
}
String unparsedExpiry = uri.getQueryParameter(EXPIRY);
if(unparsedExpiry != null && !unparsedExpiry.equals(""))
{
expiry = new Date(Long.parseLong(unparsedExpiry));
}
String unparsedHeaderColor = uri.getQueryParameter(HEADER_COLOR);
if(unparsedHeaderColor != null)
{
headerColor = Integer.parseInt(unparsedHeaderColor);
}
return new LoyaltyCard(-1, store, note, expiry, cardId, barcodeType, headerColor, headerTextColor, 0);
return new LoyaltyCard(-1, store, note, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, 0);
} catch (NullPointerException | NumberFormatException ex) {
throw new InvalidObjectException("Not a valid import URI");
}
@@ -77,13 +104,22 @@ public class ImportURIHelper {
uriBuilder.path(path);
uriBuilder.appendQueryParameter(STORE, loyaltyCard.store);
uriBuilder.appendQueryParameter(NOTE, loyaltyCard.note);
uriBuilder.appendQueryParameter(BALANCE, loyaltyCard.balance.toString());
if (loyaltyCard.balanceType != null) {
uriBuilder.appendQueryParameter(BALANCE_TYPE, loyaltyCard.balanceType.getCurrencyCode());
}
if (loyaltyCard.expiry != null) {
uriBuilder.appendQueryParameter(EXPIRY, String.valueOf(loyaltyCard.expiry.getTime()));
}
uriBuilder.appendQueryParameter(CARD_ID, loyaltyCard.cardId);
uriBuilder.appendQueryParameter(BARCODE_TYPE, loyaltyCard.barcodeType);
if(loyaltyCard.headerColor != null)
{
if(loyaltyCard.barcodeId != null) {
uriBuilder.appendQueryParameter(BARCODE_ID, loyaltyCard.barcodeId);
}
if(loyaltyCard.barcodeType != null) {
uriBuilder.appendQueryParameter(BARCODE_TYPE, loyaltyCard.barcodeType.toString());
}
if(loyaltyCard.headerColor != null) {
uriBuilder.appendQueryParameter(HEADER_COLOR, loyaltyCard.headerColor.toString());
}
//StarStatus will not be exported

View File

@@ -2,7 +2,10 @@ package protect.card_locker;
import android.database.Cursor;
import java.text.DateFormat;
import com.google.zxing.BarcodeFormat;
import java.math.BigDecimal;
import java.util.Currency;
import java.util.Date;
import androidx.annotation.Nullable;
@@ -13,29 +16,35 @@ public class LoyaltyCard
public final String store;
public final String note;
public final Date expiry;
public final BigDecimal balance;
public final Currency balanceType;
public final String cardId;
public final String barcodeType;
@Nullable
public final String barcodeId;
public final BarcodeFormat barcodeType;
@Nullable
public final Integer headerColor;
@Nullable
public final Integer headerTextColor;
public final int starStatus;
public LoyaltyCard(final int id, final String store, final String note, final Date expiry, final String cardId,
final String barcodeType, final Integer headerColor, final Integer headerTextColor,
public LoyaltyCard(final int id, final String store, final String note, final Date expiry,
final BigDecimal balance, final Currency balanceType, final String cardId,
final String barcodeId, final BarcodeFormat barcodeType, final Integer headerColor,
final int starStatus)
{
this.id = id;
this.store = store;
this.note = note;
this.expiry = expiry;
this.balance = balance;
this.balanceType = balanceType;
this.cardId = cardId;
this.barcodeId = barcodeId;
this.barcodeType = barcodeType;
this.headerColor = headerColor;
this.headerTextColor = headerTextColor;
this.starStatus = starStatus;
}
@@ -45,16 +54,29 @@ public class LoyaltyCard
String store = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STORE));
String note = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE));
long expiryLong = cursor.getLong(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.EXPIRY));
BigDecimal balance = new BigDecimal(cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE)));
String cardId = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID));
String barcodeType = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE));
String barcodeId = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_ID));
int starred = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS));
int barcodeTypeColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE);
int balanceTypeColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE);
int headerColorColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_COLOR);
int headerTextColorColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR);
BarcodeFormat barcodeType = null;
Currency balanceType = null;
Date expiry = null;
Integer headerColor = null;
Integer headerTextColor = null;
if (cursor.isNull(barcodeTypeColumn) == false)
{
barcodeType = BarcodeFormat.valueOf(cursor.getString(barcodeTypeColumn));
}
if (cursor.isNull(balanceTypeColumn) == false)
{
balanceType = Currency.getInstance(cursor.getString(balanceTypeColumn));
}
if(expiryLong > 0)
{
@@ -66,11 +88,6 @@ public class LoyaltyCard
headerColor = cursor.getInt(headerColorColumn);
}
if(cursor.isNull(headerTextColorColumn) == false)
{
headerTextColor = cursor.getInt(headerTextColorColumn);
}
return new LoyaltyCard(id, store, note, expiry, cardId, barcodeType, headerColor, headerTextColor, starred);
return new LoyaltyCard(id, store, note, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, starred);
}
}

View File

@@ -9,6 +9,7 @@ import android.widget.CursorAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.util.Date;
@@ -41,6 +42,7 @@ class LoyaltyCardCursorAdapter extends CursorAdapter
ImageView thumbnail = view.findViewById(R.id.thumbnail);
TextView storeField = view.findViewById(R.id.store);
TextView noteField = view.findViewById(R.id.note);
TextView balanceField = view.findViewById(R.id.balance);
TextView expiryField = view.findViewById(R.id.expiry);
ImageView star = view.findViewById(R.id.star);
@@ -50,19 +52,29 @@ class LoyaltyCardCursorAdapter extends CursorAdapter
// Populate fields with extracted properties
storeField.setText(loyaltyCard.store);
storeField.setTextSize(settings.getCardTitleListFontSize());
storeField.setTextSize(settings.getFontSizeMax(settings.getMediumFont()));
if(!loyaltyCard.note.isEmpty())
{
noteField.setVisibility(View.VISIBLE);
noteField.setText(loyaltyCard.note);
noteField.setTextSize(settings.getCardNoteListFontSize());
noteField.setTextSize(settings.getFontSizeMax(settings.getSmallFont()));
}
else
{
noteField.setVisibility(View.GONE);
}
if(!loyaltyCard.balance.equals(new BigDecimal("0"))) {
balanceField.setVisibility(View.VISIBLE);
balanceField.setText(context.getString(R.string.balanceSentence, Utils.formatBalance(context, loyaltyCard.balance, loyaltyCard.balanceType)));
balanceField.setTextSize(settings.getFontSizeMax(settings.getSmallFont()));
}
else
{
balanceField.setVisibility(View.GONE);
}
if(loyaltyCard.expiry != null)
{
expiryField.setVisibility(View.VISIBLE);
@@ -72,7 +84,7 @@ class LoyaltyCardCursorAdapter extends CursorAdapter
expiryField.setTextColor(context.getResources().getColor(R.color.alert));
}
expiryField.setText(context.getString(expiryString, DateFormat.getDateInstance(DateFormat.LONG).format(loyaltyCard.expiry)));
expiryField.setTextSize(settings.getCardNoteListFontSize());
expiryField.setTextSize(settings.getFontSizeMax(settings.getSmallFont()));
}
else
{

View File

@@ -1,12 +1,15 @@
package protect.card_locker;
import android.annotation.SuppressLint;
import android.app.DatePickerDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import com.google.android.material.chip.Chip;
@@ -19,7 +22,9 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.DialogFragment;
import android.os.LocaleList;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.util.Log;
import android.view.Menu;
@@ -27,29 +32,33 @@ import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.CalendarView;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.textfield.TextInputLayout;
import com.google.zxing.BarcodeFormat;
import com.jaredrummler.android.colorpicker.ColorPickerDialog;
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener;
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;
import java.util.Currency;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
public class LoyaltyCardEditActivity extends AppCompatActivity
{
@@ -62,8 +71,10 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
EditText noteFieldEdit;
ChipGroup groupsChips;
AutoCompleteTextView expiryField;
View cardAndBarcodeLayout;
EditText balanceField;
AutoCompleteTextView balanceCurrencyField;
TextView cardIdFieldView;
AutoCompleteTextView barcodeIdField;
AutoCompleteTextView barcodeTypeField;
ImageView barcodeImage;
View barcodeImageLayout;
@@ -73,8 +84,9 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
int loyaltyCardId;
boolean updateLoyaltyCard;
String barcodeType;
String cardId;
String barcodeId;
String barcodeType;
Uri importLoyaltyCardUri = null;
Integer headingColorValue = null;
@@ -86,19 +98,25 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
boolean initDone = false;
AlertDialog confirmExitDialog = null;
boolean validBalance = true;
Runnable warnOnInvalidBarcodeType;
HashMap<String, Currency> currencies = new HashMap<>();
private void extractIntentFields(Intent intent)
{
final Bundle b = intent.getExtras();
loyaltyCardId = b != null ? b.getInt("id") : 0;
updateLoyaltyCard = b != null && b.getBoolean("update", false);
barcodeType = b != null ? b.getString("barcodeType") : null;
cardId = b != null ? b.getString("cardId") : null;
barcodeId = b != null ? b.getString("barcodeId") : null;
barcodeType = b != null ? b.getString("barcodeType") : null;
importLoyaltyCardUri = intent.getData();
Log.d(TAG, "View activity: id=" + loyaltyCardId
+ ", updateLoyaltyCard=" + Boolean.toString(updateLoyaltyCard));
+ ", updateLoyaltyCard=" + updateLoyaltyCard);
}
@Override
@@ -120,14 +138,20 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
db = new DBHelper(this);
importUriHelper = new ImportURIHelper(this);
for (Currency currency : Currency.getAvailableCurrencies()) {
currencies.put(currency.getSymbol(), currency);
}
tabs = findViewById(R.id.tabs);
thumbnail = findViewById(R.id.thumbnail);
storeFieldEdit = findViewById(R.id.storeNameEdit);
noteFieldEdit = findViewById(R.id.noteEdit);
groupsChips = findViewById(R.id.groupChips);
expiryField = findViewById(R.id.expiryField);
cardAndBarcodeLayout = findViewById(R.id.cardAndBarcodeLayout);
balanceField = findViewById(R.id.balanceField);
balanceCurrencyField = findViewById(R.id.balanceCurrencyField);
cardIdFieldView = findViewById(R.id.cardIdView);
barcodeIdField = findViewById(R.id.barcodeIdField);
barcodeTypeField = findViewById(R.id.barcodeTypeField);
barcodeImage = findViewById(R.id.barcode);
barcodeImageLayout = findViewById(R.id.barcodeLayout);
@@ -135,6 +159,15 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
enterButton = findViewById(R.id.enterButton);
warnOnInvalidBarcodeType = new Runnable() {
@Override
public void run() {
if (!(boolean) barcodeImage.getTag()) {
Toast.makeText(LoyaltyCardEditActivity.this, getString(R.string.wrongValueForBarcodeType), Toast.LENGTH_LONG).show();
}
}
};
storeFieldEdit.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
@@ -183,6 +216,98 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
}
});
balanceField.setOnFocusChangeListener((v, hasFocus) -> {
if (!hasFocus) {
balanceField.setText(Utils.formatBalanceWithoutCurrencySymbol((BigDecimal) balanceField.getTag(), (Currency) balanceCurrencyField.getTag()));
}
});
balanceField.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
hasChanged = true;
try {
BigDecimal balance = Utils.parseCurrency(s.toString(), Utils.currencyHasDecimals((Currency) balanceCurrencyField.getTag()));
validBalance = true;
balanceField.setTag(balance);
} catch (NumberFormatException e) {
validBalance = false;
e.printStackTrace();
}
}
@Override
public void afterTextChanged(Editable s) { }
});
balanceCurrencyField.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
hasChanged = true;
Currency currency;
if (s.toString().equals(getString(R.string.points))) {
currency = null;
} else {
currency = currencies.get(s.toString());
}
balanceCurrencyField.setTag(currency);
BigDecimal balance = (BigDecimal) balanceField.getTag();
if (balance != null) {
balanceField.setText(Utils.formatBalanceWithoutCurrencySymbol(balance, currency));
}
}
@Override
public void afterTextChanged(Editable s) {
ArrayList<String> currencyList = new ArrayList<>(currencies.keySet());
Collections.sort(currencyList, (o1, o2) -> {
boolean o1ascii = o1.matches("^[^a-zA-Z]*$");
boolean o2ascii = o2.matches("^[^a-zA-Z]*$");
if (!o1ascii && o2ascii) {
return 1;
} else if (o1ascii && !o2ascii) {
return -1;
}
return o1.compareTo(o2);
});
// Sort locale currencies on top
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
LocaleList locales = getApplicationContext().getResources().getConfiguration().getLocales();
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);
}
} else {
String currencySymbol = Currency.getInstance(getApplicationContext().getResources().getConfiguration().locale).getSymbol();
currencyList.remove(currencySymbol);
currencyList.add(0, currencySymbol);
}
currencyList.add(0, getString(R.string.points));
ArrayAdapter<String> currencyAdapter = new ArrayAdapter<>(LoyaltyCardEditActivity.this, android.R.layout.select_dialog_item, currencyList);
balanceCurrencyField.setAdapter(currencyAdapter);
}
});
cardIdFieldView.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
@@ -191,14 +316,8 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
public void onTextChanged(CharSequence s, int start, int before, int count) {
hasChanged = true;
String formatString = barcodeTypeField.getText().toString();
if (!formatString.isEmpty()) {
if (formatString.equals(getString(R.string.noBarcode))) {
hideBarcode();
} else {
generateBarcode(s.toString(), BarcodeFormat.valueOf(formatString));
}
if (!s.toString().isEmpty()) {
generateOrHideBarcode();
}
}
@@ -206,6 +325,66 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
public void afterTextChanged(Editable s) { }
});
barcodeIdField.addTextChangedListener(new TextWatcher() {
CharSequence lastValue;
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
lastValue = s;
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
hasChanged = true;
if (s.toString().equals(getString(R.string.sameAsCardId))) {
barcodeIdField.setTag(null);
} else if (s.toString().equals(getString(R.string.setBarcodeId))) {
if (!lastValue.toString().equals(getString(R.string.setBarcodeId))) {
barcodeIdField.setText(lastValue);
};
AlertDialog.Builder builder = new AlertDialog.Builder(LoyaltyCardEditActivity.this);
builder.setTitle(R.string.setBarcodeId);
final EditText input = new EditText(LoyaltyCardEditActivity.this);
input.setInputType(InputType.TYPE_CLASS_TEXT);
if (barcodeIdField.getTag() != null) {
input.setText((String) barcodeIdField.getTag());
}
builder.setView(input);
builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
barcodeIdField.setTag(input.getText().toString());
barcodeIdField.setText(input.getText());
}
});
builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
AlertDialog dialog = builder.create();
dialog.show();
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
input.requestFocus();
}
generateOrHideBarcode();
}
@Override
public void afterTextChanged(Editable s) {
ArrayList<String> barcodeIdList = new ArrayList<>();
barcodeIdList.add(0, getString(R.string.sameAsCardId));
barcodeIdList.add(1, getString(R.string.setBarcodeId));
ArrayAdapter<String> barcodeIdAdapter = new ArrayAdapter<>(LoyaltyCardEditActivity.this, android.R.layout.select_dialog_item, barcodeIdList);
barcodeIdField.setAdapter(barcodeIdAdapter);
}
});
barcodeTypeField.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
@@ -216,15 +395,30 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
if (!s.toString().isEmpty()) {
if (s.toString().equals(getString(R.string.noBarcode))) {
hideBarcode();
barcodeTypeField.setTag(null);
} else {
generateBarcode(cardIdFieldView.getText().toString(), BarcodeFormat.valueOf(s.toString()));
try {
BarcodeFormat barcodeFormat = BarcodeFormat.valueOf(s.toString());
barcodeTypeField.setTag(barcodeFormat);
if (!BarcodeSelectorActivity.SUPPORTED_BARCODE_TYPES.contains(barcodeFormat.name())) {
Toast.makeText(LoyaltyCardEditActivity.this, getString(R.string.unsupportedBarcodeType), Toast.LENGTH_LONG).show();
}
} catch (IllegalArgumentException e) {}
}
generateOrHideBarcode();
}
}
@Override
public void afterTextChanged(Editable s) { }
public void afterTextChanged(Editable s) {
ArrayList<String> barcodeList = new ArrayList<>(BarcodeSelectorActivity.SUPPORTED_BARCODE_TYPES);
barcodeList.add(0, getString(R.string.noBarcode));
ArrayAdapter<String> barcodeAdapter = new ArrayAdapter<>(LoyaltyCardEditActivity.this, android.R.layout.select_dialog_item, barcodeList);
barcodeTypeField.setAdapter(barcodeAdapter);
}
});
tabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@@ -262,10 +456,17 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
noteFieldEdit.setText("");
expiryField.setTag(null);
expiryField.setText("");
balanceCurrencyField.setTag(null);
balanceCurrencyField.setText("");
balanceField.setTag(null);
balanceField.setText("");
cardIdFieldView.setText("");
barcodeIdField.setTag(null);
barcodeIdField.setText("");
barcodeTypeField.setText("");
}
@SuppressLint("DefaultLocale")
@Override
public void onResume()
{
@@ -297,11 +498,19 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
if(expiryField.getText().length() == 0)
{
expiryField.setTag(loyaltyCard.expiry);
if (loyaltyCard.expiry == null) {
expiryField.setText(getString(R.string.never));
} else {
expiryField.setText(DateFormat.getDateInstance(DateFormat.LONG).format(loyaltyCard.expiry));
}
formatExpiryField(loyaltyCard.expiry);
}
if(balanceCurrencyField.getText().length() == 0)
{
balanceCurrencyField.setTag(loyaltyCard.balanceType);
formatBalanceCurrencyField(loyaltyCard.balanceType);
}
if(balanceField.getText().length() == 0)
{
balanceField.setTag(loyaltyCard.balance);
balanceField.setText(Utils.formatBalanceWithoutCurrencySymbol(loyaltyCard.balance, loyaltyCard.balanceType));
}
if(cardIdFieldView.getText().length() == 0)
@@ -309,9 +518,15 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
cardIdFieldView.setText(loyaltyCard.cardId);
}
if(barcodeIdField.getText().length() == 0)
{
barcodeIdField.setTag(loyaltyCard.barcodeId);
barcodeIdField.setText(loyaltyCard.barcodeId != null ? loyaltyCard.barcodeId : getString(R.string.sameAsCardId));
}
if(barcodeTypeField.getText().length() == 0)
{
barcodeTypeField.setText(loyaltyCard.barcodeType.isEmpty() ? getString(R.string.noBarcode) : loyaltyCard.barcodeType);
barcodeTypeField.setText(loyaltyCard.barcodeType != null ? loyaltyCard.barcodeType.toString() : getString(R.string.noBarcode));
}
if(headingColorValue == null)
@@ -340,13 +555,14 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
storeFieldEdit.setText(importCard.store);
noteFieldEdit.setText(importCard.note);
expiryField.setTag(importCard.expiry);
if (importCard.expiry != null) {
expiryField.setText(DateFormat.getDateInstance(DateFormat.LONG).format(importCard.expiry));
} else {
expiryField.setText(R.string.never);
}
formatExpiryField(importCard.expiry);
balanceField.setTag(importCard.balance);
balanceCurrencyField.setTag(importCard.balanceType);
formatBalanceCurrencyField(importCard.balanceType);
cardIdFieldView.setText(importCard.cardId);
barcodeTypeField.setText(importCard.barcodeType);
barcodeIdField.setTag(importCard.barcodeId);
barcodeIdField.setText(importCard.barcodeId != null ? importCard.barcodeId : getString(R.string.sameAsCardId));
barcodeTypeField.setText(importCard.barcodeType != null ? importCard.barcodeType.toString() : getString(R.string.noBarcode));
headingColorValue = importCard.headerColor;
}
else
@@ -354,6 +570,11 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
setTitle(R.string.addCardTitle);
expiryField.setTag(null);
expiryField.setText(getString(R.string.never));
barcodeIdField.setTag(null);
barcodeIdField.setText(getString(R.string.sameAsCardId));
balanceField.setTag(new BigDecimal("0"));
balanceCurrencyField.setTag(null);
formatBalanceCurrencyField(null);
hideBarcode();
}
@@ -381,13 +602,10 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
break;
}
}
chip.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
hasChanged = true;
chip.setOnTouchListener((v, event) -> {
hasChanged = true;
return false;
}
return false;
});
groupsChips.addView(chip);
@@ -412,50 +630,52 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
// Update from intent
if (barcodeType != null) {
barcodeTypeField.setText(barcodeType.isEmpty() ? getString(R.string.noBarcode) : barcodeType);
barcodeType = null;
try {
barcodeTypeField.setText(BarcodeFormat.valueOf(barcodeType).name());
} catch (IllegalArgumentException e) {
barcodeTypeField.setText(getString(R.string.noBarcode));
}
}
if (cardId != null) {
cardIdFieldView.setText(cardId);
cardId = null;
}
if(cardIdFieldView.getText().length() > 0 && barcodeTypeField.getText().length() > 0)
{
String formatString = barcodeTypeField.getText().toString();
if(formatString.isEmpty() || formatString.equals(getString(R.string.noBarcode)))
{
hideBarcode();
}
else
{
final BarcodeFormat format = BarcodeFormat.valueOf(formatString);
final String cardIdString = cardIdFieldView.getText().toString();
generateBarcode(cardIdString, format);
if (barcodeId != null) {
if (!barcodeId.isEmpty()) {
barcodeIdField.setText(barcodeId);
} else {
barcodeIdField.setText(getString(R.string.sameAsCardId));
}
}
generateOrHideBarcode();
enterButton.setOnClickListener(new EditCardIdAndBarcode());
barcodeImage.setOnClickListener(new EditCardIdAndBarcode());
ArrayList<String> barcodeList = new ArrayList<>(BarcodeSelectorActivity.SUPPORTED_BARCODE_TYPES);
barcodeList.add(0, getString(R.string.noBarcode));
ArrayAdapter<String> barcodeAdapter = new ArrayAdapter<>(this, android.R.layout.select_dialog_item, barcodeList);
barcodeTypeField.setAdapter(barcodeAdapter);
FloatingActionButton saveButton = findViewById(R.id.fabSave);
saveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doSave();
}
});
saveButton.setOnClickListener(v -> doSave());
generateIcon(storeFieldEdit.getText().toString());
}
private void formatExpiryField(Date expiry) {
if (expiry == null) {
expiryField.setText(getString(R.string.never));
} else {
expiryField.setText(DateFormat.getDateInstance(DateFormat.LONG).format(expiry));
}
}
private void formatBalanceCurrencyField(Currency balanceType) {
if (balanceType == null) {
balanceCurrencyField.setText(getString(R.string.points));
} else {
balanceCurrencyField.setText(balanceType.getSymbol());
}
}
@Override
public void onBackPressed() {
askBeforeQuitIfChanged();
@@ -503,6 +723,7 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
}
}
class ColorSelectListener implements View.OnClickListener
{
final int defaultColor;
@@ -584,21 +805,16 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
}
}
private void doSave()
{
String store = storeFieldEdit.getText().toString();
String note = noteFieldEdit.getText().toString();
Date expiry = (Date) expiryField.getTag();
BigDecimal balance = (BigDecimal) balanceField.getTag();
Currency balanceType = balanceCurrencyField.getTag() != null ? ((Currency) balanceCurrencyField.getTag()) : null;
String cardId = cardIdFieldView.getText().toString();
String barcodeType = barcodeTypeField.getText().toString();
// We do not want to save the no barcode string to the database
// it is simply an empty there for no barcode
if(barcodeType.equals(getString(R.string.noBarcode)))
{
barcodeType = "";
}
String barcodeId = (String) barcodeIdField.getTag();
BarcodeFormat barcodeType = (BarcodeFormat) barcodeTypeField.getTag();
if(store.isEmpty())
{
@@ -612,6 +828,12 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
return;
}
if(!validBalance)
{
Snackbar.make(balanceField, getString(R.string.parsingBalanceFailed, balanceField.getText().toString()), Snackbar.LENGTH_LONG).show();
return;
}
List<Group> selectedGroups = new ArrayList<>();
for (Integer chipId : groupsChips.getCheckedChipIds()) {
@@ -621,12 +843,12 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
if(updateLoyaltyCard)
{ //update of "starStatus" not necessary, since it cannot be changed in this activity (only in ViewActivity)
db.updateLoyaltyCard(loyaltyCardId, store, note, expiry, cardId, barcodeType, headingColorValue);
db.updateLoyaltyCard(loyaltyCardId, store, note, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headingColorValue);
Log.i(TAG, "Updated " + loyaltyCardId + " to " + cardId);
}
else
{
loyaltyCardId = (int)db.insertLoyaltyCard(store, note, expiry, cardId, barcodeType, headingColorValue, 0);
loyaltyCardId = (int)db.insertLoyaltyCard(store, note, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headingColorValue, 0);
}
db.setLoyaltyCardGroups(loyaltyCardId, selectedGroups);
@@ -702,10 +924,13 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
{
super.onActivityResult(requestCode, resultCode, intent);
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent);
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent, this);
barcodeType = barcodeValues.format();
cardId = barcodeValues.content();
if (resultCode == RESULT_OK) {
cardId = barcodeValues.content();
barcodeType = barcodeValues.format();
barcodeId = "";
}
onResume();
}
@@ -718,7 +943,18 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
barcodeImageLayout.setVisibility(View.GONE);
}
private void generateBarcode(final String cardId, final BarcodeFormat barcodeFormat) {
private void generateOrHideBarcode() {
String cardIdString = barcodeIdField.getTag() != null ? barcodeIdField.getTag().toString() : cardIdFieldView.getText().toString();
BarcodeFormat barcodeFormat = (BarcodeFormat) barcodeTypeField.getTag();
if (barcodeFormat == null || cardIdString.isEmpty() || !BarcodeSelectorActivity.SUPPORTED_BARCODE_TYPES.contains(barcodeFormat.name())) {
hideBarcode();
} else {
generateBarcode(cardIdString, barcodeFormat);
}
}
private void generateBarcode(String cardIdString, BarcodeFormat barcodeFormat) {
if (barcodeImage.getHeight() == 0) {
Log.d(TAG, "ImageView size is not known known at start, waiting for load");
// The size of the ImageView is not yet available as it has not
@@ -730,12 +966,12 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
barcodeImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
Log.d(TAG, "ImageView size now known");
new BarcodeImageWriterTask(barcodeImage, cardId, barcodeFormat).execute();
new BarcodeImageWriterTask(barcodeImage, cardIdString, barcodeFormat, null, false, warnOnInvalidBarcodeType).execute();
}
});
} else {
Log.d(TAG, "ImageView size known known, creating barcode");
new BarcodeImageWriterTask(barcodeImage, cardId, barcodeFormat).execute();
new BarcodeImageWriterTask(barcodeImage, cardIdString, barcodeFormat, null, false, warnOnInvalidBarcodeType).execute();
}
showBarcode();
@@ -774,7 +1010,7 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
barcodePart.setVisibility(View.VISIBLE);
// Redraw barcode due to size change (Visibility.GONE sets it to 0)
generateBarcode(cardIdFieldView.getText().toString(), BarcodeFormat.valueOf(barcodeTypeField.getText().toString()));
generateOrHideBarcode();
} else {
throw new UnsupportedOperationException();
}

View File

@@ -2,6 +2,7 @@ package protect.card_locker;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Build;
@@ -9,12 +10,15 @@ import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.Guideline;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.core.widget.TextViewCompat;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.appcompat.widget.Toolbar;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.Menu;
@@ -24,16 +28,19 @@ import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.zxing.BarcodeFormat;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.util.Date;
import java.util.List;
import protect.card_locker.preferences.Settings;
@@ -43,14 +50,19 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
private static final String TAG = "Catima";
TextView cardIdFieldView;
BottomSheetBehavior behavior;
View bottomSheet;
ImageView bottomSheetButton;
TextView noteView;
TextView groupsView;
TextView balanceView;
TextView expiryView;
TextView storeName;
ImageButton maximizeButton;
ImageView barcodeImage;
ImageButton minimizeButton;
View collapsingToolbarLayout;
AppBarLayout appBarLayout;
int loyaltyCardId;
LoyaltyCard loyaltyCard;
boolean rotationEnabled;
@@ -59,12 +71,17 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
Settings settings;
String cardIdString;
String barcodeIdString;
BarcodeFormat format;
FloatingActionButton editButton;
Guideline centerGuideline;
SeekBar barcodeScaler;
boolean starred;
boolean backgroundNeedsDarkIcons;
boolean barcodeIsFullscreen = false;
ViewGroup.LayoutParams barcodeImageState;
private void extractIntentFields(Intent intent)
{
@@ -101,14 +118,6 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
setContentView(R.layout.loyalty_card_view_layout);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
{
actionBar.setDisplayHomeAsUpEnabled(true);
}
db = new DBHelper(this);
importURIHelper = new ImportURIHelper(this);
@@ -117,43 +126,70 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
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);
barcodeImage = findViewById(R.id.barcode);
minimizeButton = findViewById(R.id.minimizeButton);
collapsingToolbarLayout = findViewById(R.id.collapsingToolbarLayout);
appBarLayout = findViewById(R.id.app_bar_layout);
centerGuideline = findViewById(R.id.centerGuideline);
centerGuideline.setGuidelinePercent(0.5f);
barcodeScaler = findViewById(R.id.barcodeScaler);
barcodeScaler.setProgress(100);
barcodeScaler.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
Log.d(TAG, "Progress is " + progress);
Log.d(TAG, "Max is " + barcodeScaler.getMax());
float scale = (float) progress / (float) barcodeScaler.getMax();
Log.d(TAG, "Scaling to " + scale);
redrawBarcodeAfterResize();
centerGuideline.setGuidelinePercent(0.5f * scale);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
rotationEnabled = true;
// Allow making barcode fullscreen on tap
barcodeImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (barcodeIsFullscreen)
{
setFullscreen(false);
}
else
{
setFullscreen(true);
}
maximizeButton.setOnClickListener(v -> setFullscreen(true));
barcodeImage.setOnClickListener(view -> {
if (barcodeIsFullscreen)
{
setFullscreen(false);
}
else
{
setFullscreen(true);
}
});
minimizeButton.setOnClickListener(v -> setFullscreen(false));
final FloatingActionButton editButton = findViewById(R.id.fabEdit);
editButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
Bundle bundle = new Bundle();
bundle.putInt("id", loyaltyCardId);
bundle.putBoolean("update", true);
intent.putExtras(bundle);
startActivity(intent);
finish();
}
editButton = findViewById(R.id.fabEdit);
editButton.setOnClickListener(v -> {
Intent intent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
Bundle bundle = new Bundle();
bundle.putInt("id", loyaltyCardId);
bundle.putBoolean("update", true);
intent.putExtras(bundle);
startActivity(intent);
finish();
});
final BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
behavior = BottomSheetBehavior.from(bottomSheet);
behavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
@@ -172,14 +208,11 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
public void onSlide(@NonNull View bottomSheet, float slideOffset) { }
});
bottomSheetButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (behavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
} else {
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
bottomSheetButton.setOnClickListener(v -> {
if (behavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
} else {
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
});
}
@@ -200,23 +233,28 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
Log.i(TAG, "To view card: " + loyaltyCardId);
if(barcodeIsFullscreen)
{
// Completely reset state
//
// This prevents the barcode from taking up the entire screen
// on resume and thus being stretched out of proportion.
recreate();
}
// The brightness value is on a scale from [0, ..., 1], where
// '1' is the brightest. We attempt to maximize the brightness
// to help barcode readers scan the barcode.
Window window = getWindow();
if(window != null && settings.useMaxBrightnessDisplayingBarcode())
if(window != null)
{
WindowManager.LayoutParams attributes = window.getAttributes();
attributes.screenBrightness = 1F;
if (settings.useMaxBrightnessDisplayingBarcode())
{
attributes.screenBrightness = 1F;
}
if (settings.getKeepScreenOn()) {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
if (settings.getDisableLockscreenWhileViewingCard()) {
window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD|
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
}
window.setAttributes(attributes);
}
@@ -229,19 +267,22 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
return;
}
String formatString = loyaltyCard.barcodeType;
format = !formatString.isEmpty() ? BarcodeFormat.valueOf(formatString) : null;
setupOrientation();
format = loyaltyCard.barcodeType;
cardIdString = loyaltyCard.cardId;
barcodeIdString = loyaltyCard.barcodeId;
cardIdFieldView.setText(loyaltyCard.cardId);
TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(cardIdFieldView,
getResources().getInteger(R.integer.settings_card_id_min_font_size_sp)-1, settings.getCardIdFontSize(),
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
{
@@ -259,12 +300,23 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
groupsView.setVisibility(View.VISIBLE);
groupsView.setText(getString(R.string.groupsList, groupsString.toString()));
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);
@@ -274,6 +326,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
expiryView.setTextColor(getResources().getColor(R.color.alert));
}
expiryView.setText(getString(expiryString, DateFormat.getDateInstance(DateFormat.LONG).format(loyaltyCard.expiry)));
expiryView.setTextSize(settings.getFontSizeMax(settings.getMediumFont()));
}
else
{
@@ -281,16 +334,12 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
}
expiryView.setTag(loyaltyCard.expiry);
if (noteView.getVisibility() == View.VISIBLE || groupsView.getVisibility() == View.VISIBLE || expiryView.getVisibility() == View.VISIBLE) {
bottomSheet.setVisibility(View.VISIBLE);
}
else
{
bottomSheet.setVisibility(View.GONE);
if (!barcodeIsFullscreen) {
makeBottomSheetVisibleIfUseful();
}
storeName.setText(loyaltyCard.store);
storeName.setTextSize(settings.getCardTitleFontSize());
storeName.setTextSize(settings.getFontSizeMax(settings.getLargeFont()));
int backgroundHeaderColor;
if(loyaltyCard.headerColor != null)
@@ -303,6 +352,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
}
collapsingToolbarLayout.setBackgroundColor(backgroundHeaderColor);
appBarLayout.setBackgroundColor(backgroundHeaderColor);
int textColor;
if(Utils.needsDarkForeground(backgroundHeaderColor))
@@ -314,6 +364,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
textColor = Color.WHITE;
}
storeName.setTextColor(textColor);
((Toolbar) findViewById(R.id.toolbar_landscape)).setTitleTextColor(textColor);
// If the background is very bright, we should use dark icons
backgroundNeedsDarkIcons = Utils.needsDarkForeground(backgroundHeaderColor);
@@ -336,36 +387,47 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
// 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);
if(format != null)
Boolean isBarcodeSupported = true;
if (format != null && !BarcodeSelectorActivity.SUPPORTED_BARCODE_TYPES.contains(format.name())) {
isBarcodeSupported = false;
Toast.makeText(this, getString(R.string.unsupportedBarcodeType), Toast.LENGTH_LONG).show();
}
if(format != null && isBarcodeSupported)
{
findViewById(R.id.barcode).setVisibility(View.VISIBLE);
if (!barcodeIsFullscreen) {
maximizeButton.setVisibility(View.VISIBLE);
}
barcodeImage.setVisibility(View.VISIBLE);
if(barcodeImage.getHeight() == 0)
{
Log.d(TAG, "ImageView size is not known known at start, waiting for load");
// The size of the ImageView is not yet available as it has not
// yet been drawn. Wait for it to be drawn so the size is available.
barcodeImage.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener()
{
@Override
public void onGlobalLayout()
{
barcodeImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
Log.d(TAG, "ImageView size now known");
new BarcodeImageWriterTask(barcodeImage, cardIdString, format).execute();
}
});
redrawBarcodeAfterResize();
}
else
{
Log.d(TAG, "ImageView size known known, creating barcode");
new BarcodeImageWriterTask(barcodeImage, cardIdString, format).execute();
new BarcodeImageWriterTask(
barcodeImage,
barcodeIdString != null ? barcodeIdString : cardIdString,
format,
null,
false,
null)
.execute();
}
// Force redraw fullscreen state
setFullscreen(barcodeIsFullscreen);
}
else
{
findViewById(R.id.barcode).setVisibility(View.GONE);
maximizeButton.setVisibility(View.GONE);
barcodeImage.setVisibility(View.GONE);
}
}
@@ -378,7 +440,6 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
}
super.onBackPressed();
return;
}
@Override
@@ -455,7 +516,40 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
return super.onOptionsItemSelected(item);
}
private void setupOrientation()
{
Toolbar portraitToolbar = findViewById(R.id.toolbar);
Toolbar landscapeToolbar = findViewById(R.id.toolbar_landscape);
int orientation = getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
Log.d(TAG, "Detected landscape mode");
setTitle(loyaltyCard.store);
collapsingToolbarLayout.setVisibility(View.GONE);
portraitToolbar.setVisibility(View.GONE);
landscapeToolbar.setVisibility(View.VISIBLE);
setSupportActionBar(landscapeToolbar);
} else {
Log.d(TAG, "Detected portrait mode");
setTitle("");
collapsingToolbarLayout.setVisibility(View.VISIBLE);
portraitToolbar.setVisibility(View.VISIBLE);
landscapeToolbar.setVisibility(View.GONE);
setSupportActionBar(portraitToolbar);
}
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
{
actionBar.setDisplayHomeAsUpEnabled(true);
}
}
private void setOrientatonLock(MenuItem item, boolean lock)
{
if(lock)
@@ -473,6 +567,40 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
}
}
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 redrawBarcodeAfterResize()
{
if (format != null) {
barcodeImage.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
barcodeImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
Log.d(TAG, "ImageView size now known");
new BarcodeImageWriterTask(
barcodeImage,
barcodeIdString != null ? barcodeIdString : cardIdString,
format,
null,
false,
null)
.execute();
}
});
};
}
/**
* When enabled, hides the status bar and moves the barcode to the top of the screen.
*
@@ -482,10 +610,16 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
private void setFullscreen(boolean enable)
{
ActionBar actionBar = getSupportActionBar();
if(enable && !barcodeIsFullscreen)
if(enable)
{
// Save previous barcodeImage state
barcodeImageState = barcodeImage.getLayoutParams();
Log.d(TAG, "Move into of fullscreen");
// Prepare redraw after size change
redrawBarcodeAfterResize();
// Hide maximize and show minimize button and scaler
maximizeButton.setVisibility(View.GONE);
minimizeButton.setVisibility(View.VISIBLE);
barcodeScaler.setVisibility(View.VISIBLE);
// Hide actionbar
if(actionBar != null)
@@ -493,55 +627,73 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
actionBar.hide();
}
// Hide collapsingToolbar
// 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);
appBarLayout.setPadding(0, 0, 0, 0);
collapsingToolbarLayout.setVisibility(View.GONE);
findViewById(R.id.toolbar_landscape).setVisibility(View.GONE);
// Hide other UI elements
cardIdFieldView.setVisibility(View.GONE);
bottomSheet.setVisibility(View.GONE);
behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
editButton.hide();
// Set Android to fullscreen mode
getWindow().getDecorView().setSystemUiVisibility(
getWindow().getDecorView().getSystemUiVisibility()
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_FULLSCREEN
getWindow().getDecorView().getSystemUiVisibility()
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_FULLSCREEN
);
// Make barcode take all space
barcodeImage.setLayoutParams(new ConstraintLayout.LayoutParams(
ConstraintLayout.LayoutParams.MATCH_PARENT,
ConstraintLayout.LayoutParams.MATCH_PARENT
));
// Move barcode to top
barcodeImage.setScaleType(ImageView.ScaleType.FIT_START);
// Prevent centering
barcodeImage.setAdjustViewBounds(false);
// Set current state
barcodeIsFullscreen = true;
}
else if(!enable && barcodeIsFullscreen)
else if(!enable)
{
Log.d(TAG, "Move out of fullscreen");
// Reset center guideline
barcodeScaler.setProgress(100);
// Prepare redraw after size change
redrawBarcodeAfterResize();
// Show maximize and hide minimize button and scaler
maximizeButton.setVisibility(View.VISIBLE);
minimizeButton.setVisibility(View.GONE);
barcodeScaler.setVisibility(View.GONE);
// Show actionbar
if(actionBar != null)
{
actionBar.show();
}
// Show collapsingToolbar
collapsingToolbarLayout.setVisibility(View.VISIBLE);
// Show appropriate toolbar
// And restore 24dp paddingTop for appBarLayout
appBarLayout.setVisibility(View.VISIBLE);
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
appBarLayout.setPadding(0, (int) Math.ceil(metrics.density * 24), 0, 0);
setupOrientation();
// Show other UI elements
cardIdFieldView.setVisibility(View.VISIBLE);
makeBottomSheetVisibleIfUseful();
editButton.show();
// Unset fullscreen mode
getWindow().getDecorView().setSystemUiVisibility(
getWindow().getDecorView().getSystemUiVisibility()
& ~View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
& ~View.SYSTEM_UI_FLAG_FULLSCREEN
getWindow().getDecorView().getSystemUiVisibility()
& ~View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
& ~View.SYSTEM_UI_FLAG_FULLSCREEN
);
// Turn barcode back to normal
barcodeImage.setLayoutParams(barcodeImageState);
// Fix barcode centering
barcodeImage.setAdjustViewBounds(true);
// Set current state
barcodeIsFullscreen = false;
}

View File

@@ -1,13 +1,16 @@
package protect.card_locker;
import android.app.AlertDialog;
import android.app.SearchManager;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SearchView;
@@ -92,6 +95,37 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
helpText.setOnTouchListener(gestureTouchListener);
noMatchingCardsText.setOnTouchListener(gestureTouchListener);
list.setOnTouchListener(gestureTouchListener);
/*
* This was added for Huawei, but Huawei is just too much of a fucking pain.
* Just leaving this commented out if needed for the future idk
* https://twitter.com/SylvieLorxu/status/1379437902741012483
*
// Show privacy policy on first run
SharedPreferences privacyPolicyShownPref = getApplicationContext().getSharedPreferences(
getString(R.string.sharedpreference_privacy_policy_shown),
Context.MODE_PRIVATE);
if (privacyPolicyShownPref.getInt(getString(R.string.sharedpreference_privacy_policy_shown), 0) == 0) {
SharedPreferences.Editor privacyPolicyShownPrefEditor = privacyPolicyShownPref.edit();
privacyPolicyShownPrefEditor.putInt(getString(R.string.sharedpreference_privacy_policy_shown), 1);
privacyPolicyShownPrefEditor.apply();
new AlertDialog.Builder(this)
.setTitle(R.string.privacy_policy)
.setMessage(R.string.privacy_policy_popup_text)
.setPositiveButton(R.string.accept, null)
.setNegativeButton(R.string.privacy_policy, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
openPrivacyPolicy();
}
})
.setIcon(android.R.drawable.ic_dialog_info)
.show();
}
*/
}
@Override
@@ -162,7 +196,7 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
return;
}
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent);
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent, this);
if(!barcodeValues.isEmpty()) {
Intent newIntent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
@@ -290,6 +324,14 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
groupsTabLayout.setVisibility(View.VISIBLE);
}
private void openPrivacyPolicy() {
Intent browserIntent = new Intent(
Intent.ACTION_VIEW,
Uri.parse("https://thelastproject.github.io/Catima/privacy-policy")
);
startActivity(browserIntent);
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo)
{
@@ -402,6 +444,12 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
return true;
}
if(id == R.id.action_privacy_policy)
{
openPrivacyPolicy();
return true;
}
if(id == R.id.action_about)
{
Intent i = new Intent(getApplicationContext(), AboutActivity.class);

View File

@@ -132,7 +132,7 @@ public class ScanActivity extends AppCompatActivity {
{
super.onActivityResult(requestCode, resultCode, intent);
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent);
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent, this);
if (!barcodeValues.isEmpty()) {
Intent manualResult = new Intent();
@@ -154,4 +154,10 @@ public class ScanActivity extends AppCompatActivity {
}
startActivityForResult(i, Utils.SELECT_BARCODE_REQUEST);
}
public void addFromImage(View view) {
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
photoPickerIntent.setType("image/*");
startActivityForResult(photoPickerIntent, Utils.BARCODE_IMPORT_FROM_IMAGE_FILE);
}
}

View File

@@ -0,0 +1,25 @@
package protect.card_locker;
public class ThirdPartyInfo {
private final String mName;
private final String mUrl;
private final String mLicense;
public ThirdPartyInfo(String name, String url, String license) {
mName = name;
mUrl = url;
mLicense = license;
}
public String name() {
return mName;
}
public String url() {
return mUrl;
}
public String license() {
return mLicense;
}
}

View File

@@ -3,15 +3,31 @@ package protect.card_locker;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.provider.MediaStore;
import android.util.Log;
import android.widget.Toast;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.Calendar;
import java.util.Currency;
import java.util.Date;
import java.util.GregorianCalendar;
import androidx.core.graphics.ColorUtils;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.NotFoundException;
import com.google.zxing.RGBLuminanceSource;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
public class Utils {
private static final String TAG = "Catima";
@@ -19,15 +35,26 @@ public class Utils {
public static final int MAIN_REQUEST = 1;
public static final int SELECT_BARCODE_REQUEST = 2;
public static final int BARCODE_SCAN = 3;
public static final int BARCODE_IMPORT_FROM_IMAGE_FILE = 4;
static final double LUMINANCE_MIDPOINT = 0.5;
static public LetterBitmap generateIcon(Context context, String store, Integer backgroundColor) {
return generateIcon(context, store, backgroundColor, false);
}
static public LetterBitmap generateIcon(Context context, String store, Integer backgroundColor, boolean forShortcut) {
if (store.length() == 0) {
return null;
}
int tileLetterFontSize = context.getResources().getDimensionPixelSize(R.dimen.tileLetterFontSize);
int tileLetterFontSize;
if (forShortcut) {
tileLetterFontSize = context.getResources().getDimensionPixelSize(R.dimen.tileLetterFontSizeForShortcut);
} else {
tileLetterFontSize = context.getResources().getDimensionPixelSize(R.dimen.tileLetterFontSize);
}
int pixelSize = context.getResources().getDimensionPixelSize(R.dimen.cardThumbnailSize);
if (backgroundColor == null) {
@@ -42,28 +69,75 @@ public class Utils {
return ColorUtils.calculateLuminance(backgroundColor) > LUMINANCE_MIDPOINT;
}
static public BarcodeValues parseSetBarcodeActivityResult(int requestCode, int resultCode, Intent intent) {
String contents = null;
String format = null;
static public BarcodeValues parseSetBarcodeActivityResult(int requestCode, int resultCode, Intent intent, Context context) {
String contents;
String format;
if (resultCode == Activity.RESULT_OK)
{
if (resultCode != Activity.RESULT_OK) {
return new BarcodeValues(null, null);
}
if (requestCode == Utils.BARCODE_IMPORT_FROM_IMAGE_FILE) {
Log.i(TAG, "Received image file with possible barcode");
Bitmap bitmap;
try {
bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), intent.getData());
} catch (IOException e) {
Log.e(TAG, "Error getting data from image file");
e.printStackTrace();
Toast.makeText(context, R.string.errorReadingImage, Toast.LENGTH_LONG).show();
return new BarcodeValues(null, null);
}
BarcodeValues barcodeFromBitmap = getBarcodeFromBitmap(bitmap);
if (barcodeFromBitmap.isEmpty()) {
Log.i(TAG, "No barcode found in image file");
Toast.makeText(context, R.string.noBarcodeFound, Toast.LENGTH_LONG).show();
}
Log.i(TAG, "Read barcode id: " + barcodeFromBitmap.content());
Log.i(TAG, "Read format: " + barcodeFromBitmap.format());
return barcodeFromBitmap;
}
if (requestCode == Utils.BARCODE_SCAN || requestCode == Utils.SELECT_BARCODE_REQUEST) {
if (requestCode == Utils.BARCODE_SCAN) {
Log.i(TAG, "Received barcode information from camera");
} else if (requestCode == Utils.SELECT_BARCODE_REQUEST) {
Log.i(TAG, "Received barcode information from typing it");
} else {
return new BarcodeValues(null, null);
}
contents = intent.getStringExtra(BarcodeSelectorActivity.BARCODE_CONTENTS);
format = intent.getStringExtra(BarcodeSelectorActivity.BARCODE_FORMAT);
Log.i(TAG, "Read barcode id: " + contents);
Log.i(TAG, "Read format: " + format);
return new BarcodeValues(format, contents);
}
Log.i(TAG, "Read barcode id: " + contents);
Log.i(TAG, "Read format: " + format);
throw new UnsupportedOperationException("Unknown request code for parseSetBarcodeActivityResult");
}
return new BarcodeValues(format, contents);
static public BarcodeValues getBarcodeFromBitmap(Bitmap bitmap) {
// In order to decode it, the Bitmap must first be converted into a pixel array...
int[] intArray = new int[bitmap.getWidth() * bitmap.getHeight()];
bitmap.getPixels(intArray, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
// ...and then turned into a binary bitmap from its luminance
LuminanceSource source = new RGBLuminanceSource(bitmap.getWidth(), bitmap.getHeight(), intArray);
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
try {
Result barcodeResult = new MultiFormatReader().decode(binaryBitmap);
return new BarcodeValues(barcodeResult.getBarcodeFormat().name(), barcodeResult.getText());
} catch (NotFoundException e) {
return new BarcodeValues(null, null);
}
}
static public Boolean hasExpired(Date expiryDate) {
@@ -77,4 +151,62 @@ public class Utils {
return expiryDate.before(date.getTime());
}
static public String formatBalance(Context context, BigDecimal value, Currency currency) {
NumberFormat numberFormat = NumberFormat.getInstance();
if (currency == null) {
numberFormat.setMaximumFractionDigits(0);
return context.getString(R.string.balancePoints, numberFormat.format(value));
}
NumberFormat currencyFormat = NumberFormat.getCurrencyInstance();
currencyFormat.setCurrency(currency);
currencyFormat.setMinimumFractionDigits(currency.getDefaultFractionDigits());
currencyFormat.setMaximumFractionDigits(currency.getDefaultFractionDigits());
return currencyFormat.format(value);
}
static public String formatBalanceWithoutCurrencySymbol(BigDecimal value, Currency currency) {
NumberFormat numberFormat = NumberFormat.getInstance();
if (currency == null) {
numberFormat.setMaximumFractionDigits(0);
return numberFormat.format(value);
}
numberFormat.setMinimumFractionDigits(currency.getDefaultFractionDigits());
numberFormat.setMaximumFractionDigits(currency.getDefaultFractionDigits());
return numberFormat.format(value);
}
static public Boolean currencyHasDecimals(Currency currency) {
if (currency == null) {
return false;
}
return currency.getDefaultFractionDigits() != 0;
}
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);
}
}

View File

@@ -0,0 +1,93 @@
package protect.card_locker.importexport;
import org.apache.commons.csv.CSVRecord;
import protect.card_locker.FormatException;
public class CSVHelpers {
/**
* Extract a string from the items array. The index into the array
* is determined by looking up the index in the fields map using the
* "key" as the key. If no such key exists, defaultValue is returned
* if it is not null. Otherwise, a FormatException is thrown.
*/
static String extractString(String key, CSVRecord record, String defaultValue)
throws FormatException
{
String toReturn = defaultValue;
if(record.isMapped(key))
{
toReturn = record.get(key);
}
else
{
if(defaultValue == null)
{
throw new FormatException("Field not used but expected: " + key);
}
}
return toReturn;
}
/**
* Extract an integer from the items array. The index into the array
* is determined by looking up the index in the fields map using the
* "key" as the key. If no such key exists, or the data is not a valid
* int, a FormatException is thrown.
*/
static Integer extractInt(String key, CSVRecord record, boolean nullIsOk)
throws FormatException
{
if(record.isMapped(key) == false)
{
throw new FormatException("Field not used but expected: " + key);
}
String value = record.get(key);
if(value.isEmpty() && nullIsOk)
{
return null;
}
try
{
return Integer.parseInt(record.get(key));
}
catch(NumberFormatException e)
{
throw new FormatException("Failed to parse field: " + key, e);
}
}
/**
* Extract a long from the items array. The index into the array
* is determined by looking up the index in the fields map using the
* "key" as the key. If no such key exists, or the data is not a valid
* int, a FormatException is thrown.
*/
static Long extractLong(String key, CSVRecord record, boolean nullIsOk)
throws FormatException
{
if(record.isMapped(key) == false)
{
throw new FormatException("Field not used but expected: " + key);
}
String value = record.get(key);
if(value.isEmpty() && nullIsOk)
{
return null;
}
try
{
return Long.parseLong(record.get(key));
}
catch(NumberFormatException e)
{
throw new FormatException("Failed to parse field: " + key, e);
}
}
}

View File

@@ -1,4 +1,4 @@
package protect.card_locker;
package protect.card_locker.importexport;
import android.database.Cursor;
@@ -8,6 +8,10 @@ import org.apache.commons.csv.CSVPrinter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import protect.card_locker.DBHelper;
import protect.card_locker.Group;
import protect.card_locker.LoyaltyCard;
/**
* Class for exporting the database into CSV (Comma Separate Values)
* format.
@@ -50,6 +54,8 @@ public class CsvDatabaseExporter implements DatabaseExporter
DBHelper.LoyaltyCardDbIds.STORE,
DBHelper.LoyaltyCardDbIds.NOTE,
DBHelper.LoyaltyCardDbIds.EXPIRY,
DBHelper.LoyaltyCardDbIds.BALANCE,
DBHelper.LoyaltyCardDbIds.BALANCE_TYPE,
DBHelper.LoyaltyCardDbIds.CARD_ID,
DBHelper.LoyaltyCardDbIds.HEADER_COLOR,
DBHelper.LoyaltyCardDbIds.BARCODE_TYPE,
@@ -65,6 +71,8 @@ public class CsvDatabaseExporter implements DatabaseExporter
card.store,
card.note,
card.expiry != null ? card.expiry.getTime() : "",
card.balance,
card.balanceType,
card.cardId,
card.headerColor,
card.barcodeType,

View File

@@ -1,17 +1,33 @@
package protect.card_locker;
package protect.card_locker.importexport;
import android.database.sqlite.SQLiteDatabase;
import com.google.zxing.BarcodeFormat;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.json.JSONException;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.Currency;
import java.util.Date;
import java.util.List;
import java.util.zip.ZipFile;
import protect.card_locker.DBHelper;
import protect.card_locker.FormatException;
import protect.card_locker.Group;
/**
* Class for importing a database from CSV (Comma Separate Values)
@@ -22,9 +38,9 @@ import java.util.List;
*/
public class CsvDatabaseImporter implements DatabaseImporter
{
public void importData(DBHelper db, InputStreamReader input) throws IOException, FormatException, InterruptedException
public void importData(DBHelper db, InputStream input) throws IOException, FormatException, InterruptedException
{
BufferedReader bufferedReader = new BufferedReader(input);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
bufferedReader.mark(100);
@@ -194,92 +210,6 @@ public class CsvDatabaseImporter implements DatabaseImporter
}
}
/**
* Extract a string from the items array. The index into the array
* is determined by looking up the index in the fields map using the
* "key" as the key. If no such key exists, defaultValue is returned
* if it is not null. Otherwise, a FormatException is thrown.
*/
private String extractString(String key, CSVRecord record, String defaultValue)
throws FormatException
{
String toReturn = defaultValue;
if(record.isMapped(key))
{
toReturn = record.get(key);
}
else
{
if(defaultValue == null)
{
throw new FormatException("Field not used but expected: " + key);
}
}
return toReturn;
}
/**
* Extract an integer from the items array. The index into the array
* is determined by looking up the index in the fields map using the
* "key" as the key. If no such key exists, or the data is not a valid
* int, a FormatException is thrown.
*/
private Integer extractInt(String key, CSVRecord record, boolean nullIsOk)
throws FormatException
{
if(record.isMapped(key) == false)
{
throw new FormatException("Field not used but expected: " + key);
}
String value = record.get(key);
if(value.isEmpty() && nullIsOk)
{
return null;
}
try
{
return Integer.parseInt(record.get(key));
}
catch(NumberFormatException e)
{
throw new FormatException("Failed to parse field: " + key, e);
}
}
/**
* Extract a long from the items array. The index into the array
* is determined by looking up the index in the fields map using the
* "key" as the key. If no such key exists, or the data is not a valid
* int, a FormatException is thrown.
*/
private Long extractLong(String key, CSVRecord record, boolean nullIsOk)
throws FormatException
{
if(record.isMapped(key) == false)
{
throw new FormatException("Field not used but expected: " + key);
}
String value = record.get(key);
if(value.isEmpty() && nullIsOk)
{
return null;
}
try
{
return Long.parseLong(record.get(key));
}
catch(NumberFormatException e)
{
throw new FormatException("Failed to parse field: " + key, e);
}
}
/**
* Import a single loyalty card into the database using the given
* session.
@@ -287,44 +217,70 @@ public class CsvDatabaseImporter implements DatabaseImporter
private void importLoyaltyCard(SQLiteDatabase database, DBHelper helper, CSVRecord record)
throws IOException, FormatException
{
int id = extractInt(DBHelper.LoyaltyCardDbIds.ID, record, false);
int id = CSVHelpers.extractInt(DBHelper.LoyaltyCardDbIds.ID, record, false);
String store = extractString(DBHelper.LoyaltyCardDbIds.STORE, record, "");
String store = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.STORE, record, "");
if(store.isEmpty())
{
throw new FormatException("No store listed, but is required");
}
String note = extractString(DBHelper.LoyaltyCardDbIds.NOTE, record, "");
String note = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.NOTE, record, "");
Date expiry = null;
try {
expiry = new Date(extractLong(DBHelper.LoyaltyCardDbIds.EXPIRY, record, true));
expiry = new Date(CSVHelpers.extractLong(DBHelper.LoyaltyCardDbIds.EXPIRY, record, true));
} catch (NullPointerException | FormatException e) { }
String cardId = extractString(DBHelper.LoyaltyCardDbIds.CARD_ID, record, "");
BigDecimal balance;
try {
balance = new BigDecimal(CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.BALANCE, record, null));
} catch (FormatException _e ) {
// These fields did not exist in versions 1.8.1 and before
// We catch this exception so we can still import old backups
balance = new BigDecimal("0");
}
Currency balanceType = null;
String unparsedBalanceType = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE, record, "");
if(!unparsedBalanceType.isEmpty()) {
balanceType = Currency.getInstance(unparsedBalanceType);
}
String cardId = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.CARD_ID, record, "");
if(cardId.isEmpty())
{
throw new FormatException("No card ID listed, but is required");
}
String barcodeType = extractString(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE, record, "");
String barcodeId = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.BARCODE_ID, record, "");
if(barcodeId.isEmpty())
{
barcodeId = null;
}
BarcodeFormat barcodeType = null;
String unparsedBarcodeType = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE, record, "");
if(!unparsedBarcodeType.isEmpty())
{
barcodeType = BarcodeFormat.valueOf(unparsedBarcodeType);
}
Integer headerColor = null;
if(record.isMapped(DBHelper.LoyaltyCardDbIds.HEADER_COLOR))
{
headerColor = extractInt(DBHelper.LoyaltyCardDbIds.HEADER_COLOR, record, true);
headerColor = CSVHelpers.extractInt(DBHelper.LoyaltyCardDbIds.HEADER_COLOR, record, true);
}
int starStatus = 0;
try {
starStatus = extractInt(DBHelper.LoyaltyCardDbIds.STAR_STATUS, record, false);
starStatus = CSVHelpers.extractInt(DBHelper.LoyaltyCardDbIds.STAR_STATUS, record, false);
} catch (FormatException _e ) {
// This field did not exist in versions 0.28 and before
// We catch this exception so we can still import old backups
}
if (starStatus != 1) starStatus = 0;
helper.insertLoyaltyCard(database, id, store, note, expiry, cardId, barcodeType, headerColor, starStatus);
helper.insertLoyaltyCard(database, id, store, note, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, starStatus);
}
/**
@@ -334,7 +290,7 @@ public class CsvDatabaseImporter implements DatabaseImporter
private void importGroup(SQLiteDatabase database, DBHelper helper, CSVRecord record)
throws IOException, FormatException
{
String id = extractString(DBHelper.LoyaltyCardDbGroups.ID, record, null);
String id = CSVHelpers.extractString(DBHelper.LoyaltyCardDbGroups.ID, record, null);
helper.insertGroup(database, id);
}
@@ -346,8 +302,8 @@ public class CsvDatabaseImporter implements DatabaseImporter
private void importCardGroupMapping(SQLiteDatabase database, DBHelper helper, CSVRecord record)
throws IOException, FormatException
{
Integer cardId = extractInt(DBHelper.LoyaltyCardDbIdsGroups.cardID, record, false);
String groupId = extractString(DBHelper.LoyaltyCardDbIdsGroups.groupID, record, null);
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));

View File

@@ -1,8 +1,10 @@
package protect.card_locker;
package protect.card_locker.importexport;
import java.io.IOException;
import java.io.OutputStreamWriter;
import protect.card_locker.DBHelper;
/**
* Interface for a class which can export the contents of the database
* in a given format.

View File

@@ -0,0 +1,27 @@
package protect.card_locker.importexport;
import org.json.JSONException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.ParseException;
import protect.card_locker.DBHelper;
import protect.card_locker.FormatException;
/**
* Interface for a class which can import the contents of a stream
* into the database.
*/
public interface DatabaseImporter
{
/**
* Import data from the input stream in a given format into
* the database.
* @throws IOException
* @throws FormatException
*/
void importData(DBHelper db, InputStream input) throws IOException, FormatException, InterruptedException, JSONException, ParseException;
}

View File

@@ -0,0 +1,147 @@
package protect.card_locker.importexport;
import android.annotation.SuppressLint;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Color;
import com.google.zxing.BarcodeFormat;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Currency;
import java.util.Date;
import java.util.TimeZone;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import protect.card_locker.DBHelper;
import protect.card_locker.FormatException;
/**
* Class for importing a database from CSV (Comma Separate Values)
* formatted data.
*
* The database's loyalty cards are expected to appear in the CSV data.
* A header is expected for the each table showing the names of the columns.
*/
public class FidmeImporter implements DatabaseImporter
{
public void importData(DBHelper db, InputStream input) throws IOException, FormatException, JSONException, ParseException {
// We actually retrieve a .zip file
ZipInputStream zipInputStream = new ZipInputStream(input);
StringBuilder loyaltyCards = new StringBuilder();
byte[] buffer = new byte[1024];
int read = 0;
ZipEntry zipEntry;
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
if (zipEntry.getName().equals("loyalty_programs.csv")) {
while ((read = zipInputStream.read(buffer, 0, 1024)) >= 0) {
loyaltyCards.append(new String(buffer, 0, read, StandardCharsets.UTF_8));
}
}
}
if (loyaltyCards.length() == 0) {
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.withDelimiter(';').withHeader());
try {
for (CSVRecord record : fidmeParser) {
importLoyaltyCard(database, db, record);
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
}
} catch (IllegalArgumentException | IllegalStateException | InterruptedException e) {
throw new FormatException("Issue parsing CSV data", e);
} finally {
fidmeParser.close();
}
database.setTransactionSuccessful();
database.endTransaction();
database.close();
zipInputStream.close();
}
/**
* Import a single loyalty card into the database using the given
* session.
*/
private void importLoyaltyCard(SQLiteDatabase database, DBHelper helper, CSVRecord record)
throws IOException, FormatException
{
// A loyalty card export from Fidme contains the following fields:
// Retailer (store name)
// Program (program name)
// Added at (YYYY-MM-DD HH:MM:SS UTC)
// Reference (card ID)
// Firstname (card holder first name)
// Lastname (card holder last name)
// The store is called Retailer
String store = CSVHelpers.extractString("Retailer", record, "");
if (store.isEmpty())
{
throw new FormatException("No store listed, but is required");
}
// There seems to be no note field in the CSV? So let's combine other fields instead...
String program = CSVHelpers.extractString("Program", record, "");
String addedAt = CSVHelpers.extractString("Added At", record, "");
String firstName = CSVHelpers.extractString("Firstname", record, "");
String lastName = CSVHelpers.extractString("Lastname", record, "");
String combinedName = String.format("%s %s", firstName, lastName);
StringBuilder noteBuilder = new StringBuilder();
if (!program.isEmpty()) noteBuilder.append(program).append('\n');
if (!addedAt.isEmpty()) noteBuilder.append(addedAt).append('\n');
if (!combinedName.isEmpty()) noteBuilder.append(combinedName).append('\n');
String note = noteBuilder.toString();
// The ID is called reference
String cardId = CSVHelpers.extractString("Reference", record, "");
if(cardId.isEmpty())
{
throw new FormatException("No card ID listed, but is required");
}
// Sadly, Fidme exports don't contain the card type
// I guess they have an online DB of all the different companies and what type they use
// TODO: Hook this into our own loyalty card DB if we ever get one
BarcodeFormat barcodeType = null;
// No favourite data in the export either
int starStatus = 0;
helper.insertLoyaltyCard(database, store, note, null, BigDecimal.valueOf(0), null, cardId, null, barcodeType, null, starStatus);
}
}

View File

@@ -1,10 +1,15 @@
package protect.card_locker;
package protect.card_locker.importexport;
import android.util.Log;
import java.io.IOException;
import java.io.OutputStreamWriter;
import protect.card_locker.DBHelper;
import protect.card_locker.DataFormat;
import protect.card_locker.importexport.CsvDatabaseExporter;
import protect.card_locker.importexport.DatabaseExporter;
public class MultiFormatExporter
{
private static final String TAG = "Catima";
@@ -25,9 +30,12 @@ public class MultiFormatExporter
switch(format)
{
case CSV:
case Catima:
exporter = new CsvDatabaseExporter();
break;
default:
Log.e(TAG, "Failed to export data, unknown format " + format.name());
break;
}
if(exporter != null)

View File

@@ -1,9 +1,19 @@
package protect.card_locker;
package protect.card_locker.importexport;
import android.util.Log;
import org.json.JSONException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.ParseException;
import protect.card_locker.DBHelper;
import protect.card_locker.DataFormat;
import protect.card_locker.FormatException;
import protect.card_locker.importexport.CsvDatabaseImporter;
import protect.card_locker.importexport.DatabaseImporter;
public class MultiFormatImporter
{
@@ -20,15 +30,21 @@ public class MultiFormatImporter
* false otherwise. If false, no data was written to
* the database.
*/
public static boolean importData(DBHelper db, InputStreamReader input, DataFormat format)
public static boolean importData(DBHelper db, InputStream input, DataFormat format)
{
DatabaseImporter importer = null;
switch(format)
{
case CSV:
case Catima:
importer = new CsvDatabaseImporter();
break;
case Fidme:
importer = new FidmeImporter();
break;
case VoucherVault:
importer = new VoucherVaultImporter();
break;
}
if (importer != null)
@@ -38,7 +54,7 @@ public class MultiFormatImporter
importer.importData(db, input);
return true;
}
catch(IOException | FormatException | InterruptedException e)
catch(IOException | FormatException | InterruptedException | JSONException | ParseException e)
{
Log.e(TAG, "Failed to import data", e);
}

View File

@@ -0,0 +1,134 @@
package protect.card_locker.importexport;
import android.annotation.SuppressLint;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Color;
import com.google.zxing.BarcodeFormat;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Currency;
import java.util.Date;
import java.util.TimeZone;
import protect.card_locker.DBHelper;
import protect.card_locker.FormatException;
/**
* Class for importing a database from CSV (Comma Separate Values)
* formatted data.
*
* The database's loyalty cards are expected to appear in the CSV data.
* A header is expected for the each table showing the names of the columns.
*/
public class VoucherVaultImporter implements DatabaseImporter
{
public void importData(DBHelper db, InputStream input) throws IOException, FormatException, JSONException, ParseException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
sb.append(line);
}
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);
String store = jsonCard.getString("description");
Date expiry = null;
if (!jsonCard.isNull("expires")) {
@SuppressLint("SimpleDateFormat") SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
expiry = dateFormat.parse(jsonCard.getString("expires"));
}
BigDecimal balance;
if (!jsonCard.isNull("balance")) {
balance = new BigDecimal(String.valueOf(jsonCard.getDouble("balance")));
} else {
balance = new BigDecimal("0");
}
Currency balanceType = Currency.getInstance("USD");
String cardId = jsonCard.getString("code");
BarcodeFormat barcodeType = null;
String codeTypeFromJSON = jsonCard.getString("codeType");
switch (codeTypeFromJSON) {
case "CODE128":
barcodeType = BarcodeFormat.CODE_128;
break;
case "CODE39":
barcodeType = BarcodeFormat.CODE_39;
break;
case "EAN13":
barcodeType = BarcodeFormat.EAN_13;
break;
case "QR":
barcodeType = BarcodeFormat.QR_CODE;
break;
case "TEXT":
break;
default:
throw new FormatException("Unknown barcode type found: " + codeTypeFromJSON);
}
int headerColor;
String colorFromJSON = jsonCard.getString("color");
switch (colorFromJSON) {
case "GREY":
headerColor = Color.GRAY;
break;
case "BLUE":
headerColor = Color.BLUE;
break;
case "GREEN":
headerColor = Color.GREEN;
break;
case "ORANGE":
headerColor = Color.rgb(255, 165, 0);
break;
case "PURPLE":
headerColor = Color.rgb(128, 0, 128);
break;
case "RED":
headerColor = Color.RED;
break;
case "YELLOW":
headerColor = Color.YELLOW;
break;
default:
throw new FormatException("Unknown colour type foun: " + colorFromJSON);
}
db.insertLoyaltyCard(store, "", expiry, balance, balanceType, cardId, null, barcodeType, headerColor, 0);
}
database.setTransactionSuccessful();
database.endTransaction();
database.close();
bufferedReader.close();
}
}

View File

@@ -61,24 +61,34 @@ public class Settings
return AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;
}
public int getCardTitleListFontSize()
public double getFontSizeScale()
{
return getInt(R.string.settings_key_card_title_list_font_size, R.integer.settings_card_title_list_font_size_sp);
return getInt(R.string.settings_key_max_font_size_scale, R.integer.settings_max_font_size_scale_pct) / 100.0;
}
public int getCardNoteListFontSize()
public int getSmallFont()
{
return getInt(R.string.settings_key_card_note_list_font_size, R.integer.settings_card_note_list_font_size_sp);
return 14;
}
public int getCardTitleFontSize()
public int getMediumFont()
{
return getInt(R.string.settings_key_card_title_font_size, R.integer.settings_card_title_font_size_sp);
return 28;
}
public int getCardIdFontSize()
public int getLargeFont()
{
return getInt(R.string.settings_key_card_id_font_size, R.integer.settings_card_id_font_size_sp);
return 40;
}
public int getFontSizeMin(int fontSize)
{
return (int) (Math.round(fontSize / 2.0) - 1);
}
public int getFontSizeMax(int fontSize)
{
return (int) Math.round(fontSize * getFontSizeScale());
}
public boolean useMaxBrightnessDisplayingBarcode()
@@ -90,4 +100,14 @@ public class Settings
{
return getBoolean(R.string.settings_key_lock_barcode_orientation, false);
}
public boolean getKeepScreenOn()
{
return getBoolean(R.string.settings_key_keep_screen_on, true);
}
public boolean getDisableLockscreenWhileViewingCard()
{
return getBoolean(R.string.settings_key_disable_lockscreen_while_viewing_card, true);
}
}

View File

@@ -20,6 +20,7 @@ public class SettingsActivity extends AppCompatActivity
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.settings_activity);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
@@ -29,7 +30,7 @@ public class SettingsActivity extends AppCompatActivity
// Display the fragment as the main content.
getSupportFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment())
.replace(R.id.settings_container, new SettingsFragment())
.commit();
}
@@ -96,6 +97,7 @@ public class SettingsActivity extends AppCompatActivity
dialogPreference.getKey(),
dialogPreference.getMinValue(),
dialogPreference.getMaxValue(),
dialogPreference.getStepValue(),
dialogPreference.getUnitText()
);
dialogFragment.setTargetFragment(this, 0);

View File

@@ -26,7 +26,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="?attr/actionBarSize"
android:orientation="vertical">
android:orientation="vertical"
android:padding="10dp">
<TextView
android:id="@+id/aboutText"

View File

@@ -55,6 +55,7 @@
android:labelFor="@+id/cardId"
android:text="@string/cardId" />
<EditText android:id="@+id/cardId"
android:hint="AB1234"
android:importantForAutofill="no"
android:layout_width="fill_parent"
android:layout_height="wrap_content"

View File

@@ -19,11 +19,25 @@
app:zxing_viewfinder_laser="@color/zxing_custom_viewfinder_laser"
app:zxing_viewfinder_mask="@color/zxing_custom_viewfinder_mask"/>
<Button
android:id="@+id/add_manually"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/addManually"
android:layout_gravity="bottom|center_horizontal"
android:onClick="addManually"/>
android:gravity="center_horizontal"
android:orientation="vertical">
<Button
android:id="@+id/add_from_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="addFromImage"
android:text="@string/addFromImage" />
<Button
android:id="@+id/add_manually"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="addManually"
android:text="@string/addManually" />
</LinearLayout>
</merge>

View File

@@ -96,6 +96,30 @@
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<!-- Card ID -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/inputPadding"
android:paddingTop="@dimen/inputPadding"
android:orientation="horizontal">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/cardIdField"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/cardId">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/cardIdView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<!-- Note -->
<LinearLayout
android:layout_width="match_parent"
@@ -136,6 +160,7 @@
android:textSize="@dimen/inputSize" />
</LinearLayout>
<!-- Balance -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -143,7 +168,53 @@
android:paddingTop="@dimen/inputPadding"
android:orientation="horizontal">
<!-- Barcode type -->
<!-- Balance -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/balanceView"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:hint="@string/balance"
android:labelFor="@+id/balanceField">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/balanceField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="numberDecimal"
android:digits="0123456789.,$" />
</com.google.android.material.textfield.TextInputLayout>
<!-- Currency -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/balanceCurrencyView"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:hint="@string/currency"
android:labelFor="@+id/balanceCurrencyField">
<AutoCompleteTextView
android:id="@+id/balanceCurrencyField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<!-- Expiration -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/inputPadding"
android:paddingTop="@dimen/inputPadding"
android:orientation="horizontal">
<!-- Expiry date -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/expiryView"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
@@ -164,32 +235,41 @@
<TableLayout
android:id="@+id/barcodePart"
android:visibility="gone">
<!-- Card ID and Barcode type -->
<!-- Barcode ID -->
<LinearLayout
android:id="@+id/cardAndBarcodeLayout"
android:layout_width="fill_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/inputPadding"
android:paddingTop="@dimen/inputPadding"
android:orientation="horizontal"
android:baselineAligned="false">
android:orientation="horizontal">
<!-- Card ID -->
<!-- Barcode ID -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/cardIdField"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
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/cardId">
android:hint="@string/barcodeId"
android:labelFor="@+id/barcodeIdView">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/cardIdView"
<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

View File

@@ -53,6 +53,14 @@
android:ellipsize="end"
android:textSize="@dimen/noteTextSize"/>
<TextView
android:id="@+id/balance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:ellipsize="end"
android:textSize="@dimen/noteTextSize"/>
<TextView
android:id="@+id/expiry"
android:layout_width="wrap_content"

View File

@@ -2,11 +2,11 @@
<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:id="@+id/coordinator_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fitsSystemWindows="true"
>
android:fitsSystemWindows="true">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabEdit"
@@ -36,30 +36,82 @@
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_marginEnd="15.0dip"
android:layout_marginTop="10dp"
android:padding="0dp"
app:srcCompat="@drawable/ic_baseline_arrow_drop_up_24"
android:contentDescription="@string/moveBarcodeToTopOfScreen"
android:tint="@android:color/white"
android:background="@color/colorPrimary"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/barcode"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ImageView
android:id="@+id/barcode"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="20.0dip"
android:layout_marginBottom="10.0dip"
android:layout_marginStart="15.0dip"
android:layout_marginEnd="15.0dip"
android:padding="10.0dp"
android:padding="10dp"
android:background="#ffffff"
app:layout_constraintBottom_toTopOf="@+id/centerGuideline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintTop_toBottomOf="@+id/maximizeButton"
android:contentDescription="@string/barcodeImageDescription"/>
<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:padding="0dp"
app:srcCompat="@drawable/ic_baseline_arrow_drop_down_24"
android:contentDescription="@string/moveBarcodeToCenterOfScreen"
android:tint="@android:color/white"
android:background="@color/colorPrimary"
app:layout_constraintTop_toBottomOf="@+id/barcode"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<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:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginLeft="10.0dip"
android:layout_marginRight="10.0dip"
android:layout_marginBottom="80dp"
app:layout_constraintTop_toBottomOf="@+id/barcode"
android:paddingBottom="80dp"
app:layout_constraintTop_toBottomOf="@+id/minimizeButton"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@@ -80,57 +132,62 @@
<LinearLayout
android:id="@+id/bottom_sheet"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp"
android:visibility="gone"
app:behavior_hideable="false"
app:behavior_peekHeight="80dp"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
app:behavior_peekHeight="104dp"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
tools:visibility="visible"
android:fitsSystemWindows="true">
<ImageButton
android:id="@+id/bottomSheetButton"
android:layout_width="match_parent"
android:layout_height="80dp"
android:background="@color/colorPrimary"
android:gravity="center"
app:srcCompat="@drawable/ic_baseline_arrow_drop_up_24"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:tint="@android:color/white" />
android:layout_gravity="top|start"
android:tint="@android:color/white"
app:srcCompat="@drawable/ic_baseline_arrow_drop_up_24" />
<TextView
android:id="@+id/noteView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:background="@color/inputBackground"
android:gravity="center"
android:padding="20dp"
app:autoSizeTextType="uniform"
app:autoSizeMinTextSize="@dimen/singleCardNoteTextSizeMin"
app:autoSizeMaxTextSize="@dimen/singleCardNoteTextSizeMax" />
android:textSize="@dimen/singleCardNoteTextSizeMin" />
<TextView
android:id="@+id/groupsView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:background="@color/inputBackground"
android:gravity="center"
android:padding="20dp"
app:autoSizeTextType="uniform"
app:autoSizeMinTextSize="@dimen/singleCardNoteTextSizeMin"
app:autoSizeMaxTextSize="@dimen/singleCardNoteTextSizeMax" />
android:textSize="@dimen/singleCardNoteTextSizeMin" />
<TextView
android:id="@+id/balanceView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/inputBackground"
android:gravity="center"
android:padding="20dp"
android:textSize="@dimen/singleCardNoteTextSizeMin" />
<TextView
android:id="@+id/expiryView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:background="@color/inputBackground"
android:gravity="center"
android:padding="20dp"
app:autoSizeTextType="uniform"
app:autoSizeMinTextSize="@dimen/singleCardNoteTextSizeMin"
app:autoSizeMaxTextSize="@dimen/singleCardNoteTextSizeMax" />
android:textSize="@dimen/singleCardNoteTextSizeMin" />
</LinearLayout>
<com.google.android.material.appbar.AppBarLayout
@@ -140,6 +197,7 @@
android:clipToPadding="false"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="24dp"
android:weightSum="1.0"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.CollapsingToolbarLayout
@@ -177,5 +235,14 @@
app:contentInsetStart="72.0dip"
app:layout_collapseMode="pin" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar_landscape"
android:visibility="gone"
android:background="@android:color/transparent"
android:theme="@style/CardView.ActionBarTheme"
android:layout_width="fill_parent"
android:layout_height="?actionBarSize"
app:contentInsetStart="72.0dip"
app:layout_collapseMode="pin" />
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/settings_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View File

@@ -22,6 +22,10 @@
android:id="@+id/action_settings"
android:title="@string/settings"
app:showAsAction="never"/>
<item
android:id="@+id/action_privacy_policy"
android:title="@string/privacy_policy"
app:showAsAction="never"/>
<item
android:id="@+id/action_about"
android:title="@string/about"

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_search">Suche</string>
<string name="action_add">Neu</string>
<string name="action_search">Suchen</string>
<string name="action_add">Hinzufügen</string>
<string name="noGiftCards">Klicken Sie auf die Schaltfläche + (plus), um zuerst eine Karte hinzuzufügen.
\n
\nCatima trägt Ihre Karten auf dem Gerät, so dass sie immer in Reichweite sind.</string>
@@ -11,7 +11,6 @@
<string name="cardId">Kartennummer</string>
<string name="cancel">Abbrechen</string>
<string name="save">Speichern</string>
<string name="editCard">Karte bearbeiten</string>
<string name="edit">Bearbeiten</string>
<string name="delete">Löschen</string>
<string name="confirm">Bestätigen</string>
@@ -21,9 +20,9 @@
<string name="unstar">Aus der Favoritenliste entfernen</string>
<string name="deleteTitle">Karte entfernen</string>
<string name="deleteConfirmation">Bitte bestätigen Sie, dass diese Karte gelöscht werden soll.</string>
<string name="ok">Ok</string>
<string name="copy_to_clipboard">Kopiere die Nummer in die Zwischenablage</string>
<string name="sendLabel">Senden…</string>
<string name="ok">OK</string>
<string name="copy_to_clipboard">Nummer in die Zwischenablage kopieren</string>
<string name="sendLabel">Senden </string>
<string name="editCardTitle">Kundenkarte bearbeiten</string>
<string name="addCardTitle">Neue Kundenkarte</string>
<string name="scanCardBarcode">Strichcode scannen</string>
@@ -63,10 +62,6 @@
<string name="thumbnailDescription">Vorschaubild für die Karte</string>
<string name="settings">Einstellungen</string>
<string name="settings_category_title_ui">Benutzeroberfläche</string>
<string name="settings_card_title_list_font_size">Schriftgröße des Kartentitels (Listenmodus)</string>
<string name="settings_card_note_list_font_size">Schriftgröße der Kartennotiz (Listenmodus)</string>
<string name="settings_card_title_font_size">Schriftgröße des Kartentitels (Vorschau)</string>
<string name="settings_card_id_font_size">Schriftgröße der Kartennummer</string>
<string name="settings_display_barcode_max_brightness">Helligkeit bei Strichcode Ansicht erhöhen</string>
<string name="settings_lock_barcode_orientation">Strichcodeausrichtung sperren</string>
<string name="exportSuccessful">Kartendaten exportiert</string>
@@ -91,9 +86,9 @@
\nGruppen machen es einfacher, Dinge zu finden.</string>
<string name="groups">Gruppen</string>
<string name="enter_group_name">Geben Sie den Gruppennamen ein</string>
<string name="leaveWithoutSaveConfirmation">Sind Sie sicher, dass Sie diesen Bildschirm verlassen wollen\? Vorgenommene Änderungen werden nicht gespeichert.</string>
<string name="leaveWithoutSaveTitle">Beenden ohne zu speichern</string>
<string name="failedOpeningFileManager">Fehler beim Öffnen eines Dateiverwaltungsprogrammes. Stellen Sie sicher, dass eine installiert ist.</string>
<string name="leaveWithoutSaveConfirmation">Beenden ohne zu speichern\?</string>
<string name="leaveWithoutSaveTitle">Beenden</string>
<string name="failedOpeningFileManager">Installieren Sie zunächst ein Dateiverwaltungsprogramm.</string>
<string name="noBarcode">Kein Strichcode</string>
<string name="addManually">Die Karten-ID manuell eingeben</string>
<string name="moveDown">In der Liste nach unten verschieben</string>
@@ -103,4 +98,56 @@
<item quantity="other"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> Karten</item>
</plurals>
<string name="groupsList">Gruppen: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
</resources>
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
<string name="chooseImportType">Daten importieren aus\?</string>
<string name="parsingBalanceFailed"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g> scheint kein gültiges Guthaben zu sein.</string>
<string name="points">Punkte</string>
<string name="currency">Währung</string>
<string name="balance">Guthaben</string>
<string name="moveBarcodeToCenterOfScreen">Strichcode auf dem Bildschirm zentrieren</string>
<string name="moveBarcodeToTopOfScreen">Strichcode auf dem Bildschirm nach oben schieben</string>
<string name="chooseExpiryDate">Ablaufdatum wählen</string>
<string name="never">Nie</string>
<string name="expiryDate">Ablaufdatum</string>
<string name="editBarcode">Strichcode bearbeiten</string>
<string name="barcode">Strichcode</string>
<string name="card">Karte</string>
<string name="balancePoints"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g> Punkte</string>
<string name="balanceSentence">Guthaben: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
<string name="expiryStateSentenceExpired">Abgelaufen: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
<string name="expiryStateSentence">Läuft ab: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
<string name="settings_disable_lockscreen_while_viewing_card">Sperrbildschirm deaktivieren während eine Karte angesehen wird</string>
<string name="settings_keep_screen_on">Bildschirm anlassen während eine Karte angesehen wird</string>
<string name="privacy_policy_popup_text">Hinweis zum Datenschutz (oft gefordert):
\n
\nWir sammeln KEINE DATEN, was jeder bestätigen kann, da unsere Anwendung eine freie Software ist.</string>
<string name="accept">Annehmen</string>
<string name="privacy_policy">Datenschutzrichtlinie</string>
<string name="importVoucherVaultMessage">Bitte wählen Sie Ihre Voucher Vault-Exportdatei aus. Es heißt höchstwahrscheinlich vouchervault.json.
\n
\nEine Voucher Vault-Exportdatei kann durch Drücken von Exportieren erstellt werden.</string>
<string name="importVoucherVault">Aus Voucher Vault importieren</string>
<string name="importLoyaltyCardKeychainMessage">Bitte wählen Sie Ihre Loyalty Card Keychain-Exportdatei aus. Es heißt höchstwahrscheinlich LoyaltyCardKeychain.csv.
\n
\nEine Loyalty Card Keychain-Exportdatei kann erstellt werden, indem Sie im Menü Import/Export auf Exportieren klicken.</string>
<string name="importLoyaltyCardKeychain">Aus Loyalty Card Keychain importieren</string>
<string name="importFidmeMessage">Bitte wählen Sie Ihre FidMe-Exportdatei aus. Es wird höchstwahrscheinlich so etwas wie fidme-export-request-xxxxxx.zip genannt.
\n
\nEine FidMe-Exportdatei kann in der FidMe-Anwendung erstellt werden, indem Sie zu Ihrem Profil gehen, Datenschutz auswählen und dann auf Meine Daten extrahieren klicken.
\n
\nBitte beachten Sie, dass FidMe den Strichcode-Typ nicht in den Exportdaten speichert, sodass Sie die importierten Karten manuell bearbeiten müssen.</string>
<string name="importFidme">Aus FidMe importieren</string>
<string name="importCatimaMessage">Bitte wählen Sie Ihre Catima-Exportdatei aus. Es heißt höchstwahrscheinlich Catima.csv.
\n
\nEine Catima-Exportdatei kann erstellt werden, indem Sie im Menü Import/Export auf Exportieren klicken.</string>
<string name="importCatima">Aus Catima importieren</string>
<string name="setBarcodeId">Strichcodewert setzen</string>
<string name="sameAsCardId">Gleich wie Karten-ID</string>
<string name="barcodeId">Strichcodewert</string>
<string name="errorReadingImage">Das Bild konnte nicht gelesen werden</string>
<string name="noBarcodeFound">Kein Strichcode gefunden</string>
<string name="addFromImage">Bild aus der Galerie auswählen</string>
<string name="settings_max_font_size_scale">Maximale Schriftgröße</string>
<string name="unsupportedBarcodeType">Dieser Strichcodetyp kann noch nicht angezeigt werden. Er wird möglicherweise in einer neueren Version der Anwendung unterstützt.</string>
<string name="wrongValueForBarcodeType">Der Wert ist für den gewählten Strichcodetyp nicht gültig</string>
</resources>

View File

@@ -1,15 +1,12 @@
<resources
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_add">Προσθήκη</string>
<string name="noGiftCards">Δεν έχετε κάρτες προς το παρόν. Πατήστε το κουμπί \"+\" (συν) στο πάνω μέρος για να ξεκινήσετε.\n\nΤο Loyalty Card Keychain σας δίνει τη δυνατότητα να έχετε τις κάρτες σας στο τηλέφωνο σας, έτσι ώστε να τις έχετε πάντα μαζί σας.</string>
<string name="storeName">Κατάστημα</string>
<string name="note">Σημείωση</string>
<string name="cardId">Κωδικός Κάρτας</string>
<string name="cancel">Άκυρο</string>
<string name="save">Αποθήκευση</string>
<string name="editCard">Επεξεργασία Κάρτας</string>
<string name="edit">Επεξεργασία</string>
<string name="delete">Διαγραφή</string>
<string name="confirm">Επιβεβαίωση</string>
@@ -17,17 +14,15 @@
<string name="unlockScreen">Περιστροφή</string>
<string name="deleteTitle">Αφαίρεση Κάρτας</string>
<string name="deleteConfirmation">Παρακαλώ επιβεβαιώστε ότι θέλετε να διαγράψετε αυτή την κάρτα.</string>
<string name="ok">OK</string>
<string name="ok">Εντάξει</string>
<string name="copy_to_clipboard">Αντιγραφή κωδικού στο πρόχειρο</string>
<string name="sendLabel">Αποστολή&#8230;</string>
<string name="sendLabel">Αποστολή</string>
<string name="editCardTitle">Επεξεργασία Κάρτας</string>
<string name="addCardTitle">Προσθήκη Κάρτας</string>
<string name="scanCardBarcode">Σαρώστε τον κωδικό της κάρτας</string>
<string name="cardShortcut">Συντόμευση Κάρτας</string>
<string name="noCardsMessage">Δεν υπάρχουν κάρτες. προσθέστε μία πρώτα</string>
<string name="barcodeImageDescription">Εικόνα του barcode της κάρτας</string>
<string name="noStoreError">Δεν δώσατε κατάστημα</string>
<string name="noCardIdError">Δεν δώσατε κωδικό κάρτας</string>
<string name="noCardExistsError">Δεν ήταν δυνατό να εντοπιστεί κάρτα</string>
@@ -40,8 +35,8 @@
<string name="exportSuccessfulTitle">Εξαγωγή επιτυχής</string>
<string name="exportFailedTitle">Εξαγωγή ανεπιτυχής</string>
<string name="exportFailed">Δεν εξήχθη</string>
<string name="importing">Γίνεται εισαγωγή του&#8230;</string>
<string name="exporting">Γίνεται εξαγωγή του&#8230;</string>
<string name="importing">Γίνεται εισαγωγή του</string>
<string name="exporting">Γίνεται εξαγωγή του</string>
<string name="noExternalStoragePermissionError">Δεν είναι δυνατή η εισαγωγή ή εξαγωγή καρτών χωρίς την άδεια πρόσβασης στον εξωτερικό χώρο αποθήκευσης</string>
<string name="importOptionFilesystemTitle">Εισαγωγή από το σύστημα αρχείων</string>
<string name="importOptionFilesystemExplanation">Επιλέξτε ένα συγκεκριμένο αρχείο από το σύστημα αρχείων.</string>
@@ -49,7 +44,6 @@
<string name="importOptionApplicationTitle">Χρήση εξωτερικής εφαρμογής</string>
<string name="importOptionApplicationExplanation">Κάντε χρήση μίας εξωτερικής εφαρμογής όπως είναι τα Dropbox, Google Drive ή ο αγαπημένος σας διαχειριστής αρχείων για να ανοίξετε ένα αρχείο.</string>
<string name="importOptionApplicationButton">Χρήση εξωτερικής εφαρμογής</string>
<string name="about">Σχετικά</string>
<string name="app_license">Άδεια χρήσης υπό το GPLv3.</string>
<string name="about_title_fmt">Σχετικά με <xliff:g id="app_name">%s</xliff:g></string>
@@ -57,16 +51,13 @@
<string name="app_revision_fmt">Πληροφορίες Αναθεώρησης: <xliff:g id="app_revision_url">%s</xliff:g></string>
<string name="app_libraries">Το <xliff:g id="app_name">%s</xliff:g> χρησιμοποιεί τις ακόλουθες βιβλιοθήκες τρίτων: <xliff:g id="app_libraries_list">%s</xliff:g></string>
<string name="app_resources">Το <xliff:g id="app_name">%s</xliff:g> χρησιμοποιεί τους παρακάτω πόρους τρίτων: <xliff:g id="app_resources_list">%s</xliff:g></string>
<string name="selectBarcodeTitle">Επιλέξτε Barcode</string>
<string name="copy_to_clipboard_toast">Ο κωδικός της κάρτας αντιγράφτηκε στο πρόχειρο</string>
<string name="thumbnailDescription">Μικρογραφία κάρτας</string>
<string name="settings">Ρυθμίσεις</string>
<string name="settings_category_title_ui">Διεπαφή χρήστη</string>
<string name="settings_card_title_list_font_size">Μέγεθος κειμένου λίστας καρτών</string>
<string name="settings_card_note_list_font_size">Μέγεθος κειμένου λίστας σημειώσεων καρτών</string>
<string name="settings_card_title_font_size">Μέγεθος κειμένου τίτλου κάρτας</string>
<string name="settings_card_id_font_size">Μέγεθος κειμένου κωδικού κάρτας</string>
</resources>
<string name="settings_dark_theme">Σκοτεινό</string>
<string name="settings_light_theme">Φωτεινό</string>
<string name="settings_system_theme">Σύστημα</string>
<string name="barcode">Γραμμικός κώδικας</string>
</resources>

View File

@@ -1,4 +1,4 @@
<resources
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="barcode">Código de barras</string>
</resources>

View File

@@ -9,7 +9,6 @@
<string name="cardId">Id. de tarjeta</string>
<string name="cancel">Cancelar</string>
<string name="save">Guardar</string>
<string name="editCard">Editar tarjeta</string>
<string name="edit">Editar</string>
<string name="delete">Eliminar</string>
<string name="confirm">Confirmar</string>
@@ -53,10 +52,6 @@
<string name="debug_version_fmt">Versión: <xliff:g id="version">%s</xliff:g></string>
<string name="settings">Configuración</string>
<string name="settings_category_title_ui">Interfaz de usuario</string>
<string name="settings_card_title_list_font_size">Tamaño de fuente del título de la tarjeta (modo lista)</string>
<string name="settings_card_note_list_font_size">Tamaño de fuente de las notas de la tarjeta (modo lista)</string>
<string name="settings_card_title_font_size">Tamaño de fuente del título de la tarjeta (vista previa)</string>
<string name="settings_card_id_font_size">Tamaño de la letra para el ID de la tarjeta</string>
<string name="settings_display_barcode_max_brightness">Iluminar vista del código de barras</string>
<string name="exportSuccessful">Datos de las tarjetas exportados</string>
<string name="importSuccessful">Datos de las tarjetas importados</string>
@@ -103,4 +98,16 @@
<item quantity="one"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> tarjeta</item>
<item quantity="other"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> tarjetas</item>
</plurals>
<string name="points">Puntos</string>
<string name="moveBarcodeToCenterOfScreen">Centrar el código de barras en la pantalla</string>
<string name="moveBarcodeToTopOfScreen">Mover el código de barras a la zona superior de la pantalla</string>
<string name="chooseExpiryDate">Elegir fecha de caducidad</string>
<string name="never">Nunca</string>
<string name="expiryDate">Fecha de caducidad</string>
<string name="editBarcode">Editar el código de barras</string>
<string name="barcode">Código de barras</string>
<string name="card">Tarjeta</string>
<string name="balancePoints"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g> puntos</string>
<string name="expiryStateSentenceExpired">Expirado: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
<string name="expiryStateSentence">Expira: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
</resources>

View File

@@ -1,15 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_add">Ajouter</string>
<string name="noGiftCards">Appuyez d\'abord sur le bouton \"+\" (plus) pour ajouter une carte.
<string name="noGiftCards">Appuyez d\'abord sur le bouton + (plus) pour ajouter une carte.
\n
\nCatima enregistre vos cartes sur votre appareil, pour toujours les avoir à portée de main.</string>
<string name="storeName">Nom du Magasin</string>
<string name="storeName">Magasin</string>
<string name="note">Notes</string>
<string name="cardId">Numéro</string>
<string name="cancel">Annuler</string>
<string name="save">Enregistrer</string>
<string name="editCard">Modifier</string>
<string name="edit">Modifier</string>
<string name="delete">Supprimer</string>
<string name="confirm">Confirmer</string>
@@ -20,7 +19,7 @@
<string name="ok">OK</string>
<string name="copy_to_clipboard">Copier le numéro dans le presse-papier</string>
<string name="sendLabel">Envoyer…</string>
<string name="editCardTitle">Modifier la carte de fidélité</string>
<string name="editCardTitle">Modifier la carte</string>
<string name="addCardTitle">Ajouter une carte de fidélité</string>
<string name="scanCardBarcode">Scanner le code-barres</string>
<string name="cardShortcut">Raccourci de carte</string>
@@ -59,10 +58,6 @@
<string name="thumbnailDescription">Miniature de la carte</string>
<string name="settings">Paramètres</string>
<string name="settings_category_title_ui">Interface utilisateur</string>
<string name="settings_card_title_list_font_size">Taille du nom des cartes (mode liste)</string>
<string name="settings_card_note_list_font_size">Taille de la description (mode liste)</string>
<string name="settings_card_title_font_size">Taille du nom de la carte (aperçu)</string>
<string name="settings_card_id_font_size">Taille des numéros de carte</string>
<string name="settings_display_barcode_max_brightness">Augmenter la luminosité du code-barres</string>
<string name="settings_lock_barcode_orientation">Verrouiller l\'orientation du code-barres</string>
<string name="exportSuccessful">Carte exportée avec succès</string>
@@ -86,15 +81,15 @@
<string name="starImage">Étoile favorite</string>
<string name="deleteConfirmationGroup">Confirmez que vous voulez supprimer ce groupe</string>
<string name="all">Tous</string>
<string name="noGroups">Cliquez d\'abord sur le bouton \"+\" (plus) pour ajouter un groupe.
<string name="noGroups">Cliquez d\'abord sur le bouton + (plus) pour ajouter un groupe.
\n
\nLes groupes permettent de facilement retrouver vos cartes.</string>
<string name="groups">Groupes</string>
<string name="enter_group_name">Entrez le nom du groupe</string>
<string name="noBarcode">Aucun code-barres</string>
<string name="leaveWithoutSaveConfirmation">Voulez-vous quitter la page \? Toutes vos modifications seront perdues.</string>
<string name="leaveWithoutSaveTitle">Quitter sans enregistrer</string>
<string name="failedOpeningFileManager">Gestionnaire de fichiers introuvable. Installez-en un si nécessaire.</string>
<string name="leaveWithoutSaveConfirmation">Quitter sans enregistrer \?</string>
<string name="leaveWithoutSaveTitle">Quitter</string>
<string name="failedOpeningFileManager">Installez dabord un gestionnaire de fichiers.</string>
<string name="addManually">Entrer manuellement l\'identifiant de la carte</string>
<string name="moveDown">Descendre dans la liste</string>
<string name="moveUp">Monter dans la liste</string>
@@ -103,4 +98,56 @@
<item quantity="other"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> cartes</item>
</plurals>
<string name="groupsList">Groupes : <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
</resources>
<string name="accept">Accepter</string>
<string name="privacy_policy_popup_text">Avis sur la politique de confidentialité (exigé par certains magasins d\'applications) :
\n
\nNous ne collectons AUCUNE DONNÉE, ce que tout le monde peut confirmer puisque notre application est un logiciel libre.</string>
<string name="privacy_policy">Politique de confidentialité</string>
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
<string name="chooseImportType">Importer les données depuis \?</string>
<string name="parsingBalanceFailed">&lt;xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\"&gt;%s&lt;/xliff:g&gt; ne semble pas être un solde valide.</string>
<string name="points">Points</string>
<string name="currency">Monnaie</string>
<string name="balance">Solde</string>
<string name="moveBarcodeToCenterOfScreen">Centrer le code-barres sur l\'écran</string>
<string name="moveBarcodeToTopOfScreen">Déplacez le code-barres vers le haut de l\'écran</string>
<string name="chooseExpiryDate">Choisissez la date d\'expiration</string>
<string name="never">Jamais</string>
<string name="expiryDate">Date d\'expiration</string>
<string name="editBarcode">Modifier le code-barres</string>
<string name="barcode">Code-barres</string>
<string name="card">Carte</string>
<string name="balancePoints">&lt;xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\"&gt;%s&lt;/xliff:g&gt; points</string>
<string name="balanceSentence">Solde : &lt;xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\"&gt;%s&lt;/xliff:g&gt;</string>
<string name="expiryStateSentenceExpired">Expiré : &lt;xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\"&gt;%s&lt;/xliff:g&gt;</string>
<string name="expiryStateSentence">Expire : &lt;xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\"&gt;%s&lt;/xliff:g&gt;</string>
<string name="settings_disable_lockscreen_while_viewing_card">Désactiver l\'écran de verrouillage pendant l\'affichage d\'une carte</string>
<string name="settings_keep_screen_on">Garder l\'écran allumé pendant l\'affichage d\'une carte</string>
<string name="importVoucherVaultMessage">Veuillez sélectionner votre fichier d\'exportation Voucher Vault. Il est probablement nommé vouchervault.json.
\n
\nUn fichier d\'exportation Voucher Vault peut être créé en appuyant sur Exporter.</string>
<string name="importVoucherVault">Importer depuis Voucher Vault</string>
<string name="importLoyaltyCardKeychainMessage">Veuillez sélectionner votre fichier d\'exportation Loyalty Card Keychain . Il s\'appelle très probablement LoyaltyCardKeychain.csv.
\n
\nUn fichier d\'exportation Loyalty Card Keychain peut être créé en allant dans le menu Importer/Exporter et en appuyant sur Exporter.</string>
<string name="importLoyaltyCardKeychain">Importer depuis Loyalty Card Keychain</string>
<string name="importFidmeMessage">Veuillez sélectionner votre fichier d\'exportation FidMe. Il est très probablement nommé quelque chose comme fidme-export-request-xxxxxx.zip.
\n
\nUn fichier d\'exportation FidMe peut être créé dans l\'application FidMe en accédant à votre profil, en choisissant Protection des données, puis en appuyant sur Extraire mes données.
\n
\nVeuillez noter que FidMe ne stocke pas le type de code-barres dans les données d\'exportation, vous devrez donc éditer les cartes importées manuellement.</string>
<string name="importFidme">Importer depuis FidMe</string>
<string name="importCatimaMessage">Veuillez sélectionner votre fichier d\'exportation Catima. Il est probablement nommé Catima.csv.
\n
\nUn fichier d\'exportation Catima peut être créé en allant dans le menu Importer/Exporter et en appuyant sur Exporter.</string>
<string name="importCatima">Importer depuis Catima</string>
<string name="addFromImage">Sélectionner dans la galerie</string>
<string name="errorReadingImage">Impossible de lire l\'image</string>
<string name="noBarcodeFound">Aucun code-barres n\'a été trouvé</string>
<string name="setBarcodeId">Définir la valeur du code-barres</string>
<string name="sameAsCardId">Identique à lidentifiant de la carte</string>
<string name="barcodeId">Valeur du code-barres</string>
<string name="settings_max_font_size_scale">Échelle maximale de taille de police</string>
<string name="unsupportedBarcodeType">Ce type de code-barres ne peut pas encore être affiché. Il sera peut-être pris en charge dans une version plus récente de l\'application.</string>
<string name="wrongValueForBarcodeType">La valeur n\'est pas valide pour le type de code-barres sélectionné</string>
</resources>

View File

@@ -6,5 +6,4 @@
<string name="cardId">מזהה כרטיס</string>
<string name="cancel">ביטול</string>
<string name="save">שמור</string>
<string name="editCard">עריכת כרטיס</string>
</resources>
</resources>

View File

@@ -12,7 +12,6 @@
<string name="barcodeNoBarcode">Questa carta non ha un codice a barre</string>
<string name="cancel">Annulla</string>
<string name="save">Salva</string>
<string name="editCard">Modifica carta</string>
<string name="edit">Modifica</string>
<string name="delete">Elimina</string>
<string name="confirm">Conferma</string>
@@ -20,7 +19,7 @@
<string name="unlockScreen">Sblocca rotazione</string>
<string name="deleteTitle">Rimuovi carta fedeltà</string>
<string name="deleteConfirmation">Conferma di voler eliminare questa carta.</string>
<string name="ok">Ok</string>
<string name="ok">OK</string>
<string name="copy_to_clipboard">Copia ID negli appunti</string>
<string name="share">Condividi</string>
<string name="sendLabel">Invia…</string>
@@ -69,10 +68,6 @@
<string name="settings_system_theme">Sistema</string>
<string name="settings_light_theme">Chiaro</string>
<string name="settings_dark_theme">Scuro</string>
<string name="settings_card_title_list_font_size">Dimensione caratteri del titolo della carta (modalità elenco)</string>
<string name="settings_card_note_list_font_size">Dimensione caratteri nota della carta (modalità elenco)</string>
<string name="settings_card_title_font_size">Dimensione caratteri del titolo della carta (anteprima)</string>
<string name="settings_card_id_font_size">Dimensione testo dell\'ID carta</string>
<string name="settings_display_barcode_max_brightness">Aumenta luminosità dello schermo quando apro un codice a barre</string>
<string name="settings_lock_barcode_orientation">Blocca orientamento del codice a barre</string>
<string name="intent_import_card_from_url_share_text">Voglio condividere una carta fedeltà con te</string>
@@ -93,14 +88,66 @@
<string name="enter_group_name">Inserisci il nome del gruppo</string>
<string name="groupsList">Gruppi: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
<string name="addManually">Inserisci manualmente l\'ID della carta</string>
<string name="leaveWithoutSaveConfirmation">Sei sicuro/a di voler uscire da questa schermata\? Le modifiche apportate non verranno salvate.</string>
<string name="leaveWithoutSaveTitle">Esci senza salvare</string>
<string name="leaveWithoutSaveConfirmation">Uscire senza salvare\?</string>
<string name="leaveWithoutSaveTitle">Esci</string>
<string name="moveDown">Sposta in basso nell\'elenco</string>
<string name="moveUp">Sposta in alto nell\'elenco</string>
<string name="failedOpeningFileManager">Apertura di un gestore di file non riuscita. Assicurati che ne sia installato uno.</string>
<string name="failedOpeningFileManager">Installa prima un gestore di file.</string>
<string name="noBarcode">Nessun codice a barre</string>
<plurals name="groupCardCount">
<item quantity="one"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> carta</item>
<item quantity="other"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> carte</item>
</plurals>
</resources>
<string name="parsingBalanceFailed">&lt;xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\"&gt;%s&lt;/xliff:g&gt; non sembra un saldo corretto.</string>
<string name="points">Punti</string>
<string name="currency">Valuta</string>
<string name="balance">Saldo</string>
<string name="moveBarcodeToCenterOfScreen">Centra il codice a barre sullo schermo</string>
<string name="moveBarcodeToTopOfScreen">Sposta il codice a barre in cima allo schermo</string>
<string name="chooseExpiryDate">Scegli data di scadenza</string>
<string name="never">Mai</string>
<string name="expiryDate">Data di scadenza</string>
<string name="editBarcode">Modifica il codice a barre</string>
<string name="barcode">Codice a barre</string>
<string name="card">Carta</string>
<string name="balancePoints"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g> punti</string>
<string name="balanceSentence">Saldo: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
<string name="expiryStateSentenceExpired">Scaduta: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
<string name="expiryStateSentence">Scade: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
<string name="settings_keep_screen_on">Mantieni schermo acceso durante la visualizzazione di una carta</string>
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
<string name="chooseImportType">Importare i dati da\?</string>
<string name="settings_disable_lockscreen_while_viewing_card">Mantieni schermo attivo mentre visualizzi una carta</string>
<string name="accept">Accetta</string>
<string name="privacy_policy_popup_text">Informativa sulla riservatezza (spesso richiesta):
\n
\nNon raccogliamo NESSUN dato, cosa che chiunque può confermare dato che la nostra applicazione è un software libero.</string>
<string name="privacy_policy">Informativa sulla riservatezza</string>
<string name="importVoucherVaultMessage">Seleziona il tuo file di esportazione Voucher Vault. Molto probabilmente si chiama vouchervault.json.
\n
\nÈ possibile creare un file di esportazione Voucher Vault premendo Esporta.</string>
<string name="importVoucherVault">Importa da Voucher Vault</string>
<string name="importLoyaltyCardKeychainMessage">Seleziona il file di esportazione Loyalty Card Keychain . Molto probabilmente si chiama LoyaltyCardKeychain.csv.
\n
\nÈ possibile creare un file di esportazione Loyalty Card Keychain accedendo al menu Importa / Esporta e premendo Esporta.</string>
<string name="importLoyaltyCardKeychain">Importa da Loyalty Card Keychain</string>
<string name="importFidmeMessage">Seleziona il tuo file di esportazione FidMe. Molto probabilmente si chiama qualcosa come fidme-export-request-xxxxxx.zip.
\n
\nUn file di esportazione FidMe può essere creato nell\'app FidMe andando sul tuo profilo, scegliendo Protezione dati e quindi premendo Estrai i miei dati.
\n
\nTieni presente che FidMe non memorizza il tipo di codice a barre nei dati di esportazione, quindi dovrai modificare manualmente le carte importate.</string>
<string name="importFidme">Importa da FidMe</string>
<string name="importCatimaMessage">Seleziona il tuo file di esportazione Catima. Molto probabilmente si chiama Catima.csv.
\n
\nÈ possibile creare un file di esportazione Catima andando nel menu Importa / Esporta e premendo Esporta.</string>
<string name="importCatima">Importa da Catima</string>
<string name="setBarcodeId">Imposta il valore del codice a barre</string>
<string name="sameAsCardId">Uguale all\'ID della carta</string>
<string name="barcodeId">Valore del codice a barre</string>
<string name="errorReadingImage">Impossibile leggere l\'immagine</string>
<string name="noBarcodeFound">Nessun codice a barre trovato</string>
<string name="addFromImage">Seleziona immagine dalla galleria</string>
<string name="settings_max_font_size_scale">Scala massima delle dimensioni dei caratteri</string>
<string name="unsupportedBarcodeType">Questo tipo di codice a barre non può ancora essere visualizzato. Potrebbe essere supportato in una versione più recente dell\'applicazione.</string>
<string name="wrongValueForBarcodeType">Il valore non è valido per il tipo di codice a barre selezionato</string>
</resources>

View File

@@ -6,10 +6,6 @@
<string name="groups">그룹</string>
<string name="enter_group_name">그룹 이름 입력</string>
<string name="settings_lock_barcode_orientation">바코드 회전 잠금</string>
<string name="settings_card_id_font_size">카드 ID 글꼴 크기</string>
<string name="settings_card_title_font_size">카드 제목 글꼴 크기 (미리 보기)</string>
<string name="settings_card_note_list_font_size">카드 노트 글꼴 크기 (목록 모드)</string>
<string name="settings_card_title_list_font_size">카드 제목 글꼴 크기 (목록 모드)</string>
<string name="settings_dark_theme">어두움</string>
<string name="settings_light_theme">밝음</string>
<string name="debug_version_fmt">버전: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" id="version">%s</xliff:g></string>
@@ -18,7 +14,7 @@
<string name="settings_theme">테마</string>
<string name="settings_category_title_ui">사용자 인터페이스</string>
<string name="settings">설정</string>
<string name="enterBarcodeInstructions">카드 ID를 입력하고 카드에서 사용하는 바코드 이미지를 선택하세요. 바코드를 사용하지 않는 경우 \"이 카드는 바코드가 없음\"을 선택하세요.</string>
<string name="enterBarcodeInstructions">카드 ID를 입력하고 카드에서 사용하는 바코드 이미지를 선택하세요. 바코드를 사용하지 않는 경우 이 카드는 바코드가 없음을 선택하세요.</string>
<string name="app_resources"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" id="app_name">%s</xliff:g> 앱은 다음 서드 파티 리소스를 사용합니다: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" id="app_resources_list">%s</xliff:g></string>
<string name="app_libraries"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" id="app_name">%s</xliff:g> 앱은 다음 서드 파티 라이브러리를 사용합니다: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" id="app_libraries_list">%s</xliff:g></string>
<string name="app_revision_fmt">리비전 정보: &lt;xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\" id=\"app_revision_url\"&gt;%s&lt;/xliff:g&gt;</string>
@@ -44,7 +40,6 @@
<string name="confirm">확인</string>
<string name="delete">삭제</string>
<string name="edit">편집</string>
<string name="editCard">카드 편집</string>
<string name="save">저장</string>
<string name="cancel">취소</string>
<string name="unstar">즐겨찾기에서 제거</string>
@@ -84,4 +79,5 @@
<string name="noStoreError">매장을 입력하지 않음</string>
<string name="starImage">즐겨찾기 별</string>
<string name="settings_display_barcode_max_brightness">바코드를 표시할 때 화면 밝기 높이기</string>
<string name="barcode">바코드</string>
</resources>

View File

@@ -10,7 +10,6 @@
<string name="barcodeType">Strekkodetype</string>
<string name="cancel">Avbryt</string>
<string name="save">Lagre</string>
<string name="editCard">Rediger kort</string>
<string name="edit">Rediger</string>
<string name="delete">Slett</string>
<string name="confirm">Bekreft</string>
@@ -54,7 +53,7 @@
<string name="about_title_fmt">Om <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" id="app_name">%s</xliff:g></string>
<string name="debug_version_fmt">Versjon: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" id="version">%s</xliff:g></string>
<string name="app_revision_fmt">Utgivelsesinfo: <xliff:g id="app_revision_url">%s</xliff:g></string>
<string name="app_libraries"><xliff:g id="app_name">%s</xliff:g> brukfer følgende tredjepartsbibliotek: <xliff:g id="app_libraries_list">%s</xliff:g></string>
<string name="app_libraries"><xliff:g id="app_name">%s</xliff:g> bruker følgende tredjepartsbibliotek: <xliff:g id="app_libraries_list">%s</xliff:g></string>
<string name="app_resources"><xliff:g id="app_name">%s</xliff:g> bruker følgende tredjepartsressurser: <xliff:g id="app_resources_list">%s</xliff:g></string>
<string name="selectBarcodeTitle">Velg strekkode</string>
<string name="enterBarcodeInstructions">Skriv inn strekkodeverdien og velg så bildet som representerer strekkoden du ønsker å bruke.</string>
@@ -62,10 +61,6 @@
<string name="thumbnailDescription">Miniatyrbilde for kort</string>
<string name="settings">Innstillinger</string>
<string name="settings_category_title_ui">Brukergrensesnitt</string>
<string name="settings_card_title_list_font_size">Korttittelskriftstørrelse (listemodus)</string>
<string name="settings_card_note_list_font_size">Skriftstørrelse for kortmerknadsliste (listemodus)</string>
<string name="settings_card_title_font_size">Skriftstørrelse for korttittel (forhåndsvisning)</string>
<string name="settings_card_id_font_size">Skriftstørrelse for kort-ID</string>
<string name="settings_display_barcode_max_brightness">Lysere strekkodevisning</string>
<string name="settings_lock_barcode_orientation">Lås strekkodesideretning</string>
<string name="exportSuccessful">Kortdata eksportert</string>
@@ -92,9 +87,9 @@
<string name="groups">Grupper</string>
<string name="enter_group_name">Skriv inn gruppenavn</string>
<string name="noBarcode">Ingen strekkode</string>
<string name="failedOpeningFileManager">Klarte ikke å åpne noen filbehandler. Forsikre deg om at du har installert en.</string>
<string name="leaveWithoutSaveConfirmation">Er du sikker på at du vil forlate denne skjermen\? Endringer vil ikke bli lagret.</string>
<string name="leaveWithoutSaveTitle">Forlat uten å lagre</string>
<string name="failedOpeningFileManager">Installer en filbehandler først.</string>
<string name="leaveWithoutSaveConfirmation">Forlat uten å lagre\?</string>
<string name="leaveWithoutSaveTitle">Avslutt</string>
<string name="addManually">Skriv inn kort-ID manuelt</string>
<string name="moveDown">Flytt nedover i listen</string>
<string name="moveUp">Flytt oppover i listen</string>
@@ -110,4 +105,49 @@
<string name="chooseExpiryDate">Velg utløpsdato</string>
<string name="never">Aldri</string>
<string name="expiryDate">Utløpsdato</string>
</resources>
<string name="expiryStateSentenceExpired">Utløpt: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
<string name="moveBarcodeToCenterOfScreen">Sentrer strekkoden på skjermen</string>
<string name="moveBarcodeToTopOfScreen">Flytt strekkoden til toppen av skjermen</string>
<string name="parsingBalanceFailed"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g> ser ikke ut til å være en gyldig saldo.</string>
<string name="points">Poeng</string>
<string name="currency">Valuta</string>
<string name="balance">Saldo</string>
<string name="balancePoints"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g> poeng</string>
<string name="balanceSentence">Saldo: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
<string name="chooseImportType">Importer data fra\?</string>
<string name="app_loyalty_card_keychain">Kundekortsknippe</string>
<string name="settings_disable_lockscreen_while_viewing_card">Skru av låseskjerm under kortvisning</string>
<string name="settings_keep_screen_on">Behold skjerm påslått under kortvisning</string>
<string name="privacy_policy_popup_text">Mange programbutikker krever at programmer viser personvernspraksisen sin ved første oppstart. Her er vår:
\n
\nVi SAMLER IKKE IN NOEN DATA overhodet, og programmet vårt er fri programvare, så alle kan bekrefte at dette stemmer.</string>
<string name="accept">Godta</string>
<string name="privacy_policy">Personvernspraksis</string>
<string name="importFidme">Importer fra FidMe</string>
<string name="importCatima">Importer fra Catima</string>
<string name="errorReadingImage">Klarte ikke å lese bildet</string>
<string name="noBarcodeFound">Fant ingen strekkode</string>
<string name="addFromImage">Velg bilde fra galleri</string>
<string name="unsupportedBarcodeType">Denne strekkodetypen kan ikke vises for øyeblikket. Støtte kan bli lagt til i en senere versjon av programmet.</string>
<string name="setBarcodeId">Sett strekkodeverdi</string>
<string name="sameAsCardId">Samme som kort-ID</string>
<string name="barcodeId">Strekkodeverdi</string>
<string name="importVoucherVaultMessage">Velg din Voucher Vault-eksportfil. Antagelig vouchervault.json.
\n
\nEn Voucher Vault-eksportfil kan opprettes ved å trykke «Eksporter».</string>
<string name="importVoucherVault">Importer fra Voucher Vault</string>
<string name="importLoyaltyCardKeychainMessage">Velg din Kundekortknippe-eksportfil. Antagelig LoyaltyCardKeychain.csv.
\n
\nDen kan opprettes ved å gå til import/eksport-menyen der og trykke «Eksporter».</string>
<string name="importLoyaltyCardKeychain">Importer fra Kundekortknippe</string>
<string name="importFidmeMessage">Velg din FidMe-eksportfil. Antagelig fidme-export-request-xxxxxx.zip.
\n
\nDen kan opprettes i FidMe-programmet ved å gå til profilen din der, velge \"Databeskyttelse\", og så trykke «Pakk ut dataen min».
\n
\nMerk deg at FidMe ikke lagrer strekkodetypen i eksportdata, så du må redigere importerte kort manuelt.</string>
<string name="importCatimaMessage">Velg din Catima-eksportfil. Antagelig Catima.csv.
\n
\nDen kan eksporteres ved å gå til import/eksport-menyen og trykke «Eksporter».</string>
<string name="settings_max_font_size_scale">Maksimal skriftstørrelse</string>
<string name="wrongValueForBarcodeType">Verdien er ikke gyldig for valgt strekkodetype</string>
</resources>

View File

@@ -13,7 +13,6 @@
<string name="barcodeNoBarcode">Deze kaart heeft geen barcode</string>
<string name="cancel">Annuleren</string>
<string name="save">Opslaan</string>
<string name="editCard">Kaart bewerken</string>
<string name="edit">Bewerken</string>
<string name="delete">Verwijderen</string>
<string name="confirm">Bevestigen</string>
@@ -71,10 +70,6 @@
<string name="settings_system_theme">Systeem</string>
<string name="settings_light_theme">Licht</string>
<string name="settings_dark_theme">Donker</string>
<string name="settings_card_title_list_font_size">Lettergrootte van kaartnamen (lijstmodus)</string>
<string name="settings_card_note_list_font_size">Lettergrootte van aantekeningen (lijstmodus)</string>
<string name="settings_card_title_font_size">Lettergrootte van kaartnamen (voorvertoning)</string>
<string name="settings_card_id_font_size">Lettergrootte van kaartnummer</string>
<string name="settings_display_barcode_max_brightness">Scherm helderder maken bij tonen van barcode</string>
<string name="settings_lock_barcode_orientation">Barcode-oriëntatie vergrendelen</string>
<string name="intent_import_card_from_url_share_text">Ik wil een klantenkaart met je delen</string>
@@ -92,11 +87,11 @@
<string name="unstar">Verwijderen uit favorieten</string>
<string name="star">Toevoegen aan favorieten</string>
<string name="addManually">Kaartnummer handmatig invoeren</string>
<string name="leaveWithoutSaveConfirmation">Weet je zeker dat je dit wilt\? Hierdoor worden je aanpassingen niet opgeslagen.</string>
<string name="leaveWithoutSaveTitle">Afbreken zonder opslaan</string>
<string name="leaveWithoutSaveConfirmation">Weet je zeker dat je niet wilt opslaan\?</string>
<string name="leaveWithoutSaveTitle">Afsluiten</string>
<string name="moveDown">Omlaag verplaatsen</string>
<string name="moveUp">Omhoog verplaatsen</string>
<string name="failedOpeningFileManager">Kan de bestandsbeheerder niet openen - zorg dat je er een hebt geïnstalleerd.</string>
<string name="failedOpeningFileManager">Installeer een bestandsbeheerder.</string>
<string name="noBarcode">Geen barcode</string>
<plurals name="groupCardCount">
<item quantity="one"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> kaart</item>
@@ -110,4 +105,49 @@
<string name="never">Nooit</string>
<string name="expiryDate">Vervaldatum</string>
<string name="expiryStateSentence">Vervalt op <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
</resources>
<string name="expiryStateSentenceExpired">Verlopen: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
<string name="moveBarcodeToCenterOfScreen">Barcode verplaatsen naar midden van scherm</string>
<string name="moveBarcodeToTopOfScreen">Barcode verplaatsen naar bovenkant van scherm</string>
<string name="parsingBalanceFailed"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g> lijkt geen geldig saldo te zijn.</string>
<string name="points">Aantal punten</string>
<string name="currency">Valuta</string>
<string name="balance">Saldo</string>
<string name="balancePoints"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g> punten</string>
<string name="balanceSentence">Saldo: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
<string name="chooseImportType">Gegevens importeren uit…\?</string>
<string name="app_loyalty_card_keychain">Klantenkaartkluis</string>
<string name="settings_disable_lockscreen_while_viewing_card">Vergrendelscherm uitschakelen tijdens tonen van kaart</string>
<string name="settings_keep_screen_on">Scherm niet uitschakelen tijdens tonen van kaart</string>
<string name="privacy_policy_popup_text">Privacybeleid (vereist door sommige appwinkels):
\n
\nWe verzamelen HELEMAAL NIKS. Bovendien is onze app open source, zodat een ieder met eigen ogen kan zien wat de app wel of niet doet.</string>
<string name="privacy_policy">Privacybeleid</string>
<string name="accept">Accepteren</string>
<string name="importVoucherVaultMessage">Kies het gewenste Voucher Vault-exportbestand - normaliter heet dit bestand \'vouchervault.json\'.
\n
\nGa naar het Exportmenu om een Voucher Vault-exportbestand samen te stellen.</string>
<string name="importVoucherVault">Importeren uit Voucher Vault</string>
<string name="importLoyaltyCardKeychainMessage">Kies het gewenste Klantenkaartkluis-exportbestand - normaliter heet dit bestand \'LoyaltyCardKeychain.csv\'.
\n
\nGa naar het Import-/Exportmenu om een Klantenkaartkluis-exportbestand samen te stellen.</string>
<string name="importLoyaltyCardKeychain">Importeren uit Klantenkaartkluis</string>
<string name="importFidmeMessage">Kies het gewenste FidMe-exportbestand - normaliter heet dit bestand \'fidme-export-request-xxxxxx.zip\'.
\n
\nOpen de FidMe-app, ga naar je profiel en druk op \'Gegevensbescherming\' om een exportbestand samen te stellen.
\n
\nLet op: FidMe slaat de soorten barcodes niet op in het exportbestand, dus dat moet je achteraf nog instellen.</string>
<string name="importCatimaMessage">Kies het gewenste Catima-exportbestand - normaliter heet dit bestand \'Catima.csv\'.
\n
\nGa naar het Import-/Exportmenu om een Catima-exportbestand samen te stellen.</string>
<string name="importFidme">Importeren uit FidMe</string>
<string name="importCatima">Importeren uit Catima</string>
<string name="errorReadingImage">De afbeelding kan niet worden uitgelezen</string>
<string name="noBarcodeFound">Geen barcode aangetroffen</string>
<string name="addFromImage">Afbeelding kiezen uit galerij</string>
<string name="setBarcodeId">Barcodewaarde instellen</string>
<string name="sameAsCardId">Gelijk aan kaart-id</string>
<string name="barcodeId">Barcodewaarde</string>
<string name="settings_max_font_size_scale">Maximale tekstgrootte</string>
<string name="unsupportedBarcodeType">Dit type barcode kan nog niet worden getoond - we hopen hiervoor in een nieuwere versie ondersteuning toe te voegen.</string>
<string name="wrongValueForBarcodeType">Deze waarde komt niet overeen met het gekozen barcodetype</string>
</resources>

View File

@@ -10,7 +10,6 @@
<string name="barcodeNoBarcode">Ta karta nie ma kodu kreskowego</string>
<string name="cancel">Anuluj</string>
<string name="save">Zapisz</string>
<string name="editCard">Edytuj kartę</string>
<string name="edit">Edytuj</string>
<string name="delete">Usuń</string>
<string name="confirm">Potwierdź</string>
@@ -58,7 +57,7 @@
<string name="app_libraries"><xliff:g id="app_name">%s</xliff:g> wykorzystuje następujące biblioteki osób trzecich: <xliff:g id="app_libraries_list">%s</xliff:g></string>
<string name="app_resources"><xliff:g id="app_name">%s</xliff:g> wykorzystuje następujące zasoby osób trzecich: <xliff:g id="app_resources_list">%s</xliff:g></string>
<string name="selectBarcodeTitle">Wybierz kod kreskowy</string>
<string name="enterBarcodeInstructions">Wprowadź identyfikator karty, a następnie wybierz obraz reprezentujący kod kreskowy, którego chcesz użyć, lub wybierz Ta karta nie ma kodu kreskowego”, aby nie używać kodu kreskowego.</string>
<string name="enterBarcodeInstructions">Wprowadź identyfikator karty, a następnie wybierz obraz reprezentujący kod kreskowy, którego chcesz użyć, lub wybierz Ta karta nie ma kodu kreskowego”, aby nie używać kodu kreskowego.</string>
<string name="copy_to_clipboard_toast">Skopiowano identyfikator karty do schowka</string>
<string name="thumbnailDescription">Miniaturka karty</string>
<string name="settings">Ustawienia</string>
@@ -67,10 +66,6 @@
<string name="settings_system_theme">Systemowy</string>
<string name="settings_light_theme">Jasny</string>
<string name="settings_dark_theme">Ciemny</string>
<string name="settings_card_title_list_font_size">Rozmiar czcionki listy tytułów kart</string>
<string name="settings_card_note_list_font_size">Rozmiar czcionki listy kart</string>
<string name="settings_card_title_font_size">Rozmiar czcionki tytułu karty</string>
<string name="settings_card_id_font_size">Rozmiar czcionki identyfikatora karty</string>
<string name="settings_display_barcode_max_brightness">Rozjaśnij widok kodu kreskowego</string>
<string name="settings_lock_barcode_orientation">Zablokuj autoobracanie kodów kreskowych</string>
<string name="intent_import_card_from_url_share_text">Chcę udostępnić Ci kartę lojalnościową</string>
@@ -90,4 +85,4 @@
<string name="star">Dodaj do ulubionych</string>
<string name="noBarcode">Brak kodu kreskowego</string>
<string name="barcodeType">Typ kodu kreskowego</string>
</resources>
</resources>

View File

@@ -1,31 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_search">Поиск</string>
<string name="action_add">Добавить карту</string>
<string name="noGiftCards">Нажмите кнопку + (плюс), чтобы сначала добавить карту.
<string name="action_add">Добавить</string>
<string name="noGiftCards">Нажмите кнопку + (плюс), чтобы добавить карту.
\n
\nCatima хранит ваши карты на устройстве, поэтому они всегда под рукой.</string>
<string name="noMatchingGiftCards">"Ничего не найдено. Попробуйте изменить свой поиск."</string>
<string name="noMatchingGiftCards">Ничего не найдено. Попробуйте изменить поисковый запрос.</string>
<string name="storeName">Магазин</string>
<string name="note">Примечание</string>
<string name="cardId">Номер карты</string>
<string name="barcodeType">Тип штрихкода</string>
<string name="barcodeNoBarcode">Эта карта без штрихкода</string>
<string name="barcodeType">Тип штрих-кода</string>
<string name="barcodeNoBarcode">Эта карта без штрих-кода</string>
<string name="cancel">Отменить</string>
<string name="save">Сохранить</string>
<string name="editCard">Редактировать штрих-код</string>
<string name="edit">Редактировать</string>
<string name="edit">Изменить</string>
<string name="delete">Удалить карту</string>
<string name="confirm">Подтвердить</string>
<string name="lockScreen">Блокировать поворот экрана</string>
<string name="unlockScreen">Автоповорот экрана</string>
<string name="deleteTitle">Удаление карты</string>
<string name="deleteConfirmation">Пожалуйста подтвердите удаление карты.</string>
<string name="deleteConfirmation">Подтвердите удаление карты.</string>
<string name="ok">ОК</string>
<string name="copy_to_clipboard">Скопировать номер карты в буфер обмена</string>
<string name="copy_to_clipboard">Копировать номер карты</string>
<string name="share">Переслать</string>
<string name="sendLabel">Отправить…</string>
<string name="editCardTitle">Редактировать карту</string>
<string name="editCardTitle">Изменить карту</string>
<string name="addCardTitle">Добавить карту</string>
<string name="scanCardBarcode">Отсканируйте штрих-код</string>
<string name="cardShortcut">Ярлык карты</string>
@@ -34,16 +33,16 @@
<string name="noStoreError">Название магазина не указано</string>
<string name="noCardIdError">Номер карты не указан</string>
<string name="noCardExistsError">Карта не найдена</string>
<string name="failedParsingImportUriError">Не удалось разобрать импортируемый URI</string>
<string name="failedParsingImportUriError">Невозможно разобрать импортируемый URI</string>
<string name="importExport">Импорт/Экспорт</string>
<string name="exportName">Экспорт</string>
<string name="importExportHelp">Резервное копирование карт позволяет переместить их на другое устройство.</string>
<string name="importSuccessfulTitle">Импортировано</string>
<string name="importFailedTitle">Импорт не удался</string>
<string name="importFailed">Не удалось импортировать карты</string>
<string name="importFailedTitle">Импорт не выполнен</string>
<string name="importFailed">Невозможно импортировать карты</string>
<string name="exportSuccessfulTitle">Экспортировано</string>
<string name="exportFailedTitle">Экспорт не удался</string>
<string name="exportFailed">Не удалось экспортировать</string>
<string name="exportFailedTitle">Экспорт не выполнен</string>
<string name="exportFailed">Невозможно экспортировать карты</string>
<string name="importing">Импорт…</string>
<string name="exporting">Экспорт…</string>
<string name="noExternalStoragePermissionError">Импорт или экспорт невозможен без разрешения на доступ к хранилищу</string>
@@ -54,15 +53,15 @@
<string name="importOptionApplicationTitle">Использование другого приложения</string>
<string name="importOptionApplicationExplanation">Используйте любое приложение или ваш любимый файловый менеджер, чтобы открыть файл.</string>
<string name="importOptionApplicationButton">Использовать другое приложение</string>
<string name="about">О программе</string>
<string name="about">О приложении</string>
<string name="app_license">Авторское лево свободного программного обеспечения, лицензия GPLv3+.</string>
<string name="about_title_fmt">О программе <xliff:g id="app_name">%s</xliff:g></string>
<string name="about_title_fmt">О приложении <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" id="app_name">%s</xliff:g></string>
<string name="debug_version_fmt">Версия: <xliff:g id="version">%s</xliff:g></string>
<string name="app_revision_fmt">Информация о версиях: <xliff:g id="app_revision_url">%s</xliff:g></string>
<string name="app_revision_fmt">Информация о версиях: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" id="app_revision_url">%s</xliff:g></string>
<string name="app_libraries"><xliff:g id="app_name">%s</xliff:g> использует следующие сторонние библиотеки: <xliff:g id="app_libraries_list">%s</xliff:g></string>
<string name="app_resources"><xliff:g id="app_name">%s</xliff:g> использует следующие сторонние ресурсы: <xliff:g id="app_resources_list">%s</xliff:g></string>
<string name="selectBarcodeTitle">Выбор штрих-кода</string>
<string name="enterBarcodeInstructions">Введите ID карты и выберите тип штрих-кода.</string>
<string name="enterBarcodeInstructions">Введите номер карты и выберите тип штрих-кода. Или выберите \"Эта карта без штрих-кода\", чтобы не использовать его.</string>
<string name="copy_to_clipboard_toast">Номер карты скопирован в буфер обмена</string>
<string name="thumbnailDescription">Логотип карты</string>
<string name="settings">Настройки</string>
@@ -71,28 +70,86 @@
<string name="settings_system_theme">Системная</string>
<string name="settings_light_theme">Светлая</string>
<string name="settings_dark_theme">Тёмная</string>
<string name="settings_card_title_list_font_size">Размер шрифта для названий карт</string>
<string name="settings_card_note_list_font_size">Размер шрифта примечания для списка</string>
<string name="settings_card_title_font_size">Размер шрифта названия карты</string>
<string name="settings_card_id_font_size">Размер шрифта номера карты</string>
<string name="settings_display_barcode_max_brightness">Максимальная яркость при показе карты</string>
<string name="settings_lock_barcode_orientation">Портретная ориентация экрана при показе карты</string>
<string name="intent_import_card_from_url_share_text">Я хочу поделиться картой с вами</string>
<string name="exportSuccessful">Данные карты экспортированы</string>
<string name="exportSuccessful">Данные карт успешно экспортированы</string>
<string name="all">Все</string>
<string name="noGroups">Нажмите кнопку + (плюс), чтобы сначала добавить группы.
<string name="noGroups">Нажмите кнопку + (плюс), чтобы добавить группы.
\n
\nГруппы упрощают поиск.</string>
<string name="groups">Группы</string>
<string name="enter_group_name">Введите название группы</string>
<string name="importSuccessful">Данные карты лояльности успешно импортированы</string>
<string name="starImage">Любимая звезда</string>
<string name="importSuccessful">Данные карт успешно импортированы</string>
<string name="starImage">Звезда избранного</string>
<string name="app_copyright_old">На основе Loyalty Card Keychain, авторские права 20162020 Branden Archer.</string>
<string name="unstar">Удалить из избранного</string>
<string name="star">Добавить в избранное</string>
<string name="noBarcode">Нет штрих-кода</string>
<string name="deleteConfirmationGroup">Подтвердите, что хотите удалить эту группу</string>
<string name="leaveWithoutSaveConfirmation">Вы уверены, что хотите покинуть этот экран\? Изменения не будут сохранены.</string>
<string name="leaveWithoutSaveTitle">Выйти без сохранения</string>
<string name="failedOpeningFileManager">Не удалось открыть файловый менеджер. Пожалуйста, убедитись, что он установлен.</string>
<string name="leaveWithoutSaveConfirmation">Выйти без сохранения\?</string>
<string name="leaveWithoutSaveTitle">Выход</string>
<string name="failedOpeningFileManager">Требуется файловый менеджер.</string>
<string name="balancePoints"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g> баллов</string>
<string name="balanceSentence">Баланс: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
<string name="expiryStateSentenceExpired">Истёк срок действия: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
<string name="expiryStateSentence">Истекает срок действия: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
<string name="points">Баллы</string>
<string name="parsingBalanceFailed"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g> не похож на правильный баланс.</string>
<string name="addManually">Ручной ввод номера карты</string>
<string name="privacy_policy_popup_text">Уведомление о политике конфиденциальности (требуется некоторыми магазинами приложений):
\n
\nМы ВООБЩЕ НЕ собираем НИКАКИХ ДАННЫХ, что может подтвердить любой, так как наше приложение является свободным программным обеспечением.</string>
<string name="privacy_policy">Политика конфиденциальности</string>
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
<string name="chooseImportType">Откуда импортировать данные\?</string>
<string name="balance">Баланс</string>
<string name="moveBarcodeToTopOfScreen">Переместить штрих-код в верхнюю часть экрана</string>
<string name="moveBarcodeToCenterOfScreen">Центрировать штрих-код на экране</string>
<string name="currency">Валюта</string>
<string name="chooseExpiryDate">Указать срок действия</string>
<string name="never">Никогда</string>
<string name="expiryDate">Окончание срока действия</string>
<string name="editBarcode">Изменить штрих-код</string>
<string name="barcode">Штрих-код</string>
<string name="card">Карта</string>
<string name="groupsList">Группы: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
<string name="moveDown">Переместить ниже в вписке</string>
<string name="moveUp">Переместить выше в вписке</string>
<string name="settings_disable_lockscreen_while_viewing_card">Не блокировать экран при показе карты</string>
<string name="settings_keep_screen_on">Не отключать экран при показе карты</string>
<plurals name="groupCardCount">
<item quantity="one"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> карта</item>
<item quantity="few"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> карты</item>
<item quantity="many"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> карт</item>
<item quantity="other"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> карт</item>
</plurals>
<string name="accept">Принять</string>
<string name="importVoucherVaultMessage">Выберите файл экспорта Voucher Vault. Обычно он называется \"vouchervault.json\".
\n
\nФайл экспорта Voucher Vault можно создать, нажав кнопку \"Экспорт\".</string>
<string name="importVoucherVault">Импорт из Voucher Vault</string>
<string name="importFidmeMessage">Выберите файл экспорта FidMe. Обычно он называется как-то вроде \"fidme-export-request-xxxxxx.zip\".
\n
\nФайл экспорта FidMe можно создать в приложении FidMe, перейдя в свой профиль, выбрав функцию \"Защита данных\", и затем нажав кнопку \"Извлечь мои данные\".
\n
\nОбратите внимание, что FidMe не сохраняет тип штрих-кода в экспортированных данных, поэтому вам придётся редактировать импортированные данные карт вручную.</string>
<string name="importLoyaltyCardKeychainMessage">Выберите файл экспорта Loyalty Card Keychain. Обычно он называется \"LoyaltyCardKeychain.csv\".
\n
\nФайл экспорта Loyalty Card Keychain можно создать, перейдя в меню \"Импорт/Экспорт\" и нажав кнопку \"Экспорт\".</string>
<string name="importLoyaltyCardKeychain">Импорт из Loyalty Card Keychain</string>
<string name="importCatimaMessage">Выберите файл экспорта Catima. Обычно он называется \"Catima.csv\".
\n
\nФайл экспорта Catima можно создать, перейдя в меню \"Импорт/Экспорт\" и нажав кнопку \"Экспорт\".</string>
<string name="importFidme">Импорт из FidMe</string>
<string name="importCatima">Импорт из Catima</string>
<string name="errorReadingImage">Невозможно считать изображение</string>
<string name="noBarcodeFound">Штрих-код не найден</string>
<string name="addFromImage">Выбрать изображение из галереи</string>
<string name="setBarcodeId">Указать значение</string>
<string name="sameAsCardId">Как номер карты</string>
<string name="barcodeId">Значение штрих-кода</string>
<string name="settings_max_font_size_scale">Максимальный размер шрифта</string>
<string name="unsupportedBarcodeType">В настоящее время данный тип штрих-кодов не отображается. Его поддержка может быть добавлена в следующих версиях приложения.</string>
<string name="wrongValueForBarcodeType">Недопустимое значение для выбранного типа штрих-кода</string>
</resources>

View File

@@ -9,7 +9,6 @@
<string name="cardId">ID karty</string>
<string name="cancel">Zrušiť</string>
<string name="save">Uložiť</string>
<string name="editCard">Úprava karty</string>
<string name="edit">Upraviť</string>
<string name="delete">Vymazať</string>
<string name="confirm">Potvrdiť</string>
@@ -65,10 +64,6 @@
<string name="settings">Nastavenia</string>
<string name="settings_category_title_ui">Používateľské prostredie</string>
<string name="settings_card_title_list_font_size">Veľkosť názvu karty v zozname</string>
<string name="settings_card_note_list_font_size">Veľkosť poznámky v zozname</string>
<string name="settings_card_title_font_size">Veľkosť názvu karty</string>
<string name="settings_card_id_font_size">Veľkosť ID karty pod čiarovým kódom</string>
<string name="settings_display_barcode_max_brightness">Zvýšiť jas pri zobrazení čiarového kódu </string>
<string name="settings_lock_barcode_orientation">Uzamkni orientáciu čiarového kódu</string>
</resources>

View File

@@ -9,7 +9,6 @@
<string name="cardId">Št. kartice</string>
<string name="cancel">Prekliči</string>
<string name="save">Shrani</string>
<string name="editCard">Uredi kartico</string>
<string name="edit">Uredi</string>
<string name="delete">Izbriši</string>
<string name="confirm">Potrdi</string>
@@ -65,10 +64,6 @@
<string name="settings">Nastavitve</string>
<string name="settings_category_title_ui">Uporabniški vmesnik</string>
<string name="settings_card_title_list_font_size">Velikost pisave seznama naslovov kartic</string>
<string name="settings_card_note_list_font_size">Velikost pisave seznama zabeležk </string>
<string name="settings_card_title_font_size">Velikost pisave imen kartic</string>
<string name="settings_card_id_font_size">Velikost pisave številke kartice</string>
<string name="settings_display_barcode_max_brightness">Povečaj osvetljenost prikaza črtne kode</string>
<string name="settings_lock_barcode_orientation">Zakleni orientacijo črtne kode</string>
</resources>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="import_types_array">
<item>Catima</item>
<item>Fidme</item>
<item>@string/app_loyalty_card_keychain</item>
<item>Voucher Vault</item>
</string-array>
</resources>

View File

@@ -34,5 +34,6 @@
<!-- The default letter tile text size -->
<dimen name="tileLetterFontSize">33sp</dimen>
<dimen name="tileLetterFontSizeForShortcut">24dp</dimen>
<dimen name="cardViewLetterFontSize">100sp</dimen>
</resources>

View File

@@ -24,18 +24,9 @@
</array>
<!-- Default values -->
<integer name="settings_card_title_list_font_size_sp">28</integer>
<integer name="settings_card_note_list_font_size_sp">14</integer>
<integer name="settings_card_title_font_size_sp">40</integer>
<integer name="settings_card_id_font_size_sp">50</integer>
<integer name="settings_max_font_size_scale_pct">100</integer>
<!-- Constraints -->
<integer name="settings_card_title_list_max_font_size_sp">40</integer>
<integer name="settings_card_title_list_min_font_size_sp">16</integer>
<integer name="settings_card_note_list_max_font_size_sp">25</integer>
<integer name="settings_card_note_list_min_font_size_sp">10</integer>
<integer name="settings_card_title_max_font_size_sp">50</integer>
<integer name="settings_card_title_min_font_size_sp">16</integer>
<integer name="settings_card_id_max_font_size_sp">50</integer>
<integer name="settings_card_id_min_font_size_sp">16</integer>
<integer name="settings_max_font_size_scale_pct_min">50</integer>
<integer name="settings_max_font_size_scale_pct_max">200</integer>
</resources>

View File

@@ -20,7 +20,6 @@
<string name="cancel">Cancel</string>
<string name="save">Save</string>
<string name="editCard">Edit Card</string>
<string name="edit">Edit</string>
<string name="delete">Delete</string>
<string name="confirm">Confirm</string>
@@ -92,20 +91,20 @@
<string name="settings_key_light_theme" translatable="false">light</string>
<string name="settings_dark_theme">Dark</string>
<string name="settings_key_dark_theme" translatable="false">dark</string>
<string name="settings_card_title_list_font_size">Card title font size (list mode)</string>
<string name="settings_key_card_title_list_font_size" translatable="false">pref_card_title_list_font_size_sp</string>
<string name="settings_card_note_list_font_size">Card note font size (list mode)</string>
<string name="settings_key_card_note_list_font_size" translatable="false">pref_card_note_list_font_size_sp</string>
<string name="settings_card_title_font_size">Card title font size (preview)</string>
<string name="settings_key_card_title_font_size" translatable="false">pref_card_title_font_size_sp</string>
<string name="settings_card_id_font_size">Card ID font size</string>
<string name="settings_key_card_id_font_size" translatable="false">pref_card_id_font_size_sp</string>
<string name="settings_key_max_font_size_scale" translatable="false">pref_max_font_size_scale</string>
<string name="settings_max_font_size_scale">Maximum font size scale</string>
<string name="settings_display_barcode_max_brightness">Brighten barcode view</string>
<string name="settings_key_display_barcode_max_brightness" translatable="false">pref_display_card_max_brightness</string>
<string name="settings_lock_barcode_orientation">Lock barcode orientation</string>
<string name="settings_key_lock_barcode_orientation" translatable="false">pref_lock_barcode_orientation</string>
<string name="settings_keep_screen_on">Keep screen on while viewing a card</string>
<string name="settings_key_keep_screen_on" translatable="false">pref_keep_screen_on</string>
<string name="settings_disable_lockscreen_while_viewing_card">Disable lock screen while viewing a card</string>
<string name="settings_key_disable_lockscreen_while_viewing_card" translatable="false">pref_disable_lockscreen_while_viewing_card</string>
<string name="sharedpreference_active_tab" translatable="false">sharedpreference_active_tab</string>
<string name="sharedpreference_privacy_policy_shown" translatable="false">sharedpreference_privacy_policy_shown</string>
<string name="intent_import_card_from_url_share_text">I want to share a card with you</string>
<string name="intent_import_card_from_url_host" translatable="false">thelastproject.github.io</string>
@@ -125,15 +124,18 @@
<string name="all">All</string>
<string name="deleteConfirmationGroup">Please confirm you want to delete this group</string>
<string name="failedOpeningFileManager">Failed opening a file manager. Please make sure one is installed.</string>
<string name="failedOpeningFileManager">Install a file manager first.</string>
<string name="moveUp">Move up in list</string>
<string name="moveDown">Move down in list</string>
<string name="leaveWithoutSaveTitle">Leave without saving</string>
<string name="leaveWithoutSaveConfirmation">Are you sure you want to leave this screen? Changed made will not be saved.</string>
<string name="leaveWithoutSaveTitle">Exit</string>
<string name="leaveWithoutSaveConfirmation">Leave without saving?</string>
<string name="addManually">Manually enter card ID</string>
<string name="addFromImage">Select image from gallery</string>
<string name="groupsList">Groups: <xliff:g>%s</xliff:g></string>
<string name="expiryStateSentence">Expires: <xliff:g>%s</xliff:g></string>
<string name="expiryStateSentenceExpired">Expired: <xliff:g>%s</xliff:g></string>
<string name="balanceSentence">Balance: <xliff:g>%s</xliff:g></string>
<string name="balancePoints"><xliff:g>%s</xliff:g> points</string>
<string name="card">Card</string>
<string name="barcode">Barcode</string>
@@ -141,4 +143,34 @@
<string name="expiryDate">Expiry date</string>
<string name="never">Never</string>
<string name="chooseExpiryDate">Choose expiry date</string>
<string name="moveBarcodeToTopOfScreen">Move the barcode to the top of the screen</string>
<string name="moveBarcodeToCenterOfScreen">Center the barcode on the screen</string>
<string name="noBarcodeFound">No barcode was found</string>
<string name="errorReadingImage">Could not read the image</string>
<string name="balance">Balance</string>
<string name="currency">Currency</string>
<string name="points">Points</string>
<string name="parsingBalanceFailed"><xliff:g>%s</xliff:g> does not seem to be a valid balance.</string>
<string name="chooseImportType">Import data from?</string>
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
<string name="privacy_policy">Privacy Policy</string>
<string name="privacy_policy_popup_text">Privacy policy notice (required by some app stores):\n\nWe collect NO DATA AT ALL, which anyone can confirm since our app is libre software.</string>
<string name="accept">Accept</string>
<string name="importCatima">Import from Catima</string>
<string name="importCatimaMessage">Please select your Catima export file. It is most likely named Catima.csv.\n\nA Catima export file can be created by going to the Import/Export menu and pressing "Export".</string>
<string name="importFidme">Import from FidMe</string>
<string name="importFidmeMessage">Please select your FidMe export file. It is most likely named something like fidme-export-request-xxxxxx.zip.\n\nA FidMe export file can be created in the FidMe app by going to your profile, choosing "Data Protection" and then pressing "Extract my data".\n\nPlease note that FidMe does not store the barcode type in the export data so you will have to edit the imported cards manually.</string>
<string name="importLoyaltyCardKeychain">Import from Loyalty Card Keychain</string>
<string name="importLoyaltyCardKeychainMessage">Please select your Loyalty Card Keychain export file. It is most likely named LoyaltyCardKeychain.csv.\n\nA Loyalty Card Keychain export file can be created by going to the Import/Export menu and pressing "Export".</string>
<string name="importVoucherVault">Import from Voucher Vault</string>
<string name="importVoucherVaultMessage">Please select your Voucher Vault export file. It is most likely named vouchervault.json.\n\nA Voucher Vault export file can be created by pressing "Export".</string>
<string name="barcodeId">Barcode value</string>
<string name="sameAsCardId">Same as card ID</string>
<string name="setBarcodeId">Set barcode value</string>
<string name="unsupportedBarcodeType">This barcode type can\'t yet be displayed. It may be supported in a newer version of the app.</string>
<string name="wrongValueForBarcodeType">The value is not valid for the selected barcode type</string>
</resources>

View File

@@ -13,51 +13,47 @@
android:defaultValue="@string/settings_key_system_theme"
android:entries="@array/theme_value_strings"
android:entryValues="@array/theme_values"
app:iconSpaceReserved="false" />
app:iconSpaceReserved="false"
app:singleLineTitle="false" />
<nl.invissvenska.numberpickerpreference.NumberDialogPreference
android:key="@string/settings_key_card_title_list_font_size"
android:title="@string/settings_card_title_list_font_size"
app:defaultValue="@integer/settings_card_title_list_font_size_sp"
app:numberPickerPreference_maxValue="@integer/settings_card_title_list_max_font_size_sp"
app:numberPickerPreference_minValue="@integer/settings_card_title_list_min_font_size_sp"
app:iconSpaceReserved="false" />
<nl.invissvenska.numberpickerpreference.NumberDialogPreference
android:key="@string/settings_key_card_note_list_font_size"
android:title="@string/settings_card_note_list_font_size"
app:defaultValue="@integer/settings_card_note_list_font_size_sp"
app:numberPickerPreference_maxValue="@integer/settings_card_note_list_max_font_size_sp"
app:numberPickerPreference_minValue="@integer/settings_card_note_list_min_font_size_sp"
app:iconSpaceReserved="false" />
<nl.invissvenska.numberpickerpreference.NumberDialogPreference
android:key="@string/settings_key_card_title_font_size"
android:title="@string/settings_card_title_font_size"
app:defaultValue="@integer/settings_card_title_font_size_sp"
app:numberPickerPreference_maxValue="@integer/settings_card_title_max_font_size_sp"
app:numberPickerPreference_minValue="@integer/settings_card_title_min_font_size_sp"
app:iconSpaceReserved="false" />
<nl.invissvenska.numberpickerpreference.NumberDialogPreference
android:key="@string/settings_key_card_id_font_size"
android:title="@string/settings_card_id_font_size"
app:defaultValue="@integer/settings_card_id_font_size_sp"
app:numberPickerPreference_maxValue="@integer/settings_card_id_max_font_size_sp"
app:numberPickerPreference_minValue="@integer/settings_card_id_min_font_size_sp"
app:iconSpaceReserved="false" />
android:key="@string/settings_key_max_font_size_scale"
android:title="@string/settings_max_font_size_scale"
app:defaultValue="@integer/settings_max_font_size_scale_pct"
app:numberPickerPreference_maxValue="@integer/settings_max_font_size_scale_pct_max"
app:numberPickerPreference_minValue="@integer/settings_max_font_size_scale_pct_min"
app:numberPickerPreference_stepValue="10"
app:numberPickerPreference_unitText="%"
app:iconSpaceReserved="false"
app:singleLineTitle="false" />
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="@string/settings_key_display_barcode_max_brightness"
android:title="@string/settings_display_barcode_max_brightness"
app:iconSpaceReserved="false" />
app:iconSpaceReserved="false"
app:singleLineTitle="false" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="@string/settings_key_lock_barcode_orientation"
android:title="@string/settings_lock_barcode_orientation"
app:iconSpaceReserved="false" />
app:iconSpaceReserved="false"
app:singleLineTitle="false" />
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="@string/settings_key_keep_screen_on"
android:title="@string/settings_keep_screen_on"
app:iconSpaceReserved="false"
app:singleLineTitle="false" />
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="@string/settings_key_disable_lockscreen_while_viewing_card"
android:title="@string/settings_disable_lockscreen_while_viewing_card"
app:iconSpaceReserved="false"
app:singleLineTitle="false" />
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -15,7 +15,9 @@ import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Currency;
import java.util.List;
import static org.junit.Assert.assertEquals;
@@ -43,7 +45,7 @@ public class DatabaseTest
public void addRemoveOneGiftCard()
{
assertEquals(0, db.getLoyaltyCardCount());
long id = db.insertLoyaltyCard("store", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0);
long id = db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", null, BarcodeFormat.UPC_A, DEFAULT_HEADER_COLOR, 0);
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -53,9 +55,13 @@ public class DatabaseTest
assertEquals("store", loyaltyCard.store);
assertEquals("note", loyaltyCard.note);
assertEquals(null, loyaltyCard.expiry);
assertEquals(new BigDecimal("0"), loyaltyCard.balance);
assertEquals(null, loyaltyCard.balanceType);
assertEquals("cardId", loyaltyCard.cardId);
assertEquals(null, loyaltyCard.barcodeId);
assertEquals(BarcodeFormat.UPC_A, loyaltyCard.barcodeType);
assertEquals(DEFAULT_HEADER_COLOR, loyaltyCard.headerColor);
assertEquals(0, loyaltyCard.starStatus);
assertEquals(BarcodeFormat.UPC_A.toString(), loyaltyCard.barcodeType);
result = db.deleteLoyaltyCard(1);
assertTrue(result);
@@ -66,12 +72,12 @@ public class DatabaseTest
@Test
public void updateGiftCard()
{
long id = db.insertLoyaltyCard("store", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0);
long id = db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", null, BarcodeFormat.UPC_A, DEFAULT_HEADER_COLOR, 0);
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
result = db.updateLoyaltyCard(1, "store1", "note1", null, "cardId1", BarcodeFormat.AZTEC.toString(), DEFAULT_HEADER_COLOR);
result = db.updateLoyaltyCard(1, "store1", "note1", null, new BigDecimal("10.00"), Currency.getInstance("EUR"), "cardId1", null, BarcodeFormat.AZTEC, DEFAULT_HEADER_COLOR);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -80,15 +86,19 @@ public class DatabaseTest
assertEquals("store1", loyaltyCard.store);
assertEquals("note1", loyaltyCard.note);
assertEquals(null, loyaltyCard.expiry);
assertEquals(new BigDecimal("10.00"), loyaltyCard.balance);
assertEquals(Currency.getInstance("EUR"), loyaltyCard.balanceType);
assertEquals("cardId1", loyaltyCard.cardId);
assertEquals(null, loyaltyCard.barcodeId);
assertEquals(BarcodeFormat.AZTEC, loyaltyCard.barcodeType);
assertEquals(DEFAULT_HEADER_COLOR, loyaltyCard.headerColor);
assertEquals(0, loyaltyCard.starStatus);
assertEquals(BarcodeFormat.AZTEC.toString(), loyaltyCard.barcodeType);
}
@Test
public void updateGiftCardOnlyStar()
{
long id = db.insertLoyaltyCard("store", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0);
long id = db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", null, BarcodeFormat.UPC_A, DEFAULT_HEADER_COLOR, 0);
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -102,9 +112,13 @@ public class DatabaseTest
assertEquals("store", loyaltyCard.store);
assertEquals("note", loyaltyCard.note);
assertEquals(null, loyaltyCard.expiry);
assertEquals(new BigDecimal("0"), loyaltyCard.balance);
assertEquals(null, loyaltyCard.balanceType);
assertEquals("cardId", loyaltyCard.cardId);
assertEquals(null, loyaltyCard.barcodeId);
assertEquals(BarcodeFormat.UPC_A, loyaltyCard.barcodeType);
assertEquals(DEFAULT_HEADER_COLOR, loyaltyCard.headerColor);
assertEquals(1, loyaltyCard.starStatus);
assertEquals(BarcodeFormat.UPC_A.toString(), loyaltyCard.barcodeType);
}
@Test
@@ -112,8 +126,8 @@ public class DatabaseTest
{
assertEquals(0, db.getLoyaltyCardCount());
boolean result = db.updateLoyaltyCard(1, "store1", "note1", null, "cardId1",
BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR);
boolean result = db.updateLoyaltyCard(1, "store1", "note1", null, new BigDecimal("0"), null, "cardId1",
null, BarcodeFormat.UPC_A, DEFAULT_HEADER_COLOR);
assertEquals(false, result);
assertEquals(0, db.getLoyaltyCardCount());
}
@@ -121,7 +135,7 @@ public class DatabaseTest
@Test
public void emptyGiftCardValues()
{
long id = db.insertLoyaltyCard("", "", null, "", "", null, 0);
long id = db.insertLoyaltyCard("", "", null, new BigDecimal("0"), null, "", null, null, null, 0);
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -131,8 +145,13 @@ public class DatabaseTest
assertEquals("", loyaltyCard.store);
assertEquals("", loyaltyCard.note);
assertEquals(null, loyaltyCard.expiry);
assertEquals(new BigDecimal("0"), loyaltyCard.balance);
assertEquals(null, loyaltyCard.balanceType);
assertEquals("", loyaltyCard.cardId);
assertEquals("", loyaltyCard.barcodeType);
assertEquals(null, loyaltyCard.barcodeId);
assertEquals(null, loyaltyCard.barcodeType);
// headerColor is randomly generated when not given, so skip
assertEquals(0, loyaltyCard.starStatus);
}
@Test
@@ -144,8 +163,8 @@ public class DatabaseTest
// that they are sorted
for(int index = CARDS_TO_ADD-1; index >= 0; index--)
{
long id = db.insertLoyaltyCard("store" + index, "note" + index, null, "cardId" + index,
BarcodeFormat.UPC_A.toString(), index, 0);
long id = db.insertLoyaltyCard("store" + index, "note" + index, null, new BigDecimal("0"), null, "cardId" + index,
null, BarcodeFormat.UPC_A, index, 0);
boolean result = (id != -1);
assertTrue(result);
}
@@ -164,10 +183,13 @@ public class DatabaseTest
assertEquals("store"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STORE)));
assertEquals("note"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE)));
assertEquals(0, cursor.getLong(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.EXPIRY)));
assertEquals("0", cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE)));
assertEquals(null, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE)));
assertEquals("cardId"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID)));
assertEquals(null, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_ID)));
assertEquals(BarcodeFormat.UPC_A.toString(), cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE)));
assertEquals(0, cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS)));
assertEquals(index, cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_COLOR)));
assertEquals(0, cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS)));
cursor.moveToNext();
}
@@ -187,13 +209,13 @@ public class DatabaseTest
for(int index = CARDS_TO_ADD-1; index >= 0; index--)
{
if (index == CARDS_TO_ADD-1) {
id = db.insertLoyaltyCard("store" + index, "note" + index, null, "cardId" + index,
BarcodeFormat.UPC_A.toString(), index, 1);
id = db.insertLoyaltyCard("store" + index, "note" + index, null, new BigDecimal("0"), null, "cardId" + index,
null, BarcodeFormat.UPC_A, index, 1);
}
else {
id = db.insertLoyaltyCard("store" + index, "note" + index, null, "cardId" + index,
BarcodeFormat.UPC_A.toString(), index, 0);
id = db.insertLoyaltyCard("store" + index, "note" + index, null, new BigDecimal("0"), null, "cardId" + index,
null, BarcodeFormat.UPC_A, index, 0);
}
boolean result = (id != -1);
assertTrue(result);
@@ -211,10 +233,13 @@ public class DatabaseTest
assertEquals("store"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STORE)));
assertEquals("note"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE)));
assertEquals(0, cursor.getLong(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.EXPIRY)));
assertEquals("0", cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE)));
assertEquals(null, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE)));
assertEquals("cardId"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID)));
assertEquals(null, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_ID)));
assertEquals(BarcodeFormat.UPC_A.toString(), cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE)));
assertEquals(1, cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS)));
assertEquals(index, cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_COLOR)));
assertEquals(1, cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS)));
cursor.moveToNext();
@@ -222,11 +247,14 @@ public class DatabaseTest
{
assertEquals("store"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STORE)));
assertEquals("note"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE)));
assertEquals(null, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.EXPIRY)));
assertEquals("0", cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE)));
assertEquals(null, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE)));
assertEquals("cardId"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID)));
assertEquals("cardId"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID)));
assertEquals(null, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_ID)));
assertEquals(BarcodeFormat.UPC_A.toString(), cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE)));
assertEquals(0, cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS)));
assertEquals(index, cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_COLOR)));
assertEquals(0, cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS)));
cursor.moveToNext();
}
@@ -286,23 +314,51 @@ public class DatabaseTest
@Test
public void updateGroup()
{
long id = db.insertGroup("group one");
// Create card
assertEquals(0, db.getLoyaltyCardCount());
long id = db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", null, BarcodeFormat.UPC_A, DEFAULT_HEADER_COLOR, 0);
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
// Create group
long groupId = db.insertGroup("group one");
result = (groupId != -1);
assertTrue(result);
assertEquals(1, db.getGroupCount());
// Add card to group
Group group = db.getGroup("group one");
List<Group> groupList1 = new ArrayList<>();
groupList1.add(group);
db.setLoyaltyCardGroups(1, groupList1);
// Ensure the card has one group and the group has one card
List<Group> cardGroups = db.getLoyaltyCardGroups((int) id);
assertEquals(1, cardGroups.size());
assertEquals("group one", cardGroups.get(0)._id);
assertEquals(1, db.getGroupCardCount("group one"));
// Rename group
result = db.updateGroup("group one", "group one renamed");
assertTrue(result);
assertEquals(1, db.getGroupCount());
// Group one no longer exists
Group group = db.getGroup("group one");
group = db.getGroup("group one");
assertNull(group);
// But group one renamed does
Group group2 = db.getGroup("group one renamed");
assertNotNull(group2);
assertEquals("group one renamed", group2._id);
// And card is in "group one renamed"
// Ensure the card has one group and the group has one card
cardGroups = db.getLoyaltyCardGroups((int) id);
assertEquals(1, cardGroups.size());
assertEquals("group one renamed", cardGroups.get(0)._id);
assertEquals(1, db.getGroupCardCount("group one renamed"));
}
@Test
@@ -377,7 +433,7 @@ public class DatabaseTest
{
// Create card
assertEquals(0, db.getLoyaltyCardCount());
long id = db.insertLoyaltyCard("store", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0);
long id = db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", null, BarcodeFormat.UPC_A, DEFAULT_HEADER_COLOR, 0);
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -429,6 +485,7 @@ public class DatabaseTest
// Insert a budget and transaction
int newCardId = insertCardVersion1(database, "store", "cardId", BarcodeFormat.UPC_A.toString());
int newCardId2 = insertCardVersion1(database, "store", "cardId", "");
// Upgrade database
db.onUpgrade(database, DBHelper.ORIGINAL_DATABASE_VERSION, DBHelper.DATABASE_VERSION);
@@ -436,12 +493,28 @@ public class DatabaseTest
// Determine that the entries are queryable and the fields are correct
LoyaltyCard card = db.getLoyaltyCard(newCardId);
assertEquals("store", card.store);
assertEquals(null, card.expiry);
assertEquals("cardId", card.cardId);
assertEquals(BarcodeFormat.UPC_A.toString(), card.barcodeType);
assertEquals("", card.note);
assertEquals(null, card.expiry);
assertEquals(new BigDecimal("0"), card.balance);
assertEquals(null, card.balanceType);
assertEquals("cardId", card.cardId);
assertEquals(null, card.barcodeId);
assertEquals(BarcodeFormat.UPC_A, card.barcodeType);
assertEquals(null, card.headerColor);
assertEquals(null, card.headerTextColor);
assertEquals(0, card.starStatus);
// Determine that the entries are queryable and the fields are correct
LoyaltyCard card2 = db.getLoyaltyCard(newCardId2);
assertEquals("store", card2.store);
assertEquals("", card2.note);
assertEquals(null, card2.expiry);
assertEquals(new BigDecimal("0"), card2.balance);
assertEquals(null, card2.balanceType);
assertEquals("cardId", card2.cardId);
assertEquals(null, card2.barcodeId);
assertEquals(null, card2.barcodeType); // Empty string should've become null
assertEquals(null, card2.headerColor);
assertEquals(0, card2.starStatus);
database.close();
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -11,6 +11,8 @@ import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.io.InvalidObjectException;
import java.math.BigDecimal;
import java.util.Currency;
import java.util.Date;
import static org.junit.Assert.assertEquals;
@@ -38,7 +40,7 @@ public class ImportURITest {
// Generate card
Date date = new Date();
db.insertLoyaltyCard("store", "note", date, BarcodeFormat.UPC_A.toString(), LoyaltyCardDbIds.BARCODE_TYPE, Color.BLACK, 1);
db.insertLoyaltyCard("store", "note", date, new BigDecimal("100"), null, BarcodeFormat.UPC_E.toString(), BarcodeFormat.UPC_A.toString(), BarcodeFormat.QR_CODE, Color.BLACK, 1);
// Get card
LoyaltyCard card = db.getLoyaltyCard(1);
@@ -50,13 +52,16 @@ public class ImportURITest {
LoyaltyCard parsedCard = importURIHelper.parse(cardUri);
// Compare everything
assertEquals(card.barcodeType, parsedCard.barcodeType);
assertEquals(card.cardId, parsedCard.cardId);
assertEquals(card.headerColor, parsedCard.headerColor);
assertEquals(card.store, parsedCard.store);
assertEquals(card.note, parsedCard.note);
assertEquals(card.expiry, parsedCard.expiry);
assertEquals(card.store, parsedCard.store);
// No export of starStatus for single cards foreseen therefore 0 will be imported
assertEquals(card.balance, parsedCard.balance);
assertEquals(card.balanceType, parsedCard.balanceType);
assertEquals(card.cardId, parsedCard.cardId);
assertEquals(card.barcodeId, parsedCard.barcodeId);
assertEquals(card.barcodeType, parsedCard.barcodeType);
assertEquals(card.headerColor, parsedCard.headerColor);
// No export of starStatus for export URL foreseen therefore 0 will be imported
assertEquals(0, parsedCard.starStatus);
}
@@ -64,7 +69,7 @@ public class ImportURITest {
public void ensureNoCrashOnMissingHeaderFields() throws InvalidObjectException
{
// Generate card
db.insertLoyaltyCard("store", "note", null, BarcodeFormat.UPC_A.toString(), LoyaltyCardDbIds.BARCODE_TYPE, null, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("10.00"), Currency.getInstance("EUR"), BarcodeFormat.UPC_A.toString(), null, BarcodeFormat.QR_CODE, null, 0);
// Get card
LoyaltyCard card = db.getLoyaltyCard(1);
@@ -76,13 +81,17 @@ public class ImportURITest {
LoyaltyCard parsedCard = importURIHelper.parse(cardUri);
// Compare everything
assertEquals(card.barcodeType, parsedCard.barcodeType);
assertEquals(card.cardId, parsedCard.cardId);
assertEquals(card.store, parsedCard.store);
assertEquals(card.note, parsedCard.note);
assertEquals(card.expiry, parsedCard.expiry);
assertEquals(card.store, parsedCard.store);
assertEquals(card.balance, parsedCard.balance);
assertEquals(card.balanceType, parsedCard.balanceType);
assertEquals(card.cardId, parsedCard.cardId);
assertEquals(card.barcodeId, parsedCard.barcodeId);
assertEquals(card.barcodeType, parsedCard.barcodeType);
assertNull(parsedCard.headerColor);
assertNull(parsedCard.headerTextColor);
// No export of starStatus for export URL foreseen therefore 0 will be imported
assertEquals(0, parsedCard.starStatus);
}
@Test
@@ -119,10 +128,14 @@ public class ImportURITest {
}
// Compare everything
assertEquals("ITF", parsedCard.barcodeType);
assertEquals("12345", parsedCard.cardId);
assertEquals("note", parsedCard.note);
assertEquals("store", parsedCard.store);
assertEquals("note", parsedCard.note);
assertEquals(null, parsedCard.expiry);
assertEquals(new BigDecimal("0"), parsedCard.balance);
assertEquals(null, parsedCard.balanceType);
assertEquals("12345", parsedCard.cardId);
assertEquals(null, parsedCard.barcodeId);
assertEquals(BarcodeFormat.ITF, parsedCard.barcodeType);
assertEquals(Integer.valueOf(-416706), parsedCard.headerColor);
assertEquals(0, parsedCard.starStatus);
}

View File

@@ -7,6 +7,7 @@ import android.database.Cursor;
import android.graphics.Color;
import androidx.preference.PreferenceManager;
import androidx.test.core.app.ApplicationProvider;
import protect.card_locker.preferences.Settings;
import android.view.View;
import android.widget.ImageView;
@@ -21,7 +22,9 @@ import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.util.Currency;
import java.util.Date;
import static org.junit.Assert.assertEquals;
@@ -42,11 +45,10 @@ public class LoyaltyCardCursorAdapterTest
settings = PreferenceManager.getDefaultSharedPreferences(activity);
}
private void setFontSizes(int storeFontSize, int noteFontSize)
private void setFontScale(int fontSizeScale)
{
settings.edit()
.putInt(activity.getResources().getString(R.string.settings_key_card_title_list_font_size), storeFontSize)
.putInt(activity.getResources().getString(R.string.settings_key_card_note_list_font_size), noteFontSize)
.putInt(activity.getResources().getString(R.string.settings_key_max_font_size_scale), fontSizeScale)
.apply();
}
@@ -60,24 +62,26 @@ public class LoyaltyCardCursorAdapterTest
return view;
}
private void checkView(final View view, final String store, final String note, final String expiry, boolean checkFontSizes)
private void checkView(final View view, final String store, final String note, final String expiry, final String balance, boolean checkFontSizes)
{
final TextView storeField = view.findViewById(R.id.store);
final TextView noteField = view.findViewById(R.id.note);
final TextView expiryField = view.findViewById(R.id.expiry);
final TextView balanceField = view.findViewById(R.id.balance);
if(checkFontSizes)
{
int storeFontSize = settings.getInt(activity.getResources().getString(R.string.settings_key_card_title_list_font_size), 0);
int noteFontSize = settings.getInt(activity.getResources().getString(R.string.settings_key_card_note_list_font_size), 0);
Settings preferences = new Settings(activity.getApplicationContext());
int mediumFontSize = preferences.getFontSizeMax(preferences.getMediumFont());
int smallFontSize = preferences.getFontSizeMax(preferences.getSmallFont());
assertEquals(storeFontSize, (int)storeField.getTextSize());
assertEquals(noteFontSize, (int)noteField.getTextSize());
assertEquals(noteFontSize, (int)expiryField.getTextSize());
assertEquals(mediumFontSize, (int)storeField.getTextSize());
assertEquals(smallFontSize, (int)noteField.getTextSize());
assertEquals(smallFontSize, (int)expiryField.getTextSize());
}
assertEquals(store, storeField.getText().toString());
if(note.isEmpty() == false)
if(!note.isEmpty())
{
assertEquals(View.VISIBLE, noteField.getVisibility());
assertEquals(note, noteField.getText().toString());
@@ -87,7 +91,7 @@ public class LoyaltyCardCursorAdapterTest
assertEquals(View.GONE, noteField.getVisibility());
}
if(expiry.isEmpty() == false)
if(!expiry.isEmpty())
{
assertEquals(View.VISIBLE, expiryField.getVisibility());
assertEquals(expiry, expiryField.getText().toString());
@@ -96,13 +100,23 @@ public class LoyaltyCardCursorAdapterTest
{
assertEquals(View.GONE, expiryField.getVisibility());
}
if(!balance.isEmpty())
{
assertEquals(View.VISIBLE, balanceField.getVisibility());
assertEquals(balance, balanceField.getText().toString());
}
else
{
assertEquals(View.GONE, balanceField.getVisibility());
}
}
@Test
public void TestCursorAdapterEmptyNote()
{
db.insertLoyaltyCard("store", "", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
db.insertLoyaltyCard("store", "", null, new BigDecimal("0"), null, "cardId", null, BarcodeFormat.UPC_A, Color.BLACK, 0);
LoyaltyCard card = db.getLoyaltyCard(1);
Cursor cursor = db.getLoyaltyCardCursor();
@@ -110,7 +124,7 @@ public class LoyaltyCardCursorAdapterTest
View view = createView(cursor);
checkView(view, card.store, card.note, "", false);
checkView(view, card.store, card.note, "", "",false);
cursor.close();
}
@@ -118,7 +132,7 @@ public class LoyaltyCardCursorAdapterTest
@Test
public void TestCursorAdapterWithNote()
{
db.insertLoyaltyCard("store", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", null, BarcodeFormat.UPC_A, Color.BLACK, 0);
LoyaltyCard card = db.getLoyaltyCard(1);
Cursor cursor = db.getLoyaltyCardCursor();
@@ -126,7 +140,7 @@ public class LoyaltyCardCursorAdapterTest
View view = createView(cursor);
checkView(view, card.store, card.note, "", false);
checkView(view, card.store, card.note, "", "",false);
cursor.close();
}
@@ -138,20 +152,20 @@ public class LoyaltyCardCursorAdapterTest
Date expiryDate = new Date();
String dateString = context.getString(R.string.expiryStateSentence, DateFormat.getDateInstance(DateFormat.LONG).format(expiryDate));
db.insertLoyaltyCard("store", "note", expiryDate, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", expiryDate, new BigDecimal("0"), null, "cardId", null, BarcodeFormat.UPC_A, Color.BLACK, 0);
LoyaltyCard card = db.getLoyaltyCard(1);
Cursor cursor = db.getLoyaltyCardCursor();
cursor.moveToFirst();
setFontSizes(1, 2);
setFontScale(50);
View view = createView(cursor);
checkView(view, card.store, card.note, dateString, true);
checkView(view, card.store, card.note, dateString, "", true);
setFontSizes(30, 31);
setFontScale(200);
view = createView(cursor);
checkView(view, card.store, card.note, dateString, true);
checkView(view, card.store, card.note, dateString, "",true);
cursor.close();
}
@@ -159,9 +173,9 @@ public class LoyaltyCardCursorAdapterTest
@Test
public void TestCursorAdapterStarring()
{
db.insertLoyaltyCard("storeA", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
db.insertLoyaltyCard("storeB", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1);
db.insertLoyaltyCard("storeC", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1);
db.insertLoyaltyCard("storeA", "note", null, new BigDecimal("0"), null, "cardId", null, BarcodeFormat.UPC_A, Color.BLACK, 0);
db.insertLoyaltyCard("storeB", "note", null, new BigDecimal("0"), null, "cardId", null, BarcodeFormat.UPC_A, Color.BLACK, 1);
db.insertLoyaltyCard("storeC", "note", null, new BigDecimal("0"), null, "cardId", null, BarcodeFormat.UPC_A, Color.BLACK, 1);
Cursor cursor = db.getLoyaltyCardCursor();
cursor.moveToFirst();
@@ -181,4 +195,68 @@ public class LoyaltyCardCursorAdapterTest
cursor.close();
}
@Test
public void TestCursorAdapter0Points()
{
db.insertLoyaltyCard("store", "", null, new BigDecimal("0"), null, "cardId", null, BarcodeFormat.UPC_A, Color.BLACK, 0);
LoyaltyCard card = db.getLoyaltyCard(1);
Cursor cursor = db.getLoyaltyCardCursor();
cursor.moveToFirst();
View view = createView(cursor);
checkView(view, card.store, card.note, "", "",false);
cursor.close();
}
@Test
public void TestCursorAdapter0EUR()
{
db.insertLoyaltyCard("store", "", null, new BigDecimal("0"), Currency.getInstance("EUR"), "cardId", null, BarcodeFormat.UPC_A, Color.BLACK, 0);
LoyaltyCard card = db.getLoyaltyCard(1);
Cursor cursor = db.getLoyaltyCardCursor();
cursor.moveToFirst();
View view = createView(cursor);
checkView(view, card.store, card.note, "", "",false);
cursor.close();
}
@Test
public void TestCursorAdapter100Points()
{
db.insertLoyaltyCard("store", "note", null, new BigDecimal("100"), null, "cardId", null, BarcodeFormat.UPC_A, Color.BLACK, 0);
LoyaltyCard card = db.getLoyaltyCard(1);
Cursor cursor = db.getLoyaltyCardCursor();
cursor.moveToFirst();
View view = createView(cursor);
checkView(view, card.store, card.note, "", "Balance: 100 points",false);
cursor.close();
}
@Test
public void TestCursorAdapter10USD()
{
db.insertLoyaltyCard("store", "note", null, new BigDecimal("10.00"), Currency.getInstance("USD"), "cardId", null, BarcodeFormat.UPC_A, Color.BLACK, 0);
LoyaltyCard card = db.getLoyaltyCard(1);
Cursor cursor = db.getLoyaltyCardCursor();
cursor.moveToFirst();
View view = createView(cursor);
checkView(view, card.store, card.note, "", "Balance: $10.00",false);
cursor.close();
}
}

View File

@@ -26,17 +26,23 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.core.widget.TextViewCompat;
import androidx.test.core.app.ApplicationProvider;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.textfield.MaterialAutoCompleteTextView;
import com.google.android.material.textfield.TextInputLayout;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.client.android.Intents;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.Currency;
import java.util.Date;
import org.junit.Before;
import org.junit.Test;
@@ -56,10 +62,10 @@ import org.robolectric.shadows.ShadowLog;
public class LoyaltyCardViewActivityTest
{
private final String BARCODE_DATA = "428311627547";
private final String BARCODE_TYPE = BarcodeFormat.UPC_A.name();
private final BarcodeFormat BARCODE_TYPE = BarcodeFormat.UPC_A;
private final String EAN_BARCODE_DATA = "4763705295336";
private final String EAN_BARCODE_TYPE = BarcodeFormat.EAN_13.name();
private final BarcodeFormat EAN_BARCODE_TYPE = BarcodeFormat.EAN_13;
enum ViewMode
{
@@ -104,11 +110,13 @@ public class LoyaltyCardViewActivityTest
*/
private void saveLoyaltyCardWithArguments(final Activity activity,
final String store, final String note,
final Date expiry,
final String expiry,
final BigDecimal balance,
final String balanceType,
final String cardId,
final String barcodeId,
final String barcodeType,
boolean creatingNewCard)
{
boolean creatingNewCard) throws ParseException {
DBHelper db = new DBHelper(activity);
if(creatingNewCard)
{
@@ -122,13 +130,19 @@ public class LoyaltyCardViewActivityTest
final EditText storeField = activity.findViewById(R.id.storeNameEdit);
final EditText noteField = activity.findViewById(R.id.noteEdit);
final TextInputLayout expiryView = activity.findViewById(R.id.expiryView);
final EditText balanceView = activity.findViewById(R.id.balanceField);
final EditText balanceCurrencyField = activity.findViewById(R.id.balanceCurrencyField);
final TextView cardIdField = activity.findViewById(R.id.cardIdView);
final TextView barcodeIdField = activity.findViewById(R.id.barcodeIdField);
final TextView barcodeTypeField = activity.findViewById(R.id.barcodeTypeField);
storeField.setText(store);
noteField.setText(note);
expiryView.setTag(expiry);
balanceView.setText(balance.toPlainString());
balanceCurrencyField.setText(balanceType);
cardIdField.setText(cardId);
barcodeIdField.setText(barcodeId);
barcodeTypeField.setText(barcodeType);
assertEquals(false, activity.isFinishing());
@@ -140,19 +154,49 @@ public class LoyaltyCardViewActivityTest
LoyaltyCard card = db.getLoyaltyCard(1);
assertEquals(store, card.store);
assertEquals(note, card.note);
assertEquals(balance, card.balance);
// The special "Never" string shouldn't actually be written to the loyalty card
if(expiry.equals(activity.getApplicationContext().getString(R.string.never)))
{
assertEquals(null, card.expiry);
}
else
{
assertEquals(DateFormat.getDateInstance().parse(expiry), card.expiry);
}
// The special "Points" string shouldn't actually be written to the loyalty card
if(balanceType.equals(activity.getApplicationContext().getString(R.string.points)))
{
assertEquals(null, card.balanceType);
}
else
{
assertEquals(Currency.getInstance(balanceType), card.balanceType);
}
assertEquals(cardId, card.cardId);
// The special "Same as barcode ID" string shouldn't actually be written to the loyalty card
if(barcodeId.equals(activity.getApplicationContext().getString(R.string.sameAsCardId)))
{
assertEquals(null, card.barcodeId);
}
else
{
assertEquals(barcodeId, card.barcodeId);
}
// The special "No barcode" string shouldn't actually be written to the loyalty card
if(barcodeType.equals(activity.getApplicationContext().getString(R.string.noBarcode)))
{
assertEquals("", card.barcodeType);
assertEquals(null, card.barcodeType);
}
else
{
assertEquals(barcodeType, card.barcodeType);
assertEquals(BarcodeFormat.valueOf(barcodeType), card.barcodeType);
}
assertNotNull(card.headerColor);
assertNotNull(card.headerTextColor);
db.close();
}
@@ -179,7 +223,7 @@ public class LoyaltyCardViewActivityTest
Intent resultIntent = new Intent(intent);
Bundle resultBundle = new Bundle();
resultBundle.putString(BarcodeSelectorActivity.BARCODE_CONTENTS, BARCODE_DATA);
resultBundle.putString(BarcodeSelectorActivity.BARCODE_FORMAT, BARCODE_TYPE);
resultBundle.putString(BarcodeSelectorActivity.BARCODE_FORMAT, String.valueOf(BARCODE_TYPE));
resultIntent.putExtras(resultBundle);
// Respond to image capture, success
@@ -250,7 +294,10 @@ public class LoyaltyCardViewActivityTest
}
private void checkAllFields(final Activity activity, ViewMode mode,
final String store, final String note, final String expiryString, final String cardId, final String barcodeType)
final String store, final String note, final String expiryString,
final String balanceString, final String balanceTypeString,
final String cardId, final String barcodeId,
final String barcodeType)
{
if(mode == ViewMode.VIEW_CARD)
{
@@ -263,7 +310,10 @@ public class LoyaltyCardViewActivityTest
checkFieldProperties(activity, R.id.storeNameEdit, editVisibility, store);
checkFieldProperties(activity, R.id.noteEdit, editVisibility, note);
checkFieldProperties(activity, R.id.expiryView, editVisibility, expiryString);
checkFieldProperties(activity, R.id.balanceField, editVisibility, balanceString);
checkFieldProperties(activity, R.id.balanceCurrencyField, editVisibility, balanceTypeString);
checkFieldProperties(activity, R.id.cardIdView, View.VISIBLE, cardId);
checkFieldProperties(activity, R.id.barcodeIdField, View.VISIBLE, barcodeId);
checkFieldProperties(activity, R.id.barcodeTypeField, View.VISIBLE, barcodeType);
checkFieldProperties(activity, R.id.barcode, View.VISIBLE, null);
}
@@ -280,7 +330,7 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
final Context context = ApplicationProvider.getApplicationContext();
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never) , "", "");
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never) , "0", context.getString(R.string.points), "", context.getString(R.string.sameAsCardId),"");
}
@Test
@@ -298,7 +348,6 @@ public class LoyaltyCardViewActivityTest
final EditText storeField = activity.findViewById(R.id.storeNameEdit);
final EditText noteField = activity.findViewById(R.id.noteEdit);
final TextView cardIdField = activity.findViewById(R.id.cardIdView);
activity.findViewById(R.id.fabSave).performClick();
assertEquals(0, db.getLoyaltyCardCount());
@@ -330,8 +379,7 @@ public class LoyaltyCardViewActivityTest
}
@Test
public void startWithoutParametersCaptureBarcodeCreateLoyaltyCard() throws IOException
{
public void startWithoutParametersCaptureBarcodeCreateLoyaltyCard() throws IOException, ParseException {
registerMediaStoreIntentHandler();
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardEditActivity.class).create();
@@ -342,15 +390,17 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
final Context context = ApplicationProvider.getApplicationContext();
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "", "");
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "0", context.getString(R.string.points), "", context.getString(R.string.sameAsCardId),"");
// Complete barcode capture successfully
captureBarcodeWithResult(activity, true);
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), BARCODE_DATA, BARCODE_TYPE);
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "0", context.getString(R.string.points), BARCODE_DATA, context.getString(R.string.sameAsCardId), BARCODE_TYPE.toString());
shadowOf(getMainLooper()).idle();
// Save and check the loyalty card
saveLoyaltyCardWithArguments(activity, "store", "note", null, BARCODE_DATA, BARCODE_TYPE, true);
saveLoyaltyCardWithArguments(activity, "store", "note", context.getString(R.string.never), new BigDecimal("0"), context.getString(R.string.points), BARCODE_DATA, context.getString(R.string.sameAsCardId), BARCODE_TYPE.name(), true);
}
@Test
@@ -364,12 +414,12 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
final Context context = ApplicationProvider.getApplicationContext();
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "", "");
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "0", context.getString(R.string.points), "", context.getString(R.string.sameAsCardId), "");
// Complete barcode capture in failure
captureBarcodeWithResult(activity, false);
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "", "");
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "0", context.getString(R.string.points), "", context.getString(R.string.sameAsCardId),"");
}
@Test
@@ -383,12 +433,12 @@ public class LoyaltyCardViewActivityTest
LoyaltyCardEditActivity activity = (LoyaltyCardEditActivity) activityController.get();
final Context context = ApplicationProvider.getApplicationContext();
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "", "");
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "0", context.getString(R.string.points), "", context.getString(R.string.sameAsCardId),"");
// Complete barcode capture successfully
captureBarcodeWithResult(activity, true);
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), BARCODE_DATA, BARCODE_TYPE);
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "0", context.getString(R.string.points), BARCODE_DATA, context.getString(R.string.sameAsCardId), BARCODE_TYPE.toString());
// Cancel the loyalty card creation
assertEquals(false, activity.isFinishing());
@@ -435,15 +485,16 @@ public class LoyaltyCardViewActivityTest
{
ActivityController activityController = createActivityWithLoyaltyCard(true);
Activity activity = (Activity)activityController.get();
final Context context = ApplicationProvider.getApplicationContext();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, null, BARCODE_TYPE, Color.BLACK, 0);
activityController.start();
activityController.visible();
activityController.resume();
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, BARCODE_DATA, BARCODE_TYPE);
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, "0", context.getString(R.string.points), BARCODE_DATA, null, BARCODE_TYPE.toString());
db.close();
}
@@ -453,15 +504,16 @@ public class LoyaltyCardViewActivityTest
{
ActivityController activityController = createActivityWithLoyaltyCard(false);
Activity activity = (Activity)activityController.get();
final Context context = ApplicationProvider.getApplicationContext();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, null, BARCODE_TYPE, Color.BLACK, 0);
activityController.start();
activityController.visible();
activityController.resume();
checkAllFields(activity, ViewMode.VIEW_CARD, "store", "note", null, BARCODE_DATA, BARCODE_TYPE);
checkAllFields(activity, ViewMode.VIEW_CARD, "store", "note", null, "0", context.getString(R.string.points), BARCODE_DATA, null, BARCODE_TYPE.toString());
db.close();
}
@@ -471,20 +523,21 @@ public class LoyaltyCardViewActivityTest
{
ActivityController activityController = createActivityWithLoyaltyCard(true);
Activity activity = (Activity)activityController.get();
final Context context = ApplicationProvider.getApplicationContext();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, EAN_BARCODE_DATA, null, EAN_BARCODE_TYPE, Color.BLACK, 0);
activityController.start();
activityController.visible();
activityController.resume();
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, "0", context.getString(R.string.points), EAN_BARCODE_DATA, null, EAN_BARCODE_TYPE.toString());
// Complete barcode capture successfully
captureBarcodeWithResult(activity, true);
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, BARCODE_DATA, BARCODE_TYPE);
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, "0", context.getString(R.string.points), BARCODE_DATA, null, BARCODE_TYPE.toString());
db.close();
}
@@ -494,20 +547,21 @@ public class LoyaltyCardViewActivityTest
{
ActivityController activityController = createActivityWithLoyaltyCard(true);
LoyaltyCardEditActivity activity = (LoyaltyCardEditActivity) activityController.get();
final Context context = ApplicationProvider.getApplicationContext();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, EAN_BARCODE_DATA, null, EAN_BARCODE_TYPE, Color.BLACK, 0);
activityController.start();
activityController.visible();
activityController.resume();
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, "0", context.getString(R.string.points), EAN_BARCODE_DATA, null, EAN_BARCODE_TYPE.toString());
// Complete barcode capture successfully
captureBarcodeWithResult(activity, true);
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, BARCODE_DATA, BARCODE_TYPE);
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, "0", context.getString(R.string.points), BARCODE_DATA, null, BARCODE_TYPE.toString());
// Cancel the loyalty card creation
assertEquals(false, activity.isFinishing());
@@ -534,13 +588,13 @@ public class LoyaltyCardViewActivityTest
final Context context = ApplicationProvider.getApplicationContext();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, EAN_BARCODE_DATA, null, EAN_BARCODE_TYPE, Color.BLACK, 0);
activityController.start();
activityController.visible();
activityController.resume();
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), "0", context.getString(R.string.points), EAN_BARCODE_DATA, null, EAN_BARCODE_TYPE.toString());
// Set date to today
MaterialAutoCompleteTextView expiryField = activity.findViewById(R.id.expiryField);
@@ -554,7 +608,7 @@ public class LoyaltyCardViewActivityTest
shadowOf(getMainLooper()).idle();
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", DateFormat.getDateInstance(DateFormat.LONG).format(new Date()), EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", DateFormat.getDateInstance(DateFormat.LONG).format(new Date()), "0", context.getString(R.string.points), EAN_BARCODE_DATA, null, EAN_BARCODE_TYPE.toString());
db.close();
}
@@ -567,19 +621,111 @@ public class LoyaltyCardViewActivityTest
final Context context = ApplicationProvider.getApplicationContext();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", new Date(), EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", new Date(), new BigDecimal("0"), null, EAN_BARCODE_DATA, null, EAN_BARCODE_TYPE, Color.BLACK, 0);
activityController.start();
activityController.visible();
activityController.resume();
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", DateFormat.getDateInstance(DateFormat.LONG).format(new Date()), EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", DateFormat.getDateInstance(DateFormat.LONG).format(new Date()), "0", context.getString(R.string.points), EAN_BARCODE_DATA, null, EAN_BARCODE_TYPE.toString());
// Set date to never
MaterialAutoCompleteTextView expiryField = activity.findViewById(R.id.expiryField);
expiryField.setText(expiryField.getAdapter().getItem(0).toString(), false);
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), "0", context.getString(R.string.points), EAN_BARCODE_DATA, null, EAN_BARCODE_TYPE.toString());
db.close();
}
@Test
public void startWithLoyaltyCardNoBalanceSetBalance() throws IOException
{
ActivityController activityController = createActivityWithLoyaltyCard(true);
Activity activity = (Activity)activityController.get();
final Context context = ApplicationProvider.getApplicationContext();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, EAN_BARCODE_DATA, null, EAN_BARCODE_TYPE, Color.BLACK, 0);
activityController.start();
activityController.visible();
activityController.resume();
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), "0", context.getString(R.string.points), EAN_BARCODE_DATA, null, EAN_BARCODE_TYPE.toString());
// Set balance to 10 points
EditText balanceField = activity.findViewById(R.id.balanceField);
balanceField.setText("10");
shadowOf(getMainLooper()).idle();
// Change points to EUR
MaterialAutoCompleteTextView balanceTypeField = activity.findViewById(R.id.balanceCurrencyField);
balanceTypeField.setText("", false);
shadowOf(getMainLooper()).idle();
// Ensure the balance is reformatted for EUR when focus is cleared
shadowOf(getMainLooper()).idle();
balanceField.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
assertEquals("10.00", balanceField.getText().toString());
shadowOf(getMainLooper()).idle();
DatePickerDialog datePickerDialog = (DatePickerDialog) (ShadowDialog.getLatestDialog());
assertNotNull(datePickerDialog);
datePickerDialog.getButton(DatePickerDialog.BUTTON_POSITIVE).performClick();
shadowOf(getMainLooper()).idle();
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", DateFormat.getDateInstance(DateFormat.LONG).format(new Date()), "10.00", "", EAN_BARCODE_DATA, null, EAN_BARCODE_TYPE.toString());
db.close();
}
});
balanceField.clearFocus();
}
@Test
public void startWithLoyaltyCardBalanceSetNoBalance() throws IOException
{
ActivityController activityController = createActivityWithLoyaltyCard(true);
Activity activity = (Activity)activityController.get();
final Context context = ApplicationProvider.getApplicationContext();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("10.00"), Currency.getInstance("USD"), EAN_BARCODE_DATA, null, EAN_BARCODE_TYPE, Color.BLACK, 0);
activityController.start();
activityController.visible();
activityController.resume();
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), "10.00", "$", EAN_BARCODE_DATA, null, EAN_BARCODE_TYPE.toString());
shadowOf(getMainLooper()).idle();
// Change EUR to WON
MaterialAutoCompleteTextView balanceTypeField = activity.findViewById(R.id.balanceCurrencyField);
balanceTypeField.setText("", false);
shadowOf(getMainLooper()).idle();
// Ensure the balance is reformatted for WON when focus is cleared
EditText balanceField = activity.findViewById(R.id.balanceField);
balanceField.clearFocus();
assertEquals("10", balanceField.getText().toString());
shadowOf(getMainLooper()).idle();
// Set the balance to 0
balanceField.setText("0");
shadowOf(getMainLooper()).idle();
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), "0", "", EAN_BARCODE_DATA, null, EAN_BARCODE_TYPE.toString());
db.close();
}
@@ -591,12 +737,14 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, null, BARCODE_TYPE, Color.BLACK, 0);
activityController.start();
activityController.visible();
activityController.resume();
shadowOf(getMainLooper()).idle();
final Menu menu = shadowOf(activity).getOptionsMenu();
assertTrue(menu != null);
@@ -637,7 +785,7 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, null, BARCODE_TYPE, Color.BLACK, 0);
activityController.start();
activityController.visible();
@@ -657,7 +805,7 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, null, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, null, BARCODE_TYPE, null, 0);
activityController.start();
activityController.visible();
@@ -671,69 +819,66 @@ public class LoyaltyCardViewActivityTest
}
@Test
public void startLoyaltyCardWithoutColorsSave() throws IOException
{
public void startLoyaltyCardWithoutColorsSave() throws IOException, ParseException {
ActivityController activityController = createActivityWithLoyaltyCard(true);
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, null, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, null, BARCODE_TYPE, null, 0);
activityController.start();
activityController.visible();
activityController.resume();
// Save and check the loyalty card
saveLoyaltyCardWithArguments(activity, "store", "note", null, BARCODE_DATA, BARCODE_TYPE, false);
saveLoyaltyCardWithArguments(activity, "store", "note", activity.getApplicationContext().getString(R.string.never), new BigDecimal("0"), activity.getApplicationContext().getString(R.string.points), BARCODE_DATA, activity.getApplicationContext().getString(R.string.sameAsCardId), BARCODE_TYPE.toString(), false);
db.close();
}
@Test
public void startLoyaltyCardWithExplicitNoBarcodeSave() throws IOException
{
public void startLoyaltyCardWithExplicitNoBarcodeSave() throws IOException, ParseException {
ActivityController activityController = createActivityWithLoyaltyCard(true);
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, "", Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, null, null, Color.BLACK, 0);
activityController.start();
activityController.visible();
activityController.resume();
// Save and check the loyalty card
saveLoyaltyCardWithArguments(activity, "store", "note", null, BARCODE_DATA, activity.getApplicationContext().getString(R.string.noBarcode), false);
saveLoyaltyCardWithArguments(activity, "store", "note", activity.getApplicationContext().getString(R.string.never), new BigDecimal("0"), activity.getApplicationContext().getString(R.string.points), BARCODE_DATA, activity.getApplicationContext().getString(R.string.sameAsCardId), activity.getApplicationContext().getString(R.string.noBarcode), false);
db.close();
}
@Test
public void removeBarcodeFromLoyaltyCard() throws IOException
{
public void removeBarcodeFromLoyaltyCard() throws IOException, ParseException {
ActivityController activityController = createActivityWithLoyaltyCard(true);
Activity activity = (Activity)activityController.get();
final Context context = ApplicationProvider.getApplicationContext();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, null, BARCODE_TYPE, Color.BLACK, 0);
activityController.start();
activityController.visible();
activityController.resume();
// First check if the card is as expected
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), BARCODE_DATA, BARCODE_TYPE);
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), "0", context.getString(R.string.points), BARCODE_DATA, null, BARCODE_TYPE.toString());
// Complete empty barcode selection successfully
selectBarcodeWithResult(activity, BARCODE_DATA, "", true);
// Check if the barcode type is NO_BARCODE as expected
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), BARCODE_DATA, activity.getApplicationContext().getString(R.string.noBarcode));
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), "0", context.getString(R.string.points), BARCODE_DATA, null, context.getString(R.string.noBarcode));
assertEquals(View.GONE, activity.findViewById(R.id.barcodeLayout).getVisibility());
// Check if the special NO_BARCODE string doesn't get saved
saveLoyaltyCardWithArguments(activity, "store", "note", null, BARCODE_DATA, activity.getApplicationContext().getString(R.string.noBarcode), false);
saveLoyaltyCardWithArguments(activity, "store", "note", context.getString(R.string.never), new BigDecimal("0"), context.getString(R.string.points), BARCODE_DATA, context.getString(R.string.sameAsCardId), context.getString(R.string.noBarcode), false);
db.close();
}
@@ -745,15 +890,13 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, null, BARCODE_TYPE, Color.BLACK, 0);
final int STORE_FONT_SIZE = 50;
final int CARD_FONT_SIZE = 40;
final int LARGE_FONT_SIZE = 40;
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(activity);
settings.edit()
.putInt(activity.getResources().getString(R.string.settings_key_card_title_font_size), STORE_FONT_SIZE)
.putInt(activity.getResources().getString(R.string.settings_key_card_id_font_size), CARD_FONT_SIZE)
.putInt(activity.getResources().getString(R.string.settings_key_max_font_size_scale), 100)
.apply();
activityController.start();
@@ -767,8 +910,8 @@ public class LoyaltyCardViewActivityTest
TextViewCompat.getAutoSizeMaxTextSize(storeName);
TextViewCompat.getAutoSizeMaxTextSize(storeName);
assertEquals(STORE_FONT_SIZE, (int)storeName.getTextSize());
assertEquals(CARD_FONT_SIZE, TextViewCompat.getAutoSizeMaxTextSize(cardIdFieldView));
assertEquals(LARGE_FONT_SIZE, (int)storeName.getTextSize());
assertEquals(LARGE_FONT_SIZE, TextViewCompat.getAutoSizeMaxTextSize(cardIdFieldView));
shadowOf(activity).clickMenuItem(android.R.id.home);
assertEquals(true, activity.isFinishing());
@@ -785,7 +928,7 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, null, BARCODE_TYPE, Color.BLACK, 0);
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(activity);
settings.edit()
@@ -822,13 +965,15 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity) activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK,0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, null, BARCODE_TYPE, Color.BLACK,0);
activityController.start();
activityController.visible();
activityController.resume();
assertEquals(false, activity.isFinishing());
shadowOf(getMainLooper()).idle();
final Menu menu = shadowOf(activity).getOptionsMenu();
assertTrue(menu != null);
@@ -855,7 +1000,7 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, null, BARCODE_TYPE, Color.BLACK, 0);
activityController.start();
activityController.visible();
@@ -865,49 +1010,84 @@ public class LoyaltyCardViewActivityTest
ImageView barcodeImage = activity.findViewById(R.id.barcode);
View collapsingToolbarLayout = activity.findViewById(R.id.collapsingToolbarLayout);
View bottomSheet = activity.findViewById(R.id.bottom_sheet);
ImageButton maximizeButton = activity.findViewById(R.id.maximizeButton);
ImageButton minimizeButton = activity.findViewById(R.id.minimizeButton);
FloatingActionButton editButton = activity.findViewById(R.id.fabEdit);
SeekBar barcodeScaler = activity.findViewById(R.id.barcodeScaler);
// Android should not be in fullscreen mode
int uiOptions = activity.getWindow().getDecorView().getSystemUiVisibility();
assertNotEquals(uiOptions | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY, uiOptions);
assertNotEquals(uiOptions | View.SYSTEM_UI_FLAG_FULLSCREEN, uiOptions);
// Elements should be visible
// Elements should be visible (except minimize button and scaler)
assertEquals(View.VISIBLE, collapsingToolbarLayout.getVisibility());
assertEquals(View.VISIBLE, bottomSheet.getVisibility());
assertEquals(View.VISIBLE, maximizeButton.getVisibility());
assertEquals(View.GONE, minimizeButton.getVisibility());
assertEquals(View.VISIBLE, editButton.getVisibility());
assertEquals(View.GONE, barcodeScaler.getVisibility());
// Click barcode to toggle fullscreen
barcodeImage.performClick();
shadowOf(getMainLooper()).idle();
// Android should be in fullscreen mode
uiOptions = activity.getWindow().getDecorView().getSystemUiVisibility();
assertEquals(uiOptions | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY, uiOptions);
assertEquals(uiOptions | View.SYSTEM_UI_FLAG_FULLSCREEN, uiOptions);
// Elements should not be visible
// Elements should not be visible (except minimize button and scaler)
assertEquals(View.GONE, collapsingToolbarLayout.getVisibility());
assertEquals(View.GONE, bottomSheet.getVisibility());
assertEquals(View.GONE, maximizeButton.getVisibility());
assertEquals(View.VISIBLE, minimizeButton.getVisibility());
assertEquals(View.GONE, editButton.getVisibility());
assertEquals(View.VISIBLE, barcodeScaler.getVisibility());
// Clicking barcode again should deactivate fullscreen mode
barcodeImage.performClick();
shadowOf(getMainLooper()).idle();
uiOptions = activity.getWindow().getDecorView().getSystemUiVisibility();
assertNotEquals(uiOptions | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY, uiOptions);
assertNotEquals(uiOptions | View.SYSTEM_UI_FLAG_FULLSCREEN, uiOptions);
assertEquals(View.VISIBLE, collapsingToolbarLayout.getVisibility());
assertEquals(View.VISIBLE, bottomSheet.getVisibility());
assertEquals(View.VISIBLE, maximizeButton.getVisibility());
assertEquals(View.GONE, minimizeButton.getVisibility());
assertEquals(View.VISIBLE, editButton.getVisibility());
assertEquals(View.GONE, barcodeScaler.getVisibility());
// Another click back to fullscreen
barcodeImage.performClick();
shadowOf(getMainLooper()).idle();
uiOptions = activity.getWindow().getDecorView().getSystemUiVisibility();
assertEquals(uiOptions | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY, uiOptions);
assertEquals(uiOptions | View.SYSTEM_UI_FLAG_FULLSCREEN, uiOptions);
assertEquals(View.GONE, collapsingToolbarLayout.getVisibility());
assertEquals(View.GONE, bottomSheet.getVisibility());
assertEquals(View.GONE, maximizeButton.getVisibility());
assertEquals(View.VISIBLE, minimizeButton.getVisibility());
assertEquals(View.GONE, editButton.getVisibility());
assertEquals(View.VISIBLE, barcodeScaler.getVisibility());
// In full screen mode, back button should disable fullscreen
activity.onBackPressed();
shadowOf(getMainLooper()).idle();
uiOptions = activity.getWindow().getDecorView().getSystemUiVisibility();
assertNotEquals(uiOptions | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY, uiOptions);
assertNotEquals(uiOptions | View.SYSTEM_UI_FLAG_FULLSCREEN, uiOptions);
assertEquals(View.VISIBLE, collapsingToolbarLayout.getVisibility());
assertEquals(View.VISIBLE, bottomSheet.getVisibility());
assertEquals(View.VISIBLE, maximizeButton.getVisibility());
assertEquals(View.GONE, minimizeButton.getVisibility());
assertEquals(View.VISIBLE, editButton.getVisibility());
assertEquals(View.GONE, barcodeScaler.getVisibility());
// Pressing back when not in full screen should finish activity
activity.onBackPressed();
shadowOf(getMainLooper()).idle();
assertEquals(true, activity.isFinishing());
db.close();
@@ -918,7 +1098,7 @@ public class LoyaltyCardViewActivityTest
{
Date date = new Date();
Uri importUri = Uri.parse("https://thelastproject.github.io/Catima/share?store=Example%20Store&note=&expiry=" + date.getTime() + "&cardid=123456&barcodetype=AZTEC&headercolor=-416706&headertextcolor=-1");
Uri importUri = Uri.parse("https://thelastproject.github.io/Catima/share?store=Example%20Store&note=&expiry=" + date.getTime() + "&balance=10&balancetype=USD&cardid=123456&barcodetype=AZTEC&headercolor=-416706&headertextcolor=-1");
Intent intent = new Intent();
intent.setData(importUri);
@@ -932,7 +1112,9 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
final Context context = ApplicationProvider.getApplicationContext();
checkAllFields(activity, ViewMode.ADD_CARD, "Example Store", "", DateFormat.getDateInstance(DateFormat.LONG).format(date), "123456", "AZTEC");
shadowOf(getMainLooper()).idle();
checkAllFields(activity, ViewMode.ADD_CARD, "Example Store", "", DateFormat.getDateInstance(DateFormat.LONG).format(date), "10.00", "$", "123456", null, "AZTEC");
assertEquals(-416706, ((ColorDrawable) activity.findViewById(R.id.thumbnail).getBackground()).getColor());
}
@@ -953,7 +1135,7 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
final Context context = ApplicationProvider.getApplicationContext();
checkAllFields(activity, ViewMode.ADD_CARD, "Example Store", "", context.getString(R.string.never), "123456", "AZTEC");
checkAllFields(activity, ViewMode.ADD_CARD, "Example Store", "", context.getString(R.string.never), "0", context.getString(R.string.points), "123456", null, "AZTEC");
assertEquals(-416706, ((ColorDrawable) activity.findViewById(R.id.thumbnail).getBackground()).getColor());
}
}

View File

@@ -21,6 +21,7 @@ import org.robolectric.annotation.Config;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.shadows.ShadowActivity;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@@ -60,10 +61,11 @@ public class MainActivityTest
assertTrue(menu != null);
// The settings, import/export, groups, search and add button should be present
assertEquals(menu.size(), 5);
assertEquals(menu.size(), 6);
assertEquals("Search", menu.findItem(R.id.action_search).getTitle().toString());
assertEquals("Groups", menu.findItem(R.id.action_manage_groups).getTitle().toString());
assertEquals("Import/Export", menu.findItem(R.id.action_import_export).getTitle().toString());
assertEquals("Privacy Policy", menu.findItem(R.id.action_privacy_policy).getTitle().toString());
assertEquals("About", menu.findItem(R.id.action_about).getTitle().toString());
assertEquals("Settings", menu.findItem(R.id.action_settings).getTitle().toString());
}
@@ -95,7 +97,7 @@ public class MainActivityTest
assertEquals(0, list.getCount());
DBHelper db = new DBHelper(mainActivity);
db.insertLoyaltyCard("store", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", null, BarcodeFormat.UPC_A, Color.BLACK, 0);
assertEquals(View.VISIBLE, helpText.getVisibility());
assertEquals(View.GONE, noMatchingCardsText.getVisibility());
@@ -131,10 +133,10 @@ public class MainActivityTest
assertEquals(0, list.getCount());
DBHelper db = new DBHelper(mainActivity);
db.insertLoyaltyCard("storeB", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
db.insertLoyaltyCard("storeA", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
db.insertLoyaltyCard("storeD", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1);
db.insertLoyaltyCard("storeC", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1);
db.insertLoyaltyCard("storeB", "note", null, new BigDecimal("0"), null, "cardId", null, BarcodeFormat.UPC_A, Color.BLACK, 0);
db.insertLoyaltyCard("storeA", "note", null, new BigDecimal("0"), null, "cardId", null, BarcodeFormat.UPC_A, Color.BLACK, 0);
db.insertLoyaltyCard("storeD", "note", null, new BigDecimal("0"), null, "cardId", null, BarcodeFormat.UPC_A, Color.BLACK, 1);
db.insertLoyaltyCard("storeC", "note", null, new BigDecimal("0"), null, "cardId", null, BarcodeFormat.UPC_A, Color.BLACK, 1);
assertEquals(View.VISIBLE, helpText.getVisibility());
assertEquals(View.GONE, noMatchingCardsText.getVisibility());
@@ -232,8 +234,8 @@ public class MainActivityTest
TabLayout groupTabs = mainActivity.findViewById(R.id.groups);
DBHelper db = new DBHelper(mainActivity);
db.insertLoyaltyCard("The First Store", "Initial note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
db.insertLoyaltyCard("The Second Store", "Secondary note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
db.insertLoyaltyCard("The First Store", "Initial note", null, new BigDecimal("0"), null, "cardId", null, BarcodeFormat.UPC_A, Color.BLACK, 0);
db.insertLoyaltyCard("The Second Store", "Secondary note", null, new BigDecimal("0"), null, "cardId", null, BarcodeFormat.UPC_A, Color.BLACK, 0);
db.insertGroup("Group one");
List<Group> groups = new ArrayList<>();

View File

@@ -0,0 +1,104 @@
package protect.card_locker;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.util.Util;
import java.math.BigDecimal;
import java.util.Currency;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
@RunWith(RobolectricTestRunner.class)
@Config(sdk = 23)
public class UtilsTest
{
@Test
public void parseBalances()
{
assertEquals("1", Utils.parseCurrency("1", false).toPlainString());
assertEquals("1", Utils.parseCurrency("1", true).toPlainString());
assertEquals("1.00", Utils.parseCurrency("1.00", true).toPlainString());
assertEquals("1.00", Utils.parseCurrency("1,00", true).toPlainString());
assertEquals("1.00", Utils.parseCurrency("1 00", true).toPlainString());
assertEquals("25", Utils.parseCurrency("2.5", false).toPlainString());
assertEquals("25", Utils.parseCurrency("2,5", false).toPlainString());
assertEquals("25", Utils.parseCurrency("2 5", false).toPlainString());
assertEquals("205", Utils.parseCurrency("2.05", false).toPlainString());
assertEquals("205", Utils.parseCurrency("2,05", false).toPlainString());
assertEquals("205", Utils.parseCurrency("2 05", false).toPlainString());
assertEquals("2.5", Utils.parseCurrency("2.5", true).toPlainString());
assertEquals("2.5", Utils.parseCurrency("2,5", true).toPlainString());
assertEquals("2.5", Utils.parseCurrency("2 5", true).toPlainString());
assertEquals("2.05", Utils.parseCurrency("2.05", true).toPlainString());
assertEquals("2.05", Utils.parseCurrency("2,05", true).toPlainString());
assertEquals("2.05", Utils.parseCurrency("2 05", true).toPlainString());
assertEquals("2.50", Utils.parseCurrency("2.50", true).toPlainString());
assertEquals("2.50", Utils.parseCurrency("2,50", true).toPlainString());
assertEquals("2.50", Utils.parseCurrency("2 50", true).toPlainString());
assertEquals("995", Utils.parseCurrency("9.95", false).toPlainString());
assertEquals("995", Utils.parseCurrency("9,95", false).toPlainString());
assertEquals("995", Utils.parseCurrency("9 95", false).toPlainString());
assertEquals("9.95", Utils.parseCurrency("9.95", true).toPlainString());
assertEquals("9.95", Utils.parseCurrency("9,95", true).toPlainString());
assertEquals("9.95", Utils.parseCurrency("9 95", true).toPlainString());
assertEquals("1234", Utils.parseCurrency("1234", false).toPlainString());
assertEquals("1234", Utils.parseCurrency("1.234", false).toPlainString());
assertEquals("1234", Utils.parseCurrency("1,234", false).toPlainString());
assertEquals("1234", Utils.parseCurrency("1 234", false).toPlainString());
assertEquals("1234", Utils.parseCurrency("1234", true).toPlainString());
assertEquals("1234.00", Utils.parseCurrency("1234.00", true).toPlainString());
assertEquals("1234.00", Utils.parseCurrency("1.234.00", true).toPlainString());
assertEquals("1234.00", Utils.parseCurrency("1.234,00", true).toPlainString());
assertEquals("1234.00", Utils.parseCurrency("1,234.00", true).toPlainString());
assertEquals("1234.00", Utils.parseCurrency("1,234,00", true).toPlainString());
assertEquals("1234.00", Utils.parseCurrency("1 234,00", true).toPlainString());
assertEquals("1234.00", Utils.parseCurrency("1 234,00", true).toPlainString());
assertEquals("1234.00", Utils.parseCurrency("1 234 00", true).toPlainString());
}
@Test
public void formatBalances()
{
Currency euro = Currency.getInstance("EUR");
assertEquals("1", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("1"), null));
assertEquals("1.00", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("1"), euro));
assertEquals("25", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("25"), null));
assertEquals("25.00", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("25"), euro));
assertEquals("2", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("2.5"), null));
assertEquals("2.50", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("2.5"), euro));
assertEquals("2", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("2.05"), null));
assertEquals("2.05", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("2.05"), euro));
assertEquals("2", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("2.50"), null));
assertEquals("2.50", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("2.50"), euro));
assertEquals("995", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("995"), null));
assertEquals("995.00", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("995"), euro));
assertEquals("10", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("9.95"), null));
assertEquals("9.95", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("9.95"), euro));
assertEquals("1,234", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("1234"), null));
assertEquals("1,234.00", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("1234"), euro));
assertEquals("1,234", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("1234.00"), null));
assertEquals("1,234.00", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("1234.00"), euro));
}
}

View File

@@ -5,12 +5,12 @@ buildscript {
google()
jcenter()
maven { url "https://jitpack.io" }
maven { url "https://plugins.gradle.org/m2/" }
gradlePluginPortal()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.1'
classpath 'gradle.plugin.com.github.spotbugs.snom:spotbugs-gradle-plugin:4.6.0'
classpath 'com.android.tools.build:gradle:4.1.3'
classpath 'gradle.plugin.com.github.spotbugs.snom:spotbugs-gradle-plugin:4.7.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -22,7 +22,7 @@ allprojects {
google()
jcenter()
maven { url "https://jitpack.io" }
maven { url "https://plugins.gradle.org/m2/" }
gradlePluginPortal()
}
}

View File

@@ -2,10 +2,11 @@
![Android CI](https://github.com/TheLastProject/Catima/workflows/Android%20CI/badge.svg)
[![Translation status](https://hosted.weblate.org/widgets/catima/-/svg-badge.svg)](https://hosted.weblate.org/engage/catima/)
<a href="https://f-droid.org/repository/browse/?fdid=me.hackerchick.catima" target="_blank">
<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" height="90"/></a>
<a href="https://play.google.com/store/apps/details?id=me.hackerchick.catima" target="_blank">
<img src="https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png" alt="Get it on Google Play" height="90"/></a>
<a href="https://f-droid.org/repository/browse/?fdid=me.hackerchick.catima" target="_blank">
<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" height="90"/></a>
<a href="https://apt.izzysoft.de/fdroid/index/apk/me.hackerchick.catima" target="_blank">
<img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" alt="Get it on IzzyOnDroid" height="90"/></a>

View File

@@ -1,11 +0,0 @@
# Migrating from other apps
We believe people shouldn't be locked into any specific app. That's why Catima will always allow you to export your data.
We also try to support important your data from other applications whenever possible.
Currently, importing is supported from the following apps:
[Loyalty Card Keychain](protect_card_locker)
Is the app you want to import from not listed? [Tell us about it](https://github.com/TheLastProject/Catima/issues) and we will look into supporting it.

View File

@@ -1,38 +0,0 @@
# Migrating from Loyalty Card Keychain
As Catima is based on Loyalty Card Keychain, importing your data from it is very simple.
## 1. Open Loyalty Card Keychain
![Step 1](step_1.png)
## 2. Press the More Options button in the top right
![Step 2](step_2.png)
## 3. Press Import/Export
![Step 3](step_3.png)
## 4. Press Export
![Step 4](step_4.png)
## 5. Choose a save location, filename (default: LoyaltyCardKeychain.csv) and press Save
![Step 5](step_5.png)
## 6. Confirm the export was succesful
![Step 6](step_6.png)
## 7. Open Catima
![Step 7](step_7.png)
## 8. Press the More Options button in the top right
![Step 8](step_8.png)
## 9. Press Import/Export
![Step 9](step_9.png)
## 10. Press From Filesystem
![Step 10](step_10.png)
## 11. Choose the file you saved in step 5
![Step 11](step_11.png)
## That's it, you've succesfully imported your Loyalty Card Keychain database into Catima

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

View File

@@ -1,7 +1,7 @@
# PRIVACY POLICY FOR LOYALTY CARD KEYCHAIN
# PRIVACY POLICY FOR CATIMA
This privacy policy governs your use of the software application Catima (“Application”) for mobile devices
that was created by Protect. The Application is designed to store and display barcodes.
that was created by Sylvia van Os. The Application is designed to store and display barcodes.
# What information does the Application obtain and how is it used?

View File

@@ -2,9 +2,9 @@
Someone wants to share a card with you. To import this card, you will first need to install the Catima app. It is free, Open Source and contains no ads.
<a href="https://f-droid.org/repository/browse/?fdid=me.hackerchick.catima" target="_blank">
<img src="https://f-droidgitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" height="90"/></a>
<a href="https://play.google.com/store/apps/details?id=me.hackerchick.catima" target="_blank">
<img src="https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png" alt="Get it on Google Play" height="90"/></a>
<a href="https://f-droid.org/repository/browse/?fdid=me.hackerchick.catima" target="_blank">
<img src="https://f-droidgitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" height="90"/></a>
After installing the app, just click the link you were given again and choose "Import into Catima".

View File

@@ -1,7 +1,7 @@
Bist Du es auch leid, beim Bezahlen an der Kasse im Supermarkt jedes mal nach dieser Bonuskarte aus Plastik zu suchen? Genervt davon, dass die Brieftasche durch die ganzen Plastikkarten gefühlte fünf Meter dick ist? Wäre es nicht toll, eine <i>freie</i> Lösung für das Problem zu haben, die Deine Daten nicht noch zusätzlich mit weiteren Anbietern teilt?
Bist du es auch leid, beim Bezahlen an der Kasse im Supermarkt jedes mal nach dieser Bonuskarte aus Plastik zu suchen? Genervt davon, dass die Brieftasche durch die ganzen Plastikkarten gefühlte fünf Meter dick ist? Wäre es nicht toll, eine <i>freie</i> Lösung für das Problem zu haben, die deine Daten nicht noch zusätzlich mit weiteren Anbietern teilt?
<i>Catima</i> ist eine App, die Deine auf Barcodes basierenden Kundenkarten auf Deinem Smartphone verwaltet. <i>Catima</i> ist Open Source und kann eines richtig gut: Deine Karten verwalten!
<i>Catima</i> ist eine Anwendung, die deine auf Strichcodes basierenden Kundenkarten auf deinem Smartphone verwaltet. <i>Catima</i> ist quelloffen und kann eines richtig gut: Deine Karten verwalten!
Neue Karten können mit Leichtigkeit hinzugefügt werden. Entweder nutzt Du die Kamera Deines Smartphones, um den Barcode direkt einzulesen oder Du gibst die unter selbigem befindliche Ziffernfolge von Hand ein. An der Kasse zeigt <i>Catima</i> sodann den Barcode auf dem Display Deines Smartphones an, sodass er vom Scanner gelesen werden kann. Sollte der Scanner dabei doch einmal streiken (was selten vorkommt und wenn, dann meist nur bei älteren Scanner-Modellen), kann der Verkäufer die ebenfalls angezeigten Ziffern wiederum auch händisch erfassen.
Neue Karten können mit Leichtigkeit hinzugefügt werden. Entweder nutzt du die Kamera deines Smartphones, um den Strichcode direkt einzulesen oder du gibst die unter selbigem befindliche Ziffernfolge von Hand ein. An der Kasse zeigt <i>Catima</i> sodann den Strichcode auf dem Bildschirm deines Mobiltelefones an, sodass er vom Scanner gelesen werden kann. Sollte der Scanner dabei doch einmal streiken (was selten vorkommt und wenn, dann meist nur bei älteren Scanner-Modellen), kann der Verkäufer die ebenfalls angezeigten Ziffern wiederum auch händisch erfassen.
Die App benötigt nur wenige Berechtigungen und greift nie auf das Internet zu. <i>Catima</i> bietet auch die Möglichkeit, Deine Kartensammlung zu exportieren sowie zu importieren. Damit kannst Du die Daten z. B. auch auf ein anderes Gerät übertragen oder etwa nach einem Werksreset wieder neu einlesen.
Die Anwendung benötigt nur wenige Berechtigungen und greift nie auf das Internet zu. <i>Catima</i> bietet auch die Möglichkeit, deine Kartensammlung zu exportieren sowie zu importieren. Damit kannst du die Daten z. B. auch auf ein anderes Gerät übertragen oder etwa nach einem Werksreset wieder neu einlesen.

View File

@@ -1 +1 @@
Verwaltet barcode-basierte Kunden-/Treuekarten auf dem Handy
Keine Plastikverschmutzung. Speichere Rabatte, Mitgliedskarten und Strichcodes.

View File

@@ -1 +1 @@
Catima
Catima Kundenkarten- und Ticketverwaltung

View File

@@ -1,7 +1,23 @@
Are you tired of searching for your plastic reward card while checking out at the store? Are you looking for a free solution which will not take your information?
Catima is a application which will store your barcode-based loyalty cards on your phone. The application is open source and tries to do one thing well: manage your cards!
New cards can be added in a snap. Either use your camera to capture the barcode, or type in the number. When the barcode is loaded at the store and displayed it can be scanned with a modern barcode scanner. (Some stores use older barcode scanners, such as flatbed scanners, instead of image scanners. These cannot read the smartphone's display. Instead, request the clerk to type in the number manually).
The application requires very few permissions, and never attempts to access the Internet. There is an option to backup your cards to local storage. From there you can send the backup somewhere safe.
Stop the search for plastic reward cards during store or webshop checkout.
<b>Scan barcodes to your device using its camera, forget about cards.</b>
😺
Forget your wallet, or keep it ultralight for valuables.
😺
With this essential everyday carry (EDC) tool you can replace useless plastic with cash.
😺
- Avoid spying with very few permissions. No Internet access and no ads.
- Add cards or codes with names and customizable colours.
- Manual code entry if there is no barcode to store, or it can't be used.
- Import cards and codes from files, Catima, Loyalty Card Keychain, Voucher Vault, and FidMe.
- Make a backup of all your cards and transfer them to a new device if you want.
- Share coupons, exclusive offers, promo codes, or cards and codes using any app.
- Dark theme and accessibility options for vision impaired users.
- Made for everyone by the libre software community.
- Localized hand-made translations for 20+ languages.
- Gratis, supported by community contributions.
- Use, study, change and share it as you wish; <i>with all</i>.
- Not only Free Software / Open Source. <i>Copylefted</i> libre software (GPLv3+) card management.
😺
Simplify your life and shopping, and never lose a paper receipt, in-store payment gift card or airplane ticket again.
Take all your rewards and bonuses with you, and save as you go.
😺

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 64 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 59 KiB

View File

@@ -1 +1 @@
Manages barcode-based store/loyalty cards on your phone
No plastic pollution. 💳 Store discounts, membership cards and barcodes in this EDC wallet.

View File

@@ -1 +1 @@
Catima - Loyalty Card & Ticket Manager
Catima — Card Wallet for Loyalty Cards, Tickets, and Coupons 🎴

View File

@@ -1,7 +1,23 @@
Vous en avez assez de chercher votre carte de fidélité en plastique à la caisse ? Vous cherchez une solution gratuite qui respecte vos données ?
Catima est une application qui stocke toutes vos cartes de fidélité à code-barres dans votre téléphone. L'application ne fera qu'une chose : gérer vos cartes du mieux qu'elle peut. Son code est ouvert, vous pouvez donc le vérifier par vous-même !
De nouvelles cartes peuvent être ajoutées en un clin d'œil. Vous pouvez soit photographier le code-barres, soit saisir le numéro manuellement. Lorsqu vous chargez une carte en caisse, son code-barres est affiché, ce qui permet au vendeur de le scanner avec n'importe quel lecteur moderne. (Certains magasins utilisent des lecteurs plus anciens, tels que des scanners à plat. Ceux-ci ne peuvent pas lire l'écran du téléphone. Le vendeur devra donc saisir le numéro manuellement).
L'application ne nécessite que très peu d'autorisations et ne tente jamais d'accéder à Internet. Il existe une fonction d'import/export de vos cartes. Celà vous permettra de les sauvegarder dans un endroit sûr.
Arrêtez de chercher des cartes de fidélité en plastique lors du passage à la caisse des magasins ou des boutiques en ligne.
<b>Scannez les codes-barres sur votre appareil en utilisant son appareil photo, oubliez les cartes</b>.
😺
Oubliez votre portefeuille, ou gardez-le ultraléger pour les objets de valeur.
😺
Avec cet outil essentiel à emporter au quotidien, vous pouvez remplacer le plastique inutile par de l'argent liquide.
😺
- Évitez l'espionnage avec très peu de permissions. Aucun accès à Internet et aucune publicité.
- Ajoutez des cartes ou des codes avec des noms et des couleurs personnalisables.
- Saisie manuelle du code s'il n'y a pas de code-barres à stocker ou s'il ne peut pas être utilisé.
- Importez des cartes et des codes depuis des fichiers, Catima, Loyalty Card Keychain, Voucher Vault et FidMe.
- Faites une sauvegarde de toutes vos cartes et transférez-les sur un nouvel appareil si vous le souhaitez.
- Partagez des coupons, des offres exclusives, des codes promotionnels ou des cartes et des codes en utilisant n'importe quelle application.
- Thème sombre et options d'accessibilité pour les utilisateurs malvoyants.
- Fait pour tout le monde par la communauté des logiciels libres.
- Traductions artisanales localisées pour plus de 20 langues.
- Gratis, soutenu par les contributions de la communauté.
- Utilisez, étudiez, modifiez et partagez comme vous le souhaitez ; <i>avec tous</i>.
- Pas seulement logiciel libre et à code source ouvert <i>logiciel libre à copyleft</i> (GPLv3+) pour la gestion des cartes.
😺
Simplifiez votre vie et vos achats, et ne perdez plus jamais un reçu papier, une carte cadeau de paiement en magasin ou un billet d'avion.
Emportez toutes vos récompenses et primes avec vous, et économisez au fur et à mesure.
😺

View File

@@ -1 +1 @@
Gère les cartes de fidélité basées sur des codes-barres sur votre téléphone
Pas de pollution plastique. Stockez les cartes de fidélité dans ce portefeuille.

View File

@@ -1 +1 @@
Catima Cartes de fidélité
Catima Cartes de fidélité, tickets et coupons

View File

@@ -0,0 +1,7 @@
Sei stanco di cercare la tua carta fedeltà durante i pagamenti in negozio? Cerchi una soluzione gratuita che non catture le tue informazioni?
Catima è un'applicazione che memorizzerà sul tuo telefono le tue carte fedeltà basate su codici a barre. L'applicazione è open source e cerca di fare bene una cosa: gestire le tue carte!
Nuove carte possono essere aggiunte in un attimo. Utilizza la fotocamera per acquisire il codice a barre o digita il numero. Quando il codice a barre viene caricato nel negozio e visualizzato, può essere scansionato con un moderno lettore di codici a barre. (Alcuni negozi utilizzano lettori di codici a barre meno recenti, come scanner piani, invece di scanner di immagini. Questi non possono leggere il display dello smartphone. In questi casi, devi chiedere all'impiegato di digitare il numero manualmente).
L'applicazione richiede pochissime autorizzazioni e non tenta mai di accedere a Internet. E' possibile eseguire il backup delle carte nella memoria locale. E da lì puoi conservare il backup da qualche parte al sicuro.

View File

@@ -0,0 +1 @@
Gestisce le carte fedeltà basate su codici a barre sul telefono

View File

@@ -0,0 +1 @@
Catima Gestore di carte fedeltà

View File

@@ -1,7 +1,23 @@
Lei av å leite opp stamkundekortet mens du betaler i butikken? Ser du etter en fri løsning som ikke stjeler data?
Catima er et program som lagrer strekkodebaserte stamkundekort på din enhet. Det er fri programvare, og gjør én ting bra, altså håndtering av kort.
Nye kort kan legges til raskt. Bruk enten kamera for å scanne inn strekkoden, eller skriv inn nummeret. Når strekkoden er innlastet på butikken kan den skannes med en moderne strekkodeskanner. (Noen butikker bruker eldre strekkodeskannere, som inkrementelle skannere, istedenfor bildeskannere. Disse kan ikke lese skjermen på en smartelefon. Få isteden en ansatt til å skrive inn nummeret manuelt).
Programmet krever veldig få tilganger, og bruker aldri Internett. Det er et valg om å sikkerhetskopiere kort lokalt. Derfra kan du sende sikkerhetskopien til en trygg plass.
Stopp leitingen etter plastikkfordelskort når du er i kassen på nett eller i butikk.
<b>Skann strekkoder til enheten din ved bruk av kameraet, og glem kortene.</b>
😺
Glem hele lommeboken, eller behold den ultralett for verdisaker.
😺
Med dette essensielle verktøyet kan du erstatte unyttig plast med kontanter.
😺
- Unngå spionasje med veldig få tilganger. Ingen tilgang til Internett, og ingen reklame.
- Legg til kort eller koder med navn og egne farger.
- Manuell kodeinnskriving hvis det ikke er noen strekkode å lagre, eller den ikke kan brukes.
- Importer kort og koder fra filer, Catima, Loyalty Card Keychain, Voucher Vault, og FidMe.
- Lag en sikkerhetskopi av alle kortene dine og overfør dem til en ny enhet hvis du ønsker det.
- Del kupponger, eksklusive tilbud, promokoder eller kort og koder ved bruk av ethvert program.
- Mørk drakt og tilgjengelighetsvalg for synshemmede brukere.
- Laget for alle av friprogramvare-gemenskapen.
- Oversettelser til mer enn 20 språk gjort for hånd.
- Gratis, med støtte fra gemenskapsbidrag.
- Bruk, studer, endre og del som du vil; <i>med alle</i>.
- Ikke bare åpen kildekode. <i>Gemenhetslig</i> fri programvare (GPLv3+).
😺
Forenkle livet ditt og handlingen, og aldri mist en papirkvittering, butikkbetalingskort, gavekort eller flybillett igjen.
Ta alle dine belønninger og bonuser med deg, og spar mens du er på farten.
😺

Some files were not shown because too many files have changed in this diff Show More