Compare commits

...

23 Commits
v0.18 ... v0.19

Author SHA1 Message Date
Branden Archer
b8a36e3c45 Merge pull request #191 from brarcher/pre-v0.19
Update for v0.19
2018-02-01 08:02:20 -05:00
Branden Archer
60deaae8d5 Update for v0.19 2018-02-01 07:54:48 -05:00
Branden Archer
a71da8d6dc Merge pull request #190 from brarcher/card-layout
Improve card view layout
2018-01-31 23:39:37 -05:00
Branden Archer
dbbbd128dc Update intro and metadata graphics 2018-01-31 23:19:19 -05:00
Branden Archer
3275279501 Remove unnecessary casts from findViewById 2018-01-31 22:49:31 -05:00
Branden Archer
2f6516ffb9 Display note on separate line, omit card id on card list
The card id was of dubious value, because the main screen
is really for selecting the card. The note may have more value,
as it may specify info about the card.
2018-01-31 22:49:31 -05:00
Branden Archer
41c8b78275 Move store name text size to dimens.xml 2018-01-31 22:49:31 -05:00
Branden Archer
6ed96393d5 Change layout of single card view
This changes the layout when viewing a single card. The new layout
will have a colored area at the top which displays the first letter
of the company, and after that the barcode. The note will be displayed
on the card list screen and is presently omitted here.

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

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

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

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

Build tools 27.0.1 has a mitigation. Avoiding aapt2 also avoids hitting
the bug.
2018-01-24 08:37:53 -05:00
Branden Archer
bffeca9033 Merge pull request #182 from brarcher/changelog
Update Changelog
2018-01-19 23:46:15 -05:00
Branden Archer
5a88afb9f3 Update Changelog 2018-01-19 23:39:25 -05:00
35 changed files with 962 additions and 412 deletions

View File

@@ -1,3 +1,15 @@
## v0.18.1 (2018-01-24)
Changes:
- Workaround crash during install on some Android versions (likely Android 5 and below). (https://github.com/brarcher/loyalty-card-locker/pull/184)
## v0.18 (2018-01-19)
Changes:
- Fix crash when importing certain types of corrupted CSV files. (https://github.com/brarcher/loyalty-card-locker/pull/177)
- Fix importing backups directly from the file system. (https://github.com/brarcher/loyalty-card-locker/pull/180)
- Fix importing backups from certain types of content providers. (https://github.com/brarcher/loyalty-card-locker/pull/179)
## v0.17 (2018-01-11)
Changes:

91
CONTRIBUTING.md Normal file
View File

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

View File

@@ -13,8 +13,8 @@ android {
applicationId "protect.card_locker"
minSdkVersion 17
targetSdkVersion 27
versionCode 19
versionName "0.18"
versionCode 21
versionName "0.19"
}
buildTypes {
release {

View File

@@ -35,6 +35,13 @@
<activity
android:name=".LoyaltyCardViewActivity"
android:theme="@style/AppTheme.NoActionBar"
android:label=""
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateHidden"
android:exported="true"/>
<activity
android:name=".LoyaltyCardEditActivity"
android:theme="@style/AppTheme.NoActionBar"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateHidden"
android:exported="true"/>

View File

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

View File

@@ -29,7 +29,7 @@ public class CardShortcutConfigure extends AppCompatActivity
setResult(RESULT_CANCELED);
setContentView(R.layout.main_activity);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
Toolbar toolbar = findViewById(R.id.toolbar);
toolbar.setVisibility(View.GONE);
final DBHelper db = new DBHelper(this);
@@ -41,7 +41,7 @@ public class CardShortcutConfigure extends AppCompatActivity
finish();
}
final ListView cardList = (ListView) findViewById(R.id.list);
final ListView cardList = findViewById(R.id.list);
cardList.setVisibility(View.VISIBLE);
Cursor cardCursor = db.getLoyaltyCardCursor();

View File

@@ -49,7 +49,7 @@ public class ImportExportActivity extends AppCompatActivity
{
super.onCreate(savedInstanceState);
setContentView(R.layout.import_export_activity);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
@@ -72,7 +72,7 @@ public class ImportExportActivity extends AppCompatActivity
}
Button exportButton = (Button)findViewById(R.id.exportButton);
Button exportButton = findViewById(R.id.exportButton);
exportButton.setOnClickListener(new View.OnClickListener()
{
@Override
@@ -86,7 +86,7 @@ public class ImportExportActivity extends AppCompatActivity
// Check that there is an activity that can bring up a file chooser
final Intent intentPickAction = new Intent(Intent.ACTION_PICK);
Button importFilesystem = (Button) findViewById(R.id.importOptionFilesystemButton);
Button importFilesystem = findViewById(R.id.importOptionFilesystemButton);
importFilesystem.setOnClickListener(new View.OnClickListener()
{
@Override
@@ -110,7 +110,7 @@ public class ImportExportActivity extends AppCompatActivity
intentGetContentAction.addCategory(Intent.CATEGORY_OPENABLE);
intentGetContentAction.setType("*/*");
Button importApplication = (Button) findViewById(R.id.importOptionApplicationButton);
Button importApplication = findViewById(R.id.importOptionApplicationButton);
importApplication.setOnClickListener(new View.OnClickListener()
{
@Override
@@ -131,7 +131,7 @@ public class ImportExportActivity extends AppCompatActivity
// This option, to import from the fixed location, should always be present
Button importButton = (Button)findViewById(R.id.importOptionFixedButton);
Button importButton = findViewById(R.id.importOptionFixedButton);
importButton.setOnClickListener(new View.OnClickListener()
{
@Override

View File

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

View File

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

View File

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

View File

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

View File

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

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 104 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

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

View File

@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/coordinator_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fitsSystemWindows="true"
>
<FrameLayout
android:clipChildren="false"
android:clipToPadding="false"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="@+id/barcode"
android:layout_width="fill_parent"
android:layout_height="0.0px"
android:layout_marginTop="20.0dip"
android:layout_marginBottom="10.0dip"
android:layout_weight="0.1"
android:layout_gravity="center_horizontal"
android:contentDescription="@string/barcodeImageDescription"/>
<TextView
android:id="@+id/cardIdView"
android:textSize="42.0sp"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_marginLeft="10.0dip"
android:layout_marginRight="10.0dip"
android:maxLines="1"
android:layout_weight="0.1"
android:textAlignment="center"
android:ellipsize="end"
android:text="1234567890"/>
</LinearLayout>
<View
android:id="@+id/drop_shadow_actionbar"
android:layout_width="fill_parent"
android:layout_height="5.0dip"
android:layout_gravity="top"/>
</FrameLayout>
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar_layout"
android:background="@android:color/transparent"
android:clipChildren="false"
android:clipToPadding="false"
android:layout_width="fill_parent"
android:layout_height="224.0dip"
android:weightSum="1.0"
android:fitsSystemWindows="true">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsingToolbarLayout"
android:clipChildren="false"
android:clipToPadding="false"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:minHeight="56.0dip"
android:layout_weight="1.0"
app:expandedTitleMarginStart="48dp"
app:expandedTitleMarginEnd="64dp"
app:contentScrim="?colorPrimary"
app:expandedTitleGravity="top">
<ImageView
android:id="@+id/storeLogo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="?actionBarSize"
app:layout_collapseMode="parallax"
android:fitsSystemWindows="true"/>
<android.support.v7.widget.Toolbar
android:id="@id/toolbar"
android:background="@android:color/transparent"
android:theme="@style/CardView.ActionBarTheme"
android:layout_width="fill_parent"
android:layout_height="?actionBarSize"
app:contentInsetStart="72.0dip"
app:layout_collapseMode="pin" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>

View File

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

View File

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

View File

@@ -85,6 +85,8 @@
<string name="copy_to_clipboard_toast">Card ID copied to clipboard</string>
<string name="thumbnailDescription">Thumbnail for card</string>
<string name="startIntro">Start Intro</string>
<string name="intro1Title">Welcome to Loyalty Card Keychain\n</string>
<string name="intro1Description">Manage your barcode-based store/loyalty cards on your phone!\n\n</string>

View File

@@ -17,6 +17,11 @@
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>
<style name="CardView.ActionBarTheme" parent="@style/ThemeOverlay.AppCompat.ActionBar">
<!-- THIS is where you can color the arrow! -->
<item name="colorControlNormal">@android:color/white</item>
</style>
<style name="AppTheme.TextView.NoData" parent="AppTheme">
<item name="android:layout_centerHorizontal">true</item>
<item name="android:layout_centerVertical">true</item>

View File

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

View File

@@ -9,7 +9,6 @@ import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
@@ -103,10 +102,10 @@ public class LoyaltyCardViewActivityTest
assertEquals(1, db.getLoyaltyCardCount());
}
final EditText storeField = (EditText) activity.findViewById(R.id.storeNameEdit);
final EditText noteField = (EditText) activity.findViewById(R.id.noteEdit);
final TextView cardIdField = (TextView) activity.findViewById(R.id.cardIdView);
final TextView barcodeTypeField = (TextView) activity.findViewById(R.id.barcodeType);
final EditText storeField = activity.findViewById(R.id.storeNameEdit);
final EditText noteField = activity.findViewById(R.id.noteEdit);
final TextView cardIdField = activity.findViewById(R.id.cardIdView);
final TextView barcodeTypeField = activity.findViewById(R.id.barcodeType);
storeField.setText(store);
noteField.setText(note);
@@ -133,7 +132,7 @@ public class LoyaltyCardViewActivityTest
private void captureBarcodeWithResult(final Activity activity, final int buttonId, final boolean success) throws IOException
{
// Start image capture
final Button captureButton = (Button) activity.findViewById(buttonId);
final Button captureButton = activity.findViewById(buttonId);
captureButton.performClick();
ShadowActivity.IntentForResult intentForResult = shadowOf(activity).peekNextStartedActivityForResult();
@@ -178,27 +177,34 @@ public class LoyaltyCardViewActivityTest
private void checkAllFields(final Activity activity, ViewMode mode,
final String store, final String note, final String cardId, final String barcodeType)
{
int captureVisibility = (mode == ViewMode.UPDATE_CARD || mode == ViewMode.ADD_CARD) ? View.VISIBLE : View.GONE;
if(mode == ViewMode.VIEW_CARD)
{
checkFieldProperties(activity, R.id.cardIdView, View.VISIBLE, cardId);
}
else
{
int captureVisibility = (mode == ViewMode.UPDATE_CARD || mode == ViewMode.ADD_CARD) ? View.VISIBLE : View.GONE;
int viewVisibility = (mode == ViewMode.VIEW_CARD) ? View.VISIBLE : View.GONE;
int editVisibility = (mode != ViewMode.VIEW_CARD) ? View.VISIBLE : View.GONE;
int viewVisibility = View.GONE;
int editVisibility = View.VISIBLE;
checkFieldProperties(activity, R.id.storeNameEdit, editVisibility, store);
checkFieldProperties(activity, R.id.storeNameView, viewVisibility, store);
checkFieldProperties(activity, R.id.noteEdit, editVisibility, note);
checkFieldProperties(activity, R.id.noteView, viewVisibility, note);
checkFieldProperties(activity, R.id.cardIdView, View.VISIBLE, cardId);
checkFieldProperties(activity, R.id.cardIdDivider, cardId.isEmpty() ? View.GONE : View.VISIBLE, null);
checkFieldProperties(activity, R.id.cardIdTableRow, cardId.isEmpty() ? View.GONE : View.VISIBLE, null);
checkFieldProperties(activity, R.id.barcodeType, View.GONE, barcodeType);
checkFieldProperties(activity, R.id.captureButton, captureVisibility, null);
checkFieldProperties(activity, R.id.barcode, View.VISIBLE, null);
checkFieldProperties(activity, R.id.storeNameEdit, editVisibility, store);
checkFieldProperties(activity, R.id.storeNameView, viewVisibility, store);
checkFieldProperties(activity, R.id.noteEdit, editVisibility, note);
checkFieldProperties(activity, R.id.noteView, viewVisibility, note);
checkFieldProperties(activity, R.id.cardIdView, View.VISIBLE, cardId);
checkFieldProperties(activity, R.id.cardIdDivider, cardId.isEmpty() ? View.GONE : View.VISIBLE, null);
checkFieldProperties(activity, R.id.cardIdTableRow, cardId.isEmpty() ? View.GONE : View.VISIBLE, null);
checkFieldProperties(activity, R.id.barcodeType, View.GONE, barcodeType);
checkFieldProperties(activity, R.id.captureButton, captureVisibility, null);
checkFieldProperties(activity, R.id.barcode, View.VISIBLE, null);
}
}
@Test
public void startWithoutParametersCheckFieldsAvailable()
{
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardViewActivity.class).create();
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardEditActivity.class).create();
activityController.start();
activityController.visible();
activityController.resume();
@@ -211,7 +217,7 @@ public class LoyaltyCardViewActivityTest
@Test
public void startWithoutParametersCannotCreateLoyaltyCard()
{
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardViewActivity.class).create();
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardEditActivity.class).create();
activityController.start();
activityController.visible();
activityController.resume();
@@ -221,9 +227,9 @@ public class LoyaltyCardViewActivityTest
DBHelper db = new DBHelper(activity);
assertEquals(0, db.getLoyaltyCardCount());
final EditText storeField = (EditText) activity.findViewById(R.id.storeNameEdit);
final EditText noteField = (EditText) activity.findViewById(R.id.noteEdit);
final TextView cardIdField = (TextView) activity.findViewById(R.id.cardIdView);
final EditText storeField = activity.findViewById(R.id.storeNameEdit);
final EditText noteField = activity.findViewById(R.id.noteEdit);
final TextView cardIdField = activity.findViewById(R.id.cardIdView);
shadowActivity.clickMenuItem(R.id.action_save);
assertEquals(0, db.getLoyaltyCardCount());
@@ -244,7 +250,7 @@ public class LoyaltyCardViewActivityTest
@Test
public void startWithoutParametersBack()
{
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardViewActivity.class).create();
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardEditActivity.class).create();
activityController.start();
activityController.visible();
activityController.resume();
@@ -261,7 +267,7 @@ public class LoyaltyCardViewActivityTest
{
registerMediaStoreIntentHandler();
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardViewActivity.class).create();
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardEditActivity.class).create();
activityController.start();
activityController.visible();
activityController.resume();
@@ -282,7 +288,7 @@ public class LoyaltyCardViewActivityTest
@Test
public void startWithoutParametersCaptureBarcodeFailure() throws IOException
{
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardViewActivity.class).create();
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardEditActivity.class).create();
activityController.start();
activityController.visible();
activityController.resume();
@@ -300,7 +306,7 @@ public class LoyaltyCardViewActivityTest
@Test
public void startWithoutParametersCaptureBarcodeCancel() throws IOException
{
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardViewActivity.class).create();
ActivityController activityController = Robolectric.buildActivity(LoyaltyCardEditActivity.class).create();
activityController.start();
activityController.visible();
activityController.resume();
@@ -326,18 +332,22 @@ public class LoyaltyCardViewActivityTest
final Bundle bundle = new Bundle();
bundle.putInt("id", 1);
Class clazz;
if(editMode)
{
bundle.putBoolean("update", true);
clazz = LoyaltyCardEditActivity.class;
}
else
{
bundle.putBoolean("view", true);
clazz = LoyaltyCardViewActivity.class;
}
intent.putExtras(bundle);
return Robolectric.buildActivity(LoyaltyCardViewActivity.class).withIntent(intent).create();
return Robolectric.buildActivity(clazz).withIntent(intent).create();
}
@Test
@@ -461,4 +471,22 @@ public class LoyaltyCardViewActivityTest
activityController.stop();
activityController.destroy();
}
@Test
public void startWithoutParametersViewBack()
{
ActivityController activityController = createActivityWithLoyaltyCard(false);
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, BARCODE_TYPE);
activityController.start();
activityController.visible();
activityController.resume();
assertEquals(false, activity.isFinishing());
shadowOf(activity).clickMenuItem(android.R.id.home);
assertEquals(true, activity.isFinishing());
}
}

View File

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

View File

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

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 99 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 44 KiB