Compare commits

...

74 Commits
v0.15 ... v0.20

Author SHA1 Message Date
Branden Archer
70c6ee17cd Merge pull request #198 from brarcher/pre-v0.20
Update for v0.20
2018-02-10 22:02:04 -05:00
Branden Archer
c233f82dc1 Update for v0.20 2018-02-10 13:50:30 -05:00
Branden Archer
be0c2507f2 Merge pull request #197 from brarcher/layout-changes
Layout changes for new card view layout
2018-02-10 13:49:22 -05:00
Branden Archer
abfcf9d6cd Disable MissingPrefix lint
The autosizing TextView, which comes from the support-v4 library,
adds several new attributes to TextViews. These attributes do not
seem to be in the app namespace (as described in the documentation),
as Android Studio and lint complains. However, funtionally the new
attributes work as expected.
2018-02-10 13:43:28 -05:00
Branden Archer
4d565b3b2f Update screenshots and wizard images 2018-02-10 13:37:17 -05:00
Branden Archer
27511a4ccd Make card id and note text selectable 2018-02-10 13:37:17 -05:00
Branden Archer
12440346fa Replace letter icon with store name 2018-02-10 13:37:17 -05:00
Branden Archer
870e4d0c4a Add note text to card view layout 2018-02-10 13:37:17 -05:00
Branden Archer
6e526de087 Auto-size card id text on card view layout 2018-02-10 13:37:16 -05:00
Branden Archer
891636b51f Use ConstraintLayout for card view layout
This way the size of the barcode can be 1/2 the screen.
2018-02-10 13:37:16 -05:00
Branden Archer
017d97d08c Merge pull request #196 from brarcher/feature-graphic
add feature graphic to metadata
2018-02-09 23:30:09 -05:00
Branden Archer
63165ca1a5 add feature graphic to metadata 2018-02-09 23:21:46 -05:00
Branden Archer
48f40a51d1 Merge pull request #192 from brarcher/brarcher-patch-1
Update CHANGELOG
2018-02-01 08:13:51 -05:00
Branden Archer
3940ba5756 Update CHANGELOG 2018-02-01 08:05:10 -05:00
Branden Archer
b8a36e3c45 Merge pull request #191 from brarcher/pre-v0.19
Update for v0.19
2018-02-01 08:02:20 -05:00
Branden Archer
60deaae8d5 Update for v0.19 2018-02-01 07:54:48 -05:00
Branden Archer
a71da8d6dc Merge pull request #190 from brarcher/card-layout
Improve card view layout
2018-01-31 23:39:37 -05:00
Branden Archer
dbbbd128dc Update intro and metadata graphics 2018-01-31 23:19:19 -05:00
Branden Archer
3275279501 Remove unnecessary casts from findViewById 2018-01-31 22:49:31 -05:00
Branden Archer
2f6516ffb9 Display note on separate line, omit card id on card list
The card id was of dubious value, because the main screen
is really for selecting the card. The note may have more value,
as it may specify info about the card.
2018-01-31 22:49:31 -05:00
Branden Archer
41c8b78275 Move store name text size to dimens.xml 2018-01-31 22:49:31 -05:00
Branden Archer
6ed96393d5 Change layout of single card view
This changes the layout when viewing a single card. The new layout
will have a colored area at the top which displays the first letter
of the company, and after that the barcode. The note will be displayed
on the card list screen and is presently omitted here.

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

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

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

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

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

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

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

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

That sounds more direct that this app's wording. Updating to match.
2017-11-27 14:50:18 -05:00
Branden Archer
22afeddcbc Add activity for handling shortcut add requests 2017-11-26 20:50:15 -05:00
85 changed files with 1402 additions and 826 deletions

View File

@@ -4,8 +4,8 @@ sudo: true
install:
- echo y | android update sdk -u -a -t tools
- echo y | android update sdk -u -a -t platform-tools
- echo y | android update sdk -u -a -t build-tools-25.0.2
- echo y | android update sdk -u -a -t android-25
- echo y | android update sdk -u -a -t build-tools-26.0.2
- yes | sdkmanager "platforms;android-27"
- echo y | android update sdk -u -a -t extra-google-m2repository
- echo y | android update sdk -u -a -t extra-android-m2repository

View File

@@ -1,3 +1,33 @@
## v0.19 (2018-02-01)
Changes:
- Improved layout for card list. (https://github.com/brarcher/loyalty-card-locker/pull/188)
- Improved layout when viewing a card. (https://github.com/brarcher/loyalty-card-locker/pull/190)
## v0.18.1 (2018-01-24)
Changes:
- Workaround crash during install on some Android versions (likely Android 5 and below). (https://github.com/brarcher/loyalty-card-locker/pull/184)
## v0.18 (2018-01-19)
Changes:
- Fix crash when importing certain types of corrupted CSV files. (https://github.com/brarcher/loyalty-card-locker/pull/177)
- Fix importing backups directly from the file system. (https://github.com/brarcher/loyalty-card-locker/pull/180)
- Fix importing backups from certain types of content providers. (https://github.com/brarcher/loyalty-card-locker/pull/179)
## v0.17 (2018-01-11)
Changes:
- Fix issue on Android SDK 24+ where using the file chooser import option would cause a crash. (https://github.com/brarcher/loyalty-card-locker/pull/170)
- New icon and color scheme. (https://github.com/brarcher/loyalty-card-locker/pull/171)
## v0.16 (2017-11-29)
Changes:
- Add support for adding loyalty card shortcuts from the launcher/homescreen. (https://github.com/brarcher/loyalty-card-locker/pull/161)
- Remove support for adding loyalty card shortcuts from the app itself. This removes the need for the shortcut permission. (https://github.com/brarcher/loyalty-card-locker/pull/163)
## v0.15 (2017-11-25)
Changes:

91
CONTRIBUTING.md Normal file
View File

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

View File

@@ -25,13 +25,13 @@ proposed changes.
# Screenshots
[<img src="https://user-images.githubusercontent.com/5264535/27416124-79b09162-56d9-11e7-967b-8923177dc228.png" width=250>](https://user-images.githubusercontent.com/5264535/27416124-79b09162-56d9-11e7-967b-8923177dc228.png)
[<img src="https://user-images.githubusercontent.com/5264535/27416127-7baea332-56d9-11e7-8a10-5be90bb02225.png" width=250>](https://user-images.githubusercontent.com/5264535/27416127-7baea332-56d9-11e7-8a10-5be90bb02225.png)
[<img src="https://user-images.githubusercontent.com/5264535/27416128-7d50f7b2-56d9-11e7-9833-1dd962f9cf66.png" width=250>](https://user-images.githubusercontent.com/5264535/27416128-7d50f7b2-56d9-11e7-9833-1dd962f9cf66.png)
[<img src="https://github.com/brarcher/loyalty-card-locker/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-01.png" width=250>](https://github.com/brarcher/loyalty-card-locker/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-01.png)
[<img src="https://github.com/brarcher/loyalty-card-locker/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-03.png" width=250>](https://github.com/brarcher/loyalty-card-locker/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-03.png)
[<img src="https://github.com/brarcher/loyalty-card-locker/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-02.png" width=250>](https://github.com/brarcher/loyalty-card-locker/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-02.png)
[<img src="https://user-images.githubusercontent.com/5264535/27416132-7ea6272c-56d9-11e7-9a52-d73424bf902c.png" width=250>](https://user-images.githubusercontent.com/5264535/27416132-7ea6272c-56d9-11e7-9a52-d73424bf902c.png)
[<img src="https://user-images.githubusercontent.com/5264535/27416137-800aee90-56d9-11e7-9cc9-2a7dc63bb4fb.png" width=250>](https://user-images.githubusercontent.com/5264535/27416137-800aee90-56d9-11e7-9cc9-2a7dc63bb4fb.png)
[<img src="https://user-images.githubusercontent.com/5264535/27416140-82d8211a-56d9-11e7-8031-c71d3077bdc6.png" width=250>](https://user-images.githubusercontent.com/5264535/27416140-82d8211a-56d9-11e7-8031-c71d3077bdc6.png)
[<img src="https://github.com/brarcher/loyalty-card-locker/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-04.png" width=250>](https://github.com/brarcher/loyalty-card-locker/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-04.png)
[<img src="https://github.com/brarcher/loyalty-card-locker/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-05.png" width=250>](https://github.com/brarcher/loyalty-card-locker/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-05.png)
[<img src="https://github.com/brarcher/loyalty-card-locker/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-06.png" width=250>](https://github.com/brarcher/loyalty-card-locker/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-06.png)
# Building

View File

@@ -7,15 +7,14 @@ findbugs {
}
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
compileSdkVersion 27
defaultConfig {
applicationId "protect.card_locker"
minSdkVersion 17
targetSdkVersion 25
versionCode 16
versionName "0.15"
targetSdkVersion 27
versionCode 22
versionName "0.20"
}
buildTypes {
release {
@@ -28,6 +27,15 @@ android {
disable "ButtonStyle"
disable "AlwaysShowAction"
disable "MissingTranslation"
disable "MissingPrefix"
}
// Starting with Android Studio 3 Robolectric is unable to find resources.
// The following allows it to find the resources.
testOptions {
unitTests {
includeAndroidResources = true
}
}
// This is for Robolectric support for SDK 23
@@ -36,18 +44,20 @@ android {
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support:design:25.3.1'
compile 'com.android.support:appcompat-v7:27.0.2'
compile 'com.android.support:design:27.0.2'
compile 'com.android.support:support-v4:27.0.2'
compile 'com.journeyapps:zxing-android-embedded:3.5.0@aar'
compile 'com.google.zxing:core:3.3.0'
compile 'org.apache.commons:commons-csv:1.2'
compile 'org.apache.commons:commons-csv:1.5'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile group: 'com.google.guava', name: 'guava', version: '20.0'
compile 'com.github.apl-devs:appintro:v4.2.0'
testCompile 'junit:junit:4.12'
testCompile "org.robolectric:robolectric:3.3.2"
}
task findbugs(type: FindBugs, dependsOn: assembleDebug) {
task findbugs(type: FindBugs, dependsOn: 'assembleDebug') {
description 'Run findbugs'
group 'verification'

View File

@@ -8,8 +8,6 @@
android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission
android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<uses-feature
android:name="android.hardware.camera"
@@ -37,6 +35,13 @@
<activity
android:name=".LoyaltyCardViewActivity"
android:theme="@style/AppTheme.NoActionBar"
android:label=""
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateHidden"
android:exported="true"/>
<activity
android:name=".LoyaltyCardEditActivity"
android:theme="@style/AppTheme.NoActionBar"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateHidden"
android:exported="true"/>
@@ -56,6 +61,16 @@
android:label=""
android:configChanges="orientation|screenSize"
android:theme="@style/AppTheme.NoActionBar"/>
<activity
android:name=".CardShortcutConfigure"
android:label="@string/cardShortcut"
android:configChanges="orientation|screenSize"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<provider
android:name="android.support.v4.content.FileProvider"
android:grantUriPermissions="true"

View File

@@ -67,7 +67,7 @@ public class BarcodeSelectorActivity extends AppCompatActivity
super.onCreate(savedInstanceState);
setContentView(R.layout.barcode_selector_activity);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
@@ -89,7 +89,7 @@ public class BarcodeSelectorActivity extends AppCompatActivity
.put(BarcodeFormat.UPC_A.name(), R.id.upcaBarcode)
.build();
EditText cardId = (EditText) findViewById(R.id.cardId);
EditText cardId = findViewById(R.id.cardId);
cardId.addTextChangedListener(new TextWatcher()
{
@Override
@@ -113,7 +113,7 @@ public class BarcodeSelectorActivity extends AppCompatActivity
// Update barcodes
for(String key : barcodeViewMap.keySet())
{
ImageView image = (ImageView)findViewById(barcodeViewMap.get(key));
ImageView image = findViewById(barcodeViewMap.get(key));
createBarcodeOption(image, key, s.toString());
}
}

View File

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

View File

@@ -40,7 +40,7 @@ public class CsvDatabaseImporter implements DatabaseImporter
parser.close();
database.setTransactionSuccessful();
}
catch(IllegalArgumentException e)
catch(IllegalArgumentException|IllegalStateException e)
{
throw new FormatException("Issue parsing CSV data", e);
}

View File

@@ -49,7 +49,7 @@ public class ImportExportActivity extends AppCompatActivity
{
super.onCreate(savedInstanceState);
setContentView(R.layout.import_export_activity);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
@@ -72,7 +72,7 @@ public class ImportExportActivity extends AppCompatActivity
}
Button exportButton = (Button)findViewById(R.id.exportButton);
Button exportButton = findViewById(R.id.exportButton);
exportButton.setOnClickListener(new View.OnClickListener()
{
@Override
@@ -85,9 +85,8 @@ public class ImportExportActivity extends AppCompatActivity
// Check that there is an activity that can bring up a file chooser
final Intent intentPickAction = new Intent(Intent.ACTION_PICK);
intentPickAction.setData(Uri.parse("file://"));
Button importFilesystem = (Button) findViewById(R.id.importOptionFilesystemButton);
Button importFilesystem = findViewById(R.id.importOptionFilesystemButton);
importFilesystem.setOnClickListener(new View.OnClickListener()
{
@Override
@@ -111,7 +110,7 @@ public class ImportExportActivity extends AppCompatActivity
intentGetContentAction.addCategory(Intent.CATEGORY_OPENABLE);
intentGetContentAction.setType("*/*");
Button importApplication = (Button) findViewById(R.id.importOptionApplicationButton);
Button importApplication = findViewById(R.id.importOptionApplicationButton);
importApplication.setOnClickListener(new View.OnClickListener()
{
@Override
@@ -132,7 +131,7 @@ public class ImportExportActivity extends AppCompatActivity
// This option, to import from the fixed location, should always be present
Button importButton = (Button)findViewById(R.id.importOptionFixedButton);
Button importButton = findViewById(R.id.importOptionFixedButton);
importButton.setOnClickListener(new View.OnClickListener()
{
@Override
@@ -236,33 +235,6 @@ public class ImportExportActivity extends AppCompatActivity
return super.onOptionsItemSelected(item);
}
private String fileNameFromUri(Uri uri)
{
if("file".equals(uri.getScheme()))
{
return uri.getPath();
}
Cursor returnCursor =
getContentResolver().query(uri, null, null, null, null);
if(returnCursor == null)
{
return null;
}
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
if(returnCursor.moveToFirst() == false)
{
returnCursor.close();
return null;
}
String name = returnCursor.getString(nameIndex);
returnCursor.close();
return name;
}
private void onImportComplete(boolean success, Uri path)
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
@@ -277,15 +249,10 @@ public class ImportExportActivity extends AppCompatActivity
}
int messageId = success ? R.string.importedFrom : R.string.importFailed;
final String template = getResources().getString(messageId);
// Get the filename of the file being imported
String filename = fileNameFromUri(path);
if(filename == null)
{
filename = "(unknown)";
}
String filename = path.toString();
final String message = String.format(template, filename);
builder.setMessage(message);
@@ -413,13 +380,24 @@ public class ImportExportActivity extends AppCompatActivity
try
{
InputStream reader = getContentResolver().openInputStream(uri);
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);
}
catch (FileNotFoundException e)
catch(FileNotFoundException e)
{
Log.e(TAG, "Failed to import file: " + uri.toString(), e);
onImportComplete(false, uri);
}
}
}

View File

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

View File

@@ -6,6 +6,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.ImageView;
import android.widget.TextView;
class LoyaltyCardCursorAdapter extends CursorAdapter
@@ -29,25 +30,29 @@ class LoyaltyCardCursorAdapter extends CursorAdapter
public void bindView(View view, Context context, Cursor cursor)
{
// Find fields to populate in inflated template
ImageView thumbnail = view.findViewById(R.id.thumbnail);
TextView storeField = (TextView) view.findViewById(R.id.store);
TextView cardIdField = (TextView) view.findViewById(R.id.cardId);
TextView noteField = (TextView) view.findViewById(R.id.note);
// Extract properties from cursor
LoyaltyCard loyaltyCard = LoyaltyCard.toLoyaltyCard(cursor);
// Populate fields with extracted properties
String storeAndNote = loyaltyCard.store;
storeField.setText(loyaltyCard.store);
if(loyaltyCard.note.isEmpty() == false)
{
String storeNameAndNoteFormat = view.getResources().getString(R.string.storeNameAndNoteFormat);
storeAndNote = String.format(storeNameAndNoteFormat, loyaltyCard.store, loyaltyCard.note);
noteField.setVisibility(View.VISIBLE);
noteField.setText(loyaltyCard.note);
}
else
{
noteField.setVisibility(View.GONE);
}
storeField.setText(storeAndNote);
String cardIdFormat = view.getResources().getString(R.string.cardIdFormat);
String cardIdLabel = view.getResources().getString(R.string.cardId);
String cardIdText = String.format(cardIdFormat, cardIdLabel, loyaltyCard.cardId);
cardIdField.setText(cardIdText);
int tileLetterFontSize = context.getResources().getDimensionPixelSize(R.dimen.tileLetterFontSize);
int pixelSize = context.getResources().getDimensionPixelSize(R.dimen.cardThumbnailSize);
LetterBitmap letterBitmap = new LetterBitmap(context, loyaltyCard.store, loyaltyCard.store, tileLetterFontSize, pixelSize, pixelSize);
thumbnail.setImageBitmap(letterBitmap.getLetterTile());
}
}

View File

@@ -0,0 +1,401 @@
package protect.card_locker;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
public class LoyaltyCardEditActivity extends AppCompatActivity
{
private static final String TAG = "CardLocker";
private static final int SELECT_BARCODE_REQUEST = 1;
EditText storeFieldEdit;
TextView storeFieldView;
EditText noteFieldEdit;
TextView noteFieldView;
TextView cardIdFieldView;
View cardIdDivider;
View cardIdTableRow;
TextView barcodeTypeField;
ImageView barcodeImage;
View barcodeImageLayout;
View barcodeCaptureLayout;
Button captureButton;
Button enterButton;
int loyaltyCardId;
boolean updateLoyaltyCard;
DBHelper db;
private void extractIntentFields(Intent intent)
{
final Bundle b = intent.getExtras();
loyaltyCardId = b != null ? b.getInt("id") : 0;
updateLoyaltyCard = b != null && b.getBoolean("update", false);
Log.d(TAG, "View activity: id=" + loyaltyCardId
+ ", updateLoyaltyCard=" + Boolean.toString(updateLoyaltyCard));
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.loyalty_card_edit_activity);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
{
actionBar.setDisplayHomeAsUpEnabled(true);
}
extractIntentFields(getIntent());
db = new DBHelper(this);
storeFieldEdit = findViewById(R.id.storeNameEdit);
storeFieldView = findViewById(R.id.storeNameView);
noteFieldEdit = findViewById(R.id.noteEdit);
noteFieldView = findViewById(R.id.noteView);
cardIdFieldView = findViewById(R.id.cardIdView);
cardIdDivider = findViewById(R.id.cardIdDivider);
cardIdTableRow = findViewById(R.id.cardIdTableRow);
barcodeTypeField = findViewById(R.id.barcodeType);
barcodeImage = findViewById(R.id.barcode);
barcodeImageLayout = findViewById(R.id.barcodeLayout);
barcodeCaptureLayout = findViewById(R.id.barcodeCaptureLayout);
captureButton = findViewById(R.id.captureButton);
enterButton = findViewById(R.id.enterButton);
}
@Override
public void onNewIntent(Intent intent)
{
Log.i(TAG, "Received new intent");
extractIntentFields(intent);
// Reset these fields, so they are re-populated in onResume().
storeFieldEdit.setText("");
noteFieldEdit.setText("");
cardIdFieldView.setText("");
barcodeTypeField.setText("");
}
@Override
public void onResume()
{
super.onResume();
Log.i(TAG, "To view card: " + loyaltyCardId);
if(updateLoyaltyCard)
{
final LoyaltyCard loyaltyCard = db.getLoyaltyCard(loyaltyCardId);
if(loyaltyCard == null)
{
Log.w(TAG, "Could not lookup loyalty card " + loyaltyCardId);
Toast.makeText(this, R.string.noCardExistsError, Toast.LENGTH_LONG).show();
finish();
return;
}
if(storeFieldEdit.getText().length() == 0)
{
storeFieldEdit.setText(loyaltyCard.store);
storeFieldView.setText(loyaltyCard.store);
}
if(noteFieldEdit.getText().length() == 0)
{
noteFieldEdit.setText(loyaltyCard.note);
noteFieldView.setText(loyaltyCard.note);
}
if(cardIdFieldView.getText().length() == 0)
{
cardIdFieldView.setText(loyaltyCard.cardId);
}
if(barcodeTypeField.getText().length() == 0)
{
barcodeTypeField.setText(loyaltyCard.barcodeType);
}
if(updateLoyaltyCard)
{
setTitle(R.string.editCardTitle);
storeFieldView.setVisibility(View.GONE);
noteFieldView.setVisibility(View.GONE);
}
else
{
barcodeCaptureLayout.setVisibility(View.GONE);
captureButton.setVisibility(View.GONE);
setTitle(R.string.viewCardTitle);
storeFieldEdit.setVisibility(View.GONE);
noteFieldEdit.setVisibility(View.GONE);
}
}
else
{
setTitle(R.string.addCardTitle);
storeFieldView.setVisibility(View.GONE);
noteFieldView.setVisibility(View.GONE);
}
if(cardIdFieldView.getText().length() > 0 && barcodeTypeField.getText().length() > 0)
{
String formatString = barcodeTypeField.getText().toString();
final BarcodeFormat format = BarcodeFormat.valueOf(formatString);
final String cardIdString = cardIdFieldView.getText().toString();
if(barcodeImage.getHeight() == 0)
{
Log.d(TAG, "ImageView size is not known known at start, waiting for load");
// The size of the ImageView is not yet available as it has not
// yet been drawn. Wait for it to be drawn so the size is available.
barcodeImage.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener()
{
@Override
public void onGlobalLayout()
{
if (Build.VERSION.SDK_INT < 16)
{
barcodeImage.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
else
{
barcodeImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
Log.d(TAG, "ImageView size now known");
new BarcodeImageWriterTask(barcodeImage, cardIdString, format).execute();
}
});
}
else
{
Log.d(TAG, "ImageView size known known, creating barcode");
new BarcodeImageWriterTask(barcodeImage, cardIdString, format).execute();
}
barcodeImageLayout.setVisibility(View.VISIBLE);
}
View.OnClickListener captureCallback = new View.OnClickListener()
{
@Override
public void onClick(View v)
{
IntentIntegrator integrator = new IntentIntegrator(LoyaltyCardEditActivity.this);
integrator.setDesiredBarcodeFormats(BarcodeSelectorActivity.SUPPORTED_BARCODE_TYPES);
String prompt = getResources().getString(R.string.scanCardBarcode);
integrator.setPrompt(prompt);
integrator.initiateScan();
}
};
captureButton.setOnClickListener(captureCallback);
enterButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
Intent i = new Intent(getApplicationContext(), BarcodeSelectorActivity.class);
String cardId = cardIdFieldView.getText().toString();
if(cardId.length() > 0)
{
final Bundle b = new Bundle();
b.putString("initialCardId", cardId);
i.putExtras(b);
}
startActivityForResult(i, SELECT_BARCODE_REQUEST);
}
});
if(cardIdFieldView.getText().length() > 0)
{
cardIdDivider.setVisibility(View.VISIBLE);
cardIdTableRow.setVisibility(View.VISIBLE);
enterButton.setText(R.string.editCard);
}
else
{
cardIdDivider.setVisibility(View.GONE);
cardIdTableRow.setVisibility(View.GONE);
enterButton.setText(R.string.enterCard);
}
}
private void doSave()
{
String store = storeFieldEdit.getText().toString();
String note = noteFieldEdit.getText().toString();
String cardId = cardIdFieldView.getText().toString();
String barcodeType = barcodeTypeField.getText().toString();
if(store.isEmpty())
{
Snackbar.make(storeFieldEdit, R.string.noStoreError, Snackbar.LENGTH_LONG).show();
return;
}
if(cardId.isEmpty() || barcodeType.isEmpty())
{
Snackbar.make(cardIdFieldView, R.string.noCardIdError, Snackbar.LENGTH_LONG).show();
return;
}
if(updateLoyaltyCard)
{
db.updateLoyaltyCard(loyaltyCardId, store, note, cardId, barcodeType);
Log.i(TAG, "Updated " + loyaltyCardId + " to " + cardId);
}
else
{
loyaltyCardId = (int)db.insertLoyaltyCard(store, note, cardId, barcodeType);
}
finish();
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
if(updateLoyaltyCard)
{
getMenuInflater().inflate(R.menu.card_update_menu, menu);
}
else
{
getMenuInflater().inflate(R.menu.card_add_menu, menu);
}
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
int id = item.getItemId();
switch(id)
{
case android.R.id.home:
finish();
break;
case R.id.action_delete:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.deleteTitle);
builder.setMessage(R.string.deleteConfirmation);
builder.setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
Log.e(TAG, "Deleting card: " + loyaltyCardId);
DBHelper db = new DBHelper(LoyaltyCardEditActivity.this);
db.deleteLoyaltyCard(loyaltyCardId);
ShortcutHelper.removeShortcut(LoyaltyCardEditActivity.this, loyaltyCardId);
finish();
dialog.dismiss();
}
});
builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
}
});
AlertDialog dialog = builder.create();
dialog.show();
return true;
case R.id.action_save:
doSave();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent)
{
String contents = null;
String format = null;
IntentResult result =
IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
if (result != null)
{
Log.i(TAG, "Received barcode information from capture");
contents = result.getContents();
format = result.getFormatName();
}
if(requestCode == SELECT_BARCODE_REQUEST && resultCode == Activity.RESULT_OK)
{
Log.i(TAG, "Received barcode information from capture");
contents = intent.getStringExtra(BarcodeSelectorActivity.BARCODE_CONTENTS);
format = intent.getStringExtra(BarcodeSelectorActivity.BARCODE_FORMAT);
}
if(contents != null && contents.isEmpty() == false &&
format != null && format.isEmpty() == false)
{
Log.i(TAG, "Read barcode id: " + contents);
Log.i(TAG, "Read format: " + format);
TextView cardIdView = findViewById(R.id.cardIdView);
cardIdView.setText(contents);
final TextView barcodeTypeField = findViewById(R.id.barcodeType);
barcodeTypeField.setText(format);
onResume();
}
}
}

View File

@@ -2,11 +2,9 @@ package protect.card_locker;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
@@ -22,59 +20,33 @@ import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
public class LoyaltyCardViewActivity extends AppCompatActivity
{
private static final String TAG = "CardLocker";
private static final int SELECT_BARCODE_REQUEST = 1;
EditText storeFieldEdit;
TextView storeFieldView;
EditText noteFieldEdit;
TextView noteFieldView;
CheckBox shortcutCheckbox;
View shortcutBorder;
View shortcutTablerow;
TextView cardIdFieldView;
View cardIdDivider;
View cardIdTableRow;
TextView barcodeTypeField;
TextView noteView;
View noteViewDivider;
TextView storeName;
ImageView barcodeImage;
View barcodeImageLayout;
View barcodeCaptureLayout;
Button captureButton;
Button enterButton;
View collapsingToolbarLayout;
int loyaltyCardId;
boolean updateLoyaltyCard;
boolean viewLoyaltyCard;
boolean rotationEnabled;
DBHelper db;
private void extractIntentFields(Intent intent)
{
final Bundle b = intent.getExtras();
loyaltyCardId = b != null ? b.getInt("id") : 0;
updateLoyaltyCard = b != null && b.getBoolean("update", false);
viewLoyaltyCard = b != null && b.getBoolean("view", false);
Log.d(TAG, "View activity: id=" + loyaltyCardId
+ ", updateLoyaltyCard=" + Boolean.toString(updateLoyaltyCard)
+ ", viewLoyaltyCard=" + Boolean.toString(viewLoyaltyCard));
Log.d(TAG, "View activity: id=" + loyaltyCardId);
}
@Override
@@ -82,8 +54,11 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
{
super.onCreate(savedInstanceState);
setContentView(R.layout.loyalty_card_view_activity);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
extractIntentFields(getIntent());
setContentView(R.layout.loyalty_card_view_layout);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
@@ -91,27 +66,14 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
actionBar.setDisplayHomeAsUpEnabled(true);
}
extractIntentFields(getIntent());
db = new DBHelper(this);
storeFieldEdit = (EditText) findViewById(R.id.storeNameEdit);
storeFieldView = (TextView) findViewById(R.id.storeNameView);
noteFieldEdit = (EditText) findViewById(R.id.noteEdit);
noteFieldView = (TextView) findViewById(R.id.noteView);
shortcutCheckbox = (CheckBox) findViewById(R.id.shortcutCheckbox);
shortcutBorder = findViewById(R.id.shortcutBorder);
shortcutTablerow = findViewById(R.id.shortcutTablerow);
cardIdFieldView = (TextView) findViewById(R.id.cardIdView);
cardIdDivider = findViewById(R.id.cardIdDivider);
cardIdTableRow = findViewById(R.id.cardIdTableRow);
barcodeTypeField = (TextView) findViewById(R.id.barcodeType);
barcodeImage = (ImageView) findViewById(R.id.barcode);
barcodeImageLayout = findViewById(R.id.barcodeLayout);
barcodeCaptureLayout = findViewById(R.id.barcodeCaptureLayout);
captureButton = (Button) findViewById(R.id.captureButton);
enterButton = (Button) findViewById(R.id.enterButton);
cardIdFieldView = findViewById(R.id.cardIdView);
noteView = findViewById(R.id.noteView);
noteViewDivider = findViewById(R.id.noteViewDivider);
storeName = findViewById(R.id.storeName);
barcodeImage = findViewById(R.id.barcode);
collapsingToolbarLayout = findViewById(R.id.collapsingToolbarLayout);
}
@Override
@@ -119,12 +81,6 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
{
Log.i(TAG, "Received new intent");
extractIntentFields(intent);
// Reset these fields, so they are re-populated in onResume().
storeFieldEdit.setText("");
noteFieldEdit.setText("");
cardIdFieldView.setText("");
barcodeTypeField.setText("");
}
@Override
@@ -134,93 +90,55 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
Log.i(TAG, "To view card: " + loyaltyCardId);
if(viewLoyaltyCard)
// The brightness value is on a scale from [0, ..., 1], where
// '1' is the brightest. We attempt to maximize the brightness
// to help barcode readers scan the barcode.
Window window = getWindow();
if(window != null)
{
// The brightness value is on a scale from [0, ..., 1], where
// '1' is the brightest. We attempt to maximize the brightness
// to help barcode readers scan the barcode.
Window window = getWindow();
if(window != null)
{
WindowManager.LayoutParams attributes = window.getAttributes();
attributes.screenBrightness = 1F;
window.setAttributes(attributes);
}
WindowManager.LayoutParams attributes = window.getAttributes();
attributes.screenBrightness = 1F;
window.setAttributes(attributes);
}
if(updateLoyaltyCard || viewLoyaltyCard)
final LoyaltyCard loyaltyCard = db.getLoyaltyCard(loyaltyCardId);
if(loyaltyCard == null)
{
final LoyaltyCard loyaltyCard = db.getLoyaltyCard(loyaltyCardId);
if(loyaltyCard == null)
{
Log.w(TAG, "Could not lookup loyalty card " + loyaltyCardId);
Toast.makeText(this, R.string.noCardExistsError, Toast.LENGTH_LONG).show();
finish();
return;
}
Log.w(TAG, "Could not lookup loyalty card " + loyaltyCardId);
Toast.makeText(this, R.string.noCardExistsError, Toast.LENGTH_LONG).show();
finish();
return;
}
if(storeFieldEdit.getText().length() == 0)
{
storeFieldEdit.setText(loyaltyCard.store);
storeFieldView.setText(loyaltyCard.store);
}
String formatString = loyaltyCard.barcodeType;
final BarcodeFormat format = BarcodeFormat.valueOf(formatString);
final String cardIdString = loyaltyCard.cardId;
if(noteFieldEdit.getText().length() == 0)
{
noteFieldEdit.setText(loyaltyCard.note);
noteFieldView.setText(loyaltyCard.note);
}
cardIdFieldView.setText(loyaltyCard.cardId);
if(cardIdFieldView.getText().length() == 0)
{
cardIdFieldView.setText(loyaltyCard.cardId);
}
if(barcodeTypeField.getText().length() == 0)
{
barcodeTypeField.setText(loyaltyCard.barcodeType);
}
if(updateLoyaltyCard)
{
setTitle(R.string.editCardTitle);
storeFieldView.setVisibility(View.GONE);
noteFieldView.setVisibility(View.GONE);
}
else
{
barcodeCaptureLayout.setVisibility(View.GONE);
captureButton.setVisibility(View.GONE);
setTitle(R.string.viewCardTitle);
storeFieldEdit.setVisibility(View.GONE);
noteFieldEdit.setVisibility(View.GONE);
}
if(loyaltyCard.note.length() > 0)
{
noteView.setText(loyaltyCard.note);
}
else
{
setTitle(R.string.addCardTitle);
storeFieldView.setVisibility(View.GONE);
noteFieldView.setVisibility(View.GONE);
noteView.setVisibility(View.GONE);
noteViewDivider.setVisibility(View.GONE);
}
shortcutBorder.setVisibility(viewLoyaltyCard ? View.GONE : View.VISIBLE);
shortcutTablerow.setVisibility(viewLoyaltyCard ? View.GONE : View.VISIBLE);
storeName.setText(loyaltyCard.store);
if(cardIdFieldView.getText().length() > 0 && barcodeTypeField.getText().length() > 0)
int cardViewLetterFontSize = getResources().getDimensionPixelSize(R.dimen.cardViewLetterFontSize);
int pixelSize = getResources().getDimensionPixelSize(R.dimen.cardThumbnailSizeLarge);
LetterBitmap letterBitmap = new LetterBitmap(this, loyaltyCard.store, loyaltyCard.store, cardViewLetterFontSize, pixelSize, pixelSize);
collapsingToolbarLayout.setBackgroundColor(letterBitmap.getBackgroundColor());
if(barcodeImage.getHeight() == 0)
{
String formatString = barcodeTypeField.getText().toString();
final BarcodeFormat format = BarcodeFormat.valueOf(formatString);
final String cardIdString = cardIdFieldView.getText().toString();
if(barcodeImage.getHeight() == 0)
{
Log.d(TAG, "ImageView size is not known known at start, waiting for load");
// The size of the ImageView is not yet available as it has not
// yet been drawn. Wait for it to be drawn so the size is available.
barcodeImage.getViewTreeObserver().addOnGlobalLayoutListener(
Log.d(TAG, "ImageView size is not known known at start, waiting for load");
// The size of the ImageView is not yet available as it has not
// yet been drawn. Wait for it to be drawn so the size is available.
barcodeImage.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener()
{
@Override
@@ -239,143 +157,19 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
new BarcodeImageWriterTask(barcodeImage, cardIdString, format).execute();
}
});
}
else
{
Log.d(TAG, "ImageView size known known, creating barcode");
new BarcodeImageWriterTask(barcodeImage, cardIdString, format).execute();
}
barcodeImageLayout.setVisibility(View.VISIBLE);
}
View.OnClickListener captureCallback = new View.OnClickListener()
{
@Override
public void onClick(View v)
{
IntentIntegrator integrator = new IntentIntegrator(LoyaltyCardViewActivity.this);
integrator.setDesiredBarcodeFormats(BarcodeSelectorActivity.SUPPORTED_BARCODE_TYPES);
String prompt = getResources().getString(R.string.scanCardBarcode);
integrator.setPrompt(prompt);
integrator.initiateScan();
}
};
captureButton.setOnClickListener(captureCallback);
enterButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
Intent i = new Intent(getApplicationContext(), BarcodeSelectorActivity.class);
String cardId = cardIdFieldView.getText().toString();
if(cardId.length() > 0)
{
final Bundle b = new Bundle();
b.putString("initialCardId", cardId);
i.putExtras(b);
}
startActivityForResult(i, SELECT_BARCODE_REQUEST);
}
});
if(cardIdFieldView.getText().length() > 0)
{
cardIdDivider.setVisibility(View.VISIBLE);
cardIdTableRow.setVisibility(View.VISIBLE);
enterButton.setText(R.string.editCard);
}
else
{
cardIdDivider.setVisibility(View.GONE);
cardIdTableRow.setVisibility(View.GONE);
enterButton.setText(R.string.enterCard);
}
}
private void doSave()
{
String store = storeFieldEdit.getText().toString();
String note = noteFieldEdit.getText().toString();
boolean shouldAddShortcut = shortcutCheckbox.isChecked();
String cardId = cardIdFieldView.getText().toString();
String barcodeType = barcodeTypeField.getText().toString();
if(store.isEmpty())
{
Snackbar.make(storeFieldEdit, R.string.noStoreError, Snackbar.LENGTH_LONG).show();
return;
Log.d(TAG, "ImageView size known known, creating barcode");
new BarcodeImageWriterTask(barcodeImage, cardIdString, format).execute();
}
if(cardId.isEmpty() || barcodeType.isEmpty())
{
Snackbar.make(cardIdFieldView, R.string.noCardIdError, Snackbar.LENGTH_LONG).show();
return;
}
if(updateLoyaltyCard)
{
db.updateLoyaltyCard(loyaltyCardId, store, note, cardId, barcodeType);
Log.i(TAG, "Updated " + loyaltyCardId + " to " + cardId);
}
else
{
loyaltyCardId = (int)db.insertLoyaltyCard(store, note, cardId, barcodeType);
}
if(shouldAddShortcut)
{
addShortcut(loyaltyCardId, store);
}
finish();
}
private void addShortcut(int id, String name)
{
Intent shortcutIntent = new Intent(this, LoyaltyCardViewActivity.class);
shortcutIntent.setAction(Intent.ACTION_MAIN);
// Prevent instances of the view activity from piling up; if one exists let this
// one replace it.
shortcutIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
Bundle bundle = new Bundle();
bundle.putInt("id", id);
bundle.putBoolean("view", true);
shortcutIntent.putExtras(bundle);
Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource
.fromContext(this, R.mipmap.ic_launcher));
intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
// Do not duplicate the shortcut if it is already there
intent.putExtra("duplicate", false);
getApplicationContext().sendBroadcast(intent);
Toast.makeText(this, R.string.addedShortcut, Toast.LENGTH_LONG).show();
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
if(viewLoyaltyCard)
{
getMenuInflater().inflate(R.menu.card_view_menu, menu);
}
else if(updateLoyaltyCard)
{
getMenuInflater().inflate(R.menu.card_update_menu, menu);
}
else
{
getMenuInflater().inflate(R.menu.card_add_menu, menu);
}
getMenuInflater().inflate(R.menu.card_view_menu, menu);
rotationEnabled = true;
@@ -393,40 +187,8 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
finish();
break;
case R.id.action_delete:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.deleteTitle);
builder.setMessage(R.string.deleteConfirmation);
builder.setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
Log.e(TAG, "Deleting card: " + loyaltyCardId);
DBHelper db = new DBHelper(LoyaltyCardViewActivity.this);
db.deleteLoyaltyCard(loyaltyCardId);
ShortcutHelper.removeShortcut(LoyaltyCardViewActivity.this, loyaltyCardId);
finish();
dialog.dismiss();
}
});
builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
}
});
AlertDialog dialog = builder.create();
dialog.show();
return true;
case R.id.action_edit:
Intent intent = new Intent(getApplicationContext(), LoyaltyCardViewActivity.class);
Intent intent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
Bundle bundle = new Bundle();
bundle.putInt("id", loyaltyCardId);
bundle.putBoolean("update", true);
@@ -450,50 +212,8 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
}
rotationEnabled = !rotationEnabled;
return true;
case R.id.action_save:
doSave();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent)
{
String contents = null;
String format = null;
IntentResult result =
IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
if (result != null)
{
Log.i(TAG, "Received barcode information from capture");
contents = result.getContents();
format = result.getFormatName();
}
if(requestCode == SELECT_BARCODE_REQUEST && resultCode == Activity.RESULT_OK)
{
Log.i(TAG, "Received barcode information from capture");
contents = intent.getStringExtra(BarcodeSelectorActivity.BARCODE_CONTENTS);
format = intent.getStringExtra(BarcodeSelectorActivity.BARCODE_FORMAT);
}
if(contents != null && contents.isEmpty() == false &&
format != null && format.isEmpty() == false)
{
Log.i(TAG, "Read barcode id: " + contents);
Log.i(TAG, "Read format: " + format);
TextView cardIdView = (TextView)findViewById(R.id.cardIdView);
cardIdView.setText(contents);
final TextView barcodeTypeField = (TextView) findViewById(R.id.barcodeType);
barcodeTypeField.setText(format);
onResume();
}
}
}

View File

@@ -40,7 +40,7 @@ public class MainActivity extends AppCompatActivity
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
updateLoyaltyCardList();
@@ -62,8 +62,8 @@ public class MainActivity extends AppCompatActivity
private void updateLoyaltyCardList()
{
final ListView cardList = (ListView) findViewById(R.id.list);
final TextView helpText = (TextView) findViewById(R.id.helpText);
final ListView cardList = findViewById(R.id.list);
final TextView helpText = findViewById(R.id.helpText);
final DBHelper db = new DBHelper(this);
if(db.getLoyaltyCardCount() > 0)
@@ -96,7 +96,6 @@ public class MainActivity extends AppCompatActivity
i.setAction("");
final Bundle b = new Bundle();
b.putInt("id", loyaltyCard.id);
b.putBoolean("view", true);
i.putExtras(b);
ShortcutHelper.updateShortcuts(MainActivity.this, loyaltyCard, i);
@@ -121,7 +120,7 @@ public class MainActivity extends AppCompatActivity
public boolean onContextItemSelected(MenuItem item)
{
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
ListView listView = (ListView) findViewById(R.id.list);
ListView listView = findViewById(R.id.list);
Cursor cardCursor = (Cursor)listView.getItemAtPosition(info.position);
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cardCursor);
@@ -153,7 +152,7 @@ public class MainActivity extends AppCompatActivity
if (id == R.id.action_add)
{
Intent i = new Intent(getApplicationContext(), LoyaltyCardViewActivity.class);
Intent i = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
startActivity(i);
return true;
}

BIN
app/src/main/res/drawable-hdpi/app_icon_intro.png Normal file → Executable file
View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

BIN
app/src/main/res/drawable-mdpi/app_icon_intro.png Normal file → Executable file
View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

BIN
app/src/main/res/drawable-xhdpi/app_icon_intro.png Normal file → Executable file
View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

BIN
app/src/main/res/drawable-xxhdpi/app_icon_intro.png Normal file → Executable file
View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 50 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 71 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 75 KiB

BIN
app/src/main/res/drawable-xxxhdpi/app_icon_intro.png Normal file → Executable file
View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 72 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 104 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 111 KiB

View File

@@ -146,55 +146,6 @@
android:background="@color/inputBorder" />
</TableRow>
<!-- Add Shortcut -->
<View
android:id="@+id/shortcutBorder"
android:layout_height="@dimen/inputBorderThickness"
android:layout_width="match_parent"
android:background="@color/inputBorder" />
<TableRow
android:id="@+id/shortcutTablerow"
android:background="@color/inputBackground"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<View
android:gravity="start"
android:layout_height="match_parent"
android:layout_width="@dimen/inputBorderThickness"
android:background="@color/inputBorder" />
<RelativeLayout
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingRight="@dimen/inputPadding"
android:paddingEnd="@dimen/inputPadding">
<TextView
android:id="@+id/shortcutField"
android:text="@string/addShortcut"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:textSize="@dimen/inputSize"
android:padding="@dimen/inputPadding"
android:layout_alignParentStart="true"/>
<CheckBox
android:id="@+id/shortcutCheckbox"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:padding="@dimen/inputPadding"
android:textSize="@dimen/inputSize"
android:layout_toEndOf="@id/shortcutField"/>
</RelativeLayout>
<View
android:gravity="end"
android:layout_height="match_parent"
android:layout_width="@dimen/inputBorderThickness"
android:background="@color/inputBorder" />
</TableRow>
<!-- Card ID -->
<View
android:id="@+id/cardIdDivider"

View File

@@ -1,32 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:orientation="vertical"
android:padding="10.0dp"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout android:orientation="horizontal"
android:padding="5.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:baselineAligned="true">
<TextView android:textSize="20.0sp"
android:id="@+id/store"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:maxLines="1"/>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:baselineAligned="false"
android:padding="@dimen/activity_margin">
<ImageView
android:id="@+id/thumbnail"
android:layout_width="@dimen/cardThumbnailSize"
android:layout_height="@dimen/cardThumbnailSize"
android:layout_marginEnd="@dimen/activity_margin"
android:src="@mipmap/ic_launcher"
android:contentDescription="@string/thumbnailDescription"/>
<LinearLayout
android:orientation="vertical"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<LinearLayout
android:id="@+id/valueLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="visible">
<TextView
android:id="@+id/store"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/storeNameTextSize"
android:textStyle="bold"/>
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/note"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:ellipsize="end"
android:textSize="@dimen/noteTextSize"/>
</LinearLayout>
<LinearLayout android:orientation="horizontal"
android:padding="5.0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:baselineAligned="true">
<TextView android:textSize="20.0sp"
android:layout_gravity="start"
android:id="@+id/cardId"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,139 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/coordinator_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fitsSystemWindows="true"
>
<FrameLayout
android:clipChildren="false"
android:clipToPadding="false"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.constraint.ConstraintLayout
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.constraint.Guideline
android:id="@+id/centerGuideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5"/>
<ImageView
android:id="@+id/barcode"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="20.0dip"
android:layout_marginBottom="10.0dip"
app:layout_constraintBottom_toTopOf="@+id/centerGuideline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:contentDescription="@string/barcodeImageDescription"/>
<TextView
android:id="@+id/cardIdView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginLeft="10.0dip"
android:layout_marginRight="10.0dip"
app:layout_constraintTop_toBottomOf="@id/centerGuideline"
app:layout_constraintBottom_toTopOf="@+id/noteViewDivider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:textAlignment="center"
app:autoSizeTextType="uniform"
app:autoSizeMinTextSize="@dimen/singleCardCardIdTextSizeMin"
app:autoSizeMaxTextSize="@dimen/singleCardCardIdTextSizeMax"
android:ellipsize="end"
android:textIsSelectable="true"/>
<View
android:id="@id/noteViewDivider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@android:color/black"
app:layout_constraintTop_toBottomOf="@id/cardIdView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="@id/noteView"
/>
<TextView
android:id="@+id/noteView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10.0dip"
android:ellipsize="end"
android:layout_gravity="bottom"
app:layout_constraintTop_toBottomOf="@id/noteViewDivider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:autoSizeTextType="uniform"
app:autoSizeMinTextSize="@dimen/singleCardNoteTextSizeMin"
app:autoSizeMaxTextSize="@dimen/singleCardNoteTextSizeMax"
android:textIsSelectable="true"/>
</android.support.constraint.ConstraintLayout>
<View
android:id="@+id/drop_shadow_actionbar"
android:layout_width="fill_parent"
android:layout_height="5.0dip"
android:layout_gravity="top"/>
</FrameLayout>
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar_layout"
android:background="@android:color/transparent"
android:clipChildren="false"
android:clipToPadding="false"
android:layout_width="fill_parent"
android:layout_height="224.0dip"
android:weightSum="1.0"
android:fitsSystemWindows="true">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsingToolbarLayout"
android:clipChildren="false"
android:clipToPadding="false"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:minHeight="56.0dip"
android:layout_weight="1.0"
app:expandedTitleMarginStart="48dp"
app:expandedTitleMarginEnd="64dp"
app:contentScrim="?colorPrimary"
app:expandedTitleGravity="top">
<TextView
android:id="@+id/storeName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:ellipsize="end"
android:textColor="@android:color/white"
android:textSize="40sp"
android:textAlignment="center"
android:layout_gravity="center"
android:layout_marginTop="?actionBarSize"
app:layout_collapseMode="parallax"
android:fitsSystemWindows="true"/>
<android.support.v7.widget.Toolbar
android:id="@id/toolbar"
android:background="@android:color/transparent"
android:theme="@style/CardView.ActionBarTheme"
android:layout_width="fill_parent"
android:layout_height="?actionBarSize"
app:contentInsetStart="72.0dip"
app:layout_collapseMode="pin" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -15,58 +15,39 @@
<string name="save">Uložit</string>
<string name="capture">Naskenovat kartu</string>
<string name="enterCard">Vložit vlastnoručně</string>
<string name="edit">Editovat</string>
<string name="delete">Smazat</string>
<string name="confirm">Potvrdit</string>
<string name="deleteTitle">Odstzranit věrnostní kartu</string>
<string name="deleteConfirmation">Opravdu chcete smazat tuto věrnostní kartu?</string>
<string name="ok">Ano</string>
<string name="copy_to_clipboard">Kopírovat ID do schránky</string>
<string name="sendLabel">Odeslat&#8230;</string>
<string name="editCardTitle">Editovat věrnostní kartu</string>
<string name="addCardTitle">Přidat věrnostní kartu</string>
<string name="viewCardTitle">Zobrazit věrnostní kartu</string>
<string name="scanCardBarcode">Oskenujte kód karty</string>
<string name="barcodeImageDescription">Obrázek kódu karty</string>
<string name="noStoreError">Nebyl zadán Obchod</string>
<string name="noCardIdError">Nebylo zadáno ID karty</string>
<string name="cardIdFormat">%1$s: %2$s</string>
<string name="importExport">Import/Export</string>
<string name="importName">Import</string>
<string name="exportName">Export</string>
<string name="importExportHelp">Zálohování dat vám umožní přesunout vaše uložené karty na jiné zařízení.</string>
<string name="importedFrom">Importováno z: %1$s</string>
<string name="exportedTo">Exportováno do: %1$s</string>
<string name="fileMissing">Doubor chybí: %1$s</string>
<string name="importSuccessfulTitle">Import proběhl úspěšně</string>
<string name="importFailedTitle">Import selhal</string>
<string name="importFailed">Import selhal: %1$s</string>
<string name="exportSuccessfulTitle">Export proběhl úspěšně</string>
<string name="exportFailedTitle">Export selhal</string>
<string name="exportFailed">Export selhal: %1$s</string>
<string name="importing">Importuji&#8230;</string>
<string name="exporting">Exportuji&#8230;</string>
<string name="noExternalStoragePermissionError">Nelze importovat nebo exportovat karty bez přístupu k externímu uložišti</string>
<string name="about">O aplikaci</string>
<string name="app_copyright_fmt">Copyright 2016-<xliff:g>%d</xliff:g> Branden Archer</string>
<string name="app_license">Licensed under the GPLv3.</string>
<string name="about_title_fmt">O aplikaci <xliff:g id="app_name">%s</xliff:g></string>
<string name="debug_version_fmt">Verze: <xliff:g id="version">%s</xliff:g></string>
<string name="app_revision_fmt">Revizní informace: <xliff:g id="app_revision_url">%s</xliff:g></string>
<string name="app_libraries"><xliff:g id="app_name">%s</xliff:g> používá tyto knihovny třetích stran: <xliff:g id="app_libraries_list">%s</xliff:g></string>
<string name="selectBarcodeTitle">Vyberte čárový kód</string>
<string name="enterBarcodeInstructions">Zadejte hodnotu čárového kódu a potm vyberte kód, který představuje čárový kód, který je na kartě.</string>
<string name="copy_to_clipboard_toast">ID karty zkopírováno do schránky</string>
<string name="importExportHelp">Zálohování dat vám umožní přesunout vaše uložené karty na jiné zařízení.</string>
<string name="importSuccessfulTitle">Import proběhl úspěšně</string>
<string name="importFailedTitle">Import selhal</string>
<string name="exportSuccessfulTitle">Export proběhl úspěšně</string>
<string name="exportFailedTitle">Export selhal</string>
<string name="exportOptionExplanation">Data jsou zapsána do kořenové složky externího uložiště.</string>
<string name="importOptionFilesystemTitle">Import ze souborového systému</string>
<string name="importOptionFilesystemExplanation">Vyberte konkrétní soubor v uložišti.</string>
@@ -77,6 +58,17 @@
<string name="importOptionFixedTitle">Import z umístění exportu</string>
<string name="importOptionFixedExplanation">Import ze stejné složky souborového systému do níž se zapisuje při exportu.</string>
<string name="importOptionFixedButton">Použít složku exportu</string>
<string name="sendLabel">Odeslat&#8230;</string>
<string name="about">O aplikaci</string>
<string name="app_copyright_fmt">Copyright 2016-<xliff:g>%d</xliff:g> Branden Archer</string>
<string name="app_license">Licensed under the GPLv3.</string>
<string name="about_title_fmt">O aplikaci <xliff:g id="app_name">%s</xliff:g></string>
<string name="debug_version_fmt">Verze: <xliff:g id="version">%s</xliff:g></string>
<string name="app_revision_fmt">Revizní informace: <xliff:g id="app_revision_url">%s</xliff:g></string>
<string name="app_libraries"><xliff:g id="app_name">%s</xliff:g> používá tyto knihovny třetích stran: <xliff:g id="app_libraries_list">%s</xliff:g></string>
<string name="selectBarcodeTitle">Vyberte čárový kód</string>
<string name="enterBarcodeInstructions">Zadejte hodnotu čárového kódu a potm vyberte kód, který představuje čárový kód, který je na kartě.</string>
<string name="copy_to_clipboard_toast">ID karty zkopírováno do schránky</string>
</resources>

View File

@@ -1,62 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<resources
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name">Loyalty Card Keychain</string>
<string name="about">Über</string>
<string name="about_title_fmt">Über <xliff:g id="app_name">%s</xliff:g></string>
<string name="action_add">Neu</string>
<string name="addCardTitle">Neue Kundenkarte</string>
<string name="app_copyright_fmt">Copyright 2016-<xliff:g>%d</xliff:g> Branden Archer</string>
<string name="app_license">Lizensiert unter der GPLv3.</string>
<string name="app_libraries"><xliff:g id="app_name">%s</xliff:g> benutzt die folgenden Fremdbibliotheken: <xliff:g id="app_libraries_list">%s</xliff:g></string>
<string name="app_resources"><xliff:g id="app_name">%s</xliff:g> verwendet folgenden Dritt-Ressourcen: <xliff:g id="app_resources_list">%s</xliff:g></string>
<string name="viewCardTitle">Kundenkarte anzeigen</string>
<string name="ok">Ok</string>
<string name="note">Notiz</string>
<string name="save">Speichern</string>
<string name="scanCardBarcode">Barcode scannen</string>
<string name="selectBarcodeTitle">Barcode auswählen</string>
<string name="storeName">Geschäft</string>
<string name="noStoreError">Kein Geschäft angegeben</string>
<string name="exportName">Exportieren</string>
<string name="exportedTo">Exportiert nach: %1$s</string>
<string name="fileMissing">Datei fehlt: %1$s</string>
<string name="importExport">Import/Export</string>
<string name="importFailed">Import fehlgeschlagen: %1$s</string>
<string name="importName">Import</string>
<string name="importedFrom">Importiert von: %1$s</string>
<string name="noCardIdError">Keine Kartennummer angegeben</string>
<string name="noExternalStoragePermissionError">Ohne die Berechtigung für den externen Speicher kann kein Import oder Export erfolgen.</string>
<string name="noGiftCards">Sie haben noch keine Kundenkarte angelegt. Über den "+" Button oben rechts, können welche angelegt werden.\n\nDiese App ermöglicht es, Kundenkarten immer mit zu führen.</string>
<string name="cancel">Abbrechen</string>
<string name="capture">Karte erfassen</string>
<string name="storeName">Geschäft</string>
<string name="note">Notiz</string>
<string name="cardId">Kartennummer</string>
<string name="cardIdFormat">%1$s: %2$s</string>
<string name="confirm">Bestätigen</string>
<string name="copy_to_clipboard">Kopiere die Nummer in die Zwischenablage</string>
<string name="barcodeType">Barcodeart</string>
<string name="delete">Löschen</string>
<string name="deleteConfirmation">Bitte bestätigen Sie, dass diese Karte gelöscht werden soll.</string>
<string name="edit">Bearbeiten</string>
<string name="editCardTitle">Kundenkarte bearbeiten</string>
<string name="cancel">Abbrechen</string>
<string name="save">Speichern</string>
<string name="capture">Karte erfassen</string>
<string name="enterCard">Karte einfügen</string>
<string name="editCard">Karte bearbeiten</string>
<string name="exportFailed">Export fehlgeschlagen: %1$s</string>
<string name="barcodeType">Barcodeart</string>
<string name="edit">Bearbeiten</string>
<string name="delete">Löschen</string>
<string name="confirm">Bestätigen</string>
<string name="deleteConfirmation">Bitte bestätigen Sie, dass diese Karte gelöscht werden soll.</string>
<string name="ok">Ok</string>
<string name="copy_to_clipboard">Kopiere die Nummer in die Zwischenablage</string>
<string name="sendLabel">Senden&#8230;</string>
<string name="editCardTitle">Kundenkarte bearbeiten</string>
<string name="addCardTitle">Neue Kundenkarte</string>
<string name="viewCardTitle">Kundenkarte anzeigen</string>
<string name="scanCardBarcode">Barcode scannen</string>
<string name="barcodeImageDescription">Bild des Barcodes</string>
<string name="copy_to_clipboard_toast">Nummer in die Zwischenablage kopiert</string>
<string name="debug_version_fmt">Version: <xliff:g id="version">%s</xliff:g></string>
<string name="enterBarcodeInstructions">Fügen Sie die Kundennummer ein, anschließend wählen Sie die korrekte Barcodeart aus.</string>
<string name="app_revision_fmt">Versions Information: <xliff:g id="app_revision_url">%s</xliff:g></string>
<string name="importing">Importiere…</string>
<string name="exporting">Exportiere…</string>
<string name="noStoreError">Kein Geschäft angegeben</string>
<string name="noCardIdError">Keine Kartennummer angegeben</string>
<string name="cardIdFormat">%1$s: %2$s</string>
<string name="importExport">Import/Export</string>
<string name="importName">Import</string>
<string name="exportName">Exportieren</string>
<string name="importExportHelp">Gesicherte Daten ermöglichen das Verschieben der Kundenkarten auf ein anderes Gerät.</string>
<string name="importedFrom">Importiert von: %1$s</string>
<string name="exportedTo">Exportiert nach: %1$s</string>
<string name="fileMissing">Datei fehlt: %1$s</string>
<string name="importSuccessfulTitle">Import erfolgreich</string>
<string name="importFailedTitle">Import fehlgeschlagen</string>
<string name="importFailed">Import fehlgeschlagen: %1$s</string>
<string name="exportSuccessfulTitle">Export erfolgreich</string>
<string name="exportFailedTitle">Export fehlgeschlagen</string>
<string name="exportFailed">Export fehlgeschlagen: %1$s</string>
<string name="importing">Importiere…</string>
<string name="exporting">Exportiere…</string>
<string name="noExternalStoragePermissionError">Ohne die Berechtigung für den externen Speicher kann kein Import oder Export erfolgen.</string>
<string name="exportOptionExplanation">Die Datei wird ins Rootverzeichnis des externen Speichers geschrieben.</string>
<string name="importOptionFilesystemTitle">Importiere vom Dateisystem</string>
<string name="importOptionFilesystemExplanation">Wähle eine Datei im Speicher aus.</string>
@@ -67,7 +58,20 @@
<string name="importOptionFixedTitle">Importiere vom Export-Pfad</string>
<string name="importOptionFixedExplanation">Importiere vom Export-Pfad.</string>
<string name="importOptionFixedButton">Verwende den Export-Pfad.</string>
<string name="sendLabel">Senden&#8230;</string>
<string name="about">Über</string>
<string name="app_copyright_fmt">Copyright 2016-<xliff:g>%d</xliff:g> Branden Archer</string>
<string name="app_license">Lizensiert unter der GPLv3.</string>
<string name="about_title_fmt">Über <xliff:g id="app_name">%s</xliff:g></string>
<string name="debug_version_fmt">Version: <xliff:g id="version">%s</xliff:g></string>
<string name="app_revision_fmt">Versions Information: <xliff:g id="app_revision_url">%s</xliff:g></string>
<string name="app_libraries"><xliff:g id="app_name">%s</xliff:g> benutzt die folgenden Fremdbibliotheken: <xliff:g id="app_libraries_list">%s</xliff:g></string>
<string name="app_resources"><xliff:g id="app_name">%s</xliff:g> verwendet folgenden Dritt-Ressourcen: <xliff:g id="app_resources_list">%s</xliff:g></string>
<string name="selectBarcodeTitle">Barcode auswählen</string>
<string name="enterBarcodeInstructions">Fügen Sie die Kundennummer ein, anschließend wählen Sie die korrekte Barcodeart aus.</string>
<string name="copy_to_clipboard_toast">Nummer in die Zwischenablage kopiert</string>
<string name="startIntro">Starte Einführung</string>
<string name="intro1Title">Willkommen zu Loyalty Card Keychain\n</string>

View File

@@ -18,36 +18,56 @@
<string name="editCard">Modifier</string>
<string name="edit">Modifier</string>
<string name="delete">Supprimer</string>
<string name="confirm">Confirmer</string>
<string name="lockScreen">Désactiver la rotation</string>
<string name="unlockScreen">Activer la rotation</string>
<string name="deleteTitle">Supprimer la carte de fidélité</string>
<string name="deleteConfirmation">Confirmez que vous souhaitez supprimer cette carte</string>
<string name="ok">OK</string>
<string name="copy_to_clipboard">Copier le numéro dans le presse-papier</string>
<string name="sendLabel">Envoyer&#8230;</string>
<string name="addedShortcut">Ajouter à l\'écran d\'accueil</string>
<string name="editCardTitle">Modifier la carte de fidélité</string>
<string name="addCardTitle">Ajouter une carte de fidélité</string>
<string name="viewCardTitle">Voir la carte de fidélité</string>
<string name="scanCardBarcode">Flasher le code-barres de la carte</string>
<string name="cardShortcut">Raccourci de carte</string>
<string name="noCardsMessage">Il n\'y a aucune carte. Ajoutez en une d\'abord.</string>
<string name="barcodeImageDescription">Image du code-barres de la carte</string>
<string name="noStoreError">Aucun nom n\'a été saisi</string>
<string name="noCardIdError">Aucun numéro n\'a été saisi</string>
<string name="noCardExistsError">N\'a pas pu retrouver la carte de fidélité</string>
<string name="cardIdFormat">%1$s: %2$s</string>
<string name="importExport">Importer/Exporter</string>
<string name="importName">Importer</string>
<string name="exportName">Exporter</string>
<string name="importExportHelp">Exporter vos données vous permet de récupérer vos cartes sur un autre appareil.</string>
<string name="importedFrom">Importé depuis : %1$s</string>
<string name="exportedTo">Exporté vers : %1$s</string>
<string name="fileMissing">Fichier manquant : %1$s</string>
<string name="importSuccessfulTitle">Importé avec succès</string>
<string name="importFailedTitle">Échec de l\'import</string>
<string name="importFailed">Échec de l\'import : %1$s</string>
<string name="exportSuccessfulTitle">Exporté avec succès</string>
<string name="exportFailedTitle">Échec de l\'export</string>
<string name="exportFailed">Échec de l\'export : %1$s</string>
<string name="importing">Import &#8230;</string>
<string name="exporting">Export &#8230;</string>
<string name="noExternalStoragePermissionError">Impossible d\'importer ou d\'exporter les données sans l\'autorisation d\'accès au stockage externe</string>
<string name="exportOptionExplanation">Les données sont sauvegardées à la racine du stockage externe.</string>
<string name="importOptionFilesystemTitle">Importer depuis le système de fichiers.</string>
<string name="importOptionFilesystemExplanation">Choisissez le fichier à importer.</string>
<string name="importOptionFilesystemButton">Système de fichiers</string>
<string name="importOptionApplicationTitle">Application externe</string>
<string name="importOptionApplicationExplanation">Utilisez une application externe comme Dropbox, Google Drive, ou votre gestionnaire de fichiers favori pour ouvrir un fichier.</string>
<string name="importOptionApplicationButton">Application externe</string>
<string name="importOptionFixedTitle">Importer depuis le même emplacement que pour l\'export</string>
<string name="importOptionFixedExplanation">Importe les données depuis le même emplacement que celui défini pour l\'export.</string>
<string name="importOptionFixedButton">Utiliser l\'emplacement de l\'export</string>
<string name="about">À propos</string>
<string name="app_copyright_fmt">Copyright 2016-<xliff:g>%d</xliff:g> Branden Archer</string>
@@ -63,23 +83,6 @@
<string name="copy_to_clipboard_toast">Numéro de carte copié dans le presse-papier</string>
<string name="importExportHelp">Exporter vos données vous permet de récupérer vos cartes sur un autre appareil.</string>
<string name="importSuccessfulTitle">Importé avec succès</string>
<string name="importFailedTitle">Échec de l\'import</string>
<string name="exportSuccessfulTitle">Exporté avec succès</string>
<string name="exportFailedTitle">Échec de l\'export</string>
<string name="exportOptionExplanation">Les données sont sauvegardées à la racine du stockage externe.</string>
<string name="importOptionFilesystemTitle">Importer depuis le système de fichiers.</string>
<string name="importOptionFilesystemExplanation">Choisissez le fichier à importer.</string>
<string name="importOptionFilesystemButton">Système de fichiers</string>
<string name="importOptionApplicationTitle">Application externe</string>
<string name="importOptionApplicationExplanation">Utilisez une application externe comme Dropbox, Google Drive, ou votre gestionnaire de fichiers favori pour ouvrir un fichier.</string>
<string name="importOptionApplicationButton">Application externe</string>
<string name="importOptionFixedTitle">Importer depuis le même emplacement que pour l\'export</string>
<string name="importOptionFixedExplanation">Importe les données depuis le même emplacement que celui défini pour l\'export.</string>
<string name="importOptionFixedButton">Utiliser l\'emplacement de l\'export</string>
<string name="sendLabel">Envoyer&#8230;</string>
<string name="startIntro">Présentation</string>
<string name="intro1Title">Bienvenue dans\nLoyalty Card Keychain\n</string>
<string name="intro1Description">Gérez vos cartes de fidélité\nsur votre téléphone !\n\n</string>

View File

@@ -0,0 +1,14 @@
<resources
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_add">הוספה</string>
<string name="cardId">מזהה כרטיס</string>
<string name="barcodeType">סוג ברקוד</string>
<string name="cancel">ביטול</string>
<string name="save">שמור</string>
<string name="capture">צלם כרטיס</string>
<string name="enterCard">הכנס כרטיס</string>
<string name="editCard">עריכת כרטיס</string>
</resources>

View File

@@ -7,65 +7,55 @@
<string name="noGiftCards">Non hai ancora alcuna tessera fedeltà al momento. Premi sul bottone "+" (più) in alto per incominciare.\n\nL\'app ti permette di portare con te le tue tessere fedeltà, così da averle sempre a disposizione.</string>
<string name="storeName">Negozio</string>
<string name="note">Note</string>
<string name="cardId">Codice</string>
<string name="barcodeType">Tipo codice a barre</string>
<string name="cancel">Annulla</string>
<string name="save">Salva</string>
<string name="capture">Scansione carta</string>
<string name="edit">Modifica</string>
<string name="delete">Elimina</string>
<string name="editCardTitle">Modifica carta</string>
<string name="addCardTitle">Aggiungi carta</string>
<string name="viewCardTitle">Mostra carta</string>
<string name="scanCardBarcode">Scansiona codice carta</string>
<string name="barcodeImageDescription">Immagine del codice a barre della carta</string>
<string name="noStoreError">Nessun negozio inserito</string>
<string name="noCardIdError">Nessun codice carta inserito</string>
<string name="cardIdFormat">%1$s: %2$s</string>
<string name="note">Note</string>
<string name="importExport">Importa/Esporta</string>
<string name="importName">Importa</string>
<string name="exportName">Esporta</string>
<string name="importedFrom">Importato da: %1$s</string>
<string name="exportedTo">Esportato in: %1$s</string>
<string name="fileMissing">File mancante: %1$s</string>
<string name="importFailed">Impossibile importare: %1$s</string>
<string name="exportFailed">Impossibile esportare: %1$s</string>
<string name="importing">Importazione in corso&#8230;</string>
<string name="exporting">Esportazione in corso&#8230;</string>
<string name="noExternalStoragePermissionError">Impossibile importare o esportare i dati senza il permesso per l\'uso della memoria esterna.</string>
<string name="about">Informazioni</string>
<string name="app_copyright_fmt">Copyright 2016-<xliff:g>%d</xliff:g> Branden Archer</string>
<string name="app_license">Pubblicato sotto licenza GPLv3.</string>
<string name="about_title_fmt">Informazioni su <xliff:g id="app_name">%s</xliff:g></string>
<string name="debug_version_fmt">Versione: <xliff:g id="version">%s</xliff:g></string>
<string name="app_revision_fmt">Informazione sulla revisione: <xliff:g id="app_revision_url">%s</xliff:g></string>
<string name="app_libraries"><xliff:g id="app_name">%s</xliff:g> usa le seguenti librerie di terze parti: <xliff:g id="app_libraries_list">%s</xliff:g></string>
<string name="app_resources"><xliff:g id="app_name">%s</xliff:g> usa le seguenti risorse di terze parti: <xliff:g id="app_resources_list">%s</xliff:g></string>
<string name="ok">Ok</string>
<string name="enterCard">Inserisci carta</string>
<string name="editCard">Modifica carta</string>
<string name="selectBarcodeTitle">Seleziona codice a barre</string>
<string name="enterBarcodeInstructions">Digita il valore del codice a barre, quindi seleziona l\'immagine che rappresenta il codice a barre che vuoi usare.</string>
<string name="copy_to_clipboard">Copia ID negli appunti</string>
<string name="copy_to_clipboard_toast">ID della carta copiato negli appunti</string>
<string name="edit">Modifica</string>
<string name="delete">Elimina</string>
<string name="confirm">Conferma</string>
<string name="lockScreen">Blocca rotazione</string>
<string name="unlockScreen">Sblocca rotazione</string>
<string name="deleteTitle">Rimuovi carta fedeltà</string>
<string name="deleteConfirmation">Conferma che vuoi eliminare questa carta.</string>
<string name="ok">Ok</string>
<string name="copy_to_clipboard">Copia ID negli appunti</string>
<string name="sendLabel">Invia&#8230;</string>
<string name="addedShortcut">Aggiunto al launcher</string>
<string name="editCardTitle">Modifica carta</string>
<string name="addCardTitle">Aggiungi carta</string>
<string name="viewCardTitle">Mostra carta</string>
<string name="scanCardBarcode">Scansiona codice carta</string>
<string name="cardShortcut">Scorciatoia per la carta</string>
<string name="noCardsMessage">Non ci sono carte. Aggiungine prima una</string>
<string name="barcodeImageDescription">Immagine del codice a barre della carta</string>
<string name="noStoreError">Nessun negozio inserito</string>
<string name="noCardIdError">Nessun codice carta inserito</string>
<string name="cardIdFormat">%1$s: %2$s</string>
<string name="importExport">Importa/Esporta</string>
<string name="importName">Importa</string>
<string name="exportName">Esporta</string>
<string name="importExportHelp">Fare il backup dei dati ti permette di spostare le tue tessere da un dispositivo ad un altro.</string>
<string name="importedFrom">Importato da: %1$s</string>
<string name="exportedTo">Esportato in: %1$s</string>
<string name="fileMissing">File mancante: %1$s</string>
<string name="importSuccessfulTitle">Importazione avvenuta con successo</string>
<string name="importFailedTitle">Importazione fallita</string>
<string name="importFailed">Impossibile importare: %1$s</string>
<string name="exportSuccessfulTitle">Esportazione avvenuta con successo</string>
<string name="exportFailedTitle">Esportazione fallita</string>
<string name="exportFailed">Impossibile esportare: %1$s</string>
<string name="importing">Importazione in corso&#8230;</string>
<string name="exporting">Esportazione in corso&#8230;</string>
<string name="noExternalStoragePermissionError">Impossibile importare o esportare i dati senza il permesso per l\'uso della memoria esterna.</string>
<string name="exportOptionExplanation">I dati sono stati scritti nella cartella principale della memoria esterna.</string>
<string name="importOptionFilesystemTitle">Importa dal file system</string>
<string name="importOptionFilesystemExplanation">Scegli un file dal file system.</string>
@@ -76,7 +66,20 @@
<string name="importOptionFixedTitle">Importa da un altro posto</string>
<string name="importOptionFixedExplanation">Importa dallo stesso posto del file system dove si è esportato.</string>
<string name="importOptionFixedButton">Usa luogo dell\'esportazione</string>
<string name="sendLabel">Invia&#8230;</string>
<string name="about">Informazioni</string>
<string name="app_copyright_fmt">Copyright 2016-<xliff:g>%d</xliff:g> Branden Archer</string>
<string name="app_license">Pubblicato sotto licenza GPLv3.</string>
<string name="about_title_fmt">Informazioni su <xliff:g id="app_name">%s</xliff:g></string>
<string name="debug_version_fmt">Versione: <xliff:g id="version">%s</xliff:g></string>
<string name="app_revision_fmt">Informazione sulla revisione: <xliff:g id="app_revision_url">%s</xliff:g></string>
<string name="app_libraries"><xliff:g id="app_name">%s</xliff:g> usa le seguenti librerie di terze parti: <xliff:g id="app_libraries_list">%s</xliff:g></string>
<string name="app_resources"><xliff:g id="app_name">%s</xliff:g> usa le seguenti risorse di terze parti: <xliff:g id="app_resources_list">%s</xliff:g></string>
<string name="selectBarcodeTitle">Seleziona codice a barre</string>
<string name="enterBarcodeInstructions">Digita il valore del codice a barre, quindi seleziona l\'immagine che rappresenta il codice a barre che vuoi usare.</string>
<string name="copy_to_clipboard_toast">ID della carta copiato negli appunti</string>
<string name="startIntro">Incomincia introduzione</string>
<string name="intro1Title">Benvenuto in Carte fedeltà\n</string>

View File

@@ -15,29 +15,22 @@
<string name="save">Išsaugoti</string>
<string name="capture">Nufotografuoti kortelę</string>
<string name="enterCard">Įvesti kortelę</string>
<string name="edit">Redaguoti</string>
<string name="delete">Ištrinti</string>
<string name="confirm">Patvirtinti</string>
<string name="deleteTitle">Panaikinti lojalumo kortelę</string>
<string name="deleteConfirmation">Prašome patvirtinti jog Jūs norite panaikinti šią lojalumo kortelę.</string>
<string name="ok">Gerai</string>
<string name="copy_to_clipboard">Nukopijuoti ID į iškarpinę</string>
<string name="editCardTitle">Redaguoti lojalumo kortelę</string>
<string name="addCardTitle">Pridėti lojalumo kortelę</string>
<string name="viewCardTitle">Paeržiūrėti lojalumo kortelę</string>
<string name="scanCardBarcode">Nuskanuokite kortelės brūkšninį kodą</string>
<string name="barcodeImageDescription">Kortelės brūkšninio kodo paveikslėlis</string>
<string name="noStoreError">Parduotuvė neįvesta</string>
<string name="noCardIdError">Neįvestas kortelės ID</string>
<string name="cardIdFormat">%1$s: %2$s</string>
<string name="importExport">Importuoti/Exportuoti</string>
<string name="importName">Importuoti</string>
<string name="exportName">Exportuoti</string>
@@ -49,7 +42,6 @@
<string name="importing">Importuoja&#8230;</string>
<string name="exporting">Eksportuoja&#8230;</string>
<string name="noExternalStoragePermissionError">Negalima importuoti/eksportuoti kortelių be išorinės atminties leidimo</string>
<string name="about">Apie</string>
<string name="app_copyright_fmt">Visos teisės saugomos 2016-<xliff:g>%d</xliff:g> Branden Archer</string>
<string name="app_license">Licenzijuota pagal GPLv3.</string>
@@ -57,10 +49,9 @@
<string name="debug_version_fmt">Versija: <xliff:g id="version">%s</xliff:g></string>
<string name="app_revision_fmt">Revizijos informacija: <xliff:g id="app_revision_url">%s</xliff:g></string>
<string name="app_libraries"><xliff:g id="app_name">%s</xliff:g> naudoja šias trečiosios šalies bibliotekas: <xliff:g id="app_libraries_list">%s</xliff:g></string>
<string name="selectBarcodeTitle">Pasirinkite brūkšninį kodą</string>
<string name="enterBarcodeInstructions">Enter the barcode value then select the image which represents the barcode you want to use</string>
<string name="copy_to_clipboard_toast">Kortelės ID nukopijuota į iškarpinę</string>
</resources>

View File

@@ -7,63 +7,57 @@
<string name="noGiftCards">Er zijn momenteel geen klantenkaarten toegevoegd. Klik de knop met "+" (plus) om te beginnen.\n\nKlantenkaartenkluis beheert klantenkaarten op een smartphone of tablet, zodat ze altijd binnen handbereik zijn.</string>
<string name="storeName">Winkel</string>
<string name="note">Notitie</string>
<string name="cardId">Kaart-ID</string>
<string name="barcodeType">Barcodetype</string>
<string name="cancel">Annuleer</string>
<string name="save">Sla op</string>
<string name="capture">Scan kaart</string>
<string name="enterCard">Voer kaart in</string>
<string name="editCard">Bewerk kaart</string>
<string name="edit">Bewerk</string>
<string name="delete">Verwijder</string>
<string name="confirm">Bevestig</string>
<string name="lockScreen">Blokkeer rotatie</string>
<string name="unlockScreen">Deblokkeer rotatie</string>
<string name="deleteTitle">Verwijder kaart</string>
<string name="deleteConfirmation">Bevestig deze kaart te verwijderen.</string>
<string name="ok">Oké</string>
<string name="copy_to_clipboard">Kopieer het ID naar het klembord</string>
<string name="sendLabel">Verzend&#8230;</string>
<string name="addedShortcut">Toegevoegd aan hoofdscherm</string>
<string name="editCardTitle">Bewerk klantenkaart</string>
<string name="addCardTitle">Voeg klantenkaart toe</string>
<string name="viewCardTitle">Bekijk klantenkaart</string>
<string name="scanCardBarcode">Scan barcode klantenkaart</string>
<string name="cardShortcut">Kaartsnelkoppeling</string>
<string name="noCardsMessage">Er zijn geen kaarten, voeg er eerst een toe</string>
<string name="barcodeImageDescription">Afbeelding barcode klantenkaart</string>
<string name="noStoreError">Geen winkel toegevoegd</string>
<string name="noCardIdError">Geen kaart-ID toegevoegd</string>
<string name="noCardExistsError">Kon klantenkaart niet vinden</string>
<string name="cardIdFormat">%1$s: %2$s</string>
<string name="note">Notitie</string>
<string name="importExport">Importeer/Exporteer</string>
<string name="importName">Importeer</string>
<string name="exportName">Exporteer</string>
<string name="importExportHelp">Data die is geback-upt maakt het mogelijk om je klantenkaarten naar een ander apparaat te verplaatsen.</string>
<string name="importedFrom">Geïmporteerd van: %1$s</string>
<string name="exportedTo">Geëxporteerd naar: %1$s</string>
<string name="fileMissing">Bestand niet gevonden: %1$s</string>
<string name="importSuccessfulTitle">Importeren succesvol</string>
<string name="importFailedTitle">Importeren mislukte</string>
<string name="importFailed">Importeren mislukte: %1$s</string>
<string name="exportSuccessfulTitle">Exporteren succesvol</string>
<string name="exportFailedTitle">Exporteren mislukt</string>
<string name="exportFailed">Exporteren mislukte: %1$s</string>
<string name="importing">Importerende&#8230;</string>
<string name="exporting">Exporterende&#8230;</string>
<string name="noExternalStoragePermissionError">Niet mogelijk te importeren of exporteren zonder rechten op externe opslag</string>
<string name="about">Over</string>
<string name="app_copyright_fmt">Auteursrecht 2016-<xliff:g>%d</xliff:g> Branden Archer</string>
<string name="app_license">Gelicenseerd met GPLv3.</string>
<string name="about_title_fmt">Over <xliff:g id="app_name">%s</xliff:g></string>
<string name="debug_version_fmt">Versie: <xliff:g id="version">%s</xliff:g></string>
<string name="app_revision_fmt">Revisieïnformatie: <xliff:g id="app_revision_url">%s</xliff:g></string>
<string name="app_libraries"><xliff:g id="app_name">%s</xliff:g> gebruikt de volgende bibliotheken van derden: <xliff:g id="app_libraries_list">%s</xliff:g></string>
<string name="ok">Oké</string>
<string name="enterCard">Voer kaart in</string>
<string name="selectBarcodeTitle">Selecteer barcode</string>
<string name="enterBarcodeInstructions">Voer de waarde van de barcode in en kies daarna de afbeelding die de barcode die je wil gebruiken representeert</string>
<string name="copy_to_clipboard">Kopieer het ID naar het klembord</string>
<string name="copy_to_clipboard_toast">Het ID is naar het klembord gekopieerd</string>
<string name="confirm">Bevestig</string>
<string name="deleteConfirmation">Bevestig deze kaart te verwijderen.</string>
<string name="deleteTitle">Verwijder kaart</string>
<string name="importExportHelp">Data die is geback-upt maakt het mogelijk om je klantenkaarten naar een ander apparaat te verplaatsen.</string>
<string name="importSuccessfulTitle">Importeren succesvol</string>
<string name="importFailedTitle">Importeren mislukte</string>
<string name="exportSuccessfulTitle">Exporteren succesvol</string>
<string name="exportFailedTitle">Exporteren mislukt</string>
<string name="exportOptionExplanation">Data is weggeschreven naar de hoogste map in externe opslag.</string>
<string name="importOptionFilesystemTitle">Importeer van filesysteem</string>
<string name="importOptionFilesystemExplanation">Kies een specifiek bestand van het filesysteem.</string>
@@ -74,6 +68,32 @@
<string name="importOptionFixedTitle">Importeer van exporteerlocatie</string>
<string name="importOptionFixedExplanation">Importeer van zelfde locatie op het filesysteem waar tijdens exporteren naar geschreven is.</string>
<string name="importOptionFixedButton">gebruik exporteerlocaite</string>
<string name="sendLabel">Verzend&#8230;</string>
</resources>
<string name="about">Over</string>
<string name="app_copyright_fmt">Auteursrecht 2016-<xliff:g>%d</xliff:g> Branden Archer</string>
<string name="app_license">Gelicenseerd met GPLv3.</string>
<string name="about_title_fmt">Over <xliff:g id="app_name">%s</xliff:g></string>
<string name="debug_version_fmt">Versie: <xliff:g id="version">%s</xliff:g></string>
<string name="app_revision_fmt">Revisieïnformatie: <xliff:g id="app_revision_url">%s</xliff:g></string>
<string name="app_libraries"><xliff:g id="app_name">%s</xliff:g> gebruikt de volgende bibliotheken van derden: <xliff:g id="app_libraries_list">%s</xliff:g></string>
<string name="app_resources"><xliff:g id="app_name">%s</xliff:g> gebruikt de volgende bronnen van derden: <xliff:g id="app_resources_list\">%s</xliff:g></string>
<string name="selectBarcodeTitle">Selecteer barcode</string>
<string name="enterBarcodeInstructions">Voer de waarde van de barcode in en kies daarna de afbeelding die de barcode die je wil gebruiken representeert</string>
<string name="copy_to_clipboard_toast">Het ID is naar het klembord gekopieerd</string>
<string name="startIntro">Start inleiding</string>
<string name="intro1Title">Welkom bij Loyalty Card Keychain\n</string>
<string name="intro1Description">Beheer je barcodegebaseerde klantenkaarten op je telefoon!\n\n</string>
<string name="intro2Title">Kaarten toevoegen\n</string>
<string name="intro2Description">Voeg een nieuwe kaart toe door in de lijst met kaarten de plus aan te raken.\n\n</string>
<string name="intro3Title">Kaarten toevoegen\n</string>
<string name="intro3Description">Om een barcode toe te voegen, scan deze met je camera of voer ze handmatig in.\n\n</string>
<string name="intro4Title">Toon kaart\n</string>
<string name="intro4Description">Om een kaart te tonen, klik in het hoofdscherm op de naam van de winkel\n\n</string>
<string name="intro5Title">Backup\n</string>
<string name="intro5Description">De kaarten kunnen worden geback-upt. Om te kaarten te exporteren of importeren, raak in het menu in het hoofdscherm Importeer/Exporteer aan.\n\n</string>
<string name="intro6Title">Terugkoppeling\n</string>
<string name="intro6Description">Deze app is gratis, vrije van advertenties en opensource. Zie details door Over aan te raken in het menu op het hoofdscher,.\n\nBeschrijft je ervaring in de app store! (:</string>
</resources>

View File

@@ -1,9 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Palette generated by Material Palette -materialpalette.com/grey/blue-grey -->
<!-- Palette generated by Material IO https://material.io/color/#!/?view.left=0&view.right=0&primary.color=3F51B5&secondary.color=FFC107 -->
<resources>
<color name="colorPrimary">#9E9E9E</color>
<color name="colorPrimaryDark">#616161</color>
<color name="colorAccent">#607D8B</color>
<color name="colorPrimary">#3f51b5</color>
<color name="colorPrimaryLight">#757de8</color>
<color name="colorPrimaryDark">#002984</color>
<color name="colorSecondary">#ffc107</color>
<color name="colorSecondaryLight">#fff350</color>
<color name="colorSecondaryDark">#c79100</color>
<color name="colorPrimaryText">#ffffff</color>
<color name="colorSecondaryText">#000000</color>
<color name="inputContrastBackground">#F8F8F8</color>
<color name="inputBackground">#FFFFFF</color>

View File

@@ -11,9 +11,26 @@
<dimen name="text_size_medium">18sp</dimen>
<dimen name="text_size_large">22sp</dimen>
<dimen name="storeNameTextSize">28sp</dimen>
<dimen name="noteTextSize">14sp</dimen>
<dimen name="singleCardCardIdTextSizeMin">15sp</dimen>
<dimen name="singleCardCardIdTextSizeMax">50sp</dimen>
<dimen name="singleCardNoteTextSizeMin">25sp</dimen>
<dimen name="singleCardNoteTextSizeMax">50sp</dimen>
<dimen name="inputBorderThickness">2dip</dimen>
<dimen name="inputBorderDividerThickness">4dip</dimen>
<dimen name="inputPadding">20dip</dimen>
<dimen name="inputSize">18sp</dimen>
<dimen name="cardThumbnailSize">46dp</dimen>
<dimen name="cardThumbnailSizeLarge">200dp</dimen>
<dimen name="activity_margin_small">8dp</dimen>
<dimen name="activity_margin">16dp</dimen>
<!-- The default letter tile text size -->
<dimen name="tileLetterFontSize">33sp</dimen>
<dimen name="cardViewLetterFontSize">100sp</dimen>
</resources>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<array name="letter_tile_colors">
<item>#f16364</item>
<item>#f58559</item>
<item>#f9a43e</item>
<item>#e4c62e</item>
<item>#67bf74</item>
<item>#59a2be</item>
<item>#2093cd</item>
<item>#ad62a7</item>
</array>
</resources>

View File

@@ -8,7 +8,6 @@
<string name="storeName">Store</string>
<string name="note">Note</string>
<string name="addShortcut">Add Shortcut</string>
<string name="cardId">Card ID</string>
<string name="barcodeType">Barcode Type</string>
@@ -27,12 +26,14 @@
<string name="ok">OK</string>
<string name="copy_to_clipboard">Copy ID to clipboard</string>
<string name="sendLabel">Send&#8230;</string>
<string name="addedShortcut">Added shortcut</string>
<string name="addedShortcut">Added to Home Screen</string>
<string name="editCardTitle">Edit Loyalty Card</string>
<string name="addCardTitle">Add Loyalty Card</string>
<string name="viewCardTitle">View Loyalty Card</string>
<string name="scanCardBarcode">Scan Card\'s Barcode</string>
<string name="cardShortcut">Card Shortcut</string>
<string name="noCardsMessage">There are no cards, add one first</string>
<string name="barcodeImageDescription">Image of card\'s barcode</string>
@@ -84,6 +85,8 @@
<string name="copy_to_clipboard_toast">Card ID copied to clipboard</string>
<string name="thumbnailDescription">Thumbnail for card</string>
<string name="startIntro">Start Intro</string>
<string name="intro1Title">Welcome to Loyalty Card Keychain\n</string>
<string name="intro1Description">Manage your barcode-based store/loyalty cards on your phone!\n\n</string>

View File

@@ -5,7 +5,7 @@
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="colorAccent">@color/colorSecondary</item>
</style>
<style name="AppTheme.NoActionBar">
@@ -17,6 +17,11 @@
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>
<style name="CardView.ActionBarTheme" parent="@style/ThemeOverlay.AppCompat.ActionBar">
<!-- THIS is where you can color the arrow! -->
<item name="colorControlNormal">@android:color/white</item>
</style>
<style name="AppTheme.TextView.NoData" parent="AppTheme">
<item name="android:layout_centerHorizontal">true</item>
<item name="android:layout_centerVertical">true</item>

View File

@@ -40,10 +40,6 @@ public class ImportExportActivityTest
info.activityInfo.exported = true;
Intent intent = new Intent(handler);
if(handler.equals(Intent.ACTION_PICK))
{
intent.setData(Uri.parse("file://"));
}
if(handler.equals(Intent.ACTION_GET_CONTENT))
{

View File

@@ -198,7 +198,11 @@ public class ImportExportTest
clearDatabase();
String corruptEntry = "ThisStringIsLikelyNotPartOfAnyFormat";
// commons-csv would throw a RuntimeException if an entry was quotes but had
// content after. For example:
// abc,def,""abc,abc
// ^ after the quote there should only be a , \n or EOF
String corruptEntry = "ThisStringIsLikelyNotPartOfAnyFormat,\"\"a";
ByteArrayInputStream inData = new ByteArrayInputStream((outData.toString() + corruptEntry).getBytes());
InputStreamReader inStream = new InputStreamReader(inData);

View File

@@ -40,13 +40,21 @@ public class LoyaltyCardCursorAdapterTest
return view;
}
private void checkView(final View view, final String store, final String cardId)
private void checkView(final View view, final String store, final String note)
{
final TextView storeField = (TextView) view.findViewById(R.id.store);
final TextView storeField = view.findViewById(R.id.store);
assertEquals(store, storeField.getText().toString());
final TextView cardIdField = (TextView) view.findViewById(R.id.cardId);
assertEquals(cardId, cardIdField.getText().toString());
final TextView noteField = view.findViewById(R.id.note);
if(note.isEmpty() == false)
{
assertEquals(View.VISIBLE, noteField.getVisibility());
assertEquals(note, noteField.getText().toString());
}
else
{
assertEquals(View.GONE, noteField.getVisibility());
}
}
@@ -61,11 +69,7 @@ public class LoyaltyCardCursorAdapterTest
View view = createView(cursor);
final String cardIdLabel = activity.getResources().getString(R.string.cardId);
final String cardIdFormat = activity.getResources().getString(R.string.cardIdFormat);
String cardIdText = String.format(cardIdFormat, cardIdLabel, card.cardId);
checkView(view, card.store, cardIdText);
checkView(view, card.store, card.note);
}
@Test
@@ -79,13 +83,6 @@ public class LoyaltyCardCursorAdapterTest
View view = createView(cursor);
final String storeNameAndNoteFormat = activity.getResources().getString(R.string.storeNameAndNoteFormat);
String storeAndNoteText = String.format(storeNameAndNoteFormat, card.store, card.note);
final String cardIdLabel = activity.getResources().getString(R.string.cardId);
final String cardIdFormat = activity.getResources().getString(R.string.cardIdFormat);
String cardIdText = String.format(cardIdFormat, cardIdLabel, card.cardId);
checkView(view, storeAndNoteText, cardIdText);
checkView(view, card.store, card.note);
}
}

View File

@@ -9,7 +9,6 @@ import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
@@ -89,7 +88,6 @@ public class LoyaltyCardViewActivityTest
*/
private void saveLoyaltyCardWithArguments(final Activity activity,
final String store, final String note,
final boolean addShortcut,
final String cardId,
final String barcodeType,
boolean creatingNewCard)
@@ -104,27 +102,20 @@ public class LoyaltyCardViewActivityTest
assertEquals(1, db.getLoyaltyCardCount());
}
final EditText storeField = (EditText) activity.findViewById(R.id.storeNameEdit);
final EditText noteField = (EditText) activity.findViewById(R.id.noteEdit);
final CheckBox shortcutCheckbox = (CheckBox) activity.findViewById(R.id.shortcutCheckbox);
final TextView cardIdField = (TextView) activity.findViewById(R.id.cardIdView);
final TextView barcodeTypeField = (TextView) activity.findViewById(R.id.barcodeType);
final EditText storeField = activity.findViewById(R.id.storeNameEdit);
final EditText noteField = activity.findViewById(R.id.noteEdit);
final TextView cardIdField = activity.findViewById(R.id.cardIdView);
final TextView barcodeTypeField = activity.findViewById(R.id.barcodeType);
storeField.setText(store);
noteField.setText(note);
shortcutCheckbox.setChecked(addShortcut);
cardIdField.setText(cardId);
barcodeTypeField.setText(barcodeType);
ShortcutAddedReceiver shortcutAddedReceiver = new ShortcutAddedReceiver();
shortcutAddedReceiver.registerReceiver(activity);
assertEquals(false, activity.isFinishing());
shadowOf(activity).clickMenuItem(R.id.action_save);
assertEquals(true, activity.isFinishing());
shortcutAddedReceiver.unregisterReceiver(activity);
assertEquals(1, db.getLoyaltyCardCount());
LoyaltyCard card = db.getLoyaltyCard(1);
@@ -132,28 +123,6 @@ public class LoyaltyCardViewActivityTest
assertEquals(note, card.note);
assertEquals(cardId, card.cardId);
assertEquals(barcodeType, card.barcodeType);
Intent shortcutRequest = shortcutAddedReceiver.lastRequest();
if(addShortcut)
{
assertNotNull(shortcutRequest);
String name = shortcutRequest.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
assertEquals(card.store, name);
Intent startIntent = shortcutRequest.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
assertNotNull(startIntent);
Bundle startBundle = startIntent.getExtras();
assertNotNull(startBundle);
assertEquals(card.id, startBundle.getInt("id", -1));
assertEquals(true, startBundle.getBoolean("view", false));
}
else
{
assertNull(shortcutRequest);
}
}
/**
@@ -163,7 +132,7 @@ public class LoyaltyCardViewActivityTest
private void captureBarcodeWithResult(final Activity activity, final int buttonId, final boolean success) throws IOException
{
// Start image capture
final Button captureButton = (Button) activity.findViewById(buttonId);
final Button captureButton = activity.findViewById(buttonId);
captureButton.performClick();
ShadowActivity.IntentForResult intentForResult = shadowOf(activity).peekNextStartedActivityForResult();
@@ -208,29 +177,34 @@ public class LoyaltyCardViewActivityTest
private void checkAllFields(final Activity activity, ViewMode mode,
final String store, final String note, final String cardId, final String barcodeType)
{
int captureVisibility = (mode == ViewMode.UPDATE_CARD || mode == ViewMode.ADD_CARD) ? View.VISIBLE : View.GONE;
if(mode == ViewMode.VIEW_CARD)
{
checkFieldProperties(activity, R.id.cardIdView, View.VISIBLE, cardId);
}
else
{
int captureVisibility = (mode == ViewMode.UPDATE_CARD || mode == ViewMode.ADD_CARD) ? View.VISIBLE : View.GONE;
int viewVisibility = (mode == ViewMode.VIEW_CARD) ? View.VISIBLE : View.GONE;
int editVisibility = (mode != ViewMode.VIEW_CARD) ? View.VISIBLE : View.GONE;
int viewVisibility = View.GONE;
int editVisibility = View.VISIBLE;
checkFieldProperties(activity, R.id.storeNameEdit, editVisibility, store);
checkFieldProperties(activity, R.id.storeNameView, viewVisibility, store);
checkFieldProperties(activity, R.id.noteEdit, editVisibility, note);
checkFieldProperties(activity, R.id.shortcutBorder, editVisibility, null);
checkFieldProperties(activity, R.id.shortcutTablerow, editVisibility, null);
checkFieldProperties(activity, R.id.noteView, viewVisibility, note);
checkFieldProperties(activity, R.id.cardIdView, View.VISIBLE, cardId);
checkFieldProperties(activity, R.id.cardIdDivider, cardId.isEmpty() ? View.GONE : View.VISIBLE, null);
checkFieldProperties(activity, R.id.cardIdTableRow, cardId.isEmpty() ? View.GONE : View.VISIBLE, null);
checkFieldProperties(activity, R.id.barcodeType, View.GONE, barcodeType);
checkFieldProperties(activity, R.id.captureButton, captureVisibility, null);
checkFieldProperties(activity, R.id.barcode, View.VISIBLE, null);
checkFieldProperties(activity, R.id.storeNameEdit, editVisibility, store);
checkFieldProperties(activity, R.id.storeNameView, viewVisibility, store);
checkFieldProperties(activity, R.id.noteEdit, editVisibility, note);
checkFieldProperties(activity, R.id.noteView, viewVisibility, note);
checkFieldProperties(activity, R.id.cardIdView, View.VISIBLE, cardId);
checkFieldProperties(activity, R.id.cardIdDivider, cardId.isEmpty() ? View.GONE : View.VISIBLE, null);
checkFieldProperties(activity, R.id.cardIdTableRow, cardId.isEmpty() ? View.GONE : View.VISIBLE, null);
checkFieldProperties(activity, R.id.barcodeType, View.GONE, barcodeType);
checkFieldProperties(activity, R.id.captureButton, captureVisibility, null);
checkFieldProperties(activity, R.id.barcode, View.VISIBLE, null);
}
}
@Test
public void startWithoutParametersCheckFieldsAvailable()
{
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardViewActivity.class).create();
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardEditActivity.class).create();
activityController.start();
activityController.visible();
activityController.resume();
@@ -243,7 +217,7 @@ public class LoyaltyCardViewActivityTest
@Test
public void startWithoutParametersCannotCreateLoyaltyCard()
{
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardViewActivity.class).create();
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardEditActivity.class).create();
activityController.start();
activityController.visible();
activityController.resume();
@@ -253,9 +227,9 @@ public class LoyaltyCardViewActivityTest
DBHelper db = new DBHelper(activity);
assertEquals(0, db.getLoyaltyCardCount());
final EditText storeField = (EditText) activity.findViewById(R.id.storeNameEdit);
final EditText noteField = (EditText) activity.findViewById(R.id.noteEdit);
final TextView cardIdField = (TextView) activity.findViewById(R.id.cardIdView);
final EditText storeField = activity.findViewById(R.id.storeNameEdit);
final EditText noteField = activity.findViewById(R.id.noteEdit);
final TextView cardIdField = activity.findViewById(R.id.cardIdView);
shadowActivity.clickMenuItem(R.id.action_save);
assertEquals(0, db.getLoyaltyCardCount());
@@ -276,7 +250,7 @@ public class LoyaltyCardViewActivityTest
@Test
public void startWithoutParametersBack()
{
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardViewActivity.class).create();
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardEditActivity.class).create();
activityController.start();
activityController.visible();
activityController.resume();
@@ -293,7 +267,7 @@ public class LoyaltyCardViewActivityTest
{
registerMediaStoreIntentHandler();
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardViewActivity.class).create();
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardEditActivity.class).create();
activityController.start();
activityController.visible();
activityController.resume();
@@ -308,36 +282,13 @@ public class LoyaltyCardViewActivityTest
checkAllFields(activity, ViewMode.ADD_CARD, "", "", BARCODE_DATA, BARCODE_TYPE);
// Save and check the gift card
saveLoyaltyCardWithArguments(activity, "store", "note", false, BARCODE_DATA, BARCODE_TYPE, true);
}
@Test
public void startWithoutParametersCaptureBarcodeCreateLoyaltyCardSaveShortcut() throws IOException
{
registerMediaStoreIntentHandler();
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardViewActivity.class).create();
activityController.start();
activityController.visible();
activityController.resume();
Activity activity = (Activity)activityController.get();
checkAllFields(activity, ViewMode.ADD_CARD, "", "", "", "");
// Complete barcode capture successfully
captureBarcodeWithResult(activity, R.id.captureButton, true);
checkAllFields(activity, ViewMode.ADD_CARD, "", "", BARCODE_DATA, BARCODE_TYPE);
// Save and check the gift card
saveLoyaltyCardWithArguments(activity, "store", "note", true, BARCODE_DATA, BARCODE_TYPE, true);
saveLoyaltyCardWithArguments(activity, "store", "note", BARCODE_DATA, BARCODE_TYPE, true);
}
@Test
public void startWithoutParametersCaptureBarcodeFailure() throws IOException
{
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardViewActivity.class).create();
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardEditActivity.class).create();
activityController.start();
activityController.visible();
activityController.resume();
@@ -355,7 +306,7 @@ public class LoyaltyCardViewActivityTest
@Test
public void startWithoutParametersCaptureBarcodeCancel() throws IOException
{
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardViewActivity.class).create();
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardEditActivity.class).create();
activityController.start();
activityController.visible();
activityController.resume();
@@ -381,18 +332,22 @@ public class LoyaltyCardViewActivityTest
final Bundle bundle = new Bundle();
bundle.putInt("id", 1);
Class clazz;
if(editMode)
{
bundle.putBoolean("update", true);
clazz = LoyaltyCardEditActivity.class;
}
else
{
bundle.putBoolean("view", true);
clazz = LoyaltyCardViewActivity.class;
}
intent.putExtras(bundle);
return Robolectric.buildActivity(LoyaltyCardViewActivity.class).withIntent(intent).create();
return Robolectric.buildActivity(clazz).withIntent(intent).create();
}
@Test
@@ -516,4 +471,22 @@ public class LoyaltyCardViewActivityTest
activityController.stop();
activityController.destroy();
}
@Test
public void startWithoutParametersViewBack()
{
ActivityController activityController = createActivityWithLoyaltyCard(false);
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE);
activityController.start();
activityController.visible();
activityController.resume();
assertEquals(false, activity.isFinishing());
shadowOf(activity).clickMenuItem(android.R.id.home);
assertEquals(true, activity.isFinishing());
}
}

View File

@@ -49,10 +49,10 @@ public class MainActivityTest
Activity activity = Robolectric.setupActivity(MainActivity.class);
assertTrue(activity != null);
TextView helpText = (TextView)activity.findViewById(R.id.helpText);
TextView helpText = activity.findViewById(R.id.helpText);
assertEquals(View.VISIBLE, helpText.getVisibility());
ListView list = (ListView)activity.findViewById(R.id.list);
ListView list = activity.findViewById(R.id.list);
assertEquals(View.GONE, list.getVisibility());
}
@@ -74,7 +74,7 @@ public class MainActivityTest
}
@Test
public void clickAddLaunchesLoyaltyCardViewActivity()
public void clickAddLaunchesLoyaltyCardEditActivity()
{
final MainActivity activity = Robolectric.setupActivity(MainActivity.class);
@@ -82,7 +82,7 @@ public class MainActivityTest
Intent intent = shadowOf(activity).peekNextStartedActivityForResult().intent;
assertEquals(new ComponentName(activity, LoyaltyCardViewActivity.class), intent.getComponent());
assertEquals(new ComponentName(activity, LoyaltyCardEditActivity.class), intent.getComponent());
assertNull(intent.getExtras());
}
@@ -95,8 +95,8 @@ public class MainActivityTest
activityController.start();
activityController.resume();
TextView helpText = (TextView)mainActivity.findViewById(R.id.helpText);
ListView list = (ListView)mainActivity.findViewById(R.id.list);
TextView helpText = mainActivity.findViewById(R.id.helpText);
ListView list = mainActivity.findViewById(R.id.list);
assertEquals(0, list.getCount());

View File

@@ -1,39 +0,0 @@
package protect.card_locker;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
public class ShortcutAddedReceiver extends BroadcastReceiver
{
public static final String SHORTCUT_ADD_REQUEST = "com.android.launcher.action.INSTALL_SHORTCUT";
private Intent _request = null;
@Override
public void onReceive(Context context, Intent intent)
{
_request = intent;
}
public void registerReceiver(Context context)
{
context.registerReceiver(this, new IntentFilter(SHORTCUT_ADD_REQUEST));
}
public void unregisterReceiver(Context context)
{
context.unregisterReceiver(this);
}
public Intent lastRequest()
{
return _request;
}
public void reset()
{
_request = null;
}
}

View File

@@ -3,9 +3,12 @@
buildscript {
repositories {
jcenter()
// For the Android Gradle plugin
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.3'
classpath 'com.android.tools.build:gradle:3.0.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -16,6 +19,7 @@ allprojects {
repositories {
jcenter()
maven { url 'https://jitpack.io' }
google()
}
}

View File

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

View File

@@ -1,6 +1,6 @@
#Sun Dec 04 15:17:03 EST 2016
#Mon Dec 25 19:10:53 EST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip

View File

@@ -0,0 +1,7 @@
Are you tired of searching for your plastic reward card while checking out at the store? Are you looking for a free solution which will not take your information?
Loyalty Card Keychain is a application which will store your barcode-based loyalty cards on your phone. The application is open source and tries to do one thing well: manage your cards!
New cards can be added in a snap. Either use your camera to capture the barcode, or type in the number. When the barcode is loaded at the store and displayed it can be scanned with a modern barcode scanner. (Some stores use older barcode scanners, such as flatbed scanners, instead of image scanners. These cannot read the smartphone's display. Instead, request the clerk to type in the number manually).
The application requires very few permissions, and never attempts to access the Internet. There is an option to backup your cards to local storage. From there you can send the backup somewhere safe.

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
metadata/en-US/images/icon.png Executable file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

View File

@@ -0,0 +1 @@
Manages barcode-based store/loyalty cards on your phone

1
metadata/en-US/title.txt Normal file
View File

@@ -0,0 +1 @@
Loyalty Card Keychain

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB