Commit e03cdf7e authored by Robert Hitt's avatar Robert Hitt
Browse files

Notifications can now be created

- As far as I can tell, they work perfectly currently,
though they're not exactly as precise as I'd like. Not sure if there's a way
to make them more precise however.
parent 84578d53
Pipeline #776 passed with stages
in 6 minutes and 7 seconds
......@@ -10,6 +10,7 @@
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".views.activities.MainActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:launchMode="singleTop">
......@@ -29,10 +30,18 @@
<activity android:name=".views.activities.AboutActivity"
android:parentActivityName=".views.activities.MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".views.activities.MainActivity"/>
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".views.activities.MainActivity"/>
</activity>
<receiver android:name=".util.NotificationReceiver"
android:process=":remote" >
</receiver>
<service android:name=".util.NotificationService"
android:exported="false" />
</application>
</manifest>
\ No newline at end of file
package srct.whatsopen.model;
public class NotificationSettings {
public NotificationSettings() {
}
public NotificationSettings(boolean opening, boolean closing, boolean interval_on, boolean interval_15, boolean interval_30, boolean interval_hour) {
this.opening = opening;
this.closing = closing;
this.interval_on = interval_on;
this.interval_15 = interval_15;
this.interval_30 = interval_30;
this.interval_hour = interval_hour;
}
public boolean opening;
public boolean closing;
public boolean interval_on;
public boolean interval_15;
public boolean interval_30;
public boolean interval_hour;
}
......@@ -38,14 +38,13 @@ public class FacilityPresenter {
public void toggleFavorite(Facility facility) {
final boolean status = !facility.isFavorited();
mFacilityView.changeFavoriteIcon(status);
Context context = mFacilityView.getContext();
Resources res = context.getResources();
int formatName = status ? R.string.toast_set_favorite : R.string.toast_unset_favorite;
String msg = String.format(res.getString(formatName), facility.getName());
Toast toast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
toast.show();
mFacilityView.changeFavoriteIcon(status);
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
// Get Realm instance and SharedPreferences
Realm realm = Realm.getDefaultInstance();
......@@ -163,6 +162,10 @@ public class FacilityPresenter {
return startTime;
}
// Currently only works if the Facility has one OpenTimes with
// startDay == 0 and endDay == 6. Should work if the Facility has 7
// OpenTimes
// TODO: fix
private boolean facilityDoesNotClose(OpenTimes openTimes) {
return (openTimes.getStartDay() == 0 && openTimes.getEndDay() == 6 &&
openTimes.getStartTime().equals("00:00:00") &&
......
......@@ -3,7 +3,6 @@ package srct.whatsopen.presenters;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.Toast;
import java.text.ParseException;
......@@ -25,10 +24,9 @@ import rx.schedulers.Schedulers;
import srct.whatsopen.model.Facility;
import srct.whatsopen.model.OpenTimes;
import srct.whatsopen.model.SpecialSchedule;
import srct.whatsopen.service.WhatsOpenService;
import srct.whatsopen.service.WhatsOpenApi;
import srct.whatsopen.util.WhatsOpenService;
import srct.whatsopen.util.WhatsOpenApi;
import srct.whatsopen.views.MainView;
import srct.whatsopen.views.activities.MainActivity;
public class MainPresenter {
......
package srct.whatsopen.presenters;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.Toast;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import io.realm.Realm;
import io.realm.RealmList;
import srct.whatsopen.model.Facility;
import srct.whatsopen.model.NotificationSettings;
import srct.whatsopen.model.OpenTimes;
import srct.whatsopen.model.SpecialSchedule;
import srct.whatsopen.util.NotificationReceiver;
import srct.whatsopen.views.NotificationView;
public class NotificationPresenter {
......@@ -31,92 +47,76 @@ public class NotificationPresenter {
public void presentNotifications(String name) {
mNotificationSettings = pref.getStringSet(name+"NotificationSettings", null);
boolean opening = false, closing = false, interval_on = false, interval_15 = false,
interval_30 = false, interval_hour = false;
NotificationSettings n = new NotificationSettings();
if(mNotificationSettings != null) {
for (String s : mNotificationSettings) {
switch (s) {
case "opening":
opening = true;
break;
case "closing":
closing = true;
break;
case "interval_on":
interval_on = true;
break;
case "interval_15":
interval_15 = true;
break;
case "interval_30":
interval_30 = true;
break;
case "interval_hour":
interval_hour = true;
break;
case "opening": n.opening = true; break;
case "closing": n.closing = true; break;
case "interval_on": n.interval_on = true; break;
case "interval_15": n.interval_15 = true; break;
case "interval_30": n.interval_30 = true; break;
case "interval_hour": n.interval_hour = true; break;
}
}
}
mNotificationView.setNotificationChecks(opening, closing, interval_on,
interval_15, interval_30, interval_hour);
mNotificationView.setNotificationChecks(n);
}
// Saves the notification settings to SharedPreferences and schedules the Notifications
public void saveNotifications(String name, boolean inEditMode, boolean opening, boolean closing,
boolean interval_on, boolean interval_15,
boolean interval_30, boolean interval_hour) {
public void saveNotifications(String name, boolean inEditMode, NotificationSettings n) {
if(inEditMode) {
editNotifications(name, opening, closing, interval_on, interval_15, interval_30,
interval_hour);
editNotifications(name, n);
} else {
setNotifications("Notifications set", name, opening, closing, interval_on,
interval_15, interval_30, interval_hour);
setNotifications("Notifications set", name, n);
}
}
// If no checkboxes are set, removes Notifications. Else, sets new Notifications
private void editNotifications(String name, boolean opening, boolean closing,
boolean interval_on, boolean interval_15,
boolean interval_30, boolean interval_hour) {
private void editNotifications(String name, NotificationSettings n) {
if(!opening && !closing && !interval_on && !interval_15 && !interval_30 && !interval_hour) {
removeNotifications(name);
if(!n.opening && !n.closing && !n.interval_on && !n.interval_15 && !n.interval_30 &&
!n.interval_hour) {
removeNotifications(name, true);
} else {
setNotifications("Notifications edited", name, opening, closing, interval_on,
interval_15, interval_30, interval_hour);
removeNotifications(name, false);
setNotifications("Notifications edited", name, n);
}
}
// Removes the Notification settings from SharedPreferences
public void removeNotifications(String name) {
public void removeNotifications(String name, boolean dismiss) {
SharedPreferences.Editor editor = pref.edit();
editor.putStringSet(name + "NotificationSettings", null);
editor.apply();
Toast.makeText(mNotificationView.getContext(), "Notifications removed",
Toast.LENGTH_SHORT).show();
if(dismiss) {
Toast.makeText(mNotificationView.getContext(), "Notifications removed",
Toast.LENGTH_SHORT).show();
mNotificationView.dismiss();
mNotificationView.dismiss();
}
}
// Saves the Notification settings to SharedPreferences
private void setNotifications(String message, String name, boolean opening, boolean closing,
boolean interval_on, boolean interval_15,
boolean interval_30, boolean interval_hour) {
private void setNotifications(String message, String name, NotificationSettings n) {
// if the Notifications are valid (i.e. one of each category is checked)
if((opening || closing) && (interval_on || interval_15 || interval_30 || interval_hour)) {
if((n.opening || n.closing) &&
(n.interval_on || n.interval_15 || n.interval_30 || n.interval_hour)) {
Set<String> set = setFromBooleans(opening, closing, interval_on, interval_15,
interval_30, interval_hour);
Set<String> set = setFromNotificationSettings(n);
SharedPreferences.Editor editor = pref.edit();
editor.putStringSet(name + "NotificationSettings", set);
editor.apply();
// TODO: make async
createAlarmsForFacility(name, n);
Toast.makeText(mNotificationView.getContext(), message, Toast.LENGTH_SHORT).show();
mNotificationView.dismiss();
......@@ -129,18 +129,170 @@ public class NotificationPresenter {
}
// Returns a String set parsed from the given booleans
private Set<String> setFromBooleans(boolean opening, boolean closing,
boolean interval_on, boolean interval_15,
boolean interval_30, boolean interval_hour) {
private Set<String> setFromNotificationSettings(NotificationSettings n) {
Set<String> set = new HashSet<>(6);
if(opening) set.add("opening");
if(closing) set.add("closing");
if(interval_on) set.add("interval_on");
if(interval_15) set.add("interval_15");
if(interval_30) set.add("interval_30");
if(interval_hour) set.add("interval_hour");
if(n.opening) set.add("opening");
if(n.closing) set.add("closing");
if(n.interval_on) set.add("interval_on");
if(n.interval_15) set.add("interval_15");
if(n.interval_30) set.add("interval_30");
if(n.interval_hour) set.add("interval_hour");
return set;
}
// Sets Alarms for the Facility with the given name
private void createAlarmsForFacility(String name, NotificationSettings notificationSettings) {
Realm realm = Realm.getDefaultInstance();
Facility facility = realm.where(Facility.class).equalTo("mName", name).findFirst();
RealmList<OpenTimes> openTimesList = getActiveSchedule(facility, Calendar.getInstance());
if(openTimesList == null || openTimesList.size() == 0)
return;
for(OpenTimes o : openTimesList) {
setAlarmsFromOpenTimes(name, o, notificationSettings);
}
realm.close();
}
private void setAlarmsFromOpenTimes(String name, OpenTimes openTimes,
NotificationSettings n) {
for(int i = openTimes.getStartDay(); i <= openTimes.getEndDay(); i++) {
// Parse Python day of week int to Calendar day of week
int day = ((i+1)%7)+1;
if(n.opening) {
if(n.interval_on)
setAlarm(name, day, "Op_on", 0, openTimes.getStartTime(),
"Opens now");
if(n.interval_15)
setAlarm(name, day, "Op_15", 15, openTimes.getStartTime(),
"Opens in 15 minutes");
if(n.interval_30)
setAlarm(name, day, "Op_30", 30, openTimes.getStartTime(),
"Opens in 30 minutes");
if(n.interval_hour)
setAlarm(name, day, "Op_hour", 60, openTimes.getStartTime(),
"Opens in an hour");
}
if(n.closing) {
if(n.interval_on)
setAlarm(name, day, "Cl_on", 0, openTimes.getEndTime(),
"Closes now");
if(n.interval_15)
setAlarm(name, day, "Cl_15", 15, openTimes.getEndTime(),
"Closes in 15 minutes");
if(n.interval_30)
setAlarm(name, day, "Cl_30", 30, openTimes.getEndTime(),
"Closes in 30 minutes");
if(n.interval_hour)
setAlarm(name, day, "Cl_hour", 60, openTimes.getEndTime(),
"Closes in an hour");
}
}
}
private void setAlarm(String name, int day, String type, int intervalMin, String time,
String message) {
Long alarmTime = parseTimeString(time, day, timeIsPassed(time, day));
int interval = intervalMin * 60000; // parse minutes to ms
alarmTime = alarmTime - interval;
int id = (name+type+day).hashCode(); // unique id for editing the Notification later
// Construct an Intent to execute the NotificationReceiver
Intent intent = new Intent(mNotificationView.getContext(), NotificationReceiver.class);
intent.putExtra("title", name);
intent.putExtra("text", message);
// Create a PendingIntent that will be triggered when the alarm goes off
PendingIntent pendingIntent = PendingIntent.getBroadcast(mNotificationView.getContext(),
id, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Set up alarm to repeat every week
AlarmManager alarm = (AlarmManager)
mNotificationView.getContext().getSystemService(Context.ALARM_SERVICE);
alarm.setRepeating(AlarmManager.RTC_WAKEUP, alarmTime, 7*1440*60000, pendingIntent);
}
private Long parseTimeString(String timeString, int day, boolean thisWeek) {
Calendar alarmCalendar = Calendar.getInstance();
int month = alarmCalendar.get(Calendar.MONTH);
int dayOfMonth = alarmCalendar.get(Calendar.DAY_OF_MONTH);
int year = alarmCalendar.get(Calendar.YEAR);
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
try {
// Set the new time
Date time = sdf.parse(timeString);
alarmCalendar.setTime(time);
// Set the current day, as setTime() changes the date to epoch
alarmCalendar.set(year, month, dayOfMonth);
// to make sure that the alarm isn't set in the past
if(!thisWeek)
alarmCalendar.add(Calendar.DATE, 1);
// set the day to the next day matching the given day of the week
while(alarmCalendar.get(Calendar.DAY_OF_WEEK) != day) {
alarmCalendar.add(Calendar.DATE, 1);
}
return alarmCalendar.getTimeInMillis();
} catch (ParseException pe) {
pe.printStackTrace();
return Long.valueOf(0);
}
}
// Returns the active schedule given the current date
private RealmList<OpenTimes> getActiveSchedule(Facility facility, Calendar now) {
RealmList<OpenTimes> openTimesList = facility.getMainSchedule().getOpenTimesList();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
Date currentDate = now.getTime();
for(SpecialSchedule s : facility.getSpecialSchedules()) {
Date startDate = sdf.parse(s.getValidStart());
Date endDate = sdf.parse(s.getValidEnd());
if(currentDate.compareTo(startDate) >= 0 && currentDate.compareTo(endDate) <= 0) {
openTimesList = s.getOpenTimesList();
return openTimesList;
}
}
} catch (ParseException pe) {
pe.printStackTrace();
return null;
}
return openTimesList;
}
// Determines if the given time is earlier in the current day
private boolean timeIsPassed(String time, int day) {
Calendar now = Calendar.getInstance();
if(now.get(Calendar.DAY_OF_WEEK) != day)
return false;
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
try {
Date timeDate = sdf.parse(time);
return now.getTime().compareTo(timeDate) > 0;
} catch (ParseException pe) {
pe.printStackTrace();
return false;
}
}
}
package srct.whatsopen.util;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class NotificationReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, NotificationService.class);
i.putExtra("title", intent.getStringExtra("title"));
i.putExtra("text", intent.getStringExtra("text"));
context.startService(i);
}
}
package srct.whatsopen.util;
import android.app.IntentService;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import srct.whatsopen.R;
public class NotificationService extends IntentService {
public NotificationService() {
super("NotificationService");
}
@Override
protected void onHandleIntent(Intent intent) {
String title = intent.getStringExtra("title");
String text = intent.getStringExtra("text");
int id = intent.getIntExtra("id", 0);
NotificationCompat.Builder builder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_access_time_black_24dp)
.setContentTitle(title)
.setContentText(text);
NotificationManager notificationManager = (NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(id, builder.build());
}
}
package srct.whatsopen.service;
package srct.whatsopen.util;
import java.util.List;
import retrofit2.Call;
import retrofit2.http.GET;
import rx.Observable;
......
package srct.whatsopen.service;
package srct.whatsopen.util;
import com.google.gson.ExclusionStrategy;
......
......@@ -5,7 +5,7 @@ import android.content.Context;
public interface FacilityView {
public Context getContext();
Context getContext();
public void changeFavoriteIcon(boolean favoriteStatus);
void changeFavoriteIcon(boolean favoriteStatus);
}
......@@ -5,9 +5,9 @@ import android.content.Context;
public interface MainView {
public void showProgressBar();
void showProgressBar();
public void dismissProgressBar();
void dismissProgressBar();
public Context getContext();
Context getContext();
}
......@@ -3,13 +3,13 @@ package srct.whatsopen.views;
import android.content.Context;
import srct.whatsopen.model.NotificationSettings;
public interface NotificationView {
public Context getContext();
Context getContext();
public void setNotificationChecks(boolean opening, boolean closing,
boolean intervalOn, boolean interval_15,
boolean interval_30, boolean intervalHour);
void setNotificationChecks(NotificationSettings n);
public void dismiss();
void dismiss();
}
......@@ -17,6 +17,7 @@ import butterknife.ButterKnife;
import butterknife.OnClick;
import srct.whatsopen.R;
import srct.whatsopen.model.NotificationSettings;
import srct.whatsopen.presenters.NotificationPresenter;
import srct.whatsopen.views.NotificationView;
......@@ -94,7 +95,7 @@ public class NotificationDialogFragment extends DialogFragment implements Notifi
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
getDialog().setTitle(mName+" Notifications");
getDialog().setTitle("Notifications");
if(inEditMode) {
mPresenter.presentNotifications(mName);
......@@ -106,11 +107,13 @@ public class NotificationDialogFragment extends DialogFragment implements Notifi