mirror of
https://github.com/CatimaLoyalty/Android.git
synced 2026-05-18 05:36:39 -04:00
Merge pull request #245 from TheLastProject/feature/StocardImport
Stocard import
This commit is contained in:
@@ -6,6 +6,8 @@ Changes:
|
||||
|
||||
- Add UPC-E support
|
||||
- Support adding a front and back photo to each card
|
||||
- Support importing password-protected zip files
|
||||
- Support importing from Stocard (Beta)
|
||||
- Fix useless whitespace in notes from Fidme import
|
||||
|
||||
## v1.14.1 (2021-06-14)
|
||||
|
||||
@@ -84,6 +84,7 @@ dependencies {
|
||||
implementation 'com.jaredrummler:colorpicker:1.1.0'
|
||||
implementation 'com.google.guava:guava:30.1.1-jre'
|
||||
implementation 'com.github.invissvenska:NumberPickerPreference:1.0.2'
|
||||
implementation 'net.lingala.zip4j:zip4j:2.8.0'
|
||||
|
||||
// SpotBugs
|
||||
implementation 'io.wcm.tooling.spotbugs:io.wcm.tooling.spotbugs.annotations:1.0.0'
|
||||
|
||||
@@ -6,7 +6,6 @@ import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
@@ -275,7 +274,7 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
return newId;
|
||||
}
|
||||
|
||||
public boolean insertLoyaltyCard(final SQLiteDatabase db, final String store,
|
||||
public long insertLoyaltyCard(final SQLiteDatabase db, final String store,
|
||||
final String note, final Date expiry, final BigDecimal balance,
|
||||
final Currency balanceType, final String cardId,
|
||||
final String barcodeId, final BarcodeFormat barcodeType,
|
||||
@@ -293,10 +292,10 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
|
||||
contentValues.put(LoyaltyCardDbIds.STAR_STATUS,starStatus);
|
||||
final long newId = db.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
|
||||
return (newId != -1);
|
||||
return newId;
|
||||
}
|
||||
|
||||
public boolean insertLoyaltyCard(final SQLiteDatabase db, final int id, final String store,
|
||||
public long insertLoyaltyCard(final SQLiteDatabase db, final int id, final String store,
|
||||
final String note, final Date expiry, final BigDecimal balance,
|
||||
final Currency balanceType, final String cardId,
|
||||
final String barcodeId, final BarcodeFormat barcodeType,
|
||||
@@ -315,7 +314,7 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
|
||||
contentValues.put(LoyaltyCardDbIds.STAR_STATUS,starStatus);
|
||||
final long newId = db.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
|
||||
return (newId != -1);
|
||||
return newId;
|
||||
}
|
||||
|
||||
public boolean updateLoyaltyCard(final int id, final String store, final String note,
|
||||
|
||||
@@ -8,10 +8,12 @@ import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.text.InputType;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.File;
|
||||
@@ -20,6 +22,8 @@ import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
@@ -27,6 +31,8 @@ import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import protect.card_locker.importexport.DataFormat;
|
||||
import protect.card_locker.importexport.ImportExportResult;
|
||||
|
||||
public class ImportExportActivity extends AppCompatActivity
|
||||
{
|
||||
@@ -115,9 +121,22 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
}
|
||||
|
||||
private void chooseImportType(Intent baseIntent) {
|
||||
List<CharSequence> betaImportOptions = new ArrayList<>();
|
||||
betaImportOptions.add("Fidme");
|
||||
betaImportOptions.add("Stocard");
|
||||
List<CharSequence> importOptions = new ArrayList<>();
|
||||
|
||||
for (String importOption : getResources().getStringArray(R.array.import_types_array)) {
|
||||
if (betaImportOptions.contains(importOption)) {
|
||||
importOption = importOption + " (BETA)";
|
||||
}
|
||||
|
||||
importOptions.add(importOption);
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.chooseImportType)
|
||||
.setItems(R.array.import_types_array, (dialog, which) -> {
|
||||
.setItems(importOptions.toArray(new CharSequence[importOptions.size()]), (dialog, which) -> {
|
||||
switch (which) {
|
||||
// Catima
|
||||
case 0:
|
||||
@@ -137,8 +156,14 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
importAlertMessage = getString(R.string.importLoyaltyCardKeychainMessage);
|
||||
importDataFormat = DataFormat.Catima;
|
||||
break;
|
||||
// Voucher Vault
|
||||
// Stocard
|
||||
case 3:
|
||||
importAlertTitle = getString(R.string.importStocard);
|
||||
importAlertMessage = getString(R.string.importStocardMessage);
|
||||
importDataFormat = DataFormat.Stocard;
|
||||
break;
|
||||
// Voucher Vault
|
||||
case 4:
|
||||
importAlertTitle = getString(R.string.importVoucherVault);
|
||||
importAlertMessage = getString(R.string.importVoucherVaultMessage);
|
||||
importDataFormat = DataFormat.VoucherVault;
|
||||
@@ -162,19 +187,19 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void startImport(final InputStream target, final Uri targetUri, final DataFormat dataFormat)
|
||||
private void startImport(final InputStream target, final Uri targetUri, final DataFormat dataFormat, final char[] password)
|
||||
{
|
||||
ImportExportTask.TaskCompleteListener listener = new ImportExportTask.TaskCompleteListener()
|
||||
{
|
||||
@Override
|
||||
public void onTaskComplete(boolean success)
|
||||
public void onTaskComplete(ImportExportResult result, DataFormat dataFormat)
|
||||
{
|
||||
onImportComplete(success, targetUri);
|
||||
onImportComplete(result, targetUri, dataFormat);
|
||||
}
|
||||
};
|
||||
|
||||
importExporter = new ImportExportTask(ImportExportActivity.this,
|
||||
dataFormat, target, listener);
|
||||
dataFormat, target, password, listener);
|
||||
importExporter.execute();
|
||||
}
|
||||
|
||||
@@ -183,9 +208,9 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
ImportExportTask.TaskCompleteListener listener = new ImportExportTask.TaskCompleteListener()
|
||||
{
|
||||
@Override
|
||||
public void onTaskComplete(boolean success)
|
||||
public void onTaskComplete(ImportExportResult result, DataFormat dataFormat)
|
||||
{
|
||||
onExportComplete(success, targetUri);
|
||||
onExportComplete(result, targetUri);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -210,7 +235,7 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
}
|
||||
}
|
||||
|
||||
if(success == false)
|
||||
if(!success)
|
||||
{
|
||||
// External storage permission rejected, inform user that
|
||||
// import/export is prevented
|
||||
@@ -245,20 +270,43 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void onImportComplete(boolean success, Uri path)
|
||||
{
|
||||
private void retryWithPassword(DataFormat dataFormat, Uri uri) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.passwordRequired);
|
||||
|
||||
final EditText input = new EditText(this);
|
||||
input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
builder.setView(input);
|
||||
|
||||
builder.setPositiveButton(R.string.ok, (dialogInterface, i) -> {
|
||||
activityResultParser(IMPORT, RESULT_OK, uri, input.getText().toString().toCharArray());
|
||||
});
|
||||
builder.setNegativeButton(R.string.cancel, (dialogInterface, i) -> dialogInterface.cancel());
|
||||
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void onImportComplete(ImportExportResult result, Uri path, DataFormat dataFormat) {
|
||||
if (result == ImportExportResult.BadPassword) {
|
||||
retryWithPassword(dataFormat, path);
|
||||
return;
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
|
||||
if(success)
|
||||
int messageId;
|
||||
|
||||
if (result == ImportExportResult.Success)
|
||||
{
|
||||
builder.setTitle(R.string.importSuccessfulTitle);
|
||||
messageId = R.string.importSuccessful;
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.setTitle(R.string.importFailedTitle);
|
||||
messageId = R.string.importFailed;
|
||||
}
|
||||
|
||||
int messageId = success ? R.string.importSuccessful : R.string.importFailed;
|
||||
final String message = getResources().getString(messageId);
|
||||
|
||||
builder.setMessage(message);
|
||||
@@ -274,53 +322,44 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private void onExportComplete(boolean success, final Uri path)
|
||||
private void onExportComplete(ImportExportResult result, final Uri path)
|
||||
{
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
|
||||
if(success)
|
||||
int messageId;
|
||||
|
||||
if(result == ImportExportResult.Success)
|
||||
{
|
||||
builder.setTitle(R.string.exportSuccessfulTitle);
|
||||
messageId = R.string.exportSuccessful;
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.setTitle(R.string.exportFailedTitle);
|
||||
messageId = R.string.exportFailed;
|
||||
}
|
||||
|
||||
int messageId = success ? R.string.exportSuccessful : R.string.exportFailed;
|
||||
final String message = getResources().getString(messageId);
|
||||
|
||||
builder.setMessage(message);
|
||||
builder.setNeutralButton(R.string.ok, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
builder.setNeutralButton(R.string.ok, (dialog, which) -> dialog.dismiss());
|
||||
|
||||
if(success)
|
||||
if(result == ImportExportResult.Success)
|
||||
{
|
||||
final CharSequence sendLabel = ImportExportActivity.this.getResources().getText(R.string.sendLabel);
|
||||
|
||||
builder.setPositiveButton(sendLabel, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
Intent sendIntent = new Intent(Intent.ACTION_SEND);
|
||||
sendIntent.putExtra(Intent.EXTRA_STREAM, path);
|
||||
sendIntent.setType("text/csv");
|
||||
builder.setPositiveButton(sendLabel, (dialog, which) -> {
|
||||
Intent sendIntent = new Intent(Intent.ACTION_SEND);
|
||||
sendIntent.putExtra(Intent.EXTRA_STREAM, path);
|
||||
sendIntent.setType("text/csv");
|
||||
|
||||
// set flag to give temporary permission to external app to use the FileProvider
|
||||
sendIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
// set flag to give temporary permission to external app to use the FileProvider
|
||||
sendIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
|
||||
ImportExportActivity.this.startActivity(Intent.createChooser(sendIntent,
|
||||
sendLabel));
|
||||
ImportExportActivity.this.startActivity(Intent.createChooser(sendIntent,
|
||||
sendLabel));
|
||||
|
||||
dialog.dismiss();
|
||||
}
|
||||
dialog.dismiss();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -340,18 +379,13 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
private void activityResultParser(int requestCode, int resultCode, Uri uri, char[] password) {
|
||||
if (resultCode != RESULT_OK)
|
||||
{
|
||||
Log.w(TAG, "Failed onActivityResult(), result=" + resultCode);
|
||||
return;
|
||||
}
|
||||
|
||||
Uri uri = data.getData();
|
||||
if(uri == null)
|
||||
{
|
||||
Log.e(TAG, "Activity returned a NULL URI");
|
||||
@@ -389,7 +423,7 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
|
||||
Log.e(TAG, "Starting file import with: " + uri.toString());
|
||||
|
||||
startImport(reader, uri, importDataFormat);
|
||||
startImport(reader, uri, importDataFormat, password);
|
||||
}
|
||||
}
|
||||
catch(FileNotFoundException e)
|
||||
@@ -397,12 +431,26 @@ public class ImportExportActivity extends AppCompatActivity
|
||||
Log.e(TAG, "Failed to import/export file: " + uri.toString(), e);
|
||||
if (requestCode == CHOOSE_EXPORT_LOCATION)
|
||||
{
|
||||
onExportComplete(false, uri);
|
||||
onExportComplete(ImportExportResult.GenericFailure, uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
onImportComplete(false, uri);
|
||||
onImportComplete(ImportExportResult.GenericFailure, uri, importDataFormat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if(data == null)
|
||||
{
|
||||
Log.e(TAG, "Activity returned NULL data");
|
||||
return;
|
||||
}
|
||||
|
||||
activityResultParser(requestCode, resultCode, data.getData(), null);
|
||||
}
|
||||
}
|
||||
@@ -13,10 +13,12 @@ import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import protect.card_locker.importexport.DataFormat;
|
||||
import protect.card_locker.importexport.ImportExportResult;
|
||||
import protect.card_locker.importexport.MultiFormatExporter;
|
||||
import protect.card_locker.importexport.MultiFormatImporter;
|
||||
|
||||
class ImportExportTask extends AsyncTask<Void, Void, Boolean>
|
||||
class ImportExportTask extends AsyncTask<Void, Void, ImportExportResult>
|
||||
{
|
||||
private static final String TAG = "Catima";
|
||||
|
||||
@@ -25,6 +27,7 @@ class ImportExportTask extends AsyncTask<Void, Void, Boolean>
|
||||
private DataFormat format;
|
||||
private OutputStream outputStream;
|
||||
private InputStream inputStream;
|
||||
private char[] password;
|
||||
private TaskCompleteListener listener;
|
||||
|
||||
private ProgressDialog progress;
|
||||
@@ -46,7 +49,7 @@ class ImportExportTask extends AsyncTask<Void, Void, Boolean>
|
||||
/**
|
||||
* Constructor which will setup a task for importing from the given InputStream.
|
||||
*/
|
||||
ImportExportTask(Activity activity, DataFormat format, InputStream input,
|
||||
ImportExportTask(Activity activity, DataFormat format, InputStream input, char[] password,
|
||||
TaskCompleteListener listener)
|
||||
{
|
||||
super();
|
||||
@@ -54,24 +57,22 @@ class ImportExportTask extends AsyncTask<Void, Void, Boolean>
|
||||
this.doImport = true;
|
||||
this.format = format;
|
||||
this.inputStream = input;
|
||||
this.password = password;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
private boolean performImport(Context context, InputStream stream, DBHelper db)
|
||||
private ImportExportResult performImport(Context context, InputStream stream, DBHelper db, char[] password)
|
||||
{
|
||||
boolean result = false;
|
||||
ImportExportResult importResult = MultiFormatImporter.importData(context, db, stream, format, password);
|
||||
|
||||
Log.i(TAG, "Import result: " + importResult.name());
|
||||
|
||||
result = MultiFormatImporter.importData(context, db, stream, format);
|
||||
|
||||
Log.i(TAG, "Import result: " + result);
|
||||
|
||||
return result;
|
||||
return importResult;
|
||||
}
|
||||
|
||||
private boolean performExport(Context context, OutputStream stream, DBHelper db)
|
||||
private ImportExportResult performExport(Context context, OutputStream stream, DBHelper db)
|
||||
{
|
||||
boolean result = false;
|
||||
ImportExportResult result = ImportExportResult.GenericFailure;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -106,14 +107,14 @@ class ImportExportTask extends AsyncTask<Void, Void, Boolean>
|
||||
progress.show();
|
||||
}
|
||||
|
||||
protected Boolean doInBackground(Void... nothing)
|
||||
protected ImportExportResult doInBackground(Void... nothing)
|
||||
{
|
||||
final DBHelper db = new DBHelper(activity);
|
||||
boolean result;
|
||||
ImportExportResult result;
|
||||
|
||||
if(doImport)
|
||||
{
|
||||
result = performImport(activity.getApplicationContext(), inputStream, db);
|
||||
result = performImport(activity.getApplicationContext(), inputStream, db, password);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -123,9 +124,9 @@ class ImportExportTask extends AsyncTask<Void, Void, Boolean>
|
||||
return result;
|
||||
}
|
||||
|
||||
protected void onPostExecute(Boolean result)
|
||||
protected void onPostExecute(ImportExportResult result)
|
||||
{
|
||||
listener.onTaskComplete(result);
|
||||
listener.onTaskComplete(result, format);
|
||||
|
||||
progress.dismiss();
|
||||
Log.i(TAG, (doImport ? "Import" : "Export") + " Complete");
|
||||
@@ -138,7 +139,7 @@ class ImportExportTask extends AsyncTask<Void, Void, Boolean>
|
||||
}
|
||||
interface TaskCompleteListener
|
||||
{
|
||||
void onTaskComplete(boolean success);
|
||||
void onTaskComplete(ImportExportResult result, DataFormat format);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package protect.card_locker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
|
||||
import java.sql.Blob;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
|
||||
@@ -104,9 +104,6 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
|
||||
ImageView cardImageFront;
|
||||
ImageView cardImageBack;
|
||||
|
||||
Bitmap frontImageBitmap;
|
||||
Bitmap backImageBitmap;
|
||||
|
||||
Button enterButton;
|
||||
|
||||
int loyaltyCardId;
|
||||
|
||||
@@ -89,6 +89,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
boolean starred;
|
||||
boolean backgroundNeedsDarkIcons;
|
||||
FullscreenType fullscreenType = FullscreenType.NONE;
|
||||
boolean isBarcodeSupported = true;
|
||||
|
||||
enum FullscreenType {
|
||||
NONE,
|
||||
@@ -406,7 +407,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
}
|
||||
expiryView.setTag(loyaltyCard.expiry);
|
||||
|
||||
if (fullscreenType != FullscreenType.NONE) {
|
||||
if (fullscreenType == FullscreenType.NONE) {
|
||||
makeBottomSheetVisibleIfUseful();
|
||||
}
|
||||
|
||||
@@ -459,8 +460,6 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
// Set shadow colour of store text so even same color on same color would be readable
|
||||
storeName.setShadowLayer(1, 1, 1, backgroundNeedsDarkIcons ? Color.BLACK : Color.WHITE);
|
||||
|
||||
Boolean isBarcodeSupported = true;
|
||||
|
||||
if (format != null && !BarcodeSelectorActivity.SUPPORTED_BARCODE_TYPES.contains(format.name())) {
|
||||
isBarcodeSupported = false;
|
||||
|
||||
@@ -682,14 +681,15 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
private void setFullscreen(FullscreenType fullscreenType)
|
||||
{
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if (fullscreenType != FullscreenType.NONE)
|
||||
{
|
||||
Log.d(TAG, "Move into of fullscreen");
|
||||
if (fullscreenType != FullscreenType.NONE) {
|
||||
Log.d(TAG, "Move into fullscreen");
|
||||
|
||||
if (fullscreenType == FullscreenType.IMAGE_FRONT) {
|
||||
barcodeImage.setImageBitmap(frontImageBitmap);
|
||||
barcodeImage.setVisibility(View.VISIBLE);
|
||||
} else if (fullscreenType == FullscreenType.IMAGE_BACK) {
|
||||
barcodeImage.setImageBitmap(backImageBitmap);
|
||||
barcodeImage.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
// Prepare redraw after size change
|
||||
redrawBarcodeAfterResize();
|
||||
@@ -737,10 +737,16 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
|
||||
barcodeScaler.setProgress(100);
|
||||
|
||||
// Prepare redraw after size change
|
||||
redrawBarcodeAfterResize();
|
||||
if (format != null && isBarcodeSupported) {
|
||||
redrawBarcodeAfterResize();
|
||||
} else {
|
||||
barcodeImage.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// Show maximize and hide minimize button and scaler
|
||||
maximizeButton.setVisibility(View.VISIBLE);
|
||||
if (format != null && isBarcodeSupported) {
|
||||
maximizeButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
minimizeButton.setVisibility(View.GONE);
|
||||
barcodeScaler.setVisibility(View.GONE);
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import java.util.Calendar;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.HashMap;
|
||||
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
|
||||
@@ -345,4 +346,12 @@ public class Utils {
|
||||
|
||||
return BitmapFactory.decodeStream(in);
|
||||
}
|
||||
|
||||
static public Object hashmapGetOrDefault(HashMap hashMap, String key, Object defaultValue) {
|
||||
Object value = hashMap.get(key);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@ package protect.card_locker.importexport;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.util.Base64;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
@@ -37,7 +34,7 @@ import protect.card_locker.Utils;
|
||||
*/
|
||||
public class CsvImporter implements Importer
|
||||
{
|
||||
public void importData(Context context, DBHelper db, InputStream input) throws IOException, FormatException, InterruptedException
|
||||
public void importData(Context context, DBHelper db, InputStream input, char[] password) throws IOException, FormatException, InterruptedException
|
||||
{
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package protect.card_locker;
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
public enum DataFormat
|
||||
{
|
||||
Catima,
|
||||
Fidme,
|
||||
Stocard,
|
||||
VoucherVault
|
||||
;
|
||||
}
|
||||
@@ -5,6 +5,9 @@ import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
import net.lingala.zip4j.io.inputstream.ZipInputStream;
|
||||
import net.lingala.zip4j.model.LocalFileHeader;
|
||||
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.CSVParser;
|
||||
import org.apache.commons.csv.CSVRecord;
|
||||
@@ -16,8 +19,6 @@ import java.io.StringReader;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.ParseException;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.FormatException;
|
||||
@@ -31,18 +32,18 @@ import protect.card_locker.FormatException;
|
||||
*/
|
||||
public class FidmeImporter implements Importer
|
||||
{
|
||||
public void importData(Context context, DBHelper db, InputStream input) throws IOException, FormatException, JSONException, ParseException {
|
||||
public void importData(Context context, DBHelper db, InputStream input, char[] password) throws IOException, FormatException, JSONException, ParseException {
|
||||
// We actually retrieve a .zip file
|
||||
ZipInputStream zipInputStream = new ZipInputStream(input);
|
||||
ZipInputStream zipInputStream = new ZipInputStream(input, password);
|
||||
|
||||
StringBuilder loyaltyCards = new StringBuilder();
|
||||
byte[] buffer = new byte[1024];
|
||||
int read = 0;
|
||||
|
||||
ZipEntry zipEntry;
|
||||
LocalFileHeader localFileHeader;
|
||||
|
||||
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
|
||||
if (zipEntry.getName().equals("loyalty_programs.csv")) {
|
||||
while ((localFileHeader = zipInputStream.getNextEntry()) != null) {
|
||||
if (localFileHeader.getFileName().equals("loyalty_programs.csv")) {
|
||||
while ((read = zipInputStream.read(buffer, 0, 1024)) >= 0) {
|
||||
loyaltyCards.append(new String(buffer, 0, read, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
public enum ImportExportResult
|
||||
{
|
||||
Success,
|
||||
GenericFailure,
|
||||
BadPassword
|
||||
;
|
||||
}
|
||||
@@ -23,5 +23,5 @@ public interface Importer
|
||||
* @throws IOException
|
||||
* @throws FormatException
|
||||
*/
|
||||
void importData(Context context, DBHelper db, InputStream input) throws IOException, FormatException, InterruptedException, JSONException, ParseException;
|
||||
void importData(Context context, DBHelper db, InputStream input, char[] password) throws IOException, FormatException, InterruptedException, JSONException, ParseException;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.DataFormat;
|
||||
|
||||
public class MultiFormatExporter
|
||||
{
|
||||
@@ -19,11 +18,11 @@ public class MultiFormatExporter
|
||||
*
|
||||
* The output stream is closed on success.
|
||||
*
|
||||
* @return true if the database was successfully exported,
|
||||
* false otherwise. If false, partial data may have been
|
||||
* @return ImportExportResult.Success if the database was successfully exported,
|
||||
* another ImportExportResult otherwise. If not Success, partial data may have been
|
||||
* written to the output stream, and it should be discarded.
|
||||
*/
|
||||
public static boolean exportData(Context context, DBHelper db, OutputStreamWriter output, DataFormat format)
|
||||
public static ImportExportResult exportData(Context context, DBHelper db, OutputStreamWriter output, DataFormat format)
|
||||
{
|
||||
Exporter exporter = null;
|
||||
|
||||
@@ -42,7 +41,7 @@ public class MultiFormatExporter
|
||||
try
|
||||
{
|
||||
exporter.exportData(context, db, output);
|
||||
return true;
|
||||
return ImportExportResult.Success;
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
@@ -53,12 +52,12 @@ public class MultiFormatExporter
|
||||
Log.e(TAG, "Failed to export data", e);
|
||||
}
|
||||
|
||||
return false;
|
||||
return ImportExportResult.GenericFailure;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.e(TAG, "Unsupported data format exported: " + format.name());
|
||||
return false;
|
||||
return ImportExportResult.GenericFailure;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package protect.card_locker.importexport;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -10,7 +12,6 @@ import java.io.InputStream;
|
||||
import java.text.ParseException;
|
||||
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.DataFormat;
|
||||
import protect.card_locker.FormatException;
|
||||
|
||||
public class MultiFormatImporter
|
||||
@@ -24,11 +25,11 @@ public class MultiFormatImporter
|
||||
* The input stream is not closed, and doing so is the
|
||||
* responsibility of the caller.
|
||||
*
|
||||
* @return true if the database was successfully imported,
|
||||
* false otherwise. If false, no data was written to
|
||||
* @return ImportExportResult.Success if the database was successfully imported,
|
||||
* or another result otherwise. If no Success, no data was written to
|
||||
* the database.
|
||||
*/
|
||||
public static boolean importData(Context context, DBHelper db, InputStream input, DataFormat format)
|
||||
public static ImportExportResult importData(Context context, DBHelper db, InputStream input, DataFormat format, char[] password)
|
||||
{
|
||||
Importer importer = null;
|
||||
|
||||
@@ -40,6 +41,9 @@ public class MultiFormatImporter
|
||||
case Fidme:
|
||||
importer = new FidmeImporter();
|
||||
break;
|
||||
case Stocard:
|
||||
importer = new StocardImporter();
|
||||
break;
|
||||
case VoucherVault:
|
||||
importer = new VoucherVaultImporter();
|
||||
break;
|
||||
@@ -49,8 +53,12 @@ public class MultiFormatImporter
|
||||
{
|
||||
try
|
||||
{
|
||||
importer.importData(context, db, input);
|
||||
return true;
|
||||
importer.importData(context, db, input, password);
|
||||
return ImportExportResult.Success;
|
||||
}
|
||||
catch(ZipException e)
|
||||
{
|
||||
return ImportExportResult.BadPassword;
|
||||
}
|
||||
catch(IOException | FormatException | InterruptedException | JSONException | ParseException e)
|
||||
{
|
||||
@@ -62,6 +70,7 @@ public class MultiFormatImporter
|
||||
{
|
||||
Log.e(TAG, "Unsupported data format imported: " + format.name());
|
||||
}
|
||||
return false;
|
||||
|
||||
return ImportExportResult.GenericFailure;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
package protect.card_locker.importexport;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
import net.lingala.zip4j.io.inputstream.ZipInputStream;
|
||||
import net.lingala.zip4j.model.LocalFileHeader;
|
||||
|
||||
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.io.Reader;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.FormatException;
|
||||
import protect.card_locker.Utils;
|
||||
|
||||
/**
|
||||
* 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 StocardImporter implements Importer
|
||||
{
|
||||
public void importData(Context context, DBHelper db, InputStream input, char[] password) throws IOException, FormatException, JSONException, ParseException {
|
||||
HashMap<String, HashMap<String, Object>> loyaltyCardHashMap = new HashMap<>();
|
||||
HashMap<String, String> providers = new HashMap<>();
|
||||
|
||||
ZipInputStream zipInputStream = new ZipInputStream(input, password);
|
||||
|
||||
String[] providersFileName = null;
|
||||
String[] cardBaseName = null;
|
||||
String cardName = "";
|
||||
LocalFileHeader localFileHeader;
|
||||
while ((localFileHeader = zipInputStream.getNextEntry()) != null) {
|
||||
String fileName = localFileHeader.getFileName();
|
||||
String[] nameParts = fileName.split("/");
|
||||
|
||||
if (providersFileName == null) {
|
||||
providersFileName = new String[] {
|
||||
nameParts[0],
|
||||
"sync",
|
||||
"data",
|
||||
"users",
|
||||
nameParts[0],
|
||||
"analytics-properties.json"
|
||||
};
|
||||
cardBaseName = new String[] {
|
||||
nameParts[0],
|
||||
"sync",
|
||||
"data",
|
||||
"users",
|
||||
nameParts[0],
|
||||
"loyalty-cards"
|
||||
};
|
||||
}
|
||||
|
||||
if (startsWith(nameParts, providersFileName, 0) && !localFileHeader.isDirectory()) {
|
||||
providers = parseProviders(zipInputStream);
|
||||
} else if (startsWith(nameParts, cardBaseName, 1)) {
|
||||
// Extract cardName
|
||||
cardName = nameParts[cardBaseName.length].split("\\.", 2)[0];
|
||||
|
||||
// This is the card itself
|
||||
if (nameParts.length == cardBaseName.length + 1) {
|
||||
// Ignore the .txt file
|
||||
if (fileName.endsWith(".json")) {
|
||||
JSONObject jsonObject = readJSON(zipInputStream);
|
||||
|
||||
loyaltyCardHashMap = appendToLoyaltyCardHashMap(
|
||||
loyaltyCardHashMap,
|
||||
cardName,
|
||||
"cardId",
|
||||
jsonObject.getString("input_id")
|
||||
);
|
||||
loyaltyCardHashMap = appendToLoyaltyCardHashMap(
|
||||
loyaltyCardHashMap,
|
||||
cardName,
|
||||
"_providerId",
|
||||
jsonObject
|
||||
.getJSONObject("input_provider_reference")
|
||||
.getString("identifier")
|
||||
.substring("/loyalty-card-providers/".length())
|
||||
);
|
||||
|
||||
try {
|
||||
loyaltyCardHashMap = appendToLoyaltyCardHashMap(
|
||||
loyaltyCardHashMap,
|
||||
cardName,
|
||||
"barcodeType",
|
||||
jsonObject.getString("input_barcode_format")
|
||||
);
|
||||
} catch (JSONException ignored) {}
|
||||
}
|
||||
} else if (fileName.endsWith("notes/default.json")) {
|
||||
loyaltyCardHashMap = appendToLoyaltyCardHashMap(
|
||||
loyaltyCardHashMap,
|
||||
cardName,
|
||||
"note",
|
||||
readJSON(zipInputStream)
|
||||
.getString("content")
|
||||
);
|
||||
} else if (fileName.endsWith("/images/front.png")) {
|
||||
loyaltyCardHashMap = appendToLoyaltyCardHashMap(
|
||||
loyaltyCardHashMap,
|
||||
cardName,
|
||||
"frontImage",
|
||||
readImage(zipInputStream)
|
||||
);
|
||||
} else if (fileName.endsWith("/images/back.png")) {
|
||||
loyaltyCardHashMap = appendToLoyaltyCardHashMap(
|
||||
loyaltyCardHashMap,
|
||||
cardName,
|
||||
"backImage",
|
||||
readImage(zipInputStream)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (loyaltyCardHashMap.keySet().size() == 0) {
|
||||
throw new FormatException("Couldn't find any loyalty cards in this Stocard export.");
|
||||
}
|
||||
|
||||
SQLiteDatabase database = db.getWritableDatabase();
|
||||
database.beginTransaction();
|
||||
|
||||
for (HashMap<String, Object> loyaltyCardData : loyaltyCardHashMap.values()) {
|
||||
String store = providers.get(loyaltyCardData.get("_providerId").toString());
|
||||
String note = (String) Utils.hashmapGetOrDefault(loyaltyCardData, "note", "");
|
||||
String cardId = (String) loyaltyCardData.get("cardId");
|
||||
String barcodeTypeString = (String) Utils.hashmapGetOrDefault(loyaltyCardData, "barcodeType", null);
|
||||
BarcodeFormat barcodeType = null;
|
||||
if (barcodeTypeString != null) {
|
||||
if (barcodeTypeString.equals("RSS_DATABAR_EXPANDED")) {
|
||||
barcodeType = BarcodeFormat.RSS_EXPANDED;
|
||||
} else {
|
||||
barcodeType = BarcodeFormat.valueOf(barcodeTypeString);
|
||||
}
|
||||
}
|
||||
|
||||
long loyaltyCardInternalId = db.insertLoyaltyCard(database, store, note, null, BigDecimal.valueOf(0), null, cardId, null, barcodeType, null, 0);
|
||||
|
||||
if (loyaltyCardData.containsKey("frontImage")) {
|
||||
Utils.saveCardImage(context, (Bitmap) loyaltyCardData.get("frontImage"), (int) loyaltyCardInternalId, true);
|
||||
}
|
||||
if (loyaltyCardData.containsKey("backImage")) {
|
||||
Utils.saveCardImage(context, (Bitmap) loyaltyCardData.get("backImage"), (int) loyaltyCardInternalId, false);
|
||||
}
|
||||
}
|
||||
|
||||
database.setTransactionSuccessful();
|
||||
database.endTransaction();
|
||||
database.close();
|
||||
|
||||
zipInputStream.close();
|
||||
}
|
||||
|
||||
private boolean startsWith(String[] full, String[] start, int minExtraLength) {
|
||||
if (full.length - minExtraLength < start.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < start.length; i++) {
|
||||
if (!start[i].contentEquals(full[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private String read(ZipInputStream zipInputStream) throws IOException {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
Reader reader = new BufferedReader(new InputStreamReader(zipInputStream, Charset.forName(StandardCharsets.UTF_8.name())));
|
||||
int c;
|
||||
while ((c = reader.read()) != -1) {
|
||||
stringBuilder.append((char) c);
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
private Bitmap readImage(ZipInputStream zipInputStream) {
|
||||
return BitmapFactory.decodeStream(zipInputStream);
|
||||
}
|
||||
|
||||
private JSONObject readJSON(ZipInputStream zipInputStream) throws IOException, JSONException {
|
||||
return new JSONObject(read(zipInputStream));
|
||||
}
|
||||
|
||||
private HashMap<String, HashMap<String, Object>> appendToLoyaltyCardHashMap(HashMap<String, HashMap<String, Object>> loyaltyCardHashMap, String cardID, String key, Object value) {
|
||||
HashMap<String, Object> loyaltyCardData = loyaltyCardHashMap.get(cardID);
|
||||
if (loyaltyCardData == null) {
|
||||
loyaltyCardData = new HashMap<>();
|
||||
}
|
||||
|
||||
loyaltyCardData.put(key, value);
|
||||
loyaltyCardHashMap.put(cardID, loyaltyCardData);
|
||||
|
||||
return loyaltyCardHashMap;
|
||||
}
|
||||
|
||||
private HashMap<String, String> parseProviders(ZipInputStream zipInputStream) throws IOException, JSONException {
|
||||
// FIXME: This is probably completely wrong, but it works for the one and only test file I have
|
||||
JSONObject jsonObject = readJSON(zipInputStream);
|
||||
|
||||
JSONArray providerIdList = jsonObject.getJSONArray("provider_id_list");
|
||||
JSONArray providerList = jsonObject.getJSONArray("provider_list");
|
||||
|
||||
// Resort, put IDs with - in them after IDs without any -
|
||||
List<String> providerIds = new ArrayList<>();
|
||||
List<String> customProviderIds = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < providerIdList.length(); i++) {
|
||||
String providerId = providerIdList.get(i).toString();
|
||||
if (providerId.contains("-")) {
|
||||
customProviderIds.add(providerId);
|
||||
} else {
|
||||
providerIds.add(providerId);
|
||||
}
|
||||
}
|
||||
providerIds.addAll(customProviderIds);
|
||||
|
||||
HashMap<String, String> providers = new HashMap<>();
|
||||
for (int i = 0; i < jsonObject.getInt("number_of_cards"); i++) {
|
||||
providers.put(providerIds.get(i), providerList.get(i).toString());
|
||||
}
|
||||
|
||||
return providers;
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ import protect.card_locker.FormatException;
|
||||
*/
|
||||
public class VoucherVaultImporter implements Importer
|
||||
{
|
||||
public void importData(Context context, DBHelper db, InputStream input) throws IOException, FormatException, JSONException, ParseException {
|
||||
public void importData(Context context, DBHelper db, InputStream input, char[] password) throws IOException, FormatException, JSONException, ParseException {
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<item>Catima</item>
|
||||
<item>Fidme</item>
|
||||
<item>@string/app_loyalty_card_keychain</item>
|
||||
<item>Stocard</item>
|
||||
<item>Voucher Vault</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
@@ -168,6 +168,8 @@
|
||||
<string name="importFidmeMessage">Find a file likely named <i>fidme-export-request-xxxxxx.zip</i> to import, and then select the barcode types manually afterwards.\nOr create it from your FidMe profile by choosing "Data Protection" and then pressing "Extract my data" first.</string>
|
||||
<string name="importLoyaltyCardKeychain">Import from Loyalty Card Keychain</string>
|
||||
<string name="importLoyaltyCardKeychainMessage">Find a file most likely named <i>LoyaltyCardKeychain.csv</i> to import.\nOr create it from the Import/Export menu in Loyalty Card Keychain by pressing "Export" first.</string>
|
||||
<string name="importStocard">Import from Stocard</string>
|
||||
<string name="importStocardMessage">Find your Stocard .zip file to import, and then select the barcode types manually afterwards.\nOr get it by emailing support@stocardapp.com and asking for an export of your data first.</string>
|
||||
<string name="importVoucherVault">Import from Voucher Vault</string>
|
||||
<string name="importVoucherVaultMessage">Find a file likely named <i>vouchervault.json</i> to import.\nOr create it by pressing "Export" in Voucher Vault first.</string>
|
||||
<string name="barcodeId">Barcode value</string>
|
||||
@@ -189,4 +191,5 @@
|
||||
<string name="updateBarcodeQuestionText">You changed the card ID. Do you want to also update the barcode to use the same value?</string>
|
||||
<string name="yes">Yes</string>
|
||||
<string name="no">No</string>
|
||||
<string name="passwordRequired">Please enter the password</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Environment;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
@@ -24,7 +20,6 @@ import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.annotation.LooperMode;
|
||||
import org.robolectric.shadows.ShadowLog;
|
||||
import org.robolectric.util.Util;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -45,9 +40,9 @@ import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
import protect.card_locker.importexport.DataFormat;
|
||||
import protect.card_locker.importexport.ImportExportResult;
|
||||
import protect.card_locker.importexport.MultiFormatExporter;
|
||||
import protect.card_locker.importexport.MultiFormatImporter;
|
||||
|
||||
@@ -338,8 +333,8 @@ public class ImportExportTest
|
||||
OutputStreamWriter outStream = new OutputStreamWriter(outData);
|
||||
|
||||
// Export data to CSV format
|
||||
boolean result = MultiFormatExporter.exportData(activity.getApplicationContext(), db, outStream, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
ImportExportResult result = MultiFormatExporter.exportData(activity.getApplicationContext(), db, outStream, DataFormat.Catima);
|
||||
assertEquals(ImportExportResult.Success, result);
|
||||
outStream.close();
|
||||
|
||||
TestHelpers.getEmptyDb(activity);
|
||||
@@ -347,8 +342,8 @@ public class ImportExportTest
|
||||
ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray());
|
||||
|
||||
// Import the CSV data
|
||||
result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inData, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inData, DataFormat.Catima, null);
|
||||
assertEquals(ImportExportResult.Success, result);
|
||||
|
||||
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
|
||||
|
||||
@@ -369,8 +364,8 @@ public class ImportExportTest
|
||||
OutputStreamWriter outStream = new OutputStreamWriter(outData);
|
||||
|
||||
// Export data to CSV format
|
||||
boolean result = MultiFormatExporter.exportData(activity.getApplicationContext(), db, outStream, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
ImportExportResult result = MultiFormatExporter.exportData(activity.getApplicationContext(), db, outStream, DataFormat.Catima);
|
||||
assertEquals(ImportExportResult.Success, result);
|
||||
outStream.close();
|
||||
|
||||
TestHelpers.getEmptyDb(activity);
|
||||
@@ -378,8 +373,8 @@ public class ImportExportTest
|
||||
ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray());
|
||||
|
||||
// Import the CSV data
|
||||
result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inData, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inData, DataFormat.Catima, null);
|
||||
assertEquals(ImportExportResult.Success, result);
|
||||
|
||||
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
|
||||
|
||||
@@ -442,8 +437,8 @@ public class ImportExportTest
|
||||
OutputStreamWriter outStream = new OutputStreamWriter(outData);
|
||||
|
||||
// Export data to CSV format
|
||||
boolean result = MultiFormatExporter.exportData(activity.getApplicationContext(), db, outStream, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
ImportExportResult result = MultiFormatExporter.exportData(activity.getApplicationContext(), db, outStream, DataFormat.Catima);
|
||||
assertEquals(ImportExportResult.Success, result);
|
||||
outStream.close();
|
||||
|
||||
TestHelpers.getEmptyDb(activity);
|
||||
@@ -451,8 +446,8 @@ public class ImportExportTest
|
||||
ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray());
|
||||
|
||||
// Import the CSV data
|
||||
result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inData, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inData, DataFormat.Catima, null);
|
||||
assertEquals(ImportExportResult.Success, result);
|
||||
|
||||
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
|
||||
assertEquals(NUM_GROUPS, db.getGroupCount());
|
||||
@@ -486,15 +481,15 @@ public class ImportExportTest
|
||||
OutputStreamWriter outStream = new OutputStreamWriter(outData);
|
||||
|
||||
// Export into CSV data
|
||||
boolean result = MultiFormatExporter.exportData(activity.getApplicationContext(), db, outStream, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
ImportExportResult result = MultiFormatExporter.exportData(activity.getApplicationContext(), db, outStream, DataFormat.Catima);
|
||||
assertEquals(ImportExportResult.Success, result);
|
||||
outStream.close();
|
||||
|
||||
ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray());
|
||||
|
||||
// Import the CSV data on top of the existing database
|
||||
result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inData, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inData, DataFormat.Catima, null);
|
||||
assertEquals(ImportExportResult.Success, result);
|
||||
|
||||
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
|
||||
|
||||
@@ -517,8 +512,8 @@ public class ImportExportTest
|
||||
OutputStreamWriter outStream = new OutputStreamWriter(outData);
|
||||
|
||||
// Export data to CSV format
|
||||
boolean result = MultiFormatExporter.exportData(activity.getApplicationContext(), db, outStream, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
ImportExportResult result = MultiFormatExporter.exportData(activity.getApplicationContext(), db, outStream, DataFormat.Catima);
|
||||
assertEquals(ImportExportResult.Success, result);
|
||||
|
||||
TestHelpers.getEmptyDb(activity);
|
||||
|
||||
@@ -531,8 +526,8 @@ public class ImportExportTest
|
||||
ByteArrayInputStream inData = new ByteArrayInputStream((outData.toString() + corruptEntry).getBytes());
|
||||
|
||||
// Attempt to import the data
|
||||
result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inData, format);
|
||||
assertEquals(false, result);
|
||||
result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inData, format, null);
|
||||
assertEquals(ImportExportResult.GenericFailure, result);
|
||||
|
||||
assertEquals(0, db.getLoyaltyCardCount());
|
||||
|
||||
@@ -542,11 +537,11 @@ public class ImportExportTest
|
||||
|
||||
class TestTaskCompleteListener implements ImportExportTask.TaskCompleteListener
|
||||
{
|
||||
Boolean success;
|
||||
ImportExportResult result;
|
||||
|
||||
public void onTaskComplete(boolean success)
|
||||
public void onTaskComplete(ImportExportResult result, DataFormat dataFormat)
|
||||
{
|
||||
this.success = success;
|
||||
this.result = result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -572,8 +567,8 @@ public class ImportExportTest
|
||||
Robolectric.flushBackgroundThreadScheduler();
|
||||
|
||||
// Check that the listener was executed
|
||||
assertNotNull(listener.success);
|
||||
assertEquals(true, listener.success);
|
||||
assertNotNull(listener.result);
|
||||
assertEquals(ImportExportResult.Success, listener.result);
|
||||
|
||||
TestHelpers.getEmptyDb(activity);
|
||||
|
||||
@@ -583,15 +578,15 @@ public class ImportExportTest
|
||||
|
||||
FileInputStream fileStream = new FileInputStream(exportFile);
|
||||
|
||||
task = new ImportExportTask(activity, DataFormat.Catima, fileStream, listener);
|
||||
task = new ImportExportTask(activity, DataFormat.Catima, fileStream, null, listener);
|
||||
task.execute();
|
||||
|
||||
// Actually run the task to completion
|
||||
Robolectric.flushBackgroundThreadScheduler();
|
||||
|
||||
// Check that the listener was executed
|
||||
assertNotNull(listener.success);
|
||||
assertEquals(true, listener.success);
|
||||
assertNotNull(listener.result);
|
||||
assertEquals(ImportExportResult.Success, listener.result);
|
||||
|
||||
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());
|
||||
|
||||
@@ -617,8 +612,8 @@ public class ImportExportTest
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(csvText.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// Import the CSV data
|
||||
boolean result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inputStream, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
ImportExportResult result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inputStream, DataFormat.Catima, null);
|
||||
assertEquals(ImportExportResult.Success, result);
|
||||
assertEquals(1, db.getLoyaltyCardCount());
|
||||
|
||||
LoyaltyCard card = db.getLoyaltyCard(1);
|
||||
@@ -655,8 +650,8 @@ public class ImportExportTest
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(csvText.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// Import the CSV data
|
||||
boolean result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inputStream, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
ImportExportResult result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inputStream, DataFormat.Catima, null);
|
||||
assertEquals(ImportExportResult.Success, result);
|
||||
assertEquals(1, db.getLoyaltyCardCount());
|
||||
|
||||
LoyaltyCard card = db.getLoyaltyCard(1);
|
||||
@@ -693,8 +688,8 @@ public class ImportExportTest
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(csvText.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// Import the CSV data
|
||||
boolean result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inputStream, DataFormat.Catima);
|
||||
assertEquals(false, result);
|
||||
ImportExportResult result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inputStream, DataFormat.Catima, null);
|
||||
assertEquals(ImportExportResult.GenericFailure, result);
|
||||
assertEquals(0, db.getLoyaltyCardCount());
|
||||
|
||||
TestHelpers.getEmptyDb(activity);
|
||||
@@ -718,8 +713,8 @@ public class ImportExportTest
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(csvText.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// Import the CSV data
|
||||
boolean result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inputStream, DataFormat.Catima);
|
||||
assertEquals(true, result);
|
||||
ImportExportResult result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inputStream, DataFormat.Catima, null);
|
||||
assertEquals(ImportExportResult.Success, result);
|
||||
assertEquals(1, db.getLoyaltyCardCount());
|
||||
|
||||
LoyaltyCard card = db.getLoyaltyCard(1);
|
||||
@@ -756,8 +751,8 @@ public class ImportExportTest
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(csvText.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// Import the CSV data
|
||||
boolean result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inputStream, DataFormat.Catima);
|
||||
assertEquals(true, result);
|
||||
ImportExportResult result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inputStream, DataFormat.Catima, null);
|
||||
assertEquals(ImportExportResult.Success, result);
|
||||
assertEquals(1, db.getLoyaltyCardCount());
|
||||
|
||||
LoyaltyCard card = db.getLoyaltyCard(1);
|
||||
@@ -794,8 +789,8 @@ public class ImportExportTest
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(csvText.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// Import the CSV data
|
||||
boolean result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inputStream, DataFormat.Catima);
|
||||
assertEquals(true, result);
|
||||
ImportExportResult result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inputStream, DataFormat.Catima, null);
|
||||
assertEquals(ImportExportResult.Success, result);
|
||||
assertEquals(1, db.getLoyaltyCardCount());
|
||||
|
||||
LoyaltyCard card = db.getLoyaltyCard(1);
|
||||
@@ -832,8 +827,8 @@ public class ImportExportTest
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(csvText.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// Import the CSV data
|
||||
boolean result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inputStream, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
ImportExportResult result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inputStream, DataFormat.Catima, null);
|
||||
assertEquals(ImportExportResult.Success, result);
|
||||
assertEquals(1, db.getLoyaltyCardCount());
|
||||
|
||||
csvText = "";
|
||||
@@ -851,8 +846,8 @@ public class ImportExportTest
|
||||
inputStream = new ByteArrayInputStream(csvText.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// Import the CSV data
|
||||
result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inputStream, DataFormat.Catima);
|
||||
assertTrue(result);
|
||||
result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inputStream, DataFormat.Catima, null);
|
||||
assertEquals(ImportExportResult.Success, result);
|
||||
assertEquals(1, db.getLoyaltyCardCount());
|
||||
|
||||
LoyaltyCard card = db.getLoyaltyCard(1);
|
||||
@@ -941,8 +936,8 @@ public class ImportExportTest
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(csvText.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// Import the CSV data
|
||||
boolean result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inputStream, DataFormat.Catima);
|
||||
assertEquals(true, result);
|
||||
ImportExportResult result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inputStream, DataFormat.Catima, null);
|
||||
assertEquals(ImportExportResult.Success, result);
|
||||
assertEquals(7, db.getLoyaltyCardCount());
|
||||
assertEquals(3, db.getGroupCount());
|
||||
|
||||
@@ -1082,8 +1077,8 @@ public class ImportExportTest
|
||||
InputStream inputStream = getClass().getResourceAsStream("fidme-export-request-2021-06-29-17-28-20.zip");
|
||||
|
||||
// Import the Fidme data
|
||||
boolean result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inputStream, DataFormat.Fidme);
|
||||
assertTrue(result);
|
||||
ImportExportResult result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inputStream, DataFormat.Fidme, null);
|
||||
assertEquals(ImportExportResult.Success, result);
|
||||
assertEquals(3, db.getLoyaltyCardCount());
|
||||
|
||||
LoyaltyCard card = db.getLoyaltyCard(1);
|
||||
@@ -1125,6 +1120,69 @@ public class ImportExportTest
|
||||
TestHelpers.getEmptyDb(activity);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void importStocard() throws IOException {
|
||||
InputStream inputStream = getClass().getResourceAsStream("50e33e49-cfa0-49ad-a297-c1d655f72b01-sync.zip");
|
||||
|
||||
// Import the Stocard data
|
||||
ImportExportResult result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inputStream, DataFormat.Stocard, null);
|
||||
assertEquals(ImportExportResult.BadPassword, result);
|
||||
assertEquals(0, db.getLoyaltyCardCount());
|
||||
|
||||
inputStream = getClass().getResourceAsStream("50e33e49-cfa0-49ad-a297-c1d655f72b01-sync.zip");
|
||||
|
||||
result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inputStream, DataFormat.Stocard, "da811b40a4dac56f0cbb2d99b21bbb9a".toCharArray());
|
||||
assertEquals(ImportExportResult.Success, result);
|
||||
assertEquals(3, db.getLoyaltyCardCount());
|
||||
|
||||
LoyaltyCard card = db.getLoyaltyCard(1);
|
||||
|
||||
assertEquals("GAMMA", card.store);
|
||||
assertEquals("", card.note);
|
||||
assertEquals(null, card.expiry);
|
||||
assertEquals(new BigDecimal("0"), card.balance);
|
||||
assertEquals(null, card.balanceType);
|
||||
assertEquals("55555", card.cardId);
|
||||
assertEquals(null, card.barcodeId);
|
||||
assertEquals(null, card.barcodeType);
|
||||
assertEquals(0, card.starStatus);
|
||||
|
||||
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 1, true));
|
||||
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 1, false));
|
||||
|
||||
card = db.getLoyaltyCard(2);
|
||||
|
||||
assertEquals("Air Miles", card.store);
|
||||
assertEquals("szjsbs", card.note);
|
||||
assertEquals(null, card.expiry);
|
||||
assertEquals(new BigDecimal("0"), card.balance);
|
||||
assertEquals(null, card.balanceType);
|
||||
assertEquals("7649484", card.cardId);
|
||||
assertEquals(null, card.barcodeId);
|
||||
assertEquals(null, card.barcodeType);
|
||||
assertEquals(0, card.starStatus);
|
||||
|
||||
assertTrue(BitmapFactory.decodeStream(getClass().getResourceAsStream("stocard-front.jpg")).sameAs(Utils.retrieveCardImage(activity.getApplicationContext(), 2, true)));
|
||||
assertTrue(BitmapFactory.decodeStream(getClass().getResourceAsStream("stocard-back.jpg")).sameAs(Utils.retrieveCardImage(activity.getApplicationContext(), 2, false)));
|
||||
|
||||
card = db.getLoyaltyCard(3);
|
||||
|
||||
assertEquals("jö", card.store);
|
||||
assertEquals("", card.note);
|
||||
assertEquals(null, card.expiry);
|
||||
assertEquals(new BigDecimal("0"), card.balance);
|
||||
assertEquals(null, card.balanceType);
|
||||
assertEquals("(01)09010374000019(21)02097564604859211217(10)01231287693", card.cardId);
|
||||
assertEquals(null, card.barcodeId);
|
||||
assertEquals(BarcodeFormat.RSS_EXPANDED, card.barcodeType);
|
||||
assertEquals(0, card.starStatus);
|
||||
|
||||
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 3, true));
|
||||
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 3, false));
|
||||
|
||||
TestHelpers.getEmptyDb(activity);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void importVoucherVault() throws IOException, FormatException, JSONException, ParseException {
|
||||
String jsonText = "[\n" +
|
||||
@@ -1153,8 +1211,8 @@ public class ImportExportTest
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(jsonText.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// Import the Voucher Vault data
|
||||
boolean result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inputStream, DataFormat.VoucherVault);
|
||||
assertTrue(result);
|
||||
ImportExportResult result = MultiFormatImporter.importData(activity.getApplicationContext(), db, inputStream, DataFormat.VoucherVault, null);
|
||||
assertEquals(ImportExportResult.Success, result);
|
||||
assertEquals(2, db.getLoyaltyCardCount());
|
||||
|
||||
LoyaltyCard card = db.getLoyaltyCard(1);
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Color;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
@@ -29,11 +28,9 @@ import androidx.preference.PreferenceManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import protect.card_locker.preferences.Settings;
|
||||
|
||||
import static android.os.Looper.getMainLooper;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.robolectric.Shadows.shadowOf;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(sdk = 23)
|
||||
|
||||
@@ -9,9 +9,7 @@ import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
@@ -57,7 +55,6 @@ import static android.os.Looper.getMainLooper;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.robolectric.Shadows.shadowOf;
|
||||
|
||||
|
||||
@@ -3,11 +3,9 @@ package protect.card_locker;
|
||||
import android.app.Activity;
|
||||
import android.content.ComponentName;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Color;
|
||||
import android.view.Menu;
|
||||
import android.view.View;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
@@ -20,7 +18,6 @@ import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.android.controller.ActivityController;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadows.ShadowActivity;
|
||||
import org.w3c.dom.Text;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
|
||||
Binary file not shown.
BIN
app/src/test/res/protect/card_locker/stocard-back.jpg
Normal file
BIN
app/src/test/res/protect/card_locker/stocard-back.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
BIN
app/src/test/res/protect/card_locker/stocard-front.jpg
Normal file
BIN
app/src/test/res/protect/card_locker/stocard-front.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
@@ -9,7 +9,7 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.1.3'
|
||||
classpath 'com.android.tools.build:gradle:4.2.2'
|
||||
classpath 'gradle.plugin.com.github.spotbugs.snom:spotbugs-gradle-plugin:4.7.0'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
|
||||
Reference in New Issue
Block a user