1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.deskclock 18 19 import android.app.Activity 20 import android.content.ContentUris 21 import android.content.Context 22 import android.content.Intent 23 import android.net.Uri 24 import android.os.AsyncTask 25 import android.os.Bundle 26 import android.os.Parcelable 27 import android.provider.AlarmClock 28 import android.text.TextUtils 29 import android.text.format.DateFormat 30 import android.text.format.DateUtils 31 32 import com.android.deskclock.AlarmUtils.popAlarmSetToast 33 import com.android.deskclock.alarms.AlarmStateManager 34 import com.android.deskclock.controller.Controller 35 import com.android.deskclock.data.DataModel 36 import com.android.deskclock.data.Timer 37 import com.android.deskclock.data.Weekdays 38 import com.android.deskclock.events.Events 39 import com.android.deskclock.provider.Alarm 40 import com.android.deskclock.provider.AlarmInstance 41 import com.android.deskclock.provider.ClockContract 42 import com.android.deskclock.provider.ClockContract.AlarmSettingColumns 43 import com.android.deskclock.provider.ClockContract.AlarmsColumns 44 import com.android.deskclock.timer.TimerFragment 45 import com.android.deskclock.timer.TimerService 46 import com.android.deskclock.uidata.UiDataModel 47 48 import java.util.Calendar 49 import java.util.Date 50 51 /** 52 * This activity is never visible. It processes all public intents defined by [AlarmClock] 53 * that apply to alarms and timers. Its definition in AndroidManifest.xml requires callers to hold 54 * the com.android.alarm.permission.SET_ALARM permission to complete the requested action. 55 */ 56 // TODO(b/165664115) Replace deprecated AsyncTask calls 57 class HandleApiCalls : Activity() { 58 private lateinit var mAppContext: Context 59 onCreatenull60 override fun onCreate(icicle: Bundle?) { 61 super.onCreate(icicle) 62 63 mAppContext = applicationContext 64 65 try { 66 val intent = intent 67 val action = intent?.action ?: return 68 LOGGER.i("onCreate: $intent") 69 70 when (action) { 71 AlarmClock.ACTION_SET_ALARM -> handleSetAlarm(intent) 72 AlarmClock.ACTION_SHOW_ALARMS -> handleShowAlarms() 73 AlarmClock.ACTION_SET_TIMER -> handleSetTimer(intent) 74 AlarmClock.ACTION_SHOW_TIMERS -> handleShowTimers(intent) 75 AlarmClock.ACTION_DISMISS_ALARM -> handleDismissAlarm(intent) 76 AlarmClock.ACTION_SNOOZE_ALARM -> handleSnoozeAlarm(intent) 77 AlarmClock.ACTION_DISMISS_TIMER -> handleDismissTimer(intent) 78 } 79 } catch (e: Exception) { 80 LOGGER.wtf(e) 81 } finally { 82 finish() 83 } 84 } 85 handleDismissAlarmnull86 private fun handleDismissAlarm(intent: Intent) { 87 // Change to the alarms tab. 88 UiDataModel.uiDataModel.selectedTab = UiDataModel.Tab.ALARMS 89 90 // Open DeskClock which is now positioned on the alarms tab. 91 startActivity(Intent(mAppContext, DeskClock::class.java)) 92 93 DismissAlarmAsync(mAppContext, intent, this).execute() 94 } 95 96 private class DismissAlarmAsync( 97 private val mContext: Context, 98 private val mIntent: Intent, 99 private val mActivity: Activity 100 ) : AsyncTask<Void?, Void?, Void?>() { doInBackgroundnull101 override fun doInBackground(vararg parameters: Void?): Void? { 102 val cr = mContext.contentResolver 103 val alarms = getEnabledAlarms(mContext) 104 if (alarms.isEmpty()) { 105 val reason = mContext.getString(R.string.no_scheduled_alarms) 106 Controller.getController().notifyVoiceFailure(mActivity, reason) 107 LOGGER.i("No scheduled alarms") 108 return null 109 } 110 111 // remove Alarms in MISSED, DISMISSED, and PREDISMISSED states 112 val i: MutableIterator<Alarm> = alarms.toMutableList().listIterator() 113 while (i.hasNext()) { 114 val instance = AlarmInstance.getNextUpcomingInstanceByAlarmId(cr, i.next().id) 115 if (instance == null || 116 instance.mAlarmState > ClockContract.InstancesColumns.FIRED_STATE) { 117 i.remove() 118 } 119 } 120 121 val searchMode = mIntent.getStringExtra(AlarmClock.EXTRA_ALARM_SEARCH_MODE) 122 if (searchMode == null && alarms.size > 1) { 123 // shows the UI where user picks which alarm they want to DISMISS 124 val pickSelectionIntent = Intent(mContext, 125 AlarmSelectionActivity::class.java) 126 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 127 .putExtra(AlarmSelectionActivity.EXTRA_ACTION, 128 AlarmSelectionActivity.ACTION_DISMISS) 129 .putExtra(AlarmSelectionActivity.EXTRA_ALARMS, 130 alarms.toTypedArray<Parcelable>()) 131 mContext.startActivity(pickSelectionIntent) 132 val voiceMessage = mContext.getString(R.string.pick_alarm_to_dismiss) 133 Controller.getController().notifyVoiceSuccess(mActivity, voiceMessage) 134 return null 135 } 136 137 // fetch the alarms that are specified by the intent 138 val fmaa = FetchMatchingAlarmsAction(mContext, alarms, mIntent, mActivity) 139 fmaa.run() 140 val matchingAlarms: List<Alarm> = fmaa.matchingAlarms 141 142 // If there are multiple matching alarms and it wasn't expected 143 // disambiguate what the user meant 144 if (AlarmClock.ALARM_SEARCH_MODE_ALL != searchMode && matchingAlarms.size > 1) { 145 val pickSelectionIntent = Intent(mContext, AlarmSelectionActivity::class.java) 146 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 147 .putExtra(AlarmSelectionActivity.EXTRA_ACTION, 148 AlarmSelectionActivity.ACTION_DISMISS) 149 .putExtra(AlarmSelectionActivity.EXTRA_ALARMS, 150 matchingAlarms.toTypedArray<Parcelable>()) 151 mContext.startActivity(pickSelectionIntent) 152 val voiceMessage = mContext.getString(R.string.pick_alarm_to_dismiss) 153 Controller.getController().notifyVoiceSuccess(mActivity, voiceMessage) 154 return null 155 } 156 157 // Apply the action to the matching alarms 158 for (alarm in matchingAlarms) { 159 dismissAlarm(alarm, mActivity) 160 LOGGER.i("Alarm dismissed: $alarm") 161 } 162 return null 163 } 164 165 companion object { getEnabledAlarmsnull166 private fun getEnabledAlarms(context: Context): List<Alarm> { 167 val selection = String.format("%s=?", AlarmsColumns.ENABLED) 168 val args = arrayOf("1") 169 return Alarm.getAlarms(context.contentResolver, selection, *args) 170 } 171 } 172 } 173 handleSnoozeAlarmnull174 private fun handleSnoozeAlarm(intent: Intent) { 175 SnoozeAlarmAsync(intent, this).execute() 176 } 177 178 private class SnoozeAlarmAsync( 179 private val mIntent: Intent, 180 private val mActivity: Activity 181 ) : AsyncTask<Void?, Void?, Void?>() { 182 private val mContext: Context = mActivity.applicationContext 183 doInBackgroundnull184 override fun doInBackground(vararg parameters: Void?): Void? { 185 val cr = mContext.contentResolver 186 val alarmInstances = AlarmInstance.getInstancesByState( 187 cr, ClockContract.InstancesColumns.FIRED_STATE) 188 if (alarmInstances.isEmpty()) { 189 val reason = mContext.getString(R.string.no_firing_alarms) 190 Controller.getController().notifyVoiceFailure(mActivity, reason) 191 LOGGER.i("No firing alarms") 192 return null 193 } 194 195 for (firingAlarmInstance in alarmInstances) { 196 snoozeAlarm(firingAlarmInstance, mContext, mActivity) 197 } 198 return null 199 } 200 } 201 202 /** 203 * Processes the SET_ALARM intent 204 * @param intent Intent passed to the app 205 */ handleSetAlarmnull206 private fun handleSetAlarm(intent: Intent) { 207 // Validate the hour, if one was given. 208 var hour = -1 209 if (intent.hasExtra(AlarmClock.EXTRA_HOUR)) { 210 hour = intent.getIntExtra(AlarmClock.EXTRA_HOUR, hour) 211 if (hour < 0 || hour > 23) { 212 val mins = intent.getIntExtra(AlarmClock.EXTRA_MINUTES, 0) 213 val voiceMessage = getString(R.string.invalid_time, hour, mins, " ") 214 Controller.getController().notifyVoiceFailure(this, voiceMessage) 215 LOGGER.i("Illegal hour: $hour") 216 return 217 } 218 } 219 220 // Validate the minute, if one was given. 221 val minutes = intent.getIntExtra(AlarmClock.EXTRA_MINUTES, 0) 222 if (minutes < 0 || minutes > 59) { 223 val voiceMessage = getString(R.string.invalid_time, hour, minutes, " ") 224 Controller.getController().notifyVoiceFailure(this, voiceMessage) 225 LOGGER.i("Illegal minute: $minutes") 226 return 227 } 228 229 val skipUi = intent.getBooleanExtra(AlarmClock.EXTRA_SKIP_UI, false) 230 val cr = contentResolver 231 232 // If time information was not provided an existing alarm cannot be located and a new one 233 // cannot be created so show the UI for creating the alarm from scratch per spec. 234 if (hour == -1) { 235 // Change to the alarms tab. 236 UiDataModel.uiDataModel.selectedTab = UiDataModel.Tab.ALARMS 237 238 // Intent has no time or an invalid time, open the alarm creation UI. 239 val createAlarm = Alarm.createIntent(this, DeskClock::class.java, Alarm.INVALID_ID) 240 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 241 .putExtra(AlarmClockFragment.ALARM_CREATE_NEW_INTENT_EXTRA, true) 242 243 // Open DeskClock which is now positioned on the alarms tab. 244 startActivity(createAlarm) 245 val voiceMessage = getString(R.string.invalid_time, hour, minutes, " ") 246 Controller.getController().notifyVoiceFailure(this, voiceMessage) 247 LOGGER.i("Missing alarm time; opening UI") 248 return 249 } 250 251 val selection = StringBuilder() 252 val argsList: MutableList<String> = ArrayList() 253 setSelectionFromIntent(intent, hour, minutes, selection, argsList) 254 255 // Try to locate an existing alarm using the intent data. 256 val args = argsList.toTypedArray() 257 val alarms = Alarm.getAlarms(cr, selection.toString(), *args) 258 259 val alarm: Alarm 260 if (alarms.isNotEmpty()) { 261 // Enable the first matching alarm. 262 alarm = alarms[0] 263 alarm.enabled = true 264 Alarm.updateAlarm(cr, alarm) 265 266 // Delete all old instances. 267 AlarmStateManager.deleteAllInstances(this, alarm.id) 268 269 Events.sendAlarmEvent(R.string.action_update, R.string.label_intent) 270 LOGGER.i("Updated alarm: $alarm") 271 } else { 272 // No existing alarm could be located; create one using the intent data. 273 alarm = Alarm() 274 updateAlarmFromIntent(alarm, intent) 275 alarm.deleteAfterUse = !alarm.daysOfWeek.isRepeating && skipUi 276 277 // Save the new alarm. 278 Alarm.addAlarm(cr, alarm) 279 280 Events.sendAlarmEvent(R.string.action_create, R.string.label_intent) 281 LOGGER.i("Created new alarm: $alarm") 282 } 283 284 // Schedule the next instance. 285 val now: Calendar = DataModel.dataModel.calendar 286 val alarmInstance = alarm.createInstanceAfter(now) 287 setupInstance(alarmInstance, skipUi) 288 289 val time = DateFormat.getTimeFormat(this).format(alarmInstance.alarmTime.time) 290 Controller.getController().notifyVoiceSuccess(this, getString(R.string.alarm_is_set, time)) 291 } 292 handleDismissTimernull293 private fun handleDismissTimer(intent: Intent) { 294 val dataUri = intent.data 295 if (dataUri != null) { 296 val selectedTimer = getSelectedTimer(dataUri) 297 if (selectedTimer != null) { 298 DataModel.dataModel.resetOrDeleteTimer(selectedTimer, R.string.label_intent) 299 Controller.getController().notifyVoiceSuccess(this, 300 resources.getQuantityString(R.plurals.expired_timers_dismissed, 1)) 301 LOGGER.i("Timer dismissed: $selectedTimer") 302 } else { 303 Controller.getController().notifyVoiceFailure(this, 304 getString(R.string.invalid_timer)) 305 LOGGER.e("Could not dismiss timer: invalid URI") 306 } 307 } else { 308 val expiredTimers: List<Timer> = DataModel.dataModel.expiredTimers 309 if (expiredTimers.isNotEmpty()) { 310 for (timer in expiredTimers) { 311 DataModel.dataModel.resetOrDeleteTimer(timer, R.string.label_intent) 312 } 313 val numberOfTimers = expiredTimers.size 314 val timersDismissedMessage = resources.getQuantityString( 315 R.plurals.expired_timers_dismissed, numberOfTimers, numberOfTimers) 316 Controller.getController().notifyVoiceSuccess(this, timersDismissedMessage) 317 LOGGER.i(timersDismissedMessage) 318 } else { 319 Controller.getController().notifyVoiceFailure(this, 320 getString(R.string.no_expired_timers)) 321 LOGGER.e("Could not dismiss timer: no expired timers") 322 } 323 } 324 } 325 getSelectedTimernull326 private fun getSelectedTimer(dataUri: Uri): Timer? { 327 return try { 328 val timerId = ContentUris.parseId(dataUri).toInt() 329 DataModel.dataModel.getTimer(timerId) 330 } catch (e: NumberFormatException) { 331 null 332 } 333 } 334 handleShowAlarmsnull335 private fun handleShowAlarms() { 336 Events.sendAlarmEvent(R.string.action_show, R.string.label_intent) 337 338 // Open DeskClock positioned on the alarms tab. 339 UiDataModel.uiDataModel.selectedTab = UiDataModel.Tab.ALARMS 340 startActivity(Intent(this, DeskClock::class.java)) 341 } 342 handleShowTimersnull343 private fun handleShowTimers(intent: Intent) { 344 Events.sendTimerEvent(R.string.action_show, R.string.label_intent) 345 346 val showTimersIntent = Intent(this, DeskClock::class.java) 347 348 val timers: List<Timer> = DataModel.dataModel.timers 349 if (timers.isNotEmpty()) { 350 val newestTimer = timers[timers.size - 1] 351 showTimersIntent.putExtra(TimerService.EXTRA_TIMER_ID, newestTimer.id) 352 } 353 354 // Open DeskClock positioned on the timers tab. 355 UiDataModel.uiDataModel.selectedTab = UiDataModel.Tab.TIMERS 356 startActivity(showTimersIntent) 357 } 358 handleSetTimernull359 private fun handleSetTimer(intent: Intent) { 360 // If no length is supplied, show the timer setup view. 361 if (!intent.hasExtra(AlarmClock.EXTRA_LENGTH)) { 362 // Change to the timers tab. 363 UiDataModel.uiDataModel.selectedTab = UiDataModel.Tab.TIMERS 364 365 // Open DeskClock which is now positioned on the timers tab and show the timer setup. 366 startActivity(TimerFragment.createTimerSetupIntent(this)) 367 LOGGER.i("Showing timer setup") 368 return 369 } 370 371 // Verify that the timer length is between one second and one day. 372 val lengthMillis = 373 DateUtils.SECOND_IN_MILLIS * intent.getIntExtra(AlarmClock.EXTRA_LENGTH, 0) 374 if (lengthMillis < Timer.MIN_LENGTH) { 375 val voiceMessage = getString(R.string.invalid_timer_length) 376 Controller.getController().notifyVoiceFailure(this, voiceMessage) 377 LOGGER.i("Invalid timer length requested: $lengthMillis") 378 return 379 } 380 381 val label = getLabelFromIntent(intent, "") 382 val skipUi = intent.getBooleanExtra(AlarmClock.EXTRA_SKIP_UI, false) 383 384 // Attempt to reuse an existing timer that is Reset with the same length and label. 385 var timer: Timer? = null 386 for (t in DataModel.dataModel.timers) { 387 if (!t.isReset) { 388 continue 389 } 390 if (t.length != lengthMillis) { 391 continue 392 } 393 if (!TextUtils.equals(label, t.label)) { 394 continue 395 } 396 397 timer = t 398 break 399 } 400 401 // Create a new timer if one could not be reused. 402 if (timer == null) { 403 timer = DataModel.dataModel.addTimer(lengthMillis, label, skipUi) 404 Events.sendTimerEvent(R.string.action_create, R.string.label_intent) 405 } 406 407 // Start the selected timer. 408 DataModel.dataModel.startTimer(timer) 409 Events.sendTimerEvent(R.string.action_start, R.string.label_intent) 410 Controller.getController().notifyVoiceSuccess(this, getString(R.string.timer_created)) 411 412 // If not instructed to skip the UI, display the running timer. 413 if (!skipUi) { 414 // Change to the timers tab. 415 UiDataModel.uiDataModel.selectedTab = UiDataModel.Tab.TIMERS 416 417 // Open DeskClock which is now positioned on the timers tab. 418 startActivity(Intent(this, DeskClock::class.java) 419 .putExtra(TimerService.EXTRA_TIMER_ID, timer.id)) 420 } 421 } 422 setupInstancenull423 private fun setupInstance(instance: AlarmInstance, skipUi: Boolean) { 424 var variableInstance = instance 425 variableInstance = AlarmInstance.addInstance(this.contentResolver, variableInstance) 426 AlarmStateManager.registerInstance(this, variableInstance, true) 427 popAlarmSetToast(this, variableInstance.alarmTime.timeInMillis) 428 if (!skipUi) { 429 // Change to the alarms tab. 430 UiDataModel.uiDataModel.selectedTab = UiDataModel.Tab.ALARMS 431 432 // Open DeskClock which is now positioned on the alarms tab. 433 val showAlarm = 434 Alarm.createIntent(this, DeskClock::class.java, variableInstance.mAlarmId!!) 435 .putExtra(AlarmClockFragment.SCROLL_TO_ALARM_INTENT_EXTRA, 436 variableInstance.mAlarmId!!) 437 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 438 startActivity(showAlarm) 439 } 440 } 441 442 /** 443 * Assemble a database where clause to search for an alarm matching the given `hour` and 444 * `minutes` as well as all of the optional information within the `intent` 445 * including: 446 * <ul> 447 * <li>alarm message</li> 448 * <li>repeat days</li> 449 * <li>vibration setting</li> 450 * <li>ringtone uri</li> 451 * </ul> 452 * 453 * @param intent contains details of the alarm to be located 454 * @param hour the hour of the day of the alarm 455 * @param minutes the minute of the hour of the alarm 456 * @param selection an out parameter containing a SQL where clause 457 * @param args an out parameter containing the values to substitute into the `selection` 458 */ setSelectionFromIntentnull459 private fun setSelectionFromIntent( 460 intent: Intent, 461 hour: Int, 462 minutes: Int, 463 selection: StringBuilder, 464 args: MutableList<String> 465 ) { 466 selection.append(AlarmsColumns.HOUR).append("=?") 467 args.add(hour.toString()) 468 selection.append(" AND ").append(AlarmsColumns.MINUTES).append("=?") 469 args.add(minutes.toString()) 470 if (intent.hasExtra(AlarmClock.EXTRA_MESSAGE)) { 471 selection.append(" AND ").append(AlarmSettingColumns.LABEL).append("=?") 472 args.add(getLabelFromIntent(intent, "")) 473 } 474 475 // Days is treated differently than other fields because if days is not specified, it 476 // explicitly means "not recurring". 477 selection.append(" AND ").append(AlarmsColumns.DAYS_OF_WEEK).append("=?") 478 args.add(getDaysFromIntent(intent, Weekdays.NONE).bits.toString()) 479 if (intent.hasExtra(AlarmClock.EXTRA_VIBRATE)) { 480 selection.append(" AND ").append(AlarmSettingColumns.VIBRATE).append("=?") 481 args.add(if (intent.getBooleanExtra(AlarmClock.EXTRA_VIBRATE, false)) "1" else "0") 482 } 483 if (intent.hasExtra(AlarmClock.EXTRA_RINGTONE)) { 484 selection.append(" AND ").append(AlarmSettingColumns.RINGTONE).append("=?") 485 486 // If the intent explicitly specified a NULL ringtone, treat it as the default ringtone. 487 val defaultRingtone: Uri = DataModel.dataModel.defaultAlarmRingtoneUri 488 val ringtone = getAlertFromIntent(intent, defaultRingtone) 489 args.add(ringtone.toString()) 490 } 491 } 492 493 companion object { 494 private val LOGGER = LogUtils.Logger("HandleApiCalls") 495 dismissAlarmnull496 fun dismissAlarm(alarm: Alarm, activity: Activity) { 497 val context = activity.applicationContext 498 val instance = AlarmInstance.getNextUpcomingInstanceByAlarmId( 499 context.contentResolver, alarm.id) 500 if (instance == null) { 501 val reason = context.getString(R.string.no_alarm_scheduled_for_this_time) 502 Controller.getController().notifyVoiceFailure(activity, reason) 503 LOGGER.i("No alarm instance to dismiss") 504 return 505 } 506 507 dismissAlarmInstance(instance, activity) 508 } 509 dismissAlarmInstancenull510 private fun dismissAlarmInstance(instance: AlarmInstance, activity: Activity) { 511 Utils.enforceNotMainLooper() 512 513 val context = activity.applicationContext 514 val alarmTime: Date = instance.alarmTime.time 515 val time = DateFormat.getTimeFormat(context).format(alarmTime) 516 517 if (instance.mAlarmState == ClockContract.InstancesColumns.FIRED_STATE || 518 instance.mAlarmState == ClockContract.InstancesColumns.SNOOZE_STATE) { 519 // Always dismiss alarms that are fired or snoozed. 520 AlarmStateManager.deleteInstanceAndUpdateParent(context, instance) 521 } else if (Utils.isAlarmWithin24Hours(instance)) { 522 // Upcoming alarms are always predismissed. 523 AlarmStateManager.setPreDismissState(context, instance) 524 } else { 525 // Otherwise the alarm cannot be dismissed at this time. 526 val reason = context.getString( 527 R.string.alarm_cant_be_dismissed_still_more_than_24_hours_away, time) 528 Controller.getController().notifyVoiceFailure(activity, reason) 529 LOGGER.i("Can't dismiss alarm more than 24 hours in advance") 530 } 531 532 // Log the successful dismissal. 533 val reason = context.getString(R.string.alarm_is_dismissed, time) 534 Controller.getController().notifyVoiceSuccess(activity, reason) 535 LOGGER.i("Alarm dismissed: $instance") 536 Events.sendAlarmEvent(R.string.action_dismiss, R.string.label_intent) 537 } 538 snoozeAlarmnull539 fun snoozeAlarm(alarmInstance: AlarmInstance, context: Context, activity: Activity) { 540 Utils.enforceNotMainLooper() 541 542 val time = DateFormat.getTimeFormat(context).format( 543 alarmInstance.alarmTime.time) 544 val reason = context.getString(R.string.alarm_is_snoozed, time) 545 AlarmStateManager.setSnoozeState(context, alarmInstance, true) 546 547 Controller.getController().notifyVoiceSuccess(activity, reason) 548 LOGGER.i("Alarm snoozed: $alarmInstance") 549 Events.sendAlarmEvent(R.string.action_snooze, R.string.label_intent) 550 } 551 552 /** 553 * @param alarm the alarm to be updated 554 * @param intent the intent containing new alarm field values to merge into the `alarm` 555 */ updateAlarmFromIntentnull556 private fun updateAlarmFromIntent(alarm: Alarm, intent: Intent) { 557 alarm.enabled = true 558 alarm.hour = intent.getIntExtra(AlarmClock.EXTRA_HOUR, alarm.hour) 559 alarm.minutes = intent.getIntExtra(AlarmClock.EXTRA_MINUTES, alarm.minutes) 560 alarm.vibrate = intent.getBooleanExtra(AlarmClock.EXTRA_VIBRATE, alarm.vibrate) 561 alarm.alert = getAlertFromIntent(intent, alarm.alert!!) 562 alarm.label = getLabelFromIntent(intent, alarm.label) 563 alarm.daysOfWeek = getDaysFromIntent(intent, alarm.daysOfWeek) 564 } 565 getLabelFromIntentnull566 private fun getLabelFromIntent(intent: Intent?, defaultLabel: String?): String { 567 val message = intent!!.extras!!.getString(AlarmClock.EXTRA_MESSAGE, defaultLabel) 568 return message ?: "" 569 } 570 getDaysFromIntentnull571 private fun getDaysFromIntent(intent: Intent, defaultWeekdays: Weekdays): Weekdays { 572 if (!intent.hasExtra(AlarmClock.EXTRA_DAYS)) { 573 return defaultWeekdays 574 } 575 576 val days: List<Int>? = intent.getIntegerArrayListExtra(AlarmClock.EXTRA_DAYS) 577 if (days != null) { 578 val daysArray = IntArray(days.size) 579 for (i in days.indices) { 580 daysArray[i] = days[i] 581 } 582 return Weekdays.fromCalendarDays(*daysArray) 583 } else { 584 // API says to use an ArrayList<Integer> but we allow the user to use a int[] too. 585 val daysArray = intent.getIntArrayExtra(AlarmClock.EXTRA_DAYS) 586 if (daysArray != null) { 587 return Weekdays.fromCalendarDays(*daysArray) 588 } 589 } 590 return defaultWeekdays 591 } 592 getAlertFromIntentnull593 private fun getAlertFromIntent(intent: Intent, defaultUri: Uri): Uri { 594 val alert = intent.getStringExtra(AlarmClock.EXTRA_RINGTONE) 595 if (alert == null) { 596 return defaultUri 597 } else if (AlarmClock.VALUE_RINGTONE_SILENT == alert || alert.isEmpty()) { 598 return AlarmSettingColumns.NO_RINGTONE_URI 599 } 600 601 return Uri.parse(alert) 602 } 603 } 604 }