Compare commits

...

1357 Commits

Author SHA1 Message Date
Sylvia van Os
d2a500824a Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-12-04 14:04:01 +01:00
Sylvia van Os
6a91d59050 Fix crash on sharing card + release 2.11.2 2021-12-04 14:03:52 +01:00
bors[bot]
451c49d05f Merge #643
643: Translations update from Hosted Weblate r=TheLastProject a=weblate

Translations update from [Hosted Weblate](https://hosted.weblate.org) for [Catima/Catima](https://hosted.weblate.org/projects/catima/catima/).


It also includes following components:

* [Catima/Fastlane](https://hosted.weblate.org/projects/catima/fastlane/)



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Nyatsuki <Odamaki@yandex.ru>
Co-authored-by: Oğuz Ersen <oguzersen@protonmail.com>
2021-12-02 11:24:18 +00:00
Oğuz Ersen
b4ec4b74ca Translated using Weblate (Turkish)
Currently translated at 26.8% (25 of 93 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/tr/
2021-12-02 10:54:32 +01:00
Nyatsuki
6c254315af Translated using Weblate (Japanese)
Currently translated at 99.1% (230 of 232 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ja/
2021-12-02 10:54:31 +01:00
Allan Nordhøy
dfaf61722f Translated using Weblate (Norwegian Bokmål)
Currently translated at 20.4% (19 of 93 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/nb_NO/
2021-12-02 10:54:30 +01:00
bors[bot]
2dbebea884 Merge #642
642: Bump robolectric from 4.7.2 to 4.7.3 r=TheLastProject a=dependabot[bot]

Bumps [robolectric](https://github.com/robolectric/robolectric) from 4.7.2 to 4.7.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/robolectric/robolectric/releases">robolectric's releases</a>.</em></p>
<blockquote>
<h2>Robolectric 4.7.3</h2>
<p>This is a minor release that fixes <a href="https://github-redirect.dependabot.com/robolectric/robolectric/issues/6883">#6883</a>, a NoClassDefError that can occur if the <code>compileSdk</code> &lt; 31. Thanks for <a href="https://github.com/ninniuz"><code>`@​ninniuz</code></a>` for reporting and <a href="https://github.com/utzcoz"><code>`@​utzcoz</code></a>` for the fix (<a href="https://github-redirect.dependabot.com/robolectric/robolectric/issues/6884">#6884</a>).</p>
<p>It also fixes a minor case of test pollution, where a single Activity could leak across tests (see 5a1f02aaf37f425f8938ae41df5d7aa1b72bba9c for details).</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="3e1d0a34a1"><code>3e1d0a3</code></a> Bump version to 4.7.3.</li>
<li><a href="aab519415e"><code>aab5194</code></a> Use 'processResources' task instead of 'jar' task for nativeruntime</li>
<li><a href="66829f64e1"><code>66829f6</code></a> Clear InputMethodManager.sInstance for SDK &gt; P</li>
<li><a href="3e596affab"><code>3e596af</code></a> Only using AttributionSource for compile SDK 31 and above</li>
<li>See full diff in <a href="https://github.com/robolectric/robolectric/compare/robolectric-4.7.2...robolectric-4.7.3">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.robolectric:robolectric&package-manager=gradle&previous-version=4.7.2&new-version=4.7.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting ``@dependabot` rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- ``@dependabot` rebase` will rebase this PR
- ``@dependabot` recreate` will recreate this PR, overwriting any edits that have been made to it
- ``@dependabot` merge` will merge this PR after your CI passes on it
- ``@dependabot` squash and merge` will squash and merge this PR after your CI passes on it
- ``@dependabot` cancel merge` will cancel a previously requested merge and block automerging
- ``@dependabot` reopen` will reopen this PR if it is closed
- ``@dependabot` close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- ``@dependabot` ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-02 07:09:06 +00:00
dependabot[bot]
d7da942481 Bump robolectric from 4.7.2 to 4.7.3
Bumps [robolectric](https://github.com/robolectric/robolectric) from 4.7.2 to 4.7.3.
- [Release notes](https://github.com/robolectric/robolectric/releases)
- [Commits](https://github.com/robolectric/robolectric/compare/robolectric-4.7.2...robolectric-4.7.3)

---
updated-dependencies:
- dependency-name: org.robolectric:robolectric
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-02 02:22:33 +00:00
Sylvia van Os
6b4a01c954 Release Catima 2.11.1 2021-11-30 18:30:29 +01:00
Sylvia van Os
aee7728f56 Merge pull request #639 from TheLastProject/create-pull-request/patch-1638292575
Update Fastlane changelogs
2021-11-30 18:16:54 +01:00
TheLastProject
6fbbe38542 Update Fastlane changelogs 2021-11-30 17:16:15 +00:00
Sylvia van Os
651744b770 Update CHANGELOG 2021-11-30 18:15:51 +01:00
Sylvia van Os
a189ac22ce Prevent white on white status bar on Android 5 2021-11-29 21:29:44 +01:00
Sylvia van Os
f4d71e4525 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-11-29 20:23:24 +01:00
Sylvia van Os
45ec062499 Remove selection animator as it can break (fixes #630) 2021-11-29 20:23:17 +01:00
Sylvia van Os
82d8e84d78 Merge pull request #633 from TheLastProject/create-pull-request/patch-1638210607
Update Fastlane changelogs
2021-11-29 19:31:13 +01:00
TheLastProject
fa25bff46f Update Fastlane changelogs 2021-11-29 18:30:07 +00:00
Sylvia van Os
5648061a31 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-11-29 19:29:44 +01:00
Sylvia van Os
9cd2f66b40 Naively fix blurriness of letter icons 2021-11-29 19:29:36 +01:00
bors[bot]
90b7b43faf Merge #626
626: Translations update from Hosted Weblate r=TheLastProject a=weblate

Translations update from [Hosted Weblate](https://hosted.weblate.org) for [Catima/Catima](https://hosted.weblate.org/projects/catima/catima/).


It also includes following components:

* [Catima/Fastlane](https://hosted.weblate.org/projects/catima/fastlane/)



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: 109247019824 <stoyan@gmx.com>
2021-11-29 07:09:59 +00:00
109247019824
cfe5865d35 Translated using Weblate (Bulgarian)
Currently translated at 100.0% (232 of 232 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/bg/
2021-11-29 00:52:08 +01:00
Allan Nordhøy
243898c441 Translated using Weblate (Norwegian Bokmål)
Currently translated at 19.5% (18 of 92 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/nb_NO/
2021-11-29 00:52:08 +01:00
Sylvia van Os
0afc6f3976 Release Catima 2.11.0 2021-11-28 23:05:54 +01:00
Sylvia van Os
fa95ea6a62 Make spotBugs happy 2021-11-28 22:57:10 +01:00
Sylvia van Os
abb559afd6 Merge pull request #624 from TheLastProject/create-pull-request/patch-1638136040
Update Fastlane changelogs
2021-11-28 22:49:05 +01:00
TheLastProject
61e7d1b154 Update Fastlane changelogs 2021-11-28 21:47:20 +00:00
Sylvia van Os
da594a8287 Fix getItemId mistake (fixes #623) 2021-11-28 22:47:02 +01:00
Sylvia van Os
15f5c4a67b Make cropper respect custom theme choice 2021-11-28 14:54:49 +01:00
Sylvia van Os
19f7af1e42 Use cropper card ratio (default) for icon layout 2021-11-28 14:14:45 +01:00
bors[bot]
b5b994642a Merge #621
621: Update contributors r=TheLastProject a=github-actions[bot]

Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action

Co-authored-by: TheLastProject <TheLastProject@users.noreply.github.com>
2021-11-28 08:38:54 +00:00
TheLastProject
45da445866 Update contributors 2021-11-28 05:15:51 +00:00
bors[bot]
55b1ad1c55 Merge #613
613: Bump NumberPickerPreference from 1.0.3 to 1.0.4 r=TheLastProject a=dependabot[bot]

Bumps [NumberPickerPreference](https://github.com/invissvenska/NumberPickerPreference) from 1.0.3 to 1.0.4.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/invissvenska/NumberPickerPreference/releases">NumberPickerPreference's releases</a>.</em></p>
<blockquote>
<h2>Version 1.0.4</h2>
<h2>What's Changed</h2>
<ul>
<li>Migrate to latest Gradle version and Maven Publish plugin by <a href="https://github.com/invissvenska"><code>`@​invissvenska</code></a>` in <a href="https://github-redirect.dependabot.com/invissvenska/NumberPickerPreference/pull/10">invissvenska/NumberPickerPreference#10</a></li>
<li>Feature/migrate to new gradle and maven plugin by <a href="https://github.com/invissvenska"><code>`@​invissvenska</code></a>` in <a href="https://github-redirect.dependabot.com/invissvenska/NumberPickerPreference/pull/11">invissvenska/NumberPickerPreference#11</a></li>
<li>Develop by <a href="https://github.com/invissvenska"><code>`@​invissvenska</code></a>` in <a href="https://github-redirect.dependabot.com/invissvenska/NumberPickerPreference/pull/12">invissvenska/NumberPickerPreference#12</a></li>
<li>Develop by <a href="https://github.com/invissvenska"><code>`@​invissvenska</code></a>` in <a href="https://github-redirect.dependabot.com/invissvenska/NumberPickerPreference/pull/14">invissvenska/NumberPickerPreference#14</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/invissvenska/NumberPickerPreference/compare/1.0.3...1.0.4">https://github.com/invissvenska/NumberPickerPreference/compare/1.0.3...1.0.4</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="9ee00acc93"><code>9ee00ac</code></a> Develop (<a href="https://github-redirect.dependabot.com/invissvenska/NumberPickerPreference/issues/14">#14</a>)</li>
<li><a href="1035c87ae1"><code>1035c87</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/invissvenska/NumberPickerPreference/issues/12">#12</a> from invissvenska/develop</li>
<li><a href="93a5d495df"><code>93a5d49</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/invissvenska/NumberPickerPreference/issues/11">#11</a> from invissvenska/feature/migrate_to_new_gradle_and_ma...</li>
<li><a href="300af7866a"><code>300af78</code></a> fix <a href="https://github-redirect.dependabot.com/invissvenska/NumberPickerPreference/issues/9">#9</a></li>
<li><a href="391b8a8593"><code>391b8a8</code></a> increment version, removed commented code from gradle</li>
<li><a href="ef4a9b3a63"><code>ef4a9b3</code></a> set java version to 11 for jitpack by adding the jitpack yml</li>
<li><a href="c28b95e5c8"><code>c28b95e</code></a> set java version to 11</li>
<li><a href="2df1805d82"><code>2df1805</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/invissvenska/NumberPickerPreference/issues/10">#10</a> from invissvenska/feature/migrate_to_new_gradle_and_ma...</li>
<li><a href="71f4f5992e"><code>71f4f59</code></a> Migrate to latest Gradle version and Maven Publish plugin</li>
<li>See full diff in <a href="https://github.com/invissvenska/NumberPickerPreference/compare/1.0.3...1.0.4">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=com.github.invissvenska:NumberPickerPreference&package-manager=gradle&previous-version=1.0.3&new-version=1.0.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting ``@dependabot` rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- ``@dependabot` rebase` will rebase this PR
- ``@dependabot` recreate` will recreate this PR, overwriting any edits that have been made to it
- ``@dependabot` merge` will merge this PR after your CI passes on it
- ``@dependabot` squash and merge` will squash and merge this PR after your CI passes on it
- ``@dependabot` cancel merge` will cancel a previously requested merge and block automerging
- ``@dependabot` reopen` will reopen this PR if it is closed
- ``@dependabot` close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- ``@dependabot` ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-26 23:09:26 +00:00
bors[bot]
fcd6f075f6 Merge #617
617: Translations update from Hosted Weblate r=TheLastProject a=weblate

Translations update from [Hosted Weblate](https://hosted.weblate.org) for [Catima/Catima](https://hosted.weblate.org/projects/catima/catima/).


It also includes following components:

* [Catima/Fastlane](https://hosted.weblate.org/projects/catima/fastlane/)



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: Joel A <joeax910@student.liu.se>
2021-11-26 22:15:35 +00:00
Joel A
3d2f119fdb Translated using Weblate (Swedish)
Currently translated at 100.0% (232 of 232 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/sv/
2021-11-26 22:53:13 +01:00
Sylvia van Os
05f4add5cd Merge pull request #616 from TheLastProject/create-pull-request/patch-1637958232
Update Fastlane changelogs
2021-11-26 21:24:28 +01:00
TheLastProject
eb71d5f717 Update Fastlane changelogs 2021-11-26 20:23:51 +00:00
Sylvia van Os
a64603d64e Fix Fastlane converter 2021-11-26 21:23:33 +01:00
Sylvia van Os
e686bb36ce Mention power screen controls support 2021-11-26 21:21:49 +01:00
bors[bot]
351a805046 Merge #505
505: Feature/power screen widgets r=TheLastProject a=TheLastProject

Continuation of #427

Fixes #146

Co-authored-by: /usr/local/ΕΨΗΕΛΩΝ <djechelon@zighinetto.org>
Co-authored-by: Sylvia van Os <sylvia@hackerchick.me>
2021-11-26 20:12:33 +00:00
Sylvia van Os
466ac404bc Show "Not found" if card was removed 2021-11-26 20:25:41 +01:00
Sylvia van Os
b5c0be8ed5 Make lint happy 2021-11-25 19:34:11 +01:00
Sylvia van Os
f963b2fd4c Make spotBugs happy 2021-11-25 19:18:14 +01:00
dependabot[bot]
c26ae45cdc Bump NumberPickerPreference from 1.0.3 to 1.0.4
Bumps [NumberPickerPreference](https://github.com/invissvenska/NumberPickerPreference) from 1.0.3 to 1.0.4.
- [Release notes](https://github.com/invissvenska/NumberPickerPreference/releases)
- [Commits](https://github.com/invissvenska/NumberPickerPreference/compare/1.0.3...1.0.4)

---
updated-dependencies:
- dependency-name: com.github.invissvenska:NumberPickerPreference
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-25 02:15:52 +00:00
Sylvia van Os
addb0896c0 Surrender to Google (fixes #439)
I can't do it anymore. I am just sick of wasting so much time trying to
get Google to listen. I am sick of talking to a brick wall. I am sick of
the complete lack of competence in Google's entire review department. I
am so tired of having the goalpost moved in front of my eyes over and
over again. And above all, I am sick and tired of asking my translators,
who I appreciate so damn much, to keep wasting their time on Google's
whims.

So here you go, Google. You win. I give up. I will not use a word that
Google Translate translates to "free", because saying my app is "free of
charge" is not allowed. I will not tell your employees over and over
again how to use a dictionary. I will not try to make your staff
understand that "free" meaning both "free of charge" and "freedom" is a
very English concept and is not a thing in the majority of languages.

It is a shame you have cornered the market, because if I could, I would
have avoided your Play Store altogether. At least F-Droid still exists,
so my users are not completely screwed whenever you next decide to
punish me for something arbitrary and incorrect again.

Please, Google, let this be the end of this nonsense.
2021-11-24 17:05:52 +01:00
Sylvia van Os
c75bef4f01 Open on single click 2021-11-23 23:25:10 +01:00
Sylvia van Os
f633fb39aa Use card icon image if available 2021-11-23 23:24:36 +01:00
Sylvia van Os
49200fff48 Fix most spotBugs issues 2021-11-23 23:23:58 +01:00
Sylvia van Os
f4bd6a3f59 Merge remote-tracking branch 'origin' into feature/power_screen_widgets 2021-11-23 20:42:16 +01:00
Sylvia van Os
18021e7653 Remove unicode character from Norwegian title as per Google guidelines 2021-11-23 20:25:17 +01:00
bors[bot]
de364a4cc4 Merge #612
612: Replaced StartActivityForResult with ResultLaunchers r=TheLastProject a=Kethen

redoing #470

currently spotBug complains about an InputStream is not being closed properly, which it didn't catch before the refactoring

I'd suggest moving opening an inputStream inside the import thread

Co-authored-by: Katharine <kwchuiaa@connect.ust.hk>
Co-authored-by: Katharine Chui <kwchuiaa@connect.ust.hk>
2021-11-23 18:17:36 +00:00
Katharine Chui
3ba42aad6d always use getContentResolver().open(Input/Output)Stream, opening streams diffrently from URIs seems to be redundent
https://developer.android.com/reference/android/content/ContentResolver#openInputStream(android.net.Uri)

spotBug is also happier with those streams not getting closed within the scope of that function, for some reason unknown
2021-11-23 18:42:21 +08:00
Katharine
63fad8bae1 ImportExportActivity ActivityResultLauncher, minor clean ups 2021-11-23 18:35:29 +08:00
Katharine
ad418bc9bd ScanActivity ActivityResultLauncher 2021-11-23 18:35:29 +08:00
Katharine
b0263d8eb5 MainActivity ActivityResultLauncher 2021-11-23 18:35:29 +08:00
Sylvia van Os
97d3969f93 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-11-22 19:11:39 +01:00
Sylvia van Os
1c85172206 Google can't decide 2021-11-22 19:11:24 +01:00
bors[bot]
711fa7ef81 Merge #611
611: Bump robolectric from 4.7.1 to 4.7.2 r=TheLastProject a=dependabot[bot]

Bumps [robolectric](https://github.com/robolectric/robolectric) from 4.7.1 to 4.7.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/robolectric/robolectric/releases">robolectric's releases</a>.</em></p>
<blockquote>
<h2>Robolectric 4.7.2</h2>
<p>This is a minor release that fixes a memory leak of Theme objects in binary resources for Android S (<a href="https://github-redirect.dependabot.com/robolectric/robolectric/issues/6872">#6872</a>). Thanks <a href="https://github.com/calvarez-ov"><code>`@​calvarez-ov</code></a>` for the report and helping debug.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="9ed0797525"><code>9ed0797</code></a> Bump version to 4.7.2.</li>
<li><a href="5a8c5def4f"><code>5a8c5de</code></a> Fix theme native object collection in Android S</li>
<li><a href="666a5213a7"><code>666a521</code></a> Migrate ShadowAccessibilityNodeInfo to reflector</li>
<li>See full diff in <a href="https://github.com/robolectric/robolectric/compare/robolectric-4.7.1...robolectric-4.7.2">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.robolectric:robolectric&package-manager=gradle&previous-version=4.7.1&new-version=4.7.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting ``@dependabot` rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- ``@dependabot` rebase` will rebase this PR
- ``@dependabot` recreate` will recreate this PR, overwriting any edits that have been made to it
- ``@dependabot` merge` will merge this PR after your CI passes on it
- ``@dependabot` squash and merge` will squash and merge this PR after your CI passes on it
- ``@dependabot` cancel merge` will cancel a previously requested merge and block automerging
- ``@dependabot` reopen` will reopen this PR if it is closed
- ``@dependabot` close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- ``@dependabot` ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-22 17:48:11 +00:00
dependabot[bot]
2220a8e3c6 Bump robolectric from 4.7.1 to 4.7.2
Bumps [robolectric](https://github.com/robolectric/robolectric) from 4.7.1 to 4.7.2.
- [Release notes](https://github.com/robolectric/robolectric/releases)
- [Commits](https://github.com/robolectric/robolectric/compare/robolectric-4.7.1...robolectric-4.7.2)

---
updated-dependencies:
- dependency-name: org.robolectric:robolectric
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-22 02:25:20 +00:00
bors[bot]
cd1bd31e23 Merge #610
610: Translations update from Hosted Weblate r=TheLastProject a=weblate

Translations update from [Hosted Weblate](https://hosted.weblate.org/projects/catima/catima/)
for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: IllusiveMan196 <hamsterrv@gmail.com>
Co-authored-by: SC <lalocas@protonmail.com>
2021-11-21 23:01:08 +00:00
SC
f812194b1a Translated using Weblate (Portuguese)
Currently translated at 100.0% (232 of 232 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/pt/
2021-11-21 23:53:04 +01:00
IllusiveMan196
79c26cfed7 Translated using Weblate (Ukrainian)
Currently translated at 15.3% (14 of 91 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/uk/
2021-11-21 23:53:04 +01:00
Sylvia van Os
85ea10303a Merge pull request #609 from TheLastProject/create-pull-request/patch-1637471752
Update contributors
2021-11-21 07:05:15 +01:00
TheLastProject
4196ad8d61 Update contributors 2021-11-21 05:15:51 +00:00
bors[bot]
90be6a418b Merge #608
608: Translations update from Hosted Weblate r=TheLastProject a=weblate

Translations update from [Hosted Weblate](https://hosted.weblate.org/projects/catima/catima/)
for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: IllusiveMan196 <hamsterrv@gmail.com>
2021-11-20 21:59:18 +00:00
IllusiveMan196
2e2563cfc0 Translated using Weblate (Ukrainian)
Currently translated at 5.4% (5 of 91 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/uk/
2021-11-20 22:53:02 +01:00
IllusiveMan196
983c207019 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (232 of 232 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/uk/
2021-11-20 22:51:35 +01:00
Sylvia van Os
6a5983b7bf Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-11-20 14:37:23 +01:00
Sylvia van Os
b34e69d5d5 Delete Portuguese title because it has been disallowed by Google 2021-11-20 14:36:55 +01:00
bors[bot]
f1cd9ac935 Merge #606
606: Translations update from Hosted Weblate r=TheLastProject a=weblate

Translations update from [Hosted Weblate](https://hosted.weblate.org/projects/catima/catima/)
for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: J. Lavoie <j.lavoie@net-c.ca>
Co-authored-by: g <muziejusinfo@gmail.com>
Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Heimen Stoffels <vistausss@fastmail.com>
Co-authored-by: solokot <solokot@gmail.com>
2021-11-20 12:02:05 +00:00
solokot
5eb56c934b Translated using Weblate (Russian)
Currently translated at 100.0% (232 of 232 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-11-20 12:53:08 +01:00
Heimen Stoffels
f9b24d4b1b Translated using Weblate (Dutch)
Currently translated at 100.0% (232 of 232 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-11-20 12:53:07 +01:00
Allan Nordhøy
537b2cba82 Translated using Weblate (Norwegian Bokmål)
Currently translated at 98.2% (228 of 232 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-11-20 12:53:07 +01:00
g
d788a274a8 Translated using Weblate (Lithuanian)
Currently translated at 100.0% (232 of 232 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/lt/
2021-11-20 12:53:07 +01:00
J. Lavoie
57c383c064 Translated using Weblate (Italian)
Currently translated at 100.0% (232 of 232 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-11-20 12:53:07 +01:00
J. Lavoie
49a8828007 Translated using Weblate (French)
Currently translated at 100.0% (232 of 232 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-11-20 12:53:06 +01:00
J. Lavoie
d56a31160a Translated using Weblate (German)
Currently translated at 100.0% (232 of 232 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-11-20 12:53:06 +01:00
Sylvia van Os
bb78f9c12a Fastlane cleanup 2021-11-20 00:32:15 +01:00
Sylvia van Os
00c005afce Tag CHANGELOG 2021-11-20 00:15:09 +01:00
Sylvia van Os
c008767bd6 Release Catima 2.10.0 2021-11-20 00:14:33 +01:00
Sylvia van Os
5e15555ad4 Fix deprecations 2021-11-19 15:07:04 +01:00
bors[bot]
916d12c504 Merge #604
604: Bump robolectric from 4.7 to 4.7.1 r=TheLastProject a=dependabot[bot]

Bumps [robolectric](https://github.com/robolectric/robolectric) from 4.7 to 4.7.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/robolectric/robolectric/releases">robolectric's releases</a>.</em></p>
<blockquote>
<h2>Robolectric 4.7.1</h2>
<p>This is a minor release that fixes <a href="https://github-redirect.dependabot.com/robolectric/robolectric/issues/6858">#6858</a>. In that issue, certain Android classes could not be mocked by Mockito due to some changes to Robolectric instrumentation performed on interfaces.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="c1e4fed866"><code>c1e4fed</code></a> Bump version to 4.7.1.</li>
<li><a href="c9e861d3d7"><code>c9e861d</code></a> Bump to version 3 of preinstrumented jars</li>
<li><a href="434a5347f1"><code>434a534</code></a> Limit instrumentation on interfaces</li>
<li>See full diff in <a href="https://github.com/robolectric/robolectric/compare/robolectric-4.7...robolectric-4.7.1">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.robolectric:robolectric&package-manager=gradle&previous-version=4.7&new-version=4.7.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting ``@dependabot` rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- ``@dependabot` rebase` will rebase this PR
- ``@dependabot` recreate` will recreate this PR, overwriting any edits that have been made to it
- ``@dependabot` merge` will merge this PR after your CI passes on it
- ``@dependabot` squash and merge` will squash and merge this PR after your CI passes on it
- ``@dependabot` cancel merge` will cancel a previously requested merge and block automerging
- ``@dependabot` reopen` will reopen this PR if it is closed
- ``@dependabot` close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- ``@dependabot` ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-19 07:49:40 +00:00
bors[bot]
854958e364 Merge #603
603: Bump zip4j from 2.9.0 to 2.9.1 r=TheLastProject a=dependabot[bot]

Bumps [zip4j](https://github.com/srikanth-lingala/zip4j) from 2.9.0 to 2.9.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/srikanth-lingala/zip4j/releases">zip4j's releases</a>.</em></p>
<blockquote>
<h2>v2.9.1</h2>
<p>Improvements:</p>
<ul>
<li><a href="https://github-redirect.dependabot.com/srikanth-lingala/zip4j/issues/341">#341</a> Add caching to Travis build process</li>
<li><a href="https://github-redirect.dependabot.com/srikanth-lingala/zip4j/issues/359">#359</a> Add PasswordCallback for encrypted Input Streams</li>
</ul>
<p>Bug fixes:</p>
<ul>
<li><a href="https://github-redirect.dependabot.com/srikanth-lingala/zip4j/issues/354">#354</a> Skip checking for directory when reading entries</li>
<li><a href="https://github-redirect.dependabot.com/srikanth-lingala/zip4j/issues/347">#347</a> Extract contents of sub-directory even when sub-directory entry does not exist</li>
<li><a href="https://github-redirect.dependabot.com/srikanth-lingala/zip4j/issues/348">#348</a> Fix bug in sub-folder extraction when folder name appears at the end of central directory</li>
<li><a href="https://github-redirect.dependabot.com/srikanth-lingala/zip4j/issues/363">#363</a> Avoid replacing Windows file name separator</li>
<li><a href="https://github-redirect.dependabot.com/srikanth-lingala/zip4j/issues/365">#365</a> Improve file header equals check to include offset of local file header</li>
<li><a href="https://github-redirect.dependabot.com/srikanth-lingala/zip4j/issues/380">#380</a> Use exception type WRONG_PASSWORD when password is null or empty for AES</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="860965237a"><code>8609652</code></a> <a href="https://github-redirect.dependabot.com/srikanth-lingala/zip4j/issues/365">#365</a> Improve file header equals check to include offset of local file header</li>
<li><a href="9eab02b675"><code>9eab02b</code></a> <a href="https://github-redirect.dependabot.com/srikanth-lingala/zip4j/issues/380">#380</a> Use exception type WRONG_PASSWORD when password is null or empty for AES</li>
<li><a href="4e5e2d45ff"><code>4e5e2d4</code></a> <a href="https://github-redirect.dependabot.com/srikanth-lingala/zip4j/issues/363">#363</a> Removed invalid test</li>
<li><a href="de9bb5f35d"><code>de9bb5f</code></a> Remove functional interface annotation</li>
<li><a href="ec78cea98c"><code>ec78cea</code></a> <a href="https://github-redirect.dependabot.com/srikanth-lingala/zip4j/issues/363">#363</a> Avoid replacing Windows file name separator</li>
<li><a href="2ec68db8b7"><code>2ec68db</code></a> <a href="https://github-redirect.dependabot.com/srikanth-lingala/zip4j/issues/359">#359</a> Add PasswordCallback for encrypted Input Streams</li>
<li><a href="7a139612ce"><code>7a13961</code></a> <a href="https://github-redirect.dependabot.com/srikanth-lingala/zip4j/issues/347">#347</a> Use exception type FILE_NOT_FOUND when entry does not exist in zip</li>
<li><a href="3224fb8ef5"><code>3224fb8</code></a> <a href="https://github-redirect.dependabot.com/srikanth-lingala/zip4j/issues/347">#347</a> Fix failing tests</li>
<li><a href="9ae5253f4b"><code>9ae5253</code></a> <a href="https://github-redirect.dependabot.com/srikanth-lingala/zip4j/issues/347">#347</a> Extract contents of sub-directory even when sub-directory entry does not...</li>
<li><a href="14aebe0d05"><code>14aebe0</code></a> <a href="https://github-redirect.dependabot.com/srikanth-lingala/zip4j/issues/348">#348</a> Fix bug in sub-folder extraction when folder name appears at the end of ...</li>
<li>Additional commits viewable in <a href="https://github.com/srikanth-lingala/zip4j/compare/v2.9.0...v2.9.1">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=net.lingala.zip4j:zip4j&package-manager=gradle&previous-version=2.9.0&new-version=2.9.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting ``@dependabot` rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- ``@dependabot` rebase` will rebase this PR
- ``@dependabot` recreate` will recreate this PR, overwriting any edits that have been made to it
- ``@dependabot` merge` will merge this PR after your CI passes on it
- ``@dependabot` squash and merge` will squash and merge this PR after your CI passes on it
- ``@dependabot` cancel merge` will cancel a previously requested merge and block automerging
- ``@dependabot` reopen` will reopen this PR if it is closed
- ``@dependabot` close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- ``@dependabot` ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-19 07:45:09 +00:00
dependabot[bot]
d295dd40fd Bump robolectric from 4.7 to 4.7.1
Bumps [robolectric](https://github.com/robolectric/robolectric) from 4.7 to 4.7.1.
- [Release notes](https://github.com/robolectric/robolectric/releases)
- [Commits](https://github.com/robolectric/robolectric/compare/robolectric-4.7...robolectric-4.7.1)

---
updated-dependencies:
- dependency-name: org.robolectric:robolectric
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-19 02:24:23 +00:00
dependabot[bot]
069a3c99ef Bump zip4j from 2.9.0 to 2.9.1
Bumps [zip4j](https://github.com/srikanth-lingala/zip4j) from 2.9.0 to 2.9.1.
- [Release notes](https://github.com/srikanth-lingala/zip4j/releases)
- [Commits](https://github.com/srikanth-lingala/zip4j/compare/v2.9.0...v2.9.1)

---
updated-dependencies:
- dependency-name: net.lingala.zip4j:zip4j
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-19 02:24:13 +00:00
bors[bot]
f1e7e5494d Merge #602
602: Translations update from Hosted Weblate r=TheLastProject a=weblate

Translations update from [Hosted Weblate](https://hosted.weblate.org/projects/catima/catima/)
for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: Oğuz Ersen <oguzersen@protonmail.com>
2021-11-18 15:58:55 +00:00
Oğuz Ersen
189fe7d101 Translated using Weblate (Turkish)
Currently translated at 19.7% (18 of 91 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/tr/
2021-11-18 16:53:14 +01:00
Oğuz Ersen
9a9ff77d1c Translated using Weblate (Turkish)
Currently translated at 100.0% (232 of 232 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/tr/
2021-11-18 16:53:13 +01:00
bors[bot]
b8c1eaf88e Merge #601
601: Translations update from Hosted Weblate r=TheLastProject a=weblate

Translations update from [Hosted Weblate](https://hosted.weblate.org/projects/catima/catima/)
for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: solokot <solokot@gmail.com>
2021-11-18 15:23:53 +00:00
solokot
e525a32511 Translated using Weblate (Russian)
Currently translated at 100.0% (230 of 230 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-11-18 16:20:39 +01:00
Sylvia van Os
de9f108534 Merge branch 'master' of github.com:TheLastProject/Catima 2021-11-18 16:20:28 +01:00
Sylvia van Os
c5f0d03a1c Add button to toggle showing details on main screen 2021-11-18 16:20:00 +01:00
Sylvia van Os
e592452d02 Use card shape for icons by default 2021-11-18 15:41:48 +01:00
bors[bot]
d3d2b37001 Merge #599 #600
599: Bump appcompat from 1.3.1 to 1.4.0 r=TheLastProject a=dependabot[bot]

Bumps appcompat from 1.3.1 to 1.4.0.


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=androidx.appcompat:appcompat&package-manager=gradle&previous-version=1.3.1&new-version=1.4.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting ``@dependabot` rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- ``@dependabot` rebase` will rebase this PR
- ``@dependabot` recreate` will recreate this PR, overwriting any edits that have been made to it
- ``@dependabot` merge` will merge this PR after your CI passes on it
- ``@dependabot` squash and merge` will squash and merge this PR after your CI passes on it
- ``@dependabot` cancel merge` will cancel a previously requested merge and block automerging
- ``@dependabot` reopen` will reopen this PR if it is closed
- ``@dependabot` close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- ``@dependabot` ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

600: Translations update from Hosted Weblate r=TheLastProject a=weblate

Translations update from [Hosted Weblate](https://hosted.weblate.org/projects/catima/catima/)
for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: solokot <solokot@gmail.com>
2021-11-18 11:13:17 +00:00
solokot
3dbe4f8327 Translated using Weblate (Russian)
Currently translated at 100.0% (230 of 230 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-11-18 09:53:07 +01:00
dependabot[bot]
3d81c1be08 Bump appcompat from 1.3.1 to 1.4.0
Bumps appcompat from 1.3.1 to 1.4.0.

---
updated-dependencies:
- dependency-name: androidx.appcompat:appcompat
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-18 02:15:36 +00:00
bors[bot]
333874e5e1 Merge #598
598: Update Fastlane changelogs r=TheLastProject a=github-actions[bot]

Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action

Co-authored-by: bors[bot] <bors[bot]@users.noreply.github.com>
2021-11-17 19:46:55 +00:00
bors[bot]
69a53a8408 Update Fastlane changelogs 2021-11-17 19:45:07 +00:00
bors[bot]
8dc479793b Merge #597
597: New card layout r=TheLastProject a=TheLastProject

TODO:
- [x] Fix cards sizing up to stay equal size but failing to size back down on group switch

Co-authored-by: Sylvia van Os <sylvia@hackerchick.me>
2021-11-17 19:39:27 +00:00
Sylvia van Os
a2d48a236b Tweak margins 2021-11-17 20:35:47 +01:00
Sylvia van Os
67f390f594 Update screenshots 2021-11-17 20:12:18 +01:00
Sylvia van Os
1a5109e036 Fix card sizing 2021-11-17 19:56:27 +01:00
bors[bot]
ff42ead6b7 Merge #595
595: Cleanup barcode selector r=TheLastProject a=TheLastProject

Should make #592 easier to implement.

Co-authored-by: Sylvia van Os <sylvia@hackerchick.me>
2021-11-17 18:17:41 +00:00
Sylvia van Os
2f8d8e79c1 Make spotBugs happy 2021-11-17 19:13:28 +01:00
bors[bot]
efa6ac1f6c Merge #596
596: Bump constraintlayout from 2.1.1 to 2.1.2 r=TheLastProject a=dependabot[bot]

Bumps [constraintlayout](https://github.com/androidx/constraintlayout) from 2.1.1 to 2.1.2.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a href="https://github.com/androidx/constraintlayout/commits">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=androidx.constraintlayout:constraintlayout&package-manager=gradle&previous-version=2.1.1&new-version=2.1.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting ``@dependabot` rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- ``@dependabot` rebase` will rebase this PR
- ``@dependabot` recreate` will recreate this PR, overwriting any edits that have been made to it
- ``@dependabot` merge` will merge this PR after your CI passes on it
- ``@dependabot` squash and merge` will squash and merge this PR after your CI passes on it
- ``@dependabot` cancel merge` will cancel a previously requested merge and block automerging
- ``@dependabot` reopen` will reopen this PR if it is closed
- ``@dependabot` close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- ``@dependabot` ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-17 17:50:21 +00:00
bors[bot]
4fc56068e1 Merge #593
593: prevent bottomSheet from growing a padding getting into/leaving fullscreen r=TheLastProject a=Kethen

From android 5 to 9, going into full screen then back adds a top padding onto the coordinator layout bottom sheet

This aims to prevent that by removing the bottom sheet before entering full screen, then adding it back after leaving full screen

Co-authored-by: Katharine Chui <kwchuiaa@connect.ust.hk>
2021-11-17 17:46:24 +00:00
Sylvia van Os
7a0253ddcd New card layout 2021-11-17 17:44:19 +01:00
dependabot[bot]
ac3647695b Bump constraintlayout from 2.1.1 to 2.1.2
Bumps [constraintlayout](https://github.com/androidx/constraintlayout) from 2.1.1 to 2.1.2.
- [Release notes](https://github.com/androidx/constraintlayout/releases)
- [Commits](https://github.com/androidx/constraintlayout/commits)

---
updated-dependencies:
- dependency-name: androidx.constraintlayout:constraintlayout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-17 02:18:51 +00:00
Sylvia van Os
fc902db170 Cleanup barcode selector 2021-11-16 23:57:56 +01:00
bors[bot]
b7230ba2a7 Merge #594
594: Translations update from Hosted Weblate r=TheLastProject a=weblate

Translations update from [Hosted Weblate](https://hosted.weblate.org/projects/catima/catima/)
for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: solokot <solokot@gmail.com>
Co-authored-by: Joel A <joeax910@student.liu.se>
Co-authored-by: SC <lalocas@protonmail.com>
2021-11-16 19:56:20 +00:00
SC
f5b71beb4b Translated using Weblate (Portuguese)
Currently translated at 17.7% (16 of 90 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/pt/
2021-11-16 20:53:18 +01:00
Joel A
380a12e926 Translated using Weblate (Swedish)
Currently translated at 98.6% (227 of 230 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/sv/
2021-11-16 20:53:17 +01:00
solokot
f92f84ee5c Translated using Weblate (Russian)
Currently translated at 3.3% (3 of 90 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/ru/
2021-11-16 20:53:16 +01:00
Katharine Chui
e1eb049d05 prevent bottomSheet from growing a padding getting into/leaving fullscreen 2021-11-16 23:44:45 +08:00
bors[bot]
e03ca71728 Merge #591
591: Bump robolectric from 4.6.1 to 4.7 r=TheLastProject a=dependabot[bot]

Bumps [robolectric](https://github.com/robolectric/robolectric) from 4.6.1 to 4.7.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/robolectric/robolectric/releases">robolectric's releases</a>.</em></p>
<blockquote>
<p>Robolectric 4.7 adds support for Android S (API level 31).</p>
<p>Another big feature is support for Apple Silicon (Mac M1). Robolectric 4.7 now contains its own native implementation of the android.database.sqlite package. For Mac M1 machines, this SQLite mode is the default. For other OS's and architecture, use <code>`@SQLiteMode(NATIVE)</code>` to enable it. This new mode is currently only available for Mac and Linux. Native SQLite should be <strong>significantly</strong> faster, around 50-90% faster for SQLite operations, than the <code>LEGACY</code> SQLite mode. There were also several other performance improvements made for SQLite operations -- <a href="https://github-redirect.dependabot.com/robolectric/robolectric/issues/6687">#6687</a>, <a href="https://github-redirect.dependabot.com/robolectric/robolectric/issues/6690">#6690</a>,  and <a href="https://github-redirect.dependabot.com/robolectric/robolectric/issues/6711">#6711</a> (thanks <a href="https://github.com/al-broco"><code>`@​al-broco</code></a>!).</p>`
<p>The support v4 shadows are also deprecated at this release, and they will be removed at Robolectric 4.8.</p>
<p>Robolectric 4.7 also supports JDK 17.</p>
<h2>What's Changed</h2>
<ul>
<li>Clear cookies after test finished for CookieManagerTest by <a href="https://github.com/utzcoz"><code>`@​utzcoz</code></a>` in <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6581">robolectric/robolectric#6581</a></li>
<li>Only using ApplicationExitInfo for compile sdk 30 and above by <a href="https://github.com/utzcoz"><code>`@​utzcoz</code></a>` in <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6590">robolectric/robolectric#6590</a></li>
<li>Rebase PR 4064 - Fix NPE during saving state of WebView by <a href="https://github.com/utzcoz"><code>`@​utzcoz</code></a>` in <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6597">robolectric/robolectric#6597</a></li>
<li>Converting the String version of directlyOn(...) to <a href="https://github.com/Direct"><code>`@​Direct</code></a>` with reflector(...). by <a href="https://github.com/hoisie"><code>`@​hoisie</code></a>` in <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6598">robolectric/robolectric#6598</a></li>
<li>Add ITelephonyRegistry to ShadowServiceManager <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6605">robolectric/robolectric#6605</a></li>
<li>Implement new #startActivity methods for ShadowCrossProfileApps. <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6601">robolectric/robolectric#6601</a></li>
<li>Add ShadowMediaExtractor. <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6592">robolectric/robolectric#6592</a></li>
<li>Converting onVsync calls from ReflectionHelpers to use <a href="https://github.com/Direct"><code>`@​Direct</code></a>` with reflector(...) instead. <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6602">robolectric/robolectric#6602</a></li>
<li>Update ShadowDisplayEventReceiver to support S <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6609">robolectric/robolectric#6609</a></li>
<li>Converting the proxy version of directlyOn(...) to <a href="https://github.com/Direct"><code>`@​Direct</code></a>` with reflector(...). by <a href="https://github.com/hoisie"><code>`@​hoisie</code></a>` in <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6610">robolectric/robolectric#6610</a></li>
<li>Fix Robolectric camera shadows to work with newer SDK versions. <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6611">robolectric/robolectric#6611</a></li>
<li>Converting recycleUnchecked calls to use <a href="https://github.com/Direct"><code>`@​Direct</code></a>` with reflector(...) in place of ReflectionHelpers. <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6603">robolectric/robolectric#6603</a></li>
<li>Cleaning up unnecessary <a href="https://github.com/Direct"><code>`@​Direct</code></a>` annotations in DirectActivityReflector. <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6616">robolectric/robolectric#6616</a></li>
<li>Fixing the incorrect method signature in AssetManagerReflector that breaks GitHub CI. <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6612">robolectric/robolectric#6612</a></li>
<li>No longer automatically log everything in ShadowLog if stream is specified <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6617">robolectric/robolectric#6617</a></li>
<li>Support IntBuffer with copyPixelsFromBuffer <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6613">robolectric/robolectric#6613</a></li>
<li>Suppress missing /system/etc/fonts.xml log noise for SDK 27 <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6618">robolectric/robolectric#6618</a></li>
<li>Add perf stats for reflector class definition <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6624">robolectric/robolectric#6624</a></li>
<li>Use Object to replace GnssAntennaInfo.Listener at ShadowLocationManager by <a href="https://github.com/utzcoz"><code>`@​utzcoz</code></a>` in <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6623">robolectric/robolectric#6623</a></li>
<li>Trimming the localrepository string. by <a href="https://github.com/Squadella"><code>`@​Squadella</code></a>` in <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6653">robolectric/robolectric#6653</a></li>
<li>Merging the the separate Reflector interfaces for Message into one main interface. <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6619">robolectric/robolectric#6619</a></li>
<li>Remove OldClassInstrumentor <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6621">robolectric/robolectric#6621</a></li>
<li>Remove unnecessary ShadowLegacyMessage.isInUse <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6626">robolectric/robolectric#6626</a></li>
<li>Converting directlyOn(...) to <a href="https://github.com/Direct"><code>`@​Direct</code></a>` with reflector(...) in ShadowWindow and ShadowPhoneWindow. <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6625">robolectric/robolectric#6625</a></li>
<li>Intercept calls to methods in {<a href="https://github.com/link"><code>`@​link</code></a>` Socket} not present in the OpenJDK. <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6622">robolectric/robolectric#6622</a></li>
<li>Use bulk operations in copyPixels{to,from}Buffer <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6628">robolectric/robolectric#6628</a></li>
<li>Improve reflector caching using a regular HashMap <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6629">robolectric/robolectric#6629</a></li>
<li>Add shadow method ShadowLauncherApps#getShortcutConfigActivityList.  <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6631">robolectric/robolectric#6631</a></li>
<li>Add OnPermissionChangedListener implementation to ShadowPackageManager. <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6627">robolectric/robolectric#6627</a></li>
<li>Update minSdkVersion to 14 in some integration_test manifests <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6630">robolectric/robolectric#6630</a></li>
<li>Merge InvokeDynamicClassInstrumentor into ClassInstrumentor <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6632">robolectric/robolectric#6632</a></li>
<li>Add a shadow method for PackageManager#getText which gets a String associated with package name and resource id. <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6634">robolectric/robolectric#6634</a></li>
<li>Pass through openInputStream calls for SCHEME_ANDROID_RESOURCE <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6636">robolectric/robolectric#6636</a></li>
<li>Names thread used by ShadowFileObserver to match behavior of FileObserver. <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6640">robolectric/robolectric#6640</a></li>
<li>Migrate from <code>AnnotationValue#toString</code> to <code>auto-common</code>'s <code>AnnotationValues.toString</code>. <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6638">robolectric/robolectric#6638</a></li>
<li>Fix fidelity issue with Cursor.getBlob on a String column <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6641">robolectric/robolectric#6641</a></li>
<li>Remove InvokeDynamic perf stats <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6644">robolectric/robolectric#6644</a></li>
<li>Fix SQLiteDatabaseTest foreign key test to match Android behavior <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6643">robolectric/robolectric#6643</a></li>
<li>Add cardId support for ShadowEuiccManager. <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6642">robolectric/robolectric#6642</a></li>
<li>Add OnUidImportanceListener implementation to ShadowActivityManager and SCREEN_ON/SCREEN_OFF broadcasts to ShadowPowerManager. <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6639">robolectric/robolectric#6639</a></li>
<li>Instrument default interface methods <a href="https://github-redirect.dependabot.com/robolectric/robolectric/pull/6645">robolectric/robolectric#6645</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="dc7bcfd314"><code>dc7bcfd</code></a> Bump version to 4.7.</li>
<li><a href="b2706bf5c2"><code>b2706bf</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/robolectric/robolectric/issues/6827">#6827</a> from hoisie/artifact-build</li>
<li><a href="ba989616d5"><code>ba98961</code></a> Add a workflow to build the nativeruntime libraries</li>
<li><a href="a5db353b55"><code>a5db353</code></a> Move clang/clang++ specification to CMakeLists</li>
<li><a href="0388825391"><code>0388825</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/robolectric/robolectric/issues/6844">#6844</a> from utzcoz/bump-asm-to-9.2</li>
<li><a href="7d44fa13a2"><code>7d44fa1</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/robolectric/robolectric/issues/6845">#6845</a> from hoisie/disable-mac-universal-build</li>
<li><a href="fe3bb98658"><code>fe3bb98</code></a> Disable the universal library for Mac OS</li>
<li><a href="5b1bd881a4"><code>5b1bd88</code></a> Bump asm to 9.2 to support JDK 17 and 18</li>
<li><a href="6a3305348d"><code>6a33053</code></a> Bump AGP to 7.1.0-beta03</li>
<li><a href="4b2ecb9b59"><code>4b2ecb9</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/robolectric/robolectric/issues/6833">#6833</a> from hoisie/add-icu-submodule</li>
<li>Additional commits viewable in <a href="https://github.com/robolectric/robolectric/compare/robolectric-4.6.1...robolectric-4.7">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.robolectric:robolectric&package-manager=gradle&previous-version=4.6.1&new-version=4.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting ``@dependabot` rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- ``@dependabot` rebase` will rebase this PR
- ``@dependabot` recreate` will recreate this PR, overwriting any edits that have been made to it
- ``@dependabot` merge` will merge this PR after your CI passes on it
- ``@dependabot` squash and merge` will squash and merge this PR after your CI passes on it
- ``@dependabot` cancel merge` will cancel a previously requested merge and block automerging
- ``@dependabot` reopen` will reopen this PR if it is closed
- ``@dependabot` close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- ``@dependabot` ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-15 07:17:16 +00:00
dependabot[bot]
9f3633b2ef Bump robolectric from 4.6.1 to 4.7
Bumps [robolectric](https://github.com/robolectric/robolectric) from 4.6.1 to 4.7.
- [Release notes](https://github.com/robolectric/robolectric/releases)
- [Commits](https://github.com/robolectric/robolectric/compare/robolectric-4.6.1...robolectric-4.7)

---
updated-dependencies:
- dependency-name: org.robolectric:robolectric
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-15 02:18:03 +00:00
Sylvia van Os
0b53bdf6eb Tag CHANGELOG release 2021-11-14 22:12:14 +01:00
Sylvia van Os
2536767437 Android Studio -> Reformat Code 2021-11-14 21:55:33 +01:00
Sylvia van Os
2bc7d13d50 Release Catima 2.9.0 2021-11-14 16:05:32 +01:00
bors[bot]
84fef5a615 Merge #585
585: using icon on card view headers r=TheLastProject a=Kethen

some previews
![image](https://user-images.githubusercontent.com/22017945/141508586-9cf1a1e7-6312-4bea-8362-16f0bbed2a27.png)
![image](https://user-images.githubusercontent.com/22017945/141508626-b97fd7bd-701b-4fe8-8883-23f71489c57a.png)
![image](https://user-images.githubusercontent.com/22017945/141508668-790d0038-d6ae-429b-9c9b-efdb341ad1e3.png)
![image](https://user-images.githubusercontent.com/22017945/141508688-10c1ee3f-c215-4955-b941-16d94ef87e60.png)
![image](https://user-images.githubusercontent.com/22017945/141508745-c344bd7d-b0db-4d53-b98d-495a3b43423f.png)
![image](https://user-images.githubusercontent.com/22017945/141508774-7bdb111f-e3c2-41d0-87b3-4f75b067840b.png)


Co-authored-by: Katharine <kwchuiaa@connect.ust.hk>
Co-authored-by: Katharine Chui <kwchuiaa@connect.ust.hk>
2021-11-14 14:45:16 +00:00
Katharine Chui
0993f3180b show icon image under system topbar, fix bottom sheet UI after rotation 2021-11-14 22:10:16 +08:00
Katharine Chui
f143e01685 fixes bottom sheet height after rotating in fullscreen, removes extra top padding in fullscreen mode, 204(5)->8 2021-11-14 22:10:16 +08:00
Katharine
cb5a98edad using icon on card view headers 2021-11-14 22:10:16 +08:00
bors[bot]
74157b2fe5 Merge #589
589: Update contributors r=TheLastProject a=github-actions[bot]

Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action

Co-authored-by: TheLastProject <TheLastProject@users.noreply.github.com>
2021-11-14 08:48:52 +00:00
TheLastProject
5b7d5599f9 Update contributors 2021-11-14 05:15:51 +00:00
bors[bot]
cdfcfebd77 Merge #588
588: Translations update from Hosted Weblate r=TheLastProject a=weblate

Translations update from [Hosted Weblate](https://hosted.weblate.org/projects/catima/catima/)
for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: Oğuz Ersen <oguzersen@protonmail.com>
2021-11-13 17:07:14 +00:00
Oğuz Ersen
301fd880f5 Translated using Weblate (Turkish)
Currently translated at 18.8% (17 of 90 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/tr/
2021-11-13 17:53:54 +01:00
bors[bot]
4a4ef1b148 Merge #587
587: use daynight theme for toolbar r=TheLastProject a=Kethen

Before:
![image](https://user-images.githubusercontent.com/22017945/141610761-9cff9903-1074-4bd2-9a29-e9273bfee90c.png)

After:
![image](https://user-images.githubusercontent.com/22017945/141610679-ff5a1110-139a-487f-9954-565887c53f7a.png)
![image](https://user-images.githubusercontent.com/22017945/141610688-9b727e9c-dbab-49d9-aa32-ac231fea647c.png)


Co-authored-by: Katharine Chui <kwchuiaa@connect.ust.hk>
2021-11-13 12:58:06 +00:00
Katharine Chui
0bc7f4a164 use daynight theme for toolbar 2021-11-13 15:47:34 +08:00
bors[bot]
5e593d0ded Merge #584
584: Translations update from Hosted Weblate r=TheLastProject a=weblate

Translations update from [Hosted Weblate](https://hosted.weblate.org/projects/catima/catima/)
for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: IllusiveMan196 <hamsterrv@gmail.com>
Co-authored-by: SC <lalocas@protonmail.com>
2021-11-12 16:42:04 +00:00
SC
e958fff7b4 Translated using Weblate (Portuguese)
Currently translated at 100.0% (230 of 230 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/pt/
2021-11-12 15:52:15 +01:00
IllusiveMan196
d81b236aa6 Translated using Weblate (Ukrainian)
Currently translated at 4.4% (4 of 90 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/uk/
2021-11-12 15:52:15 +01:00
bors[bot]
8be8737459 Merge #583
583: Translations update from Hosted Weblate r=TheLastProject a=weblate

Translations update from [Hosted Weblate](https://hosted.weblate.org/projects/catima/catima/)
for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: 109247019824 <stoyan@gmx.com>
2021-11-11 07:06:39 +00:00
109247019824
e80238bfcf Translated using Weblate (Bulgarian)
Currently translated at 99.5% (229 of 230 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/bg/
2021-11-11 06:51:21 +01:00
bors[bot]
211032c152 Merge #582
582: Translations update from Hosted Weblate r=TheLastProject a=weblate

Translations update from [Hosted Weblate](https://hosted.weblate.org/projects/catima/catima/)
for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: Weblate <noreply@weblate.org>
2021-11-10 19:30:44 +00:00
Weblate
0bf50fb491 Added translation using Weblate (Portuguese (Portugal)) 2021-11-10 20:29:33 +01:00
Sylvia van Os
6f22e2d185 Fastlane fixes 2021-11-10 20:29:18 +01:00
bors[bot]
31c341663f Merge #578
578: preserve shortcut icon shape by padding, getting round corners back on card lists r=TheLastProject a=Kethen

adding padding to icons referencing https://developer.android.com/reference/android/graphics/drawable/AdaptiveIconDrawable.html
![image](https://user-images.githubusercontent.com/22017945/140962982-88becacc-677d-4593-8b7e-15fcc2f9df7e.png)
![image](https://user-images.githubusercontent.com/22017945/140889147-a40ea4ed-5fb1-47b8-9ec1-59bab7d6577f.png)



Co-authored-by: Katharine <kwchuiaa@connect.ust.hk>
2021-11-10 18:31:16 +00:00
Katharine
40a68a55e2 simplify adaptive bitmap creation with Canvas, trigger shortcut list refresh on card save 2021-11-10 14:06:08 +08:00
Katharine
1fd0acf6e4 padding images with darken header color, remove outline artifacts behind thumbnail on card lists 2021-11-10 14:06:08 +08:00
bors[bot]
127d288ace Merge #581
581: Translations update from Hosted Weblate r=TheLastProject a=weblate

Translations update from [Hosted Weblate](https://hosted.weblate.org/projects/catima/catima/)
for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: Petr Novák <nit.monkey@gmail.com>
Co-authored-by: J. Lavoie <j.lavoie@net-c.ca>
Co-authored-by: g <muziejusinfo@gmail.com>
Co-authored-by: Heimen Stoffels <vistausss@fastmail.com>
Co-authored-by: solokot <solokot@gmail.com>
Co-authored-by: IllusiveMan196 <hamsterrv@gmail.com>
Co-authored-by: Oğuz Ersen <oguzersen@protonmail.com>
2021-11-09 21:56:30 +00:00
Oğuz Ersen
11587631a9 Translated using Weblate (Turkish)
Currently translated at 18.8% (17 of 90 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/tr/
2021-11-09 22:53:04 +01:00
Oğuz Ersen
466b3d91d3 Translated using Weblate (Turkish)
Currently translated at 100.0% (230 of 230 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/tr/
2021-11-09 22:53:03 +01:00
Petr Novák
8f4f7524c4 Translated using Weblate (Czech)
Currently translated at 98.8% (89 of 90 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/cs/
2021-11-09 22:53:03 +01:00
IllusiveMan196
f35aa2a064 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (230 of 230 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/uk/
2021-11-09 22:53:00 +01:00
solokot
c20206fe28 Translated using Weblate (Russian)
Currently translated at 100.0% (230 of 230 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-11-09 22:53:00 +01:00
Heimen Stoffels
45de3f6616 Translated using Weblate (Dutch)
Currently translated at 100.0% (230 of 230 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-11-09 22:52:59 +01:00
g
6f42b30790 Translated using Weblate (Lithuanian)
Currently translated at 100.0% (230 of 230 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/lt/
2021-11-09 22:52:59 +01:00
J. Lavoie
35eab50775 Translated using Weblate (Italian)
Currently translated at 100.0% (230 of 230 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-11-09 22:52:59 +01:00
J. Lavoie
19d302e0a4 Translated using Weblate (French)
Currently translated at 100.0% (230 of 230 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-11-09 22:52:59 +01:00
J. Lavoie
a17ccfa41a Translated using Weblate (German)
Currently translated at 100.0% (230 of 230 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-11-09 22:52:58 +01:00
Petr Novák
a22e05284c Translated using Weblate (Czech)
Currently translated at 100.0% (230 of 230 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/cs/
2021-11-09 22:52:58 +01:00
bors[bot]
64491a1f7a Merge #576
576: Bump ucrop from 2.2.6 to 2.2.7 r=TheLastProject a=dependabot[bot]

Bumps [ucrop](https://github.com/Yalantis/uCrop) from 2.2.6 to 2.2.7.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/Yalantis/uCrop/releases">ucrop's releases</a>.</em></p>
<blockquote>
<h2>2.2.7</h2>
<p>Merged <a href="https://github-redirect.dependabot.com/Yalantis/uCrop/pull/732">PR 732</a>:
-- added support of &quot;content&quot; schema
-- added support for SAF requirements</p>
<p>Merged <a href="https://github-redirect.dependabot.com/Yalantis/uCrop/pull/758">PR 758</a>:
-- added ability to set OkHttpClient with custom settings
-- updated dependencies (exif interface, app compat, transition, okhttp3)</p>
<h2>2.2.6-native</h2>
<p>-- Fixed Bitmap too large exception</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="64c8f5b085"><code>64c8f5b</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/Yalantis/uCrop/issues/762">#762</a> from Yalantis/improve/update_sample_app</li>
<li><a href="70546c1016"><code>70546c1</code></a> Updated dependencies</li>
<li><a href="0ff3e6a8e8"><code>0ff3e6a</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/Yalantis/uCrop/issues/732">#732</a> from fabio-blanco/fix/non_native_android-10_saf-desti...</li>
<li><a href="cd17761b36"><code>cd17761</code></a> Merge branch 'master-non-native' into merge_non_native_android-10_saf-destina...</li>
<li><a href="586493a762"><code>586493a</code></a> Added an IllegalArgumentException in the copyFile method to inform that an un...</li>
<li><a href="32e0fcd887"><code>32e0fcd</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/Yalantis/uCrop/issues/758">#758</a> from Yalantis/fix/non_native_ssl_handshake_exception</li>
<li><a href="eb2d32fe96"><code>eb2d32f</code></a> Refactorings on BitmapCropTask for improving readability protections against ...</li>
<li><a href="fd651666f6"><code>fd65166</code></a> added UCropInitializer,</li>
<li><a href="22b24bafa3"><code>22b24ba</code></a> NullPointerException protection on weak reference of context get on crop method.</li>
<li><a href="fd919ea540"><code>fd919ea</code></a> added documentation for UCropHttpClientStore methods</li>
<li>Additional commits viewable in <a href="https://github.com/Yalantis/uCrop/compare/2.2.6...2.2.7">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=com.github.yalantis:ucrop&package-manager=gradle&previous-version=2.2.6&new-version=2.2.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting ``@dependabot` rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- ``@dependabot` rebase` will rebase this PR
- ``@dependabot` recreate` will recreate this PR, overwriting any edits that have been made to it
- ``@dependabot` merge` will merge this PR after your CI passes on it
- ``@dependabot` squash and merge` will squash and merge this PR after your CI passes on it
- ``@dependabot` cancel merge` will cancel a previously requested merge and block automerging
- ``@dependabot` reopen` will reopen this PR if it is closed
- ``@dependabot` close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- ``@dependabot` ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-09 17:33:46 +00:00
bors[bot]
472d8ae18d Merge #577
577: offer 1:1 by default when cropping icon, set header color only for icon r=TheLastProject a=Kethen

header color is only change after an icon is set
cropper offers 1:1 by default instead when an icon is getting cropped

Co-authored-by: Katharine <kwchuiaa@connect.ust.hk>
2021-11-08 18:45:38 +00:00
Sylvia van Os
388db6feab Uppercase Card for consistency too 2021-11-08 19:42:51 +01:00
Katharine
5833d49fbf offer 1:1 by default when cropping icon, set header color only for icon 2021-11-08 19:10:41 +08:00
dependabot[bot]
ffb2fadcfc Bump ucrop from 2.2.6 to 2.2.7
Bumps [ucrop](https://github.com/Yalantis/uCrop) from 2.2.6 to 2.2.7.
- [Release notes](https://github.com/Yalantis/uCrop/releases)
- [Commits](https://github.com/Yalantis/uCrop/compare/2.2.6...2.2.7)

---
updated-dependencies:
- dependency-name: com.github.yalantis:ucrop
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-08 02:26:27 +00:00
Sylvia van Os
5f534b9646 Merge pull request #573 from TheLastProject/create-pull-request/patch-1636313316
Update Fastlane changelogs
2021-11-07 20:46:20 +01:00
bors[bot]
e011324b9f Merge #572
572: Translations update from Weblate r=TheLastProject a=weblate

Translations update from [Weblate](https://hosted.weblate.org/projects/catima/catima/) for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: Marnick L'Eau <leaumar@mailbox.org>
Co-authored-by: Oğuz Ersen <oguzersen@protonmail.com>
2021-11-07 19:29:09 +00:00
TheLastProject
21df76a2b6 Update Fastlane changelogs 2021-11-07 19:28:36 +00:00
Oğuz Ersen
1af9789cf6 Translated using Weblate (Turkish)
Currently translated at 18.8% (17 of 90 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/tr/
2021-11-07 20:28:20 +01:00
Marnick L'Eau
b891e05fb9 Translated using Weblate (Dutch)
Currently translated at 10.0% (9 of 90 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/nl/
2021-11-07 20:28:20 +01:00
Sylvia van Os
55e09b602f Update CHANGELOG 2021-11-07 20:28:09 +01:00
Sylvia van Os
af4075b9e2 Choose image as card icon (#333) 2021-11-07 20:27:28 +01:00
bors[bot]
da0a221c85 Merge #571
571: Translations update from Weblate r=TheLastProject a=weblate

Translations update from [Weblate](https://hosted.weblate.org/projects/catima/catima/) for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: Sylvia van Os <sylvia@hackerchick.me>
Co-authored-by: J. Lavoie <j.lavoie@net-c.ca>
Co-authored-by: g <muziejusinfo@gmail.com>
Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Heimen Stoffels <vistausss@fastmail.com>
Co-authored-by: IllusiveMan196 <hamsterrv@gmail.com>
Co-authored-by: Marnick L'Eau <leaumar@mailbox.org>
Co-authored-by: Eric <spice2wolf@gmail.com>
Co-authored-by: Oğuz Ersen <oguzersen@protonmail.com>
Co-authored-by: SC <lalocas@protonmail.com>
2021-11-07 16:57:03 +00:00
SC
191445b822 Translated using Weblate (Portuguese)
Currently translated at 100.0% (228 of 228 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/pt/
2021-11-07 17:51:18 +01:00
Oğuz Ersen
d890b062a9 Translated using Weblate (Turkish)
Currently translated at 100.0% (228 of 228 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/tr/
2021-11-07 17:51:18 +01:00
Eric
59173443e7 Translated using Weblate (Chinese (Simplified))
Currently translated at 82.8% (189 of 228 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/zh_Hans/
2021-11-07 17:51:18 +01:00
Marnick L'Eau
5da76c9d9c Translated using Weblate (Dutch)
Currently translated at 5.5% (5 of 90 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/nl/
2021-11-07 17:51:17 +01:00
IllusiveMan196
f048566929 Translated using Weblate (Ukrainian)
Currently translated at 99.5% (227 of 228 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/uk/
2021-11-07 17:51:17 +01:00
Heimen Stoffels
fe0f4314db Translated using Weblate (Dutch)
Currently translated at 100.0% (228 of 228 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-11-07 17:51:16 +01:00
Allan Nordhøy
8d39bd671c Translated using Weblate (Norwegian Bokmål)
Currently translated at 98.2% (224 of 228 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-11-07 17:51:16 +01:00
g
9ad33a047f Translated using Weblate (Lithuanian)
Currently translated at 100.0% (228 of 228 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/lt/
2021-11-07 17:51:16 +01:00
J. Lavoie
2a78335607 Translated using Weblate (Italian)
Currently translated at 100.0% (228 of 228 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-11-07 17:51:16 +01:00
J. Lavoie
d99c72982d Translated using Weblate (French)
Currently translated at 100.0% (228 of 228 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-11-07 17:51:15 +01:00
J. Lavoie
79ced018d5 Translated using Weblate (German)
Currently translated at 100.0% (228 of 228 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-11-07 17:51:15 +01:00
Sylvia van Os
f80c79ffb1 Translated using Weblate (English)
Currently translated at 100.0% (228 of 228 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/en/
2021-11-07 17:51:15 +01:00
Sylvia van Os
11970004f0 Android Studio reformat 2021-11-07 15:38:55 +01:00
bors[bot]
a348ad62a2 Merge #570
570: Update contributors r=TheLastProject a=github-actions[bot]

Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action

Co-authored-by: TheLastProject <TheLastProject@users.noreply.github.com>
2021-11-07 12:01:42 +00:00
TheLastProject
22d612427f Update contributors 2021-11-07 05:15:30 +00:00
Sylvia van Os
a907b28fe8 Merge pull request #569 from TheLastProject/create-pull-request/patch-1636205659
Update Fastlane changelogs
2021-11-06 14:34:58 +01:00
TheLastProject
663612dce9 Update Fastlane changelogs 2021-11-06 13:34:18 +00:00
Sylvia van Os
a55da89eb2 Update CHANGELOG 2021-11-06 14:33:59 +01:00
bors[bot]
63ceb2589f Merge #559
559: #372 image rotation and crop r=TheLastProject a=Kethen

`@waffshappen` suggested https://github.com/Yalantis/uCrop
targets #372



https://user-images.githubusercontent.com/22017945/139484703-e33a5da8-ccb5-4d6a-b70c-3325e34fcec9.mp4

 


Co-authored-by: Katharine <kwchuiaa@connect.ust.hk>
2021-11-06 13:25:39 +00:00
Katharine
dd981d72d7 do not attempt image load if removed, typo fixes, do not cast literals, offer full image on cropper start 2021-11-06 12:47:08 +08:00
Katharine
8940a8ea77 card edit activity: revised instance saving, revised temp image handling 2021-11-05 00:01:03 +08:00
Katharine
57cfac3172 save state for cropper 2021-11-04 10:48:47 +08:00
Katharine
f475844bd0 wrapping up test case debugging 2021-11-04 10:48:47 +08:00
Katharine
4536453fdf debugging test case breakage 2021-11-04 10:48:47 +08:00
Katharine
3ee533b815 migrate to registerForActivityResult callbacks 2021-11-04 10:48:47 +08:00
Katharine
a744c19cce handle null intent from ucrop for when user just exits without finish cropping 2021-11-04 10:48:46 +08:00
Katharine
9cc66c5d67 minor touch ups from android studio suggestions 2021-11-04 10:48:46 +08:00
Katharine
3e5b018b55 fxing spotbug raised issues 2021-11-04 10:48:46 +08:00
Katharine
f6fee780ee cropper poc 2021-11-04 10:48:32 +08:00
bors[bot]
6971f23d0a Merge #567
567: Translations update from Weblate r=TheLastProject a=weblate

Translations update from [Weblate](https://hosted.weblate.org/projects/catima/catima/) for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: J. Lavoie <j.lavoie@net-c.ca>
Co-authored-by: solokot <solokot@gmail.com>
Co-authored-by: Oğuz Ersen <oguzersen@protonmail.com>
2021-11-03 21:34:43 +00:00
Oğuz Ersen
6d33576936 Translated using Weblate (Turkish)
Currently translated at 100.0% (228 of 228 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/tr/
2021-11-03 22:33:22 +01:00
solokot
b6c9b9059e Translated using Weblate (Russian)
Currently translated at 100.0% (228 of 228 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-11-03 22:33:21 +01:00
J. Lavoie
000bd7b734 Translated using Weblate (Italian)
Currently translated at 100.0% (228 of 228 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-11-03 22:33:21 +01:00
J. Lavoie
3dde4de775 Translated using Weblate (French)
Currently translated at 100.0% (228 of 228 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-11-03 22:33:21 +01:00
J. Lavoie
9b867370c2 Translated using Weblate (German)
Currently translated at 100.0% (228 of 228 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-11-03 22:33:20 +01:00
bors[bot]
c092a04390 Merge #566
566: Update Fastlane changelogs r=TheLastProject a=github-actions[bot]

Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action

Co-authored-by: TheLastProject <TheLastProject@users.noreply.github.com>
2021-11-03 20:40:54 +00:00
TheLastProject
50d80c1a9e Update Fastlane changelogs 2021-11-03 20:38:28 +00:00
Sylvia van Os
67e2147d33 Document awesome new group editing 2021-11-03 21:37:59 +01:00
bors[bot]
cfe2aaaad8 Merge #557
557: Group management POC for #129 r=TheLastProject a=Kethen

Proof of concept for #129, please review and comment on desired changes
A few new strings are added so translations will be needed

meanwhile I think all added group has to be trimmed to avoid having identical groups like in the example

https://user-images.githubusercontent.com/22017945/139289359-32f42b48-a40e-4039-8f0b-72bbe43cd0de.mp4



https://user-images.githubusercontent.com/22017945/139294845-28c88e03-871f-49d1-9b45-f0648f2f7b50.mp4






Co-authored-by: Katharine <kwchuiaa@connect.ust.hk>
Co-authored-by: Katharine Chui <kwchuiaa@connect.ust.hk>
2021-11-03 18:40:28 +00:00
Katharine
88a91de63b removed commented code, input check for group add, input check and minor revisions 2021-11-03 10:13:59 +08:00
Katharine
2cdeb1af9c use HashMap<> constructor to clone HashMap in LoyaltyCardCursorAdapter 2021-11-03 10:13:59 +08:00
Katharine
c815c3908f added a comment explaining why setText is used there 2021-11-03 10:13:59 +08:00
Katharine
9e831924c6 remove Parcel in Group, remove onResume/onPause in ManageGroupActivity and clean ups 2021-11-03 10:13:59 +08:00
Katharine
cfc901855b update help text in group edit activity 2021-11-03 10:13:59 +08:00
Katharine Chui
a00a69e0c0 UI changes, group cache, code cleanup
hash maps are cloned manually to make android studio and spotbug happy
2021-11-03 10:13:59 +08:00
Katharine
426acc701e remove setSort which will likely not be implemented in here 2021-11-03 10:13:59 +08:00
Katharine
d6eccd11a5 UI touch ups 2021-11-03 10:13:59 +08:00
Katharine
cb8275771f remove database shortcuts 2021-11-03 10:13:59 +08:00
Katharine
9252c01aa7 Group does not need to be a Parcelable anymore 2021-11-03 10:13:59 +08:00
Katharine
b4b544e342 remove modularity 2021-11-03 10:13:59 +08:00
Katharine
f667fcbebe requested manifest changes 2021-11-03 10:13:59 +08:00
Katharine
cc402c39be fixing errors reported by spotbugs 2021-11-03 10:13:59 +08:00
Katharine
d5d921a1c8 Group management POC 2021-11-03 10:13:59 +08:00
bors[bot]
d8794811a1 Merge #565
565: Translations update from Weblate r=TheLastProject a=weblate

Translations update from [Weblate](https://hosted.weblate.org/projects/catima/catima/) for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
2021-11-02 16:17:28 +00:00
Allan Nordhøy
e56795307e Translated using Weblate (Norwegian Bokmål)
Currently translated at 99.5% (221 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-11-02 16:35:12 +01:00
bors[bot]
6ad99d47aa Merge #561
561: Update contributors r=TheLastProject a=github-actions[bot]

Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action

Co-authored-by: TheLastProject <TheLastProject@users.noreply.github.com>
2021-10-31 06:12:36 +00:00
TheLastProject
2998a4d553 Update contributors 2021-10-31 05:15:42 +00:00
bors[bot]
63db8fa0f3 Merge #560
560: Translations update from Weblate r=TheLastProject a=weblate

Translations update from [Weblate](https://hosted.weblate.org/projects/catima/catima/) for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: SC <lalocas@protonmail.com>
2021-10-30 16:40:26 +00:00
SC
ee14d9e0c5 Translated using Weblate (Portuguese)
Currently translated at 16.8% (15 of 89 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/pt/
2021-10-30 18:38:21 +02:00
SC
1455265045 Translated using Weblate (Portuguese)
Currently translated at 100.0% (222 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/pt/
2021-10-30 18:38:17 +02:00
bors[bot]
4f5cd87912 Merge #558
558: Translations update from Weblate r=TheLastProject a=weblate

Translations update from [Weblate](https://hosted.weblate.org/projects/catima/catima/) for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: Eric <spice2wolf@gmail.com>
Co-authored-by: Joel A <joeax910@student.liu.se>
Co-authored-by: mondstern <mondstern@snopyta.org>
2021-10-28 21:11:28 +00:00
mondstern
d5516c59a6 Translated using Weblate (Welsh)
Currently translated at 16.2% (36 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/cy/
2021-10-28 23:01:42 +02:00
mondstern
5654abe6e9 Translated using Weblate (Latvian)
Currently translated at 56.3% (125 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/lv/
2021-10-28 23:01:42 +02:00
Joel A
b17a50f9c2 Translated using Weblate (Swedish)
Currently translated at 99.0% (220 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/sv/
2021-10-28 23:01:42 +02:00
Eric
87c9bb590b Translated using Weblate (Chinese (Simplified))
Currently translated at 81.5% (181 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/zh_Hans/
2021-10-28 23:01:42 +02:00
bors[bot]
dc9360eb8a Merge #556
556: Translations update from Weblate r=TheLastProject a=weblate

Translations update from [Weblate](https://hosted.weblate.org/projects/catima/catima/) for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: 109247019824 <stoyan@gmx.com>
Co-authored-by: Oğuz Ersen <oguzersen@protonmail.com>
Co-authored-by: mondstern <mondstern@snopyta.org>
Co-authored-by: Reza Almanda <rezaalmanda27@gmail.com>
Co-authored-by: Sylvia van Os <sylvia@hackerchick.me>
2021-10-28 20:57:28 +00:00
Sylvia van Os
c8d2aa26f5 Add cy to selector 2021-10-28 22:56:59 +02:00
Sylvia van Os
a5074eb191 Fix invalid translation 2021-10-28 22:37:00 +02:00
mondstern
9df0a8ff57 Added translation using Weblate (Welsh) 2021-10-28 20:01:19 +02:00
mondstern
a79cf72ad7 Translated using Weblate (Latvian)
Currently translated at 21.1% (47 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/lv/
2021-10-28 12:01:22 +02:00
mondstern
db0134a44c Translated using Weblate (Croatian)
Currently translated at 51.8% (115 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/hr/
2021-10-28 12:01:21 +02:00
Reza Almanda
5f6424778a Translated using Weblate (Indonesian)
Currently translated at 100.0% (222 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/id/
2021-10-28 12:01:21 +02:00
mondstern
950264105b Translated using Weblate (Portuguese)
Currently translated at 51.3% (114 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/pt/
2021-10-28 12:01:20 +02:00
Oğuz Ersen
7b2d9eb47f Translated using Weblate (Turkish)
Currently translated at 17.9% (16 of 89 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/tr/
2021-10-28 12:01:19 +02:00
109247019824
088ee66b5a Translated using Weblate (Bulgarian)
Currently translated at 100.0% (222 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/bg/
2021-10-28 12:01:18 +02:00
bors[bot]
2487edf4b2 Merge #554
554: Made name and ID inputs single-line r=TheLastProject a=franga2000

Since I already had the IDE open... (also still need that last Hacktoberfest PR 😅)

Fixes #553 

Co-authored-by: Miha Frangež <miha.frangez@gmail.com>
2021-10-27 21:39:37 +00:00
Miha Frangež
e16d2e22ac Made name and ID inputs single-line 2021-10-27 23:24:06 +02:00
bors[bot]
7b335c69ba Merge #552
552: Translations update from Weblate r=TheLastProject a=weblate

Translations update from [Weblate](https://hosted.weblate.org/projects/catima/catima/) for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: mondstern <mondstern@snopyta.org>
Co-authored-by: Sylvia van Os <sylvia@hackerchick.me>
2021-10-27 18:33:36 +00:00
Sylvia van Os
c1bd7043b1 Add Latvian to language selector 2021-10-27 20:32:58 +02:00
mondstern
71f3b5bc5f Added translation using Weblate (Latvian) 2021-10-27 20:26:24 +02:00
Sylvia van Os
e2f33bf4b4 Release Catima 2.8.1 2021-10-27 18:24:46 +02:00
bors[bot]
b1685ac90e Merge #549
549: Update Fastlane changelogs r=TheLastProject a=github-actions[bot]

Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action

Co-authored-by: TheLastProject <TheLastProject@users.noreply.github.com>
2021-10-27 15:40:23 +00:00
TheLastProject
394cc6b30f Update Fastlane changelogs 2021-10-27 15:38:49 +00:00
Sylvia van Os
75e320e108 Update CHANGELOG 2021-10-27 17:38:19 +02:00
bors[bot]
e8b38b3367 Merge #548
548: fix: Overlapping TextView and ImageView. r=TheLastProject a=KasinaDheeraj

fixes #542 

Co-authored-by: Dheeraj <dheerukd2002@gmail.com>
2021-10-27 15:30:33 +00:00
Dheeraj
ffb24c973a fix: Overlapping TextView and ImageView. 2021-10-27 19:21:09 +05:30
bors[bot]
d43693b1d8 Merge #547
547: Update Fastlane changelogs r=TheLastProject a=github-actions[bot]

Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action

Co-authored-by: TheLastProject <TheLastProject@users.noreply.github.com>
2021-10-27 11:13:39 +00:00
TheLastProject
b7f17181f9 Update Fastlane changelogs 2021-10-27 11:12:50 +00:00
Sylvia van Os
10dc84820c Update CHANGELOG.md 2021-10-27 13:12:30 +02:00
bors[bot]
5f0d92470e Merge #545
545: Update CHANGELOG r=TheLastProject a=TheLastProject

bors r+

Co-authored-by: Sylvia van Os <sylvia@hackerchick.me>
2021-10-27 11:04:35 +00:00
Sylvia van Os
6bb4376e4d Update CHANGELOG 2021-10-27 12:57:24 +02:00
bors[bot]
92da792378 Merge #539
539: fix for #533, fix for #510 + LoyaltyCardViewActivity crashing on returning from day/night theme change r=TheLastProject a=Kethen

please review, fixes #533, and avoids a crash from loading LoyaltyCardViewActivity, switch out to settings and switch day/night mode, switch back, which fixes #510 as well

Co-authored-by: Katharine <kwchuiaa@connect.ust.hk>
2021-10-27 10:55:51 +00:00
Katharine
81499cd362 onProgressChanged handles only user input now, always store user zoom level input 2021-10-27 18:49:09 +08:00
Katharine
fc91f1ae63 avoid system theme change activity crash 2021-10-27 18:24:23 +08:00
Katharine
cec7bc880e view dots that respects themening in app settings 2021-10-27 18:24:23 +08:00
Katharine
4027df2119 refactor centerGuideLine resizing logic -sylvia 2021-10-27 18:24:23 +08:00
bors[bot]
93132961d3 Merge #544
544: fixing #320 by not recreating main activity returning from another activity, requires testing r=TheLastProject a=Kethen

targetting #320

Co-authored-by: Katharine <kwchuiaa@connect.ust.hk>
2021-10-27 09:34:24 +00:00
Katharine
638528d4fc do not recreate main activity returning from another activity 2021-10-27 15:22:31 +08:00
bors[bot]
67ef623761 Merge #543
543: Translations update from Weblate r=TheLastProject a=weblate

Translations update from [Weblate](https://hosted.weblate.org/projects/catima/catima/) for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: mondstern <mondstern@snopyta.org>
Co-authored-by: Rosdyana Kusuma <rosdyana.kusuma@gmail.com>
2021-10-27 06:58:40 +00:00
mondstern
cb3afe1878 Translated using Weblate (Croatian)
Currently translated at 27.4% (61 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/hr/
2021-10-27 08:02:54 +02:00
mondstern
e3f4aec1c5 Translated using Weblate (Luxembourgish)
Currently translated at 51.8% (115 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/lb/
2021-10-27 08:02:53 +02:00
Rosdyana Kusuma
d2f196da21 Translated using Weblate (Indonesian)
Currently translated at 98.6% (219 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/id/
2021-10-27 08:02:52 +02:00
mondstern
e491a960d7 Translated using Weblate (Icelandic)
Currently translated at 52.2% (116 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/is/
2021-10-27 08:02:52 +02:00
mondstern
f5c83793bf Translated using Weblate (Portuguese)
Currently translated at 33.3% (74 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/pt/
2021-10-27 08:02:51 +02:00
Sylvia van Os
1624d56edb Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker into feature/power_screen_widgets 2021-10-26 21:42:14 +02:00
bors[bot]
e334a5e454 Merge #538
538: Bump zxing-android-embedded from 4.2.0 to 4.3.0 r=TheLastProject a=dependabot[bot]

Bumps [zxing-android-embedded](https://github.com/journeyapps/zxing-android-embedded) from 4.2.0 to 4.3.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/journeyapps/zxing-android-embedded/releases">zxing-android-embedded's releases</a>.</em></p>
<blockquote>
<h2>v4.3.0</h2>
<ul>
<li>Minimum SDK version 19, but requires additional config (see readme) for &lt; 24 compatibility.</li>
<li>Add ScanOptions and ScanContract for use with <code>registerForActivityResult()</code>.</li>
<li>Deprecates IntentIntegrator. It is still available, but <code>registerForActivityResult()</code> is recommended instead.</li>
<li>Use minimal AndroidX libraries.</li>
<li>Use zxing:core 3.4.1 by default.</li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/journeyapps/zxing-android-embedded/compare/v4.2.0...v4.3.0">https://github.com/journeyapps/zxing-android-embedded/compare/v4.2.0...v4.3.0</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/journeyapps/zxing-android-embedded/blob/master/CHANGES.md">zxing-android-embedded's changelog</a>.</em></p>
<blockquote>
<h3>4.3.0 (2021-10-25)</h3>
<ul>
<li>Minimum SDK version 19, but requires additional config (see readme) for &lt; 24 compatibility.</li>
<li>Add ScanOptions and ScanContract for use with <code>registerForActivityResult()</code>.</li>
<li>Deprecates IntentIntegrator.</li>
<li>Use minimal AndroidX libraries.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="24d02945fe"><code>24d0294</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/journeyapps/zxing-android-embedded/issues/665">#665</a> from journeyapps/fixes</li>
<li><a href="6ccf3a9d1a"><code>6ccf3a9</code></a> Fix lint report location.</li>
<li><a href="23f29e5a56"><code>23f29e5</code></a> Ignore lint error.</li>
<li><a href="1a8d949325"><code>1a8d949</code></a> Tweaks.</li>
<li><a href="f1554b7478"><code>f1554b7</code></a> Use GitHub Actions.</li>
<li><a href="cc12c103e0"><code>cc12c10</code></a> v4.3.0 and cleanup.</li>
<li><a href="a13ce2b161"><code>a13ce2b</code></a> Cleanup.</li>
<li><a href="0c97cf70cc"><code>0c97cf7</code></a> ScanOptions and ScanContract.</li>
<li><a href="9beeac7960"><code>9beeac7</code></a> minSdkVersion 19. Multidex and desugaring.</li>
<li><a href="cc34502f13"><code>cc34502</code></a> Smaller libs.</li>
<li>Additional commits viewable in <a href="https://github.com/journeyapps/zxing-android-embedded/compare/v4.2.0...v4.3.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=com.journeyapps:zxing-android-embedded&package-manager=gradle&previous-version=4.2.0&new-version=4.3.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting ``@dependabot` rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- ``@dependabot` rebase` will rebase this PR
- ``@dependabot` recreate` will recreate this PR, overwriting any edits that have been made to it
- ``@dependabot` merge` will merge this PR after your CI passes on it
- ``@dependabot` squash and merge` will squash and merge this PR after your CI passes on it
- ``@dependabot` cancel merge` will cancel a previously requested merge and block automerging
- ``@dependabot` reopen` will reopen this PR if it is closed
- ``@dependabot` close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- ``@dependabot` ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- ``@dependabot` ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-26 19:19:40 +00:00
bors[bot]
cbbd071f4f Merge #541
541: Translations update from Weblate r=TheLastProject a=weblate

Translations update from [Weblate](https://hosted.weblate.org/projects/catima/catima/) for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: mondstern <mondstern@snopyta.org>
Co-authored-by: Sylvia van Os <sylvia@hackerchick.me>
2021-10-26 18:39:49 +00:00
Sylvia van Os
524a6b1ffb Add hr to language selector 2021-10-26 20:38:38 +02:00
mondstern
7954adf7e3 Added translation using Weblate (Croatian) 2021-10-26 20:11:22 +02:00
Sylvia van Os
0b31a08b9f Really fix 2021-10-26 19:23:39 +02:00
Sylvia van Os
a0884aa81c Fix empty changelog check 2021-10-26 19:20:11 +02:00
Sylvia van Os
46f64d1a61 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-10-26 19:12:26 +02:00
Sylvia van Os
81cb6ad4fd Don't generate fastlane file if changelog entry is empty 2021-10-26 19:12:05 +02:00
dependabot[bot]
f2f330e3e4 Bump zxing-android-embedded from 4.2.0 to 4.3.0
Bumps [zxing-android-embedded](https://github.com/journeyapps/zxing-android-embedded) from 4.2.0 to 4.3.0.
- [Release notes](https://github.com/journeyapps/zxing-android-embedded/releases)
- [Changelog](https://github.com/journeyapps/zxing-android-embedded/blob/master/CHANGES.md)
- [Commits](https://github.com/journeyapps/zxing-android-embedded/compare/v4.2.0...v4.3.0)

---
updated-dependencies:
- dependency-name: com.journeyapps:zxing-android-embedded
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-26 02:16:29 +00:00
Sylvia van Os
5b14670785 Fix changelog 2021-10-26 00:06:52 +02:00
bors[bot]
c5f68aa50f Merge #535
535: Translations update from Weblate r=TheLastProject a=weblate

Translations update from [Weblate](https://hosted.weblate.org/projects/catima/catima/) for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: mondstern <mondstern@snopyta.org>
2021-10-25 18:12:29 +00:00
mondstern
5203f89e9d Added translation using Weblate (Luxembourgish) 2021-10-25 20:09:59 +02:00
Sylvia van Os
8f11d73649 Merge pull request #534 from weblate/weblate-catima-catima
Translations update from Weblate
2021-10-25 20:00:17 +02:00
mondstern
3d376e2a90 Translated using Weblate (Icelandic)
Currently translated at 31.9% (71 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/is/
2021-10-25 19:53:19 +02:00
Sylvia van Os
0e814c3359 Merge pull request #531 from weblate/weblate-catima-catima
Translations update from Weblate
2021-10-25 17:56:26 +02:00
Oğuz Ersen
4755cc3f7f Translated using Weblate (Turkish)
Currently translated at 17.0% (15 of 88 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/tr/
2021-10-25 17:55:10 +02:00
Sylvia van Os
7afbe41f95 Release Catima 2.8.0 2021-10-25 17:28:02 +02:00
Sylvia van Os
6d4cbee499 Merge pull request #529 from TheLastProject/create-pull-request/patch-1635175160
Update Fastlane changelogs
2021-10-25 17:20:22 +02:00
TheLastProject
f16a597b91 Update Fastlane changelogs 2021-10-25 15:19:19 +00:00
Sylvia van Os
3ba0649891 Add missing languages 2021-10-25 17:18:51 +02:00
Sylvia van Os
297ecc371e Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-10-25 17:14:57 +02:00
Sylvia van Os
79c8570507 Work around a race condition crash 2021-10-25 17:11:19 +02:00
Ronak Upadhyay
8f81d2d141 Fixes #516 Image scale according to text size (#517) 2021-10-24 22:26:41 +02:00
bors[bot]
325535af12 Merge #528
528: Translations update from Weblate r=TheLastProject a=weblate

Translations update from [Weblate](https://hosted.weblate.org/projects/catima/catima/) for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: Nyatsuki <Odamaki@yandex.ru>
2021-10-24 08:34:37 +00:00
Nyatsuki
f7af4169a3 Translated using Weblate (Japanese)
Currently translated at 98.6% (219 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ja/
2021-10-24 10:33:11 +02:00
bors[bot]
e5ec45f877 Merge #527
527: i18n: Add in-rID r=TheLastProject a=rosdyana

This PR adds localization strings for Bahasa Indonesia (in-rID) and adds the option to the locale menu.

Co-authored-by: Rosdyana Kusuma <rosdyana.kusuma@gmail.com>
2021-10-24 08:27:19 +00:00
bors[bot]
902a4e411b Merge #526
526: Update contributors r=TheLastProject a=github-actions[bot]

Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action

Co-authored-by: TheLastProject <TheLastProject@users.noreply.github.com>
2021-10-24 08:18:33 +00:00
Rosdyana Kusuma
03ec2b4492 Add localization for Bahasa Indonesia 2021-10-24 14:14:57 +08:00
TheLastProject
6a2f8a630a Update contributors 2021-10-24 05:15:59 +00:00
bors[bot]
78e998fad9 Merge #525
525: Translations update from Weblate r=TheLastProject a=weblate

Translations update from [Weblate](https://hosted.weblate.org/projects/catima/catima/) for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: Oğuz Ersen <oguzersen@protonmail.com>
Co-authored-by: mondstern <mondstern@snopyta.org>
2021-10-23 22:06:40 +00:00
mondstern
e6ccef8ad0 Translated using Weblate (Icelandic)
Currently translated at 25.6% (57 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/is/
2021-10-24 00:04:22 +02:00
Oğuz Ersen
acbd614b50 Translated using Weblate (Turkish)
Currently translated at 17.0% (15 of 88 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/tr/
2021-10-24 00:04:22 +02:00
bors[bot]
445e661fac Merge #523
523: i18n: Add zh-tw r=TheLastProject a=Still34

# Summary

This PR adds localization strings for Chinese (Taiwan) (`zh-TW`) and adds the option to the locale menu.

Co-authored-by: Still Hsu <dev@stillu.cc>
2021-10-23 21:59:30 +00:00
Still Hsu
d774231c05 Add zh-tw strings 2021-10-24 05:54:36 +08:00
bors[bot]
b5d2a5dbab Merge #522
522: Translations update from Weblate r=TheLastProject a=weblate

Translations update from [Weblate](https://hosted.weblate.org/projects/catima/catima/) for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: mondstern <mondstern@snopyta.org>
2021-10-23 19:34:13 +00:00
mondstern
6ab61eb78a Added translation using Weblate (Icelandic) 2021-10-23 21:31:36 +02:00
bors[bot]
bbd8bb7d29 Merge #521
521: Update Fastlane changelogs r=TheLastProject a=github-actions[bot]

Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action

Co-authored-by: TheLastProject <TheLastProject@users.noreply.github.com>
2021-10-22 21:35:28 +00:00
TheLastProject
3e082b598f Update Fastlane changelogs 2021-10-22 21:34:15 +00:00
Sylvia van Os
70510ed9d1 Fix unit tests 2021-10-22 23:33:50 +02:00
Sylvia van Os
5da1c67dd6 Update CHANGELOG 2021-10-22 22:17:39 +02:00
Sylvia van Os
dfa237958e Fix several small sizing bugs 2021-10-22 22:17:13 +02:00
Ankit Tiwari
127d53e85a Save Size of code (#508) 2021-10-22 21:11:51 +02:00
bors[bot]
7cce782143 Merge #518
518: Translations update from Weblate r=TheLastProject a=weblate

Translations update from [Weblate](https://hosted.weblate.org/projects/catima/catima/) for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: mondstern <mondstern@snopyta.org>
2021-10-21 20:51:42 +00:00
mondstern
662eb853d7 Translated using Weblate (Portuguese)
Currently translated at 23.8% (53 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/pt/
2021-10-21 22:37:14 +02:00
mondstern
d8e58f8b38 Translated using Weblate (Esperanto)
Currently translated at 52.2% (116 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/eo/
2021-10-21 22:37:14 +02:00
mondstern
4748e24dda Translated using Weblate (Romanian)
Currently translated at 59.0% (131 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ro/
2021-10-21 22:37:13 +02:00
bors[bot]
5af024f482 Merge #515
515: Translations update from Weblate r=TheLastProject a=weblate

Translations update from [Weblate](https://hosted.weblate.org/projects/catima/catima/) for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: mondstern <mondstern@snopyta.org>
2021-10-20 19:45:55 +00:00
mondstern
8adc013330 Added translation using Weblate (Portuguese) 2021-10-20 21:44:55 +02:00
bors[bot]
4edfe962ef Merge #514
514: Translations update from Weblate r=TheLastProject a=weblate

Translations update from [Weblate](https://hosted.weblate.org/projects/catima/catima/) for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: Petr Novák <nit.monkey@gmail.com>
Co-authored-by: IllusiveMan196 <hamsterrv@gmail.com>
Co-authored-by: mondstern <mondstern@snopyta.org>
2021-10-20 18:15:27 +00:00
mondstern
d80a71c852 Translated using Weblate (Danish)
Currently translated at 52.7% (117 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/da/
2021-10-20 20:07:38 +02:00
Petr Novák
299c9f2b4d Translated using Weblate (Czech)
Currently translated at 98.8% (87 of 88 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/cs/
2021-10-20 20:07:37 +02:00
IllusiveMan196
03e3e45f07 Translated using Weblate (Ukrainian)
Currently translated at 3.4% (3 of 88 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/uk/
2021-10-20 20:07:34 +02:00
IllusiveMan196
c35c316788 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (222 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/uk/
2021-10-20 20:07:33 +02:00
Petr Novák
d68f0c60d4 Translated using Weblate (Czech)
Currently translated at 100.0% (222 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/cs/
2021-10-20 20:07:33 +02:00
waffshappen
5faa28a7e7 Replace AsyncTask with a Compatiblity Layer and further Compat Fixes (#511) 2021-10-20 19:47:06 +02:00
bors[bot]
2fffb89179 Merge #509
509: Translations update from Weblate r=TheLastProject a=weblate

Translations update from [Weblate](https://hosted.weblate.org/projects/catima/catima/) for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: Gediminas Murauskas <muziejusinfo@gmail.com>
Co-authored-by: 109247019824 <stoyan@gmx.com>
Co-authored-by: Oğuz Ersen <oguzersen@protonmail.com>
2021-10-19 16:02:41 +00:00
Oğuz Ersen
bd27ccd535 Translated using Weblate (Turkish)
Currently translated at 17.0% (15 of 88 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/tr/
2021-10-19 15:39:49 +02:00
109247019824
3149234f5d Translated using Weblate (Bulgarian)
Currently translated at 100.0% (222 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/bg/
2021-10-19 15:39:48 +02:00
Gediminas Murauskas
dcf55c93a8 Translated using Weblate (Lithuanian)
Currently translated at 100.0% (222 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/lt/
2021-10-19 15:39:48 +02:00
bors[bot]
3f3394c1e9 Merge #507
507: feat/copyable-textview r=TheLastProject a=aditya3901

## Issue Fix
Fixes #389 
Made textviews copyable :)

Co-authored-by: aditya3901 <2020itb058.aditya@students.iiests.ac.in>
2021-10-18 16:06:10 +00:00
aditya3901
2b22dd4a1f feat/copyable-textview 2021-10-18 14:29:30 +05:30
Sylvia van Os
7f1b1484ac Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker into feature/power_screen_widgets 2021-10-17 20:02:42 +02:00
bors[bot]
a70710ca6a Merge #504
504: Update Fastlane changelogs r=TheLastProject a=github-actions[bot]

Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action

Co-authored-by: TheLastProject <TheLastProject@users.noreply.github.com>
2021-10-17 17:09:24 +00:00
TheLastProject
cd3baf5bd9 Update Fastlane changelogs 2021-10-17 17:08:28 +00:00
Sylvia van Os
1138d6387e Update CHANGELOG 2021-10-17 19:08:08 +02:00
bors[bot]
b664fd391a Merge #503
503: Translations update from Weblate r=TheLastProject a=weblate

Translations update from [Weblate](https://hosted.weblate.org/projects/catima/catima/) for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: J. Lavoie <j.lavoie@net-c.ca>
Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Heimen Stoffels <vistausss@fastmail.com>
Co-authored-by: solokot <solokot@gmail.com>
Co-authored-by: Oğuz Ersen <oguzersen@protonmail.com>
2021-10-17 15:51:25 +00:00
Oğuz Ersen
11cc65bae3 Translated using Weblate (Turkish)
Currently translated at 17.0% (15 of 88 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/tr/
2021-10-17 17:41:02 +02:00
Oğuz Ersen
eaafa56503 Translated using Weblate (Turkish)
Currently translated at 100.0% (222 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/tr/
2021-10-17 17:41:02 +02:00
solokot
09c6d58639 Translated using Weblate (Russian)
Currently translated at 100.0% (222 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-10-17 17:41:02 +02:00
Heimen Stoffels
1767a009a7 Translated using Weblate (Dutch)
Currently translated at 100.0% (222 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-10-17 17:41:02 +02:00
Allan Nordhøy
a2e25512ec Translated using Weblate (Norwegian Bokmål)
Currently translated at 99.5% (221 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-10-17 17:41:02 +02:00
J. Lavoie
824b931764 Translated using Weblate (Italian)
Currently translated at 100.0% (222 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-10-17 17:41:02 +02:00
J. Lavoie
0cb75fd421 Translated using Weblate (French)
Currently translated at 100.0% (222 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-10-17 17:41:02 +02:00
J. Lavoie
36f0d814f6 Translated using Weblate (German)
Currently translated at 100.0% (222 of 222 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-10-17 17:41:02 +02:00
bors[bot]
2aa0c46d30 Merge #502
502: Slovenian translations r=TheLastProject a=franga2000

Added some new strings and fixed a few obvious mistakes that I'm guessing came from machine translation

Co-authored-by: Miha Frangež <miha.frangez@gmail.com>
2021-10-17 15:35:00 +00:00
Miha Frangež
ff63dbfdaa Fixed bad machine translations 2021-10-17 17:16:44 +02:00
Miha Frangež
5cacdbd192 New Slovenian translations 2021-10-17 17:07:32 +02:00
Ankit Tiwari
c99bf206cc Dynamically size barcode based on their shape (square or rectangle) (#501) 2021-10-17 14:42:56 +02:00
bors[bot]
f546bb2681 Merge #500
500: Update contributors r=TheLastProject a=github-actions[bot]

Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action

Co-authored-by: TheLastProject <TheLastProject@users.noreply.github.com>
2021-10-17 08:56:26 +00:00
TheLastProject
bef750e047 Update contributors 2021-10-17 05:10:28 +00:00
bors[bot]
6998599377 Merge #499
499: Update Fastlane changelogs r=TheLastProject a=github-actions[bot]

Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action

Co-authored-by: TheLastProject <TheLastProject@users.noreply.github.com>
2021-10-16 15:23:34 +00:00
TheLastProject
11164f9a73 Update Fastlane changelogs 2021-10-16 15:21:38 +00:00
Sylvia van Os
b867795a08 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-10-16 17:21:18 +02:00
Sylvia van Os
9998205051 Update CHANGELOG 2021-10-16 17:21:05 +02:00
bors[bot]
7692f19ea6 Merge #494
494: Inter Related Code changes for exporting encrypted zip r=TheLastProject a=ankittiwari101

1. Created new method `multipleCardsExportImportPasswordProtected()` in ImportExportTest.java
2. Added 5th Parameter `char[] password` to exportData method of MultiFormater.java
3. From this new method passed password field to `importData` and `exportData` method calls.
4. In CatimaExporter.java added a password parameter to ZipOutputStream(output,password) if a non null char[] array of non zero length is received for password parameter and setEncryptFiles(true) using AES 256 Encryption(by default)"

Remaining - Dialogue Box for entering password.

Note - This is only a draft pull request, if everything here looks good, I'll work next on The Password Dialog which is the final piece in the puzzle.

Co-authored-by: Ankit Tiwari <ankitr.tiwari@gmail.com>
Co-authored-by: Sylvia van Os <sylvia@hackerchick.me>
2021-10-16 15:13:11 +00:00
Sylvia van Os
d8361cc288 String improvements 2021-10-16 16:49:47 +02:00
bors[bot]
93d0724c60 Merge #498
498: Translations update from Weblate r=TheLastProject a=weblate

Translations update from [Weblate](https://hosted.weblate.org/projects/catima/catima/) for Catima/Catima.



Current translation status:

![Weblate translation status](https://hosted.weblate.org/widgets/catima/-/catima/horizontal-auto.svg)


Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Sylvia van Os <sylvia@hackerchick.me>
2021-10-16 14:43:46 +00:00
Sylvia van Os
2c03f193f0 Translated using Weblate (Norwegian Bokmål)
Currently translated at 19.3% (17 of 88 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/nb_NO/
2021-10-16 16:40:17 +02:00
Ankit Tiwari
7028a1f4ca Changes to AlertDialog for entering password 2021-10-16 20:04:24 +05:30
Ankit Tiwari
32d62dd9ba Requested Modifications Made. 2021-10-16 01:34:44 +05:30
Ankit Tiwari
401fc98b4d Passing password to ZipInputStream constructor in importData method of CatimaImporter.java 2021-10-16 00:15:32 +05:30
Ankit Tiwari
993589427b Changed password parameter to a char[] instead of null in ImportExportTest.java line 613 2021-10-15 23:10:38 +05:30
Ankit Tiwari
2390568bdf Added Encrypted Backup feature.The Zip File is Password Protected, which is asked from the user at the time of export immediately on click of Export Button. 2021-10-15 22:51:52 +05:30
Allan Nordhøy
50ccf6da46 Translated using Weblate (Norwegian Bokmål)
Currently translated at 19.3% (17 of 88 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/nb_NO/
2021-10-15 15:45:28 +02:00
Ankit Tiwari
44066e6599 Extracted zip parameter logic into a new function and used it on imageZipParameters as well 2021-10-15 00:22:51 +05:30
Ankit Tiwari
fc9c616869 1.Created new method multipleCardsExportImportPasswordProtected() in ImportExportTest.java \n 2.Added 5th Paramete 2021-10-14 23:23:01 +05:30
bors[bot]
f09f35e44b Merge #493
493: Delete merged branch r=TheLastProject a=TheLastProject



Co-authored-by: Sylvia van Os <sylvia@hackerchick.me>
2021-10-14 17:45:05 +00:00
Sylvia van Os
f81fb0c673 Delete merged branch 2021-10-14 19:44:36 +02:00
bors[bot]
b0b4e80da4 Merge #492
492: Setup bors r=TheLastProject a=TheLastProject



Co-authored-by: Sylvia van Os <sylvia@hackerchick.me>
2021-10-14 17:37:40 +00:00
Sylvia van Os
923982e449 Don't override author info 2021-10-14 19:37:03 +02:00
Sylvia van Os
d05c97946d Setup bors 2021-10-14 19:07:24 +02:00
Sylvia van Os
504a1fd01b Prepare for Bors 2021-10-14 18:37:46 +02:00
Sylvia van Os
5efacec417 Merge pull request #491 from weblate/weblate-catima-catima
Translations update from Weblate
2021-10-13 20:03:00 +02:00
Oğuz Ersen
595d10fc44 Translated using Weblate (Turkish)
Currently translated at 17.0% (15 of 88 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/tr/
2021-10-13 19:55:32 +02:00
Sylvia van Os
2128f0a601 Merge pull request #490 from TheLastProject/create-pull-request/patch-1634144237
Update Fastlane changelogs
2021-10-13 19:07:15 +02:00
TheLastProject
0b0a45da05 Update Fastlane changelogs 2021-10-13 16:57:17 +00:00
Sylvia van Os
94fb4b4411 Update CHANGELOG 2021-10-13 18:56:54 +02:00
Sylvia van Os
fc7932d3bf Merge pull request #489 from ankittiwari101/fix-sliding-bug
Fixed Sliding Bug in MainActivity.java
2021-10-13 18:54:59 +02:00
Ankit Tiwari
97a5311593 Fixed Bug where an Empty List (consisting of only a single textview) within the Tab would not swipe left or right 2021-10-13 16:55:05 +05:30
Sylvia van Os
c7db676b2b Add IzzyOnDroid badge 2021-10-12 17:31:51 +02:00
Sylvia van Os
f8c8b3b2e6 Merge pull request #487 from TheLastProject/dependabot/gradle/com.android.tools.build-gradle-7.0.3
Bump gradle from 7.0.2 to 7.0.3
2021-10-12 17:23:04 +02:00
dependabot[bot]
df4cb5b189 Bump gradle from 7.0.2 to 7.0.3
Bumps gradle from 7.0.2 to 7.0.3.

---
updated-dependencies:
- dependency-name: com.android.tools.build:gradle
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-12 02:21:39 +00:00
Sylvia van Os
d2b2a53109 Merge pull request #485 from TacoTheDank/materialCardview
Use MaterialCardView
2021-10-11 21:27:30 +02:00
Sylvia van Os
5107f88998 Merge pull request #483 from TheLastProject/dependabot/gradle/gradle.plugin.com.github.spotbugs.snom-spotbugs-gradle-plugin-4.7.5
Bump spotbugs-gradle-plugin from 4.7.0 to 4.7.5
2021-10-11 21:25:48 +02:00
dependabot[bot]
78f2e1698f Bump spotbugs-gradle-plugin from 4.7.0 to 4.7.5
Bumps spotbugs-gradle-plugin from 4.7.0 to 4.7.5.

---
updated-dependencies:
- dependency-name: gradle.plugin.com.github.spotbugs.snom:spotbugs-gradle-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-11 19:20:03 +00:00
Sylvia van Os
6887cd098b Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-10-11 21:19:15 +02:00
Sylvia van Os
0881d745f5 Fix more spotbugs EI_EXPOSE_REP2 2021-10-11 21:18:22 +02:00
Sylvia van Os
94b3a07f90 Merge pull request #484 from weblate/weblate-catima-catima
Translations update from Weblate
2021-10-11 20:42:40 +02:00
Joel A
dc280b4023 Translated using Weblate (Swedish)
Currently translated at 2.2% (2 of 87 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/sv/
2021-10-11 20:22:20 +02:00
Joel A
edce6d6cd3 Translated using Weblate (Swedish)
Currently translated at 99.0% (218 of 220 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/sv/
2021-10-11 20:22:20 +02:00
solokot
23dc47a5b9 Translated using Weblate (Russian)
Currently translated at 100.0% (220 of 220 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-10-11 20:22:20 +02:00
Sylvia van Os
683b4f46d9 Fix spotbugs EI_EXPOSE_REP2 2021-10-11 20:22:06 +02:00
Sylvia van Os
ac021bce2a Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-10-11 19:58:26 +02:00
Sylvia van Os
b0c34cbca0 Fix spotbugs RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE 2021-10-11 19:58:09 +02:00
TacoTheDank
4a0c8270ef Replace CardView with MaterialCardView 2021-10-10 22:34:53 -04:00
TacoTheDank
793f1e2f87 Remove unneeded MultiDex 2021-10-10 22:34:40 -04:00
avikkundu
0101b6fa25 added translation in bengali (#480) 2021-10-10 17:04:49 +02:00
Sylvia van Os
47dff850fb Merge pull request #479 from weblate/weblate-catima-catima
Translations update from Weblate
2021-10-10 12:33:11 +02:00
Oğuz Ersen
fe7503c3ac Translated using Weblate (Turkish)
Currently translated at 16.0% (14 of 87 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/tr/
2021-10-10 12:31:11 +02:00
Petr Novák
5df7d8a530 Translated using Weblate (Czech)
Currently translated at 98.8% (86 of 87 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/cs/
2021-10-10 12:31:10 +02:00
Sylvia van Os
ee129a3373 Merge pull request #478 from TheLastProject/create-pull-request/patch-1633842920
Update contributors
2021-10-10 08:17:10 +02:00
TheLastProject
b979b1fc86 Update contributors 2021-10-10 05:15:20 +00:00
Sylvia van Os
361ebb8b80 Merge pull request #476 from TheLastProject/create-pull-request/patch-1633819619
Update Fastlane changelogs
2021-10-10 00:49:36 +02:00
TheLastProject
fd49466e0d Update Fastlane changelogs 2021-10-09 22:46:59 +00:00
Sylvia van Os
bf8f00f877 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-10-10 00:46:33 +02:00
Sylvia van Os
9f0b7604cb Fix botched migration and release 2.7.3 2021-10-10 00:46:20 +02:00
Sylvia van Os
6d151e7377 Merge pull request #474 from TheLastProject/create-pull-request/patch-1633816331
Update Fastlane changelogs
2021-10-10 00:02:54 +02:00
TheLastProject
6ecc94526e Update Fastlane changelogs 2021-10-09 21:52:10 +00:00
Sylvia van Os
1462911ffa Fix regression and release 2.7.2 2021-10-09 23:50:57 +02:00
Sylvia van Os
952f7a933b Merge pull request #471 from weblate/weblate-catima-catima
Translations update from Weblate
2021-10-09 21:12:21 +02:00
109247019824
b76c4ef352 Translated using Weblate (Bulgarian)
Currently translated at 4.7% (4 of 85 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/bg/
2021-10-09 21:04:49 +02:00
Sylvia van Os
48048cdb82 Merge pull request #468 from weblate/weblate-catima-catima
Translations update from Weblate
2021-10-08 18:56:48 +02:00
Michael Gangolf
a555f1c41b Translated using Weblate (German)
Currently translated at 100.0% (220 of 220 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-10-08 17:30:09 +02:00
Sylvia van Os
2ab7ebb2c0 Merge pull request #466 from weblate/weblate-catima-catima
Translations update from Weblate
2021-10-08 09:22:00 +02:00
Oğuz Ersen
e2b0113687 Translated using Weblate (Turkish)
Currently translated at 14.1% (12 of 85 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/tr/
2021-10-08 07:04:26 +02:00
Heimen Stoffels
dc7e9b541a Translated using Weblate (Dutch)
Currently translated at 100.0% (220 of 220 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-10-08 07:04:26 +02:00
Allan Nordhøy
b44ef7dfc0 Translated using Weblate (Norwegian Bokmål)
Currently translated at 99.5% (219 of 220 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-10-08 07:04:25 +02:00
Gediminas Murauskas
4d1f4a64fa Translated using Weblate (Lithuanian)
Currently translated at 100.0% (220 of 220 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/lt/
2021-10-08 07:04:24 +02:00
Sylvia van Os
fd7e6e4993 Release Catima 2.7.1 2021-10-07 20:04:19 +02:00
Sylvia van Os
b749c79a81 Merge pull request #464 from TheLastProject/create-pull-request/patch-1633550408
Update Fastlane changelogs
2021-10-06 22:01:05 +02:00
TheLastProject
5d019a8e5b Update Fastlane changelogs 2021-10-06 20:00:07 +00:00
Sylvia van Os
4e203aebfe Improve search with spaces 2021-10-06 21:59:45 +02:00
Sylvia van Os
fb2bede135 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-10-06 19:58:44 +02:00
Sylvia van Os
5a88909cd2 Make spotbugs happy 2021-10-06 19:58:29 +02:00
/usr/local/ΕΨΗΕΛΩΝ
ab1e6eed0c Cards on power screen 2021-10-06 15:07:06 +02:00
/usr/local/ΕΨΗΕΛΩΝ
ba5cf81bb4 Upgrade Java version to 11
Required to use java.util.concurrent.Flow
Could opt for Java 9, but 11 is LTS
2021-10-06 14:40:02 +02:00
Sylvia van Os
017034a788 Google. Again. 2021-10-06 12:03:09 +02:00
Sylvia van Os
66ff6f8199 Merge branch 'master' of github.com:TheLastProject/Catima 2021-10-06 11:17:38 +02:00
Sylvia van Os
ddccbad020 Fuck you, Google. Fuck you. 2021-10-06 11:17:28 +02:00
Sylvia van Os
578fb068ee Merge pull request #463 from weblate/weblate-catima-catima
Translations update from Weblate
2021-10-06 09:28:31 +02:00
Petr Novák
d624eb3842 Translated using Weblate (Czech)
Currently translated at 98.8% (83 of 84 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/cs/
2021-10-06 09:23:05 +02:00
Oğuz Ersen
f46fedda4b Translated using Weblate (Turkish)
Currently translated at 13.0% (11 of 84 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/tr/
2021-10-06 09:23:05 +02:00
109247019824
6e5ac2ca3d Translated using Weblate (Bulgarian)
Currently translated at 100.0% (220 of 220 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/bg/
2021-10-06 09:23:05 +02:00
IllusiveMan196
20de874ea1 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (220 of 220 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/uk/
2021-10-06 09:23:05 +02:00
Petr Novák
c8a207083b Translated using Weblate (Czech)
Currently translated at 100.0% (220 of 220 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/cs/
2021-10-06 09:23:05 +02:00
Sylvia van Os
ab7505c67a Work around Google's incompetence 2021-10-06 09:22:57 +02:00
Sylvia van Os
b26050b6bf Fix version name 2021-10-05 20:39:20 +02:00
Sylvia van Os
84e9c8efd4 Merge remote-tracking branch 'weblate/master' 2021-10-05 20:35:52 +02:00
Sylvia van Os
2811a14627 Release Catima 2.7.0 2021-10-05 20:28:36 +02:00
Sylvia van Os
3b6d4d44b0 Release Catima 2.7 2021-10-05 20:27:54 +02:00
Sylvia van Os
71bc304c51 Fix version upgrade 2021-10-05 20:27:11 +02:00
Sylvia van Os
7b0652ff11 Fix no results sometimes not correctly displayed 2021-10-05 20:15:10 +02:00
Sylvia van Os
61a3054655 Cleanup 2021-10-05 20:04:03 +02:00
Sylvia van Os
54c1cc3661 Merge pull request #457 from TheLastProject/create-pull-request/patch-1633455787
Update Fastlane changelogs
2021-10-05 19:45:20 +02:00
TheLastProject
ee0f9e04de Update Fastlane changelogs 2021-10-05 17:43:07 +00:00
Sylvia van Os
ebe5289d6e Implement FTS (#455)
This allows for unicode insensitive search and fast search over both
store and note data
2021-10-05 19:42:52 +02:00
Sylvia van Os
15ba15c602 Cleanup 2021-10-04 21:03:50 +02:00
Sylvia van Os
fd0448efc4 Merge pull request #454 from weblate/weblate-catima-catima
Translations update from Weblate
2021-10-04 19:28:39 +02:00
Oğuz Ersen
43fa2623d4 Translated using Weblate (Turkish)
Currently translated at 13.0% (11 of 84 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/tr/
2021-10-04 19:08:55 +02:00
Oğuz Ersen
c1b9babf33 Translated using Weblate (Turkish)
Currently translated at 100.0% (220 of 220 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/tr/
2021-10-04 19:08:55 +02:00
Petr Novák
4c8edf9d9a Translated using Weblate (Czech)
Currently translated at 75.0% (63 of 84 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/cs/
2021-10-04 19:08:55 +02:00
solokot
ff4271f51d Translated using Weblate (Russian)
Currently translated at 3.5% (3 of 84 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/ru/
2021-10-04 19:08:55 +02:00
IllusiveMan196
2424ab01cf Translated using Weblate (Ukrainian)
Currently translated at 95.9% (211 of 220 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/uk/
2021-10-04 19:08:55 +02:00
solokot
b38dfeeed7 Translated using Weblate (Russian)
Currently translated at 100.0% (220 of 220 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-10-04 19:08:55 +02:00
Thomas Bertels
7912cd190f Translated using Weblate (French)
Currently translated at 100.0% (220 of 220 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-10-04 19:08:55 +02:00
Petr Novák
11962ad930 Translated using Weblate (Czech)
Currently translated at 97.7% (215 of 220 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/cs/
2021-10-04 19:08:55 +02:00
Thomas Bertels
a8e2c65072 Translated using Weblate (English)
Currently translated at 100.0% (220 of 220 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/en/
2021-10-04 19:08:55 +02:00
Sylvia van Os
1b8ca5f467 Merge pull request #453 from LunarWatcher/master
Additional Norwegian translations
2021-10-04 19:08:46 +02:00
Olivia
46fff6da5b More changelogs 2021-10-04 18:40:45 +02:00
Olivia
47e75f64ed Import/Eksport is not an action in that form 2021-10-04 18:32:35 +02:00
Olivia
fb95c8c9d4 never seen that word used before, and we use copyleft
https://no.wikipedia.org/wiki/Copyleft
2021-10-04 18:27:21 +02:00
Olivia
66cc97214c Add translations 2021-10-04 18:21:36 +02:00
Biren
f52423ed70 Removed click listener from card icon (#452)
* Removed the unnecessary click listener in the card icon from LoyaltyCardCursorAdapter.java

* Removed declaration and assignment of mThumbnailContainer from LoyaltyCardCursorAdapter.java
2021-10-04 17:38:34 +02:00
Sylvia van Os
29bea052eb Merge pull request #451 from TheLastProject/create-pull-request/patch-1633349604
Update Fastlane changelogs
2021-10-04 14:15:35 +02:00
TheLastProject
b683f1fce4 Update Fastlane changelogs 2021-10-04 12:13:24 +00:00
Sylvia van Os
2ec55473a5 Add support expiration notice for Android 4.4 2021-10-04 14:13:09 +02:00
Sylvia van Os
2fe89ffcbc Merge pull request #448 from weblate/weblate-catima-catima
Translations update from Weblate
2021-10-04 07:38:09 +02:00
Allan Nordhøy
7e88a10884 Translated using Weblate (Russian)
Currently translated at 99.5% (219 of 220 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-10-04 07:32:30 +02:00
solokot
f260051160 Translated using Weblate (Russian)
Currently translated at 99.5% (219 of 220 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-10-04 07:24:49 +02:00
Allan Nordhøy
b8dacc2459 Translated using Weblate (English)
Currently translated at 100.0% (220 of 220 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/en/
2021-10-04 07:24:49 +02:00
Sylvia van Os
b311dd99dc Merge branch 'master' of https://hosted.weblate.org/git/catima/catima 2021-10-03 23:00:27 +02:00
Petr Novák
1d7a9843d8 Translated using Weblate (Czech)
Currently translated at 99.0% (218 of 220 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/cs/
2021-10-03 22:57:01 +02:00
Sylvia van Os
03e59ad00f Make lint happy 2021-10-03 22:56:53 +02:00
Sylvia van Os
c083af1c76 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-10-03 22:53:43 +02:00
Sylvia van Os
6abb0a2a75 Fix tests 2021-10-03 22:53:31 +02:00
Sylvia van Os
e672e7f1a6 Merge pull request #446 from weblate/weblate-catima-catima
Translations update from Weblate
2021-10-03 22:50:56 +02:00
Heimen Stoffels
852a638ea1 Translated using Weblate (Dutch)
Currently translated at 100.0% (220 of 220 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-10-03 22:50:04 +02:00
Petr Novák
ec1b642614 Translated using Weblate (Czech)
Currently translated at 22.8% (19 of 83 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/cs/
2021-10-03 22:50:04 +02:00
solokot
145dac72af Translated using Weblate (Russian)
Currently translated at 95.9% (212 of 221 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-10-03 22:50:04 +02:00
Heimen Stoffels
7d2679b2a3 Translated using Weblate (Dutch)
Currently translated at 100.0% (221 of 221 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-10-03 22:50:04 +02:00
Petr Novák
ffbcd2183b Translated using Weblate (Czech)
Currently translated at 95.4% (211 of 221 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/cs/
2021-10-03 22:50:04 +02:00
Sylvia van Os
b9d646868c Merge pull request #447 from TheLastProject/create-pull-request/patch-1633294115
Update Fastlane changelogs
2021-10-03 22:50:01 +02:00
TheLastProject
2ce530a644 Update Fastlane changelogs 2021-10-03 20:48:34 +00:00
Sylvia van Os
1c8e9ba1cf Fix CHANGELOG header 2021-10-03 22:48:15 +02:00
Sylvia van Os
9a2560dbc2 Fix translatable 2021-10-03 22:46:36 +02:00
Sylvia van Os
f900c4299d Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-10-03 22:44:05 +02:00
Sylvia van Os
f78494f882 Cleanups 2021-10-03 22:43:51 +02:00
Sylvia van Os
b55f10b59d Merge pull request #445 from weblate/weblate-catima-catima
Translations update from Weblate
2021-10-03 22:26:03 +02:00
Petr Novák
277ee90421 Translated using Weblate (Czech)
Currently translated at 20.4% (17 of 83 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/cs/
2021-10-03 22:17:50 +02:00
Aditya Das
fcdde83038 feature/About-Page (#440) 2021-10-03 22:17:45 +02:00
Sylvia van Os
12fb29ecce Merge pull request #442 from TheLastProject/create-pull-request/patch-1633234512
Update contributors
2021-10-03 10:29:56 +02:00
TheLastProject
ae7dd63d6b Update contributors 2021-10-03 04:15:12 +00:00
Sylvia van Os
48f680f7bd Merge pull request #436 from TheLastProject/dependabot/gradle/com.journeyapps-zxing-android-embedded-4.2.0
Bump zxing-android-embedded from 4.1.0 to 4.2.0
2021-10-02 15:22:25 +02:00
dependabot[bot]
e3d4e393b3 Bump zxing-android-embedded from 4.1.0 to 4.2.0
Bumps [zxing-android-embedded](https://github.com/journeyapps/zxing-android-embedded) from 4.1.0 to 4.2.0.
- [Release notes](https://github.com/journeyapps/zxing-android-embedded/releases)
- [Changelog](https://github.com/journeyapps/zxing-android-embedded/blob/master/CHANGES.md)
- [Commits](https://github.com/journeyapps/zxing-android-embedded/commits)

---
updated-dependencies:
- dependency-name: com.journeyapps:zxing-android-embedded
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-02 12:00:39 +00:00
Sylvia van Os
81f13c255a Merge pull request #435 from TheLastProject/dependabot/gradle/org.apache.commons-commons-csv-1.9.0
Bump commons-csv from 1.8 to 1.9.0
2021-10-02 13:59:50 +02:00
dependabot[bot]
97ce322d0c Bump commons-csv from 1.8 to 1.9.0
Bumps commons-csv from 1.8 to 1.9.0.

---
updated-dependencies:
- dependency-name: org.apache.commons:commons-csv
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-02 11:56:02 +00:00
Sylvia van Os
a3e876d9a2 Merge pull request #434 from TheLastProject/dependabot/gradle/net.lingala.zip4j-zip4j-2.9.0
Bump zip4j from 2.8.0 to 2.9.0
2021-10-02 13:55:22 +02:00
Rohan Babbar
234356f8f2 Modified to Android 12 Splash Screen (#430) 2021-10-02 13:44:41 +02:00
Sylvia van Os
d3222f7bdc Merge pull request #438 from weblate/weblate-catima-catima
Translations update from Weblate
2021-10-02 12:14:48 +02:00
Nyatsuki
a5658e72c7 Translated using Weblate (Japanese)
Currently translated at 98.5% (206 of 209 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ja/
2021-10-02 08:34:54 +02:00
dependabot[bot]
d1e1fcfabe Bump zip4j from 2.8.0 to 2.9.0
Bumps [zip4j](https://github.com/srikanth-lingala/zip4j) from 2.8.0 to 2.9.0.
- [Release notes](https://github.com/srikanth-lingala/zip4j/releases)
- [Commits](https://github.com/srikanth-lingala/zip4j/compare/v2.8.0...v2.9.0)

---
updated-dependencies:
- dependency-name: net.lingala.zip4j:zip4j
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-01 22:24:58 +00:00
Sylvia van Os
9d6bd0770c Configure dependabot 2021-10-02 00:24:33 +02:00
Sylvia van Os
6f76ee389b Merge pull request #431 from weblate/weblate-catima-catima
Translations update from Weblate
2021-10-01 10:04:50 +02:00
mondstern
87aa74f231 Translated using Weblate (Danish)
Currently translated at 52.6% (110 of 209 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/da/
2021-09-30 23:35:10 +02:00
IllusiveMan196
a0dc80c1a5 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (209 of 209 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/uk/
2021-09-30 23:35:09 +02:00
mondstern
d8dbe25a64 Translated using Weblate (Romanian)
Currently translated at 26.3% (55 of 209 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ro/
2021-09-30 23:35:08 +02:00
Sylvia van Os
e3bec085df Merge pull request #426 from weblate/weblate-catima-catima
Translations update from Weblate
2021-09-27 23:01:02 +02:00
mondstern
4d4b92b33f Translated using Weblate (Bosnian)
Currently translated at 21.6% (18 of 83 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/bs/
2021-09-27 22:39:12 +02:00
mondstern
cc58c769cc Translated using Weblate (Bosnian)
Currently translated at 56.4% (118 of 209 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/bs/
2021-09-27 22:39:11 +02:00
IllusiveMan196
37f08f6fc4 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (209 of 209 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/uk/
2021-09-27 22:39:10 +02:00
Sylvia van Os
73e02e7c06 Merge pull request #425 from weblate/weblate-catima-catima
Translations update from Weblate
2021-09-26 19:43:32 +02:00
mondstern
7a1b6fccc2 Translated using Weblate (Bosnian)
Currently translated at 20.0% (42 of 209 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/bs/
2021-09-26 19:38:46 +02:00
Allan Nordhøy
2473e3f91a Translated using Weblate (Norwegian Bokmål)
Currently translated at 4.8% (4 of 83 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/nb_NO/
2021-09-26 19:38:45 +02:00
Allan Nordhøy
0a6bb2805c Translated using Weblate (Norwegian Bokmål)
Currently translated at 99.0% (207 of 209 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-09-26 19:38:45 +02:00
Sylvia van Os
1eecc6f065 Merge pull request #424 from TheLastProject/create-pull-request/patch-1632629697
Update contributors
2021-09-26 11:35:13 +02:00
TheLastProject
b8b4fe4958 Update contributors 2021-09-26 04:14:56 +00:00
Sylvia van Os
6dd8b6798b Merge pull request #423 from weblate/weblate-catima-catima
Translations update from Weblate
2021-09-25 23:50:30 +02:00
mondstern
69d0a3f4aa Added translation using Weblate (Bosnian) 2021-09-25 23:45:29 +02:00
Sylvia van Os
ce81934890 Merge pull request #422 from weblate/weblate-catima-catima
Translations update from Weblate
2021-09-25 22:58:07 +02:00
Oğuz Ersen
ba06f47e3e Translated using Weblate (Turkish)
Currently translated at 12.0% (10 of 83 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/tr/
2021-09-25 16:38:09 +02:00
Sylvia van Os
cfc37d4af6 Merge pull request #419 from TheLastProject/create-pull-request/patch-1632573184
Update Fastlane changelogs
2021-09-25 14:33:36 +02:00
TheLastProject
43cd6edda2 Update Fastlane changelogs 2021-09-25 12:33:04 +00:00
Sylvia van Os
0bd262d82f Release Catima 2.6.1 2021-09-25 14:32:43 +02:00
Sylvia van Os
f09bafa104 Update contributors on schedule instead 2021-09-25 13:17:28 +02:00
Sylvia van Os
20e34ee365 Merge pull request #417 from TheLastProject/create-pull-request/patch-1632560906
Update contributors
2021-09-25 11:09:38 +02:00
TheLastProject
7a6bd8f661 Update contributors 2021-09-25 09:08:25 +00:00
Sylvia van Os
54c3765e36 Merge pull request #415 from weblate/weblate-catima-catima
Translations update from Weblate
2021-09-25 11:07:58 +02:00
J. Lavoie
4d7f563b0d Translated using Weblate (German)
Currently translated at 100.0% (209 of 209 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-09-25 11:06:49 +02:00
QuangDNguyen2211
4e0ecaa7be Changing Reverse button in sorting into a checkbox (#406) 2021-09-25 11:06:44 +02:00
Sylvia van Os
4f41d238eb Merge pull request #414 from weblate/weblate-catima-catima
Translations update from Weblate
2021-09-24 21:15:04 +02:00
mondstern
7c3d021427 Translated using Weblate (Romanian)
Currently translated at 18.6% (39 of 209 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ro/
2021-09-24 20:35:55 +02:00
Sylvia van Os
bfde036484 Merge pull request #413 from TheLastProject/create-pull-request/patch-1632296753
Update contributors
2021-09-22 09:46:53 +02:00
TheLastProject
84ef4ad030 Update contributors 2021-09-22 07:45:52 +00:00
Sylvia van Os
3b85fccd60 Merge pull request #412 from weblate/weblate-catima-catima
Translations update from Weblate
2021-09-22 09:45:24 +02:00
Petr Novák
830d0f6e6a Translated using Weblate (Czech)
Currently translated at 6.0% (5 of 82 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/cs/
2021-09-22 09:35:00 +02:00
Petr Novák
1ceede27a3 Translated using Weblate (Czech)
Currently translated at 100.0% (209 of 209 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/cs/
2021-09-22 09:34:59 +02:00
Sylvia van Os
fa33cdaca4 Enable autoVerify for Android 12 on catima.app 2021-09-21 21:13:04 +02:00
Sylvia van Os
f39fbb55a1 Merge pull request #411 from TheLastProject/create-pull-request/patch-1632248508
Update contributors
2021-09-21 20:23:17 +02:00
TheLastProject
f3ffa0ab88 Update contributors 2021-09-21 18:21:48 +00:00
waffshappen
48e1fcc38e TargetSDK, Version Upgrades, Deprecations and XLint (#405)
* Updated TargetSdk, enabled showing Deprecations by default (Warn) and implement some replacements for deprecated Methods
2021-09-21 20:21:02 +02:00
Sylvia van Os
5b889c4c0c Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-09-20 23:00:31 +02:00
Sylvia van Os
616ca77c39 Fix possible crash
I don't think this should ever happen, unless someone manually edited
their import or DB, but this crash was logged in Google Play Console
anyway so...
2021-09-20 22:58:37 +02:00
Sylvia van Os
59bf064783 Merge pull request #404 from TheLastProject/create-pull-request/patch-1632167904
Update contributors
2021-09-20 21:59:46 +02:00
TheLastProject
220393c445 Update contributors 2021-09-20 19:58:24 +00:00
Sylvia van Os
b976c03fb0 Merge pull request #403 from weblate/weblate-catima-catima
Translations update from Weblate
2021-09-20 21:57:42 +02:00
mondstern
dad0493666 Translated using Weblate (Danish)
Currently translated at 34.4% (72 of 209 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/da/
2021-09-20 21:39:12 +02:00
109247019824
776613c507 Translated using Weblate (Bulgarian)
Currently translated at 3.6% (3 of 82 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/bg/
2021-09-20 21:39:11 +02:00
109247019824
7570d9d319 Translated using Weblate (Bulgarian)
Currently translated at 100.0% (209 of 209 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/bg/
2021-09-20 21:39:11 +02:00
Sylvia van Os
2e648d1062 Remove cat face everywhere 2021-09-19 15:46:00 +02:00
Sylvia van Os
5ad02ae9cc Release Catima 2.6.0 2021-09-19 14:48:52 +02:00
Sylvia van Os
99fc568419 Make full description nicer for screen readers 2021-09-19 12:35:41 +02:00
Sylvia van Os
5f835716e0 Consistent badge colouring 2021-09-18 16:46:25 +02:00
Sylvia van Os
1f6fe787eb Remove Python from CodeQL 2021-09-17 21:04:34 +02:00
Sylvia van Os
1b08d30611 Enable CodeQL 2021-09-17 20:57:29 +02:00
Sylvia van Os
05ad33a756 Fix Google Play badge 2021-09-17 20:55:14 +02:00
Sylvia van Os
b103366a10 Merge pull request #398 from weblate/weblate-catima-catima
Translations update from Weblate
2021-09-17 15:41:52 +02:00
J. Lavoie
343d37d4b8 Translated using Weblate (Italian)
Currently translated at 100.0% (209 of 209 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-09-17 15:36:59 +02:00
J. Lavoie
60dc050633 Translated using Weblate (French)
Currently translated at 100.0% (209 of 209 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-09-17 15:36:59 +02:00
J. Lavoie
96c3583393 Translated using Weblate (German)
Currently translated at 100.0% (209 of 209 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-09-17 15:36:58 +02:00
Sylvia van Os
43656dbcca Merge pull request #396 from TheLastProject/create-pull-request/patch-1631739087
Update contributors
2021-09-15 22:51:48 +02:00
TheLastProject
7c38bbe6ab Update contributors 2021-09-15 20:51:26 +00:00
Sylvia van Os
c6c6448501 Merge pull request #395 from weblate/weblate-catima-catima
Translations update from Weblate
2021-09-15 22:50:52 +02:00
Oğuz Ersen
7ff9da2d7f Translated using Weblate (Turkish)
Currently translated at 10.9% (9 of 82 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/tr/
2021-09-15 22:36:50 +02:00
Oğuz Ersen
39893cc66d Translated using Weblate (Turkish)
Currently translated at 100.0% (209 of 209 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/tr/
2021-09-15 22:36:49 +02:00
Joel A
5110618d7d Translated using Weblate (Swedish)
Currently translated at 100.0% (209 of 209 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/sv/
2021-09-15 22:36:49 +02:00
mondstern
a856831ee1 Translated using Weblate (Slovak)
Currently translated at 53.5% (112 of 209 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/sk/
2021-09-15 22:36:48 +02:00
solokot
c5fc6a4212 Translated using Weblate (Russian)
Currently translated at 100.0% (209 of 209 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-09-15 22:36:48 +02:00
Heimen Stoffels
7708646c68 Translated using Weblate (Dutch)
Currently translated at 100.0% (209 of 209 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-09-15 22:36:47 +02:00
Gediminas Murauskas
c84dbac020 Translated using Weblate (Lithuanian)
Currently translated at 100.0% (209 of 209 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/lt/
2021-09-15 22:36:47 +02:00
Flav
56c2297e02 Translated using Weblate (French)
Currently translated at 97.6% (204 of 209 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-09-15 22:36:47 +02:00
String E. Fighter
6956805e62 Translated using Weblate (German)
Currently translated at 99.5% (208 of 209 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-09-15 22:36:46 +02:00
Sylvia van Os
7736e30043 Merge pull request #394 from TheLastProject/create-pull-request/patch-1631641876
Update Fastlane changelogs
2021-09-14 19:53:27 +02:00
TheLastProject
ace441f072 Update Fastlane changelogs 2021-09-14 17:51:15 +00:00
Sylvia van Os
a37e99ce39 Prevent OOM on scanning large images for barcode 2021-09-14 19:50:50 +02:00
github-actions[bot]
6b1bdbaa78 Update Fastlane changelogs (#393)
Co-authored-by: TheLastProject <TheLastProject@users.noreply.github.com>
2021-09-14 19:19:12 +02:00
Sylvia van Os
b4077a2fc3 Accept changelog entry without date (unreleased) 2021-09-14 19:17:26 +02:00
Sylvia van Os
2f660c6fec Basic ordering support (#367) 2021-09-14 19:14:38 +02:00
Sylvia van Os
cc0511a2aa Merge pull request #391 from TheLastProject/create-pull-request/patch-1631555183
Update contributors
2021-09-13 19:46:59 +02:00
TheLastProject
2f367efa70 Update contributors 2021-09-13 17:46:22 +00:00
Sylvia van Os
bff1b54b7e Merge pull request #390 from weblate/weblate-catima-catima
Translations update from Weblate
2021-09-13 19:45:46 +02:00
Joel A
010e9556b8 Translated using Weblate (Swedish)
Currently translated at 100.0% (199 of 199 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/sv/
2021-09-13 19:35:39 +02:00
Sylvia van Os
7c54c01dfe Release Catima 2.5.0 2021-09-10 20:58:17 +02:00
Sylvia van Os
5709c8dbec Fix spotBugs 2021-09-10 19:30:10 +02:00
Sylvia van Os
0f434d7fe7 Merge pull request #387 from TheLastProject/create-pull-request/patch-1631269680
Update contributors
2021-09-10 12:29:51 +02:00
TheLastProject
a2d7e70431 Update contributors 2021-09-10 10:28:00 +00:00
Sylvia van Os
0c0c462372 Merge pull request #386 from weblate/weblate-catima-catima
Translations update from Weblate
2021-09-10 12:27:31 +02:00
Oğuz Ersen
f630539d9a Translated using Weblate (Turkish)
Currently translated at 9.8% (8 of 81 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/tr/
2021-09-10 07:35:44 +02:00
Oğuz Ersen
7e27ddcc94 Translated using Weblate (Turkish)
Currently translated at 100.0% (199 of 199 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/tr/
2021-09-10 07:35:43 +02:00
Nyatsuki
f9b61c24e9 Translated using Weblate (Japanese)
Currently translated at 99.4% (198 of 199 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ja/
2021-09-10 07:35:41 +02:00
IllusiveMan196
d26a138e3e Translated using Weblate (Ukrainian)
Currently translated at 100.0% (199 of 199 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/uk/
2021-09-10 07:35:40 +02:00
solokot
e695a4e993 Translated using Weblate (Russian)
Currently translated at 100.0% (199 of 199 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-09-10 07:35:40 +02:00
Heimen Stoffels
8cae6aed4a Translated using Weblate (Dutch)
Currently translated at 100.0% (199 of 199 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-09-10 07:35:40 +02:00
Allan Nordhøy
56b72dd544 Translated using Weblate (Norwegian Bokmål)
Currently translated at 99.4% (198 of 199 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-09-10 07:35:40 +02:00
Gediminas Murauskas
b935b30e00 Translated using Weblate (Lithuanian)
Currently translated at 100.0% (199 of 199 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/lt/
2021-09-10 07:35:40 +02:00
J. Lavoie
b9a6c6248d Translated using Weblate (Italian)
Currently translated at 100.0% (199 of 199 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-09-10 07:35:39 +02:00
J. Lavoie
09d8703545 Translated using Weblate (French)
Currently translated at 100.0% (199 of 199 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-09-10 07:35:39 +02:00
J. Lavoie
6e25211f12 Translated using Weblate (German)
Currently translated at 100.0% (199 of 199 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-09-10 07:35:39 +02:00
Sylvia van Os
6f1dc74f66 Consistent barcode naming 2021-09-10 00:49:06 +02:00
Sylvia van Os
61b1317fc5 Fix dots increasing each onResume 2021-09-09 22:48:02 +02:00
Sylvia van Os
129ee2d5aa Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-09-08 23:14:00 +02:00
Sylvia van Os
2358ea1f89 Swipe logic fixes 2021-09-08 23:13:43 +02:00
Sylvia van Os
87a172afd5 Merge pull request #384 from TheLastProject/create-pull-request/patch-1631124290
Update Fastlane changelogs
2021-09-08 20:05:30 +02:00
TheLastProject
5a64b6996e Update Fastlane changelogs 2021-09-08 18:04:50 +00:00
Sylvia van Os
9ba07982bf Don't reset group on back button press 2021-09-08 20:04:19 +02:00
Sylvia van Os
98c41ec003 Merge pull request #383 from TheLastProject/create-pull-request/patch-1631118155
Update contributors
2021-09-08 18:23:16 +02:00
TheLastProject
bc9875e7fc Update contributors 2021-09-08 16:22:34 +00:00
Sylvia van Os
b8bc7eded3 Merge pull request #382 from weblate/weblate-catima-catima
Translations update from Weblate
2021-09-08 18:22:06 +02:00
Oğuz Ersen
6625deadf7 Added translation using Weblate (Turkish) 2021-09-08 16:28:03 +02:00
Sylvia van Os
c781e4ff4e Merge pull request #380 from weblate/weblate-catima-catima
Translations update from Weblate
2021-09-07 19:51:37 +02:00
Joel A
b5a2508027 Translated using Weblate (Swedish)
Currently translated at 4.9% (4 of 81 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/sv/
2021-09-07 19:51:22 +02:00
github-actions[bot]
4916917ac9 Update Fastlane changelogs (#381)
Co-authored-by: TheLastProject <TheLastProject@users.noreply.github.com>
2021-09-07 19:51:19 +02:00
Sylvia van Os
4f9b1dae04 Show first image as main (#377) 2021-09-07 19:50:26 +02:00
Sylvia van Os
9b841399f7 Merge pull request #379 from TheLastProject/create-pull-request/patch-1630965291
Update contributors
2021-09-06 23:55:36 +02:00
TheLastProject
45fdc81aa7 Update contributors 2021-09-06 21:54:51 +00:00
Sylvia van Os
fdb28a3e52 Merge pull request #378 from weblate/weblate-catima-catima
Translations update from Weblate
2021-09-06 23:54:23 +02:00
Joel A
a151a9334e Translated using Weblate (Swedish)
Currently translated at 3.7% (3 of 81 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/sv/
2021-09-06 23:34:40 +02:00
Sylvia van Os
618c7a294d Merge pull request #376 from TheLastProject/create-pull-request/patch-1630874360
Update contributors
2021-09-05 22:39:57 +02:00
TheLastProject
f926d61213 Update contributors 2021-09-05 20:39:19 +00:00
Sylvia van Os
d572add506 Merge pull request #375 from weblate/weblate-catima-catima
Translations update from Weblate
2021-09-05 22:38:51 +02:00
Joel A
012da7397f Translated using Weblate (Swedish)
Currently translated at 100.0% (198 of 198 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/sv/
2021-09-05 22:34:42 +02:00
109247019824
92e4908ea8 Translated using Weblate (Bulgarian)
Currently translated at 100.0% (198 of 198 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/bg/
2021-09-05 22:34:42 +02:00
IllusiveMan196
a051d628ab Translated using Weblate (Ukrainian)
Currently translated at 3.7% (3 of 81 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/uk/
2021-09-05 22:34:42 +02:00
Nyatsuki
0116346035 Translated using Weblate (Japanese)
Currently translated at 99.4% (197 of 198 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ja/
2021-09-05 22:34:41 +02:00
IllusiveMan196
3049b7b0ae Translated using Weblate (Ukrainian)
Currently translated at 100.0% (198 of 198 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/uk/
2021-09-05 22:34:41 +02:00
solokot
0c484d26ee Translated using Weblate (Russian)
Currently translated at 100.0% (198 of 198 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-09-05 22:34:41 +02:00
Heimen Stoffels
55bea5262e Translated using Weblate (Dutch)
Currently translated at 100.0% (198 of 198 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-09-05 22:34:40 +02:00
Allan Nordhøy
f64d0dcc16 Translated using Weblate (Norwegian Bokmål)
Currently translated at 99.4% (197 of 198 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-09-05 22:34:40 +02:00
Gediminas Murauskas
ad23a671a2 Translated using Weblate (Lithuanian)
Currently translated at 100.0% (198 of 198 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/lt/
2021-09-05 22:34:40 +02:00
J. Lavoie
718e6b433d Translated using Weblate (Italian)
Currently translated at 100.0% (198 of 198 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-09-05 22:34:40 +02:00
J. Lavoie
65e2965df2 Translated using Weblate (French)
Currently translated at 100.0% (198 of 198 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-09-05 22:34:39 +02:00
J. Lavoie
2bf815942c Translated using Weblate (German)
Currently translated at 100.0% (198 of 198 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-09-05 22:34:39 +02:00
Sylvia van Os
b5547c5cdf Merge pull request #371 from TheLastProject/create-pull-request/patch-1630770974
Update Fastlane changelogs
2021-09-04 17:58:08 +02:00
TheLastProject
371fd00606 Update Fastlane changelogs 2021-09-04 15:56:14 +00:00
Sylvia van Os
4f2a448e7e Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-09-04 17:55:52 +02:00
Sylvia van Os
2755641e6b Fix could not read image not always appearing 2021-09-04 17:55:38 +02:00
Sylvia van Os
3e80066a68 Don't crash when selecting video in scan activity 2021-09-04 17:55:21 +02:00
Sylvia van Os
2e78f77be6 Merge pull request #368 from TheLastProject/create-pull-request/patch-1630691444
Update Fastlane changelogs
2021-09-03 19:52:24 +02:00
TheLastProject
d042d9a7e8 Update Fastlane changelogs 2021-09-03 17:50:44 +00:00
Sylvia van Os
c4064a2ed1 Fix various small accessibility issues 2021-09-03 19:50:17 +02:00
Sylvia van Os
0ac0fad091 Merge pull request #366 from weblate/weblate-catima-catima
Translations update from Weblate
2021-09-02 23:50:05 +02:00
mondstern
7fdad67ea4 Translated using Weblate (Slovak)
Currently translated at 35.0% (69 of 197 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/sk/
2021-09-02 21:32:45 +02:00
Sylvia van Os
be21bc1654 Merge pull request #365 from TheLastProject/create-pull-request/patch-1630602047
Update contributors
2021-09-02 19:02:28 +02:00
TheLastProject
e65fb4390f Update contributors 2021-09-02 17:00:46 +00:00
Sylvia van Os
9f7bd4a0bd Update CONTRIBUTING.md 2021-09-02 19:00:07 +02:00
Sylvia van Os
b43817cbde Merge pull request #363 from weblate/weblate-catima-catima
Translations update from Weblate
2021-09-01 18:06:34 +02:00
Sylvia van Os
da0e2ebb99 Fix bad escaping 2021-09-01 17:23:29 +02:00
mondstern
aa87f33676 Translated using Weblate (Swedish)
Currently translated at 100.0% (197 of 197 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/sv/
2021-09-01 17:14:49 +02:00
mondstern
4e7d4383e8 Translated using Weblate (Bulgarian)
Currently translated at 100.0% (197 of 197 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/bg/
2021-09-01 17:14:49 +02:00
mondstern
0702aa1c79 Translated using Weblate (Esperanto)
Currently translated at 53.8% (106 of 197 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/eo/
2021-09-01 17:14:49 +02:00
mondstern
cd97f89d90 Translated using Weblate (Finnish)
Currently translated at 100.0% (197 of 197 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fi/
2021-09-01 17:14:49 +02:00
Heimen Stoffels
eab6c4d2bd Translated using Weblate (Dutch)
Currently translated at 5.0% (4 of 80 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/nl/
2021-09-01 17:14:49 +02:00
mondstern
20775cbc79 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (197 of 197 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/uk/
2021-09-01 17:14:49 +02:00
mondstern
90ecb1997f Translated using Weblate (Slovenian)
Currently translated at 57.8% (114 of 197 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/sl/
2021-09-01 17:14:49 +02:00
Allan Nordhøy
64b1483fa5 Translated using Weblate (Norwegian Bokmål)
Currently translated at 99.4% (196 of 197 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-09-01 17:14:49 +02:00
mondstern
8365a719d8 Translated using Weblate (Spanish)
Currently translated at 100.0% (197 of 197 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/es/
2021-09-01 17:14:49 +02:00
mondstern
068e793682 Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (197 of 197 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-09-01 17:14:49 +02:00
Sylvia van Os
2e1c72e401 Fail build on bad translations 2021-09-01 17:14:38 +02:00
Sylvia van Os
3e213c6996 Unit tests are very unstable, retry once on fail 2021-09-01 12:32:30 +02:00
Sylvia van Os
ec65abadc3 PR consistency 2021-09-01 12:27:06 +02:00
Sylvia van Os
d24b56b5c1 Merge pull request #360 from TheLastProject/create-pull-request/patch
Update Fastlane changelogs
2021-08-31 20:38:09 +02:00
TheLastProject
b56f4e1653 Update Fastlane changelogs 2021-08-31 18:36:29 +00:00
Sylvia van Os
efba8773c5 Convert Markdown to simple link for Fastlane 2021-08-31 20:30:21 +02:00
Sylvia van Os
15339a8c44 Merge pull request #358 from TheLastProject/create-pull-request/patch
Update contributors
2021-08-31 18:13:47 +02:00
TheLastProject
050021de5c Update contributors 2021-08-31 16:12:42 +00:00
Sylvia van Os
16cff71cfb Merge pull request #357 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-31 18:12:10 +02:00
Heimen Stoffels
76226a319f Translated using Weblate (Dutch)
Currently translated at 5.0% (4 of 80 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/nl/
2021-08-31 16:33:12 +02:00
Maciej Błędkowski
eed138a2fb Translated using Weblate (Polish)
Currently translated at 100.0% (197 of 197 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/pl/
2021-08-31 16:33:12 +02:00
Sylvia van Os
fd202fc940 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-08-30 21:36:29 +02:00
Sylvia van Os
5364b57cee Make PR instead of auto-committing 2021-08-30 21:35:51 +02:00
Sylvia van Os
3136b80f26 Merge pull request #356 from TheLastProject/create-pull-request/patch
Update Fastlane changelogs
2021-08-30 21:29:33 +02:00
TheLastProject
f3471b082f Update Fastlane changelogs 2021-08-30 19:28:21 +00:00
Sylvia van Os
9d24ef7791 Move to correct location 2021-08-30 21:27:55 +02:00
Sylvia van Os
6223ccd4bc Automatically Convert CHANGELOG to Fastlane 2021-08-30 21:26:30 +02:00
Sylvia van Os
a81cc27414 Add badges showing current available versions 2021-08-29 23:39:04 +02:00
Sylvia van Os
78481516b0 Bump versioncode 2021-08-29 16:38:07 +02:00
Sylvia van Os
423b74273e Release 2.4.0 2021-08-29 16:28:00 +02:00
Sylvia van Os
83e0a988d9 Improve contrast of cards 2021-08-29 16:14:32 +02:00
Sylvia van Os
1adea88ff1 Merge pull request #352 from TheLastProject/feature/cardLayout
Main screen card layout
2021-08-29 01:33:06 +02:00
Sylvia van Os
dda912c8c5 Update CHANGELOG 2021-08-29 01:27:47 +02:00
Sylvia van Os
f3b31bb306 Update screenshots 2021-08-29 01:27:10 +02:00
Sylvia van Os
3b31c4e05a Set min-height for long store names 2021-08-29 01:16:31 +02:00
Sylvia van Os
1155108086 Fix title layout issue 2021-08-29 00:46:36 +02:00
Sylvia van Os
73bb7ded09 Fix recyclerview not rendering all in unit test 2021-08-28 01:51:44 +02:00
Sylvia van Os
d8655eb631 Don't make cards bigger than needed 2021-08-28 01:42:19 +02:00
Sylvia van Os
55cddfef91 Fix incorrect data due to recycling 2021-08-28 01:39:33 +02:00
Sylvia van Os
c35dc736f4 Cleanup buggy code 2021-08-28 01:03:25 +02:00
Sylvia van Os
23b762a500 2 colums in landscape 2021-08-28 00:05:33 +02:00
Sylvia van Os
b9b638de45 Initial card layout 2021-08-27 23:42:03 +02:00
thelastproject/contributors-to-file
5f57507dda Update contributor list 2021-08-27 20:40:38 +00:00
Sylvia van Os
852eb6612c Merge pull request #351 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-27 22:40:04 +02:00
solokot
d96aac7bbf Translated using Weblate (Russian)
Currently translated at 100.0% (197 of 197 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-08-27 22:32:54 +02:00
Allan Nordhøy
9018479959 Translated using Weblate (Norwegian Bokmål)
Currently translated at 90.8% (179 of 197 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-08-27 22:32:54 +02:00
Gediminas Murauskas
ce8a99e840 Translated using Weblate (Lithuanian)
Currently translated at 100.0% (197 of 197 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/lt/
2021-08-27 22:32:54 +02:00
J. Lavoie
9236f66bc6 Translated using Weblate (Italian)
Currently translated at 100.0% (197 of 197 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-08-27 22:32:53 +02:00
J. Lavoie
37b3964351 Translated using Weblate (French)
Currently translated at 100.0% (197 of 197 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-08-27 22:32:53 +02:00
J. Lavoie
a60d902fda Translated using Weblate (German)
Currently translated at 100.0% (197 of 197 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-08-27 22:32:53 +02:00
Petr Novák
a97ce6f9aa Translated using Weblate (Czech)
Currently translated at 100.0% (197 of 197 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/cs/
2021-08-27 22:32:53 +02:00
waffshappen
a15bce04e8 German Translation (#349) 2021-08-26 00:03:39 +02:00
thelastproject/contributors-to-file
34fa68f852 Update contributor list 2021-08-25 21:31:08 +00:00
Sylvia van Os
e5fef55fc7 Ignore bots 2021-08-25 23:30:41 +02:00
Sylvia van Os
c5cca1b14d Merge pull request #348 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-25 23:24:45 +02:00
Heimen Stoffels
f34ec1cc4b Translated using Weblate (Dutch)
Currently translated at 100.0% (197 of 197 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-08-25 23:23:34 +02:00
solokot
95aafa5c99 Translated using Weblate (Russian)
Currently translated at 100.0% (196 of 196 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-08-25 23:23:34 +02:00
Heimen Stoffels
00160b0c45 Translated using Weblate (Dutch)
Currently translated at 100.0% (196 of 196 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-08-25 23:23:34 +02:00
Allan Nordhøy
a7e6d56bc9 Translated using Weblate (Norwegian Bokmål)
Currently translated at 91.3% (179 of 196 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-08-25 23:23:34 +02:00
Gediminas Murauskas
9dc8d5e2f4 Translated using Weblate (Lithuanian)
Currently translated at 100.0% (196 of 196 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/lt/
2021-08-25 23:23:34 +02:00
thelastproject/contributors-to-file
cf7cf679e0 Update contributor list 2021-08-25 21:23:31 +00:00
Sylvia van Os
4e7e0d9480 Fix pagination in contributors-to-file 2021-08-25 23:23:05 +02:00
thelastproject/contributors-to-file
fbb3013b20 Update contributor list 2021-08-25 20:57:46 +00:00
waffshappen
ca37e2aa84 Introduce a fallback text when a group is empty (#347) 2021-08-25 22:57:22 +02:00
Sylvia van Os
e6bf60d457 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-08-25 21:39:10 +02:00
Sylvia van Os
35835fda34 Make spotBugs happy 2021-08-25 21:38:52 +02:00
Sylvia van Os
a3133de6cf Merge pull request #346 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-25 21:27:31 +02:00
solokot
40fb10a2ec Translated using Weblate (Russian)
Currently translated at 100.0% (195 of 195 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-08-25 21:18:16 +02:00
Sylvia van Os
856c735ba3 Add all contributors to About screen
You all are amazing, thank you for helping me make this app :)
2021-08-25 21:17:55 +02:00
thelastproject/contributors-to-file
16fb0df55d Update contributor list 2021-08-25 18:40:47 +00:00
Sylvia van Os
5658acbb07 Update contributor list on push not on schedule 2021-08-25 20:38:51 +02:00
Sylvia van Os
8b5faac0e0 Fix filename 2021-08-25 20:37:39 +02:00
Sylvia van Os
10be9b58ce Write contributors to file 2021-08-25 20:37:24 +02:00
Sylvia van Os
832840be08 Merge pull request #345 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-25 12:10:31 +02:00
Quentin PAGÈS
5806156495 Translated using Weblate (Occitan)
Currently translated at 37.4% (73 of 195 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/oc/
2021-08-25 11:32:59 +02:00
Petr Novák
0be9b908ab Translated using Weblate (Czech)
Currently translated at 66.6% (2 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/cs/
2021-08-25 11:32:59 +02:00
IllusiveMan196
718d2b7567 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (195 of 195 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/uk/
2021-08-25 11:32:58 +02:00
IllusiveMan196
fed9754908 Translated using Weblate (Russian)
Currently translated at 100.0% (195 of 195 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-08-25 11:32:58 +02:00
solokot
c0e09ca429 Translated using Weblate (Russian)
Currently translated at 100.0% (195 of 195 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-08-25 11:32:58 +02:00
Heimen Stoffels
4a3737b846 Translated using Weblate (Dutch)
Currently translated at 100.0% (195 of 195 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-08-25 11:32:57 +02:00
Gediminas Murauskas
b4add4a0ea Translated using Weblate (Lithuanian)
Currently translated at 100.0% (195 of 195 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/lt/
2021-08-25 11:32:57 +02:00
J. Lavoie
5aabbd629e Translated using Weblate (Italian)
Currently translated at 100.0% (195 of 195 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-08-25 11:32:57 +02:00
J. Lavoie
a5ef50317d Translated using Weblate (French)
Currently translated at 100.0% (195 of 195 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-08-25 11:32:56 +02:00
J. Lavoie
48a8cc927a Translated using Weblate (German)
Currently translated at 100.0% (195 of 195 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-08-25 11:32:56 +02:00
Petr Novák
cbb97d7d57 Translated using Weblate (Czech)
Currently translated at 100.0% (195 of 195 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/cs/
2021-08-25 11:32:56 +02:00
Sylvia van Os
5eee344035 Don't close ScanActivity on camera error 2021-08-25 00:29:40 +02:00
Sylvia van Os
4c7e0c7174 Fix build 2021-08-23 18:40:48 +02:00
Sylvia van Os
37bb9c86f4 More small cleanups 2021-08-23 00:59:31 +02:00
Sylvia van Os
83ea6ffbf7 Cleanups 2021-08-22 16:16:27 +02:00
Sylvia van Os
f4945c2cd2 Merge pull request #342 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-22 10:45:01 +02:00
IllusiveMan196
24d9c7825a Translated using Weblate (Ukrainian)
Currently translated at 97.9% (191 of 195 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/uk/
2021-08-22 02:43:38 +02:00
Sylvia van Os
2aa7bb4639 Update CHANGELOG 2021-08-22 01:29:14 +02:00
Subhashish Anand
110b3bcef3 Add theme colour support 2021-08-22 01:14:24 +02:00
Sylvia van Os
f625efc76d Merge pull request #341 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-21 21:36:27 +02:00
Sylvia van Os
22608a9802 Translated using Weblate (Dutch)
Currently translated at 100.0% (175 of 175 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-08-21 21:35:36 +02:00
Sylvia van Os
abf6c1328d Update importers list 2021-08-20 22:33:26 +02:00
Sylvia van Os
225058ab2b Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-08-19 21:26:04 +02:00
Sylvia van Os
275dc6caa2 Release Catima 2.3.0 2021-08-19 21:25:35 +02:00
Sylvia van Os
ceb7b12154 Merge pull request #335 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-18 17:41:48 +02:00
Luna Jernberg
849da26198 Translated using Weblate (Swedish)
Currently translated at 99.4% (174 of 175 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/sv/
2021-08-18 17:36:39 +02:00
solokot
5808c991bb Translated using Weblate (Russian)
Currently translated at 100.0% (175 of 175 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-08-18 17:36:39 +02:00
Heimen Stoffels
e8554cb221 Translated using Weblate (Dutch)
Currently translated at 100.0% (175 of 175 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-08-18 17:36:39 +02:00
Allan Nordhøy
46092c060d Translated using Weblate (Norwegian Bokmål)
Currently translated at 90.2% (158 of 175 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-08-18 17:36:38 +02:00
Gediminas Murauskas
736eebd45c Translated using Weblate (Lithuanian)
Currently translated at 100.0% (175 of 175 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/lt/
2021-08-18 17:36:38 +02:00
J. Lavoie
f72db53345 Translated using Weblate (Italian)
Currently translated at 100.0% (175 of 175 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-08-18 17:36:38 +02:00
J. Lavoie
b8c017259e Translated using Weblate (French)
Currently translated at 100.0% (175 of 175 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-08-18 17:36:38 +02:00
J. Lavoie
4b4a237b9a Translated using Weblate (German)
Currently translated at 100.0% (175 of 175 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-08-18 17:36:37 +02:00
Petr Novák
17034e992f Translated using Weblate (Czech)
Currently translated at 60.0% (105 of 175 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/cs/
2021-08-18 17:36:37 +02:00
Sylvia van Os
bf143038b0 Update CHANGELOG 2021-08-17 22:46:53 +02:00
Sylvia van Os
73805ad3bb Fix importing images from export 2021-08-17 22:46:03 +02:00
Sylvia van Os
7006e35ebf Remove pages managed in Catima-Website repository 2021-08-16 18:23:03 +02:00
Sylvia van Os
fc598018b2 Remove duplicate Privacy Policy
It is managed in https://github.com/TheLastProject/Catima-Website/ now
2021-08-16 18:21:36 +02:00
Sylvia van Os
d01ec85c32 Merge pull request #332 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-15 22:23:59 +02:00
Nyatsuki
04ee918152 Translated using Weblate (Japanese)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ja/
2021-08-15 22:18:44 +02:00
Tymofii Lytvynenko
943b70647b Translated using Weblate (Ukrainian)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/uk/
2021-08-15 22:18:44 +02:00
Sylvia van Os
edba5d5dca Add locale chooser (#304)
* Add locale chooser

* Prevent AppBundle crashes

* Reload activity on language change

* Make spotBugs happy
2021-08-15 22:18:40 +02:00
Sylvia van Os
86be5d1994 Update image-actions 2021-08-15 17:44:42 +02:00
Sylvia van Os
5be2308606 Release 2.2.3 2021-08-13 20:21:02 +02:00
Sylvia van Os
a2e81d08d7 Merge pull request #330 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-13 15:34:50 +02:00
Joel A
9f5f0e79f4 Translated using Weblate (Swedish)
Currently translated at 66.6% (2 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/sv/
2021-08-13 13:32:16 +02:00
Joel A
3f8c4c94ce Translated using Weblate (Swedish)
Currently translated at 99.4% (171 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/sv/
2021-08-13 13:32:16 +02:00
mondstern
46895c0037 Translated using Weblate (Finnish)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/fi/
2021-08-13 13:32:15 +02:00
mondstern
65c98a4840 Translated using Weblate (Finnish)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fi/
2021-08-13 13:32:15 +02:00
Sylvia van Os
67c58c10b2 Create splash screen 2021-08-12 23:36:55 +02:00
Sylvia van Os
8d82a4616f Don't remove needs info 2021-08-12 13:06:15 +02:00
Sylvia van Os
01a272f8e6 Autoclose issues being in needs info state for over 90 days 2021-08-11 17:57:22 +02:00
Sylvia van Os
0235a397cf Merge pull request #328 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-11 09:04:18 +02:00
Joel A
e37ee6f812 Translated using Weblate (Swedish)
Currently translated at 85.4% (147 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/sv/
2021-08-11 05:32:37 +02:00
mondstern
2b5ea47d9f Translated using Weblate (Bulgarian)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/bg/
2021-08-11 05:32:37 +02:00
mondstern
0fcdaf3f60 Translated using Weblate (Bulgarian)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/bg/
2021-08-11 05:32:37 +02:00
Jane Kong
b2cb38b61d Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/zh_Hans/
2021-08-11 05:32:36 +02:00
Jane Kong
fd1fe168cc Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/zh_Hans/
2021-08-11 05:32:36 +02:00
mondstern
9deb80ff9b Translated using Weblate (Ukrainian)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/uk/
2021-08-11 05:32:35 +02:00
Sylvia van Os
75fdd7390f Merge pull request #325 from recursiveribbons/sql-refactor
Refactored DBHelper
2021-08-09 20:35:55 +02:00
recursiveribbons
7cd6c17b55 Refactored DBHelper 2021-08-09 17:35:25 +02:00
Sylvia van Os
c273d8d17f Fix widget creating different-looking shortcut than app shortcuts 2021-08-08 23:49:10 +02:00
Sylvia van Os
c1b02f58ff Fix indent 2021-08-08 23:04:55 +02:00
Sylvia van Os
0bac2dfb62 Merge pull request #322 from TacoTheDank/master
Some cleanup and compatibility stuff
2021-08-08 23:03:58 +02:00
TacoTheDank
4f0288e396 Use ActivityCompat.recreate 2021-08-08 16:33:55 -04:00
TacoTheDank
55f0399325 Use ShortcutInfoCompat 2021-08-08 16:33:42 -04:00
Sylvia van Os
e756c6e50b Fix dir names 2021-08-08 19:30:37 +02:00
Sylvia van Os
b8c022dc70 Merge pull request #323 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-08 19:18:06 +02:00
Joel A
d823ddd8aa Translated using Weblate (Swedish)
Currently translated at 33.3% (1 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/sv/
2021-08-08 19:13:17 +02:00
Joel A
352b42bba0 Translated using Weblate (Swedish)
Currently translated at 82.5% (142 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/sv/
2021-08-08 19:13:17 +02:00
Sylvia van Os
89894131d9 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-08-08 19:12:49 +02:00
Sylvia van Os
5ddcb0a668 Fix crash on rotation in edit view 2021-08-08 19:12:25 +02:00
TacoTheDank
5d5b1f0a63 Use AndroidX ExifInterface 2021-08-07 17:08:50 -04:00
TacoTheDank
03e638786b Create TextWatcher stub 2021-08-07 17:08:28 -04:00
TacoTheDank
7d5b2f8dc8 Update NumberPickerPreference library 2021-08-07 16:47:01 -04:00
Sylvia van Os
13257d17fc Merge pull request #321 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-07 13:48:38 +02:00
Joel A
5e8a589d9a Added translation using Weblate (Swedish) 2021-08-07 13:47:51 +02:00
Sylvia van Os
44711043b9 Release Catima 2.2.1 2021-08-07 13:28:02 +02:00
Sylvia van Os
80ee8aa860 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-08-07 13:16:35 +02:00
Sylvia van Os
cd6685b974 Fix flashing in ManageGroupsActivity 2021-08-07 13:16:21 +02:00
Sylvia van Os
b5f464000b Merge pull request #318 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-07 05:54:03 +02:00
J. Lavoie
27159323d5 Translated using Weblate (Italian)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-08-07 02:41:29 +02:00
J. Lavoie
9551ce8a8b Translated using Weblate (French)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-08-07 02:41:29 +02:00
J. Lavoie
525471f749 Translated using Weblate (German)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-08-07 02:41:28 +02:00
Sylvia van Os
57bedd0300 Actually fix properly 2021-08-05 20:51:29 +02:00
Sylvia van Os
c3345a1a15 Fix last commit 2021-08-05 20:35:44 +02:00
Sylvia van Os
15f6bd86a1 Change card title size within range 2021-08-05 20:21:18 +02:00
Sylvia van Os
e2ad1c9da2 Add animations to card list 2021-08-05 19:38:37 +02:00
Sylvia van Os
7082669612 Fix importing Catima export with multiline note 2021-08-05 18:28:14 +02:00
Sylvia van Os
d595d24769 Merge pull request #313 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-04 23:26:51 +02:00
Gediminas Murauskas
4287d66f6f Translated using Weblate (Lithuanian)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/lt/
2021-08-04 23:24:45 +02:00
Sylvia van Os
311361d105 Merge pull request #312 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-04 23:24:41 +02:00
Sylvia van Os
4a88e39deb Fix syntax 2021-08-04 22:38:13 +02:00
mondstern
4511590263 Translated using Weblate (Danish)
Currently translated at 66.6% (2 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/da/
2021-08-04 22:32:46 +02:00
mondstern
d0c30ffa1c Translated using Weblate (Danish)
Currently translated at 27.3% (47 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/da/
2021-08-04 22:32:46 +02:00
Allan Nordhøy
813c2bff85 Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/nb_NO/
2021-08-04 22:32:45 +02:00
solokot
0c3cf43841 Translated using Weblate (Russian)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-08-04 22:32:45 +02:00
Heimen Stoffels
6c651f7e3e Translated using Weblate (Dutch)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-08-04 22:32:45 +02:00
Allan Nordhøy
ef7fc92920 Translated using Weblate (Norwegian Bokmål)
Currently translated at 90.1% (155 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-08-04 22:32:44 +02:00
Sylvia van Os
ccaf70c749 Merge pull request #311 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-03 20:59:42 +02:00
mondstern
2299bf9d86 Added translation using Weblate (Danish) 2021-08-03 20:58:37 +02:00
Sylvia van Os
b65f8f32ca Merge pull request #310 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-03 20:48:23 +02:00
mondstern
47695d3a72 Translated using Weblate (Czech)
Currently translated at 66.6% (2 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/cs/
2021-08-03 20:43:07 +02:00
solokot
439c660b2e Translated using Weblate (Russian)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-08-03 20:43:07 +02:00
Gediminas Murauskas
4293d7d4b2 Translated using Weblate (Lithuanian)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/lt/
2021-08-03 20:43:07 +02:00
J. Lavoie
e368e66b8a Translated using Weblate (Italian)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-08-03 20:43:07 +02:00
Mattia
992a7f9e84 Translated using Weblate (Italian)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-08-03 20:43:07 +02:00
J. Lavoie
ecf0faf00a Translated using Weblate (French)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-08-03 20:43:07 +02:00
J. Lavoie
64178f3fd1 Translated using Weblate (Spanish)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/es/
2021-08-03 20:43:07 +02:00
J. Lavoie
dab6588800 Translated using Weblate (German)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-08-03 20:43:07 +02:00
mondstern
f71bb32592 Translated using Weblate (Czech)
Currently translated at 51.1% (88 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/cs/
2021-08-03 20:43:07 +02:00
Sylvia van Os
0c44212d92 Make SpotBugs happy 2021-08-03 20:42:57 +02:00
Sylvia van Os
ce149c91e9 Improve Stocard importer 2021-08-03 20:34:44 +02:00
Sylvia van Os
05dfd48f57 Release Catima 2.2.0 2021-08-02 23:42:32 +02:00
Sylvia van Os
ef955b866d Remove unused imports 2021-08-02 23:09:49 +02:00
Sylvia van Os
a843b5a1b9 Make lint happier 2021-08-02 22:58:06 +02:00
Sylvia van Os
8305d58ccc Fix maximize button appearing on no barcode 2021-08-02 22:57:55 +02:00
Sylvia van Os
6af79d5f41 General cleanups 2021-08-02 20:47:48 +02:00
Sylvia van Os
3b326d6f9c Also remove from AboutActivity 2021-08-02 20:37:13 +02:00
Sylvia van Os
1456e5073e Get rid of barely used dependency 2021-08-02 20:36:20 +02:00
Sylvia van Os
6a13dbf66a Comma-separate group names in loyalty card view 2021-08-01 23:03:56 +02:00
Sylvia van Os
c5b1718e8e Pre-select active group on creating new card 2021-08-01 22:33:44 +02:00
Sylvia van Os
e2bcc24867 Make links in notes clickable 2021-08-01 21:39:48 +02:00
Sylvia van Os
c86cc22a93 Release v2.1.0 2021-08-01 21:00:12 +02:00
Sylvia van Os
765577ae9e Merge pull request #306 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-01 20:50:42 +02:00
Heimen Stoffels
d1d15644b5 Translated using Weblate (Dutch)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-08-01 20:36:29 +02:00
Sylvia van Os
5d41b213db Remove translations to break lint 2021-08-01 20:36:17 +02:00
Sylvia van Os
aa515b1192 Be more explicit in quantity
Translators still keep triggering the linter with errors such as the
following:
Error: The quantity 'one' matches more than one specific number in this locale (1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …), but the message did not include a formatting argument (such as %d). This is usually an internationalization error. See full issue explanation for more. [ImpliedQuantity]
2021-08-01 12:50:56 +02:00
Sylvia van Os
068660fe3f Merge pull request #305 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-01 12:42:14 +02:00
solokot
99ffaf97d1 Translated using Weblate (Russian)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-08-01 12:32:51 +02:00
Heimen Stoffels
b53999c1cf Translated using Weblate (Dutch)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-08-01 12:32:51 +02:00
Allan Nordhøy
57f87d7dc0 Translated using Weblate (Norwegian Bokmål)
Currently translated at 90.6% (156 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-08-01 12:32:50 +02:00
J. Lavoie
760f6a873d Translated using Weblate (Italian)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-08-01 12:32:50 +02:00
J. Lavoie
675bd43ff8 Translated using Weblate (French)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-08-01 12:32:50 +02:00
J. Lavoie
07532ce001 Translated using Weblate (Spanish)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/es/
2021-08-01 12:32:50 +02:00
J. Lavoie
908a7055c7 Translated using Weblate (German)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-08-01 12:32:49 +02:00
Sylvia van Os
2be371caed Forgot number 2021-07-30 19:46:10 +02:00
Sylvia van Os
f4f3f2e307 Increase i18n flexibility
The following may seem weird, but it is necessary to give translators enough flexibility. For example, in Russian, Android's plural quantity "one" actually refers to "any number ending on 1 but not ending in 11". So while in English the extra non-plural form seems unnecessary duplication, it is necessary to give translators enough flexibility. Therefore, we use the plain string when meaning exactly 1, and otherwise use the plural forms.
2021-07-30 19:40:43 +02:00
Sylvia van Os
edbf76c7dc Merge pull request #303 from weblate/weblate-catima-catima
Translations update from Weblate
2021-07-30 19:02:48 +02:00
109247019824
f8615f45f0 Translated using Weblate (Bulgarian)
Currently translated at 100.0% (170 of 170 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/bg/
2021-07-30 18:18:55 +02:00
IllusiveMan196
c3bc3e9911 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (170 of 170 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/uk/
2021-07-30 18:18:55 +02:00
solokot
8adcca1df2 Translated using Weblate (Russian)
Currently translated at 100.0% (170 of 170 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-07-30 18:18:55 +02:00
Heimen Stoffels
bdde8669ac Translated using Weblate (Dutch)
Currently translated at 100.0% (170 of 170 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-07-30 18:18:55 +02:00
solokot
59db2df525 Translated using Weblate (Russian)
Currently translated at 100.0% (170 of 170 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-07-30 18:18:55 +02:00
Sylvia van Os
3b83ff5b0e Fix syntax 2021-07-30 18:18:32 +02:00
Heimen Stoffels
19fb290a51 Translated using Weblate (Dutch)
Currently translated at 100.0% (170 of 170 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-07-30 18:15:39 +02:00
Allan Nordhøy
e1e823d9e0 Translated using Weblate (Norwegian Bokmål)
Currently translated at 90.0% (153 of 170 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-07-30 18:15:30 +02:00
Sylvia van Os
7ed477b670 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-07-29 22:16:47 +02:00
Sylvia van Os
1960fb0b6a Fix fullscreen loss on rotation 2021-07-29 22:16:36 +02:00
Sylvia van Os
e913482790 Merge pull request #299 from comradekingu/patch-7
New strings reworked
2021-07-29 21:54:43 +02:00
Allan Nordhøy
2e5bd76d31 New strings reworked 2021-07-29 19:47:10 +00:00
Sylvia van Os
876ae979da Simplify updateTempState
Hopefully fixes the few weird crashes on Google Play
2021-07-29 21:42:21 +02:00
Sylvia van Os
d918c15ad6 Prevent ArithmeticException 2021-07-29 21:18:21 +02:00
Sylvia van Os
d57cb307c3 Support multi-deleting cards 2021-07-29 20:38:37 +02:00
Sylvia van Os
cbcf1bcd99 Use desugaring to upgrade zxing without having to increase minSdk
As documented by the zxing team on https://github.com/zxing/zxing/wiki/Frequently-Asked-Questions#it-doesnt-work-with-java-7--no-interface-method-sortljavautilcomparator
2021-07-28 23:05:27 +02:00
Sylvia van Os
96bc10583f Fix colour dialog selected colour 2021-07-27 22:04:09 +02:00
Sylvia van Os
5ca4d29a36 Release Catima 2.0.4 2021-07-27 21:42:04 +02:00
Sylvia van Os
2efb666fae Fix loyalty card colour changing 2021-07-27 21:38:54 +02:00
Sylvia van Os
89dce1068f Shortcut fixes 2021-07-27 21:26:24 +02:00
Sylvia van Os
9854125af9 Make lint happy 2021-07-26 23:37:17 +02:00
Sylvia van Os
6e307ab1f0 Update dependencies 2021-07-26 23:10:51 +02:00
Sylvia van Os
c4f0d1bef6 Release Catima 2.0.3 2021-07-25 22:32:40 +02:00
Sylvia van Os
5201788818 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-07-25 21:52:10 +02:00
Sylvia van Os
8472bc9755 Make SpotBugs happy 2021-07-25 21:51:46 +02:00
Sylvia van Os
ce0f531831 Merge pull request #289 from weblate/weblate-catima-catima
Translations update from Weblate
2021-07-25 21:41:26 +02:00
Quentin PAGÈS
18e9c3ccb5 Translated using Weblate (Occitan)
Currently translated at 32.3% (55 of 170 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/oc/
2021-07-25 21:40:25 +02:00
Sylvia van Os
19afe8e69c Simplify edit activity and fix some status bugs 2021-07-25 21:40:15 +02:00
Sylvia van Os
8e5bace7cc Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-07-23 23:52:58 +02:00
Sylvia van Os
1ca85e9d7b Create unit test for broken onResume behaviour 2021-07-23 23:52:45 +02:00
Sylvia van Os
eb24af8266 Add Zip4j to used libraries list 2021-07-22 12:14:39 +02:00
Sylvia van Os
12ba01eb87 Merge pull request #287 from weblate/weblate-catima-catima
Translations update from Weblate
2021-07-21 23:32:51 +02:00
Quentin PAGÈS
a0e30bdccc Added translation using Weblate (Occitan) 2021-07-21 23:26:38 +02:00
Sylvia van Os
4663e22128 Rename Spanish metadata to something fastlane supply can push to Google Play 2021-07-21 18:42:27 +02:00
Sylvia van Os
7bd1d16d24 Merge pull request #286 from weblate/weblate-catima-catima
Translations update from Weblate
2021-07-21 18:37:32 +02:00
Diego
fd838cfd43 Translated using Weblate (Spanish)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/es/
2021-07-21 18:32:48 +02:00
Diego
1eca79b4cb Translated using Weblate (Spanish)
Currently translated at 100.0% (170 of 170 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/es/
2021-07-21 18:32:48 +02:00
Sylvia van Os
1d694f5f2c Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-07-21 18:23:05 +02:00
Sylvia van Os
96a046b165 Fix share/import URL crash on Android 6 2021-07-21 18:22:50 +02:00
Sylvia van Os
5ff9c9c469 Merge pull request #282 from weblate/weblate-catima-catima
Translations update from Weblate
2021-07-19 07:02:06 +02:00
Nyatsuki
836cdd87bc Translated using Weblate (Japanese)
Currently translated at 99.4% (169 of 170 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ja/
2021-07-19 06:33:37 +02:00
109247019824
0af6dd4d44 Translated using Weblate (Bulgarian)
Currently translated at 100.0% (170 of 170 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/bg/
2021-07-17 23:32:59 +02:00
IllusiveMan196
034d62a643 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/uk/
2021-07-17 23:32:59 +02:00
IllusiveMan196
417224602e Translated using Weblate (Ukrainian)
Currently translated at 100.0% (170 of 170 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/uk/
2021-07-17 23:32:58 +02:00
Sylvia van Os
c711aeae7f Fix build 2021-07-16 20:44:51 +02:00
109247019824
1f12543e3e Translated using Weblate (Bulgarian)
Currently translated at 94.7% (162 of 171 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/bg/
2021-07-16 20:34:00 +02:00
Nyatsuki
dc31175b5d Translated using Weblate (Japanese)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/ja/
2021-07-16 20:34:00 +02:00
Nyatsuki
719b8112eb Translated using Weblate (Japanese)
Currently translated at 100.0% (171 of 171 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ja/
2021-07-16 20:34:00 +02:00
solokot
f19a3c507b Translated using Weblate (Russian)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/ru/
2021-07-16 20:34:00 +02:00
solokot
2416ec396a Translated using Weblate (Russian)
Currently translated at 100.0% (171 of 171 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-07-16 20:34:00 +02:00
Heimen Stoffels
d1fe92e967 Translated using Weblate (Dutch)
Currently translated at 100.0% (171 of 171 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-07-16 20:34:00 +02:00
Gediminas Murauskas
ff5fd49b89 Translated using Weblate (Lithuanian)
Currently translated at 100.0% (171 of 171 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/lt/
2021-07-16 20:34:00 +02:00
J. Lavoie
1015e0d94e Translated using Weblate (Italian)
Currently translated at 100.0% (171 of 171 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-07-16 20:34:00 +02:00
J. Lavoie
d872828e7d Translated using Weblate (French)
Currently translated at 100.0% (171 of 171 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-07-16 20:34:00 +02:00
J. Lavoie
8f6ad6d1bd Translated using Weblate (German)
Currently translated at 100.0% (171 of 171 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-07-16 20:34:00 +02:00
Sylvia van Os
05fd629ad4 Fix bad strings 2021-07-16 20:33:46 +02:00
Sylvia van Os
efdb0dd6bb Consolidate 2 very similar strings 2021-07-16 20:28:25 +02:00
Sylvia van Os
2e0482beef Always move import/export to overflow menu 2021-07-15 23:03:28 +02:00
Sylvia van Os
0f25743da4 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-07-15 22:45:32 +02:00
Sylvia van Os
e970bf185a Lowercase .zip filename 2021-07-15 22:45:12 +02:00
Sylvia van Os
60f3547b01 Merge pull request #275 from weblate/weblate-catima-catima
Translations update from Weblate
2021-07-15 22:18:09 +02:00
109247019824
ae09db428b Translated using Weblate (Bulgarian)
Currently translated at 21.0% (36 of 171 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/bg/
2021-07-15 22:09:55 +02:00
Sylvia van Os
ebca7ca150 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-07-15 22:09:44 +02:00
Sylvia van Os
b6ef6806a0 Make selected in main screen selectable 2021-07-15 22:09:31 +02:00
Sylvia van Os
20a9cb30c4 Merge pull request #273 from weblate/weblate-catima-catima
Translations update from Weblate
2021-07-15 21:37:14 +02:00
109247019824
a43112f469 Translated using Weblate (Bulgarian)
Currently translated at 11.1% (19 of 171 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/bg/
2021-07-15 21:33:48 +02:00
109247019824
2a95b2f530 Added translation using Weblate (Bulgarian) 2021-07-15 21:30:47 +02:00
Sylvia van Os
cb979bfbec Release Catima 2.0 2021-07-14 20:26:39 +02:00
Sylvia van Os
feabe353b0 Fix loyalty card viewer appbar top margin 2021-07-14 20:08:28 +02:00
Sylvia van Os
7987932100 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-07-14 19:39:34 +02:00
Sylvia van Os
e496c69e15 Fix FAB being below other elements in Android 4 2021-07-14 19:39:19 +02:00
Sylvia van Os
e0300f8f21 Merge pull request #269 from weblate/weblate-catima-catima
Translations update from Weblate
2021-07-14 12:04:15 +02:00
phlostically
64cbcb2ef7 Translated using Weblate (Esperanto)
Currently translated at 40.9% (70 of 171 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/eo/
2021-07-14 11:32:33 +02:00
J. Lavoie
7e24f02a73 Translated using Weblate (French)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/fr/
2021-07-14 11:32:32 +02:00
J. Lavoie
ed89eab782 Translated using Weblate (Italian)
Currently translated at 97.0% (166 of 171 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-07-14 11:32:31 +02:00
J. Lavoie
17863e8920 Translated using Weblate (French)
Currently translated at 100.0% (171 of 171 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-07-14 11:32:31 +02:00
J. Lavoie
6e82e6fc5d Translated using Weblate (German)
Currently translated at 97.0% (166 of 171 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-07-14 11:32:31 +02:00
Sylvia van Os
64b1103f13 Merge pull request #268 from weblate/weblate-catima-catima
Translations update from Weblate
2021-07-13 23:59:44 +02:00
phlostically
e4274df941 Added translation using Weblate (Esperanto) 2021-07-13 23:58:37 +02:00
Sylvia van Os
c786b4b5a7 Merge pull request #267 from TheLastProject/dependabot/bundler/addressable-2.8.0
Bump addressable from 2.7.0 to 2.8.0
2021-07-13 21:48:18 +02:00
dependabot[bot]
95405270cb Bump addressable from 2.7.0 to 2.8.0
Bumps [addressable](https://github.com/sporkmonger/addressable) from 2.7.0 to 2.8.0.
- [Release notes](https://github.com/sporkmonger/addressable/releases)
- [Changelog](https://github.com/sporkmonger/addressable/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sporkmonger/addressable/compare/addressable-2.7.0...addressable-2.8.0)

---
updated-dependencies:
- dependency-name: addressable
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-13 19:41:48 +00:00
Sylvia van Os
9ef8eb2934 Merge pull request #265 from weblate/weblate-catima-catima
Translations update from Weblate
2021-07-12 21:44:33 +02:00
Gediminas Murauskas
4f70b06edb Translated using Weblate (Lithuanian)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/lt/
2021-07-12 20:32:56 +02:00
Heimen Stoffels
5677aa2b38 Translated using Weblate (Dutch)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/nl/
2021-07-12 20:32:56 +02:00
solokot
44997e1bbd Translated using Weblate (Russian)
Currently translated at 100.0% (171 of 171 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-07-12 20:32:55 +02:00
Heimen Stoffels
a96c569314 Translated using Weblate (Dutch)
Currently translated at 100.0% (171 of 171 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-07-12 20:32:55 +02:00
Sylvia van Os
5545220d53 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-07-11 19:34:05 +02:00
Sylvia van Os
4f47ee30ba Update supported import list 2021-07-11 19:33:52 +02:00
Sylvia van Os
4f9f318960 Merge pull request #264 from weblate/weblate-catima-catima
Translations update from Weblate
2021-07-11 19:14:42 +02:00
solokot
5dcfcf0ad0 Translated using Weblate (Russian)
Currently translated at 100.0% (169 of 169 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-07-11 19:10:51 +02:00
Heimen Stoffels
eb809974c4 Translated using Weblate (Dutch)
Currently translated at 100.0% (169 of 169 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-07-11 19:10:51 +02:00
Allan Nordhøy
815b037be7 Translated using Weblate (Norwegian Bokmål)
Currently translated at 89.9% (152 of 169 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-07-11 19:10:51 +02:00
Sylvia van Os
3eff150835 Implement flashlight toggle on scan 2021-07-11 19:10:38 +02:00
Sylvia van Os
e191e8bbe6 Update supported type list 2021-07-11 16:16:14 +02:00
Sylvia van Os
5aaf135325 Update screenshots 2021-07-11 16:14:18 +02:00
Sylvia van Os
6fd4f617e5 Fix .csv import fallback 2021-07-11 15:52:22 +02:00
Sylvia van Os
621e4ce162 Merge pull request #259 from comradekingu/patch-6
App strings reworked 4
2021-07-11 13:40:31 +02:00
Sylvia van Os
bf07e8935f Merge pull request #262 from weblate/weblate-catima-catima
Translations update from Weblate
2021-07-11 13:37:41 +02:00
Sylvia van Os
e69d6a453b Consistent phrasing 2021-07-11 13:35:53 +02:00
Gediminas Murauskas
579c2fc640 Translated using Weblate (Lithuanian)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/lt/
2021-07-11 13:32:42 +02:00
solokot
ba870481dd Translated using Weblate (Russian)
Currently translated at 100.0% (167 of 167 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-07-11 13:32:42 +02:00
Heimen Stoffels
0c10936e75 Translated using Weblate (Dutch)
Currently translated at 100.0% (167 of 167 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-07-11 13:32:42 +02:00
Gediminas Murauskas
26cf4250ad Translated using Weblate (Lithuanian)
Currently translated at 31.7% (53 of 167 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/lt/
2021-07-11 13:32:42 +02:00
Sylvia van Os
a8d5f38d5c Update privacy policy URL 2021-07-11 13:32:32 +02:00
Sylvia van Os
5e1bac4bcf Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-07-11 13:31:15 +02:00
Sylvia van Os
6df8b7b1ea Generate catima.app share links 2021-07-11 13:31:03 +02:00
Sylvia van Os
964c603405 Only list new format 2021-07-11 12:31:51 +02:00
Sylvia van Os
f64eea6470 Update app/src/main/res/values/strings.xml
Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
2021-07-11 12:31:22 +02:00
Sylvia van Os
aa97ad49d2 Create CNAME 2021-07-11 12:20:33 +02:00
Sylvia van Os
6221da72f8 Delete CNAME 2021-07-11 05:48:05 +02:00
Sylvia van Os
fe8ec38bf3 Create CNAME 2021-07-11 00:42:20 +02:00
Sylvia van Os
7324353d74 Use location hash instead of query parameters in share URL for increased privacy 2021-07-10 18:33:54 +02:00
Sylvia van Os
c5f0ee3a66 Fix bottom sheet alignment issue 2021-07-10 13:06:00 +02:00
Sylvia van Os
a8b4b25afb Merge pull request #260 from weblate/weblate-catima-catima
Translations update from Weblate
2021-07-10 05:58:57 +02:00
solokot
9744ede674 Translated using Weblate (Russian)
Currently translated at 100.0% (166 of 166 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-07-10 05:00:16 +02:00
Heimen Stoffels
43505d427d Translated using Weblate (Dutch)
Currently translated at 100.0% (166 of 166 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-07-10 05:00:16 +02:00
Allan Nordhøy
65e54c63ef Translated using Weblate (Norwegian Bokmål)
Currently translated at 90.9% (151 of 166 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-07-10 05:00:15 +02:00
J. Lavoie
31b306d432 Translated using Weblate (Italian)
Currently translated at 100.0% (166 of 166 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-07-10 05:00:15 +02:00
J. Lavoie
25bd1de09d Translated using Weblate (French)
Currently translated at 100.0% (166 of 166 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-07-10 05:00:15 +02:00
J. Lavoie
c82e0d82a9 Translated using Weblate (German)
Currently translated at 100.0% (166 of 166 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-07-10 05:00:14 +02:00
Sylvia van Os
6625488d84 Merge pull request #258 from TheLastProject/feature/catimaImportExportZip
Import and export .csv and images as .zip
2021-07-09 22:26:13 +02:00
Sylvia van Os
a1f632ace3 Set CharSet 2021-07-09 22:20:00 +02:00
Sylvia van Os
b9ea7fd1ed Create export->import test 2021-07-09 22:08:46 +02:00
Allan Nordhøy
8dff8d392e Fixes to import dialogs 2021-07-08 22:07:47 +00:00
Allan Nordhøy
b10d50d5a0 Revert accidental deletion 2021-07-08 22:05:38 +00:00
Allan Nordhøy
0b54d87f4f App strings reworked 4 2021-07-08 20:47:35 +00:00
Sylvia van Os
a9568d6adb Import and export .csv and images as .zip 2021-07-08 22:27:40 +02:00
Sylvia van Os
18c5aa4707 Support for new Voucher Vault export 2021-07-08 19:59:27 +02:00
Sylvia van Os
f53c61bbec Merge pull request #245 from TheLastProject/feature/StocardImport
Stocard import
2021-07-08 19:41:22 +02:00
Sylvia van Os
25c9c67ca2 Make spotBugs happy 2021-07-08 19:37:04 +02:00
Sylvia van Os
2efbf664c9 Finish Stocard importing 2021-07-08 19:29:52 +02:00
Sylvia van Os
44023969a7 Merge pull request #255 from weblate/weblate-catima-catima
Translations update from Weblate
2021-07-08 01:38:36 +02:00
Allan Nordhøy
0e5216b010 Translated using Weblate (Norwegian Bokmål)
Currently translated at 90.1% (147 of 163 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-07-08 01:33:02 +02:00
Sylvia van Os
f258506936 Merge pull request #253 from TheLastProject/feature/upce
Add UPC-E support
2021-07-07 23:19:59 +02:00
Sylvia van Os
8bad342374 Add UPC-E support 2021-07-07 19:14:11 +02:00
Sylvia van Os
476a219bec getOrDefault requires API level 24 2021-07-06 23:36:21 +02:00
Sylvia van Os
24e19e26ba Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker into feature/StocardImport 2021-07-06 23:29:50 +02:00
Sylvia van Os
3cb16ae3e9 Update CHANGELOG 2021-07-06 23:26:50 +02:00
Sylvia van Os
c7ddf957fa Clean up unused imports 2021-07-06 23:25:55 +02:00
Sylvia van Os
0de8cd93ad Ask password when import fails with BadPassword 2021-07-06 23:25:05 +02:00
Sylvia van Os
614e5bac2d Mark Fidme and Stocard as beta 2021-07-06 22:44:02 +02:00
Sylvia van Os
e76bd9363c Make Stocard import work 2021-07-06 21:54:19 +02:00
Sylvia van Os
97c508c920 Stocard import skeleton 2021-07-06 00:20:35 +02:00
Sylvia van Os
0f178c1cac Merge pull request #250 from weblate/weblate-catima-catima
Translations update from Weblate
2021-07-05 09:03:10 +02:00
String E. Fighter
8eab852e1a Translated using Weblate (German)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/de/
2021-07-05 08:33:19 +02:00
String E. Fighter
539f8846d8 Translated using Weblate (German)
Currently translated at 100.0% (163 of 163 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-07-05 08:33:19 +02:00
Sylvia van Os
fb239b6974 Merge pull request #247 from weblate/weblate-catima-catima
Translations update from Weblate
2021-07-02 09:36:29 +02:00
J. Lavoie
85b553e17b Translated using Weblate (Finnish)
Currently translated at 100.0% (163 of 163 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fi/
2021-07-02 09:33:32 +02:00
J. Lavoie
f085f1e9e6 Translated using Weblate (Italian)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/it/
2021-07-02 09:33:32 +02:00
J. Lavoie
56b387c725 Translated using Weblate (German)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/de/
2021-07-02 09:33:32 +02:00
J. Lavoie
daf0cdaa71 Translated using Weblate (French)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/fr/
2021-07-02 09:33:31 +02:00
J. Lavoie
6a7bebdbcd Translated using Weblate (Italian)
Currently translated at 100.0% (163 of 163 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-07-02 09:33:31 +02:00
J. Lavoie
7f72c3bcaf Translated using Weblate (French)
Currently translated at 100.0% (163 of 163 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-07-02 09:33:31 +02:00
J. Lavoie
52bff53756 Translated using Weblate (German)
Currently translated at 100.0% (163 of 163 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-07-02 09:33:30 +02:00
Sylvia van Os
73743b7f1e Merge pull request #246 from weblate/weblate-catima-catima
Translations update from Weblate
2021-06-30 07:29:13 +02:00
solokot
acfcd3d2d2 Translated using Weblate (Russian)
Currently translated at 100.0% (163 of 163 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-06-30 05:33:04 +02:00
Heimen Stoffels
24e8d12b73 Translated using Weblate (Dutch)
Currently translated at 100.0% (163 of 163 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-06-30 05:33:04 +02:00
Allan Nordhøy
28068dcddc Translated using Weblate (Norwegian Bokmål)
Currently translated at 90.1% (147 of 163 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-06-30 05:33:03 +02:00
Sylvia van Os
3e5ab76636 Update CHANGELOG 2021-06-29 22:40:36 +02:00
Sylvia van Os
3dceec8ec0 Support for password-protected zip files 2021-06-29 22:40:02 +02:00
Sylvia van Os
0cc409d087 Add Fidme test 2021-06-29 20:02:49 +02:00
Sylvia van Os
346acfa3f5 Fix CHANGELOG 2021-06-28 20:55:04 +02:00
Sylvia van Os
8d48da431e Adding and viewing front/back images (#215)
* Adding and viewing front/back images

* Fix import and export

* Fix unit tests

* Smaller preview pictures but clickable to make big

* Implement removing image

* Add card photo direct from camera

* Read Exif rotation info from picture taken

* Fix bad copy-paste

* Refactor to use local file system

* Delete card images when deleting card

* Prepare for image-based unit tests in ViewActivityTest
2021-06-28 20:40:52 +02:00
Sylvia van Os
b913fad847 Merge pull request #243 from weblate/weblate-catima-catima
Translations update from Weblate
2021-06-22 18:34:37 +02:00
IllusiveMan196
c900642f8e Translated using Weblate (Ukrainian)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/uk/
2021-06-22 18:33:24 +02:00
IllusiveMan196
c7d0da9e20 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (155 of 155 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/uk/
2021-06-22 18:33:24 +02:00
Sylvia van Os
f66b368cc2 Merge pull request #242 from weblate/weblate-catima-catima
Translations update from Weblate
2021-06-21 18:07:18 +02:00
huuhaa
4dad96472f Translated using Weblate (Finnish)
Currently translated at 100.0% (155 of 155 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fi/
2021-06-21 15:34:11 +02:00
Sylvia van Os
01bb4f0fc4 Merge pull request #240 from weblate/weblate-catima-catima
Translations update from Weblate
2021-06-20 08:09:08 +02:00
Allan Nordhøy
9ae5d74c7c Translated using Weblate (Norwegian Bokmål)
Currently translated at 90.9% (141 of 155 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-06-20 02:44:38 +02:00
Sylvia van Os
5af6d9b61c Fix language names for Google Play 2021-06-17 19:04:02 +02:00
Sylvia van Os
eb89a04c72 Merge pull request #239 from weblate/weblate-catima-catima
Translations update from Weblate
2021-06-15 18:05:47 +02:00
huuhaa
1019e40987 Translated using Weblate (Finnish)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/fi/
2021-06-15 12:32:35 +02:00
huuhaa
175d860885 Translated using Weblate (Finnish)
Currently translated at 100.0% (155 of 155 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fi/
2021-06-15 12:32:35 +02:00
Sylvia van Os
de8aa6b6fd Release v1.14.1 2021-06-14 21:27:44 +02:00
Sylvia van Os
a5a7be02f6 Translators are awesome <3 2021-06-14 21:18:21 +02:00
Sylvia van Os
14b7f8af81 Merge pull request #237 from weblate/weblate-catima-catima
Translations update from Weblate
2021-06-14 06:57:44 +02:00
huuhaa
62d01abf92 Translated using Weblate (Finnish)
Currently translated at 100.0% (155 of 155 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fi/
2021-06-14 04:32:36 +02:00
solokot
a328fa8f4a Translated using Weblate (Russian)
Currently translated at 100.0% (155 of 155 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-06-14 04:32:34 +02:00
Sylvia van Os
9e55271db1 Merge pull request #236 from weblate/weblate-catima-catima
Translations update from Weblate
2021-06-13 01:17:04 +02:00
huuhaa
e09ba941b8 Added translation using Weblate (Finnish) 2021-06-13 01:11:13 +02:00
Sylvia van Os
7562b662b7 Update target SDK 2021-06-10 16:50:38 +02:00
Sylvia van Os
ce65163377 Don't show update barcode dialog if value is the same as card ID 2021-06-10 16:49:52 +02:00
Sylvia van Os
da445255ec Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-06-10 13:18:00 +02:00
Sylvia van Os
fb36aecf42 Add missing barcode ID field to export 2021-06-10 13:17:48 +02:00
Sylvia van Os
3d624eae97 Merge pull request #233 from weblate/weblate-catima-catima
Translations update from Weblate
2021-06-08 05:21:38 +02:00
solokot
0a05676a87 Translated using Weblate (Russian)
Currently translated at 100.0% (155 of 155 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-06-08 02:46:40 +02:00
Heimen Stoffels
bce6ae0da6 Translated using Weblate (Dutch)
Currently translated at 100.0% (155 of 155 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-06-08 02:46:40 +02:00
J. Lavoie
2268465d2e Translated using Weblate (Italian)
Currently translated at 100.0% (155 of 155 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-06-08 02:46:40 +02:00
J. Lavoie
7a9953cfee Translated using Weblate (French)
Currently translated at 100.0% (155 of 155 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-06-08 02:46:40 +02:00
J. Lavoie
ecc11c120b Translated using Weblate (German)
Currently translated at 100.0% (155 of 155 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-06-08 02:46:39 +02:00
Sylvia van Os
f4d4e3d6fb Release v1.14 2021-06-07 20:21:48 +02:00
Sylvia van Os
929633e4dd Ask to update barcode value if card ID changes 2021-06-06 21:13:44 +02:00
Sylvia van Os
eec7359603 Merge pull request #230 from weblate/weblate-catima-catima
Translations update from Weblate
2021-05-25 12:07:42 +02:00
solokot
4168ec3b43 Translated using Weblate (Russian)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/ru/
2021-05-25 08:33:41 +02:00
Sylvia van Os
6568ebb01c Merge pull request #224 from weblate/weblate-catima-catima
Translations update from Weblate
2021-05-09 11:56:14 +02:00
Heimen Stoffels
99e2a75d46 Translated using Weblate (Dutch)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/nl/
2021-05-09 11:33:39 +02:00
Allan Nordhøy
43ae42c7c5 Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/nb_NO/
2021-05-09 11:33:39 +02:00
Allan Nordhøy
a7aa3e9e0e Shorter title and description (#220) 2021-05-08 00:51:49 +02:00
Weblate (bot)
5a9f0a44fd Translations update from Weblate (#222)
* Translated using Weblate (Japanese)

Currently translated at 100.0% (151 of 151 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ja/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 99.3% (150 of 151 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/zh_Hans/

Co-authored-by: Nyatsuki <Odamaki@yandex.ru>
Co-authored-by: Kevin Sicong Jiang <kev4313@yahoo.com>
2021-05-02 13:02:04 +02:00
Kevin Sicong Jiang
4a1858e47b Fix active tab lost on rotation (#221) 2021-05-01 23:41:19 +02:00
Sylvia van Os
d8d8a59707 Merge pull request #219 from weblate/weblate-catima-catima
Translations update from Weblate
2021-05-01 10:57:58 +02:00
Kevin Sicong Jiang
b027beea35 Added translation using Weblate (Chinese (Simplified)) 2021-05-01 10:35:08 +02:00
Sylvia van Os
b4d0651e99 Merge pull request #216 from weblate/weblate-catima-catima
Translations update from Weblate
2021-04-29 13:42:59 +02:00
Allan Nordhøy
b40380dff6 Translated using Weblate (Slovenian)
Currently translated at 41.7% (63 of 151 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/sl/
2021-04-29 13:32:10 +02:00
Allan Nordhøy
b1a0a98004 Translated using Weblate (Norwegian Bokmål)
Currently translated at 84.1% (127 of 151 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-04-29 13:32:10 +02:00
J. Lavoie
044d363f47 Translated using Weblate (Italian)
Currently translated at 100.0% (151 of 151 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-04-29 13:32:09 +02:00
J. Lavoie
35d659be31 Translated using Weblate (French)
Currently translated at 100.0% (151 of 151 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-04-29 13:32:09 +02:00
Adolfo Jayme Barrientos
8bc1e2d321 Translated using Weblate (Spanish)
Currently translated at 77.4% (117 of 151 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/es/
2021-04-29 13:32:09 +02:00
J. Lavoie
b8811ba053 Translated using Weblate (German)
Currently translated at 100.0% (151 of 151 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-04-29 13:32:08 +02:00
Sylvia van Os
cbaf172e9d Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-04-28 12:45:39 +02:00
Sylvia van Os
78c831cb68 Add Voucher Vault PDF 417 export support
a2911e0738
2021-04-28 12:45:02 +02:00
Sylvia van Os
279e775fb6 Merge pull request #212 from weblate/weblate-catima-catima
Translations update from Weblate
2021-04-27 09:12:53 +02:00
solokot
e5b30c9528 Translated using Weblate (Russian)
Currently translated at 100.0% (151 of 151 strings)

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

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-04-27 04:01:03 +02:00
Sylvia van Os
5feb59612d Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-04-26 20:58:20 +02:00
Sylvia van Os
09bd9b3882 Update CHANGELOG 2021-04-26 20:54:53 +02:00
Sylvia van Os
beb619000c Merge pull request #211 from weblate/weblate-catima-catima
Translations update from Weblate
2021-04-26 20:53:33 +02:00
Heimen Stoffels
72425dd39e Translated using Weblate (Dutch)
Currently translated at 100.0% (151 of 151 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-04-26 20:52:01 +02:00
Sylvia van Os
a93d240d9a Fix translation oops 2021-04-26 20:51:48 +02:00
Sylvia van Os
d380e284b1 Merge pull request #205 from TheLastProject/feature/multiselect
Support multi-selection
2021-04-26 20:18:06 +02:00
Sylvia van Os
cbbb434aae spotBugs 2021-04-26 20:12:05 +02:00
Sylvia van Os
6dc8490b5e Fix lint 2021-04-26 19:42:57 +02:00
Sylvia van Os
b2c57258b3 Fix unit tests 2021-04-26 19:23:22 +02:00
Sylvia van Os
fe0ae4049b Merge pull request #209 from weblate/weblate-catima-catima
Translations update from Weblate
2021-04-25 11:14:23 +02:00
Nyatsuki
0517a7514e Translated using Weblate (Japanese)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/ja/
2021-04-25 09:32:11 +02:00
Sylvia van Os
39544ac853 Really fix typo 2021-04-18 22:43:38 +02:00
Sylvia van Os
f46e1d09ba Fix typo 2021-04-18 22:42:41 +02:00
Sylvia van Os
6421f09eab Implement multi-copy and multi-share 2021-04-18 13:47:51 +02:00
Arshbeer Singh
45663065f9 WIP Issue #14 (and #65)
Implement Functionality to Copy Multiple Cards
2021-04-18 00:43:03 +02:00
Arshbeer Singh
67328724fa WIP Issue #14 (and #65)
Add Ability to Select Multiple Cards
Highlight Card on Long Press/Click
Replace ListView with RecyclerView for Extra Features and Functionality
Add Card Long Press Animations
Replace CursorAdapter with a combination of RecyclerViewAdapter and Cursor Adapter
2021-04-17 23:16:54 +02:00
Arshbeer Singh
55373e82a5 Fix Issue #65 2021-04-17 15:48:48 +02:00
Sylvia van Os
acdb5d6fe7 Merge pull request #203 from weblate/weblate-catima-catima
Translations update from Weblate
2021-04-17 13:30:03 +02:00
Nyatsuki
d9461c476a Translated using Weblate (Japanese)
Currently translated at 100.0% (147 of 147 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ja/
2021-04-17 13:27:05 +02:00
solokot
deee5f6aa2 Translated using Weblate (Russian)
Currently translated at 100.0% (147 of 147 strings)

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

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-04-17 13:27:02 +02:00
J. Lavoie
a87d8bbfe4 Translated using Weblate (Italian)
Currently translated at 100.0% (147 of 147 strings)

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

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

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-04-17 13:27:01 +02:00
Sylvia van Os
9469ae37e1 Merge pull request #202 from weblate/weblate-catima-catima
Translations update from Weblate
2021-04-16 12:02:17 +02:00
Nyatsuki
9e2d65b2cd Added translation using Weblate (Japanese) 2021-04-16 11:26:53 +02:00
Allan Nordhøy
d509c06815 App strings reworked 3 (#188) 2021-04-15 21:51:41 +02:00
Sylvia van Os
70faa7636a Merge pull request #201 from weblate/weblate-catima-catima
Translations update from Weblate
2021-04-15 21:30:42 +02:00
psa-jforestier
4072bc7607 Translated using Weblate (French)
Currently translated at 100.0% (147 of 147 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-04-15 21:26:59 +02:00
Sylvia van Os
eced502985 Merge pull request #200 from weblate/weblate-catima-catima
Translations update from Weblate
2021-04-14 16:06:52 +02:00
solokot
47441dbb9a Translated using Weblate (Russian)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/ru/
2021-04-14 15:27:05 +02:00
Allan Nordhøy
e7729d9763 README reworked (#191) 2021-04-12 22:11:21 +02:00
Sylvia van Os
df40b72f77 Fastlane fixes 2021-04-11 00:26:22 +02:00
Sylvia van Os
1a1c028565 Release 1.13 2021-04-10 13:48:13 +02:00
Sylvia van Os
5cd77c3a25 Export V2 test 2021-04-10 13:28:26 +02:00
Sylvia van Os
369631d00c Improve import/export test 2021-04-10 10:14:59 +02:00
Sylvia van Os
350031624c Add V2 import test 2021-04-09 23:01:38 +02:00
Sylvia van Os
846f4d4904 Test barcode type migration 2021-04-07 21:48:19 +02:00
Sylvia van Os
5081eb2dce Lighter grey for non-usable barcode types 2021-04-07 21:19:56 +02:00
Sylvia van Os
e964fda54a Fix tests and barcode ID import 2021-04-07 20:42:11 +02:00
Sylvia van Os
aaa4fc1ef3 Fix bad replace 2021-04-07 18:10:07 +02:00
Sylvia van Os
93331e1a27 Merge pull request #195 from weblate/weblate-catima-catima
Translations update from Weblate
2021-04-07 16:58:28 +02:00
Heimen Stoffels
3251a6266b Translated using Weblate (Dutch)
Currently translated at 100.0% (3 of 3 strings)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-01-18 20:32:13 +01:00
Sylvia van Os
1d1109d665 Fix crash in edit view 2021-01-18 19:45:45 +01:00
Sylvia van Os
c11e995684 Fix blurry barcode in edit view 2021-01-18 19:00:27 +01:00
Sylvia van Os
184af4d272 Fix wrong string on expired 2021-01-17 23:38:07 +01:00
Sylvia van Os
d7356704ea Make spotBugs happy 2021-01-17 23:31:05 +01:00
Sylvia van Os
66573fd2da Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-01-17 22:56:14 +01:00
Sylvia van Os
36ff2333b7 Fix all unit tests 2021-01-17 22:54:24 +01:00
Sylvia van Os
9325f33da6 Merge pull request #138 from weblate/weblate-catima-catima
Translations update from Weblate
2021-01-17 18:35:28 +01:00
Sylvia van Os
9e40d9b6b3 Fix incorrect escaping 2021-01-17 18:34:55 +01:00
Sylvia van Os
4679051df1 Fix incorrect escaping 2021-01-17 18:34:13 +01:00
Heimen Stoffels
27cbad1b44 Translated using Weblate (Dutch)
Currently translated at 100.0% (119 of 119 strings)

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

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

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

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

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ko/
2021-01-13 09:32:08 +01:00
523 changed files with 22777 additions and 5383 deletions

11
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "gradle" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"

View File

@@ -2,9 +2,13 @@ name: Android CI
on:
push:
branches: [ master ]
branches:
- master
- staging
- trying
pull_request:
branches: [ master ]
branches:
- master
jobs:
build:
@@ -13,21 +17,25 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: set up JDK 1.8
uses: actions/setup-java@v1
- name: Fail on bad translations
run: if grep -ri "&lt;xliff" app/src/main/res/values*/strings.xml; then echo "Invalidly escaped translations found"; exit 1; fi
- uses: gradle/wrapper-validation-action@v1
- name: set up JDK 11
uses: actions/setup-java@v2
with:
java-version: 1.8
distribution: 'adopt'
java-version: '11'
- name: Build
run: ./gradlew assembleRelease
- name: Check lint
run: ./gradlew lintRelease
- name: Run unit tests
run: ./gradlew testReleaseUnitTest
run: ./gradlew testReleaseUnitTest || ./gradlew testReleaseUnitTest
- name: SpotBugs
run: ./gradlew spotbugsRelease
- name: Archive test results
if: always()
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v2
with:
name: test-results
path: app/build/reports

View File

@@ -0,0 +1,23 @@
name: 'Close issues and PRs needing info for too long'
on:
schedule:
- cron: '30 1 * * *'
permissions:
issues: write
pull-requests: write
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v4
with:
days-before-stale: -1
days-before-close: 90
close-issue-message: 'This issue is missing necessary information and cannot be worked on in its current state. It has therefore been closed to keep the issue tracker clean. If you have more information, feel free to reopen it.'
close-pr-message: 'This PR is missing necessary information and cannot be merged in its current state. It has therefore been closed to keep the issue tracker clean. If you have more information, feel free to reopen it.'
only-labels: 'needs info'
stale-issue-label: 'needs info'
stale-pr-label: 'needs info'
remove-stale-when-updated: false

View File

@@ -10,20 +10,23 @@ on:
- '**.webp'
jobs:
build:
# Only run on Pull Requests within the same repository, and not from forks.
if: github.event.pull_request.head.repo.full_name == github.repository
name: calibreapp/image-actions
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@master
uses: actions/checkout@v2
- name: Compress Images
id: calibre
uses: calibreapp/image-actions@master
uses: calibreapp/image-actions@1.1.0
with:
githubToken: ${{ secrets.GITHUB_TOKEN }}
ignorePaths: 'app/src/test'
compressOnly: true
- name: Create New Pull Request If Needed
if: steps.calibre.outputs.markdown != ''
uses: peter-evans/create-pull-request@master
uses: peter-evans/create-pull-request@v3
with:
title: Compressed Images
branch-suffix: timestamp

View File

@@ -0,0 +1,26 @@
name: Convert CHANGELOG to Fastlane
on:
push:
branches:
- master
jobs:
convert_changelog_to_fastlane:
runs-on: ubuntu-latest
name: Convert CHANGELOG to Fastlane
steps:
- name: Checkout repo
id: checkout
uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Run converter script
run: python .scripts/changelog_to_fastlane.py
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3
with:
title: "Update Fastlane changelogs"
commit-message: "Update Fastlane changelogs"
branch-suffix: timestamp

73
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,73 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches:
- master
pull_request:
# The branches below must be a subset of the branches above
branches:
- master
schedule:
- cron: '33 1 * * 4'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'java' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@@ -0,0 +1,25 @@
name: Write contributors to file
on:
schedule:
- cron: '3 4 * * 0'
jobs:
contributors_to_file:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master'
name: Write contributors to file
steps:
- name: Checkout repo
id: checkout
uses: actions/checkout@v2
- name: Update contributors
id: update_contributors
uses: TheLastProject/contributors-to-file-action@v2
with:
file_in_repo: app/src/main/res/raw/contributors.txt
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3
with:
title: "Update contributors"
commit-message: "Update contributors"
branch-suffix: timestamp

1
.gitignore vendored
View File

@@ -7,3 +7,4 @@ build/
captures/
**/release
**/debug
app/*.log

View File

@@ -0,0 +1,33 @@
#!/usr/bin/python3
import os
import re
changelogs = {}
with open('CHANGELOG.md') as changelog:
version_code = None
text = []
for line in changelog:
if line.startswith("## "):
if version_code != None:
changelogs[version_code] = text
text = []
match = re.match("## \S* - (\d*).*", line)
if not match:
raise ValueError(f"Invalid version line: {line}")
version_code = match.group(1)
elif line:
# Turn Markdown [links](to_url) into links (to_url)
text.append(re.sub(r'\[(.*?)\]\((.*?)\)', r'\1 (\2)', line))
for version, description in changelogs.items():
description = "".join(description).strip()
if not description:
continue
with open(os.path.join("fastlane", "metadata", "android", "en-US", "changelogs", f"{version}.txt"), "w") as fastlane_file:
fastlane_file.write(description)

View File

@@ -1,103 +1,299 @@
# Changelog
## v1.6.2 (2021-01-04)
## v2.11.2 - 95 (2021-12-04)
Changes:
- Fix crash on sharing card
## v2.11.1 - 94 (2021-11-30)
- Fix blurriness of main screen letter icons
- Fix icons sometimes disappearing after selection
- Fix status bar icons possibly being invisible on Android 5
## v2.11.0 - 93 (2021-11-28)
- Add Catima to [Quick Access Device Controls](https://developer.android.com/guide/topics/ui/device-control)
- Fix some groups not showing up correctly in group management screen
## v2.10.0 - 92 (2021-11-20)
- New main screen layout
- Fix bottomsheet sizing issues when switching in and out of fullscreen
## v2.9.0 - 91 (2021-11-14)
- Improved group management support
- Support cropping images
- Fix image data loss when saving after rotating in edit view
- Ability to set a custom image as card icon
## v2.8.1 - 90 (2021-10-27)
- Fix dots in card view having the wrong colour when changing theme manually
- Fix crash in card view on rotation/theme change
- Fix flashing of cards list
- Fix text overlaying star icon
## v2.8.0 - 89 (2021-10-25)
- Fix swiping between groups not working on an empty group
- Allow password-protecting exports
- Improve usage of space for QR codes
- Save the last used zoom level per card
- Fix a crash when swiping right after a tap
## v2.7.3 - 88 (2021-10-10)
- Fix incorrect migration making first card become invisible
## v2.7.2 - 87 (2021-10-09)
- Fix regression breaking import/export
## v2.7.1 - 86 (2021-10-07)
- Improve search with spaces
## v2.7.0 - 85 (2021-10-05)
Android 4.4 is no longer supported starting with this release. If you want to use Catima on Android 4.4, please use version 2.6.1.
- Improved Android 12 support
- Improved about screen
- Search now ignores accents
## v2.6.1 - 84 (2021-09-25)
- Minor bugfixes and improvements
## v2.6.0 - 83 (2021-09-19)
- Support for changing the sorting order
- Prevent Out Of Memory on scanning large pictures for barcode
## v2.5.0 - 82 (2021-09-10)
- Improved support for screen readers
- Don't crash when trying to open a video from gallery
- Swipe support on loyalty card view screen
- Don't reset group on back button press
## v2.4.0 - 81 (2021-08-29)
- Improve card list for landscape and tablet display
- Add theming colour support (thanks, Subhashish Anand!)
- Don't close scan activity on camera error (so manual entry is still possible)
- Add all contributors to the about dialog
## v2.3.0 - 80 (2021-08-19)
- Fix images not imported from backup
- Option to override language
## v2.2.3 - 79 (2021-08-13)
- Fix widget creating different-looking shortcut than app shortcuts
- Replace default Android black screen with splash screen
## v2.2.2 - 78 (2021-08-08)
- Fix crash on rotation in loyalty card edit activity
## v2.2.1 - 77 (2021-08-07)
- Improve Stocard importer
- Fix importing Catima export with multiline note
- Scale card title in acceptable range
- Animation improvements
## v2.2.0 - 76 (2021-08-02)
- Make links in notes clickable
- Pre-select group the user is currently in when creating a new card
- Comma-separate group names in loyalty card view
- Fix maximize button appearing on no barcode
## v2.1.0 - 75 (2021-08-01)
- Fix selected colour in colour changing dialog
- Support for deleting multiple cards at once
- Fix possible ArithmeticException when resizing image
- Fix fullscreen is closed when rotating device
## v2.0.4 - 74 (2021-07-27)
- Fix shortcut creation
- Generate card-specific shortcut icon
- Fix ability to change loyalty card colour
## v2.0.3 - 73 (2021-07-25)
- Fix loading photos when editing existing card
## v2.0.2 - 72 (2021-07-25)
- Fix inability to configure photos in new loyalty card
## v2.0.1 - 71 (2021-07-21)
- Several minor translation and UI fixes
- Fix crash in import/sharing loyalty card on Android 6
## v2.0 - 70 (2021-07-14)
- BREAKING CHANGE: The backup format changed, see https://github.com/TheLastProject/Catima/wiki/Export-format
- BREAKING CHANGE: The URL sharing format changed, see https://github.com/TheLastProject/Catima/wiki/Card-sharing-URL-format
- Make it possible to enable or disable the flashlight while scanning
- Add UPC-E support
- Support adding a front and back photo to each card
- Support importing password-protected zip files
- Support importing from Stocard (Beta)
- Fix useless whitespace in notes from Fidme import
- Support new Voucher Vault export format
- Fix Floating Action Buttons being behind other UI elements on Android 4
- Fix loyalty card viewer appbar top margin
## v1.14.1 - 69 (2021-06-14)
- Add missing barcode ID to export
- Don't show update barcode dialog if value is the same as card ID
- Add Finnish translation
## v1.14 - 68 (2021-06-07)
- Support new PDF417 export from Voucher Vault
- Support copying multiple barcodes at once
- Support sharing multiple loyalty cards at once
- Ask to update barcode value if card ID changes
## v1.13 - 67 (2021-04-10)
- Add option to set a separate barcode value from card ID
- Simplify font sizing configuration
- Several small UI fixes
- Use letter icon for shortcuts too
- Always show all barcode types in manual entry
- Remove privacy policy first start dialog
## v1.12 - 66 (2021-03-30)
- Support importing [Fidme](https://play.google.com/store/apps/details?id=fr.snapp.fidme) exports
- Allow importing a card from a picture stored in the user's Android gallery
- Fix multiline note cutoff
- Change "Thank you" text on privacy dialog to "Accept" because Huawei is overly pedantic
## v1.11 - 64 (2021-03-21)
- Add privacy policy dialog on first start (required by Huawei)
## v1.10 - 63 (2021-03-07)
- Support importing [Voucher Vault](https://github.com/tim-smart/vouchervault/) exports
- Option to keep the screen on while viewing a loyalty card
- Option to suspend the lock screen while viewing a loyalty card
## v1.9.2 - 62 (2021-02-24)
- Fix parsing balance for countries using space as separator
## v1.9.1 - 61 (2021-02-23)
- Improve balance parsing logic
- Fix currency decimal display on main screen
## v1.9 - 59 (2021-02-22)
- Add balance support
- Reorganize barcode tab of edit view
## v1.8.1 - 58 (2021-02-12)
- Fix Crash on versions before Android 7
## v1.8 - 57 (2021-01-28)
- Add support for scaling the barcode when moving to top to fit even more small scanners
- Fix bottom sheet jumping after switching to fullscreen
- Make header in loyalty card view small in landscape mode
- Fix cards not staying in group when group gets renamed
## v1.7.1 - 56 (2021-01-18)
- Fix crash on switching to barcode tab in edit view if there is no barcode
## v1.7.0 - 55 (2021-01-18)
- Separate edit UI in tabs to make it feel more spacious
- Add expiry field support
## v1.6.2 - 54 (2021-01-04)
- Fix edit button or more info bottom sheet drawing over barcode ID
## v1.6.1 (2020-12-16)
Changes:
## v1.6.1 - 64 (2020-12-16)
- Fix regression causing manual barcode entry to not be saved
## v1.6.0 (2020-12-15)
Changes:
## v1.6.0 - 52 (2020-12-15)
- 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:
## v1.5.1 - 51 (2020-12-03)
- Fix bottomsheet background being transparent
## v1.5.0 (2020-12-03)
Changes:
## v1.5.0 - 50 (2020-12-03)
- Improve contrast by always using white text on red buttons
- Draggable bottom sheet in loyalty card view
## v1.4.1 (2020-12-01)
Changes:
## v1.4.1 - 49 (2020-12-01)
- Improved translations
- Small UI fixes
## v1.4.0 (2020-11-28)
Changes:
## v1.4.0 - 48 (2020-11-28)
- 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:
## v1.3.0 - 47 (2020-11-22)
- 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:
## v1.2.2 - 46 (2020-11-19)
- Remember active group tab between screens and sessions
## v1.2.1 (2020-11-17)
Changes:
## v1.2.1 - 45 (2020-11-17)
- Fix home screen swiping triggering during vertical swipes too
## v1.2.0 (2020-11-17)
Changes:
## v1.2.0 - 44 (2020-11-17)
- Add swiping between groups on the home screen
- Fix crash with cards lacking header colour
## v1.1.0 (2020-11-11)
Changes:
## v1.1.0 - 43 (2020-11-11)
- Improved edit UI
- Removed header text colour option (now automatically generated based on brightness)
- Updated translations
## v1.0.1 (2020-11-07)
Changes:
## v1.0.1 - 42 (2020-11-07)
- Fix crash in search with no groups
## v1.0 (2020-11-06)
Changes:
## v1.0 - 41 (2020-11-06)
- Added rounded edges to card icons on main overview
- Added support for grouping entries
## v0.29 (2020-10-29)
Changes:
## v0.29 - 40 (2020-10-29)
- Rebrand to Catima
- Removed intro
@@ -106,31 +302,23 @@ Changes:
- Add favourites support
- Fix disabled auto-rotate being ignored
## v0.28 (2020-03-09)
Changes:
## v0.28 - 39 (2020-03-09)
- 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:
## v0.27 - 38 (2020-01-26)
- 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:
## v0.26.1 - 37 (2020-01-09)
- Fix issue with sharing cards without background color ([#343](https://github.com/brarcher/loyalty-card-locker/pull/343))
## v0.26 (2020-01-05)
Changes:
## v0.26 - 36 (2020-01-05)
- 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))
@@ -147,57 +335,41 @@ Changes:
- Polish
- Russian
## v0.25.4 (2019-10-04)
Changes:
## v0.25.4 - 35 (2019-10-04)
- Enable app backups
- Update French and Slovenian translations
## v0.25.3 (2019-03-02)
Changes:
## v0.25.3 - 34 (2019-03-02)
- Update Russian translations
## v0.25.2 (2019-01-05)
Changes:
## v0.25.2 - 33 (2019-01-05)
- Update and add translations
## v0.25.1 (2018-10-14)
Changes:
## v0.25.1 - 32 (2018-10-14)
- 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:
## v0.25 - 31 (2018-10-07)
- 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:
## v0.24 - 30 (2018-07-31)
- 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:
## v0.23.4 - 29 (2018-05-12)
- 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:
## v0.23.3 - 28 (2018-05-05)
- Added translations
- Polish ([pull #232](https://github.com/brarcher/loyalty-card-locker/pull/232))
@@ -205,87 +377,63 @@ Changes:
- 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:
## v0.23.2 - 27 (2018-03-11)
- 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:
## v0.23.1 - 26 (2018-03-07)
- 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:
## v0.23 - 25 (2018-02-28)
- 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:
## v0.22 - 24 (2018-02-19)
- 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:
## v0.21 - 23 (2018-02-17)
- 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:
## v0.20 - 22 (2018-02-10)
- 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:
## v0.19 - 21 (2018-02-01)
- 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:
## v0.18.1 - 20 (2018-01-24)
- 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:
## v0.18 - 19 (2018-01-19)
- 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:
## v0.17 - 18 (2018-01-11)
- 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:
## v0.16 - 17 (2017-11-29)
- 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:
## v0.15 - 16 (2017-11-25)
- 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))
@@ -293,37 +441,27 @@ Changes:
- 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:
## v0.14 - 15 (2017-10-26)
- 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:
## v0.13 - 14 (2017-07-25)
- 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:
## v0.12 - 13 (2017-07-16)
- 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:
## v0.11.1 - 12 (2017-06-29)
- Prevent a crash when rotation the screen in the first run intro wizard.
## v0.11 (2017-06-26)
Improvements:
## v0.11 - 11 (2017-06-26)
- 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))
@@ -331,15 +469,13 @@ Improvements:
- 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:
## v0.10 - 10 (2017-02-12)
- 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)
## v0.9 - 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".
@@ -349,55 +485,36 @@ Additional features/improvements:
- 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:
## v0.8 - 8 (2016-11-22)
- 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:
## v0.7 - 7 (2016-07-14)
- 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:
## v0.6 - 6 (2016-05-23)
- 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:
## v0.5 - 5 (2016-05-16)
- 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:
## v0.4 - 4 (2016-04-09)
- 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)
## v0.3 - 3 (2016-02-11)
- Now officially supports the following list of 1D and 2D barcodes:
- AZTEC
@@ -414,13 +531,13 @@ Bug fixes:
- Generated barcodes are larger, easier to scan from a scanning device
## v0.2 (2016-02-07)
## v0.2 - 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)
## v0.1 - 1 (2016-01-30)
- Ability to create/edit/delete loyalty cards
- Capture barcode of loyalty card using a camera

View File

@@ -1,9 +1,9 @@
How to Submit Patches to the Loyalty Card Keychain Project
How to Submit Patches to the Catima Project
===============================================================================
https://github.com/brarcher/budget-watch
https://github.com/TheLastProject/Catima
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
Catima 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.
@@ -21,16 +21,16 @@ These are the Android lint checker, run using:
# ./gradlew lintRelease
and FindBugs, run using:
and SpotBugs, run using:
# ./gradlew findbugs
# ./gradlew spotbugsRelease
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 Catima 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.
@@ -85,7 +85,7 @@ your real name, saying:
## 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.
and potentially merged into the main Catima repository. The preferred
way to do this is to submit a Pull Request to the Catima project.
Changes need to apply cleanly onto the master branch and pass all
unit tests and produce no errors during static analysis.

View File

@@ -1,58 +1,75 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.2)
addressable (2.7.0)
CFPropertyList (3.0.3)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
artifactory (3.0.15)
atomos (0.1.3)
aws-eventstream (1.1.0)
aws-partitions (1.388.0)
aws-sdk-core (3.109.1)
aws-eventstream (1.2.0)
aws-partitions (1.501.0)
aws-sdk-core (3.121.0)
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-sdk-kms (1.48.0)
aws-sdk-core (~> 3, >= 3.120.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.83.1)
aws-sdk-core (~> 3, >= 3.109.0)
aws-sdk-s3 (1.102.0)
aws-sdk-core (~> 3, >= 3.120.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.1)
aws-sigv4 (1.2.2)
aws-sigv4 (~> 1.4)
aws-sigv4 (1.4.0)
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)
commander (4.6.0)
highline (~> 2.0.0)
declarative (0.0.20)
declarative-option (0.1.0)
digest-crc (0.6.1)
rake (~> 13.0)
digest-crc (0.6.4)
rake (>= 12.0.0, < 14.0.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)
emoji_regex (3.2.2)
excon (0.85.0)
faraday (1.7.2)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
faraday-httpclient (~> 1.0.1)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.1)
faraday-patron (~> 1.0)
faraday-rack (~> 1.0)
multipart-post (>= 1.2, < 3)
ruby2_keywords
ruby2_keywords (>= 0.0.4)
faraday-cookie_jar (0.0.7)
faraday (>= 0.8.0)
http-cookie (~> 1.0.0)
faraday_middleware (1.0.0)
faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-net_http (1.0.1)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
faraday_middleware (1.1.0)
faraday (~> 1.0)
fastimage (2.2.0)
fastlane (2.165.0)
fastimage (2.2.5)
fastlane (2.193.1)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.3, < 3.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.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)
commander (~> 4.6)
dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 4.0)
excon (>= 0.71.0, < 1.0.0)
@@ -61,18 +78,20 @@ GEM
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)
google-apis-androidpublisher_v3 (~> 0.3)
google-apis-playcustomapp_v1 (~> 0.1)
google-cloud-storage (~> 1.31)
highline (~> 2.0)
json (< 3.0.0)
jwt (>= 2.1.0, < 3)
mini_magick (>= 4.9.4, < 5.0.0)
multipart-post (~> 2.0.0)
naturally (~> 2.2)
optparse (~> 0.1.1)
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)
@@ -82,73 +101,85 @@ GEM
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
gh_inspector (1.1.3)
google-api-client (0.38.0)
google-apis-androidpublisher_v3 (0.11.0)
google-apis-core (>= 0.4, < 2.a)
google-apis-core (0.4.1)
addressable (~> 2.5, >= 2.5.1)
googleauth (~> 0.9)
httpclient (>= 2.8.1, < 3.0)
googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a)
mini_mime (~> 1.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
signet (~> 0.12)
google-cloud-core (1.5.0)
retriable (>= 2.0, < 4.a)
rexml
webrick
google-apis-iamcredentials_v1 (0.7.0)
google-apis-core (>= 0.4, < 2.a)
google-apis-playcustomapp_v1 (0.5.0)
google-apis-core (>= 0.4, < 2.a)
google-apis-storage_v1 (0.6.0)
google-apis-core (>= 0.4, < 2.a)
google-cloud-core (1.6.0)
google-cloud-env (~> 1.0)
google-cloud-errors (~> 1.0)
google-cloud-env (1.4.0)
google-cloud-env (1.5.0)
faraday (>= 0.17.3, < 2.0)
google-cloud-errors (1.0.1)
google-cloud-storage (1.29.1)
google-cloud-errors (1.1.0)
google-cloud-storage (1.34.1)
addressable (~> 2.5)
digest-crc (~> 0.4)
google-api-client (~> 0.33)
google-cloud-core (~> 1.2)
googleauth (~> 0.9)
google-apis-iamcredentials_v1 (~> 0.1)
google-apis-storage_v1 (~> 0.1)
google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
googleauth (0.14.0)
googleauth (0.17.1)
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)
signet (~> 0.15)
highline (2.0.3)
http-cookie (1.0.4)
domain_name (~> 0.5)
httpclient (2.8.3)
jmespath (1.4.0)
json (2.3.1)
jwt (2.2.2)
json (2.5.1)
jwt (2.2.3)
memoist (0.16.2)
mini_magick (4.10.1)
mini_mime (1.0.2)
mini_magick (4.11.0)
mini_mime (1.1.1)
multi_json (1.15.0)
multipart-post (2.0.0)
nanaimo (0.3.0)
naturally (2.2.0)
naturally (2.2.1)
optparse (0.1.1)
os (1.1.1)
plist (3.5.0)
plist (3.6.0)
public_suffix (4.0.6)
rake (13.0.1)
representable (3.0.4)
rake (13.0.6)
representable (3.1.1)
declarative (< 0.1.0)
declarative-option (< 0.2.0)
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rexml (3.2.5)
rouge (2.0.7)
ruby2_keywords (0.0.2)
rubyzip (2.3.0)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
security (0.1.3)
signet (0.14.0)
addressable (~> 2.3)
signet (0.16.0)
addressable (~> 2.8)
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)
trailblazer-option (0.1.1)
tty-cursor (0.7.1)
tty-screen (0.8.1)
tty-spinner (0.9.3)
@@ -156,18 +187,20 @@ GEM
uber (0.1.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.7)
unf_ext (0.0.8)
unicode-display_width (1.7.0)
webrick (1.7.0)
word_wrap (1.0.0)
xcodeproj (1.19.0)
xcodeproj (1.21.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.3.0)
rexml (~> 3.2.4)
xcpretty (0.3.0)
rouge (~> 2.0.7)
xcpretty-travis-formatter (1.0.0)
xcpretty-travis-formatter (1.0.1)
xcpretty (~> 0.2, >= 0.0.7)
PLATFORMS

View File

@@ -11,17 +11,18 @@ spotbugs {
}
android {
compileSdkVersion 29
buildToolsVersion "30.0.3"
compileSdkVersion 31
buildToolsVersion "31.0.0"
defaultConfig {
applicationId "me.hackerchick.catima"
minSdkVersion 19
targetSdkVersion 29
versionCode 54
versionName "1.6.2"
minSdkVersion 21
targetSdkVersion 31
versionCode 95
versionName "2.11.2"
vectorDrawables.useSupportLibrary true
multiDexEnabled true
}
buildTypes {
@@ -36,10 +37,20 @@ android {
}
}
bundle {
language {
enableSplit = false
}
}
compileOptions {
encoding "UTF-8"
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
// Flag to enable support for the new language APIs
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
lintOptions {
@@ -47,10 +58,21 @@ android {
"MissingTranslation", "MissingPrefix"
}
sourceSets {
test {
resources.srcDirs += ['src/test/res']
}
}
// Starting with Android Studio 3 Robolectric is unable to find resources.
// The following allows it to find the resources.
testOptions {
unitTests {
all {
testLogging {
events 'started', 'passed', 'skipped', 'failed'
}
}
includeAndroidResources true
}
}
@@ -58,23 +80,33 @@ android {
dependencies {
// AndroidX
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.appcompat:appcompat:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
implementation 'androidx.exifinterface:exifinterface:1.3.3'
implementation 'androidx.palette:palette:1.0.0'
implementation 'androidx.preference:preference:1.1.1'
implementation 'com.google.android.material:material:1.2.1'
implementation 'com.google.android.material:material:1.4.0'
implementation 'com.github.yalantis:ucrop:2.2.7'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
// Splash Screen
implementation 'androidx.core:core-splashscreen:1.0.0-alpha02'
// Third-party
implementation 'com.journeyapps:zxing-android-embedded:4.1.0@aar'
implementation 'com.journeyapps:zxing-android-embedded:4.3.0@aar'
implementation 'com.google.zxing:core:3.4.1'
implementation 'org.apache.commons:commons-csv:1.8'
implementation 'org.apache.commons:commons-csv:1.9.0'
implementation 'com.jaredrummler:colorpicker:1.1.0'
implementation 'com.google.guava:guava:30.1-jre'
implementation 'com.github.invissvenska:NumberPickerPreference:1.0.1'
implementation 'com.github.invissvenska:NumberPickerPreference:1.0.4'
implementation 'net.lingala.zip4j:zip4j:2.9.1'
// SpotBugs
implementation 'io.wcm.tooling.spotbugs:io.wcm.tooling.spotbugs.annotations:1.0.0'
// Testing
testImplementation 'junit:junit:4.13.1'
testImplementation 'org.robolectric:robolectric:4.0.2'
testImplementation 'androidx.test:core:1.4.0'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.robolectric:robolectric:4.7.3'
}
tasks.withType(SpotBugsTask) {

View File

@@ -1,14 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="protect.card_locker"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="protect.card_locker">
<uses-permission
android:name="android.permission.CAMERA"/>
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-sdk tools:overrideLibrary="com.google.zxing.client.android" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-feature
android:name="android.hardware.camera"
@@ -17,8 +16,6 @@
android:name="android.hardware.camera.autofocus"
android:required="false" />
<uses-sdk tools:overrideLibrary="com.google.zxing.client.android" />
<application
android:name=".LoyaltyCardLockerApplication"
android:allowBackup="true"
@@ -27,83 +24,117 @@
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name="protect.card_locker.MainActivity"
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
android:theme="@style/Theme.App.Starting">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER"/>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".AboutActivity"
android:label="@string/about"
android:theme="@style/AppTheme.NoActionBar">
</activity>
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".ManageGroupsActivity"
android:label="@string/groups"
android:theme="@style/AppTheme.NoActionBar">
</activity>
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".ManageGroupActivity"
android:label="@string/group_edit"
android:theme="@style/AppTheme.NoActionBar"/>
<activity
android:name=".LoyaltyCardViewActivity"
android:exported="true"
android:theme="@style/AppTheme.NoActionBar"
android:label=""
android:windowSoftInputMode="stateHidden"
android:exported="true"/>
android:windowSoftInputMode="stateHidden" />
<activity
android:name=".LoyaltyCardEditActivity"
android:exported="true"
android:theme="@style/AppTheme.NoActionBar"
android:windowSoftInputMode="stateHidden"
android:exported="true">
<intent-filter android:label="@string/app_name">
android:windowSoftInputMode="stateHidden">
<intent-filter
android:autoVerify="true"
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" />
<!-- Main card sharing URIs -->
<data android:scheme="http" />
<data android:scheme="https" />
<data
android:host="@string/intent_import_card_from_url_host_catima_app"
android:pathPrefix="@string/intent_import_card_from_url_path_prefix_catima_app" />
</intent-filter>
<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" />
<!-- Old card sharing URIs -->
<data android:scheme="http" />
<data android:scheme="https" />
<data
android:host="@string/intent_import_card_from_url_host_thelastproject"
android:pathPrefix="@string/intent_import_card_from_url_path_prefix_thelastproject" />
<data
android:host="@string/intent_import_card_from_url_host_brarcher"
android:pathPrefix="@string/intent_import_card_from_url_path_prefix_brarcher" />
</intent-filter>
</activity>
<activity
android:name=".ScanActivity"
android:label="@string/scanCardBarcode"
android:theme="@style/AppTheme.NoActionBar"/>
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".BarcodeSelectorActivity"
android:label="@string/selectBarcodeTitle"
android:theme="@style/AppTheme.NoActionBar"
android:windowSoftInputMode="stateHidden"/>
android:windowSoftInputMode="stateHidden" />
<activity
android:name=".preferences.SettingsActivity"
android:label="@string/settings"/>
android:label="@string/settings"
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".ImportExportActivity"
android:label="@string/importExport"
android:theme="@style/AppTheme.NoActionBar"/>
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".CardShortcutConfigure"
android:exported="true"
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"/>
<action android:name="android.intent.action.CREATE_SHORTCUT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name="com.yalantis.ucrop.UCropActivity"
android:theme="@style/AppTheme.NoActionBar" />
<provider
android:name="androidx.core.content.FileProvider"
android:grantUriPermissions="true"
android:authorities="${applicationId}"
android:exported="false"
android:authorities="${applicationId}">
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider_paths"/>
android:resource="@xml/file_provider_paths" />
</provider>
<service android:name=".CardsOnPowerScreenService" android:label="@string/app_name"
android:permission="android.permission.BIND_CONTROLS" android:exported="true">
<intent-filter>
<action android:name="android.service.controls.ControlsProviderService" />
</intent-filter>
</service>
</application>
</manifest>
</manifest>

View File

@@ -1,111 +1,172 @@
package protect.card_locker;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import com.google.common.collect.ImmutableMap;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Map;
import java.util.List;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.Toolbar;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.text.HtmlCompat;
public class AboutActivity extends AppCompatActivity
{
public class AboutActivity extends CatimaAppCompatActivity implements View.OnClickListener {
private static final String TAG = "Catima";
ConstraintLayout version_history, translate, license, repo, privacy, error, credits, rate;
@Override
protected void onCreate(Bundle savedInstanceState)
{
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle(R.string.about);
setContentView(R.layout.about_activity);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
{
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
final Map<String, String> USED_LIBRARIES = new ImmutableMap.Builder<String, String>()
.put("Commons CSV", "https://commons.apache.org/proper/commons-csv/")
.put("Guava", "https://github.com/google/guava")
.put("ZXing", "https://github.com/zxing/zxing")
.put("ZXing Android Embedded", "https://github.com/journeyapps/zxing-android-embedded")
.put("Color Picker", "https://github.com/jaredrummler/ColorPicker")
.put("NumberPickerPreference", "https://github.com/invissvenska/NumberPickerPreference")
.build();
StringBuilder contributors = new StringBuilder().append("<br/>");
final Map<String, String> USED_ASSETS = ImmutableMap.of
(
"Android icons", "https://www.apache.org/licenses/LICENSE-2.0.txt"
);
BufferedReader reader = new BufferedReader(new InputStreamReader(getResources().openRawResource(R.raw.contributors), StandardCharsets.UTF_8));
try {
while (true) {
String tmp = reader.readLine();
if (tmp == null || tmp.isEmpty()) {
reader.close();
break;
}
contributors.append("<br/>");
contributors.append(tmp);
}
} catch (IOException ignored) {
}
final List<ThirdPartyInfo> USED_LIBRARIES = new ArrayList<>();
USED_LIBRARIES.add(new ThirdPartyInfo("Color Picker", "https://github.com/jaredrummler/ColorPicker", "Apache 2.0"));
USED_LIBRARIES.add(new ThirdPartyInfo("Commons CSV", "https://commons.apache.org/proper/commons-csv/", "Apache 2.0"));
USED_LIBRARIES.add(new ThirdPartyInfo("NumberPickerPreference", "https://github.com/invissvenska/NumberPickerPreference", "GNU LGPL 3.0"));
USED_LIBRARIES.add(new ThirdPartyInfo("Zip4j", "https://github.com/srikanth-lingala/zip4j", "Apache 2.0"));
USED_LIBRARIES.add(new ThirdPartyInfo("ZXing", "https://github.com/zxing/zxing", "Apache 2.0"));
USED_LIBRARIES.add(new ThirdPartyInfo("ZXing Android Embedded", "https://github.com/journeyapps/zxing-android-embedded", "Apache 2.0"));
final List<ThirdPartyInfo> USED_ASSETS = new ArrayList<>();
USED_ASSETS.add(new ThirdPartyInfo("Android icons", "https://fonts.google.com/icons?selected=Material+Icons", "Apache 2.0"));
StringBuilder libs = new StringBuilder().append("<br/>");
for (Map.Entry<String, String> entry : USED_LIBRARIES.entrySet())
{
libs.append("<br/><a href=\"").append(entry.getValue()).append("\">").append(entry.getKey()).append("</a><br/>");
for (ThirdPartyInfo entry : USED_LIBRARIES) {
libs.append("<br/><a href=\"").append(entry.url()).append("\">").append(entry.name()).append("</a> (").append(entry.license()).append(")");
}
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/>");
for (ThirdPartyInfo entry : USED_ASSETS) {
resources.append("<br/><a href=\"").append(entry.url()).append("\">").append(entry.name()).append("</a> (").append(entry.license()).append(")");
}
String appName = getString(R.string.app_name);
int year = Calendar.getInstance().get(Calendar.YEAR);
String version = "?";
try
{
try {
PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), 0);
version = pi.versionName;
}
catch (PackageManager.NameNotFoundException e)
{
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Package name not found", e);
}
TextView copyright = findViewById(R.id.credits_sub);
copyright.setText(String.format(getString(R.string.app_copyright_fmt), year));
TextView vHistory = findViewById(R.id.version_history_sub);
vHistory.setText(String.format(getString(R.string.debug_version_fmt), version));
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());
version_history = findViewById(R.id.version_history);
translate = findViewById(R.id.translate);
license = findViewById(R.id.license);
repo = findViewById(R.id.repo);
privacy = findViewById(R.id.privacy);
error = findViewById(R.id.report_error);
credits = findViewById(R.id.credits);
rate = findViewById(R.id.rate);
version_history.setOnClickListener(this);
translate.setOnClickListener(this);
license.setOnClickListener(this);
repo.setOnClickListener(this);
privacy.setOnClickListener(this);
error.setOnClickListener(this);
rate.setOnClickListener(this);
StringBuilder contributorInfo = new StringBuilder();
contributorInfo.append(HtmlCompat.fromHtml(String.format(getString(R.string.app_contributors), contributors.toString()), HtmlCompat.FROM_HTML_MODE_COMPACT));
contributorInfo.append("\n\n");
contributorInfo.append(getString(R.string.app_copyright_old));
contributorInfo.append("\n\n");
contributorInfo.append(HtmlCompat.fromHtml(String.format(getString(R.string.app_libraries), libs.toString()), HtmlCompat.FROM_HTML_MODE_COMPACT));
contributorInfo.append("\n\n");
contributorInfo.append(HtmlCompat.fromHtml(String.format(getString(R.string.app_resources), resources.toString()), HtmlCompat.FROM_HTML_MODE_COMPACT));
credits.setOnClickListener(view -> new AlertDialog.Builder(this)
.setTitle(R.string.credits)
.setMessage(contributorInfo.toString())
.setPositiveButton(R.string.ok, (dialogInterface, i) -> {
})
.show());
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
finish();
}
return super.onOptionsItemSelected(item);
}
}
@Override
public void onClick(View view) {
int id = view.getId();
String url;
if (id == R.id.version_history) {
url = "https://catima.app/changelog/";
} else if (id == R.id.translate) {
url = "https://hosted.weblate.org/engage/catima/";
} else if (id == R.id.license) {
url = "https://github.com/TheLastProject/Catima/blob/master/LICENSE";
} else if (id == R.id.repo) {
url = "https://github.com/TheLastProject/Catima/";
} else if (id == R.id.privacy) {
url = "https://catima.app/privacy-policy/";
} else if (id == R.id.report_error) {
url = "https://github.com/TheLastProject/Catima/issues";
} else if (id == R.id.rate) {
url = "https://play.google.com/store/apps/details?id=me.hackerchick.catima";
} else {
return;
}
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
startActivity(intent);
}
}

View File

@@ -1,28 +1,34 @@
package protect.card_locker;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.graphics.Color;
import android.graphics.PorterDuff;
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;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import java.lang.ref.WeakReference;
import protect.card_locker.async.CompatCallable;
/**
* This task will generate a barcode and load it into an ImageView.
* Only a weak reference of the ImageView is kept, so this class will not
* prevent the ImageView from being garbage collected.
*/
class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
{
public class BarcodeImageWriterTask implements CompatCallable<Bitmap> {
private static final String TAG = "Catima";
private static final int IS_VALID = 999;
private final Context mContext;
private boolean isSuccesful;
// When drawn in a smaller window 1D barcodes for some reason end up
// squished, whereas 2D barcodes look fine.
private static final int MAX_WIDTH_1D = 1500;
@@ -30,14 +36,23 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
private final WeakReference<ImageView> imageViewReference;
private final WeakReference<TextView> textViewReference;
private final String cardId;
private final BarcodeFormat format;
private String cardId;
private final CatimaBarcode format;
private final int imageHeight;
private final int imageWidth;
private final boolean showFallback;
private final Runnable callback;
BarcodeImageWriterTask(
Context context, ImageView imageView, String cardIdString,
CatimaBarcode barcodeFormat, TextView textView,
boolean showFallback, Runnable callback
) {
mContext = context;
isSuccesful = true;
this.callback = callback;
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);
@@ -47,29 +62,21 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
final int MAX_WIDTH = getMaxWidth(format);
if(imageView.getWidth() < MAX_WIDTH)
{
if (imageView.getWidth() < MAX_WIDTH) {
imageHeight = imageView.getHeight();
imageWidth = imageView.getWidth();
}
else
{
} 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);
double ratio = (double) MAX_WIDTH / (double) imageView.getWidth();
imageHeight = (int) (imageView.getHeight() * ratio);
}
this.showFallback = showFallback;
}
BarcodeImageWriterTask(ImageView imageView, String cardIdString, BarcodeFormat barcodeFormat)
{
this(imageView, cardIdString, barcodeFormat, null);
}
private int getMaxWidth(BarcodeFormat format)
{
switch(format)
{
private int getMaxWidth(CatimaBarcode format) {
switch (format.format()) {
// 2D barcodes
case AZTEC:
case DATA_MATRIX:
@@ -96,23 +103,51 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
}
}
public Bitmap doInBackground(Void... params)
{
if (cardId.isEmpty())
{
private String getFallbackString(CatimaBarcode format) {
switch (format.format()) {
// 2D barcodes
case AZTEC:
return "AZTEC";
case DATA_MATRIX:
return "DATA_MATRIX";
case PDF_417:
return "PDF_417";
case QR_CODE:
return "QR_CODE";
// 1D barcodes:
case CODABAR:
return "C0C";
case CODE_39:
return "CODE_39";
case CODE_128:
return "CODE_128";
case EAN_8:
return "32123456";
case EAN_13:
return "5901234123457";
case ITF:
return "1003";
case UPC_A:
return "123456789012";
case UPC_E:
return "0123456";
default:
throw new IllegalArgumentException("No fallback known for this barcode type");
}
}
private Bitmap generate() {
if (cardId.isEmpty()) {
return null;
}
MultiFormatWriter writer = new MultiFormatWriter();
BitMatrix bitMatrix;
try
{
try
{
bitMatrix = writer.encode(cardId, format, imageWidth, imageHeight, null);
}
catch(Exception e)
{
try {
try {
bitMatrix = writer.encode(cardId, format.format(), imageWidth, imageHeight, null);
} catch (Exception e) {
// Cast a wider net here and catch any exception, as there are some
// cases where an encoder may fail if the data is invalid for the
// barcode type. If this happens, we want to fail gracefully.
@@ -127,11 +162,9 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
int[] pixels = new int[bitMatrixWidth * bitMatrixHeight];
for (int y = 0; y < bitMatrixHeight; y++)
{
for (int y = 0; y < bitMatrixHeight; y++) {
int offset = y * bitMatrixWidth;
for (int x = 0; x < bitMatrixWidth; x++)
{
for (int x = 0; x < bitMatrixWidth; x++) {
int color = bitMatrix.get(x, y) ? BLACK : WHITE;
pixels[offset + x] = color;
}
@@ -151,57 +184,103 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
int widthScale = imageWidth / bitMatrixHeight;
int scalingFactor = Math.min(heightScale, widthScale);
if(scalingFactor > 1)
{
if (scalingFactor > 1) {
bitmap = Bitmap.createScaledBitmap(bitmap, bitMatrixWidth * scalingFactor, bitMatrixHeight * scalingFactor, false);
}
return bitmap;
}
catch (WriterException e)
{
} catch (WriterException e) {
Log.e(TAG, "Failed to generate barcode of type " + format + ": " + cardId, e);
}
catch(OutOfMemoryError e)
{
} catch (OutOfMemoryError e) {
Log.w(TAG, "Insufficient memory to render barcode, "
+ imageWidth + "x" + imageHeight + ", " + format.name()
+ ", length=" + cardId.length(), e);
+ imageWidth + "x" + imageHeight + ", " + format.name()
+ ", length=" + cardId.length(), e);
}
return null;
}
protected void onPostExecute(Bitmap result)
{
public Bitmap doInBackground(Void... params) {
// Only do the hard tasks if we've not already been cancelled
if (!Thread.currentThread().isInterrupted()) {
Bitmap bitmap = generate();
if (bitmap == null) {
isSuccesful = false;
if (showFallback && !Thread.currentThread().isInterrupted()) {
Log.i(TAG, "Barcode generation failed, generating fallback...");
cardId = getFallbackString(format);
bitmap = generate();
return bitmap;
}
} else {
return bitmap;
}
}
// We've been interrupted - create a empty fallback
Bitmap.Config config = Bitmap.Config.ARGB_8888;
return Bitmap.createBitmap(imageWidth, imageHeight, config);
}
public void onPostExecute(Object castResult) {
Bitmap result = (Bitmap) castResult;
Log.i(TAG, "Finished generating barcode image of type " + format + ": " + cardId);
ImageView imageView = imageViewReference.get();
if(imageView == null)
{
if (imageView == null) {
// The ImageView no longer exists, nothing to do
return;
}
String formatPrettyName = format.prettyName();
imageView.setTag(isSuccesful);
imageView.setImageBitmap(result);
imageView.setContentDescription(mContext.getString(R.string.barcodeImageDescriptionWithType, formatPrettyName));
TextView textView = textViewReference.get();
if(result != null)
{
if (result != null) {
Log.i(TAG, "Displaying barcode");
imageView.setVisibility(View.VISIBLE);
if (isSuccesful) {
imageView.setColorFilter(null);
} else {
imageView.setColorFilter(Color.LTGRAY, PorterDuff.Mode.LIGHTEN);
}
if (textView != null) {
textView.setVisibility(View.VISIBLE);
textView.setText(format.name());
textView.setText(formatPrettyName);
}
}
else
{
} else {
Log.i(TAG, "Barcode generation failed, removing image from display");
imageView.setVisibility(View.GONE);
if (textView != null) {
textView.setVisibility(View.GONE);
}
}
if (callback != null) {
callback.run();
}
}
@Override
public void onPreExecute() {
// No Action
}
/**
* Provided to comply with Callable while keeping the original Syntax of AsyncTask
*
* @return generated Bitmap
*/
@Override
public Bitmap call() {
return doInBackground();
}
}

View File

@@ -2,30 +2,22 @@ package protect.card_locker;
import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
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.os.Handler;
import android.os.Looper;
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 android.widget.ListView;
import android.widget.Toast;
import com.google.common.collect.ImmutableMap;
import com.google.zxing.BarcodeFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Map;
import java.util.ArrayList;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.Toolbar;
/**
* This activity is callable and will allow a user to enter
@@ -33,186 +25,89 @@ import java.util.Map;
* the data. The user may then select any barcode, where its
* data and type will be returned to the caller.
*/
public class BarcodeSelectorActivity extends AppCompatActivity
{
public class BarcodeSelectorActivity extends CatimaAppCompatActivity implements BarcodeSelectorAdapter.BarcodeSelectorListener {
private static final String TAG = "Catima";
// Result this activity will return
public static final String BARCODE_CONTENTS = "contents";
public static final String BARCODE_FORMAT = "format";
// These are all the barcode types that the zxing library
// is able to generate a barcode for, and thus should be
// the only barcodes which we should attempt to scan.
public static final Collection<String> SUPPORTED_BARCODE_TYPES = Collections.unmodifiableList(
Arrays.asList(
BarcodeFormat.AZTEC.name(),
BarcodeFormat.CODE_39.name(),
BarcodeFormat.CODE_128.name(),
BarcodeFormat.CODABAR.name(),
BarcodeFormat.DATA_MATRIX.name(),
BarcodeFormat.EAN_8.name(),
BarcodeFormat.EAN_13.name(),
BarcodeFormat.ITF.name(),
BarcodeFormat.PDF_417.name(),
BarcodeFormat.QR_CODE.name(),
BarcodeFormat.UPC_A.name()
));
private final Handler typingDelayHandler = new Handler(Looper.getMainLooper());
public static final Integer INPUT_DELAY = 250;
private Map<String, Pair<Integer, Integer>> barcodeViewMap;
private LinkedList<AsyncTask> barcodeGeneratorTasks = new LinkedList<>();
private BarcodeSelectorAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState)
{
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle(R.string.selectBarcodeTitle);
setContentView(R.layout.barcode_selector_activity);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
{
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
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 = findViewById(R.id.cardId);
cardId.addTextChangedListener(new TextWatcher()
{
ListView mBarcodeList = findViewById(R.id.barcodes);
mAdapter = new BarcodeSelectorAdapter(this, new ArrayList<>(), this);
mBarcodeList.setAdapter(mAdapter);
cardId.addTextChangedListener(new SimpleTextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after)
{
// Noting to do
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Delay the input processing so we avoid overload
typingDelayHandler.removeCallbacksAndMessages(null);
@Override
public void onTextChanged(CharSequence s, int start, int before, int count)
{
Log.d(TAG, "Entered text: " + s);
typingDelayHandler.postDelayed(() -> {
Log.d(TAG, "Entered text: " + s);
// Stop any async tasks which may not have been started yet
for(AsyncTask task : barcodeGeneratorTasks)
{
task.cancel(false);
}
barcodeGeneratorTasks.clear();
runOnUiThread(() -> {
generateBarcodes(s.toString());
// Update barcodes
for(String key : barcodeViewMap.keySet())
{
ImageView image = findViewById(barcodeViewMap.get(key).first);
TextView text = findViewById(barcodeViewMap.get(key).second);
createBarcodeOption(image, key, s.toString(), text);
}
View noBarcodeButtonView = findViewById(R.id.noBarcode);
setButtonListener(noBarcodeButtonView, s.toString());
noBarcodeButtonView.setEnabled(s.length() > 0);
}
@Override
public void afterTextChanged(Editable s)
{
// Noting to do
View noBarcodeButtonView = findViewById(R.id.noBarcode);
setButtonListener(noBarcodeButtonView, s.toString());
noBarcodeButtonView.setEnabled(s.length() > 0);
});
}, INPUT_DELAY);
}
});
final Bundle b = getIntent().getExtras();
final String initialCardId = b != null ? b.getString("initialCardId") : null;
if(initialCardId != null)
{
if (initialCardId != null) {
cardId.setText(initialCardId);
} else {
generateBarcodes("");
}
}
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 generateBarcodes(String value) {
// Update barcodes
ArrayList<CatimaBarcodeWithValue> barcodes = new ArrayList<>();
for (BarcodeFormat barcodeFormat : CatimaBarcode.barcodeFormats) {
CatimaBarcode catimaBarcode = CatimaBarcode.fromBarcode(barcodeFormat);
barcodes.add(new CatimaBarcodeWithValue(catimaBarcode, value));
}
mAdapter.setBarcodes(barcodes);
}
private void createBarcodeOption(final ImageView image, final String formatType, final String cardId, final TextView text)
{
final BarcodeFormat format = BarcodeFormat.valueOf(formatType);
if(format == null)
{
Log.w(TAG, "Unsupported barcode format: " + formatType);
return;
}
image.setImageBitmap(null);
image.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
Log.d(TAG, "Selected barcode type " + formatType);
Intent result = new Intent();
result.putExtra(BARCODE_FORMAT, formatType);
result.putExtra(BARCODE_CONTENTS, cardId);
BarcodeSelectorActivity.this.setResult(RESULT_OK, result);
finish();
}
private void setButtonListener(final View button, final String cardId) {
button.setOnClickListener(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();
});
if(image.getHeight() == 0)
{
// 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.
image.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener()
{
@Override
public void onGlobalLayout()
{
Log.d(TAG, "Global layout finished, type: + " + formatType + ", width: " + image.getWidth());
image.getViewTreeObserver().removeOnGlobalLayoutListener(this);
Log.d(TAG, "Generating barcode for type " + formatType);
BarcodeImageWriterTask task = new BarcodeImageWriterTask(image, cardId, format, text);
barcodeGeneratorTasks.add(task);
task.execute();
}
});
}
else
{
Log.d(TAG, "Generating barcode for type " + formatType);
BarcodeImageWriterTask task = new BarcodeImageWriterTask(image, cardId, format, text);
barcodeGeneratorTasks.add(task);
task.execute();
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
if (item.getItemId() == android.R.id.home)
{
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
setResult(Activity.RESULT_CANCELED);
finish();
return true;
@@ -220,4 +115,26 @@ public class BarcodeSelectorActivity extends AppCompatActivity
return super.onOptionsItemSelected(item);
}
@Override
public void onRowClicked(int inputPosition, View view) {
CatimaBarcodeWithValue barcodeWithValue = mAdapter.getItem(inputPosition);
CatimaBarcode catimaBarcode = barcodeWithValue.catimaBarcode();
if (!mAdapter.isValid(view)) {
Toast.makeText(this, getString(R.string.wrongValueForBarcodeType), Toast.LENGTH_LONG).show();
return;
}
String barcodeFormat = catimaBarcode.format().name();
String value = barcodeWithValue.value();
Log.d(TAG, "Selected barcode type " + barcodeFormat);
Intent result = new Intent();
result.putExtra(BARCODE_FORMAT, barcodeFormat);
result.putExtra(BARCODE_CONTENTS, value);
BarcodeSelectorActivity.this.setResult(RESULT_OK, result);
finish();
}
}

View File

@@ -0,0 +1,102 @@
package protect.card_locker;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import protect.card_locker.async.TaskHandler;
public class BarcodeSelectorAdapter extends ArrayAdapter<CatimaBarcodeWithValue> {
private static final String TAG = "Catima";
private final TaskHandler mTasks = new TaskHandler();
private final BarcodeSelectorListener mListener;
private static class ViewHolder {
ImageView image;
TextView text;
}
public interface BarcodeSelectorListener {
void onRowClicked(int inputPosition, View view);
}
public BarcodeSelectorAdapter(Context context, ArrayList<CatimaBarcodeWithValue> barcodes, BarcodeSelectorListener barcodeSelectorListener) {
super(context, 0, barcodes);
mListener = barcodeSelectorListener;
}
public void setBarcodes(ArrayList<CatimaBarcodeWithValue> barcodes) {
clear();
addAll(barcodes);
notifyDataSetChanged();
mTasks.flushTaskList(TaskHandler.TYPE.BARCODE, true, false, false);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
CatimaBarcodeWithValue catimaBarcodeWithValue = getItem(position);
CatimaBarcode catimaBarcode = catimaBarcodeWithValue.catimaBarcode();
String value = catimaBarcodeWithValue.value();
ViewHolder viewHolder;
if (convertView == null) {
viewHolder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(R.layout.barcode_layout, parent, false);
viewHolder.image = convertView.findViewById(R.id.barcodeImage);
viewHolder.text = convertView.findViewById(R.id.barcodeName);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
createBarcodeOption(viewHolder.image, catimaBarcode.format().name(), value, viewHolder.text);
View finalConvertView = convertView;
convertView.setOnClickListener(view -> mListener.onRowClicked(position, finalConvertView));
return convertView;
}
public boolean isValid(View view) {
ViewHolder viewHolder = (ViewHolder) view.getTag();
return viewHolder.image.getTag() != null && (boolean) viewHolder.image.getTag();
}
private void createBarcodeOption(final ImageView image, final String formatType, final String cardId, final TextView text) {
final CatimaBarcode format = CatimaBarcode.fromName(formatType);
image.setImageBitmap(null);
if (image.getHeight() == 0) {
// 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.
image.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Log.d(TAG, "Global layout finished, type: + " + formatType + ", width: " + image.getWidth());
image.getViewTreeObserver().removeOnGlobalLayoutListener(this);
Log.d(TAG, "Generating barcode for type " + formatType);
BarcodeImageWriterTask barcodeWriter = new BarcodeImageWriterTask(getContext(), image, cardId, format, text, true, null);
mTasks.executeTask(TaskHandler.TYPE.BARCODE, barcodeWriter);
}
});
} else {
Log.d(TAG, "Generating barcode for type " + formatType);
BarcodeImageWriterTask barcodeWriter = new BarcodeImageWriterTask(getContext(), image, cardId, format, text, true, null);
mTasks.executeTask(TaskHandler.TYPE.BARCODE, barcodeWriter);
}
}
}

View File

@@ -0,0 +1,76 @@
package protect.card_locker;
import android.database.Cursor;
import androidx.recyclerview.widget.RecyclerView;
public abstract class BaseCursorAdapter<V extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<V> {
public Cursor mCursor;
private boolean mDataValid;
private int mRowIDColumn;
private String mRowIDColumnName;
public BaseCursorAdapter(Cursor inputCursor, String rowIDColumnName) {
setHasStableIds(true);
mRowIDColumnName = rowIDColumnName;
swapCursor(inputCursor);
}
public abstract void onBindViewHolder(V inputHolder, Cursor inputCursor);
@Override
public void onBindViewHolder(V inputHolder, int inputPosition) {
if (!mDataValid) {
throw new IllegalStateException("Cannot bind view holder when cursor is in invalid state.");
}
if (!mCursor.moveToPosition(inputPosition)) {
throw new IllegalStateException("Could not move cursor to position " + inputPosition + " when trying to bind view holder");
}
onBindViewHolder(inputHolder, mCursor);
}
@Override
public int getItemCount() {
if (mDataValid) {
return mCursor.getCount();
} else {
return 0;
}
}
@Override
public long getItemId(int inputPosition) {
if (!mDataValid) {
throw new IllegalStateException("Cannot lookup item id when cursor is in invalid state.");
}
if (!mCursor.moveToPosition(inputPosition)) {
throw new IllegalStateException("Could not move cursor to position " + inputPosition + " when trying to get an item id");
}
return mCursor.getLong(mRowIDColumn);
}
public void swapCursor(Cursor inputCursor) {
if (inputCursor == mCursor) {
return;
}
if (inputCursor != null) {
mCursor = inputCursor;
mRowIDColumn = mCursor.getColumnIndex(mRowIDColumnName);
mDataValid = true;
notifyDataSetChanged();
} else {
notifyItemRangeRemoved(0, getItemCount());
mCursor = null;
mRowIDColumn = -1;
mDataValid = false;
}
}
}

View File

@@ -1,27 +1,30 @@
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;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.pm.ShortcutInfoCompat;
import androidx.core.content.pm.ShortcutManagerCompat;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
/**
* The configuration screen for creating a shortcut.
*/
public class CardShortcutConfigure extends AppCompatActivity
{
public class CardShortcutConfigure extends AppCompatActivity implements LoyaltyCardCursorAdapter.CardAdapterListener {
static final String TAG = "Catima";
final DBHelper mDb = new DBHelper(this);
@Override
public void onCreate(Bundle bundle)
{
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
// Set the result to CANCELED. This will cause nothing to happen if the
@@ -32,52 +35,54 @@ public class CardShortcutConfigure extends AppCompatActivity
Toolbar toolbar = findViewById(R.id.toolbar);
toolbar.setVisibility(View.GONE);
// Hide new button because it won't work here anyway
FloatingActionButton newFab = findViewById(R.id.fabAdd);
newFab.setVisibility(View.GONE);
final DBHelper db = new DBHelper(this);
// If there are no cards, bail
if(db.getLoyaltyCardCount() == 0)
{
if (db.getLoyaltyCardCount() == 0) {
Toast.makeText(this, R.string.noCardsMessage, Toast.LENGTH_LONG).show();
finish();
}
final ListView cardList = findViewById(R.id.list);
final RecyclerView cardList = findViewById(R.id.list);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
cardList.setLayoutManager(mLayoutManager);
cardList.setItemAnimator(new DefaultItemAnimator());
cardList.setVisibility(View.VISIBLE);
Cursor cardCursor = db.getLoyaltyCardCursor();
final LoyaltyCardCursorAdapter adapter = new LoyaltyCardCursorAdapter(this, cardCursor);
final LoyaltyCardCursorAdapter adapter = new LoyaltyCardCursorAdapter(this, cardCursor, this);
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);
private void onClickAction(int position) {
Cursor selected = mDb.getLoyaltyCardCursor();
selected.moveToPosition(position);
LoyaltyCard loyaltyCard = LoyaltyCard.toLoyaltyCard(selected);
Log.d(TAG, "Creating shortcut for card " + loyaltyCard.store + "," + loyaltyCard.id);
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);
ShortcutInfoCompat shortcut = ShortcutHelper.createShortcutBuilder(CardShortcutConfigure.this, loyaltyCard).build();
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);
setResult(RESULT_OK, ShortcutManagerCompat.createShortcutResultIntent(CardShortcutConfigure.this, shortcut));
finish();
}
});
finish();
}
@Override
public void onRowClicked(int inputPosition) {
onClickAction(inputPosition);
}
@Override
public void onRowLongClicked(int inputPosition) {
// do nothing
}
}

View File

@@ -0,0 +1,159 @@
package protect.card_locker;
import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.service.controls.Control;
import android.service.controls.ControlsProviderService;
import android.service.controls.DeviceTypes;
import android.service.controls.actions.ControlAction;
import android.service.controls.templates.StatelessTemplate;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import java.util.List;
import java.util.concurrent.Flow;
import java.util.function.Consumer;
@RequiresApi(Build.VERSION_CODES.R)
public class CardsOnPowerScreenService extends ControlsProviderService {
public static final String PREFIX = "catima-";
static final String TAG = "Catima";
private final DBHelper dbHelper = new DBHelper(this);
@NonNull
@Override
public Flow.Publisher<Control> createPublisherForAllAvailable() {
Cursor loyaltyCardCursor = dbHelper.getLoyaltyCardCursor();
return subscriber -> {
while (loyaltyCardCursor.moveToNext()) {
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(loyaltyCardCursor);
Intent openIntent = new Intent(this, LoyaltyCardViewActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra("id", card.id);
PendingIntent pendingIntent = PendingIntent.getActivity(getBaseContext(), card.id, openIntent, PendingIntent.FLAG_IMMUTABLE);
subscriber.onNext(
new Control.StatelessBuilder(PREFIX + card.id, pendingIntent)
.setControlId(PREFIX + card.id)
.setTitle(card.store)
.setDeviceType(DeviceTypes.TYPE_GENERIC_OPEN_CLOSE)
.setSubtitle(card.note)
.setCustomIcon(Icon.createWithBitmap(getIcon(this, card)))
.build()
);
}
subscriber.onComplete();
};
}
@NonNull
@Override
public Flow.Publisher<Control> createPublisherFor(@NonNull List<String> controlIds) {
return subscriber -> {
subscriber.onSubscribe(new NoOpSubscription());
for (String controlId : controlIds) {
Control control;
try {
Integer cardId = this.controlIdToCardId(controlId);
LoyaltyCard card = dbHelper.getLoyaltyCard(cardId);
Intent openIntent = new Intent(this, LoyaltyCardViewActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra("id", card.id);
PendingIntent pendingIntent = PendingIntent.getActivity(getBaseContext(), card.id, openIntent, PendingIntent.FLAG_IMMUTABLE);
control = new Control.StatefulBuilder(controlId, pendingIntent)
.setTitle(card.store)
.setDeviceType(DeviceTypes.TYPE_GENERIC_OPEN_CLOSE)
.setSubtitle(card.note)
.setStatus(Control.STATUS_OK)
.setControlTemplate(new StatelessTemplate(controlId))
.setCustomIcon(Icon.createWithBitmap(getIcon(this, card)))
.build();
} catch (NullPointerException ignored) {
Intent mainScreenIntent = new Intent(this, MainActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(getBaseContext(), -1, mainScreenIntent, PendingIntent.FLAG_IMMUTABLE);
control = new Control.StatefulBuilder(controlId, pendingIntent)
.setStatus(Control.STATUS_NOT_FOUND)
.build();
}
Log.d(TAG, "Dispatching widget " + controlId);
subscriber.onNext(control);
}
subscriber.onComplete();
};
}
private Bitmap getIcon(Context context, LoyaltyCard loyaltyCard) {
Bitmap cardIcon = Utils.retrieveCardImage(context, loyaltyCard.id, ImageLocationType.icon);
if (cardIcon != null) {
return cardIcon;
}
return Utils.generateIcon(this, loyaltyCard.store, loyaltyCard.headerColor).getLetterTile();
}
private Integer controlIdToCardId(String controlId) {
if (controlId == null)
return null;
if (!controlId.startsWith(PREFIX)) {
Log.w(TAG, "Unsupported control ID format: " + controlId);
return null;
}
controlId = controlId.substring(PREFIX.length());
try {
return Integer.parseInt(controlId);
} catch (RuntimeException ex) {
Log.e(TAG, "Unsupported control ID format. Expected numeric after prefix, found: " + controlId);
return null;
}
}
@Override
public void performControlAction(@NonNull String controlId, @NonNull ControlAction action, @NonNull Consumer<Integer> consumer) {
consumer.accept(ControlAction.RESPONSE_OK);
Intent openIntent = new Intent(this, LoyaltyCardViewActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra("id", controlIdToCardId(controlId));
startActivity(openIntent);
closePowerScreenOnAndroid11();
}
@SuppressLint({"MissingPermission", "deprecation"})
private void closePowerScreenOnAndroid11() {
// Android 12 will auto-close the power screen, but earlier versions won't
// Lint complains about this but on Android 11 the permission is not needed
// On Android 12, we don't need it, and Google will probably get angry if we ask for it
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
}
}
/**
* A no-op subscription.
* <p>
* Flow.Subscriptions are made to last during time and receive periodic updates.
* Our app does not require sending periodic updates of loyalty cards, so we are just ignoring anything in the subscription
* Also, our db is quick enough to respond that the Publisher is immediately sending and completing data.
* This facility is overkill, but if we don't call onSubscribe the service won't work
*/
private static class NoOpSubscription implements Flow.Subscription {
@Override
public void request(long l) {
}
@Override
public void cancel() {
}
}
}

View File

@@ -0,0 +1,53 @@
package protect.card_locker;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.util.TypedValue;
import java.util.HashMap;
import androidx.appcompat.app.AppCompatActivity;
import androidx.preference.PreferenceManager;
public class CatimaAppCompatActivity extends AppCompatActivity {
SharedPreferences pref;
HashMap<String, Integer> supportedThemes;
@Override
protected void attachBaseContext(Context base) {
// Apply chosen language
super.attachBaseContext(Utils.updateBaseContextLocale(base));
}
@Override
public Resources.Theme getTheme() {
if (supportedThemes == null) {
supportedThemes = new HashMap<>();
supportedThemes.put(getString(R.string.settings_key_blue_theme), R.style.AppTheme_blue);
supportedThemes.put(getString(R.string.settings_key_brown_theme), R.style.AppTheme_brown);
supportedThemes.put(getString(R.string.settings_key_green_theme), R.style.AppTheme_green);
supportedThemes.put(getString(R.string.settings_key_grey_theme), R.style.AppTheme_grey);
supportedThemes.put(getString(R.string.settings_key_magenta_theme), R.style.AppTheme_magenta);
supportedThemes.put(getString(R.string.settings_key_pink_theme), R.style.AppTheme_pink);
supportedThemes.put(getString(R.string.settings_key_sky_blue_theme), R.style.AppTheme_sky_blue);
supportedThemes.put(getString(R.string.settings_key_violet_theme), R.style.AppTheme_violet);
}
Resources.Theme theme = super.getTheme();
pref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
String themeName = pref.getString(getString(R.string.setting_key_theme_color), getString(R.string.settings_key_catima_theme));
theme.applyStyle(Utils.mapGetOrDefault(supportedThemes, themeName, R.style.AppTheme_NoActionBar), true);
return theme;
}
public int getThemeColor() {
TypedValue typedValue = new TypedValue();
Resources.Theme theme = getTheme();
theme.resolveAttribute(R.attr.colorPrimary, typedValue, true);
return typedValue.data;
}
}

View File

@@ -0,0 +1,90 @@
package protect.card_locker;
import com.google.zxing.BarcodeFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class CatimaBarcode {
public static final List<BarcodeFormat> barcodeFormats = Collections.unmodifiableList(Arrays.asList(
BarcodeFormat.AZTEC,
BarcodeFormat.CODE_39,
BarcodeFormat.CODE_128,
BarcodeFormat.CODABAR,
BarcodeFormat.DATA_MATRIX,
BarcodeFormat.EAN_8,
BarcodeFormat.EAN_13,
BarcodeFormat.ITF,
BarcodeFormat.PDF_417,
BarcodeFormat.QR_CODE,
BarcodeFormat.UPC_A,
BarcodeFormat.UPC_E
));
public static final List<String> barcodePrettyNames = Collections.unmodifiableList(Arrays.asList(
"Aztec",
"Code 39",
"Code 128",
"Codabar",
"Data Matrix",
"EAN 8",
"EAN 13",
"ITF",
"PDF 417",
"QR Code",
"UPC A",
"UPC E"
));
private final BarcodeFormat mBarcodeFormat;
private CatimaBarcode(BarcodeFormat barcodeFormat) {
mBarcodeFormat = barcodeFormat;
}
public static CatimaBarcode fromBarcode(BarcodeFormat barcodeFormat) {
return new CatimaBarcode(barcodeFormat);
}
public static CatimaBarcode fromName(String name) {
return new CatimaBarcode(BarcodeFormat.valueOf(name));
}
public static CatimaBarcode fromPrettyName(String prettyName) {
try {
return new CatimaBarcode(barcodeFormats.get(barcodePrettyNames.indexOf(prettyName)));
} catch (IndexOutOfBoundsException e) {
throw new IllegalArgumentException("No barcode type with pretty name " + prettyName + " known!");
}
}
public boolean isSupported() {
return barcodeFormats.contains(mBarcodeFormat);
}
public boolean isSquare() {
return mBarcodeFormat == BarcodeFormat.AZTEC
|| mBarcodeFormat == BarcodeFormat.DATA_MATRIX
|| mBarcodeFormat == BarcodeFormat.MAXICODE
|| mBarcodeFormat == BarcodeFormat.QR_CODE;
}
public BarcodeFormat format() {
return mBarcodeFormat;
}
public String name() {
return mBarcodeFormat.name();
}
public String prettyName() {
int index = barcodeFormats.indexOf(mBarcodeFormat);
if (index == -1 || index >= barcodePrettyNames.size()) {
return mBarcodeFormat.name();
}
return barcodePrettyNames.get(index);
}
}

View File

@@ -0,0 +1,19 @@
package protect.card_locker;
public class CatimaBarcodeWithValue {
private final CatimaBarcode mCatimaBarcode;
private final String mValue;
public CatimaBarcodeWithValue(CatimaBarcode catimaBarcode, String value) {
mCatimaBarcode = catimaBarcode;
mValue = value;
}
public CatimaBarcode catimaBarcode() {
return mCatimaBarcode;
}
public String value() {
return mValue;
}
}

View File

@@ -0,0 +1,25 @@
package protect.card_locker;
import android.app.Activity;
import android.content.Context;
import android.widget.Toast;
import com.journeyapps.barcodescanner.CaptureManager;
import com.journeyapps.barcodescanner.DecoratedBarcodeView;
public class CatimaCaptureManager extends CaptureManager {
private final Context mContext;
public CatimaCaptureManager(Activity activity, DecoratedBarcodeView barcodeView) {
super(activity, barcodeView);
mContext = activity.getApplicationContext();
}
@Override
protected void displayFrameworkBugMessageAndExit(String message) {
// We don't want to exit, as we also have a enter from card image and add manually button here
// So we show a toast instead
Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();
}
}

View File

@@ -1,106 +0,0 @@
package protect.card_locker;
import android.database.Cursor;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
import java.io.IOException;
import java.io.OutputStreamWriter;
/**
* Class for exporting the database into CSV (Comma Separate Values)
* format.
*/
public class CsvDatabaseExporter implements DatabaseExporter
{
public void exportData(DBHelper db, OutputStreamWriter output) throws IOException, InterruptedException
{
CSVPrinter printer = new CSVPrinter(output, CSVFormat.RFC4180);
// Print the version
printer.printRecord("2");
printer.println();
// Print the header for groups
printer.printRecord(DBHelper.LoyaltyCardDbGroups.ID);
Cursor groupCursor = db.getGroupCursor();
while(groupCursor.moveToNext())
{
Group group = Group.toGroup(groupCursor);
printer.printRecord(group._id);
if(Thread.currentThread().isInterrupted())
{
throw new InterruptedException();
}
}
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

@@ -1,321 +0,0 @@
package protect.card_locker;
import android.database.sqlite.SQLiteDatabase;
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)
* formatted data.
*
* The database's loyalty cards are expected to appear in the CSV data.
* A header is expected for the each table showing the names of the columns.
*/
public class 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));
}
bufferedReader.close();
}
public void parseV1(DBHelper db, BufferedReader input) throws IOException, FormatException, InterruptedException
{
final CSVParser parser = new CSVParser(input, CSVFormat.RFC4180.withHeader());
SQLiteDatabase database = db.getWritableDatabase();
database.beginTransaction();
try
{
for (CSVRecord record : parser)
{
importLoyaltyCard(database, db, record);
if(Thread.currentThread().isInterrupted())
{
throw new InterruptedException();
}
}
parser.close();
database.setTransactionSuccessful();
}
catch(IllegalArgumentException|IllegalStateException e)
{
throw new FormatException("Issue parsing CSV data", e);
}
finally
{
database.endTransaction();
database.close();
}
}
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();
}
}
} catch (IllegalArgumentException | IllegalStateException e) {
throw new FormatException("Issue parsing CSV data", e);
} finally {
groupParser.close();
}
}
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();
}
}
} catch (IllegalArgumentException | IllegalStateException e) {
throw new FormatException("Issue parsing CSV data", e);
} finally {
cardParser.close();
}
}
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();
}
}
} catch (IllegalArgumentException | IllegalStateException e) {
throw new FormatException("Issue parsing CSV data", e);
} finally {
cardGroupParser.close();
}
}
/**
* Extract a string from the items array. The index into the array
* is determined by looking up the index in the fields map using the
* "key" as the key. If no such key exists, defaultValue is returned
* if it is not null. Otherwise, a FormatException is thrown.
*/
private String extractString(String key, CSVRecord record, String defaultValue)
throws FormatException
{
String toReturn = defaultValue;
if(record.isMapped(key))
{
toReturn = record.get(key);
}
else
{
if(defaultValue == null)
{
throw new FormatException("Field not used but expected: " + key);
}
}
return toReturn;
}
/**
* Extract an integer from the items array. The index into the array
* is determined by looking up the index in the fields map using the
* "key" as the key. If no such key exists, or the data is not a valid
* int, a FormatException is thrown.
*/
private Integer extractInt(String key, CSVRecord record, boolean nullIsOk)
throws FormatException
{
if(record.isMapped(key) == false)
{
throw new FormatException("Field not used but expected: " + key);
}
String value = record.get(key);
if(value.isEmpty() && nullIsOk)
{
return null;
}
try
{
return Integer.parseInt(record.get(key));
}
catch(NumberFormatException e)
{
throw new FormatException("Failed to parse field: " + key, e);
}
}
/**
* Import a single loyalty card into the database using the given
* session.
*/
private void importLoyaltyCard(SQLiteDatabase database, DBHelper helper, CSVRecord record)
throws IOException, FormatException
{
int id = extractInt(DBHelper.LoyaltyCardDbIds.ID, record, false);
String store = extractString(DBHelper.LoyaltyCardDbIds.STORE, record, "");
if(store.isEmpty())
{
throw new FormatException("No store listed, but is required");
}
String note = extractString(DBHelper.LoyaltyCardDbIds.NOTE, record, "");
String cardId = extractString(DBHelper.LoyaltyCardDbIds.CARD_ID, record, "");
if(cardId.isEmpty())
{
throw new FormatException("No card ID listed, but is required");
}
String barcodeType = extractString(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE, record, "");
Integer headerColor = null;
if(record.isMapped(DBHelper.LoyaltyCardDbIds.HEADER_COLOR))
{
headerColor = extractInt(DBHelper.LoyaltyCardDbIds.HEADER_COLOR, record, true);
}
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

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +0,0 @@
package protect.card_locker;
public enum DataFormat
{
CSV,
;
}

View File

@@ -1,17 +0,0 @@
package protect.card_locker;
import java.io.IOException;
import java.io.OutputStreamWriter;
/**
* Interface for a class which can export the contents of the database
* in a given format.
*/
public interface DatabaseExporter
{
/**
* Export the database to the output stream in a given format.
* @throws IOException
*/
void exportData(DBHelper db, OutputStreamWriter output) throws IOException, InterruptedException;
}

View File

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

View File

@@ -5,15 +5,12 @@ package protect.card_locker;
* encountered with the format of data being
* imported or exported.
*/
public class FormatException extends Exception
{
public FormatException(String message)
{
public class FormatException extends Exception {
public FormatException(String message) {
super(message);
}
public FormatException(String message, Exception rootCause)
{
public FormatException(String message, Exception rootCause) {
super(message, rootCause);
}
}

View File

@@ -2,19 +2,39 @@ package protect.card_locker;
import android.database.Cursor;
public class Group
{
public final String _id;
import androidx.annotation.Nullable;
public Group(final String _id)
{
public class Group {
public final String _id;
public final int order;
public Group(final String _id, final int order) {
this._id = _id;
this.order = order;
}
public static Group toGroup(Cursor cursor)
{
public static Group toGroup(Cursor cursor) {
String _id = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbGroups.ID));
int order = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbGroups.ORDER));
return new Group(_id);
return new Group(_id, order);
}
@Override
public boolean equals(@Nullable Object obj) {
if (obj == null) {
return false;
}
if (!(obj instanceof Group)) {
return false;
}
Group anotherGroup = (Group) obj;
return _id.equals(anotherGroup._id) && order == anotherGroup.order;
}
@Override
public int hashCode() {
String combined = _id + "_" + order;
return combined.hashCode();
}
}

View File

@@ -5,52 +5,80 @@ 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 androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatImageButton;
import androidx.recyclerview.widget.RecyclerView;
import protect.card_locker.preferences.Settings;
class GroupCursorAdapter extends CursorAdapter
{
Settings settings;
DBHelper db;
public class GroupCursorAdapter extends BaseCursorAdapter<GroupCursorAdapter.GroupListItemViewHolder> {
Settings mSettings;
private final Context mContext;
private final GroupAdapterListener mListener;
DBHelper mDb;
public GroupCursorAdapter(Context context, Cursor cursor)
{
super(context, cursor, 0);
settings = new Settings(context);
public GroupCursorAdapter(Context inputContext, Cursor inputCursor, GroupAdapterListener inputListener) {
super(inputCursor, DBHelper.LoyaltyCardDbGroups.ORDER);
setHasStableIds(true);
mSettings = new Settings(inputContext);
mContext = inputContext.getApplicationContext();
mListener = inputListener;
mDb = new DBHelper(inputContext);
db = new DBHelper(context);
swapCursor(inputCursor);
}
// 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.
@NonNull
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent)
{
return LayoutInflater.from(context).inflate(R.layout.group_layout, parent, false);
public GroupCursorAdapter.GroupListItemViewHolder onCreateViewHolder(ViewGroup inputParent, int inputViewType) {
View itemView = LayoutInflater.from(inputParent.getContext()).inflate(R.layout.group_layout, inputParent, false);
return new GroupListItemViewHolder(itemView);
}
// 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);
public void onBindViewHolder(GroupListItemViewHolder inputHolder, Cursor inputCursor) {
Group group = Group.toGroup(inputCursor);
// Extract properties from cursor
Group group = Group.toGroup(cursor);
inputHolder.mName.setText(group._id);
Integer groupCardCount = db.getGroupCardCount(group._id);
int groupCardCount = mDb.getGroupCardCount(group._id);
inputHolder.mCardCount.setText(mContext.getResources().getQuantityString(R.plurals.groupCardCount, groupCardCount, groupCardCount));
// Populate fields with extracted properties
nameField.setText(group._id);
inputHolder.mName.setTextSize(mSettings.getFontSizeMax(mSettings.getMediumFont()));
inputHolder.mCardCount.setTextSize(mSettings.getFontSizeMax(mSettings.getSmallFont()));
countField.setText(context.getResources().getQuantityString(R.plurals.groupCardCount, groupCardCount, groupCardCount));
applyClickEvents(inputHolder);
}
nameField.setTextSize(settings.getCardTitleListFontSize());
countField.setTextSize(settings.getCardNoteListFontSize());
private void applyClickEvents(GroupListItemViewHolder inputHolder) {
inputHolder.mMoveDown.setOnClickListener(view -> mListener.onMoveDownButtonClicked(inputHolder.itemView));
inputHolder.mMoveUp.setOnClickListener(view -> mListener.onMoveUpButtonClicked(inputHolder.itemView));
inputHolder.mEdit.setOnClickListener(view -> mListener.onEditButtonClicked(inputHolder.itemView));
inputHolder.mDelete.setOnClickListener(view -> mListener.onDeleteButtonClicked(inputHolder.itemView));
}
public interface GroupAdapterListener {
void onMoveDownButtonClicked(View view);
void onMoveUpButtonClicked(View view);
void onEditButtonClicked(View view);
void onDeleteButtonClicked(View view);
}
public static class GroupListItemViewHolder extends RecyclerView.ViewHolder {
public TextView mName, mCardCount;
public AppCompatImageButton mMoveUp, mMoveDown, mEdit, mDelete;
public GroupListItemViewHolder(View inputView) {
super(inputView);
mName = inputView.findViewById(R.id.name);
mCardCount = inputView.findViewById(R.id.cardCount);
mMoveUp = inputView.findViewById(R.id.moveUp);
mMoveDown = inputView.findViewById(R.id.moveDown);
mEdit = inputView.findViewById(R.id.edit);
mDelete = inputView.findViewById(R.id.delete);
}
}
}

View File

@@ -0,0 +1,7 @@
package protect.card_locker;
public enum ImageLocationType {
front,
back,
icon
}

View File

@@ -6,49 +6,63 @@ 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 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.text.InputType;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.Toast;
import java.io.File;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
public class ImportExportActivity extends AppCompatActivity
{
import protect.card_locker.async.TaskHandler;
import protect.card_locker.importexport.DataFormat;
import protect.card_locker.importexport.ImportExportResult;
public class ImportExportActivity extends CatimaAppCompatActivity {
private static final String TAG = "Catima";
private static final int PERMISSIONS_EXTERNAL_STORAGE = 1;
private static final int CHOOSE_EXPORT_LOCATION = 2;
private static final int CHOOSE_EXPORTED_FILE = 3;
private ImportExportTask importExporter;
private String importAlertTitle;
private String importAlertMessage;
private DataFormat importDataFormat;
private String exportPassword;
private ActivityResultLauncher<Intent> fileCreateLauncher;
private ActivityResultLauncher<String> fileOpenLauncher;
private ActivityResultLauncher<Intent> filePickerLauncher;
final private TaskHandler mTasks = new TaskHandler();
@Override
protected void onCreate(Bundle savedInstanceState)
{
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle(R.string.importExport);
setContentView(R.layout.import_export_activity);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
{
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
@@ -57,110 +71,248 @@ public class ImportExportActivity extends AppCompatActivity
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)
{
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},
Manifest.permission.WRITE_EXTERNAL_STORAGE},
PERMISSIONS_EXTERNAL_STORAGE);
}
// would use ActivityResultContracts.CreateDocument() but mime type cannot be set
fileCreateLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
Intent intent = result.getData();
if (intent == null) {
Log.e(TAG, "Activity returned NULL data");
return;
}
Uri uri = intent.getData();
if (uri == null) {
Log.e(TAG, "Activity returned NULL uri");
return;
}
try {
OutputStream writer = getContentResolver().openOutputStream(uri);
Log.e(TAG, "Starting file export with: " + result.toString());
startExport(writer, uri, exportPassword.toCharArray(), true);
} catch (IOException e) {
Log.e(TAG, "Failed to export file: " + result.toString(), e);
onExportComplete(ImportExportResult.GenericFailure, uri);
}
});
fileOpenLauncher = registerForActivityResult(new ActivityResultContracts.GetContent(), result -> {
if (result == null) {
Log.e(TAG, "Activity returned NULL data");
return;
}
openFileForImport(result, null);
});
filePickerLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
Intent intent = result.getData();
if (intent == null) {
Log.e(TAG, "Activity returned NULL data");
return;
}
Uri uri = intent.getData();
if (uri == null) {
Log.e(TAG, "Activity returned NULL uri");
return;
}
openFileForImport(intent.getData(), null);
});
// Check that there is a file manager available
final Intent intentCreateDocumentAction = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intentCreateDocumentAction.addCategory(Intent.CATEGORY_OPENABLE);
intentCreateDocumentAction.setType("text/csv");
intentCreateDocumentAction.putExtra(Intent.EXTRA_TITLE, "Catima.csv");
intentCreateDocumentAction.setType("application/zip");
intentCreateDocumentAction.putExtra(Intent.EXTRA_TITLE, "catima.zip");
Button exportButton = findViewById(R.id.exportButton);
exportButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
chooseFileWithIntent(intentCreateDocumentAction, CHOOSE_EXPORT_LOCATION);
}
exportButton.setOnClickListener(v -> {
AlertDialog.Builder builder = new AlertDialog.Builder(ImportExportActivity.this);
builder.setTitle(R.string.exportPassword);
FrameLayout container = new FrameLayout(ImportExportActivity.this);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.leftMargin = 50;
params.rightMargin = 50;
final EditText input = new EditText(ImportExportActivity.this);
input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
input.setLayoutParams(params);
input.setHint(R.string.exportPasswordHint);
container.addView(input);
builder.setView(container);
builder.setPositiveButton(R.string.ok, (dialogInterface, i) -> {
exportPassword = input.getText().toString();
try {
fileCreateLauncher.launch(intentCreateDocumentAction);
} catch (ActivityNotFoundException e) {
Toast.makeText(getApplicationContext(), R.string.failedOpeningFileManager, Toast.LENGTH_LONG).show();
Log.e(TAG, "No activity found to handle intent", e);
}
});
builder.setNegativeButton(R.string.cancel, (dialogInterface, i) -> dialogInterface.cancel());
builder.show();
});
// Check that there is a file manager available
final Intent intentGetContentAction = new Intent(Intent.ACTION_GET_CONTENT);
intentGetContentAction.addCategory(Intent.CATEGORY_OPENABLE);
intentGetContentAction.setType("*/*");
Button importFilesystem = findViewById(R.id.importOptionFilesystemButton);
importFilesystem.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
chooseFileWithIntent(intentGetContentAction, CHOOSE_EXPORTED_FILE);
}
});
importFilesystem.setOnClickListener(v -> chooseImportType(false));
// Check that there is an app that data can be imported from
final Intent intentPickAction = new Intent(Intent.ACTION_PICK);
Button importApplication = findViewById(R.id.importOptionApplicationButton);
importApplication.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
chooseFileWithIntent(intentPickAction, CHOOSE_EXPORTED_FILE);
}
});
importApplication.setOnClickListener(v -> chooseImportType(true));
}
private void startImport(final InputStream target, final Uri targetUri)
{
ImportExportTask.TaskCompleteListener listener = new ImportExportTask.TaskCompleteListener()
{
private void openFileForImport(Uri uri, char[] password) {
try {
InputStream reader = getContentResolver().openInputStream(uri);
Log.e(TAG, "Starting file import with: " + uri.toString());
startImport(reader, uri, importDataFormat, password, true);
} catch (IOException e) {
Log.e(TAG, "Failed to import file: " + uri.toString(), e);
onImportComplete(ImportExportResult.GenericFailure, uri, importDataFormat);
}
}
private void chooseImportType(boolean choosePicker) {
List<CharSequence> betaImportOptions = new ArrayList<>();
betaImportOptions.add("Fidme");
betaImportOptions.add("Stocard");
List<CharSequence> importOptions = new ArrayList<>();
for (String importOption : getResources().getStringArray(R.array.import_types_array)) {
if (betaImportOptions.contains(importOption)) {
importOption = importOption + " (BETA)";
}
importOptions.add(importOption);
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.chooseImportType)
.setItems(importOptions.toArray(new CharSequence[importOptions.size()]), (dialog, which) -> {
switch (which) {
// Catima
case 0:
importAlertTitle = getString(R.string.importCatima);
importAlertMessage = getString(R.string.importCatimaMessage);
importDataFormat = DataFormat.Catima;
break;
// Fidme
case 1:
importAlertTitle = getString(R.string.importFidme);
importAlertMessage = getString(R.string.importFidmeMessage);
importDataFormat = DataFormat.Fidme;
break;
// Loyalty Card Keychain
case 2:
importAlertTitle = getString(R.string.importLoyaltyCardKeychain);
importAlertMessage = getString(R.string.importLoyaltyCardKeychainMessage);
importDataFormat = DataFormat.Catima;
break;
// Stocard
case 3:
importAlertTitle = getString(R.string.importStocard);
importAlertMessage = getString(R.string.importStocardMessage);
importDataFormat = DataFormat.Stocard;
break;
// Voucher Vault
case 4:
importAlertTitle = getString(R.string.importVoucherVault);
importAlertMessage = getString(R.string.importVoucherVaultMessage);
importDataFormat = DataFormat.VoucherVault;
break;
default:
throw new IllegalArgumentException("Unknown DataFormat");
}
new AlertDialog.Builder(this)
.setTitle(importAlertTitle)
.setMessage(importAlertMessage)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
try {
if (choosePicker) {
final Intent intentPickAction = new Intent(Intent.ACTION_PICK);
filePickerLauncher.launch(intentPickAction);
} else {
fileOpenLauncher.launch("*/*");
}
} catch (ActivityNotFoundException e) {
Toast.makeText(getApplicationContext(), R.string.failedOpeningFileManager, Toast.LENGTH_LONG).show();
Log.e(TAG, "No activity found to handle intent", e);
}
}
})
.setNegativeButton(R.string.cancel, null)
.show();
});
builder.show();
}
private void startImport(final InputStream target, final Uri targetUri, final DataFormat dataFormat, final char[] password, final boolean closeWhenDone) {
mTasks.flushTaskList(TaskHandler.TYPE.IMPORT, true, false, false);
ImportExportTask.TaskCompleteListener listener = new ImportExportTask.TaskCompleteListener() {
@Override
public void onTaskComplete(boolean success)
{
onImportComplete(success, targetUri);
public void onTaskComplete(ImportExportResult result, DataFormat dataFormat) {
onImportComplete(result, targetUri, dataFormat);
if (closeWhenDone) {
try {
target.close();
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
}
};
importExporter = new ImportExportTask(ImportExportActivity.this,
DataFormat.CSV, target, listener);
importExporter.execute();
dataFormat, target, password, listener);
mTasks.executeTask(TaskHandler.TYPE.IMPORT, importExporter);
}
private void startExport(final OutputStream target, final Uri targetUri)
{
ImportExportTask.TaskCompleteListener listener = new ImportExportTask.TaskCompleteListener()
{
private void startExport(final OutputStream target, final Uri targetUri, char[] password, final boolean closeWhenDone) {
mTasks.flushTaskList(TaskHandler.TYPE.EXPORT, true, false, false);
ImportExportTask.TaskCompleteListener listener = new ImportExportTask.TaskCompleteListener() {
@Override
public void onTaskComplete(boolean success)
{
onExportComplete(success, targetUri);
public void onTaskComplete(ImportExportResult result, DataFormat dataFormat) {
onExportComplete(result, targetUri);
if (closeWhenDone) {
try {
target.close();
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
}
};
importExporter = new ImportExportTask(ImportExportActivity.this,
DataFormat.CSV, target, listener);
importExporter.execute();
DataFormat.Catima, target, password, listener);
mTasks.executeTask(TaskHandler.TYPE.EXPORT, importExporter);
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults)
{
if(requestCode == PERMISSIONS_EXTERNAL_STORAGE)
{
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSIONS_EXTERNAL_STORAGE) {
// If request is cancelled, the result arrays are empty.
boolean success = grantResults.length > 0;
for(int grant : grantResults)
{
if(grant != PackageManager.PERMISSION_GRANTED)
{
for (int grant : grantResults) {
if (grant != PackageManager.PERMISSION_GRANTED) {
success = false;
}
}
if(success == false)
{
if (!success) {
// External storage permission rejected, inform user that
// import/export is prevented
Toast.makeText(getApplicationContext(), R.string.noExternalStoragePermissionError,
@@ -171,22 +323,17 @@ public class ImportExportActivity extends AppCompatActivity
}
@Override
protected void onDestroy()
{
if(importExporter != null && importExporter.getStatus() != AsyncTask.Status.RUNNING)
{
importExporter.cancel(true);
}
protected void onDestroy() {
mTasks.flushTaskList(TaskHandler.TYPE.IMPORT, true, false, false);
mTasks.flushTaskList(TaskHandler.TYPE.EXPORT, true, false, false);
super.onDestroy();
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if(id == android.R.id.home)
{
if (id == android.R.id.home) {
finish();
return true;
}
@@ -194,28 +341,46 @@ public class ImportExportActivity extends AppCompatActivity
return super.onOptionsItemSelected(item);
}
private void onImportComplete(boolean success, Uri path)
{
private void retryWithPassword(DataFormat dataFormat, Uri uri) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.passwordRequired);
final EditText input = new EditText(this);
input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
builder.setView(input);
builder.setPositiveButton(R.string.ok, (dialogInterface, i) -> {
openFileForImport(uri, input.getText().toString().toCharArray());
});
builder.setNegativeButton(R.string.cancel, (dialogInterface, i) -> dialogInterface.cancel());
builder.show();
}
private void onImportComplete(ImportExportResult result, Uri path, DataFormat dataFormat) {
if (result == ImportExportResult.BadPassword) {
retryWithPassword(dataFormat, path);
return;
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
if(success)
{
int messageId;
if (result == ImportExportResult.Success) {
builder.setTitle(R.string.importSuccessfulTitle);
}
else
{
messageId = R.string.importSuccessful;
} else {
builder.setTitle(R.string.importFailedTitle);
messageId = R.string.importFailed;
}
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()
{
builder.setNeutralButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which)
{
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
@@ -223,134 +388,42 @@ public class ImportExportActivity extends AppCompatActivity
builder.create().show();
}
private void onExportComplete(boolean success, final Uri path)
{
private void onExportComplete(ImportExportResult result, final Uri path) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
if(success)
{
int messageId;
if (result == ImportExportResult.Success) {
builder.setTitle(R.string.exportSuccessfulTitle);
}
else
{
messageId = R.string.exportSuccessful;
} else {
builder.setTitle(R.string.exportFailedTitle);
messageId = R.string.exportFailed;
}
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();
}
});
builder.setNeutralButton(R.string.ok, (dialog, which) -> dialog.dismiss());
if(success)
{
if (result == ImportExportResult.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");
builder.setPositiveButton(sendLabel, (dialog, 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);
// 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));
ImportExportActivity.this.startActivity(Intent.createChooser(sendIntent,
sendLabel));
dialog.dismiss();
}
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

@@ -2,19 +2,23 @@ package protect.card_locker;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.util.Log;
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;
import java.nio.charset.StandardCharsets;
class ImportExportTask extends AsyncTask<Void, Void, Boolean>
{
import protect.card_locker.async.CompatCallable;
import protect.card_locker.importexport.DataFormat;
import protect.card_locker.importexport.ImportExportResult;
import protect.card_locker.importexport.MultiFormatExporter;
import protect.card_locker.importexport.MultiFormatImporter;
public class ImportExportTask implements CompatCallable<ImportExportResult> {
private static final String TAG = "Catima";
private Activity activity;
@@ -22,6 +26,7 @@ class ImportExportTask extends AsyncTask<Void, Void, Boolean>
private DataFormat format;
private OutputStream outputStream;
private InputStream inputStream;
private char[] password;
private TaskCompleteListener listener;
private ProgressDialog progress;
@@ -29,63 +34,47 @@ class ImportExportTask extends AsyncTask<Void, Void, Boolean>
/**
* Constructor which will setup a task for exporting to the given file
*/
ImportExportTask(Activity activity, DataFormat format, OutputStream output,
TaskCompleteListener listener)
{
ImportExportTask(Activity activity, DataFormat format, OutputStream output, char[] password,
TaskCompleteListener listener) {
super();
this.activity = activity;
this.doImport = false;
this.format = format;
this.outputStream = output;
this.password = password;
this.listener = listener;
}
/**
* Constructor which will setup a task for importing from the given InputStream.
*/
ImportExportTask(Activity activity, DataFormat format, InputStream input,
TaskCompleteListener listener)
{
ImportExportTask(Activity activity, DataFormat format, InputStream input, char[] password,
TaskCompleteListener listener) {
super();
this.activity = activity;
this.doImport = true;
this.format = format;
this.inputStream = input;
this.password = password;
this.listener = listener;
}
private boolean performImport(InputStream stream, DBHelper db)
{
boolean result = false;
private ImportExportResult performImport(Context context, InputStream stream, DBHelper db, char[] password) {
ImportExportResult importResult = MultiFormatImporter.importData(context, db, stream, format, password);
try
{
InputStreamReader reader = new InputStreamReader(stream, Charset.forName("UTF-8"));
result = MultiFormatImporter.importData(db, reader, format);
reader.close();
}
catch(IOException e)
{
Log.e(TAG, "Unable to import file", e);
}
Log.i(TAG, "Import result: " + importResult.name());
Log.i(TAG, "Import result: " + result);
return result;
return importResult;
}
private boolean performExport(OutputStream stream, DBHelper db)
{
boolean result = false;
private ImportExportResult performExport(Context context, OutputStream stream, DBHelper db, char[] password) {
ImportExportResult result = ImportExportResult.GenericFailure;
try
{
OutputStreamWriter writer = new OutputStreamWriter(stream, Charset.forName("UTF-8"));
result = MultiFormatExporter.exportData(db, writer, format);
try {
OutputStreamWriter writer = new OutputStreamWriter(stream, StandardCharsets.UTF_8);
result = MultiFormatExporter.exportData(context, db, stream, format, password);
writer.close();
}
catch (IOException e)
{
} catch (IOException e) {
Log.e(TAG, "Unable to export file", e);
}
@@ -94,56 +83,56 @@ class ImportExportTask extends AsyncTask<Void, Void, Boolean>
return result;
}
protected void onPreExecute()
{
public void onPreExecute() {
progress = new ProgressDialog(activity);
progress.setTitle(doImport ? R.string.importing : R.string.exporting);
progress.setOnDismissListener(new DialogInterface.OnDismissListener()
{
progress.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog)
{
ImportExportTask.this.cancel(true);
public void onDismiss(DialogInterface dialog) {
ImportExportTask.this.stop();
}
});
progress.show();
}
protected Boolean doInBackground(Void... nothing)
{
protected ImportExportResult doInBackground(Void... nothing) {
final DBHelper db = new DBHelper(activity);
boolean result;
ImportExportResult result;
if(doImport)
{
result = performImport(inputStream, db);
}
else
{
result = performExport(outputStream, db);
if (doImport) {
result = performImport(activity.getApplicationContext(), inputStream, db, password);
} else {
result = performExport(activity.getApplicationContext(), outputStream, db, password);
}
return result;
}
protected void onPostExecute(Boolean result)
{
listener.onTaskComplete(result);
public void onPostExecute(Object castResult) {
listener.onTaskComplete((ImportExportResult) castResult, format);
progress.dismiss();
Log.i(TAG, (doImport ? "Import" : "Export") + " Complete");
}
protected void onCancelled()
{
protected void onCancelled() {
progress.dismiss();
Log.i(TAG, (doImport ? "Import" : "Export") + " Cancelled");
}
interface TaskCompleteListener
{
void onTaskComplete(boolean success);
protected void stop() {
// Whelp
}
@Override
public ImportExportResult call() {
return doInBackground();
}
interface TaskCompleteListener {
void onTaskComplete(ImportExportResult result, DataFormat format);
}
}

View File

@@ -3,93 +3,201 @@ package protect.card_locker;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import java.io.InvalidObjectException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Currency;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
public class ImportURIHelper {
private static final String STORE = DBHelper.LoyaltyCardDbIds.STORE;
private static final String NOTE = DBHelper.LoyaltyCardDbIds.NOTE;
private static final String EXPIRY = DBHelper.LoyaltyCardDbIds.EXPIRY;
private static final String BALANCE = DBHelper.LoyaltyCardDbIds.BALANCE;
private static final String BALANCE_TYPE = DBHelper.LoyaltyCardDbIds.BALANCE_TYPE;
private static final String CARD_ID = DBHelper.LoyaltyCardDbIds.CARD_ID;
private static final String BARCODE_ID = DBHelper.LoyaltyCardDbIds.BARCODE_ID;
private static final String BARCODE_TYPE = DBHelper.LoyaltyCardDbIds.BARCODE_TYPE;
private static final String HEADER_COLOR = DBHelper.LoyaltyCardDbIds.HEADER_COLOR;
private final Context context;
private final String host;
private final String path;
private final String oldHost;
private final String oldPath;
private final String[] hosts = new String[3];
private final String[] paths = new String[3];
private final String shareText;
private final String shareMultipleText;
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";
this.context = context.getApplicationContext();
hosts[0] = context.getResources().getString(R.string.intent_import_card_from_url_host_catima_app);
paths[0] = context.getResources().getString(R.string.intent_import_card_from_url_path_prefix_catima_app);
hosts[1] = context.getResources().getString(R.string.intent_import_card_from_url_host_thelastproject);
paths[1] = context.getResources().getString(R.string.intent_import_card_from_url_path_prefix_thelastproject);
hosts[2] = context.getResources().getString(R.string.intent_import_card_from_url_host_brarcher);
paths[2] = context.getResources().getString(R.string.intent_import_card_from_url_path_prefix_brarcher);
shareText = context.getResources().getString(R.string.intent_import_card_from_url_share_text);
shareMultipleText = context.getResources().getString(R.string.intent_import_card_from_url_share_multiple_text);
}
private boolean isImportUri(Uri uri) {
return (uri.getHost().equals(host) && uri.getPath().equals(path)) || (uri.getHost().equals(oldHost) && uri.getPath().equals(oldPath));
for (int i = 0; i < hosts.length; i++) {
if (uri.getHost().equals(hosts[i]) && uri.getPath().equals(paths[i])) {
return true;
}
}
return false;
}
public LoyaltyCard parse(Uri uri) throws InvalidObjectException {
if(!isImportUri(uri)) {
if (!isImportUri(uri)) {
throw new InvalidObjectException("Not an import URI");
}
try {
// These values are allowed to be null
CatimaBarcode barcodeType = null;
Date expiry = null;
BigDecimal balance = new BigDecimal("0");
Currency balanceType = null;
Integer headerColor = null;
Integer headerTextColor = null;
String store = uri.getQueryParameter(STORE);
String note = uri.getQueryParameter(NOTE);
String cardId = uri.getQueryParameter(CARD_ID);
String barcodeType = uri.getQueryParameter(BARCODE_TYPE);
if (store == null || note == null || cardId == null || barcodeType == null) throw new InvalidObjectException("Not a valid import URI");
// Store everything in a simple key/value hashmap
HashMap<String, String> kv = new HashMap<>();
String unparsedHeaderColor = uri.getQueryParameter(HEADER_COLOR);
if(unparsedHeaderColor != null)
{
// First, grab all query parameters (backwards compatibility)
for (String key : uri.getQueryParameterNames()) {
kv.put(key, uri.getQueryParameter(key));
}
// Then, parse the new and more private fragment part
// Overriding old format entries if they exist
String fragment = uri.getFragment();
if (fragment != null) {
for (String fragmentPart : fragment.split("&")) {
String[] fragmentData = fragmentPart.split("=", 2);
kv.put(fragmentData[0], URLDecoder.decode(fragmentData[1], StandardCharsets.UTF_8.name()));
}
}
// Then use all values we care about
String store = kv.get(STORE);
String note = kv.get(NOTE);
String cardId = kv.get(CARD_ID);
String barcodeId = kv.get(BARCODE_ID);
if (store == null || note == null || cardId == null)
throw new InvalidObjectException("Not a valid import URI: " + uri.toString());
String unparsedBarcodeType = kv.get(BARCODE_TYPE);
if (unparsedBarcodeType != null && !unparsedBarcodeType.equals("")) {
barcodeType = CatimaBarcode.fromName(unparsedBarcodeType);
}
String unparsedBalance = kv.get(BALANCE);
if (unparsedBalance != null && !unparsedBalance.equals("")) {
balance = new BigDecimal(unparsedBalance);
}
String unparsedBalanceType = kv.get(BALANCE_TYPE);
if (unparsedBalanceType != null && !unparsedBalanceType.equals("")) {
balanceType = Currency.getInstance(unparsedBalanceType);
}
String unparsedExpiry = kv.get(EXPIRY);
if (unparsedExpiry != null && !unparsedExpiry.equals("")) {
expiry = new Date(Long.parseLong(unparsedExpiry));
}
String unparsedHeaderColor = kv.get(HEADER_COLOR);
if (unparsedHeaderColor != null) {
headerColor = Integer.parseInt(unparsedHeaderColor);
}
return new LoyaltyCard(-1, store, note, cardId, barcodeType, headerColor, headerTextColor, 0);
} catch (NullPointerException | NumberFormatException ex) {
return new LoyaltyCard(-1, store, note, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, 0, Utils.getUnixTime(), 100);
} catch (NullPointerException | NumberFormatException | UnsupportedEncodingException ex) {
throw new InvalidObjectException("Not a valid import URI");
}
}
private StringBuilder appendFragment(StringBuilder fragment, String key, String value) throws UnsupportedEncodingException {
if (fragment.length() > 0) {
fragment.append("&");
}
// Double-encode the value to make sure it can't accidentally contain symbols that'll break the parser
fragment.append(key).append("=").append(URLEncoder.encode(value, StandardCharsets.UTF_8.name()));
return fragment;
}
// Protected for usage in tests
protected Uri toUri(LoyaltyCard loyaltyCard) {
protected Uri toUri(LoyaltyCard loyaltyCard) throws UnsupportedEncodingException {
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());
uriBuilder.authority(hosts[0]);
uriBuilder.path(paths[0]);
// Use fragment instead of QueryParameter to not leak this data to the server
StringBuilder fragment = new StringBuilder();
fragment = appendFragment(fragment, STORE, loyaltyCard.store);
fragment = appendFragment(fragment, NOTE, loyaltyCard.note);
fragment = appendFragment(fragment, BALANCE, loyaltyCard.balance.toString());
if (loyaltyCard.balanceType != null) {
fragment = appendFragment(fragment, BALANCE_TYPE, loyaltyCard.balanceType.getCurrencyCode());
}
//StarStatus will not be exported
if (loyaltyCard.expiry != null) {
fragment = appendFragment(fragment, EXPIRY, String.valueOf(loyaltyCard.expiry.getTime()));
}
fragment = appendFragment(fragment, CARD_ID, loyaltyCard.cardId);
if (loyaltyCard.barcodeId != null) {
fragment = appendFragment(fragment, BARCODE_ID, loyaltyCard.barcodeId);
}
if (loyaltyCard.barcodeType != null) {
fragment = appendFragment(fragment, BARCODE_TYPE, loyaltyCard.barcodeType.name());
}
if (loyaltyCard.headerColor != null) {
fragment = appendFragment(fragment, HEADER_COLOR, loyaltyCard.headerColor.toString());
}
// Star status will not be exported
// Front and back pictures are often too big to fit into a message in base64 nicely, not sharing either...
uriBuilder.fragment(fragment.toString());
return uriBuilder.build();
}
private void startShareIntent(Uri uri) {
public void startShareIntent(List<LoyaltyCard> loyaltyCards) throws UnsupportedEncodingException {
int loyaltyCardCount = loyaltyCards.size();
StringBuilder text = new StringBuilder();
if (loyaltyCardCount == 1) {
text.append(shareText);
} else {
text.append(shareMultipleText);
}
text.append("\n\n");
for (int i = 0; i < loyaltyCardCount; i++) {
LoyaltyCard loyaltyCard = loyaltyCards.get(i);
text.append(loyaltyCard.store + ": " + toUri(loyaltyCard));
if (i < (loyaltyCardCount - 1)) {
text.append("\n\n");
}
}
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, shareText + "\n" + uri.toString());
sendIntent.putExtra(Intent.EXTRA_TEXT, text.toString());
sendIntent.setType("text/plain");
Intent shareIntent = Intent.createChooser(sendIntent, null);
shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(shareIntent);
}
public void startShareIntent(LoyaltyCard loyaltyCard) {
startShareIntent(toUri(loyaltyCard));
}
}

View File

@@ -18,8 +18,7 @@ import android.text.TextPaint;
* alphabet or digit, if there is no letter or digit available, a default image
* is shown instead.
*/
class LetterBitmap
{
class LetterBitmap {
/**
* The number of available tile colors
@@ -37,39 +36,32 @@ class LetterBitmap
/**
* 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 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.
* @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)
{
int width, int height, Integer backgroundColor, Integer textColor) {
TextPaint paint = new TextPaint();
paint.setTypeface(Typeface.create("sans-serif-light", Typeface.BOLD));
if(textColor != null)
{
if (textColor != null) {
paint.setColor(textColor);
}
else
{
} else {
paint.setColor(Color.WHITE);
}
paint.setTextAlign(Paint.Align.CENTER);
paint.setAntiAlias(true);
if(backgroundColor == null)
{
if (backgroundColor == null) {
mColor = getDefaultColor(context, key);
}
else
{
} else {
mColor = backgroundColor;
}
@@ -80,7 +72,7 @@ class LetterBitmap
c.setBitmap(mBitmap);
c.drawColor(mColor);
char [] firstCharArray = new char[1];
char[] firstCharArray = new char[1];
firstCharArray[0] = firstChar.toUpperCase().charAt(0);
paint.setTextSize(tileLetterFontSize);
@@ -97,16 +89,14 @@ class LetterBitmap
* alphabet or digit, if there is no letter or digit available, a
* default image is shown instead
*/
public Bitmap getLetterTile()
{
public Bitmap getLetterTile() {
return mBitmap;
}
/**
* @return background color used for letter title.
*/
public int getBackgroundColor()
{
public int getBackgroundColor() {
return mColor;
}
@@ -115,8 +105,7 @@ class LetterBitmap
* @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)
{
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;
@@ -127,8 +116,7 @@ class LetterBitmap
* Determine the color which the letter tile will use if no default
* color is provided.
*/
public static int getDefaultColor(Context context, String key)
{
public static int getDefaultColor(Context context, String key) {
final Resources res = context.getResources();
TypedArray colors = res.obtainTypedArray(R.array.letter_tile_colors);

View File

@@ -1,65 +1,146 @@
package protect.card_locker;
import android.database.Cursor;
import android.os.Parcel;
import android.os.Parcelable;
import java.math.BigDecimal;
import java.util.Currency;
import java.util.Date;
import androidx.annotation.Nullable;
public class LoyaltyCard
{
public class LoyaltyCard implements Parcelable {
public final int id;
public final String store;
public final String note;
public final Date expiry;
public final BigDecimal balance;
public final Currency balanceType;
public final String cardId;
public final String barcodeType;
@Nullable
public final String barcodeId;
@Nullable
public final CatimaBarcode barcodeType;
@Nullable
public final Integer headerColor;
@Nullable
public final Integer headerTextColor;
public final int starStatus;
public final long lastUsed;
public int zoomLevel;
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)
{
public LoyaltyCard(final int id, final String store, final String note, final Date expiry,
final BigDecimal balance, final Currency balanceType, final String cardId,
@Nullable final String barcodeId, @Nullable final CatimaBarcode barcodeType,
@Nullable final Integer headerColor, final int starStatus, final long lastUsed, final int zoomLevel) {
this.id = id;
this.store = store;
this.note = note;
this.expiry = expiry;
this.balance = balance;
this.balanceType = balanceType;
this.cardId = cardId;
this.barcodeId = barcodeId;
this.barcodeType = barcodeType;
this.headerColor = headerColor;
this.headerTextColor = headerTextColor;
this.starStatus = starStatus;
this.lastUsed = lastUsed;
this.zoomLevel = zoomLevel;
}
public static LoyaltyCard toLoyaltyCard(Cursor cursor)
{
protected LoyaltyCard(Parcel in) {
id = in.readInt();
store = in.readString();
note = in.readString();
long tmpExpiry = in.readLong();
expiry = tmpExpiry != -1 ? new Date(tmpExpiry) : null;
balance = (BigDecimal) in.readValue(BigDecimal.class.getClassLoader());
balanceType = (Currency) in.readValue(Currency.class.getClassLoader());
cardId = in.readString();
barcodeId = in.readString();
String tmpBarcodeType = in.readString();
barcodeType = !tmpBarcodeType.isEmpty() ? CatimaBarcode.fromName(tmpBarcodeType) : null;
int tmpHeaderColor = in.readInt();
headerColor = tmpHeaderColor != -1 ? tmpHeaderColor : null;
starStatus = in.readInt();
lastUsed = in.readLong();
zoomLevel = in.readInt();
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(id);
parcel.writeString(store);
parcel.writeString(note);
parcel.writeLong(expiry != null ? expiry.getTime() : -1);
parcel.writeValue(balance);
parcel.writeValue(balanceType);
parcel.writeString(cardId);
parcel.writeString(barcodeId);
parcel.writeString(barcodeType != null ? barcodeType.name() : "");
parcel.writeInt(headerColor != null ? headerColor : -1);
parcel.writeInt(starStatus);
parcel.writeLong(lastUsed);
parcel.writeInt(zoomLevel);
}
public static LoyaltyCard toLoyaltyCard(Cursor cursor) {
int id = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.ID));
String store = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STORE));
String note = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE));
long expiryLong = cursor.getLong(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.EXPIRY));
BigDecimal balance = new BigDecimal(cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE)));
String cardId = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID));
String barcodeType = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE));
String barcodeId = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_ID));
int starred = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS));
long lastUsed = cursor.getLong(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.LAST_USED));
int zoomLevel = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.ZOOM_LEVEL));
int barcodeTypeColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE);
int balanceTypeColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE);
int headerColorColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_COLOR);
int headerTextColorColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR);
CatimaBarcode barcodeType = null;
Currency balanceType = null;
Date expiry = null;
Integer headerColor = null;
Integer headerTextColor = null;
if(cursor.isNull(headerColorColumn) == false)
{
if (cursor.isNull(barcodeTypeColumn) == false) {
barcodeType = CatimaBarcode.fromName(cursor.getString(barcodeTypeColumn));
}
if (cursor.isNull(balanceTypeColumn) == false) {
balanceType = Currency.getInstance(cursor.getString(balanceTypeColumn));
}
if (expiryLong > 0) {
expiry = new Date(expiryLong);
}
if (cursor.isNull(headerColorColumn) == false) {
headerColor = cursor.getInt(headerColorColumn);
}
if(cursor.isNull(headerTextColorColumn) == false)
{
headerTextColor = cursor.getInt(headerTextColorColumn);
return new LoyaltyCard(id, store, note, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, starred, lastUsed, zoomLevel);
}
@Override
public int describeContents() {
return id;
}
public static final Creator<LoyaltyCard> CREATOR = new Creator<LoyaltyCard>() {
@Override
public LoyaltyCard createFromParcel(Parcel in) {
return new LoyaltyCard(in);
}
return new LoyaltyCard(id, store, note, cardId, barcodeType, headerColor, headerTextColor, starred);
}
@Override
public LoyaltyCard[] newArray(int size) {
return new LoyaltyCard[size];
}
};
}

View File

@@ -1,66 +1,257 @@
package protect.card_locker;
import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.util.SparseBooleanArray;
import android.util.TypedValue;
import android.view.HapticFeedbackConstants;
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 androidx.core.content.ContextCompat;
import androidx.core.graphics.BlendModeColorFilterCompat;
import androidx.core.graphics.BlendModeCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.card.MaterialCardView;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.util.ArrayList;
import protect.card_locker.preferences.Settings;
class LoyaltyCardCursorAdapter extends CursorAdapter
{
Settings settings;
public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCursorAdapter.LoyaltyCardListItemViewHolder> {
private int mCurrentSelectedIndex = -1;
Settings mSettings;
boolean mDarkModeEnabled;
private final Context mContext;
private final CardAdapterListener mListener;
protected SparseBooleanArray mSelectedItems;
protected SparseBooleanArray mAnimationItemsIndex;
private boolean mReverseAllAnimations = false;
private boolean mShowDetails = true;
public LoyaltyCardCursorAdapter(Context context, Cursor cursor)
{
super(context, cursor, 0);
settings = new Settings(context);
public LoyaltyCardCursorAdapter(Context inputContext, Cursor inputCursor, CardAdapterListener inputListener) {
super(inputCursor, DBHelper.LoyaltyCardDbIds.ID);
setHasStableIds(true);
mSettings = new Settings(inputContext);
mContext = inputContext.getApplicationContext();
mListener = inputListener;
mSelectedItems = new SparseBooleanArray();
mAnimationItemsIndex = new SparseBooleanArray();
mDarkModeEnabled = Utils.isDarkModeEnabled(inputContext);
swapCursor(inputCursor);
}
// 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.loyalty_card_layout, parent, false);
public void showDetails(boolean show) {
mShowDetails = show;
notifyDataSetChanged();
}
public boolean showingDetails() {
return mShowDetails;
}
// 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
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);
public LoyaltyCardListItemViewHolder onCreateViewHolder(ViewGroup inputParent, int inputViewType) {
View itemView = LayoutInflater.from(inputParent.getContext()).inflate(R.layout.loyalty_card_layout, inputParent, false);
return new LoyaltyCardListItemViewHolder(itemView, mListener);
}
// Extract properties from cursor
LoyaltyCard loyaltyCard = LoyaltyCard.toLoyaltyCard(cursor);
public LoyaltyCard getCard(int position) {
mCursor.moveToPosition(position);
return LoyaltyCard.toLoyaltyCard(mCursor);
}
// Populate fields with extracted properties
storeField.setText(loyaltyCard.store);
public void onBindViewHolder(LoyaltyCardListItemViewHolder inputHolder, Cursor inputCursor) {
// Invisible until we want to show something more
inputHolder.mDivider.setVisibility(View.GONE);
storeField.setTextSize(settings.getCardTitleListFontSize());
int size = mSettings.getFontSizeMax(mSettings.getSmallFont());
if(!loyaltyCard.note.isEmpty())
{
noteField.setVisibility(View.VISIBLE);
noteField.setText(loyaltyCard.note);
noteField.setTextSize(settings.getCardNoteListFontSize());
}
else
{
noteField.setVisibility(View.GONE);
if (mDarkModeEnabled) {
inputHolder.mStarIcon.setColorFilter(BlendModeColorFilterCompat.createBlendModeColorFilterCompat(Color.WHITE, BlendModeCompat.SRC_ATOP));
}
if (loyaltyCard.starStatus!=0) star.setVisibility(View.VISIBLE);
else star.setVisibility(View.GONE);
LoyaltyCard loyaltyCard = LoyaltyCard.toLoyaltyCard(inputCursor);
thumbnail.setImageBitmap(Utils.generateIcon(context, loyaltyCard.store, loyaltyCard.headerColor).getLetterTile());
inputHolder.mStoreField.setText(loyaltyCard.store);
inputHolder.mStoreField.setTextSize(mSettings.getFontSizeMax(mSettings.getMediumFont()));
if (mShowDetails && !loyaltyCard.note.isEmpty()) {
inputHolder.mNoteField.setVisibility(View.VISIBLE);
inputHolder.mNoteField.setText(loyaltyCard.note);
inputHolder.mNoteField.setTextSize(size);
} else {
inputHolder.mNoteField.setVisibility(View.GONE);
}
if (mShowDetails && !loyaltyCard.balance.equals(new BigDecimal("0"))) {
int drawableSize = dpToPx((size * 24) / 14, mContext);
inputHolder.mDivider.setVisibility(View.VISIBLE);
inputHolder.mBalanceField.setVisibility(View.VISIBLE);
Drawable balanceIcon = inputHolder.mBalanceField.getCompoundDrawables()[0];
balanceIcon.setBounds(0, 0, drawableSize, drawableSize);
inputHolder.mBalanceField.setCompoundDrawablesRelative(balanceIcon, null, null, null);
if (mDarkModeEnabled) {
balanceIcon.setColorFilter(BlendModeColorFilterCompat.createBlendModeColorFilterCompat(Color.WHITE, BlendModeCompat.SRC_ATOP));
}
inputHolder.mBalanceField.setText(Utils.formatBalance(mContext, loyaltyCard.balance, loyaltyCard.balanceType));
inputHolder.mBalanceField.setTextSize(size);
} else {
inputHolder.mBalanceField.setVisibility(View.GONE);
}
if (mShowDetails && loyaltyCard.expiry != null) {
int drawableSize = dpToPx((size * 24) / 14, mContext);
inputHolder.mDivider.setVisibility(View.VISIBLE);
inputHolder.mExpiryField.setVisibility(View.VISIBLE);
Drawable expiryIcon = inputHolder.mExpiryField.getCompoundDrawables()[0];
expiryIcon.setBounds(0, 0, drawableSize, drawableSize);
inputHolder.mExpiryField.setCompoundDrawablesRelative(expiryIcon, null, null, null);
if (Utils.hasExpired(loyaltyCard.expiry)) {
expiryIcon.setColorFilter(BlendModeColorFilterCompat.createBlendModeColorFilterCompat(Color.RED, BlendModeCompat.SRC_ATOP));
inputHolder.mExpiryField.setTextColor(Color.RED);
} else if (mDarkModeEnabled) {
expiryIcon.setColorFilter(BlendModeColorFilterCompat.createBlendModeColorFilterCompat(Color.WHITE, BlendModeCompat.SRC_ATOP));
}
inputHolder.mExpiryField.setText(DateFormat.getDateInstance(DateFormat.LONG).format(loyaltyCard.expiry));
inputHolder.mExpiryField.setTextSize(size);
} else {
inputHolder.mExpiryField.setVisibility(View.GONE);
}
Bitmap cardIcon = Utils.retrieveCardImage(mContext, loyaltyCard.id, ImageLocationType.icon);
if (cardIcon != null) {
inputHolder.mCardIcon.setImageBitmap(cardIcon);
inputHolder.mCardIcon.setScaleType(ImageView.ScaleType.CENTER_CROP);
} else {
inputHolder.mCardIcon.setImageBitmap(Utils.generateIcon(mContext, loyaltyCard.store, loyaltyCard.headerColor).getLetterTile());
inputHolder.mCardIcon.setScaleType(ImageView.ScaleType.FIT_CENTER);
}
inputHolder.mIconLayout.setBackgroundColor(loyaltyCard.headerColor != null ? loyaltyCard.headerColor : ContextCompat.getColor(mContext, R.color.colorPrimary));
inputHolder.mStarIcon.setVisibility(loyaltyCard.starStatus != 0 ? View.VISIBLE : View.GONE);
inputHolder.itemView.setActivated(mSelectedItems.get(inputCursor.getPosition(), false));
applyIconAnimation(inputHolder, inputCursor.getPosition());
applyClickEvents(inputHolder, inputCursor.getPosition());
// Force redraw to fix size not shrinking after data change
inputHolder.mRow.requestLayout();
}
private void applyClickEvents(LoyaltyCardListItemViewHolder inputHolder, final int inputPosition) {
inputHolder.mRow.setOnClickListener(inputView -> mListener.onRowClicked(inputPosition));
inputHolder.mRow.setOnLongClickListener(inputView -> {
mListener.onRowLongClicked(inputPosition);
inputView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
return true;
});
}
private void applyIconAnimation(LoyaltyCardListItemViewHolder inputHolder, int inputPosition) {
if (mSelectedItems.get(inputPosition, false)) {
inputHolder.mCardIcon.setVisibility(View.GONE);
inputHolder.mTickIcon.setVisibility(View.VISIBLE);
if (mCurrentSelectedIndex == inputPosition) {
resetCurrentIndex();
}
} else {
inputHolder.mTickIcon.setVisibility(View.GONE);
inputHolder.mCardIcon.setVisibility(View.VISIBLE);
if ((mReverseAllAnimations && mAnimationItemsIndex.get(inputPosition, false)) || mCurrentSelectedIndex == inputPosition) {
resetCurrentIndex();
}
}
}
public void toggleSelection(int inputPosition) {
mCurrentSelectedIndex = inputPosition;
if (mSelectedItems.get(inputPosition, false)) {
mSelectedItems.delete(inputPosition);
mAnimationItemsIndex.delete(inputPosition);
} else {
mSelectedItems.put(inputPosition, true);
mAnimationItemsIndex.put(inputPosition, true);
}
notifyDataSetChanged();
}
public void clearSelections() {
mReverseAllAnimations = true;
mSelectedItems.clear();
notifyDataSetChanged();
}
public int getSelectedItemCount() {
return mSelectedItems.size();
}
public ArrayList<LoyaltyCard> getSelectedItems() {
ArrayList<LoyaltyCard> result = new ArrayList<>();
int i;
for (i = 0; i < mSelectedItems.size(); i++) {
mCursor.moveToPosition(mSelectedItems.keyAt(i));
result.add(LoyaltyCard.toLoyaltyCard(mCursor));
}
return result;
}
private void resetCurrentIndex() {
mCurrentSelectedIndex = -1;
}
public interface CardAdapterListener {
void onRowClicked(int inputPosition);
void onRowLongClicked(int inputPosition);
}
public static class LoyaltyCardListItemViewHolder extends RecyclerView.ViewHolder {
public TextView mStoreField, mNoteField, mBalanceField, mExpiryField;
public ImageView mCardIcon, mStarIcon, mTickIcon;
public MaterialCardView mIconLayout, mRow;
public View mDivider;
public LoyaltyCardListItemViewHolder(View inputView, CardAdapterListener inputListener) {
super(inputView);
mRow = inputView.findViewById(R.id.row);
mIconLayout = inputView.findViewById(R.id.icon_layout);
mDivider = inputView.findViewById(R.id.info_divider);
mStoreField = inputView.findViewById(R.id.store);
mNoteField = inputView.findViewById(R.id.note);
mBalanceField = inputView.findViewById(R.id.balance);
mExpiryField = inputView.findViewById(R.id.expiry);
mCardIcon = inputView.findViewById(R.id.thumbnail);
mStarIcon = inputView.findViewById(R.id.star);
mTickIcon = inputView.findViewById(R.id.selected_thumbnail);
inputView.setOnLongClickListener(view -> {
inputListener.onRowClicked(getAdapterPosition());
inputView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
return true;
});
}
}
public int dpToPx(int dp, Context mContext) {
Resources r = mContext.getResources();
int px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics());
return px;
}
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,15 @@
package protect.card_locker;
public enum LoyaltyCardField {
id,
store,
note,
expiry,
balance,
balanceType,
cardId,
barcodeId,
barcodeType,
headerColor,
starStatus
}

View File

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

View File

File diff suppressed because it is too large Load Diff

View File

@@ -6,62 +6,194 @@ import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.database.Cursor;
import android.database.CursorIndexOutOfBoundsException;
import android.os.Bundle;
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.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.CheckBox;
import android.widget.Toast;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.tabs.TabLayout;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.view.ActionMode;
import androidx.appcompat.widget.SearchView;
import androidx.appcompat.widget.Toolbar;
import androidx.core.splashscreen.SplashScreen;
import androidx.recyclerview.widget.RecyclerView;
import protect.card_locker.preferences.SettingsActivity;
public class MainActivity extends AppCompatActivity implements GestureDetector.OnGestureListener
{
public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCardCursorAdapter.CardAdapterListener, GestureDetector.OnGestureListener {
private static final String TAG = "Catima";
private Menu menu;
private GestureDetector gestureDetector;
protected String filter = "";
private final DBHelper mDB = new DBHelper(this);
private LoyaltyCardCursorAdapter mAdapter;
private ActionMode mCurrentActionMode;
private SearchView mSearchView;
private GestureDetector mGestureDetector;
protected String mFilter = "";
protected Object mGroup = null;
protected DBHelper.LoyaltyCardOrder mOrder = DBHelper.LoyaltyCardOrder.Alpha;
protected DBHelper.LoyaltyCardOrderDirection mOrderDirection = DBHelper.LoyaltyCardOrderDirection.Ascending;
protected int selectedTab = 0;
private RecyclerView mCardList;
private View mHelpText;
private View mNoMatchingCardsText;
private View mNoGroupCardsText;
private ActivityResultLauncher<Intent> mBarcodeScannerLauncher;
private ActionMode.Callback mCurrentActionModeCallback = new ActionMode.Callback() {
@Override
public boolean onCreateActionMode(ActionMode inputMode, Menu inputMenu) {
inputMode.getMenuInflater().inflate(R.menu.card_longclick_menu, inputMenu);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode inputMode, Menu inputMenu) {
return false;
}
@Override
public boolean onActionItemClicked(ActionMode inputMode, MenuItem inputItem) {
if (inputItem.getItemId() == R.id.action_copy_to_clipboard) {
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
String clipboardData;
int cardCount = mAdapter.getSelectedItemCount();
if (cardCount == 1) {
clipboardData = mAdapter.getSelectedItems().get(0).cardId;
} else {
StringBuilder cardIds = new StringBuilder();
for (int i = 0; i < cardCount; i++) {
LoyaltyCard loyaltyCard = mAdapter.getSelectedItems().get(i);
cardIds.append(loyaltyCard.store + ": " + loyaltyCard.cardId);
if (i < (cardCount - 1)) {
cardIds.append("\n");
}
}
clipboardData = cardIds.toString();
}
ClipData clip = ClipData.newPlainText(getString(R.string.card_ids_copied), clipboardData);
clipboard.setPrimaryClip(clip);
Toast.makeText(MainActivity.this, cardCount > 1 ? R.string.copy_to_clipboard_multiple_toast : R.string.copy_to_clipboard_toast, Toast.LENGTH_LONG).show();
inputMode.finish();
return true;
} else if (inputItem.getItemId() == R.id.action_share) {
final ImportURIHelper importURIHelper = new ImportURIHelper(MainActivity.this);
try {
importURIHelper.startShareIntent(mAdapter.getSelectedItems());
} catch (UnsupportedEncodingException e) {
Toast.makeText(MainActivity.this, R.string.failedGeneratingShareURL, Toast.LENGTH_LONG).show();
e.printStackTrace();
}
inputMode.finish();
return true;
} else if (inputItem.getItemId() == R.id.action_edit) {
if (mAdapter.getSelectedItemCount() != 1) {
throw new IllegalArgumentException("Cannot edit more than 1 card at a time");
}
Intent intent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
Bundle bundle = new Bundle();
bundle.putInt(LoyaltyCardEditActivity.BUNDLE_ID, mAdapter.getSelectedItems().get(0).id);
bundle.putBoolean(LoyaltyCardEditActivity.BUNDLE_UPDATE, true);
intent.putExtras(bundle);
startActivity(intent);
inputMode.finish();
return true;
} else if (inputItem.getItemId() == R.id.action_delete) {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
// The following may seem weird, but it is necessary to give translators enough flexibility.
// For example, in Russian, Android's plural quantity "one" actually refers to "any number ending on 1 but not ending in 11".
// So while in English the extra non-plural form seems unnecessary duplication, it is necessary to give translators enough flexibility.
// In here, we use the plain string when meaning exactly 1, and otherwise use the plural forms
if (mAdapter.getSelectedItemCount() == 1) {
builder.setTitle(R.string.deleteTitle);
builder.setMessage(R.string.deleteConfirmation);
} else {
builder.setTitle(getResources().getQuantityString(R.plurals.deleteCardsTitle, mAdapter.getSelectedItemCount(), mAdapter.getSelectedItemCount()));
builder.setMessage(getResources().getQuantityString(R.plurals.deleteCardsConfirmation, mAdapter.getSelectedItemCount(), mAdapter.getSelectedItemCount()));
}
builder.setPositiveButton(R.string.confirm, (dialog, which) -> {
DBHelper db = new DBHelper(MainActivity.this);
for (LoyaltyCard loyaltyCard : mAdapter.getSelectedItems()) {
Log.e(TAG, "Deleting card: " + loyaltyCard.id);
db.deleteLoyaltyCard(MainActivity.this, loyaltyCard.id);
ShortcutHelper.removeShortcut(MainActivity.this, loyaltyCard.id);
}
TabLayout.Tab tab = ((TabLayout) findViewById(R.id.groups)).getTabAt(selectedTab);
mGroup = tab != null ? tab.getTag() : null;
updateLoyaltyCardList();
dialog.dismiss();
});
builder.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
AlertDialog dialog = builder.create();
dialog.show();
return true;
}
return false;
}
@Override
public void onDestroyActionMode(ActionMode inputMode) {
mAdapter.clearSelections();
mCurrentActionMode = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
protected void onCreate(Bundle inputSavedInstanceState) {
super.onCreate(inputSavedInstanceState);
SplashScreen.installSplashScreen(this);
setTitle(R.string.app_name);
setContentView(R.layout.main_activity);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
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());
Log.d("onTabSelected", "Tab Position " + tab.getPosition());
mGroup = tab.getTag();
updateLoyaltyCardList();
// 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.putInt(getString(R.string.sharedpreference_active_tab), tab.getPosition());
activeTabPrefEditor.apply();
}
@@ -76,48 +208,109 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
}
});
gestureDetector = new GestureDetector(this, this);
mGestureDetector = new GestureDetector(this, this);
View.OnTouchListener gestureTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(final View v, final MotionEvent event){
return gestureDetector.onTouchEvent(event);
View.OnTouchListener gestureTouchListener = (v, event) -> mGestureDetector.onTouchEvent(event);
mHelpText = findViewById(R.id.helpText);
mNoMatchingCardsText = findViewById(R.id.noMatchingCardsText);
mNoGroupCardsText = findViewById(R.id.noGroupCardsText);
mCardList = findViewById(R.id.list);
mHelpText.setOnTouchListener(gestureTouchListener);
mNoMatchingCardsText.setOnTouchListener(gestureTouchListener);
mCardList.setOnTouchListener(gestureTouchListener);
mNoGroupCardsText.setOnTouchListener(gestureTouchListener);
mAdapter = new LoyaltyCardCursorAdapter(this, null, this);
mCardList.setAdapter(mAdapter);
registerForContextMenu(mCardList);
mGroup = null;
updateLoyaltyCardList();
/*
* This was added for Huawei, but Huawei is just too much of a fucking pain.
* Just leaving this commented out if needed for the future idk
* https://twitter.com/SylvieLorxu/status/1379437902741012483
*
// Show privacy policy on first run
SharedPreferences privacyPolicyShownPref = getApplicationContext().getSharedPreferences(
getString(R.string.sharedpreference_privacy_policy_shown),
Context.MODE_PRIVATE);
if (privacyPolicyShownPref.getInt(getString(R.string.sharedpreference_privacy_policy_shown), 0) == 0) {
SharedPreferences.Editor privacyPolicyShownPrefEditor = privacyPolicyShownPref.edit();
privacyPolicyShownPrefEditor.putInt(getString(R.string.sharedpreference_privacy_policy_shown), 1);
privacyPolicyShownPrefEditor.apply();
new AlertDialog.Builder(this)
.setTitle(R.string.privacy_policy)
.setMessage(R.string.privacy_policy_popup_text)
.setPositiveButton(R.string.accept, null)
.setNegativeButton(R.string.privacy_policy, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
openPrivacyPolicy();
}
})
.setIcon(android.R.drawable.ic_dialog_info)
.show();
}
*/
mBarcodeScannerLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
Intent intent = result.getData();
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(Utils.BARCODE_SCAN, result.getResultCode(), intent, this);
if (!barcodeValues.isEmpty()) {
Intent newIntent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
Bundle newBundle = new Bundle();
newBundle.putString(LoyaltyCardEditActivity.BUNDLE_BARCODETYPE, barcodeValues.format());
newBundle.putString(LoyaltyCardEditActivity.BUNDLE_CARDID, barcodeValues.content());
Bundle inputBundle = intent.getExtras();
if (inputBundle != null && inputBundle.getString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP) != null) {
newBundle.putString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP, inputBundle.getString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP));
}
newIntent.putExtras(newBundle);
startActivity(newIntent);
}
};
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() {
super.onResume();
if (menu != null)
{
SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
if (mCurrentActionMode != null) {
mAdapter.clearSelections();
mCurrentActionMode.finish();
}
if (!searchView.isIconified()) {
filter = searchView.getQuery().toString();
}
if (mSearchView != null && !mSearchView.isIconified()) {
mFilter = mSearchView.getQuery().toString();
}
// Start of active tab logic
TabLayout groupsTabLayout = findViewById(R.id.groups);
updateTabGroups(groupsTabLayout);
// Restore active tab from Shared Preference
// Restore settings 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);
SharedPreferences sortPref = getApplicationContext().getSharedPreferences(
getString(R.string.sharedpreference_sort),
Context.MODE_PRIVATE);
try {
mOrder = DBHelper.LoyaltyCardOrder.valueOf(sortPref.getString(getString(R.string.sharedpreference_sort_order), null));
mOrderDirection = DBHelper.LoyaltyCardOrderDirection.valueOf(sortPref.getString(getString(R.string.sharedpreference_sort_direction), null));
} catch (IllegalArgumentException | NullPointerException ignored) {
}
Object group = null;
mGroup = null;
if (groupsTabLayout.getTabCount() != 0) {
TabLayout.Tab tab = groupsTabLayout.getTabAt(selectedTab);
@@ -127,142 +320,76 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
groupsTabLayout.selectTab(tab);
assert tab != null;
group = tab.getTag();
mGroup = tab.getTag();
}
updateLoyaltyCardList(filter, group);
updateLoyaltyCardList();
// 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);
addButton.setOnClickListener(v -> {
Intent intent = new Intent(getApplicationContext(), ScanActivity.class);
Bundle bundle = new Bundle();
if (selectedTab != 0) {
bundle.putString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP, groupsTabLayout.getTabAt(selectedTab).getText().toString());
}
intent.putExtras(bundle);
mBarcodeScannerLauncher.launch(intent);
});
}
@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);
}
addButton.bringToFront();
}
@Override
public void onBackPressed() {
if (menu == null)
{
super.onBackPressed();
if (!mSearchView.isIconified()) {
mSearchView.setIconified(true);
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();
}
}
super.onBackPressed();
}
private void updateLoyaltyCardList(String filterText, Object tag)
{
private void updateLoyaltyCardList() {
Group group = null;
if (tag != null) {
group = (Group) tag;
if (mGroup != null) {
group = (Group) mGroup;
}
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);
mAdapter.swapCursor(mDB.getLoyaltyCardCursor(mFilter, group, mOrder, mOrderDirection));
Cursor cardCursor = db.getLoyaltyCardCursor(filterText, group);
if(db.getLoyaltyCardCount() > 0)
{
if (mDB.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);
mHelpText.setVisibility(View.GONE);
mNoGroupCardsText.setVisibility(View.GONE);
if (mAdapter.getItemCount() > 0) {
mCardList.setVisibility(View.VISIBLE);
mNoMatchingCardsText.setVisibility(View.GONE);
} else {
mCardList.setVisibility(View.GONE);
if (!mFilter.isEmpty()) {
// Actual Empty Search Result
mNoMatchingCardsText.setVisibility(View.VISIBLE);
mNoGroupCardsText.setVisibility(View.GONE);
} else {
// Group Tab with no Group Cards
mNoMatchingCardsText.setVisibility(View.GONE);
mNoGroupCardsText.setVisibility(View.VISIBLE);
}
}
else
{
noMatchingCardsText.setVisibility(View.VISIBLE);
}
}
else
{
cardList.setVisibility(View.GONE);
helpText.setVisibility(View.VISIBLE);
noMatchingCardsText.setVisibility(View.GONE);
} else {
mCardList.setVisibility(View.GONE);
mHelpText.setVisibility(View.VISIBLE);
mNoMatchingCardsText.setVisibility(View.GONE);
mNoGroupCardsText.setVisibility(View.GONE);
}
final LoyaltyCardCursorAdapter adapter = new LoyaltyCardCursorAdapter(this, cardCursor);
cardList.setAdapter(adapter);
registerForContextMenu(cardList);
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);
Intent i = new Intent(view.getContext(), LoyaltyCardViewActivity.class);
i.setAction("");
final Bundle b = new Bundle();
b.putInt("id", loyaltyCard.id);
i.putExtras(b);
ShortcutHelper.updateShortcuts(MainActivity.this, loyaltyCard, i);
startActivityForResult(i, Utils.MAIN_REQUEST);
}
});
if (mCurrentActionMode != null) {
mCurrentActionMode.finish();
}
}
public void updateTabGroups(TabLayout groupsTabLayout)
{
public void updateTabGroups(TabLayout groupsTabLayout) {
final DBHelper db = new DBHelper(this);
List<Group> newGroups = db.getGroups();
@@ -291,66 +418,21 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo)
{
super.onCreateContextMenu(menu, v, menuInfo);
if (v.getId()==R.id.list)
{
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.card_longclick_menu, menu);
}
}
@Override
public boolean onContextItemSelected(MenuItem item)
{
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
ListView listView = findViewById(R.id.list);
Cursor cardCursor = (Cursor)listView.getItemAtPosition(info.position);
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cardCursor);
if(item.getItemId() == R.id.action_clipboard)
{
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText(card.store, card.cardId);
clipboard.setPrimaryClip(clip);
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);
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
this.menu = menu;
getMenuInflater().inflate(R.menu.main_menu, menu);
public boolean onCreateOptionsMenu(Menu inputMenu) {
getMenuInflater().inflate(R.menu.main_menu, inputMenu);
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);
mSearchView = (SearchView) inputMenu.findItem(R.id.action_search).getActionView();
mSearchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
mSearchView.setSubmitButtonEnabled(false);
searchView.setOnCloseListener(new SearchView.OnCloseListener() {
@Override
public boolean onClose() {
invalidateOptionsMenu();
return false;
}
mSearchView.setOnCloseListener(() -> {
invalidateOptionsMenu();
return false;
});
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
return false;
@@ -358,65 +440,123 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
@Override
public boolean onQueryTextChange(String newText) {
filter = newText;
mFilter = newText;
TabLayout groupsTabLayout = findViewById(R.id.groups);
TabLayout.Tab currentTab = groupsTabLayout.getTabAt(groupsTabLayout.getSelectedTabPosition());
mGroup = currentTab != null ? currentTab.getTag() : null;
updateLoyaltyCardList(
newText,
currentTab != null ? currentTab.getTag() : null
);
updateLoyaltyCardList();
return true;
}
});
}
return super.onCreateOptionsMenu(menu);
return super.onCreateOptionsMenu(inputMenu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
int id = item.getItemId();
public boolean onOptionsItemSelected(MenuItem inputItem) {
int id = inputItem.getItemId();
if (id == R.id.action_manage_groups)
{
if (id == R.id.action_unfold) {
boolean shouldShow = !mAdapter.showingDetails();
if (shouldShow) {
inputItem.setIcon(R.drawable.ic_baseline_unfold_less_24);
inputItem.setTitle(R.string.action_hide_details);
} else {
inputItem.setIcon(R.drawable.ic_baseline_unfold_more_24);
inputItem.setTitle(R.string.action_show_details);
}
mAdapter.showDetails(shouldShow);
return true;
}
if (id == R.id.action_sort) {
TabLayout.Tab tab = ((TabLayout) findViewById(R.id.groups)).getTabAt(selectedTab);
AtomicInteger currentIndex = new AtomicInteger();
List<DBHelper.LoyaltyCardOrder> loyaltyCardOrders = Arrays.asList(DBHelper.LoyaltyCardOrder.values());
for (int i = 0; i < loyaltyCardOrders.size(); i++) {
if (mOrder == loyaltyCardOrders.get(i)) {
currentIndex.set(i);
break;
}
}
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle(R.string.sort_by);
final View customLayout = getLayoutInflater().inflate(R.layout.sorting_option, null);
builder.setView(customLayout);
CheckBox ch = (CheckBox) customLayout.findViewById(R.id.checkBox_reverse);
ch.setChecked(mOrderDirection == DBHelper.LoyaltyCardOrderDirection.Descending);
builder.setSingleChoiceItems(R.array.sort_types_array, currentIndex.get(), (dialog, which) -> currentIndex.set(which));
builder.setPositiveButton(R.string.sort, (dialog, which) -> {
if (ch.isChecked()) {
setSort(loyaltyCardOrders.get(currentIndex.get()), DBHelper.LoyaltyCardOrderDirection.Descending);
} else {
setSort(loyaltyCardOrders.get(currentIndex.get()), DBHelper.LoyaltyCardOrderDirection.Ascending);
}
dialog.dismiss();
});
builder.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
AlertDialog dialog = builder.create();
dialog.show();
return true;
}
if (id == R.id.action_manage_groups) {
Intent i = new Intent(getApplicationContext(), ManageGroupsActivity.class);
startActivityForResult(i, Utils.MAIN_REQUEST);
startActivity(i);
return true;
}
if(id == R.id.action_import_export)
{
if (id == R.id.action_import_export) {
Intent i = new Intent(getApplicationContext(), ImportExportActivity.class);
startActivityForResult(i, Utils.MAIN_REQUEST);
startActivity(i);
return true;
}
if(id == R.id.action_settings)
{
if (id == R.id.action_settings) {
Intent i = new Intent(getApplicationContext(), SettingsActivity.class);
startActivityForResult(i, Utils.MAIN_REQUEST);
startActivity(i);
return true;
}
if(id == R.id.action_about)
{
if (id == R.id.action_about) {
Intent i = new Intent(getApplicationContext(), AboutActivity.class);
startActivityForResult(i, Utils.MAIN_REQUEST);
startActivity(i);
return true;
}
return super.onOptionsItemSelected(item);
return super.onOptionsItemSelected(inputItem);
}
protected static boolean isDarkModeEnabled(Context inputContext)
{
Configuration config = inputContext.getResources().getConfiguration();
int currentNightMode = config.uiMode & Configuration.UI_MODE_NIGHT_MASK;
return (currentNightMode == Configuration.UI_MODE_NIGHT_YES);
private void setSort(DBHelper.LoyaltyCardOrder order, DBHelper.LoyaltyCardOrderDirection direction) {
// Update values
mOrder = order;
mOrderDirection = direction;
// Store in Shared Preference to restore next app launch
SharedPreferences sortPref = getApplicationContext().getSharedPreferences(
getString(R.string.sharedpreference_sort),
Context.MODE_PRIVATE);
SharedPreferences.Editor sortPrefEditor = sortPref.edit();
sortPrefEditor.putString(getString(R.string.sharedpreference_sort_order), order.name());
sortPrefEditor.putString(getString(R.string.sharedpreference_sort_direction), direction.name());
sortPrefEditor.apply();
// Update card list
updateLoyaltyCardList();
}
@Override
@@ -444,6 +584,12 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
mGestureDetector.onTouchEvent(ev);
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
Log.d(TAG, "On fling");
@@ -459,9 +605,10 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
}
Integer currentTab = groupsTabLayout.getSelectedTabPosition();
Log.d("onFling", "Current Tab " + currentTab);
// Swipe right
if (velocityX < -150) {
Log.d("onFling", "Right Swipe detected " + velocityX);
Integer nextTab = currentTab + 1;
if (nextTab == groupsTabLayout.getTabCount()) {
@@ -475,6 +622,7 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
// Swipe left
if (velocityX > 150) {
Log.d("onFling", "Left Swipe detected " + velocityX);
Integer nextTab = currentTab - 1;
if (nextTab < 0) {
@@ -488,4 +636,71 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
return false;
}
}
@Override
public void onRowLongClicked(int inputPosition) {
enableActionMode(inputPosition);
}
private void enableActionMode(int inputPosition) {
if (mCurrentActionMode == null) {
mCurrentActionMode = startSupportActionMode(mCurrentActionModeCallback);
}
toggleSelection(inputPosition);
}
private void toggleSelection(int inputPosition) {
mAdapter.toggleSelection(inputPosition);
int count = mAdapter.getSelectedItemCount();
if (count == 0) {
mCurrentActionMode.finish();
} else {
mCurrentActionMode.setTitle(getResources().getQuantityString(R.plurals.selectedCardCount, count, count));
MenuItem editItem = mCurrentActionMode.getMenu().findItem(R.id.action_edit);
if (count == 1) {
editItem.setVisible(true);
editItem.setEnabled(true);
} else {
editItem.setVisible(false);
editItem.setEnabled(false);
}
mCurrentActionMode.invalidate();
}
}
@Override
public void onRowClicked(int inputPosition) {
if (mAdapter.getSelectedItemCount() > 0) {
enableActionMode(inputPosition);
} else {
// FIXME
//
// There is a really nasty edge case that can happen when someone taps a card but right
// after it swipes (very small window, hard to reproduce). The cursor gets replaced and
// may not have a card at the ID number that is returned from onRowClicked.
//
// The proper fix, obviously, would involve makes sure an onFling can't happen while a
// click is being processed. Sadly, I have not yet found a way to make that possible.
LoyaltyCard loyaltyCard;
try {
loyaltyCard = mAdapter.getCard(inputPosition);
} catch (CursorIndexOutOfBoundsException e) {
Log.w(TAG, "Prevented crash from tap + swipe on ID " + inputPosition + ": " + e);
return;
}
Intent i = new Intent(this, LoyaltyCardViewActivity.class);
i.setAction("");
final Bundle b = new Bundle();
b.putInt("id", loyaltyCard.id);
i.putExtras(b);
ShortcutHelper.updateShortcuts(MainActivity.this, loyaltyCard);
startActivity(i);
}
}
}

View File

@@ -0,0 +1,216 @@
package protect.card_locker;
import android.content.Intent;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.RecyclerView;
public class ManageGroupActivity extends CatimaAppCompatActivity implements ManageGroupCursorAdapter.CardAdapterListener {
private final DBHelper mDB = new DBHelper(this);
private ManageGroupCursorAdapter mAdapter;
private final String SAVE_INSTANCE_ADAPTER_STATE = "adapterState";
private final String SAVE_INSTANCE_CURRENT_GROUP_NAME = "currentGroupName";
protected Group mGroup = null;
private RecyclerView mCardList;
private TextView mHelpText;
private EditText mGroupNameText;
private boolean mGroupNameNotInUse;
@Override
protected void onCreate(Bundle inputSavedInstanceState) {
super.onCreate(inputSavedInstanceState);
setContentView(R.layout.activity_manage_group);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mHelpText = findViewById(R.id.helpText);
mCardList = findViewById(R.id.list);
FloatingActionButton saveButton = findViewById(R.id.fabSave);
mGroupNameText = findViewById(R.id.editTextGroupName);
mGroupNameText.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) {
}
@Override
public void afterTextChanged(Editable s) {
mGroupNameNotInUse = true;
mGroupNameText.setError(null);
String currentGroupName = mGroupNameText.getText().toString().trim();
if (currentGroupName.length() == 0) {
mGroupNameText.setError(getResources().getText(R.string.group_name_is_empty));
return;
}
if (!mGroup._id.equals(currentGroupName)) {
if (mDB.getGroup(currentGroupName) != null) {
mGroupNameNotInUse = false;
mGroupNameText.setError(getResources().getText(R.string.group_name_already_in_use));
} else {
mGroupNameNotInUse = true;
}
}
}
});
Intent intent = getIntent();
String groupId = intent.getStringExtra("group");
if (groupId == null) {
throw (new IllegalArgumentException("this activity expects a group loaded into it's intent"));
}
Log.d("groupId", "groupId: " + groupId);
mGroup = mDB.getGroup(groupId);
if (mGroup == null) {
throw (new IllegalArgumentException("cannot load group " + groupId + " from database"));
}
mGroupNameText.setText(mGroup._id);
setTitle(getString(R.string.editGroup, mGroup._id));
mAdapter = new ManageGroupCursorAdapter(this, null, this, mGroup);
mCardList.setAdapter(mAdapter);
registerForContextMenu(mCardList);
if (inputSavedInstanceState != null) {
mAdapter.importInGroupState(integerArrayToAdapterState(inputSavedInstanceState.getIntegerArrayList(SAVE_INSTANCE_ADAPTER_STATE)));
mGroupNameText.setText(inputSavedInstanceState.getString(SAVE_INSTANCE_CURRENT_GROUP_NAME));
}
ActionBar actionBar = getSupportActionBar();
if (actionBar == null) {
throw (new RuntimeException("mActionBar is not expected to be null here"));
}
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setDisplayShowHomeEnabled(true);
saveButton.setOnClickListener(v -> {
String currentGroupName = mGroupNameText.getText().toString().trim();
if (!currentGroupName.equals(mGroup._id)) {
if (currentGroupName.length() == 0) {
Toast.makeText(getApplicationContext(), R.string.group_name_is_empty, Toast.LENGTH_SHORT).show();
return;
}
if (!mGroupNameNotInUse) {
Toast.makeText(getApplicationContext(), R.string.group_name_already_in_use, Toast.LENGTH_SHORT).show();
return;
}
}
mAdapter.commitToDatabase();
if (!currentGroupName.equals(mGroup._id)) {
mDB.updateGroup(mGroup._id, currentGroupName);
}
Toast.makeText(getApplicationContext(), R.string.group_updated, Toast.LENGTH_SHORT).show();
finish();
});
// this setText is here because content_main.xml is reused from main activity
mHelpText.setText(getResources().getText(R.string.noGiftCardsGroup));
updateLoyaltyCardList();
}
private ArrayList<Integer> adapterStateToIntegerArray(HashMap<Integer, Boolean> adapterState) {
ArrayList<Integer> ret = new ArrayList<>(adapterState.size() * 2);
for (Map.Entry<Integer, Boolean> entry : adapterState.entrySet()) {
ret.add(entry.getKey());
ret.add(entry.getValue() ? 1 : 0);
}
return ret;
}
private HashMap<Integer, Boolean> integerArrayToAdapterState(ArrayList<Integer> in) {
HashMap<Integer, Boolean> ret = new HashMap<>();
if (in.size() % 2 != 0) {
throw (new RuntimeException("failed restoring adapterState from integer array list"));
}
for (int i = 0; i < in.size(); i += 2) {
ret.put(in.get(i), in.get(i + 1) == 1);
}
return ret;
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putIntegerArrayList(SAVE_INSTANCE_ADAPTER_STATE, adapterStateToIntegerArray(mAdapter.exportInGroupState()));
outState.putString(SAVE_INSTANCE_CURRENT_GROUP_NAME, mGroupNameText.getText().toString());
}
private void updateLoyaltyCardList() {
mAdapter.swapCursor(mDB.getLoyaltyCardCursor());
if (mAdapter.getItemCount() == 0) {
mCardList.setVisibility(View.GONE);
mHelpText.setVisibility(View.VISIBLE);
} else {
mCardList.setVisibility(View.VISIBLE);
mHelpText.setVisibility(View.GONE);
}
}
private void leaveWithoutSaving() {
if (hasChanged()) {
AlertDialog.Builder builder = new AlertDialog.Builder(ManageGroupActivity.this);
builder.setTitle(R.string.leaveWithoutSaveTitle);
builder.setMessage(R.string.leaveWithoutSaveConfirmation);
builder.setPositiveButton(R.string.confirm, (dialog, which) -> finish());
builder.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
AlertDialog dialog = builder.create();
dialog.show();
} else {
finish();
}
}
@Override
public void onBackPressed() {
leaveWithoutSaving();
}
@Override
public boolean onSupportNavigateUp() {
onBackPressed();
return true;
}
private boolean hasChanged() {
return mAdapter.hasChanged() || !mGroup._id.equals(mGroupNameText.getText().toString().trim());
}
@Override
public void onRowLongClicked(int inputPosition) {
// do nothing for now
}
@Override
public void onRowClicked(int inputPosition) {
mAdapter.toggleSelection(inputPosition);
}
}

View File

@@ -0,0 +1,108 @@
package protect.card_locker;
import android.content.Context;
import android.database.Cursor;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ManageGroupCursorAdapter extends LoyaltyCardCursorAdapter {
private HashMap<Integer, Integer> mIndexCardMap;
private HashMap<Integer, Boolean> mInGroupOverlay;
private HashMap<Integer, Boolean> mIsLoyaltyCardInGroupCache;
private HashMap<Integer, List<Group>> mGetGroupCache;
final private Group mGroup;
final private DBHelper mDb;
public ManageGroupCursorAdapter(Context inputContext, Cursor inputCursor, CardAdapterListener inputListener, Group group) {
super(inputContext, inputCursor, inputListener);
mGroup = new Group(group._id, group.order);
mInGroupOverlay = new HashMap<>();
mDb = new DBHelper(inputContext);
}
@Override
public void swapCursor(Cursor inputCursor) {
super.swapCursor(inputCursor);
mIndexCardMap = new HashMap<>();
mIsLoyaltyCardInGroupCache = new HashMap<>();
mGetGroupCache = new HashMap<>();
}
@Override
public void onBindViewHolder(LoyaltyCardListItemViewHolder inputHolder, Cursor inputCursor) {
LoyaltyCard loyaltyCard = LoyaltyCard.toLoyaltyCard(inputCursor);
Boolean overlayValue = mInGroupOverlay.get(loyaltyCard.id);
if ((overlayValue != null ? overlayValue : isLoyaltyCardInGroup(loyaltyCard.id))) {
mAnimationItemsIndex.put(inputCursor.getPosition(), true);
mSelectedItems.put(inputCursor.getPosition(), true);
}
mIndexCardMap.put(inputCursor.getPosition(), loyaltyCard.id);
super.onBindViewHolder(inputHolder, inputCursor);
}
private List<Group> getGroups(int cardId) {
List<Group> cache = mGetGroupCache.get(cardId);
if (cache != null) {
return cache;
}
List<Group> groups = mDb.getLoyaltyCardGroups(cardId);
mGetGroupCache.put(cardId, groups);
return groups;
}
private boolean isLoyaltyCardInGroup(int cardId) {
Boolean cache = mIsLoyaltyCardInGroupCache.get(cardId);
if (cache != null) {
return cache;
}
List<Group> groups = getGroups(cardId);
if (groups.contains(mGroup)) {
mIsLoyaltyCardInGroupCache.put(cardId, true);
return true;
}
mIsLoyaltyCardInGroupCache.put(cardId, false);
return false;
}
@Override
public void toggleSelection(int inputPosition) {
super.toggleSelection(inputPosition);
Integer cardId = mIndexCardMap.get(inputPosition);
if (cardId == null) {
throw (new RuntimeException("cardId should not be null here"));
}
Boolean overlayValue = mInGroupOverlay.get(cardId);
if (overlayValue == null) {
mInGroupOverlay.put(cardId, !isLoyaltyCardInGroup(cardId));
} else {
mInGroupOverlay.remove(cardId);
}
}
public boolean hasChanged() {
return mInGroupOverlay.size() > 0;
}
public void commitToDatabase() {
for (Map.Entry<Integer, Boolean> entry : mInGroupOverlay.entrySet()) {
int cardId = entry.getKey();
List<Group> groups = getGroups(cardId);
if (entry.getValue()) {
groups.add(mGroup);
} else {
groups.remove(mGroup);
}
mDb.setLoyaltyCardGroups(cardId, groups);
}
}
public void importInGroupState(HashMap<Integer, Boolean> cardIdInGroupMap) {
mInGroupOverlay = new HashMap<>(cardIdInGroupMap);
}
public HashMap<Integer, Boolean> exportInGroupState() {
return new HashMap<>(mInGroupOverlay);
}
}

View File

@@ -1,18 +1,16 @@
package protect.card_locker;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
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 android.widget.Toast;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
@@ -20,44 +18,52 @@ import java.util.List;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
public class ManageGroupsActivity extends AppCompatActivity
{
public class ManageGroupsActivity extends CatimaAppCompatActivity implements GroupCursorAdapter.GroupAdapterListener {
private static final String TAG = "Catima";
private final DBHelper db = new DBHelper(this);
private final DBHelper mDb = new DBHelper(this);
private TextView mHelpText;
private RecyclerView mGroupList;
GroupCursorAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState)
{
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle(R.string.groups);
setContentView(R.layout.manage_groups_activity);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
{
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();
}
});
addButton.setOnClickListener(v -> createGroup());
addButton.bringToFront();
mGroupList = findViewById(R.id.list);
mHelpText = findViewById(R.id.helpText);
// Init group list
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
mGroupList.setLayoutManager(mLayoutManager);
mGroupList.setItemAnimator(new DefaultItemAnimator());
mAdapter = new GroupCursorAdapter(this, null, this);
mGroupList.setAdapter(mAdapter);
updateGroupList();
}
@Override
@@ -65,33 +71,21 @@ public class ManageGroupsActivity extends AppCompatActivity
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);
private void updateGroupList() {
mAdapter.swapCursor(mDb.getGroupCursor());
if(db.getGroupCount() > 0)
{
groupList.setVisibility(View.VISIBLE);
helpText.setVisibility(View.GONE);
}
else
{
groupList.setVisibility(View.GONE);
helpText.setVisibility(View.VISIBLE);
if (mDb.getGroupCount() == 0) {
mGroupList.setVisibility(View.GONE);
mHelpText.setVisibility(View.VISIBLE);
return;
}
Cursor groupCursor = db.getGroupCursor();
final GroupCursorAdapter adapter = new GroupCursorAdapter(this, groupCursor);
groupList.setAdapter(adapter);
registerForContextMenu(groupList);
mGroupList.setVisibility(View.VISIBLE);
mHelpText.setVisibility(View.GONE);
}
private void invalidateHomescreenActiveTab()
{
private void invalidateHomescreenActiveTab() {
SharedPreferences activeTabPref = getApplicationContext().getSharedPreferences(
getString(R.string.sharedpreference_active_tab),
Context.MODE_PRIVATE);
@@ -101,8 +95,7 @@ public class ManageGroupsActivity extends AppCompatActivity
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
@@ -112,121 +105,44 @@ public class ManageGroupsActivity extends AppCompatActivity
return super.onOptionsItemSelected(item);
}
public void moveGroupUp(View view) {
moveGroup(view, true);
}
public void moveGroupDown(View view) {
moveGroup(view, false);
}
public void editGroup(View view) {
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);
AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.AlertDialogTheme);
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.setPositiveButton(getString(R.string.ok), (dialog, which) -> {
String inputString = input.getText().toString().trim();
if (inputString.length() == 0) {
Toast.makeText(getApplicationContext(), R.string.group_name_is_empty, Toast.LENGTH_SHORT).show();
return;
}
});
builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
if (mDb.getGroup(inputString) != null) {
Toast.makeText(getApplicationContext(), R.string.group_name_already_in_use, Toast.LENGTH_SHORT).show();
return;
}
mDb.insertGroup(inputString);
updateGroupList();
});
builder.setNegativeButton(getString(R.string.cancel), (dialog, 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);
private String getGroupName(View view) {
TextView groupNameTextView = view.findViewById(R.id.name);
return (String) groupNameTextView.getText();
}
private void moveGroup(View view, boolean up) {
final String groupName = getGroupname(view);
List<Group> groups = mDb.getGroups();
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();
}
int currentIndex = mDb.getGroup(groupName).order;
int newIndex;
// Reinsert group in correct position
if (up) {
@@ -244,7 +160,7 @@ public class ManageGroupsActivity extends AppCompatActivity
groups.add(newIndex, group);
// Update database
db.reorderGroups(groups);
mDb.reorderGroups(groups);
// Update UI
updateGroupList();
@@ -252,4 +168,40 @@ public class ManageGroupsActivity extends AppCompatActivity
// Ordering may have changed, so invalidate
invalidateHomescreenActiveTab();
}
@Override
public void onMoveDownButtonClicked(View view) {
moveGroup(view, false);
}
@Override
public void onMoveUpButtonClicked(View view) {
moveGroup(view, true);
}
@Override
public void onEditButtonClicked(View view) {
Intent intent = new Intent(this, ManageGroupActivity.class);
intent.putExtra("group", getGroupName(view));
startActivity(intent);
}
@Override
public void onDeleteButtonClicked(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), (dialog, which) -> {
mDb.deleteGroup(groupName);
updateGroupList();
// Delete may change ordering, so invalidate
invalidateHomescreenActiveTab();
});
builder.setNegativeButton(getString(R.string.cancel), (dialog, which) -> dialog.cancel());
AlertDialog dialog = builder.create();
dialog.show();
}
}

View File

@@ -1,57 +0,0 @@
package protect.card_locker;
import android.util.Log;
import java.io.IOException;
import java.io.OutputStreamWriter;
public class MultiFormatExporter
{
private static final String TAG = "Catima";
/**
* Attempts to export data to the output stream in the
* given format, if possible.
*
* The output stream is closed on success.
*
* @return true if the database was successfully exported,
* false otherwise. If false, partial data may have been
* written to the output stream, and it should be discarded.
*/
public static boolean exportData(DBHelper db, OutputStreamWriter output, DataFormat format)
{
DatabaseExporter exporter = null;
switch(format)
{
case CSV:
exporter = new CsvDatabaseExporter();
break;
}
if(exporter != null)
{
try
{
exporter.exportData(db, output);
return true;
}
catch(IOException e)
{
Log.e(TAG, "Failed to export data", e);
}
catch(InterruptedException e)
{
Log.e(TAG, "Failed to export data", e);
}
return false;
}
else
{
Log.e(TAG, "Unsupported data format exported: " + format.name());
return false;
}
}
}

View File

@@ -1,53 +0,0 @@
package protect.card_locker;
import android.util.Log;
import java.io.IOException;
import java.io.InputStreamReader;
public class MultiFormatImporter
{
private static final String TAG = "Catima";
/**
* Attempts to import data from the input stream of the
* given format into the database.
*
* The input stream is not closed, and doing so is the
* responsibility of the caller.
*
* @return true if the database was successfully imported,
* false otherwise. If false, no data was written to
* the database.
*/
public static boolean importData(DBHelper db, InputStreamReader input, DataFormat format)
{
DatabaseImporter importer = null;
switch(format)
{
case CSV:
importer = new CsvDatabaseImporter();
break;
}
if (importer != null)
{
try
{
importer.importData(db, input);
return true;
}
catch(IOException | FormatException | InterruptedException e)
{
Log.e(TAG, "Failed to import data", e);
}
}
else
{
Log.e(TAG, "Unsupported data format imported: " + format.name());
}
return false;
}
}

View File

@@ -2,11 +2,14 @@ package protect.card_locker;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import com.google.zxing.ResultPoint;
import com.google.zxing.client.android.Intents;
@@ -17,49 +20,62 @@ import com.journeyapps.barcodescanner.DecoratedBarcodeView;
import java.util.List;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
/**
* Custom Scannner Activity extending from Activity to display a custom layout form scanner view.
*
* <p>
* 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 {
public class ScanActivity extends CatimaAppCompatActivity {
private static final String TAG = "Catima";
private CaptureManager capture;
private DecoratedBarcodeView barcodeScannerView;
private String cardId;
private String addGroup;
private boolean torch = false;
private ActivityResultLauncher<Intent> manualAddLauncher;
// can't use the pre-made contract because that launches the file manager for image type instead of gallery
private ActivityResultLauncher<Intent> photoPickerLauncher;
private void extractIntentFields(Intent intent) {
final Bundle b = intent.getExtras();
cardId = b != null ? b.getString("cardId") : null;
cardId = b != null ? b.getString(LoyaltyCardEditActivity.BUNDLE_CARDID) : null;
addGroup = b != null ? b.getString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP) : null;
Log.d(TAG, "Scan activity: id=" + cardId);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle(R.string.scanCardBarcode);
setContentView(R.layout.scan_activity);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
{
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
extractIntentFields(getIntent());
manualAddLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> handleActivityResult(Utils.SELECT_BARCODE_REQUEST, result.getResultCode(), result.getData()));
photoPickerLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> handleActivityResult(Utils.BARCODE_IMPORT_FROM_IMAGE_FILE, result.getResultCode(), result.getData()));
findViewById(R.id.add_from_image).setOnClickListener(this::addFromImage);
findViewById(R.id.add_manually).setOnClickListener(this::addManually);
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);
capture = new CatimaCaptureManager(this, barcodeScannerView);
Intent captureIntent = new Intent();
Bundle captureIntentBundle = new Bundle();
captureIntentBundle.putBoolean(Intents.Scan.BEEP_ENABLED, false);
@@ -72,7 +88,10 @@ public class ScanActivity extends AppCompatActivity {
Intent scanResult = new Intent();
Bundle scanResultBundle = new Bundle();
scanResultBundle.putString(BarcodeSelectorActivity.BARCODE_CONTENTS, result.getText());
scanResultBundle.putString(BarcodeSelectorActivity.BARCODE_FORMAT, result.getBarcodeFormat().toString());
scanResultBundle.putString(BarcodeSelectorActivity.BARCODE_FORMAT, result.getBarcodeFormat().name());
if (addGroup != null) {
scanResultBundle.putString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP, addGroup);
}
scanResult.putExtras(scanResultBundle);
ScanActivity.this.setResult(RESULT_OK, scanResult);
finish();
@@ -115,30 +134,59 @@ public class ScanActivity extends AppCompatActivity {
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
if (item.getItemId() == android.R.id.home)
{
public boolean onCreateOptionsMenu(Menu menu) {
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH)) {
getMenuInflater().inflate(R.menu.scan_menu, menu);
}
barcodeScannerView.setTorchOff();
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
setResult(Activity.RESULT_CANCELED);
finish();
return true;
} else if (item.getItemId() == R.id.action_toggle_flashlight) {
if (torch) {
torch = false;
barcodeScannerView.setTorchOff();
item.setTitle(R.string.turn_flashlight_on);
item.setIcon(R.drawable.ic_flashlight_off_white_24dp);
} else {
torch = true;
barcodeScannerView.setTorchOn();
item.setTitle(R.string.turn_flashlight_off);
item.setIcon(R.drawable.ic_flashlight_on_white_24dp);
}
}
return super.onOptionsItemSelected(item);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent)
{
private void handleActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent);
BarcodeValues barcodeValues;
try {
barcodeValues = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent, this);
} catch (NullPointerException e) {
Toast.makeText(this, R.string.errorReadingImage, Toast.LENGTH_LONG).show();
return;
}
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());
if (addGroup != null) {
manualResultBundle.putString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP, addGroup);
}
manualResult.putExtras(manualResultBundle);
ScanActivity.this.setResult(RESULT_OK, manualResult);
finish();
@@ -152,6 +200,12 @@ public class ScanActivity extends AppCompatActivity {
b.putString("initialCardId", cardId);
i.putExtras(b);
}
startActivityForResult(i, Utils.SELECT_BARCODE_REQUEST);
manualAddLauncher.launch(i);
}
public void addFromImage(View view) {
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
photoPickerIntent.setType("image/*");
photoPickerLauncher.launch(photoPickerIntent);
}
}

View File

@@ -1,20 +1,25 @@
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 android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Bundle;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
class ShortcutHelper
{
import androidx.core.content.pm.ShortcutInfoCompat;
import androidx.core.content.pm.ShortcutManagerCompat;
import androidx.core.graphics.ColorUtils;
import androidx.core.graphics.drawable.IconCompat;
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
@@ -22,6 +27,14 @@ class ShortcutHelper
// chance of being shown.
private static final int MAX_SHORTCUTS = 3;
// https://developer.android.com/reference/android/graphics/drawable/AdaptiveIconDrawable.html
private static final int ADAPTIVE_BITMAP_SCALE = 1;
private static final int ADAPTIVE_BITMAP_SIZE = 108 * ADAPTIVE_BITMAP_SCALE;
private static final int ADAPTIVE_BITMAP_VISIBLE_SIZE = 72 * ADAPTIVE_BITMAP_SCALE;
private static final int ADAPTIVE_BITMAP_IMAGE_SIZE = ADAPTIVE_BITMAP_VISIBLE_SIZE + 5 * ADAPTIVE_BITMAP_SCALE;
private static final int PADDING_COLOR = Color.argb(255, 255, 255, 255);
private static final int PADDING_COLOR_OVERLAY = Color.argb(127, 0, 0, 0);
/**
* 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,
@@ -29,125 +42,117 @@ class ShortcutHelper
* 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());
static void updateShortcuts(Context context, LoyaltyCard card) {
LinkedList<ShortcutInfoCompat> list = new LinkedList<>(ShortcutManagerCompat.getDynamicShortcuts(context));
String shortcutId = Integer.toString(card.id);
DBHelper dbHelper = new DBHelper(context);
// 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;
}
}
});
String shortcutId = Integer.toString(card.id);
Integer foundIndex = null;
// 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, Comparator.comparingInt(ShortcutInfoCompat::getRank));
for(int index = 0; index < list.size(); index++)
{
if(list.get(index).getId().equals(shortcutId))
{
// Found the item already
foundIndex = index;
break;
}
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.
ShortcutInfoCompat 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();
}
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();
}
ShortcutInfoCompat shortcut = createShortcutBuilder(context, card).build();
ShortcutInfo shortcut = new ShortcutInfo.Builder(context, Integer.toString(card.id))
.setShortLabel(card.store)
.setLongLabel(card.store)
.setIntent(intent)
list.addFirst(shortcut);
}
LinkedList<ShortcutInfoCompat> finalList = new LinkedList<>();
// The ranks are now updated; the order in the list is the rank.
for (int index = 0; index < list.size(); index++) {
ShortcutInfoCompat prevShortcut = list.get(index);
LoyaltyCard loyaltyCard = dbHelper.getLoyaltyCard(Integer.parseInt(prevShortcut.getId()));
ShortcutInfoCompat updatedShortcut = createShortcutBuilder(context, loyaltyCard)
.setRank(index)
.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);
finalList.addLast(updatedShortcut);
}
ShortcutManagerCompat.setDynamicShortcuts(context, 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();
static void removeShortcut(Context context, int cardId) {
List<ShortcutInfoCompat> list = ShortcutManagerCompat.getDynamicShortcuts(context);
String shortcutId = Integer.toString(cardId);
String shortcutId = Integer.toString(cardId);
for(int index = 0; index < list.size(); index++)
{
if(list.get(index).getId().equals(shortcutId))
{
list.remove(index);
break;
}
for (int index = 0; index < list.size(); index++) {
if (list.get(index).getId().equals(shortcutId)) {
list.remove(index);
break;
}
shortcutManager.setDynamicShortcuts(list);
}
ShortcutManagerCompat.setDynamicShortcuts(context, list);
}
static @NotNull
Bitmap createAdaptiveBitmap(@NotNull Bitmap in, int paddingColor) {
Bitmap ret = Bitmap.createBitmap(ADAPTIVE_BITMAP_SIZE, ADAPTIVE_BITMAP_SIZE, Bitmap.Config.ARGB_8888);
Canvas output = new Canvas(ret);
output.drawColor(ColorUtils.compositeColors(PADDING_COLOR_OVERLAY, paddingColor));
Bitmap resized = Utils.resizeBitmap(in, ADAPTIVE_BITMAP_IMAGE_SIZE);
output.drawBitmap(resized, (ADAPTIVE_BITMAP_SIZE - resized.getWidth()) / 2f, (ADAPTIVE_BITMAP_SIZE - resized.getHeight()) / 2f, null);
return ret;
}
static ShortcutInfoCompat.Builder createShortcutBuilder(Context context, LoyaltyCard loyaltyCard) {
Intent intent = new Intent(context, LoyaltyCardViewActivity.class);
intent.setAction(Intent.ACTION_MAIN);
// Prevent instances of the view activity from piling up; if one exists let this
// one replace it.
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_SINGLE_TOP);
final Bundle bundle = new Bundle();
bundle.putInt("id", loyaltyCard.id);
bundle.putBoolean("view", true);
intent.putExtras(bundle);
Bitmap iconBitmap = Utils.retrieveCardImage(context, loyaltyCard.id, ImageLocationType.icon);
if (iconBitmap == null) {
iconBitmap = Utils.generateIcon(context, loyaltyCard, true).getLetterTile();
} else {
iconBitmap = createAdaptiveBitmap(iconBitmap, loyaltyCard.headerColor == null ? PADDING_COLOR : loyaltyCard.headerColor);
}
IconCompat icon = IconCompat.createWithAdaptiveBitmap(iconBitmap);
return new ShortcutInfoCompat.Builder(context, Integer.toString(loyaltyCard.id))
.setShortLabel(loyaltyCard.store)
.setLongLabel(loyaltyCard.store)
.setIntent(intent)
.setIcon(icon);
}
}

View File

@@ -0,0 +1,18 @@
package protect.card_locker;
import android.text.Editable;
import android.text.TextWatcher;
public class SimpleTextWatcher implements 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) {
}
@Override
public void afterTextChanged(Editable s) {
}
}

View File

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

View File

@@ -3,10 +3,46 @@ package protect.card_locker;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.ImageDecoder;
import android.graphics.Matrix;
import android.os.Build;
import android.os.LocaleList;
import android.provider.MediaStore;
import android.util.Log;
import android.widget.Toast;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.NotFoundException;
import com.google.zxing.RGBLuminanceSource;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.Calendar;
import java.util.Currency;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.Map;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.graphics.ColorUtils;
import androidx.exifinterface.media.ExifInterface;
import protect.card_locker.preferences.Settings;
public class Utils {
private static final String TAG = "Catima";
@@ -15,16 +51,40 @@ public class Utils {
public static final int MAIN_REQUEST = 1;
public static final int SELECT_BARCODE_REQUEST = 2;
public static final int BARCODE_SCAN = 3;
public static final int BARCODE_IMPORT_FROM_IMAGE_FILE = 4;
public static final int CARD_IMAGE_FROM_CAMERA_FRONT = 5;
public static final int CARD_IMAGE_FROM_CAMERA_BACK = 6;
public static final int CARD_IMAGE_FROM_CAMERA_ICON = 7;
public static final int CARD_IMAGE_FROM_FILE_FRONT = 8;
public static final int CARD_IMAGE_FROM_FILE_BACK = 9;
public static final int CARD_IMAGE_FROM_FILE_ICON = 10;
static final double LUMINANCE_MIDPOINT = 0.5;
static final int BITMAP_SIZE_SMALL = 512;
static final int BITMAP_SIZE_BIG = 2048;
static public LetterBitmap generateIcon(Context context, LoyaltyCard loyaltyCard, boolean forShortcut) {
return generateIcon(context, loyaltyCard.store, loyaltyCard.headerColor, forShortcut);
}
static public LetterBitmap generateIcon(Context context, String store, Integer backgroundColor) {
return generateIcon(context, store, backgroundColor, false);
}
static public LetterBitmap generateIcon(Context context, String store, Integer backgroundColor, boolean forShortcut) {
if (store.length() == 0) {
return null;
}
int tileLetterFontSize = context.getResources().getDimensionPixelSize(R.dimen.tileLetterFontSize);
int pixelSize = context.getResources().getDimensionPixelSize(R.dimen.cardThumbnailSize);
int tileLetterFontSize;
if (forShortcut) {
tileLetterFontSize = context.getResources().getDimensionPixelSize(R.dimen.tileLetterFontSizeForShortcut);
} else {
tileLetterFontSize = context.getResources().getDimensionPixelSize(R.dimen.tileLetterFontSize);
}
int pixelSize = context.getResources().getDimensionPixelSize(R.dimen.tileLetterImageSize);
if (backgroundColor == null) {
backgroundColor = LetterBitmap.getDefaultColor(context, store);
@@ -38,27 +98,354 @@ public class Utils {
return ColorUtils.calculateLuminance(backgroundColor) > LUMINANCE_MIDPOINT;
}
static public BarcodeValues parseSetBarcodeActivityResult(int requestCode, int resultCode, Intent intent) {
String contents = null;
String format = null;
static public BarcodeValues parseSetBarcodeActivityResult(int requestCode, int resultCode, Intent intent, Context context) {
String contents;
String format;
if (resultCode == Activity.RESULT_OK)
{
if (resultCode != Activity.RESULT_OK) {
return new BarcodeValues(null, null);
}
if (requestCode == Utils.BARCODE_IMPORT_FROM_IMAGE_FILE) {
Log.i(TAG, "Received image file with possible barcode");
Bitmap bitmap;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ImageDecoder.Source image_source = ImageDecoder.createSource(context.getContentResolver(), intent.getData());
bitmap = ImageDecoder.decodeBitmap(image_source, (decoder, info, source) -> decoder.setMutableRequired(true));
} else {
bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), intent.getData());
}
} catch (IOException e) {
Log.e(TAG, "Error getting data from image file");
e.printStackTrace();
Toast.makeText(context, R.string.errorReadingImage, Toast.LENGTH_LONG).show();
return new BarcodeValues(null, null);
}
BarcodeValues barcodeFromBitmap = getBarcodeFromBitmap(bitmap);
if (barcodeFromBitmap.isEmpty()) {
Log.i(TAG, "No barcode found in image file");
Toast.makeText(context, R.string.noBarcodeFound, Toast.LENGTH_LONG).show();
}
Log.i(TAG, "Read barcode id: " + barcodeFromBitmap.content());
Log.i(TAG, "Read format: " + barcodeFromBitmap.format());
return barcodeFromBitmap;
}
if (requestCode == Utils.BARCODE_SCAN || requestCode == Utils.SELECT_BARCODE_REQUEST) {
if (requestCode == Utils.BARCODE_SCAN) {
Log.i(TAG, "Received barcode information from camera");
} else if (requestCode == Utils.SELECT_BARCODE_REQUEST) {
Log.i(TAG, "Received barcode information from typing it");
} else {
return new BarcodeValues(null, null);
}
contents = intent.getStringExtra(BarcodeSelectorActivity.BARCODE_CONTENTS);
format = intent.getStringExtra(BarcodeSelectorActivity.BARCODE_FORMAT);
Log.i(TAG, "Read barcode id: " + contents);
Log.i(TAG, "Read format: " + format);
return new BarcodeValues(format, contents);
}
Log.i(TAG, "Read barcode id: " + contents);
Log.i(TAG, "Read format: " + format);
return new BarcodeValues(format, contents);
throw new UnsupportedOperationException("Unknown request code for parseSetBarcodeActivityResult");
}
static public BarcodeValues getBarcodeFromBitmap(Bitmap bitmap) {
// This function is vulnerable to OOM, so we try again with a smaller bitmap is we get OOM
for (int i = 0; i < 10; i++) {
try {
return Utils.getBarcodeFromBitmapReal(bitmap);
} catch (OutOfMemoryError e) {
Log.w(TAG, "Ran OOM in getBarcodeFromBitmap! Trying again with smaller picture! Retry " + i + " of 10.");
bitmap = Bitmap.createScaledBitmap(bitmap, (int) Math.round(0.75 * bitmap.getWidth()), (int) Math.round(0.75 * bitmap.getHeight()), false);
}
}
// Give up
return new BarcodeValues(null, null);
}
static private BarcodeValues getBarcodeFromBitmapReal(Bitmap bitmap) {
// In order to decode it, the Bitmap must first be converted into a pixel array...
int[] intArray = new int[bitmap.getWidth() * bitmap.getHeight()];
bitmap.getPixels(intArray, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
// ...and then turned into a binary bitmap from its luminance
LuminanceSource source = new RGBLuminanceSource(bitmap.getWidth(), bitmap.getHeight(), intArray);
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
try {
Result barcodeResult = new MultiFormatReader().decode(binaryBitmap);
return new BarcodeValues(barcodeResult.getBarcodeFormat().name(), barcodeResult.getText());
} catch (NotFoundException e) {
return new BarcodeValues(null, null);
}
}
static public Boolean hasExpired(Date expiryDate) {
// today
Calendar date = new GregorianCalendar();
// reset hour, minutes, seconds and millis
date.set(Calendar.HOUR_OF_DAY, 0);
date.set(Calendar.MINUTE, 0);
date.set(Calendar.SECOND, 0);
date.set(Calendar.MILLISECOND, 0);
return expiryDate.before(date.getTime());
}
static public String formatBalance(Context context, BigDecimal value, Currency currency) {
NumberFormat numberFormat = NumberFormat.getInstance();
if (currency == null) {
numberFormat.setMaximumFractionDigits(0);
return context.getString(R.string.balancePoints, numberFormat.format(value));
}
NumberFormat currencyFormat = NumberFormat.getCurrencyInstance();
currencyFormat.setCurrency(currency);
currencyFormat.setMinimumFractionDigits(currency.getDefaultFractionDigits());
currencyFormat.setMaximumFractionDigits(currency.getDefaultFractionDigits());
return currencyFormat.format(value);
}
static public String formatBalanceWithoutCurrencySymbol(BigDecimal value, Currency currency) {
NumberFormat numberFormat = NumberFormat.getInstance();
if (currency == null) {
numberFormat.setMaximumFractionDigits(0);
return numberFormat.format(value);
}
numberFormat.setMinimumFractionDigits(currency.getDefaultFractionDigits());
numberFormat.setMaximumFractionDigits(currency.getDefaultFractionDigits());
return numberFormat.format(value);
}
static public Boolean currencyHasDecimals(Currency currency) {
if (currency == null) {
return false;
}
return currency.getDefaultFractionDigits() != 0;
}
static public BigDecimal parseCurrency(String value, Boolean hasDecimals) throws NumberFormatException {
// If there are no decimals expected, remove all separators before parsing
if (!hasDecimals) {
value = value.replaceAll("[^0-9]", "");
return new BigDecimal(value);
}
// There are many ways users can write a currency, so we fix it up a bit
// 1. Replace all non-numbers with dots
value = value.replaceAll("[^0-9]", ".");
// 2. Remove all but the last dot
while (value.split("\\.").length > 2) {
value = value.replaceFirst("\\.", "");
}
// Parse as BigDecimal
return new BigDecimal(value);
}
static public byte[] bitmapToByteArray(Bitmap bitmap) {
if (bitmap == null) {
return null;
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);
return bos.toByteArray();
}
static public Bitmap resizeBitmap(Bitmap bitmap, double maxSize) {
if (bitmap == null) {
return null;
}
double width = bitmap.getWidth();
double height = bitmap.getHeight();
if (height > width) {
double scale = height / maxSize;
height = maxSize;
width = width / scale;
} else if (width > height) {
double scale = width / maxSize;
width = maxSize;
height = height / scale;
} else {
height = maxSize;
width = maxSize;
}
return Bitmap.createScaledBitmap(bitmap, (int) Math.round(width), (int) Math.round(height), true);
}
static public Bitmap rotateBitmap(Bitmap bitmap, ExifInterface exifInterface) {
switch (exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)) {
case ExifInterface.ORIENTATION_ROTATE_90:
return rotateBitmap(bitmap, 90f);
case ExifInterface.ORIENTATION_ROTATE_180:
return rotateBitmap(bitmap, 180f);
case ExifInterface.ORIENTATION_ROTATE_270:
return rotateBitmap(bitmap, 270f);
default:
return bitmap;
}
}
static public Bitmap rotateBitmap(Bitmap bitmap, float rotation) {
if (rotation == 0) {
return bitmap;
}
Matrix matrix = new Matrix();
matrix.postRotate(rotation);
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
static public String getCardImageFileName(int loyaltyCardId, ImageLocationType type) {
StringBuilder cardImageFileNameBuilder = new StringBuilder();
cardImageFileNameBuilder.append("card_");
cardImageFileNameBuilder.append(loyaltyCardId);
cardImageFileNameBuilder.append("_");
if (type == ImageLocationType.front) {
cardImageFileNameBuilder.append("front");
} else if (type == ImageLocationType.back) {
cardImageFileNameBuilder.append("back");
} else if (type == ImageLocationType.icon) {
cardImageFileNameBuilder.append("icon");
} else {
throw new IllegalArgumentException("Unknown image type");
}
cardImageFileNameBuilder.append(".png");
return cardImageFileNameBuilder.toString();
}
static public void saveCardImage(Context context, Bitmap bitmap, String fileName) throws FileNotFoundException {
if (bitmap == null) {
context.deleteFile(fileName);
return;
}
FileOutputStream out = context.openFileOutput(fileName, Context.MODE_PRIVATE);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
}
static public void saveCardImage(Context context, Bitmap bitmap, int loyaltyCardId, ImageLocationType type) throws FileNotFoundException {
saveCardImage(context, bitmap, getCardImageFileName(loyaltyCardId, type));
}
static public Bitmap retrieveCardImage(Context context, String fileName) {
FileInputStream in;
try {
in = context.openFileInput(fileName);
} catch (FileNotFoundException e) {
return null;
}
return BitmapFactory.decodeStream(in);
}
static public Bitmap retrieveCardImage(Context context, int loyaltyCardId, ImageLocationType type) {
return retrieveCardImage(context, getCardImageFileName(loyaltyCardId, type));
}
static public <T, U> U mapGetOrDefault(Map<T, U> map, T key, U defaultValue) {
U value = map.get(key);
if (value == null) {
return defaultValue;
}
return value;
}
static public Locale stringToLocale(String localeString) {
String[] localeParts = localeString.split("-");
if (localeParts.length == 1) {
return new Locale(localeParts[0]);
}
if (localeParts[1].startsWith("r")) {
localeParts[1] = localeParts[1].substring(1);
}
return new Locale(localeParts[0], localeParts[1]);
}
static public Context updateBaseContextLocale(Context context) {
Settings settings = new Settings(context);
Locale chosenLocale = settings.getLocale();
Resources res = context.getResources();
Configuration configuration = res.getConfiguration();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
configuration.locale = chosenLocale != null ? chosenLocale : Locale.getDefault();
res.updateConfiguration(configuration, res.getDisplayMetrics());
return context;
}
LocaleList localeList = chosenLocale != null ? new LocaleList(chosenLocale) : LocaleList.getDefault();
LocaleList.setDefault(localeList);
configuration.setLocales(localeList);
return context.createConfigurationContext(configuration);
}
static public long getUnixTime() {
return System.currentTimeMillis() / 1000;
}
static public boolean isDarkModeEnabled(Context inputContext) {
int nightModeSetting = new Settings(inputContext).getTheme();
if (nightModeSetting == AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) {
Configuration config = inputContext.getResources().getConfiguration();
int currentNightMode = config.uiMode & Configuration.UI_MODE_NIGHT_MASK;
return (currentNightMode == Configuration.UI_MODE_NIGHT_YES);
} else {
return nightModeSetting == AppCompatDelegate.MODE_NIGHT_YES;
}
}
public static File createTempFile(Context context, String name) {
return new File(context.getCacheDir() + "/" + name);
}
public static String saveTempImage(Context context, Bitmap in, String name, Bitmap.CompressFormat format) {
File image = createTempFile(context, name);
try (FileOutputStream out = new FileOutputStream(image)) {
in.compress(format, 100, out);
return image.getAbsolutePath();
} catch (IOException e) {
Log.d("store temp image", "failed writing temp file for temporary image, name: " + name);
return null;
}
}
public static Bitmap loadImage(String path) {
try {
return BitmapFactory.decodeStream(new FileInputStream(path));
} catch (IOException e) {
Log.d("load image", "failed loading image from " + path);
return null;
}
}
public static Bitmap loadTempImage(Context context, String name) {
return loadImage(context.getCacheDir() + "/" + name);
}
}

View File

@@ -0,0 +1,35 @@
package protect.card_locker;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import net.lingala.zip4j.io.inputstream.ZipInputStream;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
public class ZipUtils {
static public String read(ZipInputStream zipInputStream) throws IOException {
StringBuilder stringBuilder = new StringBuilder();
Reader reader = new BufferedReader(new InputStreamReader(zipInputStream, StandardCharsets.UTF_8));
int c;
while ((c = reader.read()) != -1) {
stringBuilder.append((char) c);
}
return stringBuilder.toString();
}
static public Bitmap readImage(ZipInputStream zipInputStream) {
return BitmapFactory.decodeStream(zipInputStream);
}
static public JSONObject readJSON(ZipInputStream zipInputStream) throws IOException, JSONException {
return new JSONObject(read(zipInputStream));
}
}

View File

@@ -0,0 +1,9 @@
package protect.card_locker.async;
import java.util.concurrent.Callable;
public interface CompatCallable<T> extends Callable<T> {
void onPostExecute(Object result);
void onPreExecute();
}

View File

@@ -0,0 +1,175 @@
package protect.card_locker.async;
import android.os.Handler;
import android.os.Looper;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* AsyncTask has been deprecated so this provides very rudimentary compatibility without
* needing to redo too many Parts.
* <p>
* However this is a much, much more cooperative Behaviour than before so
* the callers need to ensure we do NOT rely on forced cancellation and feed less into the
* ThreadPools so we don't OOM/Overload the Users device
* <p>
* This assumes single-threaded callers.
*/
public class TaskHandler {
public enum TYPE {
BARCODE,
IMPORT,
EXPORT
}
HashMap<TYPE, ThreadPoolExecutor> executors = generateExecutors();
final private HashMap<TYPE, LinkedList<Future<?>>> taskList = new HashMap<>();
private final Handler uiHandler = new Handler(Looper.getMainLooper());
private HashMap<TYPE, ThreadPoolExecutor> generateExecutors() {
HashMap<TYPE, ThreadPoolExecutor> initExecutors = new HashMap<>();
for (TYPE type : TYPE.values()) {
replaceExecutor(initExecutors, type, false, false);
}
return initExecutors;
}
/**
* Replaces (or initializes) an Executor with a clean (new) one
*
* @param executors Map Reference
* @param type Which Queue
* @param flushOld attempt shutdown
* @param waitOnOld wait for Termination
*/
private void replaceExecutor(HashMap<TYPE, ThreadPoolExecutor> executors, TYPE type, Boolean flushOld, Boolean waitOnOld) {
ThreadPoolExecutor oldExecutor = executors.get(type);
if (oldExecutor != null) {
if (flushOld) {
oldExecutor.shutdownNow();
}
if (waitOnOld) {
try {
//noinspection ResultOfMethodCallIgnored
oldExecutor.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
executors.put(type, (ThreadPoolExecutor) Executors.newCachedThreadPool());
}
/**
* Queue a Pseudo-AsyncTask for execution
*
* @param type Queue
* @param callable PseudoAsyncTask
*/
public void executeTask(TYPE type, CompatCallable<?> callable) {
Runnable runner = () -> {
try {
// Run on the UI Thread
uiHandler.post(callable::onPreExecute);
// Background
final Object result = callable.call();
// Post results on UI Thread so we can show them
uiHandler.post(() -> {
callable.onPostExecute(result);
});
} catch (Exception e) {
e.printStackTrace();
}
};
LinkedList<Future<?>> list = taskList.get(type);
if (list == null) {
list = new LinkedList<>();
}
ThreadPoolExecutor executor = executors.get(type);
if (executor != null) {
Future<?> task = executor.submit(runner);
// Test Queue Cancellation:
// task.cancel(true);
list.push(task);
taskList.put(type, list);
}
}
/**
* This will attempt to cancel a currently running list of Tasks
* Useful to ignore scheduled tasks - but not able to hard-stop tasks that are running
*
* @param type Which Queue to target
* @param forceCancel attempt to close the Queue and force-replace it after
* @param waitForFinish wait and return after the old executor finished. Times out after 5s
* @param waitForCurrentlyRunning wait before cancelling tasks. Useful for tests.
*/
public void flushTaskList(TYPE type, Boolean forceCancel, Boolean waitForFinish, Boolean waitForCurrentlyRunning) {
// Only used for Testing
if (waitForCurrentlyRunning) {
ThreadPoolExecutor oldExecutor = executors.get(type);
if (oldExecutor != null) {
try {
//noinspection ResultOfMethodCallIgnored
oldExecutor.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// Attempt to cancel known Tasks and clean the List
LinkedList<Future<?>> tasks = taskList.get(type);
if (tasks != null) {
for (Future<?> task : tasks) {
if (!task.isDone() || !task.isCancelled()) {
// Interrupt any Task we can
task.cancel(true);
}
}
}
tasks = new LinkedList<>();
taskList.put(type, tasks);
if (forceCancel || waitForFinish) {
ThreadPoolExecutor oldExecutor = executors.get(type);
if (oldExecutor != null) {
if (forceCancel) {
if (waitForFinish) {
try {
//noinspection ResultOfMethodCallIgnored
oldExecutor.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
oldExecutor.shutdownNow();
replaceExecutor(executors, type, true, false);
} else {
try {
//noinspection ResultOfMethodCallIgnored
oldExecutor.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,189 @@
package protect.card_locker.importexport;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import net.lingala.zip4j.io.outputstream.ZipOutputStream;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.model.enums.EncryptionMethod;
import net.lingala.zip4j.util.InternalZipConstants;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import protect.card_locker.DBHelper;
import protect.card_locker.Group;
import protect.card_locker.ImageLocationType;
import protect.card_locker.LoyaltyCard;
import protect.card_locker.Utils;
/**
* Class for exporting the database into CSV (Comma Separate Values)
* format.
*/
public class CatimaExporter implements Exporter {
public void exportData(Context context, DBHelper db, OutputStream output, char[] password) throws IOException, InterruptedException {
// Necessary vars
int readLen;
byte[] readBuffer = new byte[InternalZipConstants.BUFF_SIZE];
// Create zip output stream
ZipOutputStream zipOutputStream;
if (password != null && password.length > 0) {
zipOutputStream = new ZipOutputStream(output, password);
} else {
zipOutputStream = new ZipOutputStream(output);
}
// Generate CSV
ByteArrayOutputStream catimaOutputStream = new ByteArrayOutputStream();
OutputStreamWriter catimaOutputStreamWriter = new OutputStreamWriter(catimaOutputStream, StandardCharsets.UTF_8);
writeCSV(db, catimaOutputStreamWriter);
// Add CSV to zip file
ZipParameters csvZipParameters = createZipParameters("catima.csv", password);
zipOutputStream.putNextEntry(csvZipParameters);
InputStream csvInputStream = new ByteArrayInputStream(catimaOutputStream.toByteArray());
while ((readLen = csvInputStream.read(readBuffer)) != -1) {
zipOutputStream.write(readBuffer, 0, readLen);
}
zipOutputStream.closeEntry();
// Loop over all cards again
Cursor cardCursor = db.getLoyaltyCardCursor();
while (cardCursor.moveToNext()) {
// For each card
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cardCursor);
// For each image
for (ImageLocationType imageLocationType : ImageLocationType.values()) {
// If it exists, add to the .zip file
Bitmap image = Utils.retrieveCardImage(context, card.id, imageLocationType);
if (image != null) {
ZipParameters imageZipParameters = createZipParameters(Utils.getCardImageFileName(card.id, imageLocationType), password);
zipOutputStream.putNextEntry(imageZipParameters);
InputStream imageInputStream = new ByteArrayInputStream(Utils.bitmapToByteArray(image));
while ((readLen = imageInputStream.read(readBuffer)) != -1) {
zipOutputStream.write(readBuffer, 0, readLen);
}
zipOutputStream.closeEntry();
}
}
}
zipOutputStream.close();
}
private ZipParameters createZipParameters(String fileName, char[] password) {
ZipParameters zipParameters = new ZipParameters();
zipParameters.setFileNameInZip(fileName);
if (password != null && password.length > 0) {
zipParameters.setEncryptFiles(true);
zipParameters.setEncryptionMethod(EncryptionMethod.AES);
}
return zipParameters;
}
private void writeCSV(DBHelper db, OutputStreamWriter output) throws IOException, InterruptedException {
CSVPrinter printer = new CSVPrinter(output, CSVFormat.RFC4180);
// Print the version
printer.printRecord("2");
printer.println();
// Print the header for groups
printer.printRecord(DBHelper.LoyaltyCardDbGroups.ID);
Cursor groupCursor = db.getGroupCursor();
while (groupCursor.moveToNext()) {
Group group = Group.toGroup(groupCursor);
printer.printRecord(group._id);
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
}
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.EXPIRY,
DBHelper.LoyaltyCardDbIds.BALANCE,
DBHelper.LoyaltyCardDbIds.BALANCE_TYPE,
DBHelper.LoyaltyCardDbIds.CARD_ID,
DBHelper.LoyaltyCardDbIds.BARCODE_ID,
DBHelper.LoyaltyCardDbIds.BARCODE_TYPE,
DBHelper.LoyaltyCardDbIds.HEADER_COLOR,
DBHelper.LoyaltyCardDbIds.STAR_STATUS,
DBHelper.LoyaltyCardDbIds.LAST_USED);
Cursor cardCursor = db.getLoyaltyCardCursor();
while (cardCursor.moveToNext()) {
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cardCursor);
printer.printRecord(card.id,
card.store,
card.note,
card.expiry != null ? card.expiry.getTime() : "",
card.balance,
card.balanceType,
card.cardId,
card.barcodeId,
card.barcodeType != null ? card.barcodeType.name() : "",
card.headerColor,
card.starStatus,
card.lastUsed);
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

@@ -0,0 +1,373 @@
package protect.card_locker.importexport;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import net.lingala.zip4j.io.inputstream.ZipInputStream;
import net.lingala.zip4j.model.LocalFileHeader;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Currency;
import java.util.Date;
import java.util.List;
import protect.card_locker.CatimaBarcode;
import protect.card_locker.DBHelper;
import protect.card_locker.FormatException;
import protect.card_locker.Group;
import protect.card_locker.Utils;
import protect.card_locker.ZipUtils;
/**
* Class for importing a database from CSV (Comma Separate Values)
* formatted data.
* <p>
* The database's loyalty cards are expected to appear in the CSV data.
* A header is expected for the each table showing the names of the columns.
*/
public class CatimaImporter implements Importer {
public void importData(Context context, DBHelper db, InputStream input, char[] password) throws IOException, FormatException, InterruptedException {
InputStream bufferedInputStream = new BufferedInputStream(input);
bufferedInputStream.mark(100);
// First, check if this is a zip file
ZipInputStream zipInputStream = new ZipInputStream(bufferedInputStream, password);
boolean isZipFile = false;
LocalFileHeader localFileHeader;
while ((localFileHeader = zipInputStream.getNextEntry()) != null) {
isZipFile = true;
String fileName = Uri.parse(localFileHeader.getFileName()).getLastPathSegment();
if (fileName.equals("catima.csv")) {
importCSV(context, db, new ByteArrayInputStream(ZipUtils.read(zipInputStream).getBytes(StandardCharsets.UTF_8)));
} else if (fileName.endsWith(".png")) {
Utils.saveCardImage(context, ZipUtils.readImage(zipInputStream), fileName);
} else {
throw new FormatException("Unexpected file in import: " + fileName);
}
}
if (!isZipFile) {
// This is not a zip file, try importing as bare CSV
bufferedInputStream.reset();
importCSV(context, db, bufferedInputStream);
}
}
public void importCSV(Context context, DBHelper db, InputStream input) throws IOException, FormatException, InterruptedException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
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(context, db, bufferedReader);
break;
case 2:
parseV2(context, db, bufferedReader);
break;
default:
throw new FormatException(String.format("No code to parse version %s", version));
}
bufferedReader.close();
}
public void parseV1(Context context, DBHelper db, BufferedReader input) throws IOException, FormatException, InterruptedException {
final CSVParser parser = new CSVParser(input, CSVFormat.RFC4180.builder().setHeader().build());
SQLiteDatabase database = db.getWritableDatabase();
database.beginTransaction();
try {
for (CSVRecord record : parser) {
importLoyaltyCard(context, database, db, record);
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
}
parser.close();
database.setTransactionSuccessful();
} catch (IllegalArgumentException | IllegalStateException e) {
throw new FormatException("Issue parsing CSV data", e);
} finally {
database.endTransaction();
database.close();
}
}
public void parseV2(Context context, DBHelper db, BufferedReader input) throws IOException, FormatException, InterruptedException {
SQLiteDatabase database = db.getWritableDatabase();
database.beginTransaction();
Integer part = 0;
String stringPart = "";
try {
while (true) {
String tmp = input.readLine();
if (tmp == null || tmp.isEmpty()) {
boolean sectionParsed = false;
switch (part) {
case 0:
// This is the version info, ignore
sectionParsed = true;
break;
case 1:
try {
parseV2Groups(db, database, stringPart);
sectionParsed = true;
} catch (FormatException e) {
// We may have a multiline field, try again
}
break;
case 2:
try {
parseV2Cards(context, db, database, stringPart);
sectionParsed = true;
} catch (FormatException e) {
// We may have a multiline field, try again
}
break;
case 3:
try {
parseV2CardGroups(db, database, stringPart);
sectionParsed = true;
} catch (FormatException e) {
// We may have a multiline field, try again
}
break;
default:
throw new FormatException("Issue parsing CSV data, too many parts for v2 parsing");
}
if (tmp == null) {
break;
}
if (sectionParsed) {
part += 1;
stringPart = "";
} else {
stringPart += tmp + "\n";
}
} 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.builder().setHeader().build());
List<CSVRecord> records = new ArrayList<>();
try {
for (CSVRecord record : groupParser) {
records.add(record);
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
}
} catch (IllegalArgumentException | IllegalStateException e) {
throw new FormatException("Issue parsing CSV data", e);
} finally {
groupParser.close();
}
for (CSVRecord record : records) {
importGroup(database, db, record);
}
}
public void parseV2Cards(Context context, DBHelper db, SQLiteDatabase database, String data) throws IOException, FormatException, InterruptedException {
// Parse cards
final CSVParser cardParser = new CSVParser(new StringReader(data), CSVFormat.RFC4180.builder().setHeader().build());
List<CSVRecord> records = new ArrayList<>();
try {
for (CSVRecord record : cardParser) {
records.add(record);
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
}
} catch (IllegalArgumentException | IllegalStateException e) {
throw new FormatException("Issue parsing CSV data", e);
} finally {
cardParser.close();
}
for (CSVRecord record : records) {
importLoyaltyCard(context, database, db, record);
}
}
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.builder().setHeader().build());
List<CSVRecord> records = new ArrayList<>();
try {
for (CSVRecord record : cardGroupParser) {
records.add(record);
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
}
} catch (IllegalArgumentException | IllegalStateException e) {
throw new FormatException("Issue parsing CSV data", e);
} finally {
cardGroupParser.close();
}
for (CSVRecord record : records) {
importCardGroupMapping(database, db, record);
}
}
/**
* Import a single loyalty card into the database using the given
* session.
*/
private void importLoyaltyCard(Context context, SQLiteDatabase database, DBHelper helper, CSVRecord record)
throws IOException, FormatException {
int id = CSVHelpers.extractInt(DBHelper.LoyaltyCardDbIds.ID, record, false);
String store = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.STORE, record, "");
if (store.isEmpty()) {
throw new FormatException("No store listed, but is required");
}
String note = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.NOTE, record, "");
Date expiry = null;
try {
expiry = new Date(CSVHelpers.extractLong(DBHelper.LoyaltyCardDbIds.EXPIRY, record, true));
} catch (NullPointerException | FormatException e) {
}
BigDecimal balance;
try {
balance = new BigDecimal(CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.BALANCE, record, null));
} catch (FormatException _e) {
// These fields did not exist in versions 1.8.1 and before
// We catch this exception so we can still import old backups
balance = new BigDecimal("0");
}
Currency balanceType = null;
String unparsedBalanceType = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE, record, "");
if (!unparsedBalanceType.isEmpty()) {
balanceType = Currency.getInstance(unparsedBalanceType);
}
String cardId = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.CARD_ID, record, "");
if (cardId.isEmpty()) {
throw new FormatException("No card ID listed, but is required");
}
String barcodeId = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.BARCODE_ID, record, "");
if (barcodeId.isEmpty()) {
barcodeId = null;
}
CatimaBarcode barcodeType = null;
String unparsedBarcodeType = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE, record, "");
if (!unparsedBarcodeType.isEmpty()) {
barcodeType = CatimaBarcode.fromName(unparsedBarcodeType);
}
Integer headerColor = null;
if (record.isMapped(DBHelper.LoyaltyCardDbIds.HEADER_COLOR)) {
headerColor = CSVHelpers.extractInt(DBHelper.LoyaltyCardDbIds.HEADER_COLOR, record, true);
}
int starStatus = 0;
try {
starStatus = CSVHelpers.extractInt(DBHelper.LoyaltyCardDbIds.STAR_STATUS, record, false);
} catch (FormatException _e) {
// This field did not exist in versions 0.28 and before
// We catch this exception so we can still import old backups
}
if (starStatus != 1) starStatus = 0;
Long lastUsed = 0L;
try {
lastUsed = CSVHelpers.extractLong(DBHelper.LoyaltyCardDbIds.LAST_USED, record, false);
} catch (FormatException _e) {
// This field did not exist in versions 2.5.0 and before
// We catch this exception so we can still import old backups
}
helper.insertLoyaltyCard(database, id, store, note, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, starStatus, lastUsed);
}
/**
* 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 = CSVHelpers.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 = CSVHelpers.extractInt(DBHelper.LoyaltyCardDbIdsGroups.cardID, record, false);
String groupId = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIdsGroups.groupID, record, null);
List<Group> cardGroups = helper.getLoyaltyCardGroups(cardId);
cardGroups.add(helper.getGroup(groupId));
helper.setLoyaltyCardGroups(database, cardId, cardGroups);
}
}

View File

@@ -0,0 +1,8 @@
package protect.card_locker.importexport;
public enum DataFormat {
Catima,
Fidme,
Stocard,
VoucherVault;
}

View File

@@ -0,0 +1,21 @@
package protect.card_locker.importexport;
import android.content.Context;
import java.io.IOException;
import java.io.OutputStream;
import protect.card_locker.DBHelper;
/**
* Interface for a class which can export the contents of the database
* in a given format.
*/
public interface Exporter {
/**
* Export the database to the output stream in a given format.
*
* @throws IOException
*/
void exportData(Context context, DBHelper db, OutputStream output, char[] password) throws IOException, InterruptedException;
}

View File

@@ -0,0 +1,134 @@
package protect.card_locker.importexport;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import net.lingala.zip4j.io.inputstream.ZipInputStream;
import net.lingala.zip4j.model.LocalFileHeader;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.json.JSONException;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import protect.card_locker.CatimaBarcode;
import protect.card_locker.DBHelper;
import protect.card_locker.FormatException;
/**
* Class for importing a database from CSV (Comma Separate Values)
* formatted data.
* <p>
* The database's loyalty cards are expected to appear in the CSV data.
* A header is expected for the each table showing the names of the columns.
*/
public class FidmeImporter implements Importer {
public void importData(Context context, DBHelper db, InputStream input, char[] password) throws IOException, FormatException, JSONException, ParseException {
// We actually retrieve a .zip file
ZipInputStream zipInputStream = new ZipInputStream(input, password);
StringBuilder loyaltyCards = new StringBuilder();
byte[] buffer = new byte[1024];
int read = 0;
LocalFileHeader localFileHeader;
while ((localFileHeader = zipInputStream.getNextEntry()) != null) {
if (localFileHeader.getFileName().equals("loyalty_programs.csv")) {
while ((read = zipInputStream.read(buffer, 0, 1024)) >= 0) {
loyaltyCards.append(new String(buffer, 0, read, StandardCharsets.UTF_8));
}
}
}
if (loyaltyCards.length() == 0) {
throw new FormatException("Couldn't find loyalty_programs.csv in zip file or it is empty");
}
SQLiteDatabase database = db.getWritableDatabase();
database.beginTransaction();
final CSVParser fidmeParser = new CSVParser(new StringReader(loyaltyCards.toString()), CSVFormat.RFC4180.builder().setDelimiter(';').setHeader().build());
try {
for (CSVRecord record : fidmeParser) {
importLoyaltyCard(database, db, record);
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
}
} catch (IllegalArgumentException | IllegalStateException | InterruptedException e) {
throw new FormatException("Issue parsing CSV data", e);
} finally {
fidmeParser.close();
}
database.setTransactionSuccessful();
database.endTransaction();
database.close();
zipInputStream.close();
}
/**
* Import a single loyalty card into the database using the given
* session.
*/
private void importLoyaltyCard(SQLiteDatabase database, DBHelper helper, CSVRecord record)
throws IOException, FormatException {
// A loyalty card export from Fidme contains the following fields:
// Retailer (store name)
// Program (program name)
// Added at (YYYY-MM-DD HH:MM:SS UTC)
// Reference (card ID)
// Firstname (card holder first name)
// Lastname (card holder last name)
// The store is called Retailer
String store = CSVHelpers.extractString("Retailer", record, "");
if (store.isEmpty()) {
throw new FormatException("No store listed, but is required");
}
// There seems to be no note field in the CSV? So let's combine other fields instead...
String program = CSVHelpers.extractString("Program", record, "").trim();
String addedAt = CSVHelpers.extractString("Added At", record, "").trim();
String firstName = CSVHelpers.extractString("Firstname", record, "").trim();
String lastName = CSVHelpers.extractString("Lastname", record, "").trim();
String combinedName = String.format("%s %s", firstName, lastName).trim();
StringBuilder noteBuilder = new StringBuilder();
if (!program.isEmpty()) noteBuilder.append(program).append('\n');
if (!addedAt.isEmpty()) noteBuilder.append(addedAt).append('\n');
if (!combinedName.isEmpty()) noteBuilder.append(combinedName).append('\n');
String note = noteBuilder.toString().trim();
// The ID is called reference
String cardId = CSVHelpers.extractString("Reference", record, "");
if (cardId.isEmpty()) {
throw new FormatException("No card ID listed, but is required");
}
// Sadly, Fidme exports don't contain the card type
// I guess they have an online DB of all the different companies and what type they use
// TODO: Hook this into our own loyalty card DB if we ever get one
CatimaBarcode barcodeType = null;
// No favourite data in the export either
int starStatus = 0;
// TODO: Front and back image
helper.insertLoyaltyCard(database, store, note, null, BigDecimal.valueOf(0), null, cardId, null, barcodeType, null, starStatus, null);
}
}

View File

@@ -0,0 +1,7 @@
package protect.card_locker.importexport;
public enum ImportExportResult {
Success,
GenericFailure,
BadPassword;
}

View File

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

View File

@@ -0,0 +1,52 @@
package protect.card_locker.importexport;
import android.content.Context;
import android.util.Log;
import java.io.IOException;
import java.io.OutputStream;
import protect.card_locker.DBHelper;
public class MultiFormatExporter {
private static final String TAG = "Catima";
/**
* Attempts to export data to the output stream in the
* given format, if possible.
* <p>
* The output stream is closed on success.
*
* @return ImportExportResult.Success if the database was successfully exported,
* another ImportExportResult otherwise. If not Success, partial data may have been
* written to the output stream, and it should be discarded.
*/
public static ImportExportResult exportData(Context context, DBHelper db, OutputStream output, DataFormat format, char[] password) {
Exporter exporter = null;
switch (format) {
case Catima:
exporter = new CatimaExporter();
break;
default:
Log.e(TAG, "Failed to export data, unknown format " + format.name());
break;
}
if (exporter != null) {
try {
exporter.exportData(context, db, output, password);
return ImportExportResult.Success;
} catch (IOException e) {
Log.e(TAG, "Failed to export data", e);
} catch (InterruptedException e) {
Log.e(TAG, "Failed to export data", e);
}
return ImportExportResult.GenericFailure;
} else {
Log.e(TAG, "Unsupported data format exported: " + format.name());
return ImportExportResult.GenericFailure;
}
}
}

View File

@@ -0,0 +1,65 @@
package protect.card_locker.importexport;
import android.content.Context;
import android.util.Log;
import net.lingala.zip4j.exception.ZipException;
import org.json.JSONException;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import protect.card_locker.DBHelper;
import protect.card_locker.FormatException;
public class MultiFormatImporter {
private static final String TAG = "Catima";
/**
* Attempts to import data from the input stream of the
* given format into the database.
* <p>
* The input stream is not closed, and doing so is the
* responsibility of the caller.
*
* @return ImportExportResult.Success if the database was successfully imported,
* or another result otherwise. If no Success, no data was written to
* the database.
*/
public static ImportExportResult importData(Context context, DBHelper db, InputStream input, DataFormat format, char[] password) {
Importer importer = null;
switch (format) {
case Catima:
importer = new CatimaImporter();
break;
case Fidme:
importer = new FidmeImporter();
break;
case Stocard:
importer = new StocardImporter();
break;
case VoucherVault:
importer = new VoucherVaultImporter();
break;
}
if (importer != null) {
try {
importer.importData(context, db, input, password);
return ImportExportResult.Success;
} catch (ZipException e) {
return ImportExportResult.BadPassword;
} catch (IOException | FormatException | InterruptedException | JSONException | ParseException | NullPointerException e) {
Log.e(TAG, "Failed to import data", e);
}
} else {
Log.e(TAG, "Unsupported data format imported: " + format.name());
}
return ImportExportResult.GenericFailure;
}
}

View File

@@ -0,0 +1,218 @@
package protect.card_locker.importexport;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import com.google.zxing.BarcodeFormat;
import net.lingala.zip4j.io.inputstream.ZipInputStream;
import net.lingala.zip4j.model.LocalFileHeader;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.HashMap;
import protect.card_locker.CatimaBarcode;
import protect.card_locker.DBHelper;
import protect.card_locker.FormatException;
import protect.card_locker.ImageLocationType;
import protect.card_locker.R;
import protect.card_locker.Utils;
import protect.card_locker.ZipUtils;
/**
* Class for importing a database from CSV (Comma Separate Values)
* formatted data.
* <p>
* The database's loyalty cards are expected to appear in the CSV data.
* A header is expected for the each table showing the names of the columns.
*/
public class StocardImporter implements Importer {
public void importData(Context context, DBHelper db, InputStream input, char[] password) throws IOException, FormatException, JSONException, ParseException {
HashMap<String, HashMap<String, Object>> loyaltyCardHashMap = new HashMap<>();
HashMap<String, HashMap<String, String>> providers = new HashMap<>();
final CSVParser parser = new CSVParser(new InputStreamReader(context.getResources().openRawResource(R.raw.stocard_stores), StandardCharsets.UTF_8), CSVFormat.RFC4180.builder().setHeader().build());
try {
for (CSVRecord record : parser) {
HashMap<String, String> recordData = new HashMap<>();
recordData.put("name", record.get("name"));
recordData.put("barcodeFormat", record.get("barcodeFormat"));
providers.put(record.get("_id"), recordData);
}
parser.close();
} catch (IllegalArgumentException | IllegalStateException e) {
throw new FormatException("Issue parsing CSV data", e);
}
ZipInputStream zipInputStream = new ZipInputStream(input, password);
String[] providersFileName = null;
String[] cardBaseName = null;
String cardName = "";
LocalFileHeader localFileHeader;
while ((localFileHeader = zipInputStream.getNextEntry()) != null) {
String fileName = localFileHeader.getFileName();
String[] nameParts = fileName.split("/");
if (providersFileName == null) {
providersFileName = new String[]{
nameParts[0],
"sync",
"data",
"users",
nameParts[0],
"analytics-properties.json"
};
cardBaseName = new String[]{
nameParts[0],
"sync",
"data",
"users",
nameParts[0],
"loyalty-cards"
};
}
if (startsWith(nameParts, cardBaseName, 1)) {
// Extract cardName
cardName = nameParts[cardBaseName.length].split("\\.", 2)[0];
// This is the card itself
if (nameParts.length == cardBaseName.length + 1) {
// Ignore the .txt file
if (fileName.endsWith(".json")) {
JSONObject jsonObject = ZipUtils.readJSON(zipInputStream);
loyaltyCardHashMap = appendToLoyaltyCardHashMap(
loyaltyCardHashMap,
cardName,
"cardId",
jsonObject.getString("input_id")
);
loyaltyCardHashMap = appendToLoyaltyCardHashMap(
loyaltyCardHashMap,
cardName,
"_providerId",
jsonObject
.getJSONObject("input_provider_reference")
.getString("identifier")
.substring("/loyalty-card-providers/".length())
);
if (jsonObject.has("input_barcode_format")) {
loyaltyCardHashMap = appendToLoyaltyCardHashMap(
loyaltyCardHashMap,
cardName,
"barcodeType",
jsonObject.getString("input_barcode_format")
);
}
}
} else if (fileName.endsWith("notes/default.json")) {
loyaltyCardHashMap = appendToLoyaltyCardHashMap(
loyaltyCardHashMap,
cardName,
"note",
ZipUtils.readJSON(zipInputStream)
.getString("content")
);
} else if (fileName.endsWith("/images/front.png")) {
loyaltyCardHashMap = appendToLoyaltyCardHashMap(
loyaltyCardHashMap,
cardName,
"frontImage",
ZipUtils.readImage(zipInputStream)
);
} else if (fileName.endsWith("/images/back.png")) {
loyaltyCardHashMap = appendToLoyaltyCardHashMap(
loyaltyCardHashMap,
cardName,
"backImage",
ZipUtils.readImage(zipInputStream)
);
}
}
}
if (loyaltyCardHashMap.keySet().size() == 0) {
throw new FormatException("Couldn't find any loyalty cards in this Stocard export.");
}
SQLiteDatabase database = db.getWritableDatabase();
database.beginTransaction();
for (HashMap<String, Object> loyaltyCardData : loyaltyCardHashMap.values()) {
String providerId = (String) loyaltyCardData.get("_providerId");
HashMap<String, String> providerData = providers.get(providerId);
String store = providerData != null ? providerData.get("name") : providerId;
String note = (String) Utils.mapGetOrDefault(loyaltyCardData, "note", "");
String cardId = (String) loyaltyCardData.get("cardId");
String barcodeTypeString = (String) Utils.mapGetOrDefault(loyaltyCardData, "barcodeType", providerData != null ? providerData.get("barcodeFormat") : null);
CatimaBarcode barcodeType = null;
if (barcodeTypeString != null) {
if (barcodeTypeString.equals("RSS_DATABAR_EXPANDED")) {
barcodeType = CatimaBarcode.fromBarcode(BarcodeFormat.RSS_EXPANDED);
} else {
barcodeType = CatimaBarcode.fromName(barcodeTypeString);
}
}
long loyaltyCardInternalId = db.insertLoyaltyCard(database, store, note, null, BigDecimal.valueOf(0), null, cardId, null, barcodeType, null, 0, null);
if (loyaltyCardData.containsKey("frontImage")) {
Utils.saveCardImage(context, (Bitmap) loyaltyCardData.get("frontImage"), (int) loyaltyCardInternalId, ImageLocationType.front);
}
if (loyaltyCardData.containsKey("backImage")) {
Utils.saveCardImage(context, (Bitmap) loyaltyCardData.get("backImage"), (int) loyaltyCardInternalId, ImageLocationType.back);
}
}
database.setTransactionSuccessful();
database.endTransaction();
database.close();
zipInputStream.close();
}
private boolean startsWith(String[] full, String[] start, int minExtraLength) {
if (full.length - minExtraLength < start.length) {
return false;
}
for (int i = 0; i < start.length; i++) {
if (!start[i].contentEquals(full[i])) {
return false;
}
}
return true;
}
private HashMap<String, HashMap<String, Object>> appendToLoyaltyCardHashMap(HashMap<String, HashMap<String, Object>> loyaltyCardHashMap, String cardID, String key, Object value) {
HashMap<String, Object> loyaltyCardData = loyaltyCardHashMap.get(cardID);
if (loyaltyCardData == null) {
loyaltyCardData = new HashMap<>();
}
loyaltyCardData.put(key, value);
loyaltyCardHashMap.put(cardID, loyaltyCardData);
return loyaltyCardHashMap;
}
}

View File

@@ -0,0 +1,141 @@
package protect.card_locker.importexport;
import android.annotation.SuppressLint;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Color;
import com.google.zxing.BarcodeFormat;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Currency;
import java.util.Date;
import java.util.TimeZone;
import protect.card_locker.CatimaBarcode;
import protect.card_locker.DBHelper;
import protect.card_locker.FormatException;
import protect.card_locker.Utils;
/**
* Class for importing a database from CSV (Comma Separate Values)
* formatted data.
* <p>
* The database's loyalty cards are expected to appear in the CSV data.
* A header is expected for the each table showing the names of the columns.
*/
public class VoucherVaultImporter implements Importer {
public void importData(Context context, DBHelper db, InputStream input, char[] password) throws IOException, FormatException, JSONException, ParseException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
sb.append(line);
}
JSONArray jsonArray = new JSONArray(sb.toString());
SQLiteDatabase database = db.getWritableDatabase();
database.beginTransaction();
// See https://github.com/tim-smart/vouchervault/issues/4#issuecomment-788226503 for more info
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonCard = jsonArray.getJSONObject(i);
String store = jsonCard.getString("description");
Date expiry = null;
if (!jsonCard.isNull("expires")) {
@SuppressLint("SimpleDateFormat") SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
expiry = dateFormat.parse(jsonCard.getString("expires"));
}
BigDecimal balance = new BigDecimal("0");
if (jsonCard.has("balanceMilliunits")) {
if (!jsonCard.isNull("balanceMilliunits")) {
balance = new BigDecimal(String.valueOf(jsonCard.getInt("balanceMilliunits") / 1000.0));
}
} else if (!jsonCard.isNull("balance")) {
balance = new BigDecimal(String.valueOf(jsonCard.getDouble("balance")));
}
Currency balanceType = Currency.getInstance("USD");
String cardId = jsonCard.getString("code");
CatimaBarcode barcodeType = null;
String codeTypeFromJSON = jsonCard.getString("codeType");
switch (codeTypeFromJSON) {
case "CODE128":
barcodeType = CatimaBarcode.fromBarcode(BarcodeFormat.CODE_128);
break;
case "CODE39":
barcodeType = CatimaBarcode.fromBarcode(BarcodeFormat.CODE_39);
break;
case "EAN13":
barcodeType = CatimaBarcode.fromBarcode(BarcodeFormat.EAN_13);
break;
case "PDF417":
barcodeType = CatimaBarcode.fromBarcode(BarcodeFormat.PDF_417);
break;
case "QR":
barcodeType = CatimaBarcode.fromBarcode(BarcodeFormat.QR_CODE);
break;
case "TEXT":
break;
default:
throw new FormatException("Unknown barcode type found: " + codeTypeFromJSON);
}
int headerColor;
String colorFromJSON = jsonCard.getString("color");
switch (colorFromJSON) {
case "GREY":
headerColor = Color.GRAY;
break;
case "BLUE":
headerColor = Color.BLUE;
break;
case "GREEN":
headerColor = Color.GREEN;
break;
case "ORANGE":
headerColor = Color.rgb(255, 165, 0);
break;
case "PURPLE":
headerColor = Color.rgb(128, 0, 128);
break;
case "RED":
headerColor = Color.RED;
break;
case "YELLOW":
headerColor = Color.YELLOW;
break;
default:
throw new FormatException("Unknown colour type found: " + colorFromJSON);
}
db.insertLoyaltyCard(store, "", expiry, balance, balanceType, cardId, null, barcodeType, headerColor, 0, Utils.getUnixTime());
}
database.setTransactionSuccessful();
database.endTransaction();
database.close();
bufferedReader.close();
}
}

View File

@@ -2,92 +2,104 @@ package protect.card_locker.preferences;
import android.content.Context;
import android.content.SharedPreferences;
import androidx.preference.PreferenceManager;
import java.util.Locale;
import androidx.annotation.IntegerRes;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.preference.PreferenceManager;
import protect.card_locker.R;
import protect.card_locker.Utils;
public class Settings
{
private Context context;
private SharedPreferences settings;
public class Settings {
private final Context mContext;
private SharedPreferences mSettings;
public Settings(Context context)
{
this.context = context;
this.settings = PreferenceManager.getDefaultSharedPreferences(context);
public Settings(Context context) {
mContext = context.getApplicationContext();
mSettings = PreferenceManager.getDefaultSharedPreferences(context);
}
private String getResString(@StringRes int resId)
{
return context.getString(resId);
private String getResString(@StringRes int resId) {
return mContext.getString(resId);
}
private int getResInt(@IntegerRes int resId)
{
return context.getResources().getInteger(resId);
private int getResInt(@IntegerRes int resId) {
return mContext.getResources().getInteger(resId);
}
private String getString(@StringRes int keyId, String defaultValue)
{
return settings.getString(getResString(keyId), defaultValue);
private String getString(@StringRes int keyId, String defaultValue) {
return mSettings.getString(getResString(keyId), defaultValue);
}
private int getInt(@StringRes int keyId, @IntegerRes int defaultId)
{
return settings.getInt(getResString(keyId), getResInt(defaultId));
private int getInt(@StringRes int keyId, @IntegerRes int defaultId) {
return mSettings.getInt(getResString(keyId), getResInt(defaultId));
}
private boolean getBoolean(@StringRes int keyId, boolean defaultValue)
{
return settings.getBoolean(getResString(keyId), defaultValue);
private boolean getBoolean(@StringRes int keyId, boolean defaultValue) {
return mSettings.getBoolean(getResString(keyId), defaultValue);
}
public int getTheme()
{
public Locale getLocale() {
String value = getString(R.string.settings_key_locale, "");
if (value.length() == 0) {
return null;
}
return Utils.stringToLocale(value);
}
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)))
{
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)))
{
} 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 double getFontSizeScale() {
return getInt(R.string.settings_key_max_font_size_scale, R.integer.settings_max_font_size_scale_pct) / 100.0;
}
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 getSmallFont() {
return 14;
}
public int getCardTitleFontSize()
{
return getInt(R.string.settings_key_card_title_font_size, R.integer.settings_card_title_font_size_sp);
public int getMediumFont() {
return 28;
}
public int getCardIdFontSize()
{
return getInt(R.string.settings_key_card_id_font_size, R.integer.settings_card_id_font_size_sp);
public int getLargeFont() {
return 40;
}
public boolean useMaxBrightnessDisplayingBarcode()
{
public int getFontSizeMin(int fontSize) {
return (int) (Math.round(fontSize / 2.0) - 1);
}
public int getFontSizeMax(int fontSize) {
return (int) Math.round(fontSize * getFontSizeScale());
}
public boolean useMaxBrightnessDisplayingBarcode() {
return getBoolean(R.string.settings_key_display_barcode_max_brightness, true);
}
public boolean getLockBarcodeScreenOrientation()
{
public boolean getLockBarcodeScreenOrientation() {
return getBoolean(R.string.settings_key_lock_barcode_orientation, false);
}
public boolean getKeepScreenOn() {
return getBoolean(R.string.settings_key_keep_screen_on, true);
}
public boolean getDisableLockscreenWhileViewingCard() {
return getBoolean(R.string.settings_key_disable_lockscreen_while_viewing_card, true);
}
}

View File

@@ -1,45 +1,54 @@
package protect.card_locker.preferences;
import android.content.Intent;
import android.os.Bundle;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentActivity;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import android.view.MenuItem;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentActivity;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import nl.invissvenska.numberpickerpreference.NumberDialogPreference;
import nl.invissvenska.numberpickerpreference.NumberPickerPreferenceDialogFragment;
import protect.card_locker.CatimaAppCompatActivity;
import protect.card_locker.R;
import protect.card_locker.Utils;
public class SettingsActivity extends AppCompatActivity
{
public class SettingsActivity extends CatimaAppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState)
{
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle(R.string.settings);
setContentView(R.layout.settings_activity);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
{
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
// Display the fragment as the main content.
SettingsFragment fragment = new SettingsFragment();
getSupportFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment())
.replace(R.id.settings_container, fragment)
.commit();
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if(id == android.R.id.home)
{
if (id == android.R.id.home) {
finish();
return true;
}
@@ -47,62 +56,82 @@ public class SettingsActivity extends AppCompatActivity
return super.onOptionsItemSelected(item);
}
public static class SettingsFragment extends PreferenceFragmentCompat
{
public static class SettingsFragment extends PreferenceFragmentCompat {
private static final String DIALOG_FRAGMENT_TAG = "SettingsFragment";
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey)
{
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.preferences);
// Show pretty names
ListPreference localePreference = findPreference(getResources().getString(R.string.settings_key_locale));
assert localePreference != null;
CharSequence[] entryValues = localePreference.getEntryValues();
List<CharSequence> entries = new ArrayList<>();
for (CharSequence entry : entryValues) {
if (entry.length() == 0) {
entries.add(getResources().getString(R.string.settings_system_locale));
} else {
Locale entryLocale = Utils.stringToLocale(entry.toString());
entries.add(entryLocale.getDisplayName(entryLocale));
}
}
localePreference.setEntries(entries.toArray(new CharSequence[entryValues.length]));
Preference themePreference = findPreference(getResources().getString(R.string.settings_key_theme));
assert themePreference != null;
themePreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
{
@Override
public boolean onPreferenceChange(Preference preference, Object o)
{
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);
}
FragmentActivity activity = getActivity();
if (activity != null) {
activity.recreate();
}
return true;
themePreference.setOnPreferenceChangeListener((preference, 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);
}
FragmentActivity activity = getActivity();
if (activity != null) {
ActivityCompat.recreate(activity);
}
return true;
});
Preference colorPreference = findPreference(getResources().getString(R.string.setting_key_theme_color));
assert colorPreference != null;
colorPreference.setOnPreferenceChangeListener((preference, o) -> {
FragmentActivity activity = getActivity();
if (activity != null) {
ActivityCompat.recreate(activity);
}
return true;
});
localePreference.setOnPreferenceChangeListener((preference, newValue) -> {
// Refresh the activity
Intent intent = new Intent(getContext(), SettingsActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
getContext().startActivity(intent);
return true;
});
}
@Override
public void onDisplayPreferenceDialog(Preference preference)
{
if (preference instanceof NumberDialogPreference)
{
public void onDisplayPreferenceDialog(Preference preference) {
if (preference instanceof NumberDialogPreference) {
NumberDialogPreference dialogPreference = (NumberDialogPreference) preference;
DialogFragment dialogFragment = NumberPickerPreferenceDialogFragment
.newInstance(
dialogPreference.getKey(),
dialogPreference.getMinValue(),
dialogPreference.getMaxValue(),
dialogPreference.getStepValue(),
dialogPreference.getUnitText()
);
dialogFragment.setTargetFragment(this, 0);
dialogFragment.show(getParentFragmentManager(), DIALOG_FRAGMENT_TAG);
}
else
{
} else {
super.onDisplayPreferenceDialog(preference);
}
}

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" android:useLevel="true"
android:dither="true">
<size android:height="12dip" android:width="12dip"/>
<solid android:color="@android:color/white"/>
</shape>

View File

@@ -1,66 +0,0 @@
<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,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M19,14L19,6c0,-1.1 -0.9,-2 -2,-2L3,4c-1.1,0 -2,0.9 -2,2v8c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM10,13c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM23,7v11c0,1.1 -0.9,2 -2,2L4,20v-2h17L21,7h2z"/>
</vector>

View File

@@ -0,0 +1,5 @@
<vector android:autoMirrored="true" 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,18h6v-2L3,16v2zM3,6v2h18L21,6L3,6zM3,13h12v-2L3,11v2z"/>
</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.41,18.59L8.83,20 12,16.83 15.17,20l1.41,-1.41L12,14l-4.59,4.59zM16.59,5.41L15.17,4 12,7.17 8.83,4 7.41,5.41 12,10l4.59,-4.59z"/>
</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,5.83L15.17,9l1.41,-1.41L12,3 7.41,7.59 8.83,9 12,5.83zM12,18.17L8.83,15l-1.41,1.41L12,21l4.59,-4.59L15.17,15 12,18.17z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="@color/colorPrimary"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFF"
android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
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,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M18,5l0,-3l-12,0l0,1.17l1.83,1.83z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M16,11l2,-3l0,-1l-8.17,0l6.17,6.17z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M2.81,2.81L1.39,4.22L8,10.83V22h8v-3.17l3.78,3.78l1.41,-1.41L2.81,2.81z"
android:fillColor="#FFFFFF"/>
</vector>

View File

@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M6,2h12v3h-12z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M6,7v1l2,3v11h8V11l2,-3V7H6zM12,15.5c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5s1.5,0.67 1.5,1.5S12.83,15.5 12,15.5z"
android:fillColor="#FFFFFF"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
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,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" android:useLevel="true"
android:dither="true">
<size android:height="8dip" android:width="8dip"/>
<solid android:color="@android:color/white"/>
</shape>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/listItemHighlight" android:state_activated="true" />
</selector>

View File

@@ -22,20 +22,333 @@
</com.google.android.material.appbar.AppBarLayout>
<LinearLayout
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:orientation="vertical">
android:padding="10dp">
<TextView
android:id="@+id/aboutText"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:linksClickable="true"
android:singleLine="false"
android:focusable="true" />
android:orientation="vertical">
</LinearLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/version_history"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp">
<TextView
android:id="@+id/version_history_main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:padding="2dp"
android:text="@string/version_history"
android:textColor="@color/colorSecondaryText"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/version_history_sub"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="2dp"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/version_history_main" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:fontFamily="sans-serif-medium"
android:text="@string/arrow"
android:textColor="@color/colorSecondaryText"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/credits"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp">
<TextView
android:id="@+id/credits_main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:padding="2dp"
android:text="@string/credits"
android:textColor="@color/colorSecondaryText"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/credits_sub"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="2dp"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/credits_main" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:fontFamily="sans-serif-medium"
android:text="@string/arrow"
android:textColor="@color/colorSecondaryText"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/translate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp">
<TextView
android:id="@+id/translate_main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:padding="2dp"
android:text="@string/help_translate_this_app"
android:textColor="@color/colorSecondaryText"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:fontFamily="sans-serif-medium"
android:text="@string/arrow"
android:textColor="@color/colorSecondaryText"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/license"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp">
<TextView
android:id="@+id/license_main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:padding="2dp"
android:text="@string/license"
android:textColor="@color/colorSecondaryText"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/license_sub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="2dp"
android:text="@string/app_license"
android:layout_marginEnd="20dp"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/license_main"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:fontFamily="sans-serif-medium"
android:text="@string/arrow"
android:textColor="@color/colorSecondaryText"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/repo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp">
<TextView
android:id="@+id/repo_main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:padding="2dp"
android:text="@string/source_repository"
android:textColor="@color/colorSecondaryText"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/repo_sub"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="2dp"
android:text="@string/on_github"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/repo_main" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:fontFamily="sans-serif-medium"
android:text="@string/arrow"
android:textColor="@color/colorSecondaryText"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/privacy"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp">
<TextView
android:id="@+id/privacy_main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:padding="2dp"
android:text="@string/privacy_policy"
android:textColor="@color/colorSecondaryText"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/privacy_sub"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="2dp"
android:text="@string/and_data_usage"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/privacy_main" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:fontFamily="sans-serif-medium"
android:text="@string/arrow"
android:textColor="@color/colorSecondaryText"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/rate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp">
<TextView
android:id="@+id/rate_main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:padding="2dp"
android:text="@string/rate_this_app"
android:textColor="@color/colorSecondaryText"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/rate_sub"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="2dp"
android:text="@string/on_google_play"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/rate_main" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:fontFamily="sans-serif-medium"
android:text="@string/arrow"
android:textColor="@color/colorSecondaryText"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:padding="8dp"
android:id="@+id/report_error"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/report_error_main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:padding="2dp"
android:text="@string/report_error"
android:textColor="@color/colorSecondaryText"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/report_error_sub"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/report_error_main"
app:layout_constraintStart_toStartOf="parent"
android:textSize="16sp"
android:text="@string/on_github"
android:padding="2dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:fontFamily="sans-serif-medium"
android:text="@string/arrow"
android:textColor="@color/colorSecondaryText"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</ScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -0,0 +1,65 @@
<?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.ManageGroupActivity">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabSave"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
app:srcCompat="@drawable/save_24dp"
android:contentDescription="@string/save"
android:layout_margin="16dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<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.tabs.TabLayout
android:id="@+id/groups"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:tabMode="scrollable" />
</com.google.android.material.appbar.AppBarLayout>
<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/editTextGroupName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
<include
android:id="@+id/include"
layout="@layout/content_main" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:orientation="vertical"
android:padding="10.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
android:layout_width="match_parent"
android:layout_height="@dimen/barcode_disp_height"
android:layout_gravity="center_horizontal"
android:id="@+id/barcodeImage"
android:layout_weight="1.0"/>
<TextView
android:id="@+id/barcodeName"
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>

View File

@@ -1,308 +1,88 @@
<?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">
<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="fill_parent"
android:layout_height="fill_parent">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_width="fill_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
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:id="@+id/scrollView">
android:orientation="vertical">
<TextView
android:id="@+id/explanationText"
android:textSize="20sp"
android:layout_gravity="center_vertical"
android:paddingStart="20.0dip"
android:paddingEnd="20.0dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/barcodeIdLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:text="@string/enterBarcodeInstructions" />
<LinearLayout
android:id="@+id/barcodeIdLayout"
android:orientation="horizontal"
android:padding="10.0dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/explanationText"
app:layout_constraintBottom_toTopOf="@+id/noBarcode"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<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" />
<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="20sp"
android:layout_gravity="center_vertical"
android:paddingStart="20.0dip"
android:paddingEnd="20.0dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/enterBarcodeInstructions" />
</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:importantForAutofill="no"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="text"/>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
<EditText
android:id="@+id/cardId"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/barcodesLayout"/>
<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="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="vertical"
android:padding="10.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
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="vertical"
android:padding="10.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
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="vertical"
android:padding="10.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
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="vertical"
android:padding="10.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
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="vertical"
android:padding="10.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
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="vertical"
android:padding="10.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
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="vertical"
android:padding="10.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
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="vertical"
android:padding="10.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
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="vertical"
android:padding="10.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
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="vertical"
android:padding="10.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
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>
android:hint="AB1234"
android:importantForAutofill="no"
android:inputType="text"
android:minHeight="48dp" />
</LinearLayout>
</ScrollView>
<Button
android:id="@+id/noBarcode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/barcodeIdLayout"
app:layout_constraintBottom_toTopOf="@+id/barcodes"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:text="@string/barcodeNoBarcode"
android:textColor="#FFFFFF"
android:enabled="false" />
<ListView
android:id="@+id/barcodes"
android:layout_width="match_parent"
android:layout_height="fill_parent"
app:layout_constraintTop_toBottomOf="@+id/noBarcode"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:scrollbars="vertical" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -26,10 +26,24 @@
android:gravity="center"
android:text="@string/noMatchingGiftCards"
android:visibility="gone"/>
<ListView
<TextView
style="@style/AppTheme.TextView.NoData"
android:id="@+id/noGroupCardsText"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="@+id/list"
android:gravity="center"
android:text="@string/noGroupCards"
android:visibility="gone"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="@integer/main_view_card_columns"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
android:background="@color/mainLoyaltyCardBackground"
android:visibility="gone"/>
</RelativeLayout>

View File

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

View File

@@ -4,7 +4,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:baselineAligned="false"
android:padding="@dimen/activity_margin">
@@ -44,8 +44,7 @@
android:layout_weight="1"
app:srcCompat="@drawable/ic_baseline_arrow_drop_up_24"
android:contentDescription="@string/moveUp"
app:tint="@color/iconColor"
android:onClick="moveGroupUp"/>
app:tint="@color/iconColor"/>
<ImageButton
android:id="@+id/moveDown"
@@ -54,8 +53,7 @@
android:layout_weight="1"
app:srcCompat="@drawable/ic_baseline_arrow_drop_down_24"
android:contentDescription="@string/moveDown"
app:tint="@color/iconColor"
android:onClick="moveGroupDown"/>
app:tint="@color/iconColor"/>
<ImageButton
android:id="@+id/edit"
@@ -64,8 +62,7 @@
android:layout_weight="1"
app:srcCompat="@drawable/ic_mode_edit_white_24dp"
android:contentDescription="@string/edit"
app:tint="@color/iconColor"
android:onClick="editGroup" />
app:tint="@color/iconColor"/>
<ImageButton
android:id="@+id/delete"
@@ -74,8 +71,7 @@
android:layout_weight="1"
app:srcCompat="@drawable/ic_delete_white_24dp"
android:contentDescription="@string/delete"
app:tint="@color/iconColor"
android:onClick="deleteGroup"/>
app:tint="@color/iconColor"/>
</LinearLayout>

View File

@@ -17,9 +17,10 @@
android:text="@string/noGroups"
android:visibility="gone"/>
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list"
android:visibility="gone"/>
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
android:visibility="gone" />
</RelativeLayout>

View File

@@ -1,9 +1,11 @@
<?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">
<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">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabSave"
@@ -26,6 +28,24 @@
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/card"/>
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/barcode"/>
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/photos"/>
</com.google.android.material.tabs.TabLayout>
</com.google.android.material.appbar.AppBarLayout>
<ScrollView android:layout_width="fill_parent"
@@ -37,169 +57,358 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- Store -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/inputPadding"
android:paddingTop="@dimen/inputPadding"
android:orientation="horizontal">
<androidx.cardview.widget.CardView
android:layout_width="@dimen/cardThumbnailSize"
android:layout_height="@dimen/cardThumbnailSize"
android:layout_marginEnd="@dimen/activity_margin"
android:layout_gravity="center_vertical"
app:cardCornerRadius="4dp"
<TableLayout
android:id="@+id/cardPart"
android:visibility="gone">
<!-- Store -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/inputPadding"
app:cardElevation="0dp">
android:paddingTop="@dimen/inputPadding"
android:orientation="horizontal">
<ImageView
android:id="@+id/thumbnail"
<com.google.android.material.card.MaterialCardView
android:layout_width="@dimen/cardThumbnailSize"
android:layout_height="@dimen/cardThumbnailSize"
android:contentDescription="@string/thumbnailDescription"
android:src="@mipmap/ic_launcher"/>
android:layout_marginEnd="@dimen/activity_margin"
android:layout_gravity="center_vertical"
app:cardCornerRadius="4dp"
android:paddingHorizontal="@dimen/inputPadding"
app:cardElevation="0dp"
app:cardBackgroundColor="@android:color/transparent"
android:outlineProvider="none">
</androidx.cardview.widget.CardView>
<ImageView
android:id="@+id/thumbnail"
android:layout_width="@dimen/cardThumbnailSize"
android:layout_height="@dimen/cardThumbnailSize"
android:contentDescription="@string/thumbnailDescription"
android:src="@mipmap/ic_launcher"/>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/storeNameField"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/storeName">
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/storeNameEdit"
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/storeNameField"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
android:hint="@string/storeName">
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/storeNameEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true" />
<!-- 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">
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<!-- Card ID -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/cardIdField"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="0dp"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:hint="@string/cardId">
android:paddingHorizontal="@dimen/inputPadding"
android:paddingTop="@dimen/inputPadding"
android:orientation="horizontal">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/cardIdView"
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/cardIdField"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
android:hint="@string/cardId">
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/cardIdView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true" />
</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>
<!-- Balance -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/inputPadding"
android:paddingTop="@dimen/inputPadding"
android:orientation="horizontal">
<!-- Balance -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/balanceView"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:hint="@string/balance"
android:labelFor="@+id/balanceField">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/balanceField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="numberDecimal"
android:digits="0123456789.,$" />
</com.google.android.material.textfield.TextInputLayout>
<!-- Currency -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/balanceCurrencyView"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:hint="@string/currency"
android:labelFor="@+id/balanceCurrencyField">
<AutoCompleteTextView
android:id="@+id/balanceCurrencyField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<!-- Expiration -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/inputPadding"
android:paddingTop="@dimen/inputPadding"
android:orientation="horizontal">
<!-- Expiry date -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/expiryView"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:hint="@string/expiryDate"
android:labelFor="@+id/expiryField">
<AutoCompleteTextView
android:id="@+id/expiryField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</TableLayout>
<TableLayout
android:id="@+id/barcodePart"
android:visibility="gone">
<!-- Barcode ID -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/inputPadding"
android:paddingTop="@dimen/inputPadding"
android:orientation="horizontal">
<!-- Barcode ID -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/barcodeIdView"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:hint="@string/barcodeId"
android:labelFor="@+id/barcodeIdView">
<AutoCompleteTextView
android:id="@+id/barcodeIdField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<!-- Barcode type -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/barcodeTypeView"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:hint="@string/barcodeType"
android:labelFor="@+id/barcodeTypeField">
android:paddingHorizontal="@dimen/inputPadding"
android:paddingTop="@dimen/inputPadding"
android:orientation="horizontal">
<AutoCompleteTextView
android:id="@+id/barcodeTypeField"
<!-- Barcode type -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/barcodeTypeView"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:hint="@string/barcodeType"
android:labelFor="@+id/barcodeTypeField">
<AutoCompleteTextView
android:id="@+id/barcodeTypeField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<!-- Barcode -->
<View
android:layout_height="@dimen/inputBorderThickness"
android:layout_width="match_parent" />
<LinearLayout android:orientation="horizontal"
android:padding="10.0dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:id="@+id/barcodeLayout">
<ImageView
android:layout_width="0dp"
android:layout_height="@dimen/barcode_disp_height"
android:layout_gravity="center_horizontal"
android:padding="10.0dp"
android:background="#ffffff"
android:id="@+id/barcode"
android:layout_weight="1.0"/>
</LinearLayout>
<!-- Buttons -->
<LinearLayout android:orientation="horizontal"
android:padding="10.0dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/barcodeCaptureLayout">
<Button android:id="@+id/enterButton"
android:layout_margin="@dimen/inputMargin"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/editBarcode"
android:textColor="#FFFFFF"
android:layout_weight="1.0"/>
</LinearLayout>
</TableLayout>
<TableLayout
android:id="@+id/picturesPart"
android:visibility="gone"
tools:visibility="visible">
<!-- Front image -->
<LinearLayout
android:id="@+id/frontImageHolder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingHorizontal="@dimen/inputPadding"
android:paddingTop="@dimen/inputPadding">
<!-- Front image -->
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
android:layout_gravity="center_vertical"
android:layout_marginStart="@dimen/activity_margin"
android:layout_marginTop="@dimen/activity_margin"
android:layout_marginEnd="@dimen/activity_margin"
android:layout_marginBottom="@dimen/activity_margin"
android:paddingHorizontal="@dimen/inputPadding"
app:cardCornerRadius="4dp"
app:cardElevation="0dp">
<!-- Barcode -->
<View
android:layout_height="@dimen/inputBorderThickness"
android:layout_width="match_parent" />
<ImageView
android:id="@+id/frontImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:minHeight="50dp"
android:contentDescription="@string/frontImageDescription"
android:scaleType="fitCenter"
app:srcCompat="@drawable/ic_camera_white" />
<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>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
<!-- Back image -->
<LinearLayout
android:id="@+id/backImageHolder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingHorizontal="@dimen/inputPadding"
android:paddingTop="@dimen/inputPadding">
<!-- Back image -->
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="@dimen/activity_margin"
android:layout_marginTop="@dimen/activity_margin"
android:layout_marginEnd="@dimen/activity_margin"
android:layout_marginBottom="@dimen/activity_margin"
android:paddingHorizontal="@dimen/inputPadding"
app:cardCornerRadius="4dp"
app:cardElevation="0dp">
<ImageView
android:id="@+id/backImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:minHeight="50dp"
android:contentDescription="@string/backImageDescription"
android:scaleType="fitCenter"
app:srcCompat="@drawable/ic_camera_white" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</TableLayout>
</TableLayout>
</ScrollView>

View File

@@ -1,64 +1,147 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/row"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:baselineAligned="false"
android:padding="@dimen/activity_margin">
android:layout_height="wrap_content"
android:layout_margin="8dp">
<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">
<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"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1">
android:layout_marginBottom="4dp">
<LinearLayout
android:id="@+id/valueLayout"
android:layout_width="wrap_content"
<com.google.android.material.card.MaterialCardView
android:id="@+id/icon_layout"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="85.6f:53.98f"
app:cardCornerRadius="8dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/store">
<ImageView
android:id="@+id/thumbnail"
android:scaleType="fitCenter"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/thumbnailDescription"
android:src="@mipmap/ic_launcher"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ImageView
android:id="@+id/selected_thumbnail"
android:scaleType="fitCenter"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/thumbnailDescription"
app:srcCompat="@drawable/ic_done"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<ImageView
android:id="@+id/star"
android:layout_width="@dimen/cardThumbnailSize"
android:layout_height="@dimen/cardThumbnailSize"
android:layout_gravity="end"
app:srcCompat="@drawable/ic_starred_white"
android:contentDescription="@string/starImage"
app:tint="?attr/colorControlNormal"
android:visibility="gone"
tools:visibility="visible"
android:elevation="4dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/store"
android:layout_width="match_parent"
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>
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:textAppearance="?attr/textAppearanceHeadline1"
app:layout_constraintTop_toBottomOf="@+id/icon_layout"
app:layout_constraintBottom_toTopOf="@+id/note"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:text="Example store"/>
<TextView
android:id="@+id/note"
android:layout_width="wrap_content"
android:layout_width="match_parent"
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>
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:textAppearance="?attr/textAppearanceBody2"
android:textColor="?android:attr/textColorSecondary"
android:visibility="gone"
tools:visibility="visible"
app:layout_constraintTop_toBottomOf="@+id/store"
app:layout_constraintBottom_toTopOf="@+id/info_divider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:text="Example note"/>
<View
android:id="@+id/info_divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:layout_marginBottom="8dp"
android:background="?android:attr/dividerVertical"
android:visibility="gone"
tools:visibility="visible"
app:layout_constraintTop_toBottomOf="@+id/note"
app:layout_constraintBottom_toTopOf="@+id/balance"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<TextView
android:id="@+id/balance"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:textAppearance="?attr/textAppearanceBody2"
android:textColor="?android:attr/textColorSecondary"
app:drawableLeftCompat="@drawable/ic_baseline_payments_24"
android:drawablePadding="4dp"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@+id/info_divider"
app:layout_constraintBottom_toTopOf="@+id/expiry"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:visibility="visible"
tools:text="525 points"/>
<TextView
android:id="@+id/expiry"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:textAppearance="?attr/textAppearanceBody2"
android:textColor="?android:attr/textColorSecondary"
app:drawableLeftCompat="@drawable/ic_baseline_access_time_24"
android:drawablePadding="4dp"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@+id/balance"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:visibility="visible"
tools:text="Tomorrow"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>

View File

@@ -2,11 +2,11 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinator_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fitsSystemWindows="true"
>
android:fitsSystemWindows="true">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabEdit"
@@ -36,30 +36,102 @@
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"
<androidx.constraintlayout.widget.Guideline
android:id="@+id/scalerGuideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.75"/>
<ImageButton
android:id="@+id/maximizeButton"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginStart="15.0dip"
android:layout_marginEnd="15.0dip"
android:layout_marginTop="10dp"
android:padding="0dp"
app:srcCompat="@drawable/ic_baseline_arrow_drop_up_24"
android:contentDescription="@string/moveBarcodeToTopOfScreen"
app:tint="#ffffff"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/mainImage"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ImageView
android:id="@+id/mainImage"
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="horizontal"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_marginStart="15.0dip"
android:layout_marginEnd="15.0dip"
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"/>
app:layout_constraintTop_toBottomOf="@+id/maximizeButton"/>
<ImageButton
android:id="@+id/minimizeButton"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginStart="15.0dip"
android:layout_marginEnd="15.0dip"
android:layout_marginTop="10dp"
android:padding="0dp"
app:srcCompat="@drawable/ic_baseline_arrow_drop_down_24"
android:contentDescription="@string/moveBarcodeToCenterOfScreen"
app:tint="#ffffff"
app:layout_constraintTop_toBottomOf="@+id/mainImage"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<LinearLayout
android:id="@+id/dotIndicator"
android:visibility="gone"
android:gravity="center_vertical"
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_marginStart="15.0dip"
android:layout_marginEnd="15.0dip"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/minimizeButton"/>
<SeekBar
android:id="@+id/barcodeScaler"
android:visibility="gone"
android:max="100"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/inputPadding"
android:layout_marginStart="15.0dip"
android:layout_marginEnd="15.0dip"
app:layout_constraintTop_toBottomOf="@+id/scalerGuideline"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/cardIdView"
android:enabled="true"
android:textIsSelectable="true"
android:focusable="true"
android:longClickable="true"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginLeft="10.0dip"
android:layout_marginRight="10.0dip"
android:layout_marginBottom="80dp"
app:layout_constraintTop_toBottomOf="@+id/barcode"
android:paddingBottom="80dp"
app:layout_constraintTop_toBottomOf="@+id/dotIndicator"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@@ -80,91 +152,160 @@
<LinearLayout
android:id="@+id/bottom_sheet"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/inputBackground"
android:orientation="vertical"
android:paddingTop="0px"
android:visibility="gone"
app:behavior_hideable="false"
app:behavior_peekHeight="80dp"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
tools:visibility="visible">
<ImageButton
android:id="@+id/bottomSheetButton"
android:layout_width="match_parent"
android:layout_height="80dp"
android:background="@color/colorPrimary"
android:gravity="center"
app:srcCompat="@drawable/ic_baseline_arrow_drop_up_24"
android:adjustViewBounds="true"
android:layout_gravity="top|start"
android:contentDescription="@string/toggleMoreInfo"
android:scaleType="fitCenter"
android:tint="@android:color/white" />
app:srcCompat="@drawable/ic_baseline_arrow_drop_up_24"
app:tint="#ffffff" />
<TextView
android:id="@+id/noteView"
<androidx.core.widget.NestedScrollView
android:id="@+id/bottomSheetContentWrapper"
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" />
android:layout_height="wrap_content">
<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
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/noteView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="all"
android:enabled="true"
android:focusable="true"
android:gravity="center"
android:longClickable="true"
android:padding="20dp"
android:textIsSelectable="true"
android:textSize="@dimen/singleCardNoteTextSizeMin" />
<TextView
android:id="@+id/groupsView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="true"
android:focusable="true"
android:gravity="center"
android:longClickable="true"
android:padding="20dp"
android:textIsSelectable="true"
android:textSize="@dimen/singleCardNoteTextSizeMin" />
<TextView
android:id="@+id/balanceView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="true"
android:focusable="true"
android:gravity="center"
android:longClickable="true"
android:padding="20dp"
android:textIsSelectable="true"
android:textSize="@dimen/singleCardNoteTextSizeMin" />
<TextView
android:id="@+id/expiryView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="true"
android:focusable="true"
android:gravity="center"
android:longClickable="true"
android:padding="20dp"
android:textIsSelectable="true"
android:textSize="@dimen/singleCardNoteTextSizeMin" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</LinearLayout>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:clipChildren="false"
android:clipToPadding="false"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:weightSum="1.0"
android:fitsSystemWindows="true">
>
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar_landscape"
android:layout_width="fill_parent"
android:layout_height="?actionBarSize"
android:background="@android:color/transparent"
android:fitsSystemWindows="false"
android:paddingTop="0dp"
android:theme="@style/CardView.ActionBarTheme"
android:visibility="gone"
app:contentInsetStart="72.0dip"
app:layout_collapseMode="pin" />
<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:clipChildren="false"
android:clipToPadding="false"
android:minHeight="56.0dip"
android:layout_weight="1.0"
app:expandedTitleMarginStart="48dp"
app:expandedTitleMarginEnd="64dp"
app:contentScrim="?colorPrimary"
app:expandedTitleGravity="top">
<TextView
app:expandedTitleGravity="top"
app:expandedTitleMarginEnd="64dp"
app:expandedTitleMarginStart="48dp">
<androidx.appcompat.widget.AppCompatTextView
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"/>
android:ellipsize="end"
android:maxLines="1"
android:textAlignment="center"
android:textColor="@android:color/white"
android:textSize="40sp"
app:layout_collapseMode="parallax" />
<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"
android:background="@android:color/transparent"
android:theme="@style/CardView.ActionBarTheme"
app:contentInsetStart="72.0dip"
app:layout_collapseMode="pin" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<ImageView
android:id="@+id/icon_image"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_alignParentTop="true"
android:fitsSystemWindows="true"
android:scaleType="centerCrop"
app:srcCompat="@drawable/ic_launcher_foreground"
tools:ignore="ContentDescription" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

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