Merge branch 'fix/sdk28' of ssh://github.com/TheLastProject/loyalty-card-locker into fix/sdk28

This commit is contained in:
Sylvia van Os
2019-12-27 17:27:48 +01:00
16 changed files with 269 additions and 53 deletions

View File

@@ -90,6 +90,11 @@ class BarcodeImageWriterTask extends AsyncTask<Void, Void, Bitmap>
public Bitmap doInBackground(Void... params)
{
if (cardId.isEmpty())
{
return null;
}
MultiFormatWriter writer = new MultiFormatWriter();
BitMatrix bitMatrix;
try

View File

@@ -116,6 +116,10 @@ public class BarcodeSelectorActivity extends AppCompatActivity
ImageView image = findViewById(barcodeViewMap.get(key));
createBarcodeOption(image, key, s.toString());
}
View noBarcodeButtonView = findViewById(R.id.noBarcode);
setButtonListener(noBarcodeButtonView, s.toString());
noBarcodeButtonView.setEnabled(s.length() > 0);
}
@Override
@@ -134,6 +138,21 @@ public class BarcodeSelectorActivity extends AppCompatActivity
}
}
private void setButtonListener(final View button, final String cardId)
{
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d(TAG, "Selected no barcode");
Intent result = new Intent();
result.putExtra(BARCODE_FORMAT, "");
result.putExtra(BARCODE_CONTENTS, cardId);
BarcodeSelectorActivity.this.setResult(RESULT_OK, result);
finish();
}
});
}
private void createBarcodeOption(final ImageView image, final String formatType, final String cardId)
{
final BarcodeFormat format = BarcodeFormat.valueOf(formatType);

View File

@@ -131,10 +131,6 @@ public class CsvDatabaseImporter implements DatabaseImporter
}
String barcodeType = extractString(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE, record, "");
if(barcodeType.isEmpty())
{
throw new FormatException("No barcode type listed, but is required");
}
Integer headerColor = null;
Integer headerTextColor = null;

View File

@@ -370,7 +370,7 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
return;
}
if(cardId.isEmpty() || barcodeType.isEmpty())
if(cardId.isEmpty())
{
Snackbar.make(cardIdFieldView, R.string.noCardIdError, Snackbar.LENGTH_LONG).show();
return;
@@ -480,7 +480,7 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
}
if(contents != null && contents.isEmpty() == false &&
format != null && format.isEmpty() == false)
format != null)
{
Log.i(TAG, "Read barcode id: " + contents);
Log.i(TAG, "Read format: " + format);

View File

@@ -4,11 +4,15 @@ package protect.card_locker;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.graphics.ColorUtils;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v4.widget.TextViewCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.content.res.AppCompatResources;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.util.TypedValue;
@@ -30,6 +34,7 @@ import protect.card_locker.preferences.Settings;
public class LoyaltyCardViewActivity extends AppCompatActivity
{
private static final String TAG = "CardLocker";
private static final double LUMINANCE_MIDPOINT = 0.5;
TextView cardIdFieldView;
TextView noteView;
@@ -44,6 +49,8 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
ImportURIHelper importURIHelper;
Settings settings;
boolean backgroundNeedsDarkIcons;
private void extractIntentFields(Intent intent)
{
final Bundle b = intent.getExtras();
@@ -51,6 +58,22 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
Log.d(TAG, "View activity: id=" + loyaltyCardId);
}
private Drawable getIcon(int icon, boolean dark)
{
Drawable unwrappedIcon = AppCompatResources.getDrawable(this, icon);
Drawable wrappedIcon = DrawableCompat.wrap(unwrappedIcon);
if(dark)
{
DrawableCompat.setTint(wrappedIcon, Color.BLACK);
}
else
{
DrawableCompat.setTintList(wrappedIcon, null);
}
return wrappedIcon;
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
@@ -118,7 +141,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
}
String formatString = loyaltyCard.barcodeType;
final BarcodeFormat format = BarcodeFormat.valueOf(formatString);
final BarcodeFormat format = !formatString.isEmpty() ? BarcodeFormat.valueOf(formatString) : null;
final String cardIdString = loyaltyCard.cardId;
cardIdFieldView.setText(loyaltyCard.cardId);
@@ -165,37 +188,58 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
collapsingToolbarLayout.setBackgroundColor(backgroundHeaderColor);
if(barcodeImage.getHeight() == 0)
// If the background is very bright, we should use dark icons
backgroundNeedsDarkIcons = (ColorUtils.calculateLuminance(backgroundHeaderColor) > LUMINANCE_MIDPOINT);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null)
{
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);
}
actionBar.setHomeAsUpIndicator(getIcon(R.drawable.ic_arrow_back_white, backgroundNeedsDarkIcons));
}
Log.d(TAG, "ImageView size now known");
new BarcodeImageWriterTask(barcodeImage, cardIdString, format).execute();
}
});
// Make notification area light if dark icons are needed
window.getDecorView().setSystemUiVisibility(backgroundNeedsDarkIcons ? View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR : 0);
// 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);
if(format != null)
{
findViewById(R.id.barcode).setVisibility(View.VISIBLE);
if(barcodeImage.getHeight() == 0)
{
Log.d(TAG, "ImageView size is not known known at start, waiting for load");
// The size of the ImageView is not yet available as it has not
// yet been drawn. Wait for it to be drawn so the size is available.
barcodeImage.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener()
{
@Override
public void onGlobalLayout()
{
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();
}
}
else
{
Log.d(TAG, "ImageView size known known, creating barcode");
new BarcodeImageWriterTask(barcodeImage, cardIdString, format).execute();
findViewById(R.id.barcode).setVisibility(View.GONE);
}
}
@Override
@@ -203,13 +247,18 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
{
getMenuInflater().inflate(R.menu.card_view_menu, menu);
if(settings.getLockBarcodeScreenOrientation())
// Always calculate lockscreen icon, it may need a black color
boolean lockBarcodeScreenOrientation = settings.getLockBarcodeScreenOrientation();
MenuItem item = menu.findItem(R.id.action_lock_unlock);
setOrientatonLock(item, lockBarcodeScreenOrientation);
if(lockBarcodeScreenOrientation)
{
MenuItem item = menu.findItem(R.id.action_lock_unlock);
setOrientatonLock(item, true);
item.setVisible(false);
}
menu.findItem(R.id.action_share).setIcon(getIcon(R.drawable.ic_share_white, backgroundNeedsDarkIcons));
menu.findItem(R.id.action_edit).setIcon(getIcon(R.drawable.ic_mode_edit_white_24dp, backgroundNeedsDarkIcons));
return super.onCreateOptionsMenu(menu);
}
@@ -258,15 +307,16 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
{
if(lock)
{
item.setIcon(R.drawable.ic_lock_outline_white_24dp);
item.setIcon(getIcon(R.drawable.ic_lock_outline_white_24dp, backgroundNeedsDarkIcons));
item.setTitle(R.string.unlockScreen);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
}
else
{
item.setIcon(R.drawable.ic_lock_open_white_24dp);
item.setIcon(getIcon(R.drawable.ic_lock_open_white_24dp, backgroundNeedsDarkIcons));
item.setTitle(R.string.lockScreen);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
}
}
}
}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

View File

@@ -64,6 +64,12 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/barcodesLayout"/>
<Button
android:id="@+id/noBarcode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/barcodeNoBarcode"
android:enabled="false" />
<LinearLayout android:orientation="horizontal"
android:padding="10.0dp"
android:layout_width="fill_parent"
@@ -198,8 +204,4 @@
</LinearLayout>
</LinearLayout>
</ScrollView>
</android.support.design.widget.CoordinatorLayout>

View File

@@ -119,7 +119,7 @@
<EditText
android:id="@+id/noteEdit"
android:inputType="textCapSentences"
android:inputType="textMultiLine"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:padding="@dimen/inputPadding"

View File

@@ -47,7 +47,7 @@
android:layout_height="0dp"
android:layout_marginLeft="10.0dip"
android:layout_marginRight="10.0dip"
app:layout_constraintTop_toBottomOf="@id/centerGuideline"
app:layout_constraintTop_toBottomOf="@+id/barcode"
app:layout_constraintBottom_toTopOf="@+id/noteViewDivider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@@ -84,7 +84,8 @@
app:autoSizeTextType="uniform"
app:autoSizeMinTextSize="@dimen/singleCardNoteTextSizeMin"
app:autoSizeMaxTextSize="@dimen/singleCardNoteTextSizeMax"
android:textIsSelectable="true"/>
android:textIsSelectable="true"
android:scrollbars="vertical"/>
</android.support.constraint.ConstraintLayout>

View File

@@ -12,6 +12,7 @@
<string name="note">Note</string>
<string name="cardId">Card ID</string>
<string name="barcodeType">Barcode Type</string>
<string name="barcodeNoBarcode">This card has no barcode</string>
<string name="cancel">Cancel</string>
<string name="save">Save</string>
@@ -85,7 +86,7 @@
<string name="app_resources"><xliff:g id="app_name">%s</xliff:g> uses the following third-party resources: <xliff:g id="app_resources_list">%s</xliff:g></string>
<string name="selectBarcodeTitle">Select Barcode</string>
<string name="enterBarcodeInstructions">Enter the barcode value then select the image which represents the barcode you want to use</string>
<string name="enterBarcodeInstructions">Enter the card ID then select the image which represents the barcode you want to use, or select &#8220;This card has no barcode&#8221; to not use a barcode.</string>
<string name="copy_to_clipboard_toast">Card ID copied to clipboard</string>

View File

@@ -0,0 +1,98 @@
package protect.card_locker;
import android.app.Activity;
import android.content.Intent;
import android.os.Looper;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.annotation.Config;
import static org.junit.Assert.assertEquals;
import static org.robolectric.Shadows.shadowOf;
@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 23)
public class BarcodeSelectorActivityTest {
@Test
public void emptyStateTest()
{
ActivityController activityController = Robolectric.buildActivity(BarcodeSelectorActivity.class).create();
activityController.start();
activityController.resume();
Activity activity = (Activity) activityController.get();
final TextView cardId = activity.findViewById(R.id.cardId);
final Button noBarcodeButton = activity.findViewById(R.id.noBarcode);
// No card ID by default
assertEquals(cardId.getText().toString(), "");
// Button should be visible but disabled
assertEquals(View.VISIBLE, noBarcodeButton.getVisibility());
assertEquals(false, noBarcodeButton.isEnabled());
}
@Test
public void nonEmptyStateTest() throws InterruptedException
{
ActivityController activityController = Robolectric.buildActivity(BarcodeSelectorActivity.class).create();
activityController.start();
activityController.resume();
Activity activity = (Activity) activityController.get();
final TextView cardId = activity.findViewById(R.id.cardId);
final Button noBarcodeButton = activity.findViewById(R.id.noBarcode);
cardId.setText("abcdefg");
shadowOf(Looper.getMainLooper()).idle();
// Button should be visible and enabled
assertEquals(View.VISIBLE, noBarcodeButton.getVisibility());
assertEquals(true, noBarcodeButton.isEnabled());
// Clicking button should create "empty" barcode
activity.findViewById(R.id.noBarcode).performClick();
Intent resultIntent = shadowOf(activity).getResultIntent();
assertEquals("", resultIntent.getStringExtra(BarcodeSelectorActivity.BARCODE_FORMAT));
assertEquals("abcdefg", resultIntent.getStringExtra(BarcodeSelectorActivity.BARCODE_CONTENTS));
}
@Test
public void nonEmptyToEmptyStateTest() throws InterruptedException
{
ActivityController activityController = Robolectric.buildActivity(BarcodeSelectorActivity.class).create();
activityController.start();
activityController.resume();
Activity activity = (Activity) activityController.get();
final TextView cardId = activity.findViewById(R.id.cardId);
final Button noBarcodeButton = activity.findViewById(R.id.noBarcode);
cardId.setText("abcdefg");
shadowOf(Looper.getMainLooper()).idle();
// Button should be visible and enabled
assertEquals(View.VISIBLE, noBarcodeButton.getVisibility());
assertEquals(true, noBarcodeButton.isEnabled());
cardId.setText("");
shadowOf(Looper.getMainLooper()).idle();
// Button should be visible but disabled
assertEquals(View.VISIBLE, noBarcodeButton.getVisibility());
assertEquals(false, noBarcodeButton.isEnabled());
}
}

View File

@@ -363,4 +363,35 @@ public class ImportExportTest
assertEquals(false, result);
assertEquals(0, db.getLoyaltyCardCount());
}
@Test
public void importWithNoBarcodeType() throws IOException
{
String csvText = "";
csvText += DBHelper.LoyaltyCardDbIds.ID + "," +
DBHelper.LoyaltyCardDbIds.STORE + "," +
DBHelper.LoyaltyCardDbIds.NOTE + "," +
DBHelper.LoyaltyCardDbIds.CARD_ID + "," +
DBHelper.LoyaltyCardDbIds.BARCODE_TYPE + "," +
DBHelper.LoyaltyCardDbIds.HEADER_COLOR + "," +
DBHelper.LoyaltyCardDbIds.HEADER_TEXT_COLOR + "\n";
csvText += "1,store,note,12345,,1,1";
ByteArrayInputStream inputStream = new ByteArrayInputStream(csvText.getBytes(StandardCharsets.UTF_8));
InputStreamReader inStream = new InputStreamReader(inputStream);
// Import the CSV data
boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV);
assertEquals(true, result);
assertEquals(1, db.getLoyaltyCardCount());
LoyaltyCard card = db.getLoyaltyCard(1);
assertEquals("store", card.store);
assertEquals("note", card.note);
assertEquals("12345", card.cardId);
assertEquals("", card.barcodeType);
assertEquals(1, (long) card.headerColor);
assertEquals(1, (long) card.headerTextColor);
}
}

View File

@@ -246,10 +246,6 @@ public class LoyaltyCardViewActivityTest
noteField.setText("note");
shadowActivity.clickMenuItem(R.id.action_save);
assertEquals(0, db.getLoyaltyCardCount());
cardIdField.setText("cardId");
shadowActivity.clickMenuItem(R.id.action_save);
assertEquals(0, db.getLoyaltyCardCount());
}
@Test
@@ -286,7 +282,7 @@ public class LoyaltyCardViewActivityTest
checkAllFields(activity, ViewMode.ADD_CARD, "", "", BARCODE_DATA, BARCODE_TYPE);
// Save and check the gift card
// Save and check the loyalty card
saveLoyaltyCardWithArguments(activity, "store", "note", BARCODE_DATA, BARCODE_TYPE, true);
}
@@ -325,7 +321,7 @@ public class LoyaltyCardViewActivityTest
checkAllFields(activity, ViewMode.ADD_CARD, "", "", BARCODE_DATA, BARCODE_TYPE);
// Cancel the gift card creation
// Cancel the loyalty card creation
assertEquals(false, activity.isFinishing());
shadowOf(activity).clickMenuItem(android.R.id.home);
assertEquals(true, activity.isFinishing());
@@ -527,10 +523,27 @@ public class LoyaltyCardViewActivityTest
activityController.visible();
activityController.resume();
// Save and check the gift card
// Save and check the loyalty card
saveLoyaltyCardWithArguments(activity, "store", "note", BARCODE_DATA, BARCODE_TYPE, false);
}
@Test
public void startLoyaltyCardWithExplicitNoBarcodeSave() throws IOException
{
ActivityController activityController = createActivityWithLoyaltyCard(true);
Activity activity = (Activity)activityController.get();
DBHelper db = new DBHelper(activity);
db.insertLoyaltyCard("store", "note", BARCODE_DATA, "", Color.BLACK, Color.WHITE);
activityController.start();
activityController.visible();
activityController.resume();
// Save and check the loyalty card
saveLoyaltyCardWithArguments(activity, "store", "note", BARCODE_DATA, "", false);
}
@Test
public void startCheckFontSizes()
{