diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3602f9e97..1e81afb71 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,14 +1,13 @@
-
+
-
-
-
+
+
+
+
+
-
-
+ android:theme="@style/Theme.App.Starting">
-
+
-
+
-
-
+ android:theme="@style/AppTheme.NoActionBar">
-
+ android:theme="@style/AppTheme.NoActionBar">
+
+ android:windowSoftInputMode="stateHidden" />
-
+ android:windowSoftInputMode="stateHidden">
+
+
-
-
+
-
-
-
+ android:theme="@style/AppTheme.NoActionBar" />
+ android:windowSoftInputMode="stateHidden" />
+ android:theme="@style/AppTheme.NoActionBar" />
+ android:theme="@style/AppTheme.NoActionBar" />
+ android:theme="@style/AppTheme.NoActionBar">
-
-
+
+
+
+
+ android:grantUriPermissions="true">
+ android:resource="@xml/file_provider_paths" />
-
+
\ No newline at end of file
diff --git a/app/src/main/java/protect/card_locker/Group.java b/app/src/main/java/protect/card_locker/Group.java
index 1d5de8603..f39de2b50 100644
--- a/app/src/main/java/protect/card_locker/Group.java
+++ b/app/src/main/java/protect/card_locker/Group.java
@@ -2,6 +2,8 @@ package protect.card_locker;
import android.database.Cursor;
+import androidx.annotation.Nullable;
+
public class Group
{
public final String _id;
@@ -19,4 +21,22 @@ public class Group
return new Group(_id, order);
}
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (obj == null){
+ return false;
+ }
+ if (!(obj instanceof Group)){
+ return false;
+ }
+ Group anotherGroup = (Group)obj;
+ return _id.equals(anotherGroup._id) && order == anotherGroup.order;
+ }
+
+ @Override
+ public int hashCode(){
+ String combined = _id + "_" + order;
+ return combined.hashCode();
+ }
}
diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardCursorAdapter.java b/app/src/main/java/protect/card_locker/LoyaltyCardCursorAdapter.java
index 8572fe152..f259768c1 100644
--- a/app/src/main/java/protect/card_locker/LoyaltyCardCursorAdapter.java
+++ b/app/src/main/java/protect/card_locker/LoyaltyCardCursorAdapter.java
@@ -35,8 +35,8 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter {
+ String currentGroupName = mGroupNameText.getText().toString().trim();
+ if(!currentGroupName.equals(mGroup._id)){
+ if(currentGroupName.length() == 0){
+ Toast.makeText(getApplicationContext(), R.string.group_name_is_empty, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ if(!mGroupNameNotInUse) {
+ Toast.makeText(getApplicationContext(), R.string.group_name_already_in_use, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ }
+
+ mAdapter.commitToDatabase();
+ if(!currentGroupName.equals(mGroup._id)){
+ mDB.updateGroup(mGroup._id, currentGroupName);
+ }
+ Toast.makeText(getApplicationContext(), R.string.group_updated, Toast.LENGTH_SHORT).show();
+ finish();
+ });
+ // this setText is here because content_main.xml is reused from main activity
+ mHelpText.setText(getResources().getText(R.string.noGiftCardsGroup));
+ updateLoyaltyCardList();
+ }
+
+ private ArrayList adapterStateToIntegerArray(HashMap adapterState){
+ ArrayList ret = new ArrayList<>(adapterState.size() * 2);
+ for (Map.Entry entry : adapterState.entrySet()) {
+ ret.add(entry.getKey());
+ ret.add(entry.getValue()?1:0);
+ }
+ return ret;
+ }
+
+ private HashMap integerArrayToAdapterState(ArrayList in) {
+ HashMap ret = new HashMap<>();
+ if (in.size() % 2 != 0){
+ throw(new RuntimeException("failed restoring adapterState from integer array list"));
+ }
+ for(int i = 0;i < in.size(); i += 2){
+ ret.put(in.get(i), in.get(i+1) == 1);
+ }
+ return ret;
+ }
+
+
+ @Override
+ protected void onSaveInstanceState (@NonNull Bundle outState){
+ super.onSaveInstanceState(outState);
+
+ outState.putIntegerArrayList(SAVE_INSTANCE_ADAPTER_STATE, adapterStateToIntegerArray(mAdapter.exportInGroupState()));
+ outState.putString(SAVE_INSTANCE_CURRENT_GROUP_NAME, mGroupNameText.getText().toString());
+ }
+
+ private void updateLoyaltyCardList() {
+ mAdapter.swapCursor(mDB.getLoyaltyCardCursor());
+
+ if(mAdapter.getCountFromCursor() == 0)
+ {
+ mCardList.setVisibility(View.GONE);
+ mHelpText.setVisibility(View.VISIBLE);
+ }else {
+ mCardList.setVisibility(View.VISIBLE);
+ mHelpText.setVisibility(View.GONE);
+ }
+ }
+
+ private void leaveWithoutSaving(){
+ if (hasChanged()){
+ AlertDialog.Builder builder = new AlertDialog.Builder(ManageGroupActivity.this);
+ builder.setTitle(R.string.leaveWithoutSaveTitle);
+ builder.setMessage(R.string.leaveWithoutSaveConfirmation);
+ 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 void onBackPressed()
+ {
+ leaveWithoutSaving();
+ }
+
+ @Override
+ public boolean onSupportNavigateUp() {
+ onBackPressed();
+ return true;
+ }
+
+ private boolean hasChanged(){
+ return mAdapter.hasChanged() || !mGroup._id.equals(mGroupNameText.getText().toString().trim());
+ }
+
+ @Override
+ public void onRowLongClicked(int inputPosition)
+ {
+ // do nothing for now
+ }
+
+ @Override
+ public void onRowClicked(int inputPosition)
+ {
+ mAdapter.toggleSelection(inputPosition);
+
+ }
+}
diff --git a/app/src/main/java/protect/card_locker/ManageGroupCursorAdapter.java b/app/src/main/java/protect/card_locker/ManageGroupCursorAdapter.java
new file mode 100644
index 000000000..96f62fdfa
--- /dev/null
+++ b/app/src/main/java/protect/card_locker/ManageGroupCursorAdapter.java
@@ -0,0 +1,111 @@
+package protect.card_locker;
+
+import android.content.Context;
+import android.database.Cursor;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ManageGroupCursorAdapter extends LoyaltyCardCursorAdapter {
+ private HashMap mIndexCardMap;
+ private HashMap mInGroupOverlay;
+ private HashMap mIsLoyaltyCardInGroupCache;
+ private HashMap> mGetGroupCache;
+ final private Group mGroup;
+ final private DBHelper mDb;
+ public ManageGroupCursorAdapter(Context inputContext, Cursor inputCursor, CardAdapterListener inputListener, Group group){
+ super(inputContext, inputCursor, inputListener);
+ mGroup = new Group(group._id, group.order);
+ mInGroupOverlay = new HashMap<>();
+ mDb = new DBHelper(inputContext);
+ }
+
+ @Override
+ public void swapCursor(Cursor inputCursor) {
+ super.swapCursor(inputCursor);
+ mIndexCardMap = new HashMap<>();
+ mIsLoyaltyCardInGroupCache = new HashMap<>();
+ mGetGroupCache = new HashMap<>();
+ }
+
+ @Override
+ public void onBindViewHolder(LoyaltyCardListItemViewHolder inputHolder, Cursor inputCursor){
+ LoyaltyCard loyaltyCard = LoyaltyCard.toLoyaltyCard(inputCursor);
+ Boolean overlayValue = mInGroupOverlay.get(loyaltyCard.id);
+ if((overlayValue != null? overlayValue: isLoyaltyCardInGroup(loyaltyCard.id))) {
+ mAnimationItemsIndex.put(inputCursor.getPosition(), true);
+ mSelectedItems.put(inputCursor.getPosition(), true);
+ }
+ mIndexCardMap.put(inputCursor.getPosition(), loyaltyCard.id);
+ super.onBindViewHolder(inputHolder, inputCursor);
+ }
+
+ private List getGroups(int cardId){
+ List cache = mGetGroupCache.get(cardId);
+ if(cache != null){
+ return cache;
+ }
+ List groups = mDb.getLoyaltyCardGroups(cardId);
+ mGetGroupCache.put(cardId, groups);
+ return groups;
+ }
+
+ private boolean isLoyaltyCardInGroup(int cardId){
+ Boolean cache = mIsLoyaltyCardInGroupCache.get(cardId);
+ if(cache != null){
+ return cache;
+ }
+ List groups = getGroups(cardId);
+ if (groups.contains(mGroup)){
+ mIsLoyaltyCardInGroupCache.put(cardId, true);
+ return true;
+ }
+ mIsLoyaltyCardInGroupCache.put(cardId, false);
+ return false;
+ }
+
+ @Override
+ public void toggleSelection(int inputPosition){
+ super.toggleSelection(inputPosition);
+ Integer cardId = mIndexCardMap.get(inputPosition);
+ if (cardId == null){
+ throw(new RuntimeException("cardId should not be null here"));
+ }
+ Boolean overlayValue = mInGroupOverlay.get(cardId);
+ if (overlayValue == null){
+ mInGroupOverlay.put(cardId, !isLoyaltyCardInGroup(cardId));
+ }else{
+ mInGroupOverlay.remove(cardId);
+ }
+ }
+
+ public boolean hasChanged() {
+ return mInGroupOverlay.size() > 0;
+ }
+
+ public void commitToDatabase(){
+ for(Map.Entry entry: mInGroupOverlay.entrySet()){
+ int cardId = entry.getKey();
+ List groups = getGroups(cardId);
+ if(entry.getValue()){
+ groups.add(mGroup);
+ }else{
+ groups.remove(mGroup);
+ }
+ mDb.setLoyaltyCardGroups(cardId, groups);
+ }
+ }
+
+ public void importInGroupState(HashMap cardIdInGroupMap) {
+ mInGroupOverlay = new HashMap<>(cardIdInGroupMap);
+ }
+
+ public HashMap exportInGroupState(){
+ return new HashMap<>(mInGroupOverlay);
+ }
+
+ public int getCountFromCursor() {
+ return super.getCursor().getCount();
+ }
+}
diff --git a/app/src/main/java/protect/card_locker/ManageGroupsActivity.java b/app/src/main/java/protect/card_locker/ManageGroupsActivity.java
index 60e6dd35f..116dc7a1c 100644
--- a/app/src/main/java/protect/card_locker/ManageGroupsActivity.java
+++ b/app/src/main/java/protect/card_locker/ManageGroupsActivity.java
@@ -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;
@@ -9,6 +10,7 @@ import android.view.View;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.TextView;
+import android.widget.Toast;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
@@ -117,7 +119,16 @@ public class ManageGroupsActivity extends CatimaAppCompatActivity implements Gro
builder.setView(input);
builder.setPositiveButton(getString(R.string.ok), (dialog, which) -> {
- mDb.insertGroup(input.getText().toString());
+ String inputString = input.getText().toString().trim();
+ if(inputString.length() == 0){
+ Toast.makeText(getApplicationContext(), R.string.group_name_is_empty, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ if (mDb.getGroup(inputString) != null) {
+ Toast.makeText(getApplicationContext(), R.string.group_name_already_in_use, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ mDb.insertGroup(inputString);
updateGroupList();
});
builder.setNegativeButton(getString(R.string.cancel), (dialog, which) -> dialog.cancel());
@@ -176,25 +187,9 @@ public class ManageGroupsActivity extends CatimaAppCompatActivity implements Gro
@Override
public void onEditButtonClicked(View view) {
- final String groupName = getGroupName(view);
-
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.enter_group_name);
- final EditText input = new EditText(this);
- input.setInputType(InputType.TYPE_CLASS_TEXT);
- input.setText(groupName);
- builder.setView(input);
-
- builder.setPositiveButton(getString(R.string.ok), (dialog, which) -> {
- String newGroupName = input.getText().toString();
- mDb.updateGroup(groupName, newGroupName);
- updateGroupList();
- });
- builder.setNegativeButton(getString(R.string.cancel), (dialog, which) -> dialog.cancel());
- AlertDialog dialog = builder.create();
- dialog.show();
- dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
- input.requestFocus();
+ Intent intent = new Intent(this, ManageGroupActivity.class);
+ intent.putExtra("group", getGroupName(view));
+ startActivity(intent);
}
@Override
diff --git a/app/src/main/res/layout/activity_manage_group.xml b/app/src/main/res/layout/activity_manage_group.xml
new file mode 100644
index 000000000..4a1855f48
--- /dev/null
+++ b/app/src/main/res/layout/activity_manage_group.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4e14f6b22..3e5e9621c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,5 +1,5 @@
-
+
Search
Add
@@ -7,6 +7,7 @@
- %d cards selected
Click the + plus button to add a card, or import some from the ⋮ menu first.
+ You don\'t have any loyalty cards yet. Once you\'ve added some you can add them to the group here.
Didn\'t find anything. Try changing your search.
Name
Note
@@ -123,12 +124,16 @@
Card data exported
Enter group name
Groups
+ Edit Group
Click the + plus button to add groups for categorization first.
This group does not contain any cards
- %d card
- %d cards
+ Group name already in use
+ Group name cannot be empty
+ Group updated
All
Delete group?
Install a file manager first.
@@ -139,6 +144,7 @@
Manually enter card ID
Select image from gallery
Groups: %s
+ Editing Group: %s
Expires: %s
Expired: %s
Balance: %s