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