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