Compare commits

...

54 Commits

Author SHA1 Message Date
Sylvia van Os
e61d29417c Release Catima 1.0 2020-11-06 20:25:46 +01:00
Sylvia van Os
cc1be04cf7 Update CHANGELOG 2020-11-06 20:19:12 +01:00
Sylvia van Os
e0ed5bd11c Update screenshots 2020-11-06 20:18:28 +01:00
Sylvia van Os
1bda5410d8 Fix max db version 2020-11-06 20:17:57 +01:00
Sylvia van Os
4bbeb27714 Feature/groups (#71)
* Basic group management

* Assign cards to groups

* Fix lint

* Fix findbugs 'dodgy code'

* Group name as unique key

* More group tests

* Import/export groups

* Implement group renaming and deleting

* Fix findBugs

* Fix chip marking in edit activity

* Group import/export tests

* Fix some state bugs

* Some last fixes

* Remove redundant if statement

* Fix findBugs

* Deduplicate code

* Cleanup

* Fix groups not showing up with new card

* Fix capture and enter button touching

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2020-10-25 17:26:43 +01:00
Sylvia van Os
565449d17c Update CHANGELOG 2020-10-24 22:34:29 +02:00
136 changed files with 3105 additions and 332 deletions

View File

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

View File

@@ -1,10 +0,0 @@
[main]
host = https://www.transifex.com
[loyalty-card-locker.stringsxml]
file_filter = app/src/main/res/values-<lang>/strings.xml
minimum_perc = 0
source_file = app/src/main/res/values/strings.xml
source_lang = en
type = ANDROID

View File

@@ -1,3 +1,19 @@
## Unreleased
Changes:
- Added rounded edges to card icons on main overview
- Added support for grouping entries
## v0.29 (2020-10-29)
Changes:
- Rebrand to Catima
- Removed intro
- Add floating action buttons
- Fix Android 5 crash when opening About screen
- Add favourites support
- Fix disabled auto-rotate being ignored
## v0.28 (2020-03-09)
Changes:

3
Gemfile Normal file
View File

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

180
Gemfile.lock Normal file
View File

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

View File

@@ -13,8 +13,8 @@ android {
applicationId "me.hackerchick.catima"
minSdkVersion 16
targetSdkVersion 29
versionCode 39
versionName "0.28"
versionCode 41
versionName "1.0"
}
buildTypes {
release {
@@ -45,7 +45,7 @@ android {
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'androidx.appcompat:appcompat:1.2.0'
compile 'com.google.android.material:material:1.2.0-alpha03'
compile 'com.google.android.material:material:1.2.1'
compile 'androidx.legacy:legacy-support-v4:1.0.0'
compile 'com.journeyapps:zxing-android-embedded:3.5.0@aar'
compile 'com.google.zxing:core:3.3.0'
@@ -55,6 +55,7 @@ dependencies {
compile group: 'com.google.guava', name: 'guava', version: '20.0'
compile 'com.github.apl-devs:appintro:v4.2.0'
compile "com.vanniktech:vntnumberpickerpreference:1.0.0"
implementation 'androidx.cardview:cardview:1.0.0'
testCompile 'junit:junit:4.12'
testCompile "org.robolectric:robolectric:4.0.2"
}

View File

@@ -12,4 +12,4 @@ public class ApplicationTest extends ApplicationTestCase<Application>
{
super(Application.class);
}
}
}

View File

@@ -33,6 +33,11 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".ManageGroupsActivity"
android:label="@string/groups"
android:theme="@style/AppTheme.NoActionBar">
</activity>
<activity
android:name=".LoyaltyCardViewActivity"
android:theme="@style/AppTheme.NoActionBar"
@@ -42,7 +47,6 @@
<activity
android:name=".LoyaltyCardEditActivity"
android:theme="@style/AppTheme.NoActionBar"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateHidden"
android:exported="true">
<intent-filter android:label="@string/app_name">
@@ -62,21 +66,17 @@
android:name=".BarcodeSelectorActivity"
android:label="@string/selectBarcodeTitle"
android:theme="@style/AppTheme.NoActionBar"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateHidden"/>
<activity
android:name=".preferences.SettingsActivity"
android:label="@string/settings"
android:configChanges="orientation|screenSize"/>
android:label="@string/settings"/>
<activity
android:name=".ImportExportActivity"
android:label="@string/importExport"
android:configChanges="orientation|screenSize"
android:theme="@style/AppTheme.NoActionBar"/>
<activity
android:name=".CardShortcutConfigure"
android:label="@string/cardShortcut"
android:configChanges="orientation|screenSize"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT"/>

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

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

View File

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

View File

@@ -1,17 +1,25 @@
package protect.card_locker;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import java.util.ArrayList;
import java.util.List;
public class DBHelper extends SQLiteOpenHelper
{
public static final String DATABASE_NAME = "Catima.db";
public static final int ORIGINAL_DATABASE_VERSION = 1;
public static final int DATABASE_VERSION = 3;
public static final int DATABASE_VERSION = 5;
static class LoyaltyCardDbGroups
{
public static final String TABLE = "groups";
public static final String ID = "_id";
}
static class LoyaltyCardDbIds
{
@@ -23,6 +31,14 @@ public class DBHelper extends SQLiteOpenHelper
public static final String HEADER_TEXT_COLOR = "headertextcolor";
public static final String CARD_ID = "cardid";
public static final String BARCODE_TYPE = "barcodetype";
public static final String STAR_STATUS = "starstatus";
}
static class LoyaltyCardDbIdsGroups
{
public static final String TABLE = "cardsGroups";
public static final String cardID = "cardId";
public static final String groupID = "groupId";
}
public DBHelper(Context context)
@@ -33,7 +49,11 @@ public class DBHelper extends SQLiteOpenHelper
@Override
public void onCreate(SQLiteDatabase db)
{
// create table for gift cards
// create table for card groups
db.execSQL("create table " + LoyaltyCardDbGroups.TABLE + "(" +
LoyaltyCardDbGroups.ID + " TEXT primary key not null)");
// create table for cards
db.execSQL("create table " + LoyaltyCardDbIds.TABLE + "(" +
LoyaltyCardDbIds.ID + " INTEGER primary key autoincrement," +
LoyaltyCardDbIds.STORE + " TEXT not null," +
@@ -41,7 +61,14 @@ public class DBHelper extends SQLiteOpenHelper
LoyaltyCardDbIds.HEADER_COLOR + " INTEGER," +
LoyaltyCardDbIds.HEADER_TEXT_COLOR + " INTEGER," +
LoyaltyCardDbIds.CARD_ID + " TEXT not null," +
LoyaltyCardDbIds.BARCODE_TYPE + " TEXT not null)");
LoyaltyCardDbIds.BARCODE_TYPE + " TEXT not null," +
LoyaltyCardDbIds.STAR_STATUS + " INTEGER DEFAULT '0' )");
// create associative table for cards in groups
db.execSQL("create table " + LoyaltyCardDbIdsGroups.TABLE + "(" +
LoyaltyCardDbIdsGroups.cardID + " INTEGER," +
LoyaltyCardDbIdsGroups.groupID + " TEXT," +
"primary key (" + LoyaltyCardDbIdsGroups.cardID + "," + LoyaltyCardDbIdsGroups.groupID +"))");
}
@Override
@@ -62,11 +89,30 @@ public class DBHelper extends SQLiteOpenHelper
db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE
+ " ADD COLUMN " + LoyaltyCardDbIds.HEADER_TEXT_COLOR + " INTEGER");
}
// Upgrade from version 3 to version 4
if(oldVersion < 4 && newVersion >= 4)
{
db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE
+ " ADD COLUMN " + LoyaltyCardDbIds.STAR_STATUS + " INTEGER DEFAULT '0'");
}
// Upgrade from version 4 to version 5
if(oldVersion < 5 && newVersion >= 5)
{
db.execSQL("create table " + LoyaltyCardDbGroups.TABLE + "(" +
LoyaltyCardDbGroups.ID + " TEXT primary key not null)");
db.execSQL("create table " + LoyaltyCardDbIdsGroups.TABLE + "(" +
LoyaltyCardDbIdsGroups.cardID + " INTEGER," +
LoyaltyCardDbIdsGroups.groupID + " TEXT," +
"primary key (" + LoyaltyCardDbIdsGroups.cardID + "," + LoyaltyCardDbIdsGroups.groupID +"))");
}
}
public long insertLoyaltyCard(final String store, final String note, final String cardId,
final String barcodeType, final Integer headerColor,
final Integer headerTextColor)
final Integer headerTextColor, final int starStatus)
{
SQLiteDatabase db = getWritableDatabase();
ContentValues contentValues = new ContentValues();
@@ -76,14 +122,15 @@ public class DBHelper extends SQLiteOpenHelper
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType);
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
contentValues.put(LoyaltyCardDbIds.HEADER_TEXT_COLOR, headerTextColor);
contentValues.put(LoyaltyCardDbIds.STAR_STATUS, starStatus);
final long newId = db.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
return newId;
}
public boolean insertLoyaltyCard(final SQLiteDatabase db, final int id,
final String store, final String note, final String cardId,
public boolean insertLoyaltyCard(final SQLiteDatabase db, final int id, final String store,
final String note, final String cardId,
final String barcodeType, final Integer headerColor,
final Integer headerTextColor)
final Integer headerTextColor, final int starStatus)
{
ContentValues contentValues = new ContentValues();
contentValues.put(LoyaltyCardDbIds.ID, id);
@@ -93,11 +140,11 @@ public class DBHelper extends SQLiteOpenHelper
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType);
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
contentValues.put(LoyaltyCardDbIds.HEADER_TEXT_COLOR, headerTextColor);
contentValues.put(LoyaltyCardDbIds.STAR_STATUS,starStatus);
final long newId = db.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
return (newId != -1);
}
public boolean updateLoyaltyCard(final int id, final String store, final String note,
final String cardId, final String barcodeType,
final Integer headerColor, final Integer headerTextColor)
@@ -116,6 +163,17 @@ public class DBHelper extends SQLiteOpenHelper
return (rowsUpdated == 1);
}
public boolean updateLoyaltyCardStarStatus(final int id, final int starStatus)
{
SQLiteDatabase db = getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(LoyaltyCardDbIds.STAR_STATUS,starStatus);
int rowsUpdated = db.update(LoyaltyCardDbIds.TABLE, contentValues,
LoyaltyCardDbIds.ID + "=?",
new String[]{Integer.toString(id)});
return (rowsUpdated == 1);
}
public LoyaltyCard getLoyaltyCard(final int id)
{
SQLiteDatabase db = getReadableDatabase();
@@ -127,6 +185,7 @@ public class DBHelper extends SQLiteOpenHelper
if(data.getCount() == 1)
{
data.moveToFirst();
card = LoyaltyCard.toLoyaltyCard(data);
}
@@ -135,12 +194,76 @@ public class DBHelper extends SQLiteOpenHelper
return card;
}
public List<Group> getLoyaltyCardGroups(final int id)
{
SQLiteDatabase db = getReadableDatabase();
Cursor data = db.rawQuery("select * from " + LoyaltyCardDbGroups.TABLE + " g " +
" LEFT JOIN " + LoyaltyCardDbIdsGroups.TABLE + " ig ON ig." + LoyaltyCardDbIdsGroups.groupID + " = g." + LoyaltyCardDbGroups.ID +
" where " + LoyaltyCardDbIdsGroups.cardID + "=?" +
" ORDER BY " + LoyaltyCardDbIdsGroups.groupID, new String[]{String.format("%d", id)});
List<Group> groups = new ArrayList<>();
if (!data.moveToFirst()) {
return groups;
}
groups.add(Group.toGroup(data));
while (data.moveToNext()) {
groups.add(Group.toGroup(data));
}
return groups;
}
public void setLoyaltyCardGroups(final int id, List<Group> groups)
{
SQLiteDatabase db = getWritableDatabase();
// First delete lookup table entries associated with this card
db.delete(LoyaltyCardDbIdsGroups.TABLE,
LoyaltyCardDbIdsGroups.cardID + " = ? ",
new String[]{String.format("%d", id)});
// Then create entries for selected values
for (Group group : groups) {
ContentValues contentValues = new ContentValues();
contentValues.put(LoyaltyCardDbIdsGroups.cardID, id);
contentValues.put(LoyaltyCardDbIdsGroups.groupID, group._id);
db.insert(LoyaltyCardDbIdsGroups.TABLE, null, contentValues);
}
}
public void setLoyaltyCardGroups(final SQLiteDatabase db, final int id, List<Group> groups)
{
// First delete lookup table entries associated with this card
db.delete(LoyaltyCardDbIdsGroups.TABLE,
LoyaltyCardDbIdsGroups.cardID + " = ? ",
new String[]{String.format("%d", id)});
// Then create entries for selected values
for (Group group : groups) {
ContentValues contentValues = new ContentValues();
contentValues.put(LoyaltyCardDbIdsGroups.cardID, id);
contentValues.put(LoyaltyCardDbIdsGroups.groupID, group._id);
db.insert(LoyaltyCardDbIdsGroups.TABLE, null, contentValues);
}
}
public boolean deleteLoyaltyCard (final int id)
{
SQLiteDatabase db = getWritableDatabase();
int rowsDeleted = db.delete(LoyaltyCardDbIds.TABLE,
// Delete card
int rowsDeleted = db.delete(LoyaltyCardDbIds.TABLE,
LoyaltyCardDbIds.ID + " = ? ",
new String[]{String.format("%d", id)});
// And delete lookup table entries associated with this card
db.delete(LoyaltyCardDbIdsGroups.TABLE,
LoyaltyCardDbIdsGroups.cardID + " = ? ",
new String[]{String.format("%d", id)});
return (rowsDeleted == 1);
}
@@ -157,16 +280,52 @@ public class DBHelper extends SQLiteOpenHelper
* @return Cursor
*/
public Cursor getLoyaltyCardCursor(final String filter)
{
return getLoyaltyCardCursor(filter, null);
}
/**
* Returns a cursor to all loyalty cards with the filter text in either the store or note in a certain group.
*
* @param filter
* @param group
* @return Cursor
*/
public Cursor getLoyaltyCardCursor(final String filter, Group group)
{
String actualFilter = String.format("%%%s%%", filter);
String[] selectionArgs = { actualFilter, actualFilter };
StringBuilder groupFilter = new StringBuilder();
String limitString = "";
SQLiteDatabase db = getReadableDatabase();
if (group != null) {
List<Integer> allowedIds = getGroupCardIds(group._id);
// Empty group
if (allowedIds.size() > 0) {
groupFilter.append("AND (");
for (int i = 0; i < allowedIds.size(); i++) {
groupFilter.append(LoyaltyCardDbIds.ID + " = " + allowedIds.get(i));
if (i != allowedIds.size() - 1) {
groupFilter.append(" OR ");
}
}
groupFilter.append(") ");
} else {
limitString = "LIMIT 0";
}
}
Cursor res = db.rawQuery("select * from " + LoyaltyCardDbIds.TABLE +
" WHERE " + LoyaltyCardDbIds.STORE + " LIKE ? " +
" OR " + LoyaltyCardDbIds.NOTE + " LIKE ? " +
" ORDER BY " + LoyaltyCardDbIds.STORE + " COLLATE NOCASE ASC", selectionArgs, null);
" WHERE (" + LoyaltyCardDbIds.STORE + " LIKE ? " +
" OR " + LoyaltyCardDbIds.NOTE + " LIKE ? )" +
groupFilter.toString() +
" ORDER BY " + LoyaltyCardDbIds.STAR_STATUS + " DESC," + LoyaltyCardDbIds.STORE + " COLLATE NOCASE ASC " +
limitString, selectionArgs, null);
return res;
}
@@ -205,5 +364,172 @@ public class DBHelper extends SQLiteOpenHelper
return numItems;
}
/**
* Returns a cursor to all groups.
*
* @return Cursor
*/
public Cursor getGroupCursor()
{
SQLiteDatabase db = getReadableDatabase();
Cursor res = db.rawQuery("select * from " + LoyaltyCardDbGroups.TABLE +
" ORDER BY " + LoyaltyCardDbGroups.ID + " COLLATE NOCASE ASC", null, null);
return res;
}
public List<Group> getGroups() {
Cursor data = getGroupCursor();
List<Group> groups = new ArrayList<>();
if (!data.moveToFirst()) {
return groups;
}
groups.add(Group.toGroup(data));
while (data.moveToNext()) {
groups.add(Group.toGroup(data));
}
return groups;
}
public Group getGroup(final String groupName)
{
SQLiteDatabase db = getReadableDatabase();
Cursor data = db.rawQuery("select * from " + LoyaltyCardDbGroups.TABLE +
" where " + LoyaltyCardDbGroups.ID + "=?", new String[]{groupName});
Group group = null;
if(data.getCount() == 1)
{
data.moveToFirst();
group = Group.toGroup(data);
}
data.close();
return group;
}
public int getGroupCount()
{
SQLiteDatabase db = getReadableDatabase();
Cursor data = db.rawQuery("SELECT Count(*) FROM " + LoyaltyCardDbGroups.TABLE, null);
int numItems = 0;
if(data.getCount() == 1)
{
data.moveToFirst();
numItems = data.getInt(0);
}
data.close();
return numItems;
}
public List<Integer> getGroupCardIds(final String groupName)
{
SQLiteDatabase db = getReadableDatabase();
Cursor data = db.rawQuery("SELECT " + LoyaltyCardDbIdsGroups.cardID +
" FROM " + LoyaltyCardDbIdsGroups.TABLE +
" WHERE " + LoyaltyCardDbIdsGroups.groupID + " =? ", new String[]{groupName});
List<Integer> cardIds = new ArrayList<>();
if (!data.moveToFirst()) {
return cardIds;
}
cardIds.add(data.getInt(0));
while (data.moveToNext()) {
cardIds.add(data.getInt(0));
}
data.close();
return cardIds;
}
public long insertGroup(final String name)
{
if (name.isEmpty()) return -1;
SQLiteDatabase db = getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(LoyaltyCardDbGroups.ID, name);
final long newId = db.insert(LoyaltyCardDbGroups.TABLE, null, contentValues);
return newId;
}
public boolean insertGroup(final SQLiteDatabase db, final String name)
{
ContentValues contentValues = new ContentValues();
contentValues.put(LoyaltyCardDbGroups.ID, name);
final long newId = db.insert(LoyaltyCardDbGroups.TABLE, null, contentValues);
return (newId != -1);
}
public boolean updateGroup(final String groupName, final String newName)
{
if (newName.isEmpty()) return false;
SQLiteDatabase db = getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(LoyaltyCardDbGroups.ID, newName);
try {
int rowsUpdated = db.update(LoyaltyCardDbGroups.TABLE, contentValues,
LoyaltyCardDbGroups.ID + "=?",
new String[]{groupName});
return (rowsUpdated == 1);
} catch (android.database.sqlite.SQLiteConstraintException _e) {
return false;
}
}
public boolean deleteGroup(final String groupName)
{
SQLiteDatabase db = getWritableDatabase();
// Delete group
int rowsDeleted = db.delete(LoyaltyCardDbGroups.TABLE,
LoyaltyCardDbGroups.ID + " = ? ",
new String[]{groupName});
// And delete lookup table entries associated with this group
db.delete(LoyaltyCardDbIdsGroups.TABLE,
LoyaltyCardDbIdsGroups.groupID + " = ? ",
new String[]{groupName});
return (rowsDeleted == 1);
}
public int getGroupCardCount(final String groupName)
{
SQLiteDatabase db = getReadableDatabase();
Cursor data = db.rawQuery("SELECT Count(*) FROM " + LoyaltyCardDbIdsGroups.TABLE +
" where " + LoyaltyCardDbIdsGroups.groupID + "=?",
new String[]{groupName});
int numItems = 0;
if(data.getCount() == 1)
{
data.moveToFirst();
numItems = data.getInt(0);
}
data.close();
return numItems;
}
}

View File

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

View File

@@ -0,0 +1,53 @@
package protect.card_locker;
import android.content.Context;
import android.database.Cursor;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.TextView;
import protect.card_locker.preferences.Settings;
class GroupCursorAdapter extends CursorAdapter
{
Settings settings;
DBHelper db;
public GroupCursorAdapter(Context context, Cursor cursor)
{
super(context, cursor, 0);
settings = new Settings(context);
db = new DBHelper(context);
}
// The newView method is used to inflate a new view and return it,
// you don't bind any data to the view at this point.
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent)
{
return LayoutInflater.from(context).inflate(R.layout.group_layout, parent, false);
}
// The bindView method is used to bind all data to a given view
// such as setting the text on a TextView.
@Override
public void bindView(View view, Context context, Cursor cursor)
{
// Find fields to populate in inflated template
TextView nameField = (TextView) view.findViewById(R.id.name);
TextView countField = (TextView) view.findViewById(R.id.cardCount);
// Extract properties from cursor
Group group = Group.toGroup(cursor);
// Populate fields with extracted properties
nameField.setText(group._id);
countField.setText(String.format(context.getString(R.string.groupCardCount), db.getGroupCardCount(group._id)));
nameField.setTextSize(settings.getCardTitleListFontSize());
countField.setTextSize(settings.getCardNoteListFontSize());
}
}

View File

@@ -10,9 +10,12 @@ public class ImportURIHelper {
private static final String NOTE = DBHelper.LoyaltyCardDbIds.NOTE;
private static final String CARD_ID = DBHelper.LoyaltyCardDbIds.CARD_ID;
private static final String BARCODE_TYPE = DBHelper.LoyaltyCardDbIds.BARCODE_TYPE;
private static final String HEADER_COLOR = DBHelper.LoyaltyCardDbIds.HEADER_COLOR;
private static final String HEADER_TEXT_COLOR = DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR;
private final Context context;
private final String host;
private final String path;
@@ -47,6 +50,8 @@ public class ImportURIHelper {
String note = uri.getQueryParameter(NOTE);
String cardId = uri.getQueryParameter(CARD_ID);
String barcodeType = uri.getQueryParameter(BARCODE_TYPE);
if (store == null || note == null || cardId == null || barcodeType == null) throw new InvalidObjectException("Not a valid import URI");
String unparsedHeaderColor = uri.getQueryParameter(HEADER_COLOR);
if(unparsedHeaderColor != null)
{
@@ -57,7 +62,8 @@ public class ImportURIHelper {
{
headerTextColor = Integer.parseInt(unparsedHeaderTextColor);
}
return new LoyaltyCard(-1, store, note, cardId, barcodeType, headerColor, headerTextColor);
return new LoyaltyCard(-1, store, note, cardId, barcodeType, headerColor, headerTextColor, 0);
} catch (NullPointerException | NumberFormatException ex) {
throw new InvalidObjectException("Not a valid import URI");
}
@@ -81,7 +87,7 @@ public class ImportURIHelper {
{
uriBuilder.appendQueryParameter(HEADER_TEXT_COLOR, loyaltyCard.headerTextColor.toString());
}
//StarStatus will not be exported
return uriBuilder.build();
}

View File

@@ -1,6 +1,7 @@
package protect.card_locker;
import android.database.Cursor;
import androidx.annotation.Nullable;
public class LoyaltyCard
@@ -17,8 +18,11 @@ public class LoyaltyCard
@Nullable
public final Integer headerTextColor;
public final int starStatus;
public LoyaltyCard(final int id, final String store, final String note, final String cardId,
final String barcodeType, final Integer headerColor, final Integer headerTextColor)
final String barcodeType, final Integer headerColor, final Integer headerTextColor,
final int starStatus)
{
this.id = id;
this.store = store;
@@ -27,6 +31,7 @@ public class LoyaltyCard
this.barcodeType = barcodeType;
this.headerColor = headerColor;
this.headerTextColor = headerTextColor;
this.starStatus = starStatus;
}
public static LoyaltyCard toLoyaltyCard(Cursor cursor)
@@ -36,6 +41,8 @@ public class LoyaltyCard
String note = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE));
String cardId = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID));
String barcodeType = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE));
int starred = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS));
int headerColorColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_COLOR);
int headerTextColorColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR);
@@ -53,6 +60,6 @@ public class LoyaltyCard
headerTextColor = cursor.getInt(headerTextColorColumn);
}
return new LoyaltyCard(id, store, note, cardId, barcodeType, headerColor, headerTextColor);
return new LoyaltyCard(id, store, note, cardId, barcodeType, headerColor, headerTextColor, starred);
}
}

View File

@@ -2,23 +2,27 @@ package protect.card_locker;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import protect.card_locker.preferences.Settings;
class LoyaltyCardCursorAdapter extends CursorAdapter
{
Settings settings;
boolean darkModeEnabled;
public LoyaltyCardCursorAdapter(Context context, Cursor cursor)
{
super(context, cursor, 0);
settings = new Settings(context);
darkModeEnabled= MainActivity.isDarkModeEnabled(context);
}
// The newView method is used to inflate a new view and return it,
@@ -36,8 +40,14 @@ class LoyaltyCardCursorAdapter extends CursorAdapter
{
// Find fields to populate in inflated template
ImageView thumbnail = view.findViewById(R.id.thumbnail);
TextView storeField = (TextView) view.findViewById(R.id.store);
TextView noteField = (TextView) view.findViewById(R.id.note);
TextView storeField = view.findViewById(R.id.store);
TextView noteField = view.findViewById(R.id.note);
ImageView star = view.findViewById(R.id.star);
if(darkModeEnabled)
{
star.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP);
}
// Extract properties from cursor
LoyaltyCard loyaltyCard = LoyaltyCard.toLoyaltyCard(cursor);
@@ -47,7 +57,7 @@ class LoyaltyCardCursorAdapter extends CursorAdapter
storeField.setTextSize(settings.getCardTitleListFontSize());
if(loyaltyCard.note.isEmpty() == false)
if(!loyaltyCard.note.isEmpty())
{
noteField.setVisibility(View.VISIBLE);
noteField.setText(loyaltyCard.note);
@@ -58,6 +68,9 @@ class LoyaltyCardCursorAdapter extends CursorAdapter
noteField.setVisibility(View.GONE);
}
if (loyaltyCard.starStatus!=0) star.setVisibility(View.VISIBLE);
else star.setVisibility(View.GONE);
int tileLetterFontSize = context.getResources().getDimensionPixelSize(R.dimen.tileLetterFontSize);
int pixelSize = context.getResources().getDimensionPixelSize(R.dimen.cardThumbnailSize);

View File

@@ -9,6 +9,8 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import com.google.android.material.chip.Chip;
import com.google.android.material.chip.ChipGroup;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import androidx.appcompat.app.ActionBar;
@@ -33,6 +35,8 @@ import com.jaredrummler.android.colorpicker.ColorPickerDialog;
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener;
import java.io.InvalidObjectException;
import java.util.ArrayList;
import java.util.List;
public class LoyaltyCardEditActivity extends AppCompatActivity
{
@@ -43,6 +47,7 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
EditText storeFieldEdit;
EditText noteFieldEdit;
ChipGroup groupsChips;
ImageView headingColorSample;
Button headingColorSelectButton;
ImageView headingStoreTextColorSample;
@@ -99,6 +104,7 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
storeFieldEdit = findViewById(R.id.storeNameEdit);
noteFieldEdit = findViewById(R.id.noteEdit);
groupsChips = findViewById(R.id.groupChips);
headingColorSample = findViewById(R.id.headingColorSample);
headingColorSelectButton = findViewById(R.id.headingColorSelectButton);
headingStoreTextColorSample = findViewById(R.id.headingStoreTextColorSample);
@@ -213,6 +219,40 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
hideBarcode();
}
if(groupsChips.getChildCount() == 0)
{
List<Group> existingGroups = db.getGroups();
List<Group> loyaltyCardGroups = db.getLoyaltyCardGroups(loyaltyCardId);
View groupsView = findViewById(R.id.groupsView);
View groupsTableRow = findViewById(R.id.groupsTableRow);
if (existingGroups.isEmpty()) {
groupsView.setVisibility(View.GONE);
groupsTableRow.setVisibility(View.GONE);
} else {
groupsView.setVisibility(View.VISIBLE);
groupsTableRow.setVisibility(View.VISIBLE);
}
for (Group group : db.getGroups()) {
Chip chip = (Chip) getLayoutInflater().inflate(R.layout.layout_chip_choice, groupsChips, false);
chip.setText(group._id);
chip.setTag(group);
chip.setChecked(false);
for (Group loyaltyCardGroup : loyaltyCardGroups) {
if (loyaltyCardGroup._id.equals(group._id)) {
chip.setChecked(true);
break;
}
}
groupsChips.addView(chip);
}
}
if(headingColorValue == null)
{
// Select a random color to start out with.
@@ -404,16 +444,25 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
return;
}
List<Group> selectedGroups = new ArrayList<>();
for (Integer chipId : groupsChips.getCheckedChipIds()) {
Chip chip = groupsChips.findViewById(chipId);
selectedGroups.add((Group) chip.getTag());
}
if(updateLoyaltyCard)
{
{ //update of "starStatus" not necessary, since it cannot be changed in this activity (only in ViewActivity)
db.updateLoyaltyCard(loyaltyCardId, store, note, cardId, barcodeType, headingColorValue, headingStoreTextColorValue);
Log.i(TAG, "Updated " + loyaltyCardId + " to " + cardId);
}
else
{
loyaltyCardId = (int)db.insertLoyaltyCard(store, note, cardId, barcodeType, headingColorValue, headingStoreTextColorValue);
loyaltyCardId = (int)db.insertLoyaltyCard(store, note, cardId, barcodeType, headingColorValue, headingStoreTextColorValue, 0);
}
db.setLoyaltyCardGroups(loyaltyCardId, selectedGroups);
finish();
}

View File

@@ -1,6 +1,5 @@
package protect.card_locker;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Color;
@@ -34,7 +33,6 @@ import com.google.zxing.BarcodeFormat;
import protect.card_locker.preferences.Settings;
public class LoyaltyCardViewActivity extends AppCompatActivity
{
private static final String TAG = "Catima";
@@ -56,6 +54,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
String cardIdString;
BarcodeFormat format;
boolean starred;
boolean backgroundNeedsDarkIcons;
boolean barcodeIsFullscreen = false;
ViewGroup.LayoutParams barcodeImageState;
@@ -317,11 +316,29 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
item.setVisible(false);
}
loyaltyCard = db.getLoyaltyCard(loyaltyCardId);
starred = loyaltyCard.starStatus != 0;
menu.findItem(R.id.action_share).setIcon(getIcon(R.drawable.ic_share_white, backgroundNeedsDarkIcons));
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
if (starred) {
menu.findItem(R.id.action_star_unstar).setIcon(getIcon(R.drawable.ic_starred_white, backgroundNeedsDarkIcons));
menu.findItem(R.id.action_star_unstar).setTitle(R.string.unstar);
}
else {
menu.findItem(R.id.action_star_unstar).setIcon(getIcon(R.drawable.ic_unstarred_white, backgroundNeedsDarkIcons));
menu.findItem(R.id.action_star_unstar).setTitle(R.string.star);
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
@@ -348,11 +365,18 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
}
rotationEnabled = !rotationEnabled;
return true;
case R.id.action_star_unstar:
starred = !starred;
db.updateLoyaltyCardStarStatus(loyaltyCardId, starred ? 1 : 0);
invalidateOptionsMenu();
return true;
}
return super.onOptionsItemSelected(item);
}
private void setOrientatonLock(MenuItem item, boolean lock)
{
if(lock)
@@ -366,7 +390,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
{
item.setIcon(getIcon(R.drawable.ic_lock_open_white_24dp, backgroundNeedsDarkIcons));
item.setTitle(R.string.lockScreen);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
}

View File

@@ -2,11 +2,10 @@ package protect.card_locker;
import android.app.SearchManager;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ClipboardManager;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
@@ -14,7 +13,6 @@ import android.database.Cursor;
import android.os.Bundle;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.appcompat.widget.SearchView;
import androidx.appcompat.widget.Toolbar;
import android.util.Log;
@@ -28,13 +26,13 @@ import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.tabs.TabLayout;
import com.google.common.collect.ImmutableMap;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import protect.card_locker.preferences.SettingsActivity;
public class MainActivity extends AppCompatActivity
@@ -44,6 +42,7 @@ public class MainActivity extends AppCompatActivity
private Menu menu;
protected String filter = "";
protected int selectedTab = 0;
@Override
protected void onCreate(Bundle savedInstanceState)
@@ -53,7 +52,26 @@ public class MainActivity extends AppCompatActivity
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
updateLoyaltyCardList("");
updateLoyaltyCardList(filter, null);
TabLayout groupsTabLayout = findViewById(R.id.groups);
groupsTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
selectedTab = tab.getPosition();
updateLoyaltyCardList(filter, tab.getTag());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
@Override
@@ -69,7 +87,22 @@ public class MainActivity extends AppCompatActivity
}
}
updateLoyaltyCardList(filter);
TabLayout groupsTabLayout = findViewById(R.id.groups);
boolean hasReset = updateTabGroups(groupsTabLayout);
Object group = null;
if (groupsTabLayout.getTabCount() != 0) {
TabLayout.Tab tab = groupsTabLayout.getTabAt(0);
if (!hasReset) {
tab = groupsTabLayout.getTabAt(selectedTab);
}
groupsTabLayout.selectTab(tab);
group = tab.getTag();
}
updateLoyaltyCardList(filter, group);
FloatingActionButton addButton = findViewById(R.id.fabAdd);
addButton.setOnClickListener(new View.OnClickListener() {
@@ -114,17 +147,31 @@ public class MainActivity extends AppCompatActivity
if (!searchView.isIconified()) {
searchView.setIconified(true);
} else {
super.onBackPressed();
TabLayout groupsTabLayout = findViewById(R.id.groups);
if (groupsTabLayout.getVisibility() == View.VISIBLE && selectedTab != 0) {
selectedTab = 0;
groupsTabLayout.selectTab(groupsTabLayout.getTabAt(0));
} else {
super.onBackPressed();
}
}
}
private void updateLoyaltyCardList(String filterText)
private void updateLoyaltyCardList(String filterText, Object tag)
{
Group group = null;
if (tag != null) {
group = (Group) tag;
}
final ListView cardList = findViewById(R.id.list);
final TextView helpText = findViewById(R.id.helpText);
final TextView noMatchingCardsText = findViewById(R.id.noMatchingCardsText);
final DBHelper db = new DBHelper(this);
Cursor cardCursor = db.getLoyaltyCardCursor(filterText, group);
if(db.getLoyaltyCardCount() > 0)
{
// We want the cardList to be visible regardless of the filtered match count
@@ -132,7 +179,7 @@ public class MainActivity extends AppCompatActivity
// the keyboard
cardList.setVisibility(View.VISIBLE);
helpText.setVisibility(View.GONE);
if(db.getLoyaltyCardCount(filterText) > 0)
if(cardCursor.getCount() > 0)
{
noMatchingCardsText.setVisibility(View.GONE);
}
@@ -148,8 +195,6 @@ public class MainActivity extends AppCompatActivity
noMatchingCardsText.setVisibility(View.GONE);
}
Cursor cardCursor = db.getLoyaltyCardCursor(filterText);
final LoyaltyCardCursorAdapter adapter = new LoyaltyCardCursorAdapter(this, cardCursor);
cardList.setAdapter(adapter);
@@ -176,6 +221,53 @@ public class MainActivity extends AppCompatActivity
});
}
public boolean updateTabGroups(TabLayout groupsTabLayout)
{
final DBHelper db = new DBHelper(this);
List<Group> newGroups = db.getGroups();
if (newGroups.size() == 0) {
groupsTabLayout.removeAllTabs();
groupsTabLayout.setVisibility(View.GONE);
return true;
}
// -1 because there is an "All" tab
boolean isChanged = groupsTabLayout.getTabCount() - 1 != newGroups.size();
if (!isChanged) {
for (int i = 0; i < newGroups.size(); i++) {
if (!((Group) groupsTabLayout.getTabAt(i + 1).getTag())._id.equals(newGroups.get(i)._id)) {
isChanged = true;
break;
}
}
}
if (isChanged) {
groupsTabLayout.removeAllTabs();
TabLayout.Tab allTab = groupsTabLayout.newTab();
allTab.setText(R.string.all);
allTab.setTag(null);
groupsTabLayout.addTab(allTab);
for (Group group : newGroups) {
TabLayout.Tab tab = groupsTabLayout.newTab();
tab.setText(group._id);
tab.setTag(group);
groupsTabLayout.addTab(tab);
}
groupsTabLayout.setVisibility(View.VISIBLE);
return true;
}
return false;
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo)
{
@@ -196,23 +288,20 @@ public class MainActivity extends AppCompatActivity
Cursor cardCursor = (Cursor)listView.getItemAtPosition(info.position);
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cardCursor);
if(card != null)
if(item.getItemId() == R.id.action_clipboard)
{
if(item.getItemId() == R.id.action_clipboard)
{
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText(card.store, card.cardId);
clipboard.setPrimaryClip(clip);
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText(card.store, card.cardId);
clipboard.setPrimaryClip(clip);
Toast.makeText(this, R.string.copy_to_clipboard_toast, Toast.LENGTH_LONG).show();
return true;
}
else if(item.getItemId() == R.id.action_share)
{
final ImportURIHelper importURIHelper = new ImportURIHelper(this);
importURIHelper.startShareIntent(card);
return true;
}
Toast.makeText(this, R.string.copy_to_clipboard_toast, Toast.LENGTH_LONG).show();
return true;
}
else if(item.getItemId() == R.id.action_share)
{
final ImportURIHelper importURIHelper = new ImportURIHelper(this);
importURIHelper.startShareIntent(card);
return true;
}
return super.onContextItemSelected(item);
@@ -248,7 +337,10 @@ public class MainActivity extends AppCompatActivity
@Override
public boolean onQueryTextChange(String newText) {
filter = newText;
updateLoyaltyCardList(newText);
TabLayout groupsTabLayout = findViewById(R.id.groups);
updateLoyaltyCardList(newText, groupsTabLayout.getTabAt(groupsTabLayout.getSelectedTabPosition()).getTag());
return true;
}
});
@@ -262,6 +354,13 @@ public class MainActivity extends AppCompatActivity
{
int id = item.getItemId();
if (id == R.id.action_manage_groups)
{
Intent i = new Intent(getApplicationContext(), ManageGroupsActivity.class);
startActivityForResult(i, MAIN_REQUEST_CODE);
return true;
}
if(id == R.id.action_import_export)
{
Intent i = new Intent(getApplicationContext(), ImportExportActivity.class);
@@ -334,9 +433,7 @@ public class MainActivity extends AppCompatActivity
// Set CSS for dark mode if dark mode
String css = "";
Configuration config = getResources().getConfiguration();
int currentNightMode = config.uiMode & Configuration.UI_MODE_NIGHT_MASK;
if(currentNightMode == Configuration.UI_MODE_NIGHT_YES)
if(isDarkModeEnabled(this))
{
css = "<style>body {color:white; background-color:black;}</style>";
}
@@ -382,4 +479,12 @@ public class MainActivity extends AppCompatActivity
})
.show();
}
protected static boolean isDarkModeEnabled(Context inputContext)
{
Configuration config = inputContext.getResources().getConfiguration();
int currentNightMode = config.uiMode & Configuration.UI_MODE_NIGHT_MASK;
return (currentNightMode == Configuration.UI_MODE_NIGHT_YES);
}
}

View File

@@ -0,0 +1,186 @@
package protect.card_locker;
import android.content.DialogInterface;
import android.database.Cursor;
import android.os.Bundle;
import android.text.InputType;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
public class ManageGroupsActivity extends AppCompatActivity
{
private static final String TAG = "Catima";
private AlertDialog newGroupDialog;
private final DBHelper db = new DBHelper(this);
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.manage_groups_activity);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
{
actionBar.setDisplayHomeAsUpEnabled(true);
}
newGroupDialog = createNewGroupDialog();
updateGroupList();
}
@Override
protected void onResume() {
super.onResume();
updateGroupList();
FloatingActionButton addButton = findViewById(R.id.fabAdd);
addButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
newGroupDialog.show();
}
});
}
@Override
public void onBackPressed() {
super.onBackPressed();
}
private void updateGroupList()
{
final ListView groupList = findViewById(R.id.list);
final TextView helpText = findViewById(R.id.helpText);
final DBHelper db = new DBHelper(this);
if(db.getGroupCount() > 0)
{
groupList.setVisibility(View.VISIBLE);
helpText.setVisibility(View.GONE);
}
else
{
groupList.setVisibility(View.GONE);
helpText.setVisibility(View.VISIBLE);
}
Cursor groupCursor = db.getGroupCursor();
final GroupCursorAdapter adapter = new GroupCursorAdapter(this, groupCursor);
groupList.setAdapter(adapter);
registerForContextMenu(groupList);
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
int id = item.getItemId();
if (id == android.R.id.home) {
finish();
}
return super.onOptionsItemSelected(item);
}
public void editGroup(View view) {
LinearLayout parentRow = (LinearLayout) view.getParent();
TextView groupNameTextView = (TextView) parentRow.findViewById(R.id.name);
final String groupName = (String) groupNameTextView.getText();
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.enter_group_name);
final EditText input = new EditText(this);
input.setInputType(InputType.TYPE_CLASS_TEXT);
input.setText(groupName);
builder.setView(input);
builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
db.updateGroup(groupName, input.getText().toString());
updateGroupList();
}
});
builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
public void deleteGroup(View view) {
LinearLayout parentRow = (LinearLayout) view.getParent();
TextView groupNameTextView = (TextView) parentRow.findViewById(R.id.name);
final String groupName = (String) groupNameTextView.getText();
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.deleteConfirmationGroup);
builder.setMessage(groupName);
builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
db.deleteGroup(groupName);
updateGroupList();
}
});
builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
private AlertDialog createNewGroupDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.enter_group_name);
final EditText input = new EditText(this);
input.setInputType(InputType.TYPE_CLASS_TEXT);
builder.setView(input);
builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
db.insertGroup(input.getText().toString());
updateGroupList();
}
});
builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
AlertDialog dialog = builder.create();
return dialog;
}
}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 519 B

After

Width:  |  Height:  |  Size: 368 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 303 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 651 B

After

Width:  |  Height:  |  Size: 466 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 829 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 809 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 853 B

After

Width:  |  Height:  |  Size: 606 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 758 B

View File

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

View File

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

View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:baselineAligned="false"
android:padding="@dimen/activity_margin">
<LinearLayout
android:orientation="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/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/storeNameTextSize"
android:textStyle="bold"/>
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/cardCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:ellipsize="end"
android:textSize="@dimen/noteTextSize"/>
</LinearLayout>
<ImageButton
android:id="@+id/edit"
android:layout_width="@dimen/cardThumbnailSize"
android:layout_height="@dimen/cardThumbnailSize"
android:layout_marginLeft="@dimen/activity_margin"
android:src="@drawable/ic_mode_edit_white_24dp"
android:contentDescription="@string/edit"
app:tint="#000000"
android:onClick="editGroup" />
<ImageButton
android:id="@+id/delete"
android:layout_width="@dimen/cardThumbnailSize"
android:layout_height="@dimen/cardThumbnailSize"
android:layout_marginLeft="@dimen/activity_margin"
android:src="@drawable/ic_delete_white_24dp"
android:contentDescription="@string/delete"
app:tint="#000000"
android:onClick="deleteGroup"/>
</LinearLayout>

View File

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

View File

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

View File

@@ -145,6 +145,62 @@
android:background="@color/inputBorder" />
</TableRow>
<!-- Groups -->
<View
android:id="@+id/groupsView"
android:layout_height="@dimen/inputBorderThickness"
android:layout_width="match_parent"
android:background="@color/inputBorder"
android:visibility="gone"/>
<TableRow
android:id="@+id/groupsTableRow"
android:background="@color/inputBackground"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone">
<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/groupsField"
android:text="@string/groups"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:textSize="@dimen/inputSize"
android:padding="@dimen/inputPadding"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
/>
<com.google.android.material.chip.ChipGroup
android:id="@+id/groupChips"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:padding="@dimen/inputPadding"
android:textSize="@dimen/inputSize"
android:layout_toEndOf="@id/groupsField"
android:layout_toRightOf="@id/groupsField"
/>
</RelativeLayout>
<View
android:gravity="end"
android:layout_height="match_parent"
android:layout_width="@dimen/inputBorderThickness"
android:background="@color/inputBorder" />
</TableRow>
<!-- Store Header Background Color -->
<View
android:layout_height="@dimen/inputBorderThickness"
@@ -432,11 +488,13 @@
android:layout_height="wrap_content"
android:id="@+id/barcodeCaptureLayout">
<Button android:id="@+id/captureButton"
android:layout_margin="@dimen/inputMargin"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/capture"
android:layout_weight="1.0"/>
<Button android:id="@+id/enterButton"
android:layout_margin="@dimen/inputMargin"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/enterCard"

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -8,14 +8,22 @@
android:baselineAligned="false"
android:padding="@dimen/activity_margin">
<ImageView
android:id="@+id/thumbnail"
<androidx.cardview.widget.CardView
android:layout_width="@dimen/cardThumbnailSize"
android:layout_height="@dimen/cardThumbnailSize"
android:layout_marginEnd="@dimen/activity_margin"
android:layout_marginRight="@dimen/activity_margin"
android:src="@mipmap/ic_launcher"
android:contentDescription="@string/thumbnailDescription"/>
app:cardCornerRadius="4dp"
app:cardElevation="0dp">
<ImageView
android:id="@+id/thumbnail"
android:layout_width="@dimen/cardThumbnailSize"
android:layout_height="@dimen/cardThumbnailSize"
android:contentDescription="@string/thumbnailDescription"
android:src="@mipmap/ic_launcher"/>
</androidx.cardview.widget.CardView>
<LinearLayout
android:orientation="vertical"
@@ -52,5 +60,12 @@
android:ellipsize="end"
android:textSize="@dimen/noteTextSize"/>
</LinearLayout>
<ImageView
android:id="@+id/star"
android:layout_width="@dimen/cardThumbnailSize"
android:layout_height="@dimen/cardThumbnailSize"
android:layout_marginLeft="@dimen/activity_margin"
android:src="@drawable/ic_starred_white"
android:tint="#000000"
android:contentDescription="@string/starImage"/>
</LinearLayout>

View File

@@ -29,6 +29,13 @@
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay"/>
<com.google.android.material.tabs.TabLayout
android:id="@+id/groups"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="scrollable"
android:visibility="gone"/>
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/content_main"/>

View File

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

View File

@@ -12,4 +12,9 @@
android:icon="@drawable/ic_share_white"
android:title="@string/share"
app:showAsAction="always"/>
<item
android:id="@+id/action_star_unstar"
android:icon="@drawable/ic_unstarred_white"
android:title="@string/star"
app:showAsAction="always" />
</menu>

View File

@@ -8,6 +8,11 @@
android:icon="@drawable/ic_search_white"
app:actionViewClass="androidx.appcompat.widget.SearchView"
app:showAsAction="always|collapseActionView"/>
<item
android:id="@+id/action_manage_groups"
android:icon="@drawable/ic_folder_white"
android:title="@string/groups"
app:showAsAction="always"/>
<item
android:id="@+id/action_import_export"
android:icon="@drawable/ic_import_export_white_24dp"

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -47,7 +47,6 @@
<string name="importOptionApplicationButton">Použít externí aplikaci</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>

View File

@@ -1,12 +1,9 @@
<resources
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_search">Suche</string>
<string name="action_add">Neu</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="noMatchingGiftCards">Es passen keine Kundenkarten zum Suchfilter. Bitte probiere verschiedene Begriffe aus.</string>
<string name="storeName">Geschäft</string>
<string name="note">Notiz</string>
<string name="cardId">Kartennummer</string>
@@ -20,19 +17,19 @@
<string name="confirm">Bestätigen</string>
<string name="lockScreen">Rotation blockieren</string>
<string name="unlockScreen">Rotation zulassen</string>
<string name="star">Zu den Favoriten hinzufügen</string>
<string name="unstar">Aus der Favoritenliste entfernen</string>
<string name="deleteTitle">Karte entfernen</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="sendLabel">Senden</string>
<string name="editCardTitle">Kundenkarte bearbeiten</string>
<string name="addCardTitle">Neue Kundenkarte</string>
<string name="scanCardBarcode">Barcode scannen</string>
<string name="scanCardBarcode">Strichcode scannen</string>
<string name="cardShortcut">Shortcut zu einer Karte</string>
<string name="noCardsMessage">Es ist noch keine Karte vorhanden, bitte zuerst eine hinzufügen</string>
<string name="barcodeImageDescription">Bild des Barcodes</string>
<string name="barcodeImageDescription">Bild des Strichcodes</string>
<string name="noStoreError">Kein Geschäft angegeben</string>
<string name="noCardIdError">Keine Kartennummer angegeben</string>
<string name="noCardExistsError">Karte konnte nicht gefunden werden</string>
@@ -48,34 +45,28 @@
<string name="exportFailed">Export fehlgeschlagen</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="noExternalStoragePermissionError">Ohne die Berechtigung für den externen Speicher kann kein Import oder Export erfolgen</string>
<string name="importOptionFilesystemTitle">Importiere aus Dateisystem</string>
<string name="importOptionFilesystemExplanation">Wähle eine Datei aus dem Speicher aus.</string>
<string name="importOptionFilesystemButton">Aus Dateisystem</string>
<string name="importOptionApplicationTitle">Externe App verwenden</string>
<string name="importOptionApplicationExplanation">Wählen Sie eine Datei aus einer App wie Dropbox, Google Drive, oder Ihrem bevorzugten Dateisystem aus.</string>
<string name="importOptionApplicationButton">Nutze eine externe App</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">Informationen zu dieser Version: <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="selectBarcodeTitle">Strichcode auswählen</string>
<string name="copy_to_clipboard_toast">Nummer in die Zwischenablage kopiert</string>
<string name="thumbnailDescription">Vorschaubild für die Karte</string>
<string name="change">Anpassen</string>
<string name="storeTextColorTitle">Textfarbe</string>
<string name="storeTextBackgroundColorTitle">Hintergrundfarbe</string>
<string name="storeNameBackgroundColorDescription">Hintergrundfarbe für den Geschäftsnamen</string>
<string name="storeNameColorDescription">Farbe für den Geschäftsnamen</string>
<string name="settings">Einstellungen</string>
<string name="settings_category_title_ui">Benutzeroberfläche</string>
<string name="settings_card_title_list_font_size">Schriftgröße des Titels in der Listenansicht</string>
@@ -83,6 +74,21 @@
<string name="settings_card_title_font_size">Schriftgröße des Titels</string>
<string name="settings_card_id_font_size">Schriftgröße der Kartennummer</string>
<string name="settings_card_note_font_size">Schriftgröße der Notiz</string>
<string name="settings_display_barcode_max_brightness">Helligkeit bei Barcode Ansicht erhöhen</string>
<string name="settings_lock_barcode_orientation">Barcodeausrichtung sperren</string>
</resources>
<string name="settings_display_barcode_max_brightness">Helligkeit bei Strichcode Ansicht erhöhen</string>
<string name="settings_lock_barcode_orientation">Strichcodeausrichtung sperren</string>
<string name="exportSuccessful">Kundenkartendaten erflogreich exportiert</string>
<string name="importSuccessful">Kundenkartendaten erflogreich importiert</string>
<string name="intent_import_card_from_url_share_text">Ich will eine Karte mit Ihnen teilen</string>
<string name="settings_dark_theme">Dunkel</string>
<string name="settings_light_theme">Hell</string>
<string name="settings_system_theme">Systemvorgabe</string>
<string name="settings_theme">Design</string>
<string name="enterBarcodeInstructions">Geben Sie die Karten-ID ein und wählen Sie das Bild aus, das den Strichcode darstellt, den Sie verwenden möchten, oder wählen Sie „Diese Karte hat keinen Strichcode“, um keinen Strichcode zu verwenden.</string>
<string name="app_copyright_old">Basiert auf Loyalty Card Keychain, Copyright 2016-2020 Branden Archer.</string>
<string name="exportOptionExplanation">Die Daten werden an einen Ort Ihrer Wahl geschrieben.</string>
<string name="failedParsingImportUriError">Der Import-URI konnte nicht analysiert werden</string>
<string name="addedShortcut">Verknüpfung hinzugefügt</string>
<string name="share">Teilen</string>
<string name="barcodeNoBarcode">Diese Karte hat keinen Strichcode</string>
<string name="barcodeType">Strichcode-Typ</string>
</resources>

View File

@@ -54,7 +54,6 @@
<string name="importOptionApplicationButton">Χρήση εξωτερικής εφαρμογής</string>
<string name="about">Σχετικά</string>
<string name="app_copyright_fmt">Copyright 2016-<xliff:g>%d</xliff:g> Branden Archer</string>
<string name="app_license">Άδεια χρήσης υπό το GPLv3.</string>
<string name="about_title_fmt">Σχετικά με <xliff:g id="app_name">%s</xliff:g></string>
<string name="debug_version_fmt">Έκδοση: <xliff:g id="version">%s</xliff:g></string>

View File

@@ -1,83 +1,88 @@
<resources
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_add">Añadir</string>
<string name="noGiftCards">Actualmente no tienes ninguna tarjeta guardada. Presiona el botón \"+\" para comenzar.\n\nEsta cartera te permite llevar tus tarjetas de fidelización en tu teléfono para que estén siempre a tu alcance.</string>
<string name="noGiftCards">Actualmente no tiene ninguna tarjeta guardada. Pulse el botón «+» para comenzar.
\n
\nCatima le permite llevar sus tarjetas en el teléfono para que estén siempre a su alcance.</string>
<string name="storeName">Tienda</string>
<string name="note">Nota</string>
<string name="cardId">ID de la Tarjeta</string>
<string name="cardId">Id. de tarjeta</string>
<string name="cancel">Cancelar</string>
<string name="save">Guardar</string>
<string name="capture">Escanear Tarjeta</string>
<string name="enterCard">Introducir Tarjeta</string>
<string name="editCard">Editar Tarjeta</string>
<string name="capture">Capturar tarjeta</string>
<string name="enterCard">Introducir tarjeta</string>
<string name="editCard">Editar tarjeta</string>
<string name="edit">Editar</string>
<string name="delete">Eliminar</string>
<string name="confirm">Confirmar</string>
<string name="lockScreen">Bloquear Rotación de Pantalla</string>
<string name="unlockScreen">Rotación de Pantalla Automática</string>
<string name="deleteTitle">Eliminar Tarjeta de Fidelización</string>
<string name="deleteConfirmation">Por favor, confirma si quieres eliminar esta tarjeta.</string>
<string name="ok">OK</string>
<string name="copy_to_clipboard">Copiar ID al portapapeles</string>
<string name="sendLabel">Enviar&#8230;</string>
<string name="editCardTitle">Editar Tarjeta de Fidelización</string>
<string name="addCardTitle">Añadir Tarjeta de Fidelización</string>
<string name="scanCardBarcode">Escanear el Código de Barras de la Tarjeta</string>
<string name="cardShortcut">Atajo de Tarjeta</string>
<string name="noCardsMessage">No hay ninguna tarjeta, añade una primero</string>
<string name="lockScreen">Bloquear rotación</string>
<string name="unlockScreen">Desbloquear rotación</string>
<string name="deleteTitle">Eliminar tarjeta</string>
<string name="deleteConfirmation">Confirme que quiere eliminar esta tarjeta.</string>
<string name="ok">Aceptar</string>
<string name="copy_to_clipboard">Copiar id. en portapapeles</string>
<string name="sendLabel">Enviar</string>
<string name="editCardTitle">Editar tarjeta</string>
<string name="addCardTitle">Añadir tarjeta</string>
<string name="scanCardBarcode">Escanear código de barras de la tarjeta</string>
<string name="cardShortcut">Atajo de tarjeta</string>
<string name="noCardsMessage">No hay ninguna tarjeta; añada una primero</string>
<string name="barcodeImageDescription">Imagen del código de barras de la tarjeta</string>
<string name="noStoreError">Establecimiento no especificado</string>
<string name="noCardIdError">ID de la Tarjeta no especificado</string>
<string name="noCardIdError">Id. de tarjeta no especificado</string>
<string name="noCardExistsError">No se ha podido encontrar la tarjeta de fidelización</string>
<string name="cardIdFormat">%1$s: %2$s</string>
<string name="importExport">Importar/Exportar</string>
<string name="importExport">Importar/exportar</string>
<string name="exportName">Exportar</string>
<string name="importExportHelp">La copia de seguridad te permite transferir tus tarjetas a otro dispositivo.</string>
<string name="importExportHelp">La copia de respaldo le permite transferir sus tarjetas a otro dispositivo.</string>
<string name="importSuccessfulTitle">Datos importados correctamente</string>
<string name="importFailedTitle">Ha ocurrido un error al importar los datos</string>
<string name="importFailed">Error al importar</string>
<string name="importFailedTitle">Falló la importación</string>
<string name="importFailed">No se pudo importar</string>
<string name="exportSuccessfulTitle">Datos exportados correctamente</string>
<string name="exportFailedTitle">Ha ocurrido un error al exportar los datos</string>
<string name="exportFailed">Error al exportar</string>
<string name="importing">Importando&#8230;</string>
<string name="exporting">Exportando&#8230;</string>
<string name="exportFailedTitle">Falló la exportación</string>
<string name="exportFailed">No se pudo exportar</string>
<string name="importing">Importando</string>
<string name="exporting">Exportando</string>
<string name="noExternalStoragePermissionError">No se pueden importar o exportar tarjetas sin el permiso de almacenamiento</string>
<string name="importOptionFilesystemTitle">Importar desde el sistema de archivos</string>
<string name="importOptionFilesystemExplanation">Seleccionar un archivo del sistema de archivos</string>
<string name="importOptionFilesystemExplanation">Elegir un archivo concreto del sistema de archivos.</string>
<string name="importOptionFilesystemButton">Desde el sistema de archivos</string>
<string name="importOptionApplicationTitle">Usar una applicación externa</string>
<string name="importOptionApplicationTitle">Utilizar aplicación externa</string>
<string name="importOptionApplicationExplanation">Use una aplicación externa como Dropbox, Google Drive o tu gestor de archivos favoritos para abrir un archivo.</string>
<string name="importOptionApplicationButton">Usar aplicación externa</string>
<string name="importOptionApplicationButton">Utilizar aplicación externa</string>
<string name="about">Acerca de</string>
<string name="app_copyright_fmt">Copyright 2016-<xliff:g>%d</xliff:g> Branden Archer</string>
<string name="app_license">Licencia GPLv3</string>
<string name="app_license">Disponible en virtud de la GPLv3.</string>
<string name="about_title_fmt">Acerca de <xliff:g id="app_name">%s</xliff:g></string>
<string name="debug_version_fmt">Versión: <xliff:g id="version">%s</xliff:g></string>
<string name="app_revision_fmt">Información sobre la Revisión: <xliff:g id="app_revision_url">%s</xliff:g></string>
<string name="app_libraries"><xliff:g id="app_name">%s</xliff:g> utiliza las siguientes librerías de terceros: <xliff:g id="app_libraries_list">%s</xliff:g></string>
<string name="app_resources"><xliff:g id="app_name">%s</xliff:g> utiliza los siguientes recursos de terceros: <xliff:g id="app_resources_list">%s</xliff:g></string>
<string name="selectBarcodeTitle">Selecciona el Código de Barras</string>
<string name="copy_to_clipboard_toast">ID de la Tarjeta copiado al portapapeles</string>
<string name="thumbnailDescription">Miniatura para la tarjeta</string>
<string name="change">Cambiar</string>
<string name="storeTextColorTitle">Color del Texto de la Tienda</string>
<string name="storeTextBackgroundColorTitle">Color Principal</string>
<string name="storeTextColorTitle">Color de texto de establecimiento</string>
<string name="storeTextBackgroundColorTitle">Color principal</string>
<string name="storeNameBackgroundColorDescription">Color de fondo para el texto de la tienda</string>
<string name="storeNameColorDescription">Color del texto de la tienda</string>
<string name="settings">Configuración</string>
<string name="settings_category_title_ui">Interfaz de Usuario</string>
<string name="settings_category_title_ui">Interfaz de usuario</string>
<string name="settings_card_title_list_font_size">Tamaño de la letra para el título (lista)</string>
<string name="settings_card_note_list_font_size">Tamaño de la letra para notas (lista)</string>
<string name="settings_card_title_font_size">Tamaño de la letra para el título</string>
<string name="settings_card_id_font_size">Tamaño de la letra para el ID de la tarjeta</string>
<string name="settings_card_note_font_size">Tamaño de la letra para notas</string>
</resources>
<string name="settings_display_barcode_max_brightness">Iluminar vista de código de barras</string>
<string name="exportSuccessful">Se exportaron correctamente los datos de tarjetas de fidelización</string>
<string name="importSuccessful">Se importaron correctamente los datos de tarjetas de fidelización</string>
<string name="intent_import_card_from_url_share_text">Quiero compartirle una tarjeta</string>
<string name="settings_lock_barcode_orientation">Bloquear orientación de código de barras</string>
<string name="settings_dark_theme">Tema oscuro</string>
<string name="settings_light_theme">Tema claro</string>
<string name="settings_system_theme">Tema del sistema</string>
<string name="settings_theme">Tema</string>
<string name="enterBarcodeInstructions">Introduzca el identificador de tarjeta y seleccione la imagen que represente el código de barras que se utilizará, o bien, elija «Esta tarjeta no tiene código de barras» para no utilizar ninguno.</string>
<string name="app_copyright_old">Basado en Loyalty Card Keychain, derechos de autor 2016-2020 de Branden Archer.</string>
<string name="exportOptionExplanation">Los datos se guardarán en la ubicación que elija.</string>
<string name="failedParsingImportUriError">No se pudo procesar el URI de importación</string>
<string name="addedShortcut">Atajo añadido</string>
<string name="share">Compartir</string>
<string name="barcodeNoBarcode">Esta tarjeta no tiene código de barras</string>
<string name="barcodeType">Tipo de código de barras</string>
<string name="noMatchingGiftCards">Ninguna tarjeta coincide con el filtro de búsqueda. Pruebe con términos distintos.</string>
<string name="action_search">Buscar</string>
</resources>

View File

@@ -1,8 +1,6 @@
<resources
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_add">Ajouter</string>
<string name="noGiftCards">Aucune carte de fidélité enregistrée. Appuyez sur le bouton \"+\" (plus) pour commencer.\n\nLoyalty Card Locker vous permet d\'enregistrer vos cartes de fidélité sur votre téléphone pour toujours les avoir à portée de main.</string>
<string name="storeName">Nom</string>
<string name="note">Note</string>
@@ -18,18 +16,16 @@
<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="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="sendLabel">Envoyer</string>
<string name="editCardTitle">Modifier la carte de fidélité</string>
<string name="addCardTitle">Ajouter une carte de fidélité</string>
<string name="scanCardBarcode">Flasher le code-barres de la carte</string>
<string name="scanCardBarcode">Scanner le code-barres</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="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>
@@ -43,38 +39,32 @@
<string name="exportSuccessfulTitle">Exporté avec succès</string>
<string name="exportFailedTitle">Échec de l\'export</string>
<string name="exportFailed">Échec de l\'export </string>
<string name="importing">Import &#8230;</string>
<string name="exporting">Export &#8230;</string>
<string name="importing">Import </string>
<string name="exporting">Export </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="importOptionFilesystemTitle">Importer depuis le système de fichiers.</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="about">À propos</string>
<string name="app_copyright_fmt">Copyright 2016-<xliff:g>%d</xliff:g> Branden Archer</string>
<string name="app_license">Licence GPLv3.</string>
<string name="about_title_fmt">À propos de <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">Notes sur les versions : <xliff:g id="app_revision_url">%s</xliff:g></string>
<string name="app_libraries"><xliff:g id="app_name">%s</xliff:g> utilise les bibliothèques-tierces suivantes : <xliff:g id="app_libraries_list">%s</xliff:g></string>
<string name="app_resources"><xliff:g id="app_name">%s</xliff:g> utilise les ressources-tierces suivantes : <xliff:g id="app_resources_list">%s</xliff:g></string>
<string name="selectBarcodeTitle">Choisissez le code-barre</string>
<string name="selectBarcodeTitle">Choisissez le code-barres</string>
<string name="copy_to_clipboard_toast">Numéro de carte copié dans le presse-papier</string>
<string name="thumbnailDescription">Miniature pour la carte</string>
<string name="change">Modifier</string>
<string name="storeTextColorTitle">Couleur du texte du magasin</string>
<string name="storeTextBackgroundColorTitle">Couleur du titre</string>
<string name="storeNameBackgroundColorDescription">Couleur de fond du texte du magasin</string>
<string name="storeNameColorDescription">Couleur pour le nom du magasin</string>
<string name="settings">Paramètres</string>
<string name="settings_category_title_ui">Interface</string>
<string name="settings_category_title_ui">Interface utilisateur</string>
<string name="settings_card_title_list_font_size">Taille de police pour les titres de carte en liste</string>
<string name="settings_card_note_list_font_size">Taille de police pour les notes de carte en liste</string>
<string name="settings_card_title_font_size">Taille de police pour les titres de carte</string>
@@ -82,4 +72,23 @@
<string name="settings_card_note_font_size">Taille de police pour les notes de carte</string>
<string name="settings_display_barcode_max_brightness">Augmenter la luminosité du code-barres</string>
<string name="settings_lock_barcode_orientation">Verrouiller l\'orientation du code-barres</string>
</resources>
<string name="exportSuccessful">Carte de fidélité exportée avec succès</string>
<string name="importSuccessful">Carte de fidélité importée avec succès</string>
<string name="intent_import_card_from_url_share_text">Je veux partager une carte avec toi</string>
<string name="settings_dark_theme">Thème sombre</string>
<string name="settings_light_theme">Thème clair</string>
<string name="settings_system_theme">Thème du système</string>
<string name="settings_theme">Thème</string>
<string name="enterBarcodeInstructions">Entrez l\'identifiant de la carte puis sélectionnez l\'image qui représente le code-barres que vous souhaitez utiliser, ou sélectionnez « Cette carte n\'a pas de code-barres » pour ne pas utiliser de code-barres.</string>
<string name="app_copyright_old">Basé sur Loyalty Card Keychain, copyright 2016-2020 Branden Archer.</string>
<string name="exportOptionExplanation">Les données seront écrites à un emplacement de votre choix.</string>
<string name="failedParsingImportUriError">Impossible d\'analyser l\'URI d\'importation</string>
<string name="addedShortcut">Raccourci ajouté</string>
<string name="share">Partager</string>
<string name="barcodeNoBarcode">Cette carte n\'a pas de code-barres</string>
<string name="barcodeType">Type de code-barres</string>
<string name="noMatchingGiftCards">Aucune carte ne correspond au filtre de recherche. Veuillez essayer d\'autres termes.</string>
<string name="action_search">Rechercher</string>
<string name="unstar">Retirer des favoris</string>
<string name="star">Ajouter aux favoris</string>
</resources>

View File

@@ -1,17 +1,13 @@
<resources
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_search">Cerca</string>
<string name="action_add">Aggiungi</string>
<string name="noGiftCards">Non hai ancora alcuna carta fedeltà. Premi sul bottone +(più) in alto per incominciare.\n\nL\'app ti permette di portare con te le tue carte fedeltà, così da averle sempre a disposizione.</string>
<string name="noMatchingGiftCards">Nessuna carta fedeltà corrisponde al filtro. Riprovare con altri valori</string>
<string name="noMatchingGiftCards">Nessuna carta fedeltà corrisponde al filtro. Riprovare con altri valori.</string>
<string name="storeName">Negozio</string>
<string name="note">Note</string>
<string name="cardId">Codice</string>
<string name="barcodeNoBarcode">Questa carta non ha un codice a barre</string>
<string name="cancel">Annulla</string>
<string name="save">Salva</string>
<string name="capture">Scansione carta</string>
@@ -27,15 +23,13 @@
<string name="ok">Ok</string>
<string name="copy_to_clipboard">Copia ID negli appunti</string>
<string name="share">Condividi</string>
<string name="sendLabel">Invia&#8230;</string>
<string name="sendLabel">Invia</string>
<string name="editCardTitle">Modifica carta</string>
<string name="addCardTitle">Aggiungi 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="noCardExistsError">Impossibile trovare la carta</string>
@@ -50,38 +44,31 @@
<string name="exportSuccessfulTitle">Esportazione avvenuta con successo</string>
<string name="exportFailedTitle">Esportazione fallita</string>
<string name="exportFailed">Impossibile esportare</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="importing">Importazione in corso</string>
<string name="exporting">Esportazione in corso</string>
<string name="noExternalStoragePermissionError">Impossibile importare o esportare i dati senza il permesso per l\'uso della memoria esterna</string>
<string name="importOptionFilesystemTitle">Importa dal file system</string>
<string name="importOptionFilesystemExplanation">Scegli un file dal file system.</string>
<string name="importOptionFilesystemButton">Dal file system</string>
<string name="importOptionApplicationTitle">Usa un\'applicazione esterna</string>
<string name="importOptionApplicationExplanation">Usa un\'applicazione esterna come Dropbox, Google Drive o il tuo file manager preferito per aprire il file.</string>
<string name="importOptionApplicationButton">Usa un\'applicazione 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 versione: <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">Immettere l\'ID della carta, quindi selezionare l\'immagine che rappresenta il codice a barre desiderato oppure selezionare &#8220;Questa carta non ha un codice a barre&#8221; per non valorizzare il dato.</string>
<string name="enterBarcodeInstructions">Immettere l\'ID della carta, quindi selezionare l\'immagine che rappresenta il codice a barre desiderato oppure selezionare «Questa carta non ha un codice a barre» per non valorizzare il dato.</string>
<string name="copy_to_clipboard_toast">ID della carta copiato negli appunti</string>
<string name="thumbnailDescription">Miniatura carta</string>
<string name="change">Cambia</string>
<string name="storeTextColorTitle">Colore titolo</string>
<string name="storeTextBackgroundColorTitle">Colore scheda</string>
<string name="storeNameBackgroundColorDescription">Colore di sfondo del titolo carta</string>
<string name="storeNameColorDescription">Colore del titolo carta</string>
<string name="settings">Impostazioni</string>
<string name="settings_category_title_ui">Interfaccia utente</string>
<string name="settings_theme">Tema</string>
@@ -96,4 +83,12 @@
<string name="settings_display_barcode_max_brightness">Aumenta luminosità dello schermo quando apro un codice a barre</string>
<string name="settings_lock_barcode_orientation">Blocca orientamento del codice a barre</string>
<string name="intent_import_card_from_url_share_text">Voglio condividere una carta fedeltà con te</string>
</resources>
<string name="exportSuccessful">Dati della carta fedeltà esportati correttamente</string>
<string name="importSuccessful">Dati della carta fedeltà importati correttamente</string>
<string name="app_copyright_old">Basato su Loyalty Card Keychain, copyright 2016-2020 Branden Archer.</string>
<string name="exportOptionExplanation">I dati verranno scritti in una posizione a tua scelta.</string>
<string name="addedShortcut">Scorciatoia aggiunta</string>
<string name="barcodeType">Tipo di codice a barre</string>
<string name="unstar">Rimuovi dai preferiti</string>
<string name="star">Aggiungi ai preferiti</string>
</resources>

View File

@@ -34,7 +34,6 @@
<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>
<string name="about_title_fmt">Apie <xliff:g id="app_name">%s</xliff:g></string>
<string name="debug_version_fmt">Versija: <xliff:g id="version">%s</xliff:g></string>

View File

@@ -52,7 +52,6 @@
<string name="importOptionApplicationExplanation">Bruk eksternt program som Nextcloud, eller din favorittfilbehandler til å åpne ei fil.</string>
<string name="importOptionApplicationButton">Bruk eksternt program</string>
<string name="about">Om</string>
<string name="app_copyright_fmt">Kopirett 2016-<xliff:g>%d</xliff:g> Branden Archer</string>
<string name="app_license">Lisensiert GPLv3+.</string>
<string name="about_title_fmt">Om <xliff:g id="app_name">%s</xliff:g>
</string>
@@ -83,4 +82,20 @@
<string name="settings_card_note_font_size">Skriftstørrelse for kortmerknad</string>
<string name="settings_display_barcode_max_brightness">Lysere strekkodevisning</string>
<string name="settings_lock_barcode_orientation">Lås strekkodesideretning</string>
</resources>
<string name="exportSuccessful">Eksporterte stamkundekortdata</string>
<string name="importSuccessful">Importerte stamkundekortdata</string>
<string name="intent_import_card_from_url_share_text">Jeg ønsker å dele et kort med deg</string>
<string name="settings_dark_theme">Mørk</string>
<string name="settings_light_theme">Lys</string>
<string name="settings_system_theme">Systemdrakt</string>
<string name="settings_theme">Drakt</string>
<string name="app_copyright_old">Basert på Kundekortknippe, opphavsrett 2016-2020 Branden Archer.</string>
<string name="failedParsingImportUriError">Kunne ikke tolke importerings-URI</string>
<string name="share">Del</string>
<string name="barcodeNoBarcode">Dette kortet har ingen strekkode</string>
<string name="noMatchingGiftCards">Fant ingen slike kort. Prøv å endre søket.</string>
<string name="action_search">Søk</string>
<string name="starImage">Favorittstjerne</string>
<string name="unstar">Fjern fra favoritter</string>
<string name="star">Legg til i favoritter</string>
</resources>

View File

@@ -64,7 +64,6 @@
<string name="importOptionApplicationButton">Externe app gebruiken</string>
<string name="about">Over</string>
<string name="app_copyright_fmt">Copyright 2016-<xliff:g>%d</xliff:g> Branden Archer</string>
<string name="app_license">Uitgebracht onder de GPLv3-licentie.</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>

View File

@@ -61,7 +61,6 @@
<string name="importOptionApplicationButton">Użyj zewnętrznej aplikacji</string>
<string name="about">O aplikacji</string>
<string name="app_copyright_fmt">© 2016 — <xliff:g>%d</xliff:g> Branden Archer.</string>
<string name="app_license">Licencjonowane na podstawie GNU General Public License, wersji 3.0.</string>
<string name="about_title_fmt">O <xliff:g id="app_name">%s</xliff:g></string>
<string name="debug_version_fmt">Wersja: <xliff:g id="version">%s</xliff:g></string>

View File

@@ -1,18 +1,14 @@
<resources
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="action_search">Поиск</string>
<string name="action_add">Добавить карту</string>
<string name="noGiftCards">Пока нет ни одной карты. Нажмите на кнопку «+» (плюс) сверху для добавления.\n\n«Карты лояльности» позволяют хранить карты скидок в телефоне, так что они всегда будут под рукой.</string>
<string name="noMatchingGiftCards">Карт не найдено, попробуйте поискать по-другому.</string>
<string name="storeName">Магазин</string>
<string name="note">Примечание</string>
<string name="cardId">Номер карты</string>
<string name="barcodeType">Тип штрихкода</string>
<string name="barcodeNoBarcode">Эта карта без штрихкода</string>
<string name="cancel">Отменить</string>
<string name="save">Сохранить</string>
<string name="capture">Сканировать карту</string>
@@ -28,16 +24,14 @@
<string name="ok">ОК</string>
<string name="copy_to_clipboard">Скопировать номер карты в буфер обмена</string>
<string name="share">Переслать</string>
<string name="sendLabel">Отправить&#8230;</string>
<string name="sendLabel">Отправить</string>
<string name="addedShortcut">Добавленный ярлык</string>
<string name="editCardTitle">Редактировать карту</string>
<string name="addCardTitle">Добавить карту</string>
<string name="scanCardBarcode">Отсканируйте штрих-код</string>
<string name="cardShortcut">Ярлык карты</string>
<string name="noCardsMessage">Карт нет, добавьте одну для начала</string>
<string name="barcodeImageDescription">Изображение штрих-кода</string>
<string name="noStoreError">Название магазина не указано</string>
<string name="noCardIdError">Номер карты не указан</string>
<string name="noCardExistsError">Карта не найдена</string>
@@ -52,8 +46,8 @@
<string name="exportSuccessfulTitle">Успешный экспорт</string>
<string name="exportFailedTitle">Экспорт не удался</string>
<string name="exportFailed">Не удалось экспортировать</string>
<string name="importing">Импорт&#8230;</string>
<string name="exporting">Экспорт&#8230;</string>
<string name="importing">Импорт</string>
<string name="exporting">Экспорт</string>
<string name="noExternalStoragePermissionError">Импорт или экспорт невозможен без разрешения на доступ к хранилищу</string>
<string name="exportOptionExplanation">Данные будут записаны в выбранное место.</string>
<string name="importOptionFilesystemTitle">Импорт из файловой системы</string>
@@ -62,29 +56,22 @@
<string name="importOptionApplicationTitle">Использование другого приложения</string>
<string name="importOptionApplicationExplanation">Использовать другое приложение такое как Dropbox, Google Drive, или ваш любимый файловый менеджер чтобы открыть файл.</string>
<string name="importOptionApplicationButton">Использовать другое приложение</string>
<string name="about">О программе</string>
<string name="app_copyright_fmt">Все права защищены 2016-<xliff:g>%d</xliff:g> Branden Archer</string>
<string name="app_license">Лицензия GPLv3.</string>
<string name="about_title_fmt">О программе <xliff:g id="app_name">%s</xliff:g></string>
<string name="debug_version_fmt">Версия: <xliff:g id="version">%s</xliff:g></string>
<string name="app_revision_fmt">Информация о версиях: <xliff:g id="app_revision_url">%s</xliff:g></string>
<string name="app_libraries"><xliff:g id="app_name">%s</xliff:g> использует следующие сторонние библиотеки: <xliff:g id="app_libraries_list">%s</xliff:g></string>
<string name="app_resources"><xliff:g id="app_name">%s</xliff:g> использует следующие сторонние ресурсы: <xliff:g id="app_resources_list">%s</xliff:g></string>
<string name="selectBarcodeTitle">Выбор штрих-кода</string>
<string name="enterBarcodeInstructions">Введите ID карты и выберите тип штрих-кода.</string>
<string name="copy_to_clipboard_toast">Номер карты скопирован в буфер обмена</string>
<string name="thumbnailDescription">Логотип карты</string>
<string name="change">Изменить</string>
<string name="storeTextColorTitle">Цвет текста</string>
<string name="storeTextBackgroundColorTitle">Цвет фона</string>
<string name="storeNameBackgroundColorDescription">Цвет фона названия магазина</string>
<string name="storeNameColorDescription">Цвет текста названия магазина</string>
<string name="settings">Настройки</string>
<string name="settings_category_title_ui">Внешний вид</string>
<string name="settings_theme">Тема</string>
@@ -99,4 +86,5 @@
<string name="settings_display_barcode_max_brightness">Максимальная яркость при показе карты</string>
<string name="settings_lock_barcode_orientation">Портретная ориентация экрана при показе карты</string>
<string name="intent_import_card_from_url_share_text">Я хочу поделиться картой с вами</string>
</resources>
<string name="exportSuccessful">Данные карты лояльности успешно экспортированы</string>
</resources>

View File

@@ -54,7 +54,6 @@
<string name="importOptionApplicationButton">Použiť externú aplikáciu</string>
<string name="about">O aplikácii</string>
<string name="app_copyright_fmt">Copyright 2016-<xliff:g>%d</xliff:g> Branden Archer</string>
<string name="app_license">Licencované GPLv3.</string>
<string name="about_title_fmt">O <xliff:g id="app_name">%s</xliff:g></string>
<string name="debug_version_fmt">Verzia: <xliff:g id="version">%s</xliff:g></string>

View File

@@ -54,7 +54,6 @@
<string name="importOptionApplicationButton">Uporabi zunanjo aplikacijo</string>
<string name="about">Več o aplikaciji</string>
<string name="app_copyright_fmt">Copyright 2016-<xliff:g>%d</xliff:g> Branden Archer</string>
<string name="app_license">Licencirano s skladu z GPLv3.</string>
<string name="about_title_fmt">Več o <xliff:g id="app_name">%s</xliff:g></string>
<string name="debug_version_fmt">Verzija: <xliff:g id="version">%s</xliff:g></string>

View File

@@ -1,6 +1,6 @@
<resources>>
<style name="AppTheme.NoActionBar">
<style name="AppTheme.NoActionBar" parent="AppTheme">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>

View File

@@ -21,6 +21,7 @@
<dimen name="inputBorderThickness">2dip</dimen>
<dimen name="inputBorderDividerThickness">4dip</dimen>
<dimen name="inputMargin">2dip</dimen>
<dimen name="inputPadding">20dip</dimen>
<dimen name="colorSamplePadding">10dip</dimen>
<dimen name="inputSize">18sp</dimen>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#223355</color>
</resources>

View File

@@ -6,7 +6,7 @@
<string name="action_add">Add</string>
<string name="noGiftCards">You don\'t have any cards at the moment. Click the "+" (plus) button to get started.\n\nCatima lets you carry your cards on your phone, so they are always within reach.</string>
<string name="noMatchingGiftCards">No cards match the search filter. Please try some different terms.</string>
<string name="noMatchingGiftCards">No cards match the filter. Please try some different terms.</string>
<string name="storeName">Store</string>
<string name="note">Note</string>
@@ -14,6 +14,9 @@
<string name="barcodeType">Barcode type</string>
<string name="barcodeNoBarcode">This card has no barcode</string>
<string name="star">Add to favorites</string>
<string name="unstar">Remove from favorites</string>
<string name="cancel">Cancel</string>
<string name="save">Save</string>
<string name="capture">Capture Card</string>
@@ -60,12 +63,12 @@
<string name="importOptionFilesystemTitle">Import from filesystem</string>
<string name="importOptionFilesystemExplanation">Choose a specific file from the filesystem.</string>
<string name="importOptionFilesystemButton">From filesystem</string>
<string name="importOptionApplicationTitle">Use external ap1plication</string>
<string name="importOptionApplicationTitle">Use external application</string>
<string name="importOptionApplicationExplanation">Use an external application like Dropbox, Google Drive, or your favorite file manager to open a file.</string>
<string name="importOptionApplicationButton">Use external application</string>
<string name="about">About</string>
<string name="app_copyright_fmt">Copyright 2019-<xliff:g>%d</xliff:g> Sylvia van Os.</string>
<string name="app_copyright_fmt" translatable="false">Copyright 2019-<xliff:g>%d</xliff:g> Sylvia van Os.</string>
<string name="app_copyright_old">Based on Loyalty Card Keychain, copyright 2016-2020 Branden Archer.</string>
<string name="app_license">Licensed under the GPLv3.</string>
<string name="about_title_fmt">About <xliff:g id="app_name">%s</xliff:g></string>
@@ -80,6 +83,7 @@
<string name="copy_to_clipboard_toast">Card ID copied to clipboard</string>
<string name="thumbnailDescription">Thumbnail for card</string>
<string name="starImage">Favorite star</string>
<string name="change">Change</string>
<string name="storeTextColorTitle">Store Text Color</string>
@@ -119,4 +123,11 @@
<string name="intent_import_card_from_url_path_prefix_old" translatable="false">/loyalty-card-locker/share</string>
<string name="importSuccessful">Successfully imported loyalty card data</string>
<string name="exportSuccessful">Successfully exported loyalty card data</string>
<string name="enter_group_name">Enter group name</string>
<string name="groups">Groups</string>
<string name="noGroups">You don\'t have any groups at the moment. Click the "+" (plus) button to get started.\n\nCards can be assigned to groups to make them easier to find.</string>
<string name="groupCardCount"><xliff:g>%d</xliff:g> cards</string>
<string name="all">All</string>
<string name="deleteConfirmationGroup">Please confirm you want to delete this group</string>
</resources>

View File

@@ -1,23 +1,24 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.DayNight.DarkActionBar">
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorSecondary">@color/colorSecondary</item>
<item name="colorAccent">@color/colorSecondary</item>
</style>
<style name="AppTheme.NoActionBar">
<style name="AppTheme.NoActionBar" parent="AppTheme">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar"/>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.MaterialComponents.Dark.ActionBar"/>
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.MaterialComponents.Light"/>
<style name="CardView.ActionBarTheme" parent="@style/ThemeOverlay.AppCompat.ActionBar">
<style name="CardView.ActionBarTheme" parent="@style/ThemeOverlay.MaterialComponents.ActionBar">
<!-- THIS is where you can color the arrow! -->
<item name="colorControlNormal">@android:color/white</item>
</style>

View File

@@ -15,7 +15,11 @@ import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -40,7 +44,7 @@ public class DatabaseTest
public void addRemoveOneGiftCard()
{
assertEquals(0, db.getLoyaltyCardCount());
long id = db.insertLoyaltyCard("store", "note", "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, DEFAULT_HEADER_TEXT_COLOR);
long id = db.insertLoyaltyCard("store", "note", "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, DEFAULT_HEADER_TEXT_COLOR, 0);
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -50,6 +54,7 @@ public class DatabaseTest
assertEquals("store", loyaltyCard.store);
assertEquals("note", loyaltyCard.note);
assertEquals("cardId", loyaltyCard.cardId);
assertEquals(0, loyaltyCard.starStatus);
assertEquals(BarcodeFormat.UPC_A.toString(), loyaltyCard.barcodeType);
result = db.deleteLoyaltyCard(1);
@@ -61,7 +66,7 @@ public class DatabaseTest
@Test
public void updateGiftCard()
{
long id = db.insertLoyaltyCard("store", "note", "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, DEFAULT_HEADER_TEXT_COLOR);
long id = db.insertLoyaltyCard("store", "note", "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, DEFAULT_HEADER_TEXT_COLOR,0);
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -75,9 +80,31 @@ public class DatabaseTest
assertEquals("store1", loyaltyCard.store);
assertEquals("note1", loyaltyCard.note);
assertEquals("cardId1", loyaltyCard.cardId);
assertEquals(0, loyaltyCard.starStatus);
assertEquals(BarcodeFormat.AZTEC.toString(), loyaltyCard.barcodeType);
}
@Test
public void updateGiftCardOnlyStar()
{
long id = db.insertLoyaltyCard("store", "note", "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, DEFAULT_HEADER_TEXT_COLOR,0);
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
result = db.updateLoyaltyCardStarStatus(1, 1);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
LoyaltyCard loyaltyCard = db.getLoyaltyCard(1);
assertNotNull(loyaltyCard);
assertEquals("store", loyaltyCard.store);
assertEquals("note", loyaltyCard.note);
assertEquals("cardId", loyaltyCard.cardId);
assertEquals(1, loyaltyCard.starStatus);
assertEquals(BarcodeFormat.UPC_A.toString(), loyaltyCard.barcodeType);
}
@Test
public void updateMissingGiftCard()
{
@@ -92,7 +119,7 @@ public class DatabaseTest
@Test
public void emptyGiftCardValues()
{
long id = db.insertLoyaltyCard("", "", "", "", null, null);
long id = db.insertLoyaltyCard("", "", "", "", null, null, 0);
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -115,7 +142,7 @@ public class DatabaseTest
for(int index = CARDS_TO_ADD-1; index >= 0; index--)
{
long id = db.insertLoyaltyCard("store" + index, "note" + index, "cardId" + index,
BarcodeFormat.UPC_A.toString(), index, index*2);
BarcodeFormat.UPC_A.toString(), index, index*2, 0);
boolean result = (id != -1);
assertTrue(result);
}
@@ -136,6 +163,66 @@ public class DatabaseTest
assertEquals("cardId"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID)));
assertEquals("cardId"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID)));
assertEquals(BarcodeFormat.UPC_A.toString(), cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE)));
assertEquals(0, cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS)));
assertEquals(index, cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_COLOR)));
assertEquals(index*2, cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR)));
cursor.moveToNext();
}
assertTrue(cursor.isAfterLast());
}
@Test
public void giftCardsViaCursorWithOneStarred() //sorting test; stared card should appear first
{
final int CARDS_TO_ADD = 10;
long id;
// Add the gift cards in reverse order and add one with STAR, to ensure
// that they are sorted
for(int index = CARDS_TO_ADD-1; index >= 0; index--)
{
if (index == CARDS_TO_ADD-1) {
id = db.insertLoyaltyCard("store" + index, "note" + index, "cardId" + index,
BarcodeFormat.UPC_A.toString(), index, index*2, 1);
}
else {
id = db.insertLoyaltyCard("store" + index, "note" + index, "cardId" + index,
BarcodeFormat.UPC_A.toString(), index, index*2, 0);
}
boolean result = (id != -1);
assertTrue(result);
}
assertEquals(CARDS_TO_ADD, db.getLoyaltyCardCount());
Cursor cursor = db.getLoyaltyCardCursor();
assertNotNull(cursor);
assertEquals(CARDS_TO_ADD, cursor.getCount());
cursor.moveToFirst();
int index = CARDS_TO_ADD-1 ;
assertEquals("store"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STORE)));
assertEquals("note"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE)));
assertEquals("cardId"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID)));
assertEquals("cardId"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID)));
assertEquals(BarcodeFormat.UPC_A.toString(), cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE)));
assertEquals(1, cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS)));
assertEquals(index, cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_COLOR)));
assertEquals(index*2, cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR)));
cursor.moveToNext();
for(index = 0; index < CARDS_TO_ADD-1; index++)
{
assertEquals("store"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STORE)));
assertEquals("note"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE)));
assertEquals("cardId"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID)));
assertEquals("cardId"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID)));
assertEquals(BarcodeFormat.UPC_A.toString(), cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE)));
assertEquals(0, cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS)));
assertEquals(index, cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_COLOR)));
assertEquals(index*2, cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR)));
@@ -171,6 +258,161 @@ public class DatabaseTest
return (int)newId;
}
@Test
public void addRemoveOneGroup()
{
assertEquals(0, db.getGroupCount());
long id = db.insertGroup("group one");
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getGroupCount());
Group group = db.getGroup("group one");
assertNotNull(group);
assertEquals("group one", group._id);
result = db.deleteGroup("group one");
assertTrue(result);
assertEquals(0, db.getGroupCount());
assertNull(db.getGroup("group one"));
}
@Test
public void updateGroup()
{
long id = db.insertGroup("group one");
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getGroupCount());
result = db.updateGroup("group one", "group one renamed");
assertTrue(result);
assertEquals(1, db.getGroupCount());
// Group one no longer exists
Group group = db.getGroup("group one");
assertNull(group);
// But group one renamed does
Group group2 = db.getGroup("group one renamed");
assertNotNull(group2);
assertEquals("group one renamed", group2._id);
}
@Test
public void updateMissingGroup()
{
assertEquals(0, db.getGroupCount());
boolean result = db.updateGroup("group one", "new name");
assertEquals(false, result);
assertEquals(0, db.getGroupCount());
}
@Test
public void emptyGroupValues()
{
long id = db.insertGroup("");
boolean result = (id != -1);
assertFalse(result);
assertEquals(0, db.getLoyaltyCardCount());
}
@Test
public void duplicateGroupName()
{
assertEquals(0, db.getGroupCount());
long id = db.insertGroup("group one");
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getGroupCount());
Group group = db.getGroup("group one");
assertNotNull(group);
assertEquals("group one", group._id);
// Should fail on duplicate
long id2 = db.insertGroup("group one");
boolean result2 = (id2 != -1);
assertFalse(result2);
assertEquals(1, db.getGroupCount());
}
@Test
public void updateGroupDuplicate()
{
long id = db.insertGroup("group one");
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getGroupCount());
long id2 = db.insertGroup("group two");
boolean result2 = (id2 != -1);
assertTrue(result2);
assertEquals(2, db.getGroupCount());
// Should fail when trying to rename group two to one
boolean result3 = db.updateGroup("group two", "group one");
assertFalse(result3);
assertEquals(2, db.getGroupCount());
// Rename failed so both should still be the same
Group group = db.getGroup("group one");
assertNotNull(group);
assertEquals("group one", group._id);
Group group2 = db.getGroup("group two");
assertNotNull(group2);
assertEquals("group two", group2._id);
}
@Test
public void cardAddAndRemoveGroups()
{
// Create card
assertEquals(0, db.getLoyaltyCardCount());
long id = db.insertLoyaltyCard("store", "note", "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, DEFAULT_HEADER_TEXT_COLOR, 0);
boolean result = (id != -1);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
// Create two groups to only one card
assertEquals(0, db.getGroupCount());
long gid = db.insertGroup("one");
boolean gresult = (gid != -1);
assertTrue(gresult);
long gid2 = db.insertGroup("two");
boolean gresult2 = (gid2 != -1);
assertTrue(gresult2);
assertEquals(2, db.getGroupCount());
Group group1 = db.getGroup("one");
// Card has no groups by default
List<Group> cardGroups = db.getLoyaltyCardGroups(1);
assertEquals(0, cardGroups.size());
// Add one groups to card
List<Group> groupList1 = new ArrayList<>();
groupList1.add(group1);
db.setLoyaltyCardGroups(1, groupList1);
List<Group> cardGroups1 = db.getLoyaltyCardGroups(1);
assertEquals(1, cardGroups1.size());
assertEquals(cardGroups1.get(0)._id, group1._id);
assertEquals(1, db.getGroupCardCount("one"));
assertEquals(0, db.getGroupCardCount("two"));
// Remove groups
db.setLoyaltyCardGroups(1, new ArrayList<Group>());
List<Group> cardGroups2 = db.getLoyaltyCardGroups(1);
assertEquals(0, cardGroups2.size());
assertEquals(0, db.getGroupCardCount("one"));
assertEquals(0, db.getGroupCardCount("two"));
}
@Test
public void databaseUpgradeFromVersion1()
{

View File

@@ -9,7 +9,6 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.view.View;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -17,7 +16,6 @@ import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowPackageManager;
@RunWith(RobolectricTestRunner.class)
@Config(sdk = 23)

View File

@@ -3,12 +3,10 @@ package protect.card_locker;
import android.app.Activity;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Environment;
import com.google.zxing.BarcodeFormat;
import org.apache.tools.ant.filters.StringInputStream;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -25,9 +23,10 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -71,7 +70,7 @@ public class ImportExportTest
{
String storeName = String.format("store, \"%4d", index);
String note = String.format("note, \"%4d", index);
long id = db.insertLoyaltyCard(storeName, note, BARCODE_DATA, BARCODE_TYPE, index, index*2);
long id = db.insertLoyaltyCard(storeName, note, BARCODE_DATA, BARCODE_TYPE, index, index*2, 0);
boolean result = (id != -1);
assertTrue(result);
}
@@ -79,6 +78,44 @@ public class ImportExportTest
assertEquals(cardsToAdd, db.getLoyaltyCardCount());
}
private void addLoyaltyCardsFiveStarred()
{
int cardsToAdd = 9;
// Add in reverse order to test sorting
for(int index = cardsToAdd; index > 4; index--)
{
String storeName = String.format("store, \"%4d", index);
String note = String.format("note, \"%4d", index);
long id = db.insertLoyaltyCard(storeName, note, BARCODE_DATA, BARCODE_TYPE, index, index*2, 1);
boolean result = (id != -1);
assertTrue(result);
}
for(int index = cardsToAdd-5; index > 0; index--)
{
String storeName = String.format("store, \"%4d", index);
String note = String.format("note, \"%4d", index);
//if index is even
long id = db.insertLoyaltyCard(storeName, note, BARCODE_DATA, BARCODE_TYPE, index, index*2, 0);
boolean result = (id != -1);
assertTrue(result);
}
assertEquals(cardsToAdd, db.getLoyaltyCardCount());
}
private void addGroups(int groupsToAdd)
{
// Add in reverse order to test sorting
for(int index = groupsToAdd; index > 0; index--)
{
String groupName = String.format("group, \"%4d", index);
long id = db.insertGroup(groupName);
boolean result = (id != -1);
assertTrue(result);
}
assertEquals(groupsToAdd, db.getGroupCount());
}
/**
* Check that all of the cards follow the pattern
* specified in addLoyaltyCards(), and are in sequential order
@@ -102,6 +139,81 @@ public class ImportExportTest
assertEquals(BARCODE_TYPE, card.barcodeType);
assertEquals(Integer.valueOf(index), card.headerColor);
assertEquals(Integer.valueOf(index*2), card.headerTextColor);
assertEquals(0, card.starStatus);
index++;
}
cursor.close();
}
/**
* Check that all of the cards follow the pattern
* specified in addLoyaltyCardsSomeStarred(), and are in sequential order
* with starred ones first
*/
private void checkLoyaltyCardsFiveStarred()
{
Cursor cursor = db.getLoyaltyCardCursor();
int index = 5;
while(index<10)
{
cursor.moveToNext();
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cursor);
String expectedStore = String.format("store, \"%4d", index);
String expectedNote = String.format("note, \"%4d", index);
assertEquals(expectedStore, card.store);
assertEquals(expectedNote, card.note);
assertEquals(BARCODE_DATA, card.cardId);
assertEquals(BARCODE_TYPE, card.barcodeType);
assertEquals(Integer.valueOf(index), card.headerColor);
assertEquals(Integer.valueOf(index*2), card.headerTextColor);
assertEquals(1, card.starStatus);
index++;
}
index = 1;
while(cursor.moveToNext() && index<5)
{
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cursor);
String expectedStore = String.format("store, \"%4d", index);
String expectedNote = String.format("note, \"%4d", index);
assertEquals(expectedStore, card.store);
assertEquals(expectedNote, card.note);
assertEquals(BARCODE_DATA, card.cardId);
assertEquals(BARCODE_TYPE, card.barcodeType);
assertEquals(Integer.valueOf(index), card.headerColor);
assertEquals(Integer.valueOf(index*2), card.headerTextColor);
assertEquals(0, card.starStatus);
index++;
}
cursor.close();
}
/**
* Check that all of the groups follow the pattern
* specified in addGroups(), and are in sequential order
* where the smallest group's index is 1
*/
private void checkGroups()
{
Cursor cursor = db.getGroupCursor();
int index = 1;
while(cursor.moveToNext())
{
Group group = Group.toGroup(cursor);
String expectedGroupName = String.format("group, \"%4d", index);
assertEquals(expectedGroupName, group._id);
index++;
}
@@ -155,6 +267,131 @@ public class ImportExportTest
}
}
@Test
public void multipleCardsExportImportSomeStarred() throws IOException
{
final int NUM_CARDS = 9;
for(DataFormat format : DataFormat.values())
{
addLoyaltyCardsFiveStarred();
ByteArrayOutputStream outData = new ByteArrayOutputStream();
OutputStreamWriter outStream = new OutputStreamWriter(outData);
// Export data to CSV format
boolean result = MultiFormatExporter.exportData(db, outStream, format);
assertTrue(result);
outStream.close();
clearDatabase();
ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray());
InputStreamReader inStream = new InputStreamReader(inData);
// Import the CSV data
result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
assertTrue(result);
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
checkLoyaltyCardsFiveStarred();
// Clear the database for the next format under test
clearDatabase();
}
}
private List<String> groupsToGroupNames(List<Group> groups)
{
List<String> groupNames = new ArrayList<>();
for (Group group : groups) {
groupNames.add(group._id);
}
return groupNames;
}
@Test
public void multipleCardsExportImportWithGroups() throws IOException
{
final int NUM_CARDS = 10;
final int NUM_GROUPS = 3;
for(DataFormat format : DataFormat.values())
{
addLoyaltyCards(NUM_CARDS);
addGroups(NUM_GROUPS);
List<Group> emptyGroup = new ArrayList<>();
List<Group> groupsForOne = new ArrayList<>();
groupsForOne.add(db.getGroup("group, \" 1"));
List<Group> groupsForTwo = new ArrayList<>();
groupsForTwo.add(db.getGroup("group, \" 1"));
groupsForTwo.add(db.getGroup("group, \" 2"));
List<Group> groupsForThree = new ArrayList<>();
groupsForThree.add(db.getGroup("group, \" 1"));
groupsForThree.add(db.getGroup("group, \" 2"));
groupsForThree.add(db.getGroup("group, \" 3"));
List<Group> groupsForFour = new ArrayList<>();
groupsForFour.add(db.getGroup("group, \" 1"));
groupsForFour.add(db.getGroup("group, \" 2"));
groupsForFour.add(db.getGroup("group, \" 3"));
List<Group> groupsForFive = new ArrayList<>();
groupsForFive.add(db.getGroup("group, \" 1"));
groupsForFive.add(db.getGroup("group, \" 3"));
db.setLoyaltyCardGroups(1, groupsForOne);
db.setLoyaltyCardGroups(2, groupsForTwo);
db.setLoyaltyCardGroups(3, groupsForThree);
db.setLoyaltyCardGroups(4, groupsForFour);
db.setLoyaltyCardGroups(5, groupsForFive);
ByteArrayOutputStream outData = new ByteArrayOutputStream();
OutputStreamWriter outStream = new OutputStreamWriter(outData);
// Export data to CSV format
boolean result = MultiFormatExporter.exportData(db, outStream, format);
assertTrue(result);
outStream.close();
clearDatabase();
ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray());
InputStreamReader inStream = new InputStreamReader(inData);
// Import the CSV data
result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
assertTrue(result);
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
assertEquals(NUM_GROUPS, db.getGroupCount());
checkLoyaltyCards();
checkGroups();
assertEquals(groupsToGroupNames(groupsForOne), groupsToGroupNames(db.getLoyaltyCardGroups(1)));
assertEquals(groupsToGroupNames(groupsForTwo), groupsToGroupNames(db.getLoyaltyCardGroups(2)));
assertEquals(groupsToGroupNames(groupsForThree), groupsToGroupNames(db.getLoyaltyCardGroups(3)));
assertEquals(groupsToGroupNames(groupsForFour), groupsToGroupNames(db.getLoyaltyCardGroups(4)));
assertEquals(groupsToGroupNames(groupsForFive), groupsToGroupNames(db.getLoyaltyCardGroups(5)));
assertEquals(emptyGroup, db.getLoyaltyCardGroups(6));
assertEquals(emptyGroup, db.getLoyaltyCardGroups(7));
assertEquals(emptyGroup, db.getLoyaltyCardGroups(8));
assertEquals(emptyGroup, db.getLoyaltyCardGroups(9));
assertEquals(emptyGroup, db.getLoyaltyCardGroups(10));
// Clear the database for the next format under test
clearDatabase();
}
}
@Test
public void importExistingCardsNotReplace() throws IOException
{
@@ -287,15 +524,17 @@ public class ImportExportTest
}
@Test
public void importWithoutColors() throws IOException
public void importWithoutColorsV1() throws IOException
{
String csvText = "";
csvText += DBHelper.LoyaltyCardDbIds.ID + "," +
DBHelper.LoyaltyCardDbIds.STORE + "," +
DBHelper.LoyaltyCardDbIds.NOTE + "," +
DBHelper.LoyaltyCardDbIds.CARD_ID + "," +
DBHelper.LoyaltyCardDbIds.BARCODE_TYPE + "\n";
csvText += "1,store,note,12345,type";
DBHelper.LoyaltyCardDbIds.STORE + "," +
DBHelper.LoyaltyCardDbIds.NOTE + "," +
DBHelper.LoyaltyCardDbIds.CARD_ID + "," +
DBHelper.LoyaltyCardDbIds.BARCODE_TYPE + "," +
DBHelper.LoyaltyCardDbIds.STAR_STATUS + "\n";
csvText += "1,store,note,12345,type,0";
ByteArrayInputStream inputStream = new ByteArrayInputStream(csvText.getBytes(StandardCharsets.UTF_8));
InputStreamReader inStream = new InputStreamReader(inputStream);
@@ -311,12 +550,13 @@ public class ImportExportTest
assertEquals("note", card.note);
assertEquals("12345", card.cardId);
assertEquals("type", card.barcodeType);
assertEquals(0, card.starStatus);
assertNull(card.headerColor);
assertNull(card.headerTextColor);
}
@Test
public void importWithoutNullColors() throws IOException
public void importWithoutNullColorsV1() throws IOException
{
String csvText = "";
csvText += DBHelper.LoyaltyCardDbIds.ID + "," +
@@ -325,8 +565,10 @@ public class ImportExportTest
DBHelper.LoyaltyCardDbIds.CARD_ID + "," +
DBHelper.LoyaltyCardDbIds.BARCODE_TYPE + "," +
DBHelper.LoyaltyCardDbIds.HEADER_COLOR + "," +
DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR + "\n";
csvText += "1,store,note,12345,type,,";
DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR + "," +
DBHelper.LoyaltyCardDbIds.STAR_STATUS + "\n";
csvText += "1,store,note,12345,type,,,0";
ByteArrayInputStream inputStream = new ByteArrayInputStream(csvText.getBytes(StandardCharsets.UTF_8));
InputStreamReader inStream = new InputStreamReader(inputStream);
@@ -342,12 +584,13 @@ public class ImportExportTest
assertEquals("note", card.note);
assertEquals("12345", card.cardId);
assertEquals("type", card.barcodeType);
assertEquals(0, card.starStatus);
assertNull(card.headerColor);
assertNull(card.headerTextColor);
}
@Test
public void importWithoutInvalidColors() throws IOException
public void importWithoutInvalidColorsV1() throws IOException
{
String csvText = "";
csvText += DBHelper.LoyaltyCardDbIds.ID + "," +
@@ -356,8 +599,10 @@ public class ImportExportTest
DBHelper.LoyaltyCardDbIds.CARD_ID + "," +
DBHelper.LoyaltyCardDbIds.BARCODE_TYPE + "," +
DBHelper.LoyaltyCardDbIds.HEADER_COLOR + "," +
DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR + "\n";
csvText += "1,store,note,12345,type,not a number,invalid";
DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR + "," +
DBHelper.LoyaltyCardDbIds.STAR_STATUS + "\n";
csvText += "1,store,note,12345,type,not a number,invalid,0";
ByteArrayInputStream inputStream = new ByteArrayInputStream(csvText.getBytes(StandardCharsets.UTF_8));
InputStreamReader inStream = new InputStreamReader(inputStream);
@@ -369,7 +614,7 @@ public class ImportExportTest
}
@Test
public void importWithNoBarcodeType() throws IOException
public void importWithNoBarcodeTypeV1() throws IOException
{
String csvText = "";
csvText += DBHelper.LoyaltyCardDbIds.ID + "," +
@@ -378,8 +623,10 @@ public class ImportExportTest
DBHelper.LoyaltyCardDbIds.CARD_ID + "," +
DBHelper.LoyaltyCardDbIds.BARCODE_TYPE + "," +
DBHelper.LoyaltyCardDbIds.HEADER_COLOR + "," +
DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR + "\n";
csvText += "1,store,note,12345,,1,1";
DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR + "," +
DBHelper.LoyaltyCardDbIds.STAR_STATUS + "\n";
csvText += "1,store,note,12345,,1,1,0";
ByteArrayInputStream inputStream = new ByteArrayInputStream(csvText.getBytes(StandardCharsets.UTF_8));
InputStreamReader inStream = new InputStreamReader(inputStream);
@@ -395,7 +642,132 @@ public class ImportExportTest
assertEquals("note", card.note);
assertEquals("12345", card.cardId);
assertEquals("", card.barcodeType);
assertEquals(0, card.starStatus);
assertEquals(1, (long) card.headerColor);
assertEquals(1, (long) card.headerTextColor);
}
@Test
public void importWithStarredFieldV1() throws IOException
{
String csvText = "";
csvText += DBHelper.LoyaltyCardDbIds.ID + "," +
DBHelper.LoyaltyCardDbIds.STORE + "," +
DBHelper.LoyaltyCardDbIds.NOTE + "," +
DBHelper.LoyaltyCardDbIds.CARD_ID + "," +
DBHelper.LoyaltyCardDbIds.BARCODE_TYPE + "," +
DBHelper.LoyaltyCardDbIds.HEADER_COLOR + "," +
DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR + "," +
DBHelper.LoyaltyCardDbIds.STAR_STATUS + "\n";
csvText += "1,store,note,12345,type,1,1,1";
ByteArrayInputStream inputStream = new ByteArrayInputStream(csvText.getBytes(StandardCharsets.UTF_8));
InputStreamReader inStream = new InputStreamReader(inputStream);
// Import the CSV data
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
assertEquals(true, result);
assertEquals(1, db.getLoyaltyCardCount());
LoyaltyCard card = db.getLoyaltyCard(1);
assertEquals("store", card.store);
assertEquals("note", card.note);
assertEquals("12345", card.cardId);
assertEquals("type", card.barcodeType);
assertEquals(1, card.starStatus);
assertEquals(1, (long) card.headerColor);
assertEquals(1, (long) card.headerTextColor);
}
@Test
public void importWithNoStarredFieldV1() throws IOException
{
String csvText = "";
csvText += DBHelper.LoyaltyCardDbIds.ID + "," +
DBHelper.LoyaltyCardDbIds.STORE + "," +
DBHelper.LoyaltyCardDbIds.NOTE + "," +
DBHelper.LoyaltyCardDbIds.CARD_ID + "," +
DBHelper.LoyaltyCardDbIds.BARCODE_TYPE + "," +
DBHelper.LoyaltyCardDbIds.HEADER_COLOR + "," +
DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR + "," +
DBHelper.LoyaltyCardDbIds.STAR_STATUS + "\n";
csvText += "1,store,note,12345,type,1,1,";
ByteArrayInputStream inputStream = new ByteArrayInputStream(csvText.getBytes(StandardCharsets.UTF_8));
InputStreamReader inStream = new InputStreamReader(inputStream);
// Import the CSV data
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
assertEquals(true, result);
assertEquals(1, db.getLoyaltyCardCount());
LoyaltyCard card = db.getLoyaltyCard(1);
assertEquals("store", card.store);
assertEquals("note", card.note);
assertEquals("12345", card.cardId);
assertEquals("type", card.barcodeType);
assertEquals(0, card.starStatus);
assertEquals(1, (long) card.headerColor);
assertEquals(1, (long) card.headerTextColor);
}
@Test
public void importWithInvalidStarFieldV1() throws IOException
{
String csvText = "";
csvText += DBHelper.LoyaltyCardDbIds.ID + "," +
DBHelper.LoyaltyCardDbIds.STORE + "," +
DBHelper.LoyaltyCardDbIds.NOTE + "," +
DBHelper.LoyaltyCardDbIds.CARD_ID + "," +
DBHelper.LoyaltyCardDbIds.BARCODE_TYPE + "," +
DBHelper.LoyaltyCardDbIds.HEADER_COLOR + "," +
DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR + "," +
DBHelper.LoyaltyCardDbIds.STAR_STATUS + "\n";
csvText += "1,store,note,12345,type,1,1,2";
ByteArrayInputStream inputStream = new ByteArrayInputStream(csvText.getBytes(StandardCharsets.UTF_8));
InputStreamReader inStream = new InputStreamReader(inputStream);
// Import the CSV data
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
csvText = "";
csvText += DBHelper.LoyaltyCardDbIds.ID + "," +
DBHelper.LoyaltyCardDbIds.STORE + "," +
DBHelper.LoyaltyCardDbIds.NOTE + "," +
DBHelper.LoyaltyCardDbIds.CARD_ID + "," +
DBHelper.LoyaltyCardDbIds.BARCODE_TYPE + "," +
DBHelper.LoyaltyCardDbIds.HEADER_COLOR + "," +
DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR + "," +
DBHelper.LoyaltyCardDbIds.STAR_STATUS + "\n";
csvText += "1,store,note,12345,type,1,1,text";
inputStream = new ByteArrayInputStream(csvText.getBytes(StandardCharsets.UTF_8));
inStream = new InputStreamReader(inputStream);
// Import the CSV data
result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
LoyaltyCard card = db.getLoyaltyCard(1);
assertEquals("store", card.store);
assertEquals("note", card.note);
assertEquals("12345", card.cardId);
assertEquals("type", card.barcodeType);
assertEquals(0, card.starStatus);
assertEquals(1, (long) card.headerColor);
assertEquals(1, (long) card.headerTextColor);
}
}

View File

@@ -11,6 +11,7 @@ import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.io.InvalidObjectException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -34,7 +35,7 @@ public class ImportURITest {
public void ensureNoDataLoss() throws InvalidObjectException
{
// Generate card
db.insertLoyaltyCard("store", "note", BarcodeFormat.UPC_A.toString(), LoyaltyCardDbIds.BARCODE_TYPE, Color.BLACK, Color.WHITE);
db.insertLoyaltyCard("store", "note", BarcodeFormat.UPC_A.toString(), LoyaltyCardDbIds.BARCODE_TYPE, Color.BLACK, Color.WHITE, 1);
// Get card
LoyaltyCard card = db.getLoyaltyCard(1);
@@ -52,13 +53,15 @@ public class ImportURITest {
assertEquals(card.headerTextColor, parsedCard.headerTextColor);
assertEquals(card.note, parsedCard.note);
assertEquals(card.store, parsedCard.store);
// No export of starStatus for single cards foreseen therefore 0 will be imported
assertEquals(0, parsedCard.starStatus);
}
@Test
public void ensureNoCrashOnMissingHeaderFields() throws InvalidObjectException
{
// Generate card
db.insertLoyaltyCard("store", "note", BarcodeFormat.UPC_A.toString(), LoyaltyCardDbIds.BARCODE_TYPE, null, null);
db.insertLoyaltyCard("store", "note", BarcodeFormat.UPC_A.toString(), LoyaltyCardDbIds.BARCODE_TYPE, null, null, 0);
// Get card
LoyaltyCard card = db.getLoyaltyCard(1);
@@ -88,4 +91,36 @@ public class ImportURITest {
// Desired behaviour
}
}
@Test
public void failToParseBadData()
{
try {
//"stare" instead of store
importURIHelper.parse(Uri.parse("https://brarcher.github.io/loyalty-card-locker/share?stare=store&note=note&cardid=12345&barcodetype=ITF&headercolor=-416706&headertextcolor=-1"));
assertTrue(false); // Shouldn't get here
} catch(InvalidObjectException ex) {
// Desired behaviour
}
}
@Test
public void parseAdditionalUnforeseenData()
{
LoyaltyCard parsedCard = null;
try {
parsedCard = importURIHelper.parse(Uri.parse("https://brarcher.github.io/loyalty-card-locker/share?store=store&note=note&cardid=12345&barcodetype=ITF&headercolor=-416706&headertextcolor=-1&notforeseen=no"));
} catch (InvalidObjectException e) {
e.printStackTrace();
}
// Compare everything
assertEquals("ITF", parsedCard.barcodeType);
assertEquals("12345", parsedCard.cardId);
assertEquals("note", parsedCard.note);
assertEquals("store", parsedCard.store);
assertEquals(Integer.valueOf(-416706), parsedCard.headerColor);
assertEquals(Integer.valueOf(-1), parsedCard.headerTextColor);
assertEquals(0, parsedCard.starStatus);
}
}

View File

@@ -6,6 +6,7 @@ import android.database.Cursor;
import android.graphics.Color;
import android.preference.PreferenceManager;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.google.zxing.BarcodeFormat;
@@ -83,7 +84,7 @@ public class LoyaltyCardCursorAdapterTest
@Test
public void TestCursorAdapterEmptyNote()
{
db.insertLoyaltyCard("store", "", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, Color.WHITE);
db.insertLoyaltyCard("store", "", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, Color.WHITE, 0);
LoyaltyCard card = db.getLoyaltyCard(1);
Cursor cursor = db.getLoyaltyCardCursor();
@@ -97,7 +98,7 @@ public class LoyaltyCardCursorAdapterTest
@Test
public void TestCursorAdapterWithNote()
{
db.insertLoyaltyCard("store", "note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, Color.WHITE);
db.insertLoyaltyCard("store", "note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, Color.WHITE, 0);
LoyaltyCard card = db.getLoyaltyCard(1);
Cursor cursor = db.getLoyaltyCardCursor();
@@ -111,7 +112,7 @@ public class LoyaltyCardCursorAdapterTest
@Test
public void TestCursorAdapterFontSizes()
{
db.insertLoyaltyCard("store", "note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, Color.WHITE);
db.insertLoyaltyCard("store", "note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, Color.WHITE, 0);
LoyaltyCard card = db.getLoyaltyCard(1);
Cursor cursor = db.getLoyaltyCardCursor();
@@ -125,4 +126,29 @@ public class LoyaltyCardCursorAdapterTest
view = createView(cursor);
checkView(view, card.store, card.note, true);
}
@Test
public void TestCursorAdapterStarring()
{
db.insertLoyaltyCard("storeA", "note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, Color.WHITE, 0);
db.insertLoyaltyCard("storeB", "note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, Color.WHITE, 1);
db.insertLoyaltyCard("storeC", "note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, Color.WHITE, 1);
Cursor cursor = db.getLoyaltyCardCursor();
cursor.moveToFirst();
View view = createView(cursor);
ImageView star = view.findViewById(R.id.star);
assertEquals(View.VISIBLE, star.getVisibility());
cursor.moveToNext();
view = createView(cursor);
star = view.findViewById(R.id.star);
assertEquals(View.VISIBLE, star.getVisibility());
cursor.moveToNext();
view = createView(cursor);
star = view.findViewById(R.id.star);
assertEquals(View.GONE, star.getVisibility());
}
}

View File

@@ -400,7 +400,7 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, Color.WHITE);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, Color.WHITE, 0);
activityController.start();
activityController.visible();
@@ -416,7 +416,7 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, Color.WHITE);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, Color.WHITE, 0);
activityController.start();
activityController.visible();
@@ -432,7 +432,7 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, Color.WHITE);
db.insertLoyaltyCard("store", "note", EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, Color.WHITE, 0);
activityController.start();
activityController.visible();
@@ -453,7 +453,7 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, Color.WHITE);
db.insertLoyaltyCard("store", "note", EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, Color.WHITE, 0);
activityController.start();
activityController.visible();
@@ -479,7 +479,7 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, Color.WHITE);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, Color.WHITE, 0);
activityController.start();
activityController.visible();
@@ -488,11 +488,12 @@ public class LoyaltyCardViewActivityTest
final Menu menu = shadowOf(activity).getOptionsMenu();
assertTrue(menu != null);
// The share and settings button should be present
assertEquals(menu.size(), 2);
// The share, settings, add and star button should be present
assertEquals(menu.size(), 3);
assertEquals("Block Rotation", menu.findItem(R.id.action_lock_unlock).getTitle().toString());
assertEquals("Share", menu.findItem(R.id.action_share).getTitle().toString());
assertEquals("Add to favorites", menu.findItem(R.id.action_star_unstar).getTitle().toString());
}
@Test
@@ -522,7 +523,7 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, Color.WHITE);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, Color.WHITE, 0);
activityController.start();
activityController.visible();
@@ -540,7 +541,7 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, null, null);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, null, null, 0);
activityController.start();
activityController.visible();
@@ -558,7 +559,7 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, null, null);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, null, null, 0);
activityController.start();
activityController.visible();
@@ -575,7 +576,7 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, "", Color.BLACK, Color.WHITE);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, "", Color.BLACK, Color.WHITE, 0);
activityController.start();
activityController.visible();
@@ -592,7 +593,7 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, Color.WHITE);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, Color.WHITE, 0);
activityController.start();
activityController.visible();
@@ -619,7 +620,7 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, Color.WHITE);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, Color.WHITE, 0);
final int STORE_FONT_SIZE = 50;
final int CARD_FONT_SIZE = 40;
@@ -661,7 +662,7 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, Color.WHITE);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, Color.WHITE, 0);
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(activity);
settings.edit()
@@ -689,6 +690,35 @@ public class LoyaltyCardViewActivityTest
}
}
@Test
public void checkPushStarIcon()
{
ActivityController activityController = createActivityWithLoyaltyCard(false);
Activity activity = (Activity) activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, Color.WHITE,0);
activityController.start();
activityController.visible();
activityController.resume();
assertEquals(false, activity.isFinishing());
final Menu menu = shadowOf(activity).getOptionsMenu();
assertTrue(menu != null);
// The share, settings and star button should be present
assertEquals(menu.size(), 3);
assertEquals("Add to favorites", menu.findItem(R.id.action_star_unstar).getTitle().toString());
shadowOf(activity).clickMenuItem(R.id.action_star_unstar);
assertEquals("Remove from favorites", menu.findItem(R.id.action_star_unstar).getTitle().toString());
shadowOf(activity).clickMenuItem(R.id.action_star_unstar);
assertEquals("Add to favorites", menu.findItem(R.id.action_star_unstar).getTitle().toString());
}
@Test
public void checkBarcodeFullscreenWorkflow()
{
@@ -696,7 +726,7 @@ public class LoyaltyCardViewActivityTest
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, Color.WHITE);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE, Color.BLACK, Color.WHITE, 0);
activityController.start();
activityController.visible();

View File

@@ -2,29 +2,28 @@ package protect.card_locker;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.graphics.Color;
import android.os.Bundle;
import androidx.appcompat.widget.SearchView;
import android.view.Menu;
import android.view.View;
import android.widget.ListView;
import android.widget.TextView;
import com.google.android.material.tabs.TabLayout;
import com.google.zxing.BarcodeFormat;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.android.controller.ActivityController;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -61,9 +60,10 @@ public class MainActivityTest
final Menu menu = shadowOf(activity).getOptionsMenu();
assertTrue(menu != null);
// The settings, search and add button should be present
assertEquals(menu.size(), 4);
// The settings, import/export, groups, search and add button should be present
assertEquals(menu.size(), 5);
assertEquals("Search", menu.findItem(R.id.action_search).getTitle().toString());
assertEquals("Groups", menu.findItem(R.id.action_manage_groups).getTitle().toString());
assertEquals("Import/Export", menu.findItem(R.id.action_import_export).getTitle().toString());
assertEquals("About", menu.findItem(R.id.action_about).getTitle().toString());
assertEquals("Settings", menu.findItem(R.id.action_settings).getTitle().toString());
@@ -98,7 +98,7 @@ public class MainActivityTest
assertEquals(0, list.getCount());
DBHelper db = new DBHelper(mainActivity);
db.insertLoyaltyCard("store", "note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, Color.WHITE);
db.insertLoyaltyCard("store", "note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, Color.WHITE, 0);
assertEquals(View.VISIBLE, helpText.getVisibility());
assertEquals(View.GONE, noMatchingCardsText.getVisibility());
@@ -116,6 +116,104 @@ public class MainActivityTest
assertNotNull(cursor);
}
@Test
public void addFourLoyaltyCardsTwoStarred() // Main screen showing starred cards on top correctly
{
ActivityController activityController = Robolectric.buildActivity(MainActivity.class).create();
Activity mainActivity = (Activity)activityController.get();
activityController.start();
activityController.resume();
TextView helpText = mainActivity.findViewById(R.id.helpText);
TextView noMatchingCardsText = mainActivity.findViewById(R.id.noMatchingCardsText);
ListView list = mainActivity.findViewById(R.id.list);
assertEquals(0, list.getCount());
DBHelper db = new DBHelper(mainActivity);
db.insertLoyaltyCard("storeB", "note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, Color.WHITE, 0);
db.insertLoyaltyCard("storeA", "note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, Color.WHITE, 0);
db.insertLoyaltyCard("storeD", "note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, Color.WHITE, 1);
db.insertLoyaltyCard("storeC", "note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, Color.WHITE, 1);
assertEquals(View.VISIBLE, helpText.getVisibility());
assertEquals(View.GONE, noMatchingCardsText.getVisibility());
assertEquals(View.GONE, list.getVisibility());
activityController.pause();
activityController.resume();
assertEquals(View.GONE, helpText.getVisibility());
assertEquals(View.GONE, noMatchingCardsText.getVisibility());
assertEquals(View.VISIBLE, list.getVisibility());
assertEquals(4, list.getAdapter().getCount());
Cursor cursor = (Cursor)list.getAdapter().getItem(0);
assertNotNull(cursor);
assertEquals("storeC",cursor.getString(cursor.getColumnIndex("store")));
cursor = (Cursor)list.getAdapter().getItem(1);
assertNotNull(cursor);
assertEquals("storeD",cursor.getString(cursor.getColumnIndex("store")));
cursor = (Cursor)list.getAdapter().getItem(2);
assertNotNull(cursor);
assertEquals("storeA",cursor.getString(cursor.getColumnIndex("store")));
cursor = (Cursor)list.getAdapter().getItem(3);
assertNotNull(cursor);
assertEquals("storeB",cursor.getString(cursor.getColumnIndex("store")));
}
@Test
public void testGroups()
{
ActivityController activityController = Robolectric.buildActivity(MainActivity.class).create();
Activity mainActivity = (Activity)activityController.get();
activityController.start();
activityController.resume();
DBHelper db = new DBHelper(mainActivity);
TabLayout groupTabs = mainActivity.findViewById(R.id.groups);
// No group tabs by default
assertEquals(0, groupTabs.getTabCount());
// Having at least one group should create two tabs: One all and one for each group
db.insertGroup("One");
activityController.pause();
activityController.resume();
assertEquals(2, groupTabs.getTabCount());
assertEquals("All", groupTabs.getTabAt(0).getText().toString());
assertEquals("One", groupTabs.getTabAt(1).getText().toString());
// Adding another group should have them sorted alphabetically
db.insertGroup("Alphabetical two");
activityController.pause();
activityController.resume();
assertEquals(3, groupTabs.getTabCount());
assertEquals("All", groupTabs.getTabAt(0).getText().toString());
assertEquals("Alphabetical two", groupTabs.getTabAt(1).getText().toString());
assertEquals("One", groupTabs.getTabAt(2).getText().toString());
// Removing a group should also change the list
db.deleteGroup("Alphabetical two");
activityController.pause();
activityController.resume();
assertEquals(2, groupTabs.getTabCount());
assertEquals("All", groupTabs.getTabAt(0).getText().toString());
assertEquals("One", groupTabs.getTabAt(1).getText().toString());
// Removing the last group should make the tabs disappear
db.deleteGroup("One");
activityController.pause();
activityController.resume();
assertEquals(0, groupTabs.getTabCount());
}
@Test
public void testFiltering()
{
@@ -128,10 +226,16 @@ public class MainActivityTest
TextView helpText = mainActivity.findViewById(R.id.helpText);
TextView noMatchingCardsText = mainActivity.findViewById(R.id.noMatchingCardsText);
ListView list = mainActivity.findViewById(R.id.list);
TabLayout groupTabs = mainActivity.findViewById(R.id.groups);
DBHelper db = new DBHelper(mainActivity);
db.insertLoyaltyCard("The First Store", "Initial note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, Color.WHITE);
db.insertLoyaltyCard("The Second Store", "Secondary note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, Color.WHITE);
db.insertLoyaltyCard("The First Store", "Initial note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, Color.WHITE, 0);
db.insertLoyaltyCard("The Second Store", "Secondary note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, Color.WHITE, 0);
db.insertGroup("Group one");
List<Group> groups = new ArrayList<>();
groups.add(db.getGroup("Group one"));
db.setLoyaltyCardGroups(1, groups);
activityController.pause();
activityController.resume();
@@ -153,6 +257,27 @@ public class MainActivityTest
assertEquals(2, list.getCount());
// Switch to Group one
groupTabs.selectTab(groupTabs.getTabAt(1));
activityController.pause();
activityController.resume();
assertEquals(View.GONE, helpText.getVisibility());
assertEquals(View.GONE, noMatchingCardsText.getVisibility());
assertEquals(View.VISIBLE, list.getVisibility());
assertEquals(1, list.getCount());
// Switch back to all groups
groupTabs.selectTab(groupTabs.getTabAt(0));
assertEquals(View.GONE, helpText.getVisibility());
assertEquals(View.GONE, noMatchingCardsText.getVisibility());
assertEquals(View.VISIBLE, list.getVisibility());
assertEquals(2, list.getCount());
mainActivity.filter = "first";
activityController.pause();
@@ -164,6 +289,27 @@ public class MainActivityTest
assertEquals(1, list.getCount());
// Switch to Group one
groupTabs.selectTab(groupTabs.getTabAt(1));
activityController.pause();
activityController.resume();
assertEquals(View.GONE, helpText.getVisibility());
assertEquals(View.GONE, noMatchingCardsText.getVisibility());
assertEquals(View.VISIBLE, list.getVisibility());
assertEquals(1, list.getCount());
// Switch back to all groups
groupTabs.selectTab(groupTabs.getTabAt(0));
assertEquals(View.GONE, helpText.getVisibility());
assertEquals(View.GONE, noMatchingCardsText.getVisibility());
assertEquals(View.VISIBLE, list.getVisibility());
assertEquals(1, list.getCount());
mainActivity.filter = "initial";
activityController.pause();
@@ -175,6 +321,27 @@ public class MainActivityTest
assertEquals(1, list.getCount());
// Switch to Group one
groupTabs.selectTab(groupTabs.getTabAt(1));
activityController.pause();
activityController.resume();
assertEquals(View.GONE, helpText.getVisibility());
assertEquals(View.GONE, noMatchingCardsText.getVisibility());
assertEquals(View.VISIBLE, list.getVisibility());
assertEquals(1, list.getCount());
// Switch back to all groups
groupTabs.selectTab(groupTabs.getTabAt(0));
assertEquals(View.GONE, helpText.getVisibility());
assertEquals(View.GONE, noMatchingCardsText.getVisibility());
assertEquals(View.VISIBLE, list.getVisibility());
assertEquals(1, list.getCount());
mainActivity.filter = "second";
activityController.pause();
@@ -186,6 +353,27 @@ public class MainActivityTest
assertEquals(1, list.getCount());
// Switch to Group one
groupTabs.selectTab(groupTabs.getTabAt(1));
activityController.pause();
activityController.resume();
assertEquals(View.GONE, helpText.getVisibility());
assertEquals(View.VISIBLE, noMatchingCardsText.getVisibility());
assertEquals(View.VISIBLE, list.getVisibility());
assertEquals(0, list.getCount());
// Switch back to all groups
groupTabs.selectTab(groupTabs.getTabAt(0));
assertEquals(View.GONE, helpText.getVisibility());
assertEquals(View.GONE, noMatchingCardsText.getVisibility());
assertEquals(View.VISIBLE, list.getVisibility());
assertEquals(1, list.getCount());
mainActivity.filter = "company";
activityController.pause();
@@ -197,6 +385,27 @@ public class MainActivityTest
assertEquals(0, list.getCount());
// Switch to Group one
groupTabs.selectTab(groupTabs.getTabAt(1));
activityController.pause();
activityController.resume();
assertEquals(View.GONE, helpText.getVisibility());
assertEquals(View.VISIBLE, noMatchingCardsText.getVisibility());
assertEquals(View.VISIBLE, list.getVisibility());
assertEquals(0, list.getCount());
// Switch back to all groups
groupTabs.selectTab(groupTabs.getTabAt(0));
assertEquals(View.GONE, helpText.getVisibility());
assertEquals(View.VISIBLE, noMatchingCardsText.getVisibility());
assertEquals(View.VISIBLE, list.getVisibility());
assertEquals(0, list.getCount());
mainActivity.filter = "";
activityController.pause();
@@ -207,5 +416,26 @@ public class MainActivityTest
assertEquals(View.VISIBLE, list.getVisibility());
assertEquals(2, list.getCount());
// Switch to Group one
groupTabs.selectTab(groupTabs.getTabAt(1));
activityController.pause();
activityController.resume();
assertEquals(View.GONE, helpText.getVisibility());
assertEquals(View.GONE, noMatchingCardsText.getVisibility());
assertEquals(View.VISIBLE, list.getVisibility());
assertEquals(1, list.getCount());
// Switch back to all groups
groupTabs.selectTab(groupTabs.getTabAt(0));
assertEquals(View.GONE, helpText.getVisibility());
assertEquals(View.GONE, noMatchingCardsText.getVisibility());
assertEquals(View.VISIBLE, list.getVisibility());
assertEquals(2, list.getCount());
}
}

BIN
banner.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@@ -1,11 +1,16 @@
# Catima
![Android CI](https://github.com/TheLastProject/Catima/workflows/Android%20CI/badge.svg)
[![Translation status](https://hosted.weblate.org/widgets/catima/-/svg-badge.svg)](https://hosted.weblate.org/engage/catima/)
<a href="https://f-droid.org/repository/browse/?fdid=me.hackerchick.catima" target="_blank">
<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" height="90"/></a>
<a href="https://play.google.com/store/apps/details?id=me.hackerchick.catima" target="_blank">
<img src="https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png" alt="Get it on Google Play" height="90"/></a>
![Logo](https://github.com/TheLastProject/Catima/raw/master/app/src/main/ic_launcher-playstore.png)
*Logo by [Rose (TangentFoxy)](http://github.com/TangentFoxy)*
Stores all of your store loyalty cards on your phone, removing the need to carry them around. Currently the following barcode types are supported:
- AZTEC
@@ -22,16 +27,14 @@ Stores all of your store loyalty cards on your phone, removing the need to carry
# Screenshots
[<img src="https://github.com/TheLastProject/Catima/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-01.png" width=250>](https://github.com/TheLastProject/Catima/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-01.png)
[<img src="https://github.com/TheLastProject/Catima/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-02.png" width=250>](https://github.com/TheLastProject/Catima/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-02.png)
[<img src="https://github.com/TheLastProject/Catima/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-03.png" width=250>](https://github.com/TheLastProject/Catima/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-03.png)
[<img src="https://github.com/TheLastProject/Catima/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-04.png" width=250>](https://github.com/TheLastProject/Catima/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-04.png)
[<img src="https://github.com/TheLastProject/Catima/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-05.png" width=250>](https://github.com/TheLastProject/Catima/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-05.png)
[<img src="https://github.com/TheLastProject/Catima/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-06.png" width=250>](https://github.com/TheLastProject/Catima/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-06.png)
[<img src="https://github.com/TheLastProject/Catima/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-07.png" width=250>](https://github.com/TheLastProject/Catima/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-07.png)
[<img src="https://github.com/TheLastProject/Catima/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-08.png" width=250>](https://github.com/TheLastProject/Catima/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-08.png)
[<img src="https://github.com/TheLastProject/Catima/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-09.png" width=250>](https://github.com/TheLastProject/Catima/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-09.png)
[<img src="https://github.com/TheLastProject/Catima/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-10.png" width=250>](https://github.com/TheLastProject/Catima/raw/master/metadata/en-US/images/phoneScreenshots/screenshot-10.png)
[<img src="https://github.com/TheLastProject/Catima/raw/master/fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot-01.png" width=250>](https://github.com/TheLastProject/Catima/raw/master/fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot-01.png)
[<img src="https://github.com/TheLastProject/Catima/raw/master/fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot-02.png" width=250>](https://github.com/TheLastProject/Catima/raw/master/fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot-02.png)
[<img src="https://github.com/TheLastProject/Catima/raw/master/fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot-03.png" width=250>](https://github.com/TheLastProject/Catima/raw/master/fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot-03.png)
[<img src="https://github.com/TheLastProject/Catima/raw/master/fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot-04.png" width=250>](https://github.com/TheLastProject/Catima/raw/master/fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot-04.png)
[<img src="https://github.com/TheLastProject/Catima/raw/master/fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot-05.png" width=250>](https://github.com/TheLastProject/Catima/raw/master/fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot-05.png)
[<img src="https://github.com/TheLastProject/Catima/raw/master/fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot-06.png" width=250>](https://github.com/TheLastProject/Catima/raw/master/fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot-06.png)
[<img src="https://github.com/TheLastProject/Catima/raw/master/fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot-07.png" width=250>](https://github.com/TheLastProject/Catima/raw/master/fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot-07.png)
[<img src="https://github.com/TheLastProject/Catima/raw/master/fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot-08.png" width=250>](https://github.com/TheLastProject/Catima/raw/master/fastlane/metadata/android/en-US/images/phoneScreenshots/screenshot-08.png)
# Migrating from other apps
@@ -54,7 +57,7 @@ Windows:
# Translating
We are going to set up a translation platform soon, please check back later.
Please contribute through [our Weblate page](https://hosted.weblate.org/projects/catima/).
# Thanks

View File

@@ -21,15 +21,18 @@ As Catima is based on Loyalty Card Keychain, importing your data from it is very
![Step 6](step_6.png)
## 7. Open Catima
TODO: Logo
![Step 7](step_7.png)
## 8. Press the Import/Export button in the top right
## 8. Press the More Options button in the top right
![Step 8](step_8.png)
## 9. Press From Filesystem
## 9. Press Import/Export
![Step 9](step_9.png)
## 10. Choose the file you saved in step 5
## 10. Press From Filesystem
![Step 10](step_10.png)
## 11. Choose the file you saved in step 5
![Step 11](step_11.png)
## That's it, you've succesfully imported your Loyalty Card Keychain database into Catima

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 116 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 90 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 84 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 85 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 68 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 69 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 82 KiB

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