Compare commits

...

81 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
1624d56edb Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker into feature/power_screen_widgets 2021-10-26 21:42:14 +02:00
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
/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
82 changed files with 602 additions and 561 deletions

View File

@@ -1,5 +1,20 @@
# Changelog
## v2.11.2 - 95 (2021-12-04)
- 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

View File

@@ -18,8 +18,8 @@ android {
applicationId "me.hackerchick.catima"
minSdkVersion 21
targetSdkVersion 31
versionCode 92
versionName "2.10.0"
versionCode 95
versionName "2.11.2"
vectorDrawables.useSupportLibrary true
multiDexEnabled true
@@ -49,8 +49,8 @@ android {
// Flag to enable support for the new language APIs
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
lintOptions {
@@ -97,7 +97,7 @@ dependencies {
implementation 'com.google.zxing:core:3.4.1'
implementation 'org.apache.commons:commons-csv:1.9.0'
implementation 'com.jaredrummler:colorpicker:1.1.0'
implementation 'com.github.invissvenska:NumberPickerPreference:1.0.3'
implementation 'com.github.invissvenska:NumberPickerPreference:1.0.4'
implementation 'net.lingala.zip4j:zip4j:2.9.1'
// SpotBugs
@@ -106,7 +106,7 @@ dependencies {
// Testing
testImplementation 'androidx.test:core:1.4.0'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.robolectric:robolectric:4.7.1'
testImplementation 'org.robolectric:robolectric:4.7.3'
}
tasks.withType(SpotBugsTask) {

View File

@@ -37,11 +37,11 @@
<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"
@@ -117,7 +117,7 @@
<activity
android:name="com.yalantis.ucrop.UCropActivity"
android:theme="@style/Theme.AppCompat.NoActionBar"/>
android:theme="@style/AppTheme.NoActionBar" />
<provider
android:name="androidx.core.content.FileProvider"
@@ -128,6 +128,13 @@
android:name="android.support.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>

View File

@@ -5,12 +5,17 @@ import android.database.Cursor;
import androidx.recyclerview.widget.RecyclerView;
public abstract class BaseCursorAdapter<V extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<V> {
private Cursor mCursor;
public Cursor mCursor;
private boolean mDataValid;
private int mRowIDColumn;
public BaseCursorAdapter(Cursor inputCursor) {
private String mRowIDColumnName;
public BaseCursorAdapter(Cursor inputCursor, String rowIDColumnName) {
setHasStableIds(true);
mRowIDColumnName = rowIDColumnName;
swapCursor(inputCursor);
}
@@ -58,6 +63,7 @@ public abstract class BaseCursorAdapter<V extends RecyclerView.ViewHolder> exten
if (inputCursor != null) {
mCursor = inputCursor;
mRowIDColumn = mCursor.getColumnIndex(mRowIDColumnName);
mDataValid = true;
notifyDataSetChanged();
} else {

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

@@ -1,24 +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 Activity activity;
private final Context mContext;
public CatimaCaptureManager(Activity activity, DecoratedBarcodeView barcodeView) {
super(activity, barcodeView);
this.activity = activity;
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(activity, message, Toast.LENGTH_LONG).show();
Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();
}
}

View File

@@ -71,12 +71,8 @@ public class DBHelper extends SQLiteOpenHelper {
Descending
}
private Context mContext;
public DBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
mContext = context;
}
@Override
@@ -583,7 +579,7 @@ public class DBHelper extends SQLiteOpenHelper {
}
}
public boolean deleteLoyaltyCard(final int id) {
public boolean deleteLoyaltyCard(Context context, final int id) {
SQLiteDatabase db = getWritableDatabase();
// Delete card
int rowsDeleted = db.delete(LoyaltyCardDbIds.TABLE,
@@ -603,7 +599,7 @@ public class DBHelper extends SQLiteOpenHelper {
// Also wipe card images associated with this card
for (ImageLocationType imageLocationType : ImageLocationType.values()) {
try {
Utils.saveCardImage(mContext, null, id, imageLocationType);
Utils.saveCardImage(context, null, id, imageLocationType);
} catch (FileNotFoundException e) {
e.printStackTrace();
}

View File

@@ -12,28 +12,21 @@ import androidx.appcompat.widget.AppCompatImageButton;
import androidx.recyclerview.widget.RecyclerView;
import protect.card_locker.preferences.Settings;
class GroupCursorAdapter extends BaseCursorAdapter<GroupCursorAdapter.GroupListItemViewHolder> {
public class GroupCursorAdapter extends BaseCursorAdapter<GroupCursorAdapter.GroupListItemViewHolder> {
Settings mSettings;
private Cursor mCursor;
private final Context mContext;
private final GroupCursorAdapter.GroupAdapterListener mListener;
private final GroupAdapterListener mListener;
DBHelper mDb;
public GroupCursorAdapter(Context inputContext, Cursor inputCursor, GroupCursorAdapter.GroupAdapterListener inputListener) {
super(inputCursor);
public GroupCursorAdapter(Context inputContext, Cursor inputCursor, GroupAdapterListener inputListener) {
super(inputCursor, DBHelper.LoyaltyCardDbGroups.ORDER);
setHasStableIds(true);
mSettings = new Settings(inputContext);
mContext = inputContext;
mContext = inputContext.getApplicationContext();
mListener = inputListener;
mDb = new DBHelper(inputContext);
swapCursor(mCursor);
}
@Override
public void swapCursor(Cursor inputCursor) {
super.swapCursor(inputCursor);
mCursor = inputCursor;
swapCursor(inputCursor);
}
@NonNull
@@ -43,11 +36,7 @@ class GroupCursorAdapter extends BaseCursorAdapter<GroupCursorAdapter.GroupListI
return new GroupListItemViewHolder(itemView);
}
public Cursor getCursor() {
return mCursor;
}
public void onBindViewHolder(GroupCursorAdapter.GroupListItemViewHolder inputHolder, Cursor inputCursor) {
public void onBindViewHolder(GroupListItemViewHolder inputHolder, Cursor inputCursor) {
Group group = Group.toGroup(inputCursor);
inputHolder.mName.setText(group._id);

View File

@@ -10,14 +10,20 @@ import android.os.Bundle;
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.FileOutputStream;
import java.io.IOException;
@@ -26,11 +32,6 @@ import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
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 protect.card_locker.async.TaskHandler;
import protect.card_locker.importexport.DataFormat;
import protect.card_locker.importexport.ImportExportResult;
@@ -39,8 +40,6 @@ 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 IMPORT = 3;
private ImportExportTask importExporter;
@@ -49,6 +48,10 @@ public class ImportExportActivity extends CatimaAppCompatActivity {
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
@@ -76,6 +79,49 @@ public class ImportExportActivity extends CatimaAppCompatActivity {
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);
@@ -83,60 +129,57 @@ public class ImportExportActivity extends CatimaAppCompatActivity {
intentCreateDocumentAction.putExtra(Intent.EXTRA_TITLE, "catima.zip");
Button exportButton = findViewById(R.id.exportButton);
exportButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(ImportExportActivity.this);
builder.setTitle(R.string.exportPassword);
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;
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);
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();
chooseFileWithIntent(intentCreateDocumentAction, CHOOSE_EXPORT_LOCATION);
});
builder.setNegativeButton(R.string.cancel, (dialogInterface, i) -> dialogInterface.cancel());
builder.show();
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) {
chooseImportType(intentGetContentAction);
}
});
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) {
chooseImportType(intentPickAction);
}
});
importApplication.setOnClickListener(v -> chooseImportType(true));
}
private void chooseImportType(Intent baseIntent) {
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");
@@ -194,7 +237,17 @@ public class ImportExportActivity extends CatimaAppCompatActivity {
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
chooseFileWithIntent(baseIntent, IMPORT);
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)
@@ -297,7 +350,7 @@ public class ImportExportActivity extends CatimaAppCompatActivity {
builder.setView(input);
builder.setPositiveButton(R.string.ok, (dialogInterface, i) -> {
activityResultParser(IMPORT, RESULT_OK, uri, input.getText().toString().toCharArray());
openFileForImport(uri, input.getText().toString().toCharArray());
});
builder.setNegativeButton(R.string.cancel, (dialogInterface, i) -> dialogInterface.cancel());
@@ -373,69 +426,4 @@ public class ImportExportActivity extends CatimaAppCompatActivity {
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);
}
}
private void activityResultParser(int requestCode, int resultCode, Uri uri, char[] password) {
if (resultCode != RESULT_OK) {
Log.w(TAG, "Failed onActivityResult(), result=" + resultCode);
return;
}
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, exportPassword.toCharArray(), true);
} else {
InputStream reader;
if (uri.getScheme() != null) {
reader = getContentResolver().openInputStream(uri);
} else {
reader = new FileInputStream(new File(uri.toString()));
}
Log.e(TAG, "Starting file import with: " + uri.toString());
startImport(reader, uri, importDataFormat, password, true);
}
} catch (IOException e) {
Log.e(TAG, "Failed to import/export file: " + uri.toString(), e);
if (requestCode == CHOOSE_EXPORT_LOCATION) {
onExportComplete(ImportExportResult.GenericFailure, uri);
} else {
onImportComplete(ImportExportResult.GenericFailure, uri, importDataFormat);
}
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (data == null) {
Log.e(TAG, "Activity returned NULL data");
return;
}
activityResultParser(requestCode, resultCode, data.getData(), null);
}
}

View File

@@ -33,7 +33,7 @@ public class ImportURIHelper {
private final String shareMultipleText;
public ImportURIHelper(Context context) {
this.context = context;
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);
@@ -197,6 +197,7 @@ public class ImportURIHelper {
sendIntent.setType("text/plain");
Intent shareIntent = Intent.createChooser(sendIntent, null);
shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(shareIntent);
}
}

View File

@@ -1,37 +0,0 @@
package protect.card_locker;
import android.animation.AnimatorInflater;
import android.animation.AnimatorSet;
import android.content.Context;
import android.view.View;
public class LoyaltyCardAnimator {
private static AnimatorSet selectedViewIn, defaultViewOut, selectedViewOut, defaultViewIn;
public static void flipView(Context inputContext, final View inputSelectedView, final View inputDefaultView, boolean inputItemSelected) {
selectedViewIn = (AnimatorSet) AnimatorInflater.loadAnimator(inputContext, R.animator.flip_left_in);
defaultViewOut = (AnimatorSet) AnimatorInflater.loadAnimator(inputContext, R.animator.flip_right_out);
selectedViewOut = (AnimatorSet) AnimatorInflater.loadAnimator(inputContext, R.animator.flip_left_out);
defaultViewIn = (AnimatorSet) AnimatorInflater.loadAnimator(inputContext, R.animator.flip_right_in);
final AnimatorSet showFrontAnim = new AnimatorSet();
final AnimatorSet showBackAnim = new AnimatorSet();
selectedViewIn.setTarget(inputSelectedView);
defaultViewOut.setTarget(inputDefaultView);
showFrontAnim.playTogether(selectedViewIn, defaultViewOut);
selectedViewOut.setTarget(inputSelectedView);
defaultViewIn.setTarget(inputDefaultView);
showBackAnim.playTogether(defaultViewIn, selectedViewOut);
if (inputItemSelected) {
showFrontAnim.start();
} else {
showBackAnim.start();
}
}
}

View File

@@ -30,7 +30,6 @@ import protect.card_locker.preferences.Settings;
public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCursorAdapter.LoyaltyCardListItemViewHolder> {
private int mCurrentSelectedIndex = -1;
private Cursor mCursor;
Settings mSettings;
boolean mDarkModeEnabled;
private final Context mContext;
@@ -41,23 +40,17 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
private boolean mShowDetails = true;
public LoyaltyCardCursorAdapter(Context inputContext, Cursor inputCursor, CardAdapterListener inputListener) {
super(inputCursor);
super(inputCursor, DBHelper.LoyaltyCardDbIds.ID);
setHasStableIds(true);
mSettings = new Settings(inputContext);
mContext = inputContext;
mContext = inputContext.getApplicationContext();
mListener = inputListener;
mSelectedItems = new SparseBooleanArray();
mAnimationItemsIndex = new SparseBooleanArray();
mDarkModeEnabled = Utils.isDarkModeEnabled(inputContext);
swapCursor(mCursor);
}
@Override
public void swapCursor(Cursor inputCursor) {
super.swapCursor(inputCursor);
mCursor = inputCursor;
swapCursor(inputCursor);
}
public void showDetails(boolean show) {
@@ -75,8 +68,9 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
return new LoyaltyCardListItemViewHolder(itemView, mListener);
}
public Cursor getCursor() {
return mCursor;
public LoyaltyCard getCard(int position) {
mCursor.moveToPosition(position);
return LoyaltyCard.toLoyaltyCard(mCursor);
}
public void onBindViewHolder(LoyaltyCardListItemViewHolder inputHolder, Cursor inputCursor) {
@@ -169,35 +163,19 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
private void applyIconAnimation(LoyaltyCardListItemViewHolder inputHolder, int inputPosition) {
if (mSelectedItems.get(inputPosition, false)) {
inputHolder.mCardIcon.setVisibility(View.GONE);
resetIconYAxis(inputHolder.mTickIcon);
inputHolder.mTickIcon.setVisibility(View.VISIBLE);
if (mCurrentSelectedIndex == inputPosition) {
LoyaltyCardAnimator.flipView(mContext, inputHolder.mTickIcon, inputHolder.mCardIcon, true);
resetCurrentIndex();
}
} else {
inputHolder.mTickIcon.setVisibility(View.GONE);
resetIconYAxis(inputHolder.mCardIcon);
inputHolder.mCardIcon.setVisibility(View.VISIBLE);
if ((mReverseAllAnimations && mAnimationItemsIndex.get(inputPosition, false)) || mCurrentSelectedIndex == inputPosition) {
LoyaltyCardAnimator.flipView(mContext, inputHolder.mTickIcon, inputHolder.mCardIcon, false);
resetCurrentIndex();
}
}
}
private void resetIconYAxis(View inputView) {
if (inputView.getRotationY() != 0) {
inputView.setRotationY(0);
}
}
public void resetAnimationIndex() {
mReverseAllAnimations = false;
mAnimationItemsIndex.clear();
}
public void toggleSelection(int inputPosition) {
mCurrentSelectedIndex = inputPosition;
if (mSelectedItems.get(inputPosition, false)) {

View File

@@ -679,10 +679,10 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
}
private void setCropperTheme() {
mCropperOptions.setToolbarColor(ContextCompat.getColor(this, R.color.colorPrimary));
mCropperOptions.setStatusBarColor(ContextCompat.getColor(this, R.color.colorPrimaryDark));
mCropperOptions.setToolbarColor(getThemeColor());
mCropperOptions.setStatusBarColor(getThemeColor());
mCropperOptions.setToolbarWidgetColor(Color.WHITE);
mCropperOptions.setActiveControlsWidgetColor(ContextCompat.getColor(this, R.color.colorPrimary));
mCropperOptions.setActiveControlsWidgetColor(getThemeColor());
}
@Override
@@ -1313,7 +1313,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity {
Log.e(TAG, "Deleting card: " + loyaltyCardId);
DBHelper db = new DBHelper(LoyaltyCardEditActivity.this);
db.deleteLoyaltyCard(loyaltyCardId);
db.deleteLoyaltyCard(LoyaltyCardEditActivity.this, loyaltyCardId);
ShortcutHelper.removeShortcut(LoyaltyCardEditActivity.this, loyaltyCardId);

View File

@@ -48,6 +48,7 @@ import androidx.appcompat.widget.Toolbar;
import androidx.constraintlayout.widget.Guideline;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.ColorUtils;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.core.widget.NestedScrollView;
import androidx.core.widget.TextViewCompat;
@@ -583,8 +584,11 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
// Make notification area light if dark icons are needed
if (Build.VERSION.SDK_INT >= 23) {
window.getDecorView().setSystemUiVisibility(backgroundNeedsDarkIcons ? View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR : 0);
window.setStatusBarColor(Color.TRANSPARENT);
} else {
// Darken statusbar if icons won't be visible otherwise
window.setStatusBarColor(backgroundNeedsDarkIcons ? ColorUtils.blendARGB(backgroundHeaderColor, Color.BLACK, 0.15f) : Color.TRANSPARENT);
}
window.setStatusBarColor(Color.TRANSPARENT);
// Set shadow colour of store text so even same color on same color would be readable
storeName.setShadowLayer(1, 1, 1, backgroundNeedsDarkIcons ? Color.BLACK : Color.WHITE);

View File

@@ -26,12 +26,15 @@ 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 CatimaAppCompatActivity implements LoyaltyCardCursorAdapter.CardAdapterListener, GestureDetector.OnGestureListener {
@@ -40,7 +43,7 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
private final DBHelper mDB = new DBHelper(this);
private LoyaltyCardCursorAdapter mAdapter;
private ActionMode mCurrentActionMode;
private Menu mMenu;
private SearchView mSearchView;
private GestureDetector mGestureDetector;
protected String mFilter = "";
protected Object mGroup = null;
@@ -52,6 +55,8 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
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) {
@@ -137,7 +142,7 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
for (LoyaltyCard loyaltyCard : mAdapter.getSelectedItems()) {
Log.e(TAG, "Deleting card: " + loyaltyCard.id);
db.deleteLoyaltyCard(loyaltyCard.id);
db.deleteLoyaltyCard(MainActivity.this, loyaltyCard.id);
ShortcutHelper.removeShortcut(MainActivity.this, loyaltyCard.id);
}
@@ -163,12 +168,6 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
public void onDestroyActionMode(ActionMode inputMode) {
mAdapter.clearSelections();
mCurrentActionMode = null;
mCardList.post(new Runnable() {
@Override
public void run() {
mAdapter.resetAnimationIndex();
}
});
}
};
@@ -260,6 +259,24 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
.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);
}
});
}
@Override
@@ -271,12 +288,8 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
mCurrentActionMode.finish();
}
if (mMenu != null) {
SearchView searchView = (SearchView) mMenu.findItem(R.id.action_search).getActionView();
if (!searchView.isIconified()) {
mFilter = searchView.getQuery().toString();
}
if (mSearchView != null && !mSearchView.isIconified()) {
mFilter = mSearchView.getQuery().toString();
}
// Start of active tab logic
@@ -320,53 +333,16 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
bundle.putString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP, groupsTabLayout.getTabAt(selectedTab).getText().toString());
}
intent.putExtras(bundle);
startActivityForResult(intent, Utils.BARCODE_SCAN);
mBarcodeScannerLauncher.launch(intent);
});
addButton.bringToFront();
}
@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
mFilter = "";
if (mMenu != null) {
MenuItem searchItem = mMenu.findItem(R.id.action_search);
searchItem.collapseActionView();
}
updateLoyaltyCardList();
return;
}
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, 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);
}
}
@Override
public void onBackPressed() {
if (mMenu != null) {
SearchView searchView = (SearchView) mMenu.findItem(R.id.action_search).getActionView();
if (!searchView.isIconified()) {
searchView.setIconified(true);
return;
}
if (!mSearchView.isIconified()) {
mSearchView.setIconified(true);
return;
}
super.onBackPressed();
@@ -443,22 +419,20 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
@Override
public boolean onCreateOptionsMenu(Menu inputMenu) {
this.mMenu = inputMenu;
getMenuInflater().inflate(R.menu.main_menu, inputMenu);
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
if (searchManager != null) {
SearchView searchView = (SearchView) inputMenu.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(() -> {
mSearchView.setOnCloseListener(() -> {
invalidateOptionsMenu();
return false;
});
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
return false;
@@ -542,25 +516,25 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
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) {
Intent i = new Intent(getApplicationContext(), ImportExportActivity.class);
startActivityForResult(i, Utils.MAIN_REQUEST);
startActivity(i);
return true;
}
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) {
Intent i = new Intent(getApplicationContext(), AboutActivity.class);
startActivityForResult(i, Utils.MAIN_REQUEST);
startActivity(i);
return true;
}
@@ -702,8 +676,6 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
if (mAdapter.getSelectedItemCount() > 0) {
enableActionMode(inputPosition);
} else {
Cursor selected = mAdapter.getCursor();
selected.moveToPosition(inputPosition);
// FIXME
//
// There is a really nasty edge case that can happen when someone taps a card but right
@@ -714,7 +686,7 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
// click is being processed. Sadly, I have not yet found a way to make that possible.
LoyaltyCard loyaltyCard;
try {
loyaltyCard = LoyaltyCard.toLoyaltyCard(selected);
loyaltyCard = mAdapter.getCard(inputPosition);
} catch (CursorIndexOutOfBoundsException e) {
Log.w(TAG, "Prevented crash from tap + swipe on ID " + inputPosition + ": " + e);
return;
@@ -728,7 +700,7 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
ShortcutHelper.updateShortcuts(MainActivity.this, loyaltyCard);
startActivityForResult(i, Utils.MAIN_REQUEST);
startActivity(i);
}
}
}

View File

@@ -165,7 +165,7 @@ public class ManageGroupActivity extends CatimaAppCompatActivity implements Mana
private void updateLoyaltyCardList() {
mAdapter.swapCursor(mDB.getLoyaltyCardCursor());
if (mAdapter.getCountFromCursor() == 0) {
if (mAdapter.getItemCount() == 0) {
mCardList.setVisibility(View.GONE);
mHelpText.setVisibility(View.VISIBLE);
} else {

View File

@@ -105,8 +105,4 @@ public class ManageGroupCursorAdapter extends LoyaltyCardCursorAdapter {
public HashMap<Integer, Boolean> exportInGroupState() {
return new HashMap<>(mInGroupOverlay);
}
public int getCountFromCursor() {
return super.getCursor().getCount();
}
}

View File

@@ -20,6 +20,8 @@ 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.widget.Toolbar;
@@ -39,6 +41,10 @@ public class ScanActivity extends CatimaAppCompatActivity {
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(LoyaltyCardEditActivity.BUNDLE_CARDID) : null;
@@ -60,6 +66,8 @@ public class ScanActivity extends CatimaAppCompatActivity {
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);
@@ -159,8 +167,7 @@ public class ScanActivity extends CatimaAppCompatActivity {
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;
@@ -193,12 +200,12 @@ public class ScanActivity extends CatimaAppCompatActivity {
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/*");
startActivityForResult(photoPickerIntent, Utils.BARCODE_IMPORT_FROM_IMAGE_FILE);
photoPickerLauncher.launch(photoPickerIntent);
}
}

View File

@@ -84,7 +84,7 @@ public class Utils {
tileLetterFontSize = context.getResources().getDimensionPixelSize(R.dimen.tileLetterFontSize);
}
int pixelSize = context.getResources().getDimensionPixelSize(R.dimen.cardThumbnailSize);
int pixelSize = context.getResources().getDimensionPixelSize(R.dimen.tileLetterImageSize);
if (backgroundColor == null) {
backgroundColor = LetterBitmap.getDefaultColor(context, store);

View File

@@ -13,32 +13,32 @@ import protect.card_locker.R;
import protect.card_locker.Utils;
public class Settings {
private Context context;
private SharedPreferences settings;
private final Context mContext;
private SharedPreferences mSettings;
public Settings(Context context) {
this.context = context;
this.settings = PreferenceManager.getDefaultSharedPreferences(context);
mContext = context.getApplicationContext();
mSettings = PreferenceManager.getDefaultSharedPreferences(context);
}
private String getResString(@StringRes int resId) {
return context.getString(resId);
return mContext.getString(resId);
}
private int getResInt(@IntegerRes int resId) {
return context.getResources().getInteger(resId);
return mContext.getResources().getInteger(resId);
}
private String getString(@StringRes int keyId, String defaultValue) {
return settings.getString(getResString(keyId), defaultValue);
return mSettings.getString(getResString(keyId), defaultValue);
}
private int getInt(@StringRes int keyId, @IntegerRes int defaultId) {
return settings.getInt(getResString(keyId), getResInt(defaultId));
return mSettings.getInt(getResString(keyId), getResInt(defaultId));
}
private boolean getBoolean(@StringRes int keyId, boolean defaultValue) {
return settings.getBoolean(getResString(keyId), defaultValue);
return mSettings.getBoolean(getResString(keyId), defaultValue);
}
public Locale getLocale() {

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="0"
android:propertyName="alpha"
android:valueFrom="1.0"
android:valueTo="0.0" />
<objectAnimator
android:duration="@integer/full_rotation_duration"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:propertyName="rotationY"
android:valueFrom="-180"
android:valueTo="0" />
<objectAnimator
android:duration="1"
android:propertyName="alpha"
android:startOffset="@integer/half_rotation_duration"
android:valueFrom="0.0"
android:valueTo="1.0" />
</set>

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="@integer/full_rotation_duration"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:propertyName="rotationY"
android:valueFrom="0"
android:valueTo="180" />
<objectAnimator
android:duration="1"
android:propertyName="alpha"
android:startOffset="@integer/half_rotation_duration"
android:valueFrom="1.0"
android:valueTo="0.0" />
</set>

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="0"
android:propertyName="alpha"
android:valueFrom="1.0"
android:valueTo="0.0" />
<objectAnimator
android:duration="@integer/full_rotation_duration"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:propertyName="rotationY"
android:valueFrom="180"
android:valueTo="0" />
<objectAnimator
android:duration="1"
android:propertyName="alpha"
android:startOffset="@integer/half_rotation_duration"
android:valueFrom="0.0"
android:valueTo="1.0" />
</set>

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="@integer/full_rotation_duration"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:propertyName="rotationY"
android:valueFrom="0"
android:valueTo="-180" />
<objectAnimator
android:duration="1"
android:propertyName="alpha"
android:startOffset="@integer/half_rotation_duration"
android:valueFrom="1.0"
android:valueTo="0.0" />
</set>

View File

@@ -16,7 +16,7 @@
android:id="@+id/icon_layout"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="2:1"
app:layout_constraintDimensionRatio="85.6f:53.98f"
app:cardCornerRadius="8dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/store">

View File

@@ -3,33 +3,34 @@ Branden Archer
J. Lavoie
Allan Nordhøy
Heimen Stoffels
mondstern
solokot
mondstern
Katharine Chui
Oğuz Ersen
IllusiveMan196
Petr Novák
Taco
Gediminas Murauskas
StoyanDimitrov
Joel A
StoyanDimitrov
Nyatsuki
Samantaz Fox
arno-github
Ankit Tiwari
Sergio Paredes
laralem
arshbeerSingh
huuhaa
Miha Frangež
Michael Moroni
Olivia (Zoe)
laralem
betsythefc
waffshappen
K. Herbert
Quentin PAGÈS
String E. Fighter
Yurical
/usr/local/ΕΨΗΕΛΩΝ
Adolfo Jayme-Barrientos
Alessandro Mandelli
KovalevArtem
@@ -56,7 +57,7 @@ Kasina Dheeraj
Flav
Franciszek Stefan
Izzy
krkk
Karol Kosek
bittin
Maciej Błędkowski
Mattia

View File

@@ -217,4 +217,7 @@
<string name="selectColor">Избиране на цвят</string>
<string name="group_name_is_empty">Името на списъка не може да е празно</string>
<string name="group_edit">Редактиране на списък</string>
<string name="action_show_details">Повече детайли</string>
<string name="action_hide_details">По-малко детайли</string>
<string name="noGiftCardsGroup">Няма карти. След като добавите ще можете да ги зачислите към списък от тук.</string>
</resources>

View File

@@ -218,4 +218,6 @@
<string name="noGiftCardsGroup">Sie haben noch keine Kundenkarten. Sobald Sie welche hinzugefügt haben, können Sie sie hier der Gruppe zuordnen.</string>
<string name="setIcon">Symbol einstellen</string>
<string name="selectColor">Farbe auswählen</string>
<string name="action_show_details">Details anzeigen</string>
<string name="action_hide_details">Details ausblenden</string>
</resources>

View File

@@ -218,4 +218,6 @@
<string name="group_name_is_empty">Le nom du groupe ne peut pas être vide</string>
<string name="setIcon">Définir l\'icône</string>
<string name="selectColor">Sélectionnez la couleur</string>
<string name="action_show_details">Afficher les détails</string>
<string name="action_hide_details">Masquer les détails</string>
</resources>

View File

@@ -218,4 +218,6 @@
<string name="group_updated">Gruppo aggiornato</string>
<string name="selectColor">Seleziona il colore</string>
<string name="setIcon">Imposta l\'icona</string>
<string name="action_show_details">Mostra i dettagli</string>
<string name="action_hide_details">Nascondi i dettagli</string>
</resources>

View File

@@ -196,7 +196,7 @@
<string name="rate_this_app">このアプリを評価する</string>
<string name="on_github">GitHub</string>
<string name="source_repository">ソースリポジトリ</string>
<string name="exportPassword">パスワードを設定してエクスポートしたファイルを保護する(任意)</string>
<string name="exportPassword">パスワードを設定する(不要なら空欄)</string>
<string name="exportPasswordHint">パスワードを入力してください</string>
<string name="version_history">更新履歴</string>
<string name="credits">貢献者</string>
@@ -206,4 +206,13 @@
<string name="report_error">エラーを報告する</string>
<string name="reverse">逆順</string>
<string name="and_data_usage">and data usage</string>
<string name="group_updated">グループを更新しました</string>
<string name="editGroup">グループ編集: <xliff:g>%s</xliff:g></string>
<string name="action_show_details">詳細を表示</string>
<string name="action_hide_details">詳細を非表示</string>
<string name="selectColor">色を選択</string>
<string name="setIcon">アイコン設定</string>
<string name="group_edit">グループ編集</string>
<string name="group_name_already_in_use">このグループ名は既に使用されています</string>
<string name="group_name_is_empty">グループ名を入力してください</string>
</resources>

View File

@@ -222,4 +222,6 @@
<string name="noGiftCardsGroup">Dar neturite jokių lojalumo kortelių. Kai kelias pridėsite, galėsite jas priskirti grupei čia.</string>
<string name="setIcon">Nustatyti piktogramą</string>
<string name="selectColor">Pasirinkti spalvą</string>
<string name="action_hide_details">Paslėpti informaciją</string>
<string name="action_show_details">Rodyti išsamią informaciją</string>
</resources>

View File

@@ -216,4 +216,8 @@
<string name="group_name_is_empty">Gruppenavn kan ikke være tomt</string>
<string name="editGroup">Redigerer gruppe: <xliff:g>%s</xliff:g></string>
<string name="noGiftCardsGroup">Du har ingen lojalitetskort enda. Når du først har det kan du legge dem til i en gruppe her.</string>
<string name="action_show_details">Vis detaljer</string>
<string name="action_hide_details">Skjul detaljer</string>
<string name="selectColor">Velg farge</string>
<string name="setIcon">Sett ikon</string>
</resources>

View File

@@ -218,4 +218,6 @@
<string name="group_edit">Groep bewerken</string>
<string name="setIcon">Kies een pictogram</string>
<string name="selectColor">Kies een kleur</string>
<string name="action_show_details">Details tonen</string>
<string name="action_hide_details">Details verbergen</string>
</resources>

View File

@@ -218,4 +218,6 @@
<string name="noGiftCardsGroup">Ainda não tem nenhum cartão de fidelidade. Assim que tenha adicionado alguns, pode atribuí-los ao grupo aqui.</string>
<string name="selectColor">Selecionar cor</string>
<string name="setIcon">Definir ícone</string>
<string name="action_show_details">Mostrar detalhes</string>
<string name="action_hide_details">Ocultar detalhes</string>
</resources>

View File

@@ -226,4 +226,6 @@
<string name="noGiftCardsGroup">У вас ещё нет ни одной карты. Как только появится несколько карт, вы сможете добавить их в группу здесь.</string>
<string name="setIcon">Выбор значка</string>
<string name="selectColor">Выбрать цвет</string>
<string name="action_hide_details">Скрыть детали</string>
<string name="action_show_details">Показать детали</string>
</resources>

View File

@@ -215,4 +215,9 @@
<string name="editGroup">Redigerar grupp: <xliff:g>%s</xliff:g></string>
<string name="selectColor">Välj färg</string>
<string name="setIcon">Välj ikon</string>
<string name="action_show_details">Visa detaljer</string>
<string name="action_hide_details">Dölj detaljer</string>
<string name="credits">Bidragsgivare</string>
<string name="source_repository">Källkodslager</string>
<string name="noGiftCardsGroup">Du har inga kundkort än. När du senare lägger till några kan du placera dem i den här gruppen.</string>
</resources>

View File

@@ -226,4 +226,6 @@
<string name="noGiftCardsGroup">У вас ще немає жодної картки. Коли ви додасте кілька, то зможете призначити їх до групи.</string>
<string name="selectColor">Вибір кольору</string>
<string name="setIcon">Вибір іконки</string>
<string name="action_show_details">Показати деталі</string>
<string name="action_hide_details">Сховати деталі</string>
</resources>

View File

@@ -33,7 +33,8 @@
<dimen name="activity_margin">16dp</dimen>
<!-- The default letter tile text size -->
<dimen name="tileLetterFontSize">33sp</dimen>
<dimen name="tileLetterFontSizeForShortcut">24dp</dimen>
<dimen name="tileLetterFontSize">66sp</dimen>
<dimen name="tileLetterFontSizeForShortcut">48dp</dimen>
<dimen name="tileLetterImageSize">92dp</dimen>
<dimen name="cardViewLetterFontSize">100sp</dimen>
</resources>

View File

@@ -29,25 +29,26 @@ import static org.junit.Assert.assertTrue;
@RunWith(RobolectricTestRunner.class)
@Config(sdk = 23)
public class DatabaseTest {
private DBHelper db;
private DBHelper mDb;
private Activity mActivity;
private static final Integer DEFAULT_HEADER_COLOR = Color.BLACK;
@Before
public void setUp() {
Activity activity = Robolectric.setupActivity(MainActivity.class);
db = TestHelpers.getEmptyDb(activity);
mActivity = Robolectric.setupActivity(MainActivity.class);
mDb = TestHelpers.getEmptyDb(mActivity);
}
@Test
public void addRemoveOneGiftCard() {
assertEquals(0, db.getLoyaltyCardCount());
long id = db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", null, CatimaBarcode.fromBarcode(BarcodeFormat.UPC_A), DEFAULT_HEADER_COLOR, 0, null);
assertEquals(0, mDb.getLoyaltyCardCount());
long id = mDb.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", null, CatimaBarcode.fromBarcode(BarcodeFormat.UPC_A), DEFAULT_HEADER_COLOR, 0, null);
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
assertEquals(1, mDb.getLoyaltyCardCount());
LoyaltyCard loyaltyCard = db.getLoyaltyCard(1);
LoyaltyCard loyaltyCard = mDb.getLoyaltyCard(1);
assertNotNull(loyaltyCard);
assertEquals("store", loyaltyCard.store);
assertEquals("note", loyaltyCard.note);
@@ -60,24 +61,24 @@ public class DatabaseTest {
assertEquals(DEFAULT_HEADER_COLOR, loyaltyCard.headerColor);
assertEquals(0, loyaltyCard.starStatus);
result = db.deleteLoyaltyCard(1);
result = mDb.deleteLoyaltyCard(mActivity, 1);
assertTrue(result);
assertEquals(0, db.getLoyaltyCardCount());
assertNull(db.getLoyaltyCard(1));
assertEquals(0, mDb.getLoyaltyCardCount());
assertNull(mDb.getLoyaltyCard(1));
}
@Test
public void updateGiftCard() {
long id = db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", null, CatimaBarcode.fromBarcode(BarcodeFormat.UPC_A), DEFAULT_HEADER_COLOR, 0, null);
long id = mDb.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", null, CatimaBarcode.fromBarcode(BarcodeFormat.UPC_A), DEFAULT_HEADER_COLOR, 0, null);
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
assertEquals(1, mDb.getLoyaltyCardCount());
result = db.updateLoyaltyCard(1, "store1", "note1", null, new BigDecimal("10.00"), Currency.getInstance("EUR"), "cardId1", null, CatimaBarcode.fromBarcode(BarcodeFormat.AZTEC), DEFAULT_HEADER_COLOR);
result = mDb.updateLoyaltyCard(1, "store1", "note1", null, new BigDecimal("10.00"), Currency.getInstance("EUR"), "cardId1", null, CatimaBarcode.fromBarcode(BarcodeFormat.AZTEC), DEFAULT_HEADER_COLOR);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
assertEquals(1, mDb.getLoyaltyCardCount());
LoyaltyCard loyaltyCard = db.getLoyaltyCard(1);
LoyaltyCard loyaltyCard = mDb.getLoyaltyCard(1);
assertNotNull(loyaltyCard);
assertEquals("store1", loyaltyCard.store);
assertEquals("note1", loyaltyCard.note);
@@ -93,16 +94,16 @@ public class DatabaseTest {
@Test
public void updateGiftCardOnlyStar() {
long id = db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", null, CatimaBarcode.fromBarcode(BarcodeFormat.UPC_A), DEFAULT_HEADER_COLOR, 0, null);
long id = mDb.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", null, CatimaBarcode.fromBarcode(BarcodeFormat.UPC_A), DEFAULT_HEADER_COLOR, 0, null);
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
assertEquals(1, mDb.getLoyaltyCardCount());
result = db.updateLoyaltyCardStarStatus(1, 1);
result = mDb.updateLoyaltyCardStarStatus(1, 1);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
assertEquals(1, mDb.getLoyaltyCardCount());
LoyaltyCard loyaltyCard = db.getLoyaltyCard(1);
LoyaltyCard loyaltyCard = mDb.getLoyaltyCard(1);
assertNotNull(loyaltyCard);
assertEquals("store", loyaltyCard.store);
assertEquals("note", loyaltyCard.note);
@@ -118,22 +119,22 @@ public class DatabaseTest {
@Test
public void updateMissingGiftCard() {
assertEquals(0, db.getLoyaltyCardCount());
assertEquals(0, mDb.getLoyaltyCardCount());
boolean result = db.updateLoyaltyCard(1, "store1", "note1", null, new BigDecimal("0"), null, "cardId1",
boolean result = mDb.updateLoyaltyCard(1, "store1", "note1", null, new BigDecimal("0"), null, "cardId1",
null, CatimaBarcode.fromBarcode(BarcodeFormat.UPC_A), DEFAULT_HEADER_COLOR);
assertEquals(false, result);
assertEquals(0, db.getLoyaltyCardCount());
assertEquals(0, mDb.getLoyaltyCardCount());
}
@Test
public void emptyGiftCardValues() {
long id = db.insertLoyaltyCard("", "", null, new BigDecimal("0"), null, "", null, null, null, 0, null);
long id = mDb.insertLoyaltyCard("", "", null, new BigDecimal("0"), null, "", null, null, null, 0, null);
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
assertEquals(1, mDb.getLoyaltyCardCount());
LoyaltyCard loyaltyCard = db.getLoyaltyCard(1);
LoyaltyCard loyaltyCard = mDb.getLoyaltyCard(1);
assertNotNull(loyaltyCard);
assertEquals("", loyaltyCard.store);
assertEquals("", loyaltyCard.note);
@@ -154,15 +155,15 @@ public class DatabaseTest {
// Add the gift cards in reverse order, to ensure
// that they are sorted
for (int index = CARDS_TO_ADD - 1; index >= 0; index--) {
long id = db.insertLoyaltyCard("store" + index, "note" + index, null, new BigDecimal("0"), null, "cardId" + index,
long id = mDb.insertLoyaltyCard("store" + index, "note" + index, null, new BigDecimal("0"), null, "cardId" + index,
null, CatimaBarcode.fromBarcode(BarcodeFormat.UPC_A), index, 0, null);
boolean result = (id != -1);
assertTrue(result);
}
assertEquals(CARDS_TO_ADD, db.getLoyaltyCardCount());
assertEquals(CARDS_TO_ADD, mDb.getLoyaltyCardCount());
Cursor cursor = db.getLoyaltyCardCursor();
Cursor cursor = mDb.getLoyaltyCardCursor();
assertNotNull(cursor);
assertEquals(CARDS_TO_ADD, cursor.getCount());
@@ -198,19 +199,19 @@ public class DatabaseTest {
// that they are sorted
for (int index = CARDS_TO_ADD - 1; index >= 0; index--) {
if (index == CARDS_TO_ADD - 1) {
id = db.insertLoyaltyCard("store" + index, "note" + index, null, new BigDecimal("0"), null, "cardId" + index,
id = mDb.insertLoyaltyCard("store" + index, "note" + index, null, new BigDecimal("0"), null, "cardId" + index,
null, CatimaBarcode.fromBarcode(BarcodeFormat.UPC_A), index, 1, null);
} else {
id = db.insertLoyaltyCard("store" + index, "note" + index, null, new BigDecimal("0"), null, "cardId" + index,
id = mDb.insertLoyaltyCard("store" + index, "note" + index, null, new BigDecimal("0"), null, "cardId" + index,
null, CatimaBarcode.fromBarcode(BarcodeFormat.UPC_A), index, 0, null);
}
boolean result = (id != -1);
assertTrue(result);
}
assertEquals(CARDS_TO_ADD, db.getLoyaltyCardCount());
assertEquals(CARDS_TO_ADD, mDb.getLoyaltyCardCount());
Cursor cursor = db.getLoyaltyCardCursor();
Cursor cursor = mDb.getLoyaltyCardCursor();
assertNotNull(cursor);
assertEquals(CARDS_TO_ADD, cursor.getCount());
@@ -279,130 +280,130 @@ public class DatabaseTest {
@Test
public void addRemoveOneGroup() {
assertEquals(0, db.getGroupCount());
long id = db.insertGroup("group one");
assertEquals(0, mDb.getGroupCount());
long id = mDb.insertGroup("group one");
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getGroupCount());
assertEquals(1, mDb.getGroupCount());
Group group = db.getGroup("group one");
Group group = mDb.getGroup("group one");
assertNotNull(group);
assertEquals("group one", group._id);
result = db.deleteGroup("group one");
result = mDb.deleteGroup("group one");
assertTrue(result);
assertEquals(0, db.getGroupCount());
assertNull(db.getGroup("group one"));
assertEquals(0, mDb.getGroupCount());
assertNull(mDb.getGroup("group one"));
}
@Test
public void updateGroup() {
// Create card
assertEquals(0, db.getLoyaltyCardCount());
long id = db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", null, CatimaBarcode.fromBarcode(BarcodeFormat.UPC_A), DEFAULT_HEADER_COLOR, 0, null);
assertEquals(0, mDb.getLoyaltyCardCount());
long id = mDb.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", null, CatimaBarcode.fromBarcode(BarcodeFormat.UPC_A), DEFAULT_HEADER_COLOR, 0, null);
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
assertEquals(1, mDb.getLoyaltyCardCount());
// Create group
long groupId = db.insertGroup("group one");
long groupId = mDb.insertGroup("group one");
result = (groupId != -1);
assertTrue(result);
assertEquals(1, db.getGroupCount());
assertEquals(1, mDb.getGroupCount());
// Add card to group
Group group = db.getGroup("group one");
Group group = mDb.getGroup("group one");
List<Group> groupList1 = new ArrayList<>();
groupList1.add(group);
db.setLoyaltyCardGroups(1, groupList1);
mDb.setLoyaltyCardGroups(1, groupList1);
// Ensure the card has one group and the group has one card
List<Group> cardGroups = db.getLoyaltyCardGroups((int) id);
List<Group> cardGroups = mDb.getLoyaltyCardGroups((int) id);
assertEquals(1, cardGroups.size());
assertEquals("group one", cardGroups.get(0)._id);
assertEquals(1, db.getGroupCardCount("group one"));
assertEquals(1, mDb.getGroupCardCount("group one"));
// Rename group
result = db.updateGroup("group one", "group one renamed");
result = mDb.updateGroup("group one", "group one renamed");
assertTrue(result);
assertEquals(1, db.getGroupCount());
assertEquals(1, mDb.getGroupCount());
// Group one no longer exists
group = db.getGroup("group one");
group = mDb.getGroup("group one");
assertNull(group);
// But group one renamed does
Group group2 = db.getGroup("group one renamed");
Group group2 = mDb.getGroup("group one renamed");
assertNotNull(group2);
assertEquals("group one renamed", group2._id);
// And card is in "group one renamed"
// Ensure the card has one group and the group has one card
cardGroups = db.getLoyaltyCardGroups((int) id);
cardGroups = mDb.getLoyaltyCardGroups((int) id);
assertEquals(1, cardGroups.size());
assertEquals("group one renamed", cardGroups.get(0)._id);
assertEquals(1, db.getGroupCardCount("group one renamed"));
assertEquals(1, mDb.getGroupCardCount("group one renamed"));
}
@Test
public void updateMissingGroup() {
assertEquals(0, db.getGroupCount());
assertEquals(0, mDb.getGroupCount());
boolean result = db.updateGroup("group one", "new name");
boolean result = mDb.updateGroup("group one", "new name");
assertEquals(false, result);
assertEquals(0, db.getGroupCount());
assertEquals(0, mDb.getGroupCount());
}
@Test
public void emptyGroupValues() {
long id = db.insertGroup("");
long id = mDb.insertGroup("");
boolean result = (id != -1);
assertFalse(result);
assertEquals(0, db.getLoyaltyCardCount());
assertEquals(0, mDb.getLoyaltyCardCount());
}
@Test
public void duplicateGroupName() {
assertEquals(0, db.getGroupCount());
long id = db.insertGroup("group one");
assertEquals(0, mDb.getGroupCount());
long id = mDb.insertGroup("group one");
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getGroupCount());
assertEquals(1, mDb.getGroupCount());
Group group = db.getGroup("group one");
Group group = mDb.getGroup("group one");
assertNotNull(group);
assertEquals("group one", group._id);
// Should fail on duplicate
long id2 = db.insertGroup("group one");
long id2 = mDb.insertGroup("group one");
boolean result2 = (id2 != -1);
assertFalse(result2);
assertEquals(1, db.getGroupCount());
assertEquals(1, mDb.getGroupCount());
}
@Test
public void updateGroupDuplicate() {
long id = db.insertGroup("group one");
long id = mDb.insertGroup("group one");
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getGroupCount());
assertEquals(1, mDb.getGroupCount());
long id2 = db.insertGroup("group two");
long id2 = mDb.insertGroup("group two");
boolean result2 = (id2 != -1);
assertTrue(result2);
assertEquals(2, db.getGroupCount());
assertEquals(2, mDb.getGroupCount());
// Should fail when trying to rename group two to one
boolean result3 = db.updateGroup("group two", "group one");
boolean result3 = mDb.updateGroup("group two", "group one");
assertFalse(result3);
assertEquals(2, db.getGroupCount());
assertEquals(2, mDb.getGroupCount());
// Rename failed so both should still be the same
Group group = db.getGroup("group one");
Group group = mDb.getGroup("group one");
assertNotNull(group);
assertEquals("group one", group._id);
Group group2 = db.getGroup("group two");
Group group2 = mDb.getGroup("group two");
assertNotNull(group2);
assertEquals("group two", group2._id);
}
@@ -410,52 +411,52 @@ public class DatabaseTest {
@Test
public void cardAddAndRemoveGroups() {
// Create card
assertEquals(0, db.getLoyaltyCardCount());
long id = db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", null, CatimaBarcode.fromBarcode(BarcodeFormat.UPC_A), DEFAULT_HEADER_COLOR, 0, null);
assertEquals(0, mDb.getLoyaltyCardCount());
long id = mDb.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", null, CatimaBarcode.fromBarcode(BarcodeFormat.UPC_A), DEFAULT_HEADER_COLOR, 0, null);
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
assertEquals(1, mDb.getLoyaltyCardCount());
// Create two groups to only one card
assertEquals(0, db.getGroupCount());
long gid = db.insertGroup("one");
assertEquals(0, mDb.getGroupCount());
long gid = mDb.insertGroup("one");
boolean gresult = (gid != -1);
assertTrue(gresult);
long gid2 = db.insertGroup("two");
long gid2 = mDb.insertGroup("two");
boolean gresult2 = (gid2 != -1);
assertTrue(gresult2);
assertEquals(2, db.getGroupCount());
assertEquals(2, mDb.getGroupCount());
Group group1 = db.getGroup("one");
Group group1 = mDb.getGroup("one");
// Card has no groups by default
List<Group> cardGroups = db.getLoyaltyCardGroups(1);
List<Group> cardGroups = mDb.getLoyaltyCardGroups(1);
assertEquals(0, cardGroups.size());
// Add one groups to card
List<Group> groupList1 = new ArrayList<>();
groupList1.add(group1);
db.setLoyaltyCardGroups(1, groupList1);
mDb.setLoyaltyCardGroups(1, groupList1);
List<Group> cardGroups1 = db.getLoyaltyCardGroups(1);
List<Group> cardGroups1 = mDb.getLoyaltyCardGroups(1);
assertEquals(1, cardGroups1.size());
assertEquals(cardGroups1.get(0)._id, group1._id);
assertEquals(1, db.getGroupCardCount("one"));
assertEquals(0, db.getGroupCardCount("two"));
assertEquals(1, mDb.getGroupCardCount("one"));
assertEquals(0, mDb.getGroupCardCount("two"));
// Remove groups
db.setLoyaltyCardGroups(1, new ArrayList<Group>());
List<Group> cardGroups2 = db.getLoyaltyCardGroups(1);
mDb.setLoyaltyCardGroups(1, new ArrayList<Group>());
List<Group> cardGroups2 = mDb.getLoyaltyCardGroups(1);
assertEquals(0, cardGroups2.size());
assertEquals(0, db.getGroupCardCount("one"));
assertEquals(0, db.getGroupCardCount("two"));
assertEquals(0, mDb.getGroupCardCount("one"));
assertEquals(0, mDb.getGroupCardCount("two"));
}
@Test
public void databaseUpgradeFromVersion1() {
SQLiteDatabase database = db.getWritableDatabase();
SQLiteDatabase database = mDb.getWritableDatabase();
// Setup the database as it appeared in revision 1
setupDatabaseVersion1(database);
@@ -465,10 +466,10 @@ public class DatabaseTest {
int newCardId2 = insertCardVersion1(database, "store", "cardId", "");
// Upgrade database
db.onUpgrade(database, DBHelper.ORIGINAL_DATABASE_VERSION, DBHelper.DATABASE_VERSION);
mDb.onUpgrade(database, DBHelper.ORIGINAL_DATABASE_VERSION, DBHelper.DATABASE_VERSION);
// Determine that the entries are queryable and the fields are correct
LoyaltyCard card = db.getLoyaltyCard(newCardId);
LoyaltyCard card = mDb.getLoyaltyCard(newCardId);
assertEquals("store", card.store);
assertEquals("", card.note);
assertEquals(null, card.expiry);
@@ -483,7 +484,7 @@ public class DatabaseTest {
assertEquals(100, card.zoomLevel);
// Determine that the entries are queryable and the fields are correct
LoyaltyCard card2 = db.getLoyaltyCard(newCardId2);
LoyaltyCard card2 = mDb.getLoyaltyCard(newCardId2);
assertEquals("store", card2.store);
assertEquals("", card2.note);
assertEquals(null, card2.expiry);

View File

@@ -1 +0,0 @@
Catima - Svobodná brašna karet

View File

@@ -0,0 +1,2 @@
- 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

View File

@@ -0,0 +1,3 @@
- Fix blurriness of main screen letter icons
- Fix icons sometimes disappearing after selection
- Fix status bar icons possibly being invisible on Android 5

View File

@@ -1 +1 @@
Catima — The Libre Card Wallet
Catima — Loyalty Card Wallet

View File

@@ -0,0 +1,2 @@
- Catima lagt til i hurtigtilgang til kontroller for enheter (https://developer.android.com/guide/topics/ui/device-control)
- Grupper vises nå riktig i gruppehåndtering

View File

@@ -8,7 +8,7 @@ Med dette essensielle verktøyet kan du erstatte unyttig plast med kontanter.
- Unngå spionasje med veldig få tilganger. Ingen tilgang til Internett, og ingen reklame.
- Legg til kort eller koder med navn og egne farger.
- Manuell kodeinnskriving hvis det ikke er noen strekkode å lagre, eller den ikke kan brukes.
- Importer kort og koder fra filer ,Catima, FidMe, Kundekortknippe, Stocard og Voucher Vault.
- Importer kort og koder fra filer, Catima, FidMe, Kundekortknippe, Stocard og Voucher Vault.
- Lag en sikkerhetskopi av alle kortene dine og overfør dem til en ny enhet hvis du ønsker det.
- Del kupponger, eksklusive tilbud, promokoder eller kort og koder ved bruk av ethvert program.
- Mørk drakt og tilgjengelighetsvalg for synshemmede brukere.

View File

@@ -1 +1 @@
Catima — for kundekort, billetter og kuponger 🎴
Catima — for kundekort, billetter og kuponger

View File

@@ -1 +0,0 @@
Catima gestor de cartões livre

View File

@@ -1,3 +0,0 @@
- Foi alterado o nome de ficheiro padrão de importar/exportar. (pull #84 (https://github.com/brarcher/loyalty-card-locker/pull/84))
- Corrigida a sequência na página de importação/exportação. (pull #87 (https://github.com/brarcher/loyalty-card-locker/pull/87))
- Disposição da página de visualização do cartão melhorada. O texto deve ser mais fácil de ler e selecionável com um toque longo. (pull #91 (https://github.com/brarcher/loyalty-card-locker/pull/91))

View File

@@ -1,5 +0,0 @@
- Ao editar um identificador de um cartão, pré-preencher o identificador existente.
- Limitada a largura dos códigos de barras gerados para reduzir o uso de memória e erros.
- Alterado o botão "Introduzir cartão" para "Editar cartão" se já existir um identificador.
- Alterado o esquema de cores para ser mais suave e compatível com o ícone da aplicação e disposição alterada ao visualizar um cartão.
- Acrescentado um assistente de introdução que será lançado no primeiro lançamento da aplicação.

View File

@@ -1 +0,0 @@
- Evitar uma falha de sistema ao rodar o ecrã no primeiro assistente de introdução.

View File

@@ -1,2 +0,0 @@
- Uma alteração na v0.11 reduziu o uso de memória do desenho do código de barras, mas afetou as dimensões do código de barras. Isto agora é alterado para manter as dimensões do código de barras enquanto reduz o uso de memória. (pull #126 (https://github.com/brarcher/loyalty-card-locker/pull/126))
- Atualização das traduções em alemão e francês. (pull #122, pull #124, pull #125)

View File

@@ -1,3 +0,0 @@
- Adicionada a opção de bloqueio da rotação do ecrã ao mostrar um cartão. Se bloqueado, o ecrã passará para a sua orientação "natural" e a rotação posterior do ecrã será bloqueada. (pull #128)
- Se um cartão for selecionado no ecrã principal mas não puder ser carregado, a aplicação falha graciosamente e lança uma mensagem. (pull #132)
- Corrigidos casos em que não foi possível encontrar identificadores de layout para o assistente de introdução. (pull #128)

View File

@@ -1,5 +0,0 @@
O Android 4.4 já não é suportado a partir desta versão. Se quiser usar o Catima no Android 4.4, use a versão 2.6.1.
- Suporte melhorado ao Android 12
- Ecrã "sobre" melhorado
- A pesquisa agora ignora acentos

View File

@@ -1 +0,0 @@
- Melhorada a pesquisa com espaços

View File

@@ -1 +0,0 @@
- Corrigir a regressão que quebra importação/exportação

View File

@@ -1 +0,0 @@
- Corrigir migração incorreta fazendo com que o primeiro cartão se torne invisível

View File

@@ -1,5 +0,0 @@
- Corrigir deslizar entre grupos que não trabalham num grupo vazio
- Permitir proteção por palavra-passe de exportações
- Melhorar o uso do espaço para códigos QR
- Memorizar o último nível de ampliação usado por cartão
- Corrigir uma falha de sistema ao deslizar logo após um toque

View File

@@ -1,6 +0,0 @@
A parte "Bloqueador" do nome não era intuitiva. Para ajudar a remediar isso, foi criado por betsythefc um novo ícone da aplicação que representa melhor o propósito da aplicação: armazenar cartões de fidelização que utilizam códigos de barras. Junto com este novo ícone o nome da aplicação foi alterado para "Loyalty Card Keychain".
Recursos adicionais/melhorias:
- A importação/exportação de cartões foi alterada para ser mais flexível.
- Adicionada tradução para lituano e francês.

View File

@@ -1,4 +0,0 @@
- Corrigir pontos na visualização do cartão tendo a cor errada ao mudar o tema manualmente
- Corrigir falha na visualização do cartão na mudança de rotação/tema
- Corrigir piscar da lista de cartões
- Corrigir o ícone de favorito sobreposto ao texto

View File

@@ -1,22 +0,0 @@
Não procure mais os cartões plástico na caixa de pagamento das lojas.
<b>Digitalize os códigos de barras para o seu dispositivo usando a câmara fotográfica e esqueça os cartões. </b>
Esqueça a sua carteira ou guarde-a para outras coisas mais úteis.
Com esta ferramenta essencial de transporte diário pode substituir o plástico inútil por dinheiro.
- Evite quebra de privacidade, a aplicação usa poucas permissões. Não precisa de acesso à Internet e não tem anúncios.
- Adicione cartões ou códigos com nomes e cores personalizáveis.
- Introduza manualmente códigos se não houver um código de barras no cartão físico ou se este estiver danificado.
- Importe cartões e códigos a partir de ficheiros de outras aplicações como Catima, FidMe, Loyalty Card Keychain, Stocard e Voucher Vault.
- Faça uma cópia de segurança de todos os seus cartões e transfira-os para um novo dispositivo, se quiser.
- Partilhe cupões, ofertas exclusivas, códigos promocionais ou cartões e códigos usando qualquer outra aplicação.
- Com tema escuro e opções de acessibilidade para utilizadores com deficiência visual.
- Feito para todos pela comunidade do software livre.
- Traduções localizadas feitas manualmente em mais de 20 idiomas.
- Aplicação gratuita, suportada por contribuições da comunidade.
- Use, estude, altere e partilhe o código-fonte da aplicação como quiser; <i>com toda a gente</i>.
- Não é apenas um Software Livre / Código Aberto. É uma aplicação de gestão de cartões de partilha <i>sob a mesma licença</i> segundo a licença (GPLv3+).
Simplifique a sua vida e as suas compras e nunca mais perca um recibo em papel, um cartão de oferta de pagamento na loja ou um bilhete de avião.
Leve todas as suas recompensas e bónus consigo e poupe.

View File

@@ -1 +0,0 @@
Para os seus cartões de afiliações, programas de fidelidade, cupões e bilhetes.

View File

@@ -1 +0,0 @@
Catima gestor de cartões livre

View File

@@ -0,0 +1,3 @@
- Öntanımlı içe/dışa aktarma dosya adı değiştirildi. (https://github.com/brarcher/loyalty-card-locker/pull/84)
- İçe/dışa aktarma sayfasındaki dizge düzeltildi. (https://github.com/brarcher/loyalty-card-locker/pull/87)
- Kart görünümü sayfasının düzeni iyileştirildi. Metnin okunması daha kolay olmalı ve uzun bir tıklama ile seçilebilir olmalıdır. https://github.com/brarcher/loyalty-card-locker/pull/91)

View File

@@ -0,0 +1 @@
- İlk çalıştırma giriş sihirbazında ekranı döndürürken oluşan çökmeyi önle.

View File

@@ -0,0 +1,3 @@
- Belirli türdeki bozuk CSV dosyalarını içe aktarırken oluşan çökme düzeltildi. (https://github.com/brarcher/loyalty-card-locker/pull/177)
- Yedekleri doğrudan dosya sisteminden içe aktarma düzeltildi. (https://github.com/brarcher/loyalty-card-locker/pull/180)
- Belirli içerik sağlayıcı türlerinden yedekleri içe aktarma düzeltildi. (https://github.com/brarcher/loyalty-card-locker/pull/179)

View File

@@ -0,0 +1,3 @@
- İtalyanca çeviri
- Tüm 1B barkod türleri için destek. (Başlangıçta yalnızca ürün 1B barkodları destekleniyordu)
- Başlangıçta eksik olan gerekli kamera izni eklendi.

View File

@@ -0,0 +1,2 @@
- Kart listesi düzeni iyileştirildi. (https://github.com/brarcher/loyalty-card-locker/pull/188)
- Kartı görüntüleme düzeni iyileştirildi. (https://github.com/brarcher/loyalty-card-locker/pull/190)

View File

@@ -0,0 +1,2 @@
- Catima'yı Hızlı Erişim Aygıt Denetimlerine ekle (https://developer.android.com/guide/topics/ui/device-control)
- Grup yönetimi ekranında bazı grupların doğru görünmemesini düzelt

View File

@@ -0,0 +1,3 @@
- Ana ekran harf simgelerinin bulanıklığını düzelt
- Simgelerin seçtikten sonra bazen kaybolmasını düzelt
- Android 5'te bazen görünmez olan durum çubuğu simgelerini düzelt

View File

@@ -0,0 +1,2 @@
- Покращено інтерфейс списку карт. (pull #188 (https://github.com/brarcher/loyalty-card-locker/pull/188))
- Покращено інтерфейс перегляду карти. (pull #190 (https://github.com/brarcher/loyalty-card-locker/pull/190))

View File

@@ -1 +1 @@
- Змінено відображення карт: показ заміток та імені магазину, багаторядкове ID карти та. (pull #197 (https://github.com/brarcher/loyalty-card-locker/pull/197))
- Змінено відображення карт: показ заміток та імені магазину, багаторядкове ID карти. (pull #197 (https://github.com/brarcher/loyalty-card-locker/pull/197))

View File

@@ -0,0 +1,2 @@
- Відкат min SDK з версії 17 до 15. (pull #226 (https://github.com/brarcher/loyalty-card-locker/pull/226))
- Стара бібліотека apache тепер використовується лише для тестів. (pull #225 (https://github.com/brarcher/loyalty-card-locker/pull/225))

View File

@@ -0,0 +1,5 @@
- Додано переклади
- Польська (pull #232 (https://github.com/brarcher/loyalty-card-locker/pull/232))
- Іспанська (pull #232 (https://github.com/brarcher/loyalty-card-locker/pull/232))
- Словацька(pull #232 (https://github.com/brarcher/loyalty-card-locker/pull/232))
- Оновлено переклади (pull #239 (https://github.com/brarcher/loyalty-card-locker/pull/239))

View File

@@ -0,0 +1,2 @@
- Виправлено іспанську локалізацію (pull #244 (https://github.com/brarcher/loyalty-card-locker/pull/244))
- Оновлено переклади (pull #244 (https://github.com/brarcher/loyalty-card-locker/pull/244))

View File

@@ -0,0 +1,14 @@
- Тепер офіційно підтримується наступний список з 1D та 2D штрих-кодів:
- AZTEC
- CODABAR
- CODE_39
- CODE_128
- DATA_MATRIX
- EAN_8
- EAN_13
- ITF
- PDF_417
- QR_CODE
- UPC_A
- Тепер згенеровані штрих-коди більші, тому легше сприймаються сканером

View File

@@ -0,0 +1,4 @@
- Додано налаштування яскравості екрану при перегляді штрих-коду (pull #259 (https://github.com/brarcher/loyalty-card-locker/pull/259))
- Додано грецьку локалізацію (pull #252 (https://github.com/brarcher/loyalty-card-locker/pull/252))
- Додано словенську локалізацію (pull #260 (https://github.com/brarcher/loyalty-card-locker/pull/260))
- Оновлено переклади (pull #260 (https://github.com/brarcher/loyalty-card-locker/pull/260), pull #254 (https://github.com/brarcher/loyalty-card-locker/pull/254))

View File

@@ -0,0 +1,2 @@
- Сортування списку карток ігнорує регістр (pull #266 (https://github.com/brarcher/loyalty-card-locker/pull/266))
- Додано налаштування для блокування орієнтації усіх карток (pull #269 (https://github.com/brarcher/loyalty-card-locker/pull/269)

View File

@@ -0,0 +1 @@
- Виправлено створення нової карти за допомогою вводу штрих-коду вручну (issue #272 (https://github.com/brarcher/loyalty-card-locker/issues/272))

View File

@@ -0,0 +1 @@
- Оновлено та додано переклади

View File

@@ -0,0 +1 @@
- Оновлено російську локалізацію