Compare commits

...

143 Commits

Author SHA1 Message Date
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?
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
Sylvia van Os
c11e995684 Fix blurry barcode in edit view 2021-01-18 19:00:27 +01:00
Sylvia van Os
184af4d272 Fix wrong string on expired 2021-01-17 23:38:07 +01:00
Sylvia van Os
d7356704ea Make spotBugs happy 2021-01-17 23:31:05 +01:00
Sylvia van Os
66573fd2da Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-01-17 22:56:14 +01:00
Sylvia van Os
36ff2333b7 Fix all unit tests 2021-01-17 22:54:24 +01:00
Sylvia van Os
9325f33da6 Merge pull request #138 from weblate/weblate-catima-catima
Translations update from Weblate
2021-01-17 18:35:28 +01:00
Sylvia van Os
9e40d9b6b3 Fix incorrect escaping 2021-01-17 18:34:55 +01:00
Sylvia van Os
4679051df1 Fix incorrect escaping 2021-01-17 18:34:13 +01:00
Heimen Stoffels
27cbad1b44 Translated using Weblate (Dutch)
Currently translated at 100.0% (119 of 119 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-01-17 18:24:50 +01:00
Allan Nordhøy
ba24b49009 Translated using Weblate (Norwegian Bokmål)
Currently translated at 98.3% (117 of 119 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-01-17 18:24:50 +01:00
Sylvia van Os
c408beb68c Progress on fixing tests 2021-01-17 18:24:21 +01:00
Sylvia van Os
5b7590e4e1 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-01-16 21:18:19 +01:00
Sylvia van Os
574832d09b Merge pull request #137 from weblate/weblate-catima-catima
Translations update from Weblate
2021-01-16 20:20:15 +01:00
Sylvia van Os
8029be7b2b Update CHANGELOG 2021-01-16 20:19:15 +01:00
Heimen Stoffels
390a2e1b5a Translated using Weblate (Dutch)
Currently translated at 100.0% (115 of 115 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-01-16 20:16:51 +01:00
Allan Nordhøy
803555f94e Translated using Weblate (Norwegian Bokmål)
Currently translated at 98.2% (113 of 115 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-01-16 20:16:51 +01:00
Sylvia van Os
6488dc0fe3 Implement expiry 2021-01-16 20:16:20 +01:00
Sylvia van Os
ddc385c39d Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-01-16 15:03:15 +01:00
Sylvia van Os
6382c54d1a Separate edit UI into tabs 2021-01-16 15:02:14 +01:00
Sylvia van Os
af515d307a Merge pull request #136 from weblate/weblate-catima-catima
Translations update from Weblate
2021-01-13 09:47:44 +01:00
Yurical
e1edeb364e Translated using Weblate (Korean)
Currently translated at 87.5% (98 of 112 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ko/
2021-01-13 09:32:08 +01:00
Sylvia van Os
18b95e96eb Release 1.6.2 2021-01-04 20:42:06 +01:00
Sylvia van Os
5960857568 Fix bottom sheet or edit button drawing over card ID 2021-01-04 20:20:44 +01:00
Sylvia van Os
6426a99be4 Merge pull request #133 from TacoTheDank/master
Library updates and AndroidX Preferences
2021-01-04 20:05:50 +01:00
Sylvia van Os
1f76809300 Memory leak and spotBugs fixes 2021-01-04 19:58:08 +01:00
Sylvia van Os
5346726ae1 Merge pull request #135 from weblate/weblate-catima-catima
Translations update from Weblate
2021-01-03 23:45:39 +01:00
Sylvia van Os
3828454a26 Fix XML 2021-01-03 23:35:56 +01:00
inesre
7995c2ba1a Translated using Weblate (Spanish)
Currently translated at 100.0% (113 of 113 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/es/
2021-01-03 23:29:14 +01:00
Sylvia van Os
9ee4184122 Archive test results 2021-01-03 21:12:11 +01:00
TacoTheDank
319ba195e4 Rudimentary migration of findbugs to spotbugs 2020-12-26 20:34:44 -05:00
TacoTheDank
a21e9f142c Resolve app name in build.gradle 2020-12-26 19:05:41 -05:00
TacoTheDank
22fbbd3341 Migrate preferences to AndroidX 2020-12-26 19:05:18 -05:00
TacoTheDank
191e22db65 Update a bunch of libraries 2020-12-26 19:05:01 -05:00
TacoTheDank
ceba99d11f Clean up build gradles 2020-12-26 19:04:48 -05:00
TacoTheDank
dd1840ce5c Update gradle wrapper 2020-12-23 19:47:27 -05:00
TacoTheDank
b1608d6a9f Update .gitignore 2020-12-23 19:47:09 -05:00
Sylvia van Os
aee03170dc Fix build 2020-12-22 18:40:02 +01:00
Sylvia van Os
419e12c0eb Merge pull request #132 from weblate/weblate-catima-catima
Translations update from Weblate
2020-12-22 16:37:18 +01:00
Yurical
6c2ce61ddc Translated using Weblate (Korean)
Currently translated at 66.3% (75 of 113 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ko/
2020-12-22 16:29:11 +01:00
K. Herbert
fc342b4197 Translated using Weblate (Italian)
Currently translated at 100.0% (113 of 113 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2020-12-22 16:29:11 +01:00
K. Herbert
a6536307ad Translated using Weblate (French)
Currently translated at 100.0% (113 of 113 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2020-12-22 16:29:11 +01:00
K. Herbert
8a517e8161 Translated using Weblate (German)
Currently translated at 100.0% (113 of 113 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2020-12-22 16:29:10 +01:00
Sylvia van Os
34f314dad8 Merge pull request #131 from weblate/weblate-catima-catima
Translations update from Weblate
2020-12-20 09:50:40 +01:00
Yurical
2e49e36e0c Added translation using Weblate (Korean) 2020-12-20 08:55:30 +01:00
Sylvia van Os
a7afdae83c Add big translation panel
Translations are very important, we want to make it more obvious this exists
2020-12-19 23:19:44 +01:00
Sylvia van Os
c705136362 No longer use this image 2020-12-18 22:12:12 +01:00
68 changed files with 3283 additions and 887 deletions

View File

@@ -23,5 +23,11 @@ jobs:
run: ./gradlew lintRelease
- name: Run unit tests
run: ./gradlew testReleaseUnitTest
- name: FindBugs
run: ./gradlew findbugs
- name: SpotBugs
run: ./gradlew spotbugsRelease
- name: Archive test results
if: always()
uses: actions/upload-artifact@v1
with:
name: test-results
path: app/build/reports

13
.gitignore vendored
View File

@@ -1,10 +1,9 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
local.properties
.idea/
.DS_Store
/build
/captures
/app/release
/app/debug
build/
captures/
**/release
**/debug

View File

@@ -1,5 +1,73 @@
# Changelog
## 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:
- Separate edit UI in tabs to make it feel more spacious
- Add expiry field support
## v1.6.2 (2021-01-04)
Changes:
- Fix edit button or more info bottom sheet drawing over barcode ID
## v1.6.1 (2020-12-16)
Changes:

1
app/.gitignore vendored
View File

@@ -1 +0,0 @@
/build

View File

@@ -1,77 +1,97 @@
apply plugin: 'com.android.application'
apply plugin: 'findbugs'
import com.github.spotbugs.snom.SpotBugsTask
findbugs {
sourceSets = []
apply plugin: 'com.android.application'
apply plugin: 'com.github.spotbugs'
spotbugs {
ignoreFailures = false
effort = 'max'
excludeFilter = file("./config/spotbugs/exclude.xml")
reportsDir = file("$buildDir/reports/spotbugs/")
}
android {
compileSdkVersion 29
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "me.hackerchick.catima"
minSdkVersion 19
targetSdkVersion 29
versionCode 53
versionName "1.6.1"
vectorDrawables.useSupportLibrary = true
versionCode 64
versionName "1.11"
vectorDrawables.useSupportLibrary true
}
buildTypes {
release {
minifyEnabled false
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
resValue "string", "app_name", "Catima"
}
debug {
applicationIdSuffix ".debug"
resValue "string", "app_name", "Catima Debug"
}
}
compileOptions {
encoding "UTF-8"
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
lintOptions {
disable "GoogleAppIndexingWarning"
disable "ButtonStyle"
disable "AlwaysShowAction"
disable "MissingTranslation"
disable "MissingPrefix"
disable "GoogleAppIndexingWarning", "ButtonStyle", "AlwaysShowAction",
"MissingTranslation", "MissingPrefix"
}
// Starting with Android Studio 3 Robolectric is unable to find resources.
// The following allows it to find the resources.
testOptions {
unitTests {
includeAndroidResources = true
all {
testLogging {
events 'started', 'passed', 'skipped', 'failed'
}
}
includeAndroidResources true
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
// AndroidX
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'com.journeyapps:zxing-android-embedded:4.0.2@aar'
implementation 'com.google.zxing:core:3.3.0'
implementation 'org.apache.commons:commons-csv:1.5'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.jaredrummler:colorpicker:1.0.2'
implementation group: 'com.google.guava', name: 'guava', version: '20.0'
implementation "com.vanniktech:vntnumberpickerpreference:1.0.0"
implementation 'androidx.cardview:cardview:1.0.0'
testImplementation 'junit:junit:4.12'
testImplementation "org.robolectric:robolectric:4.0.2"
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'
// Third-party
implementation 'com.journeyapps:zxing-android-embedded:4.1.0@aar'
implementation 'com.google.zxing:core:3.3.3' // Do not upgrade past 3.3.3! Causes a crash on versions before Android 7
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'
// Testing
testImplementation 'androidx.test:core:1.0.0'
testImplementation 'junit:junit:4.13.1'
testImplementation 'org.robolectric:robolectric:4.4'
}
task findbugs(type: FindBugs, dependsOn: 'assembleDebug') {
tasks.withType(SpotBugsTask) {
description 'Run findbugs'
description 'Run spotbugs'
group 'verification'
classes = fileTree('build/intermediates/javac/debug/compileDebugJavaWithJavac/classes')
source = fileTree('src/main/java')
classpath = files()
effort = 'max'
excludeFilter = file("./config/findbugs/exclude.xml")
//classes = fileTree('build/intermediates/javac/debug/compileDebugJavaWithJavac/classes')
//source = fileTree('src/main/java')
//classpath = files()
reports {
xml.enabled = false

View File

@@ -1,15 +0,0 @@
package protect.card_locker;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application>
{
public ApplicationTest()
{
super(Application.class);
}
}

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

@@ -41,7 +41,7 @@ public class AboutActivity extends AppCompatActivity
.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("VNTNumberPickerPreference", "https://github.com/vanniktech/VNTNumberPickerPreference")
.put("NumberPickerPreference", "https://github.com/invissvenska/NumberPickerPreference")
.build();
final Map<String, String> USED_ASSETS = ImmutableMap.of

View File

@@ -4,9 +4,13 @@ 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 java.math.BigDecimal;
import java.util.Currency;
import java.util.Date;
import java.util.ArrayList;
import java.util.List;
@@ -14,20 +18,23 @@ 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 = 6;
public static final int DATABASE_VERSION = 8;
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";
@@ -36,7 +43,7 @@ public class DBHelper extends SQLiteOpenHelper
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";
@@ -57,10 +64,14 @@ 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," +
@@ -118,16 +129,34 @@ public class DBHelper extends SQLiteOpenHelper
db.execSQL("ALTER TABLE " + LoyaltyCardDbGroups.TABLE
+ " ADD COLUMN " + LoyaltyCardDbGroups.ORDER + " INTEGER DEFAULT '0'");
}
if(oldVersion < 7 && newVersion >= 7)
{
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");
}
}
public long insertLoyaltyCard(final String store, final String note, final String cardId,
final String barcodeType, final Integer headerColor,
final int starStatus)
public long insertLoyaltyCard(final String store, final String note, final Date expiry,
final BigDecimal balance, final Currency balanceType,
final String cardId, final String 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.HEADER_COLOR, headerColor);
@@ -138,7 +167,8 @@ public class DBHelper extends SQLiteOpenHelper
}
public boolean insertLoyaltyCard(final SQLiteDatabase db, final int id, final String store,
final String note, final String cardId,
final String note, final Date expiry, final BigDecimal balance,
final Currency balanceType, final String cardId,
final String barcodeType, final Integer headerColor,
final int starStatus)
{
@@ -146,6 +176,9 @@ public class DBHelper extends SQLiteOpenHelper
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.HEADER_COLOR, headerColor);
@@ -156,13 +189,17 @@ public class DBHelper extends SQLiteOpenHelper
}
public boolean updateLoyaltyCard(final int id, final String store, final String note,
final String cardId, final String barcodeType,
final Integer headerColor)
final Date expiry, final BigDecimal balance,
final Currency balanceType, final String cardId,
final String 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.HEADER_COLOR, headerColor);
@@ -215,6 +252,7 @@ public class DBHelper extends SQLiteOpenHelper
List<Group> groups = new ArrayList<>();
if (!data.moveToFirst()) {
data.close();
return groups;
}
@@ -224,6 +262,8 @@ public class DBHelper extends SQLiteOpenHelper
groups.add(Group.toGroup(data));
}
data.close();
return groups;
}
@@ -395,6 +435,7 @@ public class DBHelper extends SQLiteOpenHelper
List<Group> groups = new ArrayList<>();
if (!data.moveToFirst()) {
data.close();
return groups;
}
@@ -404,6 +445,8 @@ public class DBHelper extends SQLiteOpenHelper
groups.add(Group.toGroup(data));
}
data.close();
return groups;
}
@@ -514,33 +557,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,7 @@ package protect.card_locker;
public enum DataFormat
{
CSV,
Catima,
VoucherVault
;
}

View File

@@ -35,10 +35,12 @@ 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 DataFormat importDataFormat;
@Override
protected void onCreate(Bundle savedInstanceState)
{
@@ -93,7 +95,7 @@ public class ImportExportActivity extends AppCompatActivity
@Override
public void onClick(View v)
{
chooseFileWithIntent(intentGetContentAction, CHOOSE_EXPORTED_FILE);
chooseImportType(intentGetContentAction);
}
});
@@ -106,12 +108,35 @@ 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:
// Loyalty Card Keychain
case 1:
importDataFormat = DataFormat.Catima;
break;
// Voucher Vault
case 2:
importDataFormat = DataFormat.VoucherVault;
break;
default:
throw new IllegalArgumentException("Unknown DataFormat");
}
chooseFileWithIntent(baseIntent, IMPORT);
});
builder.show();
}
private void startImport(final InputStream target, final Uri targetUri, final DataFormat dataFormat)
{
ImportExportTask.TaskCompleteListener listener = new ImportExportTask.TaskCompleteListener()
{
@@ -123,7 +148,7 @@ public class ImportExportActivity extends AppCompatActivity
};
importExporter = new ImportExportTask(ImportExportActivity.this,
DataFormat.CSV, target, listener);
dataFormat, target, listener);
importExporter.execute();
}
@@ -139,7 +164,7 @@ public class ImportExportActivity extends AppCompatActivity
};
importExporter = new ImportExportTask(ImportExportActivity.this,
DataFormat.CSV, target, listener);
DataFormat.Catima, target, listener);
importExporter.execute();
}
@@ -294,7 +319,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 +361,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";

View File

@@ -4,10 +4,16 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
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_TYPE = DBHelper.LoyaltyCardDbIds.BARCODE_TYPE;
@@ -40,6 +46,9 @@ public class ImportURIHelper {
try {
// These values are allowed to be null
Date expiry = null;
BigDecimal balance = new BigDecimal("0");
Currency balanceType = null;
Integer headerColor = null;
Integer headerTextColor = null;
@@ -49,13 +58,29 @@ public class ImportURIHelper {
String barcodeType = uri.getQueryParameter(BARCODE_TYPE);
if (store == null || note == null || cardId == null || barcodeType == null) throw new InvalidObjectException("Not a valid import URI");
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, cardId, barcodeType, headerColor, headerTextColor, 0);
return new LoyaltyCard(-1, store, note, expiry, balance, balanceType, cardId, barcodeType, headerColor, headerTextColor, 0);
} catch (NullPointerException | NumberFormatException ex) {
throw new InvalidObjectException("Not a valid import URI");
}
@@ -69,6 +94,13 @@ 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)

View File

@@ -2,6 +2,10 @@ package protect.card_locker;
import android.database.Cursor;
import java.math.BigDecimal;
import java.util.Currency;
import java.util.Date;
import androidx.annotation.Nullable;
public class LoyaltyCard
@@ -9,6 +13,9 @@ public class LoyaltyCard
public final int id;
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;
@@ -20,13 +27,17 @@ public class LoyaltyCard
public final int starStatus;
public LoyaltyCard(final int id, final String store, final String note, final String cardId,
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 barcodeType, final Integer headerColor, final Integer headerTextColor,
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.barcodeType = barcodeType;
this.headerColor = headerColor;
@@ -39,17 +50,31 @@ public class LoyaltyCard
int id = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.ID));
String store = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STORE));
String note = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE));
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));
int starred = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS));
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);
Currency balanceType = null;
Date expiry = null;
Integer headerColor = null;
Integer headerTextColor = null;
if (cursor.isNull(balanceTypeColumn) == false)
{
balanceType = Currency.getInstance(cursor.getString(balanceTypeColumn));
}
if(expiryLong > 0)
{
expiry = new Date(expiryLong);
}
if(cursor.isNull(headerColorColumn) == false)
{
headerColor = cursor.getInt(headerColorColumn);
@@ -60,6 +85,6 @@ public class LoyaltyCard
headerTextColor = cursor.getInt(headerTextColorColumn);
}
return new LoyaltyCard(id, store, note, cardId, barcodeType, headerColor, headerTextColor, starred);
return new LoyaltyCard(id, store, note, expiry, balance, balanceType, cardId, barcodeType, headerColor, headerTextColor, starred);
}
}

View File

@@ -8,6 +8,11 @@ import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.util.Date;
import protect.card_locker.preferences.Settings;
class LoyaltyCardCursorAdapter extends CursorAdapter
@@ -37,6 +42,8 @@ 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);
// Extract properties from cursor
@@ -58,6 +65,31 @@ class LoyaltyCardCursorAdapter extends CursorAdapter
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)));
}
else
{
balanceField.setVisibility(View.GONE);
}
if(loyaltyCard.expiry != null)
{
expiryField.setVisibility(View.VISIBLE);
int expiryString = R.string.expiryStateSentence;
if(Utils.hasExpired(loyaltyCard.expiry)) {
expiryString = R.string.expiryStateSentenceExpired;
expiryField.setTextColor(context.getResources().getColor(R.color.alert));
}
expiryField.setText(context.getString(expiryString, DateFormat.getDateInstance(DateFormat.LONG).format(loyaltyCard.expiry)));
expiryField.setTextSize(settings.getCardNoteListFontSize());
}
else
{
expiryField.setVisibility(View.GONE);
}
if (loyaltyCard.starStatus!=0) star.setVisibility(View.VISIBLE);
else star.setVisibility(View.GONE);

View File

@@ -1,10 +1,14 @@
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.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import com.google.android.material.chip.Chip;
@@ -15,7 +19,9 @@ import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
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.TextWatcher;
import android.util.Log;
@@ -27,30 +33,44 @@ import android.view.ViewTreeObserver;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.material.tabs.TabLayout;
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
{
private static final String TAG = "Catima";
TabLayout tabs;
ImageView thumbnail;
EditText storeFieldEdit;
EditText noteFieldEdit;
ChipGroup groupsChips;
View cardAndBarcodeLayout;
AutoCompleteTextView expiryField;
EditText balanceField;
AutoCompleteTextView balanceCurrencyField;
TextView cardIdFieldView;
View barcodeTypeView;
AutoCompleteTextView barcodeTypeField;
ImageView barcodeImage;
View barcodeImageLayout;
@@ -73,6 +93,10 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
boolean initDone = false;
AlertDialog confirmExitDialog = null;
boolean validBalance = true;
HashMap<String, Currency> currencies = new HashMap<>();
private void extractIntentFields(Intent intent)
{
final Bundle b = intent.getExtras();
@@ -107,13 +131,19 @@ 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);
cardAndBarcodeLayout = findViewById(R.id.cardAndBarcodeLayout);
expiryField = findViewById(R.id.expiryField);
balanceField = findViewById(R.id.balanceField);
balanceCurrencyField = findViewById(R.id.balanceCurrencyField);
cardIdFieldView = findViewById(R.id.cardIdView);
barcodeTypeView = findViewById(R.id.barcodeTypeView);
barcodeTypeField = findViewById(R.id.barcodeTypeField);
barcodeImage = findViewById(R.id.barcode);
barcodeImageLayout = findViewById(R.id.barcodeLayout);
@@ -136,6 +166,131 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
public void afterTextChanged(Editable s) { }
});
expiryField.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.never))) {
expiryField.setTag(null);
} else if (s.toString().equals(getString(R.string.chooseExpiryDate))) {
if (!lastValue.toString().equals(getString(R.string.chooseExpiryDate))) {
expiryField.setText(lastValue);
};
DialogFragment datePickerFragment = new DatePickerFragment(expiryField);
datePickerFragment.show(getSupportFragmentManager(), "datePicker");
}
}
@Override
public void afterTextChanged(Editable s) {
ArrayList<String> expiryList = new ArrayList<>();
expiryList.add(0, getString(R.string.never));
expiryList.add(1, getString(R.string.chooseExpiryDate));
ArrayAdapter<String> expiryAdapter = new ArrayAdapter<>(LoyaltyCardEditActivity.this, android.R.layout.select_dialog_item, expiryList);
expiryField.setAdapter(expiryAdapter);
}
});
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) { }
@@ -179,6 +334,27 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
@Override
public void afterTextChanged(Editable s) { }
});
tabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
@edu.umd.cs.findbugs.annotations.SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
public void onTabSelected(TabLayout.Tab tab) {
showPart(tab.getText().toString());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
@edu.umd.cs.findbugs.annotations.SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
public void onTabReselected(TabLayout.Tab tab) {
showPart(tab.getText().toString());
}
});
tabs.selectTab(tabs.getTabAt(0));
}
@Override
@@ -192,10 +368,17 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
// Reset these fields, so they are re-populated in onResume().
storeFieldEdit.setText("");
noteFieldEdit.setText("");
expiryField.setTag(null);
expiryField.setText("");
balanceCurrencyField.setTag(null);
balanceCurrencyField.setText("");
balanceField.setTag(null);
balanceField.setText("");
cardIdFieldView.setText("");
barcodeTypeField.setText("");
}
@SuppressLint("DefaultLocale")
@Override
public void onResume()
{
@@ -224,6 +407,24 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
noteFieldEdit.setText(loyaltyCard.note);
}
if(expiryField.getText().length() == 0)
{
expiryField.setTag(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)
{
cardIdFieldView.setText(loyaltyCard.cardId);
@@ -259,6 +460,11 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
storeFieldEdit.setText(importCard.store);
noteFieldEdit.setText(importCard.note);
expiryField.setTag(importCard.expiry);
formatExpiryField(importCard.expiry);
balanceField.setTag(importCard.balance);
balanceCurrencyField.setTag(importCard.balanceType);
formatBalanceCurrencyField(importCard.balanceType);
cardIdFieldView.setText(importCard.cardId);
barcodeTypeField.setText(importCard.barcodeType);
headingColorValue = importCard.headerColor;
@@ -266,6 +472,11 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
else
{
setTitle(R.string.addCardTitle);
expiryField.setTag(null);
expiryField.setText(getString(R.string.never));
balanceField.setTag(new BigDecimal("0"));
balanceCurrencyField.setTag(null);
formatBalanceCurrencyField(null);
hideBarcode();
}
@@ -293,13 +504,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);
@@ -358,16 +566,27 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
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();
@@ -446,7 +665,53 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
// Nothing to do, no change made
}
});
dialog.show(getFragmentManager(), "color-picker-dialog");
dialog.show(getSupportFragmentManager(), "color-picker-dialog");
}
}
public static class DatePickerFragment extends DialogFragment
implements DatePickerDialog.OnDateSetListener {
final EditText expiryFieldEdit;
DatePickerFragment(EditText expiryFieldEdit) {
this.expiryFieldEdit = expiryFieldEdit;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the current date as the default date in the picker
final Calendar c = Calendar.getInstance();
Date date = (Date) expiryFieldEdit.getTag();
if (date != null) {
c.setTime(date);
}
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH);
int day = c.get(Calendar.DAY_OF_MONTH);
// Create a new instance of DatePickerDialog and return it
return new DatePickerDialog(getActivity(), this, year, month, day);
}
public void onDateSet(DatePicker view, int year, int month, int day) {
Calendar c = Calendar.getInstance();
c.set(Calendar.YEAR, year);
c.set(Calendar.MONTH, month);
c.set(Calendar.DAY_OF_MONTH, day);
c.set(Calendar.HOUR, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
long unixTime = c.getTimeInMillis();
Date date = new Date(unixTime);
expiryFieldEdit.setTag(date);
expiryFieldEdit.setText(DateFormat.getDateInstance(DateFormat.LONG).format(date));
}
}
@@ -454,6 +719,9 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
{
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();
@@ -476,6 +744,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()) {
@@ -485,12 +759,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, cardId, barcodeType, headingColorValue);
db.updateLoyaltyCard(loyaltyCardId, store, note, expiry, balance, balanceType, cardId, barcodeType, headingColorValue);
Log.i(TAG, "Updated " + loyaltyCardId + " to " + cardId);
}
else
{
loyaltyCardId = (int)db.insertLoyaltyCard(store, note, cardId, barcodeType, headingColorValue, 0);
loyaltyCardId = (int)db.insertLoyaltyCard(store, note, expiry, balance, balanceType, cardId, barcodeType, headingColorValue, 0);
}
db.setLoyaltyCardGroups(loyaltyCardId, selectedGroups);
@@ -622,4 +896,30 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
thumbnail.setMinimumWidth(thumbnail.getHeight());
}
private void showPart(String part) {
View cardPart = findViewById(R.id.cardPart);
View barcodePart = findViewById(R.id.barcodePart);
if (getString(R.string.card).equals(part)) {
cardPart.setVisibility(View.VISIBLE);
barcodePart.setVisibility(View.GONE);
// Explicitly hide barcode (fixes blurriness on redraw)
hideBarcode();
} else if (getString(R.string.barcode).equals(part)) {
cardPart.setVisibility(View.GONE);
barcodePart.setVisibility(View.VISIBLE);
// Redraw barcode due to size change (Visibility.GONE sets it to 0)
String formatString = barcodeTypeField.getText().toString();
if (formatString.isEmpty() || formatString.equals(getString(R.string.noBarcode))) {
hideBarcode();
} else {
generateBarcode(cardIdFieldView.getText().toString(), BarcodeFormat.valueOf(formatString));
}
} 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,14 +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.List;
import protect.card_locker.preferences.Settings;
@@ -41,13 +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;
@@ -58,10 +73,14 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
String cardIdString;
BarcodeFormat format;
FloatingActionButton editButton;
Guideline centerGuideline;
SeekBar barcodeScaler;
boolean starred;
boolean backgroundNeedsDarkIcons;
boolean barcodeIsFullscreen = false;
ViewGroup.LayoutParams barcodeImageState;
private void extractIntentFields(Intent intent)
{
@@ -73,6 +92,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
private Drawable getIcon(int icon, boolean dark)
{
Drawable unwrappedIcon = AppCompatResources.getDrawable(this, icon);
assert unwrappedIcon != null;
Drawable wrappedIcon = DrawableCompat.wrap(unwrappedIcon);
if(dark)
{
@@ -97,14 +117,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);
@@ -113,42 +125,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) {
@@ -167,14 +207,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);
}
});
}
@@ -195,23 +232,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);
}
@@ -224,6 +266,8 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
return;
}
setupOrientation();
String formatString = loyaltyCard.barcodeType;
format = !formatString.isEmpty() ? BarcodeFormat.valueOf(formatString) : null;
cardIdString = loyaltyCard.cardId;
@@ -260,12 +304,33 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
groupsView.setVisibility(View.GONE);
}
if (noteView.getVisibility() == View.VISIBLE || groupsView.getVisibility() == View.VISIBLE) {
bottomSheet.setVisibility(View.VISIBLE);
if(!loyaltyCard.balance.equals(new BigDecimal(0))) {
balanceView.setVisibility(View.VISIBLE);
balanceView.setText(getString(R.string.balanceSentence, Utils.formatBalance(this, loyaltyCard.balance, loyaltyCard.balanceType)));
}
else
{
bottomSheet.setVisibility(View.GONE);
balanceView.setVisibility(View.GONE);
}
if(loyaltyCard.expiry != null) {
expiryView.setVisibility(View.VISIBLE);
int expiryString = R.string.expiryStateSentence;
if(Utils.hasExpired(loyaltyCard.expiry)) {
expiryString = R.string.expiryStateSentenceExpired;
expiryView.setTextColor(getResources().getColor(R.color.alert));
}
expiryView.setText(getString(expiryString, DateFormat.getDateInstance(DateFormat.LONG).format(loyaltyCard.expiry)));
}
else
{
expiryView.setVisibility(View.GONE);
}
expiryView.setTag(loyaltyCard.expiry);
if (!barcodeIsFullscreen) {
makeBottomSheetVisibleIfUseful();
}
storeName.setText(loyaltyCard.store);
@@ -282,6 +347,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
}
collapsingToolbarLayout.setBackgroundColor(backgroundHeaderColor);
appBarLayout.setBackgroundColor(backgroundHeaderColor);
int textColor;
if(Utils.needsDarkForeground(backgroundHeaderColor))
@@ -293,6 +359,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);
@@ -317,34 +384,30 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
if(format != null)
{
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();
}
// Force redraw fullscreen state
setFullscreen(barcodeIsFullscreen);
}
else
{
findViewById(R.id.barcode).setVisibility(View.GONE);
maximizeButton.setVisibility(View.GONE);
barcodeImage.setVisibility(View.GONE);
}
}
@@ -357,7 +420,6 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
}
super.onBackPressed();
return;
}
@Override
@@ -434,7 +496,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)
@@ -452,6 +547,33 @@ 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, cardIdString, format).execute();
}
});
};
}
/**
* When enabled, hides the status bar and moves the barcode to the top of the screen.
*
@@ -461,10 +583,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)
@@ -472,55 +600,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,29 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
helpText.setOnTouchListener(gestureTouchListener);
noMatchingCardsText.setOnTouchListener(gestureTouchListener);
list.setOnTouchListener(gestureTouchListener);
// 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.thank_you, 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
@@ -120,13 +146,13 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
Object group = null;
if (groupsTabLayout.getTabCount() != 0) {
TabLayout.Tab tab = groupsTabLayout.getTabAt(0);
if (selectedTab < groupsTabLayout.getTabCount()) {
tab = groupsTabLayout.getTabAt(selectedTab);
TabLayout.Tab tab = groupsTabLayout.getTabAt(selectedTab);
if (tab == null) {
tab = groupsTabLayout.getTabAt(0);
}
groupsTabLayout.selectTab(tab);
assert tab != null;
group = tab.getTag();
}
updateLoyaltyCardList(filter, group);
@@ -290,6 +316,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)
{
@@ -361,8 +395,13 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
filter = newText;
TabLayout groupsTabLayout = findViewById(R.id.groups);
TabLayout.Tab currentTab = groupsTabLayout.getTabAt(groupsTabLayout.getSelectedTabPosition());
updateLoyaltyCardList(
newText,
currentTab != null ? currentTab.getTag() : null
);
updateLoyaltyCardList(newText, groupsTabLayout.getTabCount() > 0 ? groupsTabLayout.getTabAt(groupsTabLayout.getSelectedTabPosition()).getTag() : null);
return true;
}
});
@@ -397,6 +436,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

@@ -6,6 +6,16 @@ import android.content.Intent;
import android.graphics.Color;
import android.util.Log;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Currency;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
import androidx.core.graphics.ColorUtils;
public class Utils {
@@ -61,4 +71,74 @@ public class Utils {
return new BarcodeValues(format, contents);
}
static public Boolean hasExpired(Date expiryDate) {
// today
Calendar date = new GregorianCalendar();
// reset hour, minutes, seconds and millis
date.set(Calendar.HOUR_OF_DAY, 0);
date.set(Calendar.MINUTE, 0);
date.set(Calendar.SECOND, 0);
date.set(Calendar.MILLISECOND, 0);
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

@@ -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.
@@ -49,6 +53,9 @@ public class CsvDatabaseExporter implements DatabaseExporter
printer.printRecord(DBHelper.LoyaltyCardDbIds.ID,
DBHelper.LoyaltyCardDbIds.STORE,
DBHelper.LoyaltyCardDbIds.NOTE,
DBHelper.LoyaltyCardDbIds.EXPIRY,
DBHelper.LoyaltyCardDbIds.BALANCE,
DBHelper.LoyaltyCardDbIds.BALANCE_TYPE,
DBHelper.LoyaltyCardDbIds.CARD_ID,
DBHelper.LoyaltyCardDbIds.HEADER_COLOR,
DBHelper.LoyaltyCardDbIds.BARCODE_TYPE,
@@ -63,6 +70,9 @@ public class CsvDatabaseExporter implements DatabaseExporter
printer.printRecord(card.id,
card.store,
card.note,
card.expiry != null ? card.expiry.getTime() : "",
card.balance,
card.balanceType,
card.cardId,
card.headerColor,
card.barcodeType,

View File

@@ -1,4 +1,4 @@
package protect.card_locker;
package protect.card_locker.importexport;
import android.database.sqlite.SQLiteDatabase;
@@ -10,8 +10,15 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.math.BigDecimal;
import java.util.Currency;
import java.util.Date;
import java.util.List;
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)
* formatted data.
@@ -47,6 +54,8 @@ public class CsvDatabaseImporter implements DatabaseImporter
default:
throw new FormatException(String.format("No code to parse version %s", version));
}
bufferedReader.close();
}
public void parseV1(DBHelper db, BufferedReader input) throws IOException, FormatException, InterruptedException
@@ -131,7 +140,6 @@ public class CsvDatabaseImporter implements DatabaseImporter
}
}
public void parseV2Groups(DBHelper db, SQLiteDatabase database, String data) throws IOException, FormatException, InterruptedException
{
// Parse groups
@@ -145,10 +153,10 @@ public class CsvDatabaseImporter implements DatabaseImporter
throw new InterruptedException();
}
}
groupParser.close();
} catch (IllegalArgumentException | IllegalStateException e) {
throw new FormatException("Issue parsing CSV data", e);
} finally {
groupParser.close();
}
}
@@ -165,10 +173,10 @@ public class CsvDatabaseImporter implements DatabaseImporter
throw new InterruptedException();
}
}
cardParser.close();
} catch (IllegalArgumentException | IllegalStateException e) {
throw new FormatException("Issue parsing CSV data", e);
} finally {
cardParser.close();
}
}
@@ -185,10 +193,10 @@ public class CsvDatabaseImporter implements DatabaseImporter
throw new InterruptedException();
}
}
cardGroupParser.close();
} catch (IllegalArgumentException | IllegalStateException e) {
throw new FormatException("Issue parsing CSV data", e);
} finally {
cardGroupParser.close();
}
}
@@ -248,6 +256,36 @@ public class CsvDatabaseImporter implements DatabaseImporter
}
}
/**
* 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.
@@ -264,6 +302,25 @@ public class CsvDatabaseImporter implements DatabaseImporter
}
String note = extractString(DBHelper.LoyaltyCardDbIds.NOTE, record, "");
Date expiry = null;
try {
expiry = new Date(extractLong(DBHelper.LoyaltyCardDbIds.EXPIRY, record, true));
} catch (NullPointerException | FormatException e) { }
BigDecimal balance;
try {
balance = new BigDecimal(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 = extractString(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE, record, "");
if(!unparsedBalanceType.isEmpty()) {
balanceType = Currency.getInstance(unparsedBalanceType);
}
String cardId = extractString(DBHelper.LoyaltyCardDbIds.CARD_ID, record, "");
if(cardId.isEmpty())
@@ -288,7 +345,7 @@ public class CsvDatabaseImporter implements DatabaseImporter
// We catch this exception so we can still import old backups
}
if (starStatus != 1) starStatus = 0;
helper.insertLoyaltyCard(database, id, store, note, cardId, barcodeType, headerColor, starStatus);
helper.insertLoyaltyCard(database, id, store, note, expiry, balance, balanceType, cardId, barcodeType, headerColor, starStatus);
}
/**

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

@@ -1,7 +1,13 @@
package protect.card_locker;
package protect.card_locker.importexport;
import org.json.JSONException;
import java.io.IOException;
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
@@ -15,5 +21,5 @@ public interface DatabaseImporter
* @throws IOException
* @throws FormatException
*/
void importData(DBHelper db, InputStreamReader input) throws IOException, FormatException, InterruptedException;
void importData(DBHelper db, InputStreamReader input) throws IOException, FormatException, InterruptedException, JSONException, ParseException;
}

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,18 @@
package protect.card_locker;
package protect.card_locker.importexport;
import android.util.Log;
import org.json.JSONException;
import java.io.IOException;
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
{
@@ -26,37 +35,31 @@ public class MultiFormatImporter
switch(format)
{
case CSV:
case Catima:
importer = new CsvDatabaseImporter();
break;
case VoucherVault:
importer = new VoucherVaultImporter();
break;
}
if(importer != null)
if (importer != null)
{
try
{
importer.importData(db, input);
return true;
}
catch(IOException e)
catch(IOException | FormatException | InterruptedException | JSONException | ParseException e)
{
Log.e(TAG, "Failed to input data", e);
}
catch(FormatException e)
{
Log.e(TAG, "Failed to input data", e);
}
catch(InterruptedException e)
{
Log.e(TAG, "Failed to input data", e);
Log.e(TAG, "Failed to import data", e);
}
return false;
}
else
{
Log.e(TAG, "Unsupported data format imported: " + format.name());
return false;
}
return false;
}
}

View File

@@ -0,0 +1,132 @@
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.InputStreamReader;
import java.math.BigDecimal;
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, InputStreamReader input) throws IOException, FormatException, JSONException, ParseException {
BufferedReader bufferedReader = new BufferedReader(input);
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");
String barcodeType = null;
String codeTypeFromJSON = jsonCard.getString("codeType");
switch (codeTypeFromJSON) {
case "CODE128":
barcodeType = BarcodeFormat.CODE_128.name();
break;
case "CODE39":
barcodeType = BarcodeFormat.CODE_39.name();
break;
case "EAN13":
barcodeType = BarcodeFormat.EAN_13.name();
break;
case "QR":
barcodeType = BarcodeFormat.QR_CODE.name();
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, barcodeType, headerColor, 0);
}
database.setTransactionSuccessful();
database.endTransaction();
database.close();
bufferedReader.close();
}
}

View File

@@ -2,7 +2,7 @@ package protect.card_locker.preferences;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import androidx.preference.PreferenceManager;
import androidx.annotation.IntegerRes;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AppCompatDelegate;
@@ -90,4 +90,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

@@ -1,13 +1,17 @@
package protect.card_locker.preferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentActivity;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import android.view.MenuItem;
import nl.invissvenska.numberpickerpreference.NumberDialogPreference;
import nl.invissvenska.numberpickerpreference.NumberPickerPreferenceDialogFragment;
import protect.card_locker.R;
public class SettingsActivity extends AppCompatActivity
@@ -20,11 +24,11 @@ public class SettingsActivity extends AppCompatActivity
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
{
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
actionBar.setDisplayHomeAsUpEnabled(true);
}
// Display the fragment as the main content.
getFragmentManager().beginTransaction()
getSupportFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment())
.commit();
}
@@ -43,17 +47,18 @@ public class SettingsActivity extends AppCompatActivity
return super.onOptionsItemSelected(item);
}
public static class SettingsFragment extends PreferenceFragment
public static class SettingsFragment extends PreferenceFragmentCompat
{
private static final String DIALOG_FRAGMENT_TAG = "SettingsFragment";
@Override
public void onCreate(final Bundle savedInstanceState)
public void onCreatePreferences(Bundle savedInstanceState, String rootKey)
{
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.preferences);
findPreference(getResources().getString(R.string.settings_key_theme)).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
Preference themePreference = findPreference(getResources().getString(R.string.settings_key_theme));
assert themePreference != null;
themePreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
{
@Override
public boolean onPreferenceChange(Preference preference, Object o)
@@ -71,11 +76,36 @@ public class SettingsActivity extends AppCompatActivity
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
}
getActivity().recreate();
FragmentActivity activity = getActivity();
if (activity != null) {
activity.recreate();
}
return true;
}
});
}
@Override
public void onDisplayPreferenceDialog(Preference preference)
{
if (preference instanceof NumberDialogPreference)
{
NumberDialogPreference dialogPreference = (NumberDialogPreference) preference;
DialogFragment dialogFragment = NumberPickerPreferenceDialogFragment
.newInstance(
dialogPreference.getKey(),
dialogPreference.getMinValue(),
dialogPreference.getMaxValue(),
dialogPreference.getUnitText()
);
dialogFragment.setTargetFragment(this, 0);
dialogFragment.show(getParentFragmentManager(), DIALOG_FRAGMENT_TAG);
}
else
{
super.onDisplayPreferenceDialog(preference);
}
}
}
}

View File

@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M20,3h-1L19,1h-2v2L7,3L7,1L5,1v2L4,3c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,5c0,-1.1 -0.9,-2 -2,-2zM20,21L4,21L4,8h16v13z"/>
</vector>

View File

@@ -26,6 +26,20 @@
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/card"/>
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/barcode"/>
</com.google.android.material.tabs.TabLayout>
</com.google.android.material.appbar.AppBarLayout>
<ScrollView android:layout_width="fill_parent"
@@ -37,169 +51,257 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- Store -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/inputPadding"
android:paddingTop="@dimen/inputPadding"
android:orientation="horizontal">
<androidx.cardview.widget.CardView
android:layout_width="@dimen/cardThumbnailSize"
android:layout_height="@dimen/cardThumbnailSize"
android:layout_marginEnd="@dimen/activity_margin"
android:layout_gravity="center_vertical"
app:cardCornerRadius="4dp"
<TableLayout
android:id="@+id/cardPart"
android:visibility="gone">
<!-- Store -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/inputPadding"
app:cardElevation="0dp">
android:paddingTop="@dimen/inputPadding"
android:orientation="horizontal">
<ImageView
android:id="@+id/thumbnail"
<androidx.cardview.widget.CardView
android:layout_width="@dimen/cardThumbnailSize"
android:layout_height="@dimen/cardThumbnailSize"
android:contentDescription="@string/thumbnailDescription"
android:src="@mipmap/ic_launcher"/>
android:layout_marginEnd="@dimen/activity_margin"
android:layout_gravity="center_vertical"
app:cardCornerRadius="4dp"
android:paddingHorizontal="@dimen/inputPadding"
app:cardElevation="0dp">
</androidx.cardview.widget.CardView>
<ImageView
android:id="@+id/thumbnail"
android:layout_width="@dimen/cardThumbnailSize"
android:layout_height="@dimen/cardThumbnailSize"
android:contentDescription="@string/thumbnailDescription"
android:src="@mipmap/ic_launcher"/>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/storeNameField"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/storeName">
</androidx.cardview.widget.CardView>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/storeNameEdit"
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/storeNameField"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
android:hint="@string/storeName">
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/storeNameEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<!-- Note -->
<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>
</LinearLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/noteField"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
<!-- Note -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/note">
android:paddingHorizontal="@dimen/inputPadding"
android:paddingTop="@dimen/inputPadding"
android:orientation="horizontal">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/noteEdit"
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/noteField"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
android:hint="@string/note">
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/noteEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<!-- Group -->
<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>
</LinearLayout>
<com.google.android.material.chip.ChipGroup
android:id="@+id/groupChips"
android:layout_height="match_parent"
<!-- Group -->
<LinearLayout
android:layout_width="match_parent"
android:padding="@dimen/inputPadding"
android:textSize="@dimen/inputSize" />
</LinearLayout>
<!-- Buttons -->
<LinearLayout android:orientation="horizontal"
android:padding="10.0dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/barcodeCaptureLayout">
<Button android:id="@+id/enterButton"
android:layout_margin="@dimen/inputMargin"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/editCard"
android:textColor="#FFFFFF"
android:layout_weight="1.0"/>
</LinearLayout>
android:paddingHorizontal="@dimen/inputPadding"
android:paddingTop="@dimen/inputPadding"
android:orientation="horizontal">
<!-- Card ID and Barcode type -->
<LinearLayout
android:id="@+id/cardAndBarcodeLayout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/inputPadding"
android:paddingTop="@dimen/inputPadding"
android:orientation="horizontal"
android:baselineAligned="false">
<com.google.android.material.chip.ChipGroup
android:id="@+id/groupChips"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:padding="@dimen/inputPadding"
android:textSize="@dimen/inputSize" />
</LinearLayout>
<!-- Balance -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/inputPadding"
android:paddingTop="@dimen/inputPadding"
android:orientation="horizontal">
<!-- 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"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:hint="@string/expiryDate"
android:labelFor="@+id/expiryField">
<AutoCompleteTextView
android:id="@+id/expiryField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</TableLayout>
<TableLayout
android:id="@+id/barcodePart"
android:visibility="gone">
<!-- Card ID -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/cardIdField"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="0dp"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:hint="@string/cardId">
android:paddingHorizontal="@dimen/inputPadding"
android:paddingTop="@dimen/inputPadding"
android:orientation="horizontal">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/cardIdView"
android:layout_width="match_parent"
<!-- Card ID -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/cardIdField"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="0dp"
android:layout_height="wrap_content"
/>
android:layout_weight="1.0"
android:hint="@string/cardId"
android:labelFor="@+id/cardIdView">
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/cardIdView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<!-- Barcode type -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/barcodeTypeView"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp"
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<!-- Barcode ID -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:hint="@string/barcodeType"
android:labelFor="@+id/barcodeTypeField">
android:paddingHorizontal="@dimen/inputPadding"
android:paddingTop="@dimen/inputPadding"
android:orientation="horizontal">
<AutoCompleteTextView
android:id="@+id/barcodeTypeField"
android:layout_width="match_parent"
<!-- Barcode type -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/barcodeTypeView"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inputType="none"/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
android:layout_weight="1.0"
android:hint="@string/barcodeType"
android:labelFor="@+id/barcodeTypeField">
<!-- Barcode -->
<View
android:layout_height="@dimen/inputBorderThickness"
android:layout_width="match_parent" />
<AutoCompleteTextView
android:id="@+id/barcodeTypeField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<LinearLayout android:orientation="horizontal"
android:padding="10.0dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:id="@+id/barcodeLayout">
<ImageView
android:layout_width="0dp"
android:layout_height="@dimen/barcode_disp_height"
android:layout_gravity="center_horizontal"
android:padding="10.0dp"
android:background="#ffffff"
android:id="@+id/barcode"
android:contentDescription="@string/barcodeImageDescription"
android:layout_weight="1.0"/>
</LinearLayout>
<!-- Barcode -->
<View
android:layout_height="@dimen/inputBorderThickness"
android:layout_width="match_parent" />
<LinearLayout android:orientation="horizontal"
android:padding="10.0dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:id="@+id/barcodeLayout">
<ImageView
android:layout_width="0dp"
android:layout_height="@dimen/barcode_disp_height"
android:layout_gravity="center_horizontal"
android:padding="10.0dp"
android:background="#ffffff"
android:id="@+id/barcode"
android:contentDescription="@string/barcodeImageDescription"
android:layout_weight="1.0"/>
</LinearLayout>
<!-- Buttons -->
<LinearLayout android:orientation="horizontal"
android:padding="10.0dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/barcodeCaptureLayout">
<Button android:id="@+id/enterButton"
android:layout_margin="@dimen/inputMargin"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/editBarcode"
android:textColor="#FFFFFF"
android:layout_weight="1.0"/>
</LinearLayout>
</TableLayout>
</TableLayout>
</ScrollView>

View File

@@ -52,6 +52,22 @@
android:maxLines="1"
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"
android:layout_height="wrap_content"
android:maxLines="1"
android:ellipsize="end"
android:textSize="@dimen/noteTextSize"/>
</LinearLayout>
<ImageView
android:id="@+id/star"

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,29 +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"
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"
@@ -79,46 +132,69 @@
<LinearLayout
android:id="@+id/bottom_sheet"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
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="80dp"
android:background="@color/inputBackground"
android:gravity="center"
android:padding="20dp"
app:autoSizeTextType="uniform"
app:autoSizeMaxTextSize="@dimen/singleCardNoteTextSizeMax"
app:autoSizeMinTextSize="@dimen/singleCardNoteTextSizeMin"
app:autoSizeMaxTextSize="@dimen/singleCardNoteTextSizeMax" />
app:autoSizeTextType="uniform" />
<TextView
android:id="@+id/groupsView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="80dp"
android:background="@color/inputBackground"
android:gravity="center"
android:padding="20dp"
app:autoSizeTextType="uniform"
app:autoSizeMaxTextSize="@dimen/singleCardNoteTextSizeMax"
app:autoSizeMinTextSize="@dimen/singleCardNoteTextSizeMin"
app:autoSizeMaxTextSize="@dimen/singleCardNoteTextSizeMax" />
app:autoSizeTextType="uniform" />
<TextView
android:id="@+id/balanceView"
android:layout_width="match_parent"
android:layout_height="80dp"
android:background="@color/inputBackground"
android:gravity="center"
android:padding="20dp"
app:autoSizeMaxTextSize="@dimen/singleCardNoteTextSizeMax"
app:autoSizeMinTextSize="@dimen/singleCardNoteTextSizeMin"
app:autoSizeTextType="uniform" />
<TextView
android:id="@+id/expiryView"
android:layout_width="match_parent"
android:layout_height="80dp"
android:background="@color/inputBackground"
android:gravity="center"
android:padding="20dp"
app:autoSizeMaxTextSize="@dimen/singleCardNoteTextSizeMax"
app:autoSizeMinTextSize="@dimen/singleCardNoteTextSizeMin"
app:autoSizeTextType="uniform" />
</LinearLayout>
<com.google.android.material.appbar.AppBarLayout
@@ -128,6 +204,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
@@ -165,5 +242,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

@@ -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

@@ -102,4 +102,25 @@
<item quantity="one"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> Karte</item>
<item quantity="other"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> Karten</item>
</plurals>
</resources>
<string name="groupsList">Gruppen: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
<string name="chooseImportType">Aus welcher App Daten importieren\?</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">Barcode auf dem Bildschirm zentrieren</string>
<string name="moveBarcodeToTopOfScreen">Barcode 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">Barcode bearbeiten</string>
<string name="barcode">Barcode</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>
</resources>

View File

@@ -1,8 +1,6 @@
<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>
@@ -17,17 +15,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 +36,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 +45,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 +52,16 @@
<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>
</resources>

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_add">Añadir</string>
<string name="noGiftCards">Actualmente no tiene ninguna tarjeta guardada. Pulse el botón «+» para comenzar.
<string name="noGiftCards">Pulse el botón «+» para añadir una nueva tarjeta.
\n
\nCatima le permite llevar sus tarjetas en el teléfono para que estén siempre a su alcance.</string>
\nCatima guarda sus tarjetas en el dispositivo, para que estén siempre a su alcance.</string>
<string name="storeName">Tienda</string>
<string name="note">Nota</string>
<string name="cardId">Id. de tarjeta</string>
@@ -16,7 +16,7 @@
<string name="lockScreen">Bloquear rotación</string>
<string name="unlockScreen">Desbloquear rotación</string>
<string name="deleteTitle">Eliminar tarjeta</string>
<string name="deleteConfirmation">Confirme que quiere eliminar esta tarjeta.</string>
<string name="deleteConfirmation">Por favor, confirme que quiere eliminar esta tarjeta.</string>
<string name="ok">Aceptar</string>
<string name="copy_to_clipboard">Copiar id. en portapapeles</string>
<string name="sendLabel">Enviar…</string>
@@ -24,47 +24,47 @@
<string name="addCardTitle">Añadir tarjeta</string>
<string name="scanCardBarcode">Escanear código de barras de la tarjeta</string>
<string name="cardShortcut">Atajo de tarjeta</string>
<string name="noCardsMessage">No hay ninguna tarjeta; añada una primero</string>
<string name="noCardsMessage">Añada una tarjeta primero</string>
<string name="barcodeImageDescription">Imagen del código de barras de la tarjeta</string>
<string name="noStoreError">Establecimiento no especificado</string>
<string name="noCardIdError">Id. de tarjeta no especificado</string>
<string name="noCardExistsError">No se ha podido encontrar la tarjeta de fidelización</string>
<string name="noCardExistsError">No se ha podido encontrar la tarjeta</string>
<string name="importExport">Importar/exportar</string>
<string name="exportName">Exportar</string>
<string name="importExportHelp">La copia de respaldo le permite transferir sus tarjetas a otro dispositivo.</string>
<string name="importSuccessfulTitle">Datos importados correctamente</string>
<string name="importSuccessfulTitle">Importado</string>
<string name="importFailedTitle">Falló la importación</string>
<string name="importFailed">No se pudo importar</string>
<string name="exportSuccessfulTitle">Datos exportados correctamente</string>
<string name="importFailed">No se han podido importar tarjetas</string>
<string name="exportSuccessfulTitle">Exportado</string>
<string name="exportFailedTitle">Falló la exportación</string>
<string name="exportFailed">No se pudo exportar</string>
<string name="exportFailed">No se han podido exportar las tarjetas</string>
<string name="importing">Importando…</string>
<string name="exporting">Exportando…</string>
<string name="noExternalStoragePermissionError">No se pueden importar o exportar tarjetas sin el permiso de almacenamiento</string>
<string name="noExternalStoragePermissionError">Otorgar permiso de almacenamiento para importar o exportar tarjetas</string>
<string name="importOptionFilesystemTitle">Importar desde el sistema de archivos</string>
<string name="importOptionFilesystemExplanation">Elegir un archivo concreto del sistema de archivos.</string>
<string name="importOptionFilesystemButton">Desde el sistema de archivos</string>
<string name="importOptionApplicationTitle">Utilizar aplicación externa</string>
<string name="importOptionApplicationExplanation">Use una aplicación externa como Dropbox, Google Drive o tu gestor de archivos favoritos para abrir un archivo.</string>
<string name="importOptionApplicationExplanation">Use una aplicación o su gestor de archivos favoritos para abrir un archivo.</string>
<string name="importOptionApplicationButton">Utilizar aplicación externa</string>
<string name="about">Acerca de</string>
<string name="app_license">Disponible en virtud de la GPLv3.</string>
<string name="app_license">Software libre con copyleft, disponible bajo la licencia GPLv3+.</string>
<string name="about_title_fmt">Acerca de <xliff:g id="app_name">%s</xliff:g></string>
<string name="debug_version_fmt">Versión: <xliff:g id="version">%s</xliff:g></string>
<string name="settings">Configuración</string>
<string name="settings_category_title_ui">Interfaz de usuario</string>
<string name="settings_card_title_list_font_size">Tamaño de la letra para el título (lista)</string>
<string name="settings_card_note_list_font_size">Tamaño de la letra para notas (lista)</string>
<string name="settings_card_title_font_size">Tamaño de la letra para el título</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 de código de barras</string>
<string name="exportSuccessful">Se exportaron correctamente los datos de tarjetas de fidelización</string>
<string name="importSuccessful">Se importaron correctamente los datos de tarjetas de fidelización</string>
<string name="settings_display_barcode_max_brightness">Iluminar vista del código de barras</string>
<string name="exportSuccessful">Datos de las tarjetas exportados</string>
<string name="importSuccessful">Datos de las tarjetas importados</string>
<string name="intent_import_card_from_url_share_text">Quiero compartirle una tarjeta</string>
<string name="settings_lock_barcode_orientation">Bloquear orientación de código de barras</string>
<string name="settings_dark_theme">Tema oscuro</string>
<string name="settings_light_theme">Tema claro</string>
<string name="settings_system_theme">Tema del sistema</string>
<string name="settings_lock_barcode_orientation">Bloquear orientación del código de barras</string>
<string name="settings_dark_theme">Oscuro</string>
<string name="settings_light_theme">Claro</string>
<string name="settings_system_theme">Sistema</string>
<string name="settings_theme">Tema</string>
<string name="enterBarcodeInstructions">Introduzca el identificador de tarjeta y seleccione la imagen que represente el código de barras que se utilizará, o bien, elija «Esta tarjeta no tiene código de barras» para no utilizar ninguno.</string>
<string name="app_copyright_old">Basado en Loyalty Card Keychain, derechos de autor 20162020 de Branden Archer.</string>
@@ -73,6 +73,44 @@
<string name="share">Compartir</string>
<string name="barcodeNoBarcode">Esta tarjeta no tiene código de barras</string>
<string name="barcodeType">Tipo de código de barras</string>
<string name="noMatchingGiftCards">Ninguna tarjeta coincide con el filtro de búsqueda. Pruebe con términos distintos.</string>
<string name="noMatchingGiftCards">No se ha encontrado nada. Pruebe a modificar su búsqueda.</string>
<string name="action_search">Buscar</string>
</resources>
<string name="app_libraries"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" id="app_name">%s</xliff:g> usa las siguientes librerías de terceros: <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">Información de la revisión: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" id="app_revision_url">%s</xliff:g></string>
<string name="noGroups">Pulse el botón «+» para añadir grupos.
\n
\nLos grupos permiten encontrar cosas fácilmente.</string>
<string name="starImage">Favorito</string>
<string name="thumbnailDescription">Miniatura de la tarjeta</string>
<string name="copy_to_clipboard_toast">Id. de la tarjeta copiado al portapapeles</string>
<string name="selectBarcodeTitle">Seleccionar el código de barras</string>
<string name="app_resources"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" id="app_name">%s</xliff:g> usa los siguientes recursos de terceros: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" id="app_resources_list">%s</xliff:g></string>
<string name="unstar">Eliminar de favoritos</string>
<string name="noBarcode">Sin código de barras</string>
<string name="enter_group_name">Introducir nombre del grupo</string>
<string name="groups">Grupos</string>
<string name="groupsList">Grupos: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
<string name="addManually">Introducir el id. de la tarjeta manualmente</string>
<string name="leaveWithoutSaveConfirmation">¿Está seguro de querer abandonar esta página\? Los cambios no se guardarán.</string>
<string name="leaveWithoutSaveTitle">Salir sin guardar</string>
<string name="moveDown">Bajar en la lista</string>
<string name="moveUp">Subir en la lista</string>
<string name="failedOpeningFileManager">Fallo al abrir el gestor de archivos. Por favor, asegúrese de que haya uno instalado.</string>
<string name="deleteConfirmationGroup">Por favor, confirme que desea eliminar este grupo</string>
<string name="all">Todo</string>
<string name="star">Añadir a favoritos</string>
<plurals name="groupCardCount">
<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>
</resources>

View File

@@ -102,4 +102,5 @@
<item quantity="one"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> carte</item>
<item quantity="other"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> cartes</item>
</plurals>
</resources>
<string name="groupsList">Groupes : <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
</resources>

View File

@@ -2,8 +2,10 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_search">Cerca</string>
<string name="action_add">Aggiungi</string>
<string name="noGiftCards">Non hai ancora alcuna carta fedeltà. Premi sul bottone +(più) in alto per incominciare.\n\nL\'app ti permette di portare con te le tue carte fedeltà, così da averle sempre a disposizione.</string>
<string name="noMatchingGiftCards">Nessuna carta fedeltà corrisponde al filtro. Riprovare con altri valori.</string>
<string name="noGiftCards">Fai clic sul pulsante + (più) per aggiungere prima una carta.
\n
\nCatima trasporta le tue carte sul dispositivo, quindi sono sempre a portata di mano.</string>
<string name="noMatchingGiftCards">Non ho trovato niente. Prova a cambiare la tua ricerca.</string>
<string name="storeName">Negozio</string>
<string name="note">Note</string>
<string name="cardId">Codice</string>
@@ -17,44 +19,44 @@
<string name="lockScreen">Blocca rotazione</string>
<string name="unlockScreen">Sblocca rotazione</string>
<string name="deleteTitle">Rimuovi carta fedeltà</string>
<string name="deleteConfirmation">Conferma che vuoi eliminare questa carta.</string>
<string name="deleteConfirmation">Conferma di voler eliminare questa carta.</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>
<string name="editCardTitle">Modifica carta</string>
<string name="addCardTitle">Aggiungi carta</string>
<string name="scanCardBarcode">Scansiona codice carta</string>
<string name="scanCardBarcode">Scansiona il codice carta</string>
<string name="cardShortcut">Scorciatoia per la carta</string>
<string name="noCardsMessage">Non ci sono carte. Aggiungine prima una</string>
<string name="barcodeImageDescription">Immagine del codice a barre della carta</string>
<string name="noCardsMessage">Aggiungi prima una carta</string>
<string name="barcodeImageDescription">Immagine del codice della carta</string>
<string name="noStoreError">Nessun negozio inserito</string>
<string name="noCardIdError">Nessun codice carta inserito</string>
<string name="noCardExistsError">Impossibile trovare la carta</string>
<string name="failedParsingImportUriError">Impossibile analizzare l\'URI</string>
<string name="importExport">Importa/Esporta</string>
<string name="exportName">Esporta</string>
<string name="importExportHelp">Fare il backup dei dati ti permette di spostare le tue carte da un dispositivo ad un altro.</string>
<string name="importSuccessfulTitle">Importazione avvenuta con successo</string>
<string name="importExportHelp">Il backup delle carte ti consente di spostarle su un altro dispositivo.</string>
<string name="importSuccessfulTitle">Importato</string>
<string name="importFailedTitle">Importazione fallita</string>
<string name="importFailed">Impossibile importare</string>
<string name="exportSuccessfulTitle">Esportazione avvenuta con successo</string>
<string name="importFailed">Impossibile importare le carte</string>
<string name="exportSuccessfulTitle">Esportato</string>
<string name="exportFailedTitle">Esportazione fallita</string>
<string name="exportFailed">Impossibile esportare</string>
<string name="exportFailed">Impossibile esportare le carte</string>
<string name="importing">Importazione in corso…</string>
<string name="exporting">Esportazione in corso…</string>
<string name="noExternalStoragePermissionError">Impossibile importare o esportare i dati senza il permesso per l\'uso della memoria esterna</string>
<string name="noExternalStoragePermissionError">Concedi l\'autorizzazione all\'archiviazione esterna per importare o esportare prima le carte</string>
<string name="importOptionFilesystemTitle">Importa dal file system</string>
<string name="importOptionFilesystemExplanation">Scegli un file dal file system.</string>
<string name="importOptionFilesystemButton">Dal file system</string>
<string name="importOptionApplicationTitle">Usa un\'applicazione esterna</string>
<string name="importOptionApplicationExplanation">Usa un\'applicazione esterna come Dropbox, Google Drive o il tuo file manager preferito per aprire il file.</string>
<string name="importOptionApplicationExplanation">Usa qualsiasi app o il tuo gestore di file preferito per aprire un file.</string>
<string name="importOptionApplicationButton">Usa un\'applicazione esterna</string>
<string name="about">Informazioni</string>
<string name="app_license">Pubblicato sotto licenza GPLv3.</string>
<string name="app_license">Software libero con copyleft, licenza GPLv3+.</string>
<string name="about_title_fmt">Informazioni su <xliff:g id="app_name">%s</xliff:g></string>
<string name="debug_version_fmt">Versione: <xliff:g id="version">%s</xliff:g></string>
<string name="app_revision_fmt">Informazione sulla versione: <xliff:g id="app_revision_url">%s</xliff:g></string>
<string name="app_revision_fmt">Informazioni sulla revisione: <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> usa le seguenti librerie di terze parti: <xliff:g id="app_libraries_list">%s</xliff:g></string>
<string name="app_resources"><xliff:g id="app_name">%s</xliff:g> usa le seguenti risorse di terze parti: <xliff:g id="app_resources_list">%s</xliff:g></string>
<string name="selectBarcodeTitle">Seleziona codice a barre</string>
@@ -64,18 +66,18 @@
<string name="settings">Impostazioni</string>
<string name="settings_category_title_ui">Interfaccia utente</string>
<string name="settings_theme">Tema</string>
<string name="settings_system_theme">Tema del sistema</string>
<string name="settings_light_theme">Tema chiaro</string>
<string name="settings_dark_theme">Tema scuro</string>
<string name="settings_card_title_list_font_size">Dimensione testo del titolo carta (in lista carte)</string>
<string name="settings_card_note_list_font_size">Dimensione testo delle note carta (in lista carte)</string>
<string name="settings_card_title_font_size">Dimensione testo del titolo carta</string>
<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>
<string name="exportSuccessful">Dati della carta fedeltà esportati correttamente</string>
<string name="importSuccessful">Dati della carta fedeltà importati correttamente</string>
<string name="exportSuccessful">Dati della carta importati</string>
<string name="importSuccessful">Dati della carta importati</string>
<string name="app_copyright_old">Basato su Loyalty Card Keychain, copyright 20162020 Branden Archer.</string>
<string name="exportOptionExplanation">I dati verranno scritti in una posizione a tua scelta.</string>
<string name="barcodeType">Tipo di codice a barre</string>
@@ -84,9 +86,41 @@
<string name="starImage">Stella preferita</string>
<string name="deleteConfirmationGroup">Conferma di voler eliminare questo gruppo</string>
<string name="all">Tutti</string>
<string name="noGroups">Al momento non hai gruppi. Fai clic sul pulsante + (più) per iniziare.
<string name="noGroups">Fai clic sul pulsante + (più) per aggiungere prima i gruppi.
\n
\nLe carte possono essere assegnate a gruppi per renderle più facili da trovare.</string>
\nI gruppi rendono le cose più facili da trovare.</string>
<string name="groups">Gruppi</string>
<string name="enter_group_name">Inserisci il nome del gruppo</string>
</resources>
<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="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="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>
<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">Da quale app vuoi importare i dati\?</string>
<string name="settings_disable_lockscreen_while_viewing_card">Mantieni schermo attivo mentre visualizzi una carta</string>
</resources>

View File

@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="addManually">직접 카드 ID 입력</string>
<string name="groupsList">그룹: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
<string name="all">모두</string>
<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>
<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="settings_system_theme">시스템</string>
<string name="settings_theme">테마</string>
<string name="settings_category_title_ui">사용자 인터페이스</string>
<string name="settings">설정</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>
<string name="selectBarcodeTitle">바코드 선택</string>
<string name="about">정보</string>
<string name="exporting">내보내는 중…</string>
<string name="importing">가져오는 중…</string>
<string name="importExportHelp">카드를 백업하면 다른 장치로 옮길 수 있습니다.</string>
<string name="exportName">내보내기</string>
<string name="importExport">가져오기/내보내기</string>
<string name="cardShortcut">카드 바로 가기</string>
<string name="scanCardBarcode">카드 바코드 스캔</string>
<string name="addCardTitle">카드 추가</string>
<string name="editCardTitle">카드 편집</string>
<string name="sendLabel">보내기…</string>
<string name="share">공유</string>
<string name="copy_to_clipboard">ID를 클립보드에 복사</string>
<string name="ok">확인</string>
<string name="deleteConfirmation">정말 이 카드를 삭제하시겠습니까\?</string>
<string name="deleteTitle">카드 제거</string>
<string name="unlockScreen">회전 잠금 해제</string>
<string name="lockScreen">회전 잠금</string>
<string name="confirm">확인</string>
<string name="delete">삭제</string>
<string name="edit">편집</string>
<string name="editCard">카드 편집</string>
<string name="save">저장</string>
<string name="cancel">취소</string>
<string name="unstar">즐겨찾기에서 제거</string>
<string name="star">즐겨찾기에 추가</string>
<string name="noBarcode">바코드 없음</string>
<string name="barcodeNoBarcode">이 카드는 바코드가 없음</string>
<string name="barcodeType">바코드 형식</string>
<string name="cardId">카드 ID</string>
<string name="note">노트</string>
<string name="noMatchingGiftCards">아무것도 찾지 못했습니다. 검색어를 바꿔 보세요.</string>
<string name="noGiftCards">먼저 \'+\' 버튼을 눌러 카드를 추가하세요.
\n
\nCatima는 카드를 장치에 보관하므로 어디서든 사용할 수 있습니다.</string>
<string name="action_add">추가</string>
<string name="action_search">검색</string>
<string name="copy_to_clipboard_toast">카드 ID를 클립보드에 복사함</string>
<string name="importOptionApplicationTitle">외부 앱 사용</string>
<string name="importOptionFilesystemExplanation">파일 시스템에서 파일을 선택합니다.</string>
<string name="importOptionFilesystemTitle">파일 시스템에서 가져오기</string>
<string name="exportFailed">카드를 내보낼 수 없음</string>
<string name="importOptionApplicationExplanation">다른 파일 관리자 또는 앱을 사용하여 파일을 엽니다.</string>
<string name="exportFailedTitle">내보내기 실패</string>
<string name="exportSuccessfulTitle">내보내기 완료</string>
<string name="importFailed">카드를 가져올 수 없음</string>
<string name="importFailedTitle">가져오기 실패</string>
<string name="importSuccessfulTitle">가져오기 완료</string>
<string name="noCardIdError">카드 ID를 입력하지 않음</string>
<string name="barcodeImageDescription">카드 바코드 이미지</string>
<string name="storeName">매장</string>
<string name="thumbnailDescription">카드 섬네일</string>
<string name="importOptionApplicationButton">외부 앱 사용</string>
<string name="failedParsingImportUriError">가져온 URI를 분석할 수 없음</string>
<string name="noCardExistsError">카드를 찾을 수 없음</string>
<string name="moveUp">목록에서 위로 옮기기</string>
<string name="leaveWithoutSaveTitle">저장하지 않고 종료</string>
<string name="moveDown">목록에서 아래로 옮기기</string>
<string name="noStoreError">매장을 입력하지 않음</string>
<string name="starImage">즐겨찾기 별</string>
<string name="settings_display_barcode_max_brightness">바코드를 표시할 때 화면 밝기 높이기</string>
</resources>

View File

@@ -62,8 +62,8 @@
<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</string>
<string name="settings_card_note_list_font_size">Skriftstørrelse for kortmerknadsliste</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>
@@ -103,4 +103,24 @@
<item quantity="other"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> kort</item>
</plurals>
<string name="groupsList">Grupper: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
<string name="editBarcode">Rediger strekkode</string>
<string name="barcode">Strekkode</string>
<string name="card">Kort</string>
<string name="expiryStateSentence">Utløper: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
<string name="chooseExpiryDate">Velg utløpsdato</string>
<string name="never">Aldri</string>
<string name="expiryDate">Utløpsdato</string>
<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">Hvilket program vil du importere 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>
</resources>

View File

@@ -103,4 +103,29 @@
<item quantity="other"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> kaarten</item>
</plurals>
<string name="groupsList">Groepen: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
<string name="editBarcode">Barcode aanpassen</string>
<string name="barcode">Barcode</string>
<string name="card">Kaart</string>
<string name="chooseExpiryDate">Kies een vervaldatum</string>
<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>
<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">Uit welke app wil je gegevens importeren\?</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">Veel appwinkels vereisen dat apps na de eerste keer opstarten hun privacybeleid tonen. Dat van ons is eenvoudig:
\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="thank_you">Bedankt</string>
<string name="privacy_policy">Privacybeleid</string>
</resources>

View File

@@ -1,31 +1,31 @@
<?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="editCard">Изменить штрих-код</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="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 +34,16 @@
<string name="noStoreError">Название магазина не указано</string>
<string name="noCardIdError">Номер карты не указан</string>
<string name="noCardExistsError">Карта не найдена</string>
<string name="failedParsingImportUriError">Не удалось разобрать импортируемый URI</string>
<string name="importExport">Импорт/Экспорт</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 +54,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,22 +71,22 @@
<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_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="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>
@@ -94,5 +94,40 @@
<string name="deleteConfirmationGroup">Подтвердите, что хотите удалить эту группу</string>
<string name="leaveWithoutSaveConfirmation">Вы уверены, что хотите покинуть этот экран\? Изменения не будут сохранены.</string>
<string name="leaveWithoutSaveTitle">Выйти без сохранения</string>
<string name="failedOpeningFileManager">Не удалось открыть файловый менеджер. Пожалуйста, убедитись, что он установлен.</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="thank_you">Спасибо</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>
</resources>

View File

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

View File

@@ -16,4 +16,6 @@
<color name="inputDividerBorder">#AAAAAA</color>
<color name="iconColor">#000000</color>
<color name="alert">#FF0000</color>
</resources>

View File

@@ -2,7 +2,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"
xmlns:tools="http://schemas.android.com/tools">
<string name="app_name" translatable="false">Catima</string>
<string name="action_search">Search</string>
<string name="action_add">Add</string>
@@ -105,8 +104,13 @@
<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>
@@ -133,4 +137,29 @@
<string name="leaveWithoutSaveConfirmation">Are you sure you want to leave this screen? Changed made will not be saved.</string>
<string name="addManually">Manually enter card ID</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>
<string name="editBarcode">Edit barcode</string>
<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="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 which app?</string>
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
<string name="privacy_policy">Privacy Policy</string>
<string name="thank_you">Thank you</string>
<string name="privacy_policy_popup_text">Many app stores require apps to show their privacy policy on first start. Here is ours:\n\nWe collect NO DATA AT ALL and our app is Open Source so anyone can confirm this to be true.</string>
</resources>

View File

@@ -4,52 +4,72 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory
android:title="@string/settings_category_title_ui">
android:title="@string/settings_category_title_ui"
app:iconSpaceReserved="false">
<ListPreference
android:key="@string/settings_key_theme"
android:title="@string/settings_theme"
android:defaultValue="@string/settings_key_system_theme"
android:entries="@array/theme_value_strings"
android:entryValues="@array/theme_values"/>
android:entryValues="@array/theme_values"
app:iconSpaceReserved="false" />
<com.vanniktech.vntnumberpickerpreference.VNTNumberPickerPreference
<nl.invissvenska.numberpickerpreference.NumberDialogPreference
android:key="@string/settings_key_card_title_list_font_size"
android:title="@string/settings_card_title_list_font_size"
android:defaultValue="@integer/settings_card_title_list_font_size_sp"
app:vnt_maxValue="@integer/settings_card_title_list_max_font_size_sp"
app:vnt_minValue="@integer/settings_card_title_list_min_font_size_sp" />
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" />
<com.vanniktech.vntnumberpickerpreference.VNTNumberPickerPreference
<nl.invissvenska.numberpickerpreference.NumberDialogPreference
android:key="@string/settings_key_card_note_list_font_size"
android:title="@string/settings_card_note_list_font_size"
android:defaultValue="@integer/settings_card_note_list_font_size_sp"
app:vnt_maxValue="@integer/settings_card_note_list_max_font_size_sp"
app:vnt_minValue="@integer/settings_card_note_list_min_font_size_sp" />
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" />
<com.vanniktech.vntnumberpickerpreference.VNTNumberPickerPreference
<nl.invissvenska.numberpickerpreference.NumberDialogPreference
android:key="@string/settings_key_card_title_font_size"
android:title="@string/settings_card_title_font_size"
android:defaultValue="@integer/settings_card_title_font_size_sp"
app:vnt_maxValue="@integer/settings_card_title_max_font_size_sp"
app:vnt_minValue="@integer/settings_card_title_min_font_size_sp"/>
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" />
<com.vanniktech.vntnumberpickerpreference.VNTNumberPickerPreference
<nl.invissvenska.numberpickerpreference.NumberDialogPreference
android:key="@string/settings_key_card_id_font_size"
android:title="@string/settings_card_id_font_size"
android:defaultValue="@integer/settings_card_id_font_size_sp"
app:vnt_maxValue="@integer/settings_card_id_max_font_size_sp"
app:vnt_minValue="@integer/settings_card_id_min_font_size_sp" />
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" />
<CheckBoxPreference
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="@string/settings_key_display_barcode_max_brightness"
android:title="@string/settings_display_barcode_max_brightness"/>
android:title="@string/settings_display_barcode_max_brightness"
app:iconSpaceReserved="false" />
<CheckBoxPreference
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="@string/settings_key_lock_barcode_orientation"
android:title="@string/settings_lock_barcode_orientation"/>
android:title="@string/settings_lock_barcode_orientation"
app:iconSpaceReserved="false" />
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="@string/settings_key_keep_screen_on"
android:title="@string/settings_keep_screen_on"
app:iconSpaceReserved="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" />
</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;
@@ -31,7 +33,6 @@ public class DatabaseTest
private DBHelper db;
private static final Integer DEFAULT_HEADER_COLOR = Color.BLACK;
private static final Integer DEFAULT_HEADER_TEXT_COLOR = Color.WHITE;
@Before
public void setUp()
@@ -44,7 +45,7 @@ public class DatabaseTest
public void addRemoveOneGiftCard()
{
assertEquals(0, db.getLoyaltyCardCount());
long id = db.insertLoyaltyCard("store", "note", "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0);
long id = db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0);
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -53,6 +54,9 @@ public class DatabaseTest
assertNotNull(loyaltyCard);
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(0, loyaltyCard.starStatus);
assertEquals(BarcodeFormat.UPC_A.toString(), loyaltyCard.barcodeType);
@@ -66,12 +70,12 @@ public class DatabaseTest
@Test
public void updateGiftCard()
{
long id = db.insertLoyaltyCard("store", "note", "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0);
long id = db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0);
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
result = db.updateLoyaltyCard(1, "store1", "note1", "cardId1", BarcodeFormat.AZTEC.toString(), DEFAULT_HEADER_COLOR);
result = db.updateLoyaltyCard(1, "store1", "note1", null, new BigDecimal("10.00"), Currency.getInstance("EUR"), "cardId1", BarcodeFormat.AZTEC.toString(), DEFAULT_HEADER_COLOR);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -79,6 +83,9 @@ public class DatabaseTest
assertNotNull(loyaltyCard);
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(0, loyaltyCard.starStatus);
assertEquals(BarcodeFormat.AZTEC.toString(), loyaltyCard.barcodeType);
@@ -87,7 +94,7 @@ public class DatabaseTest
@Test
public void updateGiftCardOnlyStar()
{
long id = db.insertLoyaltyCard("store", "note", "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0);
long id = db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0);
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -100,6 +107,9 @@ public class DatabaseTest
assertNotNull(loyaltyCard);
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(1, loyaltyCard.starStatus);
assertEquals(BarcodeFormat.UPC_A.toString(), loyaltyCard.barcodeType);
@@ -110,7 +120,7 @@ public class DatabaseTest
{
assertEquals(0, db.getLoyaltyCardCount());
boolean result = db.updateLoyaltyCard(1, "store1", "note1", "cardId1",
boolean result = db.updateLoyaltyCard(1, "store1", "note1", null, new BigDecimal("0"), null, "cardId1",
BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR);
assertEquals(false, result);
assertEquals(0, db.getLoyaltyCardCount());
@@ -119,7 +129,7 @@ public class DatabaseTest
@Test
public void emptyGiftCardValues()
{
long id = db.insertLoyaltyCard("", "", "", "", null, 0);
long id = db.insertLoyaltyCard("", "", null, new BigDecimal("0"), null, "", "", null, 0);
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -128,6 +138,9 @@ public class DatabaseTest
assertNotNull(loyaltyCard);
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);
}
@@ -141,7 +154,7 @@ 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, "cardId" + index,
long id = db.insertLoyaltyCard("store" + index, "note" + index, null, new BigDecimal("0"), null, "cardId" + index,
BarcodeFormat.UPC_A.toString(), index, 0);
boolean result = (id != -1);
assertTrue(result);
@@ -160,7 +173,9 @@ public class DatabaseTest
{
assertEquals("store"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STORE)));
assertEquals("note"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE)));
assertEquals("cardId"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID)));
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(BarcodeFormat.UPC_A.toString(), cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE)));
assertEquals(0, cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS)));
@@ -170,6 +185,8 @@ public class DatabaseTest
}
assertTrue(cursor.isAfterLast());
cursor.close();
}
@Test
@@ -182,12 +199,12 @@ 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, "cardId" + index,
id = db.insertLoyaltyCard("store" + index, "note" + index, null, new BigDecimal("0"), null, "cardId" + index,
BarcodeFormat.UPC_A.toString(), index, 1);
}
else {
id = db.insertLoyaltyCard("store" + index, "note" + index, "cardId" + index,
id = db.insertLoyaltyCard("store" + index, "note" + index, null, new BigDecimal("0"), null, "cardId" + index,
BarcodeFormat.UPC_A.toString(), index, 0);
}
boolean result = (id != -1);
@@ -205,7 +222,9 @@ public class DatabaseTest
int index = CARDS_TO_ADD-1 ;
assertEquals("store"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STORE)));
assertEquals("note"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE)));
assertEquals("cardId"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID)));
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(BarcodeFormat.UPC_A.toString(), cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE)));
assertEquals(1, cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS)));
@@ -217,7 +236,9 @@ public class DatabaseTest
{
assertEquals("store"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STORE)));
assertEquals("note"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE)));
assertEquals("cardId"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID)));
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(BarcodeFormat.UPC_A.toString(), cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE)));
assertEquals(0, cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS)));
@@ -227,6 +248,8 @@ public class DatabaseTest
}
assertTrue(cursor.isAfterLast());
cursor.close();
}
private void setupDatabaseVersion1(SQLiteDatabase database)
@@ -279,23 +302,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", BarcodeFormat.UPC_A.toString(), 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
@@ -370,7 +421,7 @@ public class DatabaseTest
{
// Create card
assertEquals(0, db.getLoyaltyCardCount());
long id = db.insertLoyaltyCard("store", "note", "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0);
long id = db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0);
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -429,6 +480,9 @@ 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(new BigDecimal("0"), card.balance);
assertEquals(null, card.balanceType);
assertEquals("cardId", card.cardId);
assertEquals(BarcodeFormat.UPC_A.toString(), card.barcodeType);
assertEquals("", card.note);

View File

@@ -3,16 +3,19 @@ package protect.card_locker;
import android.app.Activity;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Color;
import android.os.Environment;
import com.google.zxing.BarcodeFormat;
import org.json.JSONException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.LooperMode;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -23,11 +26,18 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Currency;
import java.util.Date;
import java.util.List;
import protect.card_locker.importexport.MultiFormatExporter;
import protect.card_locker.importexport.MultiFormatImporter;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -70,7 +80,7 @@ public class ImportExportTest
{
String storeName = String.format("store, \"%4d", index);
String note = String.format("note, \"%4d", index);
long id = db.insertLoyaltyCard(storeName, note, BARCODE_DATA, BARCODE_TYPE, index, 0);
long id = db.insertLoyaltyCard(storeName, note, null, new BigDecimal(String.valueOf(index)), null, BARCODE_DATA, BARCODE_TYPE, index, 0);
boolean result = (id != -1);
assertTrue(result);
}
@@ -86,7 +96,7 @@ public class ImportExportTest
{
String storeName = String.format("store, \"%4d", index);
String note = String.format("note, \"%4d", index);
long id = db.insertLoyaltyCard(storeName, note, BARCODE_DATA, BARCODE_TYPE, index, 1);
long id = db.insertLoyaltyCard(storeName, note, null, new BigDecimal(String.valueOf(index)), null, BARCODE_DATA, BARCODE_TYPE, index, 1);
boolean result = (id != -1);
assertTrue(result);
}
@@ -95,13 +105,82 @@ public class ImportExportTest
String storeName = String.format("store, \"%4d", index);
String note = String.format("note, \"%4d", index);
//if index is even
long id = db.insertLoyaltyCard(storeName, note, BARCODE_DATA, BARCODE_TYPE, index, 0);
long id = db.insertLoyaltyCard(storeName, note, null, new BigDecimal(String.valueOf(index)), null, BARCODE_DATA, BARCODE_TYPE, index, 0);
boolean result = (id != -1);
assertTrue(result);
}
assertEquals(cardsToAdd, db.getLoyaltyCardCount());
}
@Test
public void addLoyaltyCardsWithExpiryNeverPastTodayFuture()
{
long id = db.insertLoyaltyCard("No Expiry", "", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, 0, 0);
boolean result = (id != -1);
assertTrue(result);
LoyaltyCard card = db.getLoyaltyCard((int) id);
assertEquals("No Expiry", card.store);
assertEquals("", card.note);
assertEquals(null, card.expiry);
assertEquals(new BigDecimal("0"), card.balance);
assertEquals(null, card.balanceType);
assertEquals(BARCODE_DATA, card.cardId);
assertEquals(BARCODE_TYPE, card.barcodeType);
assertEquals(Integer.valueOf(0), card.headerColor);
assertEquals(0, card.starStatus);
id = db.insertLoyaltyCard("Past", "", new Date((long) 1), new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, 0, 0);
result = (id != -1);
assertTrue(result);
card = db.getLoyaltyCard((int) id);
assertEquals("Past", card.store);
assertEquals("", card.note);
assertTrue(card.expiry.before(new Date()));
assertEquals(new BigDecimal("0"), card.balance);
assertEquals(null, card.balanceType);
assertEquals(BARCODE_DATA, card.cardId);
assertEquals(BARCODE_TYPE, card.barcodeType);
assertEquals(Integer.valueOf(0), card.headerColor);
assertEquals(0, card.starStatus);
id = db.insertLoyaltyCard("Today", "", new Date(), new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, 0, 0);
result = (id != -1);
assertTrue(result);
card = db.getLoyaltyCard((int) id);
assertEquals("Today", card.store);
assertEquals("", card.note);
assertTrue(card.expiry.before(new Date(new Date().getTime()+86400)));
assertTrue(card.expiry.after(new Date(new Date().getTime()-86400)));
assertEquals(new BigDecimal("0"), card.balance);
assertEquals(null, card.balanceType);
assertEquals(BARCODE_DATA, card.cardId);
assertEquals(BARCODE_TYPE, card.barcodeType);
assertEquals(Integer.valueOf(0), card.headerColor);
assertEquals(0, card.starStatus);
// This will break after 19 January 2038
// If someone is still maintaining this code base by then: I love you
id = db.insertLoyaltyCard("Future", "", new Date(2147483648000L), new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, 0, 0);
result = (id != -1);
assertTrue(result);
card = db.getLoyaltyCard((int) id);
assertEquals("Future", card.store);
assertEquals("", card.note);
assertTrue(card.expiry.after(new Date(new Date().getTime()+86400)));
assertEquals(new BigDecimal("0"), card.balance);
assertEquals(null, card.balanceType);
assertEquals(BARCODE_DATA, card.cardId);
assertEquals(BARCODE_TYPE, card.barcodeType);
assertEquals(Integer.valueOf(0), card.headerColor);
assertEquals(0, card.starStatus);
assertEquals(4, db.getLoyaltyCardCount());
}
private void addGroups(int groupsToAdd)
{
// Add in reverse order to test sorting
@@ -135,6 +214,8 @@ public class ImportExportTest
assertEquals(expectedStore, card.store);
assertEquals(expectedNote, card.note);
assertEquals(new BigDecimal(String.valueOf(index)), card.balance);
assertEquals(null, card.balanceType);
assertEquals(BARCODE_DATA, card.cardId);
assertEquals(BARCODE_TYPE, card.barcodeType);
assertEquals(Integer.valueOf(index), card.headerColor);
@@ -165,6 +246,8 @@ public class ImportExportTest
assertEquals(expectedStore, card.store);
assertEquals(expectedNote, card.note);
assertEquals(new BigDecimal(String.valueOf(index)), card.balance);
assertEquals(null, card.balanceType);
assertEquals(BARCODE_DATA, card.cardId);
assertEquals(BARCODE_TYPE, card.barcodeType);
assertEquals(Integer.valueOf(index), card.headerColor);
@@ -183,6 +266,8 @@ public class ImportExportTest
assertEquals(expectedStore, card.store);
assertEquals(expectedNote, card.note);
assertEquals(new BigDecimal(String.valueOf(index)), card.balance);
assertEquals(null, card.balanceType);
assertEquals(BARCODE_DATA, card.cardId);
assertEquals(BARCODE_TYPE, card.barcodeType);
assertEquals(Integer.valueOf(index), card.headerColor);
@@ -224,6 +309,8 @@ public class ImportExportTest
{
SQLiteDatabase database = db.getWritableDatabase();
database.execSQL("delete from " + DBHelper.LoyaltyCardDbIds.TABLE);
database.execSQL("delete from " + DBHelper.LoyaltyCardDbGroups.TABLE);
database.execSQL("delete from " + DBHelper.LoyaltyCardDbIdsGroups.TABLE);
database.close();
assertEquals(0, db.getLoyaltyCardCount());
@@ -234,34 +321,31 @@ public class ImportExportTest
{
final int NUM_CARDS = 10;
for(DataFormat format : DataFormat.values())
{
addLoyaltyCards(NUM_CARDS);
addLoyaltyCards(NUM_CARDS);
ByteArrayOutputStream outData = new ByteArrayOutputStream();
OutputStreamWriter outStream = new OutputStreamWriter(outData);
ByteArrayOutputStream outData = new ByteArrayOutputStream();
OutputStreamWriter outStream = new OutputStreamWriter(outData);
// Export data to CSV format
boolean result = MultiFormatExporter.exportData(db, outStream, format);
assertTrue(result);
outStream.close();
// Export data to CSV format
boolean result = MultiFormatExporter.exportData(db, outStream, DataFormat.Catima);
assertTrue(result);
outStream.close();
clearDatabase();
clearDatabase();
ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray());
InputStreamReader inStream = new InputStreamReader(inData);
ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray());
InputStreamReader inStream = new InputStreamReader(inData);
// Import the CSV data
result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
assertTrue(result);
// Import the CSV data
result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima);
assertTrue(result);
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
checkLoyaltyCards();
checkLoyaltyCards();
// Clear the database for the next format under test
clearDatabase();
}
// Clear the database for the next format under test
clearDatabase();
}
@Test
@@ -269,34 +353,31 @@ public class ImportExportTest
{
final int NUM_CARDS = 9;
for(DataFormat format : DataFormat.values())
{
addLoyaltyCardsFiveStarred();
addLoyaltyCardsFiveStarred();
ByteArrayOutputStream outData = new ByteArrayOutputStream();
OutputStreamWriter outStream = new OutputStreamWriter(outData);
ByteArrayOutputStream outData = new ByteArrayOutputStream();
OutputStreamWriter outStream = new OutputStreamWriter(outData);
// Export data to CSV format
boolean result = MultiFormatExporter.exportData(db, outStream, format);
assertTrue(result);
outStream.close();
// Export data to CSV format
boolean result = MultiFormatExporter.exportData(db, outStream, DataFormat.Catima);
assertTrue(result);
outStream.close();
clearDatabase();
clearDatabase();
ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray());
InputStreamReader inStream = new InputStreamReader(inData);
ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray());
InputStreamReader inStream = new InputStreamReader(inData);
// Import the CSV data
result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
assertTrue(result);
// Import the CSV data
result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima);
assertTrue(result);
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
checkLoyaltyCardsFiveStarred();
checkLoyaltyCardsFiveStarred();
// Clear the database for the next format under test
clearDatabase();
}
// Clear the database for the next format under test
clearDatabase();
}
private List<String> groupsToGroupNames(List<Group> groups)
@@ -316,77 +397,74 @@ public class ImportExportTest
final int NUM_CARDS = 10;
final int NUM_GROUPS = 3;
for(DataFormat format : DataFormat.values())
{
addLoyaltyCards(NUM_CARDS);
addGroups(NUM_GROUPS);
addLoyaltyCards(NUM_CARDS);
addGroups(NUM_GROUPS);
List<Group> emptyGroup = new ArrayList<>();
List<Group> emptyGroup = new ArrayList<>();
List<Group> groupsForOne = new ArrayList<>();
groupsForOne.add(db.getGroup("group, \" 1"));
List<Group> groupsForOne = new ArrayList<>();
groupsForOne.add(db.getGroup("group, \" 1"));
List<Group> groupsForTwo = new ArrayList<>();
groupsForTwo.add(db.getGroup("group, \" 1"));
groupsForTwo.add(db.getGroup("group, \" 2"));
List<Group> groupsForTwo = new ArrayList<>();
groupsForTwo.add(db.getGroup("group, \" 1"));
groupsForTwo.add(db.getGroup("group, \" 2"));
List<Group> groupsForThree = new ArrayList<>();
groupsForThree.add(db.getGroup("group, \" 1"));
groupsForThree.add(db.getGroup("group, \" 2"));
groupsForThree.add(db.getGroup("group, \" 3"));
List<Group> groupsForThree = new ArrayList<>();
groupsForThree.add(db.getGroup("group, \" 1"));
groupsForThree.add(db.getGroup("group, \" 2"));
groupsForThree.add(db.getGroup("group, \" 3"));
List<Group> groupsForFour = new ArrayList<>();
groupsForFour.add(db.getGroup("group, \" 1"));
groupsForFour.add(db.getGroup("group, \" 2"));
groupsForFour.add(db.getGroup("group, \" 3"));
List<Group> groupsForFour = new ArrayList<>();
groupsForFour.add(db.getGroup("group, \" 1"));
groupsForFour.add(db.getGroup("group, \" 2"));
groupsForFour.add(db.getGroup("group, \" 3"));
List<Group> groupsForFive = new ArrayList<>();
groupsForFive.add(db.getGroup("group, \" 1"));
groupsForFive.add(db.getGroup("group, \" 3"));
List<Group> groupsForFive = new ArrayList<>();
groupsForFive.add(db.getGroup("group, \" 1"));
groupsForFive.add(db.getGroup("group, \" 3"));
db.setLoyaltyCardGroups(1, groupsForOne);
db.setLoyaltyCardGroups(2, groupsForTwo);
db.setLoyaltyCardGroups(3, groupsForThree);
db.setLoyaltyCardGroups(4, groupsForFour);
db.setLoyaltyCardGroups(5, groupsForFive);
db.setLoyaltyCardGroups(1, groupsForOne);
db.setLoyaltyCardGroups(2, groupsForTwo);
db.setLoyaltyCardGroups(3, groupsForThree);
db.setLoyaltyCardGroups(4, groupsForFour);
db.setLoyaltyCardGroups(5, groupsForFive);
ByteArrayOutputStream outData = new ByteArrayOutputStream();
OutputStreamWriter outStream = new OutputStreamWriter(outData);
ByteArrayOutputStream outData = new ByteArrayOutputStream();
OutputStreamWriter outStream = new OutputStreamWriter(outData);
// Export data to CSV format
boolean result = MultiFormatExporter.exportData(db, outStream, format);
assertTrue(result);
outStream.close();
// Export data to CSV format
boolean result = MultiFormatExporter.exportData(db, outStream, DataFormat.Catima);
assertTrue(result);
outStream.close();
clearDatabase();
clearDatabase();
ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray());
InputStreamReader inStream = new InputStreamReader(inData);
ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray());
InputStreamReader inStream = new InputStreamReader(inData);
// Import the CSV data
result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
assertTrue(result);
// Import the CSV data
result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima);
assertTrue(result);
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
assertEquals(NUM_GROUPS, db.getGroupCount());
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
assertEquals(NUM_GROUPS, db.getGroupCount());
checkLoyaltyCards();
checkGroups();
checkLoyaltyCards();
checkGroups();
assertEquals(groupsToGroupNames(groupsForOne), groupsToGroupNames(db.getLoyaltyCardGroups(1)));
assertEquals(groupsToGroupNames(groupsForTwo), groupsToGroupNames(db.getLoyaltyCardGroups(2)));
assertEquals(groupsToGroupNames(groupsForThree), groupsToGroupNames(db.getLoyaltyCardGroups(3)));
assertEquals(groupsToGroupNames(groupsForFour), groupsToGroupNames(db.getLoyaltyCardGroups(4)));
assertEquals(groupsToGroupNames(groupsForFive), groupsToGroupNames(db.getLoyaltyCardGroups(5)));
assertEquals(emptyGroup, db.getLoyaltyCardGroups(6));
assertEquals(emptyGroup, db.getLoyaltyCardGroups(7));
assertEquals(emptyGroup, db.getLoyaltyCardGroups(8));
assertEquals(emptyGroup, db.getLoyaltyCardGroups(9));
assertEquals(emptyGroup, db.getLoyaltyCardGroups(10));
assertEquals(groupsToGroupNames(groupsForOne), groupsToGroupNames(db.getLoyaltyCardGroups(1)));
assertEquals(groupsToGroupNames(groupsForTwo), groupsToGroupNames(db.getLoyaltyCardGroups(2)));
assertEquals(groupsToGroupNames(groupsForThree), groupsToGroupNames(db.getLoyaltyCardGroups(3)));
assertEquals(groupsToGroupNames(groupsForFour), groupsToGroupNames(db.getLoyaltyCardGroups(4)));
assertEquals(groupsToGroupNames(groupsForFive), groupsToGroupNames(db.getLoyaltyCardGroups(5)));
assertEquals(emptyGroup, db.getLoyaltyCardGroups(6));
assertEquals(emptyGroup, db.getLoyaltyCardGroups(7));
assertEquals(emptyGroup, db.getLoyaltyCardGroups(8));
assertEquals(emptyGroup, db.getLoyaltyCardGroups(9));
assertEquals(emptyGroup, db.getLoyaltyCardGroups(10));
// Clear the database for the next format under test
clearDatabase();
}
// Clear the database for the next format under test
clearDatabase();
}
@Test
@@ -394,32 +472,29 @@ public class ImportExportTest
{
final int NUM_CARDS = 10;
for(DataFormat format : DataFormat.values())
{
addLoyaltyCards(NUM_CARDS);
addLoyaltyCards(NUM_CARDS);
ByteArrayOutputStream outData = new ByteArrayOutputStream();
OutputStreamWriter outStream = new OutputStreamWriter(outData);
ByteArrayOutputStream outData = new ByteArrayOutputStream();
OutputStreamWriter outStream = new OutputStreamWriter(outData);
// Export into CSV data
boolean result = MultiFormatExporter.exportData(db, outStream, format);
assertTrue(result);
outStream.close();
// Export into CSV data
boolean result = MultiFormatExporter.exportData(db, outStream, DataFormat.Catima);
assertTrue(result);
outStream.close();
ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray());
InputStreamReader inStream = new InputStreamReader(inData);
ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray());
InputStreamReader inStream = new InputStreamReader(inData);
// Import the CSV data on top of the existing database
result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
assertTrue(result);
// Import the CSV data on top of the existing database
result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima);
assertTrue(result);
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
checkLoyaltyCards();
checkLoyaltyCards();
// Clear the database for the next format under test
clearDatabase();
}
// Clear the database for the next format under test
clearDatabase();
}
@Test
@@ -435,7 +510,7 @@ public class ImportExportTest
OutputStreamWriter outStream = new OutputStreamWriter(outData);
// Export data to CSV format
boolean result = MultiFormatExporter.exportData(db, outStream, format);
boolean result = MultiFormatExporter.exportData(db, outStream, DataFormat.Catima);
assertTrue(result);
clearDatabase();
@@ -449,11 +524,13 @@ public class ImportExportTest
ByteArrayInputStream inData = new ByteArrayInputStream((outData.toString() + corruptEntry).getBytes());
InputStreamReader inStream = new InputStreamReader(inData);
// Attempt to import the CSV data
result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
// Attempt to import the data
result = MultiFormatImporter.importData(db, inStream, format);
assertEquals(false, result);
assertEquals(0, db.getLoyaltyCardCount());
clearDatabase();
}
}
@@ -468,6 +545,7 @@ public class ImportExportTest
}
@Test
@LooperMode(LooperMode.Mode.LEGACY)
public void useImportExportTask() throws FileNotFoundException
{
final int NUM_CARDS = 10;
@@ -475,49 +553,46 @@ public class ImportExportTest
final File sdcardDir = Environment.getExternalStorageDirectory();
final File exportFile = new File(sdcardDir, "Catima.csv");
for(DataFormat format : DataFormat.values())
{
addLoyaltyCards(NUM_CARDS);
addLoyaltyCards(NUM_CARDS);
TestTaskCompleteListener listener = new TestTaskCompleteListener();
TestTaskCompleteListener listener = new TestTaskCompleteListener();
// Export to the file
FileOutputStream fileOutputStream = new FileOutputStream(exportFile);
ImportExportTask task = new ImportExportTask(activity, format, fileOutputStream, listener);
task.execute();
// Export to the file
FileOutputStream fileOutputStream = new FileOutputStream(exportFile);
ImportExportTask task = new ImportExportTask(activity, DataFormat.Catima, fileOutputStream, listener);
task.execute();
// Actually run the task to completion
Robolectric.flushBackgroundThreadScheduler();
// Actually run the task to completion
Robolectric.flushBackgroundThreadScheduler();
// Check that the listener was executed
assertNotNull(listener.success);
assertEquals(true, listener.success);
// Check that the listener was executed
assertNotNull(listener.success);
assertEquals(true, listener.success);
clearDatabase();
clearDatabase();
// Import everything back from the default location
// Import everything back from the default location
listener = new TestTaskCompleteListener();
listener = new TestTaskCompleteListener();
FileInputStream fileStream = new FileInputStream(exportFile);
FileInputStream fileStream = new FileInputStream(exportFile);
task = new ImportExportTask(activity, format, fileStream, listener);
task.execute();
task = new ImportExportTask(activity, DataFormat.Catima, fileStream, listener);
task.execute();
// Actually run the task to completion
Robolectric.flushBackgroundThreadScheduler();
// Actually run the task to completion
Robolectric.flushBackgroundThreadScheduler();
// Check that the listener was executed
assertNotNull(listener.success);
assertEquals(true, listener.success);
// Check that the listener was executed
assertNotNull(listener.success);
assertEquals(true, listener.success);
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
checkLoyaltyCards();
checkLoyaltyCards();
// Clear the database for the next format under test
clearDatabase();
}
// Clear the database for the next format under test
clearDatabase();
}
@Test
@@ -537,7 +612,7 @@ public class ImportExportTest
InputStreamReader inStream = new InputStreamReader(inputStream);
// Import the CSV data
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -545,10 +620,15 @@ public class ImportExportTest
assertEquals("store", card.store);
assertEquals("note", card.note);
assertEquals(null, card.expiry);
assertEquals(new BigDecimal("0"), card.balance);
assertEquals(null, card.balanceType);
assertEquals("12345", card.cardId);
assertEquals("type", card.barcodeType);
assertEquals(0, card.starStatus);
assertNull(card.headerColor);
clearDatabase();
}
@Test
@@ -570,7 +650,7 @@ public class ImportExportTest
InputStreamReader inStream = new InputStreamReader(inputStream);
// Import the CSV data
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -578,10 +658,15 @@ public class ImportExportTest
assertEquals("store", card.store);
assertEquals("note", card.note);
assertEquals(null, card.expiry);
assertEquals(new BigDecimal("0"), card.balance);
assertEquals(null, card.balanceType);
assertEquals("12345", card.cardId);
assertEquals("type", card.barcodeType);
assertEquals(0, card.starStatus);
assertNull(card.headerColor);
clearDatabase();
}
@Test
@@ -603,9 +688,11 @@ public class ImportExportTest
InputStreamReader inStream = new InputStreamReader(inputStream);
// Import the CSV data
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima);
assertEquals(false, result);
assertEquals(0, db.getLoyaltyCardCount());
clearDatabase();
}
@Test
@@ -627,7 +714,7 @@ public class ImportExportTest
InputStreamReader inStream = new InputStreamReader(inputStream);
// Import the CSV data
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima);
assertEquals(true, result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -635,10 +722,15 @@ public class ImportExportTest
assertEquals("store", card.store);
assertEquals("note", card.note);
assertEquals(null, card.expiry);
assertEquals(new BigDecimal("0"), card.balance);
assertEquals(null, card.balanceType);
assertEquals("12345", card.cardId);
assertEquals("", card.barcodeType);
assertEquals(0, card.starStatus);
assertEquals(1, (long) card.headerColor);
clearDatabase();
}
@Test
@@ -660,7 +752,7 @@ public class ImportExportTest
InputStreamReader inStream = new InputStreamReader(inputStream);
// Import the CSV data
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima);
assertEquals(true, result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -668,14 +760,17 @@ public class ImportExportTest
assertEquals("store", card.store);
assertEquals("note", card.note);
assertEquals(null, card.expiry);
assertEquals(new BigDecimal("0"), card.balance);
assertEquals(null, card.balanceType);
assertEquals("12345", card.cardId);
assertEquals("type", card.barcodeType);
assertEquals(1, card.starStatus);
assertEquals(1, (long) card.headerColor);
clearDatabase();
}
@Test
public void importWithNoStarredFieldV1() throws IOException
{
@@ -695,7 +790,7 @@ public class ImportExportTest
InputStreamReader inStream = new InputStreamReader(inputStream);
// Import the CSV data
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima);
assertEquals(true, result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -703,10 +798,15 @@ public class ImportExportTest
assertEquals("store", card.store);
assertEquals("note", card.note);
assertEquals(null, card.expiry);
assertEquals(new BigDecimal("0"), card.balance);
assertEquals(null, card.balanceType);
assertEquals("12345", card.cardId);
assertEquals("type", card.barcodeType);
assertEquals(0, card.starStatus);
assertEquals(1, (long) card.headerColor);
clearDatabase();
}
@Test
@@ -728,7 +828,7 @@ public class ImportExportTest
InputStreamReader inStream = new InputStreamReader(inputStream);
// Import the CSV data
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -748,7 +848,7 @@ public class ImportExportTest
inStream = new InputStreamReader(inputStream);
// Import the CSV data
result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -756,9 +856,74 @@ public class ImportExportTest
assertEquals("store", card.store);
assertEquals("note", card.note);
assertEquals(null, card.expiry);
assertEquals(new BigDecimal("0"), card.balance);
assertEquals(null, card.balanceType);
assertEquals("12345", card.cardId);
assertEquals("type", card.barcodeType);
assertEquals(0, card.starStatus);
assertEquals(1, (long) card.headerColor);
clearDatabase();
}
@Test
public void importVoucherVault() throws IOException, FormatException, JSONException, ParseException {
String jsonText = "[\n" +
" {\n" +
" \"uuid\": \"ae1ae525-3f27-481e-853a-8c30b7fa12d8\",\n" +
" \"description\": \"Clothes Store\",\n" +
" \"code\": \"123456\",\n" +
" \"codeType\": \"CODE128\",\n" +
" \"expires\": null,\n" +
" \"removeOnceExpired\": true,\n" +
" \"balance\": null,\n" +
" \"color\": \"GREY\"\n" +
" },\n" +
" {\n" +
" \"uuid\": \"29a5d3b3-eace-4311-a15c-4c7e6a010531\",\n" +
" \"description\": \"Department Store\",\n" +
" \"code\": \"26846363\",\n" +
" \"codeType\": \"CODE39\",\n" +
" \"expires\": \"2021-03-26T00:00:00.000\",\n" +
" \"removeOnceExpired\": true,\n" +
" \"balance\": 3.5,\n" +
" \"color\": \"PURPLE\"\n" +
" }\n" +
"]";
ByteArrayInputStream inputStream = new ByteArrayInputStream(jsonText.getBytes(StandardCharsets.UTF_8));
InputStreamReader inStream = new InputStreamReader(inputStream);
// Import the Voucher Vault data
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.VoucherVault);
assertTrue(result);
assertEquals(2, db.getLoyaltyCardCount());
LoyaltyCard card = db.getLoyaltyCard(1);
assertEquals("Clothes Store", card.store);
assertEquals("", card.note);
assertEquals(null, card.expiry);
assertEquals(new BigDecimal("0"), card.balance);
assertEquals(Currency.getInstance("USD"), card.balanceType);
assertEquals("123456", card.cardId);
assertEquals(BarcodeFormat.CODE_128.name(), card.barcodeType);
assertEquals(0, card.starStatus);
assertEquals(Color.GRAY, (long) card.headerColor);
card = db.getLoyaltyCard(2);
assertEquals("Department Store", card.store);
assertEquals("", card.note);
assertEquals(new Date(1616716800000L), card.expiry);
assertEquals(new BigDecimal("3.5"), card.balance);
assertEquals(Currency.getInstance("USD"), card.balanceType);
assertEquals("26846363", card.cardId);
assertEquals(BarcodeFormat.CODE_39.name(), card.barcodeType);
assertEquals(0, card.starStatus);
assertEquals(Color.rgb(128, 0, 128), (long) card.headerColor);
clearDatabase();
}
}

View File

@@ -11,6 +11,9 @@ 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;
import static org.junit.Assert.assertNull;
@@ -35,7 +38,9 @@ public class ImportURITest {
public void ensureNoDataLoss() throws InvalidObjectException
{
// Generate card
db.insertLoyaltyCard("store", "note", BarcodeFormat.UPC_A.toString(), LoyaltyCardDbIds.BARCODE_TYPE, Color.BLACK, 1);
Date date = new Date();
db.insertLoyaltyCard("store", "note", date, new BigDecimal("100"), null, BarcodeFormat.UPC_A.toString(), LoyaltyCardDbIds.BARCODE_TYPE, Color.BLACK, 1);
// Get card
LoyaltyCard card = db.getLoyaltyCard(1);
@@ -51,6 +56,9 @@ public class ImportURITest {
assertEquals(card.cardId, parsedCard.cardId);
assertEquals(card.headerColor, parsedCard.headerColor);
assertEquals(card.note, parsedCard.note);
assertEquals(card.expiry, parsedCard.expiry);
assertEquals(card.balance, parsedCard.balance);
assertEquals(card.balanceType, parsedCard.balanceType);
assertEquals(card.store, parsedCard.store);
// No export of starStatus for single cards foreseen therefore 0 will be imported
assertEquals(0, parsedCard.starStatus);
@@ -60,7 +68,7 @@ public class ImportURITest {
public void ensureNoCrashOnMissingHeaderFields() throws InvalidObjectException
{
// Generate card
db.insertLoyaltyCard("store", "note", 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(), LoyaltyCardDbIds.BARCODE_TYPE, null, 0);
// Get card
LoyaltyCard card = db.getLoyaltyCard(1);
@@ -75,6 +83,9 @@ public class ImportURITest {
assertEquals(card.barcodeType, parsedCard.barcodeType);
assertEquals(card.cardId, parsedCard.cardId);
assertEquals(card.note, parsedCard.note);
assertEquals(card.expiry, parsedCard.expiry);
assertEquals(card.balance, parsedCard.balance);
assertEquals(card.balanceType, parsedCard.balanceType);
assertEquals(card.store, parsedCard.store);
assertNull(parsedCard.headerColor);
assertNull(parsedCard.headerTextColor);
@@ -120,5 +131,8 @@ public class ImportURITest {
assertEquals("store", parsedCard.store);
assertEquals(Integer.valueOf(-416706), parsedCard.headerColor);
assertEquals(0, parsedCard.starStatus);
assertEquals(null, parsedCard.expiry);
assertEquals(new BigDecimal("0"), parsedCard.balance);
assertEquals(null, parsedCard.balanceType);
}
}

View File

@@ -1,10 +1,13 @@
package protect.card_locker;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.graphics.Color;
import android.preference.PreferenceManager;
import androidx.preference.PreferenceManager;
import androidx.test.core.app.ApplicationProvider;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
@@ -18,6 +21,11 @@ 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;
@RunWith(RobolectricTestRunner.class)
@@ -54,10 +62,12 @@ public class LoyaltyCardCursorAdapterTest
return view;
}
private void checkView(final View view, final String store, final String note, 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)
{
@@ -66,10 +76,11 @@ public class LoyaltyCardCursorAdapterTest
assertEquals(storeFontSize, (int)storeField.getTextSize());
assertEquals(noteFontSize, (int)noteField.getTextSize());
assertEquals(noteFontSize, (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());
@@ -78,13 +89,33 @@ public class LoyaltyCardCursorAdapterTest
{
assertEquals(View.GONE, noteField.getVisibility());
}
if(!expiry.isEmpty())
{
assertEquals(View.VISIBLE, expiryField.getVisibility());
assertEquals(expiry, expiryField.getText().toString());
}
else
{
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", "", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
db.insertLoyaltyCard("store", "", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
LoyaltyCard card = db.getLoyaltyCard(1);
Cursor cursor = db.getLoyaltyCardCursor();
@@ -92,13 +123,15 @@ public class LoyaltyCardCursorAdapterTest
View view = createView(cursor);
checkView(view, card.store, card.note, false);
checkView(view, card.store, card.note, "", "",false);
cursor.close();
}
@Test
public void TestCursorAdapterWithNote()
{
db.insertLoyaltyCard("store", "note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
LoyaltyCard card = db.getLoyaltyCard(1);
Cursor cursor = db.getLoyaltyCardCursor();
@@ -106,13 +139,19 @@ public class LoyaltyCardCursorAdapterTest
View view = createView(cursor);
checkView(view, card.store, card.note, false);
checkView(view, card.store, card.note, "", "",false);
cursor.close();
}
@Test
public void TestCursorAdapterFontSizes()
{
db.insertLoyaltyCard("store", "note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
final Context context = ApplicationProvider.getApplicationContext();
Date expiryDate = new Date();
String dateString = context.getString(R.string.expiryStateSentence, DateFormat.getDateInstance(DateFormat.LONG).format(expiryDate));
db.insertLoyaltyCard("store", "note", expiryDate, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
LoyaltyCard card = db.getLoyaltyCard(1);
Cursor cursor = db.getLoyaltyCardCursor();
@@ -120,20 +159,22 @@ public class LoyaltyCardCursorAdapterTest
setFontSizes(1, 2);
View view = createView(cursor);
checkView(view, card.store, card.note, true);
checkView(view, card.store, card.note, dateString, "", true);
setFontSizes(30, 31);
view = createView(cursor);
checkView(view, card.store, card.note, true);
checkView(view, card.store, card.note, dateString, "",true);
cursor.close();
}
@Test
public void TestCursorAdapterStarring()
{
db.insertLoyaltyCard("storeA", "note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
db.insertLoyaltyCard("storeB", "note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1);
db.insertLoyaltyCard("storeC", "note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1);
db.insertLoyaltyCard("storeA", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
db.insertLoyaltyCard("storeB", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1);
db.insertLoyaltyCard("storeC", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1);
Cursor cursor = db.getLoyaltyCardCursor();
cursor.moveToFirst();
@@ -150,5 +191,71 @@ public class LoyaltyCardCursorAdapterTest
view = createView(cursor);
star = view.findViewById(R.id.star);
assertEquals(View.GONE, star.getVisibility());
cursor.close();
}
@Test
public void TestCursorAdapter0Points()
{
db.insertLoyaltyCard("store", "", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), 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", BarcodeFormat.UPC_A.toString(), 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", BarcodeFormat.UPC_A.toString(), 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", BarcodeFormat.UPC_A.toString(), 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

@@ -1,5 +1,6 @@
package protect.card_locker;
import static android.os.Looper.getMainLooper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
@@ -7,28 +8,41 @@ import static org.junit.Assert.assertTrue;
import static org.robolectric.Shadows.shadowOf;
import android.app.Activity;
import android.app.DatePickerDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import androidx.preference.PreferenceManager;
import android.view.Menu;
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.util.Currency;
import java.util.Date;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,6 +52,8 @@ import org.robolectric.RuntimeEnvironment;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowActivity;
import org.robolectric.shadows.ShadowAlertDialog;
import org.robolectric.shadows.ShadowDialog;
import org.robolectric.shadows.ShadowLog;
@RunWith(RobolectricTestRunner.class)
@@ -93,6 +109,9 @@ public class LoyaltyCardViewActivityTest
*/
private void saveLoyaltyCardWithArguments(final Activity activity,
final String store, final String note,
final Date expiry,
final BigDecimal balance,
final Currency balanceType,
final String cardId,
final String barcodeType,
boolean creatingNewCard)
@@ -109,11 +128,21 @@ 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 barcodeTypeField = activity.findViewById(R.id.barcodeTypeField);
storeField.setText(store);
noteField.setText(note);
expiryView.setTag(expiry);
if (balance != null) {
balanceView.setText(balance.toPlainString());
}
if (balanceType != null) {
balanceCurrencyField.setText(balanceType.getSymbol());
}
cardIdField.setText(cardId);
barcodeTypeField.setText(barcodeType);
@@ -126,6 +155,13 @@ public class LoyaltyCardViewActivityTest
LoyaltyCard card = db.getLoyaltyCard(1);
assertEquals(store, card.store);
assertEquals(note, card.note);
assertEquals(expiry, card.expiry);
if (balance != null) {
assertEquals(balance, card.balance);
} else {
assertEquals(new BigDecimal("0"), card.balance);
}
assertEquals(balanceType, card.balanceType);
assertEquals(cardId, card.cardId);
// The special "No barcode" string shouldn't actually be written to the loyalty card
@@ -139,6 +175,8 @@ public class LoyaltyCardViewActivityTest
}
assertNotNull(card.headerColor);
assertNotNull(card.headerTextColor);
db.close();
}
/**
@@ -223,13 +261,20 @@ public class LoyaltyCardViewActivityTest
assertEquals(visibility, view.getVisibility());
if(contents != null)
{
TextView textView = (TextView)view;
assertEquals(contents, textView.getText().toString());
try {
TextView textView = (TextView) view;
assertEquals(contents, textView.getText().toString());
} catch (ClassCastException e) {
TextInputLayout textView = (TextInputLayout) view;
assertEquals(contents, textView.getEditText().getText().toString());
}
}
}
private void checkAllFields(final Activity activity, ViewMode mode,
final String store, final String note, 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 barcodeType)
{
if(mode == ViewMode.VIEW_CARD)
{
@@ -241,6 +286,9 @@ 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.barcodeTypeField, View.VISIBLE, barcodeType);
checkFieldProperties(activity, R.id.barcode, View.VISIBLE, null);
@@ -256,8 +304,9 @@ public class LoyaltyCardViewActivityTest
activityController.resume();
Activity activity = (Activity)activityController.get();
final Context context = ApplicationProvider.getApplicationContext();
checkAllFields(activity, ViewMode.ADD_CARD, "", "", "", "");
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never) , "0", context.getString(R.string.points), "", "");
}
@Test
@@ -275,7 +324,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());
@@ -287,6 +335,8 @@ public class LoyaltyCardViewActivityTest
noteField.setText("note");
activity.findViewById(R.id.fabSave).performClick();
assertEquals(0, db.getLoyaltyCardCount());
db.close();
}
@Test
@@ -315,16 +365,19 @@ public class LoyaltyCardViewActivityTest
activityController.resume();
Activity activity = (Activity)activityController.get();
final Context context = ApplicationProvider.getApplicationContext();
checkAllFields(activity, ViewMode.ADD_CARD, "", "", "", "");
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "0", context.getString(R.string.points), "", "");
// Complete barcode capture successfully
captureBarcodeWithResult(activity, true);
checkAllFields(activity, ViewMode.ADD_CARD, "", "", BARCODE_DATA, BARCODE_TYPE);
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "0", context.getString(R.string.points), BARCODE_DATA, BARCODE_TYPE);
shadowOf(getMainLooper()).idle();
// Save and check the loyalty card
saveLoyaltyCardWithArguments(activity, "store", "note", BARCODE_DATA, BARCODE_TYPE, true);
saveLoyaltyCardWithArguments(activity, "store", "note", null, null, null, BARCODE_DATA, BARCODE_TYPE, true);
}
@Test
@@ -336,13 +389,14 @@ public class LoyaltyCardViewActivityTest
activityController.resume();
Activity activity = (Activity)activityController.get();
final Context context = ApplicationProvider.getApplicationContext();
checkAllFields(activity, ViewMode.ADD_CARD, "", "", "", "");
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "0", context.getString(R.string.points), "", "");
// Complete barcode capture in failure
captureBarcodeWithResult(activity, false);
checkAllFields(activity, ViewMode.ADD_CARD, "", "", "", "");
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "0", context.getString(R.string.points), "", "");
}
@Test
@@ -354,13 +408,14 @@ public class LoyaltyCardViewActivityTest
activityController.resume();
LoyaltyCardEditActivity activity = (LoyaltyCardEditActivity) activityController.get();
final Context context = ApplicationProvider.getApplicationContext();
checkAllFields(activity, ViewMode.ADD_CARD, "", "", "", "");
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "0", context.getString(R.string.points), "", "");
// Complete barcode capture successfully
captureBarcodeWithResult(activity, true);
checkAllFields(activity, ViewMode.ADD_CARD, "", "", BARCODE_DATA, BARCODE_TYPE);
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "0", context.getString(R.string.points), BARCODE_DATA, BARCODE_TYPE);
// Cancel the loyalty card creation
assertEquals(false, activity.isFinishing());
@@ -407,15 +462,18 @@ 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", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
activityController.start();
activityController.visible();
activityController.resume();
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", BARCODE_DATA, BARCODE_TYPE);
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, "0", context.getString(R.string.points), BARCODE_DATA, BARCODE_TYPE);
db.close();
}
@Test
@@ -423,15 +481,18 @@ 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", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
activityController.start();
activityController.visible();
activityController.resume();
checkAllFields(activity, ViewMode.VIEW_CARD, "store", "note", BARCODE_DATA, BARCODE_TYPE);
checkAllFields(activity, ViewMode.VIEW_CARD, "store", "note", null, "0", context.getString(R.string.points), BARCODE_DATA, BARCODE_TYPE);
db.close();
}
@Test
@@ -439,20 +500,23 @@ 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", EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0);
activityController.start();
activityController.visible();
activityController.resume();
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, "0", context.getString(R.string.points), EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
// Complete barcode capture successfully
captureBarcodeWithResult(activity, true);
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", BARCODE_DATA, BARCODE_TYPE);
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, "0", context.getString(R.string.points), BARCODE_DATA, BARCODE_TYPE);
db.close();
}
@Test
@@ -460,20 +524,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", EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0);
activityController.start();
activityController.visible();
activityController.resume();
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, "0", context.getString(R.string.points), EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
// Complete barcode capture successfully
captureBarcodeWithResult(activity, true);
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", BARCODE_DATA, BARCODE_TYPE);
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, "0", context.getString(R.string.points), BARCODE_DATA, BARCODE_TYPE);
// Cancel the loyalty card creation
assertEquals(false, activity.isFinishing());
@@ -488,6 +553,158 @@ public class LoyaltyCardViewActivityTest
shadowOf(activity).clickMenuItem(android.R.id.home);
assertEquals(false, activity.hasChanged);
assertEquals(true, activity.isFinishing());
db.close();
}
@Test
public void startWithLoyaltyCardNoExpirySetExpiry() 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, 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, EAN_BARCODE_TYPE);
// Set date to today
MaterialAutoCompleteTextView expiryField = activity.findViewById(R.id.expiryField);
expiryField.setText(expiryField.getAdapter().getItem(1).toString(), false);
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()), "0", context.getString(R.string.points), EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
db.close();
}
@Test
public void startWithLoyaltyCardExpirySetNoExpiry() 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", new Date(), new BigDecimal("0"), null, EAN_BARCODE_DATA, 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()), "0", context.getString(R.string.points), EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
// 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), "0", context.getString(R.string.points), EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
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, 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, EAN_BARCODE_TYPE);
// 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, EAN_BARCODE_TYPE);
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, 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, EAN_BARCODE_TYPE);
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, EAN_BARCODE_TYPE);
db.close();
}
@Test
@@ -497,12 +714,14 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
activityController.start();
activityController.visible();
activityController.resume();
shadowOf(getMainLooper()).idle();
final Menu menu = shadowOf(activity).getOptionsMenu();
assertTrue(menu != null);
@@ -512,6 +731,8 @@ public class LoyaltyCardViewActivityTest
assertEquals("Block Rotation", menu.findItem(R.id.action_lock_unlock).getTitle().toString());
assertEquals("Share", menu.findItem(R.id.action_share).getTitle().toString());
assertEquals("Add to favorites", menu.findItem(R.id.action_star_unstar).getTitle().toString());
db.close();
}
@Test
@@ -541,7 +762,7 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
activityController.start();
activityController.visible();
@@ -550,6 +771,8 @@ public class LoyaltyCardViewActivityTest
assertEquals(false, activity.isFinishing());
shadowOf(activity).clickMenuItem(android.R.id.home);
assertEquals(true, activity.isFinishing());
db.close();
}
@Test
@@ -559,7 +782,7 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, null, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, null, 0);
activityController.start();
activityController.visible();
@@ -568,6 +791,8 @@ public class LoyaltyCardViewActivityTest
assertEquals(false, activity.isFinishing());
shadowOf(activity).clickMenuItem(android.R.id.home);
assertEquals(true, activity.isFinishing());
db.close();
}
@Test
@@ -577,14 +802,16 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, null, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, null, 0);
activityController.start();
activityController.visible();
activityController.resume();
// Save and check the loyalty card
saveLoyaltyCardWithArguments(activity, "store", "note", BARCODE_DATA, BARCODE_TYPE, false);
saveLoyaltyCardWithArguments(activity, "store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, false);
db.close();
}
@Test
@@ -594,14 +821,16 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, "", Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, "", Color.BLACK, 0);
activityController.start();
activityController.visible();
activityController.resume();
// Save and check the loyalty card
saveLoyaltyCardWithArguments(activity, "store", "note", BARCODE_DATA, activity.getApplicationContext().getString(R.string.noBarcode), false);
saveLoyaltyCardWithArguments(activity, "store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, activity.getApplicationContext().getString(R.string.noBarcode), false);
db.close();
}
@Test
@@ -609,26 +838,29 @@ 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", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, 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", BARCODE_DATA, BARCODE_TYPE);
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), "0", context.getString(R.string.points), BARCODE_DATA, BARCODE_TYPE);
// 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", 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, activity.getApplicationContext().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", BARCODE_DATA, activity.getApplicationContext().getString(R.string.noBarcode), false);
saveLoyaltyCardWithArguments(activity, "store", "note", null, null, null, BARCODE_DATA, activity.getApplicationContext().getString(R.string.noBarcode), false);
db.close();
}
@Test
@@ -638,7 +870,7 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
final int STORE_FONT_SIZE = 50;
final int CARD_FONT_SIZE = 40;
@@ -665,6 +897,8 @@ public class LoyaltyCardViewActivityTest
shadowOf(activity).clickMenuItem(android.R.id.home);
assertEquals(true, activity.isFinishing());
db.close();
}
@Test
@@ -676,7 +910,7 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(activity);
settings.edit()
@@ -701,6 +935,8 @@ public class LoyaltyCardViewActivityTest
String title = item.getTitle().toString();
assertEquals(title, activity.getString(R.string.lockScreen));
}
db.close();
}
}
@@ -711,13 +947,15 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity) activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, Color.BLACK,0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, 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);
@@ -727,10 +965,14 @@ public class LoyaltyCardViewActivityTest
assertEquals("Add to favorites", menu.findItem(R.id.action_star_unstar).getTitle().toString());
shadowOf(activity).clickMenuItem(R.id.action_star_unstar);
shadowOf(getMainLooper()).idle();
assertEquals("Remove from favorites", menu.findItem(R.id.action_star_unstar).getTitle().toString());
shadowOf(activity).clickMenuItem(R.id.action_star_unstar);
shadowOf(getMainLooper()).idle();
assertEquals("Add to favorites", menu.findItem(R.id.action_star_unstar).getTitle().toString());
db.close();
}
@Test
@@ -740,7 +982,7 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
activityController.start();
activityController.visible();
@@ -750,56 +992,95 @@ 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();
}
@Test
public void importCard()
{
Uri importUri = Uri.parse("https://thelastproject.github.io/Catima/share?store=Example%20Store&note=&cardid=123456&barcodetype=AZTEC&headercolor=-416706&headertextcolor=-1");
Date date = new Date();
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);
@@ -811,8 +1092,11 @@ public class LoyaltyCardViewActivityTest
activityController.resume();
Activity activity = (Activity)activityController.get();
final Context context = ApplicationProvider.getApplicationContext();
checkAllFields(activity, ViewMode.ADD_CARD, "Example Store", "", "123456", "AZTEC");
shadowOf(getMainLooper()).idle();
checkAllFields(activity, ViewMode.ADD_CARD, "Example Store", "", DateFormat.getDateInstance(DateFormat.LONG).format(date), "10.00", "$", "123456", "AZTEC");
assertEquals(-416706, ((ColorDrawable) activity.findViewById(R.id.thumbnail).getBackground()).getColor());
}
@@ -831,8 +1115,9 @@ public class LoyaltyCardViewActivityTest
activityController.resume();
Activity activity = (Activity)activityController.get();
final Context context = ApplicationProvider.getApplicationContext();
checkAllFields(activity, ViewMode.ADD_CARD, "Example Store", "", "123456", "AZTEC");
checkAllFields(activity, ViewMode.ADD_CARD, "Example Store", "", context.getString(R.string.never), "0", context.getString(R.string.points), "123456", "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", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
assertEquals(View.VISIBLE, helpText.getVisibility());
assertEquals(View.GONE, noMatchingCardsText.getVisibility());
@@ -111,6 +113,8 @@ public class MainActivityTest
assertEquals(1, list.getAdapter().getCount());
Cursor cursor = (Cursor)list.getAdapter().getItem(0);
assertNotNull(cursor);
db.close();
}
@Test
@@ -129,10 +133,10 @@ public class MainActivityTest
assertEquals(0, list.getCount());
DBHelper db = new DBHelper(mainActivity);
db.insertLoyaltyCard("storeB", "note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
db.insertLoyaltyCard("storeA", "note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
db.insertLoyaltyCard("storeD", "note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1);
db.insertLoyaltyCard("storeC", "note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1);
db.insertLoyaltyCard("storeB", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
db.insertLoyaltyCard("storeA", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
db.insertLoyaltyCard("storeD", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1);
db.insertLoyaltyCard("storeC", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1);
assertEquals(View.VISIBLE, helpText.getVisibility());
assertEquals(View.GONE, noMatchingCardsText.getVisibility());
@@ -161,6 +165,8 @@ public class MainActivityTest
cursor = (Cursor)list.getAdapter().getItem(3);
assertNotNull(cursor);
assertEquals("storeB",cursor.getString(cursor.getColumnIndex("store")));
db.close();
}
@Test
@@ -209,6 +215,8 @@ public class MainActivityTest
activityController.pause();
activityController.resume();
assertEquals(0, groupTabs.getTabCount());
db.close();
}
@Test
@@ -226,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", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
db.insertLoyaltyCard("The Second Store", "Secondary note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
db.insertLoyaltyCard("The First Store", "Initial note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
db.insertLoyaltyCard("The Second Store", "Secondary note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
db.insertGroup("Group one");
List<Group> groups = new ArrayList<>();
@@ -434,5 +442,7 @@ public class MainActivityTest
assertEquals(View.VISIBLE, list.getVisibility());
assertEquals(2, list.getCount());
db.close();
}
}

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

@@ -2,13 +2,15 @@
buildscript {
repositories {
jcenter()
// For the Android Gradle plugin
google()
jcenter()
maven { url "https://jitpack.io" }
maven { url "https://plugins.gradle.org/m2/" }
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.0'
classpath 'com.android.tools.build:gradle:4.1.1'
classpath 'gradle.plugin.com.github.spotbugs.snom:spotbugs-gradle-plugin:4.6.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -17,9 +19,10 @@ buildscript {
allprojects {
repositories {
jcenter()
maven { url 'https://jitpack.io' }
google()
jcenter()
maven { url "https://jitpack.io" }
maven { url "https://plugins.gradle.org/m2/" }
}
}

View File

@@ -2,10 +2,13 @@
![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://appgallery.huawei.com/#/app/C103806479" target="_blank">
<img src="https://huaweimobileservices.com/wp-content/uploads/2020/05/Explore-it-on-AppGallery.png" alt="Explore it on AppGallery" 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>
@@ -59,12 +62,10 @@ Windows:
# Translating
[![Translation status](https://hosted.weblate.org/widgets/catima/-/open-graph.png)](https://hosted.weblate.org/engage/catima/)
Please contribute through [our Weblate page](https://hosted.weblate.org/projects/catima/).
# Thanks
This application uses the following image:
- [Save](https://thenounproject.com/term/save/716011) by [Bernar Novalyi](https://thenounproject.com/bernar.novalyi)
# Note from Developer
This application is based on the great [Loyalty Card Keychain](https://github.com/brarcher/loyalty-card-locker) by [Branden Archer](https://github.com/brarcher). This fork was created due to the original developer having stopped development of this app.

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,11 @@
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://appgallery.huawei.com/#/app/C103806479" target="_blank">
<img src="https://huaweimobileservices.com/wp-content/uploads/2020/05/Explore-it-on-AppGallery.png" alt="Explore it on AppGallery" 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 +1 @@
Verwaltet barcode-basierte Kunden-/Treuekarten auf dem Handy
Verwaltet barcode-basierte Kunden-/Treuekarten auf dem Handy

View File

@@ -1 +1 @@
Catima
Catima - Kundenkarten- & Ticket-Manager

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

@@ -0,0 +1,7 @@
Надоело искать свою пластиковую скидочную карту на кассе в магазине? Ищете бесплатное решение, которое не будет собирать вашу личную информацию?
Catima — это приложение, которое будет хранить карты лояльности на основе штрих-кодов на вашем смартфоне. Это приложение с открытым исходным кодом и пытается сделать хорошо только одну вещь : управлять вашими картами!
Новые карты можно добавить в одно мгновение. Либо используйте камеру, чтобы считать штрих-код, либо введите номер карты вручную. Добавленный в приложение штрих-код можно считать с экрана смартфона в магазине с современным сканером штрих-кода. (В некоторых магазинах вместо сканеров изображений используются старые сканеры штрих-кодов, например, планшетные сканеры. Они не могут считывать данные с экрана. Вместо этого попросите сотрудника ввести номер вручную.).
Приложению требуется очень мало разрешений и не нужен доступ в интернет. Есть возможность резервного копирования ваших карт в локальное хранилище. Оттуда вы сможете перенести данные резервной копии в надёжное место.

View File

@@ -16,11 +16,5 @@
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# The aapt2 tool creates an APK which fails to install on Android 5 and below if it contains
# a bug. Build tools 27.0.1 has a mitigation. Avoiding aapt2 also avoids hitting the bug.
# See: https://issuetracker.google.com/issues/64434571
android.enableAapt2=false
android.enableJetifier=true
android.useAndroidX=true
android.enableUnitTestBinaryResources=true

View File

Binary file not shown.

View File

@@ -1,6 +1,5 @@
#Sun Jul 19 15:12:56 CEST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-all.zip

139
gradlew vendored
View File

@@ -1,4 +1,20 @@
#!/usr/bin/env bash
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
@@ -6,42 +22,6 @@
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
@@ -60,8 +40,49 @@ cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
@@ -85,7 +106,7 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@@ -105,10 +126,11 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
@@ -134,27 +156,30 @@ if $cygwin ; then
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
i=`expr $i + 1`
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
APP_ARGS=`save "$@"`
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

53
gradlew.bat vendored
View File

@@ -1,3 +1,19 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@@ -8,20 +24,23 @@
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -35,7 +54,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -45,34 +64,14 @@ echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell