MainActivity.java
package com.sudogeeks.talking_reminder;
import android.content.Intent;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.ActionMode;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.amulyakhare.textdrawable.TextDrawable;
import com.amulyakhare.textdrawable.util.ColorGenerator;
import com.bignerdranch.android.multiselector.ModalMultiSelectorCallback;
import com.bignerdranch.android.multiselector.MultiSelector;
import com.bignerdranch.android.multiselector.SwappingHolder;
import com.firebase.ui.auth.AuthUI;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.database.ChildEventListener;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private static final int RC_SIGN_IN = 123; // Choose an arbitrary request code value
private RecyclerView reminderListView;
private SimpleAdapter mAdapter;
private Toolbar mToolbar;
private TextView mNoReminderView;
private FloatingActionButton mAddReminderButton;
private int mTempPost;
private LinkedHashMap<Integer, Integer> IDmap = new LinkedHashMap<>();
private ReminderDatabase rb;
private MultiSelector mMultiSelector = new MultiSelector();
private AlarmReceiver mAlarmReceiver;
private FirebaseDatabase firebaseDatabase; //Entrypoint for our firebase database
private DatabaseReference reminderDatabaseReference; //To references specific part of the database
private ChildEventListener dbChildEventListener; //No notify the app whenever data changes in firebase database
private FirebaseAuth firebaseAuth; //For User authentication
private FirebaseAuth.AuthStateListener authStateListener; //Listener which invokes whenever auth state changes
private ArrayList<ReminderDO> reminderDOArrayList = new ArrayList<>();
// Multi select items in recycler view
private ModalMultiSelectorCallback mDeleteMode = new ModalMultiSelectorCallback(mMultiSelector) {
@Override
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
getMenuInflater().inflate(R.menu.menu_add_reminder, menu);
return true;
}
@Override
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
switch (menuItem.getItemId()) {
// On clicking discard reminders
case R.id.discard_reminder:
// Close the context menu
actionMode.finish();
// Get the reminder id associated with the recycler view item
for (int i = IDmap.size(); i >= 0; i--) {
if (mMultiSelector.isSelected(i, 0)) {
int id = IDmap.get(i);
// Get reminder from reminder database using id
ReminderDO temp = rb.getReminder(id);
// Delete reminder
rb.deleteReminder(temp);
// Remove reminder from recycler view
mAdapter.removeItemSelected(i);
// Delete reminder alarm
mAlarmReceiver.cancelAlarm(getApplicationContext(), id);
}
}
// Clear selected items in recycler view
mMultiSelector.clearSelections();
// Recreate the recycler items
// This is done to remap the item and reminder ids
mAdapter.onDeleteItem(getDefaultItemCount());
// Display toast to confirm delete
Toast.makeText(getApplicationContext(),
"Deleted",
Toast.LENGTH_SHORT).show();
// To check is there are saved reminders
// If there are no reminders display a message asking the user to create reminders
List<ReminderDO> mTest = rb.getAllReminders();
if (mTest.isEmpty()) {
mNoReminderView.setVisibility(View.VISIBLE);
} else {
mNoReminderView.setVisibility(View.GONE);
}
return true;
// On clicking save reminders
case R.id.save_reminder:
// Close the context menu
actionMode.finish();
// Clear selected items in recycler view
mMultiSelector.clearSelections();
return true;
default:
break;
}
return false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Routine : check user Authentication
firebaseDatabase = FirebaseDatabase.getInstance();
firebaseAuth = FirebaseAuth.getInstance();
FirebaseUser user = firebaseAuth.getCurrentUser();
//AuthStateListener: check the user's authentication state. if not signed in the send it for login/signup
authStateListener = new FirebaseAuth.AuthStateListener() {
@Override
public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
FirebaseUser user = firebaseAuth.getCurrentUser();
if (user != null) {
//User is signed in
reminderDatabaseReference = firebaseDatabase.getReference().child("reminders").child(user.getUid());
Toast.makeText(MainActivity.this, "You're now signed in.\nWelcome to Talking ReminderDO!", Toast.LENGTH_SHORT).show();
attachDatabaseReadListener();
} else {
//User is signed out
startActivityForResult(
AuthUI.getInstance()
.createSignInIntentBuilder()
.setIsSmartLockEnabled(false)
.setAvailableProviders(Arrays.asList(
new AuthUI.IdpConfig.GoogleBuilder().build(),
new AuthUI.IdpConfig.EmailBuilder().build()))
.build(),
RC_SIGN_IN);
}
}
};
// Initialize reminder database
rb = new ReminderDatabase(getApplicationContext());
// Initialize views
mToolbar = findViewById(R.id.toolbar);
mAddReminderButton = findViewById(R.id.add_reminder);
reminderListView = findViewById(R.id.reminder_list);
mNoReminderView = findViewById(R.id.no_reminder_text);
// To check is there are saved reminders
// If there are no reminders display a message asking the user to create reminders
List<ReminderDO> mTest = rb.getAllReminders();
if (mTest.isEmpty()) {
mNoReminderView.setVisibility(View.VISIBLE);
}
// Create recycler view
reminderListView.setLayoutManager(getLayoutManager());
registerForContextMenu(reminderListView);
mAdapter = new SimpleAdapter();
mAdapter.setItemCount(getDefaultItemCount());
reminderListView.setAdapter(mAdapter);
// Setup toolbar
setSupportActionBar(mToolbar);
mToolbar.setTitle(R.string.app_name);
// On clicking the floating action button
mAddReminderButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(v.getContext(), ReminderAddActivity.class);
startActivity(intent);
}
});
// Initialize alarm
mAlarmReceiver = new AlarmReceiver();
}
/* Private Helper method
* This method attaches a listener to the Firebase database reference. The listener listens to any kind of changes in the database.
*
*/
private void attachDatabaseReadListener() {
//TODO Attaching Listener to ChildEventListener
if (dbChildEventListener == null) { // Create new Event Listener only when no listener is attached yet
dbChildEventListener = new ChildEventListener() {
//Specify what to do when a new entry is added in the database. New entry here means "new Reminder object"
@Override
public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
ReminderDO reminderObject = dataSnapshot.getValue(ReminderDO.class);
reminderDOArrayList.add(reminderObject);
}
@Override
public void onChildChanged(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
}
@Override
public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {
}
@Override
public void onChildMoved(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
};
}
reminderDatabaseReference.addChildEventListener(dbChildEventListener);
}
/* Private helper method
* this method is called to Detach the database listener in the events of logout or app failure
*/
private void detachDatabaseReadListener() {
if (dbChildEventListener != null) {
reminderDatabaseReference.removeEventListener(dbChildEventListener);
dbChildEventListener = null;
}
}
@Override
protected void onPause() {
super.onPause();
if (authStateListener != null) {
firebaseAuth.removeAuthStateListener(authStateListener);
}
detachDatabaseReadListener();
//reminderAdapter.clear();
}
@Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
getMenuInflater().inflate(R.menu.menu_add_reminder, menu);
}
/*Private Helper method :
* Implement behaviour : On clicking a reminder item, user shoud be taken to Reminder Edit Activity
*/
private void selectReminder(int mClickID) {
String mStringClickID = Integer.toString(mClickID);
// Create intent to edit the reminder
// Put reminder id as extra
Intent i = new Intent(this, ReminderEditActivity.class);
i.putExtra(ReminderEditActivity.EXTRA_REMINDER_ID, mStringClickID);
startActivityForResult(i, 1);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
mAdapter.setItemCount(getDefaultItemCount());
if (requestCode == RC_SIGN_IN) {
//If user signs in is successful then only take the user to main page. Else force the user to login in first
if (resultCode == RESULT_OK) {
Toast.makeText(this, "Signed in", Toast.LENGTH_SHORT);
startActivity(new Intent(MainActivity.this, ReminderAddActivity.class));
} else if (requestCode == RESULT_CANCELED) {
Toast.makeText(this, "Sign in cancelled", Toast.LENGTH_SHORT);
finish();
}
}
}
@Override
public void onResume() {
super.onResume();
firebaseAuth.addAuthStateListener(authStateListener);
// Get all the reminders : If there are no reminders display a message
List<ReminderDO> mTest = rb.getAllReminders();
if (mTest.isEmpty()) {
mNoReminderView.setVisibility(View.VISIBLE);
} else {
mNoReminderView.setVisibility(View.GONE);
}
mAdapter.setItemCount(getDefaultItemCount());
}
protected RecyclerView.LayoutManager getLayoutManager() {
return new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
}
protected int getDefaultItemCount() {
return 100;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.sign_out:
//sign out
AuthUI.getInstance().signOut(this);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
public class SimpleAdapter extends RecyclerView.Adapter<SimpleAdapter.VerticalItemHolder> {
private ArrayList<ReminderItem> mItems;
public SimpleAdapter() {
mItems = new ArrayList<>();
}
public void onDeleteItem(int count) {
mItems.clear();
mItems.addAll(generateData(count));
}
public void removeItemSelected(int selected) {
if (mItems.isEmpty()) return;
mItems.remove(selected);
notifyItemRemoved(selected);
}
@Override
public VerticalItemHolder onCreateViewHolder(ViewGroup container, int viewType) {
LayoutInflater inflater = LayoutInflater.from(container.getContext());
View root = inflater.inflate(R.layout.recycle_items, container, false);
return new VerticalItemHolder(root, this);
}
@Override
public void onBindViewHolder(VerticalItemHolder itemHolder, int position) {
ReminderItem item = mItems.get(position);
itemHolder.setReminderTitle(item.mTitle);
itemHolder.setReminderDateTime(item.mDateTime);
itemHolder.setReminderRepeatInfo(item.mRepeat, item.mRepeatNo, item.mRepeatType);
itemHolder.setActiveImage(item.mActive);
}
@Override
public int getItemCount() {
return mItems.size();
}
public void setItemCount(int count) {
mItems.clear();
mItems.addAll(generateData(count));
notifyDataSetChanged();
}
public List<ReminderItem> generateData(int count) {
ArrayList<SimpleAdapter.ReminderItem> items = new ArrayList<>();
// Get all reminderDOS from the database
List<ReminderDO> reminderDOS = rb.getAllReminders();
// Initialize lists
List<String> Titles = new ArrayList<>();
List<String> Repeats = new ArrayList<>();
List<String> RepeatNos = new ArrayList<>();
List<String> RepeatTypes = new ArrayList<>();
List<String> Actives = new ArrayList<>();
List<String> DateAndTime = new ArrayList<>();
List<Integer> IDList = new ArrayList<>();
List<DateTimeSorter> DateTimeSortList = new ArrayList<>();
// Add details of all reminderDOS in their respective lists
for (ReminderDO reminderDO : reminderDOS) {
Titles.add(reminderDO.getTitle());
DateAndTime.add(reminderDO.getDate() + " " + reminderDO.getTime());
Repeats.add(reminderDO.getRepeat());
RepeatNos.add(reminderDO.getRepeatNo());
RepeatTypes.add(reminderDO.getRepeatType());
Actives.add(reminderDO.getActive());
IDList.add(reminderDO.getID());
}
int key = 0;
// Add date and time as DateTimeSorter objects
for (int k = 0; k < Titles.size(); k++) {
DateTimeSortList.add(new DateTimeSorter(key, DateAndTime.get(k)));
key++;
}
// Sort items according to date and time in ascending order
Collections.sort(DateTimeSortList, new DateTimeComparator());
int k = 0;
// Plug in the data to each recycler view item
for (DateTimeSorter item : DateTimeSortList) {
int i = item.getIndex();
items.add(new SimpleAdapter.ReminderItem(Titles.get(i), DateAndTime.get(i), Repeats.get(i),
RepeatNos.get(i), RepeatTypes.get(i), Actives.get(i)));
IDmap.put(k, IDList.get(i));
k++;
}
return items;
}
public class ReminderItem {
public String mTitle;
public String mDateTime;
public String mRepeat;
public String mRepeatNo;
public String mRepeatType;
public String mActive;
public ReminderItem(String Title, String DateTime, String Repeat, String RepeatNo, String RepeatType, String Active) {
this.mTitle = Title;
this.mDateTime = DateTime;
this.mRepeat = Repeat;
this.mRepeatNo = RepeatNo;
this.mRepeatType = RepeatType;
this.mActive = Active;
}
}
public class DateTimeComparator implements Comparator {
DateFormat f = new SimpleDateFormat("dd/mm/yyyy hh:mm");
public int compare(Object a, Object b) {
String o1 = ((DateTimeSorter) a).getDateTime();
String o2 = ((DateTimeSorter) b).getDateTime();
try {
return f.parse(o1).compareTo(f.parse(o2));
} catch (ParseException e) {
throw new IllegalArgumentException(e);
}
}
}
public class VerticalItemHolder extends SwappingHolder
implements View.OnClickListener, View.OnLongClickListener {
private TextView mTitleText, mDateAndTimeText, mRepeatInfoText;
private ImageView mActiveImage, mThumbnailImage;
private ColorGenerator mColorGenerator = ColorGenerator.DEFAULT;
private TextDrawable mDrawableBuilder;
private SimpleAdapter mAdapter;
public VerticalItemHolder(View itemView, SimpleAdapter adapter) {
super(itemView, mMultiSelector);
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this); //Set for long clicks
itemView.setLongClickable(true);
// Initialize adapter for the items
mAdapter = adapter;
// Initialize views
mTitleText = itemView.findViewById(R.id.recycle_title);
mDateAndTimeText = itemView.findViewById(R.id.recycle_date_time);
mRepeatInfoText = itemView.findViewById(R.id.recycle_repeat_info);
mActiveImage = itemView.findViewById(R.id.active_image);
mThumbnailImage = itemView.findViewById(R.id.thumbnail_image);
}
@Override
public void onClick(View view) {
if (!mMultiSelector.tapSelection(this)) {
mTempPost = reminderListView.getChildAdapterPosition(view);
int mReminderClickID = IDmap.get(mTempPost);
selectReminder(mReminderClickID);
} else if (mMultiSelector.getSelectedPositions().isEmpty()) {
mAdapter.setItemCount(getDefaultItemCount());
}
}
@Override
public boolean onLongClick(View view) {
AppCompatActivity activity = MainActivity.this;
activity.startSupportActionMode(mDeleteMode);
mMultiSelector.setSelected(this, true);
return true;
}
public void setReminderTitle(String reminderTitle) {
mTitleText.setText(reminderTitle);
String letter = "A";
if (reminderTitle != null && !reminderTitle.isEmpty()) {
letter = reminderTitle.substring(0, 1);
}
int color = mColorGenerator.getRandomColor();
// Create a circular icon consisting of a random background colour and first letter of title
mDrawableBuilder = TextDrawable.builder()
.buildRound(letter, color);
mThumbnailImage.setImageDrawable(mDrawableBuilder);
}
public void setReminderDateTime(String datetime) {
mDateAndTimeText.setText(datetime);
}
public void setReminderRepeatInfo(String repeat, String repeatNo, String repeatType) {
if (repeat.equals("true")) {
mRepeatInfoText.setText("Every " + repeatNo + " " + repeatType + "(s)");
} else if (repeat.equals("false")) {
mRepeatInfoText.setText("Repeat Off");
}
}
public void setActiveImage(String active) {
if (active.equals("true")) {
mActiveImage.setImageResource(R.drawable.ic_notifications_active);
} else if (active.equals("false")) {
mActiveImage.setImageResource(R.drawable.ic_notifications_off);
}
}
}
}
}
com.sudogeeks.talking_reminder.DateTimeSorter
Data Object class for creating DateTime objects to facilitate easy sorting.
Definition: DateTimeSorter.java:7
com.sudogeeks.talking_reminder.MainActivity.SimpleAdapter.VerticalItemHolder
Definition: MainActivity.java:578
com.sudogeeks.talking_reminder.ReminderDatabase.deleteReminder
void deleteReminder(ReminderDO reminderDO)
Definition: ReminderDatabase.java:208
com.sudogeeks.talking_reminder.MainActivity.onResume
void onResume()
Definition: MainActivity.java:340
com.sudogeeks.talking_reminder.ReminderDatabase.getReminder
ReminderDO getReminder(int id)
Definition: ReminderDatabase.java:106
com.sudogeeks.talking_reminder.MainActivity.onOptionsItemSelected
boolean onOptionsItemSelected(MenuItem item)
Definition: MainActivity.java:389
com.sudogeeks.talking_reminder.MainActivity.onCreateContextMenu
void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo)
Definition: MainActivity.java:294
com.sudogeeks.talking_reminder.ReminderDatabase.getAllReminders
List< ReminderDO > getAllReminders()
Definition: ReminderDatabase.java:138
com.sudogeeks.talking_reminder.MainActivity.SimpleAdapter.VerticalItemHolder.onClick
void onClick(View view)
Definition: MainActivity.java:615
com.sudogeeks.talking_reminder.MainActivity.SimpleAdapter
Adapter class (Inner Class) for implementing recycler view.
Definition: MainActivity.java:405
com.sudogeeks.talking_reminder.ReminderDO
Data Object class for Reminder This class conatins all the attributes of reminder.
Definition: ReminderDO.java:15
com.sudogeeks.talking_reminder.MainActivity.SimpleAdapter.VerticalItemHolder.onLongClick
boolean onLongClick(View view)
Definition: MainActivity.java:633
com.sudogeeks.talking_reminder.MainActivity.SimpleAdapter.VerticalItemHolder.setReminderDateTime
void setReminderDateTime(String datetime)
Definition: MainActivity.java:664
com.sudogeeks.talking_reminder.MainActivity.onCreateOptionsMenu
boolean onCreateOptionsMenu(Menu menu)
Definition: MainActivity.java:378
com.sudogeeks.talking_reminder.MainActivity
Main Activity class This activity is the entry point of the app and is responsible for handling User ...
Definition: MainActivity.java:52
com.sudogeeks.talking_reminder.MainActivity.SimpleAdapter.ReminderItem.ReminderItem
ReminderItem(String Title, String DateTime, String Repeat, String RepeatNo, String RepeatType, String Active)
Definition: MainActivity.java:547
com.sudogeeks.talking_reminder.MainActivity.onPause
void onPause()
Definition: MainActivity.java:278
com.sudogeeks.talking_reminder.ReminderDatabase
Class to manage database This class is for managing the database.
Definition: ReminderDatabase.java:15
com.sudogeeks.talking_reminder.MainActivity.SimpleAdapter.VerticalItemHolder.VerticalItemHolder
VerticalItemHolder(View itemView, SimpleAdapter adapter)
Definition: MainActivity.java:593
com.sudogeeks.talking_reminder.MainActivity.getDefaultItemCount
int getDefaultItemCount()
Definition: MainActivity.java:367
com.sudogeeks.talking_reminder.MainActivity.SimpleAdapter.DateTimeComparator
Definition: MainActivity.java:560
com.sudogeeks.talking_reminder.MainActivity.SimpleAdapter.VerticalItemHolder.setActiveImage
void setActiveImage(String active)
Definition: MainActivity.java:686
com.sudogeeks.talking_reminder.AlarmReceiver
A class implementing basic alarm functions This class includes very basic features for an alarm.
Definition: AlarmReceiver.java:21
com.sudogeeks.talking_reminder.MainActivity.SimpleAdapter.getItemCount
int getItemCount()
Definition: MainActivity.java:458
com.sudogeeks.talking_reminder.MainActivity.SimpleAdapter.onCreateViewHolder
VerticalItemHolder onCreateViewHolder(ViewGroup container, int viewType)
Definition: MainActivity.java:431
com.sudogeeks.talking_reminder.AlarmReceiver.cancelAlarm
void cancelAlarm(Context context, int ID)
Definition: AlarmReceiver.java:139
com.sudogeeks.talking_reminder.MainActivity.onCreate
void onCreate(Bundle savedInstanceState)
Definition: MainActivity.java:154
com.sudogeeks.talking_reminder.MainActivity.getLayoutManager
RecyclerView.LayoutManager getLayoutManager()
Definition: MainActivity.java:359
com.sudogeeks.talking_reminder.ReminderEditActivity
Java class for handling UI component of Edit reminder layout.
Definition: ReminderEditActivity.java:32
com.sudogeeks.talking_reminder.MainActivity.onActivityResult
void onActivityResult(int requestCode, int resultCode, Intent data)
Definition: MainActivity.java:319
com.sudogeeks.talking_reminder.MainActivity.SimpleAdapter.VerticalItemHolder.setReminderTitle
void setReminderTitle(String reminderTitle)
Definition: MainActivity.java:644
com.sudogeeks.talking_reminder.MainActivity.SimpleAdapter.generateData
List< ReminderItem > generateData(int count)
Definition: MainActivity.java:475
com.sudogeeks.talking_reminder.ReminderAddActivity
Java class for handling UI component of Add reminder layout.
Definition: ReminderAddActivity.java:48
com.sudogeeks.talking_reminder.MainActivity.SimpleAdapter.ReminderItem
Definition: MainActivity.java:530
com.sudogeeks.talking_reminder.MainActivity.SimpleAdapter.onBindViewHolder
void onBindViewHolder(VerticalItemHolder itemHolder, int position)
Definition: MainActivity.java:445
com.sudogeeks.talking_reminder.MainActivity.SimpleAdapter.VerticalItemHolder.setReminderRepeatInfo
void setReminderRepeatInfo(String repeat, String repeatNo, String repeatType)
Definition: MainActivity.java:674