Creating a Note Taking Application Using Realm Java - Part 1

in utopian-io •  7 years ago  (edited)

realmDark.jpg

Repository

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

What Will I Learn?

  • Creating a Note Saving Android Application
  • How to work with synchronize Realm.
  • How to authorize user login in Synchronize Realm.
  • User Operations in Synchronize Realm.

Resources

Difficulty

  • Intermediate

Tutorial Duration - 40 - 45Mins

Tutorial Content

In today's tutorial, we are going to be beginning a tutorial series in working with synchronized realms.

In this tutorial series, we are going to be developing a note taking application - "NOTE IT", note it is going to be an application that users will be able to take notes with it, and then be able to synchronize that note to the Realm Cloud object server which when the user uses another phone, the updated notes can be pulled from the object server and then note taking can continue.

In today's tutorial, we are going to be ensuring that the user can take notes and can also save notes on the Realm Object server and in subsequent tutorials, we are going to be adding more functionalities to the application.

Outline

  • Dependencies Used
  • Creating Add Author Activity
  • Creating Add Note Activity
  • Saving Note To Realm Object Server

Depenedencies used

  • implementation 'com.jakewharton:butterknife:8.8.1'
    annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'

The ButterKnife dependency should be placed in your application level gradle file - "build.gradle" which will make the injection of views (e.g ImageView, TextView) as easy as possible which we will be seeing in this tutorial.

  • implementation 'org.projectlombok:lombok:1.16.20'
    annotationProcessor 'org.projectlombok:lombok:1.16.20'

The lombok dependency also is placed in the application level gradle file which makes the generator of getter and setter methods for our model classes by just adding the annotations @Getter for getters and @Setter for the setter methods.

  • Realm dependency

    Steps

  1. Head to your project level gradle file and add the classpath dependency:
    classpath "io.realm:realm-gradle-plugin:5.1.0"

  2. Next, head to your application level Gradle file "build.gradle" and add the realm-android plugin to the top of the file.

apply plugin:'realm-android'

Finally, refresh your Gradle dependencies.

After you have added the necessary dependencies, your application level Gradle file should look like this :

app Gradle.PNG

And your project level Gradle file should look like this :

projectLevel.PNG

Creating Add Author Activity

In order for us to add notes, we will require the user to input an author name, then we will utilize the nickname Realm Cloud authentication provider which we will be using to kick-start this application.

To kick-start this application, creating a new Android Studio Project using the - following details:

  • Minimum SDK - API 21
  • Activity Template - BASIC ACTIVITY
  • Activity Name - MainActivity (Or Anything you like)
  • Layout Name - activity_main (Or Anything you like)
  • Title - Note It (Or Anything you like)

Next, we are going to be creating a layout resource file that we will be using to accept the Author's name (i.e the person taking down the note), for this, right click on layout folder (under the res folder) => New => Layout Resource File and then name it - dialog_input.xml.

We will be using this layout file to create a custom Alert Dialog when the user wants to add a new note for the first time.

NB: See image for more details.

new layout resource.PNG

We will then customize our layout file - (dialog_input.xml) to have -

  1. An EditText - To accept the Authors name
  2. Two Buttons:
    • SAVE button - To save the Author's name
    • CANCEL button - To cancel the dialog.

dialog_input.xml

// RootLayout - RelativeLayout
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/authors_name_textInputLayout"
android:hint="@string/enter_authors_name"
android:layout_marginTop="10dp"
/>

<Button
android:id="@+id/cancel_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginTop="10dp"
android:layout_below="@+id/authors_name"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:backgroundTint="#9e031f"
android:text="CANCEL"
android:textColor="@android:color/white" />

<Button
android:id="@+id/ok_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_below="@+id/authors_name"
android:layout_marginBottom="20dp"
android:layout_marginRight="10dp"
android:layout_marginTop="10dp"
android:backgroundTint="#37e975"
android:text="SAVE"
android:textColor="@android:color/white" />

Code Explanation

  1. The EditText is created with the id of - authors_name and has a marginTop of 10dp - android:layout_marginTop="10dp" with the hint message - "Enter Authors name"
  2. The CANCEL button is created with the id - cancel_btn with a marginLeft and Top of 10dp and is placed to the Left of the screen with - android:layout_alignParentLeft="true" and is placed below the EditText, the same explanation applies to the SAVE button but it is placed to the right of the screen with - android:layout_alignParentRight="true"

The image below shows the result of the above code -

Screenshot_20180613-020101.png

Next, we are going to be modifying our MainActivity.java class file to display an AlertDialog using the above xml as its view, and then upon accepting the author's name, we are going to be using that to create a User on our Realm cloud object Server and then start the next Activity to enable the user write and save his notes.

MainActivity.java class file

We customize the Floating Action Button so that when a user clicks on it, he has to enter an Author's name and upon entering that, he can create a new note.

To achieve that, we are going inject the FAB in our MainActivity.java class file using ButterKnife - Place your cursor on the setContentView() method => alt + ins => Generate ButterKnife Injection and then select the FloatingActionButton as shown in the images below:

butterKnife.PNG
step 1

fab.PNG
step 2

The injected code will be - @BindView(R.id.fab) FloatingActionButton fab;.

Next, in our onCreate() method, we have to initialize our Realm and also set an onClickListener on the fab button just injected -

//inside onCreate() method
Realm.init(this);
fab.setOnClickListener(this);

Then we ensure our MainActivity implements View.OnClickListener
public class MainActivity extends AppCompatActivity implements View.OnClickListener {}

Then in the onClick() method of the FAB button, we are going to be displaying an AlertDialog with the dialog_input.xml as its view and then when the user clicks the SAVE button, we are going to create a new User in our Realm Cloud Object Server but if the CANCEL button is clicked, we dismiss the AlertDialog.

We, therefore, modify the onClick() method to below:

@Override
public void onClick(View v) {
    View dialogView = LayoutInflater.from(this).inflate(R.layout.dialog_input, null);
    final EditText authors_name = dialogView.findViewById(R.id.authors_name);
    Button OK_Btn = dialogView.findViewById(R.id.ok_btn);
    Button CANCEL_btn = dialogView.findViewById(R.id.cancel_btn);

    AlertDialog.Builder noteTitleDialog = new AlertDialog.Builder(this);
    noteTitleDialog.setTitle("Author Details");
    noteTitleDialog.setView(dialogView);
    noteTitleDialog.setCancelable(false);

    final AlertDialog alertDialog = noteTitleDialog.create();

    alertDialog.show();

    OK_Btn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            String author = authors_name.toString();
            if (!author.isEmpty()) {
                showProgressDialog(true);
                logUserIn(author);
            }
            else
                showToast("Author Name cannot be empty");
        }
    });

    CANCEL_btn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            alertDialog.dismiss();
        }
    });
}

Code Explanation

  1. We firstly create a View - dialogView and then we inflate it with the dialog_input.xml file using - R.id.dialog_input) and the root value as null.
  2. We then get a reference to the EditText and the Two Buttons in the layout file - authors_name, OK_btn, CANCEL_btn respectively.
  3. We then initialize an AlertDialog.Builder object with the following details - Title - "Author Details", View - dialogView and set the Cancelable to false and then we create the Dialog and lastly we show it using - alertDialog.show().
  4. Next, we set an onClickListener on the two buttons :
    • OK btn - We ensure that the author's name is not empty by storing it in a String variable - author and then using the isEmpty() method to check if it's empty or not. If the string is empty, we call the showToast() method which is a method that shows toast's to display - "Authors name cannot be empty" and if the author string variable is not empty, we call the logUserIn() method which as the name implies logs the user in using the Realm Cloud's nickname authentication. We then call the method - showProgressDialog() that shows a progress Dialog to indicate to the user that something is loading.
    • CANCEL btn - We using dismiss the AlertDialog using - alertDialog.dismiss()

logUserIn()

You first have to go here and then create an account to follow with this tutorial, after doing that, you will get an instance as shown in the image below -

Realm Cloud Instance.PNG

The highlighted link in the image below will be used in the logUserIn() method.

private void logUserIn(String author) {
String authenticationUrl = "https://note-it-application.us1a.cloud.realm.io/auth";
SyncCredentials credentials = SyncCredentials.nickname(author, false);
SyncUser.logInAsync(credentials, authenticationUrl, new SyncUser.Callback<SyncUser>() {
    @Override
    public void onSuccess(SyncUser user) {
        showProgressDialog(false);
        startNewNote();
    }

    @Override
    public void onError(ObjectServerError error) {
        showProgressDialog(false);
        showToast("An Error Occurred...Please try again later...");
    }
});
}

Code Explanation

  1. We firstly create a String variable - authenticationUrl which is the address of our instance gotten from the Realm Cloud Object server concatenated with - "/auth".
  2. We then setup a SyncCredential object - credentials using the nickname authentication and then we specify the name of the author as the nickname and then we pass false as the second argument which indicates that this user is not an admin.
  3. We then log the user in asynchronously passing the credentials object and the authenticationUrl String Variable and in the onSuccess() method, we dismiss our progress dialog by calling the showProgressDialog() method with parameter false and then we call the startNewNote() method and if there is an error, we override the onError() method and just make a simple toast with the message - "An Error Occurred... Please try again later..."

showProgressDialog()

private void showProgressDialog(boolean toShow) {
if (toShow) {
    progressDialog = new ProgressDialog(this);
    progressDialog.setMessage("Creating New Author!Please Wait...");
    progressDialog.setCancelable(false);
    progressDialog.show();
}
else
    progressDialog.dismiss();
}
  • The above method shows a dialog is the argument passed is true, with the message being - "Creating New Author! Please Wait..." and then it dismisses the dialog is the argument passed is false.

showToast()

private void showToast(String message) {
    Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
  • The above method displays a text with the string argument passed into it.

startNewNote()

private void startNewNote() {
    startActivity(new Intent(this,NewNote.class));
}
  • The method starts the Activity - NewNote.

Next, we create a new Activity - NewNote using the empty activity template.

In our activity layout file - acitivity_new_note.xml, we are going to be adding a single EditText, so the user can write his note in it.

//RootLyaout - ConstraintLayout.
<EditText
    android:id="@+id/note"
    android:layout_width="0dp"
    android:layout_height="266dp"
    android:layout_marginEnd="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginTop="16dp"
    android:ems="10"
    android:inputType="text"
    android:gravity="start"
    android:hint="@string/enter_note"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

NB: The id of the EditText - note and it displays the hint - "Enter Note..."

Next, we will have to create a model class that we represent each note, create a new java class file and name it - Notes then modify to the following -

@Getter
@Setter
public class Notes extends RealmObject{
@PrimaryKey
@Required
private String noteId;
@Required
private String note;
@Required
private String noteTitle;
@Required
private Boolean isSaved;
@Required
private Date timestamp;

public Notes() {
    this.noteId = UUID.randomUUID().toString();
    this.noteTitle = "";
    this.note = "";
    this.isSaved = false;
    this.timestamp = new Date();
}
}

Code Explanation

  • All fields are required and so we annote them with the - @Required annotation, we then use the noteId field as the primary key hence annoted with the @PrimaryKey annotation.
  • Then a contructor is creating where we set the noteId to a RandomUUID and then the timestamp field to a new Date() which will be the exact date and time when the note is saved.

Finally, in our NewNote.java class file, we are going to get the note entered by the user, ask the user to enter a title for the note and then we are going to save the note in our Realm Cloud Object server.

For the author to be able to save his notes, we are going to be using a menu that is going to have two items, one for saving the notes and the other for logging out.

To create a new menu resource file - right click on the menu folder => New => Menu resource file and name it - menu_newnote.

Then modify its content to the following -

<item
    android:id="@+id/action_save"
    android:orderInCategory="100"
    android:title="@string/action_save"
    app:showAsAction="always"
    android:icon="@drawable/ic_save"
    />

<item
    android:id="@+id/action_logout"
    android:orderInCategory="200"
    android:title="@string/action_logout"
    app:showAsAction="always"
    android:icon="@drawable/ic_logout"
    />

Code Explanation

  • Two Options menu items are created with the id's - action_save and action_logout with the titles - "Save Note" and "Logout" respectively.

NewNote.java

Firstly, we are going to inject our EditText using ButterKnife, refer to the Creating Add Author Activity section for how to do this.

Next, we declear a realm variable - private Realm realm and then in our onCreate() method, we add the following code -

//onCreate() 
Realm.init(this);

Realm.setDefaultConfiguration(SyncConfiguration.automatic());
realm = Realm.getDefaultInstance();

Next, we have to override two methods - onCreateOptionsMenu() and the onOptionsItemsSelected() where we inflate our menu layout file in the former and handle onClicks in the latter.

onOptionsItemSelected()

@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();

switch (id) {
    case R.id.action_save:
        String string_note = note_ET.getText().toString();
        showSaveDialog(string_note);
        break;
    case R.id.action_logout:
        logoutUser();
        break;
}

return super.onOptionsItemSelected(item);
}

Code Explanation

  1. Once the user clicks the save menu option, we get the note entered by the user and then we send it as an argument to the showSaveDialog() method and when the logout option menu item is selected, we call the logoutUser() method.

showSaveDialog()
The showSaveDialog() method is as same as the onClick() method for the FAB button explained in the _MainActivity.java class file _ section with some little alterations which will only be displayed here.

//code as the MainActivity.java class file section

final EditText note_title = dialogView.findViewById(R.id.authors_name);

AlertDialog.Builder noteTitleDialog = new AlertDialog.Builder(this);
noteTitleDialog.setTitle("Note Details");
noteTitleDialog.setView(dialogView);

OK_Btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String noteTitle = note_title.getText().toString();
                final Notes note = new Notes();
                note.setNoteTitle(noteTitle);
                note.setNote(Sentnote);
                note.setIsSaved(true);
                realm.executeTransaction(new Realm.Transaction() {
                    @Override
                    public void execute(Realm realm) {
                        realm.insert(note);
                    }
                });
                showToast("Note Saved Successfully!");
                note_ET.setText("");
                alertDialog.dismiss();

            }
        });

//Same CANCEL_btn onClickListener

Code Explanation

  1. We get the note title entered by the user and store it in the String variable - noteTitle.
  2. We then create an object of the Notes class - note.
  3. We then set the details of the note -
    • note.setNoteTitle(noteTitle);
    • note.setNote(Sentnote);
    • note.setIsSaved(true);
  4. We then perform a realm transaction block and within it, we add our note to our initialized realm - realm.insert(note);.
  5. We then set our EditText to an empty string and then we dismiss our alertDialog - alertDialog.dismiss();.

logoutUser()

private void logoutUser() {
    SyncUser syncUser = SyncUser.current();
    if (!(syncUser == null)) {
        syncUser.logOut();
        Intent intent = new Intent(this, MainActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        startActivity(intent);
    }
}

Code Explanation

  1. We firstly get a SyncUser Object and then initialize it to the user already logged in by calling the .current() method .
  2. We then log the author out using - syncUser.logOut(); and then set some Flags and start the MainActivity.

We then have to close our realm when in the onDestroy() method -

@Override
protected void onDestroy() {
    super.onDestroy();
    realm.close();
}

APPLICATION EXECUTION

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

Hi @edetebenezer,

Although there are few typos, technically you have very well presented tutorial, all codes are well-explained one by one for each roadmap of this tutorial. Screenshots are neat and well-presented too. One thing that I noticed is about the annotations Getter and Setter. I suggest having it like this one @Getter and @Setter. The Steemit website is very responsive and when using @ it refers that one to a link of a Steemit username. This also applies to @Required and @PrimaryKey annotations. Overall, those are just cosmetics, and the tutorial is well-taught.

Thanks @josephace135...i would apply that in subsequent contributions.
Glad you take out time to write such a useful comment.

Hey @josephace135
Here's a tip for your valuable feedback! @Utopian-io loves and incentivises informative comments.

Contributing on Utopian
Learn how to contribute on our website.

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

Vote for Utopian Witness!

Thank you for your contribution.
While I liked the content of your contribution, I would still like to extend few advices for your upcoming contributions:

  • Avoid putting this type of code, it is very simple and repeats several times in your tutorials.

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 contribution.
Subsequent contributions will be improved.

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!