https://github.com/realm/realm-java
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.
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.
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
Head to your project level gradle file and add the classpath dependency:
classpath "io.realm:realm-gradle-plugin:5.1.0"
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 :
And your project level Gradle file should look like this :
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:
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.
We will then customize our layout file - (dialog_input.xml) to have -
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
authors_name and has a marginTop of 10dp - android:layout_marginTop="10dp" with the hint message - "Enter Authors name"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 -
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:
step 1
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
R.id.dialog_input) and the root value as null.alertDialog.show().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.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 -
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
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();
}
showToast()
private void showToast(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
startNewNote()
private void startNewNote() {
startActivity(new Intent(this,NewNote.class));
}
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
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
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
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
realm.insert(note);.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
.current() method .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();
}
Proof of Work
https://github.com/generalkolo/Note-It