Compare commits

...

193 Commits
v1.2.0 ... v1.8

Author SHA1 Message Date
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
Sylvia van Os
afa76d8f54 Fix regression in manual barcode selector 2020-12-16 18:56:01 +01:00
Sylvia van Os
cf20d03fb3 Release 1.6.0 2020-12-15 23:27:47 +01:00
Sylvia van Os
bb866819db Cleanups 2020-12-15 23:11:18 +01:00
Sylvia van Os
c9dd994120 Fix findBugs 2020-12-15 22:47:04 +01:00
Sylvia van Os
eabd988540 Fix tests 2020-12-15 22:29:01 +01:00
Sylvia van Os
af91097559 Fix back button 2020-12-15 12:06:14 +01:00
Sylvia van Os
16793d1c0b Clean up imports 2020-12-14 22:36:17 +01:00
Sylvia van Os
6f4673c9fb Theming improvements 2020-12-14 21:34:28 +01:00
Sylvia van Os
bb99321209 Camera view with manual scan button 2020-12-14 21:12:59 +01:00
Sylvia van Os
baa42c8c69 Forgot a file 2020-12-11 12:38:32 +01:00
Sylvia van Os
6c011dd9ac Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2020-12-10 23:49:46 +01:00
Sylvia van Os
9bf245a392 Convert everything to SVG 2020-12-10 23:47:49 +01:00
Sylvia van Os
412afcda1e Merge pull request #126 from weblate/weblate-catima-catima
Translations update from Weblate
2020-12-07 08:20:20 +01:00
Samantaz Fox
c8bc0864b2 Translated using Weblate (French)
Currently translated at 99.1% (117 of 118 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2020-12-07 02:29:04 +01:00
Sylvia van Os
4d59e6c48d Focus textfield on group creation/edit 2020-12-06 19:13:21 +01:00
Sylvia van Os
70cf42f144 Update translations 2020-12-05 12:24:30 +01:00
Sylvia van Os
b3fc66fa58 Merge pull request #125 from weblate/weblate-catima-catima
Translations update from Weblate
2020-12-05 12:14:06 +01:00
Allan Nordhøy
aa7315fafe Translated using Weblate (Norwegian Bokmål)
Currently translated at 98.3% (116 of 118 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2020-12-05 02:29:03 +01:00
Sylvia van Os
5325f1955e Release 1.5.1 2020-12-03 20:39:46 +01:00
Sylvia van Os
ee30180137 Release 1.5.0 2020-12-03 20:02:20 +01:00
Sylvia van Os
6bddeb2f75 Update screenshots 2020-12-03 19:42:21 +01:00
Sylvia van Os
3c69990b0d Make findBugs happy and improve bottom sheet logic 2020-12-03 19:33:20 +01:00
Sylvia van Os
43d5478ae3 Increase arrow size 2020-12-03 18:53:01 +01:00
Sylvia van Os
30a54ae22a Note size is no longer configurable 2020-12-03 18:18:32 +01:00
Sylvia van Os
9ff6b87092 More i18n fixes 2020-12-03 18:04:49 +01:00
Sylvia van Os
9763d9fb73 Merge pull request #124 from weblate/weblate-catima-catima
Translations update from Weblate
2020-12-03 18:00:59 +01:00
Heimen Stoffels
0b92686dad Translated using Weblate (Dutch)
Currently translated at 100.0% (118 of 118 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2020-12-03 17:58:34 +01:00
Sylvia van Os
cb1c834057 Fix strings 2020-12-03 17:57:53 +01:00
Sylvia van Os
98a0487d0c Merge pull request #123 from weblate/weblate-catima-catima
Translations update from Weblate
2020-12-03 17:55:08 +01:00
J. Lavoie
2df165061d Translated using Weblate (French)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/fr/
2020-12-03 17:53:52 +01:00
Heimen Stoffels
fdd7ca03cf Translated using Weblate (Dutch)
Currently translated at 100.0% (118 of 118 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2020-12-03 17:53:52 +01:00
J. Lavoie
29e996a5fe Translated using Weblate (German)
Currently translated at 100.0% (118 of 118 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2020-12-03 17:53:52 +01:00
Sylvia van Os
3c1c8baaa0 Make bottom sheet button tapable and add group info 2020-12-03 17:53:23 +01:00
Sylvia van Os
6c186c2abb Merge pull request #122 from weblate/weblate-catima-catima
Translations update from Weblate
2020-12-03 17:33:23 +01:00
J. Lavoie
9deb0b94af Translated using Weblate (French)
Currently translated at 97.4% (115 of 118 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2020-12-03 17:32:16 +01:00
Samantaz Fox
97d54e87f2 Translated using Weblate (French)
Currently translated at 97.4% (115 of 118 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2020-12-03 17:32:16 +01:00
Sylvia van Os
0e329b3c31 Merge pull request #121 from weblate/weblate-catima-catima
Translations update from Weblate
2020-12-03 17:31:59 +01:00
Samantaz Fox
bd337d6da8 Translated using Weblate (French)
Currently translated at 98.3% (116 of 118 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2020-12-03 17:24:53 +01:00
Sylvia van Os
dc7f199e0c Fix crash 2020-12-02 21:27:58 +01:00
Sylvia van Os
b54e14def2 Create bottom sheet in loyalty card view to prevent FAB rendering over note 2020-12-02 21:17:00 +01:00
Sylvia van Os
f724e4c164 Always user white text on red buttons 2020-12-02 19:43:54 +01:00
Sylvia van Os
c389a02d69 Fix tests 2020-12-02 18:36:23 +01:00
Sylvia van Os
b7db58e7d0 Make Fastlane happy 2020-12-01 22:59:06 +01:00
Sylvia van Os
eefd26fa06 Downgrade dependencies again
It just doesn't build otherwise
2020-12-01 22:54:07 +01:00
Sylvia van Os
8d4a122cb5 Fix many lint checks 2020-12-01 21:33:36 +01:00
Sylvia van Os
13ea10d0b0 Lint fixes 2020-12-01 19:54:28 +01:00
Sylvia van Os
4366eab380 Actually show toast 2020-12-01 19:47:43 +01:00
Sylvia van Os
0c61d36cb3 Increase to minSdk 19
We were already kinda not compatible with pre-19 anymore
2020-12-01 19:46:52 +01:00
Sylvia van Os
6bc5e77bbd Release 1.4.1 2020-12-01 19:35:06 +01:00
Sylvia van Os
d8ddee0667 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker into master 2020-12-01 19:01:24 +01:00
Sylvia van Os
5e8312e816 Merge pull request #119 from weblate/weblate-catima-catima
Translations update from Weblate
2020-12-01 18:59:06 +01:00
Heimen Stoffels
8e10022158 Translated using Weblate (Dutch)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/nl/
2020-12-01 18:58:00 +01:00
Sylvia van Os
b209b670a5 Small UI cleanups 2020-12-01 18:49:59 +01:00
Sylvia van Os
ecdec0f773 Merge pull request #118 from weblate/weblate-catima-catima
Translations update from Weblate
2020-12-01 12:03:13 +01:00
Heimen Stoffels
5c90107a7b Translated using Weblate (Dutch)
Currently translated at 66.6% (2 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/nl/
2020-12-01 12:01:32 +01:00
Samantaz Fox
6a8a9878d7 Translated using Weblate (French)
Currently translated at 66.6% (2 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/fr/
2020-12-01 12:01:32 +01:00
Artem
3400d10cc6 Translated using Weblate (Russian)
Currently translated at 84.6% (105 of 124 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2020-12-01 12:01:32 +01:00
Sylvia van Os
05e171170b Translated using Weblate (Russian)
Currently translated at 84.6% (105 of 124 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2020-12-01 12:01:32 +01:00
Sylvia van Os
cb967a8d8d Translated using Weblate (Dutch)
Currently translated at 100.0% (124 of 124 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2020-12-01 12:01:32 +01:00
Sylvia van Os
5835da2931 Translated using Weblate (French)
Currently translated at 100.0% (124 of 124 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2020-12-01 12:01:32 +01:00
Sylvia van Os
2e9339b8aa Fix unnecessary file 2020-12-01 12:00:52 +01:00
Sylvia van Os
bbba4b2e11 Correctly use plural string 2020-11-30 21:44:38 +01:00
Sylvia van Os
d9659ecc9d Merge remote-tracking branch 'weblate/master' into master 2020-11-30 21:23:04 +01:00
Heimen Stoffels
c558278962 Translated using Weblate (Dutch)
Currently translated at 100.0% (124 of 124 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2020-11-30 21:19:44 +01:00
Sylvia van Os
c137cb9428 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker into master 2020-11-30 21:19:07 +01:00
Sylvia van Os
1d2f54c69b Translation fixes 2020-11-30 19:53:32 +01:00
Sylvia van Os
d4a377fe18 Merge pull request #116 from weblate/weblate-catima-catima
Translations update from Weblate
2020-11-29 00:30:43 +01:00
Allan Nordhøy
9844113568 Translated using Weblate (Norwegian Bokmål)
Currently translated at 95.9% (119 of 124 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2020-11-29 00:29:00 +01:00
Sylvia van Os
c49f26db95 Release 1.4.0 2020-11-28 16:46:01 +01:00
Sylvia van Os
9744dfe6ff Make lint happy 2020-11-28 15:45:09 +01:00
Sylvia van Os
55dfd1fe09 Fix build 2020-11-28 15:24:07 +01:00
Sylvia van Os
0a3c81154c Fix colouring of manage group buttons 2020-11-28 14:44:09 +01:00
Sylvia van Os
70a9689bb2 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker into master 2020-11-28 13:52:01 +01:00
Sylvia van Os
54f6d8d26f Fix scan/manual icon colour on light theme 2020-11-28 13:51:13 +01:00
Sylvia van Os
c0622ddaec Merge pull request #115 from TheLastProject/create-pull-request/patch-1606514188
Compressed Images
2020-11-27 22:57:16 +01:00
TheLastProject
12578eab3e Compressed Images 2020-11-27 21:56:28 +00:00
Sylvia van Os
5dd57577af Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker into master 2020-11-27 22:55:20 +01:00
Sylvia van Os
d883c84b4e Fix migration guide 2020-11-27 22:54:40 +01:00
Sylvia van Os
0ebe1b387d Merge pull request #114 from weblate/weblate-catima-catima
Translations update from Weblate
2020-11-27 22:35:21 +01:00
Allan Nordhøy
942aff2af5 Translated using Weblate (Norwegian Bokmål)
Currently translated at 95.9% (117 of 122 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2020-11-27 22:34:03 +01:00
Sylvia van Os
c1606fef62 Manual group ordering 2020-11-27 22:33:59 +01:00
Sylvia van Os
cc895353a9 Update CHANGELOG 2020-11-27 14:47:27 +01:00
Sylvia van Os
73bac0ae8d Make findBugs happy 2020-11-27 13:19:35 +01:00
Sylvia van Os
158e424a47 Add dialog giving the user scan/manually enter options (#111) 2020-11-27 12:58:55 +01:00
Sylvia van Os
133fa13d3b Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker into master 2020-11-27 09:42:49 +01:00
Sylvia van Os
44ff4d0cc6 Fix gitignore 2020-11-27 09:42:23 +01:00
Sylvia van Os
a7b280c08b Merge pull request #112 from weblate/weblate-catima-catima
Translations update from Weblate
2020-11-27 02:38:25 +01:00
J. Lavoie
5fe0429c98 Translated using Weblate (French)
Currently translated at 100.0% (120 of 120 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2020-11-27 02:28:59 +01:00
J. Lavoie
bf391022fd Translated using Weblate (German)
Currently translated at 100.0% (120 of 120 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2020-11-27 02:28:59 +01:00
Sylvia van Os
a431bf8c30 Merge pull request #109 from weblate/weblate-catima-catima
Translations update from Weblate
2020-11-23 02:37:45 +01:00
Sylvia van Os
a242944ee6 Deleted translation using Weblate (French (Belgium)) 2020-11-23 02:17:54 +01:00
Samantaz Fox
84a7e84486 Translated using Weblate (French)
Currently translated at 100.0% (120 of 120 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2020-11-23 02:17:54 +01:00
Samantaz Fox
d4988f1d4c Added translation using Weblate (French (Belgium)) 2020-11-23 02:17:54 +01:00
Sylvia van Os
04e52f0a8a Move About dialog into own activity 2020-11-23 02:17:18 +01:00
Sylvia van Os
370db97af1 Release 1.3.0 2020-11-22 19:43:53 +01:00
Sylvia van Os
84d7f4fbba Fix unit tests 2020-11-22 18:26:05 +01:00
Sylvia van Os
afcbca251c Merge pull request #108 from weblate/weblate-catima-catima
Translations update from Weblate
2020-11-22 17:22:59 +01:00
Samantaz Fox
aa9df04c9c Translated using Weblate (French)
Currently translated at 99.1% (119 of 120 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2020-11-22 17:19:32 +01:00
Allan Nordhøy
a24452a962 Translated using Weblate (Norwegian Bokmål)
Currently translated at 99.1% (119 of 120 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2020-11-22 17:19:32 +01:00
Samantaz Fox
e74176abb0 Translated using Weblate (French)
Currently translated at 99.1% (119 of 120 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2020-11-22 17:19:32 +01:00
Sylvia van Os
6f4cec4cf4 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker into master 2020-11-22 17:15:41 +01:00
Sylvia van Os
febd0cf463 Really fix change detection 2020-11-22 17:13:02 +01:00
Sylvia van Os
a5f91486e9 Merge pull request #107 from SamantazFox/patch-1
Propose more explicit settings names
2020-11-22 17:12:44 +01:00
Samantaz Fox
d8300640af Propose more explicit settings names 2020-11-22 16:46:15 +01:00
Sylvia van Os
afef158169 Merge pull request #106 from weblate/weblate-catima-catima
Translations update from Weblate
2020-11-21 23:14:40 +01:00
Samantaz Fox
a728d1d84d Translated using Weblate (French)
Currently translated at 79.6% (94 of 118 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2020-11-21 23:12:42 +01:00
Sylvia van Os
9f88b6348b Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker into master 2020-11-21 23:10:56 +01:00
Sylvia van Os
6929ba1c0d Ask for confirmation before leaving edit view after changes 2020-11-21 23:08:19 +01:00
Sylvia van Os
49b296b983 Merge pull request #105 from weblate/weblate-catima-catima
Translations update from Weblate
2020-11-21 16:31:27 +01:00
Allan Nordhøy
f21a75b19a Translated using Weblate (Dutch)
Currently translated at 73.7% (87 of 118 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2020-11-21 16:28:58 +01:00
Allan Nordhøy
8946b0a9e1 Translated using Weblate (Norwegian Bokmål)
Currently translated at 99.1% (117 of 118 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2020-11-21 16:28:57 +01:00
Allan Nordhøy
eae470f13c Translated using Weblate (Italian)
Currently translated at 73.7% (87 of 118 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2020-11-21 16:28:57 +01:00
Allan Nordhøy
5ce046b3f4 Translated using Weblate (French)
Currently translated at 74.5% (88 of 118 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2020-11-21 16:28:57 +01:00
Allan Nordhøy
a51debb2e1 Translated using Weblate (Spanish)
Currently translated at 62.7% (74 of 118 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/es/
2020-11-21 16:28:57 +01:00
Allan Nordhøy
7312a4bd1a Translated using Weblate (German)
Currently translated at 73.7% (87 of 118 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2020-11-21 16:28:57 +01:00
Sylvia van Os
3c1a54e456 Remove no longer relevant unit tests 2020-11-20 15:09:16 +01:00
Sylvia van Os
6d4babf944 Always show all import/export options 2020-11-20 14:56:11 +01:00
Sylvia van Os
7ecdf52f1a Fix typo 2020-11-20 00:29:49 +01:00
Sylvia van Os
c6f28272d9 Release 1.2.2 2020-11-19 21:16:36 +01:00
Sylvia van Os
ba8c20f415 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker into master 2020-11-19 16:48:55 +01:00
Sylvia van Os
b41dcc7ba1 Add IzzyOnDroid button 2020-11-19 16:45:33 +01:00
Sylvia van Os
6a14b5706b Merge pull request #102 from weblate/weblate-catima-catima
Translations update from Weblate
2020-11-17 18:36:27 +01:00
opsik
5bde6796b9 Translated using Weblate (Russian)
Currently translated at 66.6% (2 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/ru/
2020-11-17 18:32:39 +01:00
Sylvia van Os
3c797b5ec9 Remember active group tab 2020-11-17 18:30:47 +01:00
Sylvia van Os
dec85d8fa9 Release 1.2.1 2020-11-17 14:19:21 +01:00
168 changed files with 3060 additions and 1415 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

11
.gitignore vendored
View File

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

View File

@@ -1,5 +1,94 @@
# Changelog
## 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:
- Fix regression causing manual barcode entry to not be saved
## v1.6.0 (2020-12-15)
Changes:
- Automatically focus text field when creating or editing a group
- Fix blurry icons (use SVG everywhere)
- Always open camera but add manual scan button to camera view
## v1.5.1 (2020-12-03)
Changes:
- Fix bottomsheet background being transparent
## v1.5.0 (2020-12-03)
Changes:
- Improve contrast by always using white text on red buttons
- Draggable bottom sheet in loyalty card view
## v1.4.1 (2020-12-01)
Changes:
- Improved translations
- Small UI fixes
## v1.4.0 (2020-11-28)
Changes:
- Move About screen into its own activity
- Ask user if they want to use their camera or manually enter ID on add/edit card
- Make group ordering manual instead of forced alphabetically
## v1.3.0 (2020-11-22)
Changes:
- Always show all import/export options and show a toast on actual issues (improves compat with XPrivacyLua)
- Ask for confirmation when leaving edit view after making changes without saving
## v1.2.2 (2020-11-19)
Changes:
- Remember active group tab between screens and sessions
## v1.2.1 (2020-11-17)
Changes:
- Fix home screen swiping triggering during vertical swipes too
## v1.2.0 (2020-11-17)
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 16
minSdkVersion 19
targetSdkVersion 29
versionCode 44
versionName "1.2.0"
versionCode 57
versionName "1.8"
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:3.5.0@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.github.apl-devs:appintro:v4.2.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.4.1'
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

Binary file not shown.

View File

@@ -1 +0,0 @@
[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":41,"versionName":"1.0","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]

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

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="protect.card_locker"
xmlns:android="http://schemas.android.com/apk/res/android">
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission
android:name="android.permission.CAMERA"/>
@@ -16,6 +17,8 @@
android:name="android.hardware.camera.autofocus"
android:required="false" />
<uses-sdk tools:overrideLibrary="com.google.zxing.client.android" />
<application
android:name=".LoyaltyCardLockerApplication"
android:allowBackup="true"
@@ -33,6 +36,11 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".AboutActivity"
android:label="@string/about"
android:theme="@style/AppTheme.NoActionBar">
</activity>
<activity
android:name=".ManageGroupsActivity"
android:label="@string/groups"
@@ -41,7 +49,6 @@
<activity
android:name=".LoyaltyCardViewActivity"
android:theme="@style/AppTheme.NoActionBar"
android:label=""
android:windowSoftInputMode="stateHidden"
android:exported="true"/>
<activity
@@ -62,6 +69,10 @@
android:pathPrefix="@string/intent_import_card_from_url_path_prefix_old" />
</intent-filter>
</activity>
<activity
android:name=".ScanActivity"
android:label="@string/scanCardBarcode"
android:theme="@style/AppTheme.NoActionBar"/>
<activity
android:name=".BarcodeSelectorActivity"
android:label="@string/selectBarcodeTitle"

View File

@@ -0,0 +1,111 @@
package protect.card_locker;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.view.MenuItem;
import android.widget.TextView;
import com.google.common.collect.ImmutableMap;
import java.util.Calendar;
import java.util.Map;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.text.HtmlCompat;
public class AboutActivity extends AppCompatActivity
{
private static final String TAG = "Catima";
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.about_activity);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
{
actionBar.setDisplayHomeAsUpEnabled(true);
}
final Map<String, String> USED_LIBRARIES = new ImmutableMap.Builder<String, String>()
.put("Commons CSV", "https://commons.apache.org/proper/commons-csv/")
.put("Guava", "https://github.com/google/guava")
.put("ZXing", "https://github.com/zxing/zxing")
.put("ZXing Android Embedded", "https://github.com/journeyapps/zxing-android-embedded")
.put("Color Picker", "https://github.com/jaredrummler/ColorPicker")
.put("NumberPickerPreference", "https://github.com/invissvenska/NumberPickerPreference")
.build();
final Map<String, String> USED_ASSETS = ImmutableMap.of
(
"Android icons", "https://www.apache.org/licenses/LICENSE-2.0.txt"
);
StringBuilder libs = new StringBuilder().append("<br/>");
for (Map.Entry<String, String> entry : USED_LIBRARIES.entrySet())
{
libs.append("<br/><a href=\"").append(entry.getValue()).append("\">").append(entry.getKey()).append("</a><br/>");
}
StringBuilder resources = new StringBuilder().append("<br/>");
for (Map.Entry<String, String> entry : USED_ASSETS.entrySet())
{
resources.append("<br/><a href=\"").append(entry.getValue()).append("\">").append(entry.getKey()).append("</a><br/>");
}
String appName = getString(R.string.app_name);
int year = Calendar.getInstance().get(Calendar.YEAR);
String version = "?";
try
{
PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), 0);
version = pi.versionName;
}
catch (PackageManager.NameNotFoundException e)
{
Log.w(TAG, "Package name not found", e);
}
setTitle(String.format(getString(R.string.about_title_fmt), appName));
TextView aboutTextView = findViewById(R.id.aboutText);
aboutTextView.setText(HtmlCompat.fromHtml(String.format(getString(R.string.debug_version_fmt), version) +
"<br/><br/>" +
String.format(getString(R.string.app_revision_fmt),
"<a href=\"" + getString(R.string.app_revision_url) + "\">" +
"GitHub" +
"</a>") +
"<br/><br/>" +
String.format(getString(R.string.app_copyright_fmt), year) +
"<br/><br/>" +
getString(R.string.app_copyright_old) +
"<br/><br/>" +
getString(R.string.app_license) +
"<br/><br/>" +
String.format(getString(R.string.app_libraries), appName, libs.toString()) +
"<br/><br/>" +
String.format(getString(R.string.app_resources), appName, resources.toString()), HtmlCompat.FROM_HTML_MODE_COMPACT));
aboutTextView.setMovementMethod(LinkMovementMethod.getInstance());
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
int id = item.getItemId();
if (id == android.R.id.home) {
finish();
}
return super.onOptionsItemSelected(item);
}
}

View File

@@ -3,7 +3,6 @@ package protect.card_locker;
import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
@@ -191,14 +190,7 @@ public class BarcodeSelectorActivity extends AppCompatActivity
public void onGlobalLayout()
{
Log.d(TAG, "Global layout finished, type: + " + formatType + ", width: " + image.getWidth());
if (Build.VERSION.SDK_INT < 16)
{
image.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
else
{
image.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
image.getViewTreeObserver().removeOnGlobalLayoutListener(this);
Log.d(TAG, "Generating barcode for type " + formatType);
BarcodeImageWriterTask task = new BarcodeImageWriterTask(image, cardId, format, text);

View File

@@ -0,0 +1,23 @@
package protect.card_locker;
public class BarcodeValues {
private final String mFormat;
private final String mContent;
public BarcodeValues(String format, String content) {
mFormat = format;
mContent = content;
}
public String format() {
return mFormat;
}
public String content() {
return mContent;
}
public boolean isEmpty() {
return mFormat == null && mContent == null;
}
}

View File

@@ -49,6 +49,7 @@ public class CsvDatabaseExporter implements DatabaseExporter
printer.printRecord(DBHelper.LoyaltyCardDbIds.ID,
DBHelper.LoyaltyCardDbIds.STORE,
DBHelper.LoyaltyCardDbIds.NOTE,
DBHelper.LoyaltyCardDbIds.EXPIRY,
DBHelper.LoyaltyCardDbIds.CARD_ID,
DBHelper.LoyaltyCardDbIds.HEADER_COLOR,
DBHelper.LoyaltyCardDbIds.BARCODE_TYPE,
@@ -63,6 +64,7 @@ public class CsvDatabaseExporter implements DatabaseExporter
printer.printRecord(card.id,
card.store,
card.note,
card.expiry != null ? card.expiry.getTime() : "",
card.cardId,
card.headerColor,
card.barcodeType,

View File

@@ -10,6 +10,7 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.util.Date;
import java.util.List;
/**
@@ -47,6 +48,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 +134,6 @@ public class CsvDatabaseImporter implements DatabaseImporter
}
}
public void parseV2Groups(DBHelper db, SQLiteDatabase database, String data) throws IOException, FormatException, InterruptedException
{
// Parse groups
@@ -145,10 +147,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 +167,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 +187,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 +250,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 +296,10 @@ 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) { }
String cardId = extractString(DBHelper.LoyaltyCardDbIds.CARD_ID, record, "");
if(cardId.isEmpty())
@@ -288,7 +324,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, cardId, barcodeType, headerColor, starStatus);
}
/**

View File

@@ -4,9 +4,11 @@ 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.util.Date;
import java.util.ArrayList;
import java.util.List;
@@ -14,12 +16,13 @@ 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 = 5;
public static final int DATABASE_VERSION = 7;
static class LoyaltyCardDbGroups
{
public static final String TABLE = "groups";
public static final String ID = "_id";
public static final String ORDER = "orderId";
}
static class LoyaltyCardDbIds
@@ -27,6 +30,7 @@ public class DBHelper extends SQLiteOpenHelper
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 NOTE = "note";
public static final String HEADER_COLOR = "headercolor";
public static final String HEADER_TEXT_COLOR = "headertextcolor";
@@ -52,13 +56,15 @@ public class DBHelper extends SQLiteOpenHelper
{
// create table for card groups
db.execSQL("create table " + LoyaltyCardDbGroups.TABLE + "(" +
LoyaltyCardDbGroups.ID + " TEXT primary key not null)");
LoyaltyCardDbGroups.ID + " TEXT primary key not null," +
LoyaltyCardDbGroups.ORDER + " INTEGER DEFAULT '0')");
// create table for cards
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.HEADER_COLOR + " INTEGER," +
LoyaltyCardDbIds.HEADER_TEXT_COLOR + " INTEGER," +
LoyaltyCardDbIds.CARD_ID + " TEXT not null," +
@@ -109,16 +115,30 @@ public class DBHelper extends SQLiteOpenHelper
LoyaltyCardDbIdsGroups.groupID + " TEXT," +
"primary key (" + LoyaltyCardDbIdsGroups.cardID + "," + LoyaltyCardDbIdsGroups.groupID +"))");
}
// Upgrade from version 5 to 6
if(oldVersion < 6 && newVersion >= 6)
{
db.execSQL("ALTER TABLE " + LoyaltyCardDbGroups.TABLE
+ " ADD COLUMN " + LoyaltyCardDbGroups.ORDER + " INTEGER DEFAULT '0'");
}
if(oldVersion < 7 && newVersion >= 7)
{
db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE
+ " ADD COLUMN " + LoyaltyCardDbIds.EXPIRY + " INTEGER");
}
}
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 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.CARD_ID, cardId);
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType);
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
@@ -129,7 +149,7 @@ 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 String cardId,
final String barcodeType, final Integer headerColor,
final int starStatus)
{
@@ -137,6 +157,7 @@ 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.CARD_ID, cardId);
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType);
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
@@ -147,13 +168,14 @@ 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 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.CARD_ID, cardId);
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType);
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
@@ -206,6 +228,7 @@ public class DBHelper extends SQLiteOpenHelper
List<Group> groups = new ArrayList<>();
if (!data.moveToFirst()) {
data.close();
return groups;
}
@@ -215,6 +238,8 @@ public class DBHelper extends SQLiteOpenHelper
groups.add(Group.toGroup(data));
}
data.close();
return groups;
}
@@ -376,7 +401,7 @@ public class DBHelper extends SQLiteOpenHelper
SQLiteDatabase db = getReadableDatabase();
Cursor res = db.rawQuery("select * from " + LoyaltyCardDbGroups.TABLE +
" ORDER BY " + LoyaltyCardDbGroups.ID + " COLLATE NOCASE ASC", null, null);
" ORDER BY " + LoyaltyCardDbGroups.ORDER + " ASC," + LoyaltyCardDbGroups.ID + " COLLATE NOCASE ASC", null, null);
return res;
}
@@ -386,6 +411,7 @@ public class DBHelper extends SQLiteOpenHelper
List<Group> groups = new ArrayList<>();
if (!data.moveToFirst()) {
data.close();
return groups;
}
@@ -395,9 +421,31 @@ public class DBHelper extends SQLiteOpenHelper
groups.add(Group.toGroup(data));
}
data.close();
return groups;
}
public void reorderGroups(final List<Group> groups)
{
Integer order = 0;
SQLiteDatabase db = getWritableDatabase();
ContentValues contentValues;
for (Group group : groups)
{
contentValues = new ContentValues();
contentValues.put(LoyaltyCardDbGroups.ORDER, order);
db.update(LoyaltyCardDbGroups.TABLE, contentValues,
LoyaltyCardDbGroups.ID + "=?",
new String[]{group._id});
order++;
}
}
public Group getGroup(final String groupName)
{
SQLiteDatabase db = getReadableDatabase();
@@ -467,6 +515,7 @@ public class DBHelper extends SQLiteOpenHelper
SQLiteDatabase db = getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(LoyaltyCardDbGroups.ID, name);
contentValues.put(LoyaltyCardDbGroups.ORDER, getGroupCount());
final long newId = db.insert(LoyaltyCardDbGroups.TABLE, null, contentValues);
return newId;
}
@@ -475,6 +524,7 @@ public class DBHelper extends SQLiteOpenHelper
{
ContentValues contentValues = new ContentValues();
contentValues.put(LoyaltyCardDbGroups.ID, name);
contentValues.put(LoyaltyCardDbGroups.ORDER, getGroupCount());
final long newId = db.insert(LoyaltyCardDbGroups.TABLE, null, contentValues);
return (newId != -1);
}
@@ -483,33 +533,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

@@ -37,15 +37,18 @@ class GroupCursorAdapter extends CursorAdapter
public void bindView(View view, Context context, Cursor cursor)
{
// Find fields to populate in inflated template
TextView nameField = (TextView) view.findViewById(R.id.name);
TextView countField = (TextView) view.findViewById(R.id.cardCount);
TextView nameField = view.findViewById(R.id.name);
TextView countField = view.findViewById(R.id.cardCount);
// Extract properties from cursor
Group group = Group.toGroup(cursor);
Integer groupCardCount = db.getGroupCardCount(group._id);
// Populate fields with extracted properties
nameField.setText(group._id);
countField.setText(String.format(context.getString(R.string.groupCardCount), db.getGroupCardCount(group._id)));
countField.setText(context.getResources().getQuantityString(R.plurals.groupCardCount, groupCardCount, groupCardCount));
nameField.setTextSize(settings.getCardTitleListFontSize());
countField.setTextSize(settings.getCardNoteListFontSize());

View File

@@ -2,11 +2,9 @@ package protect.card_locker;
import android.Manifest;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -30,7 +28,6 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
public class ImportExportActivity extends AppCompatActivity
{
@@ -100,14 +97,6 @@ public class ImportExportActivity extends AppCompatActivity
}
});
if(isCallable(getApplicationContext(), intentGetContentAction) == false)
{
findViewById(R.id.dividerImportFilesystem).setVisibility(View.GONE);
findViewById(R.id.importOptionFilesystemTitle).setVisibility(View.GONE);
findViewById(R.id.importOptionFilesystemExplanation).setVisibility(View.GONE);
importFilesystem.setVisibility(View.GONE);
}
// Check that there is an app that data can be imported from
final Intent intentPickAction = new Intent(Intent.ACTION_PICK);
@@ -120,14 +109,6 @@ public class ImportExportActivity extends AppCompatActivity
chooseFileWithIntent(intentPickAction, CHOOSE_EXPORTED_FILE);
}
});
if(isCallable(getApplicationContext(), intentPickAction) == false)
{
findViewById(R.id.dividerImportApplication).setVisibility(View.GONE);
findViewById(R.id.importOptionApplicationTitle).setVisibility(View.GONE);
findViewById(R.id.importOptionApplicationExplanation).setVisibility(View.GONE);
importApplication.setVisibility(View.GONE);
}
}
private void startImport(final InputStream target, final Uri targetUri)
@@ -295,31 +276,6 @@ public class ImportExportActivity extends AppCompatActivity
builder.create().show();
}
/**
* Determines if there is at least one activity that can perform the given intent
*/
private boolean isCallable(Context context, final Intent intent)
{
PackageManager manager = context.getPackageManager();
if(manager == null)
{
return false;
}
List<ResolveInfo> list = manager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for(ResolveInfo info : list)
{
if(info.activityInfo.exported)
{
// There is one activity which is available to be called
return true;
}
}
return false;
}
private void chooseFileWithIntent(Intent intent, int requestCode)
{
try
@@ -328,6 +284,7 @@ public class ImportExportActivity extends AppCompatActivity
}
catch (ActivityNotFoundException e)
{
Toast.makeText(getApplicationContext(), R.string.failedOpeningFileManager, Toast.LENGTH_LONG).show();
Log.e(TAG, "No activity found to handle intent", e);
}
}

View File

@@ -4,10 +4,12 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import java.io.InvalidObjectException;
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 CARD_ID = DBHelper.LoyaltyCardDbIds.CARD_ID;
private static final String BARCODE_TYPE = DBHelper.LoyaltyCardDbIds.BARCODE_TYPE;
@@ -40,6 +42,7 @@ public class ImportURIHelper {
try {
// These values are allowed to be null
Date expiry = null;
Integer headerColor = null;
Integer headerTextColor = null;
@@ -49,13 +52,18 @@ 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 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, cardId, barcodeType, headerColor, headerTextColor, 0);
} catch (NullPointerException | NumberFormatException ex) {
throw new InvalidObjectException("Not a valid import URI");
}
@@ -69,6 +77,9 @@ public class ImportURIHelper {
uriBuilder.path(path);
uriBuilder.appendQueryParameter(STORE, loyaltyCard.store);
uriBuilder.appendQueryParameter(NOTE, loyaltyCard.note);
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,9 @@ package protect.card_locker;
import android.database.Cursor;
import java.text.DateFormat;
import java.util.Date;
import androidx.annotation.Nullable;
public class LoyaltyCard
@@ -9,6 +12,7 @@ public class LoyaltyCard
public final int id;
public final String store;
public final String note;
public final Date expiry;
public final String cardId;
public final String barcodeType;
@@ -20,13 +24,14 @@ 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 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.cardId = cardId;
this.barcodeType = barcodeType;
this.headerColor = headerColor;
@@ -39,17 +44,23 @@ 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));
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 headerColorColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_COLOR);
int headerTextColorColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR);
Date expiry = null;
Integer headerColor = null;
Integer headerTextColor = null;
if(expiryLong > 0)
{
expiry = new Date(expiryLong);
}
if(cursor.isNull(headerColorColumn) == false)
{
headerColor = cursor.getInt(headerColorColumn);
@@ -60,6 +71,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, cardId, barcodeType, headerColor, headerTextColor, starred);
}
}

View File

@@ -2,27 +2,26 @@ package protect.card_locker;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.text.DateFormat;
import java.util.Date;
import protect.card_locker.preferences.Settings;
class LoyaltyCardCursorAdapter extends CursorAdapter
{
Settings settings;
boolean darkModeEnabled;
public LoyaltyCardCursorAdapter(Context context, Cursor cursor)
{
super(context, cursor, 0);
settings = new Settings(context);
darkModeEnabled= MainActivity.isDarkModeEnabled(context);
}
// The newView method is used to inflate a new view and return it,
@@ -42,13 +41,9 @@ 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 expiryField = view.findViewById(R.id.expiry);
ImageView star = view.findViewById(R.id.star);
if(darkModeEnabled)
{
star.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP);
}
// Extract properties from cursor
LoyaltyCard loyaltyCard = LoyaltyCard.toLoyaltyCard(cursor);
@@ -68,6 +63,22 @@ class LoyaltyCardCursorAdapter extends CursorAdapter
noteField.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,12 +1,12 @@
package protect.card_locker;
import android.app.Activity;
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;
@@ -17,67 +17,84 @@ 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.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.CalendarView;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.textfield.TextInputLayout;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
import com.jaredrummler.android.colorpicker.ColorPickerDialog;
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener;
import java.io.InvalidObjectException;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
public class LoyaltyCardEditActivity extends AppCompatActivity
{
private static final String TAG = "Catima";
protected static final int SELECT_BARCODE_REQUEST = 1;
TabLayout tabs;
ImageView thumbnail;
EditText storeFieldEdit;
EditText noteFieldEdit;
ChipGroup groupsChips;
AutoCompleteTextView expiryField;
View cardAndBarcodeLayout;
TextView cardIdFieldView;
View barcodeTypeView;
AutoCompleteTextView barcodeTypeField;
ImageView barcodeImage;
View barcodeImageLayout;
View barcodeCaptureLayout;
Button captureButton;
Button enterButton;
int loyaltyCardId;
boolean updateLoyaltyCard;
String barcodeType;
String cardId;
Uri importLoyaltyCardUri = null;
Integer headingColorValue = null;
DBHelper db;
ImportURIHelper importUriHelper;
boolean hasChanged = false;
boolean initDone = false;
AlertDialog confirmExitDialog = null;
private void extractIntentFields(Intent intent)
{
final Bundle b = intent.getExtras();
loyaltyCardId = b != null ? b.getInt("id") : 0;
updateLoyaltyCard = b != null && b.getBoolean("update", false);
barcodeType = b != null ? b.getString("barcodeType") : null;
cardId = b != null ? b.getString("cardId") : null;
importLoyaltyCardUri = intent.getData();
Log.d(TAG, "View activity: id=" + loyaltyCardId
@@ -103,19 +120,19 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
db = new DBHelper(this);
importUriHelper = new ImportURIHelper(this);
tabs = findViewById(R.id.tabs);
thumbnail = findViewById(R.id.thumbnail);
storeFieldEdit = findViewById(R.id.storeNameEdit);
noteFieldEdit = findViewById(R.id.noteEdit);
groupsChips = findViewById(R.id.groupChips);
expiryField = findViewById(R.id.expiryField);
cardAndBarcodeLayout = findViewById(R.id.cardAndBarcodeLayout);
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);
barcodeCaptureLayout = findViewById(R.id.barcodeCaptureLayout);
captureButton = findViewById(R.id.captureButton);
enterButton = findViewById(R.id.enterButton);
storeFieldEdit.addTextChangedListener(new TextWatcher() {
@@ -124,6 +141,8 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
hasChanged = true;
generateIcon(s.toString());
}
@@ -131,12 +150,47 @@ 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);
}
});
cardIdFieldView.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;
String formatString = barcodeTypeField.getText().toString();
if (!formatString.isEmpty()) {
@@ -158,6 +212,8 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
hasChanged = true;
if (!s.toString().isEmpty()) {
if (s.toString().equals(getString(R.string.noBarcode))) {
hideBarcode();
@@ -170,6 +226,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
@@ -183,6 +260,8 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
// Reset these fields, so they are re-populated in onResume().
storeFieldEdit.setText("");
noteFieldEdit.setText("");
expiryField.setTag(null);
expiryField.setText("");
cardIdFieldView.setText("");
barcodeTypeField.setText("");
}
@@ -215,6 +294,16 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
noteFieldEdit.setText(loyaltyCard.note);
}
if(expiryField.getText().length() == 0)
{
expiryField.setTag(loyaltyCard.expiry);
if (loyaltyCard.expiry == null) {
expiryField.setText(getString(R.string.never));
} else {
expiryField.setText(DateFormat.getDateInstance(DateFormat.LONG).format(loyaltyCard.expiry));
}
}
if(cardIdFieldView.getText().length() == 0)
{
cardIdFieldView.setText(loyaltyCard.cardId);
@@ -250,6 +339,12 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
storeFieldEdit.setText(importCard.store);
noteFieldEdit.setText(importCard.note);
expiryField.setTag(importCard.expiry);
if (importCard.expiry != null) {
expiryField.setText(DateFormat.getDateInstance(DateFormat.LONG).format(importCard.expiry));
} else {
expiryField.setText(R.string.never);
}
cardIdFieldView.setText(importCard.cardId);
barcodeTypeField.setText(importCard.barcodeType);
headingColorValue = importCard.headerColor;
@@ -257,6 +352,8 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
else
{
setTitle(R.string.addCardTitle);
expiryField.setTag(null);
expiryField.setText(getString(R.string.never));
hideBarcode();
}
@@ -284,6 +381,14 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
break;
}
}
chip.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
hasChanged = true;
return false;
}
});
groupsChips.addView(chip);
}
@@ -300,6 +405,21 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
thumbnail.setOnClickListener(new ColorSelectListener(headingColorValue));
if (!initDone) {
hasChanged = false;
initDone = true;
}
// Update from intent
if (barcodeType != null) {
barcodeTypeField.setText(barcodeType.isEmpty() ? getString(R.string.noBarcode) : barcodeType);
barcodeType = null;
}
if (cardId != null) {
cardIdFieldView.setText(cardId);
cardId = null;
}
if(cardIdFieldView.getText().length() > 0 && barcodeTypeField.getText().length() > 0)
{
String formatString = barcodeTypeField.getText().toString();
@@ -317,37 +437,9 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
}
}
View.OnClickListener captureCallback = new View.OnClickListener()
{
@Override
public void onClick(View v)
{
IntentIntegrator integrator = new IntentIntegrator(LoyaltyCardEditActivity.this);
integrator.setDesiredBarcodeFormats(BarcodeSelectorActivity.SUPPORTED_BARCODE_TYPES);
String prompt = getResources().getString(R.string.scanCardBarcode);
integrator.setPrompt(prompt);
integrator.setBeepEnabled(false);
integrator.initiateScan();
}
};
captureButton.setOnClickListener(captureCallback);
enterButton.setOnClickListener(new EditCardIdAndBarcode());
barcodeImage.setOnClickListener(new EditCardIdAndBarcode());
if(cardIdFieldView.getText().length() > 0)
{
cardAndBarcodeLayout.setVisibility(View.VISIBLE);
enterButton.setText(R.string.editCard);
}
else
{
cardAndBarcodeLayout.setVisibility(View.GONE);
enterButton.setText(R.string.enterCard);
}
ArrayList<String> barcodeList = new ArrayList<>(BarcodeSelectorActivity.SUPPORTED_BARCODE_TYPES);
barcodeList.add(0, getString(R.string.noBarcode));
ArrayAdapter<String> barcodeAdapter = new ArrayAdapter<>(this, android.R.layout.select_dialog_item, barcodeList);
@@ -364,22 +456,50 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
generateIcon(storeFieldEdit.getText().toString());
}
@Override
public void onBackPressed() {
askBeforeQuitIfChanged();
}
private void askBeforeQuitIfChanged() {
if (!hasChanged) {
finish();
return;
}
if (confirmExitDialog == null) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.leaveWithoutSaveTitle);
builder.setMessage(R.string.leaveWithoutSaveConfirmation);
builder.setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
dialog.dismiss();
}
});
builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
confirmExitDialog = builder.create();
}
confirmExitDialog.show();
}
class EditCardIdAndBarcode implements View.OnClickListener
{
@Override
public void onClick(View v)
{
Intent i = new Intent(getApplicationContext(), BarcodeSelectorActivity.class);
String cardId = cardIdFieldView.getText().toString();
if(cardId.length() > 0)
{
final Bundle b = new Bundle();
b.putString("initialCardId", cardId);
i.putExtras(b);
}
startActivityForResult(i, SELECT_BARCODE_REQUEST);
Intent i = new Intent(getApplicationContext(), ScanActivity.class);
final Bundle b = new Bundle();
b.putString("cardId", cardIdFieldView.getText().toString());
i.putExtras(b);
startActivityForResult(i, Utils.BARCODE_SCAN);
}
}
@@ -401,6 +521,8 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
@Override
public void onColorSelected(int dialogId, int color)
{
hasChanged = true;
headingColorValue = color;
generateIcon(storeFieldEdit.getText().toString());
@@ -412,14 +534,62 @@ 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));
}
}
private void doSave()
{
String store = storeFieldEdit.getText().toString();
String note = noteFieldEdit.getText().toString();
Date expiry = (Date) expiryField.getTag();
String cardId = cardIdFieldView.getText().toString();
String barcodeType = barcodeTypeField.getText().toString();
@@ -451,12 +621,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, 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, cardId, barcodeType, headingColorValue, 0);
}
db.setLoyaltyCardGroups(loyaltyCardId, selectedGroups);
@@ -487,7 +657,7 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
switch(id)
{
case android.R.id.home:
finish();
askBeforeQuitIfChanged();
break;
case R.id.action_delete:
@@ -532,39 +702,12 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
{
super.onActivityResult(requestCode, resultCode, intent);
String contents = null;
String format = null;
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent);
IntentResult result =
IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
if (result != null)
{
Log.i(TAG, "Received barcode information from capture");
contents = result.getContents();
format = result.getFormatName();
}
barcodeType = barcodeValues.format();
cardId = barcodeValues.content();
if(requestCode == SELECT_BARCODE_REQUEST && resultCode == Activity.RESULT_OK)
{
Log.i(TAG, "Received barcode information from typing it");
contents = intent.getStringExtra(BarcodeSelectorActivity.BARCODE_CONTENTS);
format = intent.getStringExtra(BarcodeSelectorActivity.BARCODE_FORMAT);
}
if(contents != null && contents.isEmpty() == false &&
format != null)
{
Log.i(TAG, "Read barcode id: " + contents);
Log.i(TAG, "Read format: " + format);
TextView cardIdView = findViewById(R.id.cardIdView);
cardIdView.setText(contents);
// Set special NO_BARCODE value to prevent onResume from overwriting it
barcodeTypeField.setText(format.isEmpty() ? getString(R.string.noBarcode) : format);
onResume();
}
onResume();
}
private void showBarcode() {
@@ -584,11 +727,7 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT < 16) {
barcodeImage.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
barcodeImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
barcodeImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
Log.d(TAG, "ImageView size now known");
new BarcodeImageWriterTask(barcodeImage, cardId, barcodeFormat).execute();
@@ -619,4 +758,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,18 +2,23 @@ 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;
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;
@@ -23,13 +28,20 @@ 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.text.DateFormat;
import java.util.List;
import protect.card_locker.preferences.Settings;
public class LoyaltyCardViewActivity extends AppCompatActivity
@@ -37,11 +49,18 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
private static final String TAG = "Catima";
TextView cardIdFieldView;
BottomSheetBehavior behavior;
View bottomSheet;
ImageView bottomSheetButton;
TextView noteView;
View noteViewDivider;
TextView groupsView;
TextView expiryView;
TextView storeName;
ImageButton maximizeButton;
ImageView barcodeImage;
ImageButton minimizeButton;
View collapsingToolbarLayout;
AppBarLayout appBarLayout;
int loyaltyCardId;
LoyaltyCard loyaltyCard;
boolean rotationEnabled;
@@ -52,10 +71,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)
{
@@ -67,6 +90,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)
{
@@ -91,39 +115,101 @@ 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);
cardIdFieldView = findViewById(R.id.cardIdView);
bottomSheet = findViewById(R.id.bottom_sheet);
bottomSheetButton = findViewById(R.id.bottomSheetButton);
noteView = findViewById(R.id.noteView);
noteViewDivider = findViewById(R.id.noteViewDivider);
groupsView = findViewById(R.id.groupsView);
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() {
maximizeButton.setOnClickListener(v -> setFullscreen(true));
barcodeImage.setOnClickListener(view -> {
if (barcodeIsFullscreen)
{
setFullscreen(false);
}
else
{
setFullscreen(true);
}
});
minimizeButton.setOnClickListener(v -> setFullscreen(false));
editButton = findViewById(R.id.fabEdit);
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();
});
behavior = BottomSheetBehavior.from(bottomSheet);
behavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onClick(View view) {
if(barcodeIsFullscreen)
{
setFullscreen(false);
}
else
{
setFullscreen(true);
public void onStateChanged(@NonNull View bottomSheet, int newState) {
if (newState == BottomSheetBehavior.STATE_DRAGGING) {
editButton.hide();
} else if (newState == BottomSheetBehavior.STATE_EXPANDED) {
bottomSheetButton.setImageResource(R.drawable.ic_baseline_arrow_drop_down_24);
editButton.hide();
} else if (newState == BottomSheetBehavior.STATE_COLLAPSED) {
bottomSheetButton.setImageResource(R.drawable.ic_baseline_arrow_drop_up_24);
editButton.show();
}
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) { }
});
bottomSheetButton.setOnClickListener(v -> {
if (behavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
} else {
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
});
}
@@ -143,15 +229,6 @@ 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.
@@ -172,6 +249,8 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
return;
}
setupOrientation();
String formatString = loyaltyCard.barcodeType;
format = !formatString.isEmpty() ? BarcodeFormat.valueOf(formatString) : null;
cardIdString = loyaltyCard.cardId;
@@ -183,15 +262,49 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
if(loyaltyCard.note.length() > 0)
{
noteView.setVisibility(View.VISIBLE);
noteView.setText(loyaltyCard.note);
TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(noteView,
getResources().getInteger(R.integer.settings_card_note_min_font_size_sp)-1,
settings.getCardNoteFontSize(), 1, TypedValue.COMPLEX_UNIT_SP);
}
else
{
noteView.setVisibility(View.GONE);
noteViewDivider.setVisibility(View.GONE);
}
List<Group> loyaltyCardGroups = db.getLoyaltyCardGroups(loyaltyCardId);
if(loyaltyCardGroups.size() > 0) {
StringBuilder groupsString = new StringBuilder();
for (Group group : loyaltyCardGroups) {
groupsString.append(group._id);
groupsString.append(" ");
}
groupsView.setVisibility(View.VISIBLE);
groupsView.setText(getString(R.string.groupsList, groupsString.toString()));
}
else
{
groupsView.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);
@@ -208,6 +321,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
}
collapsingToolbarLayout.setBackgroundColor(backgroundHeaderColor);
appBarLayout.setBackgroundColor(backgroundHeaderColor);
int textColor;
if(Utils.needsDarkForeground(backgroundHeaderColor))
@@ -219,6 +333,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);
@@ -243,49 +358,31 @@ 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);
}
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();
}
});
}
@Override
@@ -297,7 +394,6 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
}
super.onBackPressed();
return;
}
@Override
@@ -374,7 +470,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)
@@ -392,6 +521,33 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
}
}
private void makeBottomSheetVisibleIfUseful()
{
if (noteView.getVisibility() == View.VISIBLE || groupsView.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.
*
@@ -401,10 +557,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)
@@ -412,55 +574,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

@@ -4,14 +4,11 @@ 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.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.database.Cursor;
import android.os.Bundle;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SearchView;
import androidx.appcompat.widget.Toolbar;
@@ -23,24 +20,19 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.webkit.WebView;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.tabs.TabLayout;
import com.google.common.collect.ImmutableMap;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import protect.card_locker.preferences.SettingsActivity;
public class MainActivity extends AppCompatActivity implements GestureDetector.OnGestureListener
{
private static final String TAG = "Catima";
private static final int MAIN_REQUEST_CODE = 1;
private Menu menu;
private GestureDetector gestureDetector;
@@ -63,6 +55,14 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
public void onTabSelected(TabLayout.Tab tab) {
selectedTab = tab.getPosition();
updateLoyaltyCardList(filter, tab.getTag());
// Store active tab in Shared Preference to restore next app launch
SharedPreferences activeTabPref = getApplicationContext().getSharedPreferences(
getString(R.string.sharedpreference_active_tab),
Context.MODE_PRIVATE);
SharedPreferences.Editor activeTabPrefEditor = activeTabPref.edit();
activeTabPrefEditor.putInt(getString(R.string.sharedpreference_active_tab), selectedTab);
activeTabPrefEditor.apply();
}
@Override
@@ -107,39 +107,46 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
}
}
// Start of active tab logic
TabLayout groupsTabLayout = findViewById(R.id.groups);
boolean hasReset = updateTabGroups(groupsTabLayout);
updateTabGroups(groupsTabLayout);
// Restore active tab from Shared Preference
SharedPreferences activeTabPref = getApplicationContext().getSharedPreferences(
getString(R.string.sharedpreference_active_tab),
Context.MODE_PRIVATE);
selectedTab = activeTabPref.getInt(getString(R.string.sharedpreference_active_tab), 0);
Object group = null;
if (groupsTabLayout.getTabCount() != 0) {
TabLayout.Tab tab = groupsTabLayout.getTabAt(0);
if (!hasReset) {
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);
// End of active tab logic
FloatingActionButton addButton = findViewById(R.id.fabAdd);
addButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
startActivityForResult(i, MAIN_REQUEST_CODE);
Intent i = new Intent(getApplicationContext(), ScanActivity.class);
startActivityForResult(i, Utils.BARCODE_SCAN);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
if (requestCode == MAIN_REQUEST_CODE)
{
if (requestCode == Utils.MAIN_REQUEST) {
// We're coming back from another view so clear the search
// We only do this now to prevent a flash of all entries right after picking one
filter = "";
@@ -151,6 +158,19 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
// In case the theme changed
recreate();
return;
}
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent);
if(!barcodeValues.isEmpty()) {
Intent newIntent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
Bundle newBundle = new Bundle();
newBundle.putString("barcodeType", barcodeValues.format());
newBundle.putString("cardId", barcodeValues.content());
newIntent.putExtras(newBundle);
startActivity(newIntent);
}
}
@@ -236,12 +256,12 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
ShortcutHelper.updateShortcuts(MainActivity.this, loyaltyCard, i);
startActivityForResult(i, MAIN_REQUEST_CODE);
startActivityForResult(i, Utils.MAIN_REQUEST);
}
});
}
public boolean updateTabGroups(TabLayout groupsTabLayout)
public void updateTabGroups(TabLayout groupsTabLayout)
{
final DBHelper db = new DBHelper(this);
@@ -250,42 +270,24 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
if (newGroups.size() == 0) {
groupsTabLayout.removeAllTabs();
groupsTabLayout.setVisibility(View.GONE);
return true;
return;
}
// -1 because there is an "All" tab
boolean isChanged = groupsTabLayout.getTabCount() - 1 != newGroups.size();
groupsTabLayout.removeAllTabs();
if (!isChanged) {
for (int i = 0; i < newGroups.size(); i++) {
if (!((Group) groupsTabLayout.getTabAt(i + 1).getTag())._id.equals(newGroups.get(i)._id)) {
isChanged = true;
break;
}
}
TabLayout.Tab allTab = groupsTabLayout.newTab();
allTab.setText(R.string.all);
allTab.setTag(null);
groupsTabLayout.addTab(allTab, false);
for (Group group : newGroups) {
TabLayout.Tab tab = groupsTabLayout.newTab();
tab.setText(group._id);
tab.setTag(group);
groupsTabLayout.addTab(tab, false);
}
if (isChanged) {
groupsTabLayout.removeAllTabs();
TabLayout.Tab allTab = groupsTabLayout.newTab();
allTab.setText(R.string.all);
allTab.setTag(null);
groupsTabLayout.addTab(allTab);
for (Group group : newGroups) {
TabLayout.Tab tab = groupsTabLayout.newTab();
tab.setText(group._id);
tab.setTag(group);
groupsTabLayout.addTab(tab);
}
groupsTabLayout.setVisibility(View.VISIBLE);
return true;
}
return false;
groupsTabLayout.setVisibility(View.VISIBLE);
}
@Override
@@ -359,8 +361,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;
}
});
@@ -377,129 +384,34 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
if (id == R.id.action_manage_groups)
{
Intent i = new Intent(getApplicationContext(), ManageGroupsActivity.class);
startActivityForResult(i, MAIN_REQUEST_CODE);
startActivityForResult(i, Utils.MAIN_REQUEST);
return true;
}
if(id == R.id.action_import_export)
{
Intent i = new Intent(getApplicationContext(), ImportExportActivity.class);
startActivityForResult(i, MAIN_REQUEST_CODE);
startActivityForResult(i, Utils.MAIN_REQUEST);
return true;
}
if(id == R.id.action_settings)
{
Intent i = new Intent(getApplicationContext(), SettingsActivity.class);
startActivityForResult(i, MAIN_REQUEST_CODE);
startActivityForResult(i, Utils.MAIN_REQUEST);
return true;
}
if(id == R.id.action_about)
{
displayAboutDialog();
Intent i = new Intent(getApplicationContext(), AboutActivity.class);
startActivityForResult(i, Utils.MAIN_REQUEST);
return true;
}
return super.onOptionsItemSelected(item);
}
private void displayAboutDialog()
{
final Map<String, String> USED_LIBRARIES = new ImmutableMap.Builder<String, String>()
.put("Commons CSV", "https://commons.apache.org/proper/commons-csv/")
.put("Guava", "https://github.com/google/guava")
.put("ZXing", "https://github.com/zxing/zxing")
.put("ZXing Android Embedded", "https://github.com/journeyapps/zxing-android-embedded")
.put("AppIntro", "https://github.com/apl-devs/AppIntro")
.put("Color Picker", "https://github.com/jaredrummler/ColorPicker")
.put("VNTNumberPickerPreference", "https://github.com/vanniktech/VNTNumberPickerPreference")
.build();
final Map<String, String> USED_ASSETS = ImmutableMap.of
(
"Save by Bernar Novalyi", "https://thenounproject.com/term/save/716011"
);
StringBuilder libs = new StringBuilder().append("<ul>");
for (Map.Entry<String, String> entry : USED_LIBRARIES.entrySet())
{
libs.append("<li><a href=\"").append(entry.getValue()).append("\">").append(entry.getKey()).append("</a></li>");
}
libs.append("</ul>");
StringBuilder resources = new StringBuilder().append("<ul>");
for (Map.Entry<String, String> entry : USED_ASSETS.entrySet())
{
resources.append("<li><a href=\"").append(entry.getValue()).append("\">").append(entry.getKey()).append("</a></li>");
}
resources.append("</ul>");
String appName = getString(R.string.app_name);
int year = Calendar.getInstance().get(Calendar.YEAR);
String version = "?";
try
{
PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), 0);
version = pi.versionName;
}
catch (PackageManager.NameNotFoundException e)
{
Log.w(TAG, "Package name not found", e);
}
WebView wv = new WebView(this);
// Set CSS for dark mode if dark mode
String css = "";
if(isDarkModeEnabled(this))
{
css = "<style>body {color:white; background-color:black;}</style>";
}
String html =
"<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />" +
css +
"<h1>" +
String.format(getString(R.string.about_title_fmt),
"<a href=\"" + getString(R.string.app_webpage_url)) + "\">" +
appName +
"</a>" +
"</h1><p>" +
appName +
" " +
String.format(getString(R.string.debug_version_fmt), version) +
"</p><p>" +
String.format(getString(R.string.app_revision_fmt),
"<a href=\"" + getString(R.string.app_revision_url) + "\">" +
"GitHub" +
"</a>") +
"</p><hr/><p>" +
String.format(getString(R.string.app_copyright_fmt), year) +
"</p><p>" +
getString(R.string.app_copyright_old) +
"</p><hr/><p>" +
getString(R.string.app_license) +
"</p><hr/><p>" +
String.format(getString(R.string.app_libraries), appName, libs.toString()) +
"</p><hr/><p>" +
String.format(getString(R.string.app_resources), appName, resources.toString());
wv.loadDataWithBaseURL("file:///android_res/drawable/", html, "text/html", "utf-8", null);
new AlertDialog.Builder(this)
.setView(wv)
.setCancelable(true)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
}
})
.show();
}
protected static boolean isDarkModeEnabled(Context inputContext)
{
Configuration config = inputContext.getResources().getConfiguration();
@@ -536,6 +448,11 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
Log.d(TAG, "On fling");
// Don't swipe if we have too much vertical movement
if (Math.abs(velocityY) > (0.75 * Math.abs(velocityX))) {
return false;
}
TabLayout groupsTabLayout = findViewById(R.id.groups);
if (groupsTabLayout.getTabCount() < 2) {
return false;

View File

@@ -1,11 +1,14 @@
package protect.card_locker;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.Bundle;
import android.text.InputType;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
@@ -13,6 +16,8 @@ import android.widget.TextView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.List;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
@@ -22,7 +27,6 @@ public class ManageGroupsActivity extends AppCompatActivity
{
private static final String TAG = "Catima";
private AlertDialog newGroupDialog;
private final DBHelper db = new DBHelper(this);
@Override
@@ -38,8 +42,6 @@ public class ManageGroupsActivity extends AppCompatActivity
actionBar.setDisplayHomeAsUpEnabled(true);
}
newGroupDialog = createNewGroupDialog();
updateGroupList();
}
@@ -53,7 +55,7 @@ public class ManageGroupsActivity extends AppCompatActivity
addButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
newGroupDialog.show();
createGroup();
}
});
}
@@ -88,6 +90,16 @@ public class ManageGroupsActivity extends AppCompatActivity
registerForContextMenu(groupList);
}
private void invalidateHomescreenActiveTab()
{
SharedPreferences activeTabPref = getApplicationContext().getSharedPreferences(
getString(R.string.sharedpreference_active_tab),
Context.MODE_PRIVATE);
SharedPreferences.Editor activeTabPrefEditor = activeTabPref.edit();
activeTabPrefEditor.putInt(getString(R.string.sharedpreference_active_tab), 0);
activeTabPrefEditor.apply();
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
@@ -100,12 +112,16 @@ public class ManageGroupsActivity extends AppCompatActivity
return super.onOptionsItemSelected(item);
}
public void moveGroupUp(View view) {
moveGroup(view, true);
}
public void moveGroupDown(View view) {
moveGroup(view, false);
}
public void editGroup(View view) {
LinearLayout parentRow = (LinearLayout) view.getParent();
TextView groupNameTextView = (TextView) parentRow.findViewById(R.id.name);
final String groupName = (String) groupNameTextView.getText();
final String groupName = getGroupname(view);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.enter_group_name);
@@ -129,14 +145,12 @@ public class ManageGroupsActivity extends AppCompatActivity
});
AlertDialog dialog = builder.create();
dialog.show();
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
input.requestFocus();
}
public void deleteGroup(View view) {
LinearLayout parentRow = (LinearLayout) view.getParent();
TextView groupNameTextView = (TextView) parentRow.findViewById(R.id.name);
final String groupName = (String) groupNameTextView.getText();
final String groupName = getGroupname(view);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.deleteConfirmationGroup);
@@ -147,6 +161,8 @@ public class ManageGroupsActivity extends AppCompatActivity
public void onClick(DialogInterface dialog, int which) {
db.deleteGroup(groupName);
updateGroupList();
// Delete may change ordering, so invalidate
invalidateHomescreenActiveTab();
}
});
builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
@@ -159,7 +175,7 @@ public class ManageGroupsActivity extends AppCompatActivity
dialog.show();
}
private AlertDialog createNewGroupDialog() {
private void createGroup() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.enter_group_name);
final EditText input = new EditText(this);
@@ -180,7 +196,60 @@ public class ManageGroupsActivity extends AppCompatActivity
}
});
AlertDialog dialog = builder.create();
dialog.show();
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
input.requestFocus();
}
return dialog;
private String getGroupname(View view) {
LinearLayout parentRow = (LinearLayout) view.getParent().getParent();
TextView groupNameTextView = parentRow.findViewById(R.id.name);
return (String) groupNameTextView.getText();
}
private void moveGroup(View view, boolean up) {
final String groupName = getGroupname(view);
List<Group> groups = db.getGroups();
int currentIndex = -1;
Integer newIndex;
// Get current index in group list
for (int i = 0; i < groups.size(); i++) {
if (groups.get(i)._id.equals(groupName)) {
currentIndex = i;
break;
}
}
if (currentIndex == -1) {
throw new IndexOutOfBoundsException();
}
// Reinsert group in correct position
if (up) {
newIndex = currentIndex - 1;
} else {
newIndex = currentIndex + 1;
}
// Don't try to move out of bounds
if (newIndex < 0 || newIndex >= groups.size()) {
return;
}
Group group = groups.remove(currentIndex);
groups.add(newIndex, group);
// Update database
db.reorderGroups(groups);
// Update UI
updateGroupList();
// Ordering may have changed, so invalidate
invalidateHomescreenActiveTab();
}
}

View File

@@ -31,32 +31,23 @@ public class MultiFormatImporter
break;
}
if(importer != null)
if (importer != null)
{
try
{
importer.importData(db, input);
return true;
}
catch(IOException e)
catch(IOException | FormatException | InterruptedException 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,157 @@
package protect.card_locker;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.View;
import com.google.zxing.ResultPoint;
import com.google.zxing.client.android.Intents;
import com.journeyapps.barcodescanner.BarcodeCallback;
import com.journeyapps.barcodescanner.BarcodeResult;
import com.journeyapps.barcodescanner.CaptureManager;
import com.journeyapps.barcodescanner.DecoratedBarcodeView;
import java.util.List;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
/**
* Custom Scannner Activity extending from Activity to display a custom layout form scanner view.
*
* Based on https://github.com/journeyapps/zxing-android-embedded/blob/0fdfbce9fb3285e985bad9971c5f7c0a7a334e7b/sample/src/main/java/example/zxing/CustomScannerActivity.java
* originally licensed under Apache 2.0
*/
public class ScanActivity extends AppCompatActivity {
private static final String TAG = "Catima";
private CaptureManager capture;
private DecoratedBarcodeView barcodeScannerView;
private String cardId;
private void extractIntentFields(Intent intent) {
final Bundle b = intent.getExtras();
cardId = b != null ? b.getString("cardId") : null;
Log.d(TAG, "Scan activity: id=" + cardId);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.scan_activity);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
{
actionBar.setDisplayHomeAsUpEnabled(true);
}
extractIntentFields(getIntent());
barcodeScannerView = findViewById(R.id.zxing_barcode_scanner);
// Even though we do the actual decoding with the barcodeScannerView
// CaptureManager needs to be running to show the camera and scanning bar
capture = new CaptureManager(this, barcodeScannerView);
Intent captureIntent = new Intent();
Bundle captureIntentBundle = new Bundle();
captureIntentBundle.putBoolean(Intents.Scan.BEEP_ENABLED, false);
captureIntent.putExtras(captureIntentBundle);
capture.initializeFromIntent(captureIntent, savedInstanceState);
barcodeScannerView.decodeSingle(new BarcodeCallback() {
@Override
public void barcodeResult(BarcodeResult result) {
Intent scanResult = new Intent();
Bundle scanResultBundle = new Bundle();
scanResultBundle.putString(BarcodeSelectorActivity.BARCODE_CONTENTS, result.getText());
scanResultBundle.putString(BarcodeSelectorActivity.BARCODE_FORMAT, result.getBarcodeFormat().toString());
scanResult.putExtras(scanResultBundle);
ScanActivity.this.setResult(RESULT_OK, scanResult);
finish();
}
@Override
public void possibleResultPoints(List<ResultPoint> resultPoints) {
}
});
}
@Override
protected void onResume() {
super.onResume();
capture.onResume();
}
@Override
protected void onPause() {
super.onPause();
capture.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
capture.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
capture.onSaveInstanceState(outState);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
return barcodeScannerView.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
if (item.getItemId() == android.R.id.home)
{
setResult(Activity.RESULT_CANCELED);
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent)
{
super.onActivityResult(requestCode, resultCode, intent);
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent);
if (!barcodeValues.isEmpty()) {
Intent manualResult = new Intent();
Bundle manualResultBundle = new Bundle();
manualResultBundle.putString(BarcodeSelectorActivity.BARCODE_CONTENTS, barcodeValues.content());
manualResultBundle.putString(BarcodeSelectorActivity.BARCODE_FORMAT, barcodeValues.format());
manualResult.putExtras(manualResultBundle);
ScanActivity.this.setResult(RESULT_OK, manualResult);
finish();
}
}
public void addManually(View view) {
Intent i = new Intent(getApplicationContext(), BarcodeSelectorActivity.class);
if (cardId != null) {
final Bundle b = new Bundle();
b.putString("initialCardId", cardId);
i.putExtras(b);
}
startActivityForResult(i, Utils.SELECT_BARCODE_REQUEST);
}
}

View File

@@ -1,11 +1,25 @@
package protect.card_locker;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.util.Log;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import androidx.core.graphics.ColorUtils;
public class Utils {
private static final String TAG = "Catima";
// Activity request codes
public static final int MAIN_REQUEST = 1;
public static final int SELECT_BARCODE_REQUEST = 2;
public static final int BARCODE_SCAN = 3;
static final double LUMINANCE_MIDPOINT = 0.5;
static public LetterBitmap generateIcon(Context context, String store, Integer backgroundColor) {
@@ -27,4 +41,40 @@ public class Utils {
static public boolean needsDarkForeground(Integer backgroundColor) {
return ColorUtils.calculateLuminance(backgroundColor) > LUMINANCE_MIDPOINT;
}
static public BarcodeValues parseSetBarcodeActivityResult(int requestCode, int resultCode, Intent intent) {
String contents = null;
String format = null;
if (resultCode == Activity.RESULT_OK)
{
if (requestCode == Utils.BARCODE_SCAN) {
Log.i(TAG, "Received barcode information from camera");
} else if (requestCode == Utils.SELECT_BARCODE_REQUEST) {
Log.i(TAG, "Received barcode information from typing it");
} else {
return new BarcodeValues(null, null);
}
contents = intent.getStringExtra(BarcodeSelectorActivity.BARCODE_CONTENTS);
format = intent.getStringExtra(BarcodeSelectorActivity.BARCODE_FORMAT);
}
Log.i(TAG, "Read barcode id: " + contents);
Log.i(TAG, "Read format: " + format);
return new BarcodeValues(format, contents);
}
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());
}
}

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;
@@ -81,11 +81,6 @@ public class Settings
return getInt(R.string.settings_key_card_id_font_size, R.integer.settings_card_id_font_size_sp);
}
public int getCardNoteFontSize()
{
return getInt(R.string.settings_key_card_note_font_size, R.integer.settings_card_note_font_size_sp);
}
public boolean useMaxBrightnessDisplayingBarcode()
{
return getBoolean(R.string.settings_key_display_barcode_max_brightness, 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 233 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 306 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 303 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 582 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 554 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 336 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 439 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 368 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 410 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 397 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 234 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 299 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 303 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 225 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 354 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 343 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 239 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 783 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 758 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 421 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 564 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 466 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 398 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 512 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 494 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 829 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 809 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 606 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 243 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 328 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 665 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 651 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 355 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 758 B

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="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector>

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,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
</vector>

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="M7,10l5,5 5,-5z"/>
</vector>

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="M7,14l5,-5 5,5z"/>
</vector>

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

@@ -0,0 +1,6 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
<path android:fillColor="@android:color/white" android:pathData="M9,2L7.17,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2L9,2zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"/>
</vector>

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="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
</vector>

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="M10,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2h-8l-2,-2z"/>
</vector>

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="M9,3L5,6.99h3L8,14h2L10,6.99h3L9,3zM16,17.01L16,10h-2v7.01h-3L15,21l4,-3.99h-3z"/>
</vector>

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="M12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6h1.9c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM18,20L6,20L6,10h12v10z"/>
</vector>

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="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z"/>
</vector>

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