Creating a Note Taking Application Using Realm Java - Part 2

in #utopian-io7 years ago (edited)

realmDark.jpg

Repository

https://github.com/realm/realm-java

What Will I Learn?

  • Creating a Note Saving Android Application using Synchronous realm.
  • How to Query and update and Delete Synchronous Realm.
  • How to create Relationship between Realms.
  • How to add ChangeListener to Synchronous Realm.

Resources

Difficulty

  • Intermediate

Tutorial Duration - 40 - 45Mins

Tutorial Content

The part 1 of today's series can be found – here

In this tutorial series, we are going to be developing a note taking android application that the user can take down notes and have it synchronized to the Realm cloud object server which will enable the user to be able to have his note synchronized on multiple devices.

The Application so far allows the user to login using the nickname Realm Cloud Server Object authentication method and then the user can write a note and then save it to the realm cloud server.

In today's tutorial, we are going to be able to group user notes by authors and then each author is going to be able to see their individual notes and then a note can be updated, and also new notes can be created.

Outline

  • Dependencies Used
  • Minor Changes
  • New Feature Implementation
  • Saving Note To Realm Object Server

Dependencies used

_Please refer to part 1 here of this tutorial series to see the dependencies used. _

Minor Changes

  • Change of the FAB icon from a + sign to a more descriptive icon.
  • We are going to move the login Options Menu from the NewNote Activity to the ViewNote Activity.

Major Changes

For the major changes of our application, we are going to be doing the following:

  1. Create a new model class.
  2. Create a ViewNotes Activity.
  3. Create a RecyclerView Adapter.

Create a new model class

In order to arrange notes saved by each Author, we are going to be creating a new model class - Author, therefore, create a new java class file - right click on the java file => New => java Class.
NB: See images for more instruction

new Java Class.PNG

Next, modify the java class file to have the following codes -

@Getter
@Setter
public class Author extends RealmObject{
    @PrimaryKey
    @Required
    String id;
    @Required
    Date timeStamp;
    @Required
    String name;

    RealmList<Notes> notes;
}

Code Explanation

  • We ensure that all fields are required by adding the @Required annotation to all fields and adding the @PrimaryKey annotation to the id filed.
  • The notes object will serve as the Notes associated which each author and the field - name will hold the name of the authors.
  • We then add the @Getter and @Setter annotation with the lombok library to all the fields to eliminate all Setter and Getter boiler plate codes.

Create a ViewNotes Activity

For each Author to be able to see notes only related to them, we are going to create an Activity responsible for displaying notes associated with them in a RecyclerView and then we will implement the ability for notes to be deleted by users swiping left or right and then users will be also be able to logout by clicking the logout OptionMenu item and then in this Activity the author will be able to add new notes by clicking the Floating Action Button in our layout.

In other to make our RecyclerView Adapter aware of changes made to our realm such as a delete or an addition to the realm, we are going to be adding a ChangeListener to the list of notes associated with an Author.

  1. Create New Activity :
    To create a new activity, right click on the java folder => New => Activity => Basic Activity, to follow this tutorial, name the activity - Viewnotes.

  2. Add RecyclerView in the layout file :
    In the layout file of the created activity - content_viewnotes , modify it to as below -

//RootLayout - ConstraintLayout
<android.support.v7.widget.RecyclerView
    android:id="@+id/notes_recView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="8dp"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
/>

NB: The above code only adds a RecyclerView in our acitity layout file with the id - notes_recView that will be used to display all the notes associated with an author.

  1. Create RecyclerView Adapter.
    Before we create our Adapter class, we are going to be creating a custom view that all rows will be displayed as such.

Therefore, create a new layout file by right clicking on the layout folder beneath the res folder => New => Layout resource file and name the file - single_layout_note.

Next, modify the xml as follows:

<TextView
    android:id="@+id/note_title"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="10dp"
    android:text="@string/note_title"
    android:textSize="17sp"
    android:textColor="@android:color/black"
    />

<View
    android:layout_width="match_parent"
    android:layout_height="0.5dp"
    android:background="#000"
    />

Code Explanation
The above xml file creates a single Textview displayed on the screen with the id - note_title that creates a layout as below:

Screenshot_20180615-124941.png

Next, we are going to be creating a RecyclerView adapter that we will be tied to our RecyclerView to in order to rightly display the details of each note, we will also be handling the onClick of each notes in the adapter and then pass the specific noteId to the NewNotes activity should in case the user wants to update the details of that note.

Create a new java class file => right click on java folder => New => Java Class file and name - NotesAdapter which extend the RecyclerView.Adapter<>.Then edit the java class file as below :

public class NotesAdapter extends RecyclerView.Adapter<NotesAdapter.NotesViewHolder> {
    List<Notes> notes;
    Context context;

    public NotesAdapter(Context context,List<Notes> notes) {
        this.context = context;
        this.notes = notes;
    }

    public void setData(List<Notes> setDataNotes) {
        if (setDataNotes == null) {
            setDataNotes = Collections.emptyList();
        }
        this.notes = setDataNotes;
        notifyDataSetChanged();
    }

    public Notes getItem(int position) {
        return notes.get(position);
    }

    @Override
    public int getItemCount() {
        return notes.size();
    }

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

    @Override
    public void onBindViewHolder(NotesViewHolder holder, int position)  {
        final Notes notes = getItem(position);
        if (notes != null) {
            holder.Title.setText(notes.getNoteTitle());
            holder.Title.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent intent = new Intent(context, NewNote.class);
                    intent.putExtra("note_id", notes.getNoteId());
                    context.startActivity(intent);
                }
            });
        }
    }

    class NotesViewHolder extends RecyclerView.ViewHolder {
        TextView Title;

        NotesViewHolder(View itemView) {
            super(itemView);
            Title = itemView.findViewById(R.id.note_title);
        }
    }
}

Code Explanation

  • We create a constructor - NotesAdapter and we initialize both the Notes object - notes and also the Context variable - context.

setData()

  • The setData() method serves as a method that will inform the Adapter of a change in the Notes object. (e.g - if a note is deleted or modified, the adapter is informed of such modification) with the notifyDataSetChanged() method.

NotesViewHolder

  • In the NotesViewHolder method, we inflate the adapter view using the already created single_layout_note xml file.

onBindViewHolder

  • On the onBindViewHolder() method, we get a Note object of each of the rows and then we set the display text to the title of the text by - notes.getNoteId() and then we set an onClick listener on each row that simply starts the NewNote activity passing the note's id as an argument. This note id will be used to get the note details and populate it if the user chooses to update its details.

Next, we are going to modify our MainActivity.java class file to create a new Author once the author is authenticated on the server and then also we pass in the author ID to the ViewNotes activity java class file that will aid us to get Notes associated which that author.

To achieve the above, we are going to be modifying the onSuccess() method of our logUserIn() method and also the startViewNotesActivity() method.

NB: Only modification to the methods will be shown

logUserIn()

@Override
public void onSuccess(SyncUser user) {
    Realm.setDefaultConfiguration(SyncConfiguration.automatic());
    realm = Realm.getDefaultInstance();

    final Author note_author = new Author();

    note_author.setName(author);
    note_author.setTimeStamp(new Date());
    note_author.setId(user.getIdentity());

    realm.executeTransactionAsync(new Realm.Transaction() {
        @Override
        public void execute(Realm realm) {
            realm.insert(note_author);
        }
    });

    showProgressDialog(false);
    startViewNotesActivity();
}

Code Explanation

  1. We first initialize the default Configuration of the Realm file and then set it to - SyncConfiguration.automatic() and then we set the realm variable to the Realms default instance.
  2. Next, we create a new Author Object and then set's its ID as the created user's identity as user.getIdentity() using the user object passed into the method.
  3. We then perform an asynchronous operation on our realm by inserting the newly created author - realm.insert(note_author).
  4. Next, we call the showProgressDialog() method which stops the progressDialog currently being shown and lastly we call the startViewNotesActivity() method.

startViewNotesActivity()

private void startViewNotesActivity() {
    Intent intent = new Intent(this, Viewnotes.class);
    authors_id = SyncUser.current().getIdentity();
    intent.putExtra("author_id",authors_id);

    startActivity(intent);
}

Code Explanation

  • We start an explicit intent specifying the Viewnotes class and then we get the current identity of the logged in user - SyncUser.current().getIdentity(), store it a String variable - authors_id and then start the Activity.

Viewnotes java class file

In our Viewnotes java class file, we are going to be displaying all notes for a particular author and each author can create a new note by clicking the FAB button.

Firstly, we are going to be creating a ChangeListener that will be used to alert the Adapter class when a change is done to the notes of an author.
Above the onCreate() method, add the following code -


private NotesAdapter notesRecyclerAdapter;
private String authorsId;
private Author author;
private Realm realm;
private RealmResults<Notes> notes;
    
private RealmChangeListener<RealmResults<Notes>> realmChangeListener = new RealmChangeListener<RealmResults<Notes>>() {
    @Override
    public void onChange(RealmResults<Notes> notes) {
        notesRecyclerAdapter.setData(notes);
    }
};

Code Explanation

  1. We firstly create a NotesAdapter object - notesRecyclerAdapter which will be used as the Adapter of our RecyclerView, next a String variable - authorsId variable which will be used to retrieve the authors id sent by the MainActivity activity, next an Author object - author which will be used to get the specific logged in user and a realm object is also created and lastly a RealmResult object of type Notes - notes is created which will hold all notes associated with the specific author.
  2. Lastly, we create a RealmChangeListener object which will be register on the notes object and its essence is just to notify the adapter of changes / modifications on the objects - notesRecyclerAdapter.setData(constestants);

Next, in our onCreate() method, we have to set the configuration of our realm and then set it as the configuration of our realm variable.

//onCreate() method 
Realm.setDefaultConfiguration(SyncConfiguration.automatic());
realm = Realm.getDefaultInstance();

fab.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        startNewNoteActivity();
    }
});

setUpNotesDisplay();
}

NB: we set the default configuration of our Realm to - Realm.setDefaultConfiguration(SyncConfiguration.automatic()); and then set it as the default configuration of our realm variable, next we set an onClick listener on our FAB button and lastly call the setUpNotesDisplay() method which is responsible for getting all the notes associated with a specific author and then initialize a ItemTouchHelper.SimpleCallback object on our RecyclerView.

setUpNotesDisplay()

authorsId = getIntent().getStringExtra("author_id");

author = realm.where(Author.class).equalTo("id", authorsId).findFirst();

notes = author.getNotes().sort("timestamp", Sort.ASCENDING);
notes.addChangeListener(realmChangeListener);

notesRecyclerAdapter = new NotesAdapter(this, notes);

setTitle(author.getName() + "'s Notes");

notesRecView.setLayoutManager(new LinearLayoutManager(this));
notesRecView.setAdapter(notesRecyclerAdapter);
...

Code Explanation

  1. We get the id of the specific author using the authors_id sent to this activity and store in the - authorsId variable.
  2. Next, we set the author variable to the Author object in our Realm Cloud Object that its id field matches that of the id sent by the MainActivity activity.
  3. We then set the notes variable to the notes of that author gotten and then sort it by the timestamp field in an ASCENDING order.and then we add the change listener already created to the Note's object.
  4. We then initialize the notesRecyclerAdapter with the Notes object stored in - notes, we then set the title of the Activity to the Authors name contatenated with - "'s Notes" (e.g Edet's Notes).
  5. We then set a LayoutManager of the RecyclerView and also set the Adapter of the RecyclerView to the initialzed Adapter - notesRecyclerAdapter.

Next, we add the swipe Touch listener that will delete a note from the realm when the user swipes either to the left or to the right.

Just below the last code of the above section, we add the following -

ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {

@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
    return false;
}

@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
    int position = viewHolder.getAdapterPosition();
    final String id = notesRecyclerAdapter.getItem(position).getNoteId();
    realm.executeTransactionAsync(new Realm.Transaction() {
        @Override
        public void execute(Realm realm) {
            Notes note = realm.where(Notes.class).equalTo("noteId",id).findFirst();
            if (note != null)
                note.deleteFromRealm();

        }
    });
}
};

ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
itemTouchHelper.attachToRecyclerView(notesRecView);

Code Explanation

  1. We initialze the swipe listener to left and right - ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT
  2. In the onSwiped() method, we get the current position of the note the user swiped by calling - int position = viewHolder.getAdapterPosition(); and then we get the specific noteId of that note with - final String id = notesRecyclerAdapter.getItem(position).getNoteId();, we then execute an Asynchronous transaction on the realm and delete the note object that match that particular note ID with - note.deleteFromRealm();.

__

private void startNewNoteActivity() {
    Intent intent = new Intent(this, NewNote.class);
    intent.putExtra("author_id", authorsId);
    startActivity(intent);
}

_NB: We intend to update the note of the Author when the user clicks it from the recyclerView or add the notes to the Author Notes when a new Note is created.

NB: since we moved the logout menu from the NewNotes acitivity to the Viewnotes activity - we also implemented the onCreateOptionsMenu() method and also the onOptionsItemSelected() method to hanlde the logout of the user as explained in part 1.


NewNote Acitivity
In this acitivity, we are going to be making some modifications to the file:

  1. We are going to be retrieving the author id sent from the ViewNotes activity in order for us to save new notes to the specific Author.
  2. If the user clicks on an existing note and intends to modify it, we are going to get the note ID sent from our NotesAdapter.
  3. We intend to check if the note_id is null, the user is creating a new note and hence we are going to be creating a new Note if otherwise we are going to update the note details of the clicked note.

Modifications

  1. Before the onCreate() method, we create two new Strings - private String authors_id; and private String note_id; and then in our onCreate() method we add the following code -
//Existing onCreate() codes from part 1
authors_id = getIntent().getStringExtra("author_id");
note_id = getIntent().getStringExtra("note_id");

if (note_id != null)
    setUpNoteDetails(note_id);

Code Explanation

  • We get the authors ID and note ID sent from either the NotesAdapter or the ViewNotes activity and store in the note_id and authors_id String variable respectively.
  • Next, if the note_id is not null, that means the user clicked an already existing note from the previous activity so we call the setUpNoteDetails() passing the note_id variable.

setUpNoteDetails()

private void setUpNoteDetails(String noteID) {
    Notes noteGotten = realm.where(Notes.class).equalTo("noteId",noteID).findFirst();
    note_ET.setText(noteGotten.getNote().toString());
}

Code Explanation

  • We get the Note object that its noteId field matches that of the clicked note and store such object in noteGotten
  • Next we set the text of our EditText - note_ET to the value of the note with - note_ET.setText(noteGotten.getNote().toString());.

2.onOptionsItemSelected()
In the switch method of our onOptionsItemSelected() method, we make the following modifications

switch (id) {
    case R.id.action_save:
        String string_note = note_ET.getText().toString();
        if(note_id != null)
            updateNote(note_id,string_note);
        else
            showSaveDialog(string_note);
        break;
}

Code Explanation

  1. When the save item icon is clicked, we check if the note_id variable is not null, if its not we call the method - updateNote(note_id,string_note); witht the id of the note and also the newly entered note text saved in the String variable - string_note.
  2. If the note is null, we call the showSaveDialog() method with the note text entered by the user - showSaveDialog(string_note);.

updateNote()

private void updateNote(String noteID, final String SentNote) {
    final Notes noteToBeUpdated = realm.where(Notes.class).equalTo("noteId",noteID).findFirst();

    realm.executeTransaction(new Realm.Transaction() {
        @Override
        public void execute(Realm realm) {
            noteToBeUpdated.setNote(SentNote);
            showToast("Note Update Successful");
            finish();
        }
    });
}

Code Explanation

  • We get the note object that we intend to update by finding the note object that its noteId field matches that of the note and then we execute a realm transaction and update the note text and also make a Toast - "Note Update Successful" and close the current activity with - finish().

3.In our showSaveDialog() method we modify the asynchornous realm transaction as below -

realm.executeTransactionAsync(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        notes.setNoteTitle(noteTitle);
        notes.setNote(Sentnote);
        notes.setIsSaved(true);

        realm.where(Author.class).equalTo("id",authors_id).findFirst().getNotes().add(notes);
    }
});

Code Explanation

  1. We set the note title, note text and the boolean value to saved.
  2. We then add the note to the specified user on the realm variable - realm.where(Author.class).equalTo("id",authors_id).findFirst().getNotes().add(notes);.

APPLICATION EXECUTION

Sort:  

Hey @edetebenezer
Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!

Contributing on Utopian
Learn how to contribute on our website or by watching this tutorial on Youtube.

Want to chat? Join us on Discord https://discord.gg/h52nFrV.

Vote for Utopian Witness!

Thank you for your contribution.

  • Try not to put the tutorial too long.
  • Put more images on your work during the tutorial.

Looking forward to your upcoming tutorials.

Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.

To view those questions and the relevant answers related to your post, click here.


Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]

Thanks @portugalcoin for taking time out to moderate my post.
Subsequent contributions will be improved.
Good job. 👏

Congratulations @edetebenezer! You have completed the following achievement on Steemit and have been rewarded with new badge(s) :

Award for the number of upvotes

Click on the badge to view your Board of Honor.
If you no longer want to receive notifications, reply to this comment with the word STOP

Do not miss the last post from @steemitboard:
SteemitBoard World Cup Contest - The results, the winners and the prizes

Do you like SteemitBoard's project? Then Vote for its witness and get one more award!