/*
*
* 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.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import com.microsoft.mimickeralarm.scheduling.AlarmNotificationManager;
import com.microsoft.mimickeralarm.utilities.SharedWakeLock;
import com.microsoft.mimickeralarm.utilities.GeneralUtilities;
import java.util.UUID;
/**
* This class is the main application Service that handles the following:
*
* Hosting the AlarmRingingController which receives inbound intents with the DISPATCH_ALARM
* action. These intents are sent from the AlarmWakeReceiver.
*
* Transitioning to a foreground service to host the appropriate alarm notifications when an intent
* with the START_FOREGROUND action is received. These intents are sent from the
* AlarmNotificationManager.
*
* Toggling the partial wakelock to try to enable better alarm reliability when an intent with the
* TOOGLE_WAKELOCK action is received. These intents are sent from the AlarmNotificationManager.
*
* Transition from a foreground service back to a standard sticky service when an intent with the
* STOP_FOREGROUND action is received. These intents are sent from the AlarmNotificationManager.
*
* The service is bound to by the AlarmRingingActivity and it proxies calls to the hosted
* AlarmRingingController.
*/
public class AlarmRingingService extends Service {
public static final String ACTION_START_FOREGROUND =
"com.microsoft.mimickeralarm.ringing.AlarmRingingService.START_FOREGROUND";
public static final String ACTION_STOP_FOREGROUND =
"com.microsoft.mimickeralarm.ringing.AlarmRingingService.STOP_FOREGROUND";
public static final String ACTION_DISPATCH_ALARM =
"com.microsoft.mimickeralarm.ringing.AlarmRingingService.DISPATCH_ALARM";
public static final String ACTION_TOGGLE_WAKELOCK =
"com.microsoft.mimickeralarm.ringing.AlarmRingingService.TOGGLE_WAKELOCK";
public static final String ALARM_ID = "alarm_id";
private static final String ALARM_TIME = "alarm_time";
private static final String WAKELOCK_ENABLE = "wakelock_enable";
private static final String NOTIFICATION_TYPE = "notification_type";
public final String TAG = this.getClass().getSimpleName();
private final IBinder mBinder = new LocalBinder();
AlarmRingingController mController;
public static void startForegroundService(Context context,
UUID alarmId,
long alarmTime,
String notificationType) {
Intent serviceIntent = new Intent(AlarmRingingService.ACTION_START_FOREGROUND);
serviceIntent.setClass(context, AlarmRingingService.class);
serviceIntent.putExtra(ALARM_ID, alarmId);
serviceIntent.putExtra(ALARM_TIME, alarmTime);
serviceIntent.putExtra(NOTIFICATION_TYPE, notificationType);
context.startService(serviceIntent);
}
public static void stopForegroundService(Context context) {
Intent serviceIntent = new Intent(AlarmRingingService.ACTION_STOP_FOREGROUND);
serviceIntent.setClass(context, AlarmRingingService.class);
context.startService(serviceIntent);
}
public static void toggleWakeLock(Context context, boolean wakelockEnable) {
Intent serviceIntent = new Intent(AlarmRingingService.ACTION_TOGGLE_WAKELOCK);
serviceIntent.setClass(context, AlarmRingingService.class);
serviceIntent.putExtra(WAKELOCK_ENABLE, wakelockEnable);
context.startService(serviceIntent);
}
@Override
public void onCreate() {
super.onCreate();
GeneralUtilities.registerCrashReport(this);
Log.d(TAG, "Alarm service created!");
mController = AlarmRingingController.newInstance(getApplicationContext());
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "Alarm service started! - OnStartCommand()");
if (intent != null) {
if (ACTION_DISPATCH_ALARM.equals(intent.getAction())) {
Log.d(TAG, "Schedule ringing action!");
mController.registerAlarm(intent);
AlarmWakeReceiver.completeWakefulIntent(intent);
} else if (ACTION_START_FOREGROUND.equals(intent.getAction())) {
Log.d(TAG, "Show active notification!");
enableForegroundService(intent);
} else if (ACTION_STOP_FOREGROUND.equals(intent.getAction())) {
Log.d(TAG, "Remove active notification");
disableForegroundService();
} else if (ACTION_TOGGLE_WAKELOCK.equals(intent.getAction())) {
Log.d(TAG, "Toggle wakelock!");
toggleWakeLock(intent.getBooleanExtra(WAKELOCK_ENABLE, false));
}
}
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "Alarm service destroyed!");
}
private void enableForegroundService(Intent intent) {
UUID alarmId = (UUID) intent.getSerializableExtra(ALARM_ID);
String notificationType = intent.getStringExtra(NOTIFICATION_TYPE);
if (notificationType.equalsIgnoreCase(AlarmNotificationManager.NOTIFICATION_NEXT_ALARM)) {
long alarmTime = intent.getLongExtra(ALARM_TIME, 0);
startForeground(AlarmNotificationManager.NOTIFICATION_ID,
AlarmNotificationManager.createNextAlarmNotification(this, alarmId, alarmTime));
} else if (notificationType.equalsIgnoreCase(AlarmNotificationManager.NOTIFICATION_ALARM_RUNNING)) {
startForeground(AlarmNotificationManager.NOTIFICATION_ID,
AlarmNotificationManager.createAlarmRunningNotification(this, alarmId));
}
}
private void disableForegroundService() {
stopForeground(true);
toggleWakeLock(false);
}
private void toggleWakeLock(boolean enableWakeLock) {
if (enableWakeLock) {
SharedWakeLock.get(this).acquirePartialWakeLock();
} else {
SharedWakeLock.get(this).releasePartialWakeLock();
}
}
public void reportAlarmUXCompleted() {
Log.d(TAG, "Alarm UX completed!");
mController.alarmRingingSessionCompleted();
}
public void reportAlarmUXDismissed() {
Log.d(TAG, "Alarm UX dismissed!");
mController.alarmRingingSessionDismissed();
}
public void requestAllowUXDismiss() {
Log.d(TAG, "Allow Dismiss UX requested!");
mController.requestAllowDismiss();
}
public void silenceAlarmRinging() {
Log.d(TAG, "Alarm silenced!");
mController.silenceAlarmRinging();
}
public void startAlarmRinging() {
Log.d(TAG, "Alarm restarted!");
mController.startAlarmRinging();
}
public class LocalBinder extends Binder {
public AlarmRingingService getService() {
return AlarmRingingService.this;
}
}
}