Compare commits

..

48 Commits
v1.9 ... v1.11

Author SHA1 Message Date
Sylvia van Os
eba1ed63a6 Release 1.11 2021-03-23 09:35:43 +01:00
Sylvia van Os
74fbdc7a5e Merge pull request #167 from weblate/weblate-catima-catima
Translations update from Weblate
2021-03-21 10:34:44 +01:00
solokot
ef61aaeac6 Translated using Weblate (Russian)
Currently translated at 100.0% (3 of 3 strings)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/es/
2021-02-24 00:50:28 +01:00
Sylvia van Os
bc808d3045 Really fix 2021-02-23 20:55:01 +01:00
Sylvia van Os
b265fadec3 Improve currency parsing logic 2021-02-23 18:49:45 +01:00
Miha Frange?
940be02851 Added option to keep the screen on 2021-02-14 17:29:51 +01:00
35 changed files with 848 additions and 290 deletions

View File

@@ -1,5 +1,32 @@
# Changelog
## 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:

View File

@@ -18,8 +18,8 @@ android {
applicationId "me.hackerchick.catima"
minSdkVersion 19
targetSdkVersion 29
versionCode 59
versionName "1.9"
versionCode 64
versionName "1.11"
vectorDrawables.useSupportLibrary true
}

View File

@@ -20,14 +20,14 @@ public class DBHelper extends SQLiteOpenHelper
public static final int ORIGINAL_DATABASE_VERSION = 1;
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";
@@ -43,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";

View File

@@ -2,7 +2,7 @@ package protect.card_locker;
public enum DataFormat
{
CSV,
Catima,
VoucherVault
;
}

View File

@@ -35,10 +35,12 @@ 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 DataFormat importDataFormat;
@Override
protected void onCreate(Bundle savedInstanceState)
{
@@ -93,7 +95,7 @@ public class ImportExportActivity extends AppCompatActivity
@Override
public void onClick(View v)
{
chooseFileWithIntent(intentGetContentAction, CHOOSE_EXPORTED_FILE);
chooseImportType(intentGetContentAction);
}
});
@@ -106,12 +108,35 @@ 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:
// Loyalty Card Keychain
case 1:
importDataFormat = DataFormat.Catima;
break;
// Voucher Vault
case 2:
importDataFormat = DataFormat.VoucherVault;
break;
default:
throw new IllegalArgumentException("Unknown DataFormat");
}
chooseFileWithIntent(baseIntent, IMPORT);
});
builder.show();
}
private void startImport(final InputStream target, final Uri targetUri, final DataFormat dataFormat)
{
ImportExportTask.TaskCompleteListener listener = new ImportExportTask.TaskCompleteListener()
{
@@ -123,7 +148,7 @@ public class ImportExportActivity extends AppCompatActivity
};
importExporter = new ImportExportTask(ImportExportActivity.this,
DataFormat.CSV, target, listener);
dataFormat, target, listener);
importExporter.execute();
}
@@ -139,7 +164,7 @@ public class ImportExportActivity extends AppCompatActivity
};
importExporter = new ImportExportTask(ImportExportActivity.this,
DataFormat.CSV, target, listener);
DataFormat.Catima, target, listener);
importExporter.execute();
}
@@ -294,7 +319,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 +361,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)

View File

@@ -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";

View File

@@ -214,7 +214,7 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
hasChanged = true;
try {
BigDecimal balance = Utils.parseCurrency(s.toString());
BigDecimal balance = Utils.parseCurrency(s.toString(), Utils.currencyHasDecimals((Currency) balanceCurrencyField.getTag()));
validBalance = true;
balanceField.setTag(balance);
@@ -246,7 +246,11 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
balanceCurrencyField.setTag(currency);
balanceField.setText(Utils.formatBalanceWithoutCurrencySymbol((BigDecimal) balanceField.getTag(), currency));
BigDecimal balance = (BigDecimal) balanceField.getTag();
if (balance != null) {
balanceField.setText(Utils.formatBalanceWithoutCurrencySymbol(balance, currency));
}
}
@Override
@@ -366,10 +370,10 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
noteFieldEdit.setText("");
expiryField.setTag(null);
expiryField.setText("");
balanceField.setTag(null);
balanceField.setText("");
balanceCurrencyField.setTag(null);
balanceCurrencyField.setText("");
balanceField.setTag(null);
balanceField.setText("");
cardIdFieldView.setText("");
barcodeTypeField.setText("");
}
@@ -409,18 +413,18 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
formatExpiryField(loyaltyCard.expiry);
}
if(balanceField.getText().length() == 0)
{
balanceField.setTag(loyaltyCard.balance);
balanceField.setText(Utils.formatBalanceWithoutCurrencySymbol(loyaltyCard.balance, loyaltyCard.balanceType));
}
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)
{
cardIdFieldView.setText(loyaltyCard.cardId);

View File

@@ -236,10 +236,24 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
// '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);
}

View File

@@ -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.thank_you, 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
@@ -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);

View File

@@ -94,6 +94,8 @@ public class Utils {
NumberFormat currencyFormat = NumberFormat.getCurrencyInstance();
currencyFormat.setCurrency(currency);
currencyFormat.setMinimumFractionDigits(currency.getDefaultFractionDigits());
currencyFormat.setMaximumFractionDigits(currency.getDefaultFractionDigits());
return currencyFormat.format(value);
}
@@ -112,10 +114,24 @@ public class Utils {
return numberFormat.format(value);
}
static public BigDecimal parseCurrency(String value) throws NumberFormatException {
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 commas with dots
value = value.replace(',', '.');
// 1. Replace all non-numbers with dots
value = value.replaceAll("[^0-9]", ".");
// 2. Remove all but the last dot
while (value.split("\\.").length > 2) {

View File

@@ -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.

View File

@@ -1,4 +1,4 @@
package protect.card_locker;
package protect.card_locker.importexport;
import android.database.sqlite.SQLiteDatabase;
@@ -15,6 +15,10 @@ import java.util.Currency;
import java.util.Date;
import java.util.List;
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)
* formatted data.

View File

@@ -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.

View File

@@ -1,7 +1,13 @@
package protect.card_locker;
package protect.card_locker.importexport;
import org.json.JSONException;
import java.io.IOException;
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
@@ -15,5 +21,5 @@ public interface DatabaseImporter
* @throws IOException
* @throws FormatException
*/
void importData(DBHelper db, InputStreamReader input) throws IOException, FormatException, InterruptedException;
void importData(DBHelper db, InputStreamReader input) throws IOException, FormatException, InterruptedException, JSONException, ParseException;
}

View File

@@ -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)

View File

@@ -1,9 +1,18 @@
package protect.card_locker;
package protect.card_locker.importexport;
import android.util.Log;
import org.json.JSONException;
import java.io.IOException;
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
{
@@ -26,9 +35,12 @@ public class MultiFormatImporter
switch(format)
{
case CSV:
case Catima:
importer = new CsvDatabaseImporter();
break;
case VoucherVault:
importer = new VoucherVaultImporter();
break;
}
if (importer != null)
@@ -38,7 +50,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);
}

View File

@@ -0,0 +1,132 @@
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.InputStreamReader;
import java.math.BigDecimal;
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, InputStreamReader input) throws IOException, FormatException, JSONException, ParseException {
BufferedReader bufferedReader = new BufferedReader(input);
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();
}
}

View File

@@ -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);
}
}

View File

@@ -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"

View File

@@ -103,4 +103,24 @@
<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>
<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">Barcode auf dem Bildschirm zentrieren</string>
<string name="moveBarcodeToTopOfScreen">Barcode 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">Barcode bearbeiten</string>
<string name="barcode">Barcode</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>
</resources>

View File

@@ -103,4 +103,14 @@
<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>
</resources>

View File

@@ -103,4 +103,24 @@
<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">&lt;xliff:g xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\"&gt;%s&lt;/xliff:g&gt; 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>
</resources>

View File

@@ -119,4 +119,8 @@
<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>
</resources>
<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>
</resources>

View File

@@ -119,4 +119,13 @@
<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>
</resources>
<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="thank_you">Bedankt</string>
<string name="privacy_policy">Privacybeleid</string>
</resources>

View File

@@ -1,31 +1,31 @@
<?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="editCard">Изменить штрих-код</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="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 +34,16 @@
<string name="noStoreError">Название магазина не указано</string>
<string name="noCardIdError">Номер карты не указан</string>
<string name="noCardExistsError">Карта не найдена</string>
<string name="failedParsingImportUriError">Не удалось разобрать импортируемый URI</string>
<string name="importExport">Импорт/Экспорт</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 +54,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 +71,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="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, авторские права 20162020 Branden Archer.</string>
<string name="unstar">Удалить из избранного</string>
<string name="star">Добавить в избранное</string>
@@ -94,5 +94,40 @@
<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="thank_you">Спасибо</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>
</resources>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="import_types_array">
<item>Catima</item>
<item>@string/app_loyalty_card_keychain</item>
<item>Voucher Vault</item>
</string-array>
</resources>

View File

@@ -104,8 +104,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>
@@ -151,4 +156,10 @@
<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="thank_you">Thank you</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>
</resources>

View File

@@ -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>

View File

@@ -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;
@@ -26,11 +28,16 @@ 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;
@@ -112,20 +119,65 @@ public class ImportExportTest
boolean result = (id != -1);
assertTrue(result);
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);
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), new BigDecimal("0"), null, 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());
}
@@ -269,34 +321,31 @@ 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());
InputStreamReader inStream = new InputStreamReader(inData);
// Import the CSV data
result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
assertTrue(result);
// Import the CSV data
result = MultiFormatImporter.importData(db, inStream, 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
@@ -304,34 +353,31 @@ 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());
InputStreamReader inStream = new InputStreamReader(inData);
// Import the CSV data
result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
assertTrue(result);
// Import the CSV data
result = MultiFormatImporter.importData(db, inStream, 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)
@@ -351,77 +397,74 @@ 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());
InputStreamReader inStream = new InputStreamReader(inData);
// Import the CSV data
result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
assertTrue(result);
// Import the CSV data
result = MultiFormatImporter.importData(db, inStream, 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
@@ -429,32 +472,29 @@ 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());
InputStreamReader inStream = new InputStreamReader(inData);
// 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, inStream, 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
@@ -470,7 +510,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();
@@ -484,8 +524,8 @@ public class ImportExportTest
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, inStream, format);
assertEquals(false, result);
assertEquals(0, db.getLoyaltyCardCount());
@@ -513,49 +553,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
@@ -575,7 +612,7 @@ public class ImportExportTest
InputStreamReader inStream = new InputStreamReader(inputStream);
// Import the CSV data
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -613,7 +650,7 @@ public class ImportExportTest
InputStreamReader inStream = new InputStreamReader(inputStream);
// Import the CSV data
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -651,7 +688,7 @@ public class ImportExportTest
InputStreamReader inStream = new InputStreamReader(inputStream);
// Import the CSV data
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima);
assertEquals(false, result);
assertEquals(0, db.getLoyaltyCardCount());
@@ -677,7 +714,7 @@ public class ImportExportTest
InputStreamReader inStream = new InputStreamReader(inputStream);
// Import the CSV data
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima);
assertEquals(true, result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -715,7 +752,7 @@ public class ImportExportTest
InputStreamReader inStream = new InputStreamReader(inputStream);
// Import the CSV data
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima);
assertEquals(true, result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -753,7 +790,7 @@ public class ImportExportTest
InputStreamReader inStream = new InputStreamReader(inputStream);
// Import the CSV data
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima);
assertEquals(true, result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -791,7 +828,7 @@ public class ImportExportTest
InputStreamReader inStream = new InputStreamReader(inputStream);
// Import the CSV data
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -811,7 +848,7 @@ public class ImportExportTest
inStream = new InputStreamReader(inputStream);
// Import the CSV data
result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
result = MultiFormatImporter.importData(db, inStream, DataFormat.Catima);
assertTrue(result);
assertEquals(1, db.getLoyaltyCardCount());
@@ -830,59 +867,63 @@ 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));
InputStreamReader inStream = new InputStreamReader(inputStream);
// Import the Voucher Vault data
boolean result = MultiFormatImporter.importData(db, inStream, 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(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(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(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(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(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);
cursor.moveToNext();
card = LoyaltyCard.toLoyaltyCard(cursor);
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);
cursor.close();
clearDatabase();
}
}

View File

@@ -61,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());
}

View File

@@ -5,6 +5,10 @@ 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;
@@ -16,21 +20,85 @@ public class UtilsTest
@Test
public void parseBalances()
{
assertEquals("1000.00", Utils.parseCurrency("1.000.00").toPlainString());
assertEquals("1000.50", Utils.parseCurrency("1.000.50").toPlainString());
assertEquals("1000.50", Utils.parseCurrency("1.000,50").toPlainString());
assertEquals("1000.50", Utils.parseCurrency("1,000,50").toPlainString());
assertEquals("1000.50", Utils.parseCurrency("1,000.50").toPlainString());
assertEquals("1000", Utils.parseCurrency("1000").toPlainString());
assertEquals("995", Utils.parseCurrency("995").toPlainString());
assertEquals("9.95", Utils.parseCurrency("9.95").toPlainString());
assertEquals("9.95", Utils.parseCurrency("9,95").toPlainString());
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());
assertThrows(NumberFormatException.class, () -> Utils.parseCurrency(""));
assertThrows(NumberFormatException.class, () -> Utils.parseCurrency("."));
assertThrows(NumberFormatException.class, () -> Utils.parseCurrency(","));
assertThrows(NumberFormatException.class, () -> Utils.parseCurrency(".,"));
assertThrows(NumberFormatException.class, () -> Utils.parseCurrency("a"));
assertThrows(NumberFormatException.class, () -> Utils.parseCurrency("......................"));
}
}
@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));
}
}

View File

@@ -1,4 +1,4 @@
# 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 Sylvia van Os. The Application is designed to store and display barcodes.

View File

@@ -1 +1 @@
Verwaltet barcode-basierte Kunden-/Treuekarten auf dem Handy
Verwaltet barcode-basierte Kunden-/Treuekarten auf dem Handy

View File

@@ -1 +1 @@
Catima
Catima - Kundenkarten- & Ticket-Manager

View File

@@ -0,0 +1,7 @@
Надоело искать свою пластиковую скидочную карту на кассе в магазине? Ищете бесплатное решение, которое не будет собирать вашу личную информацию?
Catima — это приложение, которое будет хранить карты лояльности на основе штрих-кодов на вашем смартфоне. Это приложение с открытым исходным кодом и пытается сделать хорошо только одну вещь : управлять вашими картами!
Новые карты можно добавить в одно мгновение. Либо используйте камеру, чтобы считать штрих-код, либо введите номер карты вручную. Добавленный в приложение штрих-код можно считать с экрана смартфона в магазине с современным сканером штрих-кода. (В некоторых магазинах вместо сканеров изображений используются старые сканеры штрих-кодов, например, планшетные сканеры. Они не могут считывать данные с экрана. Вместо этого попросите сотрудника ввести номер вручную.).
Приложению требуется очень мало разрешений и не нужен доступ в интернет. Есть возможность резервного копирования ваших карт в локальное хранилище. Оттуда вы сможете перенести данные резервной копии в надёжное место.