diff --git a/app/build.gradle b/app/build.gradle
index d36ed4e..e818118 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -24,10 +24,11 @@ dependencies {
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
- compile 'com.android.support:appcompat-v7:26.+'
+ compile 'com.android.support:appcompat-v7:26.0.1'
+ compile 'com.android.support:design:26.0.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
- compile 'com.google.android.gms:play-services-places:11.0.2'
- compile 'com.google.android.gms:play-services-location:11.0.2'
- compile 'com.android.support:recyclerview-v7:26.+'
+ compile 'com.google.android.gms:play-services-places:11.0.4'
+ compile 'com.google.android.gms:play-services-location:11.0.4'
+ compile 'com.android.support:recyclerview-v7:26.0.1'
testCompile 'junit:junit:4.12'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index df88dfc..a501779 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -13,12 +13,11 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
-
+ android:value="@string/your_google_places_api_key" />
-
+
@@ -32,11 +31,13 @@
android:exported="false" />
+ android:exported="true" />
+
-
+
diff --git a/app/src/main/java/com/mdg/droiders/samagra/shush/AlarmBroadcastReceiver.java b/app/src/main/java/com/mdg/droiders/samagra/shush/AlarmBroadcastReceiver.java
deleted file mode 100644
index c8a82a8..0000000
--- a/app/src/main/java/com/mdg/droiders/samagra/shush/AlarmBroadcastReceiver.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.mdg.droiders.samagra.shush;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.media.AudioManager;
-
-import com.mdg.droiders.samagra.shush.utils.RingerUtils;
-
-/**
- * Created by rohan on 12/8/17.
- *
- * Broadcast Receiver that receives a broadcast from the alarm manager to silence/un-silence the phone.
- */
-
-public class AlarmBroadcastReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- boolean shush = intent.getBooleanExtra(
- context.getString(R.string.alarm_intent_extra_key), false);
- if (shush) {
- RingerUtils.setRingerMode(context, AudioManager.RINGER_MODE_SILENT);
- } else {
- RingerUtils.setRingerMode(context, AudioManager.RINGER_MODE_NORMAL);
- }
- }
-}
diff --git a/app/src/main/java/com/mdg/droiders/samagra/shush/AlarmScheduler.java b/app/src/main/java/com/mdg/droiders/samagra/shush/AlarmScheduler.java
index 134b771..9e354b2 100644
--- a/app/src/main/java/com/mdg/droiders/samagra/shush/AlarmScheduler.java
+++ b/app/src/main/java/com/mdg/droiders/samagra/shush/AlarmScheduler.java
@@ -4,8 +4,15 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.util.Log;
+import com.mdg.droiders.samagra.shush.receivers.AlarmBroadcastReceiver;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
import java.util.List;
+import java.util.Locale;
/**
* Created by rohan on 12/8/17.
@@ -14,12 +21,11 @@
*/
public class AlarmScheduler {
-
- /**
- * Request code for scheduling an alarm
- * Will be taken from the database
- */
- private static final int REQUEST_CODE = 12;
+ private static final SimpleDateFormat LOG_DATE_FORMAT
+ = new SimpleDateFormat("hh:mm a, EE ,dd MMM yyyy", Locale.ENGLISH);
+ private static final int START_ALARM_ID_INCREMENTER = 10000;
+ private static final int END_ALARM_ID_INCREMENTER = 1000;
+ private static final String LOG_TAG = "Samagra/AS/";
private AlarmManager alarmManager;
private Context context;
@@ -29,53 +35,295 @@ public AlarmScheduler(Context context) {
this.context = context;
}
+ /**
+ * Sets an alarm to shush the phone for a single day.
+ *
+ * @param startTime The time at which phone is to be shushed
+ * @param endTime The time at which phone is to be un-shushed
+ * @param day int type for week days starting from monday and zero indexed.
+ * For e.g. - Monday is 0, Tuesday is 1, ... and so on
+ * @param rowID Unique primary key of the shush alarm row.
+ */
+ public void setSingleDayAlarm(Calendar startTime, Calendar endTime, int day, Integer rowID) {
+ if (day > 6 || rowID == null) {
+ return;
+ }
+ boolean[] days = new boolean[7];
+ for (int i = 0; i < 7; i++) {
+ days[i] = day == i;
+ }
+ setWeeklyAlarm(startTime, endTime, days, rowID);
+ }
+
+ /**
+ * Cancels a shush alarm for a particular day if it exists.
+ *
+ * @param day int type for week days starting from monday and zero indexed.
+ * For e.g. - Monday is 0, Tuesday is 1, ... and so on
+ * @param rowID Unique primary key of the shush alarm row.
+ */
+ public void cancelSingleAlarm(int day, Integer rowID) {
+ if (rowID == null) {
+ return;
+ }
+ alarmManager.cancel(getDefaultPendingIntent(getStartDayID(day, rowID)));
+ alarmManager.cancel(getDefaultPendingIntent(getEndDayID(day, rowID)));
+ }
+
+ /**
+ * Sets a weekly alarm to silence your phone for days given by boolean array days.
+ *
+ * NOTE : This method only schedules alarms and does NOT cancels for some day whose
+ * corresponding index in days array is false. To cancel alarms
+ * use {@link #cancelSingleAlarm(int, Integer)}
+ *
+ * Uses : {@link #setAlarm(long, long, Integer, Integer)} to set each alarm one by one.
+ *
+ * @param startTime The time of day at which phone is to be shushed
+ * @param endTime The time of day at which phone is to be un-shushed
+ * @param days A boolean array that is true if the alarm is to be
+ * scheduled for the corresponding day. i.e. days[0] is
+ * set to be true then a weekly alarm will be set such
+ * that it will shush the phone every Monday.
+ * @param rowID Unique primary key of the shush alarm row.
+ */
+ public void setWeeklyAlarm(Calendar startTime, Calendar endTime, boolean[] days, Integer rowID) {
+ // To avoid various exceptions
+ if (rowID == null || days.length != 7) {
+ return;
+ }
+ // Instance of calendar that holds current system time
+ Calendar currentTime = Calendar.getInstance();
+
+ // Set YEAR, MONTH and DAY fields of startTime
+ // and endTime to today's YEAR, MONTH and DAY respectively
+ startTime.set(Calendar.YEAR, currentTime.get(Calendar.YEAR));
+ startTime.set(Calendar.MONTH, currentTime.get(Calendar.MONTH));
+ startTime.set(Calendar.DAY_OF_MONTH, currentTime.get(Calendar.DAY_OF_MONTH));
+ endTime.set(Calendar.YEAR, currentTime.get(Calendar.YEAR));
+ endTime.set(Calendar.MONTH, currentTime.get(Calendar.MONTH));
+ endTime.set(Calendar.DAY_OF_MONTH, currentTime.get(Calendar.DAY_OF_MONTH));
+ Log.d(LOG_TAG + "now", LOG_DATE_FORMAT.format(currentTime.getTime()));
+
+ // If startTime's hour is ahead of endTime's, assume that
+ // endTime is of that of next day
+ if (startTime.get(Calendar.HOUR_OF_DAY)
+ > endTime.get(Calendar.HOUR_OF_DAY)) {
+ endTime.add(Calendar.DAY_OF_MONTH, 1);
+ }
+
+ // If endTime is in the past increment both start and end time
+ if (endTime.compareTo(currentTime) < 0) {
+ startTime.add(Calendar.DAY_OF_MONTH, 1);
+ endTime.add(Calendar.DAY_OF_MONTH, 1);
+ }
+
+ // get Day index of start Time
+ int dayForAlarm = getDay(startTime);
+ for (int j = dayForAlarm; j < 7 + dayForAlarm; j++) {
+ int day = j % 7;
+ Log.d(LOG_TAG + "Day-Val", String.valueOf(day));
+ if (days[day]) {
+ setAlarm(startTime.getTimeInMillis(),
+ endTime.getTimeInMillis(),
+ getStartDayID(day, rowID),
+ getEndDayID(day, rowID));
+ }
+ // Update startTime and endTime
+ startTime.add(Calendar.DAY_OF_MONTH, 1);
+ endTime.add(Calendar.DAY_OF_MONTH, 1);
+ }
+ }
+
+ /**
+ * Sets a weekly alarm to silence your phone for every day.
+ *
+ * Uses : {@link #setWeeklyAlarm(Calendar, Calendar, boolean[], Integer)}
+ * to set weekly alarms.
+ *
+ * @param startHour The hour at which phone is to be shushed in 24 hour format
+ * @param startMinutes The minute at which phone is to be shushed
+ * @param endHour The hour at which phone is to be un-shushed in 24 hour format
+ * @param endMinutes The minute at which phone is to be un-shushed
+ * @param days A boolean array that is true if the alarm is to be
+ * scheduled for the corresponding day. i.e. days[0] is set to be true
+ * then a weekly alarm will be set such that it will shush
+ * the phone every Monday.
+ * @param rowID Unique primary key of the shush alarm row.
+ */
+ public void setWeeklyAlarm(int startHour, int startMinutes, int endHour,
+ int endMinutes, boolean[] days, Integer rowID) {
+ if (rowID == null || days.length != 7) {
+ return;
+ }
+ Calendar startTime = Calendar.getInstance();
+ Calendar endTime = Calendar.getInstance();
+ startTime.set(Calendar.HOUR_OF_DAY, startHour);
+ startTime.set(Calendar.MINUTE, startMinutes);
+ endTime.set(Calendar.HOUR_OF_DAY, endHour);
+ endTime.set(Calendar.MINUTE, endMinutes);
+ setWeeklyAlarm(startTime, endTime, days, rowID);
+ }
+
/**
* Set an alarm to silence the phone.
+ *
+ * Uses : {@link #setAlarm(long, Integer, boolean)} to set start alarm
+ * and the end alarm
*
* @param startTimeInMillis The time at which phone is to be shushed
* @param endTimeInMillis The time at which phone is to be un-shushed
+ * @param startAlarmID The id that uniquely identifies the start time alarm
+ * @param endAlarmID The id that uniquely identifies the end time alarm
*/
- public void setAlarm(long startTimeInMillis, long endTimeInMillis) {
+ public void setAlarm(long startTimeInMillis, long endTimeInMillis,
+ Integer startAlarmID, Integer endAlarmID) {
if (startTimeInMillis >= endTimeInMillis) {
return;
}
- alarmManager.set(
- AlarmManager.RTC_WAKEUP,
- startTimeInMillis,
- getDefaultPendingIntent(true)
- );
- alarmManager.set(
+ if (startAlarmID == null || endAlarmID == null) {
+ return;
+ }
+ if (startAlarmID.equals(endAlarmID)) {
+ Log.e(LOG_TAG + "error", "start alarm id and end alarm ids are same");
+ return;
+ }
+
+ Log.d(LOG_TAG + "startTime", LOG_DATE_FORMAT.format(new Date(startTimeInMillis)));
+ Log.d(LOG_TAG + "endTime", LOG_DATE_FORMAT.format(new Date(endTimeInMillis)));
+ setAlarm(startTimeInMillis, startAlarmID, true);
+ setAlarm(endTimeInMillis, endAlarmID, false);
+ }
+
+ /**
+ * Set an alarm to silence the phone.
+ *
+ * @param timeInMillis The time at which phone is to be shushed/un-shushed
+ * @param alarmID The alarm id used when scheduling the alarm
+ * @param shush A boolean indicating whether or not to
+ * silence the phone when the alarm is fired by the system.
+ * Silent mode will be activated if shouldShush is set to be true
+ * @see #setAlarm(long, long, Integer, Integer)
+ */
+ public void setAlarm(long timeInMillis, Integer alarmID, boolean shush) {
+ if (alarmID == null) {
+ return;
+ }
+
+ alarmManager.setExact(
AlarmManager.RTC_WAKEUP,
- endTimeInMillis,
- getDefaultPendingIntent(false)
+ timeInMillis,
+ getDefaultPendingIntent(shush, timeInMillis, alarmID)
);
}
/**
* Sets multiple alarms at once.
*
- * Uses : {@link #setAlarm(long, long)} to set each alarm one by one.
+ * Uses : {@link #setAlarm(long, long, Integer, Integer)} to set each alarm one by one.
*
* @param startTimesInMillis The list of start times at which phone is to be shushed
* @param endTimesInMillis The list of end times at which phone is to be un-shushed
*/
- public void setAlarms(List startTimesInMillis, List endTimesInMillis) {
+ public void setAlarms(List startTimesInMillis, List endTimesInMillis,
+ List startAlarmIds, List endAlarmIds) {
if (startTimesInMillis.size() != endTimesInMillis.size()) {
return;
}
for (int i = 0; i < startTimesInMillis.size(); i++) {
- setAlarm(startTimesInMillis.get(i), endTimesInMillis.get(i));
+ setAlarm(startTimesInMillis.get(i), endTimesInMillis.get(i),
+ startAlarmIds.get(i), endAlarmIds.get(i));
}
}
- private PendingIntent getDefaultPendingIntent(boolean shouldSilence) {
+ /**
+ * Get a default pending intent without any extras.
+ * Used to cancel any existing alarm whose alarm id is known.
+ *
+ * @param alarmID The alarm id used when scheduling the alarm
+ * @return a pending intent that can be used to cancel
+ * any alarm which has same pending intent
+ * @see #getDefaultPendingIntent(boolean, long, int)
+ */
+ private PendingIntent getDefaultPendingIntent(int alarmID) {
PendingIntent pendingIntent;
Intent intent = new Intent(context, AlarmBroadcastReceiver.class);
- intent.putExtra(context.getString(R.string.alarm_intent_extra_key), shouldSilence);
pendingIntent = PendingIntent.getBroadcast(
- context, REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ context, alarmID, intent, PendingIntent.FLAG_UPDATE_CURRENT
+ );
return pendingIntent;
}
+
+ /**
+ * Get a complete pending intent with all the extra values.
+ * Used to schedule an alarm for future.
+ *
+ * @param shouldShush A boolean indicating whether or not to
+ * silence the phone when the alarm is fired by the system.
+ * Silent mode will be activated if shouldShush is set to be true
+ * @param timeInMillis The "timeInMillis" at which the alarm is expected to go off.
+ * It is used to schedule a future alarm for next week.
+ * @param alarmId An int that uniquely identifies this alarm, so that the alarm
+ * may be updated/canceled in the future using the same.
+ * @return A pending intent used to schedule alarms by
+ * {@link #setAlarm(long, long, Integer, Integer)} method
+ * @see #getDefaultPendingIntent(int)
+ */
+ private PendingIntent getDefaultPendingIntent(boolean shouldShush, long timeInMillis, int alarmId) {
+ PendingIntent pendingIntent;
+
+ Intent intent = new Intent(context, AlarmBroadcastReceiver.class);
+ intent.putExtra(context.getString(R.string.alarm_intent_boolean_extra_key), shouldShush);
+ intent.putExtra(context.getString(R.string.alarm_intent_long_extra_key), timeInMillis);
+ intent.putExtra(context.getString(R.string.alarm_intent_int_extra_key), alarmId);
+ pendingIntent = PendingIntent.getBroadcast(
+ context, alarmId, intent, PendingIntent.FLAG_UPDATE_CURRENT
+ );
+
+ return pendingIntent;
+ }
+
+ /**
+ * Get day ID for start alarms(shush types) that are scheduled by alarm manager.
+ * The ID returned identifies a particular day of a particular shush alarm.
+ *
+ * @param day int type for week days starting from monday and zero indexed.
+ * For e.g. - Monday is 0, Tuesday is 1, ... and so on
+ * @param rowID Unique primary key of the shush alarm row.
+ * @return The ID used by {@link AlarmManager} to schedule weekly alarms
+ */
+ private int getStartDayID(int day, int rowID) {
+ return rowID + (day + 1) * START_ALARM_ID_INCREMENTER;
+ }
+
+ /**
+ * Get day ID for end alarms(un-shush types) that are scheduled by alarm manager.
+ * The ID returned identifies a particular day of a particular shush alarm.
+ *
+ * @param day int type for week days starting from monday and zero indexed.
+ * For e.g. - Monday is 0, Tuesday is 1, ... and so on
+ * @param rowID Unique primary key of the shush alarm row.
+ * @return The ID used by {@link AlarmManager} to schedule weekly alarms
+ */
+ private int getEndDayID(int day, int rowID) {
+ return rowID + (day + 1) * END_ALARM_ID_INCREMENTER;
+ }
+
+ /**
+ * Get day field of calendar indexed at 0 such that start day of week is monday.
+ *
+ * @param calendar The calendar whose day is to be returned
+ * @return int type for week days starting from monday and zero indexed.
+ * For e.g. - Monday is 0, Tuesday is 1, ... and so on
+ */
+ private int getDay(Calendar calendar) {
+ int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
+ dayOfWeek -= 2;
+ if (dayOfWeek == -1) {
+ dayOfWeek += 7;
+ }
+ return dayOfWeek;
+ }
}
diff --git a/app/src/main/java/com/mdg/droiders/samagra/shush/GeofenceBroadcastReceiver.java b/app/src/main/java/com/mdg/droiders/samagra/shush/GeofenceBroadcastReceiver.java
deleted file mode 100644
index e5a6928..0000000
--- a/app/src/main/java/com/mdg/droiders/samagra/shush/GeofenceBroadcastReceiver.java
+++ /dev/null
@@ -1,112 +0,0 @@
-package com.mdg.droiders.samagra.shush;
-
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.TaskStackBuilder;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.BitmapFactory;
-import android.media.AudioManager;
-import android.support.v4.app.NotificationCompat;
-import android.util.Log;
-
-import com.google.android.gms.location.Geofence;
-import com.google.android.gms.location.GeofencingEvent;
-import com.mdg.droiders.samagra.shush.utils.RingerUtils;
-
-public class GeofenceBroadcastReceiver extends BroadcastReceiver {
-
- private static final String LOG_TAG = GeofenceBroadcastReceiver.class.getName();
-
- /***
- * Handles the Broadcast message sent when the Geofence Transition is triggered
- * Careful here though, this is running on the main thread so make sure you start an AsyncTask for
- * anything that takes longer than say 10 second to run
- *
- * @param context
- * @param intent
- */
- @Override
- public void onReceive(Context context, Intent intent) {
- //get the geofencing event sent from the intent
- GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
-
- if (geofencingEvent.hasError()) {
- Log.e(LOG_TAG, String.format("Error Code : %s", geofencingEvent.getErrorCode()));
- return;
- }
-
- //get the transition type
- int geoFenceTransition = geofencingEvent.getGeofenceTransition();
-
- //Check which transition type has triggered the event
- if (geoFenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER) {
- RingerUtils.setRingerMode(context, AudioManager.RINGER_MODE_SILENT);
- } else if (geoFenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {
- RingerUtils.setRingerMode(context, AudioManager.RINGER_MODE_NORMAL);
- } else {
- Log.e(LOG_TAG, String.format("Unknown Transition , %d", geoFenceTransition));
- return;
- }
-
- //Send the notification
- sendNotification(context, geoFenceTransition);
- }
-
- /**
- * Posts a notification in the notification bar when a transition is detected
- * Uses different icon drawables for different transition types
- * If the user clicks the notification, control goes to the MainActivity
- *
- * @param context The calling context for building a task stack
- * @param transition The geofence transition type, can be Geofence.GEOFENCE_TRANSITION_ENTER
- * or Geofence.GEOFENCE_TRANSITION_EXIT
- */
- private void sendNotification(Context context, int transition) {
- //create an explicit content intent that starts the main activity
- Intent notificationIntent = new Intent(context, MainActivity.class);
-
- //Construct a task stack
- TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
-
- //Adding mainActivity to the task stack as the parent task
- stackBuilder.addParentStack(MainActivity.class);
-
- //Push the content intent onto the stack
- stackBuilder.addNextIntent(notificationIntent);
-
- //Get a pending intent consisting the entire backstack
- PendingIntent notificationPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
-
- //Get a notification builder
- NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
-
- //Check the transition type to display the relevant icon image
-
- if (transition == Geofence.GEOFENCE_TRANSITION_ENTER) {
- builder.setSmallIcon(R.drawable.ic_volume_off_white_24dp)
- .setLargeIcon(BitmapFactory.decodeResource(context.getResources(),
- R.drawable.ic_volume_off_white_24dp))
- .setContentTitle(context.getString(R.string.silent_mode_activated));
- } else if (transition == Geofence.GEOFENCE_TRANSITION_EXIT) {
- builder.setSmallIcon(R.drawable.ic_volume_up_white_24dp)
- .setLargeIcon(BitmapFactory.decodeResource(context.getResources(),
- R.drawable.ic_volume_up_white_24dp))
- .setContentTitle(context.getString(R.string.back_to_normal));
- }
-
- // Continue building the notification
- builder.setContentText(context.getString(R.string.touch_to_relaunch));
- builder.setContentIntent(notificationPendingIntent);
-
- //Auto remove the notification if the user touches it
- builder.setAutoCancel(true);
-
- NotificationManager notificationManager = (NotificationManager)
- context.getSystemService(Context.NOTIFICATION_SERVICE);
-
- notificationManager.notify(0, builder.build());
- }
-
-}
diff --git a/app/src/main/java/com/mdg/droiders/samagra/shush/Geofencing.java b/app/src/main/java/com/mdg/droiders/samagra/shush/Geofencing.java
index 50daa80..8b345f4 100644
--- a/app/src/main/java/com/mdg/droiders/samagra/shush/Geofencing.java
+++ b/app/src/main/java/com/mdg/droiders/samagra/shush/Geofencing.java
@@ -14,6 +14,7 @@
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.places.Place;
import com.google.android.gms.location.places.PlaceBuffer;
+import com.mdg.droiders.samagra.shush.receivers.GeofenceBroadcastReceiver;
import java.util.ArrayList;
import java.util.List;
diff --git a/app/src/main/java/com/mdg/droiders/samagra/shush/MainActivity.java b/app/src/main/java/com/mdg/droiders/samagra/shush/activities/MainActivity.java
similarity index 95%
rename from app/src/main/java/com/mdg/droiders/samagra/shush/MainActivity.java
rename to app/src/main/java/com/mdg/droiders/samagra/shush/activities/MainActivity.java
index efebcd5..88d70d9 100644
--- a/app/src/main/java/com/mdg/droiders/samagra/shush/MainActivity.java
+++ b/app/src/main/java/com/mdg/droiders/samagra/shush/activities/MainActivity.java
@@ -1,4 +1,4 @@
-package com.mdg.droiders.samagra.shush;
+package com.mdg.droiders.samagra.shush.activities;
import android.Manifest;
import android.app.NotificationManager;
@@ -38,6 +38,9 @@
import com.google.android.gms.location.places.PlaceBuffer;
import com.google.android.gms.location.places.Places;
import com.google.android.gms.location.places.ui.PlacePicker;
+import com.mdg.droiders.samagra.shush.Geofencing;
+import com.mdg.droiders.samagra.shush.R;
+import com.mdg.droiders.samagra.shush.adapters.PlaceListAdapter;
import com.mdg.droiders.samagra.shush.data.PlacesContract;
import java.util.ArrayList;
@@ -58,6 +61,7 @@ public class MainActivity extends AppCompatActivity implements
private PlaceListAdapter mAdapter;
private RecyclerView mRecyclerView;
private Button addPlaceButton;
+ private Button addAlarmButton;
private GoogleApiClient mClient;
private Geofencing mGeofencing;
private boolean mIsEnabled;
@@ -110,6 +114,15 @@ public void onClick(View view) {
}
});
+ addAlarmButton = findViewById(R.id.add_alarm_button);
+
+ addAlarmButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ startActivity(new Intent(MainActivity.this, TimeListActivity.class));
+ }
+ });
+
mClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
diff --git a/app/src/main/java/com/mdg/droiders/samagra/shush/activities/TimeListActivity.java b/app/src/main/java/com/mdg/droiders/samagra/shush/activities/TimeListActivity.java
new file mode 100644
index 0000000..78a681a
--- /dev/null
+++ b/app/src/main/java/com/mdg/droiders/samagra/shush/activities/TimeListActivity.java
@@ -0,0 +1,73 @@
+package com.mdg.droiders.samagra.shush.activities;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.MenuItem;
+import android.view.View;
+
+import com.mdg.droiders.samagra.shush.R;
+import com.mdg.droiders.samagra.shush.adapters.TimeListAdapter;
+
+public class TimeListActivity extends AppCompatActivity {
+
+ private RecyclerView timeRecycler;
+ private TimeListAdapter timeListAdapter;
+ private boolean isCursorRefreshed;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_time_list);
+ setUpActionBar();
+ timeRecycler = findViewById(R.id.time_list_recycler);
+ findViewById(R.id.fab).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (timeListAdapter != null) {
+ timeListAdapter.addItem();
+ }
+ }
+ });
+ timeListAdapter = new TimeListAdapter(this);
+ // Cursor is refreshed in the constructor
+ isCursorRefreshed = true;
+ timeRecycler.setLayoutManager(new LinearLayoutManager(this));
+ timeRecycler.setAdapter(timeListAdapter);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (!isCursorRefreshed) {
+ timeListAdapter.refreshCursor();
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ int id = item.getItemId();
+ switch (id) {
+ case android.R.id.home: {
+ finish();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ timeListAdapter.closeCursor();
+ isCursorRefreshed = false;
+ }
+
+ private void setUpActionBar() {
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/mdg/droiders/samagra/shush/PlaceListAdapter.java b/app/src/main/java/com/mdg/droiders/samagra/shush/adapters/PlaceListAdapter.java
similarity index 97%
rename from app/src/main/java/com/mdg/droiders/samagra/shush/PlaceListAdapter.java
rename to app/src/main/java/com/mdg/droiders/samagra/shush/adapters/PlaceListAdapter.java
index ac150a7..a07c22d 100644
--- a/app/src/main/java/com/mdg/droiders/samagra/shush/PlaceListAdapter.java
+++ b/app/src/main/java/com/mdg/droiders/samagra/shush/adapters/PlaceListAdapter.java
@@ -1,4 +1,4 @@
-package com.mdg.droiders.samagra.shush;
+package com.mdg.droiders.samagra.shush.adapters;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
@@ -8,6 +8,7 @@
import android.widget.TextView;
import com.google.android.gms.location.places.PlaceBuffer;
+import com.mdg.droiders.samagra.shush.R;
/**
* Created by samagra on 20/7/17.
diff --git a/app/src/main/java/com/mdg/droiders/samagra/shush/adapters/TimeListAdapter.java b/app/src/main/java/com/mdg/droiders/samagra/shush/adapters/TimeListAdapter.java
new file mode 100644
index 0000000..05cc483
--- /dev/null
+++ b/app/src/main/java/com/mdg/droiders/samagra/shush/adapters/TimeListAdapter.java
@@ -0,0 +1,242 @@
+package com.mdg.droiders.samagra.shush.adapters;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.mdg.droiders.samagra.shush.AlarmScheduler;
+import com.mdg.droiders.samagra.shush.R;
+import com.mdg.droiders.samagra.shush.data.PlacesContract;
+import com.mdg.droiders.samagra.shush.interfaces.TimeAdapterNotifier;
+import com.mdg.droiders.samagra.shush.viewholders.TimeRowHolder;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Locale;
+
+/**
+ * Created by rohan on 19/8/17.
+ * Adapter that binds data to the view for time entries in the Shush app.
+ */
+public class TimeListAdapter extends RecyclerView.Adapter
+ implements TimeAdapterNotifier {
+
+ // The format which is used to display date in each row
+ public static final SimpleDateFormat DISPLAY_DATE_FORMAT =
+ new SimpleDateFormat("hh:mm a", Locale.ENGLISH);
+ private static final String LOG_TAG = "Samagra/TLA/";
+
+ private Context mContext;
+ private Cursor timeDataCursor;
+ private AlarmScheduler alarmScheduler;
+
+ public TimeListAdapter(Context mContext) {
+ this.mContext = mContext;
+ refreshCursor();
+ alarmScheduler = new AlarmScheduler(mContext);
+ }
+
+ @Override
+ public TimeRowHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View row = LayoutInflater.from(mContext)
+ .inflate(R.layout.time_picker_row, parent, false);
+ return new TimeRowHolder(row, this, mContext);
+ }
+
+ @Override
+ public void onBindViewHolder(TimeRowHolder holder, int position) {
+ if (position == timeDataCursor.getCount()) {
+ holder.itemView.setVisibility(View.INVISIBLE);
+ return;
+ }
+ // isBound set to false so that listeners do not react to set events in this method.
+ holder.isBound = false;
+ holder.itemView.setVisibility(View.VISIBLE);
+ if (timeDataCursor.moveToPosition(position)) {
+
+ // Retrieving the whole row from the database and Updating the holder
+ holder.id = timeDataCursor.getInt(
+ timeDataCursor.getColumnIndexOrThrow(PlacesContract.TimeEntry._ID)
+ );
+ holder.startTime.setText(timeDataCursor.getString(
+ timeDataCursor.getColumnIndexOrThrow(PlacesContract.TimeEntry.COLUMN_START_TIME)
+ ));
+ holder.endTime.setText(timeDataCursor.getString(
+ timeDataCursor.getColumnIndexOrThrow(PlacesContract.TimeEntry.COLUMN_END_TIME)
+ ));
+ for (int i = 0; i < 7; i++) {
+ int day = timeDataCursor.getInt(
+ timeDataCursor.getColumnIndexOrThrow(getColumnFromDay(i))
+ );
+ holder.days[i].setChecked(day == 1);
+ }
+
+ // All the set events have occurred, now we can again start listening.
+ holder.isBound = true;
+ }
+
+ }
+
+ @Override
+ public int getItemCount() {
+ // An extra item is to make space for the fab
+ return timeDataCursor.getCount() + 1;
+ }
+
+ /**
+ * Adds a new row in the recycler as well as in the db with some default values.
+ */
+ public void addItem() {
+ Calendar calendar = Calendar.getInstance();
+ ContentValues values = new ContentValues();
+ values.put(PlacesContract.TimeEntry.COLUMN_START_TIME,
+ DISPLAY_DATE_FORMAT.format(calendar.getTime()));
+ values.put(PlacesContract.TimeEntry.COLUMN_END_TIME,
+ DISPLAY_DATE_FORMAT.format(calendar.getTime()));
+ values.put(PlacesContract.TimeEntry.COLUMN_MONDAY, 0);
+ values.put(PlacesContract.TimeEntry.COLUMN_TUESDAY, 0);
+ values.put(PlacesContract.TimeEntry.COLUMN_WEDNESDAY, 0);
+ values.put(PlacesContract.TimeEntry.COLUMN_THURSDAY, 0);
+ values.put(PlacesContract.TimeEntry.COLUMN_FRIDAY, 0);
+ values.put(PlacesContract.TimeEntry.COLUMN_SATURDAY, 0);
+ values.put(PlacesContract.TimeEntry.COLUMN_SUNDAY, 0);
+ mContext.getContentResolver().insert(
+ PlacesContract.TimeEntry.CONTENT_URI,
+ values
+ );
+ closeCursor();
+ refreshCursor();
+ notifyItemInserted(timeDataCursor.getCount() - 1);
+ }
+
+ /**
+ * Make a query to the database to get the updated result in the local cursor instance.
+ */
+ public void refreshCursor() {
+ timeDataCursor = mContext.getContentResolver().query(
+ PlacesContract.TimeEntry.CONTENT_URI, null, null, null, null);
+ }
+
+ /**
+ * Close the cursor to release its resources.
+ * The cursor should be closed in onStop() and refreshed in onResume().
+ */
+ public void closeCursor() {
+ timeDataCursor.close();
+ }
+
+ @Override
+ public void notifyTimeChanged(TimeRowHolder holder) {
+ ContentValues values = new ContentValues();
+ String startTimeString = holder.startTime.getText().toString();
+ String endTimeString = holder.endTime.getText().toString();
+ values.put(PlacesContract.TimeEntry.COLUMN_START_TIME, startTimeString);
+ values.put(PlacesContract.TimeEntry.COLUMN_END_TIME, endTimeString);
+
+ mContext.getContentResolver().update(
+ Uri.withAppendedPath(PlacesContract.TimeEntry.CONTENT_URI,
+ String.valueOf(holder.id)), values, null, null
+ );
+
+ Calendar startTime = Calendar.getInstance();
+ Calendar endTime = Calendar.getInstance();
+ setTimesFromHolder(holder, startTime, endTime);
+ closeCursor();
+ refreshCursor();
+ alarmScheduler.setWeeklyAlarm(startTime, endTime, getDayArr(holder), holder.id);
+ }
+
+ @Override
+ public void notifyDayAlarmSet(TimeRowHolder holder, int day) {
+
+ ContentValues values = new ContentValues();
+ values.put(getColumnFromDay(day), true);
+ mContext.getContentResolver().update(
+ Uri.withAppendedPath(
+ PlacesContract.TimeEntry.CONTENT_URI, String.valueOf(holder.id)
+ ), values, null, null
+ );
+
+ Calendar startTime = Calendar.getInstance();
+ Calendar endTime = Calendar.getInstance();
+ setTimesFromHolder(holder, startTime, endTime);
+ alarmScheduler.setSingleDayAlarm(startTime, endTime, day, holder.id);
+ }
+
+ @Override
+ public void notifyDayAlarmCancelled(TimeRowHolder holder, int day) {
+
+ ContentValues values = new ContentValues();
+ values.put(getColumnFromDay(day), false);
+ mContext.getContentResolver().update(
+ Uri.withAppendedPath(PlacesContract.TimeEntry.CONTENT_URI, String.valueOf(holder.id))
+ , values, null, null
+ );
+
+ Calendar startTime = Calendar.getInstance();
+ Calendar endTime = Calendar.getInstance();
+ setTimesFromHolder(holder, startTime, endTime);
+ alarmScheduler.cancelSingleAlarm(day, holder.id);
+ }
+
+ /**
+ * Returns the corresponding column of the day in db.
+ *
+ * @param day The day for which the column is to be returned
+ * @return The column corresponding to the given day
+ */
+ private String getColumnFromDay(int day) {
+ switch (day) {
+ case 0: {
+ return PlacesContract.TimeEntry.COLUMN_MONDAY;
+ }
+ case 1: {
+ return PlacesContract.TimeEntry.COLUMN_TUESDAY;
+ }
+ case 2: {
+ return PlacesContract.TimeEntry.COLUMN_WEDNESDAY;
+ }
+ case 3: {
+ return PlacesContract.TimeEntry.COLUMN_THURSDAY;
+ }
+ case 4: {
+ return PlacesContract.TimeEntry.COLUMN_FRIDAY;
+ }
+ case 5: {
+ return PlacesContract.TimeEntry.COLUMN_SATURDAY;
+ }
+ case 6: {
+ return PlacesContract.TimeEntry.COLUMN_SUNDAY;
+ }
+ default: {
+ return null;
+ }
+ }
+ }
+
+ private void setTimesFromHolder(TimeRowHolder holder, Calendar startTime, Calendar endTime) {
+ String startTimeString = holder.startTime.getText().toString();
+ String endTimeString = holder.endTime.getText().toString();
+ try {
+ startTime.setTime(DISPLAY_DATE_FORMAT.parse(startTimeString));
+ endTime.setTime(DISPLAY_DATE_FORMAT.parse(endTimeString));
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private boolean[] getDayArr(TimeRowHolder holder) {
+ boolean[] isAlarmSetOnDay = new boolean[7];
+ for (int i = 0; i < 7; i++) {
+ isAlarmSetOnDay[i] = holder.days[i].isChecked();
+ }
+ return isAlarmSetOnDay;
+ }
+
+}
diff --git a/app/src/main/java/com/mdg/droiders/samagra/shush/data/PlacesContentProvider.java b/app/src/main/java/com/mdg/droiders/samagra/shush/data/PlacesContentProvider.java
index 4a8cb37..54bb908 100644
--- a/app/src/main/java/com/mdg/droiders/samagra/shush/data/PlacesContentProvider.java
+++ b/app/src/main/java/com/mdg/droiders/samagra/shush/data/PlacesContentProvider.java
@@ -102,7 +102,7 @@ public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable S
String id = uri.getPathSegments().get(1);
retCursor = db.query(PlacesContract.TimeEntry.TABLE_NAME,
projection,
- "id =?",
+ "_id =?",
new String[]{id},
null,
null,
@@ -203,14 +203,14 @@ public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strin
// Get the place ID from the URI path
String id = uri.getPathSegments().get(1);
// Use selections/selectionArgs to filter for this ID
- deletedRows = db.delete(PlacesContract.PlaceEntry.TABLE_NAME,"id=?",new String[]{id});
+ deletedRows = db.delete(PlacesContract.PlaceEntry.TABLE_NAME,"_id=?",new String[]{id});
break;
// Handle the single item case, recognized by the ID included in the URI path
case SINGLE_TIME_WITH_ID:
// Get the place ID from the URI path
String timeId = uri.getPathSegments().get(1);
// Use selections/selectionArgs to filter for this ID
- deletedRows = db.delete(PlacesContract.TimeEntry.TABLE_NAME,"id=?", new String[]{timeId});
+ deletedRows = db.delete(PlacesContract.TimeEntry.TABLE_NAME,"_id=?", new String[]{timeId});
break;
default:
throw new UnsupportedOperationException("Invalid uri: "+ uri);
@@ -254,7 +254,7 @@ public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Null
// Get the place ID from the URI path
id = uri.getPathSegments().get(1);
// Use selections/selectionArgs to filter for this ID
- affectedRows = db.update(PlacesContract.TimeEntry.TABLE_NAME,contentValues,"id=?",
+ affectedRows = db.update(PlacesContract.TimeEntry.TABLE_NAME,contentValues,"_id=?",
new String[]{id});
break;
// Default exception
diff --git a/app/src/main/java/com/mdg/droiders/samagra/shush/fragments/TimePickerDialogFragment.java b/app/src/main/java/com/mdg/droiders/samagra/shush/fragments/TimePickerDialogFragment.java
new file mode 100644
index 0000000..4b4c8bc
--- /dev/null
+++ b/app/src/main/java/com/mdg/droiders/samagra/shush/fragments/TimePickerDialogFragment.java
@@ -0,0 +1,63 @@
+package com.mdg.droiders.samagra.shush.fragments;
+
+import android.app.Dialog;
+import android.app.TimePickerDialog;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.DialogFragment;
+import android.text.format.DateFormat;
+import android.widget.TimePicker;
+
+import java.util.Calendar;
+
+/**
+ * Created by rohan on 19/8/17.
+ * A fragment that displays a time picker dialog window,
+ * floating on top of its activity's window.
+ */
+public class TimePickerDialogFragment extends DialogFragment
+ implements TimePickerDialog.OnTimeSetListener {
+
+ public interface TimeSetCallback {
+ void onTimeSet(int hour, int minute);
+ }
+
+ private TimeSetCallback mCallback;
+ private Integer hourOfDay;
+ private Integer minutes;
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+
+ Calendar rightNow = Calendar.getInstance();
+ if (hourOfDay == null) {
+ hourOfDay = rightNow.get(Calendar.HOUR_OF_DAY);
+ }
+ if (minutes == null) {
+ minutes = rightNow.get(Calendar.MINUTE);
+ }
+
+ return new TimePickerDialog(getActivity(), this,
+ hourOfDay,
+ minutes,
+ DateFormat.is24HourFormat(getActivity()));
+ }
+
+ @Override
+ public void onTimeSet(TimePicker timePicker, int hourOfDay, int minute) {
+ if (mCallback != null) {
+ mCallback.onTimeSet(hourOfDay, minute);
+ }
+ }
+
+ public void setTimeSetCallback(TimeSetCallback mCallback) {
+ this.mCallback = mCallback;
+ }
+
+ public void setTime(Calendar displayTime) {
+ hourOfDay = displayTime.get(Calendar.HOUR_OF_DAY);
+ minutes = displayTime.get(Calendar.MINUTE);
+ }
+
+}
diff --git a/app/src/main/java/com/mdg/droiders/samagra/shush/interfaces/TimeAdapterNotifier.java b/app/src/main/java/com/mdg/droiders/samagra/shush/interfaces/TimeAdapterNotifier.java
new file mode 100644
index 0000000..6387449
--- /dev/null
+++ b/app/src/main/java/com/mdg/droiders/samagra/shush/interfaces/TimeAdapterNotifier.java
@@ -0,0 +1,39 @@
+package com.mdg.droiders.samagra.shush.interfaces;
+
+import com.mdg.droiders.samagra.shush.viewholders.TimeRowHolder;
+
+/**
+ * Created by rohan on 27/11/17.
+ * This interface acts as a communication link between {@link TimeRowHolder} and
+ * {@link com.mdg.droiders.samagra.shush.adapters.TimeListAdapter TimeListAdapter}.
+ */
+public interface TimeAdapterNotifier {
+
+ /**
+ * Updates db and reschedules alarm if startTime or endTime changes.
+ *
+ * @param holder The holder instance that is currently bound to the row whose
+ * time is changed.
+ */
+ void notifyTimeChanged(TimeRowHolder holder);
+
+ /**
+ * Updates db and sets alarm for the corresponding day.
+ *
+ * @param holder The holder instance that is currently bound to the row whose
+ * alarm is set.
+ * @param day The day for which alarm is set. The day is zero indexed and starts from monday.
+ */
+ void notifyDayAlarmSet(TimeRowHolder holder, int day);
+
+ /**
+ * Updates db and cancels alarm for the corresponding day.
+ *
+ * @param holder The holder instance that is currently bound to the row whose
+ * alarm is cancelled.
+ * @param day The day for which alarm is cancelled. The day is zero indexed
+ * and starts from monday.
+ */
+ void notifyDayAlarmCancelled(TimeRowHolder holder, int day);
+
+}
diff --git a/app/src/main/java/com/mdg/droiders/samagra/shush/receivers/AlarmBroadcastReceiver.java b/app/src/main/java/com/mdg/droiders/samagra/shush/receivers/AlarmBroadcastReceiver.java
new file mode 100644
index 0000000..9209830
--- /dev/null
+++ b/app/src/main/java/com/mdg/droiders/samagra/shush/receivers/AlarmBroadcastReceiver.java
@@ -0,0 +1,57 @@
+package com.mdg.droiders.samagra.shush.receivers;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioManager;
+
+import com.mdg.droiders.samagra.shush.AlarmScheduler;
+import com.mdg.droiders.samagra.shush.R;
+import com.mdg.droiders.samagra.shush.utils.RingerUtils;
+
+import java.util.Calendar;
+
+/**
+ * Created by rohan on 12/8/17.
+ *
+ * Broadcast Receiver that receives a broadcast from the alarm manager to silence/un-silence the phone.
+ */
+public class AlarmBroadcastReceiver extends BroadcastReceiver {
+
+ private static final String LOG_TAG = "Samagra/BR/";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+
+ // The phone will be shushed(silenced) if the boolean shush is true
+ boolean shush = intent.getBooleanExtra(
+ context.getString(R.string.alarm_intent_boolean_extra_key), false);
+ // The timeInMillis at which the alarm is triggered
+ // Used to trigger an alarm for next week at the same time.
+ long timeInMillis = intent.getLongExtra(
+ context.getString(R.string.alarm_intent_long_extra_key), -1);
+ // The alarm id used in pending intent
+ int alarmID = intent.getIntExtra(
+ context.getString(R.string.alarm_intent_int_extra_key), -1);
+
+ if (timeInMillis == -1 || alarmID == -1){
+ return;
+ }
+
+ if (shush) {
+ RingerUtils.setRingerMode(context, AudioManager.RINGER_MODE_SILENT);
+ RingerUtils.sendNotification(context, true);
+ } else {
+ RingerUtils.setRingerMode(context, AudioManager.RINGER_MODE_NORMAL);
+ RingerUtils.sendNotification(context, false);
+ }
+
+ Calendar nextWeek = Calendar.getInstance();
+ nextWeek.setTimeInMillis(timeInMillis);
+ nextWeek.add(Calendar.DAY_OF_MONTH, 7);
+
+ // Schedule an alarm for next week
+ new AlarmScheduler(context).setAlarm(nextWeek.getTimeInMillis(), alarmID, shush);
+
+ }
+}
diff --git a/app/src/main/java/com/mdg/droiders/samagra/shush/receivers/GeofenceBroadcastReceiver.java b/app/src/main/java/com/mdg/droiders/samagra/shush/receivers/GeofenceBroadcastReceiver.java
new file mode 100644
index 0000000..1dec6a3
--- /dev/null
+++ b/app/src/main/java/com/mdg/droiders/samagra/shush/receivers/GeofenceBroadcastReceiver.java
@@ -0,0 +1,52 @@
+package com.mdg.droiders.samagra.shush.receivers;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.util.Log;
+
+import com.google.android.gms.location.Geofence;
+import com.google.android.gms.location.GeofencingEvent;
+import com.mdg.droiders.samagra.shush.utils.RingerUtils;
+
+public class GeofenceBroadcastReceiver extends BroadcastReceiver {
+
+ private static final String LOG_TAG = GeofenceBroadcastReceiver.class.getName();
+
+ /***
+ * Handles the Broadcast message sent when the Geofence Transition is triggered
+ * Careful here though, this is running on the main thread so make sure you start an AsyncTask for
+ * anything that takes longer than say 10 second to run
+ *
+ * @param context
+ * @param intent
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ //get the geofencing event sent from the intent
+ GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
+
+ if (geofencingEvent.hasError()) {
+ Log.e(LOG_TAG, String.format("Error Code : %s", geofencingEvent.getErrorCode()));
+ return;
+ }
+
+ //get the transition type
+ int geoFenceTransition = geofencingEvent.getGeofenceTransition();
+
+ //Check which transition type has triggered the event & Send the notification
+ if (geoFenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER) {
+ RingerUtils.setRingerMode(context, AudioManager.RINGER_MODE_SILENT);
+ RingerUtils.sendNotification(context, true);
+ } else if (geoFenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {
+ RingerUtils.setRingerMode(context, AudioManager.RINGER_MODE_NORMAL);
+ RingerUtils.sendNotification(context, false);
+ } else {
+ Log.e(LOG_TAG, String.format("Unknown Transition , %d", geoFenceTransition));
+ return;
+ }
+
+ }
+
+}
diff --git a/app/src/main/java/com/mdg/droiders/samagra/shush/utils/RingerUtils.java b/app/src/main/java/com/mdg/droiders/samagra/shush/utils/RingerUtils.java
index cd34a2b..7c3163d 100644
--- a/app/src/main/java/com/mdg/droiders/samagra/shush/utils/RingerUtils.java
+++ b/app/src/main/java/com/mdg/droiders/samagra/shush/utils/RingerUtils.java
@@ -1,9 +1,18 @@
package com.mdg.droiders.samagra.shush.utils;
+import android.app.Notification;
import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.TaskStackBuilder;
import android.content.Context;
+import android.content.Intent;
+import android.graphics.BitmapFactory;
import android.media.AudioManager;
import android.os.Build;
+import android.support.v4.app.NotificationCompat;
+
+import com.mdg.droiders.samagra.shush.R;
+import com.mdg.droiders.samagra.shush.activities.MainActivity;
/**
* Created by rohan on 13/8/17.
@@ -18,13 +27,70 @@ public class RingerUtils {
* @param mode The desired mode to switch device to, can be AudioManager.RINGER_MODE_SILENT or
* AudioManager.RINGER_MODE_NORMAL
*/
- public static void setRingerMode(Context context, int mode){
+ public static void setRingerMode(Context context, int mode) {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- if (Build.VERSION.SDK_INT<24 || (Build.VERSION.SDK_INT>=24 && !notificationManager.isNotificationPolicyAccessGranted())){
+ if (Build.VERSION.SDK_INT < 24 || (Build.VERSION.SDK_INT >= 24 && notificationManager.isNotificationPolicyAccessGranted())) {
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
audioManager.setRingerMode(mode);
}
}
+ /**
+ * Posts a notification in the notification bar.
+ * Uses different icon drawables for different transition types.
+ * If the user clicks the notification, control goes to the MainActivity
+ *
+ * @param context The calling context for building a task stack
+ * @param shouldShush Boolean indicating whether the notification is created
+ * because the phone is shushed or because it is un-shushed.
+ */
+ public static void sendNotification(Context context, boolean shouldShush) {
+ //create an explicit content intent that starts the main activity
+ Intent notificationIntent = new Intent(context, MainActivity.class);
+
+ //Construct a task stack
+ TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
+
+ //Adding mainActivity to the task stack as the parent task
+ stackBuilder.addParentStack(MainActivity.class);
+
+ //Push the content intent onto the stack
+ stackBuilder.addNextIntent(notificationIntent);
+
+ //Get a pending intent consisting the entire backstack
+ PendingIntent notificationPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
+
+ //Get a notification builder
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
+
+ //Check the transition type to display the relevant icon image
+
+ if (shouldShush) {
+ builder.setSmallIcon(R.drawable.ic_volume_off_white_24dp)
+ .setLargeIcon(BitmapFactory.decodeResource(context.getResources(),
+ R.drawable.ic_volume_off_white_24dp))
+ .setContentTitle(context.getString(R.string.silent_mode_activated));
+ } else {
+ builder.setSmallIcon(R.drawable.ic_volume_up_white_24dp)
+ .setLargeIcon(BitmapFactory.decodeResource(context.getResources(),
+ R.drawable.ic_volume_up_white_24dp))
+ .setContentTitle(context.getString(R.string.back_to_normal));
+ }
+
+ // Continue building the notification
+ builder.setContentText(context.getString(R.string.touch_to_relaunch));
+ builder.setContentIntent(notificationPendingIntent);
+
+ //Auto remove the notification if the user touches it
+ builder.setAutoCancel(true);
+
+ // Notification will be shown with light, vibration and default sound.
+ builder.setDefaults(Notification.DEFAULT_ALL);
+
+ NotificationManager notificationManager = (NotificationManager)
+ context.getSystemService(Context.NOTIFICATION_SERVICE);
+
+ notificationManager.notify(0, builder.build());
+ }
}
diff --git a/app/src/main/java/com/mdg/droiders/samagra/shush/viewholders/TimeRowHolder.java b/app/src/main/java/com/mdg/droiders/samagra/shush/viewholders/TimeRowHolder.java
new file mode 100644
index 0000000..3184d87
--- /dev/null
+++ b/app/src/main/java/com/mdg/droiders/samagra/shush/viewholders/TimeRowHolder.java
@@ -0,0 +1,112 @@
+package com.mdg.droiders.samagra.shush.viewholders;
+
+import android.content.Context;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import com.mdg.droiders.samagra.shush.R;
+import com.mdg.droiders.samagra.shush.adapters.TimeListAdapter;
+import com.mdg.droiders.samagra.shush.fragments.TimePickerDialogFragment;
+import com.mdg.droiders.samagra.shush.interfaces.TimeAdapterNotifier;
+
+import java.text.ParseException;
+import java.util.Calendar;
+
+/**
+ * Created by rohan on 27/11/17.
+ * The {@link RecyclerView.ViewHolder ViewHolder} for the row which
+ * shows if a shush alarm is scheduled in the app.
+ */
+public class TimeRowHolder extends RecyclerView.ViewHolder {
+
+ private static final String DIALOG_TIME_TAG = "time_picker_dialog_tag";
+
+ public int id;
+ public View itemView;
+ public TextView startTime;
+ public TextView endTime;
+ public Switch enableSwitch;
+ public CheckBox[] days;
+
+ /**
+ * Listeners attached to the holder will only react to events
+ * if the a view is bound to the current holder.
+ */
+ public boolean isBound;
+
+ private TimePickerDialogFragment timePickerDialog;
+
+ public TimeRowHolder(View itemView, final TimeAdapterNotifier notifier,
+ final Context mContext) {
+ super(itemView);
+ this.itemView = itemView;
+ id = -1;
+ days = new CheckBox[]{
+ itemView.findViewById(R.id.monday),
+ itemView.findViewById(R.id.tuesday),
+ itemView.findViewById(R.id.wednesday),
+ itemView.findViewById(R.id.thursday),
+ itemView.findViewById(R.id.friday),
+ itemView.findViewById(R.id.saturday),
+ itemView.findViewById(R.id.sunday)
+ };
+ startTime = itemView.findViewById(R.id.start_time);
+ endTime = itemView.findViewById(R.id.end_time);
+ enableSwitch = itemView.findViewById(R.id.enable_switch);
+ timePickerDialog = new TimePickerDialogFragment();
+ View.OnClickListener timeChangeListener = new View.OnClickListener() {
+ @Override
+ public void onClick(final View view) {
+ if (!isBound) {
+ return;
+ }
+ timePickerDialog.setTimeSetCallback(new TimePickerDialogFragment.TimeSetCallback() {
+ @Override
+ public void onTimeSet(int hour, int minute) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.set(Calendar.HOUR_OF_DAY, hour);
+ calendar.set(Calendar.MINUTE, minute);
+ ((TextView) view).setText(
+ TimeListAdapter.DISPLAY_DATE_FORMAT.format(calendar.getTime()));
+ notifier.notifyTimeChanged(TimeRowHolder.this);
+ }
+ });
+ Calendar displayedTime = Calendar.getInstance();
+ try {
+ displayedTime.setTime(
+ TimeListAdapter.DISPLAY_DATE_FORMAT.parse(((TextView) view).getText().toString()));
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ timePickerDialog.setTime(displayedTime);
+ timePickerDialog.show(
+ ((AppCompatActivity) mContext).getSupportFragmentManager(),
+ DIALOG_TIME_TAG
+ );
+ }
+ };
+ startTime.setOnClickListener(timeChangeListener);
+ endTime.setOnClickListener(timeChangeListener);
+ for (int i = 0; i < 7; i++) {
+ final int finalI = i;
+ days[i].setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
+ if (!isBound) {
+ return;
+ }
+ if (checked) {
+ notifier.notifyDayAlarmSet(TimeRowHolder.this, finalI);
+ } else {
+ notifier.notifyDayAlarmCancelled(TimeRowHolder.this, finalI);
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/app/src/main/res/drawable/ic_plus.xml b/app/src/main/res/drawable/ic_plus.xml
new file mode 100644
index 0000000..d54c706
--- /dev/null
+++ b/app/src/main/res/drawable/ic_plus.xml
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 541773e..d274414 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- tools:context="com.mdg.droiders.samagra.shush.MainActivity">
+ tools:context="com.mdg.droiders.samagra.shush.activities.MainActivity">
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/time_picker_row.xml b/app/src/main/res/layout/time_picker_row.xml
new file mode 100644
index 0000000..66610b5
--- /dev/null
+++ b/app/src/main/res/layout/time_picker_row.xml
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 901bfa7..0ba56d3 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -12,5 +12,8 @@
Silent mode activatedTouch to launch the app.Back to normal
- com.mdg.droiders.samagra.shush.ShouldSilence
+ Add Shush Alarms
+ com.mdg.droiders.samagra.shush.ShouldShush
+ com.mdg.droiders.samagra.shush.TimeInMillis
+ com.mdg.droiders.samagra.shush.AlarmID
diff --git a/build.gradle b/build.gradle
index c2eea8e..428e343 100644
--- a/build.gradle
+++ b/build.gradle
@@ -15,6 +15,9 @@ buildscript {
allprojects {
repositories {
jcenter()
+ maven {
+ url "https://maven.google.com"
+ }
}
}