Search code examples
androidandroid-recyclerviewandroid-roomandroid-database

How to sort LiveData from Room Database? Doing a button to switch recyclerview's item's order


Hello, I have two problems which I can't solve at all, but which I think are ultimately related. I am triyng to do the following: I have an activity (Nota) which serves as an add/edit note java class. I want to display these notes in the recyclerview in the ListFragment. And this I can do. However, when I tried to complicate things a bit more, I arrived at a dead end. For one, I want to sort the items according to 2 parameters, primarily by location and then by number of stars (in the rating bar), when the location is the same. Secondly, I also want to have a toggle button whose job is to switch these parameters, meaning firstly to filter according to the number of stars, and then according to the location. Any help would be appreciated.

Now, moving on to what I have tried. The repository fetches the data using LiveData, which I can't seem to use for comparisons. So, I have tried to transform this to a list, I have tried to use MediatorLiveData and many other ways. Either way, I am still stuck pretty much where I started. So, this is my code:

NoteDao

public interface NoteDao {
    @Insert
    void insert(Notev2 note);

    @Update
    void update(Notev2 note);

    @Delete
    void delete(Notev2 note);

    @Query("DELETE FROM note_table")
    void deleteAllNotes();

    @Query("SELECT note_table.*, counter.count FROM note_table LEFT JOIN (SELECT note_table.location, count(note_table.location) as count FROM note_table GROUP BY note_table.location) counter ON counter.location = note_table.location ORDER BY counter.count DESC")
    LiveData<List<Notev2>> getAllNotes();
}

This is my ListFragment

public class ListFragment extends Fragment {

    public static final int ADD_NOTE_REQUEST = 1;
    public static final int EDIT_NOTE_REQUEST = 2;
    private NoteViewModel noteViewModel;

    private ListViewModel listViewModel;
    RecyclerView recyclerView_1;

    public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {
        listViewModel =
                ViewModelProviders.of(this).get(ListViewModel.class);
        View root = inflater.inflate(R.layout.fragment_list, container, false);
        final TextView textView = root.findViewById(R.id.text_list);
        listViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() {
            @Override
            public void onChanged(@Nullable String s) {
                textView.setText(s);
            }
        });


        recyclerView_1 = root.findViewById(R.id.recycler_view_list_1);
        recyclerView_1.setLayoutManager(new LinearLayoutManager(getActivity()));
        recyclerView_1.setHasFixedSize(true);

        final NoteAdapter nadapter = new NoteAdapter();
        recyclerView_1.setAdapter(nadapter);

        noteViewModel = ViewModelProviders.of(this).get(NoteViewModel.class);
        noteViewModel.getAllNotes().observe(getActivity(), new Observer<List<Notev2>>() {
            @Override
            public void onChanged(List<Notev2> notev2s) {
                nadapter.submitList(notev2s);
            }
        });

        nadapter.setOnItemClickListener(new NoteAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(Notev2 note) {
                Intent intent = new Intent(getActivity(), Nota.class);
                intent.putExtra(Nota.EXTRA_ID, note.getId());
                intent.putExtra(Nota.EXTRA_TITLE, note.getTitle_v2());
                intent.putExtra(Nota.EXTRA_LOCATION, note.getLocation_v2());
                intent.putExtra(Nota.EXTRA_STARS, note.getStars_v2());
                intent.putExtra(Nota.EXTRA_OPEN_1, note.getOpening_hours_1_v2());
                intent.putExtra(Nota.EXTRA_OPEN_2, note.getOpening_hours_2_v2());
                intent.putExtra(Nota.EXTRA_OPEN_3, note.getOpening_hours_3_v2());
                intent.putExtra(Nota.EXTRA_NOTAS, note.getNotas_v2());

                startActivityForResult(intent, EDIT_NOTE_REQUEST);
            }
        });

        final ToggleButton swap = (ToggleButton) root.findViewById(R.id.toggle_stars_location);
        swap.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (swap.isChecked()) {
                    swap.setBackgroundColor(ContextCompat.getColor(getActivity(), R.color.colorPrimary));
                    swap.setButtonDrawable(R.drawable.iswap_v2);
                    //noteViewModel.sortByStars();
                    nadapter.notifyDataSetChanged();
                }
            }
        });

        return root;

    }

    @Override
    public void onViewCreated(View view , @Nullable Bundle savedInstanceState) {
        Button button_add_nota = (Button) getView().findViewById(R.id.button_nota);

        button_add_nota.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getActivity(), Nota.class);
                startActivityForResult(intent, ADD_NOTE_REQUEST);

            }
        });


    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == ADD_NOTE_REQUEST && resultCode == MainActivity.RESULT_OK) {
            String title = data.getStringExtra(Nota.EXTRA_TITLE);
            String location = data.getStringExtra(Nota.EXTRA_LOCATION);
            String stars = data.getStringExtra(Nota.EXTRA_STARS);
            String open_1 = data.getStringExtra(Nota.EXTRA_OPEN_1);
            String open_2 = data.getStringExtra(Nota.EXTRA_OPEN_2);
            String open_3 = data.getStringExtra(Nota.EXTRA_OPEN_3);
            String notas = data.getStringExtra(Nota.EXTRA_NOTAS);

            Notev2 note = new Notev2(title, location, stars, open_1, open_2, open_3, notas);
            noteViewModel.insert(note);

            Toast.makeText(getActivity(), "Note saved", Toast.LENGTH_SHORT).show();
        } else if (requestCode == EDIT_NOTE_REQUEST && resultCode == MainActivity.RESULT_OK) {
            int id = data.getIntExtra(Nota.EXTRA_ID, -1);

            if (id == -1) {
                Toast.makeText(getActivity(), "Note can't be updated", Toast.LENGTH_SHORT).show();
                return;
            }

            String title = data.getStringExtra(Nota.EXTRA_TITLE);
            String location = data.getStringExtra(Nota.EXTRA_LOCATION);
            String stars = data.getStringExtra(Nota.EXTRA_STARS);
            String open_1 = data.getStringExtra(Nota.EXTRA_OPEN_1);
            String open_2 = data.getStringExtra(Nota.EXTRA_OPEN_2);
            String open_3 = data.getStringExtra(Nota.EXTRA_OPEN_3);
            String notas = data.getStringExtra(Nota.EXTRA_NOTAS);

            Notev2 note = new Notev2(title, location, stars, open_1, open_2, open_3, notas);
            note.setId(id);
            noteViewModel.update(note);

            Toast.makeText(getActivity(), "Note updated", Toast.LENGTH_SHORT).show();
        }  else if (requestCode == EDIT_NOTE_REQUEST && resultCode == MainActivity.RESULT_OK +10) {
            int id = data.getIntExtra(Nota.EXTRA_ID, -1);
            String title = data.getStringExtra(Nota.EXTRA_TITLE);
            String location = data.getStringExtra(Nota.EXTRA_LOCATION);
            String stars = data.getStringExtra(Nota.EXTRA_STARS);
            String open_1 = data.getStringExtra(Nota.EXTRA_OPEN_1);
            String open_2 = data.getStringExtra(Nota.EXTRA_OPEN_2);
            String open_3 = data.getStringExtra(Nota.EXTRA_OPEN_3);
            String notas = data.getStringExtra(Nota.EXTRA_NOTAS);
            Notev2 note = new Notev2(title, location, stars, open_1, open_2, open_3, notas);
            note.setId(id);
            noteViewModel.delete(note);
        } else {
            Toast.makeText(getActivity(), "Note not saved", Toast.LENGTH_SHORT).show();
        }
    }

}

This is my Nota Activity (which I use to add/edit notes)

button_save = (Button) findViewById(R.id.button_save);
        button_save.setEnabled(true);
        button_save.setClickable(true);
        button_save.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                saveNote();
            }
        });

        EditText edit_new_sight = (EditText) findViewById(R.id.new_sight);
        TextView opened_1 = findViewById(R.id.text_opened_1);
        TextView opened_2 = findViewById(R.id.text_opened_2);
        TextView opened_3 = findViewById(R.id.text_opened_3);
        RatingBar priority_bar = (RatingBar) findViewById(R.id.priority_stars);

        if (intent.hasExtra(EXTRA_ID)) {
            delete_nota.setVisibility(View.VISIBLE);
            toolbar_text.setText("Edit Sight");
            edit_new_sight.setText(intent.getStringExtra(EXTRA_TITLE));
            String slocation = intent.getStringExtra(EXTRA_LOCATION);
            if (slocation.contains("zioiu"))
                slocation = "";
            editLocation.setText(slocation);
            priority_bar.setRating(Float.parseFloat(intent.getStringExtra(EXTRA_STARS)));
            opened_1.setText(intent.getStringExtra(EXTRA_OPEN_1));
            opened_2.setText(intent.getStringExtra(EXTRA_OPEN_2));
            opened_3.setText(intent.getStringExtra(EXTRA_OPEN_3));
            notas.setText(intent.getStringExtra(EXTRA_NOTAS));

        } else {
            toolbar_text.setText("New Sight");
        }

        delete_nota.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                openDialog();
            }
        });

    }
    private void saveNote() {

        EditText noteTitlep = (EditText) findViewById(R.id.new_sight);
        EditText noteLocationp = (EditText) findViewById(R.id.new_location);
        RatingBar ratingBar = (RatingBar) findViewById(R.id.priority_stars);
        String noteStars = String.valueOf(ratingBar.getRating());
        String noteTitle = (String) "" + noteTitlep.getText();
        if (noteTitle.equals("")) {
            Toast toast = Toast.makeText(Nota.this, "Sorry, you need to input new sight!", Toast.LENGTH_LONG);
            toast.show();
        } else if (noteStars.equals("0.0")) {
            Toast toast = Toast.makeText(Nota.this, "Sorry, you need to input stars!", Toast.LENGTH_LONG);
            toast.show();
        } else {
            TextView noteOpen_1p = (TextView) findViewById(R.id.text_opened_1);
            TextView noteOpen_2p = (TextView) findViewById(R.id.text_opened_2);
            TextView noteOpen_3p = (TextView) findViewById(R.id.text_opened_3);
            EditText noteNotasp = (EditText) findViewById(R.id.edit_notes);
            String noteLocation = (String) "" + noteLocationp.getText();
            String noteOpen_1 = (String) "" + noteOpen_1p.getText();
            String noteOpen_2 = (String) "" + noteOpen_2p.getText();
            String noteOpen_3 = (String) "" + noteOpen_3p.getText();
            String noteNotas = (String) "" + noteNotasp.getText();
            if (noteLocation.equals("")) {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss z");
                String currentDateandTime = sdf.format(new Date());
                noteLocation = "zioiu" + currentDateandTime;
            }
            Intent data = new Intent();
            data.putExtra(EXTRA_TITLE, noteTitle);
            data.putExtra(EXTRA_LOCATION, noteLocation);
            data.putExtra(EXTRA_STARS, noteStars);
            data.putExtra(EXTRA_OPEN_1, noteOpen_1);
            data.putExtra(EXTRA_OPEN_2, noteOpen_2);
            data.putExtra(EXTRA_OPEN_3, noteOpen_3);
            data.putExtra(EXTRA_NOTAS, noteNotas);
            int id = getIntent().getIntExtra(EXTRA_ID, -1);
            if (id != -1) {
                data.putExtra(EXTRA_ID, id);
            }
            setResult(RESULT_OK, data);
            Toast.makeText(Nota.this, "Sight Saved", Toast.LENGTH_SHORT).show();
            finish();
        }

This is my NoteDatabase(v2)

public abstract class NoteDatabase_v2 extends RoomDatabase {
    private static NoteDatabase_v2 instance;

    public abstract NoteDao noteDao();

    public static synchronized NoteDatabase_v2 getInstance(Context context) {
        if (instance == null) {
            instance = Room.databaseBuilder(context.getApplicationContext(),
                    NoteDatabase_v2.class, "note_database")
                    .fallbackToDestructiveMigration()
                    .addCallback(roomCallback)
                    .build();
        }
        return instance;
    }

    private static RoomDatabase.Callback roomCallback = new RoomDatabase.Callback() {
        @Override
        public void onCreate(@NonNull SupportSQLiteDatabase db) {
            super.onCreate(db);
            new PopulateDbAsyncTask(instance).execute();
        }
    };

    @NonNull
    @Override
    protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration config) {
        return null;
    }

    @NonNull
    @Override
    protected InvalidationTracker createInvalidationTracker() {
        return null;
    }

    @Override
    public void clearAllTables() {

    }

    private static class PopulateDbAsyncTask extends AsyncTask<Void, Void, Void> {
        private NoteDao noteDao;

        private PopulateDbAsyncTask(NoteDatabase_v2 db) {
            noteDao = db.noteDao();
        }

        @Override
        protected Void doInBackground(Void... voids) {
            noteDao.insert(new Notev2("Title 1", "Location 1", "1", "Open_1_1", "Open_2_1", "Open_3_1", "Notas_1"));
            noteDao.insert(new Notev2("Title 2", "Location 2", "2", "Open_1_2", "Open_2_2", "Open_3_2", "Notas_2"));
            noteDao.insert(new Notev2("Title 3", "Location 3", "3", "Open_1_3", "Open_2_3", "Open_3_3", "Notas_3"));
            return null;
        }
    }

Sorry about so many javas, but there's still more This is my NoteRepository

    private NoteDao noteDao;
    private LiveData<List<Notev2>> allNotes;
    private MediatorLiveData<List<Notev2>> medallnotes;

    public NoteRepository(Application application) {
        NoteDatabase_v2 database = NoteDatabase_v2.getInstance(application);
        noteDao = database.noteDao();
        allNotes = noteDao.getAllNotes();
    }

    public void insert(Notev2 note) {
        new InsertNoteAsyncTask(noteDao).execute(note);
    }

    public void update(Notev2 note) {
        new UpdateNoteAsyncTask(noteDao).execute(note);
    }

    public void delete(Notev2 note) {
        new DeleteNoteAsyncTask(noteDao).execute(note);
    }

    public void deleteAllNotes() {
        new DeleteAllNotesAsyncTask(noteDao).execute();
    }

    public MediatorLiveData<List<Notev2>> getAllNotes() {
        return (MediatorLiveData<List<Notev2>>) allNotes;
    }

    private static class InsertNoteAsyncTask extends AsyncTask<Notev2, Void, Void> {
        private NoteDao noteDao;

        private InsertNoteAsyncTask(NoteDao noteDao) {
            this.noteDao = noteDao;
        }

        @Override
        protected Void doInBackground(Notev2... notes) {
            noteDao.insert(notes[0]);
            return null;
        }
    }

    private static class UpdateNoteAsyncTask extends AsyncTask<Notev2, Void, Void> {
        private NoteDao noteDao;

        private UpdateNoteAsyncTask(NoteDao noteDao) {
            this.noteDao = noteDao;
        }

        @Override
        protected Void doInBackground(Notev2... notes) {
            noteDao.update(notes[0]);
            return null;
        }
    }

    private static class DeleteNoteAsyncTask extends AsyncTask<Notev2, Void, Void> {
        private NoteDao noteDao;

        private DeleteNoteAsyncTask(NoteDao noteDao) {
            this.noteDao = noteDao;
        }

        @Override
        protected Void doInBackground(Notev2... notes) {
            noteDao.delete(notes[0]);
            return null;
        }
    }

    private static class DeleteAllNotesAsyncTask extends AsyncTask<Void, Void, Void> {
        private NoteDao noteDao;

        private DeleteAllNotesAsyncTask(NoteDao noteDao) {
            this.noteDao = noteDao;
        }

        @Override
        protected Void doInBackground(Void... voids) {
            noteDao.deleteAllNotes();
            return null;
        }
    }
}

This is my Note(v2)

    @PrimaryKey(autoGenerate = true)
    protected int id;
    String title;
    String location;
    String stars;
    String opening_hours_1;
    String opening_hours_2;
    String opening_hours_3;
    String notas;

    public Notev2(String title, String location, String stars, String opening_hours_1, String opening_hours_2,
                  String opening_hours_3, String notas) {
        this.title = title;
        this.location = location;
        this.stars = stars;
        this.opening_hours_1 = opening_hours_1;
        this.opening_hours_2 = opening_hours_2;
        this.opening_hours_3 = opening_hours_3;
        this.notas = notas;


    }

    public void setId(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public String getTitle_v2() {
        return title;
    }

    public String getLocation_v2() {
        return location;
    }

    public String getStars_v2() {
        return stars;
    }

    public String getOpening_hours_1_v2() {
        return opening_hours_1;
    }

    public String getOpening_hours_2_v2() {
        return opening_hours_2;
    }

    public String getOpening_hours_3_v2() {
        return opening_hours_3;
    }

    public String getNotas_v2() {
        return notas;
    }


}

This is my NoteViewModel

public class NoteViewModel extends AndroidViewModel {
    private NoteRepository repository;
    private LiveData<List<Notev2>> allNotes;

    public NoteViewModel(@NonNull Application application) {
        super(application);
        repository = new NoteRepository(application);
        allNotes = repository.getAllNotes();
    }

    public void insert(Notev2 note) {
        repository.insert(note);
    }

    public void update(Notev2 note) {
        repository.update(note);
    }

    public void delete(Notev2 note) {
        repository.delete(note);
    }

    public LiveData<List<Notev2>> getAllNotes() {
        return allNotes;
    }

    public void sortByStars() {
        Collections.sort((List<Notev2>) allNotes, ByStars);
    }
    public Comparator<Notev2> ByStars = new Comparator<Notev2>() {
        @Override
        public int compare(Notev2 o1, Notev2 o2) {
            return Integer.valueOf(o1.stars).compareTo(Integer.valueOf(o2.stars));
        }
    };
}

And finally, this is my NoteAdapter

public class NoteAdapter extends ListAdapter<Notev2, NoteAdapter.NoteHolder> {
    private OnItemClickListener listener;

    public NoteAdapter() {
        super(DIFF_CALLBACK);
    }

    private static final DiffUtil.ItemCallback<Notev2> DIFF_CALLBACK = new DiffUtil.ItemCallback<Notev2>() {
        @Override
        public boolean areItemsTheSame(Notev2 oldItem, Notev2 newItem) {
            return oldItem.getId() == newItem.getId();
        }

        @Override
        public boolean areContentsTheSame(Notev2 oldItem, Notev2 newItem) {
            return oldItem.getTitle_v2().equals(newItem.getTitle_v2()) &&
                    oldItem.getLocation_v2().equals(newItem.getLocation_v2()) &&
                    oldItem.getStars_v2().equals(newItem.getStars_v2()) &&
                    oldItem.getOpening_hours_1_v2().equals(newItem.getOpening_hours_1_v2()) &&
                    oldItem.getOpening_hours_2_v2().equals(newItem.getOpening_hours_2_v2()) &&
                    oldItem.getOpening_hours_3_v2().equals(newItem.getOpening_hours_3_v2()) &&
                    oldItem.getNotas_v2().equals(newItem.getNotas_v2());
        }
    };

    @NonNull
    @Override
    public NoteHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.cardview_list_1, parent, false);
        return new NoteHolder(itemView);

    }

    @Override
    public void onBindViewHolder(@NonNull NoteHolder holder, int position) {
        Notev2 currentNote = getItem(position);
        holder.nTitle.setText(currentNote.getTitle_v2());
        String slocation = "" + currentNote.getLocation_v2();
        if (slocation.contains("zioiu"))
            slocation = "";
        holder.nLocation.setText(slocation);
        Float numStars = Float.parseFloat(currentNote.getStars_v2());
        holder.nStars.setRating(numStars);
        holder.nOpen.setText(new StringBuilder().append(currentNote.getOpening_hours_1_v2()).append(currentNote.getOpening_hours_2_v2()).append(currentNote.getOpening_hours_3_v2()).toString());
        holder.nNotas.setText(currentNote.getNotas_v2());
    }

    public Notev2 getNoteAt(int position) {
        return getItem(position);
    }


    class NoteHolder extends RecyclerView.ViewHolder {
        TextView nTitle,nLocation,nOpen,nNotas;
        RatingBar nStars;

        public NoteHolder(View itemView) {
            super(itemView);
            nTitle = itemView.findViewById(R.id.textview_title);
            nLocation = itemView.findViewById(R.id.textview_location);
            nStars = itemView.findViewById(R.id.ratingbar_priority);
            nOpen = itemView.findViewById(R.id.textview_openinghours);
            nNotas = itemView.findViewById(R.id.textview_notas);

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int position = getAdapterPosition();
                    if (listener != null && position != RecyclerView.NO_POSITION) {
                        listener.onItemClick(getItem(position));
                    }
                }
            });
        }
    }

    public interface OnItemClickListener {
        void onItemClick(Notev2 note);
    }

    public void setOnItemClickListener(OnItemClickListener listener) {
        this.listener = listener;
    }

}

Thank you very much if you managed to read it this far. All help is welcome.


Solution

  • After much investigation, I finally arrived at an answer to my question. The key is to create new dao queries to filter the information, according to what you need. This is my update NoteDao

    @Dao
    public interface NoteDao {
        @Insert
        void insert(Notev2 note);
    
        @Update
        void update(Notev2 note);
    
        @Delete
        void delete(Notev2 note);
    
        @Query("DELETE FROM note_table")
        void deleteAllNotes();
    
        @Query("SELECT note_table.*, counter.count FROM note_table LEFT JOIN (SELECT note_table.location, count(note_table.location) as count FROM note_table GROUP BY note_table.location) counter ON counter.location = note_table.location ORDER BY counter.count DESC, stars DESC")
        LiveData<List<Notev2>> getAllNotes();
    
        @Query("SELECT note_table.*, counter.count FROM note_table LEFT JOIN (SELECT note_table.location, count(note_table.location) as count FROM note_table GROUP BY note_table.location) counter ON counter.location = note_table.location ORDER BY stars DESC, counter.count DESC")
        LiveData<List<Notev2>> getAllNotesStars();
    
        //@Query("SELECT location, count(location) FROM note_table GROUP BY location ORDER BY count(location) DESC ")
        //LiveData<List<String>> getAllLocations();
    }