1

So, I am working on a project and I want to delete an item from a RecyclerView but I want the user to be able to restore that item by pressing "UNDO" in a Snackbar. The RecyclerView items are pulling their data from an SQLite database and when the user deletes an item it will remove it from the RecyclerView and the corresponding row of my table in the database as well. My problem is I cannot seem to figure out how to recover that deleted data (The SQLite data).

I tried to make a method in my DataBaseHelper class which takes the position as a parameter so my cursor knows which row to access then I used the cursor to assign all the values of that row to an AlarmData(my setter and getter class) object and return that back to my Adapter Class so I can try to restore the table's row in my Snackbar's UNDO action method.

Here's the code:

DataBaseHelper.java

// Is public AlarmData even legal? that's my setter and getter class
// and the object type I'm trying to return

   public AlarmData recoverAlarms(int position) {

    AlarmData recoverData = new AlarmData();

    String ALARM_RESTORE_QUERY = "SELECT * FROM " + TABLE_ALARMS + " WHERE " + COLUMN_INDEX +
            " = " + position + ";";

    SQLiteDatabase db = getReadableDatabase();
    Cursor cursor = db.rawQuery(ALARM_RESTORE_QUERY, null);

    try {
        if (cursor.moveToFirst()) {
            do {

                recoverData.set_dispLabel(cursor.getString(cursor.getColumnIndex(COLUMN_LABEL)));
                recoverData.set_dispTime(cursor.getString(cursor.getColumnIndex(COLUMN_TIME)));
                recoverData.set_alarmId(cursor.getInt(cursor.getColumnIndex(COLUMN_INDEX)));

            } while (cursor.moveToNext());
        }
    } catch (Exception e) {
        throw e;
    } finally {
        if (cursor != null && !cursor.isClosed()) {
            cursor.close();
            db.close();
        }

    }

    return recoverData;
}

AlarmAdapter.java

   public void deleteAlarm(final int position, final View view) {

    final AlarmData recoverAlarmData = new DataBaseHelper(context, null, null, 1).recoverAlarms(position); //INITIALIZES VARIABLE TO THE OBJECT THAT'S RETURNED
    final AlarmData recoverItem = alarmData.get(position); //Recovers RecyclerView item (this works)
    final DataBaseHelper dataBaseHelper = new DataBaseHelper(context, null, null, 1);


    alarmData.remove(position);
    notifyItemRemoved(position);
    dataBaseHelper.deleteAlarms(position); //DELETES TABLE'S ROW

    Snackbar snackbar = Snackbar.make(view, "Alarm Removed", Snackbar.LENGTH_LONG)
            .setAction("UNDO", new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                    alarmData.add(position, recoverItem);
                    notifyItemInserted(position); //RESTORES RECYCLERVIEW ITEM

                    dataBaseHelper.addAlarm(recoverAlarmData); //TRIES TO RECOVER TABLE

                }
            });

    snackbar.show();


}

Just to clairify, I'd like to know how I can make a copy of a specific row in my database so I can recreate it when the user hits UNDO on the Snackbar. and also maybe explain to me what I'm doing wrong. Lol..

I hope I didn't show my noobishness too much here. Your help would be greatly appreciated. Thank you!

  • I don't know how is this usually made, but what about not deleting it? Add an `active` column in your table, when the user deletes the field you set it as "0" (unactive) and if he undoes it you return the `active` value to "1". In your recyclerview you only show the `active` elements. Other question would be how to delete the unactive items after some time. – Alberto Méndez May 26 '16 at 10:01
  • You could just show the snak bar and update the recycler view but not really execute the delete method. Make it hold for some time until the snack bar disappears or the use hits undo. If the use hits undo insert the item to the recyclerview and notify the adapter of the changes. If the snack bar disappears without user interaction make necessary changes to the database possibly in a new thread. – Malith Lakshan May 26 '16 at 10:09
  • Either you can add a column in your table name as isDeleted type boolean. Do not perform **Hard Delete** apply **Soft Delete** means do not delete the item from table just make an **update query** if user deletes set it TRUE and else FALSE if undo.Now you just need to show only UN-Deleted data. make a query that will fetch only False flag's data. – Devendra Singh May 26 '16 at 10:23
  • Thank you for your help guys. I happened to stumble upon a solution that requires very little effort. Lol Apparently Snackbars have very handy callback methods! – Andrew Krause May 26 '16 at 11:18

1 Answers1

5

I probably should have spent a little more time looking before asking. I found the perfect solution here: How can I be notified when a Snackbar has dismissed itself?

Apparently Snackbars have awesome callback methods you can override. Here's the documentation: https://developer.android.com/reference/android/support/design/widget/Snackbar.Callback.html

I simply use an if statement to check what the reason for the Snackbar's dismissal was. I will delete the row in my table for any reason except for if its dismissal was do to an action click (when the user hits "UNDO").

My code:

AlarmAdapter

        Snackbar snackbar = Snackbar.make(view, "Alarm Removed", Snackbar.LENGTH_LONG)
            .setAction("UNDO", new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                    alarmData.add(position, recoverItem);
                    notifyItemInserted(position);

                }

            }).setCallback(new Snackbar.Callback() {
                @Override
                public void onDismissed(Snackbar snackbar, int dismissType) {
                    super.onDismissed(snackbar, dismissType);

                    if(dismissType == DISMISS_EVENT_TIMEOUT || dismissType == DISMISS_EVENT_SWIPE
                            || dismissType == DISMISS_EVENT_CONSECUTIVE || dismissType == DISMISS_EVENT_MANUAL)
                    dataBaseHelper.deleteAlarms(position);

                }
            });
Community
  • 1
  • 1
  • 4
    You could simplify that to `if (dismissType != DISMISS_EVENT_ACTION)`. – Sumit Jul 18 '16 at 21:41
  • Works in late 2018. The signature has changed and includes an "event" int. 1 = pressed undo, 2 = snackbar timed out – alextsil Nov 28 '18 at 10:22