mirror of
https://github.com/CatimaLoyalty/Android.git
synced 2025-12-24 15:47:53 -05:00
Compare commits
143 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
252efabdc6 | ||
|
|
018efaeebb | ||
|
|
56522b42e2 | ||
|
|
122a80f29c | ||
|
|
9363a31a77 | ||
|
|
21f3c58e32 | ||
|
|
205f7bb59d | ||
|
|
1cc0f11a5d | ||
|
|
d2168f4d91 | ||
|
|
ba46e2a0b8 | ||
|
|
6fb102d9b5 | ||
|
|
83b3d5d31c | ||
|
|
c93b7527ad | ||
|
|
07bfc95bf9 | ||
|
|
d34554ac07 | ||
|
|
63754798ef | ||
|
|
9ca4b7fbe1 | ||
|
|
4d6021ad8c | ||
|
|
f76df0d252 | ||
|
|
60a172813f | ||
|
|
01025e862a | ||
|
|
7e9c7db813 | ||
|
|
d8b121f503 | ||
|
|
79a143ebaf | ||
|
|
0b78f36784 | ||
|
|
a4c24c6436 | ||
|
|
f59f9ddec8 | ||
|
|
d21f2d12c9 | ||
|
|
30971b7e85 | ||
|
|
7ab9e0f8b0 | ||
|
|
6e63543cd1 | ||
|
|
d2de5db792 | ||
|
|
a1bb6e3bed | ||
|
|
e937fc60a7 | ||
|
|
1213ee99da | ||
|
|
7dadce600b | ||
|
|
bb1eae6f79 | ||
|
|
cf871e9606 | ||
|
|
0b92a12694 | ||
|
|
7636c3648c | ||
|
|
f62352cf05 | ||
|
|
8fbbfb137e | ||
|
|
afb960257e | ||
|
|
b9e152e3c4 | ||
|
|
bd1d33867d | ||
|
|
eba1ed63a6 | ||
|
|
74fbdc7a5e | ||
|
|
ef61aaeac6 | ||
|
|
b5ee7d7a2d | ||
|
|
f51ad0295a | ||
|
|
d0d3289efa | ||
|
|
353d8a7ecd | ||
|
|
d221969b5e | ||
|
|
97553e9253 | ||
|
|
11b839dabe | ||
|
|
bc5252b2ef | ||
|
|
5a6b7944b1 | ||
|
|
a9794daca5 | ||
|
|
aee59550e4 | ||
|
|
dcc5e5921c | ||
|
|
12df426dea | ||
|
|
9b4085e955 | ||
|
|
a4f7d4e131 | ||
|
|
70b313021c | ||
|
|
d1bdab5c66 | ||
|
|
237405279f | ||
|
|
810fa5f621 | ||
|
|
64b709266b | ||
|
|
f9a66ba3a0 | ||
|
|
bfd36bae9f | ||
|
|
15116ab673 | ||
|
|
d49d019f63 | ||
|
|
ba9d1f3891 | ||
|
|
01596d5637 | ||
|
|
9bd71d5694 | ||
|
|
6d1e7ee3bb | ||
|
|
0ce71039f1 | ||
|
|
a03b89bc93 | ||
|
|
156b720102 | ||
|
|
c13b5dda18 | ||
|
|
ffa39000f7 | ||
|
|
40de4a8dc4 | ||
|
|
db22703ec0 | ||
|
|
8b59ef152a | ||
|
|
01bd91ff66 | ||
|
|
b558d2178a | ||
|
|
efc8e6ae33 | ||
|
|
aaf481a82c | ||
|
|
d03d96b194 | ||
|
|
1c92787254 | ||
|
|
bc808d3045 | ||
|
|
b265fadec3 | ||
|
|
d2f5cd05b5 | ||
|
|
2dba2c63a8 | ||
|
|
3fffcdbd67 | ||
|
|
6c53b8e9ca | ||
|
|
8ac2fe575c | ||
|
|
b4a992abf8 | ||
|
|
45c71f01c2 | ||
|
|
9f914d4943 | ||
|
|
3f25f99236 | ||
|
|
d54f574558 | ||
|
|
0af1935d20 | ||
|
|
9a37d917f7 | ||
|
|
d4f62435ae | ||
|
|
0baf1ba348 | ||
|
|
4a568d02d3 | ||
|
|
1dc30ebaad | ||
|
|
4fbedbc30c | ||
|
|
1675ebf1dc | ||
|
|
2d6dfdcfdf | ||
|
|
bacb5b97ec | ||
|
|
940be02851 | ||
|
|
34f8c830fd | ||
|
|
ee7f5c0498 | ||
|
|
9a5c3bc661 | ||
|
|
db9ec7daea | ||
|
|
16b683fe9d | ||
|
|
0e10c644d8 | ||
|
|
fd283a5e06 | ||
|
|
e406320f17 | ||
|
|
40d36ec8fb | ||
|
|
99a3849942 | ||
|
|
26a6a786dc | ||
|
|
55a5884852 | ||
|
|
c20ce1e555 | ||
|
|
a90899d41e | ||
|
|
413d479ea0 | ||
|
|
8ff452cfd2 | ||
|
|
54c279ec89 | ||
|
|
000d7722da | ||
|
|
0bb0df21e4 | ||
|
|
0dcfc11c53 | ||
|
|
58f8a6a929 | ||
|
|
6a80c633a8 | ||
|
|
031bfd94e0 | ||
|
|
45443abaf9 | ||
|
|
b81da59bcc | ||
|
|
86f3530d95 | ||
|
|
863a378bf0 | ||
|
|
a9d81b8321 | ||
|
|
46865f349b | ||
|
|
1d1109d665 |
66
CHANGELOG.md
66
CHANGELOG.md
@@ -1,6 +1,70 @@
|
||||
# Changelog
|
||||
|
||||
## Unreleased
|
||||
## v1.12 (2021-03-30)
|
||||
|
||||
Changes:
|
||||
|
||||
- Support importing [Fidme](https://play.google.com/store/apps/details?id=fr.snapp.fidme) exports
|
||||
- Allow importing a card from a picture stored in the user's Android gallery
|
||||
- Fix multiline note cutoff
|
||||
- Change "Thank you" text on privacy dialog to "Accept" because Huawei is overly pedantic
|
||||
|
||||
## v1.11 (2021-03-21)
|
||||
|
||||
Changes:
|
||||
|
||||
- Add privacy policy dialog on first start (required by Huawei)
|
||||
|
||||
## v1.10 (2021-03-07)
|
||||
|
||||
Changes:
|
||||
|
||||
- Support importing [Voucher Vault](https://github.com/tim-smart/vouchervault/) exports
|
||||
- Option to keep the screen on while viewing a loyalty card
|
||||
- Option to suspend the lock screen while viewing a loyalty card
|
||||
|
||||
## v1.9.2 (2021-02-24)
|
||||
|
||||
Changes:
|
||||
|
||||
- Fix parsing balance for countries using space as separator
|
||||
|
||||
## v1.9.1 (2021-02-23)
|
||||
|
||||
Changes:
|
||||
|
||||
- Improve balance parsing logic
|
||||
- Fix currency decimal display on main screen
|
||||
|
||||
## v1.9 (2021-02-22)
|
||||
|
||||
Changes:
|
||||
|
||||
- Add balance support
|
||||
- Reorganize barcode tab of edit view
|
||||
|
||||
## v1.8.1 (2021-02-12)
|
||||
|
||||
Changes:
|
||||
|
||||
- Fix Crash on versions before Android 7
|
||||
|
||||
## v1.8 (2021-01-28)
|
||||
|
||||
Changes:
|
||||
|
||||
- Add support for scaling the barcode when moving to top to fit even more small scanners
|
||||
- Fix bottom sheet jumping after switching to fullscreen
|
||||
- Make header in loyalty card view small in landscape mode
|
||||
- Fix cards not staying in group when group gets renamed
|
||||
|
||||
## v1.7.1 (2021-01-18)
|
||||
|
||||
Changes:
|
||||
|
||||
- Fix crash on switching to barcode tab in edit view if there is no barcode
|
||||
|
||||
## v1.7.0 (2021-01-18)
|
||||
|
||||
Changes:
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ android {
|
||||
applicationId "me.hackerchick.catima"
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 29
|
||||
versionCode 55
|
||||
versionName "1.7.0"
|
||||
versionCode 66
|
||||
versionName "1.12"
|
||||
|
||||
vectorDrawables.useSupportLibrary true
|
||||
}
|
||||
@@ -72,7 +72,7 @@ dependencies {
|
||||
|
||||
// Third-party
|
||||
implementation 'com.journeyapps:zxing-android-embedded:4.1.0@aar'
|
||||
implementation 'com.google.zxing:core:3.4.1'
|
||||
implementation 'com.google.zxing:core:3.3.3' // Do not upgrade past 3.3.3! Causes a crash on versions before Android 7
|
||||
implementation 'org.apache.commons:commons-csv:1.8'
|
||||
implementation 'com.jaredrummler:colorpicker:1.1.0'
|
||||
implementation 'com.google.guava:guava:30.1-jre'
|
||||
|
||||
@@ -49,7 +49,6 @@
|
||||
<activity
|
||||
android:name=".LoyaltyCardViewActivity"
|
||||
android:theme="@style/AppTheme.NoActionBar"
|
||||
android:label=""
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:exported="true"/>
|
||||
<activity
|
||||
|
||||
@@ -4,9 +4,12 @@ import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.graphics.Color;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -15,21 +18,23 @@ 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 = 7;
|
||||
public static final int DATABASE_VERSION = 8;
|
||||
|
||||
static class LoyaltyCardDbGroups
|
||||
public static class LoyaltyCardDbGroups
|
||||
{
|
||||
public static final String TABLE = "groups";
|
||||
public static final String ID = "_id";
|
||||
public static final String ORDER = "orderId";
|
||||
}
|
||||
|
||||
static class LoyaltyCardDbIds
|
||||
public static class LoyaltyCardDbIds
|
||||
{
|
||||
public static final String TABLE = "cards";
|
||||
public static final String ID = "_id";
|
||||
public static final String STORE = "store";
|
||||
public static final String EXPIRY = "expiry";
|
||||
public static final String BALANCE = "balance";
|
||||
public static final String BALANCE_TYPE = "balancetype";
|
||||
public static final String NOTE = "note";
|
||||
public static final String HEADER_COLOR = "headercolor";
|
||||
public static final String HEADER_TEXT_COLOR = "headertextcolor";
|
||||
@@ -38,7 +43,7 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
public static final String STAR_STATUS = "starstatus";
|
||||
}
|
||||
|
||||
static class LoyaltyCardDbIdsGroups
|
||||
public static class LoyaltyCardDbIdsGroups
|
||||
{
|
||||
public static final String TABLE = "cardsGroups";
|
||||
public static final String cardID = "cardId";
|
||||
@@ -59,11 +64,14 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
LoyaltyCardDbGroups.ORDER + " INTEGER DEFAULT '0')");
|
||||
|
||||
// create table for cards
|
||||
// Balance is TEXT and not REAL to be able to store a BigDecimal without precision loss
|
||||
db.execSQL("create table " + LoyaltyCardDbIds.TABLE + "(" +
|
||||
LoyaltyCardDbIds.ID + " INTEGER primary key autoincrement," +
|
||||
LoyaltyCardDbIds.STORE + " TEXT not null," +
|
||||
LoyaltyCardDbIds.NOTE + " TEXT not null," +
|
||||
LoyaltyCardDbIds.EXPIRY + " INTEGER," +
|
||||
LoyaltyCardDbIds.BALANCE + " TEXT not null DEFAULT '0'," +
|
||||
LoyaltyCardDbIds.BALANCE_TYPE + " TEXT," +
|
||||
LoyaltyCardDbIds.HEADER_COLOR + " INTEGER," +
|
||||
LoyaltyCardDbIds.HEADER_TEXT_COLOR + " INTEGER," +
|
||||
LoyaltyCardDbIds.CARD_ID + " TEXT not null," +
|
||||
@@ -127,9 +135,18 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE
|
||||
+ " ADD COLUMN " + LoyaltyCardDbIds.EXPIRY + " INTEGER");
|
||||
}
|
||||
|
||||
if(oldVersion < 8 && newVersion >= 8)
|
||||
{
|
||||
db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE
|
||||
+ " ADD COLUMN " + LoyaltyCardDbIds.BALANCE + " TEXT not null DEFAULT '0'");
|
||||
db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE
|
||||
+ " ADD COLUMN " + LoyaltyCardDbIds.BALANCE_TYPE + " TEXT");
|
||||
}
|
||||
}
|
||||
|
||||
public long insertLoyaltyCard(final String store, final String note, final Date expiry,
|
||||
final BigDecimal balance, final Currency balanceType,
|
||||
final String cardId, final String barcodeType,
|
||||
final Integer headerColor, final int starStatus)
|
||||
{
|
||||
@@ -138,6 +155,8 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
contentValues.put(LoyaltyCardDbIds.STORE, store);
|
||||
contentValues.put(LoyaltyCardDbIds.NOTE, note);
|
||||
contentValues.put(LoyaltyCardDbIds.EXPIRY, expiry != null ? expiry.getTime() : null);
|
||||
contentValues.put(LoyaltyCardDbIds.BALANCE, balance.toString());
|
||||
contentValues.put(LoyaltyCardDbIds.BALANCE_TYPE, balanceType != null ? balanceType.getCurrencyCode() : null);
|
||||
contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId);
|
||||
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType);
|
||||
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
|
||||
@@ -147,8 +166,30 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
return newId;
|
||||
}
|
||||
|
||||
public boolean insertLoyaltyCard(final SQLiteDatabase db, final String store,
|
||||
final String note, final Date expiry, final BigDecimal balance,
|
||||
final Currency balanceType, final String cardId,
|
||||
final String barcodeType, final Integer headerColor,
|
||||
final int starStatus)
|
||||
{
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(LoyaltyCardDbIds.STORE, store);
|
||||
contentValues.put(LoyaltyCardDbIds.NOTE, note);
|
||||
contentValues.put(LoyaltyCardDbIds.EXPIRY, expiry != null ? expiry.getTime() : null);
|
||||
contentValues.put(LoyaltyCardDbIds.BALANCE, balance.toString());
|
||||
contentValues.put(LoyaltyCardDbIds.BALANCE_TYPE, balanceType != null ? balanceType.getCurrencyCode() : null);
|
||||
contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId);
|
||||
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType);
|
||||
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
|
||||
contentValues.put(LoyaltyCardDbIds.HEADER_TEXT_COLOR, Color.WHITE);
|
||||
contentValues.put(LoyaltyCardDbIds.STAR_STATUS,starStatus);
|
||||
final long newId = db.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
|
||||
return (newId != -1);
|
||||
}
|
||||
|
||||
public boolean insertLoyaltyCard(final SQLiteDatabase db, final int id, final String store,
|
||||
final String note, final Date expiry, final String cardId,
|
||||
final String note, final Date expiry, final BigDecimal balance,
|
||||
final Currency balanceType, final String cardId,
|
||||
final String barcodeType, final Integer headerColor,
|
||||
final int starStatus)
|
||||
{
|
||||
@@ -157,6 +198,8 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
contentValues.put(LoyaltyCardDbIds.STORE, store);
|
||||
contentValues.put(LoyaltyCardDbIds.NOTE, note);
|
||||
contentValues.put(LoyaltyCardDbIds.EXPIRY, expiry != null ? expiry.getTime() : null);
|
||||
contentValues.put(LoyaltyCardDbIds.BALANCE, balance.toString());
|
||||
contentValues.put(LoyaltyCardDbIds.BALANCE_TYPE, balanceType != null ? balanceType.getCurrencyCode() : null);
|
||||
contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId);
|
||||
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType);
|
||||
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
|
||||
@@ -167,7 +210,8 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
}
|
||||
|
||||
public boolean updateLoyaltyCard(final int id, final String store, final String note,
|
||||
final Date expiry, final String cardId,
|
||||
final Date expiry, final BigDecimal balance,
|
||||
final Currency balanceType, final String cardId,
|
||||
final String barcodeType, final Integer headerColor)
|
||||
{
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
@@ -175,6 +219,8 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
contentValues.put(LoyaltyCardDbIds.STORE, store);
|
||||
contentValues.put(LoyaltyCardDbIds.NOTE, note);
|
||||
contentValues.put(LoyaltyCardDbIds.EXPIRY, expiry != null ? expiry.getTime() : null);
|
||||
contentValues.put(LoyaltyCardDbIds.BALANCE, balance.toString());
|
||||
contentValues.put(LoyaltyCardDbIds.BALANCE_TYPE, balanceType != null ? balanceType.getCurrencyCode() : null);
|
||||
contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId);
|
||||
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType);
|
||||
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
|
||||
@@ -532,33 +578,66 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
{
|
||||
if (newName.isEmpty()) return false;
|
||||
|
||||
boolean success = false;
|
||||
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(LoyaltyCardDbGroups.ID, newName);
|
||||
ContentValues groupContentValues = new ContentValues();
|
||||
groupContentValues.put(LoyaltyCardDbGroups.ID, newName);
|
||||
|
||||
ContentValues lookupContentValues = new ContentValues();
|
||||
lookupContentValues.put(LoyaltyCardDbIdsGroups.groupID, newName);
|
||||
|
||||
db.beginTransaction();
|
||||
try {
|
||||
int rowsUpdated = db.update(LoyaltyCardDbGroups.TABLE, contentValues,
|
||||
// Update group name
|
||||
int groupsChanged = db.update(LoyaltyCardDbGroups.TABLE, groupContentValues,
|
||||
LoyaltyCardDbGroups.ID + "=?",
|
||||
new String[]{groupName});
|
||||
return (rowsUpdated == 1);
|
||||
} catch (android.database.sqlite.SQLiteConstraintException _e) {
|
||||
return false;
|
||||
|
||||
// Also update lookup tables
|
||||
db.update(LoyaltyCardDbIdsGroups.TABLE, lookupContentValues,
|
||||
LoyaltyCardDbIdsGroups.groupID + "=?",
|
||||
new String[]{groupName});
|
||||
|
||||
if (groupsChanged == 1) {
|
||||
db.setTransactionSuccessful();
|
||||
success = true;
|
||||
}
|
||||
} catch (SQLiteException e) {
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public boolean deleteGroup(final String groupName)
|
||||
{
|
||||
boolean success = false;
|
||||
|
||||
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});
|
||||
db.beginTransaction();
|
||||
try {
|
||||
// Delete group
|
||||
int groupsDeleted = db.delete(LoyaltyCardDbGroups.TABLE,
|
||||
LoyaltyCardDbGroups.ID + " = ? ",
|
||||
new String[]{groupName});
|
||||
|
||||
return (rowsDeleted == 1);
|
||||
// And delete lookup table entries associated with this group
|
||||
db.delete(LoyaltyCardDbIdsGroups.TABLE,
|
||||
LoyaltyCardDbIdsGroups.groupID + " = ? ",
|
||||
new String[]{groupName});
|
||||
|
||||
if (groupsDeleted == 1) {
|
||||
db.setTransactionSuccessful();
|
||||
success = true;
|
||||
}
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public int getGroupCardCount(final String groupName)
|
||||
|
||||
@@ -2,7 +2,8 @@ package protect.card_locker;
|
||||
|
||||
public enum DataFormat
|
||||
{
|
||||
CSV,
|
||||
|
||||
Catima,
|
||||
Fidme,
|
||||
VoucherVault
|
||||
;
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
* Interface for a class which can import the contents of a stream
|
||||
* into the database.
|
||||
*/
|
||||
public interface DatabaseImporter
|
||||
{
|
||||
/**
|
||||
* Import data from the input stream in a given format into
|
||||
* the database.
|
||||
* @throws IOException
|
||||
* @throws FormatException
|
||||
*/
|
||||
void importData(DBHelper db, InputStreamReader input) throws IOException, FormatException, InterruptedException;
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
import android.provider.ContactsContract;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
@@ -28,6 +29,7 @@ import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.text.DateFormat;
|
||||
|
||||
public class ImportExportActivity extends AppCompatActivity
|
||||
{
|
||||
@@ -35,10 +37,14 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
|
||||
private static final int PERMISSIONS_EXTERNAL_STORAGE = 1;
|
||||
private static final int CHOOSE_EXPORT_LOCATION = 2;
|
||||
private static final int CHOOSE_EXPORTED_FILE = 3;
|
||||
private static final int IMPORT = 3;
|
||||
|
||||
private ImportExportTask importExporter;
|
||||
|
||||
private String importAlertTitle;
|
||||
private String importAlertMessage;
|
||||
private DataFormat importDataFormat;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
@@ -93,7 +99,7 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
chooseFileWithIntent(intentGetContentAction, CHOOSE_EXPORTED_FILE);
|
||||
chooseImportType(intentGetContentAction);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -106,12 +112,60 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
chooseFileWithIntent(intentPickAction, CHOOSE_EXPORTED_FILE);
|
||||
chooseImportType(intentPickAction);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void startImport(final InputStream target, final Uri targetUri)
|
||||
private void chooseImportType(Intent baseIntent) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.chooseImportType)
|
||||
.setItems(R.array.import_types_array, (dialog, which) -> {
|
||||
switch (which) {
|
||||
// Catima
|
||||
case 0:
|
||||
importAlertTitle = getString(R.string.importCatima);
|
||||
importAlertMessage = getString(R.string.importCatimaMessage);
|
||||
importDataFormat = DataFormat.Catima;
|
||||
break;
|
||||
// Fidme
|
||||
case 1:
|
||||
importAlertTitle = getString(R.string.importFidme);
|
||||
importAlertMessage = getString(R.string.importFidmeMessage);
|
||||
importDataFormat = DataFormat.Fidme;
|
||||
break;
|
||||
// Loyalty Card Keychain
|
||||
case 2:
|
||||
importAlertTitle = getString(R.string.importLoyaltyCardKeychain);
|
||||
importAlertMessage = getString(R.string.importLoyaltyCardKeychainMessage);
|
||||
importDataFormat = DataFormat.Catima;
|
||||
break;
|
||||
// Voucher Vault
|
||||
case 3:
|
||||
importAlertTitle = getString(R.string.importVoucherVault);
|
||||
importAlertMessage = getString(R.string.importVoucherVaultMessage);
|
||||
importDataFormat = DataFormat.VoucherVault;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown DataFormat");
|
||||
}
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(importAlertTitle)
|
||||
.setMessage(importAlertMessage)
|
||||
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
chooseFileWithIntent(baseIntent, IMPORT);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void startImport(final InputStream target, final Uri targetUri, final DataFormat dataFormat)
|
||||
{
|
||||
ImportExportTask.TaskCompleteListener listener = new ImportExportTask.TaskCompleteListener()
|
||||
{
|
||||
@@ -123,7 +177,7 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
};
|
||||
|
||||
importExporter = new ImportExportTask(ImportExportActivity.this,
|
||||
DataFormat.CSV, target, listener);
|
||||
dataFormat, target, listener);
|
||||
importExporter.execute();
|
||||
}
|
||||
|
||||
@@ -139,7 +193,7 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
};
|
||||
|
||||
importExporter = new ImportExportTask(ImportExportActivity.this,
|
||||
DataFormat.CSV, target, listener);
|
||||
DataFormat.Catima, target, listener);
|
||||
importExporter.execute();
|
||||
}
|
||||
|
||||
@@ -294,7 +348,7 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
{
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (resultCode != RESULT_OK || (requestCode != CHOOSE_EXPORT_LOCATION && requestCode != CHOOSE_EXPORTED_FILE))
|
||||
if (resultCode != RESULT_OK)
|
||||
{
|
||||
Log.w(TAG, "Failed onActivityResult(), result=" + resultCode);
|
||||
return;
|
||||
@@ -336,8 +390,9 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
reader = new FileInputStream(new File(uri.toString()));
|
||||
}
|
||||
|
||||
Log.e(TAG, "Starting file export with: " + uri.toString());
|
||||
startImport(reader, uri);
|
||||
Log.e(TAG, "Starting file import with: " + uri.toString());
|
||||
|
||||
startImport(reader, uri, importDataFormat);
|
||||
}
|
||||
}
|
||||
catch(FileNotFoundException e)
|
||||
|
||||
@@ -13,6 +13,9 @@ import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import protect.card_locker.importexport.MultiFormatExporter;
|
||||
import protect.card_locker.importexport.MultiFormatImporter;
|
||||
|
||||
class ImportExportTask extends AsyncTask<Void, Void, Boolean>
|
||||
{
|
||||
private static final String TAG = "Catima";
|
||||
@@ -58,16 +61,8 @@ class ImportExportTask extends AsyncTask<Void, Void, Boolean>
|
||||
{
|
||||
boolean result = false;
|
||||
|
||||
try
|
||||
{
|
||||
InputStreamReader reader = new InputStreamReader(stream, Charset.forName("UTF-8"));
|
||||
result = MultiFormatImporter.importData(db, reader, format);
|
||||
reader.close();
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
Log.e(TAG, "Unable to import file", e);
|
||||
}
|
||||
|
||||
result = MultiFormatImporter.importData(db, stream, format);
|
||||
|
||||
Log.i(TAG, "Import result: " + result);
|
||||
|
||||
|
||||
@@ -4,12 +4,16 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import java.io.InvalidObjectException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
|
||||
public class ImportURIHelper {
|
||||
private static final String STORE = DBHelper.LoyaltyCardDbIds.STORE;
|
||||
private static final String NOTE = DBHelper.LoyaltyCardDbIds.NOTE;
|
||||
private static final String EXPIRY = DBHelper.LoyaltyCardDbIds.EXPIRY;
|
||||
private static final String BALANCE = DBHelper.LoyaltyCardDbIds.BALANCE;
|
||||
private static final String BALANCE_TYPE = DBHelper.LoyaltyCardDbIds.BALANCE_TYPE;
|
||||
private static final String CARD_ID = DBHelper.LoyaltyCardDbIds.CARD_ID;
|
||||
private static final String BARCODE_TYPE = DBHelper.LoyaltyCardDbIds.BARCODE_TYPE;
|
||||
|
||||
@@ -43,6 +47,8 @@ public class ImportURIHelper {
|
||||
try {
|
||||
// These values are allowed to be null
|
||||
Date expiry = null;
|
||||
BigDecimal balance = new BigDecimal("0");
|
||||
Currency balanceType = null;
|
||||
Integer headerColor = null;
|
||||
Integer headerTextColor = null;
|
||||
|
||||
@@ -52,18 +58,29 @@ public class ImportURIHelper {
|
||||
String barcodeType = uri.getQueryParameter(BARCODE_TYPE);
|
||||
if (store == null || note == null || cardId == null || barcodeType == null) throw new InvalidObjectException("Not a valid import URI");
|
||||
|
||||
String unparsedBalance = uri.getQueryParameter(BALANCE);
|
||||
if(unparsedBalance != null && !unparsedBalance.equals(""))
|
||||
{
|
||||
balance = new BigDecimal(unparsedBalance);
|
||||
}
|
||||
String unparsedBalanceType = uri.getQueryParameter(BALANCE_TYPE);
|
||||
if (unparsedBalanceType != null && !unparsedBalanceType.equals(""))
|
||||
{
|
||||
balanceType = Currency.getInstance(unparsedBalanceType);
|
||||
}
|
||||
String unparsedExpiry = uri.getQueryParameter(EXPIRY);
|
||||
if(unparsedExpiry != null && !unparsedExpiry.equals(""))
|
||||
{
|
||||
expiry = new Date(Long.parseLong(unparsedExpiry));
|
||||
}
|
||||
|
||||
String unparsedHeaderColor = uri.getQueryParameter(HEADER_COLOR);
|
||||
if(unparsedHeaderColor != null)
|
||||
{
|
||||
headerColor = Integer.parseInt(unparsedHeaderColor);
|
||||
}
|
||||
|
||||
return new LoyaltyCard(-1, store, note, expiry, cardId, barcodeType, headerColor, headerTextColor, 0);
|
||||
return new LoyaltyCard(-1, store, note, expiry, balance, balanceType, cardId, barcodeType, headerColor, headerTextColor, 0);
|
||||
} catch (NullPointerException | NumberFormatException ex) {
|
||||
throw new InvalidObjectException("Not a valid import URI");
|
||||
}
|
||||
@@ -77,6 +94,10 @@ public class ImportURIHelper {
|
||||
uriBuilder.path(path);
|
||||
uriBuilder.appendQueryParameter(STORE, loyaltyCard.store);
|
||||
uriBuilder.appendQueryParameter(NOTE, loyaltyCard.note);
|
||||
uriBuilder.appendQueryParameter(BALANCE, loyaltyCard.balance.toString());
|
||||
if (loyaltyCard.balanceType != null) {
|
||||
uriBuilder.appendQueryParameter(BALANCE_TYPE, loyaltyCard.balanceType.getCurrencyCode());
|
||||
}
|
||||
if (loyaltyCard.expiry != null) {
|
||||
uriBuilder.appendQueryParameter(EXPIRY, String.valueOf(loyaltyCard.expiry.getTime()));
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@ package protect.card_locker;
|
||||
|
||||
import android.database.Cursor;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -13,6 +14,8 @@ public class LoyaltyCard
|
||||
public final String store;
|
||||
public final String note;
|
||||
public final Date expiry;
|
||||
public final BigDecimal balance;
|
||||
public final Currency balanceType;
|
||||
public final String cardId;
|
||||
public final String barcodeType;
|
||||
|
||||
@@ -24,7 +27,8 @@ public class LoyaltyCard
|
||||
|
||||
public final int starStatus;
|
||||
|
||||
public LoyaltyCard(final int id, final String store, final String note, final Date expiry, final String cardId,
|
||||
public LoyaltyCard(final int id, final String store, final String note, final Date expiry,
|
||||
final BigDecimal balance, final Currency balanceType, final String cardId,
|
||||
final String barcodeType, final Integer headerColor, final Integer headerTextColor,
|
||||
final int starStatus)
|
||||
{
|
||||
@@ -32,6 +36,8 @@ public class LoyaltyCard
|
||||
this.store = store;
|
||||
this.note = note;
|
||||
this.expiry = expiry;
|
||||
this.balance = balance;
|
||||
this.balanceType = balanceType;
|
||||
this.cardId = cardId;
|
||||
this.barcodeType = barcodeType;
|
||||
this.headerColor = headerColor;
|
||||
@@ -45,17 +51,25 @@ public class LoyaltyCard
|
||||
String store = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STORE));
|
||||
String note = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE));
|
||||
long expiryLong = cursor.getLong(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.EXPIRY));
|
||||
BigDecimal balance = new BigDecimal(cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE)));
|
||||
String cardId = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID));
|
||||
String barcodeType = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE));
|
||||
int starred = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS));
|
||||
|
||||
int balanceTypeColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE);
|
||||
int headerColorColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_COLOR);
|
||||
int headerTextColorColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR);
|
||||
|
||||
Currency balanceType = null;
|
||||
Date expiry = null;
|
||||
Integer headerColor = null;
|
||||
Integer headerTextColor = null;
|
||||
|
||||
if (cursor.isNull(balanceTypeColumn) == false)
|
||||
{
|
||||
balanceType = Currency.getInstance(cursor.getString(balanceTypeColumn));
|
||||
}
|
||||
|
||||
if(expiryLong > 0)
|
||||
{
|
||||
expiry = new Date(expiryLong);
|
||||
@@ -71,6 +85,6 @@ public class LoyaltyCard
|
||||
headerTextColor = cursor.getInt(headerTextColorColumn);
|
||||
}
|
||||
|
||||
return new LoyaltyCard(id, store, note, expiry, cardId, barcodeType, headerColor, headerTextColor, starred);
|
||||
return new LoyaltyCard(id, store, note, expiry, balance, balanceType, cardId, barcodeType, headerColor, headerTextColor, starred);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import android.widget.CursorAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
@@ -41,6 +42,7 @@ class LoyaltyCardCursorAdapter extends CursorAdapter
|
||||
ImageView thumbnail = view.findViewById(R.id.thumbnail);
|
||||
TextView storeField = view.findViewById(R.id.store);
|
||||
TextView noteField = view.findViewById(R.id.note);
|
||||
TextView balanceField = view.findViewById(R.id.balance);
|
||||
TextView expiryField = view.findViewById(R.id.expiry);
|
||||
ImageView star = view.findViewById(R.id.star);
|
||||
|
||||
@@ -63,6 +65,15 @@ class LoyaltyCardCursorAdapter extends CursorAdapter
|
||||
noteField.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if(!loyaltyCard.balance.equals(new BigDecimal("0"))) {
|
||||
balanceField.setVisibility(View.VISIBLE);
|
||||
balanceField.setText(context.getString(R.string.balanceSentence, Utils.formatBalance(context, loyaltyCard.balance, loyaltyCard.balanceType)));
|
||||
}
|
||||
else
|
||||
{
|
||||
balanceField.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if(loyaltyCard.expiry != null)
|
||||
{
|
||||
expiryField.setVisibility(View.VISIBLE);
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.DatePickerDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.google.android.material.chip.Chip;
|
||||
@@ -19,6 +22,7 @@ import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
|
||||
import android.os.LocaleList;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
@@ -30,26 +34,29 @@ import android.view.ViewTreeObserver;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.AutoCompleteTextView;
|
||||
import android.widget.Button;
|
||||
import android.widget.CalendarView;
|
||||
import android.widget.DatePicker;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.jaredrummler.android.colorpicker.ColorPickerDialog;
|
||||
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener;
|
||||
|
||||
import java.io.InvalidObjectException;
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class LoyaltyCardEditActivity extends AppCompatActivity
|
||||
{
|
||||
@@ -62,13 +69,16 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
|
||||
EditText noteFieldEdit;
|
||||
ChipGroup groupsChips;
|
||||
AutoCompleteTextView expiryField;
|
||||
View cardAndBarcodeLayout;
|
||||
EditText balanceField;
|
||||
AutoCompleteTextView balanceCurrencyField;
|
||||
TextView cardIdFieldView;
|
||||
AutoCompleteTextView barcodeTypeField;
|
||||
ImageView barcodeImage;
|
||||
View barcodeImageLayout;
|
||||
View barcodeCaptureLayout;
|
||||
|
||||
Button captureButton;
|
||||
Button importImageButton;
|
||||
Button enterButton;
|
||||
|
||||
int loyaltyCardId;
|
||||
@@ -86,6 +96,10 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
|
||||
boolean initDone = false;
|
||||
AlertDialog confirmExitDialog = null;
|
||||
|
||||
boolean validBalance = true;
|
||||
|
||||
HashMap<String, Currency> currencies = new HashMap<>();
|
||||
|
||||
private void extractIntentFields(Intent intent)
|
||||
{
|
||||
final Bundle b = intent.getExtras();
|
||||
@@ -120,13 +134,18 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
|
||||
db = new DBHelper(this);
|
||||
importUriHelper = new ImportURIHelper(this);
|
||||
|
||||
for (Currency currency : Currency.getAvailableCurrencies()) {
|
||||
currencies.put(currency.getSymbol(), currency);
|
||||
}
|
||||
|
||||
tabs = findViewById(R.id.tabs);
|
||||
thumbnail = findViewById(R.id.thumbnail);
|
||||
storeFieldEdit = findViewById(R.id.storeNameEdit);
|
||||
noteFieldEdit = findViewById(R.id.noteEdit);
|
||||
groupsChips = findViewById(R.id.groupChips);
|
||||
expiryField = findViewById(R.id.expiryField);
|
||||
cardAndBarcodeLayout = findViewById(R.id.cardAndBarcodeLayout);
|
||||
balanceField = findViewById(R.id.balanceField);
|
||||
balanceCurrencyField = findViewById(R.id.balanceCurrencyField);
|
||||
cardIdFieldView = findViewById(R.id.cardIdView);
|
||||
barcodeTypeField = findViewById(R.id.barcodeTypeField);
|
||||
barcodeImage = findViewById(R.id.barcode);
|
||||
@@ -183,6 +202,98 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
|
||||
}
|
||||
});
|
||||
|
||||
balanceField.setOnFocusChangeListener((v, hasFocus) -> {
|
||||
if (!hasFocus) {
|
||||
balanceField.setText(Utils.formatBalanceWithoutCurrencySymbol((BigDecimal) balanceField.getTag(), (Currency) balanceCurrencyField.getTag()));
|
||||
}
|
||||
});
|
||||
|
||||
balanceField.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
hasChanged = true;
|
||||
|
||||
try {
|
||||
BigDecimal balance = Utils.parseCurrency(s.toString(), Utils.currencyHasDecimals((Currency) balanceCurrencyField.getTag()));
|
||||
validBalance = true;
|
||||
|
||||
balanceField.setTag(balance);
|
||||
} catch (NumberFormatException e) {
|
||||
validBalance = false;
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) { }
|
||||
});
|
||||
|
||||
balanceCurrencyField.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
hasChanged = true;
|
||||
|
||||
Currency currency;
|
||||
|
||||
if (s.toString().equals(getString(R.string.points))) {
|
||||
currency = null;
|
||||
} else {
|
||||
currency = currencies.get(s.toString());
|
||||
}
|
||||
|
||||
balanceCurrencyField.setTag(currency);
|
||||
|
||||
BigDecimal balance = (BigDecimal) balanceField.getTag();
|
||||
|
||||
if (balance != null) {
|
||||
balanceField.setText(Utils.formatBalanceWithoutCurrencySymbol(balance, currency));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
ArrayList<String> currencyList = new ArrayList<>(currencies.keySet());
|
||||
Collections.sort(currencyList, (o1, o2) -> {
|
||||
boolean o1ascii = o1.matches("^[^a-zA-Z]*$");
|
||||
boolean o2ascii = o2.matches("^[^a-zA-Z]*$");
|
||||
|
||||
if (!o1ascii && o2ascii) {
|
||||
return 1;
|
||||
} else if (o1ascii && !o2ascii) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return o1.compareTo(o2);
|
||||
});
|
||||
|
||||
// Sort locale currencies on top
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
LocaleList locales = getApplicationContext().getResources().getConfiguration().getLocales();
|
||||
|
||||
for (int i = locales.size() - 1; i > 0; i--) {
|
||||
Locale locale = locales.get(i);
|
||||
String currencySymbol = Currency.getInstance(locale).getSymbol();
|
||||
currencyList.remove(currencySymbol);
|
||||
currencyList.add(0, currencySymbol);
|
||||
}
|
||||
} else {
|
||||
String currencySymbol = Currency.getInstance(getApplicationContext().getResources().getConfiguration().locale).getSymbol();
|
||||
currencyList.remove(currencySymbol);
|
||||
currencyList.add(0, currencySymbol);
|
||||
}
|
||||
|
||||
currencyList.add(0, getString(R.string.points));
|
||||
ArrayAdapter<String> currencyAdapter = new ArrayAdapter<>(LoyaltyCardEditActivity.this, android.R.layout.select_dialog_item, currencyList);
|
||||
balanceCurrencyField.setAdapter(currencyAdapter);
|
||||
}
|
||||
});
|
||||
|
||||
cardIdFieldView.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
|
||||
@@ -262,10 +373,15 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
|
||||
noteFieldEdit.setText("");
|
||||
expiryField.setTag(null);
|
||||
expiryField.setText("");
|
||||
balanceCurrencyField.setTag(null);
|
||||
balanceCurrencyField.setText("");
|
||||
balanceField.setTag(null);
|
||||
balanceField.setText("");
|
||||
cardIdFieldView.setText("");
|
||||
barcodeTypeField.setText("");
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
@Override
|
||||
public void onResume()
|
||||
{
|
||||
@@ -297,11 +413,19 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
|
||||
if(expiryField.getText().length() == 0)
|
||||
{
|
||||
expiryField.setTag(loyaltyCard.expiry);
|
||||
if (loyaltyCard.expiry == null) {
|
||||
expiryField.setText(getString(R.string.never));
|
||||
} else {
|
||||
expiryField.setText(DateFormat.getDateInstance(DateFormat.LONG).format(loyaltyCard.expiry));
|
||||
}
|
||||
formatExpiryField(loyaltyCard.expiry);
|
||||
}
|
||||
|
||||
if(balanceCurrencyField.getText().length() == 0)
|
||||
{
|
||||
balanceCurrencyField.setTag(loyaltyCard.balanceType);
|
||||
formatBalanceCurrencyField(loyaltyCard.balanceType);
|
||||
}
|
||||
|
||||
if(balanceField.getText().length() == 0)
|
||||
{
|
||||
balanceField.setTag(loyaltyCard.balance);
|
||||
balanceField.setText(Utils.formatBalanceWithoutCurrencySymbol(loyaltyCard.balance, loyaltyCard.balanceType));
|
||||
}
|
||||
|
||||
if(cardIdFieldView.getText().length() == 0)
|
||||
@@ -340,11 +464,10 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
|
||||
storeFieldEdit.setText(importCard.store);
|
||||
noteFieldEdit.setText(importCard.note);
|
||||
expiryField.setTag(importCard.expiry);
|
||||
if (importCard.expiry != null) {
|
||||
expiryField.setText(DateFormat.getDateInstance(DateFormat.LONG).format(importCard.expiry));
|
||||
} else {
|
||||
expiryField.setText(R.string.never);
|
||||
}
|
||||
formatExpiryField(importCard.expiry);
|
||||
balanceField.setTag(importCard.balance);
|
||||
balanceCurrencyField.setTag(importCard.balanceType);
|
||||
formatBalanceCurrencyField(importCard.balanceType);
|
||||
cardIdFieldView.setText(importCard.cardId);
|
||||
barcodeTypeField.setText(importCard.barcodeType);
|
||||
headingColorValue = importCard.headerColor;
|
||||
@@ -354,6 +477,9 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
|
||||
setTitle(R.string.addCardTitle);
|
||||
expiryField.setTag(null);
|
||||
expiryField.setText(getString(R.string.never));
|
||||
balanceField.setTag(new BigDecimal("0"));
|
||||
balanceCurrencyField.setTag(null);
|
||||
formatBalanceCurrencyField(null);
|
||||
hideBarcode();
|
||||
}
|
||||
|
||||
@@ -381,13 +507,10 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
|
||||
break;
|
||||
}
|
||||
}
|
||||
chip.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
hasChanged = true;
|
||||
chip.setOnTouchListener((v, event) -> {
|
||||
hasChanged = true;
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
groupsChips.addView(chip);
|
||||
@@ -446,16 +569,27 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
|
||||
barcodeTypeField.setAdapter(barcodeAdapter);
|
||||
|
||||
FloatingActionButton saveButton = findViewById(R.id.fabSave);
|
||||
saveButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
doSave();
|
||||
}
|
||||
});
|
||||
saveButton.setOnClickListener(v -> doSave());
|
||||
|
||||
generateIcon(storeFieldEdit.getText().toString());
|
||||
}
|
||||
|
||||
private void formatExpiryField(Date expiry) {
|
||||
if (expiry == null) {
|
||||
expiryField.setText(getString(R.string.never));
|
||||
} else {
|
||||
expiryField.setText(DateFormat.getDateInstance(DateFormat.LONG).format(expiry));
|
||||
}
|
||||
}
|
||||
|
||||
private void formatBalanceCurrencyField(Currency balanceType) {
|
||||
if (balanceType == null) {
|
||||
balanceCurrencyField.setText(getString(R.string.points));
|
||||
} else {
|
||||
balanceCurrencyField.setText(balanceType.getSymbol());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
askBeforeQuitIfChanged();
|
||||
@@ -503,6 +637,7 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ColorSelectListener implements View.OnClickListener
|
||||
{
|
||||
final int defaultColor;
|
||||
@@ -584,12 +719,13 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void doSave()
|
||||
{
|
||||
String store = storeFieldEdit.getText().toString();
|
||||
String note = noteFieldEdit.getText().toString();
|
||||
Date expiry = (Date) expiryField.getTag();
|
||||
BigDecimal balance = (BigDecimal) balanceField.getTag();
|
||||
Currency balanceType = balanceCurrencyField.getTag() != null ? ((Currency) balanceCurrencyField.getTag()) : null;
|
||||
String cardId = cardIdFieldView.getText().toString();
|
||||
String barcodeType = barcodeTypeField.getText().toString();
|
||||
|
||||
@@ -612,6 +748,12 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
|
||||
return;
|
||||
}
|
||||
|
||||
if(!validBalance)
|
||||
{
|
||||
Snackbar.make(balanceField, getString(R.string.parsingBalanceFailed, balanceField.getText().toString()), Snackbar.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
List<Group> selectedGroups = new ArrayList<>();
|
||||
|
||||
for (Integer chipId : groupsChips.getCheckedChipIds()) {
|
||||
@@ -621,12 +763,12 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
|
||||
|
||||
if(updateLoyaltyCard)
|
||||
{ //update of "starStatus" not necessary, since it cannot be changed in this activity (only in ViewActivity)
|
||||
db.updateLoyaltyCard(loyaltyCardId, store, note, expiry, cardId, barcodeType, headingColorValue);
|
||||
db.updateLoyaltyCard(loyaltyCardId, store, note, expiry, balance, balanceType, cardId, barcodeType, headingColorValue);
|
||||
Log.i(TAG, "Updated " + loyaltyCardId + " to " + cardId);
|
||||
}
|
||||
else
|
||||
{
|
||||
loyaltyCardId = (int)db.insertLoyaltyCard(store, note, expiry, cardId, barcodeType, headingColorValue, 0);
|
||||
loyaltyCardId = (int)db.insertLoyaltyCard(store, note, expiry, balance, balanceType, cardId, barcodeType, headingColorValue, 0);
|
||||
}
|
||||
|
||||
db.setLoyaltyCardGroups(loyaltyCardId, selectedGroups);
|
||||
@@ -702,7 +844,7 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
|
||||
{
|
||||
super.onActivityResult(requestCode, resultCode, intent);
|
||||
|
||||
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent);
|
||||
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent, this);
|
||||
|
||||
barcodeType = barcodeValues.format();
|
||||
cardId = barcodeValues.content();
|
||||
@@ -774,7 +916,12 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
|
||||
barcodePart.setVisibility(View.VISIBLE);
|
||||
|
||||
// Redraw barcode due to size change (Visibility.GONE sets it to 0)
|
||||
generateBarcode(cardIdFieldView.getText().toString(), BarcodeFormat.valueOf(barcodeTypeField.getText().toString()));
|
||||
String formatString = barcodeTypeField.getText().toString();
|
||||
if (formatString.isEmpty() || formatString.equals(getString(R.string.noBarcode))) {
|
||||
hideBarcode();
|
||||
} else {
|
||||
generateBarcode(cardIdFieldView.getText().toString(), BarcodeFormat.valueOf(formatString));
|
||||
}
|
||||
} else {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package protect.card_locker;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
@@ -9,12 +10,15 @@ import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.constraintlayout.widget.Guideline;
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
import androidx.core.widget.TextViewCompat;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Menu;
|
||||
@@ -24,16 +28,19 @@ import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.material.appbar.AppBarLayout;
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import protect.card_locker.preferences.Settings;
|
||||
@@ -43,14 +50,19 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
private static final String TAG = "Catima";
|
||||
|
||||
TextView cardIdFieldView;
|
||||
BottomSheetBehavior behavior;
|
||||
View bottomSheet;
|
||||
ImageView bottomSheetButton;
|
||||
TextView noteView;
|
||||
TextView groupsView;
|
||||
TextView balanceView;
|
||||
TextView expiryView;
|
||||
TextView storeName;
|
||||
ImageButton maximizeButton;
|
||||
ImageView barcodeImage;
|
||||
ImageButton minimizeButton;
|
||||
View collapsingToolbarLayout;
|
||||
AppBarLayout appBarLayout;
|
||||
int loyaltyCardId;
|
||||
LoyaltyCard loyaltyCard;
|
||||
boolean rotationEnabled;
|
||||
@@ -61,10 +73,14 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
String cardIdString;
|
||||
BarcodeFormat format;
|
||||
|
||||
FloatingActionButton editButton;
|
||||
|
||||
Guideline centerGuideline;
|
||||
SeekBar barcodeScaler;
|
||||
|
||||
boolean starred;
|
||||
boolean backgroundNeedsDarkIcons;
|
||||
boolean barcodeIsFullscreen = false;
|
||||
ViewGroup.LayoutParams barcodeImageState;
|
||||
|
||||
private void extractIntentFields(Intent intent)
|
||||
{
|
||||
@@ -101,14 +117,6 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
|
||||
setContentView(R.layout.loyalty_card_view_layout);
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if(actionBar != null)
|
||||
{
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
db = new DBHelper(this);
|
||||
importURIHelper = new ImportURIHelper(this);
|
||||
|
||||
@@ -117,43 +125,70 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
bottomSheetButton = findViewById(R.id.bottomSheetButton);
|
||||
noteView = findViewById(R.id.noteView);
|
||||
groupsView = findViewById(R.id.groupsView);
|
||||
balanceView = findViewById(R.id.balanceView);
|
||||
expiryView = findViewById(R.id.expiryView);
|
||||
storeName = findViewById(R.id.storeName);
|
||||
maximizeButton = findViewById(R.id.maximizeButton);
|
||||
barcodeImage = findViewById(R.id.barcode);
|
||||
minimizeButton = findViewById(R.id.minimizeButton);
|
||||
collapsingToolbarLayout = findViewById(R.id.collapsingToolbarLayout);
|
||||
appBarLayout = findViewById(R.id.app_bar_layout);
|
||||
|
||||
centerGuideline = findViewById(R.id.centerGuideline);
|
||||
centerGuideline.setGuidelinePercent(0.5f);
|
||||
barcodeScaler = findViewById(R.id.barcodeScaler);
|
||||
barcodeScaler.setProgress(100);
|
||||
barcodeScaler.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
Log.d(TAG, "Progress is " + progress);
|
||||
Log.d(TAG, "Max is " + barcodeScaler.getMax());
|
||||
float scale = (float) progress / (float) barcodeScaler.getMax();
|
||||
Log.d(TAG, "Scaling to " + scale);
|
||||
|
||||
redrawBarcodeAfterResize();
|
||||
centerGuideline.setGuidelinePercent(0.5f * scale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
rotationEnabled = true;
|
||||
|
||||
// Allow making barcode fullscreen on tap
|
||||
barcodeImage.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (barcodeIsFullscreen)
|
||||
{
|
||||
setFullscreen(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
setFullscreen(true);
|
||||
}
|
||||
maximizeButton.setOnClickListener(v -> setFullscreen(true));
|
||||
barcodeImage.setOnClickListener(view -> {
|
||||
if (barcodeIsFullscreen)
|
||||
{
|
||||
setFullscreen(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
setFullscreen(true);
|
||||
}
|
||||
});
|
||||
minimizeButton.setOnClickListener(v -> setFullscreen(false));
|
||||
|
||||
final FloatingActionButton editButton = findViewById(R.id.fabEdit);
|
||||
editButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt("id", loyaltyCardId);
|
||||
bundle.putBoolean("update", true);
|
||||
intent.putExtras(bundle);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
editButton = findViewById(R.id.fabEdit);
|
||||
editButton.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt("id", loyaltyCardId);
|
||||
bundle.putBoolean("update", true);
|
||||
intent.putExtras(bundle);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
});
|
||||
|
||||
final BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
|
||||
behavior = BottomSheetBehavior.from(bottomSheet);
|
||||
behavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
|
||||
@Override
|
||||
public void onStateChanged(@NonNull View bottomSheet, int newState) {
|
||||
@@ -172,14 +207,11 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
public void onSlide(@NonNull View bottomSheet, float slideOffset) { }
|
||||
});
|
||||
|
||||
bottomSheetButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (behavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
|
||||
behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||
} else {
|
||||
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
|
||||
}
|
||||
bottomSheetButton.setOnClickListener(v -> {
|
||||
if (behavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
|
||||
behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||
} else {
|
||||
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -200,23 +232,28 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
|
||||
Log.i(TAG, "To view card: " + loyaltyCardId);
|
||||
|
||||
if(barcodeIsFullscreen)
|
||||
{
|
||||
// Completely reset state
|
||||
//
|
||||
// This prevents the barcode from taking up the entire screen
|
||||
// on resume and thus being stretched out of proportion.
|
||||
recreate();
|
||||
}
|
||||
|
||||
// The brightness value is on a scale from [0, ..., 1], where
|
||||
// '1' is the brightest. We attempt to maximize the brightness
|
||||
// to help barcode readers scan the barcode.
|
||||
Window window = getWindow();
|
||||
if(window != null && settings.useMaxBrightnessDisplayingBarcode())
|
||||
if(window != null)
|
||||
{
|
||||
WindowManager.LayoutParams attributes = window.getAttributes();
|
||||
attributes.screenBrightness = 1F;
|
||||
|
||||
if (settings.useMaxBrightnessDisplayingBarcode())
|
||||
{
|
||||
attributes.screenBrightness = 1F;
|
||||
}
|
||||
|
||||
if (settings.getKeepScreenOn()) {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
|
||||
if (settings.getDisableLockscreenWhileViewingCard()) {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD|
|
||||
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
|
||||
}
|
||||
|
||||
window.setAttributes(attributes);
|
||||
}
|
||||
|
||||
@@ -229,6 +266,8 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
return;
|
||||
}
|
||||
|
||||
setupOrientation();
|
||||
|
||||
String formatString = loyaltyCard.barcodeType;
|
||||
format = !formatString.isEmpty() ? BarcodeFormat.valueOf(formatString) : null;
|
||||
cardIdString = loyaltyCard.cardId;
|
||||
@@ -265,6 +304,15 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
groupsView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if(!loyaltyCard.balance.equals(new BigDecimal(0))) {
|
||||
balanceView.setVisibility(View.VISIBLE);
|
||||
balanceView.setText(getString(R.string.balanceSentence, Utils.formatBalance(this, loyaltyCard.balance, loyaltyCard.balanceType)));
|
||||
}
|
||||
else
|
||||
{
|
||||
balanceView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if(loyaltyCard.expiry != null) {
|
||||
expiryView.setVisibility(View.VISIBLE);
|
||||
|
||||
@@ -281,12 +329,8 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
}
|
||||
expiryView.setTag(loyaltyCard.expiry);
|
||||
|
||||
if (noteView.getVisibility() == View.VISIBLE || groupsView.getVisibility() == View.VISIBLE || expiryView.getVisibility() == View.VISIBLE) {
|
||||
bottomSheet.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else
|
||||
{
|
||||
bottomSheet.setVisibility(View.GONE);
|
||||
if (!barcodeIsFullscreen) {
|
||||
makeBottomSheetVisibleIfUseful();
|
||||
}
|
||||
|
||||
storeName.setText(loyaltyCard.store);
|
||||
@@ -303,6 +347,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
}
|
||||
|
||||
collapsingToolbarLayout.setBackgroundColor(backgroundHeaderColor);
|
||||
appBarLayout.setBackgroundColor(backgroundHeaderColor);
|
||||
|
||||
int textColor;
|
||||
if(Utils.needsDarkForeground(backgroundHeaderColor))
|
||||
@@ -314,6 +359,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
textColor = Color.WHITE;
|
||||
}
|
||||
storeName.setTextColor(textColor);
|
||||
((Toolbar) findViewById(R.id.toolbar_landscape)).setTitleTextColor(textColor);
|
||||
|
||||
// If the background is very bright, we should use dark icons
|
||||
backgroundNeedsDarkIcons = Utils.needsDarkForeground(backgroundHeaderColor);
|
||||
@@ -338,34 +384,30 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
|
||||
if(format != null)
|
||||
{
|
||||
findViewById(R.id.barcode).setVisibility(View.VISIBLE);
|
||||
if (!barcodeIsFullscreen) {
|
||||
maximizeButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
barcodeImage.setVisibility(View.VISIBLE);
|
||||
if(barcodeImage.getHeight() == 0)
|
||||
{
|
||||
Log.d(TAG, "ImageView size is not known known at start, waiting for load");
|
||||
// The size of the ImageView is not yet available as it has not
|
||||
// yet been drawn. Wait for it to be drawn so the size is available.
|
||||
barcodeImage.getViewTreeObserver().addOnGlobalLayoutListener(
|
||||
new ViewTreeObserver.OnGlobalLayoutListener()
|
||||
{
|
||||
@Override
|
||||
public void onGlobalLayout()
|
||||
{
|
||||
barcodeImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
|
||||
Log.d(TAG, "ImageView size now known");
|
||||
new BarcodeImageWriterTask(barcodeImage, cardIdString, format).execute();
|
||||
}
|
||||
});
|
||||
redrawBarcodeAfterResize();
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.d(TAG, "ImageView size known known, creating barcode");
|
||||
new BarcodeImageWriterTask(barcodeImage, cardIdString, format).execute();
|
||||
}
|
||||
|
||||
// Force redraw fullscreen state
|
||||
setFullscreen(barcodeIsFullscreen);
|
||||
}
|
||||
else
|
||||
{
|
||||
findViewById(R.id.barcode).setVisibility(View.GONE);
|
||||
maximizeButton.setVisibility(View.GONE);
|
||||
barcodeImage.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,7 +420,6 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
}
|
||||
|
||||
super.onBackPressed();
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -455,7 +496,40 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void setupOrientation()
|
||||
{
|
||||
Toolbar portraitToolbar = findViewById(R.id.toolbar);
|
||||
Toolbar landscapeToolbar = findViewById(R.id.toolbar_landscape);
|
||||
|
||||
int orientation = getResources().getConfiguration().orientation;
|
||||
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
Log.d(TAG, "Detected landscape mode");
|
||||
|
||||
setTitle(loyaltyCard.store);
|
||||
|
||||
collapsingToolbarLayout.setVisibility(View.GONE);
|
||||
portraitToolbar.setVisibility(View.GONE);
|
||||
landscapeToolbar.setVisibility(View.VISIBLE);
|
||||
|
||||
setSupportActionBar(landscapeToolbar);
|
||||
} else {
|
||||
Log.d(TAG, "Detected portrait mode");
|
||||
|
||||
setTitle("");
|
||||
|
||||
collapsingToolbarLayout.setVisibility(View.VISIBLE);
|
||||
portraitToolbar.setVisibility(View.VISIBLE);
|
||||
landscapeToolbar.setVisibility(View.GONE);
|
||||
|
||||
setSupportActionBar(portraitToolbar);
|
||||
}
|
||||
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if(actionBar != null)
|
||||
{
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
}
|
||||
private void setOrientatonLock(MenuItem item, boolean lock)
|
||||
{
|
||||
if(lock)
|
||||
@@ -473,6 +547,33 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
}
|
||||
}
|
||||
|
||||
private void makeBottomSheetVisibleIfUseful()
|
||||
{
|
||||
if (noteView.getVisibility() == View.VISIBLE || groupsView.getVisibility() == View.VISIBLE || balanceView.getVisibility() == View.VISIBLE || expiryView.getVisibility() == View.VISIBLE) {
|
||||
bottomSheet.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else
|
||||
{
|
||||
bottomSheet.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void redrawBarcodeAfterResize()
|
||||
{
|
||||
if (format != null) {
|
||||
barcodeImage.getViewTreeObserver().addOnGlobalLayoutListener(
|
||||
new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
barcodeImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
|
||||
Log.d(TAG, "ImageView size now known");
|
||||
new BarcodeImageWriterTask(barcodeImage, cardIdString, format).execute();
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* When enabled, hides the status bar and moves the barcode to the top of the screen.
|
||||
*
|
||||
@@ -482,10 +583,16 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
private void setFullscreen(boolean enable)
|
||||
{
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if(enable && !barcodeIsFullscreen)
|
||||
if(enable)
|
||||
{
|
||||
// Save previous barcodeImage state
|
||||
barcodeImageState = barcodeImage.getLayoutParams();
|
||||
Log.d(TAG, "Move into of fullscreen");
|
||||
// Prepare redraw after size change
|
||||
redrawBarcodeAfterResize();
|
||||
|
||||
// Hide maximize and show minimize button and scaler
|
||||
maximizeButton.setVisibility(View.GONE);
|
||||
minimizeButton.setVisibility(View.VISIBLE);
|
||||
barcodeScaler.setVisibility(View.VISIBLE);
|
||||
|
||||
// Hide actionbar
|
||||
if(actionBar != null)
|
||||
@@ -493,55 +600,73 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
actionBar.hide();
|
||||
}
|
||||
|
||||
// Hide collapsingToolbar
|
||||
// Hide toolbars
|
||||
//
|
||||
// Appbar needs to be invisible and have padding removed
|
||||
// Or the barcode will be centered instead of on top of the screen
|
||||
// Don't ask me why...
|
||||
appBarLayout.setVisibility(View.INVISIBLE);
|
||||
appBarLayout.setPadding(0, 0, 0, 0);
|
||||
collapsingToolbarLayout.setVisibility(View.GONE);
|
||||
findViewById(R.id.toolbar_landscape).setVisibility(View.GONE);
|
||||
|
||||
// Hide other UI elements
|
||||
cardIdFieldView.setVisibility(View.GONE);
|
||||
bottomSheet.setVisibility(View.GONE);
|
||||
behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||
editButton.hide();
|
||||
|
||||
// Set Android to fullscreen mode
|
||||
getWindow().getDecorView().setSystemUiVisibility(
|
||||
getWindow().getDecorView().getSystemUiVisibility()
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
getWindow().getDecorView().getSystemUiVisibility()
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
);
|
||||
|
||||
// Make barcode take all space
|
||||
barcodeImage.setLayoutParams(new ConstraintLayout.LayoutParams(
|
||||
ConstraintLayout.LayoutParams.MATCH_PARENT,
|
||||
ConstraintLayout.LayoutParams.MATCH_PARENT
|
||||
));
|
||||
|
||||
// Move barcode to top
|
||||
barcodeImage.setScaleType(ImageView.ScaleType.FIT_START);
|
||||
|
||||
// Prevent centering
|
||||
barcodeImage.setAdjustViewBounds(false);
|
||||
|
||||
// Set current state
|
||||
barcodeIsFullscreen = true;
|
||||
}
|
||||
else if(!enable && barcodeIsFullscreen)
|
||||
else if(!enable)
|
||||
{
|
||||
Log.d(TAG, "Move out of fullscreen");
|
||||
|
||||
// Reset center guideline
|
||||
barcodeScaler.setProgress(100);
|
||||
|
||||
// Prepare redraw after size change
|
||||
redrawBarcodeAfterResize();
|
||||
|
||||
// Show maximize and hide minimize button and scaler
|
||||
maximizeButton.setVisibility(View.VISIBLE);
|
||||
minimizeButton.setVisibility(View.GONE);
|
||||
barcodeScaler.setVisibility(View.GONE);
|
||||
|
||||
// Show actionbar
|
||||
if(actionBar != null)
|
||||
{
|
||||
actionBar.show();
|
||||
}
|
||||
|
||||
// Show collapsingToolbar
|
||||
collapsingToolbarLayout.setVisibility(View.VISIBLE);
|
||||
// Show appropriate toolbar
|
||||
// And restore 24dp paddingTop for appBarLayout
|
||||
appBarLayout.setVisibility(View.VISIBLE);
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
getWindowManager().getDefaultDisplay().getMetrics(metrics);
|
||||
appBarLayout.setPadding(0, (int) Math.ceil(metrics.density * 24), 0, 0);
|
||||
setupOrientation();
|
||||
|
||||
// Show other UI elements
|
||||
cardIdFieldView.setVisibility(View.VISIBLE);
|
||||
makeBottomSheetVisibleIfUseful();
|
||||
editButton.show();
|
||||
|
||||
// Unset fullscreen mode
|
||||
getWindow().getDecorView().setSystemUiVisibility(
|
||||
getWindow().getDecorView().getSystemUiVisibility()
|
||||
& ~View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
& ~View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
getWindow().getDecorView().getSystemUiVisibility()
|
||||
& ~View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
& ~View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
);
|
||||
|
||||
// Turn barcode back to normal
|
||||
barcodeImage.setLayoutParams(barcodeImageState);
|
||||
|
||||
// Fix barcode centering
|
||||
barcodeImage.setAdjustViewBounds(true);
|
||||
|
||||
// Set current state
|
||||
barcodeIsFullscreen = false;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
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.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
@@ -92,6 +95,29 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
|
||||
helpText.setOnTouchListener(gestureTouchListener);
|
||||
noMatchingCardsText.setOnTouchListener(gestureTouchListener);
|
||||
list.setOnTouchListener(gestureTouchListener);
|
||||
|
||||
// Show privacy policy on first run
|
||||
SharedPreferences privacyPolicyShownPref = getApplicationContext().getSharedPreferences(
|
||||
getString(R.string.sharedpreference_privacy_policy_shown),
|
||||
Context.MODE_PRIVATE);
|
||||
|
||||
if (privacyPolicyShownPref.getInt(getString(R.string.sharedpreference_privacy_policy_shown), 0) == 0) {
|
||||
SharedPreferences.Editor privacyPolicyShownPrefEditor = privacyPolicyShownPref.edit();
|
||||
privacyPolicyShownPrefEditor.putInt(getString(R.string.sharedpreference_privacy_policy_shown), 1);
|
||||
privacyPolicyShownPrefEditor.apply();
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.privacy_policy)
|
||||
.setMessage(R.string.privacy_policy_popup_text)
|
||||
.setPositiveButton(R.string.accept, null)
|
||||
.setNegativeButton(R.string.privacy_policy, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int whichButton) {
|
||||
openPrivacyPolicy();
|
||||
}
|
||||
})
|
||||
.setIcon(android.R.drawable.ic_dialog_info)
|
||||
.show();
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -162,7 +188,7 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
|
||||
return;
|
||||
}
|
||||
|
||||
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent);
|
||||
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent, this);
|
||||
|
||||
if(!barcodeValues.isEmpty()) {
|
||||
Intent newIntent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
|
||||
@@ -290,6 +316,14 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
|
||||
groupsTabLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void openPrivacyPolicy() {
|
||||
Intent browserIntent = new Intent(
|
||||
Intent.ACTION_VIEW,
|
||||
Uri.parse("https://thelastproject.github.io/Catima/privacy-policy")
|
||||
);
|
||||
startActivity(browserIntent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo)
|
||||
{
|
||||
@@ -402,6 +436,12 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O
|
||||
return true;
|
||||
}
|
||||
|
||||
if(id == R.id.action_privacy_policy)
|
||||
{
|
||||
openPrivacyPolicy();
|
||||
return true;
|
||||
}
|
||||
|
||||
if(id == R.id.action_about)
|
||||
{
|
||||
Intent i = new Intent(getApplicationContext(), AboutActivity.class);
|
||||
|
||||
@@ -132,7 +132,7 @@ public class ScanActivity extends AppCompatActivity {
|
||||
{
|
||||
super.onActivityResult(requestCode, resultCode, intent);
|
||||
|
||||
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent);
|
||||
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent, this);
|
||||
|
||||
if (!barcodeValues.isEmpty()) {
|
||||
Intent manualResult = new Intent();
|
||||
@@ -154,4 +154,10 @@ public class ScanActivity extends AppCompatActivity {
|
||||
}
|
||||
startActivityForResult(i, Utils.SELECT_BARCODE_REQUEST);
|
||||
}
|
||||
|
||||
public void addFromImage(View view) {
|
||||
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
|
||||
photoPickerIntent.setType("image/*");
|
||||
startActivityForResult(photoPickerIntent, Utils.BARCODE_IMPORT_FROM_IMAGE_FILE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,15 +3,31 @@ package protect.card_locker;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.BinaryBitmap;
|
||||
import com.google.zxing.LuminanceSource;
|
||||
import com.google.zxing.MultiFormatReader;
|
||||
import com.google.zxing.NotFoundException;
|
||||
import com.google.zxing.RGBLuminanceSource;
|
||||
import com.google.zxing.Result;
|
||||
import com.google.zxing.common.HybridBinarizer;
|
||||
|
||||
public class Utils {
|
||||
private static final String TAG = "Catima";
|
||||
|
||||
@@ -19,6 +35,7 @@ public class Utils {
|
||||
public static final int MAIN_REQUEST = 1;
|
||||
public static final int SELECT_BARCODE_REQUEST = 2;
|
||||
public static final int BARCODE_SCAN = 3;
|
||||
public static final int BARCODE_IMPORT_FROM_IMAGE_FILE = 4;
|
||||
|
||||
static final double LUMINANCE_MIDPOINT = 0.5;
|
||||
|
||||
@@ -42,28 +59,75 @@ public class Utils {
|
||||
return ColorUtils.calculateLuminance(backgroundColor) > LUMINANCE_MIDPOINT;
|
||||
}
|
||||
|
||||
static public BarcodeValues parseSetBarcodeActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
String contents = null;
|
||||
String format = null;
|
||||
static public BarcodeValues parseSetBarcodeActivityResult(int requestCode, int resultCode, Intent intent, Context context) {
|
||||
String contents;
|
||||
String format;
|
||||
|
||||
if (resultCode == Activity.RESULT_OK)
|
||||
{
|
||||
if (resultCode != Activity.RESULT_OK) {
|
||||
return new BarcodeValues(null, null);
|
||||
}
|
||||
|
||||
if (requestCode == Utils.BARCODE_IMPORT_FROM_IMAGE_FILE) {
|
||||
Log.i(TAG, "Received image file with possible barcode");
|
||||
|
||||
Bitmap bitmap;
|
||||
try {
|
||||
bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), intent.getData());
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error getting data from image file");
|
||||
e.printStackTrace();
|
||||
Toast.makeText(context, R.string.errorReadingImage, Toast.LENGTH_LONG).show();
|
||||
return new BarcodeValues(null, null);
|
||||
}
|
||||
|
||||
BarcodeValues barcodeFromBitmap = getBarcodeFromBitmap(bitmap);
|
||||
|
||||
if (barcodeFromBitmap.isEmpty()) {
|
||||
Log.i(TAG, "No barcode found in image file");
|
||||
Toast.makeText(context, R.string.noBarcodeFound, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
Log.i(TAG, "Read barcode id: " + barcodeFromBitmap.content());
|
||||
Log.i(TAG, "Read format: " + barcodeFromBitmap.format());
|
||||
|
||||
return barcodeFromBitmap;
|
||||
}
|
||||
|
||||
if (requestCode == Utils.BARCODE_SCAN || requestCode == Utils.SELECT_BARCODE_REQUEST) {
|
||||
if (requestCode == Utils.BARCODE_SCAN) {
|
||||
Log.i(TAG, "Received barcode information from camera");
|
||||
} else if (requestCode == Utils.SELECT_BARCODE_REQUEST) {
|
||||
Log.i(TAG, "Received barcode information from typing it");
|
||||
} else {
|
||||
return new BarcodeValues(null, null);
|
||||
}
|
||||
|
||||
contents = intent.getStringExtra(BarcodeSelectorActivity.BARCODE_CONTENTS);
|
||||
format = intent.getStringExtra(BarcodeSelectorActivity.BARCODE_FORMAT);
|
||||
|
||||
Log.i(TAG, "Read barcode id: " + contents);
|
||||
Log.i(TAG, "Read format: " + format);
|
||||
|
||||
return new BarcodeValues(format, contents);
|
||||
}
|
||||
|
||||
Log.i(TAG, "Read barcode id: " + contents);
|
||||
Log.i(TAG, "Read format: " + format);
|
||||
throw new UnsupportedOperationException("Unknown request code for parseSetBarcodeActivityResult");
|
||||
}
|
||||
|
||||
return new BarcodeValues(format, contents);
|
||||
static public BarcodeValues getBarcodeFromBitmap(Bitmap bitmap) {
|
||||
// In order to decode it, the Bitmap must first be converted into a pixel array...
|
||||
int[] intArray = new int[bitmap.getWidth() * bitmap.getHeight()];
|
||||
bitmap.getPixels(intArray, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
|
||||
|
||||
// ...and then turned into a binary bitmap from its luminance
|
||||
LuminanceSource source = new RGBLuminanceSource(bitmap.getWidth(), bitmap.getHeight(), intArray);
|
||||
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
|
||||
|
||||
try {
|
||||
Result barcodeResult = new MultiFormatReader().decode(binaryBitmap);
|
||||
|
||||
return new BarcodeValues(barcodeResult.getBarcodeFormat().name(), barcodeResult.getText());
|
||||
} catch (NotFoundException e) {
|
||||
return new BarcodeValues(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
static public Boolean hasExpired(Date expiryDate) {
|
||||
@@ -77,4 +141,62 @@ public class Utils {
|
||||
|
||||
return expiryDate.before(date.getTime());
|
||||
}
|
||||
|
||||
static public String formatBalance(Context context, BigDecimal value, Currency currency) {
|
||||
NumberFormat numberFormat = NumberFormat.getInstance();
|
||||
|
||||
if (currency == null) {
|
||||
numberFormat.setMaximumFractionDigits(0);
|
||||
return context.getString(R.string.balancePoints, numberFormat.format(value));
|
||||
}
|
||||
|
||||
NumberFormat currencyFormat = NumberFormat.getCurrencyInstance();
|
||||
currencyFormat.setCurrency(currency);
|
||||
currencyFormat.setMinimumFractionDigits(currency.getDefaultFractionDigits());
|
||||
currencyFormat.setMaximumFractionDigits(currency.getDefaultFractionDigits());
|
||||
|
||||
return currencyFormat.format(value);
|
||||
}
|
||||
|
||||
static public String formatBalanceWithoutCurrencySymbol(BigDecimal value, Currency currency) {
|
||||
NumberFormat numberFormat = NumberFormat.getInstance();
|
||||
|
||||
if (currency == null) {
|
||||
numberFormat.setMaximumFractionDigits(0);
|
||||
return numberFormat.format(value);
|
||||
}
|
||||
|
||||
numberFormat.setMinimumFractionDigits(currency.getDefaultFractionDigits());
|
||||
numberFormat.setMaximumFractionDigits(currency.getDefaultFractionDigits());
|
||||
|
||||
return numberFormat.format(value);
|
||||
}
|
||||
|
||||
static public Boolean currencyHasDecimals(Currency currency) {
|
||||
if (currency == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return currency.getDefaultFractionDigits() != 0;
|
||||
}
|
||||
|
||||
static public BigDecimal parseCurrency(String value, Boolean hasDecimals) throws NumberFormatException {
|
||||
// If there are no decimals expected, remove all separators before parsing
|
||||
if (!hasDecimals) {
|
||||
value = value.replaceAll("[^0-9]", "");
|
||||
return new BigDecimal(value);
|
||||
}
|
||||
|
||||
// There are many ways users can write a currency, so we fix it up a bit
|
||||
// 1. Replace all non-numbers with dots
|
||||
value = value.replaceAll("[^0-9]", ".");
|
||||
|
||||
// 2. Remove all but the last dot
|
||||
while (value.split("\\.").length > 2) {
|
||||
value = value.replaceFirst("\\.", "");
|
||||
}
|
||||
|
||||
// Parse as BigDecimal
|
||||
return new BigDecimal(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import org.apache.commons.csv.CSVRecord;
|
||||
|
||||
import protect.card_locker.FormatException;
|
||||
|
||||
public class CSVHelpers {
|
||||
/**
|
||||
* Extract a string from the items array. The index into the array
|
||||
* is determined by looking up the index in the fields map using the
|
||||
* "key" as the key. If no such key exists, defaultValue is returned
|
||||
* if it is not null. Otherwise, a FormatException is thrown.
|
||||
*/
|
||||
static String extractString(String key, CSVRecord record, String defaultValue)
|
||||
throws FormatException
|
||||
{
|
||||
String toReturn = defaultValue;
|
||||
|
||||
if(record.isMapped(key))
|
||||
{
|
||||
toReturn = record.get(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(defaultValue == null)
|
||||
{
|
||||
throw new FormatException("Field not used but expected: " + key);
|
||||
}
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract an integer from the items array. The index into the array
|
||||
* is determined by looking up the index in the fields map using the
|
||||
* "key" as the key. If no such key exists, or the data is not a valid
|
||||
* int, a FormatException is thrown.
|
||||
*/
|
||||
static Integer extractInt(String key, CSVRecord record, boolean nullIsOk)
|
||||
throws FormatException
|
||||
{
|
||||
if(record.isMapped(key) == false)
|
||||
{
|
||||
throw new FormatException("Field not used but expected: " + key);
|
||||
}
|
||||
|
||||
String value = record.get(key);
|
||||
if(value.isEmpty() && nullIsOk)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return Integer.parseInt(record.get(key));
|
||||
}
|
||||
catch(NumberFormatException e)
|
||||
{
|
||||
throw new FormatException("Failed to parse field: " + key, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a long from the items array. The index into the array
|
||||
* is determined by looking up the index in the fields map using the
|
||||
* "key" as the key. If no such key exists, or the data is not a valid
|
||||
* int, a FormatException is thrown.
|
||||
*/
|
||||
static Long extractLong(String key, CSVRecord record, boolean nullIsOk)
|
||||
throws FormatException
|
||||
{
|
||||
if(record.isMapped(key) == false)
|
||||
{
|
||||
throw new FormatException("Field not used but expected: " + key);
|
||||
}
|
||||
|
||||
String value = record.get(key);
|
||||
if(value.isEmpty() && nullIsOk)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return Long.parseLong(record.get(key));
|
||||
}
|
||||
catch(NumberFormatException e)
|
||||
{
|
||||
throw new FormatException("Failed to parse field: " + key, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package protect.card_locker;
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import android.database.Cursor;
|
||||
|
||||
@@ -8,6 +8,10 @@ import org.apache.commons.csv.CSVPrinter;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.Group;
|
||||
import protect.card_locker.LoyaltyCard;
|
||||
|
||||
/**
|
||||
* Class for exporting the database into CSV (Comma Separate Values)
|
||||
* format.
|
||||
@@ -50,6 +54,8 @@ public class CsvDatabaseExporter implements DatabaseExporter
|
||||
DBHelper.LoyaltyCardDbIds.STORE,
|
||||
DBHelper.LoyaltyCardDbIds.NOTE,
|
||||
DBHelper.LoyaltyCardDbIds.EXPIRY,
|
||||
DBHelper.LoyaltyCardDbIds.BALANCE,
|
||||
DBHelper.LoyaltyCardDbIds.BALANCE_TYPE,
|
||||
DBHelper.LoyaltyCardDbIds.CARD_ID,
|
||||
DBHelper.LoyaltyCardDbIds.HEADER_COLOR,
|
||||
DBHelper.LoyaltyCardDbIds.BARCODE_TYPE,
|
||||
@@ -65,6 +71,8 @@ public class CsvDatabaseExporter implements DatabaseExporter
|
||||
card.store,
|
||||
card.note,
|
||||
card.expiry != null ? card.expiry.getTime() : "",
|
||||
card.balance,
|
||||
card.balanceType,
|
||||
card.cardId,
|
||||
card.headerColor,
|
||||
card.barcodeType,
|
||||
@@ -1,17 +1,31 @@
|
||||
package protect.card_locker;
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.CSVParser;
|
||||
import org.apache.commons.csv.CSVRecord;
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.StringReader;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.ParseException;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.FormatException;
|
||||
import protect.card_locker.Group;
|
||||
|
||||
/**
|
||||
* Class for importing a database from CSV (Comma Separate Values)
|
||||
@@ -22,9 +36,9 @@ import java.util.List;
|
||||
*/
|
||||
public class CsvDatabaseImporter implements DatabaseImporter
|
||||
{
|
||||
public void importData(DBHelper db, InputStreamReader input) throws IOException, FormatException, InterruptedException
|
||||
public void importData(DBHelper db, InputStream input) throws IOException, FormatException, InterruptedException
|
||||
{
|
||||
BufferedReader bufferedReader = new BufferedReader(input);
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
|
||||
|
||||
bufferedReader.mark(100);
|
||||
|
||||
@@ -194,92 +208,6 @@ public class CsvDatabaseImporter implements DatabaseImporter
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a string from the items array. The index into the array
|
||||
* is determined by looking up the index in the fields map using the
|
||||
* "key" as the key. If no such key exists, defaultValue is returned
|
||||
* if it is not null. Otherwise, a FormatException is thrown.
|
||||
*/
|
||||
private String extractString(String key, CSVRecord record, String defaultValue)
|
||||
throws FormatException
|
||||
{
|
||||
String toReturn = defaultValue;
|
||||
|
||||
if(record.isMapped(key))
|
||||
{
|
||||
toReturn = record.get(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(defaultValue == null)
|
||||
{
|
||||
throw new FormatException("Field not used but expected: " + key);
|
||||
}
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract an integer from the items array. The index into the array
|
||||
* is determined by looking up the index in the fields map using the
|
||||
* "key" as the key. If no such key exists, or the data is not a valid
|
||||
* int, a FormatException is thrown.
|
||||
*/
|
||||
private Integer extractInt(String key, CSVRecord record, boolean nullIsOk)
|
||||
throws FormatException
|
||||
{
|
||||
if(record.isMapped(key) == false)
|
||||
{
|
||||
throw new FormatException("Field not used but expected: " + key);
|
||||
}
|
||||
|
||||
String value = record.get(key);
|
||||
if(value.isEmpty() && nullIsOk)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return Integer.parseInt(record.get(key));
|
||||
}
|
||||
catch(NumberFormatException e)
|
||||
{
|
||||
throw new FormatException("Failed to parse field: " + key, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a long from the items array. The index into the array
|
||||
* is determined by looking up the index in the fields map using the
|
||||
* "key" as the key. If no such key exists, or the data is not a valid
|
||||
* int, a FormatException is thrown.
|
||||
*/
|
||||
private Long extractLong(String key, CSVRecord record, boolean nullIsOk)
|
||||
throws FormatException
|
||||
{
|
||||
if(record.isMapped(key) == false)
|
||||
{
|
||||
throw new FormatException("Field not used but expected: " + key);
|
||||
}
|
||||
|
||||
String value = record.get(key);
|
||||
if(value.isEmpty() && nullIsOk)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return Long.parseLong(record.get(key));
|
||||
}
|
||||
catch(NumberFormatException e)
|
||||
{
|
||||
throw new FormatException("Failed to parse field: " + key, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a single loyalty card into the database using the given
|
||||
* session.
|
||||
@@ -287,44 +215,59 @@ public class CsvDatabaseImporter implements DatabaseImporter
|
||||
private void importLoyaltyCard(SQLiteDatabase database, DBHelper helper, CSVRecord record)
|
||||
throws IOException, FormatException
|
||||
{
|
||||
int id = extractInt(DBHelper.LoyaltyCardDbIds.ID, record, false);
|
||||
int id = CSVHelpers.extractInt(DBHelper.LoyaltyCardDbIds.ID, record, false);
|
||||
|
||||
String store = extractString(DBHelper.LoyaltyCardDbIds.STORE, record, "");
|
||||
String store = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.STORE, record, "");
|
||||
if(store.isEmpty())
|
||||
{
|
||||
throw new FormatException("No store listed, but is required");
|
||||
}
|
||||
|
||||
String note = extractString(DBHelper.LoyaltyCardDbIds.NOTE, record, "");
|
||||
String note = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.NOTE, record, "");
|
||||
Date expiry = null;
|
||||
try {
|
||||
expiry = new Date(extractLong(DBHelper.LoyaltyCardDbIds.EXPIRY, record, true));
|
||||
expiry = new Date(CSVHelpers.extractLong(DBHelper.LoyaltyCardDbIds.EXPIRY, record, true));
|
||||
} catch (NullPointerException | FormatException e) { }
|
||||
|
||||
String cardId = extractString(DBHelper.LoyaltyCardDbIds.CARD_ID, record, "");
|
||||
BigDecimal balance;
|
||||
try {
|
||||
balance = new BigDecimal(CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.BALANCE, record, null));
|
||||
} catch (FormatException _e ) {
|
||||
// These fields did not exist in versions 1.8.1 and before
|
||||
// We catch this exception so we can still import old backups
|
||||
balance = new BigDecimal("0");
|
||||
}
|
||||
|
||||
Currency balanceType = null;
|
||||
String unparsedBalanceType = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE, record, "");
|
||||
if(!unparsedBalanceType.isEmpty()) {
|
||||
balanceType = Currency.getInstance(unparsedBalanceType);
|
||||
}
|
||||
|
||||
String cardId = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.CARD_ID, record, "");
|
||||
if(cardId.isEmpty())
|
||||
{
|
||||
throw new FormatException("No card ID listed, but is required");
|
||||
}
|
||||
|
||||
String barcodeType = extractString(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE, record, "");
|
||||
String barcodeType = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE, record, "");
|
||||
|
||||
Integer headerColor = null;
|
||||
|
||||
if(record.isMapped(DBHelper.LoyaltyCardDbIds.HEADER_COLOR))
|
||||
{
|
||||
headerColor = extractInt(DBHelper.LoyaltyCardDbIds.HEADER_COLOR, record, true);
|
||||
headerColor = CSVHelpers.extractInt(DBHelper.LoyaltyCardDbIds.HEADER_COLOR, record, true);
|
||||
}
|
||||
|
||||
int starStatus = 0;
|
||||
try {
|
||||
starStatus = extractInt(DBHelper.LoyaltyCardDbIds.STAR_STATUS, record, false);
|
||||
starStatus = CSVHelpers.extractInt(DBHelper.LoyaltyCardDbIds.STAR_STATUS, record, false);
|
||||
} catch (FormatException _e ) {
|
||||
// This field did not exist in versions 0.28 and before
|
||||
// We catch this exception so we can still import old backups
|
||||
}
|
||||
if (starStatus != 1) starStatus = 0;
|
||||
helper.insertLoyaltyCard(database, id, store, note, expiry, cardId, barcodeType, headerColor, starStatus);
|
||||
helper.insertLoyaltyCard(database, id, store, note, expiry, balance, balanceType, cardId, barcodeType, headerColor, starStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -334,7 +277,7 @@ public class CsvDatabaseImporter implements DatabaseImporter
|
||||
private void importGroup(SQLiteDatabase database, DBHelper helper, CSVRecord record)
|
||||
throws IOException, FormatException
|
||||
{
|
||||
String id = extractString(DBHelper.LoyaltyCardDbGroups.ID, record, null);
|
||||
String id = CSVHelpers.extractString(DBHelper.LoyaltyCardDbGroups.ID, record, null);
|
||||
|
||||
helper.insertGroup(database, id);
|
||||
}
|
||||
@@ -346,8 +289,8 @@ public class CsvDatabaseImporter implements DatabaseImporter
|
||||
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);
|
||||
Integer cardId = CSVHelpers.extractInt(DBHelper.LoyaltyCardDbIdsGroups.cardID, record, false);
|
||||
String groupId = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIdsGroups.groupID, record, null);
|
||||
|
||||
List<Group> cardGroups = helper.getLoyaltyCardGroups(cardId);
|
||||
cardGroups.add(helper.getGroup(groupId));
|
||||
@@ -1,8 +1,10 @@
|
||||
package protect.card_locker;
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
import protect.card_locker.DBHelper;
|
||||
|
||||
/**
|
||||
* Interface for a class which can export the contents of the database
|
||||
* in a given format.
|
||||
@@ -0,0 +1,27 @@
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.text.ParseException;
|
||||
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.FormatException;
|
||||
|
||||
/**
|
||||
* Interface for a class which can import the contents of a stream
|
||||
* into the database.
|
||||
*/
|
||||
public interface DatabaseImporter
|
||||
{
|
||||
/**
|
||||
* Import data from the input stream in a given format into
|
||||
* the database.
|
||||
* @throws IOException
|
||||
* @throws FormatException
|
||||
*/
|
||||
void importData(DBHelper db, InputStream input) throws IOException, FormatException, InterruptedException, JSONException, ParseException;
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Color;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.CSVParser;
|
||||
import org.apache.commons.csv.CSVRecord;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.StringReader;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.FormatException;
|
||||
|
||||
/**
|
||||
* Class for importing a database from CSV (Comma Separate Values)
|
||||
* formatted data.
|
||||
*
|
||||
* The database's loyalty cards are expected to appear in the CSV data.
|
||||
* A header is expected for the each table showing the names of the columns.
|
||||
*/
|
||||
public class FidmeImporter implements DatabaseImporter
|
||||
{
|
||||
public void importData(DBHelper db, InputStream input) throws IOException, FormatException, JSONException, ParseException {
|
||||
// We actually retrieve a .zip file
|
||||
ZipInputStream zipInputStream = new ZipInputStream(input);
|
||||
|
||||
StringBuilder loyaltyCards = new StringBuilder();
|
||||
byte[] buffer = new byte[1024];
|
||||
int read = 0;
|
||||
|
||||
ZipEntry zipEntry;
|
||||
|
||||
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
|
||||
if (zipEntry.getName().equals("loyalty_programs.csv")) {
|
||||
while ((read = zipInputStream.read(buffer, 0, 1024)) >= 0) {
|
||||
loyaltyCards.append(new String(buffer, 0, read, StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (loyaltyCards.length() == 0) {
|
||||
throw new FormatException("Couldn't find loyalty_programs.csv in zip file or it is empty");
|
||||
}
|
||||
|
||||
SQLiteDatabase database = db.getWritableDatabase();
|
||||
database.beginTransaction();
|
||||
|
||||
final CSVParser fidmeParser = new CSVParser(new StringReader(loyaltyCards.toString()), CSVFormat.RFC4180.withDelimiter(';').withHeader());
|
||||
|
||||
try {
|
||||
for (CSVRecord record : fidmeParser) {
|
||||
importLoyaltyCard(database, db, record);
|
||||
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new InterruptedException();
|
||||
}
|
||||
}
|
||||
} catch (IllegalArgumentException | IllegalStateException | InterruptedException e) {
|
||||
throw new FormatException("Issue parsing CSV data", e);
|
||||
} finally {
|
||||
fidmeParser.close();
|
||||
}
|
||||
|
||||
database.setTransactionSuccessful();
|
||||
database.endTransaction();
|
||||
database.close();
|
||||
|
||||
zipInputStream.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a single loyalty card into the database using the given
|
||||
* session.
|
||||
*/
|
||||
private void importLoyaltyCard(SQLiteDatabase database, DBHelper helper, CSVRecord record)
|
||||
throws IOException, FormatException
|
||||
{
|
||||
// A loyalty card export from Fidme contains the following fields:
|
||||
// Retailer (store name)
|
||||
// Program (program name)
|
||||
// Added at (YYYY-MM-DD HH:MM:SS UTC)
|
||||
// Reference (card ID)
|
||||
// Firstname (card holder first name)
|
||||
// Lastname (card holder last name)
|
||||
|
||||
// The store is called Retailer
|
||||
String store = CSVHelpers.extractString("Retailer", record, "");
|
||||
|
||||
if (store.isEmpty())
|
||||
{
|
||||
throw new FormatException("No store listed, but is required");
|
||||
}
|
||||
|
||||
// There seems to be no note field in the CSV? So let's combine other fields instead...
|
||||
String program = CSVHelpers.extractString("Program", record, "");
|
||||
String addedAt = CSVHelpers.extractString("Added At", record, "");
|
||||
String firstName = CSVHelpers.extractString("Firstname", record, "");
|
||||
String lastName = CSVHelpers.extractString("Lastname", record, "");
|
||||
|
||||
String combinedName = String.format("%s %s", firstName, lastName);
|
||||
|
||||
StringBuilder noteBuilder = new StringBuilder();
|
||||
if (!program.isEmpty()) noteBuilder.append(program).append('\n');
|
||||
if (!addedAt.isEmpty()) noteBuilder.append(addedAt).append('\n');
|
||||
if (!combinedName.isEmpty()) noteBuilder.append(combinedName).append('\n');
|
||||
String note = noteBuilder.toString();
|
||||
|
||||
// The ID is called reference
|
||||
String cardId = CSVHelpers.extractString("Reference", record, "");
|
||||
if(cardId.isEmpty())
|
||||
{
|
||||
throw new FormatException("No card ID listed, but is required");
|
||||
}
|
||||
|
||||
// Sadly, Fidme exports don't contain the card type
|
||||
// I guess they have an online DB of all the different companies and what type they use
|
||||
// TODO: Hook this into our own loyalty card DB if we ever get one
|
||||
String barcodeType = "";
|
||||
|
||||
// No favourite data in the export either
|
||||
int starStatus = 0;
|
||||
|
||||
helper.insertLoyaltyCard(database, store, note, null, BigDecimal.valueOf(0), null, cardId, barcodeType, null, starStatus);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,15 @@
|
||||
package protect.card_locker;
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.DataFormat;
|
||||
import protect.card_locker.importexport.CsvDatabaseExporter;
|
||||
import protect.card_locker.importexport.DatabaseExporter;
|
||||
|
||||
public class MultiFormatExporter
|
||||
{
|
||||
private static final String TAG = "Catima";
|
||||
@@ -25,9 +30,12 @@ public class MultiFormatExporter
|
||||
|
||||
switch(format)
|
||||
{
|
||||
case CSV:
|
||||
case Catima:
|
||||
exporter = new CsvDatabaseExporter();
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Failed to export data, unknown format " + format.name());
|
||||
break;
|
||||
}
|
||||
|
||||
if(exporter != null)
|
||||
@@ -1,9 +1,19 @@
|
||||
package protect.card_locker;
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.text.ParseException;
|
||||
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.DataFormat;
|
||||
import protect.card_locker.FormatException;
|
||||
import protect.card_locker.importexport.CsvDatabaseImporter;
|
||||
import protect.card_locker.importexport.DatabaseImporter;
|
||||
|
||||
public class MultiFormatImporter
|
||||
{
|
||||
@@ -20,15 +30,21 @@ public class MultiFormatImporter
|
||||
* false otherwise. If false, no data was written to
|
||||
* the database.
|
||||
*/
|
||||
public static boolean importData(DBHelper db, InputStreamReader input, DataFormat format)
|
||||
public static boolean importData(DBHelper db, InputStream input, DataFormat format)
|
||||
{
|
||||
DatabaseImporter importer = null;
|
||||
|
||||
switch(format)
|
||||
{
|
||||
case CSV:
|
||||
case Catima:
|
||||
importer = new CsvDatabaseImporter();
|
||||
break;
|
||||
case Fidme:
|
||||
importer = new FidmeImporter();
|
||||
break;
|
||||
case VoucherVault:
|
||||
importer = new VoucherVaultImporter();
|
||||
break;
|
||||
}
|
||||
|
||||
if (importer != null)
|
||||
@@ -38,7 +54,7 @@ public class MultiFormatImporter
|
||||
importer.importData(db, input);
|
||||
return true;
|
||||
}
|
||||
catch(IOException | FormatException | InterruptedException e)
|
||||
catch(IOException | FormatException | InterruptedException | JSONException | ParseException e)
|
||||
{
|
||||
Log.e(TAG, "Failed to import data", e);
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Color;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.FormatException;
|
||||
|
||||
/**
|
||||
* Class for importing a database from CSV (Comma Separate Values)
|
||||
* formatted data.
|
||||
*
|
||||
* The database's loyalty cards are expected to appear in the CSV data.
|
||||
* A header is expected for the each table showing the names of the columns.
|
||||
*/
|
||||
public class VoucherVaultImporter implements DatabaseImporter
|
||||
{
|
||||
public void importData(DBHelper db, InputStream input) throws IOException, FormatException, JSONException, ParseException {
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
sb.append(line);
|
||||
}
|
||||
JSONArray jsonArray = new JSONArray(sb.toString());
|
||||
|
||||
SQLiteDatabase database = db.getWritableDatabase();
|
||||
database.beginTransaction();
|
||||
|
||||
// See https://github.com/tim-smart/vouchervault/issues/4#issuecomment-788226503 for more info
|
||||
for (int i = 0; i < jsonArray.length(); i++) {
|
||||
JSONObject jsonCard = jsonArray.getJSONObject(i);
|
||||
|
||||
String store = jsonCard.getString("description");
|
||||
|
||||
Date expiry = null;
|
||||
if (!jsonCard.isNull("expires")) {
|
||||
@SuppressLint("SimpleDateFormat") SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
|
||||
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
expiry = dateFormat.parse(jsonCard.getString("expires"));
|
||||
}
|
||||
|
||||
BigDecimal balance;
|
||||
if (!jsonCard.isNull("balance")) {
|
||||
balance = new BigDecimal(String.valueOf(jsonCard.getDouble("balance")));
|
||||
} else {
|
||||
balance = new BigDecimal("0");
|
||||
}
|
||||
|
||||
Currency balanceType = Currency.getInstance("USD");
|
||||
|
||||
String cardId = jsonCard.getString("code");
|
||||
|
||||
String barcodeType = null;
|
||||
|
||||
String codeTypeFromJSON = jsonCard.getString("codeType");
|
||||
switch (codeTypeFromJSON) {
|
||||
case "CODE128":
|
||||
barcodeType = BarcodeFormat.CODE_128.name();
|
||||
break;
|
||||
case "CODE39":
|
||||
barcodeType = BarcodeFormat.CODE_39.name();
|
||||
break;
|
||||
case "EAN13":
|
||||
barcodeType = BarcodeFormat.EAN_13.name();
|
||||
break;
|
||||
case "QR":
|
||||
barcodeType = BarcodeFormat.QR_CODE.name();
|
||||
break;
|
||||
case "TEXT":
|
||||
break;
|
||||
default:
|
||||
throw new FormatException("Unknown barcode type found: " + codeTypeFromJSON);
|
||||
}
|
||||
|
||||
int headerColor;
|
||||
|
||||
String colorFromJSON = jsonCard.getString("color");
|
||||
switch (colorFromJSON) {
|
||||
case "GREY":
|
||||
headerColor = Color.GRAY;
|
||||
break;
|
||||
case "BLUE":
|
||||
headerColor = Color.BLUE;
|
||||
break;
|
||||
case "GREEN":
|
||||
headerColor = Color.GREEN;
|
||||
break;
|
||||
case "ORANGE":
|
||||
headerColor = Color.rgb(255, 165, 0);
|
||||
break;
|
||||
case "PURPLE":
|
||||
headerColor = Color.rgb(128, 0, 128);
|
||||
break;
|
||||
case "RED":
|
||||
headerColor = Color.RED;
|
||||
break;
|
||||
case "YELLOW":
|
||||
headerColor = Color.YELLOW;
|
||||
break;
|
||||
default:
|
||||
throw new FormatException("Unknown colour type foun: " + colorFromJSON);
|
||||
}
|
||||
|
||||
db.insertLoyaltyCard(store, "", expiry, balance, balanceType, cardId, barcodeType, headerColor, 0);
|
||||
}
|
||||
|
||||
database.setTransactionSuccessful();
|
||||
database.endTransaction();
|
||||
database.close();
|
||||
|
||||
bufferedReader.close();
|
||||
}
|
||||
}
|
||||
@@ -90,4 +90,14 @@ public class Settings
|
||||
{
|
||||
return getBoolean(R.string.settings_key_lock_barcode_orientation, false);
|
||||
}
|
||||
|
||||
public boolean getKeepScreenOn()
|
||||
{
|
||||
return getBoolean(R.string.settings_key_keep_screen_on, true);
|
||||
}
|
||||
|
||||
public boolean getDisableLockscreenWhileViewingCard()
|
||||
{
|
||||
return getBoolean(R.string.settings_key_disable_lockscreen_while_viewing_card, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,11 +19,25 @@
|
||||
app:zxing_viewfinder_laser="@color/zxing_custom_viewfinder_laser"
|
||||
app:zxing_viewfinder_mask="@color/zxing_custom_viewfinder_mask"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/add_manually"
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/addManually"
|
||||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:onClick="addManually"/>
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical">
|
||||
|
||||
<Button
|
||||
android:id="@+id/add_from_image"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="addFromImage"
|
||||
android:text="@string/addFromImage" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/add_manually"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="addManually"
|
||||
android:text="@string/addManually" />
|
||||
</LinearLayout>
|
||||
</merge>
|
||||
@@ -136,6 +136,7 @@
|
||||
android:textSize="@dimen/inputSize" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Balance -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -143,7 +144,53 @@
|
||||
android:paddingTop="@dimen/inputPadding"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<!-- Barcode type -->
|
||||
<!-- Balance -->
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/balanceView"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.0"
|
||||
android:hint="@string/balance"
|
||||
android:labelFor="@+id/balanceField">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/balanceField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="numberDecimal"
|
||||
android:digits="0123456789.,$" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<!-- Currency -->
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/balanceCurrencyView"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.0"
|
||||
android:hint="@string/currency"
|
||||
android:labelFor="@+id/balanceCurrencyField">
|
||||
|
||||
<AutoCompleteTextView
|
||||
android:id="@+id/balanceCurrencyField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="none"/>
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Expiration -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="@dimen/inputPadding"
|
||||
android:paddingTop="@dimen/inputPadding"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<!-- Expiry date -->
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/expiryView"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||
@@ -164,15 +211,14 @@
|
||||
<TableLayout
|
||||
android:id="@+id/barcodePart"
|
||||
android:visibility="gone">
|
||||
<!-- Card ID and Barcode type -->
|
||||
|
||||
<!-- Card ID -->
|
||||
<LinearLayout
|
||||
android:id="@+id/cardAndBarcodeLayout"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="@dimen/inputPadding"
|
||||
android:paddingTop="@dimen/inputPadding"
|
||||
android:orientation="horizontal"
|
||||
android:baselineAligned="false">
|
||||
android:orientation="horizontal">
|
||||
|
||||
<!-- Card ID -->
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
@@ -181,7 +227,8 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.0"
|
||||
android:hint="@string/cardId">
|
||||
android:hint="@string/cardId"
|
||||
android:labelFor="@+id/cardIdView">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/cardIdView"
|
||||
@@ -190,6 +237,15 @@
|
||||
/>
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Barcode ID -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="@dimen/inputPadding"
|
||||
android:paddingTop="@dimen/inputPadding"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<!-- Barcode type -->
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
|
||||
@@ -53,6 +53,14 @@
|
||||
android:ellipsize="end"
|
||||
android:textSize="@dimen/noteTextSize"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/balance"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
android:textSize="@dimen/noteTextSize"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/expiry"
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/coordinator_layout"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
>
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fabEdit"
|
||||
@@ -36,30 +36,82 @@
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_percent="0.5"/>
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/scalerGuideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_percent="0.75"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/maximizeButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="15.0dip"
|
||||
android:layout_marginEnd="15.0dip"
|
||||
android:layout_marginTop="10dp"
|
||||
android:padding="0dp"
|
||||
app:srcCompat="@drawable/ic_baseline_arrow_drop_up_24"
|
||||
android:contentDescription="@string/moveBarcodeToTopOfScreen"
|
||||
android:tint="@android:color/white"
|
||||
android:background="@color/colorPrimary"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/barcode"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/barcode"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="20.0dip"
|
||||
android:layout_marginBottom="10.0dip"
|
||||
android:layout_marginStart="15.0dip"
|
||||
android:layout_marginEnd="15.0dip"
|
||||
android:padding="10.0dp"
|
||||
android:padding="10dp"
|
||||
android:background="#ffffff"
|
||||
app:layout_constraintBottom_toTopOf="@+id/centerGuideline"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/maximizeButton"
|
||||
android:contentDescription="@string/barcodeImageDescription"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/minimizeButton"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="15.0dip"
|
||||
android:layout_marginEnd="15.0dip"
|
||||
android:padding="0dp"
|
||||
app:srcCompat="@drawable/ic_baseline_arrow_drop_down_24"
|
||||
android:contentDescription="@string/moveBarcodeToCenterOfScreen"
|
||||
android:tint="@android:color/white"
|
||||
android:background="@color/colorPrimary"
|
||||
app:layout_constraintTop_toBottomOf="@+id/barcode"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/barcodeScaler"
|
||||
android:visibility="gone"
|
||||
android:max="100"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/inputPadding"
|
||||
android:layout_marginStart="15.0dip"
|
||||
android:layout_marginEnd="15.0dip"
|
||||
app:layout_constraintTop_toBottomOf="@+id/scalerGuideline"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/cardIdView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginLeft="10.0dip"
|
||||
android:layout_marginRight="10.0dip"
|
||||
android:layout_marginBottom="80dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/barcode"
|
||||
android:paddingBottom="80dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/minimizeButton"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -80,57 +132,62 @@
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/bottom_sheet"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="20dp"
|
||||
android:visibility="gone"
|
||||
app:behavior_hideable="false"
|
||||
app:behavior_peekHeight="80dp"
|
||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
|
||||
app:behavior_peekHeight="104dp"
|
||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
|
||||
tools:visibility="visible"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/bottomSheetButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="80dp"
|
||||
android:background="@color/colorPrimary"
|
||||
android:gravity="center"
|
||||
app:srcCompat="@drawable/ic_baseline_arrow_drop_up_24"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="fitCenter"
|
||||
android:tint="@android:color/white" />
|
||||
android:layout_gravity="top|start"
|
||||
android:tint="@android:color/white"
|
||||
app:srcCompat="@drawable/ic_baseline_arrow_drop_up_24" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/noteView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/inputBackground"
|
||||
android:gravity="center"
|
||||
android:padding="20dp"
|
||||
app:autoSizeTextType="uniform"
|
||||
app:autoSizeMinTextSize="@dimen/singleCardNoteTextSizeMin"
|
||||
app:autoSizeMaxTextSize="@dimen/singleCardNoteTextSizeMax" />
|
||||
android:textSize="@dimen/singleCardNoteTextSizeMin" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/groupsView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/inputBackground"
|
||||
android:gravity="center"
|
||||
android:padding="20dp"
|
||||
app:autoSizeTextType="uniform"
|
||||
app:autoSizeMinTextSize="@dimen/singleCardNoteTextSizeMin"
|
||||
app:autoSizeMaxTextSize="@dimen/singleCardNoteTextSizeMax" />
|
||||
android:textSize="@dimen/singleCardNoteTextSizeMin" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/balanceView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/inputBackground"
|
||||
android:gravity="center"
|
||||
android:padding="20dp"
|
||||
android:textSize="@dimen/singleCardNoteTextSizeMin" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/expiryView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/inputBackground"
|
||||
android:gravity="center"
|
||||
android:padding="20dp"
|
||||
app:autoSizeTextType="uniform"
|
||||
app:autoSizeMinTextSize="@dimen/singleCardNoteTextSizeMin"
|
||||
app:autoSizeMaxTextSize="@dimen/singleCardNoteTextSizeMax" />
|
||||
android:textSize="@dimen/singleCardNoteTextSizeMin" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
@@ -140,6 +197,7 @@
|
||||
android:clipToPadding="false"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="24dp"
|
||||
android:weightSum="1.0"
|
||||
android:fitsSystemWindows="true">
|
||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
@@ -177,5 +235,14 @@
|
||||
app:contentInsetStart="72.0dip"
|
||||
app:layout_collapseMode="pin" />
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar_landscape"
|
||||
android:visibility="gone"
|
||||
android:background="@android:color/transparent"
|
||||
android:theme="@style/CardView.ActionBarTheme"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="?actionBarSize"
|
||||
app:contentInsetStart="72.0dip"
|
||||
app:layout_collapseMode="pin" />
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
@@ -22,6 +22,10 @@
|
||||
android:id="@+id/action_settings"
|
||||
android:title="@string/settings"
|
||||
app:showAsAction="never"/>
|
||||
<item
|
||||
android:id="@+id/action_privacy_policy"
|
||||
android:title="@string/privacy_policy"
|
||||
app:showAsAction="never"/>
|
||||
<item
|
||||
android:id="@+id/action_about"
|
||||
android:title="@string/about"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?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="action_search">Suchen</string>
|
||||
<string name="action_add">Hinzufügen</string>
|
||||
<string name="noGiftCards">Klicken Sie auf die Schaltfläche + (plus), um zuerst eine Karte hinzuzufügen.
|
||||
\n
|
||||
\nCatima trägt Ihre Karten auf dem Gerät, so dass sie immer in Reichweite sind.</string>
|
||||
@@ -11,7 +11,6 @@
|
||||
<string name="cardId">Kartennummer</string>
|
||||
<string name="cancel">Abbrechen</string>
|
||||
<string name="save">Speichern</string>
|
||||
<string name="editCard">Karte bearbeiten</string>
|
||||
<string name="edit">Bearbeiten</string>
|
||||
<string name="delete">Löschen</string>
|
||||
<string name="confirm">Bestätigen</string>
|
||||
@@ -21,9 +20,9 @@
|
||||
<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…</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="copy_to_clipboard">Nummer in die Zwischenablage kopieren</string>
|
||||
<string name="sendLabel">Senden …</string>
|
||||
<string name="editCardTitle">Kundenkarte bearbeiten</string>
|
||||
<string name="addCardTitle">Neue Kundenkarte</string>
|
||||
<string name="scanCardBarcode">Strichcode scannen</string>
|
||||
@@ -103,4 +102,47 @@
|
||||
<item quantity="other"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> Karten</item>
|
||||
</plurals>
|
||||
<string name="groupsList">Gruppen: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
|
||||
</resources>
|
||||
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
|
||||
<string name="chooseImportType">Aus welcher App Daten importieren\?</string>
|
||||
<string name="parsingBalanceFailed"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g> scheint kein gültiges Guthaben zu sein.</string>
|
||||
<string name="points">Punkte</string>
|
||||
<string name="currency">Währung</string>
|
||||
<string name="balance">Guthaben</string>
|
||||
<string name="moveBarcodeToCenterOfScreen">Strichcode auf dem Bildschirm zentrieren</string>
|
||||
<string name="moveBarcodeToTopOfScreen">Strichcode auf dem Bildschirm nach oben schieben</string>
|
||||
<string name="chooseExpiryDate">Ablaufdatum wählen</string>
|
||||
<string name="never">Nie</string>
|
||||
<string name="expiryDate">Ablaufdatum</string>
|
||||
<string name="editBarcode">Strichcode bearbeiten</string>
|
||||
<string name="barcode">Strichcode</string>
|
||||
<string name="card">Karte</string>
|
||||
<string name="balancePoints"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g> Punkte</string>
|
||||
<string name="balanceSentence">Guthaben: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
|
||||
<string name="expiryStateSentenceExpired">Abgelaufen: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
|
||||
<string name="expiryStateSentence">Läuft ab: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Sperrbildschirm deaktivieren während eine Karte angesehen wird</string>
|
||||
<string name="settings_keep_screen_on">Bildschirm anlassen während eine Karte angesehen wird</string>
|
||||
<string name="privacy_policy_popup_text">Es wird oft verlangt, dass Anwendungen beim ersten Start ihre Datenschutzrichtlinien anzeigen. Hier ist unsere:
|
||||
\n
|
||||
\nWir sammeln KEINE DATEN und unsere Anwendung ist quelloffen, sodass jeder bestätigen kann, dass dies wahr ist.</string>
|
||||
<string name="accept">Annehmen</string>
|
||||
<string name="privacy_policy">Datenschutzrichtlinie</string>
|
||||
<string name="importVoucherVaultMessage">Bitte wählen Sie Ihre Voucher Vault-Exportdatei aus. Es heißt höchstwahrscheinlich vouchervault.json.
|
||||
\n
|
||||
\nEine Voucher Vault-Exportdatei kann durch Drücken von Exportieren erstellt werden.</string>
|
||||
<string name="importVoucherVault">Aus Voucher Vault importieren</string>
|
||||
<string name="importLoyaltyCardKeychainMessage">Bitte wählen Sie Ihre Loyalty Card Keychain-Exportdatei aus. Es heißt höchstwahrscheinlich LoyaltyCardKeychain.csv.
|
||||
\n
|
||||
\nEine Loyalty Card Keychain-Exportdatei kann erstellt werden, indem Sie im Menü Import/Export auf Exportieren klicken.</string>
|
||||
<string name="importLoyaltyCardKeychain">Aus Loyalty Card Keychain importieren</string>
|
||||
<string name="importFidmeMessage">Bitte wählen Sie Ihre Fidme-Exportdatei aus. Es wird höchstwahrscheinlich so etwas wie fidme-export-request-xxxxxx.zip genannt.
|
||||
\n
|
||||
\nEine Fidme-Exportdatei kann in der Fidme-Anwendung erstellt werden, indem Sie zu Ihrem Profil gehen, Datenschutz auswählen und dann auf Meine Daten extrahieren klicken.
|
||||
\n
|
||||
\nBitte beachten Sie, dass Fidme den Strichcode-Typ nicht in den Exportdaten speichert, sodass Sie die importierten Karten manuell bearbeiten müssen.</string>
|
||||
<string name="importFidme">Aus Fidme importieren</string>
|
||||
<string name="importCatimaMessage">Bitte wählen Sie Ihre Catima-Exportdatei aus. Es heißt höchstwahrscheinlich Catima.csv.
|
||||
\n
|
||||
\nEine Catima-Exportdatei kann erstellt werden, indem Sie im Menü Import/Export auf Exportieren klicken.</string>
|
||||
<string name="importCatima">Aus Catima importieren</string>
|
||||
</resources>
|
||||
@@ -1,15 +1,12 @@
|
||||
<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">Προσθήκη</string>
|
||||
|
||||
<string name="noGiftCards">Δεν έχετε κάρτες προς το παρόν. Πατήστε το κουμπί \"+\" (συν) στο πάνω μέρος για να ξεκινήσετε.\n\nΤο Loyalty Card Keychain σας δίνει τη δυνατότητα να έχετε τις κάρτες σας στο τηλέφωνο σας, έτσι ώστε να τις έχετε πάντα μαζί σας.</string>
|
||||
<string name="storeName">Κατάστημα</string>
|
||||
<string name="note">Σημείωση</string>
|
||||
<string name="cardId">Κωδικός Κάρτας</string>
|
||||
<string name="cancel">Άκυρο</string>
|
||||
<string name="save">Αποθήκευση</string>
|
||||
<string name="editCard">Επεξεργασία Κάρτας</string>
|
||||
<string name="edit">Επεξεργασία</string>
|
||||
<string name="delete">Διαγραφή</string>
|
||||
<string name="confirm">Επιβεβαίωση</string>
|
||||
@@ -17,17 +14,15 @@
|
||||
<string name="unlockScreen">Περιστροφή</string>
|
||||
<string name="deleteTitle">Αφαίρεση Κάρτας</string>
|
||||
<string name="deleteConfirmation">Παρακαλώ επιβεβαιώστε ότι θέλετε να διαγράψετε αυτή την κάρτα.</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="ok">Εντάξει</string>
|
||||
<string name="copy_to_clipboard">Αντιγραφή κωδικού στο πρόχειρο</string>
|
||||
<string name="sendLabel">Αποστολή…</string>
|
||||
<string name="sendLabel">Αποστολή…</string>
|
||||
<string name="editCardTitle">Επεξεργασία Κάρτας</string>
|
||||
<string name="addCardTitle">Προσθήκη Κάρτας</string>
|
||||
<string name="scanCardBarcode">Σαρώστε τον κωδικό της κάρτας</string>
|
||||
<string name="cardShortcut">Συντόμευση Κάρτας</string>
|
||||
<string name="noCardsMessage">Δεν υπάρχουν κάρτες. προσθέστε μία πρώτα</string>
|
||||
|
||||
<string name="barcodeImageDescription">Εικόνα του barcode της κάρτας</string>
|
||||
|
||||
<string name="noStoreError">Δεν δώσατε κατάστημα</string>
|
||||
<string name="noCardIdError">Δεν δώσατε κωδικό κάρτας</string>
|
||||
<string name="noCardExistsError">Δεν ήταν δυνατό να εντοπιστεί κάρτα</string>
|
||||
@@ -40,8 +35,8 @@
|
||||
<string name="exportSuccessfulTitle">Εξαγωγή επιτυχής</string>
|
||||
<string name="exportFailedTitle">Εξαγωγή ανεπιτυχής</string>
|
||||
<string name="exportFailed">Δεν εξήχθη</string>
|
||||
<string name="importing">Γίνεται εισαγωγή του…</string>
|
||||
<string name="exporting">Γίνεται εξαγωγή του…</string>
|
||||
<string name="importing">Γίνεται εισαγωγή του…</string>
|
||||
<string name="exporting">Γίνεται εξαγωγή του…</string>
|
||||
<string name="noExternalStoragePermissionError">Δεν είναι δυνατή η εισαγωγή ή εξαγωγή καρτών χωρίς την άδεια πρόσβασης στον εξωτερικό χώρο αποθήκευσης</string>
|
||||
<string name="importOptionFilesystemTitle">Εισαγωγή από το σύστημα αρχείων</string>
|
||||
<string name="importOptionFilesystemExplanation">Επιλέξτε ένα συγκεκριμένο αρχείο από το σύστημα αρχείων.</string>
|
||||
@@ -49,7 +44,6 @@
|
||||
<string name="importOptionApplicationTitle">Χρήση εξωτερικής εφαρμογής</string>
|
||||
<string name="importOptionApplicationExplanation">Κάντε χρήση μίας εξωτερικής εφαρμογής όπως είναι τα Dropbox, Google Drive ή ο αγαπημένος σας διαχειριστής αρχείων για να ανοίξετε ένα αρχείο.</string>
|
||||
<string name="importOptionApplicationButton">Χρήση εξωτερικής εφαρμογής</string>
|
||||
|
||||
<string name="about">Σχετικά</string>
|
||||
<string name="app_license">Άδεια χρήσης υπό το GPLv3.</string>
|
||||
<string name="about_title_fmt">Σχετικά με <xliff:g id="app_name">%s</xliff:g></string>
|
||||
@@ -57,16 +51,17 @@
|
||||
<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">Επιλέξτε Barcode</string>
|
||||
<string name="copy_to_clipboard_toast">Ο κωδικός της κάρτας αντιγράφτηκε στο πρόχειρο</string>
|
||||
|
||||
<string name="thumbnailDescription">Μικρογραφία κάρτας</string>
|
||||
|
||||
<string name="settings">Ρυθμίσεις</string>
|
||||
<string name="settings_category_title_ui">Διεπαφή χρήστη</string>
|
||||
<string name="settings_card_title_list_font_size">Μέγεθος κειμένου λίστας καρτών</string>
|
||||
<string name="settings_card_note_list_font_size">Μέγεθος κειμένου λίστας σημειώσεων καρτών</string>
|
||||
<string name="settings_card_title_font_size">Μέγεθος κειμένου τίτλου κάρτας</string>
|
||||
<string name="settings_card_id_font_size">Μέγεθος κειμένου κωδικού κάρτας</string>
|
||||
</resources>
|
||||
<string name="settings_dark_theme">Σκοτεινό</string>
|
||||
<string name="settings_light_theme">Φωτεινό</string>
|
||||
<string name="settings_system_theme">Σύστημα</string>
|
||||
<string name="barcode">Γραμμικός κώδικας</string>
|
||||
</resources>
|
||||
@@ -1,4 +1,4 @@
|
||||
<resources
|
||||
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
|
||||
</resources>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="barcode">Código de barras</string>
|
||||
</resources>
|
||||
@@ -9,7 +9,6 @@
|
||||
<string name="cardId">Id. de tarjeta</string>
|
||||
<string name="cancel">Cancelar</string>
|
||||
<string name="save">Guardar</string>
|
||||
<string name="editCard">Editar tarjeta</string>
|
||||
<string name="edit">Editar</string>
|
||||
<string name="delete">Eliminar</string>
|
||||
<string name="confirm">Confirmar</string>
|
||||
@@ -103,4 +102,16 @@
|
||||
<item quantity="one"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> tarjeta</item>
|
||||
<item quantity="other"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> tarjetas</item>
|
||||
</plurals>
|
||||
<string name="points">Puntos</string>
|
||||
<string name="moveBarcodeToCenterOfScreen">Centrar el código de barras en la pantalla</string>
|
||||
<string name="moveBarcodeToTopOfScreen">Mover el código de barras a la zona superior de la pantalla</string>
|
||||
<string name="chooseExpiryDate">Elegir fecha de caducidad</string>
|
||||
<string name="never">Nunca</string>
|
||||
<string name="expiryDate">Fecha de caducidad</string>
|
||||
<string name="editBarcode">Editar el código de barras</string>
|
||||
<string name="barcode">Código de barras</string>
|
||||
<string name="card">Tarjeta</string>
|
||||
<string name="balancePoints"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g> puntos</string>
|
||||
<string name="expiryStateSentenceExpired">Expirado: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
|
||||
<string name="expiryStateSentence">Expira: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
|
||||
</resources>
|
||||
|
||||
@@ -4,12 +4,11 @@
|
||||
<string name="noGiftCards">Appuyez d\'abord sur le bouton \"+\" (plus) pour ajouter une carte.
|
||||
\n
|
||||
\nCatima enregistre vos cartes sur votre appareil, pour toujours les avoir à portée de main.</string>
|
||||
<string name="storeName">Nom du Magasin</string>
|
||||
<string name="storeName">Magasin</string>
|
||||
<string name="note">Notes</string>
|
||||
<string name="cardId">Numéro</string>
|
||||
<string name="cancel">Annuler</string>
|
||||
<string name="save">Enregistrer</string>
|
||||
<string name="editCard">Modifier</string>
|
||||
<string name="edit">Modifier</string>
|
||||
<string name="delete">Supprimer</string>
|
||||
<string name="confirm">Confirmer</string>
|
||||
@@ -20,7 +19,7 @@
|
||||
<string name="ok">OK</string>
|
||||
<string name="copy_to_clipboard">Copier le numéro dans le presse-papier</string>
|
||||
<string name="sendLabel">Envoyer…</string>
|
||||
<string name="editCardTitle">Modifier la carte de fidélité</string>
|
||||
<string name="editCardTitle">Modifier la carte</string>
|
||||
<string name="addCardTitle">Ajouter une carte de fidélité</string>
|
||||
<string name="scanCardBarcode">Scanner le code-barres</string>
|
||||
<string name="cardShortcut">Raccourci de carte</string>
|
||||
@@ -103,4 +102,50 @@
|
||||
<item quantity="other"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> cartes</item>
|
||||
</plurals>
|
||||
<string name="groupsList">Groupes : <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
|
||||
</resources>
|
||||
<string name="accept">Accepter</string>
|
||||
<string name="privacy_policy_popup_text">De nombreux magasins d\'applications exigent que les applications affichent leur politique de confidentialité au premier démarrage. Voici la nôtre :
|
||||
\n
|
||||
\nNous ne collectons AUCUNE DONNÉE et le code source de notre application est ouvert afin que tout le monde puisse le confirmer.</string>
|
||||
<string name="privacy_policy">Politique de confidentialité</string>
|
||||
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
|
||||
<string name="chooseImportType">Importer des données à partir de quelle application \?</string>
|
||||
<string name="parsingBalanceFailed"><xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> ne semble pas être un solde valide.</string>
|
||||
<string name="points">Points</string>
|
||||
<string name="currency">Monnaie</string>
|
||||
<string name="balance">Solde</string>
|
||||
<string name="moveBarcodeToCenterOfScreen">Centrer le code-barres sur l\'écran</string>
|
||||
<string name="moveBarcodeToTopOfScreen">Déplacez le code-barres vers le haut de l\'écran</string>
|
||||
<string name="chooseExpiryDate">Choisissez la date d\'expiration</string>
|
||||
<string name="never">Jamais</string>
|
||||
<string name="expiryDate">Date d\'expiration</string>
|
||||
<string name="editBarcode">Modifier le code-barres</string>
|
||||
<string name="barcode">Code-barres</string>
|
||||
<string name="card">Carte</string>
|
||||
<string name="balancePoints"><xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> points</string>
|
||||
<string name="balanceSentence">Solde : <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g></string>
|
||||
<string name="expiryStateSentenceExpired">Expiré : <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g></string>
|
||||
<string name="expiryStateSentence">Expire : <xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g></string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Désactiver l\'écran de verrouillage pendant l\'affichage d\'une carte</string>
|
||||
<string name="settings_keep_screen_on">Garder l\'écran allumé pendant l\'affichage d\'une carte</string>
|
||||
<string name="importVoucherVaultMessage">Veuillez sélectionner votre fichier d\'exportation Voucher Vault. Il est probablement nommé vouchervault.json.
|
||||
\n
|
||||
\nUn fichier d\'exportation Voucher Vault peut être créé en appuyant sur Exporter.</string>
|
||||
<string name="importVoucherVault">Importer depuis Voucher Vault</string>
|
||||
<string name="importLoyaltyCardKeychainMessage">Veuillez sélectionner votre fichier d\'exportation Loyalty Card Keychain . Il s\'appelle très probablement LoyaltyCardKeychain.csv.
|
||||
\n
|
||||
\nUn fichier d\'exportation Loyalty Card Keychain peut être créé en allant dans le menu Importer/Exporter et en appuyant sur Exporter.</string>
|
||||
<string name="importLoyaltyCardKeychain">Importer depuis Loyalty Card Keychain</string>
|
||||
<string name="importFidmeMessage">Veuillez sélectionner votre fichier d\'exportation Fidme. Il est très probablement nommé quelque chose comme fidme-export-request-xxxxxx.zip.
|
||||
\n
|
||||
\nUn fichier d\'exportation Fidme peut être créé dans l\'application Fidme en accédant à votre profil, en choisissant Protection des données, puis en appuyant sur Extraire mes données.
|
||||
\n
|
||||
\nVeuillez noter que Fidme ne stocke pas le type de code-barres dans les données d\'exportation, vous devrez donc éditer les cartes importées manuellement.</string>
|
||||
<string name="importFidme">Importer depuis Fidme</string>
|
||||
<string name="importCatimaMessage">Veuillez sélectionner votre fichier d\'exportation Catima. Il est probablement nommé Catima.csv.
|
||||
\n
|
||||
\nUn fichier d\'exportation Catima peut être créé en allant dans le menu Importer/Exporter et en appuyant sur Exporter.</string>
|
||||
<string name="importCatima">Importer depuis Catima</string>
|
||||
<string name="addFromImage">Sélectionner dans la galerie</string>
|
||||
<string name="errorReadingImage">Erreur lors de la lecture de l\'image</string>
|
||||
<string name="noBarcodeFound">Aucun code-barres n\'a été trouvé</string>
|
||||
</resources>
|
||||
@@ -6,5 +6,4 @@
|
||||
<string name="cardId">מזהה כרטיס</string>
|
||||
<string name="cancel">ביטול</string>
|
||||
<string name="save">שמור</string>
|
||||
<string name="editCard">עריכת כרטיס</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
<string name="barcodeNoBarcode">Questa carta non ha un codice a barre</string>
|
||||
<string name="cancel">Annulla</string>
|
||||
<string name="save">Salva</string>
|
||||
<string name="editCard">Modifica carta</string>
|
||||
<string name="edit">Modifica</string>
|
||||
<string name="delete">Elimina</string>
|
||||
<string name="confirm">Conferma</string>
|
||||
@@ -20,7 +19,7 @@
|
||||
<string name="unlockScreen">Sblocca rotazione</string>
|
||||
<string name="deleteTitle">Rimuovi carta fedeltà</string>
|
||||
<string name="deleteConfirmation">Conferma di voler eliminare questa carta.</string>
|
||||
<string name="ok">Ok</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="copy_to_clipboard">Copia ID negli appunti</string>
|
||||
<string name="share">Condividi</string>
|
||||
<string name="sendLabel">Invia…</string>
|
||||
@@ -103,4 +102,47 @@
|
||||
<item quantity="one"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> carta</item>
|
||||
<item quantity="other"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> carte</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
<string name="parsingBalanceFailed"><xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">%s</xliff:g> non sembra un saldo corretto.</string>
|
||||
<string name="points">Punti</string>
|
||||
<string name="currency">Valuta</string>
|
||||
<string name="balance">Saldo</string>
|
||||
<string name="moveBarcodeToCenterOfScreen">Centra il codice a barre sullo schermo</string>
|
||||
<string name="moveBarcodeToTopOfScreen">Sposta il codice a barre in cima allo schermo</string>
|
||||
<string name="chooseExpiryDate">Scegli data di scadenza</string>
|
||||
<string name="never">Mai</string>
|
||||
<string name="expiryDate">Data di scadenza</string>
|
||||
<string name="editBarcode">Modifica il codice a barre</string>
|
||||
<string name="barcode">Codice a barre</string>
|
||||
<string name="card">Carta</string>
|
||||
<string name="balancePoints"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g> punti</string>
|
||||
<string name="balanceSentence">Saldo: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
|
||||
<string name="expiryStateSentenceExpired">Scaduta: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
|
||||
<string name="expiryStateSentence">Scade: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
|
||||
<string name="settings_keep_screen_on">Mantieni schermo acceso durante la visualizzazione di una carta</string>
|
||||
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
|
||||
<string name="chooseImportType">Da quale app vuoi importare i dati\?</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Mantieni schermo attivo mentre visualizzi una carta</string>
|
||||
<string name="accept">Accetta</string>
|
||||
<string name="privacy_policy_popup_text">È spesso richiesto alle applicazioni di mostrare la loro politica sulla riservatezza al primo avvio. Ecco la nostra:
|
||||
\n
|
||||
\nNon raccogliamo ALCUN DATO e il codice sorgente della nostra applicazione è aperto, quindi chiunque può confermare che ciò sia vero.</string>
|
||||
<string name="privacy_policy">Informativa sulla riservatezza</string>
|
||||
<string name="importVoucherVaultMessage">Seleziona il tuo file di esportazione Voucher Vault. Molto probabilmente si chiama vouchervault.json.
|
||||
\n
|
||||
\nÈ possibile creare un file di esportazione Voucher Vault premendo Esporta.</string>
|
||||
<string name="importVoucherVault">Importa da Voucher Vault</string>
|
||||
<string name="importLoyaltyCardKeychainMessage">Seleziona il file di esportazione Loyalty Card Keychain . Molto probabilmente si chiama LoyaltyCardKeychain.csv.
|
||||
\n
|
||||
\nÈ possibile creare un file di esportazione Loyalty Card Keychain accedendo al menu Importa / Esporta e premendo Esporta.</string>
|
||||
<string name="importLoyaltyCardKeychain">Importa da Loyalty Card Keychain</string>
|
||||
<string name="importFidmeMessage">Seleziona il tuo file di esportazione Fidme. Molto probabilmente si chiama qualcosa come fidme-export-request-xxxxxx.zip.
|
||||
\n
|
||||
\nUn file di esportazione Fidme può essere creato nell\'app Fidme andando sul tuo profilo, scegliendo Protezione dati e quindi premendo Estrai i miei dati.
|
||||
\n
|
||||
\nTieni presente che Fidme non memorizza il tipo di codice a barre nei dati di esportazione, quindi dovrai modificare manualmente le carte importate.</string>
|
||||
<string name="importFidme">Importa da Fidme</string>
|
||||
<string name="importCatimaMessage">Seleziona il tuo file di esportazione Catima. Molto probabilmente si chiama Catima.csv.
|
||||
\n
|
||||
\nÈ possibile creare un file di esportazione Catima andando nel menu Importa / Esporta e premendo Esporta.</string>
|
||||
<string name="importCatima">Importa da Catima</string>
|
||||
</resources>
|
||||
@@ -44,7 +44,6 @@
|
||||
<string name="confirm">확인</string>
|
||||
<string name="delete">삭제</string>
|
||||
<string name="edit">편집</string>
|
||||
<string name="editCard">카드 편집</string>
|
||||
<string name="save">저장</string>
|
||||
<string name="cancel">취소</string>
|
||||
<string name="unstar">즐겨찾기에서 제거</string>
|
||||
@@ -84,4 +83,5 @@
|
||||
<string name="noStoreError">매장을 입력하지 않음</string>
|
||||
<string name="starImage">즐겨찾기 별</string>
|
||||
<string name="settings_display_barcode_max_brightness">바코드를 표시할 때 화면 밝기 높이기</string>
|
||||
<string name="barcode">바코드</string>
|
||||
</resources>
|
||||
@@ -10,7 +10,6 @@
|
||||
<string name="barcodeType">Strekkodetype</string>
|
||||
<string name="cancel">Avbryt</string>
|
||||
<string name="save">Lagre</string>
|
||||
<string name="editCard">Rediger kort</string>
|
||||
<string name="edit">Rediger</string>
|
||||
<string name="delete">Slett</string>
|
||||
<string name="confirm">Bekreft</string>
|
||||
@@ -110,4 +109,22 @@
|
||||
<string name="chooseExpiryDate">Velg utløpsdato</string>
|
||||
<string name="never">Aldri</string>
|
||||
<string name="expiryDate">Utløpsdato</string>
|
||||
</resources>
|
||||
<string name="expiryStateSentenceExpired">Utløpt: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
|
||||
<string name="moveBarcodeToCenterOfScreen">Sentrer strekkoden på skjermen</string>
|
||||
<string name="moveBarcodeToTopOfScreen">Flytt strekkoden til toppen av skjermen</string>
|
||||
<string name="parsingBalanceFailed"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g> ser ikke ut til å være en gyldig saldo.</string>
|
||||
<string name="points">Poeng</string>
|
||||
<string name="currency">Valuta</string>
|
||||
<string name="balance">Saldo</string>
|
||||
<string name="balancePoints"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g> poeng</string>
|
||||
<string name="balanceSentence">Saldo: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
|
||||
<string name="chooseImportType">Hvilket program vil du importere data fra\?</string>
|
||||
<string name="app_loyalty_card_keychain">Kundekortsknippe</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Skru av låseskjerm under kortvisning</string>
|
||||
<string name="settings_keep_screen_on">Behold skjerm påslått under kortvisning</string>
|
||||
<string name="privacy_policy_popup_text">Mange programbutikker krever at programmer viser personvernspraksisen sin ved første oppstart. Her er vår:
|
||||
\n
|
||||
\nVi SAMLER IKKE IN NOEN DATA overhodet, og programmet vårt er fri programvare, så alle kan bekrefte at dette stemmer.</string>
|
||||
<string name="accept">Godta</string>
|
||||
<string name="privacy_policy">Personvernspraksis</string>
|
||||
</resources>
|
||||
@@ -13,7 +13,6 @@
|
||||
<string name="barcodeNoBarcode">Deze kaart heeft geen barcode</string>
|
||||
<string name="cancel">Annuleren</string>
|
||||
<string name="save">Opslaan</string>
|
||||
<string name="editCard">Kaart bewerken</string>
|
||||
<string name="edit">Bewerken</string>
|
||||
<string name="delete">Verwijderen</string>
|
||||
<string name="confirm">Bevestigen</string>
|
||||
@@ -110,4 +109,43 @@
|
||||
<string name="never">Nooit</string>
|
||||
<string name="expiryDate">Vervaldatum</string>
|
||||
<string name="expiryStateSentence">Vervalt op <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
|
||||
</resources>
|
||||
<string name="expiryStateSentenceExpired">Verlopen: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
|
||||
<string name="moveBarcodeToCenterOfScreen">Barcode verplaatsen naar midden van scherm</string>
|
||||
<string name="moveBarcodeToTopOfScreen">Barcode verplaatsen naar bovenkant van scherm</string>
|
||||
<string name="parsingBalanceFailed"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g> lijkt geen geldig saldo te zijn.</string>
|
||||
<string name="points">Aantal punten</string>
|
||||
<string name="currency">Valuta</string>
|
||||
<string name="balance">Saldo</string>
|
||||
<string name="balancePoints"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g> punten</string>
|
||||
<string name="balanceSentence">Saldo: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
|
||||
<string name="chooseImportType">Uit welke app wil je gegevens importeren\?</string>
|
||||
<string name="app_loyalty_card_keychain">Klantenkaartkluis</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Vergrendelscherm uitschakelen tijdens tonen van kaart</string>
|
||||
<string name="settings_keep_screen_on">Scherm niet uitschakelen tijdens tonen van kaart</string>
|
||||
<string name="privacy_policy_popup_text">Veel appwinkels vereisen dat apps na de eerste keer opstarten hun privacybeleid tonen. Dat van ons is eenvoudig:
|
||||
\n
|
||||
\nWe verzamelen HELEMAAL NIKS. Bovendien is onze app open source, zodat een ieder met eigen ogen kan zien wat de app wel of niet doet.</string>
|
||||
<string name="privacy_policy">Privacybeleid</string>
|
||||
<string name="accept">Accepteren</string>
|
||||
<string name="importVoucherVaultMessage">Kies het gewenste Voucher Vault-exportbestand - normaliter heet dit bestand \'vouchervault.json\'.
|
||||
\n
|
||||
\nGa naar het Exportmenu om een Voucher Vault-exportbestand samen te stellen.</string>
|
||||
<string name="importVoucherVault">Importeren uit Voucher Vault</string>
|
||||
<string name="importLoyaltyCardKeychainMessage">Kies het gewenste Klantenkaartkluis-exportbestand - normaliter heet dit bestand \'LoyaltyCardKeychain.csv\'.
|
||||
\n
|
||||
\nGa naar het Import-/Exportmenu om een Klantenkaartkluis-exportbestand samen te stellen.</string>
|
||||
<string name="importLoyaltyCardKeychain">Importeren uit Klantenkaartkluis</string>
|
||||
<string name="importFidmeMessage">Kies het gewenste Fidme-exportbestand - normaliter heet dit bestand \'fidme-export-request-xxxxxx.zip\'.
|
||||
\n
|
||||
\nOpen de Fidme-app, ga naar je profiel en druk op \'Gegevensbescherming\' om een exportbestand samen te stellen.
|
||||
\n
|
||||
\nLet op: Fidme slaat de soorten barcodes niet op in het exportbestand, dus dat moet je na achteraf nog instellen.</string>
|
||||
<string name="importCatimaMessage">Kies het gewenste Catima-exportbestand - normaliter heet dit bestand \'Catima.csv\'.
|
||||
\n
|
||||
\nGa naar het Import-/Exportmenu om een Catima-exportbestand samen te stellen.</string>
|
||||
<string name="importFidme">Importeren uit Fidme</string>
|
||||
<string name="importCatima">Importeren uit Catima</string>
|
||||
<string name="errorReadingImage">De afbeelding kan niet worden uitgelezen</string>
|
||||
<string name="noBarcodeFound">Geen barcode aangetroffen</string>
|
||||
<string name="addFromImage">Afbeelding kiezen uit galerij</string>
|
||||
</resources>
|
||||
@@ -10,7 +10,6 @@
|
||||
<string name="barcodeNoBarcode">Ta karta nie ma kodu kreskowego</string>
|
||||
<string name="cancel">Anuluj</string>
|
||||
<string name="save">Zapisz</string>
|
||||
<string name="editCard">Edytuj kartę</string>
|
||||
<string name="edit">Edytuj</string>
|
||||
<string name="delete">Usuń</string>
|
||||
<string name="confirm">Potwierdź</string>
|
||||
|
||||
@@ -1,31 +1,30 @@
|
||||
<?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">Нажмите кнопку + (плюс), чтобы сначала добавить карту.
|
||||
<string name="action_add">Добавить</string>
|
||||
<string name="noGiftCards">Нажмите кнопку + (плюс), чтобы добавить карту.
|
||||
\n
|
||||
\nCatima хранит ваши карты на устройстве, поэтому они всегда под рукой.</string>
|
||||
<string name="noMatchingGiftCards">"Ничего не найдено. Попробуйте изменить свой поиск."</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="barcodeType">Тип штрих-кода</string>
|
||||
<string name="barcodeNoBarcode">Эта карта без штрих-кода</string>
|
||||
<string name="cancel">Отменить</string>
|
||||
<string name="save">Сохранить</string>
|
||||
<string name="editCard">Редактировать штрих-код</string>
|
||||
<string name="edit">Редактировать</string>
|
||||
<string name="edit">Изменить</string>
|
||||
<string name="delete">Удалить карту</string>
|
||||
<string name="confirm">Подтвердить</string>
|
||||
<string name="lockScreen">Блокировать поворот экрана</string>
|
||||
<string name="unlockScreen">Автоповорот экрана</string>
|
||||
<string name="deleteTitle">Удаление карты</string>
|
||||
<string name="deleteConfirmation">Пожалуйста подтвердите удаление карты.</string>
|
||||
<string name="deleteConfirmation">Подтвердите удаление карты.</string>
|
||||
<string name="ok">ОК</string>
|
||||
<string name="copy_to_clipboard">Скопировать номер карты в буфер обмена</string>
|
||||
<string name="copy_to_clipboard">Копировать номер карты</string>
|
||||
<string name="share">Переслать</string>
|
||||
<string name="sendLabel">Отправить…</string>
|
||||
<string name="editCardTitle">Редактировать карту</string>
|
||||
<string name="editCardTitle">Изменить карту</string>
|
||||
<string name="addCardTitle">Добавить карту</string>
|
||||
<string name="scanCardBarcode">Отсканируйте штрих-код</string>
|
||||
<string name="cardShortcut">Ярлык карты</string>
|
||||
@@ -34,16 +33,16 @@
|
||||
<string name="noStoreError">Название магазина не указано</string>
|
||||
<string name="noCardIdError">Номер карты не указан</string>
|
||||
<string name="noCardExistsError">Карта не найдена</string>
|
||||
<string name="failedParsingImportUriError">Не удалось разобрать импортируемый URI</string>
|
||||
<string name="failedParsingImportUriError">Невозможно разобрать импортируемый URI</string>
|
||||
<string name="importExport">Импорт/Экспорт</string>
|
||||
<string name="exportName">Экспорт</string>
|
||||
<string name="importExportHelp">Резервное копирование карт позволяет переместить их на другое устройство.</string>
|
||||
<string name="importSuccessfulTitle">Импортировано</string>
|
||||
<string name="importFailedTitle">Импорт не удался</string>
|
||||
<string name="importFailed">Не удалось импортировать карты</string>
|
||||
<string name="importFailedTitle">Импорт не выполнен</string>
|
||||
<string name="importFailed">Невозможно импортировать карты</string>
|
||||
<string name="exportSuccessfulTitle">Экспортировано</string>
|
||||
<string name="exportFailedTitle">Экспорт не удался</string>
|
||||
<string name="exportFailed">Не удалось экспортировать</string>
|
||||
<string name="exportFailedTitle">Экспорт не выполнен</string>
|
||||
<string name="exportFailed">Невозможно экспортировать карты</string>
|
||||
<string name="importing">Импорт…</string>
|
||||
<string name="exporting">Экспорт…</string>
|
||||
<string name="noExternalStoragePermissionError">Импорт или экспорт невозможен без разрешения на доступ к хранилищу</string>
|
||||
@@ -54,15 +53,15 @@
|
||||
<string name="importOptionApplicationTitle">Использование другого приложения</string>
|
||||
<string name="importOptionApplicationExplanation">Используйте любое приложение или ваш любимый файловый менеджер, чтобы открыть файл.</string>
|
||||
<string name="importOptionApplicationButton">Использовать другое приложение</string>
|
||||
<string name="about">О программе</string>
|
||||
<string name="about">О приложении</string>
|
||||
<string name="app_license">Авторское лево свободного программного обеспечения, лицензия GPLv3+.</string>
|
||||
<string name="about_title_fmt">О программе <xliff:g id="app_name">%s</xliff:g></string>
|
||||
<string name="about_title_fmt">О приложении <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" 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_revision_fmt">Информация о версиях: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" 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="enterBarcodeInstructions">Введите номер карты и выберите тип штрих-кода.</string>
|
||||
<string name="copy_to_clipboard_toast">Номер карты скопирован в буфер обмена</string>
|
||||
<string name="thumbnailDescription">Логотип карты</string>
|
||||
<string name="settings">Настройки</string>
|
||||
@@ -71,22 +70,22 @@
|
||||
<string name="settings_system_theme">Системная</string>
|
||||
<string name="settings_light_theme">Светлая</string>
|
||||
<string name="settings_dark_theme">Тёмная</string>
|
||||
<string name="settings_card_title_list_font_size">Размер шрифта для названий карт</string>
|
||||
<string name="settings_card_note_list_font_size">Размер шрифта примечания для списка</string>
|
||||
<string name="settings_card_title_font_size">Размер шрифта названия карты</string>
|
||||
<string name="settings_card_title_list_font_size">Размер шрифта названия карты (список)</string>
|
||||
<string name="settings_card_note_list_font_size">Размер шрифта примечания (список)</string>
|
||||
<string name="settings_card_title_font_size">Размер шрифта названия карты (просмотр)</string>
|
||||
<string name="settings_card_id_font_size">Размер шрифта номера карты</string>
|
||||
<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>
|
||||
<string name="exportSuccessful">Данные карты экспортированы</string>
|
||||
<string name="exportSuccessful">Данные карт успешно экспортированы</string>
|
||||
<string name="all">Все</string>
|
||||
<string name="noGroups">Нажмите кнопку + (плюс), чтобы сначала добавить группы.
|
||||
<string name="noGroups">Нажмите кнопку + (плюс), чтобы добавить группы.
|
||||
\n
|
||||
\nГруппы упрощают поиск.</string>
|
||||
<string name="groups">Группы</string>
|
||||
<string name="enter_group_name">Введите название группы</string>
|
||||
<string name="importSuccessful">Данные карты лояльности успешно импортированы</string>
|
||||
<string name="starImage">Любимая звезда</string>
|
||||
<string name="importSuccessful">Данные карт успешно импортированы</string>
|
||||
<string name="starImage">Звезда избранного</string>
|
||||
<string name="app_copyright_old">На основе Loyalty Card Keychain, авторские права 2016–2020 Branden Archer.</string>
|
||||
<string name="unstar">Удалить из избранного</string>
|
||||
<string name="star">Добавить в избранное</string>
|
||||
@@ -94,5 +93,61 @@
|
||||
<string name="deleteConfirmationGroup">Подтвердите, что хотите удалить эту группу</string>
|
||||
<string name="leaveWithoutSaveConfirmation">Вы уверены, что хотите покинуть этот экран\? Изменения не будут сохранены.</string>
|
||||
<string name="leaveWithoutSaveTitle">Выйти без сохранения</string>
|
||||
<string name="failedOpeningFileManager">Не удалось открыть файловый менеджер. Пожалуйста, убедитись, что он установлен.</string>
|
||||
<string name="failedOpeningFileManager">Невозможно открыть файловый менеджер. Убедитесь, что он установлен.</string>
|
||||
<string name="balancePoints"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g> баллов</string>
|
||||
<string name="balanceSentence">Баланс: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
|
||||
<string name="expiryStateSentenceExpired">Истёк срок действия: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
|
||||
<string name="expiryStateSentence">Истекает срок действия: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
|
||||
<string name="points">Баллы</string>
|
||||
<string name="parsingBalanceFailed"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g> не похож на правильный баланс.</string>
|
||||
<string name="addManually">Ручной ввод номера карты</string>
|
||||
<string name="privacy_policy_popup_text">Многие магазины приложений требуют, чтобы приложение показывало свою политику конфиденциальности при первом запуске. Вот наша:
|
||||
\n
|
||||
\nМы НЕ собираем НИКАКИХ ДАННЫХ и наше приложение имеет открытый исходный код, так что любой может убедиться в том, что это правда.</string>
|
||||
<string name="privacy_policy">Политика конфиденциальности</string>
|
||||
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
|
||||
<string name="chooseImportType">Из какого приложения импортировать данные\?</string>
|
||||
<string name="balance">Баланс</string>
|
||||
<string name="moveBarcodeToTopOfScreen">Переместить штрих-код в верхнюю часть экрана</string>
|
||||
<string name="moveBarcodeToCenterOfScreen">Центрировать штрих-код на экране</string>
|
||||
<string name="currency">Валюта</string>
|
||||
<string name="chooseExpiryDate">Указать срок действия</string>
|
||||
<string name="never">Никогда</string>
|
||||
<string name="expiryDate">Окончание срока действия</string>
|
||||
<string name="editBarcode">Изменить штрих-код</string>
|
||||
<string name="barcode">Штрих-код</string>
|
||||
<string name="card">Карта</string>
|
||||
<string name="groupsList">Группы: <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%s</xliff:g></string>
|
||||
<string name="moveDown">Переместить ниже в вписке</string>
|
||||
<string name="moveUp">Переместить выше в вписке</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Не блокировать экран при показе карты</string>
|
||||
<string name="settings_keep_screen_on">Не отключать экран при показе карты</string>
|
||||
<plurals name="groupCardCount">
|
||||
<item quantity="one"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> карта</item>
|
||||
<item quantity="few"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> карты</item>
|
||||
<item quantity="many"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> карт</item>
|
||||
<item quantity="other"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> карт</item>
|
||||
</plurals>
|
||||
<string name="accept">Принять</string>
|
||||
<string name="importVoucherVaultMessage">Выберите файл экспорта Voucher Vault. Обычно он называется \"vouchervault.json\".
|
||||
\n
|
||||
\nФайл экспорта Voucher Vault можно создать, нажав кнопку \"Экспорт\".</string>
|
||||
<string name="importVoucherVault">Импорт из Voucher Vault</string>
|
||||
<string name="importFidmeMessage">Выберите файл экспорта Fidme. Обычно он называется как-то вроде \"fidme-export-request-xxxxxx.zip\".
|
||||
\n
|
||||
\nФайл экспорта Fidme можно создать в приложении Fidme, перейдя в свой профиль, выбрав функцию \"Защита данных\", и затем нажав кнопку \"Извлечь мои данные\".
|
||||
\n
|
||||
\nОбратите внимание, что Fidme не сохраняет тип штрих-кода в экспортированных данных, поэтому вам придётся редактировать импортированные данные карт вручную.</string>
|
||||
<string name="importLoyaltyCardKeychainMessage">Выберите файл экспорта Loyalty Card Keychain. Обычно он называется \"LoyaltyCardKeychain.csv\".
|
||||
\n
|
||||
\nФайл экспорта Loyalty Card Keychain можно создать, перейдя в меню \"Импорт/Экспорт\" и нажав кнопку \"Экспорт\".</string>
|
||||
<string name="importLoyaltyCardKeychain">Импорт из Loyalty Card Keychain</string>
|
||||
<string name="importCatimaMessage">Выберите файл экспорта Catima. Обычно он называется \"Catima.csv\".
|
||||
\n
|
||||
\nФайл экспорта Catima можно создать, перейдя в меню \"Импорт/Экспорт\" и нажав кнопку \"Экспорт\".</string>
|
||||
<string name="importFidme">Импорт из Fidme</string>
|
||||
<string name="importCatima">Импорт из Catima</string>
|
||||
<string name="errorReadingImage">Ошибка при чтении изображения</string>
|
||||
<string name="noBarcodeFound">Штрих-код не найден</string>
|
||||
<string name="addFromImage">Выбрать изображение из галереи</string>
|
||||
</resources>
|
||||
@@ -9,7 +9,6 @@
|
||||
<string name="cardId">ID karty</string>
|
||||
<string name="cancel">Zrušiť</string>
|
||||
<string name="save">Uložiť</string>
|
||||
<string name="editCard">Úprava karty</string>
|
||||
<string name="edit">Upraviť</string>
|
||||
<string name="delete">Vymazať</string>
|
||||
<string name="confirm">Potvrdiť</string>
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
<string name="cardId">Št. kartice</string>
|
||||
<string name="cancel">Prekliči</string>
|
||||
<string name="save">Shrani</string>
|
||||
<string name="editCard">Uredi kartico</string>
|
||||
<string name="edit">Uredi</string>
|
||||
<string name="delete">Izbriši</string>
|
||||
<string name="confirm">Potrdi</string>
|
||||
|
||||
9
app/src/main/res/values/arrays.xml
Normal file
9
app/src/main/res/values/arrays.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="import_types_array">
|
||||
<item>Catima</item>
|
||||
<item>Fidme</item>
|
||||
<item>@string/app_loyalty_card_keychain</item>
|
||||
<item>Voucher Vault</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="save">Save</string>
|
||||
<string name="editCard">Edit Card</string>
|
||||
<string name="edit">Edit</string>
|
||||
<string name="delete">Delete</string>
|
||||
<string name="confirm">Confirm</string>
|
||||
@@ -104,8 +103,13 @@
|
||||
<string name="settings_key_display_barcode_max_brightness" translatable="false">pref_display_card_max_brightness</string>
|
||||
<string name="settings_lock_barcode_orientation">Lock barcode orientation</string>
|
||||
<string name="settings_key_lock_barcode_orientation" translatable="false">pref_lock_barcode_orientation</string>
|
||||
<string name="settings_keep_screen_on">Keep screen on while viewing a card</string>
|
||||
<string name="settings_key_keep_screen_on" translatable="false">pref_keep_screen_on</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Disable lock screen while viewing a card</string>
|
||||
<string name="settings_key_disable_lockscreen_while_viewing_card" translatable="false">pref_disable_lockscreen_while_viewing_card</string>
|
||||
|
||||
<string name="sharedpreference_active_tab" translatable="false">sharedpreference_active_tab</string>
|
||||
<string name="sharedpreference_privacy_policy_shown" translatable="false">sharedpreference_privacy_policy_shown</string>
|
||||
|
||||
<string name="intent_import_card_from_url_share_text">I want to share a card with you</string>
|
||||
<string name="intent_import_card_from_url_host" translatable="false">thelastproject.github.io</string>
|
||||
@@ -131,9 +135,12 @@
|
||||
<string name="leaveWithoutSaveTitle">Leave without saving</string>
|
||||
<string name="leaveWithoutSaveConfirmation">Are you sure you want to leave this screen? Changed made will not be saved.</string>
|
||||
<string name="addManually">Manually enter card ID</string>
|
||||
<string name="addFromImage">Select image from gallery</string>
|
||||
<string name="groupsList">Groups: <xliff:g>%s</xliff:g></string>
|
||||
<string name="expiryStateSentence">Expires: <xliff:g>%s</xliff:g></string>
|
||||
<string name="expiryStateSentenceExpired">Expired: <xliff:g>%s</xliff:g></string>
|
||||
<string name="balanceSentence">Balance: <xliff:g>%s</xliff:g></string>
|
||||
<string name="balancePoints"><xliff:g>%s</xliff:g> points</string>
|
||||
|
||||
<string name="card">Card</string>
|
||||
<string name="barcode">Barcode</string>
|
||||
@@ -141,4 +148,29 @@
|
||||
<string name="expiryDate">Expiry date</string>
|
||||
<string name="never">Never</string>
|
||||
<string name="chooseExpiryDate">Choose expiry date</string>
|
||||
<string name="moveBarcodeToTopOfScreen">Move the barcode to the top of the screen</string>
|
||||
<string name="moveBarcodeToCenterOfScreen">Center the barcode on the screen</string>
|
||||
|
||||
<string name="noBarcodeFound">No barcode was found</string>
|
||||
<string name="errorReadingImage">Error reading image</string>
|
||||
|
||||
<string name="balance">Balance</string>
|
||||
<string name="currency">Currency</string>
|
||||
<string name="points">Points</string>
|
||||
|
||||
<string name="parsingBalanceFailed"><xliff:g>%s</xliff:g> does not seem to be a valid balance.</string>
|
||||
<string name="chooseImportType">Import data from which app?</string>
|
||||
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
|
||||
|
||||
<string name="privacy_policy">Privacy Policy</string>
|
||||
<string name="privacy_policy_popup_text">Many app stores require apps to show their privacy policy on first start. Here is ours:\n\nWe collect NO DATA AT ALL and our app is Open Source so anyone can confirm this to be true.</string>
|
||||
<string name="accept">Accept</string>
|
||||
<string name="importCatima">Import from Catima</string>
|
||||
<string name="importCatimaMessage">Please select your Catima export file. It is most likely named Catima.csv.\n\nA Catima export file can be created by going to the Import/Export menu and pressing "Export".</string>
|
||||
<string name="importFidme">Import from Fidme</string>
|
||||
<string name="importFidmeMessage">Please select your Fidme export file. It is most likely named something like fidme-export-request-xxxxxx.zip.\n\nA Fidme export file can be created in the Fidme app by going to your profile, choosing "Data Protection" and then pressing "Extract my data".\n\nPlease note that Fidme does not store the barcode type in the export data so you will have to edit the imported cards manually.</string>
|
||||
<string name="importLoyaltyCardKeychain">Import from Loyalty Card Keychain</string>
|
||||
<string name="importLoyaltyCardKeychainMessage">Please select your Loyalty Card Keychain export file. It is most likely named LoyaltyCardKeychain.csv.\n\nA Loyalty Card Keychain export file can be created by going to the Import/Export menu and pressing "Export".</string>
|
||||
<string name="importVoucherVault">Import from Voucher Vault</string>
|
||||
<string name="importVoucherVaultMessage">Please select your Voucher Vault export file. It is most likely named vouchervault.json.\n\nA Voucher Vault export file can be created by pressing "Export".</string>
|
||||
</resources>
|
||||
|
||||
@@ -58,6 +58,18 @@
|
||||
android:key="@string/settings_key_lock_barcode_orientation"
|
||||
android:title="@string/settings_lock_barcode_orientation"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
android:key="@string/settings_key_keep_screen_on"
|
||||
android:title="@string/settings_keep_screen_on"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
android:key="@string/settings_key_disable_lockscreen_while_viewing_card"
|
||||
android:title="@string/settings_disable_lockscreen_while_viewing_card"
|
||||
app:iconSpaceReserved="false" />
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
@@ -15,7 +15,9 @@ import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Currency;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -43,7 +45,7 @@ public class DatabaseTest
|
||||
public void addRemoveOneGiftCard()
|
||||
{
|
||||
assertEquals(0, db.getLoyaltyCardCount());
|
||||
long id = db.insertLoyaltyCard("store", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0);
|
||||
long id = db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0);
|
||||
boolean result = (id != -1);
|
||||
assertTrue(result);
|
||||
assertEquals(1, db.getLoyaltyCardCount());
|
||||
@@ -53,6 +55,8 @@ public class DatabaseTest
|
||||
assertEquals("store", loyaltyCard.store);
|
||||
assertEquals("note", loyaltyCard.note);
|
||||
assertEquals(null, loyaltyCard.expiry);
|
||||
assertEquals(new BigDecimal("0"), loyaltyCard.balance);
|
||||
assertEquals(null, loyaltyCard.balanceType);
|
||||
assertEquals("cardId", loyaltyCard.cardId);
|
||||
assertEquals(0, loyaltyCard.starStatus);
|
||||
assertEquals(BarcodeFormat.UPC_A.toString(), loyaltyCard.barcodeType);
|
||||
@@ -66,12 +70,12 @@ public class DatabaseTest
|
||||
@Test
|
||||
public void updateGiftCard()
|
||||
{
|
||||
long id = db.insertLoyaltyCard("store", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0);
|
||||
long id = db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0);
|
||||
boolean result = (id != -1);
|
||||
assertTrue(result);
|
||||
assertEquals(1, db.getLoyaltyCardCount());
|
||||
|
||||
result = db.updateLoyaltyCard(1, "store1", "note1", null, "cardId1", BarcodeFormat.AZTEC.toString(), DEFAULT_HEADER_COLOR);
|
||||
result = db.updateLoyaltyCard(1, "store1", "note1", null, new BigDecimal("10.00"), Currency.getInstance("EUR"), "cardId1", BarcodeFormat.AZTEC.toString(), DEFAULT_HEADER_COLOR);
|
||||
assertTrue(result);
|
||||
assertEquals(1, db.getLoyaltyCardCount());
|
||||
|
||||
@@ -80,6 +84,8 @@ public class DatabaseTest
|
||||
assertEquals("store1", loyaltyCard.store);
|
||||
assertEquals("note1", loyaltyCard.note);
|
||||
assertEquals(null, loyaltyCard.expiry);
|
||||
assertEquals(new BigDecimal("10.00"), loyaltyCard.balance);
|
||||
assertEquals(Currency.getInstance("EUR"), loyaltyCard.balanceType);
|
||||
assertEquals("cardId1", loyaltyCard.cardId);
|
||||
assertEquals(0, loyaltyCard.starStatus);
|
||||
assertEquals(BarcodeFormat.AZTEC.toString(), loyaltyCard.barcodeType);
|
||||
@@ -88,7 +94,7 @@ public class DatabaseTest
|
||||
@Test
|
||||
public void updateGiftCardOnlyStar()
|
||||
{
|
||||
long id = db.insertLoyaltyCard("store", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0);
|
||||
long id = db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0);
|
||||
boolean result = (id != -1);
|
||||
assertTrue(result);
|
||||
assertEquals(1, db.getLoyaltyCardCount());
|
||||
@@ -102,6 +108,8 @@ public class DatabaseTest
|
||||
assertEquals("store", loyaltyCard.store);
|
||||
assertEquals("note", loyaltyCard.note);
|
||||
assertEquals(null, loyaltyCard.expiry);
|
||||
assertEquals(new BigDecimal("0"), loyaltyCard.balance);
|
||||
assertEquals(null, loyaltyCard.balanceType);
|
||||
assertEquals("cardId", loyaltyCard.cardId);
|
||||
assertEquals(1, loyaltyCard.starStatus);
|
||||
assertEquals(BarcodeFormat.UPC_A.toString(), loyaltyCard.barcodeType);
|
||||
@@ -112,7 +120,7 @@ public class DatabaseTest
|
||||
{
|
||||
assertEquals(0, db.getLoyaltyCardCount());
|
||||
|
||||
boolean result = db.updateLoyaltyCard(1, "store1", "note1", null, "cardId1",
|
||||
boolean result = db.updateLoyaltyCard(1, "store1", "note1", null, new BigDecimal("0"), null, "cardId1",
|
||||
BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR);
|
||||
assertEquals(false, result);
|
||||
assertEquals(0, db.getLoyaltyCardCount());
|
||||
@@ -121,7 +129,7 @@ public class DatabaseTest
|
||||
@Test
|
||||
public void emptyGiftCardValues()
|
||||
{
|
||||
long id = db.insertLoyaltyCard("", "", null, "", "", null, 0);
|
||||
long id = db.insertLoyaltyCard("", "", null, new BigDecimal("0"), null, "", "", null, 0);
|
||||
boolean result = (id != -1);
|
||||
assertTrue(result);
|
||||
assertEquals(1, db.getLoyaltyCardCount());
|
||||
@@ -131,6 +139,8 @@ public class DatabaseTest
|
||||
assertEquals("", loyaltyCard.store);
|
||||
assertEquals("", loyaltyCard.note);
|
||||
assertEquals(null, loyaltyCard.expiry);
|
||||
assertEquals(new BigDecimal("0"), loyaltyCard.balance);
|
||||
assertEquals(null, loyaltyCard.balanceType);
|
||||
assertEquals("", loyaltyCard.cardId);
|
||||
assertEquals("", loyaltyCard.barcodeType);
|
||||
}
|
||||
@@ -144,7 +154,7 @@ public class DatabaseTest
|
||||
// that they are sorted
|
||||
for(int index = CARDS_TO_ADD-1; index >= 0; index--)
|
||||
{
|
||||
long id = db.insertLoyaltyCard("store" + index, "note" + index, null, "cardId" + index,
|
||||
long id = db.insertLoyaltyCard("store" + index, "note" + index, null, new BigDecimal("0"), null, "cardId" + index,
|
||||
BarcodeFormat.UPC_A.toString(), index, 0);
|
||||
boolean result = (id != -1);
|
||||
assertTrue(result);
|
||||
@@ -164,6 +174,8 @@ public class DatabaseTest
|
||||
assertEquals("store"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STORE)));
|
||||
assertEquals("note"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE)));
|
||||
assertEquals(0, cursor.getLong(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.EXPIRY)));
|
||||
assertEquals("0", cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE)));
|
||||
assertEquals(null, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE)));
|
||||
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)));
|
||||
@@ -187,12 +199,12 @@ public class DatabaseTest
|
||||
for(int index = CARDS_TO_ADD-1; index >= 0; index--)
|
||||
{
|
||||
if (index == CARDS_TO_ADD-1) {
|
||||
id = db.insertLoyaltyCard("store" + index, "note" + index, null, "cardId" + index,
|
||||
id = db.insertLoyaltyCard("store" + index, "note" + index, null, new BigDecimal("0"), null, "cardId" + index,
|
||||
BarcodeFormat.UPC_A.toString(), index, 1);
|
||||
}
|
||||
|
||||
else {
|
||||
id = db.insertLoyaltyCard("store" + index, "note" + index, null, "cardId" + index,
|
||||
id = db.insertLoyaltyCard("store" + index, "note" + index, null, new BigDecimal("0"), null, "cardId" + index,
|
||||
BarcodeFormat.UPC_A.toString(), index, 0);
|
||||
}
|
||||
boolean result = (id != -1);
|
||||
@@ -211,6 +223,8 @@ public class DatabaseTest
|
||||
assertEquals("store"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STORE)));
|
||||
assertEquals("note"+index, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE)));
|
||||
assertEquals(0, cursor.getLong(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.EXPIRY)));
|
||||
assertEquals("0", cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE)));
|
||||
assertEquals(null, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE)));
|
||||
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)));
|
||||
@@ -222,7 +236,9 @@ public class DatabaseTest
|
||||
{
|
||||
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(null, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.EXPIRY)));
|
||||
assertEquals("0", cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE)));
|
||||
assertEquals(null, cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE)));
|
||||
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)));
|
||||
@@ -286,23 +302,51 @@ public class DatabaseTest
|
||||
@Test
|
||||
public void updateGroup()
|
||||
{
|
||||
long id = db.insertGroup("group one");
|
||||
// Create card
|
||||
assertEquals(0, db.getLoyaltyCardCount());
|
||||
long id = db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0);
|
||||
boolean result = (id != -1);
|
||||
assertTrue(result);
|
||||
assertEquals(1, db.getLoyaltyCardCount());
|
||||
|
||||
// Create group
|
||||
long groupId = db.insertGroup("group one");
|
||||
result = (groupId != -1);
|
||||
assertTrue(result);
|
||||
assertEquals(1, db.getGroupCount());
|
||||
|
||||
// Add card to group
|
||||
Group group = db.getGroup("group one");
|
||||
List<Group> groupList1 = new ArrayList<>();
|
||||
groupList1.add(group);
|
||||
db.setLoyaltyCardGroups(1, groupList1);
|
||||
|
||||
// Ensure the card has one group and the group has one card
|
||||
List<Group> cardGroups = db.getLoyaltyCardGroups((int) id);
|
||||
assertEquals(1, cardGroups.size());
|
||||
assertEquals("group one", cardGroups.get(0)._id);
|
||||
assertEquals(1, db.getGroupCardCount("group one"));
|
||||
|
||||
// Rename group
|
||||
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");
|
||||
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);
|
||||
|
||||
// And card is in "group one renamed"
|
||||
// Ensure the card has one group and the group has one card
|
||||
cardGroups = db.getLoyaltyCardGroups((int) id);
|
||||
assertEquals(1, cardGroups.size());
|
||||
assertEquals("group one renamed", cardGroups.get(0)._id);
|
||||
assertEquals(1, db.getGroupCardCount("group one renamed"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -377,7 +421,7 @@ public class DatabaseTest
|
||||
{
|
||||
// Create card
|
||||
assertEquals(0, db.getLoyaltyCardCount());
|
||||
long id = db.insertLoyaltyCard("store", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0);
|
||||
long id = db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), DEFAULT_HEADER_COLOR, 0);
|
||||
boolean result = (id != -1);
|
||||
assertTrue(result);
|
||||
assertEquals(1, db.getLoyaltyCardCount());
|
||||
@@ -437,6 +481,8 @@ public class DatabaseTest
|
||||
LoyaltyCard card = db.getLoyaltyCard(newCardId);
|
||||
assertEquals("store", card.store);
|
||||
assertEquals(null, card.expiry);
|
||||
assertEquals(new BigDecimal("0"), card.balance);
|
||||
assertEquals(null, card.balanceType);
|
||||
assertEquals("cardId", card.cardId);
|
||||
assertEquals(BarcodeFormat.UPC_A.toString(), card.barcodeType);
|
||||
assertEquals("", card.note);
|
||||
|
||||
@@ -3,10 +3,12 @@ package protect.card_locker;
|
||||
import android.app.Activity;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Color;
|
||||
import android.os.Environment;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -24,12 +26,18 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import protect.card_locker.importexport.MultiFormatExporter;
|
||||
import protect.card_locker.importexport.MultiFormatImporter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
@@ -72,7 +80,7 @@ public class ImportExportTest
|
||||
{
|
||||
String storeName = String.format("store, \"%4d", index);
|
||||
String note = String.format("note, \"%4d", index);
|
||||
long id = db.insertLoyaltyCard(storeName, note, null, BARCODE_DATA, BARCODE_TYPE, index, 0);
|
||||
long id = db.insertLoyaltyCard(storeName, note, null, new BigDecimal(String.valueOf(index)), null, BARCODE_DATA, BARCODE_TYPE, index, 0);
|
||||
boolean result = (id != -1);
|
||||
assertTrue(result);
|
||||
}
|
||||
@@ -88,7 +96,7 @@ public class ImportExportTest
|
||||
{
|
||||
String storeName = String.format("store, \"%4d", index);
|
||||
String note = String.format("note, \"%4d", index);
|
||||
long id = db.insertLoyaltyCard(storeName, note, null, BARCODE_DATA, BARCODE_TYPE, index, 1);
|
||||
long id = db.insertLoyaltyCard(storeName, note, null, new BigDecimal(String.valueOf(index)), null, BARCODE_DATA, BARCODE_TYPE, index, 1);
|
||||
boolean result = (id != -1);
|
||||
assertTrue(result);
|
||||
}
|
||||
@@ -97,33 +105,79 @@ public class ImportExportTest
|
||||
String storeName = String.format("store, \"%4d", index);
|
||||
String note = String.format("note, \"%4d", index);
|
||||
//if index is even
|
||||
long id = db.insertLoyaltyCard(storeName, note, null, BARCODE_DATA, BARCODE_TYPE, index, 0);
|
||||
long id = db.insertLoyaltyCard(storeName, note, null, new BigDecimal(String.valueOf(index)), null, BARCODE_DATA, BARCODE_TYPE, index, 0);
|
||||
boolean result = (id != -1);
|
||||
assertTrue(result);
|
||||
}
|
||||
assertEquals(cardsToAdd, db.getLoyaltyCardCount());
|
||||
}
|
||||
|
||||
private void addLoyaltyCardsWithExpiryNeverPastTodayFuture()
|
||||
@Test
|
||||
public void addLoyaltyCardsWithExpiryNeverPastTodayFuture()
|
||||
{
|
||||
long id = db.insertLoyaltyCard("No Expiry", "", null, BARCODE_DATA, BARCODE_TYPE, 0, 0);
|
||||
long id = db.insertLoyaltyCard("No Expiry", "", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, 0, 0);
|
||||
boolean result = (id != -1);
|
||||
assertTrue(result);
|
||||
|
||||
id = db.insertLoyaltyCard("Past", "", new Date((long) 1), BARCODE_DATA, BARCODE_TYPE, 0, 0);
|
||||
LoyaltyCard card = db.getLoyaltyCard((int) id);
|
||||
assertEquals("No Expiry", card.store);
|
||||
assertEquals("", card.note);
|
||||
assertEquals(null, card.expiry);
|
||||
assertEquals(new BigDecimal("0"), card.balance);
|
||||
assertEquals(null, card.balanceType);
|
||||
assertEquals(BARCODE_DATA, card.cardId);
|
||||
assertEquals(BARCODE_TYPE, card.barcodeType);
|
||||
assertEquals(Integer.valueOf(0), card.headerColor);
|
||||
assertEquals(0, card.starStatus);
|
||||
|
||||
id = db.insertLoyaltyCard("Past", "", new Date((long) 1), new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, 0, 0);
|
||||
result = (id != -1);
|
||||
assertTrue(result);
|
||||
|
||||
id = db.insertLoyaltyCard("Today", "", new Date(), BARCODE_DATA, BARCODE_TYPE, 0, 0);
|
||||
card = db.getLoyaltyCard((int) id);
|
||||
assertEquals("Past", card.store);
|
||||
assertEquals("", card.note);
|
||||
assertTrue(card.expiry.before(new Date()));
|
||||
assertEquals(new BigDecimal("0"), card.balance);
|
||||
assertEquals(null, card.balanceType);
|
||||
assertEquals(BARCODE_DATA, card.cardId);
|
||||
assertEquals(BARCODE_TYPE, card.barcodeType);
|
||||
assertEquals(Integer.valueOf(0), card.headerColor);
|
||||
assertEquals(0, card.starStatus);
|
||||
|
||||
id = db.insertLoyaltyCard("Today", "", new Date(), new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, 0, 0);
|
||||
result = (id != -1);
|
||||
assertTrue(result);
|
||||
|
||||
card = db.getLoyaltyCard((int) id);
|
||||
assertEquals("Today", card.store);
|
||||
assertEquals("", card.note);
|
||||
assertTrue(card.expiry.before(new Date(new Date().getTime()+86400)));
|
||||
assertTrue(card.expiry.after(new Date(new Date().getTime()-86400)));
|
||||
assertEquals(new BigDecimal("0"), card.balance);
|
||||
assertEquals(null, card.balanceType);
|
||||
assertEquals(BARCODE_DATA, card.cardId);
|
||||
assertEquals(BARCODE_TYPE, card.barcodeType);
|
||||
assertEquals(Integer.valueOf(0), card.headerColor);
|
||||
assertEquals(0, card.starStatus);
|
||||
|
||||
// This will break after 19 January 2038
|
||||
// If someone is still maintaining this code base by then: I love you
|
||||
id = db.insertLoyaltyCard("Future", "", new Date(2147483648L), BARCODE_DATA, BARCODE_TYPE, 0, 0);
|
||||
id = db.insertLoyaltyCard("Future", "", new Date(2147483648000L), new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, 0, 0);
|
||||
result = (id != -1);
|
||||
assertTrue(result);
|
||||
|
||||
card = db.getLoyaltyCard((int) id);
|
||||
assertEquals("Future", card.store);
|
||||
assertEquals("", card.note);
|
||||
assertTrue(card.expiry.after(new Date(new Date().getTime()+86400)));
|
||||
assertEquals(new BigDecimal("0"), card.balance);
|
||||
assertEquals(null, card.balanceType);
|
||||
assertEquals(BARCODE_DATA, card.cardId);
|
||||
assertEquals(BARCODE_TYPE, card.barcodeType);
|
||||
assertEquals(Integer.valueOf(0), card.headerColor);
|
||||
assertEquals(0, card.starStatus);
|
||||
|
||||
assertEquals(4, db.getLoyaltyCardCount());
|
||||
}
|
||||
|
||||
@@ -160,6 +214,8 @@ public class ImportExportTest
|
||||
|
||||
assertEquals(expectedStore, card.store);
|
||||
assertEquals(expectedNote, card.note);
|
||||
assertEquals(new BigDecimal(String.valueOf(index)), card.balance);
|
||||
assertEquals(null, card.balanceType);
|
||||
assertEquals(BARCODE_DATA, card.cardId);
|
||||
assertEquals(BARCODE_TYPE, card.barcodeType);
|
||||
assertEquals(Integer.valueOf(index), card.headerColor);
|
||||
@@ -190,6 +246,8 @@ public class ImportExportTest
|
||||
|
||||
assertEquals(expectedStore, card.store);
|
||||
assertEquals(expectedNote, card.note);
|
||||
assertEquals(new BigDecimal(String.valueOf(index)), card.balance);
|
||||
assertEquals(null, card.balanceType);
|
||||
assertEquals(BARCODE_DATA, card.cardId);
|
||||
assertEquals(BARCODE_TYPE, card.barcodeType);
|
||||
assertEquals(Integer.valueOf(index), card.headerColor);
|
||||
@@ -208,6 +266,8 @@ public class ImportExportTest
|
||||
|
||||
assertEquals(expectedStore, card.store);
|
||||
assertEquals(expectedNote, card.note);
|
||||
assertEquals(new BigDecimal(String.valueOf(index)), card.balance);
|
||||
assertEquals(null, card.balanceType);
|
||||
assertEquals(BARCODE_DATA, card.cardId);
|
||||
assertEquals(BARCODE_TYPE, card.barcodeType);
|
||||
assertEquals(Integer.valueOf(index), card.headerColor);
|
||||
@@ -261,34 +321,30 @@ public class ImportExportTest
|
||||
{
|
||||
final int NUM_CARDS = 10;
|
||||
|
||||
for(DataFormat format : DataFormat.values())
|
||||
{
|
||||
addLoyaltyCards(NUM_CARDS);
|
||||
addLoyaltyCards(NUM_CARDS);
|
||||
|
||||
ByteArrayOutputStream outData = new ByteArrayOutputStream();
|
||||
OutputStreamWriter outStream = new OutputStreamWriter(outData);
|
||||
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();
|
||||
// Export data to CSV format
|
||||
boolean result = MultiFormatExporter.exportData(db, outStream, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
outStream.close();
|
||||
|
||||
clearDatabase();
|
||||
clearDatabase();
|
||||
|
||||
ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray());
|
||||
InputStreamReader inStream = new InputStreamReader(inData);
|
||||
ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray());
|
||||
|
||||
// Import the CSV data
|
||||
result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
|
||||
assertTrue(result);
|
||||
// Import the CSV data
|
||||
result = MultiFormatImporter.importData(db, inData, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
|
||||
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
|
||||
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
|
||||
|
||||
checkLoyaltyCards();
|
||||
checkLoyaltyCards();
|
||||
|
||||
// Clear the database for the next format under test
|
||||
clearDatabase();
|
||||
}
|
||||
// Clear the database for the next format under test
|
||||
clearDatabase();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -296,34 +352,30 @@ public class ImportExportTest
|
||||
{
|
||||
final int NUM_CARDS = 9;
|
||||
|
||||
for(DataFormat format : DataFormat.values())
|
||||
{
|
||||
addLoyaltyCardsFiveStarred();
|
||||
addLoyaltyCardsFiveStarred();
|
||||
|
||||
ByteArrayOutputStream outData = new ByteArrayOutputStream();
|
||||
OutputStreamWriter outStream = new OutputStreamWriter(outData);
|
||||
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();
|
||||
// Export data to CSV format
|
||||
boolean result = MultiFormatExporter.exportData(db, outStream, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
outStream.close();
|
||||
|
||||
clearDatabase();
|
||||
clearDatabase();
|
||||
|
||||
ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray());
|
||||
InputStreamReader inStream = new InputStreamReader(inData);
|
||||
ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray());
|
||||
|
||||
// Import the CSV data
|
||||
result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
|
||||
assertTrue(result);
|
||||
// Import the CSV data
|
||||
result = MultiFormatImporter.importData(db, inData, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
|
||||
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
|
||||
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
|
||||
|
||||
checkLoyaltyCardsFiveStarred();
|
||||
checkLoyaltyCardsFiveStarred();
|
||||
|
||||
// Clear the database for the next format under test
|
||||
clearDatabase();
|
||||
}
|
||||
// Clear the database for the next format under test
|
||||
clearDatabase();
|
||||
}
|
||||
|
||||
private List<String> groupsToGroupNames(List<Group> groups)
|
||||
@@ -343,77 +395,73 @@ public class ImportExportTest
|
||||
final int NUM_CARDS = 10;
|
||||
final int NUM_GROUPS = 3;
|
||||
|
||||
for(DataFormat format : DataFormat.values())
|
||||
{
|
||||
addLoyaltyCards(NUM_CARDS);
|
||||
addGroups(NUM_GROUPS);
|
||||
addLoyaltyCards(NUM_CARDS);
|
||||
addGroups(NUM_GROUPS);
|
||||
|
||||
List<Group> emptyGroup = new ArrayList<>();
|
||||
List<Group> emptyGroup = new ArrayList<>();
|
||||
|
||||
List<Group> groupsForOne = new ArrayList<>();
|
||||
groupsForOne.add(db.getGroup("group, \" 1"));
|
||||
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> 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> 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> 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"));
|
||||
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);
|
||||
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);
|
||||
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();
|
||||
// Export data to CSV format
|
||||
boolean result = MultiFormatExporter.exportData(db, outStream, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
outStream.close();
|
||||
|
||||
clearDatabase();
|
||||
clearDatabase();
|
||||
|
||||
ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray());
|
||||
InputStreamReader inStream = new InputStreamReader(inData);
|
||||
ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray());
|
||||
|
||||
// Import the CSV data
|
||||
result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
|
||||
assertTrue(result);
|
||||
// Import the CSV data
|
||||
result = MultiFormatImporter.importData(db, inData, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
|
||||
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
|
||||
assertEquals(NUM_GROUPS, db.getGroupCount());
|
||||
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
|
||||
assertEquals(NUM_GROUPS, db.getGroupCount());
|
||||
|
||||
checkLoyaltyCards();
|
||||
checkGroups();
|
||||
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));
|
||||
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();
|
||||
}
|
||||
// Clear the database for the next format under test
|
||||
clearDatabase();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -421,32 +469,28 @@ public class ImportExportTest
|
||||
{
|
||||
final int NUM_CARDS = 10;
|
||||
|
||||
for(DataFormat format : DataFormat.values())
|
||||
{
|
||||
addLoyaltyCards(NUM_CARDS);
|
||||
addLoyaltyCards(NUM_CARDS);
|
||||
|
||||
ByteArrayOutputStream outData = new ByteArrayOutputStream();
|
||||
OutputStreamWriter outStream = new OutputStreamWriter(outData);
|
||||
ByteArrayOutputStream outData = new ByteArrayOutputStream();
|
||||
OutputStreamWriter outStream = new OutputStreamWriter(outData);
|
||||
|
||||
// Export into CSV data
|
||||
boolean result = MultiFormatExporter.exportData(db, outStream, format);
|
||||
assertTrue(result);
|
||||
outStream.close();
|
||||
// Export into CSV data
|
||||
boolean result = MultiFormatExporter.exportData(db, outStream, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
outStream.close();
|
||||
|
||||
ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray());
|
||||
InputStreamReader inStream = new InputStreamReader(inData);
|
||||
ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray());
|
||||
|
||||
// Import the CSV data on top of the existing database
|
||||
result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
|
||||
assertTrue(result);
|
||||
// Import the CSV data on top of the existing database
|
||||
result = MultiFormatImporter.importData(db, inData, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
|
||||
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
|
||||
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
|
||||
|
||||
checkLoyaltyCards();
|
||||
checkLoyaltyCards();
|
||||
|
||||
// Clear the database for the next format under test
|
||||
clearDatabase();
|
||||
}
|
||||
// Clear the database for the next format under test
|
||||
clearDatabase();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -462,7 +506,7 @@ public class ImportExportTest
|
||||
OutputStreamWriter outStream = new OutputStreamWriter(outData);
|
||||
|
||||
// Export data to CSV format
|
||||
boolean result = MultiFormatExporter.exportData(db, outStream, format);
|
||||
boolean result = MultiFormatExporter.exportData(db, outStream, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
|
||||
clearDatabase();
|
||||
@@ -474,10 +518,9 @@ public class ImportExportTest
|
||||
String corruptEntry = "ThisStringIsLikelyNotPartOfAnyFormat,\"\"a";
|
||||
|
||||
ByteArrayInputStream inData = new ByteArrayInputStream((outData.toString() + corruptEntry).getBytes());
|
||||
InputStreamReader inStream = new InputStreamReader(inData);
|
||||
|
||||
// Attempt to import the CSV data
|
||||
result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
|
||||
// Attempt to import the data
|
||||
result = MultiFormatImporter.importData(db, inData, format);
|
||||
assertEquals(false, result);
|
||||
|
||||
assertEquals(0, db.getLoyaltyCardCount());
|
||||
@@ -505,49 +548,46 @@ public class ImportExportTest
|
||||
final File sdcardDir = Environment.getExternalStorageDirectory();
|
||||
final File exportFile = new File(sdcardDir, "Catima.csv");
|
||||
|
||||
for(DataFormat format : DataFormat.values())
|
||||
{
|
||||
addLoyaltyCards(NUM_CARDS);
|
||||
addLoyaltyCards(NUM_CARDS);
|
||||
|
||||
TestTaskCompleteListener listener = new TestTaskCompleteListener();
|
||||
TestTaskCompleteListener listener = new TestTaskCompleteListener();
|
||||
|
||||
// Export to the file
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(exportFile);
|
||||
ImportExportTask task = new ImportExportTask(activity, format, fileOutputStream, listener);
|
||||
task.execute();
|
||||
// Export to the file
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(exportFile);
|
||||
ImportExportTask task = new ImportExportTask(activity, DataFormat.Catima, fileOutputStream, listener);
|
||||
task.execute();
|
||||
|
||||
// Actually run the task to completion
|
||||
Robolectric.flushBackgroundThreadScheduler();
|
||||
// Actually run the task to completion
|
||||
Robolectric.flushBackgroundThreadScheduler();
|
||||
|
||||
// Check that the listener was executed
|
||||
assertNotNull(listener.success);
|
||||
assertEquals(true, listener.success);
|
||||
// Check that the listener was executed
|
||||
assertNotNull(listener.success);
|
||||
assertEquals(true, listener.success);
|
||||
|
||||
clearDatabase();
|
||||
clearDatabase();
|
||||
|
||||
// Import everything back from the default location
|
||||
// Import everything back from the default location
|
||||
|
||||
listener = new TestTaskCompleteListener();
|
||||
listener = new TestTaskCompleteListener();
|
||||
|
||||
FileInputStream fileStream = new FileInputStream(exportFile);
|
||||
FileInputStream fileStream = new FileInputStream(exportFile);
|
||||
|
||||
task = new ImportExportTask(activity, format, fileStream, listener);
|
||||
task.execute();
|
||||
task = new ImportExportTask(activity, DataFormat.Catima, fileStream, listener);
|
||||
task.execute();
|
||||
|
||||
// Actually run the task to completion
|
||||
Robolectric.flushBackgroundThreadScheduler();
|
||||
// Actually run the task to completion
|
||||
Robolectric.flushBackgroundThreadScheduler();
|
||||
|
||||
// Check that the listener was executed
|
||||
assertNotNull(listener.success);
|
||||
assertEquals(true, listener.success);
|
||||
// Check that the listener was executed
|
||||
assertNotNull(listener.success);
|
||||
assertEquals(true, listener.success);
|
||||
|
||||
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
|
||||
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
|
||||
|
||||
checkLoyaltyCards();
|
||||
checkLoyaltyCards();
|
||||
|
||||
// Clear the database for the next format under test
|
||||
clearDatabase();
|
||||
}
|
||||
// Clear the database for the next format under test
|
||||
clearDatabase();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -564,10 +604,9 @@ public class ImportExportTest
|
||||
csvText += "1,store,note,12345,type,0";
|
||||
|
||||
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);
|
||||
boolean result = MultiFormatImporter.importData(db, inputStream, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
assertEquals(1, db.getLoyaltyCardCount());
|
||||
|
||||
@@ -576,6 +615,8 @@ public class ImportExportTest
|
||||
assertEquals("store", card.store);
|
||||
assertEquals("note", card.note);
|
||||
assertEquals(null, card.expiry);
|
||||
assertEquals(new BigDecimal("0"), card.balance);
|
||||
assertEquals(null, card.balanceType);
|
||||
assertEquals("12345", card.cardId);
|
||||
assertEquals("type", card.barcodeType);
|
||||
assertEquals(0, card.starStatus);
|
||||
@@ -600,10 +641,9 @@ public class ImportExportTest
|
||||
csvText += "1,store,note,12345,type,,,0";
|
||||
|
||||
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);
|
||||
boolean result = MultiFormatImporter.importData(db, inputStream, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
assertEquals(1, db.getLoyaltyCardCount());
|
||||
|
||||
@@ -612,6 +652,8 @@ public class ImportExportTest
|
||||
assertEquals("store", card.store);
|
||||
assertEquals("note", card.note);
|
||||
assertEquals(null, card.expiry);
|
||||
assertEquals(new BigDecimal("0"), card.balance);
|
||||
assertEquals(null, card.balanceType);
|
||||
assertEquals("12345", card.cardId);
|
||||
assertEquals("type", card.barcodeType);
|
||||
assertEquals(0, card.starStatus);
|
||||
@@ -636,10 +678,9 @@ public class ImportExportTest
|
||||
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);
|
||||
|
||||
// Import the CSV data
|
||||
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
|
||||
boolean result = MultiFormatImporter.importData(db, inputStream, DataFormat.Catima);
|
||||
assertEquals(false, result);
|
||||
assertEquals(0, db.getLoyaltyCardCount());
|
||||
|
||||
@@ -662,10 +703,9 @@ public class ImportExportTest
|
||||
csvText += "1,store,note,12345,,1,1,0";
|
||||
|
||||
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);
|
||||
boolean result = MultiFormatImporter.importData(db, inputStream, DataFormat.Catima);
|
||||
assertEquals(true, result);
|
||||
assertEquals(1, db.getLoyaltyCardCount());
|
||||
|
||||
@@ -674,6 +714,8 @@ public class ImportExportTest
|
||||
assertEquals("store", card.store);
|
||||
assertEquals("note", card.note);
|
||||
assertEquals(null, card.expiry);
|
||||
assertEquals(new BigDecimal("0"), card.balance);
|
||||
assertEquals(null, card.balanceType);
|
||||
assertEquals("12345", card.cardId);
|
||||
assertEquals("", card.barcodeType);
|
||||
assertEquals(0, card.starStatus);
|
||||
@@ -698,10 +740,9 @@ public class ImportExportTest
|
||||
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);
|
||||
boolean result = MultiFormatImporter.importData(db, inputStream, DataFormat.Catima);
|
||||
assertEquals(true, result);
|
||||
assertEquals(1, db.getLoyaltyCardCount());
|
||||
|
||||
@@ -710,6 +751,8 @@ public class ImportExportTest
|
||||
assertEquals("store", card.store);
|
||||
assertEquals("note", card.note);
|
||||
assertEquals(null, card.expiry);
|
||||
assertEquals(new BigDecimal("0"), card.balance);
|
||||
assertEquals(null, card.balanceType);
|
||||
assertEquals("12345", card.cardId);
|
||||
assertEquals("type", card.barcodeType);
|
||||
assertEquals(1, card.starStatus);
|
||||
@@ -734,10 +777,9 @@ public class ImportExportTest
|
||||
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);
|
||||
boolean result = MultiFormatImporter.importData(db, inputStream, DataFormat.Catima);
|
||||
assertEquals(true, result);
|
||||
assertEquals(1, db.getLoyaltyCardCount());
|
||||
|
||||
@@ -746,6 +788,8 @@ public class ImportExportTest
|
||||
assertEquals("store", card.store);
|
||||
assertEquals("note", card.note);
|
||||
assertEquals(null, card.expiry);
|
||||
assertEquals(new BigDecimal("0"), card.balance);
|
||||
assertEquals(null, card.balanceType);
|
||||
assertEquals("12345", card.cardId);
|
||||
assertEquals("type", card.barcodeType);
|
||||
assertEquals(0, card.starStatus);
|
||||
@@ -770,10 +814,9 @@ public class ImportExportTest
|
||||
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);
|
||||
boolean result = MultiFormatImporter.importData(db, inputStream, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
assertEquals(1, db.getLoyaltyCardCount());
|
||||
|
||||
@@ -790,10 +833,9 @@ public class ImportExportTest
|
||||
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);
|
||||
result = MultiFormatImporter.importData(db, inputStream, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
assertEquals(1, db.getLoyaltyCardCount());
|
||||
|
||||
@@ -802,6 +844,8 @@ public class ImportExportTest
|
||||
assertEquals("store", card.store);
|
||||
assertEquals("note", card.note);
|
||||
assertEquals(null, card.expiry);
|
||||
assertEquals(new BigDecimal("0"), card.balance);
|
||||
assertEquals(null, card.balanceType);
|
||||
assertEquals("12345", card.cardId);
|
||||
assertEquals("type", card.barcodeType);
|
||||
assertEquals(0, card.starStatus);
|
||||
@@ -810,51 +854,62 @@ public class ImportExportTest
|
||||
clearDatabase();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void importVoucherVault() throws IOException, FormatException, JSONException, ParseException {
|
||||
String jsonText = "[\n" +
|
||||
" {\n" +
|
||||
" \"uuid\": \"ae1ae525-3f27-481e-853a-8c30b7fa12d8\",\n" +
|
||||
" \"description\": \"Clothes Store\",\n" +
|
||||
" \"code\": \"123456\",\n" +
|
||||
" \"codeType\": \"CODE128\",\n" +
|
||||
" \"expires\": null,\n" +
|
||||
" \"removeOnceExpired\": true,\n" +
|
||||
" \"balance\": null,\n" +
|
||||
" \"color\": \"GREY\"\n" +
|
||||
" },\n" +
|
||||
" {\n" +
|
||||
" \"uuid\": \"29a5d3b3-eace-4311-a15c-4c7e6a010531\",\n" +
|
||||
" \"description\": \"Department Store\",\n" +
|
||||
" \"code\": \"26846363\",\n" +
|
||||
" \"codeType\": \"CODE39\",\n" +
|
||||
" \"expires\": \"2021-03-26T00:00:00.000\",\n" +
|
||||
" \"removeOnceExpired\": true,\n" +
|
||||
" \"balance\": 3.5,\n" +
|
||||
" \"color\": \"PURPLE\"\n" +
|
||||
" }\n" +
|
||||
"]";
|
||||
|
||||
private void checkLoyaltyCardsExpiry()
|
||||
{
|
||||
Cursor cursor = db.getLoyaltyCardCursor();
|
||||
cursor.moveToNext();
|
||||
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cursor);
|
||||
assertEquals("Never", card.store);
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(jsonText.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// Import the Voucher Vault data
|
||||
boolean result = MultiFormatImporter.importData(db, inputStream, DataFormat.VoucherVault);
|
||||
assertTrue(result);
|
||||
assertEquals(2, db.getLoyaltyCardCount());
|
||||
|
||||
LoyaltyCard card = db.getLoyaltyCard(1);
|
||||
|
||||
assertEquals("Clothes Store", card.store);
|
||||
assertEquals("", card.note);
|
||||
assertEquals(null, card.expiry);
|
||||
assertEquals(BARCODE_DATA, card.cardId);
|
||||
assertEquals(BARCODE_TYPE, card.barcodeType);
|
||||
assertEquals(Integer.valueOf(0), card.headerColor);
|
||||
assertEquals(new BigDecimal("0"), card.balance);
|
||||
assertEquals(Currency.getInstance("USD"), card.balanceType);
|
||||
assertEquals("123456", card.cardId);
|
||||
assertEquals(BarcodeFormat.CODE_128.name(), card.barcodeType);
|
||||
assertEquals(0, card.starStatus);
|
||||
assertEquals(Color.GRAY, (long) card.headerColor);
|
||||
|
||||
cursor.moveToNext();
|
||||
card = LoyaltyCard.toLoyaltyCard(cursor);
|
||||
assertEquals("Past", card.store);
|
||||
card = db.getLoyaltyCard(2);
|
||||
|
||||
assertEquals("Department Store", card.store);
|
||||
assertEquals("", card.note);
|
||||
assertTrue(card.expiry.before(new Date()));
|
||||
assertEquals(BARCODE_DATA, card.cardId);
|
||||
assertEquals(BARCODE_TYPE, card.barcodeType);
|
||||
assertEquals(Integer.valueOf(0), card.headerColor);
|
||||
assertEquals(new Date(1616716800000L), card.expiry);
|
||||
assertEquals(new BigDecimal("3.5"), card.balance);
|
||||
assertEquals(Currency.getInstance("USD"), card.balanceType);
|
||||
assertEquals("26846363", card.cardId);
|
||||
assertEquals(BarcodeFormat.CODE_39.name(), card.barcodeType);
|
||||
assertEquals(0, card.starStatus);
|
||||
assertEquals(Color.rgb(128, 0, 128), (long) card.headerColor);
|
||||
|
||||
cursor.moveToNext();
|
||||
card = LoyaltyCard.toLoyaltyCard(cursor);
|
||||
assertEquals("Today", card.store);
|
||||
assertEquals("", card.note);
|
||||
assertTrue(card.expiry.before(new Date(new Date().getTime()+86400)));
|
||||
assertTrue(card.expiry.after(new Date(new Date().getTime()-86400)));
|
||||
assertEquals(BARCODE_DATA, card.cardId);
|
||||
assertEquals(BARCODE_TYPE, card.barcodeType);
|
||||
assertEquals(Integer.valueOf(0), card.headerColor);
|
||||
assertEquals(0, card.starStatus);
|
||||
|
||||
cursor.moveToNext();
|
||||
card = LoyaltyCard.toLoyaltyCard(cursor);
|
||||
assertEquals("Future", card.store);
|
||||
assertEquals("", card.note);
|
||||
assertTrue(card.expiry.after(new Date(new Date().getTime()+86400)));
|
||||
assertEquals(BARCODE_DATA, card.cardId);
|
||||
assertEquals(BARCODE_TYPE, card.barcodeType);
|
||||
assertEquals(Integer.valueOf(0), card.headerColor);
|
||||
assertEquals(0, card.starStatus);
|
||||
|
||||
cursor.close();
|
||||
clearDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import java.io.InvalidObjectException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -38,7 +40,7 @@ public class ImportURITest {
|
||||
// Generate card
|
||||
Date date = new Date();
|
||||
|
||||
db.insertLoyaltyCard("store", "note", date, BarcodeFormat.UPC_A.toString(), LoyaltyCardDbIds.BARCODE_TYPE, Color.BLACK, 1);
|
||||
db.insertLoyaltyCard("store", "note", date, new BigDecimal("100"), null, BarcodeFormat.UPC_A.toString(), LoyaltyCardDbIds.BARCODE_TYPE, Color.BLACK, 1);
|
||||
|
||||
// Get card
|
||||
LoyaltyCard card = db.getLoyaltyCard(1);
|
||||
@@ -55,6 +57,8 @@ public class ImportURITest {
|
||||
assertEquals(card.headerColor, parsedCard.headerColor);
|
||||
assertEquals(card.note, parsedCard.note);
|
||||
assertEquals(card.expiry, parsedCard.expiry);
|
||||
assertEquals(card.balance, parsedCard.balance);
|
||||
assertEquals(card.balanceType, parsedCard.balanceType);
|
||||
assertEquals(card.store, parsedCard.store);
|
||||
// No export of starStatus for single cards foreseen therefore 0 will be imported
|
||||
assertEquals(0, parsedCard.starStatus);
|
||||
@@ -64,7 +68,7 @@ public class ImportURITest {
|
||||
public void ensureNoCrashOnMissingHeaderFields() throws InvalidObjectException
|
||||
{
|
||||
// Generate card
|
||||
db.insertLoyaltyCard("store", "note", null, BarcodeFormat.UPC_A.toString(), LoyaltyCardDbIds.BARCODE_TYPE, null, 0);
|
||||
db.insertLoyaltyCard("store", "note", null, new BigDecimal("10.00"), Currency.getInstance("EUR"), BarcodeFormat.UPC_A.toString(), LoyaltyCardDbIds.BARCODE_TYPE, null, 0);
|
||||
|
||||
// Get card
|
||||
LoyaltyCard card = db.getLoyaltyCard(1);
|
||||
@@ -80,6 +84,8 @@ public class ImportURITest {
|
||||
assertEquals(card.cardId, parsedCard.cardId);
|
||||
assertEquals(card.note, parsedCard.note);
|
||||
assertEquals(card.expiry, parsedCard.expiry);
|
||||
assertEquals(card.balance, parsedCard.balance);
|
||||
assertEquals(card.balanceType, parsedCard.balanceType);
|
||||
assertEquals(card.store, parsedCard.store);
|
||||
assertNull(parsedCard.headerColor);
|
||||
assertNull(parsedCard.headerTextColor);
|
||||
@@ -125,5 +131,8 @@ public class ImportURITest {
|
||||
assertEquals("store", parsedCard.store);
|
||||
assertEquals(Integer.valueOf(-416706), parsedCard.headerColor);
|
||||
assertEquals(0, parsedCard.starStatus);
|
||||
assertEquals(null, parsedCard.expiry);
|
||||
assertEquals(new BigDecimal("0"), parsedCard.balance);
|
||||
assertEquals(null, parsedCard.balanceType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,9 @@ import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DateFormat;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -60,11 +62,12 @@ public class LoyaltyCardCursorAdapterTest
|
||||
return view;
|
||||
}
|
||||
|
||||
private void checkView(final View view, final String store, final String note, final String expiry, boolean checkFontSizes)
|
||||
private void checkView(final View view, final String store, final String note, final String expiry, final String balance, boolean checkFontSizes)
|
||||
{
|
||||
final TextView storeField = view.findViewById(R.id.store);
|
||||
final TextView noteField = view.findViewById(R.id.note);
|
||||
final TextView expiryField = view.findViewById(R.id.expiry);
|
||||
final TextView balanceField = view.findViewById(R.id.balance);
|
||||
|
||||
if(checkFontSizes)
|
||||
{
|
||||
@@ -77,7 +80,7 @@ public class LoyaltyCardCursorAdapterTest
|
||||
}
|
||||
|
||||
assertEquals(store, storeField.getText().toString());
|
||||
if(note.isEmpty() == false)
|
||||
if(!note.isEmpty())
|
||||
{
|
||||
assertEquals(View.VISIBLE, noteField.getVisibility());
|
||||
assertEquals(note, noteField.getText().toString());
|
||||
@@ -87,7 +90,7 @@ public class LoyaltyCardCursorAdapterTest
|
||||
assertEquals(View.GONE, noteField.getVisibility());
|
||||
}
|
||||
|
||||
if(expiry.isEmpty() == false)
|
||||
if(!expiry.isEmpty())
|
||||
{
|
||||
assertEquals(View.VISIBLE, expiryField.getVisibility());
|
||||
assertEquals(expiry, expiryField.getText().toString());
|
||||
@@ -96,13 +99,23 @@ public class LoyaltyCardCursorAdapterTest
|
||||
{
|
||||
assertEquals(View.GONE, expiryField.getVisibility());
|
||||
}
|
||||
|
||||
if(!balance.isEmpty())
|
||||
{
|
||||
assertEquals(View.VISIBLE, balanceField.getVisibility());
|
||||
assertEquals(balance, balanceField.getText().toString());
|
||||
}
|
||||
else
|
||||
{
|
||||
assertEquals(View.GONE, balanceField.getVisibility());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void TestCursorAdapterEmptyNote()
|
||||
{
|
||||
db.insertLoyaltyCard("store", "", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("store", "", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
|
||||
LoyaltyCard card = db.getLoyaltyCard(1);
|
||||
|
||||
Cursor cursor = db.getLoyaltyCardCursor();
|
||||
@@ -110,7 +123,7 @@ public class LoyaltyCardCursorAdapterTest
|
||||
|
||||
View view = createView(cursor);
|
||||
|
||||
checkView(view, card.store, card.note, "", false);
|
||||
checkView(view, card.store, card.note, "", "",false);
|
||||
|
||||
cursor.close();
|
||||
}
|
||||
@@ -118,7 +131,7 @@ public class LoyaltyCardCursorAdapterTest
|
||||
@Test
|
||||
public void TestCursorAdapterWithNote()
|
||||
{
|
||||
db.insertLoyaltyCard("store", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
|
||||
LoyaltyCard card = db.getLoyaltyCard(1);
|
||||
|
||||
Cursor cursor = db.getLoyaltyCardCursor();
|
||||
@@ -126,7 +139,7 @@ public class LoyaltyCardCursorAdapterTest
|
||||
|
||||
View view = createView(cursor);
|
||||
|
||||
checkView(view, card.store, card.note, "", false);
|
||||
checkView(view, card.store, card.note, "", "",false);
|
||||
|
||||
cursor.close();
|
||||
}
|
||||
@@ -138,7 +151,7 @@ public class LoyaltyCardCursorAdapterTest
|
||||
Date expiryDate = new Date();
|
||||
String dateString = context.getString(R.string.expiryStateSentence, DateFormat.getDateInstance(DateFormat.LONG).format(expiryDate));
|
||||
|
||||
db.insertLoyaltyCard("store", "note", expiryDate, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("store", "note", expiryDate, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
|
||||
LoyaltyCard card = db.getLoyaltyCard(1);
|
||||
|
||||
Cursor cursor = db.getLoyaltyCardCursor();
|
||||
@@ -147,11 +160,11 @@ public class LoyaltyCardCursorAdapterTest
|
||||
setFontSizes(1, 2);
|
||||
View view = createView(cursor);
|
||||
|
||||
checkView(view, card.store, card.note, dateString, true);
|
||||
checkView(view, card.store, card.note, dateString, "", true);
|
||||
|
||||
setFontSizes(30, 31);
|
||||
view = createView(cursor);
|
||||
checkView(view, card.store, card.note, dateString, true);
|
||||
checkView(view, card.store, card.note, dateString, "",true);
|
||||
|
||||
cursor.close();
|
||||
}
|
||||
@@ -159,9 +172,9 @@ public class LoyaltyCardCursorAdapterTest
|
||||
@Test
|
||||
public void TestCursorAdapterStarring()
|
||||
{
|
||||
db.insertLoyaltyCard("storeA", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("storeB", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1);
|
||||
db.insertLoyaltyCard("storeC", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1);
|
||||
db.insertLoyaltyCard("storeA", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("storeB", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1);
|
||||
db.insertLoyaltyCard("storeC", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1);
|
||||
|
||||
Cursor cursor = db.getLoyaltyCardCursor();
|
||||
cursor.moveToFirst();
|
||||
@@ -181,4 +194,68 @@ public class LoyaltyCardCursorAdapterTest
|
||||
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestCursorAdapter0Points()
|
||||
{
|
||||
db.insertLoyaltyCard("store", "", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
|
||||
LoyaltyCard card = db.getLoyaltyCard(1);
|
||||
|
||||
Cursor cursor = db.getLoyaltyCardCursor();
|
||||
cursor.moveToFirst();
|
||||
|
||||
View view = createView(cursor);
|
||||
|
||||
checkView(view, card.store, card.note, "", "",false);
|
||||
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestCursorAdapter0EUR()
|
||||
{
|
||||
db.insertLoyaltyCard("store", "", null, new BigDecimal("0"), Currency.getInstance("EUR"), "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
|
||||
LoyaltyCard card = db.getLoyaltyCard(1);
|
||||
|
||||
Cursor cursor = db.getLoyaltyCardCursor();
|
||||
cursor.moveToFirst();
|
||||
|
||||
View view = createView(cursor);
|
||||
|
||||
checkView(view, card.store, card.note, "", "",false);
|
||||
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestCursorAdapter100Points()
|
||||
{
|
||||
db.insertLoyaltyCard("store", "note", null, new BigDecimal("100"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
|
||||
LoyaltyCard card = db.getLoyaltyCard(1);
|
||||
|
||||
Cursor cursor = db.getLoyaltyCardCursor();
|
||||
cursor.moveToFirst();
|
||||
|
||||
View view = createView(cursor);
|
||||
|
||||
checkView(view, card.store, card.note, "", "Balance: 100 points",false);
|
||||
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestCursorAdapter10USD()
|
||||
{
|
||||
db.insertLoyaltyCard("store", "note", null, new BigDecimal("10.00"), Currency.getInstance("USD"), "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
|
||||
LoyaltyCard card = db.getLoyaltyCard(1);
|
||||
|
||||
Cursor cursor = db.getLoyaltyCardCursor();
|
||||
cursor.moveToFirst();
|
||||
|
||||
View view = createView(cursor);
|
||||
|
||||
checkView(view, card.store, card.note, "", "Balance: $10.00",false);
|
||||
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,17 +26,22 @@ import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
import androidx.core.widget.TextViewCompat;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.google.android.material.textfield.MaterialAutoCompleteTextView;
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.client.android.Intents;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DateFormat;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -105,6 +110,8 @@ public class LoyaltyCardViewActivityTest
|
||||
private void saveLoyaltyCardWithArguments(final Activity activity,
|
||||
final String store, final String note,
|
||||
final Date expiry,
|
||||
final BigDecimal balance,
|
||||
final Currency balanceType,
|
||||
final String cardId,
|
||||
final String barcodeType,
|
||||
boolean creatingNewCard)
|
||||
@@ -122,12 +129,20 @@ public class LoyaltyCardViewActivityTest
|
||||
final EditText storeField = activity.findViewById(R.id.storeNameEdit);
|
||||
final EditText noteField = activity.findViewById(R.id.noteEdit);
|
||||
final TextInputLayout expiryView = activity.findViewById(R.id.expiryView);
|
||||
final EditText balanceView = activity.findViewById(R.id.balanceField);
|
||||
final EditText balanceCurrencyField = activity.findViewById(R.id.balanceCurrencyField);
|
||||
final TextView cardIdField = activity.findViewById(R.id.cardIdView);
|
||||
final TextView barcodeTypeField = activity.findViewById(R.id.barcodeTypeField);
|
||||
|
||||
storeField.setText(store);
|
||||
noteField.setText(note);
|
||||
expiryView.setTag(expiry);
|
||||
if (balance != null) {
|
||||
balanceView.setText(balance.toPlainString());
|
||||
}
|
||||
if (balanceType != null) {
|
||||
balanceCurrencyField.setText(balanceType.getSymbol());
|
||||
}
|
||||
cardIdField.setText(cardId);
|
||||
barcodeTypeField.setText(barcodeType);
|
||||
|
||||
@@ -140,6 +155,13 @@ public class LoyaltyCardViewActivityTest
|
||||
LoyaltyCard card = db.getLoyaltyCard(1);
|
||||
assertEquals(store, card.store);
|
||||
assertEquals(note, card.note);
|
||||
assertEquals(expiry, card.expiry);
|
||||
if (balance != null) {
|
||||
assertEquals(balance, card.balance);
|
||||
} else {
|
||||
assertEquals(new BigDecimal("0"), card.balance);
|
||||
}
|
||||
assertEquals(balanceType, card.balanceType);
|
||||
assertEquals(cardId, card.cardId);
|
||||
|
||||
// The special "No barcode" string shouldn't actually be written to the loyalty card
|
||||
@@ -250,7 +272,9 @@ public class LoyaltyCardViewActivityTest
|
||||
}
|
||||
|
||||
private void checkAllFields(final Activity activity, ViewMode mode,
|
||||
final String store, final String note, final String expiryString, final String cardId, final String barcodeType)
|
||||
final String store, final String note, final String expiryString,
|
||||
final String balanceString, final String balanceTypeString,
|
||||
final String cardId, final String barcodeType)
|
||||
{
|
||||
if(mode == ViewMode.VIEW_CARD)
|
||||
{
|
||||
@@ -263,6 +287,8 @@ public class LoyaltyCardViewActivityTest
|
||||
checkFieldProperties(activity, R.id.storeNameEdit, editVisibility, store);
|
||||
checkFieldProperties(activity, R.id.noteEdit, editVisibility, note);
|
||||
checkFieldProperties(activity, R.id.expiryView, editVisibility, expiryString);
|
||||
checkFieldProperties(activity, R.id.balanceField, editVisibility, balanceString);
|
||||
checkFieldProperties(activity, R.id.balanceCurrencyField, editVisibility, balanceTypeString);
|
||||
checkFieldProperties(activity, R.id.cardIdView, View.VISIBLE, cardId);
|
||||
checkFieldProperties(activity, R.id.barcodeTypeField, View.VISIBLE, barcodeType);
|
||||
checkFieldProperties(activity, R.id.barcode, View.VISIBLE, null);
|
||||
@@ -280,7 +306,7 @@ public class LoyaltyCardViewActivityTest
|
||||
Activity activity = (Activity)activityController.get();
|
||||
final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never) , "", "");
|
||||
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never) , "0", context.getString(R.string.points), "", "");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -298,7 +324,6 @@ public class LoyaltyCardViewActivityTest
|
||||
|
||||
final EditText storeField = activity.findViewById(R.id.storeNameEdit);
|
||||
final EditText noteField = activity.findViewById(R.id.noteEdit);
|
||||
final TextView cardIdField = activity.findViewById(R.id.cardIdView);
|
||||
|
||||
activity.findViewById(R.id.fabSave).performClick();
|
||||
assertEquals(0, db.getLoyaltyCardCount());
|
||||
@@ -342,15 +367,17 @@ public class LoyaltyCardViewActivityTest
|
||||
Activity activity = (Activity)activityController.get();
|
||||
final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "", "");
|
||||
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "0", context.getString(R.string.points), "", "");
|
||||
|
||||
// Complete barcode capture successfully
|
||||
captureBarcodeWithResult(activity, true);
|
||||
|
||||
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), BARCODE_DATA, BARCODE_TYPE);
|
||||
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "0", context.getString(R.string.points), BARCODE_DATA, BARCODE_TYPE);
|
||||
|
||||
shadowOf(getMainLooper()).idle();
|
||||
|
||||
// Save and check the loyalty card
|
||||
saveLoyaltyCardWithArguments(activity, "store", "note", null, BARCODE_DATA, BARCODE_TYPE, true);
|
||||
saveLoyaltyCardWithArguments(activity, "store", "note", null, null, null, BARCODE_DATA, BARCODE_TYPE, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -364,12 +391,12 @@ public class LoyaltyCardViewActivityTest
|
||||
Activity activity = (Activity)activityController.get();
|
||||
final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "", "");
|
||||
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "0", context.getString(R.string.points), "", "");
|
||||
|
||||
// Complete barcode capture in failure
|
||||
captureBarcodeWithResult(activity, false);
|
||||
|
||||
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "", "");
|
||||
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "0", context.getString(R.string.points), "", "");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -383,12 +410,12 @@ public class LoyaltyCardViewActivityTest
|
||||
LoyaltyCardEditActivity activity = (LoyaltyCardEditActivity) activityController.get();
|
||||
final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "", "");
|
||||
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "0", context.getString(R.string.points), "", "");
|
||||
|
||||
// Complete barcode capture successfully
|
||||
captureBarcodeWithResult(activity, true);
|
||||
|
||||
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), BARCODE_DATA, BARCODE_TYPE);
|
||||
checkAllFields(activity, ViewMode.ADD_CARD, "", "", context.getString(R.string.never), "0", context.getString(R.string.points), BARCODE_DATA, BARCODE_TYPE);
|
||||
|
||||
// Cancel the loyalty card creation
|
||||
assertEquals(false, activity.isFinishing());
|
||||
@@ -435,15 +462,16 @@ public class LoyaltyCardViewActivityTest
|
||||
{
|
||||
ActivityController activityController = createActivityWithLoyaltyCard(true);
|
||||
Activity activity = (Activity)activityController.get();
|
||||
final Context context = ApplicationProvider.getApplicationContext();
|
||||
DBHelper db = new DBHelper(activity);
|
||||
|
||||
db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
|
||||
|
||||
activityController.start();
|
||||
activityController.visible();
|
||||
activityController.resume();
|
||||
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, BARCODE_DATA, BARCODE_TYPE);
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, "0", context.getString(R.string.points), BARCODE_DATA, BARCODE_TYPE);
|
||||
|
||||
db.close();
|
||||
}
|
||||
@@ -453,15 +481,16 @@ public class LoyaltyCardViewActivityTest
|
||||
{
|
||||
ActivityController activityController = createActivityWithLoyaltyCard(false);
|
||||
Activity activity = (Activity)activityController.get();
|
||||
final Context context = ApplicationProvider.getApplicationContext();
|
||||
DBHelper db = new DBHelper(activity);
|
||||
|
||||
db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
|
||||
|
||||
activityController.start();
|
||||
activityController.visible();
|
||||
activityController.resume();
|
||||
|
||||
checkAllFields(activity, ViewMode.VIEW_CARD, "store", "note", null, BARCODE_DATA, BARCODE_TYPE);
|
||||
checkAllFields(activity, ViewMode.VIEW_CARD, "store", "note", null, "0", context.getString(R.string.points), BARCODE_DATA, BARCODE_TYPE);
|
||||
|
||||
db.close();
|
||||
}
|
||||
@@ -471,20 +500,21 @@ public class LoyaltyCardViewActivityTest
|
||||
{
|
||||
ActivityController activityController = createActivityWithLoyaltyCard(true);
|
||||
Activity activity = (Activity)activityController.get();
|
||||
final Context context = ApplicationProvider.getApplicationContext();
|
||||
DBHelper db = new DBHelper(activity);
|
||||
|
||||
db.insertLoyaltyCard("store", "note", null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0);
|
||||
|
||||
activityController.start();
|
||||
activityController.visible();
|
||||
activityController.resume();
|
||||
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, "0", context.getString(R.string.points), EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
|
||||
|
||||
// Complete barcode capture successfully
|
||||
captureBarcodeWithResult(activity, true);
|
||||
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, BARCODE_DATA, BARCODE_TYPE);
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, "0", context.getString(R.string.points), BARCODE_DATA, BARCODE_TYPE);
|
||||
|
||||
db.close();
|
||||
}
|
||||
@@ -494,20 +524,21 @@ public class LoyaltyCardViewActivityTest
|
||||
{
|
||||
ActivityController activityController = createActivityWithLoyaltyCard(true);
|
||||
LoyaltyCardEditActivity activity = (LoyaltyCardEditActivity) activityController.get();
|
||||
final Context context = ApplicationProvider.getApplicationContext();
|
||||
DBHelper db = new DBHelper(activity);
|
||||
|
||||
db.insertLoyaltyCard("store", "note", null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0);
|
||||
|
||||
activityController.start();
|
||||
activityController.visible();
|
||||
activityController.resume();
|
||||
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, "0", context.getString(R.string.points), EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
|
||||
|
||||
// Complete barcode capture successfully
|
||||
captureBarcodeWithResult(activity, true);
|
||||
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, BARCODE_DATA, BARCODE_TYPE);
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", null, "0", context.getString(R.string.points), BARCODE_DATA, BARCODE_TYPE);
|
||||
|
||||
// Cancel the loyalty card creation
|
||||
assertEquals(false, activity.isFinishing());
|
||||
@@ -534,13 +565,13 @@ public class LoyaltyCardViewActivityTest
|
||||
final Context context = ApplicationProvider.getApplicationContext();
|
||||
DBHelper db = new DBHelper(activity);
|
||||
|
||||
db.insertLoyaltyCard("store", "note", null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0);
|
||||
|
||||
activityController.start();
|
||||
activityController.visible();
|
||||
activityController.resume();
|
||||
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), "0", context.getString(R.string.points), EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
|
||||
|
||||
// Set date to today
|
||||
MaterialAutoCompleteTextView expiryField = activity.findViewById(R.id.expiryField);
|
||||
@@ -554,7 +585,7 @@ public class LoyaltyCardViewActivityTest
|
||||
|
||||
shadowOf(getMainLooper()).idle();
|
||||
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", DateFormat.getDateInstance(DateFormat.LONG).format(new Date()), EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", DateFormat.getDateInstance(DateFormat.LONG).format(new Date()), "0", context.getString(R.string.points), EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
|
||||
|
||||
db.close();
|
||||
}
|
||||
@@ -567,19 +598,111 @@ public class LoyaltyCardViewActivityTest
|
||||
final Context context = ApplicationProvider.getApplicationContext();
|
||||
DBHelper db = new DBHelper(activity);
|
||||
|
||||
db.insertLoyaltyCard("store", "note", new Date(), EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("store", "note", new Date(), new BigDecimal("0"), null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0);
|
||||
|
||||
activityController.start();
|
||||
activityController.visible();
|
||||
activityController.resume();
|
||||
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", DateFormat.getDateInstance(DateFormat.LONG).format(new Date()), EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", DateFormat.getDateInstance(DateFormat.LONG).format(new Date()), "0", context.getString(R.string.points), EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
|
||||
|
||||
// Set date to never
|
||||
MaterialAutoCompleteTextView expiryField = activity.findViewById(R.id.expiryField);
|
||||
expiryField.setText(expiryField.getAdapter().getItem(0).toString(), false);
|
||||
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), "0", context.getString(R.string.points), EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
|
||||
|
||||
db.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startWithLoyaltyCardNoBalanceSetBalance() throws IOException
|
||||
{
|
||||
ActivityController activityController = createActivityWithLoyaltyCard(true);
|
||||
Activity activity = (Activity)activityController.get();
|
||||
final Context context = ApplicationProvider.getApplicationContext();
|
||||
DBHelper db = new DBHelper(activity);
|
||||
|
||||
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0);
|
||||
|
||||
activityController.start();
|
||||
activityController.visible();
|
||||
activityController.resume();
|
||||
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), "0", context.getString(R.string.points), EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
|
||||
|
||||
// Set balance to 10 points
|
||||
EditText balanceField = activity.findViewById(R.id.balanceField);
|
||||
balanceField.setText("10");
|
||||
|
||||
shadowOf(getMainLooper()).idle();
|
||||
|
||||
// Change points to EUR
|
||||
MaterialAutoCompleteTextView balanceTypeField = activity.findViewById(R.id.balanceCurrencyField);
|
||||
balanceTypeField.setText("€", false);
|
||||
|
||||
shadowOf(getMainLooper()).idle();
|
||||
|
||||
// Ensure the balance is reformatted for EUR when focus is cleared
|
||||
shadowOf(getMainLooper()).idle();
|
||||
balanceField.setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
||||
@Override
|
||||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
assertEquals("10.00", balanceField.getText().toString());
|
||||
|
||||
shadowOf(getMainLooper()).idle();
|
||||
|
||||
DatePickerDialog datePickerDialog = (DatePickerDialog) (ShadowDialog.getLatestDialog());
|
||||
assertNotNull(datePickerDialog);
|
||||
datePickerDialog.getButton(DatePickerDialog.BUTTON_POSITIVE).performClick();
|
||||
|
||||
shadowOf(getMainLooper()).idle();
|
||||
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", DateFormat.getDateInstance(DateFormat.LONG).format(new Date()), "10.00", "€", EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
|
||||
|
||||
db.close();
|
||||
}
|
||||
});
|
||||
balanceField.clearFocus();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startWithLoyaltyCardBalanceSetNoBalance() throws IOException
|
||||
{
|
||||
ActivityController activityController = createActivityWithLoyaltyCard(true);
|
||||
Activity activity = (Activity)activityController.get();
|
||||
final Context context = ApplicationProvider.getApplicationContext();
|
||||
DBHelper db = new DBHelper(activity);
|
||||
|
||||
db.insertLoyaltyCard("store", "note", null, new BigDecimal("10.00"), Currency.getInstance("USD"), EAN_BARCODE_DATA, EAN_BARCODE_TYPE, Color.BLACK, 0);
|
||||
|
||||
activityController.start();
|
||||
activityController.visible();
|
||||
activityController.resume();
|
||||
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), "10.00", "$", EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
|
||||
|
||||
shadowOf(getMainLooper()).idle();
|
||||
|
||||
// Change EUR to WON
|
||||
MaterialAutoCompleteTextView balanceTypeField = activity.findViewById(R.id.balanceCurrencyField);
|
||||
balanceTypeField.setText("₩", false);
|
||||
|
||||
shadowOf(getMainLooper()).idle();
|
||||
|
||||
// Ensure the balance is reformatted for WON when focus is cleared
|
||||
EditText balanceField = activity.findViewById(R.id.balanceField);
|
||||
balanceField.clearFocus();
|
||||
assertEquals("10", balanceField.getText().toString());
|
||||
|
||||
shadowOf(getMainLooper()).idle();
|
||||
|
||||
// Set the balance to 0
|
||||
balanceField.setText("0");
|
||||
|
||||
shadowOf(getMainLooper()).idle();
|
||||
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), "0", "₩", EAN_BARCODE_DATA, EAN_BARCODE_TYPE);
|
||||
|
||||
db.close();
|
||||
}
|
||||
@@ -591,12 +714,14 @@ public class LoyaltyCardViewActivityTest
|
||||
Activity activity = (Activity)activityController.get();
|
||||
DBHelper db = new DBHelper(activity);
|
||||
|
||||
db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
|
||||
|
||||
activityController.start();
|
||||
activityController.visible();
|
||||
activityController.resume();
|
||||
|
||||
shadowOf(getMainLooper()).idle();
|
||||
|
||||
final Menu menu = shadowOf(activity).getOptionsMenu();
|
||||
assertTrue(menu != null);
|
||||
|
||||
@@ -637,7 +762,7 @@ public class LoyaltyCardViewActivityTest
|
||||
|
||||
Activity activity = (Activity)activityController.get();
|
||||
DBHelper db = new DBHelper(activity);
|
||||
db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
|
||||
|
||||
activityController.start();
|
||||
activityController.visible();
|
||||
@@ -657,7 +782,7 @@ public class LoyaltyCardViewActivityTest
|
||||
|
||||
Activity activity = (Activity)activityController.get();
|
||||
DBHelper db = new DBHelper(activity);
|
||||
db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, null, 0);
|
||||
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, null, 0);
|
||||
|
||||
activityController.start();
|
||||
activityController.visible();
|
||||
@@ -677,14 +802,14 @@ public class LoyaltyCardViewActivityTest
|
||||
|
||||
Activity activity = (Activity)activityController.get();
|
||||
DBHelper db = new DBHelper(activity);
|
||||
db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, null, 0);
|
||||
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, null, 0);
|
||||
|
||||
activityController.start();
|
||||
activityController.visible();
|
||||
activityController.resume();
|
||||
|
||||
// Save and check the loyalty card
|
||||
saveLoyaltyCardWithArguments(activity, "store", "note", null, BARCODE_DATA, BARCODE_TYPE, false);
|
||||
saveLoyaltyCardWithArguments(activity, "store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, false);
|
||||
|
||||
db.close();
|
||||
}
|
||||
@@ -696,14 +821,14 @@ public class LoyaltyCardViewActivityTest
|
||||
|
||||
Activity activity = (Activity)activityController.get();
|
||||
DBHelper db = new DBHelper(activity);
|
||||
db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, "", Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, "", Color.BLACK, 0);
|
||||
|
||||
activityController.start();
|
||||
activityController.visible();
|
||||
activityController.resume();
|
||||
|
||||
// Save and check the loyalty card
|
||||
saveLoyaltyCardWithArguments(activity, "store", "note", null, BARCODE_DATA, activity.getApplicationContext().getString(R.string.noBarcode), false);
|
||||
saveLoyaltyCardWithArguments(activity, "store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, activity.getApplicationContext().getString(R.string.noBarcode), false);
|
||||
|
||||
db.close();
|
||||
}
|
||||
@@ -716,24 +841,24 @@ public class LoyaltyCardViewActivityTest
|
||||
final Context context = ApplicationProvider.getApplicationContext();
|
||||
DBHelper db = new DBHelper(activity);
|
||||
|
||||
db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
|
||||
|
||||
activityController.start();
|
||||
activityController.visible();
|
||||
activityController.resume();
|
||||
|
||||
// First check if the card is as expected
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), BARCODE_DATA, BARCODE_TYPE);
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), "0", context.getString(R.string.points), BARCODE_DATA, BARCODE_TYPE);
|
||||
|
||||
// Complete empty barcode selection successfully
|
||||
selectBarcodeWithResult(activity, BARCODE_DATA, "", true);
|
||||
|
||||
// Check if the barcode type is NO_BARCODE as expected
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), BARCODE_DATA, activity.getApplicationContext().getString(R.string.noBarcode));
|
||||
checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", context.getString(R.string.never), "0", context.getString(R.string.points), BARCODE_DATA, activity.getApplicationContext().getString(R.string.noBarcode));
|
||||
assertEquals(View.GONE, activity.findViewById(R.id.barcodeLayout).getVisibility());
|
||||
|
||||
// Check if the special NO_BARCODE string doesn't get saved
|
||||
saveLoyaltyCardWithArguments(activity, "store", "note", null, BARCODE_DATA, activity.getApplicationContext().getString(R.string.noBarcode), false);
|
||||
saveLoyaltyCardWithArguments(activity, "store", "note", null, null, null, BARCODE_DATA, activity.getApplicationContext().getString(R.string.noBarcode), false);
|
||||
|
||||
db.close();
|
||||
}
|
||||
@@ -745,7 +870,7 @@ public class LoyaltyCardViewActivityTest
|
||||
|
||||
Activity activity = (Activity)activityController.get();
|
||||
DBHelper db = new DBHelper(activity);
|
||||
db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
|
||||
|
||||
final int STORE_FONT_SIZE = 50;
|
||||
final int CARD_FONT_SIZE = 40;
|
||||
@@ -785,7 +910,7 @@ public class LoyaltyCardViewActivityTest
|
||||
|
||||
Activity activity = (Activity)activityController.get();
|
||||
DBHelper db = new DBHelper(activity);
|
||||
db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
|
||||
|
||||
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||
settings.edit()
|
||||
@@ -822,13 +947,15 @@ public class LoyaltyCardViewActivityTest
|
||||
|
||||
Activity activity = (Activity) activityController.get();
|
||||
DBHelper db = new DBHelper(activity);
|
||||
db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK,0);
|
||||
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK,0);
|
||||
activityController.start();
|
||||
activityController.visible();
|
||||
activityController.resume();
|
||||
|
||||
assertEquals(false, activity.isFinishing());
|
||||
|
||||
shadowOf(getMainLooper()).idle();
|
||||
|
||||
final Menu menu = shadowOf(activity).getOptionsMenu();
|
||||
assertTrue(menu != null);
|
||||
|
||||
@@ -855,7 +982,7 @@ public class LoyaltyCardViewActivityTest
|
||||
|
||||
Activity activity = (Activity)activityController.get();
|
||||
DBHelper db = new DBHelper(activity);
|
||||
db.insertLoyaltyCard("store", "note", null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, BARCODE_DATA, BARCODE_TYPE, Color.BLACK, 0);
|
||||
|
||||
activityController.start();
|
||||
activityController.visible();
|
||||
@@ -865,49 +992,84 @@ public class LoyaltyCardViewActivityTest
|
||||
|
||||
ImageView barcodeImage = activity.findViewById(R.id.barcode);
|
||||
View collapsingToolbarLayout = activity.findViewById(R.id.collapsingToolbarLayout);
|
||||
View bottomSheet = activity.findViewById(R.id.bottom_sheet);
|
||||
ImageButton maximizeButton = activity.findViewById(R.id.maximizeButton);
|
||||
ImageButton minimizeButton = activity.findViewById(R.id.minimizeButton);
|
||||
FloatingActionButton editButton = activity.findViewById(R.id.fabEdit);
|
||||
SeekBar barcodeScaler = activity.findViewById(R.id.barcodeScaler);
|
||||
|
||||
// Android should not be in fullscreen mode
|
||||
int uiOptions = activity.getWindow().getDecorView().getSystemUiVisibility();
|
||||
assertNotEquals(uiOptions | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY, uiOptions);
|
||||
assertNotEquals(uiOptions | View.SYSTEM_UI_FLAG_FULLSCREEN, uiOptions);
|
||||
|
||||
// Elements should be visible
|
||||
// Elements should be visible (except minimize button and scaler)
|
||||
assertEquals(View.VISIBLE, collapsingToolbarLayout.getVisibility());
|
||||
assertEquals(View.VISIBLE, bottomSheet.getVisibility());
|
||||
assertEquals(View.VISIBLE, maximizeButton.getVisibility());
|
||||
assertEquals(View.GONE, minimizeButton.getVisibility());
|
||||
assertEquals(View.VISIBLE, editButton.getVisibility());
|
||||
assertEquals(View.GONE, barcodeScaler.getVisibility());
|
||||
|
||||
// Click barcode to toggle fullscreen
|
||||
barcodeImage.performClick();
|
||||
shadowOf(getMainLooper()).idle();
|
||||
|
||||
// Android should be in fullscreen mode
|
||||
uiOptions = activity.getWindow().getDecorView().getSystemUiVisibility();
|
||||
assertEquals(uiOptions | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY, uiOptions);
|
||||
assertEquals(uiOptions | View.SYSTEM_UI_FLAG_FULLSCREEN, uiOptions);
|
||||
|
||||
// Elements should not be visible
|
||||
// Elements should not be visible (except minimize button and scaler)
|
||||
assertEquals(View.GONE, collapsingToolbarLayout.getVisibility());
|
||||
assertEquals(View.GONE, bottomSheet.getVisibility());
|
||||
assertEquals(View.GONE, maximizeButton.getVisibility());
|
||||
assertEquals(View.VISIBLE, minimizeButton.getVisibility());
|
||||
assertEquals(View.GONE, editButton.getVisibility());
|
||||
assertEquals(View.VISIBLE, barcodeScaler.getVisibility());
|
||||
|
||||
// Clicking barcode again should deactivate fullscreen mode
|
||||
barcodeImage.performClick();
|
||||
shadowOf(getMainLooper()).idle();
|
||||
uiOptions = activity.getWindow().getDecorView().getSystemUiVisibility();
|
||||
assertNotEquals(uiOptions | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY, uiOptions);
|
||||
assertNotEquals(uiOptions | View.SYSTEM_UI_FLAG_FULLSCREEN, uiOptions);
|
||||
assertEquals(View.VISIBLE, collapsingToolbarLayout.getVisibility());
|
||||
assertEquals(View.VISIBLE, bottomSheet.getVisibility());
|
||||
assertEquals(View.VISIBLE, maximizeButton.getVisibility());
|
||||
assertEquals(View.GONE, minimizeButton.getVisibility());
|
||||
assertEquals(View.VISIBLE, editButton.getVisibility());
|
||||
assertEquals(View.GONE, barcodeScaler.getVisibility());
|
||||
|
||||
// Another click back to fullscreen
|
||||
barcodeImage.performClick();
|
||||
shadowOf(getMainLooper()).idle();
|
||||
uiOptions = activity.getWindow().getDecorView().getSystemUiVisibility();
|
||||
assertEquals(uiOptions | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY, uiOptions);
|
||||
assertEquals(uiOptions | View.SYSTEM_UI_FLAG_FULLSCREEN, uiOptions);
|
||||
assertEquals(View.GONE, collapsingToolbarLayout.getVisibility());
|
||||
assertEquals(View.GONE, bottomSheet.getVisibility());
|
||||
assertEquals(View.GONE, maximizeButton.getVisibility());
|
||||
assertEquals(View.VISIBLE, minimizeButton.getVisibility());
|
||||
assertEquals(View.GONE, editButton.getVisibility());
|
||||
assertEquals(View.VISIBLE, barcodeScaler.getVisibility());
|
||||
|
||||
// In full screen mode, back button should disable fullscreen
|
||||
activity.onBackPressed();
|
||||
shadowOf(getMainLooper()).idle();
|
||||
uiOptions = activity.getWindow().getDecorView().getSystemUiVisibility();
|
||||
assertNotEquals(uiOptions | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY, uiOptions);
|
||||
assertNotEquals(uiOptions | View.SYSTEM_UI_FLAG_FULLSCREEN, uiOptions);
|
||||
assertEquals(View.VISIBLE, collapsingToolbarLayout.getVisibility());
|
||||
assertEquals(View.VISIBLE, bottomSheet.getVisibility());
|
||||
assertEquals(View.VISIBLE, maximizeButton.getVisibility());
|
||||
assertEquals(View.GONE, minimizeButton.getVisibility());
|
||||
assertEquals(View.VISIBLE, editButton.getVisibility());
|
||||
assertEquals(View.GONE, barcodeScaler.getVisibility());
|
||||
|
||||
// Pressing back when not in full screen should finish activity
|
||||
activity.onBackPressed();
|
||||
shadowOf(getMainLooper()).idle();
|
||||
assertEquals(true, activity.isFinishing());
|
||||
|
||||
db.close();
|
||||
@@ -918,7 +1080,7 @@ public class LoyaltyCardViewActivityTest
|
||||
{
|
||||
Date date = new Date();
|
||||
|
||||
Uri importUri = Uri.parse("https://thelastproject.github.io/Catima/share?store=Example%20Store¬e=&expiry=" + date.getTime() + "&cardid=123456&barcodetype=AZTEC&headercolor=-416706&headertextcolor=-1");
|
||||
Uri importUri = Uri.parse("https://thelastproject.github.io/Catima/share?store=Example%20Store¬e=&expiry=" + date.getTime() + "&balance=10&balancetype=USD&cardid=123456&barcodetype=AZTEC&headercolor=-416706&headertextcolor=-1");
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setData(importUri);
|
||||
@@ -932,7 +1094,9 @@ public class LoyaltyCardViewActivityTest
|
||||
Activity activity = (Activity)activityController.get();
|
||||
final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
checkAllFields(activity, ViewMode.ADD_CARD, "Example Store", "", DateFormat.getDateInstance(DateFormat.LONG).format(date), "123456", "AZTEC");
|
||||
shadowOf(getMainLooper()).idle();
|
||||
|
||||
checkAllFields(activity, ViewMode.ADD_CARD, "Example Store", "", DateFormat.getDateInstance(DateFormat.LONG).format(date), "10.00", "$", "123456", "AZTEC");
|
||||
assertEquals(-416706, ((ColorDrawable) activity.findViewById(R.id.thumbnail).getBackground()).getColor());
|
||||
}
|
||||
|
||||
@@ -953,7 +1117,7 @@ public class LoyaltyCardViewActivityTest
|
||||
Activity activity = (Activity)activityController.get();
|
||||
final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
checkAllFields(activity, ViewMode.ADD_CARD, "Example Store", "", context.getString(R.string.never), "123456", "AZTEC");
|
||||
checkAllFields(activity, ViewMode.ADD_CARD, "Example Store", "", context.getString(R.string.never), "0", context.getString(R.string.points), "123456", "AZTEC");
|
||||
assertEquals(-416706, ((ColorDrawable) activity.findViewById(R.id.thumbnail).getBackground()).getColor());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.robolectric.annotation.Config;
|
||||
import org.robolectric.android.controller.ActivityController;
|
||||
import org.robolectric.shadows.ShadowActivity;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -60,10 +61,11 @@ public class MainActivityTest
|
||||
assertTrue(menu != null);
|
||||
|
||||
// The settings, import/export, groups, search and add button should be present
|
||||
assertEquals(menu.size(), 5);
|
||||
assertEquals(menu.size(), 6);
|
||||
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("Privacy Policy", menu.findItem(R.id.action_privacy_policy).getTitle().toString());
|
||||
assertEquals("About", menu.findItem(R.id.action_about).getTitle().toString());
|
||||
assertEquals("Settings", menu.findItem(R.id.action_settings).getTitle().toString());
|
||||
}
|
||||
@@ -95,7 +97,7 @@ public class MainActivityTest
|
||||
assertEquals(0, list.getCount());
|
||||
|
||||
DBHelper db = new DBHelper(mainActivity);
|
||||
db.insertLoyaltyCard("store", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("store", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
|
||||
|
||||
assertEquals(View.VISIBLE, helpText.getVisibility());
|
||||
assertEquals(View.GONE, noMatchingCardsText.getVisibility());
|
||||
@@ -131,10 +133,10 @@ public class MainActivityTest
|
||||
assertEquals(0, list.getCount());
|
||||
|
||||
DBHelper db = new DBHelper(mainActivity);
|
||||
db.insertLoyaltyCard("storeB", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("storeA", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("storeD", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1);
|
||||
db.insertLoyaltyCard("storeC", "note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1);
|
||||
db.insertLoyaltyCard("storeB", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("storeA", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("storeD", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1);
|
||||
db.insertLoyaltyCard("storeC", "note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1);
|
||||
|
||||
assertEquals(View.VISIBLE, helpText.getVisibility());
|
||||
assertEquals(View.GONE, noMatchingCardsText.getVisibility());
|
||||
@@ -232,8 +234,8 @@ public class MainActivityTest
|
||||
TabLayout groupTabs = mainActivity.findViewById(R.id.groups);
|
||||
|
||||
DBHelper db = new DBHelper(mainActivity);
|
||||
db.insertLoyaltyCard("The First Store", "Initial note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("The Second Store", "Secondary note", null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("The First Store", "Initial note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
|
||||
db.insertLoyaltyCard("The Second Store", "Secondary note", null, new BigDecimal("0"), null, "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 0);
|
||||
|
||||
db.insertGroup("Group one");
|
||||
List<Group> groups = new ArrayList<>();
|
||||
|
||||
104
app/src/test/java/protect/card_locker/UtilsTest.java
Normal file
104
app/src/test/java/protect/card_locker/UtilsTest.java
Normal file
@@ -0,0 +1,104 @@
|
||||
package protect.card_locker;
|
||||
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.util.Util;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Currency;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(sdk = 23)
|
||||
public class UtilsTest
|
||||
{
|
||||
@Test
|
||||
public void parseBalances()
|
||||
{
|
||||
assertEquals("1", Utils.parseCurrency("1", false).toPlainString());
|
||||
|
||||
assertEquals("1", Utils.parseCurrency("1", true).toPlainString());
|
||||
assertEquals("1.00", Utils.parseCurrency("1.00", true).toPlainString());
|
||||
assertEquals("1.00", Utils.parseCurrency("1,00", true).toPlainString());
|
||||
assertEquals("1.00", Utils.parseCurrency("1 00", true).toPlainString());
|
||||
|
||||
assertEquals("25", Utils.parseCurrency("2.5", false).toPlainString());
|
||||
assertEquals("25", Utils.parseCurrency("2,5", false).toPlainString());
|
||||
assertEquals("25", Utils.parseCurrency("2 5", false).toPlainString());
|
||||
assertEquals("205", Utils.parseCurrency("2.05", false).toPlainString());
|
||||
assertEquals("205", Utils.parseCurrency("2,05", false).toPlainString());
|
||||
assertEquals("205", Utils.parseCurrency("2 05", false).toPlainString());
|
||||
|
||||
assertEquals("2.5", Utils.parseCurrency("2.5", true).toPlainString());
|
||||
assertEquals("2.5", Utils.parseCurrency("2,5", true).toPlainString());
|
||||
assertEquals("2.5", Utils.parseCurrency("2 5", true).toPlainString());
|
||||
assertEquals("2.05", Utils.parseCurrency("2.05", true).toPlainString());
|
||||
assertEquals("2.05", Utils.parseCurrency("2,05", true).toPlainString());
|
||||
assertEquals("2.05", Utils.parseCurrency("2 05", true).toPlainString());
|
||||
assertEquals("2.50", Utils.parseCurrency("2.50", true).toPlainString());
|
||||
assertEquals("2.50", Utils.parseCurrency("2,50", true).toPlainString());
|
||||
assertEquals("2.50", Utils.parseCurrency("2 50", true).toPlainString());
|
||||
|
||||
assertEquals("995", Utils.parseCurrency("9.95", false).toPlainString());
|
||||
assertEquals("995", Utils.parseCurrency("9,95", false).toPlainString());
|
||||
assertEquals("995", Utils.parseCurrency("9 95", false).toPlainString());
|
||||
|
||||
assertEquals("9.95", Utils.parseCurrency("9.95", true).toPlainString());
|
||||
assertEquals("9.95", Utils.parseCurrency("9,95", true).toPlainString());
|
||||
assertEquals("9.95", Utils.parseCurrency("9 95", true).toPlainString());
|
||||
|
||||
assertEquals("1234", Utils.parseCurrency("1234", false).toPlainString());
|
||||
assertEquals("1234", Utils.parseCurrency("1.234", false).toPlainString());
|
||||
assertEquals("1234", Utils.parseCurrency("1,234", false).toPlainString());
|
||||
assertEquals("1234", Utils.parseCurrency("1 234", false).toPlainString());
|
||||
|
||||
assertEquals("1234", Utils.parseCurrency("1234", true).toPlainString());
|
||||
assertEquals("1234.00", Utils.parseCurrency("1234.00", true).toPlainString());
|
||||
assertEquals("1234.00", Utils.parseCurrency("1.234.00", true).toPlainString());
|
||||
assertEquals("1234.00", Utils.parseCurrency("1.234,00", true).toPlainString());
|
||||
assertEquals("1234.00", Utils.parseCurrency("1,234.00", true).toPlainString());
|
||||
assertEquals("1234.00", Utils.parseCurrency("1,234,00", true).toPlainString());
|
||||
assertEquals("1234.00", Utils.parseCurrency("1 234,00", true).toPlainString());
|
||||
assertEquals("1234.00", Utils.parseCurrency("1 234,00", true).toPlainString());
|
||||
assertEquals("1234.00", Utils.parseCurrency("1 234 00", true).toPlainString());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formatBalances()
|
||||
{
|
||||
Currency euro = Currency.getInstance("EUR");
|
||||
|
||||
assertEquals("1", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("1"), null));
|
||||
assertEquals("1.00", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("1"), euro));
|
||||
|
||||
assertEquals("25", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("25"), null));
|
||||
assertEquals("25.00", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("25"), euro));
|
||||
|
||||
assertEquals("2", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("2.5"), null));
|
||||
assertEquals("2.50", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("2.5"), euro));
|
||||
|
||||
assertEquals("2", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("2.05"), null));
|
||||
assertEquals("2.05", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("2.05"), euro));
|
||||
|
||||
assertEquals("2", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("2.50"), null));
|
||||
assertEquals("2.50", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("2.50"), euro));
|
||||
|
||||
assertEquals("995", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("995"), null));
|
||||
assertEquals("995.00", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("995"), euro));
|
||||
|
||||
assertEquals("10", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("9.95"), null));
|
||||
assertEquals("9.95", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("9.95"), euro));
|
||||
|
||||
assertEquals("1,234", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("1234"), null));
|
||||
assertEquals("1,234.00", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("1234"), euro));
|
||||
|
||||
assertEquals("1,234", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("1234.00"), null));
|
||||
assertEquals("1,234.00", Utils.formatBalanceWithoutCurrencySymbol(new BigDecimal("1234.00"), euro));
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,13 @@
|
||||

|
||||
[](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>
|
||||
<a href="https://appgallery.huawei.com/#/app/C103806479" target="_blank">
|
||||
<img src="https://huaweimobileservices.com/wp-content/uploads/2020/05/Explore-it-on-AppGallery.png" alt="Explore it on AppGallery" height="90"/></a>
|
||||
|
||||
<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://apt.izzysoft.de/fdroid/index/apk/me.hackerchick.catima" target="_blank">
|
||||
<img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" alt="Get it on IzzyOnDroid" height="90"/></a>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# PRIVACY POLICY FOR LOYALTY CARD KEYCHAIN
|
||||
# PRIVACY POLICY FOR CATIMA
|
||||
|
||||
This privacy policy governs your use of the software application Catima (“Application”) for mobile devices
|
||||
that was created by Protect. The Application is designed to store and display barcodes.
|
||||
that was created by Sylvia van Os. The Application is designed to store and display barcodes.
|
||||
|
||||
# What information does the Application obtain and how is it used?
|
||||
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
Someone wants to share a card with you. To import this card, you will first need to install the Catima app. It is free, Open Source and contains no ads.
|
||||
|
||||
<a href="https://f-droid.org/repository/browse/?fdid=me.hackerchick.catima" target="_blank">
|
||||
<img src="https://f-droidgitlab.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>
|
||||
<a href="https://appgallery.huawei.com/#/app/C103806479" target="_blank">
|
||||
<img src="https://huaweimobileservices.com/wp-content/uploads/2020/05/Explore-it-on-AppGallery.png" alt="Explore it on AppGallery" height="90"/></a>
|
||||
<a href="https://f-droid.org/repository/browse/?fdid=me.hackerchick.catima" target="_blank">
|
||||
<img src="https://f-droidgitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" height="90"/></a>
|
||||
|
||||
After installing the app, just click the link you were given again and choose "Import into Catima".
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Bist Du es auch leid, beim Bezahlen an der Kasse im Supermarkt jedes mal nach dieser Bonuskarte aus Plastik zu suchen? Genervt davon, dass die Brieftasche durch die ganzen Plastikkarten gefühlte fünf Meter dick ist? Wäre es nicht toll, eine <i>freie</i> Lösung für das Problem zu haben, die Deine Daten nicht noch zusätzlich mit weiteren Anbietern teilt?
|
||||
Bist du es auch leid, beim Bezahlen an der Kasse im Supermarkt jedes mal nach dieser Bonuskarte aus Plastik zu suchen? Genervt davon, dass die Brieftasche durch die ganzen Plastikkarten gefühlte fünf Meter dick ist? Wäre es nicht toll, eine <i>freie</i> Lösung für das Problem zu haben, die deine Daten nicht noch zusätzlich mit weiteren Anbietern teilt?
|
||||
|
||||
<i>Catima</i> ist eine App, die Deine auf Barcodes basierenden Kundenkarten auf Deinem Smartphone verwaltet. <i>Catima</i> ist Open Source und kann eines richtig gut: Deine Karten verwalten!
|
||||
<i>Catima</i> ist eine Anwendung, die deine auf Strichcodes basierenden Kundenkarten auf deinem Smartphone verwaltet. <i>Catima</i> ist quelloffen und kann eines richtig gut: Deine Karten verwalten!
|
||||
|
||||
Neue Karten können mit Leichtigkeit hinzugefügt werden. Entweder nutzt Du die Kamera Deines Smartphones, um den Barcode direkt einzulesen – oder Du gibst die unter selbigem befindliche Ziffernfolge von Hand ein. An der Kasse zeigt <i>Catima</i> sodann den Barcode auf dem Display Deines Smartphones an, sodass er vom Scanner gelesen werden kann. Sollte der Scanner dabei doch einmal streiken (was selten vorkommt – und wenn, dann meist nur bei älteren Scanner-Modellen), kann der Verkäufer die ebenfalls angezeigten Ziffern wiederum auch händisch erfassen.
|
||||
Neue Karten können mit Leichtigkeit hinzugefügt werden. Entweder nutzt du die Kamera deines Smartphones, um den Strichcode direkt einzulesen – oder du gibst die unter selbigem befindliche Ziffernfolge von Hand ein. An der Kasse zeigt <i>Catima</i> sodann den Strichcode auf dem Bildschirm deines Mobiltelefones an, sodass er vom Scanner gelesen werden kann. Sollte der Scanner dabei doch einmal streiken (was selten vorkommt – und wenn, dann meist nur bei älteren Scanner-Modellen), kann der Verkäufer die ebenfalls angezeigten Ziffern wiederum auch händisch erfassen.
|
||||
|
||||
Die App benötigt nur wenige Berechtigungen – und greift nie auf das Internet zu. <i>Catima</i> bietet auch die Möglichkeit, Deine Kartensammlung zu exportieren sowie zu importieren. Damit kannst Du die Daten z. B. auch auf ein anderes Gerät übertragen – oder etwa nach einem Werksreset wieder neu einlesen.
|
||||
Die Anwendung benötigt nur wenige Berechtigungen – und greift nie auf das Internet zu. <i>Catima</i> bietet auch die Möglichkeit, deine Kartensammlung zu exportieren sowie zu importieren. Damit kannst du die Daten z. B. auch auf ein anderes Gerät übertragen – oder etwa nach einem Werksreset wieder neu einlesen.
|
||||
|
||||
@@ -1 +1 @@
|
||||
Verwaltet barcode-basierte Kunden-/Treuekarten auf dem Handy
|
||||
Verwaltet Strichcode-basierte Kunden-/Treuekarten auf dem Mobiltelefon
|
||||
|
||||
@@ -1 +1 @@
|
||||
Catima
|
||||
Catima - Kundenkarten- und Ticketverwaltung
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 64 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 59 KiB |
@@ -1 +1 @@
|
||||
Catima – Cartes de fidélité
|
||||
Catima – Gestionnaire de cartes de fidélité
|
||||
|
||||
7
fastlane/metadata/android/it/full_description.txt
Normal file
7
fastlane/metadata/android/it/full_description.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
Sei stanco di cercare la tua carta fedeltà durante i pagamenti in negozio? Cerchi una soluzione gratuita che non catture le tue informazioni?
|
||||
|
||||
Catima è un'applicazione che memorizzerà sul tuo telefono le tue carte fedeltà basate su codici a barre. L'applicazione è open source e cerca di fare bene una cosa: gestire le tue carte!
|
||||
|
||||
Nuove carte possono essere aggiunte in un attimo. Utilizza la fotocamera per acquisire il codice a barre o digita il numero. Quando il codice a barre viene caricato nel negozio e visualizzato, può essere scansionato con un moderno lettore di codici a barre. (Alcuni negozi utilizzano lettori di codici a barre meno recenti, come scanner piani, invece di scanner di immagini. Questi non possono leggere il display dello smartphone. In questi casi, devi chiedere all'impiegato di digitare il numero manualmente).
|
||||
|
||||
L'applicazione richiede pochissime autorizzazioni e non tenta mai di accedere a Internet. E' possibile eseguire il backup delle carte nella memoria locale. E da lì puoi conservare il backup da qualche parte al sicuro.
|
||||
1
fastlane/metadata/android/it/short_description.txt
Normal file
1
fastlane/metadata/android/it/short_description.txt
Normal file
@@ -0,0 +1 @@
|
||||
Gestisce le carte fedeltà basate su codici a barre sul telefono
|
||||
1
fastlane/metadata/android/it/title.txt
Normal file
1
fastlane/metadata/android/it/title.txt
Normal file
@@ -0,0 +1 @@
|
||||
Catima – Gestore di carte fedeltà
|
||||
@@ -2,6 +2,6 @@ Ben jij het ook zo zat om tijdens het afrekenen steeds weer te moeten graaien na
|
||||
|
||||
Dan is Catima wat voor jou! Met deze app kun je je op barcode gebaseerde klantenkaarten opslaan op je telefoon. De app is open source en heeft maar één doel: het beheren en organiseren van je kaarten!
|
||||
|
||||
Je voegt nieuwe kaarten in een handomdraai toe, met je camera of door het nummer ervan in te voeren. In de winkel hoef je vervolgens alleen maar de kaart te openen zodat deze kan worden gescand door elke moderne barcodescanner. (Sommige winkels gebruiken nog ouderwetse barcodescanners, zoals flatbedscanners, welke je scherm niet kunnen aflezen. In dat geval moet de kassamedewerker het nummer handmatig invoeren.).
|
||||
Je voegt nieuwe kaarten in een handomdraai toe, met je camera of door het nummer ervan in te voeren. In de winkel hoef je vervolgens alleen maar de kaart te openen zodat deze kan worden gescand door elke moderne barcodescanner. (Sommige winkels gebruiken nog ouderwetse barcodescanners, zoals scanners, welke je scherm niet kunnen aflezen. In dat geval moet de kassamedewerker het nummer handmatig invoeren.).
|
||||
|
||||
Catima vereist nauwelijks rechten en maakt geen contact met het internet. Je kunt je kaarten back-uppen naar je lokale opslag en de back-up vervolgens overzetten naar een veilig opslagmedium.
|
||||
|
||||
7
fastlane/metadata/android/ru-RU/full_description.txt
Normal file
7
fastlane/metadata/android/ru-RU/full_description.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
Надоело искать свою пластиковую скидочную карту на кассе в магазине? Ищете бесплатное решение, которое не будет собирать вашу личную информацию?
|
||||
|
||||
Catima — это приложение, которое будет хранить карты лояльности на основе штрих-кодов на вашем смартфоне. Это приложение с открытым исходным кодом и пытается сделать хорошо только одну вещь : управлять вашими картами!
|
||||
|
||||
Новые карты можно добавить в одно мгновение. Либо используйте камеру, чтобы считать штрих-код, либо введите номер карты вручную. Добавленный в приложение штрих-код можно считать с экрана смартфона в магазине с современным сканером штрих-кода. (В некоторых магазинах вместо сканеров изображений используются старые сканеры штрих-кодов, например, планшетные сканеры. Они не могут считывать данные с экрана. Вместо этого попросите сотрудника ввести номер вручную.).
|
||||
|
||||
Приложению требуется очень мало разрешений и не нужен доступ в интернет. Есть возможность резервного копирования ваших карт в локальное хранилище. Оттуда вы сможете перенести данные резервной копии в надёжное место.
|
||||
Reference in New Issue
Block a user