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 17 package com.android.deskclock; 18 19 import android.app.ActionBar; 20 import android.app.ActionBar.Tab; 21 import android.app.Activity; 22 import android.app.Fragment; 23 import android.app.FragmentTransaction; 24 import android.content.ActivityNotFoundException; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.SharedPreferences; 28 import android.graphics.drawable.TransitionDrawable; 29 import android.os.Bundle; 30 import android.os.Handler; 31 import android.os.Message; 32 import android.preference.PreferenceManager; 33 import android.support.v13.app.FragmentPagerAdapter; 34 import android.support.v4.view.ViewPager; 35 import android.util.Log; 36 import android.view.Menu; 37 import android.view.MenuItem; 38 import android.view.MotionEvent; 39 import android.view.View; 40 import android.view.View.OnTouchListener; 41 import android.view.animation.Animation; 42 import android.view.animation.Animation.AnimationListener; 43 import android.view.animation.AnimationUtils; 44 import android.view.animation.TranslateAnimation; 45 import android.widget.PopupMenu; 46 import android.widget.TextView; 47 48 import com.android.deskclock.stopwatch.StopwatchFragment; 49 import com.android.deskclock.stopwatch.StopwatchService; 50 import com.android.deskclock.stopwatch.Stopwatches; 51 import com.android.deskclock.timer.TimerFragment; 52 import com.android.deskclock.timer.TimerObj; 53 import com.android.deskclock.timer.Timers; 54 import com.android.deskclock.worldclock.CitiesActivity; 55 56 import java.util.ArrayList; 57 import java.util.TimeZone; 58 59 /** 60 * DeskClock clock view for desk docks. 61 */ 62 public class DeskClock extends Activity implements LabelDialogFragment.TimerLabelDialogHandler { 63 private static final boolean DEBUG = false; 64 65 private static final String LOG_TAG = "DeskClock"; 66 67 // Alarm action for midnight (so we can update the date display). 68 private static final String KEY_SELECTED_TAB = "selected_tab"; 69 private static final String KEY_CLOCK_STATE = "clock_state"; 70 71 public static final String SELECT_TAB_INTENT_EXTRA = "deskclock.select.tab"; 72 73 private ActionBar mActionBar; 74 private Tab mTimerTab; 75 private Tab mClockTab; 76 private Tab mStopwatchTab; 77 78 private ViewPager mViewPager; 79 private TabsAdapter mTabsAdapter; 80 81 public static final int TIMER_TAB_INDEX = 0; 82 public static final int CLOCK_TAB_INDEX = 1; 83 public static final int STOPWATCH_TAB_INDEX = 2; 84 85 private int mSelectedTab; 86 87 @Override 88 public void onNewIntent(Intent newIntent) { 89 super.onNewIntent(newIntent); 90 if (DEBUG) Log.d(LOG_TAG, "onNewIntent with intent: " + newIntent); 91 92 // update our intent so that we can consult it to determine whether or 93 // not the most recent launch was via a dock event 94 setIntent(newIntent); 95 96 // Timer receiver may ask to go to the timers fragment if a timer expired. 97 int tab = newIntent.getIntExtra(SELECT_TAB_INTENT_EXTRA, -1); 98 if (tab != -1) { 99 if (mActionBar != null) { 100 mActionBar.setSelectedNavigationItem(tab); 101 } 102 } 103 } 104 105 private void initViews() { 106 107 if (mTabsAdapter == null) { 108 mViewPager = new ViewPager(this); 109 mViewPager.setId(R.id.desk_clock_pager); 110 mTabsAdapter = new TabsAdapter(this, mViewPager); 111 createTabs(mSelectedTab); 112 } 113 setContentView(mViewPager); 114 mActionBar.setSelectedNavigationItem(mSelectedTab); 115 } 116 117 private void createTabs(int selectedIndex) { 118 mActionBar = getActionBar(); 119 120 mActionBar.setDisplayOptions(0); 121 if (mActionBar != null) { 122 mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 123 mTimerTab = mActionBar.newTab(); 124 mTimerTab.setIcon(R.drawable.timer_tab); 125 mTimerTab.setContentDescription(R.string.menu_timer); 126 mTabsAdapter.addTab(mTimerTab, TimerFragment.class,TIMER_TAB_INDEX); 127 128 mClockTab = mActionBar.newTab(); 129 mClockTab.setIcon(R.drawable.clock_tab); 130 mClockTab.setContentDescription(R.string.menu_clock); 131 mTabsAdapter.addTab(mClockTab, ClockFragment.class,CLOCK_TAB_INDEX); 132 mStopwatchTab = mActionBar.newTab(); 133 mStopwatchTab.setIcon(R.drawable.stopwatch_tab); 134 mStopwatchTab.setContentDescription(R.string.menu_stopwatch); 135 mTabsAdapter.addTab(mStopwatchTab, StopwatchFragment.class,STOPWATCH_TAB_INDEX); 136 mActionBar.setSelectedNavigationItem(selectedIndex); 137 } 138 } 139 140 @Override 141 protected void onCreate(Bundle icicle) { 142 super.onCreate(icicle); 143 144 mSelectedTab = CLOCK_TAB_INDEX; 145 if (icicle != null) { 146 mSelectedTab = icicle.getInt(KEY_SELECTED_TAB, CLOCK_TAB_INDEX); 147 } 148 149 // Timer receiver may ask the app to go to the timer fragment if a timer expired 150 Intent i = getIntent(); 151 if (i != null) { 152 int tab = i.getIntExtra(SELECT_TAB_INTENT_EXTRA, -1); 153 if (tab != -1) { 154 mSelectedTab = tab; 155 } 156 } 157 initViews(); 158 setHomeTimeZone(); 159 } 160 161 @Override 162 protected void onResume() { 163 super.onResume(); 164 165 Intent stopwatchIntent = new Intent(getApplicationContext(), StopwatchService.class); 166 stopwatchIntent.setAction(Stopwatches.KILL_NOTIF); 167 startService(stopwatchIntent); 168 169 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); 170 SharedPreferences.Editor editor = prefs.edit(); 171 editor.putBoolean(Timers.NOTIF_APP_OPEN, true); 172 editor.apply(); 173 Intent timerIntent = new Intent(); 174 timerIntent.setAction(Timers.NOTIF_IN_USE_CANCEL); 175 sendBroadcast(timerIntent); 176 } 177 178 @Override 179 public void onPause() { 180 181 Intent intent = new Intent(getApplicationContext(), StopwatchService.class); 182 intent.setAction(Stopwatches.SHOW_NOTIF); 183 startService(intent); 184 185 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); 186 SharedPreferences.Editor editor = prefs.edit(); 187 editor.putBoolean(Timers.NOTIF_APP_OPEN, false); 188 editor.apply(); 189 Utils.showInUseNotifications(this); 190 191 super.onPause(); 192 } 193 194 @Override 195 protected void onSaveInstanceState(Bundle outState) { 196 super.onSaveInstanceState(outState); 197 outState.putInt(KEY_SELECTED_TAB, mActionBar.getSelectedNavigationIndex()); 198 } 199 200 public void clockButtonsOnClick(View v) { 201 if (v == null) 202 return; 203 switch (v.getId()) { 204 case R.id.alarms_button: 205 startActivity(new Intent(this, AlarmClock.class)); 206 break; 207 case R.id.cities_button: 208 startActivity(new Intent(this, CitiesActivity.class)); 209 break; 210 case R.id.menu_button: 211 showMenu(v); 212 break; 213 default: 214 break; 215 } 216 } 217 218 private void showMenu(View v) { 219 PopupMenu popupMenu = new PopupMenu(this, v); 220 popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener () { 221 @Override 222 public boolean onMenuItemClick(MenuItem item) { 223 switch (item.getItemId()) { 224 case R.id.menu_item_settings: 225 startActivity(new Intent(DeskClock.this, SettingsActivity.class)); 226 return true; 227 case R.id.menu_item_help: 228 Intent i = item.getIntent(); 229 if (i != null) { 230 try { 231 startActivity(i); 232 } catch (ActivityNotFoundException e) { 233 // No activity found to match the intent - ignore 234 } 235 } 236 return true; 237 case R.id.menu_item_night_mode: 238 startActivity(new Intent(DeskClock.this, ScreensaverActivity.class)); 239 default: 240 break; 241 } 242 return true; 243 } 244 }); 245 popupMenu.inflate(R.menu.desk_clock_menu); 246 247 Menu menu = popupMenu.getMenu(); 248 MenuItem help = menu.findItem(R.id.menu_item_help); 249 if (help != null) { 250 Utils.prepareHelpMenuItem(this, help); 251 } 252 popupMenu.show(); 253 } 254 255 /*** 256 * Insert the local time zone as the Home Time Zone if one is not set 257 */ 258 private void setHomeTimeZone() { 259 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); 260 String homeTimeZone = prefs.getString(SettingsActivity.KEY_HOME_TZ, ""); 261 if (!homeTimeZone.isEmpty()) { 262 return; 263 } 264 homeTimeZone = TimeZone.getDefault().getID(); 265 SharedPreferences.Editor editor = prefs.edit(); 266 editor.putString(SettingsActivity.KEY_HOME_TZ, homeTimeZone); 267 editor.apply(); 268 Log.v(LOG_TAG, "Setting home time zone to " + homeTimeZone); 269 } 270 271 public boolean isClockTab() { 272 return mViewPager.getCurrentItem() == CLOCK_TAB_INDEX; 273 } 274 275 /*** 276 * Adapter for wrapping together the ActionBar's tab with the ViewPager 277 */ 278 279 private class TabsAdapter extends FragmentPagerAdapter 280 implements ActionBar.TabListener, ViewPager.OnPageChangeListener { 281 282 private static final String KEY_TAB_POSITION = "tab_position"; 283 284 final class TabInfo { 285 private final Class<?> clss; 286 private final Bundle args; 287 288 TabInfo(Class<?> _class, int position) { 289 clss = _class; 290 args = new Bundle(); 291 args.putInt(KEY_TAB_POSITION, position); 292 } 293 294 public int getPosition() { 295 return args.getInt(KEY_TAB_POSITION, 0); 296 } 297 } 298 299 private final ArrayList<TabInfo> mTabs = new ArrayList <TabInfo>(); 300 ActionBar mMainActionBar; 301 Context mContext; 302 ViewPager mPager; 303 304 public TabsAdapter(Activity activity, ViewPager pager) { 305 super(activity.getFragmentManager()); 306 mContext = activity; 307 mMainActionBar = activity.getActionBar(); 308 mPager = pager; 309 mPager.setAdapter(this); 310 mPager.setOnPageChangeListener(this); 311 } 312 313 @Override 314 public Fragment getItem(int position) { 315 TabInfo info = mTabs.get(position); 316 DeskClockFragment f = (DeskClockFragment) Fragment.instantiate( 317 mContext, info.clss.getName(), info.args); 318 return f; 319 } 320 321 @Override 322 public int getCount() { 323 return mTabs.size(); 324 } 325 326 public void addTab(ActionBar.Tab tab, Class<?> clss, int position) { 327 TabInfo info = new TabInfo(clss, position); 328 tab.setTag(info); 329 tab.setTabListener(this); 330 mTabs.add(info); 331 mMainActionBar.addTab(tab); 332 notifyDataSetChanged(); 333 } 334 335 @Override 336 public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 337 // Do nothing 338 } 339 340 @Override 341 public void onPageSelected(int position) { 342 mMainActionBar.setSelectedNavigationItem(position); 343 } 344 345 @Override 346 public void onPageScrollStateChanged(int state) { 347 // Do nothing 348 } 349 350 @Override 351 public void onTabReselected(Tab arg0, FragmentTransaction arg1) { 352 // Do nothing 353 } 354 355 @Override 356 public void onTabSelected(Tab tab, FragmentTransaction ft) { 357 TabInfo info = (TabInfo)tab.getTag(); 358 mPager.setCurrentItem(info.getPosition()); 359 } 360 361 @Override 362 public void onTabUnselected(Tab arg0, FragmentTransaction arg1) { 363 // Do nothing 364 365 } 366 } 367 368 public static abstract class OnTapListener implements OnTouchListener { 369 private float mLastTouchX; 370 private float mLastTouchY; 371 private long mLastTouchTime; 372 private TextView mMakePressedTextView; 373 private int mPressedColor, mGrayColor; 374 private final float MAX_MOVEMENT_ALLOWED = 20; 375 private final long MAX_TIME_ALLOWED = 500; 376 377 public OnTapListener(Activity activity, TextView makePressedView) { 378 mMakePressedTextView = makePressedView; 379 mPressedColor = activity.getResources().getColor(Utils.getPressedColorId()); 380 mGrayColor = activity.getResources().getColor(Utils.getGrayColorId()); 381 } 382 383 @Override 384 public boolean onTouch(View v, MotionEvent e) { 385 switch (e.getAction()) { 386 case (MotionEvent.ACTION_DOWN): 387 mLastTouchTime = Utils.getTimeNow(); 388 mLastTouchX = e.getX(); 389 mLastTouchY = e.getY(); 390 if (mMakePressedTextView != null) { 391 mMakePressedTextView.setTextColor(mPressedColor); 392 } 393 break; 394 case (MotionEvent.ACTION_UP): 395 float xDiff = Math.abs(e.getX()-mLastTouchX); 396 float yDiff = Math.abs(e.getY()-mLastTouchY); 397 long timeDiff = (Utils.getTimeNow() - mLastTouchTime); 398 if (xDiff < MAX_MOVEMENT_ALLOWED && yDiff < MAX_MOVEMENT_ALLOWED 399 && timeDiff < MAX_TIME_ALLOWED) { 400 if (mMakePressedTextView != null) { 401 v = mMakePressedTextView; 402 } 403 processClick(v); 404 resetValues(); 405 return true; 406 } 407 resetValues(); 408 break; 409 case (MotionEvent.ACTION_MOVE): 410 xDiff = Math.abs(e.getX()-mLastTouchX); 411 yDiff = Math.abs(e.getY()-mLastTouchY); 412 if (xDiff >= MAX_MOVEMENT_ALLOWED || yDiff >= MAX_MOVEMENT_ALLOWED) { 413 resetValues(); 414 } 415 break; 416 default: 417 resetValues(); 418 } 419 return false; 420 } 421 422 private void resetValues() { 423 mLastTouchX = -1*MAX_MOVEMENT_ALLOWED + 1; 424 mLastTouchY = -1*MAX_MOVEMENT_ALLOWED + 1; 425 mLastTouchTime = -1*MAX_TIME_ALLOWED + 1; 426 if (mMakePressedTextView != null) { 427 mMakePressedTextView.setTextColor(mGrayColor); 428 } 429 } 430 431 protected abstract void processClick(View v); 432 } 433 434 /** Called by the LabelDialogFormat class after the dialog is finished. **/ 435 @Override 436 public void onDialogLabelSet(TimerObj timer, String label, String tag) { 437 Fragment frag = getFragmentManager().findFragmentByTag(tag); 438 if (frag instanceof TimerFragment) { 439 ((TimerFragment) frag).setLabel(timer, label); 440 } 441 } 442 } 443