How to create a Sound Recording Application using ObjectBox as DB in android studio (Final Part)
Repository
https://github.com/objectbox/objectbox-java
What Will I Learn?
- How to create a Sound Recording application using ObjectBox DB.
- How to use Query Builders.
- How to Delete ObjectBox objects.
- How to register a FileObserver on a Folder.
- How to use the
.equal()
Query to filter ObjectBox Objects.
Resources
- ObjectBox Website https://objectbox.io/
- ObjectBox Github - https://github.com/objectbox/objectbox-java
Difficulty
- Intermediate
Tutorial Duration - 30 - 35Mins
Tutorial Content
This happens to be the part four and final part of this tutorial series and the part one can be found here and the part two here and the part three here
In today's tutorial, we are going to be adding more functionality to our Application which can currently record, and then users can play their recordings and they can rename recordings which will have an effect both in the application and also in the file directory of the users phone.
Today, we are going to be adding a delete function in our application which would enable users to delete recordings.We will then include a DirectoryFileObserver on the directory where we save our recordings incase the user deletes a recording outside of our application we also need to delete the recording inside the application also.
Outline
- Implement the DELETE functionality in our custom Adapter.
- Include the FileObserver in our SavedRecordingsFragment java class file.
Implement the DELETE functionality.
To implement the DELETE functionality, we are going to be adding a delete option to the setOnLongClickListener which was registered on our cardview but before we do that, we have to make some minor changes to our SavedRecordingsAdapter
file which is making our RECORDING
object which happens to be our Recordings
box gotten from our MyApplicationClass
file to a global variable.
To achieve the above, add this (private Box<Recordings> RECORDINGS;
) to our list of already existing private variables and edit our adapter constructor to the following -
public SavedRecordingsAdapter(Context context, List<Recordings> recordings) {
super();
mContext = context;
this.recordings = recordings;
//Get a Box for the Recordings.class POJO
RECORDINGS = ((MyApplicationClass)mContext.getApplicationContext()).getBoxStore().boxFor(Recordings.class);
}
With that done, we need to add the delete option to our cardview's setOnLongClickListener so follow the below guidelines -
- Add another String to our option_entries ArrayList object -
option_entries.add(mContext.getString(R.string.dialog_file_delete));
NB: the R.string.dialog_file_delete has a value of - Delete - We need to edit our setItems() method for our builder (AlertDialog.Builder) object to the following -
builder.setItems(items, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
if (item == 0) {
renameFileDialog(holder.getAdapterPosition());
}
if (item == 1) {
deleteFileDialog(holder.getAdapterPosition());
}
}
});
NB: As explained in the part three of this tutorial, the above code registers an onClick listener to our items and here if the user clicks on the second item which will have the index number of one (1), we call a method - deleteFileDialog)
and pass the current position of the Adapter which can be gotten from - holder.getAdapterPosition()
as an arguement.
deleteFileDialog()
public void deleteFileDialog (final int position) {
// File delete confirm
AlertDialog.Builder confirmDelete = new AlertDialog.Builder(mContext);
confirmDelete.setTitle(mContext.getString(R.string.dialog_title_delete));
confirmDelete.setMessage(mContext.getString(R.string.dialog_text_delete));
confirmDelete.setCancelable(true);
confirmDelete.setPositiveButton(mContext.getString(R.string.dialog_action_yes),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
try {
//remove item from ObjectBox, RecyclerView, and storage (user's phone)
remove(position);
} catch (Exception e) {
Log.e(LOG_TAG, "exception", e);
}
dialog.dismiss();
}
});
confirmDelete.setNegativeButton(mContext.getString(R.string.dialog_action_no),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.dismiss();
}
});
AlertDialog alert = confirmDelete.create();
alert.show();
}
Code Explanation
- Firstly, we create an
AlertDialog.Builder
object -confirmDelete
which we set its title to a String with the id - R.string.dialog_title_delete which has the value of Delete Recording and then we set the message of the builder to a String with the id - R.string.dialog.dialog_text_delete which has a value of - Are you sure you would like to delete this file? and we then set the dialog to be cancelable by setting its isCancelable() method to true. - We then set the text of the positive button to a String with the id - R.string.dialog_action_yes which has a value of - Yes and we then set an onCLickListener which will then call the method -
remove()
with position as its argumnent which holds the position of the recording in our RecyclerView we intend to delete. - We then set the negetiveButton of our dialog to a String with the id of *R.string.dialog_action_no which has the value of - No and then the onClick listener dismisses the dialog with -
dialog.dismiss()
- Lastly, we create the Dialog using the dialog builder object and then we show the dialog -
alert.show()
.
remove()
private void remove(int position) {
//remove item from database, RecyclerView and storage
//delete file from storage
File file = new File(getItem(position).getRecording_path());
file.delete();
Toast.makeText(mContext, String.format(mContext.getString(R.string.toast_file_delete), getItem(position).getRecording_name()),Toast.LENGTH_SHORT).show();
//Get the particular box item that we intend to delete
Recordings recordingToDelete = RECORDINGS.get(recording.getId());
//Delete the object from the ObjectBox
RECORDINGS.remove(recordingToDelete);
notifyItemRemoved(position);
}
Code Explanation
- We create a File object (file) by gettting the path of the recording that it's position was passed to the method by calling the
getRecording_path()
which we then delete by -file.delete()
. - We then show a Toast to the user telling the user that the recording has been deleted.
- Next thing we have to do now is to delete the recording from our ObjectBox database. To do that, we create a new
Recordings
object -recordingToDelete
which will be the recording that it's id property corresponds to the one with the id of the recording that was long pressed on. - We then remove the recording from our ObjectBox database by calling the
remove()
method on theRECORDINGS
object which we just made global a few steps ago and passing therecordingToDelete
object to it -RECORDINGS.remove(recordingToDelete)
. - Lastly, we call the
notifyItemRemoved()
method and pass the position of the item that has been removed.
Having done all this, the next thing we have to do now is to set a FileObeserver
on the directory where we save our recordings on the users phone will will then listen if any recording has been deleted and then delete the same recording from the database.
To achieve the following, we have to open our - SavedRecordingsFragment
class file and add the below class to the exisiting code -
class DirectoryFileObserver extends FileObserver {
private String asbsolutePath;
public DirectoryFileObserver(String path) {
super(path,FileObserver.DELETE);
asbsolutePath = path;
}
@Override
public void onEvent(int event, @Nullable String path) {
event &= FileObserver.ALL_EVENTS;
if((FileObserver.DELETE & event) != 0 ){
// user deletes a recording file out of the app
String filePath = android.os.Environment.getExternalStorageDirectory().toString()
+ "/Soundbox/" + path;
Log.d("SoundBox/", "File deleted ["
+ android.os.Environment.getExternalStorageDirectory().toString()
+ "/Soundbox/" + path + "]");
// remove file from database and recyclerview
savedRecordingsAdapter.deleteRecordingWithPath(filePath);
}
}
}
Code Explanation
- The DirectoryFileObserver class extends the
FileObserver
class, in our constructor, we set the private variable -absolutePath
to the value of the path passed into the constructor and we then call the super method with the path sent and also a mask ofFileObserver.DELETE
which listens for DELETE operations. - We then override the
onEvent
method of the class and we use an if statement to check if the operation performed was a delete operation if it was, we get the path of the file deleted and then we call our AdaptersdeleteRecordingWithPath()
method passing the path of the file as its method.
NB: thedeleteRecordingWithPath()
method will be created in the next section.
< hr />
deleteRecordingWithPath()
Lastly for this turoial, we need to setup the deleteRecordingWithPath()
method in our custom adapter - SavedRecordingsAdapter
.Before the onBindViewHolder()
method, add the following code -
public void deleteRecordingWithPath(String filePath) {
//user deletes a saved recording out of the application through another application
Recordings record = RECORDINGS.query().equal(Recordings_.recording_path,filePath).build().findFirst();
if (record != null){
RECORDINGS.remove(record.getId());
notifyDataSetChanged();
}
}
Code Explanation
- First, we use the
RECORDINGS
object which is an Box of Recordings type to get the actual recording that its path matches the path sent in the method and save it in an object -record
- We achieve the above by calling a qyuery on the
RECORDINGS
object and then using theequal()
method where we specified the field we intend to search through which is the recording_path field of the class which can be gotten by -Recordings_.recording_path,filePath
. We then search through the field looking for a direct match to the recording with the value of the -filePath
variable, we then build and call thefindFirst()
method which returns the first recording to match the query.
- We achieve the above by calling a qyuery on the
- Next, we use an if statement to ensure that the
record
object is not null after which we remove the recording from ourRECORDING
object and calll thenotifyDataSetChanged()
method which tells the adapter that a change has occured.
APPLICATION EXECUTION
Proof of Work
https://github.com/generalkolo/SoundRecorderObjectBox
Curriculum
Thank you for your contribution.
Very good work with these tutorials "How to create a Sound Recording Application using ObjectBox as DB in android studio".
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.
Would start using Dtube for my uploads for upcoming tutorials.
Thank you for your review, @portugalcoin!
So far this week you've reviewed 14 contributions. Keep up the good work!
Hey @edetebenezer
Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!
Want to chat? Join us on Discord https://discord.gg/h52nFrV.
Vote for Utopian Witness!
Hi @edetebenezer! We are @steem-ua, a new Steem dApp, computing UserAuthority for all accounts on Steem. We are currently in test modus upvoting quality Utopian-io contributions! Nice work!
Congratulations @edetebenezer! You received a personal award!
Click here to view your Board of Honor
Congratulations @edetebenezer! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :
Click here to view your Board of Honor
If you no longer want to receive notifications, reply to this comment with the word
STOP
Congratulations @edetebenezer! You received a personal award!
You can view your badges on your Steem Board and compare to others on the Steem Ranking
Vote for @Steemitboard as a witness to get one more award and increased upvotes!