mirror of
https://github.com/CatimaLoyalty/Android.git
synced 2026-03-30 13:21:43 -04:00
Group management POC
This commit is contained in:
@@ -1,14 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="protect.card_locker"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="protect.card_locker">
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.CAMERA"/>
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-sdk tools:overrideLibrary="com.google.zxing.client.android" />
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera"
|
||||
@@ -17,8 +16,6 @@
|
||||
android:name="android.hardware.camera.autofocus"
|
||||
android:required="false" />
|
||||
|
||||
<uses-sdk tools:overrideLibrary="com.google.zxing.client.android"/>
|
||||
|
||||
<application
|
||||
android:name=".LoyaltyCardLockerApplication"
|
||||
android:allowBackup="true"
|
||||
@@ -27,98 +24,106 @@
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name="protect.card_locker.MainActivity"
|
||||
android:name=".ManageGroupActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.App.Starting"/>
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.App.Starting"
|
||||
android:exported="true">
|
||||
android:theme="@style/Theme.App.Starting">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".AboutActivity"
|
||||
android:label="@string/about"
|
||||
android:theme="@style/AppTheme.NoActionBar">
|
||||
</activity>
|
||||
android:theme="@style/AppTheme.NoActionBar"></activity>
|
||||
<activity
|
||||
android:name=".ManageGroupsActivity"
|
||||
android:label="@string/groups"
|
||||
android:theme="@style/AppTheme.NoActionBar">
|
||||
</activity>
|
||||
android:theme="@style/AppTheme.NoActionBar"></activity>
|
||||
<activity
|
||||
android:name=".LoyaltyCardViewActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/AppTheme.NoActionBar"
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:exported="true"/>
|
||||
android:windowSoftInputMode="stateHidden" />
|
||||
<activity
|
||||
android:name=".LoyaltyCardEditActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/AppTheme.NoActionBar"
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:exported="true">
|
||||
<intent-filter android:autoVerify="true" android:label="@string/app_name">
|
||||
android:windowSoftInputMode="stateHidden">
|
||||
<intent-filter
|
||||
android:autoVerify="true"
|
||||
android:label="@string/app_name">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<!-- Main card sharing URIs -->
|
||||
<data android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
|
||||
<data android:host="@string/intent_import_card_from_url_host_catima_app"
|
||||
<data
|
||||
android:host="@string/intent_import_card_from_url_host_catima_app"
|
||||
android:pathPrefix="@string/intent_import_card_from_url_path_prefix_catima_app" />
|
||||
</intent-filter>
|
||||
<intent-filter android:label="@string/app_name">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<!-- Old card sharing URIs -->
|
||||
<data android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
|
||||
<data android:host="@string/intent_import_card_from_url_host_thelastproject"
|
||||
<data
|
||||
android:host="@string/intent_import_card_from_url_host_thelastproject"
|
||||
android:pathPrefix="@string/intent_import_card_from_url_path_prefix_thelastproject" />
|
||||
<data android:host="@string/intent_import_card_from_url_host_brarcher"
|
||||
<data
|
||||
android:host="@string/intent_import_card_from_url_host_brarcher"
|
||||
android:pathPrefix="@string/intent_import_card_from_url_path_prefix_brarcher" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ScanActivity"
|
||||
android:label="@string/scanCardBarcode"
|
||||
android:theme="@style/AppTheme.NoActionBar"/>
|
||||
android:theme="@style/AppTheme.NoActionBar" />
|
||||
<activity
|
||||
android:name=".BarcodeSelectorActivity"
|
||||
android:label="@string/selectBarcodeTitle"
|
||||
android:theme="@style/AppTheme.NoActionBar"
|
||||
android:windowSoftInputMode="stateHidden"/>
|
||||
android:windowSoftInputMode="stateHidden" />
|
||||
<activity
|
||||
android:name=".preferences.SettingsActivity"
|
||||
android:label="@string/settings"
|
||||
android:theme="@style/AppTheme.NoActionBar"/>
|
||||
android:theme="@style/AppTheme.NoActionBar" />
|
||||
<activity
|
||||
android:name=".ImportExportActivity"
|
||||
android:label="@string/importExport"
|
||||
android:theme="@style/AppTheme.NoActionBar"/>
|
||||
android:theme="@style/AppTheme.NoActionBar" />
|
||||
<activity
|
||||
android:name=".CardShortcutConfigure"
|
||||
android:exported="true"
|
||||
android:label="@string/cardShortcut"
|
||||
android:theme="@style/AppTheme.NoActionBar"
|
||||
android:exported="true">
|
||||
android:theme="@style/AppTheme.NoActionBar">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.CREATE_SHORTCUT"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<action android:name="android.intent.action.CREATE_SHORTCUT" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:grantUriPermissions="true"
|
||||
android:authorities="${applicationId}"
|
||||
android:exported="false"
|
||||
android:authorities="${applicationId}">
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_provider_paths"/>
|
||||
android:resource="@xml/file_provider_paths" />
|
||||
</provider>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
</manifest>
|
||||
@@ -613,6 +613,21 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
}
|
||||
}
|
||||
|
||||
public void addLoyaltyCardToGroup(int cardId, String groupId){
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(LoyaltyCardDbIdsGroups.cardID, cardId);
|
||||
contentValues.put(LoyaltyCardDbIdsGroups.groupID, groupId);
|
||||
db.insert(LoyaltyCardDbIdsGroups.TABLE, null, contentValues);
|
||||
}
|
||||
|
||||
public void removeLoyaltyCardFromGroup(int cardId, String groupId){
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
db.delete(LoyaltyCardDbIdsGroups.TABLE,
|
||||
whereAttrs(LoyaltyCardDbIdsGroups.cardID, LoyaltyCardDbIdsGroups.groupID),
|
||||
withArgs(cardId, groupId));
|
||||
}
|
||||
|
||||
public boolean deleteLoyaltyCard(final int id)
|
||||
{
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
@@ -718,6 +733,45 @@ public class DBHelper extends SQLiteOpenHelper
|
||||
limitString, filter.trim().isEmpty() ? null : new String[] { TextUtils.join("* ", filter.split(" ")) + '*' }, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a cursor to all loyalty cards with the filter text in either the store and whether card is in the provided group
|
||||
*
|
||||
* @param filter
|
||||
* @param group
|
||||
* @param order
|
||||
* @return Cursor
|
||||
*/
|
||||
public Cursor getIfLoyaltyCardsAreInGroupCursor(String filter, Group group, LoyaltyCardOrder order, LoyaltyCardOrderDirection direction) {
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
|
||||
if (group == null) {
|
||||
throw new IllegalArgumentException("group cannot be null");
|
||||
}
|
||||
String orderField = getFieldForOrder(order);
|
||||
String[] selectionArgs;
|
||||
if(filter.trim().isEmpty()) {
|
||||
selectionArgs = new String[]{
|
||||
group._id
|
||||
};
|
||||
}else{
|
||||
selectionArgs = new String[]{
|
||||
group._id,
|
||||
TextUtils.join("* ", filter.split(" ")) + '*'
|
||||
};
|
||||
}
|
||||
return db.rawQuery("SELECT " + "*" +
|
||||
" FROM " + LoyaltyCardDbIds.TABLE + " LEFT OUTER JOIN " +
|
||||
" (SELECT " + LoyaltyCardDbIdsGroups.TABLE + ".*" +
|
||||
" FROM " + LoyaltyCardDbIdsGroups.TABLE +
|
||||
" WHERE " + LoyaltyCardDbIdsGroups.groupID + " = ? " +
|
||||
" ) " + " AS " + LoyaltyCardDbIdsGroups.TABLE +
|
||||
" ON " + LoyaltyCardDbIds.TABLE + "." + LoyaltyCardDbIds.ID + " = " + LoyaltyCardDbIdsGroups.TABLE + "." + LoyaltyCardDbIdsGroups.cardID +
|
||||
(filter.trim().isEmpty() ? "" : " WHERE " + LoyaltyCardDbIds.TABLE + "." + LoyaltyCardDbIds.STORE + " MATCH ? ") +
|
||||
" ORDER BY " + LoyaltyCardDbIds.TABLE + "." + orderField,
|
||||
selectionArgs,
|
||||
null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of loyalty cards.
|
||||
*
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class Group
|
||||
public class Group implements Parcelable
|
||||
{
|
||||
public final String _id;
|
||||
public final int order;
|
||||
@@ -12,6 +14,25 @@ public class Group
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
protected Group(Parcel in){
|
||||
this._id = in.readString();
|
||||
this.order = in.readInt();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel parcel, int i){
|
||||
parcel.writeString(_id);
|
||||
parcel.writeInt(order);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
// group table does not have an integer ID
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static Group toGroup(Cursor cursor)
|
||||
{
|
||||
String _id = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbGroups.ID));
|
||||
@@ -19,4 +40,16 @@ public class Group
|
||||
|
||||
return new Group(_id, order);
|
||||
}
|
||||
|
||||
public static final Creator<Group> CREATOR = new Creator<Group>() {
|
||||
@Override
|
||||
public Group createFromParcel(Parcel in) {
|
||||
return new Group(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group[] newArray(int size) {
|
||||
return new Group[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
355
app/src/main/java/protect/card_locker/ManageGroupActivity.java
Normal file
355
app/src/main/java/protect/card_locker/ManageGroupActivity.java
Normal file
@@ -0,0 +1,355 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.app.SearchManager;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.database.CursorIndexOutOfBoundsException;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.view.ActionMode;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.graphics.BlendModeColorFilterCompat;
|
||||
import androidx.core.graphics.BlendModeCompat;
|
||||
import androidx.core.splashscreen.SplashScreen;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import protect.card_locker.preferences.SettingsActivity;
|
||||
|
||||
public class ManageGroupActivity extends CatimaAppCompatActivity implements ManageGroupCursorAdapter.CardAdapterListener
|
||||
{
|
||||
private static final String TAG = "Catima";
|
||||
|
||||
private final DBHelper mDB = new DBHelper(this);
|
||||
private ManageGroupCursorAdapter mAdapter;
|
||||
private ActionMode mCurrentActionMode;
|
||||
private Menu mMenu;
|
||||
|
||||
// currently unused
|
||||
protected String mFilter = "";
|
||||
protected DBHelper.LoyaltyCardOrderDirection mOrderDirection = DBHelper.LoyaltyCardOrderDirection.Ascending;
|
||||
protected DBHelper.LoyaltyCardOrder mOrder = DBHelper.LoyaltyCardOrder.Alpha;
|
||||
|
||||
protected Group mGroup = null;
|
||||
private RecyclerView mCardList;
|
||||
private View mHelpText;
|
||||
private View mNoMatchingCardsText;
|
||||
private View mNoGroupCardsText;
|
||||
private EditText mGroupNameText;
|
||||
private TextView mGroupNameLabel;
|
||||
private ActionBar mActionBar;
|
||||
|
||||
private HashMap<Integer, Boolean> mAdapterState;
|
||||
private String mCurrentGroupName;
|
||||
|
||||
private boolean mGroupNameNotInUse;
|
||||
|
||||
private boolean mDarkMode;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle inputSavedInstanceState)
|
||||
{
|
||||
super.onCreate(inputSavedInstanceState);
|
||||
setContentView(R.layout.activity_manage_group);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
mHelpText = findViewById(R.id.helpText);
|
||||
mNoMatchingCardsText = findViewById(R.id.noMatchingCardsText);
|
||||
mNoGroupCardsText = findViewById(R.id.noGroupCardsText);
|
||||
mCardList = findViewById(R.id.list);
|
||||
|
||||
mGroupNameText = findViewById(R.id.editTextGroupName);
|
||||
mGroupNameLabel = findViewById(R.id.textViewEditGroupName);
|
||||
|
||||
mAdapter = new ManageGroupCursorAdapter(this, null, this);
|
||||
mCardList.setAdapter(mAdapter);
|
||||
registerForContextMenu(mCardList);
|
||||
|
||||
mGroup = null;
|
||||
|
||||
mDarkMode = Utils.isDarkModeEnabled(getApplicationContext());
|
||||
|
||||
if (inputSavedInstanceState != null) {
|
||||
ManageGroupActivityInGroupState adapterState = inputSavedInstanceState.getParcelable("mAdapterState");
|
||||
if (adapterState != null) {
|
||||
mAdapterState = adapterState.getMap();
|
||||
}
|
||||
mCurrentGroupName = inputSavedInstanceState.getString("mCurrentGroupName");
|
||||
}
|
||||
|
||||
mActionBar = getSupportActionBar();
|
||||
|
||||
mActionBar.setDisplayHomeAsUpEnabled(true);
|
||||
mActionBar.setDisplayShowHomeEnabled(true);
|
||||
|
||||
}
|
||||
|
||||
private void resetGroupNameTextColor() {
|
||||
if (mDarkMode) {
|
||||
mGroupNameText.setTextColor(Color.WHITE);
|
||||
} else{
|
||||
mGroupNameText.setTextColor(Color.BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkIfGroupNameIsInUse(){
|
||||
mGroupNameNotInUse = false;
|
||||
String currentGroupName = mGroupNameText.getText().toString();
|
||||
if (!mGroup._id.equals(currentGroupName.trim())) {
|
||||
Group group = mDB.getGroup(currentGroupName.trim());
|
||||
if (group != null) {
|
||||
mGroupNameNotInUse = false;
|
||||
mGroupNameText.setTextColor(Color.RED);
|
||||
} else {
|
||||
mGroupNameNotInUse = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume()
|
||||
{
|
||||
super.onResume();
|
||||
|
||||
Intent intent = getIntent();
|
||||
mGroup = intent.getParcelableExtra("group");
|
||||
if (mGroup == null){
|
||||
throw(new IllegalArgumentException("this activity expects a group loaded into it's intent"));
|
||||
}
|
||||
|
||||
setTitle(getString(R.string.edit) + ": " + mGroup._id);
|
||||
|
||||
if (mCurrentGroupName == null){
|
||||
mGroupNameText.setText(mGroup._id);
|
||||
}else{
|
||||
mGroupNameText.setText(mCurrentGroupName);
|
||||
checkIfGroupNameIsInUse();
|
||||
}
|
||||
|
||||
mGroupNameText.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
resetGroupNameTextColor();
|
||||
checkIfGroupNameIsInUse();
|
||||
}
|
||||
});
|
||||
|
||||
if (mAdapterState != null){
|
||||
mAdapter.importInGroupState(mAdapterState);
|
||||
}
|
||||
updateLoyaltyCardList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState (Bundle outState){
|
||||
super.onSaveInstanceState(outState);
|
||||
if(mAdapterState != null){
|
||||
ManageGroupActivityInGroupState state = new ManageGroupActivityInGroupState(mAdapterState);
|
||||
outState.putParcelable("mAdapterState", state);
|
||||
}
|
||||
if(mCurrentGroupName != null){
|
||||
outState.putString("mCurrentGroupName", mCurrentGroupName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause(){
|
||||
super.onPause();
|
||||
|
||||
mAdapterState = mAdapter.exportInGroupState();
|
||||
mCurrentGroupName = mGroupNameText.getText().toString();
|
||||
}
|
||||
|
||||
private void updateLoyaltyCardList() {
|
||||
mAdapter.swapCursor(mDB.getIfLoyaltyCardsAreInGroupCursor(mFilter, mGroup, mOrder, mOrderDirection));
|
||||
|
||||
if(mAdapter.getCountFromCursor() > 0)
|
||||
{
|
||||
// We want the cardList to be visible regardless of the filtered match count
|
||||
// to ensure that the noMatchingCardsText doesn't end up being shown below
|
||||
// the keyboard
|
||||
mHelpText.setVisibility(View.GONE);
|
||||
mNoGroupCardsText.setVisibility(View.GONE);
|
||||
if(mAdapter.getItemCount() > 0)
|
||||
{
|
||||
mCardList.setVisibility(View.VISIBLE);
|
||||
mNoMatchingCardsText.setVisibility(View.GONE);
|
||||
}
|
||||
else
|
||||
{
|
||||
mCardList.setVisibility(View.GONE);
|
||||
if (!mFilter.isEmpty()) {
|
||||
// Actual Empty Search Result
|
||||
mNoMatchingCardsText.setVisibility(View.VISIBLE);
|
||||
mNoGroupCardsText.setVisibility(View.GONE);
|
||||
} else {
|
||||
// Group Tab with no Group Cards
|
||||
mNoMatchingCardsText.setVisibility(View.GONE);
|
||||
mNoGroupCardsText.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mCardList.setVisibility(View.GONE);
|
||||
mHelpText.setVisibility(View.VISIBLE);
|
||||
mNoMatchingCardsText.setVisibility(View.GONE);
|
||||
mNoGroupCardsText.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu inputMenu)
|
||||
{
|
||||
this.mMenu = inputMenu;
|
||||
|
||||
getMenuInflater().inflate(R.menu.manage_group_menu, inputMenu);
|
||||
|
||||
MenuItem confirmButton = inputMenu.findItem(R.id.action_confirm);
|
||||
Drawable icon = confirmButton.getIcon();
|
||||
icon.mutate();
|
||||
icon.setTint(Color.WHITE);
|
||||
confirmButton.setIcon(icon);
|
||||
return super.onCreateOptionsMenu(inputMenu);
|
||||
}
|
||||
|
||||
private void leaveWithoutSaving(){
|
||||
if (hasChanged()){
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(ManageGroupActivity.this);
|
||||
builder.setTitle(R.string.discard_changes);
|
||||
builder.setMessage(R.string.discard_changes_confirm);
|
||||
builder.setPositiveButton(R.string.confirm, (dialog, which) -> finish());
|
||||
builder.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.show();
|
||||
}else{
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem inputItem)
|
||||
{
|
||||
int id = inputItem.getItemId();
|
||||
|
||||
if (id == R.id.action_confirm)
|
||||
{
|
||||
String currentGroupName = mGroupNameText.getText().toString();
|
||||
if(!currentGroupName.trim().equals(mGroup._id)){
|
||||
if(!mGroupNameNotInUse) {
|
||||
Toast toast = Toast.makeText(getApplicationContext(), R.string.group_name_already_in_use, Toast.LENGTH_SHORT);
|
||||
toast.show();
|
||||
return true;
|
||||
}
|
||||
if(currentGroupName.trim().length() == 0){
|
||||
Toast toast = Toast.makeText(getApplicationContext(), R.string.group_name_is_empty, Toast.LENGTH_SHORT);
|
||||
toast.show();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
mAdapter.commitToDatabase(getApplicationContext(), mGroup._id);
|
||||
Toast toast = Toast.makeText(getApplicationContext(), R.string.group_updated, Toast.LENGTH_SHORT);
|
||||
if(!currentGroupName.trim().equals(mGroup._id)){
|
||||
mDB.updateGroup(mGroup._id, currentGroupName.trim());
|
||||
}
|
||||
toast.show();
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(inputItem);
|
||||
}
|
||||
@Override
|
||||
public void onBackPressed()
|
||||
{
|
||||
leaveWithoutSaving();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSupportNavigateUp() {
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean hasChanged(){
|
||||
return mAdapter.hasChanged() || !mGroup._id.equals(mGroupNameText.getText().toString().trim());
|
||||
}
|
||||
|
||||
private void setSort(DBHelper.LoyaltyCardOrder order, DBHelper.LoyaltyCardOrderDirection direction) {
|
||||
// Update values
|
||||
mOrder = order;
|
||||
mOrderDirection = direction;
|
||||
|
||||
// Store in Shared Preference to restore next app launch
|
||||
SharedPreferences sortPref = getApplicationContext().getSharedPreferences(
|
||||
getString(R.string.sharedpreference_sort),
|
||||
Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor sortPrefEditor = sortPref.edit();
|
||||
sortPrefEditor.putString(getString(R.string.sharedpreference_sort_order), order.name());
|
||||
sortPrefEditor.putString(getString(R.string.sharedpreference_sort_direction), direction.name());
|
||||
sortPrefEditor.apply();
|
||||
|
||||
// Update card list
|
||||
updateLoyaltyCardList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRowLongClicked(int inputPosition)
|
||||
{
|
||||
// do nothing for now
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRowClicked(int inputPosition)
|
||||
{
|
||||
Cursor selected = mAdapter.getCursor();
|
||||
mAdapter.toggleSelection(inputPosition);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ManageGroupActivityInGroupState implements Parcelable {
|
||||
HashMap<Integer, Boolean> map;
|
||||
|
||||
|
||||
|
||||
public ManageGroupActivityInGroupState(HashMap<Integer, Boolean> in){
|
||||
map = (HashMap<Integer, Boolean>)in.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(map.size());
|
||||
|
||||
for(Map.Entry<Integer, Boolean> entry : map.entrySet()){
|
||||
dest.writeInt(entry.getKey());
|
||||
dest.writeInt(entry.getValue() ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
protected ManageGroupActivityInGroupState(Parcel in){
|
||||
map = new HashMap<Integer, Boolean>();
|
||||
int length = in.readInt();
|
||||
for (int i = 0;i < length; i++){
|
||||
int key = in.readInt();
|
||||
boolean value = in.readInt() == 1 ? true : false;
|
||||
map.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static final Creator<ManageGroupActivityInGroupState> CREATOR = new Creator<ManageGroupActivityInGroupState>() {
|
||||
@Override
|
||||
public ManageGroupActivityInGroupState createFromParcel(Parcel in) {
|
||||
return new ManageGroupActivityInGroupState(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ManageGroupActivityInGroupState[] newArray(int size) {
|
||||
return new ManageGroupActivityInGroupState[size];
|
||||
}
|
||||
};
|
||||
|
||||
public HashMap<Integer, Boolean> getMap(){
|
||||
return (HashMap<Integer,Boolean>)map.clone();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Log;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.util.TypedValue;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.graphics.BlendModeColorFilterCompat;
|
||||
import androidx.core.graphics.BlendModeCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import protect.card_locker.preferences.Settings;
|
||||
|
||||
public class ManageGroupCursorAdapter extends BaseCursorAdapter<ManageGroupCursorAdapter.ManageGroupListItemViewHolder> {
|
||||
private int mCurrentSelectedIndex = -1;
|
||||
private Cursor mCursor;
|
||||
Settings mSettings;
|
||||
boolean mDarkModeEnabled;
|
||||
private Context mContext;
|
||||
private CardAdapterListener mListener;
|
||||
private SparseBooleanArray mSelectedItems;
|
||||
private SparseBooleanArray mAnimationItemsIndex;
|
||||
private boolean mReverseAllAnimations = false;
|
||||
private HashMap<Integer, ManageGroupLoyaltyCard> mIndexCardMap;
|
||||
private HashMap<Integer, Boolean> mInGroupOverlay;
|
||||
|
||||
|
||||
public ManageGroupCursorAdapter(Context inputContext, Cursor inputCursor, CardAdapterListener inputListener) {
|
||||
super(inputCursor);
|
||||
setHasStableIds(true);
|
||||
mSettings = new Settings(inputContext);
|
||||
mContext = inputContext;
|
||||
mListener = inputListener;
|
||||
mSelectedItems = new SparseBooleanArray();
|
||||
mAnimationItemsIndex = new SparseBooleanArray();
|
||||
|
||||
mDarkModeEnabled = Utils.isDarkModeEnabled(inputContext);
|
||||
|
||||
|
||||
mInGroupOverlay = new HashMap<Integer, Boolean>();
|
||||
swapCursor(mCursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void swapCursor(Cursor inputCursor) {
|
||||
mIndexCardMap = new HashMap<Integer, ManageGroupLoyaltyCard>();
|
||||
|
||||
super.swapCursor(inputCursor);
|
||||
mCursor = inputCursor;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ManageGroupListItemViewHolder onCreateViewHolder(ViewGroup inputParent, int inputViewType) {
|
||||
View itemView = LayoutInflater.from(inputParent.getContext()).inflate(R.layout.manage_group_loyalty_card_layout, inputParent, false);
|
||||
return new ManageGroupListItemViewHolder(itemView, mListener);
|
||||
}
|
||||
|
||||
public Cursor getCursor() {
|
||||
return mCursor;
|
||||
}
|
||||
|
||||
public void onBindViewHolder(ManageGroupListItemViewHolder inputHolder, Cursor inputCursor) {
|
||||
// Invisible until we want to show something more
|
||||
inputHolder.mDivider.setVisibility(View.GONE);
|
||||
|
||||
int size = mSettings.getFontSizeMax(mSettings.getSmallFont());
|
||||
|
||||
if (mDarkModeEnabled) {
|
||||
inputHolder.mStarIcon.setColorFilter(BlendModeColorFilterCompat.createBlendModeColorFilterCompat(Color.WHITE, BlendModeCompat.SRC_ATOP));
|
||||
}
|
||||
|
||||
ManageGroupLoyaltyCard cardEntry = ManageGroupLoyaltyCard.toCard(inputCursor);
|
||||
|
||||
inputHolder.mStoreField.setText(cardEntry.store);
|
||||
inputHolder.mStoreField.setTextSize(mSettings.getFontSizeMax(mSettings.getMediumFont()));
|
||||
if (!cardEntry.note.isEmpty()) {
|
||||
inputHolder.mNoteField.setVisibility(View.VISIBLE);
|
||||
inputHolder.mNoteField.setText(cardEntry.note);
|
||||
inputHolder.mNoteField.setTextSize(size);
|
||||
} else {
|
||||
inputHolder.mNoteField.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (!cardEntry.balance.equals(new BigDecimal("0"))) {
|
||||
int drawableSize = dpToPx((size*24)/14, mContext);
|
||||
inputHolder.mDivider.setVisibility(View.VISIBLE);
|
||||
inputHolder.mBalanceField.setVisibility(View.VISIBLE);
|
||||
Drawable balanceIcon = inputHolder.mBalanceField.getCompoundDrawables()[0];
|
||||
balanceIcon.setBounds(0,0,drawableSize,drawableSize);
|
||||
inputHolder.mBalanceField.setCompoundDrawablesRelative(balanceIcon, null, null, null);
|
||||
if (mDarkModeEnabled) {
|
||||
balanceIcon.setColorFilter(BlendModeColorFilterCompat.createBlendModeColorFilterCompat(Color.WHITE, BlendModeCompat.SRC_ATOP));
|
||||
}
|
||||
inputHolder.mBalanceField.setText(Utils.formatBalance(mContext, cardEntry.balance, cardEntry.balanceType));
|
||||
inputHolder.mBalanceField.setTextSize(size);
|
||||
} else {
|
||||
inputHolder.mBalanceField.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (cardEntry.expiry != null) {
|
||||
int drawableSize = dpToPx((size*24)/14, mContext);
|
||||
inputHolder.mDivider.setVisibility(View.VISIBLE);
|
||||
inputHolder.mExpiryField.setVisibility(View.VISIBLE);
|
||||
Drawable expiryIcon = inputHolder.mExpiryField.getCompoundDrawables()[0];
|
||||
expiryIcon.setBounds(0,0, drawableSize, drawableSize);
|
||||
inputHolder.mExpiryField.setCompoundDrawablesRelative(expiryIcon, null, null, null);
|
||||
if (Utils.hasExpired(cardEntry.expiry)) {
|
||||
expiryIcon.setColorFilter(BlendModeColorFilterCompat.createBlendModeColorFilterCompat(Color.RED, BlendModeCompat.SRC_ATOP));
|
||||
inputHolder.mExpiryField.setTextColor(Color.RED);
|
||||
} else if (mDarkModeEnabled) {
|
||||
expiryIcon.setColorFilter(BlendModeColorFilterCompat.createBlendModeColorFilterCompat(Color.WHITE, BlendModeCompat.SRC_ATOP));
|
||||
}
|
||||
inputHolder.mExpiryField.setText(DateFormat.getDateInstance(DateFormat.LONG).format(cardEntry.expiry));
|
||||
inputHolder.mExpiryField.setTextSize(size);
|
||||
} else {
|
||||
inputHolder.mExpiryField.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// inputHolder.mStarIcon.setVisibility(cardEntry.starStatus != 0 ? View.VISIBLE : View.GONE);
|
||||
inputHolder.mCardIcon.setImageBitmap(Utils.generateIcon(mContext, cardEntry.store, cardEntry.headerColor).getLetterTile());
|
||||
int imageSize = dpToPx( (size*46)/14, mContext);
|
||||
inputHolder.mCardIcon.getLayoutParams().height = imageSize;
|
||||
inputHolder.mCardIcon.getLayoutParams().width = imageSize;
|
||||
inputHolder.mStarIcon.getLayoutParams().height = imageSize;
|
||||
inputHolder.mStarIcon.getLayoutParams().width = imageSize;
|
||||
inputHolder.mTickIcon.getLayoutParams().height = imageSize;
|
||||
inputHolder.mTickIcon.getLayoutParams().width = imageSize;
|
||||
|
||||
/* Changing Padding and Margin of different views according to font size
|
||||
* Views Included:
|
||||
* a) InformationContainer padding
|
||||
* b) Store left padding
|
||||
* c) Divider Margin
|
||||
* d) note top margin
|
||||
* e) row margin
|
||||
* */
|
||||
int marginPaddingSize = dpToPx((size*16)/14, mContext );
|
||||
inputHolder.mInformationContainer.setPadding(marginPaddingSize, marginPaddingSize, marginPaddingSize, marginPaddingSize);
|
||||
inputHolder.mStoreField.setPadding(marginPaddingSize, 0, 0, 0);
|
||||
LinearLayout.LayoutParams lpDivider = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT );
|
||||
lpDivider.setMargins(0, marginPaddingSize, 0, marginPaddingSize);
|
||||
inputHolder.mDivider.setLayoutParams(lpDivider);
|
||||
LinearLayout.LayoutParams lpNoteField = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT );
|
||||
lpNoteField.setMargins(0, marginPaddingSize/2, 0, 0);
|
||||
inputHolder.mNoteField.setLayoutParams(lpNoteField);
|
||||
LinearLayout.LayoutParams lpRow = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT );
|
||||
lpRow.setMargins(marginPaddingSize/2, marginPaddingSize/2, marginPaddingSize/2, marginPaddingSize/2);
|
||||
inputHolder.mRow.setLayoutParams(lpRow);
|
||||
|
||||
inputHolder.itemView.setActivated(mSelectedItems.get(inputCursor.getPosition(), false));
|
||||
|
||||
Boolean overlayValue = mInGroupOverlay.get(cardEntry.id);
|
||||
if((overlayValue != null? overlayValue: cardEntry.is_in_group)) {
|
||||
mAnimationItemsIndex.put(inputCursor.getPosition(), true);
|
||||
mSelectedItems.put(inputCursor.getPosition(), true);
|
||||
}
|
||||
|
||||
applyIconAnimation(inputHolder, inputCursor.getPosition());
|
||||
applyClickEvents(inputHolder, inputCursor.getPosition());
|
||||
|
||||
mIndexCardMap.put(inputCursor.getPosition(), cardEntry);
|
||||
}
|
||||
|
||||
private void applyClickEvents(ManageGroupListItemViewHolder inputHolder, final int inputPosition) {
|
||||
inputHolder.mRow.setOnClickListener(inputView -> mListener.onRowClicked(inputPosition));
|
||||
inputHolder.mInformationContainer.setOnClickListener(inputView -> mListener.onRowClicked(inputPosition));
|
||||
|
||||
inputHolder.mRow.setOnLongClickListener(inputView -> {
|
||||
mListener.onRowLongClicked(inputPosition);
|
||||
inputView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||
return true;
|
||||
});
|
||||
|
||||
inputHolder.mInformationContainer.setOnLongClickListener(inputView -> {
|
||||
mListener.onRowLongClicked(inputPosition);
|
||||
inputView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void applyIconAnimation(ManageGroupListItemViewHolder inputHolder, int inputPosition) {
|
||||
if (mSelectedItems.get(inputPosition, false)) {
|
||||
inputHolder.mThumbnailFrontContainer.setVisibility(View.GONE);
|
||||
resetIconYAxis(inputHolder.mThumbnailBackContainer);
|
||||
inputHolder.mThumbnailBackContainer.setVisibility(View.VISIBLE);
|
||||
inputHolder.mThumbnailBackContainer.setAlpha(1);
|
||||
if (mCurrentSelectedIndex == inputPosition) {
|
||||
LoyaltyCardAnimator.flipView(mContext, inputHolder.mThumbnailBackContainer, inputHolder.mThumbnailFrontContainer, true);
|
||||
resetCurrentIndex();
|
||||
}
|
||||
} else {
|
||||
inputHolder.mThumbnailBackContainer.setVisibility(View.GONE);
|
||||
resetIconYAxis(inputHolder.mThumbnailFrontContainer);
|
||||
inputHolder.mThumbnailFrontContainer.setVisibility(View.VISIBLE);
|
||||
inputHolder.mThumbnailFrontContainer.setAlpha(1);
|
||||
if ((mReverseAllAnimations && mAnimationItemsIndex.get(inputPosition, false)) || mCurrentSelectedIndex == inputPosition) {
|
||||
LoyaltyCardAnimator.flipView(mContext, inputHolder.mThumbnailBackContainer, inputHolder.mThumbnailFrontContainer, false);
|
||||
resetCurrentIndex();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void resetIconYAxis(View inputView) {
|
||||
if (inputView.getRotationY() != 0) {
|
||||
inputView.setRotationY(0);
|
||||
}
|
||||
}
|
||||
|
||||
public void resetAnimationIndex() {
|
||||
mReverseAllAnimations = false;
|
||||
mAnimationItemsIndex.clear();
|
||||
}
|
||||
|
||||
|
||||
public void toggleSelection(int inputPosition) {
|
||||
mCurrentSelectedIndex = inputPosition;
|
||||
if (mSelectedItems.get(inputPosition, false)) {
|
||||
mSelectedItems.delete(inputPosition);
|
||||
mAnimationItemsIndex.delete(inputPosition);
|
||||
} else {
|
||||
mSelectedItems.put(inputPosition, true);
|
||||
mAnimationItemsIndex.put(inputPosition, true);
|
||||
}
|
||||
ManageGroupLoyaltyCard cardEntry = mIndexCardMap.get(inputPosition);
|
||||
Boolean overlayValue = mInGroupOverlay.get(cardEntry.id);
|
||||
if (overlayValue == null){
|
||||
mInGroupOverlay.put(cardEntry.id, !cardEntry.is_in_group);
|
||||
}else{
|
||||
mInGroupOverlay.put(cardEntry.id, !overlayValue);
|
||||
}
|
||||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void resetCurrentIndex() {
|
||||
mCurrentSelectedIndex = -1;
|
||||
}
|
||||
|
||||
public interface CardAdapterListener {
|
||||
void onRowClicked(int inputPosition);
|
||||
|
||||
void onRowLongClicked(int inputPosition);
|
||||
}
|
||||
|
||||
private HashMap<Integer, ManageGroupLoyaltyCard> fetchWholeQuery (){
|
||||
HashMap<Integer, ManageGroupLoyaltyCard> res = new HashMap<Integer, ManageGroupLoyaltyCard>();
|
||||
int oldPosition = mCursor.getPosition();
|
||||
mCursor.moveToFirst();
|
||||
while(!mCursor.isAfterLast()){
|
||||
ManageGroupLoyaltyCard cardEntry = ManageGroupLoyaltyCard.toCard(mCursor);
|
||||
res.put(cardEntry.id, cardEntry);
|
||||
mCursor.moveToNext();
|
||||
}
|
||||
mCursor.moveToPosition(oldPosition);
|
||||
return res;
|
||||
}
|
||||
|
||||
public void commitToDatabase(Context context, String groupId){
|
||||
DBHelper dbHelper = new DBHelper(context);
|
||||
HashMap<Integer, ManageGroupLoyaltyCard> cache = fetchWholeQuery();
|
||||
for(Map.Entry<Integer, Boolean> entry : mInGroupOverlay.entrySet()){
|
||||
ManageGroupLoyaltyCard cardEntry = cache.get(entry.getKey());
|
||||
if (cardEntry == null){
|
||||
Log.d("commitToDatabase", "card with id " + entry.getKey() + " was removed from database unexpectedly");
|
||||
continue;
|
||||
}
|
||||
if (entry.getValue() != cardEntry.is_in_group) {
|
||||
if (entry.getValue()) {
|
||||
dbHelper.addLoyaltyCardToGroup(entry.getKey(), groupId);
|
||||
} else {
|
||||
dbHelper.removeLoyaltyCardFromGroup(entry.getKey(), groupId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasChanged(){
|
||||
HashMap<Integer, ManageGroupLoyaltyCard> cache = fetchWholeQuery();
|
||||
for(Map.Entry<Integer, Boolean> entry : mInGroupOverlay.entrySet()){
|
||||
ManageGroupLoyaltyCard cardEntry = cache.get(entry.getKey());
|
||||
if(cardEntry.is_in_group != entry.getValue()){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getCountFromCursor(){
|
||||
return mCursor.getCount();
|
||||
}
|
||||
|
||||
public HashMap<Integer, Boolean> exportInGroupState(){
|
||||
return (HashMap<Integer, Boolean>)mInGroupOverlay.clone();
|
||||
}
|
||||
|
||||
public void importInGroupState(HashMap<Integer, Boolean> cardIdInGroupMap){
|
||||
mInGroupOverlay = (HashMap<Integer, Boolean>)cardIdInGroupMap.clone();
|
||||
}
|
||||
|
||||
|
||||
public static class ManageGroupListItemViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public TextView mStoreField, mNoteField, mBalanceField, mExpiryField;
|
||||
public LinearLayout mInformationContainer;
|
||||
public ImageView mCardIcon, mStarIcon, mTickIcon;
|
||||
public MaterialCardView mRow;
|
||||
public View mDivider;
|
||||
public RelativeLayout mThumbnailFrontContainer, mThumbnailBackContainer;
|
||||
|
||||
public ManageGroupListItemViewHolder(View inputView, CardAdapterListener inputListener) {
|
||||
super(inputView);
|
||||
mRow = inputView.findViewById(R.id.row);
|
||||
mDivider = inputView.findViewById(R.id.info_divider);
|
||||
mThumbnailFrontContainer = inputView.findViewById(R.id.thumbnail_front);
|
||||
mThumbnailBackContainer = inputView.findViewById(R.id.thumbnail_back);
|
||||
mInformationContainer = inputView.findViewById(R.id.information_container);
|
||||
mStoreField = inputView.findViewById(R.id.store);
|
||||
mNoteField = inputView.findViewById(R.id.note);
|
||||
mBalanceField = inputView.findViewById(R.id.balance);
|
||||
mExpiryField = inputView.findViewById(R.id.expiry);
|
||||
mCardIcon = inputView.findViewById(R.id.thumbnail);
|
||||
mStarIcon = inputView.findViewById(R.id.star);
|
||||
mTickIcon = inputView.findViewById(R.id.selected_thumbnail);
|
||||
inputView.setOnLongClickListener(view -> {
|
||||
inputListener.onRowClicked(getAdapterPosition());
|
||||
inputView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public int dpToPx(int dp, Context mContext){
|
||||
Resources r = mContext.getResources();
|
||||
int px = (int)TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics());
|
||||
return px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package protect.card_locker;
|
||||
// Was thinking about extending LoyaltyCard but this makes things more modular..?
|
||||
import android.database.Cursor;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class ManageGroupLoyaltyCard implements Parcelable {
|
||||
public final int id;
|
||||
public final String store;
|
||||
public final String note;
|
||||
public final Date expiry;
|
||||
public final BigDecimal balance;
|
||||
public final Currency balanceType;
|
||||
public final String cardId;
|
||||
|
||||
@Nullable
|
||||
public final String barcodeId;
|
||||
|
||||
@Nullable
|
||||
public final CatimaBarcode barcodeType;
|
||||
|
||||
@Nullable
|
||||
public final Integer headerColor;
|
||||
|
||||
public final int starStatus;
|
||||
public final long lastUsed;
|
||||
public int zoomLevel;
|
||||
|
||||
public final boolean is_in_group;
|
||||
|
||||
public ManageGroupLoyaltyCard(final int id, final String store, final String note, final Date expiry,
|
||||
final BigDecimal balance, final Currency balanceType, final String cardId,
|
||||
@Nullable final String barcodeId, @Nullable final CatimaBarcode barcodeType,
|
||||
@Nullable final Integer headerColor, final int starStatus, final long lastUsed,final int zoomLevel,
|
||||
final boolean is_in_group)
|
||||
{
|
||||
this.id = id;
|
||||
this.store = store;
|
||||
this.note = note;
|
||||
this.expiry = expiry;
|
||||
this.balance = balance;
|
||||
this.balanceType = balanceType;
|
||||
this.cardId = cardId;
|
||||
this.barcodeId = barcodeId;
|
||||
this.barcodeType = barcodeType;
|
||||
this.headerColor = headerColor;
|
||||
this.starStatus = starStatus;
|
||||
this.lastUsed = lastUsed;
|
||||
this.zoomLevel = zoomLevel;
|
||||
this.is_in_group = is_in_group;
|
||||
}
|
||||
|
||||
protected ManageGroupLoyaltyCard(Parcel in) {
|
||||
id = in.readInt();
|
||||
store = in.readString();
|
||||
note = in.readString();
|
||||
long tmpExpiry = in.readLong();
|
||||
expiry = tmpExpiry != -1 ? new Date(tmpExpiry) : null;
|
||||
balance = (BigDecimal) in.readValue(BigDecimal.class.getClassLoader());
|
||||
balanceType = (Currency) in.readValue(Currency.class.getClassLoader());
|
||||
cardId = in.readString();
|
||||
barcodeId = in.readString();
|
||||
String tmpBarcodeType = in.readString();
|
||||
barcodeType = !tmpBarcodeType.isEmpty() ? CatimaBarcode.fromName(tmpBarcodeType) : null;
|
||||
int tmpHeaderColor = in.readInt();
|
||||
headerColor = tmpHeaderColor != -1 ? tmpHeaderColor : null;
|
||||
starStatus = in.readInt();
|
||||
lastUsed = in.readLong();
|
||||
zoomLevel = in.readInt();
|
||||
if (in.readInt() == 1) {
|
||||
is_in_group = true;
|
||||
}else{
|
||||
is_in_group = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel parcel, int i) {
|
||||
parcel.writeInt(id);
|
||||
parcel.writeString(store);
|
||||
parcel.writeString(note);
|
||||
parcel.writeLong(expiry != null ? expiry.getTime() : -1);
|
||||
parcel.writeValue(balance);
|
||||
parcel.writeValue(balanceType);
|
||||
parcel.writeString(cardId);
|
||||
parcel.writeString(barcodeId);
|
||||
parcel.writeString(barcodeType != null ? barcodeType.name() : "");
|
||||
parcel.writeInt(headerColor != null ? headerColor : -1);
|
||||
parcel.writeInt(starStatus);
|
||||
parcel.writeLong(lastUsed);
|
||||
parcel.writeInt(zoomLevel);
|
||||
if (is_in_group) {
|
||||
parcel.writeInt(1);
|
||||
}else{
|
||||
parcel.writeInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
public static ManageGroupLoyaltyCard toCard(Cursor cursor)
|
||||
{
|
||||
int id = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.ID));
|
||||
String store = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STORE));
|
||||
String note = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE));
|
||||
long expiryLong = cursor.getLong(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.EXPIRY));
|
||||
BigDecimal balance = new BigDecimal(cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE)));
|
||||
String cardId = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID));
|
||||
String barcodeId = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_ID));
|
||||
int starred = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS));
|
||||
long lastUsed = cursor.getLong(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.LAST_USED));
|
||||
int zoomLevel = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.ZOOM_LEVEL));
|
||||
|
||||
int barcodeTypeColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE);
|
||||
int balanceTypeColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE);
|
||||
int headerColorColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_COLOR);
|
||||
|
||||
boolean was_in_group = !cursor.isNull(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIdsGroups.groupID));
|
||||
|
||||
CatimaBarcode barcodeType = null;
|
||||
Currency balanceType = null;
|
||||
Date expiry = null;
|
||||
Integer headerColor = null;
|
||||
|
||||
if (cursor.isNull(barcodeTypeColumn) == false)
|
||||
{
|
||||
barcodeType = CatimaBarcode.fromName(cursor.getString(barcodeTypeColumn));
|
||||
}
|
||||
|
||||
if (cursor.isNull(balanceTypeColumn) == false)
|
||||
{
|
||||
balanceType = Currency.getInstance(cursor.getString(balanceTypeColumn));
|
||||
}
|
||||
|
||||
if(expiryLong > 0)
|
||||
{
|
||||
expiry = new Date(expiryLong);
|
||||
}
|
||||
|
||||
if(cursor.isNull(headerColorColumn) == false)
|
||||
{
|
||||
headerColor = cursor.getInt(headerColorColumn);
|
||||
}
|
||||
|
||||
return new ManageGroupLoyaltyCard(id, store, note, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, starred, lastUsed,zoomLevel,was_in_group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public static final Creator<LoyaltyCard> CREATOR = new Creator<LoyaltyCard>() {
|
||||
@Override
|
||||
public LoyaltyCard createFromParcel(Parcel in) {
|
||||
return new LoyaltyCard(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoyaltyCard[] newArray(int size) {
|
||||
return new LoyaltyCard[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.text.InputType;
|
||||
@@ -176,7 +177,12 @@ public class ManageGroupsActivity extends CatimaAppCompatActivity implements Gro
|
||||
|
||||
@Override
|
||||
public void onEditButtonClicked(View view) {
|
||||
final String groupName = getGroupName(view);
|
||||
Group group = mDb.getGroup(getGroupName(view));
|
||||
Intent intent = new Intent(this, ManageGroupActivity.class);
|
||||
intent.putExtra("group", group);
|
||||
startActivity(intent);
|
||||
/*
|
||||
final String groupName = c;
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.enter_group_name);
|
||||
@@ -195,6 +201,7 @@ public class ManageGroupsActivity extends CatimaAppCompatActivity implements Gro
|
||||
dialog.show();
|
||||
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
|
||||
input.requestFocus();
|
||||
*/
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
56
app/src/main/res/layout/activity_manage_group.xml
Normal file
56
app/src/main/res/layout/activity_manage_group.xml
Normal file
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context="protect.card_locker.ManageGroupActivity">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/groups"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:tabMode="scrollable" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewEditGroupName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/enter_group_name" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editTextGroupName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:inputType="textPersonName" />
|
||||
|
||||
<include
|
||||
android:id="@+id/include"
|
||||
layout="@layout/content_manage_group" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
49
app/src/main/res/layout/content_manage_group.xml
Normal file
49
app/src/main/res/layout/content_manage_group.xml
Normal file
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
tools:context="protect.card_locker.ManageGroupActivity"
|
||||
tools:showIn="@layout/activity_manage_group">
|
||||
|
||||
<TextView
|
||||
style="@style/AppTheme.TextView.NoData"
|
||||
android:id="@+id/helpText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:text="@string/noGiftCards"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<TextView
|
||||
style="@style/AppTheme.TextView.NoData"
|
||||
android:id="@+id/noMatchingCardsText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:text="@string/noMatchingGiftCards"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<TextView
|
||||
style="@style/AppTheme.TextView.NoData"
|
||||
android:id="@+id/noGroupCardsText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:text="@string/noGroupCards"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list"
|
||||
app:layoutManager="androidx.recyclerview.widget.StaggeredGridLayoutManager"
|
||||
app:spanCount="@integer/main_view_card_columns"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scrollbars="vertical"
|
||||
android:background="@color/mainLoyaltyCardBackground"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</RelativeLayout>
|
||||
147
app/src/main/res/layout/manage_group_loyalty_card_layout.xml
Normal file
147
app/src/main/res/layout/manage_group_loyalty_card_layout.xml
Normal file
@@ -0,0 +1,147 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/row"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/information_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/thumbnail_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:cardCornerRadius="4dp"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/thumbnail_front"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignTop="@+id/storeContainer"
|
||||
android:layout_alignBottom="@+id/storeContainer"
|
||||
android:layout_gravity="center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/thumbnail"
|
||||
android:scaleType="centerCrop"
|
||||
android:layout_width="@dimen/cardThumbnailSize"
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
android:layout_centerVertical="true"
|
||||
android:contentDescription="@string/thumbnailDescription"
|
||||
android:src="@mipmap/ic_launcher" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/thumbnail_back"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/selected_thumbnail"
|
||||
android:layout_width="@dimen/cardThumbnailSize"
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
android:layout_centerInParent="true"
|
||||
android:contentDescription="@string/thumbnailDescription"
|
||||
android:scaleType="centerCrop"
|
||||
app:srcCompat="@drawable/ic_done" />
|
||||
|
||||
</RelativeLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/store"
|
||||
android:paddingStart="16dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="left"
|
||||
android:layout_toRightOf="@+id/thumbnail_container"
|
||||
android:layout_toLeftOf="@+id/star"
|
||||
android:textAppearance="?attr/textAppearanceHeadline6" />
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/star"
|
||||
android:layout_width="@dimen/cardThumbnailSize"
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
android:layout_centerVertical="true"
|
||||
android:scaleType="centerCrop"
|
||||
app:srcCompat="@drawable/ic_starred_white"
|
||||
android:contentDescription="@string/starImage"
|
||||
app:tint="?attr/colorControlNormal"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:visibility="gone" />
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/note"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textAppearance="?attr/textAppearanceBody2"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:visibility="gone" />
|
||||
|
||||
<View android:id="@+id/info_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:background="?android:attr/dividerVertical"
|
||||
android:visibility="gone" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
<TextView
|
||||
android:id="@+id/balance"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingEnd="8dp"
|
||||
android:textAppearance="?attr/textAppearanceBody2"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
app:drawableLeftCompat="@drawable/ic_baseline_payments_24"
|
||||
android:drawablePadding="4dp"
|
||||
android:layout_alignParentStart="true"
|
||||
android:visibility="gone" />
|
||||
<TextView
|
||||
android:id="@+id/expiry"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:gravity="center_vertical"
|
||||
android:textAppearance="?attr/textAppearanceBody2"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
app:drawableLeftCompat="@drawable/ic_baseline_access_time_24"
|
||||
android:drawablePadding="4dp"
|
||||
android:layout_toEndOf="@+id/balance"
|
||||
android:visibility="gone" />
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
10
app/src/main/res/menu/manage_group_menu.xml
Normal file
10
app/src/main/res/menu/manage_group_menu.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context="protect.card_locker.ManageGroupActivity">
|
||||
<item
|
||||
android:id="@+id/action_confirm"
|
||||
android:icon="@drawable/ic_done"
|
||||
android:title="@string/action_search"
|
||||
app:showAsAction="always|collapseActionView" />
|
||||
</menu>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" xmlns:tools="http://schemas.android.com/tools">
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="action_search">Search</string>
|
||||
<string name="action_add">Add</string>
|
||||
<plurals name="selectedCardCount">
|
||||
@@ -129,6 +129,9 @@
|
||||
<item quantity="one"><xliff:g>%d</xliff:g> card</item>
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> cards</item>
|
||||
</plurals>
|
||||
<string name="group_name_already_in_use">Group name already in use!</string>
|
||||
<string name="group_name_is_empty">Group name cannot be empty!</string>
|
||||
<string name="group_updated">Group updated!</string>
|
||||
<string name="all">All</string>
|
||||
<string name="deleteConfirmationGroup">Delete group?</string>
|
||||
<string name="failedOpeningFileManager">Install a file manager first.</string>
|
||||
@@ -245,4 +248,6 @@
|
||||
<string name="rate_this_app">Rate this app</string>
|
||||
<string name="on_google_play">on Google Play</string>
|
||||
<string name="report_error">Report Error</string>
|
||||
<string name="discard_changes">Discard Changes</string>
|
||||
<string name="discard_changes_confirm">There are unsaved changes that were made, do you wish to leave without saving?</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user