1

After removing the item from the Realtime Database and RecyclerView, I wish to add an UNDO function to recover the deleted item just now. May I know how to achieve it?

Below is my source code in Adapter class:

public Adapter(@NonNull FirebaseRecyclerOptions options) { super(options); }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate
                (R.layout.retrieved_layout,parent,false);
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position, @NonNull Model model) {
        holder.name.setText(model.getTask());
        holder.date.setText(model.getDate());
    }


    public static class MyViewHolder extends RecyclerView.ViewHolder {
        TextView name = itemView.findViewById(R.id.taskTv);
        TextView date = itemView.findViewById(R.id.dateTv);

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
        }
    }

And here is my MainActivity:

 recyclerView = findViewById(R.id.recyclerview);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    recyclerView.setHasFixedSize(true);

    LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
    linearLayoutManager.setReverseLayout(true);
    linearLayoutManager.setStackFromEnd(true);

    loader = new ProgressDialog(this);
    mAuth = FirebaseAuth.getInstance();
    onlineUserID = mAuth.getCurrentUser().getUid();
    reference = FirebaseDatabase.getInstance().getReference().child("task").child(onlineUserID);

    add = findViewById(R.id.add);
    add.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            addTask();
        }
    });

    ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleCallback);
    itemTouchHelper.attachToRecyclerView(recyclerView);

    // add task to recycler view
    FirebaseRecyclerOptions<Model> options = new FirebaseRecyclerOptions.Builder<Model>()
            .setQuery(reference, Model.class)
            .build();
    adapter = new Adapter(options);
}

// add task to real time database
private void addTask() {
    AlertDialog.Builder myDialog = new AlertDialog.Builder(this);
    LayoutInflater inflater = LayoutInflater.from(this);

    View view = inflater.inflate(R.layout.input, null);
    myDialog.setView(view);
    AlertDialog dialog = myDialog.create();

    final EditText task = view.findViewById(R.id.taskName);
    ImageView upload = view.findViewById(R.id.upload);

    upload.setOnClickListener((v) -> {
        String mTask = task.getText().toString().trim();
        String id = reference.push().getKey();
        String date = DateFormat.getDateInstance().format(new Date());

        if(TextUtils.isEmpty(mTask)){
            task.setError("Task name required");
            return;
        }
        else {
            loader.setMessage("Adding your data");
            loader.setCanceledOnTouchOutside(false);
            loader.show();

            Model model = new Model(mTask,id,date);
            reference.child(id).setValue(model).addOnCompleteListener(new OnCompleteListener<Void>() {
                @Override
                public void onComplete(@NonNull Task<Void> task) {
                    if(task.isSuccessful()) {
                        Toast.makeText(MainActivity.this, "Task added successfully !", Toast.LENGTH_SHORT).show();
                        loader.dismiss();
                    }
                }
            });
        }
        dialog.dismiss();
    });
    dialog.show();
}

// add task to recycler view
@Override
protected void onStart() {
    super.onStart();
    recyclerView.setAdapter(adapter);
    adapter.startListening();
}

// swipe to delete task
ItemTouchHelper.SimpleCallback simpleCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
    @Override
    public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
        return false;
    }

    @Override
    public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
        if (direction == ItemTouchHelper.LEFT) {
            reference.child(key).removeValue();
            Snackbar.make(recyclerView,"Task deleted", Snackbar.LENGTH_SHORT)
                    .setAction("Undo", new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {

                        }
                    })
         .show();
        }
    }
Alex Mamo
  • 130,605
  • 17
  • 163
  • 193

3 Answers3

1

@Eyosiyas's answer will work but it has a downside. If you want to get all children of a node where the property "isActive" holds the value of true, it will work perfectly fine. However, if you need to add another filter, for example, to filter the data according to a "time" property, this won't work, because the Firebase Realtime Database does not support queries on multiple properties.

What options do you have?

You can perform the delete operation, and if the user clicks on an "UNDO" button, write the data back. It's not the best solution, as it implies a delete operation followed by a write operation. Or you can simulate the delete operation. How? When the user clicks on the delete button, don't perform the actual delete operation. Wait to see if the user clicks "UNDO". If the user clicks "UNDO" don't delete anything, otherwise, delete the element.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
0

If you delete it from the real-time database then it is gone. If you want to achieve the undo functionality, I suggest you create another value isActive. Then when the user deletes you simply change isActive to false and don't show that data. If the user wants the data back you simply change isActive to true and the data is back.

So the overall process is that you don't actually delete the data but you deactivate it.

Eyosiyas
  • 1,422
  • 1
  • 9
  • 25
0

Eyosiyas's answer is correct but you can delete the item after undo option is no longer available. Let's say you have a field in your data model named 'deleted' and when user removes the item, you set it to true. At this point all selects from database should exclude items flagged as deleted. I noticed you show a SnackBar with undo action. When undo is clicked you should set deleted back to false and when SnackBar is dismissed you can actually delete item from database (because there is no way user can redo deletion without SnackBar and its action being available).

// Set 'deleted' to true in database
snackbar = Snackbar.make(
    binding.root,
    getString(R.string.item_deleted),
    Snackbar.LENGTH_LONG
).setAction(getString(R.string.undo)) {
    // Set 'deleted' back to false in database
}.addCallback(object : Snackbar.Callback() {
    override fun onDismissed(transientBottomBar: Snackbar, event: Int) {
        if (event != DISMISS_EVENT_ACTION)
            // Delete item from database
    }
}).setDuration(5000)
snackbar.show()
Reza Mohammadi
  • 106
  • 1
  • 1
  • 6