16d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux/* 26d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * Copyright (C) 2015 The Android Open Source Project 36d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * 46d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 56d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * in compliance with the License. You may obtain a copy of the License at 66d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * 76d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * http://www.apache.org/licenses/LICENSE-2.0 86d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * 96d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * Unless required by applicable law or agreed to in writing, software distributed under the License 106d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 116d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * or implied. See the License for the specific language governing permissions and limitations under 126d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * the License. 136d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux */ 146d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 156d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuxpackage com.android.deskclock.timer; 166d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 174c74e17b4d318c695cc12a0f79d26e2a4ae2af95James Lemieuximport android.content.Intent; 184c74e17b4d318c695cc12a0f79d26e2a4ae2af95James Lemieuximport android.content.pm.ActivityInfo; 196d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport android.os.Bundle; 206d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport android.os.SystemClock; 216d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport android.support.annotation.NonNull; 22df41926ee8cf9bf97866cc6c44a875eb82246fb6Christine Franksimport android.text.TextUtils; 236d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport android.transition.AutoTransition; 246d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport android.transition.TransitionManager; 256d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport android.view.Gravity; 266d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport android.view.KeyEvent; 276d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport android.view.View; 286d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport android.view.ViewGroup; 296d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport android.view.WindowManager; 306d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport android.widget.FrameLayout; 316d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport android.widget.TextView; 326d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 336d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport com.android.deskclock.BaseActivity; 342548f5593d52e5db4321afd61c4b40f8b4c4072cSean Stoutimport com.android.deskclock.LogUtils; 356d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport com.android.deskclock.R; 366d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport com.android.deskclock.data.DataModel; 376d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport com.android.deskclock.data.Timer; 386d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport com.android.deskclock.data.TimerListener; 396d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 406d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport java.util.List; 416d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 426d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux/** 436d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * This activity is designed to be shown over the lock screen. As such, it displays the expired 446d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * timers and a single button to reset them all. Each expired timer can also be reset to one minute 456d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * with a button in the user interface. All other timer operations are disabled in this activity. 466d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux */ 476d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuxpublic class ExpiredTimersActivity extends BaseActivity { 486d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 496d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux /** Scheduled to update the timers while at least one is expired. */ 506d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux private final Runnable mTimeUpdateRunnable = new TimeUpdateRunnable(); 516d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 526d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux /** Updates the timers displayed in this activity as the backing data changes. */ 536d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux private final TimerListener mTimerChangeWatcher = new TimerChangeWatcher(); 546d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 556d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux /** The scene root for transitions when expired timers are added/removed from this container. */ 566d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux private ViewGroup mExpiredTimersScrollView; 576d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 586d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux /** Displays the expired timers. */ 596d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux private ViewGroup mExpiredTimersView; 606d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 616d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux @Override 626d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux protected void onCreate(Bundle savedInstanceState) { 636d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux super.onCreate(savedInstanceState); 646d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 652548f5593d52e5db4321afd61c4b40f8b4c4072cSean Stout final List<Timer> expiredTimers = getExpiredTimers(); 662548f5593d52e5db4321afd61c4b40f8b4c4072cSean Stout 672548f5593d52e5db4321afd61c4b40f8b4c4072cSean Stout // If no expired timers, finish 682548f5593d52e5db4321afd61c4b40f8b4c4072cSean Stout if (expiredTimers.size() == 0) { 692548f5593d52e5db4321afd61c4b40f8b4c4072cSean Stout LogUtils.i("No expired timers, skipping display."); 702548f5593d52e5db4321afd61c4b40f8b4c4072cSean Stout finish(); 712548f5593d52e5db4321afd61c4b40f8b4c4072cSean Stout return; 722548f5593d52e5db4321afd61c4b40f8b4c4072cSean Stout } 732548f5593d52e5db4321afd61c4b40f8b4c4072cSean Stout 746d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux setContentView(R.layout.expired_timers_activity); 756d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 766d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux mExpiredTimersView = (ViewGroup) findViewById(R.id.expired_timers_list); 776d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux mExpiredTimersScrollView = (ViewGroup) findViewById(R.id.expired_timers_scroll); 786d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 796d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux findViewById(R.id.fab).setOnClickListener(new FabClickListener()); 806d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 816d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux final View view = findViewById(R.id.expired_timers_activity); 826d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE); 836d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 846d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED 856d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON 866d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON 876d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD 886d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON); 896d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 904c74e17b4d318c695cc12a0f79d26e2a4ae2af95James Lemieux // Close dialogs and window shade, so this is fully visible 914c74e17b4d318c695cc12a0f79d26e2a4ae2af95James Lemieux sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); 924c74e17b4d318c695cc12a0f79d26e2a4ae2af95James Lemieux 934c74e17b4d318c695cc12a0f79d26e2a4ae2af95James Lemieux // Honor rotation on tablets; fix the orientation on phones. 94feaedf7c77195021484a9bcd7e3d7c40cbeba78eJustin Klaassen if (!getResources().getBoolean(R.bool.rotateAlarmAlert)) { 954c74e17b4d318c695cc12a0f79d26e2a4ae2af95James Lemieux setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR); 964c74e17b4d318c695cc12a0f79d26e2a4ae2af95James Lemieux } 974c74e17b4d318c695cc12a0f79d26e2a4ae2af95James Lemieux 986d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux // Create views for each of the expired timers. 992548f5593d52e5db4321afd61c4b40f8b4c4072cSean Stout for (Timer timer : expiredTimers) { 1006d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux addTimer(timer); 1016d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 1026d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 1036d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux // Update views in response to timer data changes. 1046d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux DataModel.getDataModel().addTimerListener(mTimerChangeWatcher); 1056d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 1066d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 1076d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux @Override 1086d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux protected void onResume() { 1096d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux super.onResume(); 1106d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux startUpdatingTime(); 1116d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 1126d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 1136d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux @Override 1146d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux protected void onPause() { 1156d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux super.onPause(); 1166d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux stopUpdatingTime(); 1176d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 1186d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 1196d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux @Override 1206d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux public void onDestroy() { 1216d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux super.onDestroy(); 1226d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux DataModel.getDataModel().removeTimerListener(mTimerChangeWatcher); 1236d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 1246d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 1256d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux @Override 1266d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux public boolean dispatchKeyEvent(@NonNull KeyEvent event) { 1276d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux if (event.getAction() == KeyEvent.ACTION_UP) { 1286d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux switch (event.getKeyCode()) { 1296d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux case KeyEvent.KEYCODE_VOLUME_UP: 1306d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux case KeyEvent.KEYCODE_VOLUME_DOWN: 1316d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux case KeyEvent.KEYCODE_VOLUME_MUTE: 1326d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux case KeyEvent.KEYCODE_CAMERA: 1336d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux case KeyEvent.KEYCODE_FOCUS: 1346d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux DataModel.getDataModel().resetExpiredTimers(R.string.label_hardware_button); 1356d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux return true; 1366d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 1376d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 1386d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux return super.dispatchKeyEvent(event); 1396d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 1406d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 1416d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux /** 1426d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * Post the first runnable to update times within the UI. It will reschedule itself as needed. 1436d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux */ 1446d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux private void startUpdatingTime() { 1456d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux // Ensure only one copy of the runnable is ever scheduled by first stopping updates. 1466d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux stopUpdatingTime(); 1476d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux mExpiredTimersView.post(mTimeUpdateRunnable); 1486d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 1496d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 1506d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux /** 1516d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * Remove the runnable that updates times within the UI. 1526d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux */ 1536d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux private void stopUpdatingTime() { 1546d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux mExpiredTimersView.removeCallbacks(mTimeUpdateRunnable); 1556d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 1566d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 1576d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux /** 1586d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * Create and add a new view that corresponds with the given {@code timer}. 1596d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux */ 1606d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux private void addTimer(Timer timer) { 1616d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux TransitionManager.beginDelayedTransition(mExpiredTimersScrollView, new AutoTransition()); 1626d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 163b132d921bb64fc69145d040f18f504be6a2f0029Justin Klaassen final int timerId = timer.getId(); 1646d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux final TimerItem timerItem = (TimerItem) 1656d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux getLayoutInflater().inflate(R.layout.timer_item, mExpiredTimersView, false); 1666d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux // Store the timer id as a tag on the view so it can be located on delete. 167b132d921bb64fc69145d040f18f504be6a2f0029Justin Klaassen timerItem.setId(timerId); 1686d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux mExpiredTimersView.addView(timerItem); 1696d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 1706d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux // Hide the label hint for expired timers. 1716d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux final TextView labelView = (TextView) timerItem.findViewById(R.id.timer_label); 1726d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux labelView.setHint(null); 173df41926ee8cf9bf97866cc6c44a875eb82246fb6Christine Franks labelView.setVisibility(TextUtils.isEmpty(timer.getLabel()) ? View.GONE : View.VISIBLE); 1746d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 1756d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux // Add logic to the "Add 1 Minute" button. 1766d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux final View addMinuteButton = timerItem.findViewById(R.id.reset_add); 1776d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux addMinuteButton.setOnClickListener(new View.OnClickListener() { 1786d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux @Override 1796d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux public void onClick(View v) { 180b132d921bb64fc69145d040f18f504be6a2f0029Justin Klaassen final Timer timer = DataModel.getDataModel().getTimer(timerId); 1816d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux DataModel.getDataModel().addTimerMinute(timer); 1826d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 1836d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux }); 1846d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 1856d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux // If the first timer was just added, center it. 1866d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux final List<Timer> expiredTimers = getExpiredTimers(); 1876d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux if (expiredTimers.size() == 1) { 1886d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux centerFirstTimer(); 1896d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } else if (expiredTimers.size() == 2) { 1906d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux uncenterFirstTimer(); 1916d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 1926d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 1936d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 1946d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux /** 1956d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * Remove an existing view that corresponds with the given {@code timer}. 1966d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux */ 1976d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux private void removeTimer(Timer timer) { 1986d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux TransitionManager.beginDelayedTransition(mExpiredTimersScrollView, new AutoTransition()); 1996d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 200b132d921bb64fc69145d040f18f504be6a2f0029Justin Klaassen final int timerId = timer.getId(); 201b132d921bb64fc69145d040f18f504be6a2f0029Justin Klaassen final int count = mExpiredTimersView.getChildCount(); 202b132d921bb64fc69145d040f18f504be6a2f0029Justin Klaassen for (int i = 0; i < count; ++i) { 203b132d921bb64fc69145d040f18f504be6a2f0029Justin Klaassen final View timerView = mExpiredTimersView.getChildAt(i); 204b132d921bb64fc69145d040f18f504be6a2f0029Justin Klaassen if (timerView.getId() == timerId) { 205b132d921bb64fc69145d040f18f504be6a2f0029Justin Klaassen mExpiredTimersView.removeView(timerView); 206b132d921bb64fc69145d040f18f504be6a2f0029Justin Klaassen break; 207b132d921bb64fc69145d040f18f504be6a2f0029Justin Klaassen } 208b132d921bb64fc69145d040f18f504be6a2f0029Justin Klaassen } 2096d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 2106d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux // If the second last timer was just removed, center the last timer. 2116d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux final List<Timer> expiredTimers = getExpiredTimers(); 2126d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux if (expiredTimers.isEmpty()) { 2136d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux finish(); 2146d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } else if (expiredTimers.size() == 1) { 2156d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux centerFirstTimer(); 2166d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 2176d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 2186d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 2196d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux /** 2206d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * Center the single timer. 2216d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux */ 2226d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux private void centerFirstTimer() { 2236d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux final FrameLayout.LayoutParams lp = 2246d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux (FrameLayout.LayoutParams) mExpiredTimersView.getLayoutParams(); 2256d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux lp.gravity = Gravity.CENTER; 2266d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux mExpiredTimersView.requestLayout(); 2276d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 2286d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 2296d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux /** 2306d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * Display the multiple timers as a scrollable list. 2316d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux */ 2326d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux private void uncenterFirstTimer() { 2336d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux final FrameLayout.LayoutParams lp = 2346d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux (FrameLayout.LayoutParams) mExpiredTimersView.getLayoutParams(); 2356d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux lp.gravity = Gravity.NO_GRAVITY; 2366d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux mExpiredTimersView.requestLayout(); 2376d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 2386d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 2396d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux private List<Timer> getExpiredTimers() { 2406d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux return DataModel.getDataModel().getExpiredTimers(); 2416d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 2426d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 2436d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux /** 2446d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * Periodically refreshes the state of each timer. 2456d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux */ 2466d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux private class TimeUpdateRunnable implements Runnable { 2476d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux @Override 2486d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux public void run() { 2496d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux final long startTime = SystemClock.elapsedRealtime(); 2506d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 251b132d921bb64fc69145d040f18f504be6a2f0029Justin Klaassen final int count = mExpiredTimersView.getChildCount(); 252b132d921bb64fc69145d040f18f504be6a2f0029Justin Klaassen for (int i = 0; i < count; ++i) { 2536d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux final TimerItem timerItem = (TimerItem) mExpiredTimersView.getChildAt(i); 254b132d921bb64fc69145d040f18f504be6a2f0029Justin Klaassen final Timer timer = DataModel.getDataModel().getTimer(timerItem.getId()); 255b132d921bb64fc69145d040f18f504be6a2f0029Justin Klaassen if (timer != null) { 256b132d921bb64fc69145d040f18f504be6a2f0029Justin Klaassen timerItem.update(timer); 257b132d921bb64fc69145d040f18f504be6a2f0029Justin Klaassen } 2586d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 2596d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 2606d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux final long endTime = SystemClock.elapsedRealtime(); 2616d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 2626d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux // Try to maintain a consistent period of time between redraws. 263b132d921bb64fc69145d040f18f504be6a2f0029Justin Klaassen final long delay = Math.max(0L, startTime + 20L - endTime); 2646d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux mExpiredTimersView.postDelayed(this, delay); 2656d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 2666d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 2676d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 2686d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux /** 2696d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * Clicking the fab resets all expired timers. 2706d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux */ 2716d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux private class FabClickListener implements View.OnClickListener { 2726d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux @Override 2736d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux public void onClick(View v) { 2746d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux stopUpdatingTime(); 2756d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux DataModel.getDataModel().removeTimerListener(mTimerChangeWatcher); 2766d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux DataModel.getDataModel().resetExpiredTimers(R.string.label_deskclock); 2776d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux finish(); 2786d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 2796d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 2806d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 2816d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux /** 2826d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * Adds and removes expired timers from this activity based on their state changes. 2836d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux */ 2846d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux private class TimerChangeWatcher implements TimerListener { 2856d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux @Override 2866d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux public void timerAdded(Timer timer) { 2876d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux if (timer.isExpired()) { 2886d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux addTimer(timer); 2896d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 2906d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 2916d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 2926d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux @Override 2936d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux public void timerUpdated(Timer before, Timer after) { 2946d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux if (!before.isExpired() && after.isExpired()) { 2956d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux addTimer(after); 2966d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } else if (before.isExpired() && !after.isExpired()) { 2976d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux removeTimer(before); 2986d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 2996d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 3006d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux 3016d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux @Override 3026d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux public void timerRemoved(Timer timer) { 3036d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux if (timer.isExpired()) { 3046d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux removeTimer(timer); 3056d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 3066d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 3076d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux } 308b132d921bb64fc69145d040f18f504be6a2f0029Justin Klaassen} 309