/*
*
* Copyright (c) Microsoft. All rights reserved.
* Licensed under the MIT license.
*
* Project Oxford: http://ProjectOxford.ai
*
* Project Oxford Mimicker Alarm Github:
* https://github.com/Microsoft/ProjectOxford-Apps-MimickerAlarm
*
* Copyright (c) Microsoft Corporation
* All rights reserved.
*
* MIT License:
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.microsoft.mimickeralarm.ringing;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import com.microsoft.mimickeralarm.model.Alarm;
import com.microsoft.mimickeralarm.model.AlarmList;
import com.microsoft.mimickeralarm.scheduling.AlarmNotificationManager;
import com.microsoft.mimickeralarm.scheduling.AlarmScheduler;
import com.microsoft.mimickeralarm.utilities.SharedWakeLock;
import java.util.UUID;
/**
* This class is hosted by the AlarmRingingService. It controls the visibility of the alarm
* ringing user experience and the playing of the alarm ringtone and vibration.
*
* This class derives from the AlarmRingingSessionDispatcher which is an abstract class that
* handles the serialized queueing logic for the ringing of alarms. The controller implements the
* abstract methods which are called when the first item enters the queue (for resource
* initialization), when each item is dispatched and when the queue is drained (for resource
* cleanup).
*
* The alarm ringing user experience (AlarmRingingActivity) calls back into this class via bound
* calls to the AlarmRingingService to notify when:
*
* We should start and stop playing the alarm ringtone and vibrate the device
* The user experience was completed by finishing a game correctly
* The user experience was dismissed without finishing a game correctly e.g. by pressing the
* home button. In this case the alarm intent is resent to ensure the ringing experience is
* reshown to the user
* The user experience wants to be dismissed but in a scenario where another activity or task is
* launched i.e. the share flow
*/
public final class AlarmRingingController extends AlarmRingingSessionDispatcher {
private Context mContext;
private AlarmRingtonePlayer mRingtonePlayer;
private AlarmVibrator mVibrator;
private Alarm mCurrentAlarm;
private boolean mAllowDismissRequested;
public AlarmRingingController(Context context) {
mContext = context;
mRingtonePlayer = new AlarmRingtonePlayer(mContext);
mVibrator = new AlarmVibrator(mContext);
}
public static AlarmRingingController newInstance(Context context) {
return new AlarmRingingController(context);
}
@Override
public void beforeDispatchFirstAlarmRingingSession() {
mRingtonePlayer.initialize();
mVibrator.initialize();
SharedWakeLock.get(mContext).acquireFullWakeLock();
}
@Override
protected void alarmRingingSessionCompleted() {
// We need to handle the case where the alarm timed out. In that case we
// wont get an explicit call from the AlarmRingingActivity to silence the alarm
silenceAlarmRinging();
mCurrentAlarm = null;
super.alarmRingingSessionCompleted();
}
@Override
public void allAlarmRingingSessionsComplete() {
// Cleanup the state now that we are done with all ringing sessions
mVibrator.cleanup();
mRingtonePlayer.cleanup();
SharedWakeLock.get(mContext).releaseFullWakeLock();
// We should now update the notification to show the next alarm if appropriate
AlarmNotificationManager.get(mContext).handleNextAlarmNotificationStatus();
}
@Override
public void dispatchAlarmRingingSession(Intent intent) {
if (intent != null) {
UUID alarmId = (UUID) intent.getExtras().getSerializable(AlarmScheduler.ARGS_ALARM_ID);
mCurrentAlarm = AlarmList.get(mContext).getAlarm(alarmId);
startAlarmRinging();
launchRingingUserExperience(alarmId);
AlarmNotificationManager.get(mContext).handleAlarmRunningNotificationStatus(alarmId);
}
}
public void silenceAlarmRinging() {
mVibrator.stop();
mRingtonePlayer.stop();
}
public void startAlarmRinging() {
if (mCurrentAlarm.shouldVibrate()) {
mVibrator.vibrate();
}
Uri ringtone = mCurrentAlarm.getAlarmTone();
if (ringtone != null) {
mRingtonePlayer.play(ringtone);
}
}
public void requestAllowDismiss() {
mAllowDismissRequested = true;
}
// alarmRingingSessionCompleted should always be called before this method. If not, we should
// restart the AlarmRingingActivity so that we can successfully finish the alarm session
public void alarmRingingSessionDismissed() {
if (mAllowDismissRequested) {
mAllowDismissRequested = false;
} else {
if (mCurrentAlarm != null) {
launchRingingUserExperience(mCurrentAlarm.getId());
}
}
}
private void launchRingingUserExperience(UUID alarmId) {
Intent ringingIntent = new Intent(mContext, AlarmRingingActivity.class);
ringingIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
ringingIntent.putExtra(AlarmRingingService.ALARM_ID, alarmId);
mContext.startActivity(ringingIntent);
}
}