DeskClock.java revision 421751e187dfa61bc1e48b52cdaf366628b06dd4
1/* 2 * Copyright (C) 2009 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 17package com.android.deskclock; 18 19import android.app.ActionBar; 20import android.app.ActionBar.Tab; 21import android.app.Activity; 22import android.app.Fragment; 23import android.app.FragmentTransaction; 24import android.content.ActivityNotFoundException; 25import android.content.Context; 26import android.content.Intent; 27import android.content.SharedPreferences; 28import android.os.Bundle; 29import android.os.Handler; 30import android.os.Message; 31import android.preference.PreferenceManager; 32import android.support.v13.app.FragmentPagerAdapter; 33import android.support.v4.view.ViewPager; 34import android.util.Log; 35import android.view.Menu; 36import android.view.MenuItem; 37import android.view.View; 38import android.view.animation.AnimationUtils; 39import android.widget.PopupMenu; 40import android.widget.Toast; 41 42import com.android.deskclock.stopwatch.StopwatchFragment; 43import com.android.deskclock.stopwatch.StopwatchService; 44import com.android.deskclock.stopwatch.Stopwatches; 45import com.android.deskclock.timer.TimerFragment; 46import com.android.deskclock.timer.Timers; 47import com.android.deskclock.worldclock.CitiesActivity; 48 49import java.util.ArrayList; 50import java.util.TimeZone; 51 52/** 53 * DeskClock clock view for desk docks. 54 */ 55public class DeskClock extends Activity { 56 private static final boolean DEBUG = false; 57 58 private static final String LOG_TAG = "DeskClock"; 59 60 // Alarm action for midnight (so we can update the date display). 61 private static final String KEY_SELECTED_TAB = "selected_tab"; 62 private static final String KEY_CLOCK_STATE = "clock_state"; 63 64 public static final String SELECT_TAB_INTENT_EXTRA = "deskclock.select.tab"; 65 66 private ActionBar mActionBar; 67 private Tab mTimerTab; 68 private Tab mClockTab; 69 private Tab mStopwatchTab; 70 71 private ViewPager mViewPager; 72 private TabsAdapter mTabsAdapter; 73 74 public static final int TIMER_TAB_INDEX = 0; 75 public static final int CLOCK_TAB_INDEX = 1; 76 public static final int STOPWATCH_TAB_INDEX = 2; 77 78 private int mSelectedTab; 79 private final boolean mDimmed = false; 80 81 private int mClockState = CLOCK_NORMAL; 82 private static final int CLOCK_NORMAL = 0; 83 private static final int CLOCK_LIGHTS_OUT = 1; 84 private static final int CLOCK_DIMMED = 2; 85 86 87 88 // Delay before hiding the action bar and buttons 89 private static final long LIGHTSOUT_TIMEOUT = 10 * 1000; // 10 seconds 90 // Delay before dimming the screen 91 private static final long DIM_TIMEOUT = 10 * 1000; // 10 seconds 92 93 // Opacity of black layer between clock display and wallpaper. 94 private final float DIM_BEHIND_AMOUNT_NORMAL = 0.4f; 95 private final float DIM_BEHIND_AMOUNT_DIMMED = 0.8f; // higher contrast when display dimmed 96 97 private final int SCREEN_SAVER_TIMEOUT_MSG = 0x2000; 98 private final int SCREEN_SAVER_MOVE_MSG = 0x2001; 99 private final int DIM_TIMEOUT_MSG = 0x2002; 100 private final int LIGHTSOUT_TIMEOUT_MSG = 0x2003; 101 private final int BACK_TO_NORMAL_MSG = 0x2004; 102 103 104 105 private final Handler mHandy = new Handler() { 106 @Override 107 public void handleMessage(Message m) { 108 /* if (m.what == LIGHTSOUT_TIMEOUT_MSG) { 109 doLightsOut(true); 110 mClockState = CLOCK_LIGHTS_OUT; 111 // Only dim if clock fragment is visible 112 if (mViewPager.getCurrentItem() == CLOCK_TAB_INDEX) { 113 scheduleDim(); 114 } 115 } else if (m.what == DIM_TIMEOUT_MSG) { 116 mClockState = CLOCK_DIMMED; 117 doDim(true); 118 } else if (m.what == BACK_TO_NORMAL_MSG){ 119 // ignore user interaction and do not go back to normal if a button was clicked 120 DeskClockFragment f = 121 (DeskClockFragment) mTabsAdapter.getFragment(mViewPager.getCurrentItem()); 122 if (f != null && f.isButtonClicked()) { 123 return; 124 } 125 126 int oldState = mClockState; 127 mClockState = CLOCK_NORMAL; 128 129 switch (oldState) { 130 case CLOCK_LIGHTS_OUT: 131 doLightsOut(false); 132 break; 133 case CLOCK_DIMMED: 134 doLightsOut(false); 135 doDim(true); 136 break; 137 } 138 }*/ 139 } 140 }; 141 142 @Override 143 public void onNewIntent(Intent newIntent) { 144 super.onNewIntent(newIntent); 145 if (DEBUG) Log.d(LOG_TAG, "onNewIntent with intent: " + newIntent); 146 147 // update our intent so that we can consult it to determine whether or 148 // not the most recent launch was via a dock event 149 setIntent(newIntent); 150 151 // Timer receiver may ask to go to the timers fragment if a timer expired. 152 int tab = newIntent.getIntExtra(SELECT_TAB_INTENT_EXTRA, -1); 153 if (tab != -1) { 154 if (mActionBar != null) { 155 mActionBar.setSelectedNavigationItem(tab); 156 } 157 } 158 } 159 160 private void initViews() { 161 162 if (mTabsAdapter == null) { 163 mViewPager = new ViewPager(this); 164 mViewPager.setId(R.id.desk_clock_pager); 165 mTabsAdapter = new TabsAdapter(this, mViewPager); 166 createTabs(mSelectedTab); 167 } 168 setContentView(mViewPager); 169 mActionBar.setSelectedNavigationItem(mSelectedTab); 170 } 171 172 private void createTabs(int selectedIndex) { 173 mActionBar = getActionBar(); 174 175 mActionBar.setDisplayOptions(0); 176 if (mActionBar != null) { 177 mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 178 mTimerTab = mActionBar.newTab(); 179 mTimerTab.setIcon(R.drawable.timer_tab); 180 mTimerTab.setContentDescription(R.string.menu_timer); 181 mTabsAdapter.addTab(mTimerTab, TimerFragment.class,TIMER_TAB_INDEX); 182 183 mClockTab = mActionBar.newTab(); 184 mClockTab.setIcon(R.drawable.clock_tab); 185 mClockTab.setContentDescription(R.string.menu_clock); 186 mTabsAdapter.addTab(mClockTab, ClockFragment.class,CLOCK_TAB_INDEX); 187 mStopwatchTab = mActionBar.newTab(); 188 mStopwatchTab.setIcon(R.drawable.stopwatch_tab); 189 mStopwatchTab.setContentDescription(R.string.menu_stopwatch); 190 mTabsAdapter.addTab(mStopwatchTab, StopwatchFragment.class,STOPWATCH_TAB_INDEX); 191 mActionBar.setSelectedNavigationItem(selectedIndex); 192 } 193 } 194 195 @Override 196 protected void onCreate(Bundle icicle) { 197 super.onCreate(icicle); 198 199 mSelectedTab = CLOCK_TAB_INDEX; 200 if (icicle != null) { 201 mSelectedTab = icicle.getInt(KEY_SELECTED_TAB, CLOCK_TAB_INDEX); 202 mClockState = icicle.getInt(KEY_CLOCK_STATE, CLOCK_NORMAL); 203 } 204 205 // Timer receiver may ask the app to go to the timer fragment if a timer expired 206 Intent i = getIntent(); 207 if (i != null) { 208 int tab = i.getIntExtra(SELECT_TAB_INTENT_EXTRA, -1); 209 if (tab != -1) { 210 mSelectedTab = tab; 211 } 212 } 213 initViews(); 214 setHomeTimeZone(); 215 } 216 217 @Override 218 protected void onResume() { 219 super.onResume(); 220 setClockState(false); 221 Intent stopwatchIntent = new Intent(getApplicationContext(), StopwatchService.class); 222 stopwatchIntent.setAction(Stopwatches.KILL_NOTIF); 223 startService(stopwatchIntent); 224 225 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); 226 SharedPreferences.Editor editor = prefs.edit(); 227 editor.putBoolean(Timers.NOTIF_APP_OPEN, true); 228 editor.apply(); 229 Intent timerIntent = new Intent(); 230 timerIntent.setAction(Timers.NOTIF_IN_USE_CANCEL); 231 sendBroadcast(timerIntent); 232 } 233 234 @Override 235 public void onPause() { 236 Intent intent = new Intent(getApplicationContext(), StopwatchService.class); 237 intent.setAction(Stopwatches.SHOW_NOTIF); 238 startService(intent); 239 240 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); 241 SharedPreferences.Editor editor = prefs.edit(); 242 editor.putBoolean(Timers.NOTIF_APP_OPEN, false); 243 editor.apply(); 244 Intent timerIntent = new Intent(); 245 timerIntent.setAction(Timers.NOTIF_IN_USE_SHOW); 246 sendBroadcast(timerIntent); 247 248 super.onPause(); 249 } 250 251 @Override 252 protected void onSaveInstanceState(Bundle outState) { 253 super.onSaveInstanceState(outState); 254 outState.putInt(KEY_SELECTED_TAB, mActionBar.getSelectedNavigationIndex()); 255 outState.putInt(KEY_CLOCK_STATE, mClockState); 256 } 257 258 private void setClockState(boolean fade) { 259 doDim(fade); 260 switch(mClockState) { 261 case CLOCK_NORMAL: 262 doLightsOut(false); 263 break; 264 case CLOCK_LIGHTS_OUT: 265 case CLOCK_DIMMED: 266 doLightsOut(true); 267 break; 268 default: 269 break; 270 } 271 } 272 273 public void clockButtonsOnClick(View v) { 274 if (v == null) 275 return; 276 switch (v.getId()) { 277 case R.id.alarms_button: 278 startActivity(new Intent(this, AlarmClock.class)); 279 break; 280 case R.id.cities_button: 281 startActivity(new Intent(this, CitiesActivity.class)); 282 break; 283 case R.id.menu_button: 284 showMenu(v); 285 break; 286 default: 287 break; 288 } 289 } 290 291 private void showMenu(View v) { 292 PopupMenu popupMenu = new PopupMenu(this, v); 293 popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener () { 294 @Override 295 public boolean onMenuItemClick(MenuItem item) { 296 switch (item.getItemId()) { 297 case R.id.menu_item_settings: 298 startActivity(new Intent(DeskClock.this, SettingsActivity.class)); 299 return true; 300 case R.id.menu_item_help: 301 Intent i = item.getIntent(); 302 if (i != null) { 303 try { 304 startActivity(i); 305 } catch (ActivityNotFoundException e) { 306 // No activity found to match the intent - ignore 307 } 308 } 309 return true; 310 default: 311 break; 312 } 313 return true; 314 } 315 }); 316 popupMenu.inflate(R.menu.desk_clock_menu); 317 318 Menu menu = popupMenu.getMenu(); 319 MenuItem help = menu.findItem(R.id.menu_item_help); 320 if (help != null) { 321 Utils.prepareHelpMenuItem(this, help); 322 } 323 popupMenu.show(); 324 } 325 326 /*** 327 * Insert the local time zone as the Home Time Zone if one is not set 328 */ 329 private void setHomeTimeZone() { 330 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); 331 String homeTimeZone = prefs.getString(SettingsActivity.KEY_HOME_TZ, ""); 332 if (!homeTimeZone.isEmpty()) { 333 return; 334 } 335 homeTimeZone = TimeZone.getDefault().getID(); 336 SharedPreferences.Editor editor = prefs.edit(); 337 editor.putString(SettingsActivity.KEY_HOME_TZ, homeTimeZone); 338 editor.apply(); 339 Log.v(LOG_TAG, "Setting home time zone to " + homeTimeZone); 340 } 341 342 private void scheduleLightsOut() { 343 mHandy.removeMessages(LIGHTSOUT_TIMEOUT_MSG); 344 mHandy.sendMessageDelayed(Message.obtain(mHandy, LIGHTSOUT_TIMEOUT_MSG), LIGHTSOUT_TIMEOUT); 345 } 346 347 public void doLightsOut(boolean state) { 348 349 if (state) { 350 mViewPager.setSystemUiVisibility(View.STATUS_BAR_HIDDEN); 351 mActionBar.hide(); 352 } else { 353 mViewPager.setSystemUiVisibility(View.STATUS_BAR_VISIBLE); 354 mActionBar.show(); 355 } 356 357 // in clock view show/hide the buttons at the bottom 358 if (mViewPager.getCurrentItem() == CLOCK_TAB_INDEX) { 359 // TODO: switch to listeners 360/* Fragment f = mTabsAdapter.getFragment(CLOCK_TAB_INDEX); 361 if (f != null) { 362 ((ClockFragment)f).showButtons(!state); 363 }*/ 364 } 365 if (!state) { 366 // Make sure dim will not start before lights out 367 mHandy.removeMessages(DIM_TIMEOUT_MSG); 368 scheduleLightsOut(); 369 } 370 } 371 372 private void doDim(boolean fade) { 373 if (mClockState == CLOCK_DIMMED) { 374 mViewPager.startAnimation( 375 AnimationUtils.loadAnimation(this, fade ? R.anim.dim : R.anim.dim_instant)); 376 } else { 377 mViewPager.startAnimation( 378 AnimationUtils.loadAnimation(this, fade ? R.anim.undim : R.anim.undim_instant)); 379 } 380 } 381 382 private void scheduleDim() { 383 mHandy.removeMessages(DIM_TIMEOUT_MSG); 384 mHandy.sendMessageDelayed(Message.obtain(mHandy, DIM_TIMEOUT_MSG), DIM_TIMEOUT); 385 } 386 387 @Override 388 public void onUserInteraction() { 389 super.onUserInteraction(); 390 mHandy.removeMessages(BACK_TO_NORMAL_MSG); 391 mHandy.sendMessage(Message.obtain(mHandy, BACK_TO_NORMAL_MSG)); 392 } 393 394 /*** 395 * Adapter for wrapping together the ActionBar's tab with the ViewPager 396 */ 397 398 private class TabsAdapter extends FragmentPagerAdapter 399 implements ActionBar.TabListener, ViewPager.OnPageChangeListener { 400 401 private static final String KEY_TAB_POSITION = "tab_position"; 402 403 final class TabInfo { 404 private final Class<?> clss; 405 private final Bundle args; 406 407 TabInfo(Class<?> _class, int position) { 408 clss = _class; 409 args = new Bundle(); 410 args.putInt(KEY_TAB_POSITION, position); 411 } 412 413 public int getPosition() { 414 return args.getInt(KEY_TAB_POSITION, 0); 415 } 416 } 417 418 private final ArrayList<TabInfo> mTabs = new ArrayList <TabInfo>(); 419 ActionBar mMainActionBar; 420 Context mContext; 421 ViewPager mPager; 422 423 public TabsAdapter(Activity activity, ViewPager pager) { 424 super(activity.getFragmentManager()); 425 mContext = activity; 426 mMainActionBar = activity.getActionBar(); 427 mPager = pager; 428 mPager.setAdapter(this); 429 mPager.setOnPageChangeListener(this); 430 } 431 432 @Override 433 public Fragment getItem(int position) { 434 TabInfo info = mTabs.get(position); 435 DeskClockFragment f = (DeskClockFragment) Fragment.instantiate( 436 mContext, info.clss.getName(), info.args); 437 return f; 438 } 439 440 @Override 441 public int getCount() { 442 return mTabs.size(); 443 } 444 445 public void addTab(ActionBar.Tab tab, Class<?> clss, int position) { 446 TabInfo info = new TabInfo(clss, position); 447 tab.setTag(info); 448 tab.setTabListener(this); 449 mTabs.add(info); 450 mMainActionBar.addTab(tab); 451 notifyDataSetChanged(); 452 } 453 454 @Override 455 public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 456 // Do nothing 457 } 458 459 @Override 460 public void onPageSelected(int position) { 461 mMainActionBar.setSelectedNavigationItem(position); 462 onUserInteraction(); 463 } 464 465 @Override 466 public void onPageScrollStateChanged(int state) { 467 // Do nothing 468 } 469 470 @Override 471 public void onTabReselected(Tab arg0, FragmentTransaction arg1) { 472 // Do nothing 473 } 474 475 @Override 476 public void onTabSelected(Tab tab, FragmentTransaction ft) { 477 TabInfo info = (TabInfo)tab.getTag(); 478 mPager.setCurrentItem(info.getPosition()); 479 onUserInteraction(); 480 } 481 482 @Override 483 public void onTabUnselected(Tab arg0, FragmentTransaction arg1) { 484 // Do nothing 485 486 } 487 } 488} 489