Compare commits

...

781 Commits
v0.7 ... v1.6.1

Author SHA1 Message Date
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
Sylvia van Os
979d88d2e6 Release 1.2.0 2020-11-17 13:52:57 +01:00
Sylvia van Os
67b961c672 Fix crash on rendering card with no header colour 2020-11-17 13:51:44 +01:00
Sylvia van Os
ed62ad321b Add support for swiping between groups in main view 2020-11-17 00:00:09 +01:00
Sylvia van Os
251bdcd500 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker into master 2020-11-16 20:01:35 +01:00
Sylvia van Os
16f9b8f3a5 Update deprecated requirements 2020-11-16 19:59:09 +01:00
Sylvia van Os
6be52f9b31 Merge pull request #98 from weblate/weblate-catima-catima
Translations update from Weblate
2020-11-16 17:31:37 +01:00
Airat
d8b901c3a0 Translated using Weblate (Russian)
Currently translated at 89.6% (104 of 116 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2020-11-16 17:28:49 +01:00
Franciszek Stefan
ed52d2d666 Translated using Weblate (Polish)
Currently translated at 100.0% (116 of 116 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/pl/
2020-11-16 17:28:48 +01:00
Sylvia van Os
ff6b62c3f0 Merge pull request #97 from x-jokay/feature/update-name
Update csv filename
2020-11-13 20:21:22 +01:00
D. Domig
da8b15a985 Update csv filename 2020-11-13 20:20:05 +01:00
Sylvia van Os
eccf994379 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker into master 2020-11-13 19:08:33 +01:00
Sylvia van Os
66622817cc Make more findable 2020-11-13 19:08:11 +01:00
Sylvia van Os
3fcfe8afe1 Merge pull request #96 from x-jokay/feature/fix-markdown-issues
Fix some markdownlint issues
2020-11-13 16:00:56 +01:00
D. Domig
2c5dae37fd Fix some markdownlint issues 2020-11-13 15:54:53 +01:00
Sylvia van Os
295e63981d Merge pull request #95 from weblate/weblate-catima-catima
Translations update from Weblate
2020-11-12 07:06:51 +01:00
Allan Nordhøy
9ac0ee53b5 Translated using Weblate (Norwegian Bokmål)
Currently translated at 98.2% (114 of 116 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2020-11-12 00:28:38 +01:00
Sylvia van Os
7e1dbf905a Release 1.1.0 2020-11-11 18:35:01 +01:00
Sylvia van Os
63de6f50ed Merge pull request #92 from TheLastProject/feature/iconFriendlyEditUI
Icon-friendly edit UI
2020-11-10 22:50:03 +01:00
Sylvia van Os
6b24f56c79 Fix unit tests 2020-11-10 22:25:16 +01:00
Sylvia van Os
e601d1a659 Editable card ID and barcode type 2020-11-10 22:05:53 +01:00
Sylvia van Os
1641631bdc Get rid of headingStoreTextColorValue 2020-11-10 19:11:57 +01:00
Sylvia van Os
91c2efbdfc Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker into feature/iconFriendlyEditUI 2020-11-10 18:28:56 +01:00
Sylvia van Os
462f77e26f Merge pull request #94 from weblate/weblate-catima-catima
Translations update from Weblate
2020-11-10 18:28:17 +01:00
Sylvia van Os
0aa1f8b7dd 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/
2020-11-10 18:27:07 +01:00
Allan Nordhøy
0f9509c0e5 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/
2020-11-10 18:27:07 +01:00
Sylvia van Os
95d497c7bd Fix build 2020-11-10 18:26:54 +01:00
Sylvia van Os
d760fbd3cb Merge pull request #93 from comradekingu/patch-3
Strings reworked
2020-11-10 18:00:07 +01:00
Sylvia van Os
ad82492f63 Update app/src/main/res/values/strings.xml
Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
2020-11-10 17:59:46 +01:00
Allan Nordhøy
e84a23c58e Spelling: Try changing your search 2020-11-10 12:14:17 +00:00
Allan Nordhøy
67cbb34425 Spelling: Didn't find anything 2020-11-10 12:09:16 +00:00
Allan Nordhøy
0c4fed5c50 Spelling: Grouped 2020-11-10 12:03:28 +00:00
Allan Nordhøy
e1a72a948d Requested changes made 2020-11-10 12:01:58 +00:00
Allan Nordhøy
50d9205aef Spelling: Font size for card titles 2020-11-10 09:18:25 +00:00
Allan Nordhøy
9093c88e23 Strings reworked 2020-11-10 09:17:16 +00:00
Sylvia van Os
3c793fe54f Fix logic and unit tests 2020-11-09 22:31:35 +01:00
Sylvia van Os
a8960eb506 Initial edit UI changes 2020-11-09 21:34:29 +01:00
Sylvia van Os
7913e95abf Merge pull request #91 from weblate/weblate-catima-catima
Translations update from Weblate
2020-11-09 18:12:21 +01:00
Sylvia van Os
fac7b76798 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/
2020-11-09 18:10:03 +01:00
mondstern
aa9c4b81b4 Translated using Weblate (Norwegian Bokmål)
Currently translated at 99.1% (114 of 115 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2020-11-09 18:10:03 +01:00
J. Lavoie
5b3fc845f9 Translated using Weblate (Italian)
Currently translated at 100.0% (115 of 115 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2020-11-09 18:10:03 +01:00
J. Lavoie
c8c7498dc9 Translated using Weblate (French)
Currently translated at 100.0% (115 of 115 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2020-11-09 18:10:03 +01:00
J. Lavoie
712763969c Translated using Weblate (German)
Currently translated at 100.0% (115 of 115 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2020-11-09 18:10:03 +01:00
Vancha March
d3f1e0d80b 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/
2020-11-09 18:10:03 +01:00
mondstern
e8ab92d8c1 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/
2020-11-09 18:10:03 +01:00
Sylvia van Os
e0e4ba0012 Fix build 2020-11-08 20:09:26 +01:00
Sylvia van Os
cb4cede2da Rename nb_NO for consistency 2020-11-08 19:23:44 +01:00
Sylvia van Os
7bfdb3a8c3 Merge pull request #87 from weblate/weblate-catima-catima
Translations update from Weblate
2020-11-08 05:46:41 +01:00
Allan Nordhøy
c4210da1a5 Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/nb_NO/
2020-11-08 01:26:54 +01:00
Allan Nordhøy
e082f45333 Translated using Weblate (Norwegian Bokmål)
Currently translated at 93.9% (108 of 115 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2020-11-08 01:26:54 +01:00
Sylvia van Os
d10c8668b6 Fix crash detected by Google Play Console pre-launch report 2020-11-07 11:34:35 +01:00
Sylvia van Os
a766a25f6a Fix unit test 2020-11-06 21:13:57 +01:00
Sylvia van Os
daa2f4137c Forgot to update CHANGELOG 2020-11-06 20:28:52 +01:00
Sylvia van Os
e61d29417c Release Catima 1.0 2020-11-06 20:25:46 +01:00
Sylvia van Os
cc1be04cf7 Update CHANGELOG 2020-11-06 20:19:12 +01:00
Sylvia van Os
e0ed5bd11c Update screenshots 2020-11-06 20:18:28 +01:00
Sylvia van Os
1bda5410d8 Fix max db version 2020-11-06 20:17:57 +01:00
Sylvia van Os
4bbeb27714 Feature/groups (#71)
* Basic group management

* Assign cards to groups

* Fix lint

* Fix findbugs 'dodgy code'

* Group name as unique key

* More group tests

* Import/export groups

* Implement group renaming and deleting

* Fix findBugs

* Fix chip marking in edit activity

* Group import/export tests

* Fix some state bugs

* Some last fixes

* Remove redundant if statement

* Fix findBugs

* Deduplicate code

* Cleanup

* Fix groups not showing up with new card

* Fix capture and enter button touching

* Fix dialog button look
2020-11-06 18:12:06 +01:00
Sylvia van Os
c04f2c6785 Use longer en-US title 2020-11-06 18:09:52 +01:00
Sylvia van Os
70fa8ff11c Merge pull request #84 from TangentFoxy/patch-1
Fix typo in Import/Export Activity
2020-11-06 09:00:50 +01:00
Rose Liverman
1637895866 Fix typo in Import/Export Activity 2020-11-05 22:22:24 -07:00
Sylvia van Os
6e0d48e42d Update CHANGELOG 2020-11-05 21:38:34 +01:00
Sylvia van Os
9e71a0d86c Fix fastlane 2020-11-05 20:29:37 +01:00
Sylvia van Os
20365e31be Merge pull request #81 from weblate/weblate-catima-catima
Translations update from Weblate
2020-11-03 19:28:17 +01:00
J. Lavoie
fb29adbb22 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-11-03 19:26:48 +01:00
J. Lavoie
c4f0ab37a9 Translated using Weblate (Italian)
Currently translated at 99.0% (108 of 109 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2020-11-03 19:26:48 +01:00
J. Lavoie
038def0114 Translated using Weblate (French)
Currently translated at 99.0% (108 of 109 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2020-11-03 19:26:48 +01:00
Sylvia van Os
1f962d88c0 Merge pull request #80 from arshbeerSingh/master
Fix Issue #56
2020-11-03 18:12:00 +01:00
Arshbeer Singh
d897f3b137 Fix Issue #56 2020-11-03 03:31:04 -08:00
Sylvia van Os
dba3692521 Merge pull request #79 from IzzySoft/fastlane
Fastlane: add German summary/description
2020-11-02 22:37:26 +01:00
Izzy
7ab6ec5f75 Fastlane: add German summary/description 2020-11-02 21:52:00 +01:00
Sylvia van Os
0d6e002324 Fix README 2020-11-02 18:58:05 +01:00
Sylvia van Os
9b2748cf61 Migrate to Fastlane 2020-11-02 18:00:43 +01:00
Sylvia van Os
e505bce0ba Merge pull request #78 from weblate/weblate-catima-catima
Translations update from Weblate
2020-11-02 11:07:23 +01:00
Allan Nordhøy
e25d8910c5 Translated using Weblate (Norwegian Bokmål)
Currently translated at 96.3% (105 of 109 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2020-11-01 14:26:48 +01:00
Artem
3e79001e84 Translated using Weblate (Russian)
Currently translated at 98.1% (104 of 106 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2020-11-01 14:26:48 +01:00
J. Lavoie
4b432ea94c Translated using Weblate (Italian)
Currently translated at 100.0% (106 of 106 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2020-11-01 14:26:48 +01:00
J. Lavoie
78752e1cf2 Translated using Weblate (French)
Currently translated at 100.0% (106 of 106 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2020-11-01 14:26:48 +01:00
J. Lavoie
129608befd Translated using Weblate (German)
Currently translated at 100.0% (106 of 106 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2020-11-01 14:26:48 +01:00
Sylvia van Os
0c2bf8b8a5 Add weblate link and button 2020-10-30 23:03:24 +01:00
Sylvia van Os
6bc08a21e8 Merge pull request #76 from TheLastProject/create-pull-request/patch-1603996252
Compressed Images
2020-10-29 21:40:23 +01:00
Sylvia van Os
fa5d094197 Add logo to migration guide 2020-10-29 20:04:56 +01:00
Sylvia van Os
c988276ced Release v0.29 2020-10-29 19:31:42 +01:00
TheLastProject
7d5f79b7e1 Compressed Images 2020-10-29 18:30:52 +00:00
Sylvia van Os
67ca1ad505 Fix README 2020-10-29 19:30:37 +01:00
Sylvia van Os
210a0c564a Add logo 2020-10-29 19:29:39 +01:00
Sylvia van Os
0a3df441d3 Merge pull request #75 from arshbeerSingh/master
Fix Issue #72
2020-10-28 18:49:07 +01:00
Arshbeer Singh
3a97d6b191 Fix Issue #72 2020-10-27 13:51:20 -07:00
Sylvia van Os
7f5ba2c133 Merge pull request #73 from TheLastProject/create-pull-request/patch-1603737257
Compressed Images
2020-10-26 19:58:26 +01:00
Sylvia van Os
ccb4c3c5a2 Revert "Optimize current images"
This reverts commit a160583b30.
2020-10-26 19:57:57 +01:00
TheLastProject
e8b436a696 Compressed Images 2020-10-26 18:34:16 +00:00
Sylvia van Os
a160583b30 Optimize current images 2020-10-26 19:33:12 +01:00
Sylvia van Os
6c24b64837 Update CHANGELOG 2020-10-26 19:28:19 +01:00
Sylvia van Os
04b824257b Add https://github.com/calibreapp/image-actions 2020-10-26 19:26:28 +01:00
Sylvia van Os
70251d7ea8 Fix app bypassing Android orientation confirmation 2020-10-26 18:48:57 +01:00
Sylvia van Os
fa4ab0ba59 Fix lint 2020-10-25 23:01:39 +01:00
Sylvia van Os
8880e35a7a Fix some merge issues 2020-10-25 18:14:35 +01:00
t351206
c9f3054682 New feature: Favorite cards (#70) 2020-10-25 18:01:26 +01:00
Sylvia van Os
537141fd70 Fix build 2020-10-25 18:00:18 +01:00
Sylvia van Os
f027b1f1b8 Merge remote-tracking branch 'weblate/master' 2020-10-25 17:55:54 +01:00
Allan Nordhøy
44d7c8520d Translated using Weblate (Norwegian Bokmål)
Currently translated at 96.6% (114 of 118 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2020-10-25 17:26:45 +01:00
J. Lavoie
9d9f73fbba Translated using Weblate (Italian)
Currently translated at 100.0% (118 of 118 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2020-10-25 17:26:44 +01:00
J. Lavoie
5c74517e13 Translated using Weblate (French)
Currently translated at 100.0% (118 of 118 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2020-10-25 17:26:44 +01:00
J. Lavoie
ded2f003f6 Translated using Weblate (Spanish)
Currently translated at 100.0% (118 of 118 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/es/
2020-10-25 17:26:44 +01:00
Adolfo Jayme Barrientos
fd89caee64 Translated using Weblate (Spanish)
Currently translated at 100.0% (118 of 118 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/es/
2020-10-25 17:26:44 +01:00
J. Lavoie
3fd7991663 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-10-25 17:26:43 +01:00
Sylvia van Os
565449d17c Update CHANGELOG 2020-10-24 22:34:29 +02:00
Sylvia van Os
45f266cc93 Cleanup 2020-10-24 14:45:14 +02:00
Sylvia van Os
a29edd90ac Support importing cards shared from loyalty-card-locker 2020-10-24 14:27:33 +02:00
Sylvia van Os
799b82e199 Update androidx.appcompat:appcompat to 1.2.0 2020-10-24 14:17:30 +02:00
Sylvia van Os
91e2b7acdd Remove no longer existing activity 2020-10-24 14:10:27 +02:00
Sylvia van Os
e59fc1ea0a Migrate from Loyalty Card Keychain 2020-10-23 20:03:01 +02:00
Sylvia van Os
2926d0d716 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2020-10-23 20:02:01 +02:00
Sylvia van Os
bcc988ff2c Fix upstream name 2020-10-23 19:25:10 +02:00
Sylvia van Os
c03c02c6ea Fix build status badge 2020-10-23 15:32:48 +02:00
Sylvia van Os
25d2a5ec5d Update screenshots 2020-10-22 20:55:00 +02:00
Sylvia van Os
344f7a9bc8 Fix unit tests 2020-10-22 20:11:26 +02:00
Sylvia van Os
e32993ceb5 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2020-10-22 19:49:48 +02:00
Sylvia van Os
efae7bda4c Modernize UI with FABs 2020-10-22 19:36:00 +02:00
Sylvia van Os
1cbf90a596 Remove intro 2020-10-22 18:35:33 +02:00
Sylvia van Os
9d4529b06b Initial rebrand 2020-10-22 18:15:07 +02:00
Sylvia van Os
c471c35b82 Set theme jekyll-theme-cayman 2020-10-21 20:50:23 +02:00
Branden Archer
ccd848c0b8 Merge pull request #368 from TheLastProject/fix/367
Improve export/import dialog messages
2020-06-20 20:06:14 -07:00
Sylvia van Os
d8776da8b6 Improve export/import dialog messages 2020-06-16 21:47:39 +02:00
Branden Archer
9de6a2e0da Merge pull request #363 from brarcher/pre-v0.28
Update for v0.28
2020-03-09 22:31:54 -07:00
Branden Archer
9f1d5ad1b3 Update CHANGELOG for v0.28 2020-03-09 22:26:19 -07:00
Branden Archer
290abd9c56 Update for v0.28 2020-03-09 22:22:07 -07:00
Branden Archer
72a04b570a Merge pull request #362 from brarcher/translations
Update translations from Transifex
2020-03-09 22:21:28 -07:00
Branden Archer
1fccfff338 Update translations from Transifex 2020-03-09 22:16:54 -07:00
Branden Archer
d22f8cc5e9 Merge pull request #358 from brarcher/brarcher-readme
Mention hiatus in README
2020-03-08 22:32:37 -07:00
Branden Archer
7fc88cd5af Merge branch 'master' into brarcher-readme 2020-03-08 22:28:16 -07:00
Branden Archer
ab485991f1 Merge pull request #361 from brarcher/brarcher-patch-1
Delete .travis.yml
2020-03-08 22:27:34 -07:00
Branden Archer
e579168a03 Delete .travis.yml 2020-03-08 22:17:19 -07:00
Branden Archer
38fc515372 Merge pull request #360 from brarcher/github-action
Create android.yml
2020-03-08 22:15:55 -07:00
Branden Archer
aad074e29e Create android.yml
This creates a GitHub Action for building and
testing the project, as an attempt to replace
Travis-CI.
2020-03-08 22:10:54 -07:00
Branden Archer
4321bf5ac8 Merge pull request #352 from TheLastProject/feature/choose_export_location
Choose export location
2020-03-08 22:00:51 -07:00
Sylvia van Os
694e9b88d7 Fix translation inconsistency breaking Travis build 2020-03-08 20:52:38 +01:00
Sylvia van Os
6ba4804d9a Merge branch 'master' of ssh://github.com/brarcher/loyalty-card-locker into feature/choose_export_location 2020-03-08 18:10:54 +01:00
Branden Archer
288531dff0 Mention hiatus in README 2020-02-24 23:07:56 -08:00
Branden Archer
678f126403 Merge pull request #333 from sergiowalls/master
Display barcode format
2020-02-24 23:02:41 -08:00
Branden Archer
3ae496427a Merge branch 'master' into master 2020-02-24 22:51:31 -08:00
Branden Archer
046348612f Merge pull request #351 from TheLastProject/fix/fullscreen_leave_uncenter
Fix barcode centering when leaving fullscreen
2020-02-24 22:33:08 -08:00
Sylvia van Os
68bcd167a2 Merge branch 'master' into fix/fullscreen_leave_uncenter 2020-02-21 15:23:54 +01:00
Sylvia van Os
b7892df877 Merge branch 'master' into feature/choose_export_location 2020-02-21 15:21:50 +01:00
Sergio Paredes
bc63f3163c Merge branch 'master' into master 2020-02-04 15:25:13 +01:00
Branden Archer
2f562b432c Merge pull request #357 from brarcher/translations
Update nb_NO translations from Weblate
2020-02-01 16:41:19 -08:00
Branden Archer
b776f90b87 Update translations from Weblate 2020-02-01 16:13:14 -08:00
Sylvia van Os
6877828853 Make linter happy 2020-01-28 21:10:21 +01:00
Sylvia van Os
c94c3cb47e Choose export location 2020-01-28 21:01:33 +01:00
Sylvia van Os
454052638f Fix barcode centering 2020-01-28 17:32:57 +01:00
Sergio Paredes
1b060b26a5 Merge branch 'master' into master 2020-01-28 15:10:02 +01:00
Branden Archer
7970bb8a40 Merge pull request #350 from brarcher/pre-v0.27
Update for v0.27
2020-01-26 22:41:14 -08:00
Branden Archer
8fa868e99b Update for v0.27 2020-01-26 22:26:40 -08:00
Branden Archer
77a6cca57d Merge pull request #329 from TheLastProject/fix/dark_mode_unscannable
Fix dark mode scanning issues
2020-01-26 22:21:43 -08:00
Branden Archer
5ab3a62e16 Merge branch 'master' into fix/dark_mode_unscannable 2020-01-26 22:15:18 -08:00
Sergio
aa2ccac22b #308 Restore test check for barcode type 2020-01-27 00:53:55 +01:00
Sergio
e1f4ed65bb #308 Add missing cast from CharSequence to String 2020-01-27 00:53:55 +01:00
Sergio
72c1a57953 #308 Add new constructor for BarcodeImageWriterTask without associated TextView. Make barcode format selectable 2020-01-27 00:53:55 +01:00
Sergio
24061dae97 #308 Hide barcode format on add loyalty card view 2020-01-27 00:53:55 +01:00
Sergio
649daf43df Revert "#308 Display barcode format on display loyalty card view"
This reverts commit c194a9dd
2020-01-27 00:53:54 +01:00
Sergio
fb9cf7a393 #308 Display barcode format on edit loyalty card view 2020-01-27 00:53:54 +01:00
Sergio
0d67680e7f #308 Display barcode format on select barcode view 2020-01-27 00:53:54 +01:00
Sergio
b5d41b0ab2 #308 Display barcode format on display loyalty card view 2020-01-27 00:53:54 +01:00
Branden Archer
171ec1cd7e Merge pull request #348 from TheLastProject/feature/fullscreenBarcode
Move barcode to top on tap
2020-01-26 09:25:27 -08:00
Branden Archer
54ee93b0cb Merge branch 'master' into feature/fullscreenBarcode 2020-01-25 23:41:11 -08:00
Sylvia van Os
fd6c66a86a Improve documentation 2020-01-21 20:12:15 +01:00
Branden Archer
91044fdc87 Merge pull request #346 from TheLastProject/fix/swap_import_buttons
Fix swapped import buttons
2020-01-20 22:38:08 -08:00
Sylvia van Os
3600065ef8 Robolectric crashes if activity recreates 2020-01-19 19:55:08 +01:00
Sylvia van Os
4a7e8b6eba Reset state on paused to prevent layout glitches 2020-01-19 19:07:06 +01:00
Sylvia van Os
2c12c312e3 Add unit tests 2020-01-19 17:46:50 +01:00
Sylvia van Os
f8943af402 Tap barcode to align to top 2020-01-19 16:45:17 +01:00
Sylvia van Os
3990992f68 Fix swapped import buttons 2020-01-18 13:37:47 +01:00
Branden Archer
d2ac0dacda Merge pull request #344 from brarcher/pre-v0.26.1
Update for v0.26.1
2020-01-09 20:35:20 -08:00
Branden Archer
1a35150677 Update CHANGELOG for v0.26.1 2020-01-09 19:45:08 -08:00
Branden Archer
bf48e394e9 Update app for v0.26.1 2020-01-09 19:45:08 -08:00
Branden Archer
003aba52af Merge pull request #343 from TheLastProject/fix/sharing_npe
Fix NPE when sharing cards without header values
2020-01-09 19:22:00 -08:00
Sylvia van Os
aad98ba55b Fix NPE when sharing cards without header values 2020-01-09 20:16:54 +01:00
Branden Archer
2b50e503da Merge pull request #339 from brarcher/pre-v0.26
Update for v0.26
2020-01-05 20:14:23 -08:00
Branden Archer
ab4c360c37 Update CHANGELOG for v0.26 2020-01-05 20:06:54 -08:00
Branden Archer
c76580d6be Update app version to 0.26 2020-01-05 20:06:54 -08:00
Branden Archer
dc8ac6b574 Merge pull request #338 from brarcher/update-translations
Update translations
2020-01-05 19:45:12 -08:00
Branden Archer
0c23f25aca Update translations
These were pulled from the Transifex project:
transifex.com/na-243/loyalty-card-locker
2020-01-05 19:35:51 -08:00
Branden Archer
47b9bbd30d Merge pull request #336 from TheLastProject/fix/convert_to_barcodeless
Fix converting loyalty card to barcodeless
2020-01-04 17:28:39 -08:00
Sylvia van Os
ce81d3afd4 Fix as reviewed and add test 2020-01-02 21:15:06 +01:00
Sylvia van Os
cc99af13e4 Fix tests 2020-01-01 19:43:23 +01:00
Sylvia van Os
ccf3d1f3d6 Fix converting loyalty card to barcodeless
The LoyaltyCardEditActivity assumes on many places that an empty string
means it doesn't know a value yet. This patch ensures that the
BarcodeSelectorActivity returns a special string so that the
LoyaltyCardEditActivity can distinguish explicitly picking no barcode
from a not yet populated field.
2019-12-31 18:23:45 +01:00
Branden Archer
330ded0b5d Merge pull request #334 from TheLastProject/fix/sdk28
Up targetSdk for Google Play
2019-12-30 19:45:46 -08:00
Sylvia van Os
0bcb0a7ab8 Up targetSdk to 29 2019-12-29 13:01:02 +01:00
Sylvia van Os
403c1fca33 Fix last error Travis reported 2019-12-28 21:25:17 +01:00
Sylvia van Os
ca12f8f914 Fix some of the errors Travis reported 2019-12-28 21:19:00 +01:00
Sylvia van Os
d5ba632bb3 Fix unit tests with Robolectric 4 2019-12-28 21:07:47 +01:00
Branden Archer
b9d158583b Update to AndroidX
This is mostly the work of Android Studio's
   Refactor > Migrate to AndroidX...
option. That mostly worked. The two places which were manually updated
were:
  - The reference to AppBarLayout$ScrollingViewBehavior in
    loyalty_card_edit_activity.xml
  - The suggested constraintlayout version, 2.0.0-beta4 caused build
    issues that 1.1.3 avoids
2019-12-28 19:57:27 +01:00
Sylvia van Os
32b4178950 Fix some warnings 2019-12-27 17:40:12 +01:00
Sylvia van Os
d9f97380d9 Fix status bar being white 2019-12-27 17:34:58 +01:00
Sylvia van Os
4e2fe97de5 Merge branch 'fix/sdk28' of ssh://github.com/TheLastProject/loyalty-card-locker into fix/sdk28 2019-12-27 17:27:48 +01:00
Sylvia van Os
1a3dee2c26 Update support libraries 2019-12-27 17:27:40 +01:00
Sylvia van Os
53f3a2109b Merge branch 'master' into fix/sdk28 2019-12-27 17:16:21 +01:00
Sylvia van Os
c5c2f702d0 Up targetSdk for Google Play 2019-12-27 17:14:07 +01:00
Branden Archer
396801ba74 Merge pull request #326 from TheLastProject/feature/multiline_notes
Allow multiline notes
2019-12-24 19:33:31 -08:00
Branden Archer
67163539d5 Merge branch 'master' into feature/multiline_notes 2019-12-24 19:25:59 -08:00
Branden Archer
c1be6f2a88 Merge pull request #330 from TheLastProject/fix/white_on_white_icons
Fix white on white icons
2019-12-20 20:56:50 -08:00
Sylvia van Os
c0d1b446d1 Make same text and header color at least slightly visible 2019-12-16 13:23:16 +01:00
Sylvia van Os
d24a418beb Fix status bar icon colors 2019-12-16 11:36:23 +01:00
Sylvia van Os
1f348295a0 Merge branch 'master' of ssh://github.com/brarcher/loyalty-card-locker into fix/white_on_white_icons 2019-12-15 21:36:56 +01:00
Sylvia van Os
280fe76609 Use DrawableCompat tinting instead 2019-12-15 21:29:34 +01:00
Branden Archer
8a3b623ea6 Merge pull request #324 from TheLastProject/feature/barcodeless_cards
Allow barcodeless cards
2019-12-15 12:01:42 -08:00
Sylvia van Os
e1f66e1917 Implement requested changes 2019-12-15 20:45:12 +01:00
Sylvia van Os
787bda1c77 Add test for empty barcode type 2019-12-15 14:28:48 +01:00
Sylvia van Os
c83b151c08 Revert "Check for empty cardId in BarcodeSelectorActivity instead"
This reverts commit 62f7241bca.
2019-12-15 12:54:51 +01:00
Sylvia van Os
a642b12795 Use a const for luminance halfway point 2019-12-15 12:50:08 +01:00
Sylvia van Os
4e1fb16359 Empty barcodeType is allowed now 2019-12-14 19:03:17 +01:00
Sylvia van Os
0d7d1658d6 Also add padding to edit activity 2019-12-12 19:42:17 +01:00
Sylvia van Os
7b7624a3e0 Fix white on white icons 2019-12-11 17:42:56 +01:00
Sylvia van Os
488125c492 Merge branch 'master' into feature/barcodeless_cards 2019-12-11 14:25:41 +01:00
Sylvia van Os
3aeea02300 Test button state 2019-12-11 14:23:13 +01:00
Sylvia van Os
72227f19e8 Fix comments 2019-12-11 11:38:53 +01:00
Sylvia van Os
62f7241bca Check for empty cardId in BarcodeSelectorActivity instead 2019-12-11 10:44:31 +01:00
Sylvia van Os
80fb7e2cb3 Add 10dp white padding around all QR codes 2019-12-10 20:34:07 +01:00
Branden Archer
ed048e6424 Merge pull request #325 from 532910/master
typo fix
2019-12-09 17:32:31 -08:00
Sylvia van Os
bda8abfe3d Allow multiline notes 2019-12-09 14:44:15 +01:00
Sylvia van Os
19755f4f86 Add test 2019-12-09 13:44:25 +01:00
Sylvia van Os
9a2d195cb2 Don't generate a barcode if id is empty
This prevents a bug with an empty PDF 417 barcode being generated
2019-12-09 13:20:42 +01:00
Sylvia van Os
429309deef UI improvements 2019-12-09 13:10:37 +01:00
sergio
864b82d547 typo fix
URI is an abbreviation, must be URI, not Uri.
2019-12-09 12:38:10 +03:00
Branden Archer
77a2ed29db Merge branch 'master' into feature/barcodeless_cards 2019-12-08 20:15:47 -08:00
Branden Archer
acfb92e9fb Merge pull request #322 from TheLastProject/feature/dark_mode
Dark mode
2019-12-08 18:16:20 -08:00
Sylvia van Os
61c2c0c84a Completely removed rectangle cleanup code, 1D codes seem to need this padding 2019-12-08 18:15:08 +01:00
Sylvia van Os
08afc16d01 Don't remove paddings smaller than 20 pixels 2019-12-08 18:05:09 +01:00
Sylvia van Os
91a4461863 Fix code style 2019-12-05 19:49:45 +01:00
Sylvia van Os
e37288b842 Remove useless null check 2019-12-04 23:10:02 +01:00
Sylvia van Os
fc930f40e4 Merge branch 'master' into feature/barcodeless_cards 2019-12-04 16:36:58 +01:00
Sylvia van Os
33b84ad4c3 Allow barcodeless cards 2019-12-04 15:59:49 +01:00
Sylvia van Os
368a2a30f6 Fix white border in dark theme 2019-12-04 13:27:53 +01:00
Sylvia van Os
5774064da7 Don't draw more than the actual barcode 2019-12-04 13:04:42 +01:00
Sylvia van Os
8673405a50 Merge branch 'master' into feature/dark_mode 2019-12-04 12:45:50 +01:00
Sylvia van Os
4ec4baa53b Fix card title colour issues 2019-12-04 12:45:16 +01:00
Branden Archer
f00be863c2 Merge pull request #323 from TheLastProject/master
Fix GitHub pages
2019-12-03 17:30:18 -08:00
Sylvia van Os
e0d85f9c8d Fix GitHub pages 2019-12-03 17:12:58 +01:00
Branden Archer
3754243748 Merge branch 'master' into feature/dark_mode 2019-12-02 23:30:32 -08:00
Branden Archer
5ef2c4b1da Merge pull request #319 from TheLastProject/fix/improve_note_sizing
Improve note sizing
2019-12-02 23:29:57 -08:00
Branden Archer
8315067caf Merge branch 'master' into fix/improve_note_sizing 2019-12-02 23:19:48 -08:00
Branden Archer
7bbf5b469c Merge pull request #321 from TheLastProject/feature/card_sharing
Add ability to share and receive loyalty cards
2019-12-02 23:13:09 -08:00
Sylvia van Os
0de50e72a6 Fixes, start with right theme 2019-12-02 18:03:36 +01:00
Sylvia van Os
4240fd55ba Style about dialog for dark mode 2019-12-02 17:09:49 +01:00
Sylvia van Os
29e8896059 Remove another unneeded import 2019-12-02 16:33:49 +01:00
Sylvia van Os
a46200f794 Merge branch 'master' into feature/dark_mode 2019-12-02 16:32:58 +01:00
Sylvia van Os
061b1db245 Cleanup 2019-12-02 16:28:16 +01:00
Sylvia van Os
8d3d9a21f7 Allow switching theme 2019-12-02 16:24:24 +01:00
Sylvia van Os
9b5a731d48 Start with dark mode 2019-12-02 15:05:19 +01:00
Sylvia van Os
5e0e8a6f67 Merge branch 'master' into fix/improve_note_sizing 2019-12-02 14:31:13 +01:00
Sylvia van Os
07c74b79f3 Use README as index page, add share page with explanation 2019-12-02 13:14:15 +01:00
Sylvia van Os
5e836758e5 Fix function calls 2019-12-02 12:57:44 +01:00
Sylvia van Os
e314580ac3 Cleanup 2019-12-02 12:56:42 +01:00
Sylvia van Os
2ef739fb4f Fix typo 2019-12-01 22:55:17 +01:00
Sylvia van Os
bae6f746a1 Another import test 2019-12-01 22:38:31 +01:00
Sylvia van Os
22e8d7f699 Fix linter warning 2019-12-01 20:53:37 +01:00
Sylvia van Os
48a1084c40 Fix typo 2019-12-01 20:46:13 +01:00
Sylvia van Os
3d2ad1693a Tests 2019-12-01 20:42:37 +01:00
Sylvia van Os
28b3aa2018 Add ability to share and receive loyalty cards 2019-12-01 18:22:55 +01:00
Branden Archer
ed4adad087 Merge pull request #320 from TheLastProject/feature/card_filter
Add loyalty card filter
2019-11-28 20:22:51 -08:00
Sylvia van Os
5a898b4c4c Address comments 2019-11-28 23:07:27 +01:00
Sylvia van Os
7a78eadabb Remove unneeded imports 2019-11-24 20:08:56 +01:00
Sylvia van Os
d7556b9951 Remove unneeded imports 2019-11-24 19:05:26 +01:00
Sylvia van Os
c936e3c9c0 Switch to SearchView 2019-11-23 20:27:37 +01:00
Sylvia van Os
e33565abdc Add filtering test 2019-11-23 15:27:31 +01:00
Sylvia van Os
30419b5896 Explain focusable change 2019-11-23 14:35:03 +01:00
Sylvia van Os
f0426b98dc Cleanup 2019-11-23 14:29:10 +01:00
Sylvia van Os
bd3d67574e Bump minSdk because of rawQuery usage 2019-11-22 21:03:55 +01:00
Sylvia van Os
52a09056e8 Remove default focus 2019-11-22 20:54:34 +01:00
Sylvia van Os
21a8dc6867 Show warning when no matches 2019-11-22 20:46:06 +01:00
Sylvia van Os
e048fdff59 Fix tests 2019-11-22 20:24:50 +01:00
Sylvia van Os
0a8c85d24c Cleanup 2019-11-22 19:24:03 +01:00
Sylvia van Os
07d938701f Add loyalty card filter 2019-11-22 17:32:40 +01:00
Sylvia van Os
c0c126192f Improve note sizing
Signed-off-by: Sylvia van Os <sylvia@hackerchick.me>
2019-11-22 14:00:21 +01:00
Branden Archer
7cb5eba18f Merge pull request #313 from brarcher/pre-v0.25.4
Update for 0.25.4
2019-10-04 23:05:35 -07:00
Branden Archer
134d7fd824 Update for 0.25.4 2019-10-04 22:47:54 -07:00
Branden Archer
1615bc38bd Merge pull request #312 from brarcher/translations
Update translations
2019-10-04 22:45:19 -07:00
Branden Archer
3ecdf71331 Update translations
These were fetched from Transifex
2019-10-04 22:37:46 -07:00
Branden Archer
66a2cd438c Merge pull request #311 from brarcher/allow-backup
Allow backups of app data
2019-10-04 22:35:12 -07:00
Branden Archer
3fdff3c85a Allow backups of app data
Setting the allowBackup option enables the app to participate
in backup and restore infrastructure.
2019-10-04 22:27:17 -07:00
Branden Archer
56709d40b5 Merge pull request #297 from brarcher/pre-v0.25.3
Update for 0.25.3
2019-03-02 11:59:43 -08:00
Branden Archer
a8c4c73611 Update CHANGELOG 2019-03-02 11:38:21 -08:00
Branden Archer
31dd0e1de2 Update for 0.25.3 2019-03-02 11:37:09 -08:00
Branden Archer
f51e97fdf7 Merge pull request #296 from brarcher/translations
Update Russian translations
2019-03-02 11:36:23 -08:00
Branden Archer
1fcf7101f2 Update Russian translations 2019-03-02 10:19:10 -08:00
Branden Archer
dbb7a4ea62 Merge pull request #292 from brarcher/readme
Update translation instructions in README
2019-01-28 19:26:50 -08:00
Branden Archer
a2fa8edcf5 Update translation instructions in README 2019-01-28 07:08:28 -08:00
Branden Archer
fd71eda561 Merge pull request #290 from brarcher/changelog
Update CHANGELOG
2019-01-05 16:09:09 -08:00
Branden Archer
058676bb0f Update CHANGELOG 2019-01-05 16:01:54 -08:00
Branden Archer
1fb59979f3 Merge pull request #289 from brarcher/pre-v0.25.2
Update for v0.25.2
2019-01-05 16:00:11 -08:00
Branden Archer
9bde7ce4da Update for v0.25.2 2019-01-05 15:40:48 -08:00
Branden Archer
db3d93e1bc Merge pull request #288 from brarcher/translations
Update translations
2019-01-05 15:40:22 -08:00
Branden Archer
4fd7872c87 Update translations 2019-01-05 15:24:47 -08:00
Branden Archer
fc313f0cfa Merge pull request #280 from comradekingu/patch-1
Norwegian Bokmål translation
2018-10-19 21:03:00 -07:00
Allan Nordhøy
03cf97cca7 Added self-referencing "about" tag 2018-10-20 03:22:17 +02:00
Allan Nordhøy
a7204eb9d5 Norwegian Bokmål translation 2018-10-19 15:14:37 +02:00
Branden Archer
81d7250306 Merge pull request #275 from brarcher/pre-v0.25.1
Update for v0.25.1
2018-10-14 11:53:02 -07:00
Branden Archer
580025c99e Update for v0.25.1 2018-10-14 11:46:34 -07:00
Branden Archer
d88bede310 Merge pull request #274 from brarcher/translations
Update translations
2018-10-14 11:46:06 -07:00
Branden Archer
4c9bc911ad Update translations 2018-10-14 11:39:21 -07:00
Branden Archer
38d6d6ccff Merge pull request #273 from brarcher/fix-manual-barcode
Fix manual barcode
2018-10-14 11:38:55 -07:00
Branden Archer
866eaea680 Correct log statement when entering barcode manually 2018-10-14 11:24:42 -07:00
Branden Archer
5619c1c003 Revert "Remove unneeded code"
The log statement here was misleading. This code is responsible for
receiving a barcode that a user typed in manually.

This reverts commit cafa09a86a.
2018-10-14 11:24:42 -07:00
Branden Archer
b8311044db Update class file location for FindBugs
Gradle now places the compiled class file under a different path.
2018-10-14 11:24:42 -07:00
Branden Archer
3679b9a48a Update gradle and android plugin 2018-10-14 11:07:21 -07:00
Branden Archer
727f3f32fe Merge pull request #271 from brarcher/pre-v0.25
Update for v0.25
2018-10-07 15:28:12 -07:00
Branden Archer
6fa70afac5 Update for v0.25 2018-10-07 15:18:08 -07:00
Branden Archer
c5ad261392 Merge pull request #270 from brarcher/translations
Update translations
2018-10-07 15:12:38 -07:00
Branden Archer
28018eb681 Update translations 2018-10-07 15:05:38 -07:00
Branden Archer
28f7b3db02 Merge pull request #269 from brarcher/orientation-setting
Add option to lock orientation state
2018-10-06 18:33:41 -04:00
Branden Archer
10e720de91 Add option to lock orientation state
This new option will control if the screen orientation
lock option is displayed or not. If set, the orientation lock
is forced and the unlock option is hidden.
2018-10-06 15:27:36 -07:00
Branden Archer
4de952ba2d Set rotation state in onCreate()
The menu creation need not change this state
2018-10-06 14:24:50 -07:00
Branden Archer
c5de27abe3 Move orientation locking code to separate method 2018-10-06 14:24:05 -07:00
Branden Archer
cafa09a86a Remove unneeded code
The IntentIntegrator parses out the result, so this
code is not necessary.
2018-09-18 10:53:16 -04:00
Branden Archer
1635737658 Merge pull request #267 from brarcher/translations
Update translations
2018-09-17 10:20:54 -04:00
Branden Archer
6ee60687b6 Update translations 2018-09-17 10:03:05 -04:00
Branden Archer
0afd9358e6 Merge pull request #266 from brarcher/fix-sorting
Sort card list case insensitive
2018-09-15 23:04:35 -04:00
Branden Archer
cad3b492a2 Sort card list case insensitive 2018-09-15 22:50:36 -04:00
Branden Archer
59bde6916c Merge pull request #261 from brarcher/pre-v0.24
Update for v0.24
2018-07-31 21:55:52 -04:00
Branden Archer
eb6364b932 Update CHANGELOG 2018-07-31 21:48:33 -04:00
Branden Archer
ae7ac21785 Update for v0.24 2018-07-31 21:44:28 -04:00
Branden Archer
2dd02f42f5 Merge pull request #260 from brarcher/transifex
Add/update translations
2018-07-27 23:25:37 -04:00
Branden Archer
3b8e38001c Update translations 2018-07-27 23:18:38 -04:00
Branden Archer
fbb0ab27ec Add Slovenian translations 2018-07-27 23:18:38 -04:00
Branden Archer
ceea5182d1 Merge pull request #259 from brarcher/setting-barcode-brightness
Configure brightening screen when display a barcode
2018-07-25 22:43:21 -04:00
Branden Archer
34c07800dd Configure brightening screen when display a barcode
When a barcode is displayed the screen's brightness is set to
the maximum, in an attempt to provide the best chance for a barcode
scanner to capture the barcode. However, it may not always
be desired to increase the brightness, for example when using
the app in low light.

This change adds a setting to control the screen brightening
behavior when a barcode is viewed.
2018-07-25 22:24:27 -04:00
Branden Archer
8c7a3273e8 Merge pull request #256 from brarcher/privacy
Update privacy-policy
2018-07-25 22:07:35 -04:00
Branden Archer
49cd9a28c7 Update privacy-policy 2018-06-26 17:30:38 -04:00
Branden Archer
392024f84f Merge pull request #254 from brarcher/transifex
Transifex config file and translation updates
2018-06-16 18:08:47 -04:00
Branden Archer
165f963f7d Update translations 2018-06-16 17:54:29 -04:00
Branden Archer
aca44c5830 Add Transifex config file 2018-06-16 17:54:29 -04:00
Branden Archer
e2ed5b515a Merge pull request #252 from brarcher/greek
Add Greek translations
2018-06-13 09:33:08 -04:00
Branden Archer
ba6705b96e Add Greek translations 2018-06-13 09:19:38 -04:00
Branden Archer
9f407e5eb0 Merge pull request #246 from brarcher/changelog
Update CHANGELOG
2018-05-12 21:40:56 -04:00
Branden Archer
c00da00be5 Update CHANGELOG 2018-05-12 11:56:48 -04:00
Branden Archer
c2ea640a71 Merge pull request #245 from brarcher/pre-v0.23.4
Update for v0.23.4
2018-05-12 11:48:08 -04:00
Branden Archer
ba5259f0f6 Update for v0.23.4 2018-05-12 11:41:57 -04:00
Branden Archer
b1abfdff2e Merge pull request #244 from brarcher/translations
Translations
2018-05-12 11:41:09 -04:00
Branden Archer
39219531b4 Update translations 2018-05-12 11:33:16 -04:00
Branden Archer
048edf186e Fix Spanish translations
The wrong language was in this file.
2018-05-12 11:32:40 -04:00
Branden Archer
ee6d415d26 Merge pull request #240 from brarcher/pre-v0.23.3
Update for v0.23.3
2018-05-05 20:38:42 -04:00
Branden Archer
5143e3fe45 Update for v0.23.3 2018-05-05 20:26:28 -04:00
Branden Archer
306ee053c1 Merge pull request #239 from brarcher/translations
Update translations
2018-05-05 20:23:40 -04:00
Branden Archer
40871690d5 Update translations 2018-05-05 18:28:33 -04:00
Branden Archer
0bbbabce97 Merge pull request #232 from kRkk/master
Add Polish translation
2018-03-22 22:37:31 -04:00
Karol
f59c485472 Add Polish translation 2018-03-22 22:48:20 +01:00
Branden Archer
d8543f456c Merge pull request #228 from brarcher/changelog
Update CHANGELOG for v0.23.2
2018-03-11 23:51:07 -04:00
Branden Archer
fcf88a1ace Update CHANGELOG for v0.23.2 2018-03-11 23:33:42 -04:00
Branden Archer
a2e99c2b9d Merge pull request #227 from brarcher/pre-v0.23.2
Update for v0.23.2
2018-03-11 23:27:29 -04:00
Branden Archer
607dff03d4 Update for v0.23.2 2018-03-11 23:20:01 -04:00
Branden Archer
73b6c99d9e Merge pull request #226 from brarcher/sdk-15
Add additional layout options to support SDK 15
2018-03-11 23:19:18 -04:00
Branden Archer
036748ee56 Add additional layout options to support SDK<17
Google Play statistics show that the earliest Android version
in use which has installs for this app is 4.0.3, which is SDK 15.
This changes cleans up some layouts so that it will work on
SDK 15.
2018-03-11 23:11:51 -04:00
Branden Archer
5cae97482f Merge pull request #225 from brarcher/test-cleanup
Remove unnecessary apache legacy usage
2018-03-11 22:44:13 -04:00
Branden Archer
f67d27167e Remove unnecessary apache legacy usage
The version of robolectric used no longer requires this.
2018-03-11 22:33:31 -04:00
Branden Archer
0832c7fc97 Merge pull request #222 from brarcher/changelog
Update CHANGELOG
2018-03-08 11:33:32 -05:00
Branden Archer
f5e1a2d732 Update CHANGELOG 2018-03-07 23:29:17 -05:00
Branden Archer
7b799df98a Merge pull request #221 from brarcher/pre-v0.23.1
Update for pre-v0.23.1
2018-03-07 23:25:10 -05:00
Branden Archer
968c3e71a1 Update for pre-v0.23.1 2018-03-07 23:14:53 -05:00
Branden Archer
29d8da99cd Merge pull request #219 from brarcher/barcode-oom
Catch case where barcode rendering runs into an OOM
2018-03-04 14:17:53 -05:00
Branden Archer
79213aa3ec Catch case where barcode rendering runs into an OOM
There are still cases where the barcode could not be rendered
because the process runs out of its memory limit. To avoid
a crash, catch the failure and log it. The barcode will
not be displayed, but at least the app will not crash.
2018-03-04 13:10:12 -05:00
Branden Archer
50fdfe92fa Merge pull request #217 from brarcher/changelog
Update CHANGELOG
2018-02-28 23:13:10 -05:00
Branden Archer
4eab1121b0 Update CHANGELOG 2018-02-28 23:07:15 -05:00
Branden Archer
d3bcd3e273 Merge pull request #215 from brarcher/pre-v0.23
Update for v0.23
2018-02-28 23:05:38 -05:00
Branden Archer
9d1c9c3309 Merge branch 'master' into pre-v0.23 2018-02-28 22:59:37 -05:00
Branden Archer
61e039c456 Merge pull request #216 from brarcher/disable-beep
Disable beep upon successful barcode scan
2018-02-28 22:59:27 -05:00
Branden Archer
0e2d725591 Disable beep upon successful barcode scan 2018-02-28 22:53:05 -05:00
Branden Archer
759d42cfcf Update for v0.23 2018-02-28 22:48:49 -05:00
Branden Archer
2312788a57 Merge pull request #213 from brarcher/header-size
Reduce space used in header for card view
2018-02-26 20:52:44 -05:00
Branden Archer
0820be3986 Reduce space used in header for card view
The landscape view left little room for the barcode and card ID
because the header took up a lot of space. Further, on smaller
phones it would take up a large portion of the screen.

This change reduces the header from 224dp to two action bar sizes
plus the size of the textview for the store name.
2018-02-19 23:21:12 -05:00
Branden Archer
8a11953276 Merge pull request #211 from brarcher/changelog
Update CHANGELOG
2018-02-19 15:30:48 -05:00
Branden Archer
182b3ff3c0 Update CHANGELOG 2018-02-19 10:34:41 -05:00
Branden Archer
79dad1b0a6 Merge pull request #210 from brarcher/pre-v0.22
Update for v0.22
2018-02-19 10:29:16 -05:00
Branden Archer
04466edb01 Update for v0.22 2018-02-18 23:30:04 -05:00
Branden Archer
af3ce5a0ff Merge pull request #209 from brarcher/barcode-rendering
Barcode rendering improvements
2018-02-18 23:24:52 -05:00
Branden Archer
3291185812 Render 1D barcodes with a larger width
For some reason when 1D barcodes are rendered in a smaller width,
they end up scrunched up and not using all available space.
As a result, they do not scale to the entire width of the screen.
In many cases the barcode is not scannable even in landscape.

To resolve this, render the 1D barcodes in a larger space. The space
is still bounded, to prevent OOMs on tablets or really
wide screen devices.
2018-02-18 23:14:51 -05:00
Branden Archer
c9df3bd1d2 Redraw barcode when screen rotates 2018-02-18 23:14:51 -05:00
Branden Archer
057c7923a8 Merge pull request #208 from brarcher/translations
Update translations
2018-02-17 23:49:45 -05:00
Branden Archer
4195d73c53 Update translations 2018-02-17 23:43:21 -05:00
Branden Archer
ed08f0577a Merge pull request #207 from brarcher/changelog-1
Update CHANGELOG
2018-02-17 23:34:21 -05:00
Branden Archer
a1257a1692 Update CHANGELOG 2018-02-17 23:27:26 -05:00
Branden Archer
a51815a172 Merge pull request #206 from brarcher/pre-v0.21
Update for v0.21
2018-02-17 22:56:35 -05:00
Branden Archer
8eee8f6a0f Update for v0.21 2018-02-17 22:44:49 -05:00
Branden Archer
1817c184ae Merge pull request #205 from brarcher/fix-no-color-import
Fix no color import
2018-02-17 22:44:00 -05:00
Branden Archer
773d759ef5 Fix imports with missing colors 2018-02-17 22:37:10 -05:00
Branden Archer
cbc51cfb73 Remove debug toast 2018-02-17 22:37:10 -05:00
Branden Archer
46c3d190d8 Merge pull request #204 from brarcher/custom-font-sizes
Add settings for font sizes
2018-02-17 16:29:54 -05:00
Branden Archer
9951999ca4 Add settings for font sizes 2018-02-17 16:05:10 -05:00
Branden Archer
4843a1093d Merge pull request #203 from brarcher/color-picker
Custom colors
2018-02-17 16:02:54 -05:00
Branden Archer
a4bde722a6 Add config for text and background colors
This adds config options for the colors used for the store text
and background when displaying the store name in the single
card view or the thumbnail for the card list.
2018-02-17 15:50:32 -05:00
Branden Archer
ff5e5d5833 Remove unused code handling adding a card
This case is handled in another acitivity now.
2018-02-12 15:13:23 -05:00
Branden Archer
685eaec8b5 Remove views which are no longer used 2018-02-12 15:13:23 -05:00
Branden Archer
a3556cc66d Merge pull request #200 from brarcher/barcode-space
Add quite space before and after barcode
2018-02-10 22:27:45 -05:00
Branden Archer
13543ea9f6 Add quite space before and after barcode
This is to help barcode scanners determine where the end of
the barcode is.
2018-02-10 22:21:35 -05:00
Branden Archer
d7bf31c308 Merge pull request #199 from brarcher/changelog-1
Update CHANGELOG
2018-02-10 22:13:27 -05:00
Branden Archer
a77a968619 Update CHANGELOG 2018-02-10 22:06:35 -05:00
Branden Archer
70c6ee17cd Merge pull request #198 from brarcher/pre-v0.20
Update for v0.20
2018-02-10 22:02:04 -05:00
Branden Archer
c233f82dc1 Update for v0.20 2018-02-10 13:50:30 -05:00
Branden Archer
be0c2507f2 Merge pull request #197 from brarcher/layout-changes
Layout changes for new card view layout
2018-02-10 13:49:22 -05:00
Branden Archer
abfcf9d6cd Disable MissingPrefix lint
The autosizing TextView, which comes from the support-v4 library,
adds several new attributes to TextViews. These attributes do not
seem to be in the app namespace (as described in the documentation),
as Android Studio and lint complains. However, funtionally the new
attributes work as expected.
2018-02-10 13:43:28 -05:00
Branden Archer
4d565b3b2f Update screenshots and wizard images 2018-02-10 13:37:17 -05:00
Branden Archer
27511a4ccd Make card id and note text selectable 2018-02-10 13:37:17 -05:00
Branden Archer
12440346fa Replace letter icon with store name 2018-02-10 13:37:17 -05:00
Branden Archer
870e4d0c4a Add note text to card view layout 2018-02-10 13:37:17 -05:00
Branden Archer
6e526de087 Auto-size card id text on card view layout 2018-02-10 13:37:16 -05:00
Branden Archer
891636b51f Use ConstraintLayout for card view layout
This way the size of the barcode can be 1/2 the screen.
2018-02-10 13:37:16 -05:00
Branden Archer
017d97d08c Merge pull request #196 from brarcher/feature-graphic
add feature graphic to metadata
2018-02-09 23:30:09 -05:00
Branden Archer
63165ca1a5 add feature graphic to metadata 2018-02-09 23:21:46 -05:00
Branden Archer
48f40a51d1 Merge pull request #192 from brarcher/brarcher-patch-1
Update CHANGELOG
2018-02-01 08:13:51 -05:00
Branden Archer
3940ba5756 Update CHANGELOG 2018-02-01 08:05:10 -05:00
Branden Archer
b8a36e3c45 Merge pull request #191 from brarcher/pre-v0.19
Update for v0.19
2018-02-01 08:02:20 -05:00
Branden Archer
60deaae8d5 Update for v0.19 2018-02-01 07:54:48 -05:00
Branden Archer
a71da8d6dc Merge pull request #190 from brarcher/card-layout
Improve card view layout
2018-01-31 23:39:37 -05:00
Branden Archer
dbbbd128dc Update intro and metadata graphics 2018-01-31 23:19:19 -05:00
Branden Archer
3275279501 Remove unnecessary casts from findViewById 2018-01-31 22:49:31 -05:00
Branden Archer
2f6516ffb9 Display note on separate line, omit card id on card list
The card id was of dubious value, because the main screen
is really for selecting the card. The note may have more value,
as it may specify info about the card.
2018-01-31 22:49:31 -05:00
Branden Archer
41c8b78275 Move store name text size to dimens.xml 2018-01-31 22:49:31 -05:00
Branden Archer
6ed96393d5 Change layout of single card view
This changes the layout when viewing a single card. The new layout
will have a colored area at the top which displays the first letter
of the company, and after that the barcode. The note will be displayed
on the card list screen and is presently omitted here.

As this layout is experimental, the layout for editing a card is
not yet updated. To achieve this the card viewing activity is split
into two separate classes, where one handles viewing and one handles
editing. When the view layout finalizes the edit icon can be updated
as time allows.
2018-01-31 22:49:31 -05:00
Branden Archer
3519e03568 Merge pull request #188 from brarcher/card-list-layout
Improve card list layout
2018-01-28 15:08:03 -05:00
Branden Archer
1aef3f5253 Preserve float result from division
The arguments to drawText for x and y are float, but
dividing two integers truncates the result to an integer.
2018-01-28 14:21:53 -05:00
Branden Archer
c97e80432f Support non-English letters for default card thumbnail 2018-01-28 09:49:21 -05:00
Branden Archer
c6265eb9e3 Add generated icon for cards in card list
This add a thumbnail of a single letter for each card,
consisting of the first letter of the card and a
pastel background.

This was adapted from the andOTP project.
2018-01-27 20:05:57 -05:00
Branden Archer
935cd97f99 Improve layout for card list
This now includes an icon and emphasizes the store name.

This was adapted from the andOTP project.
2018-01-27 20:03:37 -05:00
Branden Archer
e65d096e76 Merge pull request #187 from brarcher/pre-v0.18.1
Update for v0.18.1
2018-01-24 17:39:03 -05:00
Branden Archer
8d38918a12 Update for v0.18.1 2018-01-24 17:28:37 -05:00
Branden Archer
57f2c7acd4 Merge pull request #186 from brarcher/changelog
Update CHANGELOG
2018-01-24 17:28:02 -05:00
Branden Archer
7fb6e4a8d3 Update CHANGELOG 2018-01-24 17:19:10 -05:00
Branden Archer
56d20939be Merge pull request #185 from brarcher/contributing
Create CONTRIBUTING.md
2018-01-24 16:21:42 -05:00
Branden Archer
a3968f8894 Create CONTRIBUTING.md 2018-01-24 16:06:05 -05:00
Branden Archer
74dd292f5a Merge pull request #184 from brarcher/install-crash-workaround
Workaround install crash on Android 5 and below
2018-01-24 13:22:08 -05:00
Branden Archer
d7a7528114 Workaround install crash on Android 5 and below
The aapt2 tool, new to Android Studio 3, creates an APK which fails
to install on Android 5 and below if it contains the following bug:

   https://issuetracker.google.com/issues/64434571

Build tools 27.0.1 has a mitigation. Avoiding aapt2 also avoids hitting
the bug.
2018-01-24 08:37:53 -05:00
Branden Archer
bffeca9033 Merge pull request #182 from brarcher/changelog
Update Changelog
2018-01-19 23:46:15 -05:00
Branden Archer
5a88afb9f3 Update Changelog 2018-01-19 23:39:25 -05:00
Branden Archer
f775f9224b Merge pull request #181 from brarcher/pre-v0.18
Update for v0.18
2018-01-19 23:36:49 -05:00
Branden Archer
17acb8370c Update for v0.18 2018-01-19 23:16:15 -05:00
Branden Archer
23766fe9f0 Merge pull request #180 from brarcher/import-from-filesystem
Import from filesystem
2018-01-19 23:09:28 -05:00
Branden Archer
f1b0a26591 Allow file paths when importing directly from filesystem
The import path directly from the file system may not
result in using a content:// or file:// Uri, but instead
a file system path. If this occurs, attempt to use a FileInputStream
to read the contents.
2018-01-19 22:57:25 -05:00
Branden Archer
f1b2c0d93d Report Uri after import
The filename from Uri code was removed in a previous commit.
To give some feedback about what was imported, the Uri itself
is reported.
2018-01-19 22:57:25 -05:00
Branden Archer
4034997d7f Merge pull request #179 from brarcher/import-crash
Do not bother to show file name after an import
2018-01-19 08:27:36 -05:00
Branden Archer
d3bbaf39f4 Do not bother to show file name after an import
It was observed by one user that the Uri to filename lookup
failed with an IllegalStateException. The code which
converts a Uri to a file path may not be that great,
especially now that the app is expected to not receive
file Uris. Instead of determining the best way to lookup
the file names, as it is not that important it will be
removed.
2018-01-19 00:12:36 -05:00
Branden Archer
ae7684921f Merge pull request #177 from brarcher/csv-crash
Fix crash on some corruption when importing CSV
2018-01-17 08:31:21 -05:00
Branden Archer
e73974536c Update how SDK 27 is acceptd on Travis-CI
There are issues with using the "andorid" tool to accept
some licenses on Travis-CI. The theory from Travis-CI is that
the android binary is out of date. An alternative is using
sdkmanager.
2018-01-17 08:04:26 -05:00
Branden Archer
43072e283f Fix crash on some corruption when importing CSV
Commons-CSV would throw a RuntimeException in some cases of
bad CSV input. This was later changed to throwing an
IllegalStateException. Updating to v1.5 to pick-up the change.
2018-01-16 22:04:36 -05:00
Branden Archer
a3a70d459b Merge pull request #176 from brarcher/readme
Update screenshots in README
2018-01-11 23:06:05 -05:00
Branden Archer
edfc91376f Update screenshots in README
The screenshots now use the images from the metadata
folder in the repo.
2018-01-11 22:35:58 -05:00
Branden Archer
5fb68055ea Merge pull request #175 from brarcher/changelog
Update CHANGELOG
2018-01-11 22:24:43 -05:00
Branden Archer
085a3f10e7 Update CHANGELOG 2018-01-11 22:11:53 -05:00
Branden Archer
4070a6b4e0 Merge pull request #174 from brarcher/transifex
Import translations from Transifex
2018-01-11 22:08:26 -05:00
Branden Archer
bc547a1d2b Import translations from Transifex 2018-01-11 22:01:49 -05:00
Branden Archer
4f9c75da9e Merge pull request #173 from brarcher/pre-v0.17
Update for v0.17
2018-01-11 21:48:52 -05:00
Branden Archer
fde679751a Update for v0.17 2018-01-11 21:36:52 -05:00
Branden Archer
d4720db2e7 Merge pull request #172 from brarcher/update-sdk-27
Update to Android SDK 27
2018-01-03 23:02:12 -05:00
Branden Archer
245935242f Update to Android SDK 27 2018-01-03 22:20:00 -05:00
Branden Archer
e217bd28fb Merge pull request #171 from brarcher/new-icon
New app icon and color scheme
2018-01-03 21:26:32 -05:00
Branden Archer
ad17bc6bf3 Update first start wizard images with new logo and colors
Note that the second slide has been changed. Originally there was
text over the image with an arrow. This text could not be translated
and was part of the instruction. The arrow is really not needed,
so it has been removed with the overlay text.

Also, the originals folder has been removed. The screenshots which
were used to create the wizard images are in the metadata folder.
2018-01-03 21:17:59 -05:00
Branden Archer
17ab1365bf Add metadata in fastlane format
This is for F-Droid to use these for the app's listing.
2018-01-03 21:17:58 -05:00
Branden Archer
7ebbe6413f Update app icons and colors with new design
This icon and feature graphic are contributed under
CC0 1.0 Universal license by Samy, @samymarboy.
2018-01-03 21:17:58 -05:00
Branden Archer
adbc6bf999 Merge pull request #170 from brarcher/file-picker
Remove file:// Uri from backup file picker
2017-12-31 16:24:18 -05:00
Branden Archer
52c41f4c49 Remove file:// Uri from backup file picker
On Android SDK 24+ exposing a file Uri causes a failure.
Picking a file from a file chooser using data of a file://
Uri also hits this. As this is not necessary, removing the
Uri.
2017-12-31 15:53:57 -05:00
Branden Archer
858e317d8f Merge pull request #169 from brarcher/android-studio-3
Update gradle, et al, for Android Studio 3
2017-12-25 21:53:02 -05:00
Branden Archer
898e822a28 Update gradle and gradle android plugin
This is to support Android Studio 3
2017-12-25 19:17:35 -05:00
Branden Archer
6f45f635aa Quote FindBugs dependsOn target
later versions of gradle depend on this
2017-12-25 19:17:04 -05:00
Branden Archer
7eeb87578f Merge pull request #164 from brarcher/pre-v0.16
Update for v0.16
2017-11-29 20:23:57 -05:00
Branden Archer
badba551d0 Update CHANGELOG 2017-11-29 20:18:12 -05:00
Branden Archer
49abf8a918 Update for v0.16 2017-11-29 20:15:10 -05:00
Branden Archer
5fba338223 Merge pull request #163 from brarcher/shortcut
Remove support for adding shortcuts from within the app
2017-11-29 20:12:25 -05:00
Branden Archer
388a0723dc Remove support for adding shortcuts from within the app
The preferred way to create a shortcut is through the launcher.
Although it is possible for an app to create shortcuts, there is
concern that allowing the app to do so may not be desirable
due to potentials for abuse.

As there is support now for adding shortcuts through the launcher,
this app do not need to also support creating them itself.
2017-11-29 19:03:34 -05:00
Branden Archer
e14da63d06 Merge pull request #161 from brarcher/add-shortcuts-from-launcher
Add shortcuts from launcher
2017-11-27 14:56:44 -05:00
Branden Archer
1673c0229a Update strings around adding shortcuts
Chrome uses the wording "Add to home screen" when adding
a shortcut.

   https://developer.chrome.com/multidevice/android/installtohomescreen

That sounds more direct that this app's wording. Updating to match.
2017-11-27 14:50:18 -05:00
Branden Archer
22afeddcbc Add activity for handling shortcut add requests 2017-11-26 20:50:15 -05:00
Branden Archer
7758c61079 Merge pull request #157 from brarcher/pre-v0.15
Pre v0.15
2017-11-25 14:48:27 -05:00
Branden Archer
47b30ca9fb Update for v0.15 2017-11-25 14:41:20 -05:00
Branden Archer
7b4d587b13 Update CHANGELOG for v0.15 2017-11-25 14:40:40 -05:00
Branden Archer
685ee018c6 Merge pull request #155 from brarcher/shortcuts
Add support for shortcuts
2017-11-25 14:40:07 -05:00
Branden Archer
e0915578ba Redraw card info when launched by a shortcut
When a shortcut launches the view activity, if the activity
already exists it will get onNewIntent() called. This needs to
clear out the view items so the next card will be redrawn.
2017-11-25 14:33:59 -05:00
Branden Archer
c733a6c3b9 Start shortcuts with singleTop, but not all launches
This will prevent shortcuts from creating many views, and causing
them to leak. However, do not use singleTop for all launches
of the view activity, else the edit mode cannot be entered.
2017-11-25 14:33:59 -05:00
Branden Archer
ec17255a43 Remove unneeded local variables
The global variables have the same information; no need to look
up the information again.
2017-11-25 14:33:59 -05:00
Branden Archer
1fc7baa5a0 Capture view items in onCreate
Some of these will need to be accessed prior to onResume()
2017-11-25 14:33:59 -05:00
Branden Archer
0df411ee96 Revert "Add widget for directly opening specific card"
This is largely unneeded, as shortcuts are directly supported
now, and the widget was a hacky replacement for shortcuts.

This reverts commit ebe6139a64.
2017-11-25 14:33:59 -05:00
Branden Archer
23178d9694 Optionally add shortcut when creating/editing a card
When a card is being added or created, a checkbox will now
be presented asking if a shortcut should be made. If selected,
when saving a shortcut will be added to the home screen. The
shortcut will directly launch the given card in view mode.
2017-11-25 14:33:59 -05:00
Branden Archer
94accc951d Let insertLoyaltyCard return the new ID
Card IDs for new cards will soon need to be known once they
are created. This change updates this call to return the
new id.
2017-11-25 14:33:59 -05:00
Branden Archer
0207e12aed Revert "Only let one card viewing activity exist at a time"
This prevented the editing of cards, as the same activity
was used for viewing and editing, but the viewing "instance"
was calling finish(). It is simpler to remove the singleTop
option.

This reverts commit 4e02252b75.
2017-11-25 14:33:59 -05:00
Branden Archer
c760465b3e Merge pull request #156 from brarcher/mimetype
Report accurate mime type when sending backup data
2017-11-25 14:33:44 -05:00
Branden Archer
f480bd0c7e Report the correct mime type for exported data
Technically text/plain is correct-ish, but text/csv is more correct.
2017-11-25 14:25:47 -05:00
Branden Archer
5599560258 Remove unneeded imports 2017-11-25 14:25:47 -05:00
Branden Archer
7b4c119d7d Merge pull request #153 from brarcher/content-imports
Support importing/exporting via content providers
2017-11-21 21:24:52 -05:00
Branden Archer
6144353079 List correct file name on imports 2017-11-21 21:15:42 -05:00
Branden Archer
03a5334961 Export backups using FileProvider
On Android 7+ providing another activity a file:// Uri is
discouraged. This changes instead uses a content provider
backed by a FileProvider to give backup data to other
activities.
2017-11-21 21:14:47 -05:00
Branden Archer
04e0a5716e Flatten error handling when importing from an activity 2017-11-21 21:12:21 -05:00
Branden Archer
91e3f9f785 Support importing content URIs and file URIs
When importing backed up settings other activities may provide
data via a content URI. This is especially likely on Android 7+,
where providing a file URI is flagged as a security issue.
To support such activities, this commit enables supporting
content URIs for importing settings.
2017-11-21 18:15:56 -05:00
Branden Archer
2ebc862e27 Remove file arg from TaskCompleteListener
Soon more than files will be imported, as content URIs will
also be supported. This then makes the File argument for
onTaskComplete() not always useful, as there may not
be a direct file used. To this end, removing the File
argument as the caller should know what was passed
to the ImportExport task anyway.
2017-11-21 16:41:25 -05:00
Branden Archer
5d122affce Merge pull request #150 from brarcher/proguard
do not obfuscate using proguard
2017-10-01 14:32:48 -04:00
Branden Archer
9e09b9052a do not obfuscate using proguard 2017-10-01 13:53:11 -04:00
Branden Archer
2753b8826f Merge pull request #149 from brarcher/singletop
Only let one card viewing activity exist at a time
2017-09-28 12:59:19 -04:00
Branden Archer
4e02252b75 Only let one card viewing activity exist at a time
Now that there are shortcuts which can launch card viewing activities
directly, if a user does not back out of the activity then there will
be a pile up of activities over time. To prevent this, only let
one such view activity exist at a time.
2017-09-28 11:31:00 -04:00
Branden Archer
647ce00e72 Merge pull request #148 from brarcher/text
Capitalize first letter in store and note fields
2017-09-28 10:03:30 -04:00
Branden Archer
86a5f2fb50 Capitalize first letter in store and note fields 2017-09-28 09:46:14 -04:00
Branden Archer
dca5129031 Merge pull request #147 from brarcher/proguard
Do not strip line number info with proguard
2017-09-27 12:14:48 -04:00
Branden Archer
e30eb00bf7 Do not strip line number info with proguard 2017-09-27 11:46:31 -04:00
Branden Archer
11e5cb8ec2 Merge pull request #146 from brarcher/pre-v0.14
Update for v0.14
2017-09-26 23:25:48 -04:00
Branden Archer
bdd5c8fbbb Update CHANGELOG 2017-09-26 23:19:06 -04:00
Branden Archer
cd79839748 Update for v0.14 2017-09-26 23:14:47 -04:00
Branden Archer
d6b47914c8 Merge pull request #145 from brarcher/app-shortcuts
Add card shortcuts for most recently used cards
2017-09-26 23:12:00 -04:00
Branden Archer
ce1acb83f0 Add card shortcuts for most recently used cards
This adds app shortcuts for the most recently used cards.
When a new card is accessed, it is added to the shortcuts list.
When the list exceeds its maximum size, the least recently
used shortcut is discarded.

Android limits the maximum number of shortcuts to 5, however
it recommends in its documentation to limit this to 4. This
commit limits this to 3, however, as that is aesthetically pleasing.
2017-09-26 22:52:32 -04:00
Branden Archer
bc360aa06c Merge pull request #142 from brarcher/widget
Add widget for directly opening specific card
2017-09-19 13:49:47 -04:00
Branden Archer
ebe6139a64 Add widget for directly opening specific card 2017-09-19 12:58:48 -04:00
Branden Archer
d354fd1877 Merge pull request #138 from brarcher/transifex
Remove missing translations stubbed to English
2017-08-10 21:49:46 -04:00
Branden Archer
00b612d6c7 Remove missing translations stubbed to English
By request, a project on Transifex was created to better
manage translations. To make things less confusing, instead
of using the MissingTranslations lint error, and thus needing
to add stub translations, the progress of translations is now
handled on Transifex.

transifex.com/na-243/loyalty-card-locker/
2017-08-10 21:25:13 -04:00
Branden Archer
a307511193 Merge pull request #137 from brarcher/readme
README: add translating info
2017-08-10 21:22:41 -04:00
Branden Archer
f7f358c5c3 README: add translating info 2017-08-10 21:15:10 -04:00
Branden Archer
f7b50a72a4 Merge pull request #135 from brarcher/changelog
Update CHANGELOG for v0.11.1, v0.12, v0.13
2017-07-26 18:18:36 -04:00
Branden Archer
68ce1fe9fd Update CHANGELOG for v0.11.1, v0.12, v0.13 2017-07-26 18:12:01 -04:00
Branden Archer
e822ab0b56 Merge pull request #133 from brarcher/pre-v0.13
Update for v0.13
2017-07-25 10:04:44 -04:00
Branden Archer
e33ab682a6 Update for v0.13 2017-07-25 09:58:19 -04:00
Branden Archer
0d50ad6d10 Merge pull request #132 from brarcher/check-for-card
Verify card exists before displaying it
2017-07-23 10:27:06 -04:00
Branden Archer
4143e5c286 Bail if loyalty card could not be found
This case was hit at least once by a user, though the scenario
is not known. If it is hit, post a message and bail gracefully,
until the reason can be determined.
2017-07-23 10:05:05 -04:00
Branden Archer
15425d51aa Add debugging when entering view activity 2017-07-22 23:52:21 -04:00
Branden Archer
3abcb32a75 Merge pull request #130 from brarcher/text-update
Change lock/unlock text to say block/unblock
2017-07-18 22:25:47 -04:00
Branden Archer
93124a88a5 Change lock/unlock text to say block/unblock 2017-07-18 22:18:52 -04:00
Branden Archer
29b00e3b59 Merge pull request #129 from airon90/patch-1
Update strings.xml
2017-07-18 22:06:08 -04:00
Michael Moroni
04174154d6 Update strings.xml
Instead of "Block screen" I translated "Block rotation" because I think it's more intuitive
2017-07-17 12:49:39 +02:00
Branden Archer
bf60976d10 Merge pull request #128 from brarcher/lock-orientation
Add option to lock screen orientation
2017-07-16 21:32:55 -04:00
Branden Archer
fb7e3e12f2 Add menu option to lock screen when viewing card
When passing a phone to a clerk to scan the barcode, if the
phone is rotated and the screen reloads it can be bothersome
or confusion. To avoid this situation, a new option is
added to lock the screen.

A menu icon is now added which defaults as unlocked. When
touched the app sets its orientation to the "natural" orientation
of the device. When touched again the sensor dictates the
orientation of the device.
2017-07-16 21:26:40 -04:00
Branden Archer
641d29a6f8 Remove usage of depreated location of ActivityController 2017-07-16 21:26:40 -04:00
Branden Archer
0f2ee296b4 Remove container class for intro slides
Even with passing a bundle with the layout ID sometimes
some users will encounter a situation where the _layout
variable is null. To avoid further issues, creating a
class for each intro slide, so the layout is encoded
into the class itself.
2017-07-16 21:26:40 -04:00
Branden Archer
ecd2ecd517 Merge pull request #127 from brarcher/pre-v0.12
Update for v0.12
2017-07-12 22:55:31 -04:00
Branden Archer
f3e7d8f5c1 Update for v0.12 2017-07-12 22:47:29 -04:00
Branden Archer
f4522fb17d Merge pull request #126 from brarcher/scale-barcode
Scale barcode height to match reduced width
2017-07-12 22:14:03 -04:00
Branden Archer
824a72b156 Scale height of barcode to match ratio when reducing width
When reducing the pixel size of the width, if the height is not
scaled to match then 1D barcodes end up being squished and are
too narrow to scan. To avoid this, scale the height to match.
Then, when the barcode is loaded into a Bitmap it will scale up
to the correct size.

It was found that on a Galaxy S4 a barcode width of 400 px started
to see some blurriness, but 500 px still looked sharp. Reducing
the maximum width to 500 px.
2017-07-11 23:16:26 -04:00
Branden Archer
116a44fd1c Let BarcodeImageWriterTask's constructor be package private 2017-07-11 23:13:46 -04:00
Branden Archer
b6190d3250 Merge pull request #125 from arno-github/patch-3
French update
2017-07-03 15:55:48 -04:00
arno-github
82c40408f7 French update 2017-07-03 21:10:21 +02:00
Branden Archer
c6c9fb763a Merge pull request #124 from arno-github/patch-2
Update french translation
2017-07-02 22:36:30 -04:00
arno-github
dda5dcc652 Update french translation
Cosmetic changes
2017-07-02 20:00:21 +02:00
Branden Archer
d039ad9fa4 Merge pull request #122 from Entze/master
add & refine german translation
2017-06-29 21:44:02 -04:00
Lukas Grassauer
5c70c98eb9 Merge branch 'master' of github.com:brarcher/loyalty-card-locker 2017-06-29 16:28:17 +02:00
Lukas Grassauer
2face21985 add missing and revise de translations 2017-06-29 16:23:10 +02:00
Branden Archer
b38ec7b753 Merge pull request #121 from brarcher/pre-v0.11.1
Update for v0.11.1
2017-06-29 10:13:07 -04:00
Branden Archer
32d5aec76b Update for v0.11.1 2017-06-28 22:49:44 -04:00
Branden Archer
bdbcaf4b1e Merge pull request #120 from brarcher/handleConfigChanges
handle orientation and resize config change for IntroActivity
2017-06-28 22:48:50 -04:00
Branden Archer
f54905f218 handle orientation and resize config change for IntroActivity
If orientation and resize are not handled if the screen is
rotate or resized the IntroSlide fragment loses its layout,
and when it tries inflates the layout it fails to do so.
2017-06-28 22:42:02 -04:00
Branden Archer
2d3bd4a375 Merge pull request #118 from brarcher/credit
Add credit for save icon & update copyright string format
2017-06-26 22:56:36 -04:00
Branden Archer
de4ab95437 Add credit for save icon in about dialog 2017-06-26 22:10:09 -04:00
Branden Archer
09fba71710 Fix string format for copyright year
The year is an integer, but the format was for a string.
2017-06-26 22:10:09 -04:00
Branden Archer
2b84762b99 Merge pull request #117 from brarcher/changelog
Update CHANGELOG for v0.11
2017-06-26 22:07:38 -04:00
Branden Archer
3a4850d5cf Merge branch 'master' into changelog 2017-06-26 21:59:46 -04:00
Branden Archer
5bc8a915d6 Update CHANGELOG for v0.11 2017-06-26 21:59:36 -04:00
Branden Archer
15cbd5b641 Merge pull request #116 from brarcher/pre-v0.11
Update for v0.11 release
2017-06-26 21:59:07 -04:00
Branden Archer
d980079d76 Update for v0.11 release 2017-06-26 21:44:16 -04:00
Branden Archer
e58cbd972e Merge pull request #112 from brarcher/readme
Add attribution for save icon in README
2017-06-24 21:38:15 -04:00
Branden Archer
1fbecd439b Add attribution for save icon in README 2017-06-22 22:16:42 -04:00
Branden Archer
707306ac81 Merge pull request #111 from brarcher/readme
Update README with new screenshots
2017-06-21 23:42:12 -04:00
Branden Archer
86152586cd Update README with new screenshots 2017-06-21 23:32:09 -04:00
Branden Archer
fa33e4873e Merge pull request #109 from Airon90/patch-1
Update strings.xml
2017-06-20 22:43:40 -04:00
Branden Archer
24e4ef2d9b Merge branch 'master' into patch-1 2017-06-20 22:35:04 -04:00
Branden Archer
eac4142392 Escape ' in strings.xml 2017-06-20 22:23:33 -04:00
Branden Archer
f38a499bd8 Merge pull request #110 from brarcher/french
Update French translations
2017-06-20 22:21:45 -04:00
Branden Archer
0b8f52b87e Update French translations
Provided by GitHub user arno-github.
2017-06-20 22:14:27 -04:00
Michael Moroni
c8255a2648 Update strings.xml
Updated Italian (it) translation
2017-06-20 19:41:13 +02:00
Branden Archer
f28326e363 Merge pull request #108 from brarcher/intro
First run intro wizard
2017-06-18 23:46:37 -04:00
Branden Archer
a9952d021b Update to Robolectric 3.3.2
This is necessary, as Robolectric 3.0 did not work
with the newer Android SDK. Further, Robolectric 3.3.2
did not like running tests at SDK 17, so this was
also bumped.
2017-06-18 23:39:02 -04:00
Branden Archer
9274339be8 Add intro wizard for application 2017-06-18 23:37:51 -04:00
Branden Archer
18caa01089 Move the ic_launcher.xcf file into a separate directory 2017-06-18 23:05:23 -04:00
Branden Archer
ac495e1bf2 Add missing card add menu
There was no save button when creating a new card.
2017-06-18 23:05:22 -04:00
Branden Archer
a8eecb8841 Merge pull request #107 from brarcher/layout-and-colors
Layout and colors
2017-06-18 22:02:20 -04:00
Branden Archer
abdde45669 Update card view layout
This layout uses a TableLayout and shows lines dividing the
different parts of an entry.
2017-06-18 20:47:37 -04:00
Branden Archer
13b2faab39 Use save button in action bar 2017-06-18 15:37:40 -04:00
Branden Archer
433d31c30f Add super call in onCreateOptionsMenu 2017-06-18 15:37:40 -04:00
Branden Archer
fa7dcd253a Change menus names to focus where they are, not what they contain 2017-06-18 15:37:40 -04:00
Branden Archer
ac057f50e2 Change color palette to better match icon
The green primary color was a bit of contrast with the icons grey.
2017-06-18 15:37:40 -04:00
Branden Archer
ef33c7a33f Merge pull request #106 from brarcher/french-strings
Updated French strings.xml
2017-06-18 14:36:38 -04:00
Branden Archer
6028f7d674 Updated French strings.xml
Contributed by arno-github
2017-06-18 14:30:55 -04:00
Branden Archer
da5f999eed Merge pull request #104 from brarcher/edit-card
Update "Enter card" button if card id exists
2017-06-11 15:13:38 -04:00
Branden Archer
0a9124c834 Update "Enter card" button if card id exists
If someone is editing an existing card, or is adding a card
and wants to change the value, update the name of the button
from "Enter card" to "Edit card", to give the impression that
a new card id need not by typed from scratch.
2017-06-11 15:06:10 -04:00
Branden Archer
c173012955 Merge pull request #103 from brarcher/limit-width
Limit width of barcodes
2017-06-11 15:05:27 -04:00
Branden Archer
64db9e593d Limit width of barcodes
To reduce the amount of memory used in generating barcodes, limit
the width of the barcodes to no more than 3x the height.
This should be plenty sufficient to generate a usable barcode.

On devices with a really long width generating a barcode of 200x2560
is not useful and can result in an OutOfMemoryError.
2017-06-11 14:56:17 -04:00
Branden Archer
acde4ef181 Update to zxing 3.3.0 and zxing-android 3.5.0 2017-06-11 14:55:39 -04:00
Branden Archer
807ea25ac9 Merge pull request #102 from brarcher/privacy-policy
Create privacy-policy.md
2017-06-10 15:40:50 -04:00
Branden Archer
ad4476fead Create privacy-policy.md
This is required by Google for apps in the play store
which use certain permissions, such as the camera.
2017-06-10 15:33:25 -04:00
Branden Archer
f2c0e3904f Merge pull request #98 from brarcher/travis
Travis-CI changes
2017-05-20 18:07:47 -04:00
Branden Archer
0eeadbf77f Build only the release variant
Building both debug and release was unnecessary, and only
increased the time to validate a pull request
2017-05-20 13:36:23 -04:00
Branden Archer
c0e95bd4da do not emit xml results
gradle will output the failures, so this is unnecessary
2017-05-20 13:35:57 -04:00
Branden Archer
b7e8b8d36e Merge pull request #96 from brarcher/about
About
2017-03-06 21:49:53 -05:00
Branden Archer
2bca8b738a Change about dialog info to use map instead of String[][] 2017-03-05 14:42:19 -05:00
Branden Archer
2b617ceca5 Remove mention of Freepik icons from README
The main application icon was replaced, and the icon from
Freepik is no longer used.
2017-03-05 14:42:19 -05:00
Branden Archer
29838f4f73 Merge pull request #95 from brarcher/readme
Add Google Play badge
2017-03-04 00:08:04 -05:00
Branden Archer
1403d4a98e Add Google Play badge
Adds a Google Play badge and updated the F-Droid badge to match
2017-03-03 23:56:43 -05:00
Branden Archer
c4b860bb3d Merge pull request #94 from brarcher/pre-populate-enter-card
Pre-populate Barcode Selector
2017-02-12 22:48:35 -05:00
Branden Archer
2768f4c590 Start with all barcode images visible
If the user enters the barcode selector view with a pre-existing
id, the barcodes are immediately drawn. However, if the ImageViews
were invisible their initial size is not known, and as such the
generated barcode images are fuzzy or not as expected. To avoid this,
all images are set to be visible. The expectation is that once the
barcodes are drawn for the first time if they are invalid the images
will be made invisible anyway.
2017-02-12 22:42:25 -05:00
Branden Archer
8660d4e218 Pre-populate card id in Barcode Selector
If a user is attempting to update a card id of an existing card,
before when the Enter Card button was selected the barcode selector
view would be empty. To be more useful, the view will now be
pre-populated with the existing card id.
2017-02-12 22:42:25 -05:00
Branden Archer
596d899449 Merge pull request #93 from brarcher/changelog
Update changelog for v0.10
2017-02-12 15:53:25 -05:00
Branden Archer
0db5a6f803 Update changelog for v0.10 2017-02-12 15:40:20 -05:00
Branden Archer
bbd3782bf2 Merge pull request #92 from brarcher/pre-v0.10
Update for v0.10 release
2017-02-12 15:35:18 -05:00
Branden Archer
b69f2d470b Update for v0.10 release 2017-02-12 15:25:49 -05:00
Branden Archer
ce4dc01c9d Merge pull request #91 from brarcher/view-layout
View layout
2017-02-11 22:43:58 -05:00
Branden Archer
5cea7eba6c Allow text to be selectable in view mode
When text is long clicked it is now selectable, so that one can
copy it to the clipboard.
2017-02-11 22:33:57 -05:00
Branden Archer
0f7d64bed6 Show card data in a TextView when viewing
Earlier when a card was viewed it was shown in an EditView which
was disabled. This was not optimal, as the resulting text was
dimmed. To help the text be easier to read, a TextView is now
used to display the data.

In addition, the layout of the card viewing screen has been adjusted
2017-02-11 22:33:57 -05:00
Branden Archer
330b6ecd54 Merge pull request #87 from brarcher/string-correction
String correction
2017-01-28 19:58:56 -05:00
Branden Archer
3f5bcbb941 Add log message when exporting data 2017-01-28 16:22:14 -05:00
Branden Archer
9c74b2a264 Correct typo in comment 2017-01-28 16:22:14 -05:00
Branden Archer
dfc804fbe5 correct "Use external application" title
The title mistakenly said filesystem.
2017-01-28 16:22:14 -05:00
Branden Archer
fa73a30bf4 Use string const for button
There is a string for the "Use external application" button,
but it was not used.
2017-01-28 16:22:14 -05:00
Branden Archer
449419b647 Merge pull request #85 from brarcher/no-download-extras
Remove downloading extras during travis build
2017-01-17 21:43:35 -05:00
Branden Archer
36916b622b Remove downloading extras during travis build
The application does not use google play and other extra libraries,
so downloading them during the build should be unnecessary.
2017-01-17 21:25:50 -05:00
Branden Archer
03f97374bd Merge pull request #84 from brarcher/export-filename
Change default import/export file name
2017-01-17 21:24:35 -05:00
Branden Archer
8650878d95 Change default import/export file name
The application is now "Loyalty Card Keychain", but "Locker"
still appears in the application's default import/export
file. Updating the file to match the new name of the application.
2017-01-17 21:06:07 -05:00
Branden Archer
defb8793f6 Merge pull request #83 from brarcher/changelog
Update CHANGELOG for v0.9
2017-01-17 20:58:12 -05:00
Branden Archer
c6b7fd6032 Update CHANGELOG for v0.9 2017-01-17 20:47:31 -05:00
Branden Archer
5960f03f9b Merge pull request #82 from brarcher/pre-v0.9
Update for release v0.9
2017-01-16 23:07:37 -05:00
Branden Archer
b1df0c0baf Update for release v0.9 2017-01-16 22:53:21 -05:00
Branden Archer
bfb131c811 Merge pull request #81 from brarcher/keychain
Update the name "Locker" to "Keychain"
2017-01-16 22:50:36 -05:00
Branden Archer
4580ee8017 Update the "Locker" name to "Keychain"
The icon on the application is no longer a locker, so the
Locker part of the name no longer makes sense. Changing it
to Keychain, as it may make more sense.
2017-01-16 22:44:11 -05:00
Branden Archer
d76a6d79d7 Merge pull request #76 from brarcher/select-import-file
Select import file, allow exported file to be sent
2017-01-16 22:30:54 -05:00
Branden Archer
b38e035fb6 Allow sending newly exported data
With this change once data is exported it can be sent to
another application.

Translation contributions:
Clonewayx: cs
PanderMusubi: nl
pbeckmann: de
Airon90: it
arno-github : fr

# Conflicts:
#	app/src/main/res/values-it/strings.xml
2017-01-16 22:23:08 -05:00
Branden Archer
eeb41376c5 Add ability to select file to import
To date the only way to import a file is to hope the exported file
is in the correct location. To remove this requirement, this change
checks if there are any activities available on the system which
can look in the file system. If there are, new options for
importing data will be available.

In addition, the import/export activity's layout was updated,
adding more explanation.

Translation contributions:
Clonewayx: cs
PanderMusubi: nl
pbeckmann: de
Airon90: it
arno-github : fr
2017-01-16 22:23:08 -05:00
Branden Archer
89f649e5ef Correct TAG name 2017-01-16 22:23:08 -05:00
Branden Archer
4bde8f49b1 update gradle and android plugin 2017-01-16 22:23:08 -05:00
Branden Archer
87c1bbdbf4 Merge pull request #80 from arno-github/arno-github-patch-4
New french strings update
2016-12-10 11:17:16 -05:00
arno-github
7029818789 Update strings.xml 2016-12-10 15:10:41 +01:00
arno-github
b64c29bf96 Update french strings 2016-12-10 14:30:13 +01:00
arno-github
24317f4d99 Update strings.xml 2016-12-10 14:26:20 +01:00
arno-github
b9d3699038 Update strings.xml 2016-12-10 14:21:19 +01:00
arno-github
ed3c9ceb50 Update strings.xml 2016-12-10 14:13:03 +01:00
arno-github
0a938628cb Update strings.xml 2016-12-10 11:53:44 +01:00
arno-github
5720da1581 Merge pull request #1 from arno-github/arno-github-patch-1
Create strings.xml
2016-12-10 11:52:03 +01:00
arno-github
5393903872 Create strings.xml 2016-12-10 11:50:20 +01:00
Branden Archer
8e0a4b978d Merge pull request #75 from betsythefc/master
Icon V2
2016-12-06 21:53:07 -05:00
betsythefc
df21c2577d Improved icon with keyring hole 2016-12-05 20:47:52 -08:00
betsythefc
a7d5b6fa0c New icon V2 2016-12-05 11:35:40 -08:00
betsythefc
b49501e66a Merge remote-tracking branch 'brarcher/master' 2016-12-05 11:30:54 -08:00
Branden Archer
65d0f57358 Merge pull request #62 from techwebpd/patch-1
Lithuanian strings.xml
2016-12-03 11:35:32 -05:00
Branden Archer
927ac61912 Merge branch 'master' into patch-1 2016-12-03 11:29:52 -05:00
Branden Archer
fd2b84f318 Remove string that did not need translating. 2016-12-03 11:29:44 -05:00
betsythefc
ed4afed538 New Icon 2016-12-02 11:19:18 -08:00
Branden Archer
007427023b Merge pull request #70 from brarcher/changelog
Create CHANGELOG.md
2016-11-22 22:44:13 -05:00
Branden Archer
b4ff68eddb Create CHANGELOG.md
Per-request from:
https://github.com/brarcher/loyalty-card-locker/issues/48
2016-11-22 22:35:49 -05:00
Branden Archer
151b81daa7 Merge pull request #69 from brarcher/pre-v0.8
Update for release v0.8
2016-11-22 22:21:35 -05:00
Branden Archer
c090af1c7d Update for release v0.8 2016-11-22 22:13:29 -05:00
Branden Archer
54a0f89821 Merge pull request #66 from lgasp/patch-1
Small update to italian translation
2016-11-06 15:07:58 -05:00
lgasp
f01058d5b8 Update strings.xml 2016-11-05 11:19:48 +01:00
lgasp
e01c77681b proposing less confusing trnalsation fo r button
"Scan code" is translated as "salva tessera" that corresponds to English "save card",  that is similar to the button " save" on the same screen.
"Scansione codice"is a more literal, and hopefully more clear, translational
2016-11-04 19:48:54 +01:00
Branden Archer
c49ecdca56 Merge pull request #57 from pbeckmann/master
Add a german translation.
2016-11-02 14:37:18 -04:00
Peer Beckmann
b7672e5247 Merge branch 'master' into master 2016-11-01 09:50:18 +01:00
techwebpd
71777a74fd Lithuanian strings.xml
lithuanian translation
2016-10-28 16:28:19 +03:00
Branden Archer
bebab039d7 Merge pull request #60 from brarcher/screenshots
Simplify screenshots in README.md
2016-09-12 00:10:06 -04:00
Branden Archer
588a573878 Simplify screenshots in README.md 2016-09-11 23:56:32 -04:00
Branden Archer
a9bbf8fb65 Merge pull request #59 from brarcher/screenshots
Add screenshots to README.md
2016-08-28 15:44:22 -04:00
Branden Archer
175135d695 Add screenshots to README.md
Requested in:
https://github.com/brarcher/loyalty-card-locker/issues/56
2016-08-28 15:30:23 -04:00
Branden Archer
9f08d07a6a Merge pull request #58 from Clonewayx/patch-1
Czech translation
2016-08-27 13:49:07 -04:00
Clonewayx
07f50b1544 Update and rename strings.xml to strings.xml
remove untranslatable line
2016-08-27 18:04:00 +02:00
Clonewayx
584064a4a2 Czech translation 2016-08-27 06:58:12 +02:00
Peer Beckmann
484f357a82 Add a german translation. 2016-08-25 19:57:55 +02:00
Branden Archer
e32b40a024 Merge pull request #55 from brarcher/delete-confirmation
Delete confirmation
2016-07-22 08:04:15 -04:00
Branden Archer
010b88d477 Attempt to force travis builds on non-container infrastructure
There is an issue with running the travis builds. The root
cause is unknown. Attempting to use the non-container
version of the infrastructure to see if the issue persists.
2016-07-22 07:56:23 -04:00
Branden Archer
fe0bb7b870 Add a confirmation before deleting card
https://github.com/brarcher/loyalty-card-locker/issues/53
2016-07-22 07:56:22 -04:00
Branden Archer
925f780599 Merge pull request #54 from brarcher/screen-brightness
Adjust brightness to its maximum when displaying a card
2016-07-20 00:04:42 -04:00
Branden Archer
9492d206d9 Adjust brightness to its maximum when displaying a card
Some users have mentioned that a barcode scanner has the best
success when the screen is at its brightest. To enable the best
results, the screen brightness will now be adjusted to its
maximum when displaying a loyalty card.

https://github.com/brarcher/loyalty-card-locker/issues/21
2016-07-19 23:42:36 -04:00
Branden Archer
a32415b4d2 Reduce the number of cards tested in import/export tests
There is no need to have so many cards tested in the test, just
a few will do to prove the point.
2016-07-19 23:41:31 -04:00
202 changed files with 9944 additions and 1193 deletions

27
.github/workflows/android.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: Android CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Build
run: ./gradlew assembleRelease
- name: Check lint
run: ./gradlew lintRelease
- name: Run unit tests
run: ./gradlew testReleaseUnitTest
- name: FindBugs
run: ./gradlew findbugs

View File

@@ -0,0 +1,31 @@
name: Compress Images on Push to Master
on:
push:
branches:
- master
paths:
- '**.jpg'
- '**.jpeg'
- '**.png'
- '**.webp'
jobs:
build:
name: calibreapp/image-actions
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@master
- name: Compress Images
id: calibre
uses: calibreapp/image-actions@master
with:
githubToken: ${{ secrets.GITHUB_TOKEN }}
compressOnly: true
- name: Create New Pull Request If Needed
if: steps.calibre.outputs.markdown != ''
uses: peter-evans/create-pull-request@master
with:
title: Compressed Images
branch-suffix: timestamp
commit-message: Compressed Images
body: ${{ steps.calibre.outputs.markdown }}

2
.gitignore vendored
View File

@@ -6,3 +6,5 @@
.DS_Store
/build
/captures
/app/release
/app/debug

View File

@@ -1,23 +0,0 @@
language: android
android:
components:
# Uncomment the lines below if you want to
# use the latest revision of Android SDK Tools
- platform-tools
- tools
# The BuildTools version used by your project
- build-tools-23.0.2
# The SDK version used to compile your project
- android-23
# Additional components
- extra
script: ./gradlew build lint findbugs test
after_failure:
- cat app/build/outputs/lint-results.xml
- cat app/build/reports/findbugs/findbugs.html
- cat app/build/reports/tests/debug/index.html

421
CHANGELOG.md Normal file
View File

@@ -0,0 +1,421 @@
# Changelog
## 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:
- Add swiping between groups on the home screen
- Fix crash with cards lacking header colour
## v1.1.0 (2020-11-11)
Changes:
- Improved edit UI
- Removed header text colour option (now automatically generated based on brightness)
- Updated translations
## v1.0.1 (2020-11-07)
Changes:
- Fix crash in search with no groups
## v1.0 (2020-11-06)
Changes:
- Added rounded edges to card icons on main overview
- Added support for grouping entries
## v0.29 (2020-10-29)
Changes:
- Rebrand to Catima
- Removed intro
- Add floating action buttons
- Fix Android 5 crash when opening About screen
- Add favourites support
- Fix disabled auto-rotate being ignored
## v0.28 (2020-03-09)
Changes:
- Fix barcode centering when exiting full screen ([#351](https://github.com/brarcher/loyalty-card-locker/pull/351))
- Allow backup export location to be selected ([#352](https://github.com/brarcher/loyalty-card-locker/pull/352))
- Update translations ([#357](https://github.com/brarcher/loyalty-card-locker/pull/357)) & ([#362](https://github.com/brarcher/loyalty-card-locker/pull/362))
## v0.27 (2020-01-26)
Changes:
- Tapping on a barcode now moves it to the top of the screen ([#348](https://github.com/brarcher/loyalty-card-locker/pull/348))
- Add white space around barcodes to improve scanning in dark mode ([#328](https://github.com/brarcher/loyalty-card-locker/issues/328))
- Fix swapped import buttons. ([#346](https://github.com/brarcher/loyalty-card-locker/pull/346))
## v0.26.1 (2020-01-09)
Changes:
- Fix issue with sharing cards without background color ([#343](https://github.com/brarcher/loyalty-card-locker/pull/343))
## v0.26 (2020-01-05)
Changes:
- Add ability to search for a card ([#320](https://github.com/brarcher/loyalty-card-locker/pull/320))
- Add ability to share and receive loyalty cards ([#321](https://github.com/brarcher/loyalty-card-locker/pull/321))
- Dark mode support ([#322](https://github.com/brarcher/loyalty-card-locker/pull/322))
- Loyalty cards can now be barcodeless (e.g. not have a barcode) ([#324](https://github.com/brarcher/loyalty-card-locker/pull/324))
- Notes can span multiple lines ([#326](https://github.com/brarcher/loyalty-card-locker/pull/326))
- Improvements with the sizing of notes ([#319](https://github.com/brarcher/loyalty-card-locker/pull/319))
- Improve notification and app icon visibility ([#330](https://github.com/brarcher/loyalty-card-locker/pull/330))
- Update target SDK to Android 10
- Improve the following translations:
- German
- Italian
- Dutch
- Polish
- Russian
## v0.25.4 (2019-10-04)
Changes:
- Enable app backups
- Update French and Slovenian translations
## v0.25.3 (2019-03-02)
Changes:
- Update Russian translations
## v0.25.2 (2019-01-05)
Changes:
- Update and add translations
## v0.25.1 (2018-10-14)
Changes:
- Fix creating new card by manually entering barcode ([issue #272](https://github.com/brarcher/loyalty-card-locker/issues/272))
## v0.25 (2018-10-07)
Changes:
- Sort card list case insensitive ([pull #266](https://github.com/brarcher/loyalty-card-locker/pull/266))
- Add setting to lock orientation for all cards ([pull #269](https://github.com/brarcher/loyalty-card-locker/pull/269)
## v0.24 (2018-07-31)
Changes:
- Add a setting to control screen brightness when displaying a barcode ([pull #259](https://github.com/brarcher/loyalty-card-locker/pull/259))
- Add Greek translations ([pull #252](https://github.com/brarcher/loyalty-card-locker/pull/252))
- Add Slovenian translations ([pull #260](https://github.com/brarcher/loyalty-card-locker/pull/260))
- Update translations ([pull #260](https://github.com/brarcher/loyalty-card-locker/pull/260), [pull #254](https://github.com/brarcher/loyalty-card-locker/pull/254))
## v0.23.4 (2018-05-12)
Changes:
- Fix Spanish translations ([pull #244](https://github.com/brarcher/loyalty-card-locker/pull/244))
- Update translations ([pull #244](https://github.com/brarcher/loyalty-card-locker/pull/244))
## v0.23.3 (2018-05-05)
Changes:
- Added translations
- Polish ([pull #232](https://github.com/brarcher/loyalty-card-locker/pull/232))
- Spanish ([pull #232](https://github.com/brarcher/loyalty-card-locker/pull/232))
- Slovak ([pull #232](https://github.com/brarcher/loyalty-card-locker/pull/232))
- Updated translations ([pull #239](https://github.com/brarcher/loyalty-card-locker/pull/239))
## v0.23.2 (2018-03-11)
Changes:
- Reduce min SDK from 17 to 15. ([pull #226](https://github.com/brarcher/loyalty-card-locker/pull/226))
- Remove usage of legacy apache library, used only in unit tests but no longer needed. ([pull #225](https://github.com/brarcher/loyalty-card-locker/pull/225))
## v0.23.1 (2018-03-07)
Changes:
- Prevent crash when rendering a barcode exhausts the application's memory. ([pull #219](https://github.com/brarcher/loyalty-card-locker/pull/219))
## v0.23 (2018-02-28)
Changes:
- Reduce space in header when viewing a card. ([pull #213](https://github.com/brarcher/loyalty-card-locker/pull/213))
- Disable beep when scanning a barcode. ([pull #216](https://github.com/brarcher/loyalty-card-locker/pull/216))
## v0.22 (2018-02-19)
Changes:
- Update translations. ([pull #208](https://github.com/brarcher/loyalty-card-locker/pull/208))
- Barcode rendering updates: ([pull #209](https://github.com/brarcher/loyalty-card-locker/pull/209))
- Reload card view activity when screen is rotated, so barcode image is correct size.
- Render 1D barcodes in a larger space, allowing them to better fill the screen.
## v0.21 (2018-02-17)
Changes:
- Add quiet space at the start/end of barcodes. ([pull #200](https://github.com/brarcher/loyalty-card-locker/pull/200))
- Add options to configure the colors used for the store name font and background. ([pull #203](https://github.com/brarcher/loyalty-card-locker/pull/203))
- Add options to adjust font sizes on the card listing page and single card page. ([pull #204](https://github.com/brarcher/loyalty-card-locker/pull/204))
## v0.20 (2018-02-10)
Changes:
- Changes to Card view to display the note, allow the card ID to take multiple lines, and show the store name. ([pull #197](https://github.com/brarcher/loyalty-card-locker/pull/197))
## v0.19 (2018-02-01)
Changes:
- Improved layout for card list. ([pull #188](https://github.com/brarcher/loyalty-card-locker/pull/188))
- Improved layout when viewing a card. ([pull #190](https://github.com/brarcher/loyalty-card-locker/pull/190))
## v0.18.1 (2018-01-24)
Changes:
- Workaround crash during install on some Android versions (likely Android 5 and below). ([pull #184](https://github.com/brarcher/loyalty-card-locker/pull/184))
## v0.18 (2018-01-19)
Changes:
- Fix crash when importing certain types of corrupted CSV files. ([pull #177](https://github.com/brarcher/loyalty-card-locker/pull/177))
- Fix importing backups directly from the file system. ([pull #180](https://github.com/brarcher/loyalty-card-locker/pull/180))
- Fix importing backups from certain types of content providers. ([pull #179](https://github.com/brarcher/loyalty-card-locker/pull/179))
## v0.17 (2018-01-11)
Changes:
- Fix issue on Android SDK 24+ where using the file chooser import option would cause a crash. ([pull #170](https://github.com/brarcher/loyalty-card-locker/pull/170))
- New icon and color scheme. ([pull #171](https://github.com/brarcher/loyalty-card-locker/pull/171))
## v0.16 (2017-11-29)
Changes:
- Add support for adding loyalty card shortcuts from the launcher/homescreen. ([pull #161](https://github.com/brarcher/loyalty-card-locker/pull/161))
- Remove support for adding loyalty card shortcuts from the app itself. This removes the need for the shortcut permission. ([pull #163](https://github.com/brarcher/loyalty-card-locker/pull/163))
## v0.15 (2017-11-25)
Changes:
- Add support for adding shortcuts to home screen when adding or editing a card. ([pull #155](https://github.com/brarcher/loyalty-card-locker/pull/155))
- Remove widget, as it was a poor substitute for shortcuts. ([pull #155](https://github.com/brarcher/loyalty-card-locker/pull/155))
- Fix exporting backups on Android 7+. ([pull #153](https://github.com/brarcher/loyalty-card-locker/pull/153))
- Report more accurate mime type when exporting backup data. ([pull #156](https://github.com/brarcher/loyalty-card-locker/pull/156))
- Fix bug where a card could not be edited. ([pull #155](https://github.com/brarcher/loyalty-card-locker/pull/155))
## v0.14 (2017-10-26)
Changes:
- Add support for app shortcuts (Android 7.1+), where the most recently used cards will appear as shortcuts. ([pull #145](https://github.com/brarcher/loyalty-card-locker/pull/145))
- Add a widget which works like a pinned app shortcut, to support devices which run below Android 7.1. ([pull #142](https://github.com/brarcher/loyalty-card-locker/pull/142))
## v0.13 (2017-07-25)
Changes:
- Add screen rotation lock menu option when displaying a card. If locked, the screen will transition to its "natural" orientation and further screen rotation will be blocked. ([pull #128](https://github.com/brarcher/loyalty-card-locker/pull/128))
- If a card is selected from the main screen but cannot be loaded, the application fails gracefully and posts a message. ([pull #132](https://github.com/brarcher/loyalty-card-locker/pull/132))
- Fix case where layout IDs for intro wizard could not be found. ([pull #128](https://github.com/brarcher/loyalty-card-locker/pull/128))
## v0.12 (2017-07-16)
Changes:
- A change in v0.11 reduced the memory usage of barcode drawing, but affected the barcode dimensions. This is now changed to maintain the barcode dimensions while reducing memory usage. ([pull #126](https://github.com/brarcher/loyalty-card-locker/pull/126))
- Update German and French translations. ([pull #122](https://github.com/brarcher/loyalty-card-locker/pull/122), [pull #124](https://github.com/brarcher/loyalty-card-locker/pull/124), [pull #125](https://github.com/brarcher/loyalty-card-locker/pull/125))
## v0.11.1 (2017-06-29)
Changes:
- Prevent a crash when rotation the screen in the first run intro wizard.
## v0.11 (2017-06-26)
Improvements:
- When editing a card ID, pre-populate the existing ID to start. ([pull #94](https://github.com/brarcher/loyalty-card-locker/pull/94))
- Limit the width of generated barcodes to reduce memory usage and out of memory errors. ([pull #103](https://github.com/brarcher/loyalty-card-locker/pull/103))
- When editing a card, change the "Enter Card" button to say "Edit Card" if a card ID already exists. ([pull #104](https://github.com/brarcher/loyalty-card-locker/pull/104))
- Change the color scheme to be softer and compatible with the app icon, and change the layout when viewing a card to be cleaner. ([pull #107](https://github.com/brarcher/loyalty-card-locker/pull/107))
- Add an intro wizard which launches on the app's first launch. ([pull #108](https://github.com/brarcher/loyalty-card-locker/pull/108))
## v0.10 (2017-02-12)
Improvements:
- Changed the default import/export filename. ([pull #84](https://github.com/brarcher/loyalty-card-locker/pull/84))
- Correct string on the import/export page. ([pull #87](https://github.com/brarcher/loyalty-card-locker/pull/87))
- Improve layout of card view page. The text should be easier to read, and is selectable with a long click. ([pull #91](https://github.com/brarcher/loyalty-card-locker/pull/91))
## v0.9 (2017-01-17)
The "Locker" part of the name was not intuitive. To help remedy this a new application icon was created by betsythefc which better represents the purpose of the application: to store loyalty cards which use barcodes. Along with this new icon the name of the application has been changed to "Loyalty Card Keychain".
Additional features/improvements:
- Importing/Exporting cards was changed to be more flexible. ([pull #76](https://github.com/brarcher/loyalty-card-locker/pull/76))
- Translations for Lithuanian added. ([pull #62](https://github.com/brarcher/loyalty-card-locker/pull/62))
- Translations for French added. ([pull #80](https://github.com/brarcher/loyalty-card-locker/pull/80))
## v0.8 (2016-11-22)
New features/improvements:
- Screen brightness increased to its maximum when displaying a card, to help barcode scanners successfully capture the barcode. ([pull #54](https://github.com/brarcher/loyalty-card-locker/pull/54))
- Add a delete confirmation when deleting a card. ([pull #55](https://github.com/brarcher/loyalty-card-locker/pull/55))
- Add translations for German ([pull #57](https://github.com/brarcher/loyalty-card-locker/pull/57)) and Czech ([pull #58](https://github.com/brarcher/loyalty-card-locker/pull/58)).
- Clarification change for Italian translation. ([pull #66](https://github.com/brarcher/loyalty-card-locker/pull/66))
## v0.7 (2016-07-14)
New features/improvements:
- Long-click of a card brings up option to copy card ID to the clipboard. ([pull #49](https://github.com/brarcher/loyalty-card-locker/issues/49))
Bug fixes:
- Back button on Input/Export view now works, moving user to main view
## v0.6 (2016-05-23)
New features/improvements:
- Allow user to enter barcode manually. If a user elects to enter a barcode manually, a list of all valid and supported barcode images is displayed. The user then may select the barcode image which matches what the user wants. [issue #33](https://github.com/brarcher/loyalty-card-locker/issues/33), [pull #44](https://github.com/brarcher/loyalty-card-locker/pull/44)
Bug fixes:
- Resolve issue where some displayed barcodes were blurry. ([issue #37](https://github.com/brarcher/loyalty-card-locker/issues/37))
## v0.5 (2016-05-16)
New features/improvements:
- An about dialog can be opened from the main screen, which gives details about the application and project on GitHub ([issue #19](https://github.com/brarcher/loyalty-card-locker/issues/19))
- Allow loyalty card information to be imported from/exported to a CSV file in external storage ([issue #36](https://github.com/brarcher/loyalty-card-locker/issues/36), [issue #20](https://github.com/brarcher/loyalty-card-locker/issues/20))
## v0.4 (2016-04-09)
New features/improvements:
- Dutch translation
- Allow name field to be editable after adding loyalty card
- Add an optional note field
Bug fixes:
- Resolve all issues identified by FindBugs and require all FindBugs issues be resolved prior to pull request acceptance
## v0.3 (2016-02-11)
- Now officially supports the following list of 1D and 2D barcodes:
- AZTEC
- CODABAR
- CODE_39
- CODE_128
- DATA_MATRIX
- EAN_8
- EAN_13
- ITF
- PDF_417
- QR_CODE
- UPC_A
- Generated barcodes are larger, easier to scan from a scanning device
## v0.2 (2016-02-07)
- Italian translations
- Support for all 1D barcode types. (Originally only product 1D barcodes were supported)
- Add required camera permission, which was initially missing.
## v0.1 (2016-01-30)
- Ability to create/edit/delete loyalty cards
- Capture barcode of loyalty card using a camera
- Display captured barcode, for scanning at a store

91
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,91 @@
How to Submit Patches to the Loyalty Card Keychain Project
===============================================================================
https://github.com/brarcher/budget-watch
This document is intended to act as a guide to help you contribute to the
Loyalty Card Keychain project. It is not perfect, and there will always be exceptions
to the rules described here, but by following the instructions below you
should have a much easier time getting your work merged with the upstream
project.
## Test Your Code
There are four possible tests you can run to verify your code. The first
is unit tests, which check the basic functionality of the application, and
can be run by gradle using:
# ./gradlew testReleaseUnitTest
The second and third check for common problems using static analysis.
These are the Android lint checker, run using:
# ./gradlew lintRelease
and FindBugs, run using:
# ./gradlew findbugs
The final check is by testing the application on a live device and verifying
the basic functionality works as expected.
## Make Sure Your Code is Tested
The Loyalty Card Keychain code uses a fair number of unit tests to verify that
the basic functionality is working. Submissions which add functionality
or significantly change the existing code should include additional tests
to verify the proper operation of the proposed changes.
## Explain Your Work
At the top of every patch you should include a description of the problem you
are trying to solve, how you solved it, and why you chose the solution you
implemented. If you are submitting a bug fix, it is also incredibly helpful
if you can describe/include a reproducer for the problem in the description as
well as instructions on how to test for the bug and verify that it has been
fixed.
## Sign Your Work
The sign-off is a simple line at the end of the patch description, which
certifies that you wrote it or otherwise have the right to pass it on as an
open-source patch. The "Developer's Certificate of Origin" pledge is taken
from the Linux Kernel and the rules are pretty simple:
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
... then you just add a line to the bottom of your patch description, with
your real name, saying:
Signed-off-by: Random J Developer <random@developer.example.org>
## Submit Patch(es) for Review
Finally, you will need to submit your patches so that they can be reviewed
and potentially merged into the main Loyalty Card Keychain repository. The preferred
way to do this is to submit a Pull Request to the Loyalty Card Keychain project.
Changes need to apply cleanly onto the master branch and pass all
unit tests and produce no errors during static analysis.

3
Gemfile Normal file
View File

@@ -0,0 +1,3 @@
source "https://rubygems.org"
gem "fastlane"

180
Gemfile.lock Normal file
View File

@@ -0,0 +1,180 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.2)
addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
atomos (0.1.3)
aws-eventstream (1.1.0)
aws-partitions (1.388.0)
aws-sdk-core (3.109.1)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.239.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1.0)
aws-sdk-kms (1.39.0)
aws-sdk-core (~> 3, >= 3.109.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.83.1)
aws-sdk-core (~> 3, >= 3.109.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.1)
aws-sigv4 (1.2.2)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
claide (1.0.3)
colored (1.2)
colored2 (3.1.2)
commander-fastlane (4.4.6)
highline (~> 1.7.2)
declarative (0.0.20)
declarative-option (0.1.0)
digest-crc (0.6.1)
rake (~> 13.0)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
dotenv (2.7.6)
emoji_regex (3.2.0)
excon (0.78.0)
faraday (1.1.0)
multipart-post (>= 1.2, < 3)
ruby2_keywords
faraday-cookie_jar (0.0.7)
faraday (>= 0.8.0)
http-cookie (~> 1.0.0)
faraday_middleware (1.0.0)
faraday (~> 1.0)
fastimage (2.2.0)
fastlane (2.165.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.3, < 3.0.0)
aws-sdk-s3 (~> 1.0)
babosa (>= 1.0.3, < 2.0.0)
bundler (>= 1.12.0, < 3.0.0)
colored
commander-fastlane (>= 4.4.6, < 5.0.0)
dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 4.0)
excon (>= 0.71.0, < 1.0.0)
faraday (~> 1.0)
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (~> 1.0)
fastimage (>= 2.1.0, < 3.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-api-client (>= 0.37.0, < 0.39.0)
google-cloud-storage (>= 1.15.0, < 2.0.0)
highline (>= 1.7.2, < 2.0.0)
json (< 3.0.0)
jwt (>= 2.1.0, < 3)
mini_magick (>= 4.9.4, < 5.0.0)
multipart-post (~> 2.0.0)
plist (>= 3.1.0, < 4.0.0)
rubyzip (>= 2.0.0, < 3.0.0)
security (= 0.1.3)
simctl (~> 1.6.3)
slack-notifier (>= 2.0.0, < 3.0.0)
terminal-notifier (>= 2.0.0, < 3.0.0)
terminal-table (>= 1.4.5, < 2.0.0)
tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
gh_inspector (1.1.3)
google-api-client (0.38.0)
addressable (~> 2.5, >= 2.5.1)
googleauth (~> 0.9)
httpclient (>= 2.8.1, < 3.0)
mini_mime (~> 1.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
signet (~> 0.12)
google-cloud-core (1.5.0)
google-cloud-env (~> 1.0)
google-cloud-errors (~> 1.0)
google-cloud-env (1.4.0)
faraday (>= 0.17.3, < 2.0)
google-cloud-errors (1.0.1)
google-cloud-storage (1.29.1)
addressable (~> 2.5)
digest-crc (~> 0.4)
google-api-client (~> 0.33)
google-cloud-core (~> 1.2)
googleauth (~> 0.9)
mini_mime (~> 1.0)
googleauth (0.14.0)
faraday (>= 0.17.3, < 2.0)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (~> 0.14)
highline (1.7.10)
http-cookie (1.0.3)
domain_name (~> 0.5)
httpclient (2.8.3)
jmespath (1.4.0)
json (2.3.1)
jwt (2.2.2)
memoist (0.16.2)
mini_magick (4.10.1)
mini_mime (1.0.2)
multi_json (1.15.0)
multipart-post (2.0.0)
nanaimo (0.3.0)
naturally (2.2.0)
os (1.1.1)
plist (3.5.0)
public_suffix (4.0.6)
rake (13.0.1)
representable (3.0.4)
declarative (< 0.1.0)
declarative-option (< 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rouge (2.0.7)
ruby2_keywords (0.0.2)
rubyzip (2.3.0)
security (0.1.3)
signet (0.14.0)
addressable (~> 2.3)
faraday (>= 0.17.3, < 2.0)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simctl (1.6.8)
CFPropertyList
naturally
slack-notifier (2.3.2)
terminal-notifier (2.0.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
tty-cursor (0.7.1)
tty-screen (0.8.1)
tty-spinner (0.9.3)
tty-cursor (~> 0.7)
uber (0.1.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.7)
unicode-display_width (1.7.0)
word_wrap (1.0.0)
xcodeproj (1.19.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.3.0)
xcpretty (0.3.0)
rouge (~> 2.0.7)
xcpretty-travis-formatter (1.0.0)
xcpretty (~> 0.2, >= 0.0.7)
PLATFORMS
ruby
DEPENDENCIES
fastlane
BUNDLED WITH
2.1.4

View File

@@ -1,42 +0,0 @@
# Loyalty Card Locker
[![Build Status](https://travis-ci.org/brarcher/loyalty-card-locker.svg?branch=master)](https://travis-ci.org/brarcher/loyalty-card-locker)
[![F-Droid](https://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Get_it_on_F-Droid.svg/160px-Get_it_on_F-Droid.svg.png)](https://f-droid.org/repository/browse/?fdid=protect.card_locker "Loyalty Card Locker on F-Droid")
Stores all of your store loyalty cards on your phone, removing the need to carry them around. Currently the following barcode types are supported:
- AZTEC
- CODABAR
- CODE_39
- CODE_128
- DATA_MATRIX
- EAN_8
- EAN_13
- ITF
- PDF_417
- QR_CODE
- UPC_A
If there is any interest in improving this project, kindly submit a pull request with
proposed changes.
# Building
To build, use the gradle wrapper scripts provided in the top level directory of the project. The following will
compile the application and run all unit tests:
GNU/Linux, OSX, UNIX:
```
./gradlew build
```
Windows:
```
./gradlew.bat build
```
# Thanks
App icon originals by [Freepik](https://www.flaticon.com) and distributed under the [CC BY 3.0](http://creativecommons.org/licenses/by/3.0/) license,
and formatted using [Android Asset Studio](https://romannurik.github.io/AndroidAssetStudio/index.html).

View File

@@ -7,50 +7,65 @@ findbugs {
}
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
compileSdkVersion 29
defaultConfig {
applicationId "protect.card_locker"
minSdkVersion 11
targetSdkVersion 23
versionCode 7
versionName "0.7"
applicationId "me.hackerchick.catima"
minSdkVersion 19
targetSdkVersion 29
versionCode 53
versionName "1.6.1"
vectorDrawables.useSupportLibrary = true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
applicationIdSuffix ".debug"
}
}
lintOptions {
disable "GoogleAppIndexingWarning"
disable "ButtonStyle"
disable "AlwaysShowAction"
disable "MissingTranslation"
disable "MissingPrefix"
}
// This is for Robolectric support for SDK 23
useLibrary 'org.apache.http.legacy'
// Starting with Android Studio 3 Robolectric is unable to find resources.
// The following allows it to find the resources.
testOptions {
unitTests {
includeAndroidResources = true
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.android.support:design:23.1.1'
compile 'com.journeyapps:zxing-android-embedded:3.2.0@aar'
compile 'com.google.zxing:core:3.2.1'
compile 'org.apache.commons:commons-csv:1.2'
compile group: 'com.google.guava', name: 'guava', version: '18.0'
testCompile 'junit:junit:4.12'
testCompile "org.robolectric:robolectric:3.0"
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'com.journeyapps:zxing-android-embedded:4.0.2@aar'
implementation 'com.google.zxing:core:3.3.0'
implementation 'org.apache.commons:commons-csv:1.5'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.jaredrummler:colorpicker:1.0.2'
implementation group: 'com.google.guava', name: 'guava', version: '20.0'
implementation "com.vanniktech:vntnumberpickerpreference:1.0.0"
implementation 'androidx.cardview:cardview:1.0.0'
testImplementation 'junit:junit:4.12'
testImplementation "org.robolectric:robolectric:4.0.2"
}
task findbugs(type: FindBugs, dependsOn: assembleDebug) {
task findbugs(type: FindBugs, dependsOn: 'assembleDebug') {
description 'Run findbugs'
group 'verification'
classes = fileTree('build/intermediates/classes/debug/')
classes = fileTree('build/intermediates/javac/debug/compileDebugJavaWithJavac/classes')
source = fileTree('src/main/java')
classpath = files()

View File

@@ -15,3 +15,10 @@
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
-keepattributes SourceFile,LineNumberTable
# This keep the class and method names the same, for debugging stack traces
-dontobfuscate

View File

@@ -12,4 +12,4 @@ public class ApplicationTest extends ApplicationTestCase<Application>
{
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,8 +17,11 @@
android:name="android.hardware.camera.autofocus"
android:required="false" />
<uses-sdk tools:overrideLibrary="com.google.zxing.client.android" />
<application
android:allowBackup="false"
android:name=".LoyaltyCardLockerApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
@@ -32,22 +36,74 @@
<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"
android:theme="@style/AppTheme.NoActionBar">
</activity>
<activity
android:name=".LoyaltyCardViewActivity"
android:theme="@style/AppTheme.NoActionBar"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateHidden"/>
android:label=""
android:windowSoftInputMode="stateHidden"
android:exported="true"/>
<activity
android:name=".LoyaltyCardEditActivity"
android:theme="@style/AppTheme.NoActionBar"
android:windowSoftInputMode="stateHidden"
android:exported="true">
<intent-filter android:label="@string/app_name">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- Accepts URIs that begin with "https://github.com/brarcher/loyalty-card-locker/” -->
<data android:scheme="https"
android:host="@string/intent_import_card_from_url_host"
android:pathPrefix="@string/intent_import_card_from_url_path_prefix" />
<data android:scheme="https"
android:host="@string/intent_import_card_from_url_host_old"
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"
android:theme="@style/AppTheme.NoActionBar"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateHidden"/>
<activity
android:name=".preferences.SettingsActivity"
android:label="@string/settings"/>
<activity
android:name=".ImportExportActivity"
android:label="@string/importExport"
android:configChanges="orientation|screenSize"
android:theme="@style/AppTheme.NoActionBar"/>
<activity
android:name=".CardShortcutConfigure"
android:label="@string/cardShortcut"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:grantUriPermissions="true"
android:exported="false"
android:authorities="${applicationId}">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider_paths"/>
</provider>
</application>
</manifest>

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

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("VNTNumberPickerPreference", "https://github.com/vanniktech/VNTNumberPickerPreference")
.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

@@ -5,6 +5,7 @@ import android.os.AsyncTask;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatWriter;
@@ -20,28 +21,88 @@ import java.lang.ref.WeakReference;
*/
class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
{
private static final String TAG = "LoyaltyCardLocker";
private static final String TAG = "Catima";
// When drawn in a smaller window 1D barcodes for some reason end up
// squished, whereas 2D barcodes look fine.
private static final int MAX_WIDTH_1D = 1500;
private static final int MAX_WIDTH_2D = 500;
private final WeakReference<ImageView> imageViewReference;
private final WeakReference<TextView> textViewReference;
private final String cardId;
private final BarcodeFormat format;
private final int imageHeight;
private final int imageWidth;
public BarcodeImageWriterTask(ImageView imageView, String cardIdString,
BarcodeFormat barcodeFormat)
BarcodeImageWriterTask(ImageView imageView, String cardIdString,
BarcodeFormat barcodeFormat, TextView textView)
{
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<>(imageView);
textViewReference = new WeakReference<>(textView);
cardId = cardIdString;
format = barcodeFormat;
imageHeight = imageView.getHeight();
imageWidth = imageView.getWidth();
final int MAX_WIDTH = getMaxWidth(format);
if(imageView.getWidth() < MAX_WIDTH)
{
imageHeight = imageView.getHeight();
imageWidth = imageView.getWidth();
}
else
{
// Scale down the image to reduce the memory needed to produce it
imageWidth = MAX_WIDTH;
double ratio = (double)MAX_WIDTH / (double)imageView.getWidth();
imageHeight = (int)(imageView.getHeight() * ratio);
}
}
BarcodeImageWriterTask(ImageView imageView, String cardIdString, BarcodeFormat barcodeFormat)
{
this(imageView, cardIdString, barcodeFormat, null);
}
private int getMaxWidth(BarcodeFormat format)
{
switch(format)
{
// 2D barcodes
case AZTEC:
case DATA_MATRIX:
case MAXICODE:
case PDF_417:
case QR_CODE:
return MAX_WIDTH_2D;
// 1D barcodes:
case CODABAR:
case CODE_39:
case CODE_93:
case CODE_128:
case EAN_8:
case EAN_13:
case ITF:
case UPC_A:
case UPC_E:
case RSS_14:
case RSS_EXPANDED:
case UPC_EAN_EXTENSION:
default:
return MAX_WIDTH_1D;
}
}
public Bitmap doInBackground(Void... params)
{
if (cardId.isEmpty())
{
return null;
}
MultiFormatWriter writer = new MultiFormatWriter();
BitMatrix bitMatrix;
try
@@ -101,6 +162,12 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
{
Log.e(TAG, "Failed to generate barcode of type " + format + ": " + cardId, e);
}
catch(OutOfMemoryError e)
{
Log.w(TAG, "Insufficient memory to render barcode, "
+ imageWidth + "x" + imageHeight + ", " + format.name()
+ ", length=" + cardId.length(), e);
}
return null;
}
@@ -116,16 +183,25 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
}
imageView.setImageBitmap(result);
TextView textView = textViewReference.get();
if(result != null)
{
Log.i(TAG, "Displaying barcode");
imageView.setVisibility(View.VISIBLE);
if (textView != null) {
textView.setVisibility(View.VISIBLE);
textView.setText(format.name());
}
}
else
{
Log.i(TAG, "Barcode generation failed, removing image from display");
imageView.setVisibility(View.GONE);
if (textView != null) {
textView.setVisibility(View.GONE);
}
}
}
}
}

View File

@@ -3,19 +3,20 @@ 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 android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.util.Pair;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import com.google.common.collect.ImmutableMap;
import com.google.zxing.BarcodeFormat;
@@ -34,7 +35,7 @@ import java.util.Map;
*/
public class BarcodeSelectorActivity extends AppCompatActivity
{
private static final String TAG = "LoyaltyCardLocker";
private static final String TAG = "Catima";
// Result this activity will return
public static final String BARCODE_CONTENTS = "contents";
@@ -58,7 +59,7 @@ public class BarcodeSelectorActivity extends AppCompatActivity
BarcodeFormat.UPC_A.name()
));
private Map<String, Integer> barcodeViewMap;
private Map<String, Pair<Integer, Integer>> barcodeViewMap;
private LinkedList<AsyncTask> barcodeGeneratorTasks = new LinkedList<>();
@Override
@@ -67,7 +68,7 @@ public class BarcodeSelectorActivity extends AppCompatActivity
super.onCreate(savedInstanceState);
setContentView(R.layout.barcode_selector_activity);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
@@ -75,21 +76,21 @@ public class BarcodeSelectorActivity extends AppCompatActivity
actionBar.setDisplayHomeAsUpEnabled(true);
}
barcodeViewMap = ImmutableMap.<String, Integer>builder()
.put(BarcodeFormat.AZTEC.name(), R.id.aztecBarcode)
.put(BarcodeFormat.CODE_39.name(), R.id.code39Barcode)
.put(BarcodeFormat.CODE_128.name(), R.id.code128Barcode)
.put(BarcodeFormat.CODABAR.name(), R.id.codabarBarcode)
.put(BarcodeFormat.DATA_MATRIX.name(), R.id.datamatrixBarcode)
.put(BarcodeFormat.EAN_8.name(), R.id.ean8Barcode)
.put(BarcodeFormat.EAN_13.name(), R.id.ean13Barcode)
.put(BarcodeFormat.ITF.name(), R.id.itfBarcode)
.put(BarcodeFormat.PDF_417.name(), R.id.pdf417Barcode)
.put(BarcodeFormat.QR_CODE.name(), R.id.qrcodeBarcode)
.put(BarcodeFormat.UPC_A.name(), R.id.upcaBarcode)
barcodeViewMap = ImmutableMap.<String, Pair<Integer, Integer>>builder()
.put(BarcodeFormat.AZTEC.name(), new Pair<>(R.id.aztecBarcode, R.id.aztecBarcodeText))
.put(BarcodeFormat.CODE_39.name(), new Pair<>(R.id.code39Barcode, R.id.code39BarcodeText))
.put(BarcodeFormat.CODE_128.name(), new Pair<>(R.id.code128Barcode, R.id.code128BarcodeText))
.put(BarcodeFormat.CODABAR.name(), new Pair<>(R.id.codabarBarcode, R.id.codabarBarcodeText))
.put(BarcodeFormat.DATA_MATRIX.name(), new Pair<>(R.id.datamatrixBarcode, R.id.datamatrixBarcodeText))
.put(BarcodeFormat.EAN_8.name(), new Pair<>(R.id.ean8Barcode, R.id.ean8BarcodeText))
.put(BarcodeFormat.EAN_13.name(), new Pair<>(R.id.ean13Barcode, R.id.ean13BarcodeText))
.put(BarcodeFormat.ITF.name(), new Pair<>(R.id.itfBarcode, R.id.itfBarcodeText))
.put(BarcodeFormat.PDF_417.name(), new Pair<>(R.id.pdf417Barcode, R.id.pdf417BarcodeText))
.put(BarcodeFormat.QR_CODE.name(), new Pair<>(R.id.qrcodeBarcode, R.id.qrcodeBarcodeText))
.put(BarcodeFormat.UPC_A.name(), new Pair<>(R.id.upcaBarcode, R.id.upcaBarcodeText))
.build();
EditText cardId = (EditText) findViewById(R.id.cardId);
EditText cardId = findViewById(R.id.cardId);
cardId.addTextChangedListener(new TextWatcher()
{
@Override
@@ -113,9 +114,14 @@ public class BarcodeSelectorActivity extends AppCompatActivity
// Update barcodes
for(String key : barcodeViewMap.keySet())
{
ImageView image = (ImageView)findViewById(barcodeViewMap.get(key));
createBarcodeOption(image, key, s.toString());
ImageView image = findViewById(barcodeViewMap.get(key).first);
TextView text = findViewById(barcodeViewMap.get(key).second);
createBarcodeOption(image, key, s.toString(), text);
}
View noBarcodeButtonView = findViewById(R.id.noBarcode);
setButtonListener(noBarcodeButtonView, s.toString());
noBarcodeButtonView.setEnabled(s.length() > 0);
}
@Override
@@ -124,9 +130,32 @@ public class BarcodeSelectorActivity extends AppCompatActivity
// Noting to do
}
});
final Bundle b = getIntent().getExtras();
final String initialCardId = b != null ? b.getString("initialCardId") : null;
if(initialCardId != null)
{
cardId.setText(initialCardId);
}
}
private void createBarcodeOption(final ImageView image, final String formatType, final String cardId)
private void setButtonListener(final View button, final String cardId)
{
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d(TAG, "Selected no barcode");
Intent result = new Intent();
result.putExtra(BARCODE_FORMAT, "");
result.putExtra(BARCODE_CONTENTS, cardId);
BarcodeSelectorActivity.this.setResult(RESULT_OK, result);
finish();
}
});
}
private void createBarcodeOption(final ImageView image, final String formatType, final String cardId, final TextView text)
{
final BarcodeFormat format = BarcodeFormat.valueOf(formatType);
if(format == null)
@@ -136,7 +165,6 @@ public class BarcodeSelectorActivity extends AppCompatActivity
}
image.setImageBitmap(null);
image.setVisibility(View.GONE);
image.setOnClickListener(new View.OnClickListener()
{
@Override
@@ -162,17 +190,10 @@ 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);
BarcodeImageWriterTask task = new BarcodeImageWriterTask(image, cardId, format, text);
barcodeGeneratorTasks.add(task);
task.execute();
}
@@ -181,7 +202,7 @@ public class BarcodeSelectorActivity extends AppCompatActivity
else
{
Log.d(TAG, "Generating barcode for type " + formatType);
BarcodeImageWriterTask task = new BarcodeImageWriterTask(image, cardId, format);
BarcodeImageWriterTask task = new BarcodeImageWriterTask(image, cardId, format, text);
barcodeGeneratorTasks.add(task);
task.execute();
}

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

@@ -0,0 +1,83 @@
package protect.card_locker;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.os.Parcelable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;
/**
* The configuration screen for creating a shortcut.
*/
public class CardShortcutConfigure extends AppCompatActivity
{
static final String TAG = "Catima";
@Override
public void onCreate(Bundle bundle)
{
super.onCreate(bundle);
// Set the result to CANCELED. This will cause nothing to happen if the
// aback button is pressed.
setResult(RESULT_CANCELED);
setContentView(R.layout.main_activity);
Toolbar toolbar = findViewById(R.id.toolbar);
toolbar.setVisibility(View.GONE);
final DBHelper db = new DBHelper(this);
// If there are no cards, bail
if(db.getLoyaltyCardCount() == 0)
{
Toast.makeText(this, R.string.noCardsMessage, Toast.LENGTH_LONG).show();
finish();
}
final ListView cardList = findViewById(R.id.list);
cardList.setVisibility(View.VISIBLE);
Cursor cardCursor = db.getLoyaltyCardCursor();
final LoyaltyCardCursorAdapter adapter = new LoyaltyCardCursorAdapter(this, cardCursor);
cardList.setAdapter(adapter);
cardList.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
{
Cursor selected = (Cursor) parent.getItemAtPosition(position);
LoyaltyCard loyaltyCard = LoyaltyCard.toLoyaltyCard(selected);
Log.d(TAG, "Creating shortcut for card " + loyaltyCard.store + "," + loyaltyCard.id);
Intent shortcutIntent = new Intent(CardShortcutConfigure.this, LoyaltyCardViewActivity.class);
shortcutIntent.setAction(Intent.ACTION_MAIN);
// Prevent instances of the view activity from piling up; if one exists let this
// one replace it.
shortcutIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
Bundle bundle = new Bundle();
bundle.putInt("id", loyaltyCard.id);
bundle.putBoolean("view", true);
shortcutIntent.putExtras(bundle);
Parcelable icon = Intent.ShortcutIconResource.fromContext(CardShortcutConfigure.this, R.mipmap.ic_launcher);
Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, loyaltyCard.store);
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, icon);
setResult(RESULT_OK, intent);
finish();
}
});
}
}

View File

@@ -18,24 +18,21 @@ public class CsvDatabaseExporter implements DatabaseExporter
{
CSVPrinter printer = new CSVPrinter(output, CSVFormat.RFC4180);
// Print the header
printer.printRecord(DBHelper.LoyaltyCardDbIds.ID,
DBHelper.LoyaltyCardDbIds.STORE,
DBHelper.LoyaltyCardDbIds.NOTE,
DBHelper.LoyaltyCardDbIds.CARD_ID,
DBHelper.LoyaltyCardDbIds.BARCODE_TYPE);
// Print the version
printer.printRecord("2");
Cursor cursor = db.getLoyaltyCardCursor();
printer.println();
while(cursor.moveToNext())
// Print the header for groups
printer.printRecord(DBHelper.LoyaltyCardDbGroups.ID);
Cursor groupCursor = db.getGroupCursor();
while(groupCursor.moveToNext())
{
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cursor);
Group group = Group.toGroup(groupCursor);
printer.printRecord(card.id,
card.store,
card.note,
card.cardId,
card.barcodeType);
printer.printRecord(group._id);
if(Thread.currentThread().isInterrupted())
{
@@ -43,7 +40,66 @@ public class CsvDatabaseExporter implements DatabaseExporter
}
}
cursor.close();
groupCursor.close();
// Print an empty line
printer.println();
// Print the header for cards
printer.printRecord(DBHelper.LoyaltyCardDbIds.ID,
DBHelper.LoyaltyCardDbIds.STORE,
DBHelper.LoyaltyCardDbIds.NOTE,
DBHelper.LoyaltyCardDbIds.CARD_ID,
DBHelper.LoyaltyCardDbIds.HEADER_COLOR,
DBHelper.LoyaltyCardDbIds.BARCODE_TYPE,
DBHelper.LoyaltyCardDbIds.STAR_STATUS);
Cursor cardCursor = db.getLoyaltyCardCursor();
while(cardCursor.moveToNext())
{
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cardCursor);
printer.printRecord(card.id,
card.store,
card.note,
card.cardId,
card.headerColor,
card.barcodeType,
card.starStatus);
if(Thread.currentThread().isInterrupted())
{
throw new InterruptedException();
}
}
cardCursor.close();
// Print an empty line
printer.println();
// Print the header for card group mappings
printer.printRecord(DBHelper.LoyaltyCardDbIdsGroups.cardID,
DBHelper.LoyaltyCardDbIdsGroups.groupID);
Cursor cardCursor2 = db.getLoyaltyCardCursor();
while(cardCursor2.moveToNext())
{
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cardCursor2);
for (Group group : db.getLoyaltyCardGroups(card.id)) {
printer.printRecord(card.id, group._id);
}
if(Thread.currentThread().isInterrupted())
{
throw new InterruptedException();
}
}
cardCursor2.close();
printer.close();
}

View File

@@ -6,8 +6,11 @@ import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.util.List;
/**
* Class for importing a database from CSV (Comma Separate Values)
@@ -19,6 +22,34 @@ import java.io.InputStreamReader;
public class CsvDatabaseImporter implements DatabaseImporter
{
public void importData(DBHelper db, InputStreamReader input) throws IOException, FormatException, InterruptedException
{
BufferedReader bufferedReader = new BufferedReader(input);
bufferedReader.mark(100);
Integer version = 1;
try {
version = Integer.parseInt(bufferedReader.readLine());
} catch (NumberFormatException _e) {
// Assume version 1
}
bufferedReader.reset();
switch (version) {
case 1:
parseV1(db, bufferedReader);
break;
case 2:
parseV2(db, bufferedReader);
break;
default:
throw new FormatException(String.format("No code to parse version %s", version));
}
}
public void parseV1(DBHelper db, BufferedReader input) throws IOException, FormatException, InterruptedException
{
final CSVParser parser = new CSVParser(input, CSVFormat.RFC4180.withHeader());
@@ -40,7 +71,7 @@ public class CsvDatabaseImporter implements DatabaseImporter
parser.close();
database.setTransactionSuccessful();
}
catch(IllegalArgumentException e)
catch(IllegalArgumentException|IllegalStateException e)
{
throw new FormatException("Issue parsing CSV data", e);
}
@@ -51,6 +82,116 @@ public class CsvDatabaseImporter implements DatabaseImporter
}
}
public void parseV2(DBHelper db, BufferedReader input) throws IOException, FormatException, InterruptedException
{
SQLiteDatabase database = db.getWritableDatabase();
database.beginTransaction();
Integer part = 0;
String stringPart = "";
try {
while (true) {
String tmp = input.readLine();
if (tmp == null || tmp.isEmpty()) {
switch (part) {
case 0:
// This is the version info, ignore
break;
case 1:
parseV2Groups(db, database, stringPart);
break;
case 2:
parseV2Cards(db, database, stringPart);
break;
case 3:
parseV2CardGroups(db, database, stringPart);
break;
default:
throw new FormatException("Issue parsing CSV data, too many parts for v2 parsing");
}
if (tmp == null) {
break;
}
part += 1;
stringPart = "";
} else {
stringPart += tmp + "\n";
}
}
database.setTransactionSuccessful();
} catch (FormatException e) {
throw new FormatException("Issue parsing CSV data", e);
} finally {
database.endTransaction();
database.close();
}
}
public void parseV2Groups(DBHelper db, SQLiteDatabase database, String data) throws IOException, FormatException, InterruptedException
{
// Parse groups
final CSVParser groupParser = new CSVParser(new StringReader(data), CSVFormat.RFC4180.withHeader());
try {
for (CSVRecord record : groupParser) {
importGroup(database, db, record);
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
}
groupParser.close();
} catch (IllegalArgumentException | IllegalStateException e) {
throw new FormatException("Issue parsing CSV data", e);
}
}
public void parseV2Cards(DBHelper db, SQLiteDatabase database, String data) throws IOException, FormatException, InterruptedException
{
// Parse cards
final CSVParser cardParser = new CSVParser(new StringReader(data), CSVFormat.RFC4180.withHeader());
try {
for (CSVRecord record : cardParser) {
importLoyaltyCard(database, db, record);
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
}
cardParser.close();
} catch (IllegalArgumentException | IllegalStateException e) {
throw new FormatException("Issue parsing CSV data", e);
}
}
public void parseV2CardGroups(DBHelper db, SQLiteDatabase database, String data) throws IOException, FormatException, InterruptedException
{
// Parse card group mappings
final CSVParser cardGroupParser = new CSVParser(new StringReader(data), CSVFormat.RFC4180.withHeader());
try {
for (CSVRecord record : cardGroupParser) {
importCardGroupMapping(database, db, record);
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
}
cardGroupParser.close();
} catch (IllegalArgumentException | IllegalStateException e) {
throw new FormatException("Issue parsing CSV data", e);
}
}
/**
* Extract a string from the items array. The index into the array
* is determined by looking up the index in the fields map using the
@@ -83,7 +224,7 @@ public class CsvDatabaseImporter implements DatabaseImporter
* "key" as the key. If no such key exists, or the data is not a valid
* int, a FormatException is thrown.
*/
private int extractInt(String key, CSVRecord record)
private Integer extractInt(String key, CSVRecord record, boolean nullIsOk)
throws FormatException
{
if(record.isMapped(key) == false)
@@ -91,6 +232,12 @@ public class CsvDatabaseImporter implements DatabaseImporter
throw new FormatException("Field not used but expected: " + key);
}
String value = record.get(key);
if(value.isEmpty() && nullIsOk)
{
return null;
}
try
{
return Integer.parseInt(record.get(key));
@@ -108,7 +255,7 @@ public class CsvDatabaseImporter implements DatabaseImporter
private void importLoyaltyCard(SQLiteDatabase database, DBHelper helper, CSVRecord record)
throws IOException, FormatException
{
int id = extractInt(DBHelper.LoyaltyCardDbIds.ID, record);
int id = extractInt(DBHelper.LoyaltyCardDbIds.ID, record, false);
String store = extractString(DBHelper.LoyaltyCardDbIds.STORE, record, "");
if(store.isEmpty())
@@ -125,11 +272,49 @@ public class CsvDatabaseImporter implements DatabaseImporter
}
String barcodeType = extractString(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE, record, "");
if(barcodeType.isEmpty())
Integer headerColor = null;
if(record.isMapped(DBHelper.LoyaltyCardDbIds.HEADER_COLOR))
{
throw new FormatException("No barcode type listed, but is required");
headerColor = extractInt(DBHelper.LoyaltyCardDbIds.HEADER_COLOR, record, true);
}
helper.insertLoyaltyCard(database, id, store, note, cardId, barcodeType);
int starStatus = 0;
try {
starStatus = extractInt(DBHelper.LoyaltyCardDbIds.STAR_STATUS, record, false);
} catch (FormatException _e ) {
// This field did not exist in versions 0.28 and before
// We catch this exception so we can still import old backups
}
if (starStatus != 1) starStatus = 0;
helper.insertLoyaltyCard(database, id, store, note, cardId, barcodeType, headerColor, starStatus);
}
}
/**
* Import a single group into the database using the given
* session.
*/
private void importGroup(SQLiteDatabase database, DBHelper helper, CSVRecord record)
throws IOException, FormatException
{
String id = extractString(DBHelper.LoyaltyCardDbGroups.ID, record, null);
helper.insertGroup(database, id);
}
/**
* Import a single card to group mapping into the database using the given
* session.
*/
private void importCardGroupMapping(SQLiteDatabase database, DBHelper helper, CSVRecord record)
throws IOException, FormatException
{
Integer cardId = extractInt(DBHelper.LoyaltyCardDbIdsGroups.cardID, record, false);
String groupId = extractString(DBHelper.LoyaltyCardDbIdsGroups.groupID, record, null);
List<Group> cardGroups = helper.getLoyaltyCardGroups(cardId);
cardGroups.add(helper.getGroup(groupId));
helper.setLoyaltyCardGroups(database, cardId, cardGroups);
}
}

View File

@@ -1,17 +1,27 @@
package protect.card_locker;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.graphics.Color;
import java.util.ArrayList;
import java.util.List;
public class DBHelper extends SQLiteOpenHelper
{
public static final String DATABASE_NAME = "LoyaltyCards.db";
public static final String DATABASE_NAME = "Catima.db";
public static final int ORIGINAL_DATABASE_VERSION = 1;
public static final int DATABASE_VERSION = 2;
public static final int DATABASE_VERSION = 6;
static class LoyaltyCardDbGroups
{
public static final String TABLE = "groups";
public static final String ID = "_id";
public static final String ORDER = "orderId";
}
static class LoyaltyCardDbIds
{
@@ -19,8 +29,18 @@ public class DBHelper extends SQLiteOpenHelper
public static final String ID = "_id";
public static final String STORE = "store";
public static final String NOTE = "note";
public static final String HEADER_COLOR = "headercolor";
public static final String HEADER_TEXT_COLOR = "headertextcolor";
public static final String CARD_ID = "cardid";
public static final String BARCODE_TYPE = "barcodetype";
public static final String STAR_STATUS = "starstatus";
}
static class LoyaltyCardDbIdsGroups
{
public static final String TABLE = "cardsGroups";
public static final String cardID = "cardId";
public static final String groupID = "groupId";
}
public DBHelper(Context context)
@@ -31,13 +51,27 @@ public class DBHelper extends SQLiteOpenHelper
@Override
public void onCreate(SQLiteDatabase db)
{
// create table for gift cards
// create table for card groups
db.execSQL("create table " + LoyaltyCardDbGroups.TABLE + "(" +
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.HEADER_COLOR + " INTEGER," +
LoyaltyCardDbIds.HEADER_TEXT_COLOR + " INTEGER," +
LoyaltyCardDbIds.CARD_ID + " TEXT not null," +
LoyaltyCardDbIds.BARCODE_TYPE + " TEXT not null)");
LoyaltyCardDbIds.BARCODE_TYPE + " TEXT not null," +
LoyaltyCardDbIds.STAR_STATUS + " INTEGER DEFAULT '0' )");
// create associative table for cards in groups
db.execSQL("create table " + LoyaltyCardDbIdsGroups.TABLE + "(" +
LoyaltyCardDbIdsGroups.cardID + " INTEGER," +
LoyaltyCardDbIdsGroups.groupID + " TEXT," +
"primary key (" + LoyaltyCardDbIdsGroups.cardID + "," + LoyaltyCardDbIdsGroups.groupID +"))");
}
@Override
@@ -49,10 +83,46 @@ public class DBHelper extends SQLiteOpenHelper
db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE
+ " ADD COLUMN " + LoyaltyCardDbIds.NOTE + " TEXT not null default ''");
}
// Upgrade from version 2 to version 3
if(oldVersion < 3 && newVersion >= 3)
{
db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE
+ " ADD COLUMN " + LoyaltyCardDbIds.HEADER_COLOR + " INTEGER");
db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE
+ " ADD COLUMN " + LoyaltyCardDbIds.HEADER_TEXT_COLOR + " INTEGER");
}
// Upgrade from version 3 to version 4
if(oldVersion < 4 && newVersion >= 4)
{
db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE
+ " ADD COLUMN " + LoyaltyCardDbIds.STAR_STATUS + " INTEGER DEFAULT '0'");
}
// Upgrade from version 4 to version 5
if(oldVersion < 5 && newVersion >= 5)
{
db.execSQL("create table " + LoyaltyCardDbGroups.TABLE + "(" +
LoyaltyCardDbGroups.ID + " TEXT primary key not null)");
db.execSQL("create table " + LoyaltyCardDbIdsGroups.TABLE + "(" +
LoyaltyCardDbIdsGroups.cardID + " INTEGER," +
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'");
}
}
public boolean insertLoyaltyCard(final String store, final String note, final String cardId,
final String barcodeType)
public long insertLoyaltyCard(final String store, final String note, final String cardId,
final String barcodeType, final Integer headerColor,
final int starStatus)
{
SQLiteDatabase db = getWritableDatabase();
ContentValues contentValues = new ContentValues();
@@ -60,13 +130,17 @@ public class DBHelper extends SQLiteOpenHelper
contentValues.put(LoyaltyCardDbIds.NOTE, note);
contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId);
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType);
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
contentValues.put(LoyaltyCardDbIds.HEADER_TEXT_COLOR, Color.WHITE);
contentValues.put(LoyaltyCardDbIds.STAR_STATUS, starStatus);
final long newId = db.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
return (newId != -1);
return newId;
}
public boolean insertLoyaltyCard(final SQLiteDatabase db, final int id,
final String store, final String note, final String cardId,
final String barcodeType)
public boolean insertLoyaltyCard(final SQLiteDatabase db, final int id, final String store,
final String note, final String cardId,
final String barcodeType, final Integer headerColor,
final int starStatus)
{
ContentValues contentValues = new ContentValues();
contentValues.put(LoyaltyCardDbIds.ID, id);
@@ -74,13 +148,16 @@ public class DBHelper extends SQLiteOpenHelper
contentValues.put(LoyaltyCardDbIds.NOTE, note);
contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId);
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType);
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
contentValues.put(LoyaltyCardDbIds.HEADER_TEXT_COLOR, Color.WHITE);
contentValues.put(LoyaltyCardDbIds.STAR_STATUS,starStatus);
final long newId = db.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
return (newId != -1);
}
public boolean updateLoyaltyCard(final int id, final String store, final String note,
final String cardId, final String barcodeType)
final String cardId, final String barcodeType,
final Integer headerColor)
{
SQLiteDatabase db = getWritableDatabase();
ContentValues contentValues = new ContentValues();
@@ -88,6 +165,19 @@ public class DBHelper extends SQLiteOpenHelper
contentValues.put(LoyaltyCardDbIds.NOTE, note);
contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId);
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType);
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
contentValues.put(LoyaltyCardDbIds.HEADER_TEXT_COLOR, Color.WHITE);
int rowsUpdated = db.update(LoyaltyCardDbIds.TABLE, contentValues,
LoyaltyCardDbIds.ID + "=?",
new String[]{Integer.toString(id)});
return (rowsUpdated == 1);
}
public boolean updateLoyaltyCardStarStatus(final int id, final int starStatus)
{
SQLiteDatabase db = getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(LoyaltyCardDbIds.STAR_STATUS,starStatus);
int rowsUpdated = db.update(LoyaltyCardDbIds.TABLE, contentValues,
LoyaltyCardDbIds.ID + "=?",
new String[]{Integer.toString(id)});
@@ -105,6 +195,7 @@ public class DBHelper extends SQLiteOpenHelper
if(data.getCount() == 1)
{
data.moveToFirst();
card = LoyaltyCard.toLoyaltyCard(data);
}
@@ -113,27 +204,352 @@ public class DBHelper extends SQLiteOpenHelper
return card;
}
public List<Group> getLoyaltyCardGroups(final int id)
{
SQLiteDatabase db = getReadableDatabase();
Cursor data = db.rawQuery("select * from " + LoyaltyCardDbGroups.TABLE + " g " +
" LEFT JOIN " + LoyaltyCardDbIdsGroups.TABLE + " ig ON ig." + LoyaltyCardDbIdsGroups.groupID + " = g." + LoyaltyCardDbGroups.ID +
" where " + LoyaltyCardDbIdsGroups.cardID + "=?" +
" ORDER BY " + LoyaltyCardDbIdsGroups.groupID, new String[]{String.format("%d", id)});
List<Group> groups = new ArrayList<>();
if (!data.moveToFirst()) {
return groups;
}
groups.add(Group.toGroup(data));
while (data.moveToNext()) {
groups.add(Group.toGroup(data));
}
return groups;
}
public void setLoyaltyCardGroups(final int id, List<Group> groups)
{
SQLiteDatabase db = getWritableDatabase();
// First delete lookup table entries associated with this card
db.delete(LoyaltyCardDbIdsGroups.TABLE,
LoyaltyCardDbIdsGroups.cardID + " = ? ",
new String[]{String.format("%d", id)});
// Then create entries for selected values
for (Group group : groups) {
ContentValues contentValues = new ContentValues();
contentValues.put(LoyaltyCardDbIdsGroups.cardID, id);
contentValues.put(LoyaltyCardDbIdsGroups.groupID, group._id);
db.insert(LoyaltyCardDbIdsGroups.TABLE, null, contentValues);
}
}
public void setLoyaltyCardGroups(final SQLiteDatabase db, final int id, List<Group> groups)
{
// First delete lookup table entries associated with this card
db.delete(LoyaltyCardDbIdsGroups.TABLE,
LoyaltyCardDbIdsGroups.cardID + " = ? ",
new String[]{String.format("%d", id)});
// Then create entries for selected values
for (Group group : groups) {
ContentValues contentValues = new ContentValues();
contentValues.put(LoyaltyCardDbIdsGroups.cardID, id);
contentValues.put(LoyaltyCardDbIdsGroups.groupID, group._id);
db.insert(LoyaltyCardDbIdsGroups.TABLE, null, contentValues);
}
}
public boolean deleteLoyaltyCard (final int id)
{
SQLiteDatabase db = getWritableDatabase();
int rowsDeleted = db.delete(LoyaltyCardDbIds.TABLE,
// Delete card
int rowsDeleted = db.delete(LoyaltyCardDbIds.TABLE,
LoyaltyCardDbIds.ID + " = ? ",
new String[]{String.format("%d", id)});
// And delete lookup table entries associated with this card
db.delete(LoyaltyCardDbIdsGroups.TABLE,
LoyaltyCardDbIdsGroups.cardID + " = ? ",
new String[]{String.format("%d", id)});
return (rowsDeleted == 1);
}
public Cursor getLoyaltyCardCursor()
{
// An empty string will match everything
return getLoyaltyCardCursor("");
}
/**
* Returns a cursor to all loyalty cards with the filter text in either the store or note.
*
* @param filter
* @return Cursor
*/
public Cursor getLoyaltyCardCursor(final String filter)
{
return getLoyaltyCardCursor(filter, null);
}
/**
* Returns a cursor to all loyalty cards with the filter text in either the store or note in a certain group.
*
* @param filter
* @param group
* @return Cursor
*/
public Cursor getLoyaltyCardCursor(final String filter, Group group)
{
String actualFilter = String.format("%%%s%%", filter);
String[] selectionArgs = { actualFilter, actualFilter };
StringBuilder groupFilter = new StringBuilder();
String limitString = "";
SQLiteDatabase db = getReadableDatabase();
Cursor res = db.rawQuery("select * from " + LoyaltyCardDbIds.TABLE +
" ORDER BY " + LoyaltyCardDbIds.STORE, null);
if (group != null) {
List<Integer> allowedIds = getGroupCardIds(group._id);
// Empty group
if (allowedIds.size() > 0) {
groupFilter.append("AND (");
for (int i = 0; i < allowedIds.size(); i++) {
groupFilter.append(LoyaltyCardDbIds.ID + " = " + allowedIds.get(i));
if (i != allowedIds.size() - 1) {
groupFilter.append(" OR ");
}
}
groupFilter.append(") ");
} else {
limitString = "LIMIT 0";
}
}
Cursor res = db.rawQuery("select * from " + LoyaltyCardDbIds.TABLE +
" WHERE (" + LoyaltyCardDbIds.STORE + " LIKE ? " +
" OR " + LoyaltyCardDbIds.NOTE + " LIKE ? )" +
groupFilter.toString() +
" ORDER BY " + LoyaltyCardDbIds.STAR_STATUS + " DESC," + LoyaltyCardDbIds.STORE + " COLLATE NOCASE ASC " +
limitString, selectionArgs, null);
return res;
}
public int getLoyaltyCardCount()
{
// An empty string will match everything
return getLoyaltyCardCount("");
}
/**
* Returns the amount of loyalty cards with the filter text in either the store or note.
*
* @param filter
* @return Integer
*/
public int getLoyaltyCardCount(String filter)
{
String actualFilter = String.format("%%%s%%", filter);
String[] selectionArgs = { actualFilter, actualFilter };
SQLiteDatabase db = getReadableDatabase();
Cursor data = db.rawQuery("SELECT Count(*) FROM " + LoyaltyCardDbIds.TABLE, null);
Cursor data = db.rawQuery("SELECT Count(*) FROM " + LoyaltyCardDbIds.TABLE +
" WHERE " + LoyaltyCardDbIds.STORE + " LIKE ? " +
" OR " + LoyaltyCardDbIds.NOTE + " LIKE ? "
, selectionArgs, null);
int numItems = 0;
if(data.getCount() == 1)
{
data.moveToFirst();
numItems = data.getInt(0);
}
data.close();
return numItems;
}
/**
* Returns a cursor to all groups.
*
* @return Cursor
*/
public Cursor getGroupCursor()
{
SQLiteDatabase db = getReadableDatabase();
Cursor res = db.rawQuery("select * from " + LoyaltyCardDbGroups.TABLE +
" ORDER BY " + LoyaltyCardDbGroups.ORDER + " ASC," + LoyaltyCardDbGroups.ID + " COLLATE NOCASE ASC", null, null);
return res;
}
public List<Group> getGroups() {
Cursor data = getGroupCursor();
List<Group> groups = new ArrayList<>();
if (!data.moveToFirst()) {
return groups;
}
groups.add(Group.toGroup(data));
while (data.moveToNext()) {
groups.add(Group.toGroup(data));
}
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();
Cursor data = db.rawQuery("select * from " + LoyaltyCardDbGroups.TABLE +
" where " + LoyaltyCardDbGroups.ID + "=?", new String[]{groupName});
Group group = null;
if(data.getCount() == 1)
{
data.moveToFirst();
group = Group.toGroup(data);
}
data.close();
return group;
}
public int getGroupCount()
{
SQLiteDatabase db = getReadableDatabase();
Cursor data = db.rawQuery("SELECT Count(*) FROM " + LoyaltyCardDbGroups.TABLE, null);
int numItems = 0;
if(data.getCount() == 1)
{
data.moveToFirst();
numItems = data.getInt(0);
}
data.close();
return numItems;
}
public List<Integer> getGroupCardIds(final String groupName)
{
SQLiteDatabase db = getReadableDatabase();
Cursor data = db.rawQuery("SELECT " + LoyaltyCardDbIdsGroups.cardID +
" FROM " + LoyaltyCardDbIdsGroups.TABLE +
" WHERE " + LoyaltyCardDbIdsGroups.groupID + " =? ", new String[]{groupName});
List<Integer> cardIds = new ArrayList<>();
if (!data.moveToFirst()) {
return cardIds;
}
cardIds.add(data.getInt(0));
while (data.moveToNext()) {
cardIds.add(data.getInt(0));
}
data.close();
return cardIds;
}
public long insertGroup(final String name)
{
if (name.isEmpty()) return -1;
SQLiteDatabase db = getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(LoyaltyCardDbGroups.ID, name);
contentValues.put(LoyaltyCardDbGroups.ORDER, getGroupCount());
final long newId = db.insert(LoyaltyCardDbGroups.TABLE, null, contentValues);
return newId;
}
public boolean insertGroup(final SQLiteDatabase db, final String name)
{
ContentValues contentValues = new ContentValues();
contentValues.put(LoyaltyCardDbGroups.ID, name);
contentValues.put(LoyaltyCardDbGroups.ORDER, getGroupCount());
final long newId = db.insert(LoyaltyCardDbGroups.TABLE, null, contentValues);
return (newId != -1);
}
public boolean updateGroup(final String groupName, final String newName)
{
if (newName.isEmpty()) return false;
SQLiteDatabase db = getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(LoyaltyCardDbGroups.ID, newName);
try {
int rowsUpdated = db.update(LoyaltyCardDbGroups.TABLE, contentValues,
LoyaltyCardDbGroups.ID + "=?",
new String[]{groupName});
return (rowsUpdated == 1);
} catch (android.database.sqlite.SQLiteConstraintException _e) {
return false;
}
}
public boolean deleteGroup(final String groupName)
{
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});
return (rowsDeleted == 1);
}
public int getGroupCardCount(final String groupName)
{
SQLiteDatabase db = getReadableDatabase();
Cursor data = db.rawQuery("SELECT Count(*) FROM " + LoyaltyCardDbIdsGroups.TABLE +
" where " + LoyaltyCardDbIdsGroups.groupID + "=?",
new String[]{groupName});
int numItems = 0;

View File

@@ -0,0 +1,20 @@
package protect.card_locker;
import android.database.Cursor;
public class Group
{
public final String _id;
public Group(final String _id)
{
this._id = _id;
}
public static Group toGroup(Cursor cursor)
{
String _id = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbGroups.ID));
return new Group(_id);
}
}

View File

@@ -0,0 +1,56 @@
package protect.card_locker;
import android.content.Context;
import android.database.Cursor;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.TextView;
import protect.card_locker.preferences.Settings;
class GroupCursorAdapter extends CursorAdapter
{
Settings settings;
DBHelper db;
public GroupCursorAdapter(Context context, Cursor cursor)
{
super(context, cursor, 0);
settings = new Settings(context);
db = new DBHelper(context);
}
// The newView method is used to inflate a new view and return it,
// you don't bind any data to the view at this point.
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent)
{
return LayoutInflater.from(context).inflate(R.layout.group_layout, parent, false);
}
// The bindView method is used to bind all data to a given view
// such as setting the text on a TextView.
@Override
public void bindView(View view, Context context, Cursor cursor)
{
// Find fields to populate in inflated template
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(context.getResources().getQuantityString(R.plurals.groupCardCount, groupCardCount, groupCardCount));
nameField.setTextSize(settings.getCardTitleListFontSize());
countField.setTextSize(settings.getCardNoteListFontSize());
}
}

View File

@@ -1,32 +1,50 @@
package protect.card_locker;
import android.Manifest;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class ImportExportActivity extends AppCompatActivity
{
private static final int PERMISSIONS_EXTERNAL_STORAGE_IMPORT = 1;
private static final int PERMISSIONS_EXTERNAL_STORAGE_EXPORT = 2;
private static final String TAG = "Catima";
ImportExportTask importExporter;
private static final int PERMISSIONS_EXTERNAL_STORAGE = 1;
private static final int CHOOSE_EXPORT_LOCATION = 2;
private static final int CHOOSE_EXPORTED_FILE = 3;
private ImportExportTask importExporter;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.import_export_activity);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
@@ -34,82 +52,114 @@ public class ImportExportActivity extends AppCompatActivity
actionBar.setDisplayHomeAsUpEnabled(true);
}
Button importButton = (Button)findViewById(R.id.importButton);
importButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
if (ContextCompat.checkSelfPermission(ImportExportActivity.this,
Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
{
startImport();
}
else
{
ActivityCompat.requestPermissions(ImportExportActivity.this,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
PERMISSIONS_EXTERNAL_STORAGE_IMPORT);
}
}
});
// If the application does not have permissions to external
// storage, ask for it now
Button exportButton = (Button)findViewById(R.id.exportButton);
if (ContextCompat.checkSelfPermission(ImportExportActivity.this,
Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(ImportExportActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
{
ActivityCompat.requestPermissions(ImportExportActivity.this,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE},
PERMISSIONS_EXTERNAL_STORAGE);
}
// Check that there is a file manager available
final Intent intentCreateDocumentAction = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intentCreateDocumentAction.addCategory(Intent.CATEGORY_OPENABLE);
intentCreateDocumentAction.setType("text/csv");
intentCreateDocumentAction.putExtra(Intent.EXTRA_TITLE, "Catima.csv");
Button exportButton = findViewById(R.id.exportButton);
exportButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
if (ContextCompat.checkSelfPermission(ImportExportActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
{
startExport();
}
else
{
ActivityCompat.requestPermissions(ImportExportActivity.this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
PERMISSIONS_EXTERNAL_STORAGE_EXPORT);
}
chooseFileWithIntent(intentCreateDocumentAction, CHOOSE_EXPORT_LOCATION);
}
});
// Check that there is a file manager available
final Intent intentGetContentAction = new Intent(Intent.ACTION_GET_CONTENT);
intentGetContentAction.addCategory(Intent.CATEGORY_OPENABLE);
intentGetContentAction.setType("*/*");
Button importFilesystem = findViewById(R.id.importOptionFilesystemButton);
importFilesystem.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
chooseFileWithIntent(intentGetContentAction, CHOOSE_EXPORTED_FILE);
}
});
// Check that there is an app that data can be imported from
final Intent intentPickAction = new Intent(Intent.ACTION_PICK);
Button importApplication = findViewById(R.id.importOptionApplicationButton);
importApplication.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
chooseFileWithIntent(intentPickAction, CHOOSE_EXPORTED_FILE);
}
});
}
private void startImport()
private void startImport(final InputStream target, final Uri targetUri)
{
ImportExportTask.TaskCompleteListener listener = new ImportExportTask.TaskCompleteListener()
{
@Override
public void onTaskComplete(boolean success)
{
onImportComplete(success, targetUri);
}
};
importExporter = new ImportExportTask(ImportExportActivity.this,
true, DataFormat.CSV);
DataFormat.CSV, target, listener);
importExporter.execute();
}
private void startExport()
private void startExport(final OutputStream target, final Uri targetUri)
{
ImportExportTask.TaskCompleteListener listener = new ImportExportTask.TaskCompleteListener()
{
@Override
public void onTaskComplete(boolean success)
{
onExportComplete(success, targetUri);
}
};
importExporter = new ImportExportTask(ImportExportActivity.this,
false, DataFormat.CSV);
DataFormat.CSV, target, listener);
importExporter.execute();
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults)
{
if(requestCode == PERMISSIONS_EXTERNAL_STORAGE_IMPORT ||
requestCode == PERMISSIONS_EXTERNAL_STORAGE_EXPORT)
if(requestCode == PERMISSIONS_EXTERNAL_STORAGE)
{
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED)
boolean success = grantResults.length > 0;
for(int grant : grantResults)
{
// permission was granted.
if(requestCode == PERMISSIONS_EXTERNAL_STORAGE_IMPORT)
if(grant != PackageManager.PERMISSION_GRANTED)
{
startImport();
}
else
{
startExport();
success = false;
}
}
else
if(success == false)
{
// External storage permission rejected, inform user that
// import/export is prevented
@@ -143,4 +193,164 @@ public class ImportExportActivity extends AppCompatActivity
return super.onOptionsItemSelected(item);
}
private void onImportComplete(boolean success, Uri path)
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
if(success)
{
builder.setTitle(R.string.importSuccessfulTitle);
}
else
{
builder.setTitle(R.string.importFailedTitle);
}
int messageId = success ? R.string.importSuccessful : R.string.importFailed;
final String message = getResources().getString(messageId);
builder.setMessage(message);
builder.setNeutralButton(R.string.ok, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
}
});
builder.create().show();
}
private void onExportComplete(boolean success, final Uri path)
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
if(success)
{
builder.setTitle(R.string.exportSuccessfulTitle);
}
else
{
builder.setTitle(R.string.exportFailedTitle);
}
int messageId = success ? R.string.exportSuccessful : R.string.exportFailed;
final String message = getResources().getString(messageId);
builder.setMessage(message);
builder.setNeutralButton(R.string.ok, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
}
});
if(success)
{
final CharSequence sendLabel = ImportExportActivity.this.getResources().getText(R.string.sendLabel);
builder.setPositiveButton(sendLabel, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_STREAM, path);
sendIntent.setType("text/csv");
// set flag to give temporary permission to external app to use the FileProvider
sendIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
ImportExportActivity.this.startActivity(Intent.createChooser(sendIntent,
sendLabel));
dialog.dismiss();
}
});
}
builder.create().show();
}
private void chooseFileWithIntent(Intent intent, int requestCode)
{
try
{
startActivityForResult(intent, requestCode);
}
catch (ActivityNotFoundException e)
{
Toast.makeText(getApplicationContext(), R.string.failedOpeningFileManager, Toast.LENGTH_LONG).show();
Log.e(TAG, "No activity found to handle intent", e);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != RESULT_OK || (requestCode != CHOOSE_EXPORT_LOCATION && requestCode != CHOOSE_EXPORTED_FILE))
{
Log.w(TAG, "Failed onActivityResult(), result=" + resultCode);
return;
}
Uri uri = data.getData();
if(uri == null)
{
Log.e(TAG, "Activity returned a NULL URI");
return;
}
try
{
if (requestCode == CHOOSE_EXPORT_LOCATION)
{
OutputStream writer;
if (uri.getScheme() != null)
{
writer = getContentResolver().openOutputStream(uri);
}
else
{
writer = new FileOutputStream(new File(uri.toString()));
}
Log.e(TAG, "Starting file export with: " + uri.toString());
startExport(writer, uri);
}
else
{
InputStream reader;
if(uri.getScheme() != null)
{
reader = getContentResolver().openInputStream(uri);
}
else
{
reader = new FileInputStream(new File(uri.toString()));
}
Log.e(TAG, "Starting file export with: " + uri.toString());
startImport(reader, uri);
}
}
catch(FileNotFoundException e)
{
Log.e(TAG, "Failed to import/export file: " + uri.toString(), e);
if (requestCode == CHOOSE_EXPORT_LOCATION)
{
onExportComplete(false, uri);
}
else
{
onImportComplete(false, uri);
}
}
}
}

View File

@@ -4,67 +4,63 @@ import android.app.Activity;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
class ImportExportTask extends AsyncTask<Void, Void, Void>
class ImportExportTask extends AsyncTask<Void, Void, Boolean>
{
private static final String TAG = "BudgetWatch";
private static final String TARGET_FILE = "LoyaltyCardLocker.csv";
private static final String TAG = "Catima";
private Activity activity;
private boolean doImport;
private DataFormat format;
private OutputStream outputStream;
private InputStream inputStream;
private TaskCompleteListener listener;
private ProgressDialog progress;
public ImportExportTask(Activity activity, boolean doImport, DataFormat format)
/**
* Constructor which will setup a task for exporting to the given file
*/
ImportExportTask(Activity activity, DataFormat format, OutputStream output,
TaskCompleteListener listener)
{
super();
this.activity = activity;
this.doImport = doImport;
this.doImport = false;
this.format = format;
this.outputStream = output;
this.listener = listener;
}
private void toastWithArg(int stringId, String argument)
/**
* Constructor which will setup a task for importing from the given InputStream.
*/
ImportExportTask(Activity activity, DataFormat format, InputStream input,
TaskCompleteListener listener)
{
final String template = activity.getResources().getString(stringId);
final String message = String.format(template, argument);
activity.runOnUiThread(new Runnable()
{
@Override
public void run()
{
Toast.makeText(activity, message, Toast.LENGTH_LONG).show();
}
});
super();
this.activity = activity;
this.doImport = true;
this.format = format;
this.inputStream = input;
this.listener = listener;
}
private void performImport(File importFile, DBHelper db)
private boolean performImport(InputStream stream, DBHelper db)
{
if(importFile.exists() == false)
{
toastWithArg(R.string.fileMissing, importFile.getAbsolutePath());
return;
}
boolean result = false;
try
{
FileInputStream fileReader = new FileInputStream(importFile);
InputStreamReader reader = new InputStreamReader(fileReader, Charset.forName("UTF-8"));
InputStreamReader reader = new InputStreamReader(stream, Charset.forName("UTF-8"));
result = MultiFormatImporter.importData(db, reader, format);
reader.close();
}
@@ -73,18 +69,18 @@ class ImportExportTask extends AsyncTask<Void, Void, Void>
Log.e(TAG, "Unable to import file", e);
}
int messageId = result ? R.string.importedFrom : R.string.importFailed;
toastWithArg(messageId, importFile.getAbsolutePath());
Log.i(TAG, "Import result: " + result);
return result;
}
private void performExport(File exportFile, DBHelper db)
private boolean performExport(OutputStream stream, DBHelper db)
{
boolean result = false;
try
{
FileOutputStream fileWriter = new FileOutputStream(exportFile);
OutputStreamWriter writer = new OutputStreamWriter(fileWriter, Charset.forName("UTF-8"));
OutputStreamWriter writer = new OutputStreamWriter(stream, Charset.forName("UTF-8"));
result = MultiFormatExporter.exportData(db, writer, format);
writer.close();
}
@@ -93,8 +89,9 @@ class ImportExportTask extends AsyncTask<Void, Void, Void>
Log.e(TAG, "Unable to export file", e);
}
int messageId = result ? R.string.exportedTo : R.string.exportFailed;
toastWithArg(messageId, exportFile.getAbsolutePath());
Log.i(TAG, "Export result: " + result);
return result;
}
protected void onPreExecute()
@@ -114,26 +111,27 @@ class ImportExportTask extends AsyncTask<Void, Void, Void>
progress.show();
}
protected Void doInBackground(Void... nothing)
protected Boolean doInBackground(Void... nothing)
{
final File sdcardDir = Environment.getExternalStorageDirectory();
final File importExportFile = new File(sdcardDir, TARGET_FILE);
final DBHelper db = new DBHelper(activity);
boolean result;
if(doImport)
{
performImport(importExportFile, db);
result = performImport(inputStream, db);
}
else
{
performExport(importExportFile, db);
result = performExport(outputStream, db);
}
return null;
return result;
}
protected void onPostExecute(Void result)
protected void onPostExecute(Boolean result)
{
listener.onTaskComplete(result);
progress.dismiss();
Log.i(TAG, (doImport ? "Import" : "Export") + " Complete");
}
@@ -143,4 +141,9 @@ class ImportExportTask extends AsyncTask<Void, Void, Void>
progress.dismiss();
Log.i(TAG, (doImport ? "Import" : "Export") + " Cancelled");
}
interface TaskCompleteListener
{
void onTaskComplete(boolean success);
}
}

View File

@@ -0,0 +1,95 @@
package protect.card_locker;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import java.io.InvalidObjectException;
public class ImportURIHelper {
private static final String STORE = DBHelper.LoyaltyCardDbIds.STORE;
private static final String NOTE = DBHelper.LoyaltyCardDbIds.NOTE;
private static final String CARD_ID = DBHelper.LoyaltyCardDbIds.CARD_ID;
private static final String BARCODE_TYPE = DBHelper.LoyaltyCardDbIds.BARCODE_TYPE;
private static final String HEADER_COLOR = DBHelper.LoyaltyCardDbIds.HEADER_COLOR;
private final Context context;
private final String host;
private final String path;
private final String oldHost;
private final String oldPath;
private final String shareText;
public ImportURIHelper(Context context) {
this.context = context;
host = context.getResources().getString(R.string.intent_import_card_from_url_host);
path = context.getResources().getString(R.string.intent_import_card_from_url_path_prefix);
oldHost = "brarcher.github.io";
oldPath = "/loyalty-card-locker/share";
shareText = context.getResources().getString(R.string.intent_import_card_from_url_share_text);
}
private boolean isImportUri(Uri uri) {
return (uri.getHost().equals(host) && uri.getPath().equals(path)) || (uri.getHost().equals(oldHost) && uri.getPath().equals(oldPath));
}
public LoyaltyCard parse(Uri uri) throws InvalidObjectException {
if(!isImportUri(uri)) {
throw new InvalidObjectException("Not an import URI");
}
try {
// These values are allowed to be null
Integer headerColor = null;
Integer headerTextColor = null;
String store = uri.getQueryParameter(STORE);
String note = uri.getQueryParameter(NOTE);
String cardId = uri.getQueryParameter(CARD_ID);
String barcodeType = uri.getQueryParameter(BARCODE_TYPE);
if (store == null || note == null || cardId == null || barcodeType == null) throw new InvalidObjectException("Not a valid import URI");
String unparsedHeaderColor = uri.getQueryParameter(HEADER_COLOR);
if(unparsedHeaderColor != null)
{
headerColor = Integer.parseInt(unparsedHeaderColor);
}
return new LoyaltyCard(-1, store, note, cardId, barcodeType, headerColor, headerTextColor, 0);
} catch (NullPointerException | NumberFormatException ex) {
throw new InvalidObjectException("Not a valid import URI");
}
}
// Protected for usage in tests
protected Uri toUri(LoyaltyCard loyaltyCard) {
Uri.Builder uriBuilder = new Uri.Builder();
uriBuilder.scheme("https");
uriBuilder.authority(host);
uriBuilder.path(path);
uriBuilder.appendQueryParameter(STORE, loyaltyCard.store);
uriBuilder.appendQueryParameter(NOTE, loyaltyCard.note);
uriBuilder.appendQueryParameter(CARD_ID, loyaltyCard.cardId);
uriBuilder.appendQueryParameter(BARCODE_TYPE, loyaltyCard.barcodeType);
if(loyaltyCard.headerColor != null)
{
uriBuilder.appendQueryParameter(HEADER_COLOR, loyaltyCard.headerColor.toString());
}
//StarStatus will not be exported
return uriBuilder.build();
}
private void startShareIntent(Uri uri) {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, shareText + "\n" + uri.toString());
sendIntent.setType("text/plain");
Intent shareIntent = Intent.createChooser(sendIntent, null);
context.startActivity(shareIntent);
}
public void startShareIntent(LoyaltyCard loyaltyCard) {
startShareIntent(toUri(loyaltyCard));
}
}

View File

@@ -0,0 +1,140 @@
package protect.card_locker;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.text.TextPaint;
/**
* Original from https://github.com/andOTP/andOTP/blob/master/app/src/main/java/org/shadowice/flocke/andotp/Utilities/LetterBitmap.java
* which was originally from http://stackoverflow.com/questions/23122088/colored-boxed-with-letters-a-la-gmail
* Used to create a {@link Bitmap} that contains a letter used in the English
* alphabet or digit, if there is no letter or digit available, a default image
* is shown instead.
*/
class LetterBitmap
{
/**
* The number of available tile colors
*/
private static final int NUM_OF_TILE_COLORS = 8;
/**
* The letter bitmap
*/
private final Bitmap mBitmap;
/**
* The background color of the letter bitmap
*/
private final Integer mColor;
/**
* Constructor for <code>LetterTileProvider</code>
*
* @param context The {@link Context} to use
* @param displayName The name used to create the letter for the tile
* @param key The key used to generate the background color for the tile
* @param tileLetterFontSize The font size used to display the letter
* @param width The desired width of the tile
* @param height The desired height of the tile
* @param backgroundColor (optional) color to use for background.
* @param textColor (optional) color to use for text.
*/
public LetterBitmap(Context context, String displayName, String key, int tileLetterFontSize,
int width, int height, Integer backgroundColor, Integer textColor)
{
TextPaint paint = new TextPaint();
paint.setTypeface(Typeface.create("sans-serif-light", Typeface.BOLD));
if(textColor != null)
{
paint.setColor(textColor);
}
else
{
paint.setColor(Color.WHITE);
}
paint.setTextAlign(Paint.Align.CENTER);
paint.setAntiAlias(true);
if(backgroundColor == null)
{
mColor = getDefaultColor(context, key);
}
else
{
mColor = backgroundColor;
}
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
String firstChar = displayName.substring(0, 1);
final Canvas c = new Canvas();
c.setBitmap(mBitmap);
c.drawColor(mColor);
char [] firstCharArray = new char[1];
firstCharArray[0] = firstChar.toUpperCase().charAt(0);
paint.setTextSize(tileLetterFontSize);
// The bounds that enclose the letter
Rect bounds = new Rect();
paint.getTextBounds(firstCharArray, 0, 1, bounds);
c.drawText(firstCharArray, 0, 1, width / 2.0f, height / 2.0f
+ (bounds.bottom - bounds.top) / 2.0f, paint);
}
/**
* @return A {@link Bitmap} that contains a letter used in the English
* alphabet or digit, if there is no letter or digit available, a
* default image is shown instead
*/
public Bitmap getLetterTile()
{
return mBitmap;
}
/**
* @return background color used for letter title.
*/
public int getBackgroundColor()
{
return mColor;
}
/**
* @param key The key used to generate the tile color
* @return A new or previously chosen color for <code>key</code> used as the
* tile background color
*/
private static int pickColor(String key, TypedArray colors)
{
// String.hashCode() is not supposed to change across java versions, so
// this should guarantee the same key always maps to the same color
final int color = Math.abs(key.hashCode()) % NUM_OF_TILE_COLORS;
return colors.getColor(color, Color.BLACK);
}
/**
* Determine the color which the letter tile will use if no default
* color is provided.
*/
public static int getDefaultColor(Context context, String key)
{
final Resources res = context.getResources();
TypedArray colors = res.obtainTypedArray(R.array.letter_tile_colors);
int color = pickColor(key, colors);
colors.recycle();
return color;
}
}

View File

@@ -2,6 +2,8 @@ package protect.card_locker;
import android.database.Cursor;
import androidx.annotation.Nullable;
public class LoyaltyCard
{
public final int id;
@@ -10,13 +12,26 @@ public class LoyaltyCard
public final String cardId;
public final String barcodeType;
public LoyaltyCard(final int id, final String store, final String note, final String cardId, final String barcodeType)
@Nullable
public final Integer headerColor;
@Nullable
public final Integer headerTextColor;
public final int starStatus;
public LoyaltyCard(final int id, final String store, final String note, final String cardId,
final String barcodeType, final Integer headerColor, final Integer headerTextColor,
final int starStatus)
{
this.id = id;
this.store = store;
this.note = note;
this.cardId = cardId;
this.barcodeType = barcodeType;
this.headerColor = headerColor;
this.headerTextColor = headerTextColor;
this.starStatus = starStatus;
}
public static LoyaltyCard toLoyaltyCard(Cursor cursor)
@@ -26,7 +41,25 @@ public class LoyaltyCard
String note = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE));
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));
return new LoyaltyCard(id, store, note, cardId, barcodeType);
int headerColorColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_COLOR);
int headerTextColorColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR);
Integer headerColor = null;
Integer headerTextColor = null;
if(cursor.isNull(headerColorColumn) == false)
{
headerColor = cursor.getInt(headerColorColumn);
}
if(cursor.isNull(headerTextColorColumn) == false)
{
headerTextColor = cursor.getInt(headerTextColorColumn);
}
return new LoyaltyCard(id, store, note, cardId, barcodeType, headerColor, headerTextColor, starred);
}
}

View File

@@ -6,13 +6,18 @@ 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 protect.card_locker.preferences.Settings;
class LoyaltyCardCursorAdapter extends CursorAdapter
{
Settings settings;
public LoyaltyCardCursorAdapter(Context context, Cursor cursor)
{
super(context, cursor, 0);
settings = new Settings(context);
}
// The newView method is used to inflate a new view and return it,
@@ -29,25 +34,33 @@ class LoyaltyCardCursorAdapter extends CursorAdapter
public void bindView(View view, Context context, Cursor cursor)
{
// Find fields to populate in inflated template
TextView storeField = (TextView) view.findViewById(R.id.store);
TextView cardIdField = (TextView) view.findViewById(R.id.cardId);
ImageView thumbnail = view.findViewById(R.id.thumbnail);
TextView storeField = view.findViewById(R.id.store);
TextView noteField = view.findViewById(R.id.note);
ImageView star = view.findViewById(R.id.star);
// Extract properties from cursor
LoyaltyCard loyaltyCard = LoyaltyCard.toLoyaltyCard(cursor);
// Populate fields with extracted properties
String storeAndNote = loyaltyCard.store;
if(loyaltyCard.note.isEmpty() == false)
storeField.setText(loyaltyCard.store);
storeField.setTextSize(settings.getCardTitleListFontSize());
if(!loyaltyCard.note.isEmpty())
{
String storeNameAndNoteFormat = view.getResources().getString(R.string.storeNameAndNoteFormat);
storeAndNote = String.format(storeNameAndNoteFormat, loyaltyCard.store, loyaltyCard.note);
noteField.setVisibility(View.VISIBLE);
noteField.setText(loyaltyCard.note);
noteField.setTextSize(settings.getCardNoteListFontSize());
}
else
{
noteField.setVisibility(View.GONE);
}
storeField.setText(storeAndNote);
if (loyaltyCard.starStatus!=0) star.setVisibility(View.VISIBLE);
else star.setVisibility(View.GONE);
String cardIdFormat = view.getResources().getString(R.string.cardIdFormat);
String cardIdLabel = view.getResources().getString(R.string.cardId);
String cardIdText = String.format(cardIdFormat, cardIdLabel, loyaltyCard.cardId);
cardIdField.setText(cardIdText);
thumbnail.setImageBitmap(Utils.generateIcon(context, loyaltyCard.store, loyaltyCard.headerColor).getLetterTile());
}
}

View File

@@ -0,0 +1,625 @@
package protect.card_locker;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import com.google.android.material.chip.Chip;
import com.google.android.material.chip.ChipGroup;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
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.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.google.zxing.BarcodeFormat;
import com.jaredrummler.android.colorpicker.ColorPickerDialog;
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener;
import java.io.InvalidObjectException;
import java.util.ArrayList;
import java.util.List;
public class LoyaltyCardEditActivity extends AppCompatActivity
{
private static final String TAG = "Catima";
ImageView thumbnail;
EditText storeFieldEdit;
EditText noteFieldEdit;
ChipGroup groupsChips;
View cardAndBarcodeLayout;
TextView cardIdFieldView;
View barcodeTypeView;
AutoCompleteTextView barcodeTypeField;
ImageView barcodeImage;
View barcodeImageLayout;
View barcodeCaptureLayout;
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
+ ", updateLoyaltyCard=" + Boolean.toString(updateLoyaltyCard));
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.loyalty_card_edit_activity);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
{
actionBar.setDisplayHomeAsUpEnabled(true);
}
extractIntentFields(getIntent());
db = new DBHelper(this);
importUriHelper = new ImportURIHelper(this);
thumbnail = findViewById(R.id.thumbnail);
storeFieldEdit = findViewById(R.id.storeNameEdit);
noteFieldEdit = findViewById(R.id.noteEdit);
groupsChips = findViewById(R.id.groupChips);
cardAndBarcodeLayout = findViewById(R.id.cardAndBarcodeLayout);
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);
enterButton = findViewById(R.id.enterButton);
storeFieldEdit.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;
generateIcon(s.toString());
}
@Override
public void afterTextChanged(Editable s) { }
});
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()) {
if (formatString.equals(getString(R.string.noBarcode))) {
hideBarcode();
} else {
generateBarcode(s.toString(), BarcodeFormat.valueOf(formatString));
}
}
}
@Override
public void afterTextChanged(Editable s) { }
});
barcodeTypeField.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;
if (!s.toString().isEmpty()) {
if (s.toString().equals(getString(R.string.noBarcode))) {
hideBarcode();
} else {
generateBarcode(cardIdFieldView.getText().toString(), BarcodeFormat.valueOf(s.toString()));
}
}
}
@Override
public void afterTextChanged(Editable s) { }
});
}
@Override
public void onNewIntent(Intent intent)
{
super.onNewIntent(intent);
Log.i(TAG, "Received new intent");
extractIntentFields(intent);
// Reset these fields, so they are re-populated in onResume().
storeFieldEdit.setText("");
noteFieldEdit.setText("");
cardIdFieldView.setText("");
barcodeTypeField.setText("");
}
@Override
public void onResume()
{
super.onResume();
Log.i(TAG, "To view card: " + loyaltyCardId);
if(updateLoyaltyCard)
{
final LoyaltyCard loyaltyCard = db.getLoyaltyCard(loyaltyCardId);
if(loyaltyCard == null)
{
Log.w(TAG, "Could not lookup loyalty card " + loyaltyCardId);
Toast.makeText(this, R.string.noCardExistsError, Toast.LENGTH_LONG).show();
finish();
return;
}
if(storeFieldEdit.getText().length() == 0)
{
storeFieldEdit.setText(loyaltyCard.store);
}
if(noteFieldEdit.getText().length() == 0)
{
noteFieldEdit.setText(loyaltyCard.note);
}
if(cardIdFieldView.getText().length() == 0)
{
cardIdFieldView.setText(loyaltyCard.cardId);
}
if(barcodeTypeField.getText().length() == 0)
{
barcodeTypeField.setText(loyaltyCard.barcodeType.isEmpty() ? getString(R.string.noBarcode) : loyaltyCard.barcodeType);
}
if(headingColorValue == null)
{
headingColorValue = loyaltyCard.headerColor;
if(headingColorValue == null)
{
headingColorValue = LetterBitmap.getDefaultColor(this, loyaltyCard.store);
}
}
setTitle(R.string.editCardTitle);
}
else if(importLoyaltyCardUri != null)
{
// Try to parse
LoyaltyCard importCard;
try {
importCard = importUriHelper.parse(importLoyaltyCardUri);
} catch (InvalidObjectException ex) {
Toast.makeText(this, R.string.failedParsingImportUriError, Toast.LENGTH_LONG).show();
finish();
return;
}
storeFieldEdit.setText(importCard.store);
noteFieldEdit.setText(importCard.note);
cardIdFieldView.setText(importCard.cardId);
barcodeTypeField.setText(importCard.barcodeType);
headingColorValue = importCard.headerColor;
}
else
{
setTitle(R.string.addCardTitle);
hideBarcode();
}
if(groupsChips.getChildCount() == 0)
{
List<Group> existingGroups = db.getGroups();
List<Group> loyaltyCardGroups = db.getLoyaltyCardGroups(loyaltyCardId);
if (existingGroups.isEmpty()) {
groupsChips.setVisibility(View.GONE);
} else {
groupsChips.setVisibility(View.VISIBLE);
}
for (Group group : db.getGroups()) {
Chip chip = (Chip) getLayoutInflater().inflate(R.layout.layout_chip_choice, groupsChips, false);
chip.setText(group._id);
chip.setTag(group);
chip.setChecked(false);
for (Group loyaltyCardGroup : loyaltyCardGroups) {
if (loyaltyCardGroup._id.equals(group._id)) {
chip.setChecked(true);
break;
}
}
chip.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
hasChanged = true;
return false;
}
});
groupsChips.addView(chip);
}
}
if(headingColorValue == null)
{
// Select a random color to start out with.
TypedArray colors = getResources().obtainTypedArray(R.array.letter_tile_colors);
final int color = (int)(Math.random() * colors.length());
headingColorValue = colors.getColor(color, Color.BLACK);
colors.recycle();
}
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();
if(formatString.isEmpty() || formatString.equals(getString(R.string.noBarcode)))
{
hideBarcode();
}
else
{
final BarcodeFormat format = BarcodeFormat.valueOf(formatString);
final String cardIdString = cardIdFieldView.getText().toString();
generateBarcode(cardIdString, format);
}
}
enterButton.setOnClickListener(new EditCardIdAndBarcode());
barcodeImage.setOnClickListener(new EditCardIdAndBarcode());
ArrayList<String> barcodeList = new ArrayList<>(BarcodeSelectorActivity.SUPPORTED_BARCODE_TYPES);
barcodeList.add(0, getString(R.string.noBarcode));
ArrayAdapter<String> barcodeAdapter = new ArrayAdapter<>(this, android.R.layout.select_dialog_item, barcodeList);
barcodeTypeField.setAdapter(barcodeAdapter);
FloatingActionButton saveButton = findViewById(R.id.fabSave);
saveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doSave();
}
});
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(), ScanActivity.class);
final Bundle b = new Bundle();
b.putString("cardId", cardIdFieldView.getText().toString());
i.putExtras(b);
startActivityForResult(i, Utils.BARCODE_SCAN);
}
}
class ColorSelectListener implements View.OnClickListener
{
final int defaultColor;
ColorSelectListener(int defaultColor)
{
this.defaultColor = defaultColor;
}
@Override
public void onClick(View v)
{
ColorPickerDialog dialog = ColorPickerDialog.newBuilder().setColor(defaultColor).create();
dialog.setColorPickerDialogListener(new ColorPickerDialogListener()
{
@Override
public void onColorSelected(int dialogId, int color)
{
hasChanged = true;
headingColorValue = color;
generateIcon(storeFieldEdit.getText().toString());
}
@Override
public void onDialogDismissed(int dialogId)
{
// Nothing to do, no change made
}
});
dialog.show(getFragmentManager(), "color-picker-dialog");
}
}
private void doSave()
{
String store = storeFieldEdit.getText().toString();
String note = noteFieldEdit.getText().toString();
String cardId = cardIdFieldView.getText().toString();
String barcodeType = barcodeTypeField.getText().toString();
// We do not want to save the no barcode string to the database
// it is simply an empty there for no barcode
if(barcodeType.equals(getString(R.string.noBarcode)))
{
barcodeType = "";
}
if(store.isEmpty())
{
Snackbar.make(storeFieldEdit, R.string.noStoreError, Snackbar.LENGTH_LONG).show();
return;
}
if(cardId.isEmpty())
{
Snackbar.make(cardIdFieldView, R.string.noCardIdError, Snackbar.LENGTH_LONG).show();
return;
}
List<Group> selectedGroups = new ArrayList<>();
for (Integer chipId : groupsChips.getCheckedChipIds()) {
Chip chip = groupsChips.findViewById(chipId);
selectedGroups.add((Group) chip.getTag());
}
if(updateLoyaltyCard)
{ //update of "starStatus" not necessary, since it cannot be changed in this activity (only in ViewActivity)
db.updateLoyaltyCard(loyaltyCardId, store, note, cardId, barcodeType, headingColorValue);
Log.i(TAG, "Updated " + loyaltyCardId + " to " + cardId);
}
else
{
loyaltyCardId = (int)db.insertLoyaltyCard(store, note, cardId, barcodeType, headingColorValue, 0);
}
db.setLoyaltyCardGroups(loyaltyCardId, selectedGroups);
finish();
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
if(updateLoyaltyCard)
{
getMenuInflater().inflate(R.menu.card_update_menu, menu);
}
else
{
getMenuInflater().inflate(R.menu.card_add_menu, menu);
}
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
int id = item.getItemId();
switch(id)
{
case android.R.id.home:
askBeforeQuitIfChanged();
break;
case R.id.action_delete:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.deleteTitle);
builder.setMessage(R.string.deleteConfirmation);
builder.setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
Log.e(TAG, "Deleting card: " + loyaltyCardId);
DBHelper db = new DBHelper(LoyaltyCardEditActivity.this);
db.deleteLoyaltyCard(loyaltyCardId);
ShortcutHelper.removeShortcut(LoyaltyCardEditActivity.this, loyaltyCardId);
finish();
dialog.dismiss();
}
});
builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
}
});
AlertDialog dialog = builder.create();
dialog.show();
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);
barcodeType = barcodeValues.format();
cardId = barcodeValues.content();
onResume();
}
private void showBarcode() {
barcodeImageLayout.setVisibility(View.VISIBLE);
}
private void hideBarcode() {
barcodeImageLayout.setVisibility(View.GONE);
}
private void generateBarcode(final String cardId, final BarcodeFormat barcodeFormat) {
if (barcodeImage.getHeight() == 0) {
Log.d(TAG, "ImageView size is not known known at start, waiting for load");
// The size of the ImageView is not yet available as it has not
// 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, cardId, barcodeFormat).execute();
}
});
} else {
Log.d(TAG, "ImageView size known known, creating barcode");
new BarcodeImageWriterTask(barcodeImage, cardId, barcodeFormat).execute();
}
showBarcode();
}
private void generateIcon(String store) {
if (headingColorValue == null) {
return;
}
thumbnail.setBackgroundColor(headingColorValue);
LetterBitmap letterBitmap = Utils.generateIcon(this, store, headingColorValue);
if (letterBitmap != null) {
thumbnail.setImageBitmap(letterBitmap.getLetterTile());
} else {
thumbnail.setImageBitmap(null);
}
thumbnail.setMinimumWidth(thumbnail.getHeight());
}
}

View File

@@ -0,0 +1,15 @@
package protect.card_locker;
import android.app.Application;
import androidx.appcompat.app.AppCompatDelegate;
import protect.card_locker.preferences.Settings;
public class LoyaltyCardLockerApplication extends Application {
public void onCreate() {
super.onCreate();
Settings settings = new Settings(getApplicationContext());
AppCompatDelegate.setDefaultNightMode(settings.getTheme());
}
}

View File

@@ -1,47 +1,191 @@
package protect.card_locker;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
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.Log;
import android.util.TypedValue;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.Button;
import android.widget.EditText;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
import java.util.List;
import protect.card_locker.preferences.Settings;
public class LoyaltyCardViewActivity extends AppCompatActivity
{
private static final String TAG = "CardLocker";
private static final String TAG = "Catima";
private static final int SELECT_BARCODE_REQUEST = 1;
TextView cardIdFieldView;
View bottomSheet;
ImageView bottomSheetButton;
TextView noteView;
TextView groupsView;
TextView storeName;
ImageView barcodeImage;
View collapsingToolbarLayout;
int loyaltyCardId;
LoyaltyCard loyaltyCard;
boolean rotationEnabled;
DBHelper db;
ImportURIHelper importURIHelper;
Settings settings;
String cardIdString;
BarcodeFormat format;
boolean starred;
boolean backgroundNeedsDarkIcons;
boolean barcodeIsFullscreen = false;
ViewGroup.LayoutParams barcodeImageState;
private void extractIntentFields(Intent intent)
{
final Bundle b = intent.getExtras();
loyaltyCardId = b != null ? b.getInt("id") : 0;
Log.d(TAG, "View activity: id=" + loyaltyCardId);
}
private Drawable getIcon(int icon, boolean dark)
{
Drawable unwrappedIcon = AppCompatResources.getDrawable(this, icon);
Drawable wrappedIcon = DrawableCompat.wrap(unwrappedIcon);
if(dark)
{
DrawableCompat.setTint(wrappedIcon, Color.BLACK);
}
else
{
DrawableCompat.setTintList(wrappedIcon, null);
}
return wrappedIcon;
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.loyalty_card_view_activity);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
settings = new Settings(this);
extractIntentFields(getIntent());
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);
groupsView = findViewById(R.id.groupsView);
storeName = findViewById(R.id.storeName);
barcodeImage = findViewById(R.id.barcode);
collapsingToolbarLayout = findViewById(R.id.collapsingToolbarLayout);
rotationEnabled = true;
// Allow making barcode fullscreen on tap
barcodeImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(barcodeIsFullscreen)
{
setFullscreen(false);
}
else
{
setFullscreen(true);
}
}
});
final FloatingActionButton editButton = findViewById(R.id.fabEdit);
editButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
Bundle bundle = new Bundle();
bundle.putInt("id", loyaltyCardId);
bundle.putBoolean("update", true);
intent.putExtras(bundle);
startActivity(intent);
finish();
}
});
final BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
behavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
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(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (behavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
} else {
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
}
});
}
@Override
public void onNewIntent(Intent intent)
{
super.onNewIntent(intent);
Log.i(TAG, "Received new intent");
extractIntentFields(intent);
}
@Override
@@ -49,214 +193,207 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
{
super.onResume();
final Bundle b = getIntent().getExtras();
final int loyaltyCardId = b != null ? b.getInt("id") : 0;
final boolean updateLoyaltyCard = b != null && b.getBoolean("update", false);
final boolean viewLoyaltyCard = b != null && b.getBoolean("view", false);
Log.i(TAG, "To view card: " + loyaltyCardId);
final EditText storeField = (EditText) findViewById(R.id.storeName);
final EditText noteField = (EditText) findViewById(R.id.note);
final EditText cardIdField = (EditText) findViewById(R.id.cardId);
final EditText barcodeTypeField = (EditText) findViewById(R.id.barcodeType);
final ImageView barcodeImage = (ImageView) findViewById(R.id.barcode);
final View barcodeIdLayout = findViewById(R.id.barcodeIdLayout);
final View barcodeTypeLayout = findViewById(R.id.barcodeTypeLayout);
final View barcodeImageLayout = findViewById(R.id.barcodeLayout);
final View barcodeCaptureLayout = findViewById(R.id.barcodeCaptureLayout);
final Button captureButton = (Button) findViewById(R.id.captureButton);
final Button enterButton = (Button) findViewById(R.id.enterButton);
final Button saveButton = (Button) findViewById(R.id.saveButton);
final Button cancelButton = (Button) findViewById(R.id.cancelButton);
final DBHelper db = new DBHelper(this);
if(updateLoyaltyCard || viewLoyaltyCard)
if(barcodeIsFullscreen)
{
final LoyaltyCard loyaltyCard = db.getLoyaltyCard(loyaltyCardId);
// Completely reset state
//
// This prevents the barcode from taking up the entire screen
// on resume and thus being stretched out of proportion.
recreate();
}
if(storeField.getText().length() == 0)
{
storeField.setText(loyaltyCard.store);
}
// The brightness value is on a scale from [0, ..., 1], where
// '1' is the brightest. We attempt to maximize the brightness
// to help barcode readers scan the barcode.
Window window = getWindow();
if(window != null && settings.useMaxBrightnessDisplayingBarcode())
{
WindowManager.LayoutParams attributes = window.getAttributes();
attributes.screenBrightness = 1F;
window.setAttributes(attributes);
}
if(noteField.getText().length() == 0)
{
noteField.setText(loyaltyCard.note);
}
loyaltyCard = db.getLoyaltyCard(loyaltyCardId);
if(loyaltyCard == null)
{
Log.w(TAG, "Could not lookup loyalty card " + loyaltyCardId);
Toast.makeText(this, R.string.noCardExistsError, Toast.LENGTH_LONG).show();
finish();
return;
}
if(cardIdField.getText().length() == 0)
{
cardIdField.setText(loyaltyCard.cardId);
}
String formatString = loyaltyCard.barcodeType;
format = !formatString.isEmpty() ? BarcodeFormat.valueOf(formatString) : null;
cardIdString = loyaltyCard.cardId;
if(barcodeTypeField.getText().length() == 0)
{
barcodeTypeField.setText(loyaltyCard.barcodeType);
}
cardIdFieldView.setText(loyaltyCard.cardId);
TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(cardIdFieldView,
getResources().getInteger(R.integer.settings_card_id_min_font_size_sp)-1, settings.getCardIdFontSize(),
1, TypedValue.COMPLEX_UNIT_SP);
if(viewLoyaltyCard)
{
storeField.setEnabled(false);
noteField.setEnabled(false);
}
if(updateLoyaltyCard)
{
setTitle(R.string.editCardTitle);
}
else
{
barcodeCaptureLayout.setVisibility(View.GONE);
captureButton.setVisibility(View.GONE);
saveButton.setVisibility(View.GONE);
cancelButton.setVisibility(View.GONE);
setTitle(R.string.viewCardTitle);
}
if(loyaltyCard.note.length() > 0)
{
noteView.setVisibility(View.VISIBLE);
noteView.setText(loyaltyCard.note);
}
else
{
setTitle(R.string.addCardTitle);
noteView.setVisibility(View.GONE);
}
if(cardIdField.getText().length() == 0)
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
{
barcodeIdLayout.setVisibility(View.GONE);
groupsView.setVisibility(View.GONE);
}
barcodeTypeLayout.setVisibility(View.GONE);
if(cardIdField.getText().length() > 0 && barcodeTypeField.getText().length() > 0)
if (noteView.getVisibility() == View.VISIBLE || groupsView.getVisibility() == View.VISIBLE) {
bottomSheet.setVisibility(View.VISIBLE);
}
else
{
String formatString = barcodeTypeField.getText().toString();
final BarcodeFormat format = BarcodeFormat.valueOf(formatString);
final String cardIdString = cardIdField.getText().toString();
bottomSheet.setVisibility(View.GONE);
}
storeName.setText(loyaltyCard.store);
storeName.setTextSize(settings.getCardTitleFontSize());
int backgroundHeaderColor;
if(loyaltyCard.headerColor != null)
{
backgroundHeaderColor = loyaltyCard.headerColor;
}
else
{
backgroundHeaderColor = LetterBitmap.getDefaultColor(this, loyaltyCard.store);
}
collapsingToolbarLayout.setBackgroundColor(backgroundHeaderColor);
int textColor;
if(Utils.needsDarkForeground(backgroundHeaderColor))
{
textColor = Color.BLACK;
}
else
{
textColor = Color.WHITE;
}
storeName.setTextColor(textColor);
// If the background is very bright, we should use dark icons
backgroundNeedsDarkIcons = Utils.needsDarkForeground(backgroundHeaderColor);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
{
actionBar.setHomeAsUpIndicator(getIcon(R.drawable.ic_arrow_back_white, backgroundNeedsDarkIcons));
}
// Make notification area light if dark icons are needed
if(Build.VERSION.SDK_INT >= 23)
{
window.getDecorView().setSystemUiVisibility(backgroundNeedsDarkIcons ? View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR : 0);
}
if(Build.VERSION.SDK_INT >= 21)
{
window.setStatusBarColor(Color.TRANSPARENT);
}
// Set shadow colour of store text so even same color on same color would be readable
storeName.setShadowLayer(1, 1, 1, backgroundNeedsDarkIcons ? Color.BLACK : Color.WHITE);
if(format != null)
{
findViewById(R.id.barcode).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()
new ViewTreeObserver.OnGlobalLayoutListener()
{
if (Build.VERSION.SDK_INT < 16)
{
barcodeImage.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
else
@Override
public void onGlobalLayout()
{
barcodeImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
Log.d(TAG, "ImageView size now known");
new BarcodeImageWriterTask(barcodeImage, cardIdString, format).execute();
}
});
Log.d(TAG, "ImageView size now known");
new BarcodeImageWriterTask(barcodeImage, cardIdString, format).execute();
}
});
}
else
{
Log.d(TAG, "ImageView size known known, creating barcode");
new BarcodeImageWriterTask(barcodeImage, cardIdString, format).execute();
}
}
else
{
findViewById(R.id.barcode).setVisibility(View.GONE);
}
}
barcodeIdLayout.setVisibility(View.VISIBLE);
barcodeImageLayout.setVisibility(View.VISIBLE);
@Override
public void onBackPressed() {
if (barcodeIsFullscreen)
{
setFullscreen(false);
return;
}
View.OnClickListener captureCallback = new View.OnClickListener()
{
@Override
public void onClick(View v)
{
IntentIntegrator integrator = new IntentIntegrator(LoyaltyCardViewActivity.this);
integrator.setDesiredBarcodeFormats(BarcodeSelectorActivity.SUPPORTED_BARCODE_TYPES);
String prompt = getResources().getString(R.string.scanCardBarcode);
integrator.setPrompt(prompt);
integrator.initiateScan();
}
};
captureButton.setOnClickListener(captureCallback);
enterButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
Intent i = new Intent(getApplicationContext(), BarcodeSelectorActivity.class);
startActivityForResult(i, SELECT_BARCODE_REQUEST);
}
});
saveButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(final View v)
{
String store = storeField.getText().toString();
String note = noteField.getText().toString();
String cardId = cardIdField.getText().toString();
String barcodeType = barcodeTypeField.getText().toString();
if(store.isEmpty())
{
Snackbar.make(v, R.string.noStoreError, Snackbar.LENGTH_LONG).show();
return;
}
if(cardId.isEmpty() || barcodeType.isEmpty())
{
Snackbar.make(v, R.string.noCardIdError, Snackbar.LENGTH_LONG).show();
return;
}
if(updateLoyaltyCard)
{
db.updateLoyaltyCard(loyaltyCardId, store, note, cardId, barcodeType);
Log.i(TAG, "Updated " + loyaltyCardId + " to " + cardId);
}
else
{
db.insertLoyaltyCard(store, note, cardId, barcodeType);
}
finish();
}
});
cancelButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
finish();
}
});
super.onBackPressed();
return;
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
final Bundle b = getIntent().getExtras();
final boolean updateLoyaltyCard = b != null && b.getBoolean("update", false);
final boolean viewLoyaltyCard = b != null && b.getBoolean("view", false);
getMenuInflater().inflate(R.menu.card_view_menu, menu);
if(viewLoyaltyCard)
// Always calculate lockscreen icon, it may need a black color
boolean lockBarcodeScreenOrientation = settings.getLockBarcodeScreenOrientation();
MenuItem item = menu.findItem(R.id.action_lock_unlock);
setOrientatonLock(item, lockBarcodeScreenOrientation);
if(lockBarcodeScreenOrientation)
{
getMenuInflater().inflate(R.menu.card_edit_menu, menu);
}
else if(updateLoyaltyCard)
{
getMenuInflater().inflate(R.menu.card_delete_menu, menu);
item.setVisible(false);
}
loyaltyCard = db.getLoyaltyCard(loyaltyCardId);
starred = loyaltyCard.starStatus != 0;
menu.findItem(R.id.action_share).setIcon(getIcon(R.drawable.ic_share_white, backgroundNeedsDarkIcons));
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
if (starred) {
menu.findItem(R.id.action_star_unstar).setIcon(getIcon(R.drawable.ic_starred_white, backgroundNeedsDarkIcons));
menu.findItem(R.id.action_star_unstar).setTitle(R.string.unstar);
}
else {
menu.findItem(R.id.action_star_unstar).setIcon(getIcon(R.drawable.ic_unstarred_white, backgroundNeedsDarkIcons));
menu.findItem(R.id.action_star_unstar).setTitle(R.string.star);
}
return true;
}
@@ -265,70 +402,127 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
{
int id = item.getItemId();
final Bundle b = getIntent().getExtras();
final int loyaltyCardId = b != null ? b.getInt("id") : 0;
switch(id)
{
case android.R.id.home:
finish();
break;
case R.id.action_delete:
Log.e(TAG, "Deleting card: " + loyaltyCardId);
DBHelper db = new DBHelper(this);
db.deleteLoyaltyCard(loyaltyCardId);
finish();
case R.id.action_share:
importURIHelper.startShareIntent(loyaltyCard);
return true;
case R.id.action_edit:
Intent intent = new Intent(getApplicationContext(), LoyaltyCardViewActivity.class);
Bundle bundle = new Bundle();
bundle.putInt("id", loyaltyCardId);
bundle.putBoolean("update", true);
intent.putExtras(bundle);
startActivity(intent);
finish();
case R.id.action_lock_unlock:
if(rotationEnabled)
{
setOrientatonLock(item, true);
}
else
{
setOrientatonLock(item, false);
}
rotationEnabled = !rotationEnabled;
return true;
case R.id.action_star_unstar:
starred = !starred;
db.updateLoyaltyCardStarStatus(loyaltyCardId, starred ? 1 : 0);
invalidateOptionsMenu();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent)
private void setOrientatonLock(MenuItem item, boolean lock)
{
String contents = null;
String format = null;
IntentResult result =
IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
if (result != null)
if(lock)
{
Log.i(TAG, "Received barcode information from capture");
contents = result.getContents();
format = result.getFormatName();
item.setIcon(getIcon(R.drawable.ic_lock_outline_white_24dp, backgroundNeedsDarkIcons));
item.setTitle(R.string.unlockScreen);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
}
if(requestCode == SELECT_BARCODE_REQUEST && resultCode == Activity.RESULT_OK)
else
{
Log.i(TAG, "Received barcode information from capture");
contents = intent.getStringExtra(BarcodeSelectorActivity.BARCODE_CONTENTS);
format = intent.getStringExtra(BarcodeSelectorActivity.BARCODE_FORMAT);
}
if(contents != null && contents.isEmpty() == false &&
format != null && format.isEmpty() == false)
{
Log.i(TAG, "Read barcode id: " + contents);
Log.i(TAG, "Read format: " + format);
final EditText cardIdField = (EditText) findViewById(R.id.cardId);
cardIdField.setText(contents);
final EditText barcodeTypeField = (EditText) findViewById(R.id.barcodeType);
barcodeTypeField.setText(format);
onResume();
item.setIcon(getIcon(R.drawable.ic_lock_open_white_24dp, backgroundNeedsDarkIcons));
item.setTitle(R.string.lockScreen);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
}
}
/**
* When enabled, hides the status bar and moves the barcode to the top of the screen.
*
* The purpose of this function is to make sure the barcode can be scanned from the phone
* by machines which offer no space to insert the complete device.
*/
private void setFullscreen(boolean enable)
{
ActionBar actionBar = getSupportActionBar();
if(enable && !barcodeIsFullscreen)
{
// Save previous barcodeImage state
barcodeImageState = barcodeImage.getLayoutParams();
// Hide actionbar
if(actionBar != null)
{
actionBar.hide();
}
// Hide collapsingToolbar
collapsingToolbarLayout.setVisibility(View.GONE);
// Set Android to fullscreen mode
getWindow().getDecorView().setSystemUiVisibility(
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)
{
// Show actionbar
if(actionBar != null)
{
actionBar.show();
}
// Show collapsingToolbar
collapsingToolbarLayout.setVisibility(View.VISIBLE);
// Unset fullscreen mode
getWindow().getDecorView().setSystemUiVisibility(
getWindow().getDecorView().getSystemUiVisibility()
& ~View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
& ~View.SYSTEM_UI_FLAG_FULLSCREEN
);
// Turn barcode back to normal
barcodeImage.setLayoutParams(barcodeImageState);
// Fix barcode centering
barcodeImage.setAdjustViewBounds(true);
// Set current state
barcodeIsFullscreen = false;
}
}
}

View File

@@ -1,72 +1,240 @@
package protect.card_locker;
import android.app.SearchManager;
import android.content.ClipData;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ClipboardManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SearchView;
import androidx.appcompat.widget.Toolbar;
import android.util.Log;
import android.view.ContextMenu;
import android.view.GestureDetector;
import android.view.Menu;
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 java.util.List;
import java.util.Calendar;
import protect.card_locker.preferences.SettingsActivity;
public class MainActivity extends AppCompatActivity
public class MainActivity extends AppCompatActivity implements GestureDetector.OnGestureListener
{
private static final String TAG = "LoyaltyCardLocker";
private static final String TAG = "Catima";
private Menu menu;
private GestureDetector gestureDetector;
protected String filter = "";
protected int selectedTab = 0;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
updateLoyaltyCardList();
updateLoyaltyCardList(filter, null);
TabLayout groupsTabLayout = findViewById(R.id.groups);
groupsTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
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
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
gestureDetector = new GestureDetector(this, this);
View.OnTouchListener gestureTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(final View v, final MotionEvent event){
return gestureDetector.onTouchEvent(event);
}
};
final View helpText = findViewById(R.id.helpText);
final View noMatchingCardsText = findViewById(R.id.noMatchingCardsText);
final View list = findViewById(R.id.list);
helpText.setOnTouchListener(gestureTouchListener);
noMatchingCardsText.setOnTouchListener(gestureTouchListener);
list.setOnTouchListener(gestureTouchListener);
}
@Override
protected void onResume()
{
protected void onResume() {
super.onResume();
updateLoyaltyCardList();
if (menu != null)
{
SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
if (!searchView.isIconified()) {
filter = searchView.getQuery().toString();
}
}
// Start of active tab logic
TabLayout groupsTabLayout = findViewById(R.id.groups);
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 (selectedTab < groupsTabLayout.getTabCount()) {
tab = groupsTabLayout.getTabAt(selectedTab);
}
groupsTabLayout.selectTab(tab);
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(), ScanActivity.class);
startActivityForResult(i, Utils.BARCODE_SCAN);
}
});
}
private void updateLoyaltyCardList()
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
if (requestCode == Utils.MAIN_REQUEST) {
// We're coming back from another view so clear the search
// We only do this now to prevent a flash of all entries right after picking one
filter = "";
if (menu != null)
{
MenuItem searchItem = menu.findItem(R.id.action_search);
searchItem.collapseActionView();
}
// 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);
}
}
@Override
public void onBackPressed() {
if (menu == null)
{
super.onBackPressed();
return;
}
SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
if (!searchView.isIconified()) {
searchView.setIconified(true);
} else {
TabLayout groupsTabLayout = findViewById(R.id.groups);
if (groupsTabLayout.getVisibility() == View.VISIBLE && selectedTab != 0) {
selectedTab = 0;
groupsTabLayout.selectTab(groupsTabLayout.getTabAt(0));
} else {
super.onBackPressed();
}
}
}
private void updateLoyaltyCardList(String filterText, Object tag)
{
final ListView cardList = (ListView) findViewById(R.id.list);
final TextView helpText = (TextView) findViewById(R.id.helpText);
Group group = null;
if (tag != null) {
group = (Group) tag;
}
final ListView cardList = findViewById(R.id.list);
final TextView helpText = findViewById(R.id.helpText);
final TextView noMatchingCardsText = findViewById(R.id.noMatchingCardsText);
final DBHelper db = new DBHelper(this);
Cursor cardCursor = db.getLoyaltyCardCursor(filterText, group);
if(db.getLoyaltyCardCount() > 0)
{
// We want the cardList to be visible regardless of the filtered match count
// to ensure that the noMatchingCardsText doesn't end up being shown below
// the keyboard
cardList.setVisibility(View.VISIBLE);
helpText.setVisibility(View.GONE);
if(cardCursor.getCount() > 0)
{
noMatchingCardsText.setVisibility(View.GONE);
}
else
{
noMatchingCardsText.setVisibility(View.VISIBLE);
}
}
else
{
cardList.setVisibility(View.GONE);
helpText.setVisibility(View.VISIBLE);
noMatchingCardsText.setVisibility(View.GONE);
}
Cursor cardCursor = db.getLoyaltyCardCursor();
final LoyaltyCardCursorAdapter adapter = new LoyaltyCardCursorAdapter(this, cardCursor);
cardList.setAdapter(adapter);
@@ -81,15 +249,47 @@ public class MainActivity extends AppCompatActivity
LoyaltyCard loyaltyCard = LoyaltyCard.toLoyaltyCard(selected);
Intent i = new Intent(view.getContext(), LoyaltyCardViewActivity.class);
i.setAction("");
final Bundle b = new Bundle();
b.putInt("id", loyaltyCard.id);
b.putBoolean("view", true);
i.putExtras(b);
startActivity(i);
ShortcutHelper.updateShortcuts(MainActivity.this, loyaltyCard, i);
startActivityForResult(i, Utils.MAIN_REQUEST);
}
});
}
public void updateTabGroups(TabLayout groupsTabLayout)
{
final DBHelper db = new DBHelper(this);
List<Group> newGroups = db.getGroups();
if (newGroups.size() == 0) {
groupsTabLayout.removeAllTabs();
groupsTabLayout.setVisibility(View.GONE);
return;
}
groupsTabLayout.removeAllTabs();
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);
}
groupsTabLayout.setVisibility(View.VISIBLE);
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo)
{
@@ -105,12 +305,12 @@ public class MainActivity extends AppCompatActivity
public boolean onContextItemSelected(MenuItem item)
{
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
ListView listView = (ListView) findViewById(R.id.list);
ListView listView = findViewById(R.id.list);
Cursor cardCursor = (Cursor)listView.getItemAtPosition(info.position);
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cardCursor);
if(card != null && item.getItemId() == R.id.action_clipboard)
if(item.getItemId() == R.id.action_clipboard)
{
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText(card.store, card.cardId);
@@ -119,6 +319,12 @@ public class MainActivity extends AppCompatActivity
Toast.makeText(this, R.string.copy_to_clipboard_toast, Toast.LENGTH_LONG).show();
return true;
}
else if(item.getItemId() == R.id.action_share)
{
final ImportURIHelper importURIHelper = new ImportURIHelper(this);
importURIHelper.startShareIntent(card);
return true;
}
return super.onContextItemSelected(item);
}
@@ -126,7 +332,42 @@ public class MainActivity extends AppCompatActivity
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
this.menu = menu;
getMenuInflater().inflate(R.menu.main_menu, menu);
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
if (searchManager != null) {
SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
searchView.setSubmitButtonEnabled(false);
searchView.setOnCloseListener(new SearchView.OnCloseListener() {
@Override
public boolean onClose() {
invalidateOptionsMenu();
return false;
}
});
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
filter = newText;
TabLayout groupsTabLayout = findViewById(R.id.groups);
updateLoyaltyCardList(newText, groupsTabLayout.getTabCount() > 0 ? groupsTabLayout.getTabAt(groupsTabLayout.getSelectedTabPosition()).getTag() : null);
return true;
}
});
}
return super.onCreateOptionsMenu(menu);
}
@@ -135,96 +376,111 @@ public class MainActivity extends AppCompatActivity
{
int id = item.getItemId();
if (id == R.id.action_add)
if (id == R.id.action_manage_groups)
{
Intent i = new Intent(getApplicationContext(), LoyaltyCardViewActivity.class);
startActivity(i);
Intent i = new Intent(getApplicationContext(), ManageGroupsActivity.class);
startActivityForResult(i, Utils.MAIN_REQUEST);
return true;
}
if(id == R.id.action_import_export)
{
Intent i = new Intent(getApplicationContext(), ImportExportActivity.class);
startActivity(i);
startActivityForResult(i, Utils.MAIN_REQUEST);
return true;
}
if(id == R.id.action_settings)
{
Intent i = new Intent(getApplicationContext(), SettingsActivity.class);
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()
protected static boolean isDarkModeEnabled(Context inputContext)
{
final String[][] USED_LIBRARIES = new String[][]
{
new String[] {"Commons CSV", "https://commons.apache.org/proper/commons-csv/"},
new String[] {"Guava", "https://github.com/google/guava"},
new String[] {"ZXing", "https://github.com/zxing/zxing"},
new String[] {"ZXing Android Embedded", "https://github.com/journeyapps/zxing-android-embedded"},
};
Configuration config = inputContext.getResources().getConfiguration();
int currentNightMode = config.uiMode & Configuration.UI_MODE_NIGHT_MASK;
return (currentNightMode == Configuration.UI_MODE_NIGHT_YES);
}
StringBuilder libs = new StringBuilder().append("<ul>");
for (String[] library : USED_LIBRARIES)
{
libs.append("<li><a href=\"").append(library[1]).append("\">").append(library[0]).append("</a></li>");
}
libs.append("</ul>");
@Override
public boolean onDown(MotionEvent e) {
return false;
}
String appName = getString(R.string.app_name);
int year = Calendar.getInstance().get(Calendar.YEAR);
@Override
public void onShowPress(MotionEvent e) {
String version = "?";
try
{
PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), 0);
version = pi.versionName;
}
catch (PackageManager.NameNotFoundException e)
{
Log.w(TAG, "Package name not found", e);
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
@Override
public void onLongPress(MotionEvent e) {
}
@Override
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;
}
WebView wv = new WebView(this);
String html =
"<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />" +
"<img src=\"file:///android_res/mipmap/ic_launcher.png\" alt=\"" + appName + "\"/>" +
"<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) + "\">" +
getString(R.string.app_revision_url) +
"</a>") +
"</p><hr/><p>" +
String.format(getString(R.string.app_copyright_fmt), year) +
"</p><hr/><p>" +
getString(R.string.app_license) +
"</p><hr/><p>" +
String.format(getString(R.string.app_libraries), appName, libs.toString());
TabLayout groupsTabLayout = findViewById(R.id.groups);
if (groupsTabLayout.getTabCount() < 2) {
return false;
}
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();
Integer currentTab = groupsTabLayout.getSelectedTabPosition();
// Swipe right
if (velocityX < -150) {
Integer nextTab = currentTab + 1;
if (nextTab == groupsTabLayout.getTabCount()) {
groupsTabLayout.selectTab(groupsTabLayout.getTabAt(0));
} else {
groupsTabLayout.selectTab(groupsTabLayout.getTabAt(nextTab));
}
return true;
}
// Swipe left
if (velocityX > 150) {
Integer nextTab = currentTab - 1;
if (nextTab < 0) {
groupsTabLayout.selectTab(groupsTabLayout.getTabAt(groupsTabLayout.getTabCount() - 1));
} else {
groupsTabLayout.selectTab(groupsTabLayout.getTabAt(nextTab));
}
return true;
}
return false;
}
}

View File

@@ -0,0 +1,255 @@
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;
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;
import androidx.appcompat.widget.Toolbar;
public class ManageGroupsActivity extends AppCompatActivity
{
private static final String TAG = "Catima";
private final DBHelper db = new DBHelper(this);
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.manage_groups_activity);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
{
actionBar.setDisplayHomeAsUpEnabled(true);
}
updateGroupList();
}
@Override
protected void onResume() {
super.onResume();
updateGroupList();
FloatingActionButton addButton = findViewById(R.id.fabAdd);
addButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
createGroup();
}
});
}
@Override
public void onBackPressed() {
super.onBackPressed();
}
private void updateGroupList()
{
final ListView groupList = findViewById(R.id.list);
final TextView helpText = findViewById(R.id.helpText);
final DBHelper db = new DBHelper(this);
if(db.getGroupCount() > 0)
{
groupList.setVisibility(View.VISIBLE);
helpText.setVisibility(View.GONE);
}
else
{
groupList.setVisibility(View.GONE);
helpText.setVisibility(View.VISIBLE);
}
Cursor groupCursor = db.getGroupCursor();
final GroupCursorAdapter adapter = new GroupCursorAdapter(this, groupCursor);
groupList.setAdapter(adapter);
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)
{
int id = item.getItemId();
if (id == android.R.id.home) {
finish();
}
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) {
final String groupName = getGroupname(view);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.enter_group_name);
final EditText input = new EditText(this);
input.setInputType(InputType.TYPE_CLASS_TEXT);
input.setText(groupName);
builder.setView(input);
builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
db.updateGroup(groupName, input.getText().toString());
updateGroupList();
}
});
builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
AlertDialog dialog = builder.create();
dialog.show();
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
input.requestFocus();
}
public void deleteGroup(View view) {
final String groupName = getGroupname(view);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.deleteConfirmationGroup);
builder.setMessage(groupName);
builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
@Override
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() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
private void createGroup() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.enter_group_name);
final EditText input = new EditText(this);
input.setInputType(InputType.TYPE_CLASS_TEXT);
builder.setView(input);
builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
db.insertGroup(input.getText().toString());
updateGroupList();
}
});
builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
AlertDialog dialog = builder.create();
dialog.show();
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
input.requestFocus();
}
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

@@ -7,7 +7,7 @@ import java.io.OutputStreamWriter;
public class MultiFormatExporter
{
private static final String TAG = "LoyaltyCardLocker";
private static final String TAG = "Catima";
/**
* Attempts to export data to the output stream in the

View File

@@ -7,7 +7,7 @@ import java.io.InputStreamReader;
public class MultiFormatImporter
{
private static final String TAG = "LoyaltyCardLocker";
private static final String TAG = "Catima";
/**
* Attempts to import data from the input stream of the

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

@@ -0,0 +1,153 @@
package protect.card_locker;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.graphics.drawable.Icon;
import android.os.Build;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
class ShortcutHelper
{
// Android documentation says that no more than 5 shortcuts
// are supported. However, that may be too many, as not all
// launcher will show all 5. Instead, the number is limited
// to 3 here, so that the most recent shortcut has a good
// chance of being shown.
private static final int MAX_SHORTCUTS = 3;
/**
* Add a card to the app shortcuts, and maintain a list of the most
* recently used cards. If there is already a shortcut for the card,
* the card is marked as the most recently used card. If adding this
* card exceeds the max number of shortcuts, then the least recently
* used card shortcut is discarded.
*/
@TargetApi(25)
static void updateShortcuts(Context context, LoyaltyCard card, Intent intent)
{
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N_MR1)
{
ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class);
LinkedList<ShortcutInfo> list = new LinkedList<>(shortcutManager.getDynamicShortcuts());
String shortcutId = Integer.toString(card.id);
// Sort the shortcuts by rank, so working with the relative order will be easier.
// This sorts so that the lowest rank is first.
Collections.sort(list, new Comparator<ShortcutInfo>()
{
@Override
public int compare(ShortcutInfo o1, ShortcutInfo o2)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1)
{
return o1.getRank() - o2.getRank();
}
else
{
return 0;
}
}
});
Integer foundIndex = null;
for(int index = 0; index < list.size(); index++)
{
if(list.get(index).getId().equals(shortcutId))
{
// Found the item already
foundIndex = index;
break;
}
}
if(foundIndex != null)
{
// If the item is already found, then the list needs to be
// reordered, so that the selected item now has the lowest
// rank, thus letting it survive longer.
ShortcutInfo found = list.remove(foundIndex.intValue());
list.addFirst(found);
}
else
{
// The item is new to the list. First, we need to trim the list
// until it is able to accept a new item, then the item is
// inserted.
while(list.size() >= MAX_SHORTCUTS)
{
list.pollLast();
}
ShortcutInfo shortcut = new ShortcutInfo.Builder(context, Integer.toString(card.id))
.setShortLabel(card.store)
.setLongLabel(card.store)
.setIntent(intent)
.build();
list.addFirst(shortcut);
}
LinkedList<ShortcutInfo> finalList = new LinkedList<>();
// The ranks are now updated; the order in the list is the rank.
for(int index = 0; index < list.size(); index++)
{
ShortcutInfo prevShortcut = list.get(index);
Intent shortcutIntent = prevShortcut.getIntent();
// Prevent instances of the view activity from piling up; if one exists let this
// one replace it.
shortcutIntent.setFlags(shortcutIntent.getFlags() | Intent.FLAG_ACTIVITY_SINGLE_TOP);
ShortcutInfo updatedShortcut = new ShortcutInfo.Builder(context, prevShortcut.getId())
.setShortLabel(prevShortcut.getShortLabel())
.setLongLabel(prevShortcut.getLongLabel())
.setIntent(shortcutIntent)
.setIcon(Icon.createWithResource(context, R.drawable.circle))
.setRank(index)
.build();
finalList.addLast(updatedShortcut);
}
shortcutManager.setDynamicShortcuts(finalList);
}
}
/**
* Remove the given card id from the app shortcuts, if such a
* shortcut exists.
*/
@TargetApi(25)
static void removeShortcut(Context context, int cardId)
{
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N_MR1)
{
ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class);
List<ShortcutInfo> list = shortcutManager.getDynamicShortcuts();
String shortcutId = Integer.toString(cardId);
for(int index = 0; index < list.size(); index++)
{
if(list.get(index).getId().equals(shortcutId))
{
list.remove(index);
break;
}
}
shortcutManager.setDynamicShortcuts(list);
}
}
}

View File

@@ -0,0 +1,64 @@
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 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) {
if (store.length() == 0) {
return null;
}
int tileLetterFontSize = context.getResources().getDimensionPixelSize(R.dimen.tileLetterFontSize);
int pixelSize = context.getResources().getDimensionPixelSize(R.dimen.cardThumbnailSize);
if (backgroundColor == null) {
backgroundColor = LetterBitmap.getDefaultColor(context, store);
}
return new LetterBitmap(context, store, store,
tileLetterFontSize, pixelSize, pixelSize, backgroundColor, needsDarkForeground(backgroundColor) ? Color.BLACK : Color.WHITE);
}
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);
}
}

View File

@@ -0,0 +1,93 @@
package protect.card_locker.preferences;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import androidx.annotation.IntegerRes;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AppCompatDelegate;
import protect.card_locker.R;
public class Settings
{
private Context context;
private SharedPreferences settings;
public Settings(Context context)
{
this.context = context;
this.settings = PreferenceManager.getDefaultSharedPreferences(context);
}
private String getResString(@StringRes int resId)
{
return context.getString(resId);
}
private int getResInt(@IntegerRes int resId)
{
return context.getResources().getInteger(resId);
}
private String getString(@StringRes int keyId, String defaultValue)
{
return settings.getString(getResString(keyId), defaultValue);
}
private int getInt(@StringRes int keyId, @IntegerRes int defaultId)
{
return settings.getInt(getResString(keyId), getResInt(defaultId));
}
private boolean getBoolean(@StringRes int keyId, boolean defaultValue)
{
return settings.getBoolean(getResString(keyId), defaultValue);
}
public int getTheme()
{
String value = getString(R.string.settings_key_theme, getResString(R.string.settings_key_system_theme));
if(value.equals(getResString(R.string.settings_key_light_theme)))
{
return AppCompatDelegate.MODE_NIGHT_NO;
}
else if(value.equals(getResString(R.string.settings_key_dark_theme)))
{
return AppCompatDelegate.MODE_NIGHT_YES;
}
return AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;
}
public int getCardTitleListFontSize()
{
return getInt(R.string.settings_key_card_title_list_font_size, R.integer.settings_card_title_list_font_size_sp);
}
public int getCardNoteListFontSize()
{
return getInt(R.string.settings_key_card_note_list_font_size, R.integer.settings_card_note_list_font_size_sp);
}
public int getCardTitleFontSize()
{
return getInt(R.string.settings_key_card_title_font_size, R.integer.settings_card_title_font_size_sp);
}
public int getCardIdFontSize()
{
return getInt(R.string.settings_key_card_id_font_size, R.integer.settings_card_id_font_size_sp);
}
public boolean useMaxBrightnessDisplayingBarcode()
{
return getBoolean(R.string.settings_key_display_barcode_max_brightness, true);
}
public boolean getLockBarcodeScreenOrientation()
{
return getBoolean(R.string.settings_key_lock_barcode_orientation, false);
}
}

View File

@@ -0,0 +1,81 @@
package protect.card_locker.preferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import android.view.MenuItem;
import protect.card_locker.R;
public class SettingsActivity extends AppCompatActivity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
{
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
// Display the fragment as the main content.
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment())
.commit();
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
int id = item.getItemId();
if(id == android.R.id.home)
{
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
public static class SettingsFragment extends PreferenceFragment
{
@Override
public void onCreate(final Bundle savedInstanceState)
{
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()
{
@Override
public boolean onPreferenceChange(Preference preference, Object o)
{
if(o.toString().equals(getResources().getString(R.string.settings_key_light_theme)))
{
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
}
else if(o.toString().equals(getResources().getString(R.string.settings_key_dark_theme)))
{
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
}
else
{
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
}
getActivity().recreate();
return true;
}
});
}
}
}

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 239 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 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: 302 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: 355 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,66 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="192dp"
android:height="192dp"
android:viewportWidth="50.8"
android:viewportHeight="50.8">
<path
android:pathData="M14.3354,20.1954l20.7318,-9.2304l5.7612,12.9398l-20.7318,9.2304z"
android:strokeLineJoin="miter"
android:strokeWidth="0.529167"
android:fillColor="#f0f0f0"
android:strokeColor="#c80000"/>
<path
android:pathData="M14.8755,10.9648l23.2041,10.3311l-6.8874,15.4694l-23.2041,-10.3311z"
android:strokeLineJoin="miter"
android:strokeWidth="0.529167"
android:fillColor="#f0f0f0"
android:strokeColor="#c80000"/>
<path
android:pathData="M16.5599,16.1348l26.5459,7.6119l-4.5489,15.8639l-26.5459,-7.6119z"
android:strokeLineJoin="round"
android:strokeWidth="1.5875"
android:fillColor="#c80000"
android:strokeColor="#c80000"
android:fillType="evenOdd"/>
<path
android:pathData="M12.011,15.4955h27.6157v16.5032h-27.6157z"
android:strokeLineJoin="round"
android:strokeWidth="1.5875"
android:fillColor="#ff0000"
android:strokeColor="#ff0000"
android:fillType="evenOdd"/>
<path
android:pathData="M7.8471,23.7471a4.3659,8.5899 0,1 0,8.7317 0a4.3659,8.5899 0,1 0,-8.7317 0z"
android:strokeLineJoin="round"
android:strokeWidth="0.91078"
android:fillColor="#ff0000"
android:strokeColor="#ff0000"/>
<path
android:pathData="m24.4983,25.781a1.6711,1.6711 0,0 1,-1.3809 1.6457,1.6711 1.6711,0 0,1 -1.8605,-1.0741"
android:strokeLineJoin="round"
android:strokeWidth="0.529167"
android:fillColor="#00000000"
android:strokeColor="#f0f0f0"
android:strokeLineCap="round"/>
<path
android:pathData="m27.7991,26.333a1.6711,1.6711 0,0 1,-1.8605 1.0741,1.6711 1.6711,0 0,1 -1.3809,-1.6457"
android:strokeLineJoin="round"
android:strokeWidth="0.529167"
android:fillColor="#00000000"
android:strokeColor="#f0f0f0"
android:strokeLineCap="round"/>
<path
android:pathData="m16.0606,22.271 l2.6458,-2.6458 2.6458,2.6458"
android:strokeLineJoin="miter"
android:strokeWidth="0.529167"
android:fillColor="#00000000"
android:strokeColor="#f0f0f0"
android:strokeLineCap="butt"/>
<path
android:pathData="m27.7023,22.271 l2.6458,-2.6458 2.6458,2.6458"
android:strokeLineJoin="miter"
android:strokeWidth="0.529167"
android:fillColor="#00000000"
android:strokeColor="#f0f0f0"
android:strokeLineCap="butt"/>
</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,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,71 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<group android:scaleX="1.5307087"
android:scaleY="1.5307087"
android:translateX="15.12"
android:translateY="15.12">
<path
android:pathData="M14.3354,20.1954l20.7318,-9.2304l5.7612,12.9398l-20.7318,9.2304z"
android:strokeLineJoin="miter"
android:strokeWidth="0.529167"
android:fillColor="#f0f0f0"
android:strokeColor="#c80000"/>
<path
android:pathData="M14.8755,10.9648l23.2041,10.3311l-6.8874,15.4694l-23.2041,-10.3311z"
android:strokeLineJoin="miter"
android:strokeWidth="0.529167"
android:fillColor="#f0f0f0"
android:strokeColor="#c80000"/>
<path
android:pathData="M16.5599,16.1348l26.5459,7.6119l-4.5489,15.8639l-26.5459,-7.6119z"
android:strokeLineJoin="round"
android:strokeWidth="1.5875"
android:fillColor="#c80000"
android:strokeColor="#c80000"
android:fillType="evenOdd"/>
<path
android:pathData="M12.011,15.4955h27.6157v16.5032h-27.6157z"
android:strokeLineJoin="round"
android:strokeWidth="1.5875"
android:fillColor="#ff0000"
android:strokeColor="#ff0000"
android:fillType="evenOdd"/>
<path
android:pathData="M7.8471,23.7471a4.3659,8.5899 0,1 0,8.7317 0a4.3659,8.5899 0,1 0,-8.7317 0z"
android:strokeLineJoin="round"
android:strokeWidth="0.91078"
android:fillColor="#ff0000"
android:strokeColor="#ff0000"/>
<path
android:pathData="m24.4983,25.781a1.6711,1.6711 0,0 1,-1.3809 1.6457,1.6711 1.6711,0 0,1 -1.8605,-1.0741"
android:strokeLineJoin="round"
android:strokeWidth="0.529167"
android:fillColor="#00000000"
android:strokeColor="#f0f0f0"
android:strokeLineCap="round"/>
<path
android:pathData="m27.7991,26.333a1.6711,1.6711 0,0 1,-1.8605 1.0741,1.6711 1.6711,0 0,1 -1.3809,-1.6457"
android:strokeLineJoin="round"
android:strokeWidth="0.529167"
android:fillColor="#00000000"
android:strokeColor="#f0f0f0"
android:strokeLineCap="round"/>
<path
android:pathData="m16.0606,22.271 l2.6458,-2.6458 2.6458,2.6458"
android:strokeLineJoin="miter"
android:strokeWidth="0.529167"
android:fillColor="#00000000"
android:strokeColor="#f0f0f0"
android:strokeLineCap="butt"/>
<path
android:pathData="m27.7023,22.271 l2.6458,-2.6458 2.6458,2.6458"
android:strokeLineJoin="miter"
android:strokeWidth="0.529167"
android:fillColor="#00000000"
android:strokeColor="#f0f0f0"
android:strokeLineCap="butt"/>
</group>
</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>

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="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
</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="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
</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,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
</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,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
</vector>

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="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
</vector>

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="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z"/>
</vector>

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="protect.card_locker.MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay"/>
</com.google.android.material.appbar.AppBarLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="?attr/actionBarSize"
android:orientation="vertical">
<TextView
android:id="@+id/aboutText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:linksClickable="true"
android:singleLine="false"
android:focusable="true" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -1,23 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
</com.google.android.material.appbar.AppBarLayout>
<ScrollView
android:layout_width="fill_parent"
@@ -55,6 +55,7 @@
android:labelFor="@+id/cardId"
android:text="@string/cardId" />
<EditText android:id="@+id/cardId"
android:importantForAutofill="no"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="text"/>
@@ -64,142 +65,244 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/barcodesLayout"/>
<LinearLayout android:orientation="horizontal"
<Button
android:id="@+id/noBarcode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/barcodeNoBarcode"
android:textColor="#FFFFFF"
android:enabled="false" />
<LinearLayout android:orientation="vertical"
android:padding="10.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="@dimen/barcode_disp_height"
android:layout_gravity="center_horizontal"
android:id="@+id/aztecBarcode"
android:contentDescription="@string/barcodeImageDescription"
android:layout_weight="1.0"/>
<TextView
android:id="@+id/aztecBarcodeText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10.0dip"
android:gravity="center"
android:textSize="@dimen/text_size_medium"
android:textStyle="bold"
android:layout_weight="1.0" />
</LinearLayout>
<LinearLayout android:orientation="horizontal"
<LinearLayout android:orientation="vertical"
android:padding="10.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="@dimen/barcode_disp_height"
android:layout_gravity="center_horizontal"
android:id="@+id/code39Barcode"
android:contentDescription="@string/barcodeImageDescription"
android:layout_weight="1.0"/>
<TextView
android:id="@+id/code39BarcodeText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10.0dip"
android:gravity="center"
android:textSize="@dimen/text_size_medium"
android:textStyle="bold"
android:layout_weight="1.0" />
</LinearLayout>
<LinearLayout android:orientation="horizontal"
<LinearLayout android:orientation="vertical"
android:padding="10.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="@dimen/barcode_disp_height"
android:layout_gravity="center_horizontal"
android:id="@+id/code128Barcode"
android:contentDescription="@string/barcodeImageDescription"
android:layout_weight="1.0"/>
<TextView
android:id="@+id/code128BarcodeText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10.0dip"
android:gravity="center"
android:textSize="@dimen/text_size_medium"
android:textStyle="bold"
android:layout_weight="1.0" />
</LinearLayout>
<LinearLayout android:orientation="horizontal"
<LinearLayout android:orientation="vertical"
android:padding="10.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="@dimen/barcode_disp_height"
android:layout_gravity="center_horizontal"
android:id="@+id/codabarBarcode"
android:contentDescription="@string/barcodeImageDescription"
android:layout_weight="1.0"/>
<TextView
android:id="@+id/codabarBarcodeText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10.0dip"
android:gravity="center"
android:textSize="@dimen/text_size_medium"
android:textStyle="bold"
android:layout_weight="1.0" />
</LinearLayout>
<LinearLayout android:orientation="horizontal"
<LinearLayout android:orientation="vertical"
android:padding="10.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="@dimen/barcode_disp_height"
android:layout_gravity="center_horizontal"
android:id="@+id/datamatrixBarcode"
android:contentDescription="@string/barcodeImageDescription"
android:layout_weight="1.0"/>
<TextView
android:id="@+id/datamatrixBarcodeText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10.0dip"
android:gravity="center"
android:textSize="@dimen/text_size_medium"
android:textStyle="bold"
android:layout_weight="1.0" />
</LinearLayout>
<LinearLayout android:orientation="horizontal"
<LinearLayout android:orientation="vertical"
android:padding="10.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="@dimen/barcode_disp_height"
android:layout_gravity="center_horizontal"
android:id="@+id/ean8Barcode"
android:contentDescription="@string/barcodeImageDescription"
android:layout_weight="1.0"/>
<TextView
android:id="@+id/ean8BarcodeText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10.0dip"
android:gravity="center"
android:textSize="@dimen/text_size_medium"
android:textStyle="bold"
android:layout_weight="1.0" />
</LinearLayout>
<LinearLayout android:orientation="horizontal"
<LinearLayout android:orientation="vertical"
android:padding="10.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="@dimen/barcode_disp_height"
android:layout_gravity="center_horizontal"
android:id="@+id/ean13Barcode"
android:contentDescription="@string/barcodeImageDescription"
android:layout_weight="1.0"/>
<TextView
android:id="@+id/ean13BarcodeText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10.0dip"
android:gravity="center"
android:textSize="@dimen/text_size_medium"
android:textStyle="bold"
android:layout_weight="1.0" />
</LinearLayout>
<LinearLayout android:orientation="horizontal"
<LinearLayout android:orientation="vertical"
android:padding="10.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="@dimen/barcode_disp_height"
android:layout_gravity="center_horizontal"
android:id="@+id/itfBarcode"
android:contentDescription="@string/barcodeImageDescription"
android:layout_weight="1.0"/>
<TextView
android:id="@+id/itfBarcodeText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10.0dip"
android:gravity="center"
android:textSize="@dimen/text_size_medium"
android:textStyle="bold"
android:layout_weight="1.0" />
</LinearLayout>
<LinearLayout android:orientation="horizontal"
<LinearLayout android:orientation="vertical"
android:padding="10.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="@dimen/barcode_disp_height"
android:layout_gravity="center_horizontal"
android:id="@+id/pdf417Barcode"
android:contentDescription="@string/barcodeImageDescription"
android:layout_weight="1.0"/>
<TextView
android:id="@+id/pdf417BarcodeText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10.0dip"
android:gravity="center"
android:textSize="@dimen/text_size_medium"
android:textStyle="bold"
android:layout_weight="1.0" />
</LinearLayout>
<LinearLayout android:orientation="horizontal"
<LinearLayout android:orientation="vertical"
android:padding="10.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="@dimen/barcode_disp_height"
android:layout_gravity="center_horizontal"
android:id="@+id/qrcodeBarcode"
android:contentDescription="@string/barcodeImageDescription"
android:layout_weight="1.0"/>
<TextView
android:id="@+id/qrcodeBarcodeText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10.0dip"
android:gravity="center"
android:textSize="@dimen/text_size_medium"
android:textStyle="bold"
android:layout_weight="1.0" />
</LinearLayout>
<LinearLayout android:orientation="horizontal"
<LinearLayout android:orientation="vertical"
android:padding="10.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="@dimen/barcode_disp_height"
android:layout_gravity="center_horizontal"
android:id="@+id/upcaBarcode"
android:contentDescription="@string/barcodeImageDescription"
android:layout_weight="1.0"/>
<TextView
android:id="@+id/upcaBarcodeText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10.0dip"
android:gravity="center"
android:textSize="@dimen/text_size_medium"
android:textStyle="bold"
android:layout_weight="1.0" />
</LinearLayout>
</LinearLayout>
</ScrollView>
</android.support.design.widget.CoordinatorLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -13,13 +13,23 @@
style="@style/AppTheme.TextView.NoData"
android:id="@+id/helpText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/noGiftCards"
android:visibility="gone"/>
<TextView
style="@style/AppTheme.TextView.NoData"
android:id="@+id/noMatchingCardsText"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/noMatchingGiftCards"
android:visibility="gone"/>
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:id="@+id/list"
android:visibility="gone"/>
</RelativeLayout>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Based on https://github.com/journeyapps/zxing-android-embedded/blob/0fdfbce9fb3285e985bad9971c5f7c0a7a334e7b/sample/src/main/res/layout/custom_barcode_scanner.xml originally released under Apache 2.0 -->
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.journeyapps.barcodescanner.BarcodeView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/zxing_barcode_surface"
app:zxing_framing_rect_width="250dp"
app:zxing_framing_rect_height="250dp"/>
<com.journeyapps.barcodescanner.ViewfinderView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/zxing_viewfinder_view"
app:zxing_possible_result_points="@color/zxing_custom_possible_result_points"
app:zxing_result_view="@color/zxing_custom_result_view"
app:zxing_viewfinder_laser="@color/zxing_custom_viewfinder_laser"
app:zxing_viewfinder_mask="@color/zxing_custom_viewfinder_mask"/>
<Button
android:id="@+id/add_manually"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/addManually"
android:layout_gravity="bottom|center_horizontal"
android:onClick="addManually"/>
</merge>

View File

@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:baselineAligned="false"
android:padding="@dimen/activity_margin">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:textSize="@dimen/storeNameTextSize"
android:textStyle="bold"/>
<TextView
android:id="@+id/cardCount"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:maxLines="1"
android:ellipsize="end"
android:textSize="@dimen/noteTextSize"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageButton
android:id="@+id/moveUp"
android:layout_width="@dimen/cardThumbnailSize"
android:layout_height="@dimen/cardThumbnailSize"
android:layout_weight="1"
app:srcCompat="@drawable/ic_baseline_arrow_drop_up_24"
android:contentDescription="@string/moveUp"
app:tint="@color/iconColor"
android:onClick="moveGroupUp"/>
<ImageButton
android:id="@+id/moveDown"
android:layout_width="@dimen/cardThumbnailSize"
android:layout_height="@dimen/cardThumbnailSize"
android:layout_weight="1"
app:srcCompat="@drawable/ic_baseline_arrow_drop_down_24"
android:contentDescription="@string/moveDown"
app:tint="@color/iconColor"
android:onClick="moveGroupDown"/>
<ImageButton
android:id="@+id/edit"
android:layout_width="@dimen/cardThumbnailSize"
android:layout_height="@dimen/cardThumbnailSize"
android:layout_weight="1"
app:srcCompat="@drawable/ic_mode_edit_white_24dp"
android:contentDescription="@string/edit"
app:tint="@color/iconColor"
android:onClick="editGroup" />
<ImageButton
android:id="@+id/delete"
android:layout_width="@dimen/cardThumbnailSize"
android:layout_height="@dimen/cardThumbnailSize"
android:layout_weight="1"
app:srcCompat="@drawable/ic_delete_white_24dp"
android:contentDescription="@string/delete"
app:tint="@color/iconColor"
android:onClick="deleteGroup"/>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="protect.card_locker.MainActivity"
tools:showIn="@layout/main_activity">
<TextView
style="@style/AppTheme.TextView.NoData"
android:id="@+id/helpText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/noGroups"
android:visibility="gone"/>
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/list"
android:visibility="gone"/>
</RelativeLayout>

View File

@@ -1,54 +1,135 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
</com.google.android.material.appbar.AppBarLayout>
<LinearLayout android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="20dp"
android:textSize="20sp"
android:layout_gravity="center"
android:text="@string/importExportHelp"/>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout android:orientation="horizontal"
<LinearLayout android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Button
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingBottom="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/importName"
android:layout_weight="1.0"
android:id="@+id/importButton"/>
<Button
android:layout_width="wrap_content"
android:textSize="@dimen/text_size_medium"
android:layout_gravity="center"
android:text="@string/importExportHelp"/>
<View
android:id="@+id/dividerExport"
android:layout_width="fill_parent"
android:layout_height="1dp"
android:layout_margin="16dp"
android:background="?android:attr/listDivider"/>
<TextView
android:id="@+id/exportOptionTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/exportName"
android:layout_weight="1.0"
android:id="@+id/exportButton"/>
android:textSize="@dimen/text_size_large"
android:text="@string/exportName"/>
<TextView
android:id="@+id/exportOptionExplanation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textSize="@dimen/text_size_medium"
android:text="@string/exportOptionExplanation"/>
<Button
android:id="@+id/exportButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="8dp"
android:text="@string/exportName" />
<View
android:id="@+id/dividerImportFilesystem"
android:layout_width="fill_parent"
android:layout_height="1dp"
android:layout_margin="16dp"
android:background="?android:attr/listDivider"/>
<TextView
android:id="@+id/importOptionFilesystemTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/text_size_large"
android:text="@string/importOptionFilesystemTitle"/>
<TextView
android:id="@+id/importOptionFilesystemExplanation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textSize="@dimen/text_size_medium"
android:text="@string/importOptionFilesystemExplanation"/>
<Button
android:id="@+id/importOptionFilesystemButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="8dp"
android:text="@string/importOptionFilesystemButton" />
<View
android:id="@+id/dividerImportApplication"
android:layout_width="fill_parent"
android:layout_height="1dp"
android:layout_margin="16dp"
android:background="?android:attr/listDivider"/>
<TextView
android:id="@+id/importOptionApplicationTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/text_size_large"
android:text="@string/importOptionApplicationTitle"/>
<TextView
android:id="@+id/importOptionApplicationExplanation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textSize="@dimen/text_size_medium"
android:text="@string/importOptionApplicationExplanation"/>
<Button
android:id="@+id/importOptionApplicationButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="8dp"
android:text="@string/importOptionApplicationButton" />
</LinearLayout>
</LinearLayout>
</ScrollView>
</android.support.design.widget.CoordinatorLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -0,0 +1,9 @@
<com.google.android.material.chip.Chip xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="8dp"
android:paddingRight="8dp"
style="@style/Widget.MaterialComponents.Chip.Choice"
app:checkedIconVisible="true"
android:textAppearance="?android:attr/textAppearance" />

View File

@@ -0,0 +1,206 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabSave"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
app:srcCompat="@drawable/save_24dp"
android:contentDescription="@string/save"
android:layout_margin="16dp" />
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<ScrollView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@color/inputContrastBackground"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- Store -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/inputPadding"
android:paddingTop="@dimen/inputPadding"
android:orientation="horizontal">
<androidx.cardview.widget.CardView
android:layout_width="@dimen/cardThumbnailSize"
android:layout_height="@dimen/cardThumbnailSize"
android:layout_marginEnd="@dimen/activity_margin"
android:layout_gravity="center_vertical"
app:cardCornerRadius="4dp"
android:paddingHorizontal="@dimen/inputPadding"
app:cardElevation="0dp">
<ImageView
android:id="@+id/thumbnail"
android:layout_width="@dimen/cardThumbnailSize"
android:layout_height="@dimen/cardThumbnailSize"
android:contentDescription="@string/thumbnailDescription"
android:src="@mipmap/ic_launcher"/>
</androidx.cardview.widget.CardView>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/storeNameField"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/storeName">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/storeNameEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<!-- Note -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/inputPadding"
android:paddingTop="@dimen/inputPadding"
android:orientation="horizontal">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/noteField"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/note">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/noteEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<!-- Group -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/inputPadding"
android:paddingTop="@dimen/inputPadding"
android:orientation="horizontal">
<com.google.android.material.chip.ChipGroup
android:id="@+id/groupChips"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:padding="@dimen/inputPadding"
android:textSize="@dimen/inputSize" />
</LinearLayout>
<!-- Buttons -->
<LinearLayout android:orientation="horizontal"
android:padding="10.0dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/barcodeCaptureLayout">
<Button android:id="@+id/enterButton"
android:layout_margin="@dimen/inputMargin"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/editCard"
android:textColor="#FFFFFF"
android:layout_weight="1.0"/>
</LinearLayout>
<!-- Card ID and Barcode type -->
<LinearLayout
android:id="@+id/cardAndBarcodeLayout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/inputPadding"
android:paddingTop="@dimen/inputPadding"
android:orientation="horizontal"
android:baselineAligned="false">
<!-- Card ID -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/cardIdField"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:hint="@string/cardId">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/cardIdView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</com.google.android.material.textfield.TextInputLayout>
<!-- Barcode type -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/barcodeTypeView"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:hint="@string/barcodeType"
android:labelFor="@+id/barcodeTypeField">
<AutoCompleteTextView
android:id="@+id/barcodeTypeField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<!-- Barcode -->
<View
android:layout_height="@dimen/inputBorderThickness"
android:layout_width="match_parent" />
<LinearLayout android:orientation="horizontal"
android:padding="10.0dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:id="@+id/barcodeLayout">
<ImageView
android:layout_width="0dp"
android:layout_height="@dimen/barcode_disp_height"
android:layout_gravity="center_horizontal"
android:padding="10.0dp"
android:background="#ffffff"
android:id="@+id/barcode"
android:contentDescription="@string/barcodeImageDescription"
android:layout_weight="1.0"/>
</LinearLayout>
</TableLayout>
</ScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -1,32 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:orientation="vertical"
android:padding="10.0dp"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout android:orientation="horizontal"
android:padding="5.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:baselineAligned="true">
<TextView android:textSize="20.0sp"
android:id="@+id/store"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:maxLines="1"/>
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:baselineAligned="false"
android:padding="@dimen/activity_margin">
<LinearLayout android:orientation="horizontal"
android:padding="5.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:baselineAligned="true">
<TextView android:textSize="20.0sp"
android:layout_gravity="start"
android:id="@+id/cardId"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<androidx.cardview.widget.CardView
android:layout_width="@dimen/cardThumbnailSize"
android:layout_height="@dimen/cardThumbnailSize"
android:layout_marginEnd="@dimen/activity_margin"
app:cardCornerRadius="4dp"
app:cardElevation="0dp">
</LinearLayout>
<ImageView
android:id="@+id/thumbnail"
android:layout_width="@dimen/cardThumbnailSize"
android:layout_height="@dimen/cardThumbnailSize"
android:contentDescription="@string/thumbnailDescription"
android:src="@mipmap/ic_launcher"/>
</androidx.cardview.widget.CardView>
<LinearLayout
android:orientation="vertical"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1">
<LinearLayout
android:id="@+id/valueLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="visible" >
<TextView
android:id="@+id/store"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/storeNameTextSize"
android:textStyle="bold"/>
</LinearLayout>
<TextView
android:id="@+id/note"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:ellipsize="end"
android:textSize="@dimen/noteTextSize"/>
</LinearLayout>
<ImageView
android:id="@+id/star"
android:layout_width="@dimen/cardThumbnailSize"
android:layout_height="@dimen/cardThumbnailSize"
android:layout_marginStart="@dimen/activity_margin"
app:srcCompat="@drawable/ic_starred_white"
app:tint="@color/iconColor"
android:contentDescription="@string/starImage"/>
</LinearLayout>

View File

@@ -1,164 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:id="@+id/scrollView">
<LinearLayout android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<LinearLayout android:orientation="horizontal"
android:padding="10.0dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView android:textSize="16.0sp"
android:textStyle="bold"
android:layout_gravity="center_vertical"
android:paddingStart="20.0dip"
android:paddingEnd="20.0dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@+id/storeName"
android:text="@string/storeName" />
<LinearLayout android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<EditText android:id="@+id/storeName"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="text"/>
</LinearLayout>
</LinearLayout>
<LinearLayout android:orientation="horizontal"
android:padding="10.0dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView android:textSize="16.0sp"
android:textStyle="bold"
android:layout_gravity="center_vertical"
android:paddingStart="20.0dip"
android:paddingEnd="20.0dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@+id/note"
android:text="@string/note" />
<LinearLayout android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<EditText android:id="@+id/note"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="text"/>
</LinearLayout>
</LinearLayout>
<LinearLayout android:orientation="horizontal"
android:padding="10.0dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/barcodeIdLayout">
<TextView android:textSize="16.0sp"
android:textStyle="bold"
android:layout_gravity="center_vertical"
android:paddingStart="20.0dip"
android:paddingEnd="20.0dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@+id/cardId"
android:text="@string/cardId" />
<EditText android:id="@+id/cardId"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:enabled="false"
android:inputType="text"/>
</LinearLayout>
<LinearLayout android:orientation="horizontal"
android:padding="10.0dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/barcodeTypeLayout">
<TextView android:textSize="16.0sp"
android:textStyle="bold"
android:layout_gravity="center_vertical"
android:paddingStart="20.0dip"
android:paddingEnd="20.0dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@+id/barcodeType"
android:text="@string/barcodeType" />
<EditText android:id="@+id/barcodeType"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:enabled="false"
android:inputType="text"/>
</LinearLayout>
<LinearLayout android:orientation="horizontal"
android:padding="10.0dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:id="@+id/barcodeLayout">
<ImageView
android:layout_width="0dp"
android:layout_height="@dimen/barcode_disp_height"
android:layout_gravity="center_horizontal"
android:id="@+id/barcode"
android:contentDescription="@string/barcodeImageDescription"
android:layout_weight="1.0"/>
</LinearLayout>
<LinearLayout android:orientation="horizontal"
android:padding="10.0dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/barcodeCaptureLayout">
<Button android:id="@+id/captureButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/capture"
android:layout_weight="1.0"/>
<Button android:id="@+id/enterButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/enterCard"
android:layout_weight="1.0"/>
</LinearLayout>
<LinearLayout android:orientation="horizontal"
android:padding="10.0dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:baselineAligned="true">
<Button android:id="@+id/cancelButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/cancel"
android:layout_weight="1.0" />
<Button android:id="@+id/saveButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/save"
android:layout_weight="1.0" />
</LinearLayout>
</LinearLayout>
</ScrollView>
</android.support.design.widget.CoordinatorLayout>

View File

@@ -0,0 +1,169 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/coordinator_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fitsSystemWindows="true"
>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabEdit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
app:srcCompat="@drawable/ic_mode_edit_white_24dp"
android:contentDescription="@string/edit"
android:layout_margin="16dp" />
<FrameLayout
android:clipChildren="false"
android:clipToPadding="false"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/centerGuideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5"/>
<ImageView
android:id="@+id/barcode"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="20.0dip"
android:layout_marginBottom="10.0dip"
android:layout_marginStart="15.0dip"
android:layout_marginEnd="15.0dip"
android:padding="10.0dp"
android:background="#ffffff"
app:layout_constraintBottom_toTopOf="@+id/centerGuideline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:contentDescription="@string/barcodeImageDescription"/>
<TextView
android:id="@+id/cardIdView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginLeft="10.0dip"
android:layout_marginRight="10.0dip"
app:layout_constraintTop_toBottomOf="@+id/barcode"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:textAlignment="center"
app:autoSizeTextType="uniform"
app:autoSizeMinTextSize="@dimen/singleCardCardIdTextSizeMin"
app:autoSizeMaxTextSize="@dimen/singleCardCardIdTextSizeMax"
android:ellipsize="end"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<View
android:id="@+id/drop_shadow_actionbar"
android:layout_width="fill_parent"
android:layout_height="5.0dip"
android:layout_gravity="top"/>
</FrameLayout>
<LinearLayout
android:id="@+id/bottom_sheet"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:behavior_hideable="false"
app:behavior_peekHeight="80dp"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<ImageButton
android:id="@+id/bottomSheetButton"
android:layout_width="match_parent"
android:layout_height="80dp"
android:background="@color/colorPrimary"
android:gravity="center"
app:srcCompat="@drawable/ic_baseline_arrow_drop_up_24"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:tint="@android:color/white" />
<TextView
android:id="@+id/noteView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/inputBackground"
android:gravity="center"
android:padding="20dp"
app:autoSizeTextType="uniform"
app:autoSizeMinTextSize="@dimen/singleCardNoteTextSizeMin"
app:autoSizeMaxTextSize="@dimen/singleCardNoteTextSizeMax" />
<TextView
android:id="@+id/groupsView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/inputBackground"
android:gravity="center"
android:padding="20dp"
app:autoSizeTextType="uniform"
app:autoSizeMinTextSize="@dimen/singleCardNoteTextSizeMin"
app:autoSizeMaxTextSize="@dimen/singleCardNoteTextSizeMax" />
</LinearLayout>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar_layout"
android:background="@android:color/transparent"
android:clipChildren="false"
android:clipToPadding="false"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:weightSum="1.0"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsingToolbarLayout"
android:clipChildren="false"
android:clipToPadding="false"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:minHeight="56.0dip"
android:layout_weight="1.0"
app:expandedTitleMarginStart="48dp"
app:expandedTitleMarginEnd="64dp"
app:contentScrim="?colorPrimary"
app:expandedTitleGravity="top">
<TextView
android:id="@+id/storeName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:ellipsize="end"
android:textColor="@android:color/white"
android:textSize="40sp"
android:textAlignment="center"
android:layout_gravity="center"
android:layout_marginTop="?actionBarSize"
android:layout_marginBottom="?actionBarSize"
app:layout_collapseMode="parallax"
android:fitsSystemWindows="true"/>
<androidx.appcompat.widget.Toolbar
android:id="@id/toolbar"
android:background="@android:color/transparent"
android:theme="@style/CardView.ActionBarTheme"
android:layout_width="fill_parent"
android:layout_height="?actionBarSize"
app:contentInsetStart="72.0dip"
app:layout_collapseMode="pin" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
@@ -8,20 +8,36 @@
android:fitsSystemWindows="true"
tools:context="protect.card_locker.MainActivity">
<android.support.design.widget.AppBarLayout
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabAdd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
app:srcCompat="@drawable/ic_add_white_24dp"
android:contentDescription="@string/action_add"
android:layout_margin="16dp" />
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay"/>
</android.support.design.widget.AppBarLayout>
<com.google.android.material.tabs.TabLayout
android:id="@+id/groups"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="scrollable"
android:visibility="gone"/>
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/content_main"/>
</android.support.design.widget.CoordinatorLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="protect.card_locker.ManageGroupsActivity">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabAdd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
app:srcCompat="@drawable/ic_add_white_24dp"
android:contentDescription="@string/action_add"
android:layout_margin="16dp" />
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay"/>
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/group_main"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Based on https://github.com/journeyapps/zxing-android-embedded/blob/0fdfbce9fb3285e985bad9971c5f7c0a7a334e7b/sample/src/main/res/layout/activity_custom_scanner.xml originally licensed under Apache 2.0 -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ScanActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<com.journeyapps.barcodescanner.DecoratedBarcodeView
android:id="@+id/zxing_barcode_scanner"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:zxing_scanner_layout="@layout/custom_barcode_scanner">
</com.journeyapps.barcodescanner.DecoratedBarcodeView>
</RelativeLayout>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
</menu>

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_edit"
android:icon="@drawable/ic_mode_edit_white_24dp"
android:title="@string/edit"
app:showAsAction="always"/>
</menu>

View File

@@ -6,4 +6,8 @@
android:id="@+id/action_clipboard"
android:title="@string/copy_to_clipboard"
app:showAsAction="always"/>
<item
android:id="@+id/action_share"
android:title="@string/share"
app:showAsAction="always"/>
</menu>

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_delete"
android:icon="@drawable/ic_delete_white_24dp"

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