blob: 8e2fada4b5df7b933ee018a840882f3064ec6ce9 [file] [log] [blame]
/*
* Copyright 2014 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.iosched.service;
import android.app.*;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.util.Log;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.wearable.DataApi;
import com.google.android.gms.wearable.PutDataMapRequest;
import com.google.android.gms.wearable.PutDataRequest;
import com.google.android.gms.wearable.Wearable;
import com.google.samples.apps.iosched.R;
import com.google.samples.apps.iosched.provider.ScheduleContract;
import com.google.samples.apps.iosched.ui.BrowseSessionsActivity;
import com.google.samples.apps.iosched.ui.MapFragment;
import com.google.samples.apps.iosched.ui.MyScheduleActivity;
import com.google.samples.apps.iosched.ui.SessionFeedbackActivity;
import com.google.samples.apps.iosched.ui.phone.MapActivity;
import com.google.samples.apps.iosched.util.FeedbackUtils;
import com.google.samples.apps.iosched.util.PrefUtils;
import com.google.samples.apps.iosched.util.UIUtils;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static com.google.samples.apps.iosched.util.LogUtils.LOGD;
import static com.google.samples.apps.iosched.util.LogUtils.LOGE;
import static com.google.samples.apps.iosched.util.LogUtils.makeLogTag;
/**
* Background service to handle scheduling of starred session notification via
* {@link android.app.AlarmManager}.
*/
public class SessionAlarmService extends IntentService
implements GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
private static final String TAG = makeLogTag(SessionAlarmService.class);
public static final String ACTION_NOTIFY_SESSION =
"com.google.samples.apps.iosched.action.NOTIFY_SESSION";
public static final String ACTION_NOTIFY_SESSION_FEEDBACK =
"com.google.samples.apps.iosched.action.NOTIFY_SESSION_FEEDBACK";
public static final String ACTION_SCHEDULE_FEEDBACK_NOTIFICATION =
"com.google.samples.apps.iosched.action.SCHEDULE_FEEDBACK_NOTIFICATION";
public static final String ACTION_SCHEDULE_STARRED_BLOCK =
"com.google.samples.apps.iosched.action.SCHEDULE_STARRED_BLOCK";
public static final String ACTION_SCHEDULE_ALL_STARRED_BLOCKS =
"com.google.samples.apps.iosched.action.SCHEDULE_ALL_STARRED_BLOCKS";
public static final String EXTRA_SESSION_START =
"com.google.samples.apps.iosched.extra.SESSION_START";
public static final String EXTRA_SESSION_END =
"com.google.samples.apps.iosched.extra.SESSION_END";
public static final String EXTRA_SESSION_ALARM_OFFSET =
"com.google.samples.apps.iosched.extra.SESSION_ALARM_OFFSET";
public static final String EXTRA_SESSION_ID =
"com.google.samples.apps.iosched.extra.SESSION_ID";
public static final String EXTRA_SESSION_TITLE =
"com.google.samples.apps.iosched.extra.SESSION_TITLE";
public static final String EXTRA_SESSION_ROOM =
"com.google.samples.apps.iosched.extra.SESSION_ROOM";
public static final String EXTRA_SESSION_SPEAKERS =
"com.google.samples.apps.iosched.extra.SESSION_SPEAKERS";
public static final int NOTIFICATION_ID = 100;
public static final int FEEDBACK_NOTIFICATION_ID = 101;
// pulsate every 1 second, indicating a relatively high degree of urgency
private static final int NOTIFICATION_LED_ON_MS = 100;
private static final int NOTIFICATION_LED_OFF_MS = 1000;
private static final int NOTIFICATION_ARGB_COLOR = 0xff0088ff; // cyan
private static final long MILLI_TEN_MINUTES = 600000;
private static final long MILLI_FIVE_MINUTES = 300000;
private static final long MILLI_ONE_MINUTE = 60000;
private static final long UNDEFINED_ALARM_OFFSET = -1;
private static final long UNDEFINED_VALUE = -1;
public static final String ACTION_NOTIFICATION_DISMISSAL
= "com.google.sample.apps.iosched.ACTION_NOTIFICATION_DISMISSAL";
private GoogleApiClient mGoogleApiClient;
public static final String KEY_SESSION_ID = "session-id";
private static final String KEY_SESSION_NAME = "session-name";
private static final String KEY_SPEAKER_NAME = "speaker-name";
private static final String KEY_SESSION_ROOM = "session-room";
public static final String PATH_FEEDBACK = "/iowear/feedback";
// special session ID that identifies a debug notification
public static final String DEBUG_SESSION_ID = "debug-session-id";
public SessionAlarmService() {
super(TAG);
}
@Override
public void onCreate() {
super.onCreate();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Wearable.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
}
@Override
protected void onHandleIntent(Intent intent) {
mGoogleApiClient.blockingConnect(2000, TimeUnit.MILLISECONDS);
final String action = intent.getAction();
LOGD(TAG, "SessionAlarmService handling " + action);
if (ACTION_SCHEDULE_ALL_STARRED_BLOCKS.equals(action)) {
LOGD(TAG, "Scheduling all starred blocks.");
scheduleAllStarredBlocks();
scheduleAllStarredSessionFeedbacks();
return;
} else if (ACTION_NOTIFY_SESSION_FEEDBACK.equals(action)) {
LOGD(TAG, "Showing session feedback notification.");
notifySessionFeedback(DEBUG_SESSION_ID.equals(intent.getStringExtra(EXTRA_SESSION_ID)));
return;
}
final long sessionEnd = intent.getLongExtra(SessionAlarmService.EXTRA_SESSION_END,
UNDEFINED_VALUE);
if (sessionEnd == UNDEFINED_VALUE) {
LOGD(TAG, "IGNORING ACTION -- missing sessionEnd parameter");
return;
}
final long sessionAlarmOffset =
intent.getLongExtra(SessionAlarmService.EXTRA_SESSION_ALARM_OFFSET,
UNDEFINED_ALARM_OFFSET);
LOGD(TAG, "Session alarm offset is: " + sessionAlarmOffset);
// Feedback notifications have a slightly different set of extras.
if (ACTION_SCHEDULE_FEEDBACK_NOTIFICATION.equals(action)) {
final String sessionId = intent.getStringExtra(SessionAlarmService.EXTRA_SESSION_ID);
final String sessionTitle = intent.getStringExtra(
SessionAlarmService.EXTRA_SESSION_TITLE);
if (sessionTitle == null || sessionEnd == UNDEFINED_VALUE ||
sessionId == null) {
LOGE(TAG, "Attempted to schedule for feedback without providing extras.");
return;
}
LOGD(TAG, "Scheduling feedback alarm for session: " + sessionTitle);
scheduleFeedbackAlarm(sessionEnd, sessionAlarmOffset, sessionTitle);
return;
}
final long sessionStart =
intent.getLongExtra(SessionAlarmService.EXTRA_SESSION_START, UNDEFINED_VALUE);
if (sessionStart == UNDEFINED_VALUE) {
LOGD(TAG, "IGNORING ACTION -- no session start parameter.");
return;
}
if (ACTION_NOTIFY_SESSION.equals(action)) {
LOGD(TAG, "Notifying about sessions starting at " +
sessionStart + " = " + (new Date(sessionStart)).toString());
LOGD(TAG, "-> Alarm offset: " + sessionAlarmOffset);
notifySession(sessionStart, sessionAlarmOffset);
} else if (ACTION_SCHEDULE_STARRED_BLOCK.equals(action)) {
LOGD(TAG, "Scheduling session alarm.");
LOGD(TAG, "-> Session start: " + sessionStart + " = " + (new Date(sessionStart))
.toString());
LOGD(TAG, "-> Session end: " + sessionEnd + " = " + (new Date(sessionEnd)).toString());
LOGD(TAG, "-> Alarm offset: " + sessionAlarmOffset);
scheduleAlarm(sessionStart, sessionEnd, sessionAlarmOffset);
}
}
public void scheduleFeedbackAlarm(final long sessionEnd,
final long alarmOffset, final String sessionTitle) {
// By default, feedback alarms fire 5 minutes before session end time. If alarm offset is
// provided, alarm is set to go off that much time from now (useful for testing).
long alarmTime;
if (alarmOffset == UNDEFINED_ALARM_OFFSET) {
alarmTime = sessionEnd - MILLI_FIVE_MINUTES;
} else {
alarmTime = UIUtils.getCurrentTime(this) + alarmOffset;
}
LOGD(TAG, "Scheduling session feedback alarm for session '" + sessionTitle + "'");
LOGD(TAG, " -> end time: " + sessionEnd + " = " + (new Date(sessionEnd)).toString());
LOGD(TAG, " -> alarm time: " + alarmTime + " = " + (new Date(alarmTime)).toString());
final Intent feedbackIntent = new Intent(
ACTION_NOTIFY_SESSION_FEEDBACK,
null,
this,
SessionAlarmService.class);
PendingIntent pi = PendingIntent.getService(
this, 1, feedbackIntent, PendingIntent.FLAG_CANCEL_CURRENT);
final AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, alarmTime, pi);
}
private void scheduleAlarm(final long sessionStart,
final long sessionEnd, final long alarmOffset) {
NotificationManager nm =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.cancel(NOTIFICATION_ID);
final long currentTime = UIUtils.getCurrentTime(this);
// If the session is already started, do not schedule system notification.
if (currentTime > sessionStart) {
LOGD(TAG, "Not scheduling alarm because target time is in the past: " + sessionStart);
return;
}
// By default, sets alarm to go off at 10 minutes before session start time. If alarm
// offset is provided, alarm is set to go off by that much time from now.
long alarmTime;
if (alarmOffset == UNDEFINED_ALARM_OFFSET) {
alarmTime = sessionStart - MILLI_TEN_MINUTES;
} else {
alarmTime = currentTime + alarmOffset;
}
LOGD(TAG, "Scheduling alarm for " + alarmTime + " = " + (new Date(alarmTime)).toString());
final Intent notifIntent = new Intent(
ACTION_NOTIFY_SESSION,
null,
this,
SessionAlarmService.class);
// Setting data to ensure intent's uniqueness for different session start times.
notifIntent.setData(
new Uri.Builder().authority("com.google.samples.apps.iosched")
.path(String.valueOf(sessionStart)).build()
);
notifIntent.putExtra(SessionAlarmService.EXTRA_SESSION_START, sessionStart);
LOGD(TAG, "-> Intent extra: session start " + sessionStart);
notifIntent.putExtra(SessionAlarmService.EXTRA_SESSION_END, sessionEnd);
LOGD(TAG, "-> Intent extra: session end " + sessionEnd);
notifIntent.putExtra(SessionAlarmService.EXTRA_SESSION_ALARM_OFFSET, alarmOffset);
LOGD(TAG, "-> Intent extra: session alarm offset " + alarmOffset);
PendingIntent pi = PendingIntent.getService(this,
0,
notifIntent,
PendingIntent.FLAG_CANCEL_CURRENT);
final AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
// Schedule an alarm to be fired to notify user of added sessions are about to begin.
LOGD(TAG, "-> Scheduling RTC_WAKEUP alarm at " + alarmTime);
am.set(AlarmManager.RTC_WAKEUP, alarmTime, pi);
}
// A starred session is about to end; notify the user to provide session feedback.
// Constructs and triggers a system notification. Does nothing if the session has already
// concluded.
private void notifySessionFeedback(boolean debug) {
LOGD(TAG, "Considering firing notification for session feedback.");
if (debug) {
LOGD(TAG, "Note: this is a debug notification.");
}
// Don't fire notification if this feature is disabled in settings
if (!PrefUtils.shouldShowSessionFeedbackReminders(this)) {
LOGD(TAG, "Skipping session feedback notification. Disabled in settings.");
return;
}
final Cursor c = getContentResolver().query(
ScheduleContract.Sessions.CONTENT_MY_SCHEDULE_URI,
SessionsNeedingFeedbackQuery.PROJECTION,
SessionsNeedingFeedbackQuery.WHERE_CLAUSE, null, null);
if (c == null) {
return;
}
List<String> needFeedbackIds = new ArrayList<String>();
List<String> needFeedbackTitles = new ArrayList<String>();
while (c.moveToNext()) {
String sessionId = c.getString(SessionsNeedingFeedbackQuery.SESSION_ID);
String sessionTitle = c.getString(SessionsNeedingFeedbackQuery.SESSION_TITLE);
// Avoid repeated notifications.
if (UIUtils.isFeedbackNotificationFiredForSession(this, sessionId)) {
LOGD(TAG, "Skipping repeated session feedback notification for session '"
+ sessionTitle + "'");
continue;
}
needFeedbackIds.add(sessionId);
needFeedbackTitles.add(sessionTitle);
}
if (needFeedbackIds.size() == 0) {
// the user has already been notified of all sessions needing feedback
return;
}
LOGD(TAG, "Going forward with session feedback notification for "
+ needFeedbackIds.size() + " session(s).");
final Resources res = getResources();
// this is used to synchronize deletion of notifications on phone and wear
Intent dismissalIntent = new Intent(ACTION_NOTIFICATION_DISMISSAL);
// TODO: fix Wear dismiss integration
//dismissalIntent.putExtra(KEY_SESSION_ID, sessionId);
PendingIntent dismissalPendingIntent = PendingIntent
.getService(this, (int) new Date().getTime(), dismissalIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
String provideFeedbackTicker = res.getString(R.string.session_feedback_notification_ticker);
NotificationCompat.Builder notifBuilder = new NotificationCompat.Builder(this)
//.setColor(getResources().getColor(R.color.theme_primary))
// Note: setColor() is available in the support lib v21+.
// We commented it out because we want the source to compile
// against support lib v20. If you are using support lib
// v21 or above on Android L, uncomment this line.
.setContentText(provideFeedbackTicker)
.setTicker(provideFeedbackTicker)
.setLights(
SessionAlarmService.NOTIFICATION_ARGB_COLOR,
SessionAlarmService.NOTIFICATION_LED_ON_MS,
SessionAlarmService.NOTIFICATION_LED_OFF_MS)
.setSmallIcon(R.drawable.ic_stat_notification)
.setPriority(Notification.PRIORITY_LOW)
.setLocalOnly(true) // make it local to the phone
.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE)
.setDeleteIntent(dismissalPendingIntent)
.setAutoCancel(true);
if (needFeedbackIds.size() == 1) {
// Only 1 session needs feedback
Uri sessionUri = ScheduleContract.Sessions.buildSessionUri(needFeedbackIds.get(0));
PendingIntent pi = TaskStackBuilder.create(this)
.addNextIntent(new Intent(this, MyScheduleActivity.class))
.addNextIntent(new Intent(Intent.ACTION_VIEW, sessionUri, this,
SessionFeedbackActivity.class))
.getPendingIntent(1, PendingIntent.FLAG_CANCEL_CURRENT);
notifBuilder.setContentTitle(needFeedbackTitles.get(0))
.setContentIntent(pi);
} else {
// Show information about several sessions that need feedback
PendingIntent pi = TaskStackBuilder.create(this)
.addNextIntent(new Intent(this, MyScheduleActivity.class))
.getPendingIntent(1, PendingIntent.FLAG_CANCEL_CURRENT);
NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
inboxStyle.setBigContentTitle(provideFeedbackTicker);
for (String title : needFeedbackTitles) {
inboxStyle.addLine(title);
}
notifBuilder.setContentTitle(
getResources().getQuantityString(R.plurals.session_plurals,
needFeedbackIds.size(), needFeedbackIds.size()))
.setStyle(inboxStyle)
.setContentIntent(pi);
}
NotificationManager nm = (NotificationManager) getSystemService(
Context.NOTIFICATION_SERVICE);
LOGD(TAG, "Now showing session feedback notification!");
nm.notify(FEEDBACK_NOTIFICATION_ID, notifBuilder.build());
for (int i = 0; i < needFeedbackIds.size(); i++) {
setupNotificationOnWear(needFeedbackIds.get(i), null, needFeedbackTitles.get(i), null);
}
}
/**
* Builds corresponding notification for the Wear device that is paired to this handset. This
* is done by adding a Data Item to teh Data Store; the Wear device will be notified to build a
* local notification.
*/
private void setupNotificationOnWear(String sessionId, String sessionRoom, String sessionName,
String speaker) {
if (!mGoogleApiClient.isConnected()) {
Log.e(TAG, "setupNotificationOnWear(): Failed to send data item since there was no "
+ "connectivity to Google API Client");
return;
}
PutDataMapRequest putDataMapRequest = PutDataMapRequest
.create(FeedbackUtils.getFeedbackPath(sessionId));
putDataMapRequest.getDataMap().putLong("time", new Date().getTime());
putDataMapRequest.getDataMap().putString(KEY_SESSION_ID, sessionId);
putDataMapRequest.getDataMap().putString(KEY_SESSION_NAME, sessionName);
putDataMapRequest.getDataMap().putString(KEY_SPEAKER_NAME, speaker);
putDataMapRequest.getDataMap().putString(KEY_SESSION_ROOM, sessionRoom);
PutDataRequest request = putDataMapRequest.asPutDataRequest();
Wearable.DataApi.putDataItem(mGoogleApiClient, request)
.setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
@Override
public void onResult(DataApi.DataItemResult dataItemResult) {
LOGD(TAG, "setupNotificationOnWear(): Sending notification result success:"
+ dataItemResult.getStatus().isSuccess()
);
}
});
}
// Starred sessions are about to begin. Constructs and triggers system notification.
private void notifySession(final long sessionStart, final long alarmOffset) {
long currentTime = UIUtils.getCurrentTime(this);
final long intervalEnd = sessionStart + MILLI_TEN_MINUTES;
LOGD(TAG, "Considering notifying for time interval.");
LOGD(TAG, " Interval start: " + sessionStart + "=" + (new Date(sessionStart)).toString());
LOGD(TAG, " Interval end: " + intervalEnd + "=" + (new Date(intervalEnd)).toString());
LOGD(TAG, " Current time is: " + currentTime + "=" + (new Date(currentTime)).toString());
if (sessionStart < currentTime) {
LOGD(TAG, "Skipping session notification (too late -- time interval already started)");
return;
}
if (!PrefUtils.shouldShowSessionReminders(this)) {
// skip if disabled in settings
LOGD(TAG, "Skipping session notification for sessions. Disabled in settings.");
return;
}
// Avoid repeated notifications.
if (alarmOffset == UNDEFINED_ALARM_OFFSET && UIUtils.isNotificationFiredForBlock(
this, ScheduleContract.Blocks.generateBlockId(sessionStart, intervalEnd))) {
LOGD(TAG, "Skipping session notification (already notified)");
return;
}
final ContentResolver cr = getContentResolver();
LOGD(TAG, "Looking for sessions in interval " + sessionStart + " - " + intervalEnd);
Cursor c = cr.query(
ScheduleContract.Sessions.CONTENT_MY_SCHEDULE_URI,
SessionDetailQuery.PROJECTION,
ScheduleContract.Sessions.STARTING_AT_TIME_INTERVAL_SELECTION,
ScheduleContract.Sessions.buildAtTimeIntervalArgs(sessionStart, intervalEnd),
null);
int starredCount = c.getCount();
LOGD(TAG, "# starred sessions in that interval: " + c.getCount());
String singleSessionId = null;
String singleSessionRoomId = null;
ArrayList<String> starredSessionTitles = new ArrayList<String>();
while (c.moveToNext()) {
singleSessionId = c.getString(SessionDetailQuery.SESSION_ID);
singleSessionRoomId = c.getString(SessionDetailQuery.ROOM_ID);
starredSessionTitles.add(c.getString(SessionDetailQuery.SESSION_TITLE));
LOGD(TAG, "-> Title: " + c.getString(SessionDetailQuery.SESSION_TITLE));
}
if (starredCount < 1) {
return;
}
// Generates the pending intent which gets fired when the user taps on the notification.
// NOTE: Use TaskStackBuilder to comply with Android's design guidelines
// related to navigation from notifications.
Intent baseIntent = new Intent(this, MyScheduleActivity.class);
baseIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
TaskStackBuilder taskBuilder = TaskStackBuilder.create(this)
.addNextIntent(baseIntent);
// For a single session, tapping the notification should open the session details (b/15350787)
if (starredCount == 1) {
taskBuilder.addNextIntent(new Intent(Intent.ACTION_VIEW,
ScheduleContract.Sessions.buildSessionUri(singleSessionId)));
}
PendingIntent pi = taskBuilder.getPendingIntent(0, PendingIntent.FLAG_CANCEL_CURRENT);
final Resources res = getResources();
String contentText;
int minutesLeft = (int) (sessionStart - currentTime + 59000) / 60000;
if (minutesLeft < 1) {
minutesLeft = 1;
}
if (starredCount == 1) {
contentText = res.getString(R.string.session_notification_text_1, minutesLeft);
} else {
contentText = res.getQuantityString(R.plurals.session_notification_text,
starredCount - 1,
minutesLeft,
starredCount - 1);
}
NotificationCompat.Builder notifBuilder = new NotificationCompat.Builder(this)
.setContentTitle(starredSessionTitles.get(0))
.setContentText(contentText)
//.setColor(getResources().getColor(R.color.theme_primary))
// Note: setColor() is available in the support lib v21+.
// We commented it out because we want the source to compile
// against support lib v20. If you are using support lib
// v21 or above on Android L, uncomment this line.
.setTicker(res.getQuantityString(R.plurals.session_notification_ticker,
starredCount,
starredCount))
.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE)
.setLights(
SessionAlarmService.NOTIFICATION_ARGB_COLOR,
SessionAlarmService.NOTIFICATION_LED_ON_MS,
SessionAlarmService.NOTIFICATION_LED_OFF_MS)
.setSmallIcon(R.drawable.ic_stat_notification)
.setContentIntent(pi)
.setPriority(Notification.PRIORITY_MAX)
.setAutoCancel(true);
if (minutesLeft > 5) {
notifBuilder.addAction(R.drawable.ic_alarm_holo_dark,
String.format(res.getString(R.string.snooze_x_min), 5),
createSnoozeIntent(sessionStart, intervalEnd, 5));
}
if (starredCount == 1 && PrefUtils.isAttendeeAtVenue(this)) {
notifBuilder.addAction(R.drawable.ic_map_holo_dark,
res.getString(R.string.title_map),
createRoomMapIntent(singleSessionRoomId));
}
String bigContentTitle;
if (starredCount == 1 && starredSessionTitles.size() > 0) {
bigContentTitle = starredSessionTitles.get(0);
} else {
bigContentTitle = res.getQuantityString(R.plurals.session_notification_title,
starredCount,
minutesLeft,
starredCount);
}
NotificationCompat.InboxStyle richNotification = new NotificationCompat.InboxStyle(
notifBuilder)
.setBigContentTitle(bigContentTitle);
// Adds starred sessions starting at this time block to the notification.
for (int i = 0; i < starredCount; i++) {
richNotification.addLine(starredSessionTitles.get(i));
}
NotificationManager nm = (NotificationManager) getSystemService(
Context.NOTIFICATION_SERVICE);
LOGD(TAG, "Now showing notification.");
nm.notify(NOTIFICATION_ID, richNotification.build());
}
private PendingIntent createSnoozeIntent(final long sessionStart, final long sessionEnd,
final int snoozeMinutes) {
Intent scheduleIntent = new Intent(
SessionAlarmService.ACTION_SCHEDULE_STARRED_BLOCK,
null, this, SessionAlarmService.class);
scheduleIntent.putExtra(SessionAlarmService.EXTRA_SESSION_START, sessionStart);
scheduleIntent.putExtra(SessionAlarmService.EXTRA_SESSION_END, sessionEnd);
scheduleIntent.putExtra(SessionAlarmService.EXTRA_SESSION_ALARM_OFFSET,
snoozeMinutes * MILLI_ONE_MINUTE);
return PendingIntent.getService(this, 0, scheduleIntent,
PendingIntent.FLAG_CANCEL_CURRENT);
}
private PendingIntent createRoomMapIntent(final String roomId) {
Intent mapIntent = new Intent(getApplicationContext(),
UIUtils.getMapActivityClass(getApplicationContext()));
mapIntent.putExtra(MapFragment.EXTRA_ROOM, roomId);
mapIntent.putExtra(MapActivity.EXTRA_DETACHED_MODE, true);
return TaskStackBuilder
.create(getApplicationContext())
.addNextIntent(new Intent(this, BrowseSessionsActivity.class))
.addNextIntent(mapIntent)
.getPendingIntent(0, PendingIntent.FLAG_CANCEL_CURRENT);
}
private void scheduleAllStarredBlocks() {
final ContentResolver cr = getContentResolver();
final Cursor c = cr.query(ScheduleContract.Sessions.CONTENT_MY_SCHEDULE_URI,
new String[]{"distinct " + ScheduleContract.Sessions.SESSION_START,
ScheduleContract.Sessions.SESSION_END,
ScheduleContract.Sessions.SESSION_IN_MY_SCHEDULE},
null,
null,
null
);
if (c == null) {
return;
}
while (c.moveToNext()) {
final long sessionStart = c.getLong(0);
final long sessionEnd = c.getLong(1);
scheduleAlarm(sessionStart, sessionEnd, UNDEFINED_ALARM_OFFSET);
}
}
// Schedules feedback alarms for all starred sessions.
private void scheduleAllStarredSessionFeedbacks() {
final ContentResolver cr = getContentResolver();
// TODO: Should we also check that SESSION_IN_MY_SCHEDULE is true?
final Cursor c = cr.query(ScheduleContract.Sessions.CONTENT_MY_SCHEDULE_URI,
new String[]{
ScheduleContract.Sessions.SESSION_TITLE,
ScheduleContract.Sessions.SESSION_END,
ScheduleContract.Sessions.SESSION_IN_MY_SCHEDULE,
},
null,
null,
null
);
if (c == null) {
return;
}
while (c.moveToNext()) {
final String sessionTitle = c.getString(0);
final long sessionEnd = c.getLong(1);
scheduleFeedbackAlarm(sessionEnd, UNDEFINED_ALARM_OFFSET, sessionTitle);
}
}
public interface SessionDetailQuery {
String[] PROJECTION = {
ScheduleContract.Sessions.SESSION_ID,
ScheduleContract.Sessions.SESSION_TITLE,
ScheduleContract.Sessions.ROOM_ID,
ScheduleContract.Sessions.SESSION_IN_MY_SCHEDULE
};
int SESSION_ID = 0;
int SESSION_TITLE = 1;
int ROOM_ID = 2;
}
public interface SessionsNeedingFeedbackQuery {
String[] PROJECTION = {
ScheduleContract.Sessions.SESSION_ID,
ScheduleContract.Sessions.SESSION_TITLE,
ScheduleContract.Sessions.SESSION_IN_MY_SCHEDULE,
ScheduleContract.Sessions.HAS_GIVEN_FEEDBACK,
};
int SESSION_ID = 0;
int SESSION_TITLE = 1;
public static final String WHERE_CLAUSE =
ScheduleContract.Sessions.HAS_GIVEN_FEEDBACK + "=0";
}
@Override
public void onConnected(Bundle connectionHint) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Connected to Google Api Service");
}
}
@Override
public void onConnectionSuspended(int cause) {
// Ignore
}
@Override
public void onConnectionFailed(ConnectionResult result) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Disconnected from Google Api Service");
}
}
}