diff --git a/app/build.gradle b/app/build.gradle index de466ed48..44390ab57 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -51,6 +51,7 @@ dependencies { compile 'com.google.zxing:core:3.3.0' compile 'org.apache.commons:commons-csv:1.5' compile 'com.android.support.constraint:constraint-layout:1.0.2' + compile 'com.jaredrummler:colorpicker:1.0.2' compile group: 'com.google.guava', name: 'guava', version: '20.0' compile 'com.github.apl-devs:appintro:v4.2.0' testCompile 'junit:junit:4.12' diff --git a/app/src/main/java/protect/card_locker/CsvDatabaseExporter.java b/app/src/main/java/protect/card_locker/CsvDatabaseExporter.java index 17d8046b4..a5aac91f0 100644 --- a/app/src/main/java/protect/card_locker/CsvDatabaseExporter.java +++ b/app/src/main/java/protect/card_locker/CsvDatabaseExporter.java @@ -23,6 +23,8 @@ public class CsvDatabaseExporter implements DatabaseExporter DBHelper.LoyaltyCardDbIds.STORE, DBHelper.LoyaltyCardDbIds.NOTE, DBHelper.LoyaltyCardDbIds.CARD_ID, + DBHelper.LoyaltyCardDbIds.HEADER_COLOR, + DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR, DBHelper.LoyaltyCardDbIds.BARCODE_TYPE); Cursor cursor = db.getLoyaltyCardCursor(); @@ -35,6 +37,8 @@ public class CsvDatabaseExporter implements DatabaseExporter card.store, card.note, card.cardId, + card.headerColor, + card.headerTextColor, card.barcodeType); if(Thread.currentThread().isInterrupted()) diff --git a/app/src/main/java/protect/card_locker/CsvDatabaseImporter.java b/app/src/main/java/protect/card_locker/CsvDatabaseImporter.java index 6637f8a12..f4de13790 100644 --- a/app/src/main/java/protect/card_locker/CsvDatabaseImporter.java +++ b/app/src/main/java/protect/card_locker/CsvDatabaseImporter.java @@ -130,6 +130,16 @@ public class CsvDatabaseImporter implements DatabaseImporter throw new FormatException("No barcode type listed, but is required"); } - helper.insertLoyaltyCard(database, id, store, note, cardId, barcodeType); + Integer headerColor = null; + Integer headerTextColor = null; + + if(record.isMapped(DBHelper.LoyaltyCardDbIds.HEADER_COLOR) && + record.isMapped(DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR)) + { + headerColor = extractInt(DBHelper.LoyaltyCardDbIds.HEADER_COLOR, record); + headerTextColor = extractInt(DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR, record); + } + + helper.insertLoyaltyCard(database, id, store, note, cardId, barcodeType, headerColor, headerTextColor); } } diff --git a/app/src/main/java/protect/card_locker/DBHelper.java b/app/src/main/java/protect/card_locker/DBHelper.java index 034cbea81..a7c5ad471 100644 --- a/app/src/main/java/protect/card_locker/DBHelper.java +++ b/app/src/main/java/protect/card_locker/DBHelper.java @@ -11,7 +11,7 @@ public class DBHelper extends SQLiteOpenHelper { public static final String DATABASE_NAME = "LoyaltyCards.db"; public static final int ORIGINAL_DATABASE_VERSION = 1; - public static final int DATABASE_VERSION = 2; + public static final int DATABASE_VERSION = 3; static class LoyaltyCardDbIds { @@ -19,6 +19,8 @@ public class DBHelper extends SQLiteOpenHelper public static final String ID = "_id"; public static final String STORE = "store"; public static final String NOTE = "note"; + public static final String HEADER_COLOR = "headercolor"; + public static final String HEADER_TEXT_COLOR = "headertextcolor"; public static final String CARD_ID = "cardid"; public static final String BARCODE_TYPE = "barcodetype"; } @@ -36,6 +38,8 @@ public class DBHelper extends SQLiteOpenHelper LoyaltyCardDbIds.ID + " INTEGER primary key autoincrement," + LoyaltyCardDbIds.STORE + " TEXT not null," + LoyaltyCardDbIds.NOTE + " TEXT not null," + + LoyaltyCardDbIds.HEADER_COLOR + " INTEGER," + + LoyaltyCardDbIds.HEADER_TEXT_COLOR + " INTEGER," + LoyaltyCardDbIds.CARD_ID + " TEXT not null," + LoyaltyCardDbIds.BARCODE_TYPE + " TEXT not null)"); } @@ -49,10 +53,20 @@ public class DBHelper extends SQLiteOpenHelper db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE + " ADD COLUMN " + LoyaltyCardDbIds.NOTE + " TEXT not null default ''"); } + + // Upgrade from version 2 to version 3 + if(oldVersion < 3 && newVersion >= 3) + { + db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE + + " ADD COLUMN " + LoyaltyCardDbIds.HEADER_COLOR + " INTEGER"); + db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE + + " ADD COLUMN " + LoyaltyCardDbIds.HEADER_TEXT_COLOR + " INTEGER"); + } } public long insertLoyaltyCard(final String store, final String note, final String cardId, - final String barcodeType) + final String barcodeType, final Integer headerColor, + final Integer headerTextColor) { SQLiteDatabase db = getWritableDatabase(); ContentValues contentValues = new ContentValues(); @@ -60,13 +74,16 @@ public class DBHelper extends SQLiteOpenHelper contentValues.put(LoyaltyCardDbIds.NOTE, note); contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId); contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType); + contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor); + contentValues.put(LoyaltyCardDbIds.HEADER_TEXT_COLOR, headerTextColor); final long newId = db.insert(LoyaltyCardDbIds.TABLE, null, contentValues); return newId; } public boolean insertLoyaltyCard(final SQLiteDatabase db, final int id, final String store, final String note, final String cardId, - final String barcodeType) + final String barcodeType, final Integer headerColor, + final Integer headerTextColor) { ContentValues contentValues = new ContentValues(); contentValues.put(LoyaltyCardDbIds.ID, id); @@ -74,13 +91,16 @@ public class DBHelper extends SQLiteOpenHelper contentValues.put(LoyaltyCardDbIds.NOTE, note); contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId); contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType); + contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor); + contentValues.put(LoyaltyCardDbIds.HEADER_TEXT_COLOR, headerTextColor); final long newId = db.insert(LoyaltyCardDbIds.TABLE, null, contentValues); return (newId != -1); } public boolean updateLoyaltyCard(final int id, final String store, final String note, - final String cardId, final String barcodeType) + final String cardId, final String barcodeType, + final Integer headerColor, final Integer headerTextColor) { SQLiteDatabase db = getWritableDatabase(); ContentValues contentValues = new ContentValues(); @@ -88,6 +108,8 @@ public class DBHelper extends SQLiteOpenHelper contentValues.put(LoyaltyCardDbIds.NOTE, note); contentValues.put(LoyaltyCardDbIds.CARD_ID, cardId); contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType); + contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor); + contentValues.put(LoyaltyCardDbIds.HEADER_TEXT_COLOR, headerTextColor); int rowsUpdated = db.update(LoyaltyCardDbIds.TABLE, contentValues, LoyaltyCardDbIds.ID + "=?", new String[]{Integer.toString(id)}); diff --git a/app/src/main/java/protect/card_locker/LetterBitmap.java b/app/src/main/java/protect/card_locker/LetterBitmap.java index a15fbbaa3..2afc75f2e 100644 --- a/app/src/main/java/protect/card_locker/LetterBitmap.java +++ b/app/src/main/java/protect/card_locker/LetterBitmap.java @@ -4,7 +4,6 @@ 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; @@ -44,20 +43,35 @@ class LetterBitmap * @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 + * @param backgroundColor (optional) color to use for background. + * @param textColor (optional) color to use for text. */ - public LetterBitmap(Context context, String displayName, String key, int tileLetterFontSize, int width, int height) + public LetterBitmap(Context context, String displayName, String key, int tileLetterFontSize, + int width, int height, Integer backgroundColor, Integer textColor) { - final Resources res = context.getResources(); - TextPaint paint = new TextPaint(); paint.setTypeface(Typeface.create("sans-serif-light", Typeface.BOLD)); - paint.setColor(Color.WHITE); + + if(textColor != null) + { + paint.setColor(textColor); + } + else + { + 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(); + if(backgroundColor == null) + { + mColor = getDefaultColor(context, key); + } + else + { + mColor = backgroundColor; + } mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); String firstChar = displayName.substring(0, 1); @@ -101,11 +115,26 @@ class LetterBitmap * @return A new or previously chosen color for key used as the * tile background color */ - private int pickColor(String key, TypedArray colors) + private static 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); } + + /** + * Determine the color which the letter tile will use if no default + * color is provided. + */ + public static int getDefaultColor(Context context, String key) + { + final Resources res = context.getResources(); + + TypedArray colors = res.obtainTypedArray(R.array.letter_tile_colors); + int color = pickColor(key, colors); + colors.recycle(); + + return color; + } } \ No newline at end of file diff --git a/app/src/main/java/protect/card_locker/LoyaltyCard.java b/app/src/main/java/protect/card_locker/LoyaltyCard.java index 591491344..a1dac3a69 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCard.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCard.java @@ -1,6 +1,7 @@ package protect.card_locker; import android.database.Cursor; +import android.support.annotation.Nullable; public class LoyaltyCard { @@ -10,13 +11,22 @@ public class LoyaltyCard public final String cardId; public final String barcodeType; - public LoyaltyCard(final int id, final String store, final String note, final String cardId, final String barcodeType) + @Nullable + public final Integer headerColor; + + @Nullable + public final Integer headerTextColor; + + public LoyaltyCard(final int id, final String store, final String note, final String cardId, + final String barcodeType, final Integer headerColor, final Integer headerTextColor) { this.id = id; this.store = store; this.note = note; this.cardId = cardId; this.barcodeType = barcodeType; + this.headerColor = headerColor; + this.headerTextColor = headerTextColor; } public static LoyaltyCard toLoyaltyCard(Cursor cursor) @@ -27,6 +37,22 @@ public class LoyaltyCard String cardId = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID)); String barcodeType = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE)); - return new LoyaltyCard(id, store, note, cardId, barcodeType); + int headerColorColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_COLOR); + int headerTextColorColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR); + + Integer headerColor = null; + Integer headerTextColor = null; + + if(cursor.isNull(headerColorColumn) == false) + { + headerColor = cursor.getInt(headerColorColumn); + } + + if(cursor.isNull(headerTextColorColumn) == false) + { + headerTextColor = cursor.getInt(headerTextColorColumn); + } + + return new LoyaltyCard(id, store, note, cardId, barcodeType, headerColor, headerTextColor); } } diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardCursorAdapter.java b/app/src/main/java/protect/card_locker/LoyaltyCardCursorAdapter.java index 7950277e2..b6b463275 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardCursorAdapter.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardCursorAdapter.java @@ -52,7 +52,11 @@ class LoyaltyCardCursorAdapter extends CursorAdapter 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); + + Integer letterBackgroundColor = loyaltyCard.headerColor; + Integer letterTextColor = loyaltyCard.headerTextColor; + LetterBitmap letterBitmap = new LetterBitmap(context, loyaltyCard.store, loyaltyCard.store, + tileLetterFontSize, pixelSize, pixelSize, letterBackgroundColor, letterTextColor); thumbnail.setImageBitmap(letterBitmap.getLetterTile()); } } diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java index 915d7e5c1..884a097a5 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java @@ -3,6 +3,8 @@ package protect.card_locker; import android.app.Activity; import android.content.DialogInterface; import android.content.Intent; +import android.content.res.TypedArray; +import android.graphics.Color; import android.os.Build; import android.os.Bundle; import android.support.design.widget.Snackbar; @@ -24,6 +26,8 @@ import android.widget.Toast; import com.google.zxing.BarcodeFormat; import com.google.zxing.integration.android.IntentIntegrator; import com.google.zxing.integration.android.IntentResult; +import com.jaredrummler.android.colorpicker.ColorPickerDialog; +import com.jaredrummler.android.colorpicker.ColorPickerDialogListener; public class LoyaltyCardEditActivity extends AppCompatActivity { @@ -33,6 +37,10 @@ public class LoyaltyCardEditActivity extends AppCompatActivity EditText storeFieldEdit; EditText noteFieldEdit; + ImageView headingColorSample; + Button headingColorSelectButton; + ImageView headingStoreTextColorSample; + Button headingStoreTextColorSelectButton; TextView cardIdFieldView; View cardIdDivider; View cardIdTableRow; @@ -46,6 +54,8 @@ public class LoyaltyCardEditActivity extends AppCompatActivity int loyaltyCardId; boolean updateLoyaltyCard; + Integer headingColorValue = null; + Integer headingStoreTextColorValue = null; DBHelper db; @@ -79,6 +89,10 @@ public class LoyaltyCardEditActivity extends AppCompatActivity storeFieldEdit = findViewById(R.id.storeNameEdit); noteFieldEdit = findViewById(R.id.noteEdit); + headingColorSample = findViewById(R.id.headingColorSample); + headingColorSelectButton = findViewById(R.id.headingColorSelectButton); + headingStoreTextColorSample = findViewById(R.id.headingStoreTextColorSample); + headingStoreTextColorSelectButton = findViewById(R.id.headingStoreTextColorSelectButton); cardIdFieldView = findViewById(R.id.cardIdView); cardIdDivider = findViewById(R.id.cardIdDivider); cardIdTableRow = findViewById(R.id.cardIdTableRow); @@ -142,6 +156,26 @@ public class LoyaltyCardEditActivity extends AppCompatActivity barcodeTypeField.setText(loyaltyCard.barcodeType); } + if(headingColorValue == null) + { + headingColorValue = loyaltyCard.headerColor; + if(headingColorValue == null) + { + headingColorValue = LetterBitmap.getDefaultColor(this, loyaltyCard.store); + } + headingColorSample.setBackgroundColor(headingColorValue); + } + + if(headingStoreTextColorValue == null) + { + headingStoreTextColorValue = loyaltyCard.headerTextColor; + if(headingStoreTextColorValue == null) + { + headingStoreTextColorValue = Color.WHITE; + } + headingStoreTextColorSample.setBackgroundColor(headingStoreTextColorValue); + } + setTitle(R.string.editCardTitle); } else @@ -149,6 +183,26 @@ public class LoyaltyCardEditActivity extends AppCompatActivity setTitle(R.string.addCardTitle); } + if(headingColorValue == null) + { + // Select a random color to start out with. + TypedArray colors = getResources().obtainTypedArray(R.array.letter_tile_colors); + final int color = (int)(Math.random() * colors.length()); + headingColorValue = colors.getColor(color, Color.BLACK); + colors.recycle(); + + headingColorSample.setBackgroundColor(headingColorValue); + } + + if(headingStoreTextColorValue == null) + { + headingStoreTextColorValue = Color.WHITE; + headingStoreTextColorSample.setBackgroundColor(headingStoreTextColorValue); + } + + headingColorSelectButton.setOnClickListener(new ColorSelectListener(headingColorValue, true)); + headingStoreTextColorSelectButton.setOnClickListener(new ColorSelectListener(headingStoreTextColorValue, false)); + if(cardIdFieldView.getText().length() > 0 && barcodeTypeField.getText().length() > 0) { String formatString = barcodeTypeField.getText().toString(); @@ -238,6 +292,50 @@ public class LoyaltyCardEditActivity extends AppCompatActivity } } + class ColorSelectListener implements View.OnClickListener + { + final int defaultColor; + final boolean isBackgroundColor; + + ColorSelectListener(int defaultColor, boolean isBackgroundColor) + { + this.defaultColor = defaultColor; + this.isBackgroundColor = isBackgroundColor; + } + + @Override + public void onClick(View v) + { + ColorPickerDialog dialog = ColorPickerDialog.newBuilder().setColor(defaultColor).create(); + dialog.setColorPickerDialogListener(new ColorPickerDialogListener() + { + @Override + public void onColorSelected(int dialogId, int color) + { + Toast.makeText(LoyaltyCardEditActivity.this, "Color: " + Integer.toHexString(color), Toast.LENGTH_LONG).show(); + + if(isBackgroundColor) + { + headingColorSample.setBackgroundColor(color); + headingColorValue = color; + } + else + { + headingStoreTextColorSample.setBackgroundColor(color); + headingStoreTextColorValue = color; + } + } + + @Override + public void onDialogDismissed(int dialogId) + { + // Nothing to do, no change made + } + }); + dialog.show(getFragmentManager(), "color-picker-dialog"); + } + } + private void doSave() { String store = storeFieldEdit.getText().toString(); @@ -259,12 +357,12 @@ public class LoyaltyCardEditActivity extends AppCompatActivity if(updateLoyaltyCard) { - db.updateLoyaltyCard(loyaltyCardId, store, note, cardId, barcodeType); + db.updateLoyaltyCard(loyaltyCardId, store, note, cardId, barcodeType, headingColorValue, headingStoreTextColorValue); Log.i(TAG, "Updated " + loyaltyCardId + " to " + cardId); } else { - loyaltyCardId = (int)db.insertLoyaltyCard(store, note, cardId, barcodeType); + loyaltyCardId = (int)db.insertLoyaltyCard(store, note, cardId, barcodeType, headingColorValue, headingStoreTextColorValue); } finish(); diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java b/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java index ac62911fb..dba1573b9 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java @@ -1,15 +1,12 @@ package protect.card_locker; -import android.app.Activity; -import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.graphics.Color; 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; @@ -19,8 +16,6 @@ import android.view.View; import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowManager; -import android.widget.Button; -import android.widget.EditText; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; @@ -128,10 +123,28 @@ public class LoyaltyCardViewActivity extends AppCompatActivity storeName.setText(loyaltyCard.store); - 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); - collapsingToolbarLayout.setBackgroundColor(letterBitmap.getBackgroundColor()); + int textColor; + if(loyaltyCard.headerTextColor != null) + { + textColor = loyaltyCard.headerTextColor; + } + else + { + textColor = Color.WHITE; + } + storeName.setTextColor(textColor); + + int backgroundHeaderColor; + if(loyaltyCard.headerColor != null) + { + backgroundHeaderColor = loyaltyCard.headerColor; + } + else + { + backgroundHeaderColor = LetterBitmap.getDefaultColor(this, loyaltyCard.store); + } + + collapsingToolbarLayout.setBackgroundColor(backgroundHeaderColor); if(barcodeImage.getHeight() == 0) { diff --git a/app/src/main/java/protect/card_locker/MainActivity.java b/app/src/main/java/protect/card_locker/MainActivity.java index 26ed92422..01f94bb9a 100644 --- a/app/src/main/java/protect/card_locker/MainActivity.java +++ b/app/src/main/java/protect/card_locker/MainActivity.java @@ -181,14 +181,14 @@ public class MainActivity extends AppCompatActivity private void displayAboutDialog() { - final Map USED_LIBRARIES = ImmutableMap.of - ( - "Commons CSV", "https://commons.apache.org/proper/commons-csv/", - "Guava", "https://github.com/google/guava", - "ZXing", "https://github.com/zxing/zxing", - "ZXing Android Embedded", "https://github.com/journeyapps/zxing-android-embedded", - "AppIntro", "https://github.com/apl-devs/AppIntro" - ); + final Map USED_LIBRARIES = new ImmutableMap.Builder() + .put("Commons CSV", "https://commons.apache.org/proper/commons-csv/") + .put("Guava", "https://github.com/google/guava") + .put("ZXing", "https://github.com/zxing/zxing") + .put("ZXing Android Embedded", "https://github.com/journeyapps/zxing-android-embedded") + .put("AppIntro", "https://github.com/apl-devs/AppIntro") + .put("Color Picker", "https://github.com/jaredrummler/ColorPicker") + .build(); final Map USED_ASSETS = ImmutableMap.of ( diff --git a/app/src/main/res/layout/loyalty_card_edit_activity.xml b/app/src/main/res/layout/loyalty_card_edit_activity.xml index f03f5395f..04003fd0b 100644 --- a/app/src/main/res/layout/loyalty_card_edit_activity.xml +++ b/app/src/main/res/layout/loyalty_card_edit_activity.xml @@ -129,6 +129,148 @@ android:background="@color/inputBorder" /> + + + + + + + + + + + + + +