CodeProject
This post is part 3 of a three part Android development tutorial series where I provide a walk-through on how to create and publish your first Android app. There are three post in this series
Post 1
Covers project creation and Material Design Navigation Drawer
Post 2
Covers User Interface, adding/managing of Attendants and EventsPost 3
Covers adding business logic, functionalities and Data Persistence
When users enter information about an event into our app, they expect that information to be there next time they open their app. They expect your app to save their information. While the user is typing the information into your app, Android holds that information temporarily in the device memory, when the user is done with your app or if the user goes to use another app, it is your responsibility to save the information you collected. Android provides a few storage options for you to save your app data long term.
Two of the most common storage options in Android are SharedPreference and SQLite Database. The main difference between Android SharedPreference and SQLite is the size and the structure of the data that you want to save. If you want to save the list of things you want to do next summer you can save that list to the SharedPreference. It is both a small and unstructured data; however if you want to record the names of all your extended family and their contact information then you will do well to use a SQLite database because this will be a relatively bigger and structured data.
It will take a few blog post to cover SQLite in Android so I will publish a different tutorial series that will examine SQLite in detail. For this app we will use Object Relational Mapper or ORM for short. ORMs are tools that let developers work with a database using their familiar programming language. It is still expected that you understand the basic concept of databases which are columns and tables. What the ORM does is take your Java class like Attendant.java and map it or create for you a corresponding database called Attendant.
Sugar ORM
For this app we will use Sugar ORM to save our data. It is dead simple to use. There are other ORMs like the ones listed in the in side screen shot, also you can find comparison of Android ORMs in this post by Also Ziflag.
Add Sugar ORM – lets add Sugard ORM to our Attendance app in the following steps, the full walkthrough on how to setup Sugar ORM can be found here.
Step 1: Add Sugar ORM dependency to your gradle file like this
compile 'com.github.satyan:sugar:1.3'
and then Specify SugarApp as your application class in manifest: Here is the top of my Manifest after adding SugarApp
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="okason.com.attendanceapp" >
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name="com.orm.SugarApp"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
tools:replace="android:icon">
<meta-data
android:name="DATABASE"
android:value="attendance_app.db" />
<meta-data
android:name="VERSION"
android:value="1" />
<meta-data
android:name="QUERY_LOG"
android:value="true" />
<meta-data
android:name="DOMAIN_PACKAGE_NAME"
android:value="okason.com.attendanceapp" />
<activity
android:name=".Activities.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Step 2: Extend SugarRecord for all the classes that you need to save. Here is the updated list of our model classes after extending from Sugar ORM. Default constructor is required in all the classes.
Attendance. java class – this class holds an instance of every attendance, this way every check in and check out is associated with an attendant and an event.
public class Attendance extends SugarRecord<Attendance>{
private Long CheckInTime;
private Long CheckOutTime;
private Event Event;
private Attendant Attendant;
public Attendance(){}
}
Event.java class to hold an instance of an event
public class Event extends SugarRecord<Event>{
private String Name;
private Long EventDate;
private String Venue;
private String City;
private String SerializedAttendantsList;
private String EventPicturePath;
public Event(){}
}
Attendant.java – this class holds an instance of an Attendant in our app.
public class Attendant extends SugarRecord<Attendant>{
private String Name;
private String Email;
private String Phone;
private String StreetAddress;
private String City;
private String State;
private String PostalCode;
private boolean IsCheckedIn;
private String ProfileImagePath;
private int ProfileImageId;
private String SerializedAttendances;
@Ignore
private List<Attendance> Attendances;
public Attendant(){}
}
Believe it or not, that is it with the database creation – or rather database setup. The database has not actually been created, the first time you save an object that is an instance of any these classes, Sugar ORM will create a database table with the same name as the classes. For example, here how to create and save an Attendant
Attendant guest1 = new Attendant();
guest1.setName("Debbie Sam");
guest1.setEmail("deb@email.net");
guest1.setProfileImageId(R.drawable.headshot_1);
guest1.save();
This will trigger the Sugar ORM initialization process which will take a peek at your class – a process known as reflection and then use the information it discovered to create a database table named Attendant with columns corresponding to the properties in your class such as Name, Email, Street, etc – that is a great time saver versus creating it all by your self. Please make sure that you can save an object at this point before moving on.
Getters and Setters
Remember that for each of your class, you have to generate getters and setters. Right click on an empty space inside the class, click on Generate and select Getters and Setters, then highlight all your listed properties and click OK
Time to add some logic to our app. Our app has to do something other than a fancy UI. How do we know what logic to implement – we derive a list the list of functionalities to implement from the conversation we have with the project owner. Here is the list of functionalities we will implement for this app.
- Create Attendant
- Create Event
- Set an Event as Active
- Check In Attendant to a specific event
- Check Out Attendant from a specific event
- Share an event
- Delete an event
And we will implement the above functionalities with the following steps
Step 1: Create Attendant – We already have the UI that we can perform data entry with, and we have already enabled data persistence for our objects using Sugar ORM and now we need the logic that connects the UI to the database. To complete this functionality we need to update the AddAttendantFragment.java like this:
- In the AddAttendantFragment.java class, create class member variables to represent each field in the UI like this:
private EditText mName, mEmail, mPhone, mStreet, mCity, mZip, mState;
- Using findViewById associate each variable above to their corresponding xml widget like this
mName = (EditText) mRootView.findViewById(R.id.edit_text_client_name);
mEmail = (EditText)mRootView.findViewById(R.id.edit_text_client_email);
mPhone = (EditText) mRootView.findViewById(R.id.edit_text_client_phone);
mPhone.setInputType(InputType.TYPE_CLASS_PHONE);
mPhone.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
mStreet = (EditText) mRootView.findViewById(R.id.edit_text_client_street_address);
mCity = (EditText) mRootView.findViewById(R.id.edit_text_client_city);
mState = (EditText) mRootView.findViewById(R.id.edit_text_client_state);
mZip = (EditText) mRootView.findViewById(R.id.edit_text_client_zip_code);
- Attach an onClick listener to the button to listen for when the user touches the save button
mSaveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (requiredFieldsCompleted()){
saveAttendant();
}
}
});
- When the user touches the save button, check to see if the required fields are completed
private boolean requiredFieldsCompleted() {
boolean result = false;
if (mName.getText() != null && !mName.getText().toString().isEmpty()) {
result = true;
} else {
mName.setError(getString(R.string.required_fields_empty));
}
if (mEmail.getText() != null && !mEmail.getText().toString().isEmpty()) {
result = true;
} else {
mEmail.setError(getString(R.string.required_fields_empty));
}
return result;
}
- If the required fields are completed then create a new instance of an Attendant class that you defined in the models folder
Attendant mAttendant = new Attendant();
- Use the text values of the class member variables to obtain the value that the user entered and use those to populate the Attendant object properties
- Save the Attendant object
mAttendant.save();
- Notify the user that the Attendant has been saved
Toast.makeText(getActivity(), mAttendant.getName() + " saved", Toast.LENGTH_SHORT).show();
- Clear the fields so the user can start over the process again
private void resetFields(){
mName.setText("");
mEmail.setText("");
mPhone.setText("");
mStreet.setText("");
mCity.setText("");
mState.setText("");
mZip.setText("");
}
And you can find the completed logic here.
Step 2: Create Event – We will follow the same process of adding a new attendant to add a new event, we get programmatic reference to the widgets on the screen and then listen for when the user clicks the save button. When the save button is clicked we check to see that the fields we deem as important/required a completed and if they are we create a new object, populate that object with the data that was entered into the fields and save. Here is completed logic to add a new event. Note the conversion that is required to get the date value that was entered.
Step 3: – Set Event as Active – for simplicity we will limit this app to check-in and check-out people from one event at a time. We accomplish this by setting the Id of any event to a SharedPreference, next time we want to set another event as the active event we simply override that id. The event has to be saved first in order for the event to have an id that we can save.
The method to set an event as the active ID will be set in the EventsListFragment.java, when the user touch an event we prompt to see if they want to set that event as the active and if yes, we use this method to save the event as the active event.
private void setEventAsActive(Event selectedEvent){
SharedPreferences mPref = getActivity().getSharedPreferences(Constants.PREFERENCE_FILE_KEY, Context.MODE_PRIVATE);
SharedPreferences.Editor mEditor = mPref.edit();
mEditor.putLong(Constants.ACVTIVE_EVENT_ID, selectedEvent.getId());
mEditor.commit();
Toast.makeText(getActivity(), selectedEvent.getName() + " is now the Active Event", Toast.LENGTH_SHORT).show();
}
Step 4: Check In Attendant to a specific event – we have now arrived at the core functionality of the app which is the ability to check in an attendant into an Event. There are many ways we can implement this functionality depending on the what this app is used for. Some examples are:
- Daycare check-in – this app could be used for a daycare check-in, in which case if a child gets checked in we need to add their name to the number of kids getting lunch so the cook will know the number of lunches to make
- Place of worship check-in – this app could be used to check-in worshipers to their place of worship as they arrive, the check-in functionality could just to update their record of attendance.
- Classroom check-in – this could be used to check-in students to class, and the teacher can provide numerous things that need to happen when the student check-in such as check if they are registered for the class, check lateness, etc
- What other use case can you see for this app, use the comment box to let me know, you never know we can explore it together.
For this demo, we are keeping the app generic so the check-in functionality is simply to toggle the Check-in button to Check-out and record the time of the Check-in. We will implement this in the AttendantAdapter.java that we created in part 2 of this tutorial series with following steps:
- In the onBindViewHolder method of the AttendantsAdapter.java class we setup a listener for when the check-in button is clicked
- When we detect a button click, we find out who is the attendant that want to check-in like this:
- Then we determine if this is a check-in or check-out click, if the Toggle button is “on” then it is a check-in if it is “off” then it is a check-out
- Then we create a new Attendance record and set the check-in time to be the current time
- We display a toast informing the user that he/she has been checked-in
- Here is the logic to implement the above steps
public void onBindViewHolder(AttendantsAdapter.ViewHolder holder, int position) {
final Attendant selectedAttendant = mAttendants.get(position);
holder.checkInCheckOutButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
boolean on = ((ToggleButton) v).isChecked();
if (on){
selectedAttendant.setIsCheckedIn(true);
Attendance mAttendance = new Attendance();
SharedPreferences mPref = mContext.getSharedPreferences(Constants.ATTENDANT_ID, Context.MODE_PRIVATE);
SharedPreferences.Editor mEditor = mPref.edit();
long idOfTheActiveEvent = mPref.getLong(Constants.ACVTIVE_EVENT_ID, 0);
if (idOfTheActiveEvent > 0){
Event activeEvent = Event.findById(Event.class, idOfTheActiveEvent);
if (activeEvent != null){
mAttendance.setEvent(activeEvent);
mAttendance.setAttendant(selectedAttendant);
mAttendance.setCheckInTime(System.currentTimeMillis());
Toast.makeText(mContext, selectedAttendant.getName() + " checked in ", Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(mContext, "Unable to checked in, no active event found ", Toast.LENGTH_SHORT).show();
}
}
} else {
selectedAttendant.setIsCheckedIn(false);
Toast.makeText(mContext, selectedAttendant.getName() + " checked out ", Toast.LENGTH_SHORT).show();
}
}
});
}
Step 5: Check Out Attendant from a specific event – this step is the reverse of the check-in step and this is what will do to check-out an attendant:
- If we detect that the Toggle button click is for a check-out, we get all the attendance record in the database
- We loop through the record to see which one belongs to this user
- If we find the record that belongs to this user,
- We check if it has a check in time and no checkout time
- If we find a check-in and no check-out then we check-out the user
- Here is the logic to check out an Attendant
else {
List<Attendance> AllTheAttendanceRecords = Attendance.listAll(Attendance.class);
if (AllTheAttendanceRecords != null && AllTheAttendanceRecords.size() > 0) {
for (Attendance record: AllTheAttendanceRecords){
if (record.getAttendant().equals(selectedAttendant)){
if (record.getCheckInTime() != null && record.getCheckOutTime() == null){
record.setCheckOutTime(System.currentTimeMillis());
Toast.makeText(mContext, selectedAttendant.getName() + " checked out ", Toast.LENGTH_SHORT).show();
}
break;
}
Toast.makeText(mContext, " Unable to check out - no attendance record found ", Toast.LENGTH_SHORT).show();
}
}
}
Step 6: Share an Event – why not the organizer will like to share an event and implementing the Share intent is one of the frequent actions you will perform in the apps that you will build. Here is the method to share an Event and this needs to be implemented in the EventsListFragment.java
private void shareEvent(Event selectedEvent){
java.text.DateFormat dateFormat = DateFormat.getMediumDateFormat(getActivity());
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_TEXT, "You are invited to our upcoming event - " +
selectedEvent.getName() + " on " + dateFormat.format(selectedEvent.getEventDate()));
shareIntent.setType("text/plain");
startActivity(shareIntent);
}
Step 7: Delete an Event – oops, the organizer changed their mind and want now to deleted an event. In the EventsListFragment.java where all the events are listed, we implement this method to delete an event like this:
private void deleteEvent(Event selectedEvent){
AlertDialog.Builder saveDialog = new AlertDialog.Builder(getActivity());
saveDialog.setTitle("Delete Event!");
saveDialog.setMessage("Are you sure you want to delete " + selectedEvent.getName() + "?");
saveDialog.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
selectedEvent.delete();
}
});
saveDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
saveDialog.show();
}
Summary
We touched on quite a few concepts here that it will take much longer post to try to explain them one by one. If you have questions use the comments box to ask. I will create a separate post for App publishing
If you like this tutorial please share it with someone who can benefit from it or through your social media, If you want to be notified when I release the next tutorial in the series please use the opt-in form below to join my mailing list. If you need clarifications, use the comment button below to ask questions. If you have feedback for me on how I can improve my tutorials or what topic you want to learn more about, use the contact form to reach out to me.
<form action='//valokafor.us4.list-manage.com/subscribe/post?u=0600ce94a59d7720819aa3dd8&id=6e5492cf7d' class='frm' method='post'><input type='text' placeholder='Email Address' name='EMAIL' /><input type='text' placeholder='First Name' name='FNAME' /><input type='hidden' name='b_0600ce94a59d7720819aa3dd8_6e5492cf7d' value='' />
<input type='submit' value="Send Me New Tutorials">
</form>
Follow Me
<script>jQuery(window).load(function() { ThriveApp.load_script('twitter'); });</script>
<script>jQuery(window).load(function() { ThriveApp.load_script('google'); });</script>
<script type='IN/MemberProfile' data-format='inline' data-id='https://www.linkedin.com/in/valokafor'></script>
<script>jQuery(window).load(function() { ThriveApp.load_script('linkedin'); });</script>
Source Code
You can find the Source Code for this Tutorial @ Github
The post Create Your First Android App – Part 3 appeared first on Val Okafor.