DeskClock.java revision 117f6da3ed857877c24573cddd2bc2260b73e370
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.animation.ArgbEvaluator; 20import android.animation.ObjectAnimator; 21import android.app.ActionBar; 22import android.app.ActionBar.Tab; 23import android.app.Activity; 24import android.app.Fragment; 25import android.app.FragmentManager; 26import android.app.FragmentTransaction; 27import android.content.ActivityNotFoundException; 28import android.content.Context; 29import android.content.Intent; 30import android.content.SharedPreferences; 31import android.content.res.Configuration; 32import android.graphics.Outline; 33import android.os.Bundle; 34import android.os.Handler; 35import android.preference.PreferenceManager; 36import android.support.v13.app.FragmentPagerAdapter; 37import android.support.v4.view.ViewPager; 38import android.text.TextUtils; 39import android.text.format.DateUtils; 40import android.util.Log; 41import android.view.Menu; 42import android.view.MenuItem; 43import android.view.MotionEvent; 44import android.view.View; 45import android.view.View.OnClickListener; 46import android.view.View.OnTouchListener; 47import android.view.ViewOutlineProvider; 48import android.widget.ImageButton; 49import android.widget.TextView; 50 51import com.android.deskclock.alarms.AlarmStateManager; 52import com.android.deskclock.provider.Alarm; 53import com.android.deskclock.stopwatch.StopwatchFragment; 54import com.android.deskclock.stopwatch.StopwatchService; 55import com.android.deskclock.stopwatch.Stopwatches; 56import com.android.deskclock.timer.TimerFragment; 57import com.android.deskclock.timer.TimerObj; 58import com.android.deskclock.timer.Timers; 59 60import java.util.ArrayList; 61import java.util.HashSet; 62import java.util.Locale; 63import java.util.TimeZone; 64 65/** 66 * DeskClock clock view for desk docks. 67 */ 68public class DeskClock extends Activity implements LabelDialogFragment.TimerLabelDialogHandler, 69 LabelDialogFragment.AlarmLabelDialogHandler { 70 private static final boolean DEBUG = false; 71 private static final String LOG_TAG = "DeskClock"; 72 // Alarm action for midnight (so we can update the date display). 73 private static final String KEY_SELECTED_TAB = "selected_tab"; 74 private static final String KEY_CLOCK_STATE = "clock_state"; 75 // Check whether to change background every minute 76 private static final long BACKGROUND_COLOR_CHECK_DELAY_MILLIS = DateUtils.MINUTE_IN_MILLIS; 77 // The depth of fab, use it to create shadow 78 private static final float FAB_DEPTH = 20f; 79 80 private boolean mIsFirstLaunch; 81 private ActionBar mActionBar; 82 private Tab mAlarmTab; 83 private Tab mClockTab; 84 private Tab mTimerTab; 85 private Tab mStopwatchTab; 86 private Menu mMenu; 87 private ViewPager mViewPager; 88 private TabsAdapter mTabsAdapter; 89 private Handler mHander; 90 public ImageButton mFab; 91 private int mSelectedTab; 92 private int mLastHourColor; 93 private final Runnable mBackgroundColorChanger = new Runnable() { 94 @Override 95 public void run() { 96 final int currHourColor = Utils.getCurrentHourColor(); 97 if (mLastHourColor != currHourColor) { 98 final ObjectAnimator animator = ObjectAnimator.ofInt(getWindow().getDecorView(), 99 "backgroundColor", mLastHourColor, currHourColor); 100 animator.setDuration(getResources().getInteger( 101 android.R.integer.config_longAnimTime)); 102 animator.setEvaluator(new ArgbEvaluator()); 103 animator.start(); 104 mLastHourColor = currHourColor; 105 } 106 mHander.postDelayed(this, BACKGROUND_COLOR_CHECK_DELAY_MILLIS); 107 } 108 }; 109 110 public static final int ALARM_TAB_INDEX = 0; 111 public static final int CLOCK_TAB_INDEX = 1; 112 public static final int TIMER_TAB_INDEX = 2; 113 public static final int STOPWATCH_TAB_INDEX = 3; 114 // Tabs indices are switched for right-to-left since there is no 115 // native support for RTL in the ViewPager. 116 public static final int RTL_ALARM_TAB_INDEX = 3; 117 public static final int RTL_CLOCK_TAB_INDEX = 2; 118 public static final int RTL_TIMER_TAB_INDEX = 1; 119 public static final int RTL_STOPWATCH_TAB_INDEX = 0; 120 public static final String SELECT_TAB_INTENT_EXTRA = "deskclock.select.tab"; 121 122 // TODO(rachelzhang): adding a broadcast receiver to adjust color when the timezone/time 123 // changes in the background. 124 125 @Override 126 protected void onStart() { 127 super.onStart(); 128 if (mHander == null) { 129 mHander = new Handler(); 130 } 131 mHander.postDelayed(mBackgroundColorChanger, BACKGROUND_COLOR_CHECK_DELAY_MILLIS); 132 } 133 134 @Override 135 protected void onStop() { 136 super.onStop(); 137 mHander.removeCallbacks(mBackgroundColorChanger); 138 } 139 140 @Override 141 public void onNewIntent(Intent newIntent) { 142 super.onNewIntent(newIntent); 143 if (DEBUG) Log.d(LOG_TAG, "onNewIntent with intent: " + newIntent); 144 145 // update our intent so that we can consult it to determine whether or 146 // not the most recent launch was via a dock event 147 setIntent(newIntent); 148 149 // Timer receiver may ask to go to the timers fragment if a timer expired. 150 int tab = newIntent.getIntExtra(SELECT_TAB_INTENT_EXTRA, -1); 151 if (tab != -1) { 152 if (mActionBar != null) { 153 mActionBar.setSelectedNavigationItem(tab); 154 } 155 } 156 } 157 158 private static final ViewOutlineProvider OVAL_OUTLINE_PROVIDER = new ViewOutlineProvider() { 159 @Override 160 public void getOutline(View view, Outline outline) { 161 outline.setOval(0, 0, view.getWidth(), view.getHeight()); 162 } 163 }; 164 165 private void initViews() { 166 setContentView(R.layout.desk_clock); 167 mFab = (ImageButton) findViewById(R.id.fab); 168 mFab.setOutlineProvider(OVAL_OUTLINE_PROVIDER); 169 mFab.setTranslationZ(FAB_DEPTH); 170 if (mTabsAdapter == null) { 171 mViewPager = (ViewPager) findViewById(R.id.desk_clock_pager); 172 // Keep all four tabs to minimize jank. 173 mViewPager.setOffscreenPageLimit(3); 174 mTabsAdapter = new TabsAdapter(this, mViewPager); 175 createTabs(mSelectedTab); 176 } 177 178 mFab.setOnClickListener(new OnClickListener() { 179 @Override 180 public void onClick(View view) { 181 DeskClockFragment fragment = (DeskClockFragment) mTabsAdapter.getItem( 182 getRtlPosition(mSelectedTab)); 183 fragment.respondClick(view); 184 } 185 }); 186 187 mActionBar.setSelectedNavigationItem(mSelectedTab); 188 } 189 190 private void createTabs(int selectedIndex) { 191 mActionBar = getActionBar(); 192 193 if (mActionBar != null) { 194 mActionBar.setDisplayOptions(0); 195 mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 196 197 mAlarmTab = mActionBar.newTab(); 198 mAlarmTab.setIcon(R.drawable.alarm_tab); 199 mAlarmTab.setContentDescription(R.string.menu_alarm); 200 mTabsAdapter.addTab(mAlarmTab, AlarmClockFragment.class, ALARM_TAB_INDEX); 201 202 mClockTab = mActionBar.newTab(); 203 mClockTab.setIcon(R.drawable.clock_tab); 204 mClockTab.setContentDescription(R.string.menu_clock); 205 mTabsAdapter.addTab(mClockTab, ClockFragment.class, CLOCK_TAB_INDEX); 206 207 mTimerTab = mActionBar.newTab(); 208 mTimerTab.setIcon(R.drawable.timer_tab); 209 mTimerTab.setContentDescription(R.string.menu_timer); 210 mTabsAdapter.addTab(mTimerTab, TimerFragment.class, TIMER_TAB_INDEX); 211 212 mStopwatchTab = mActionBar.newTab(); 213 mStopwatchTab.setIcon(R.drawable.stopwatch_tab); 214 mStopwatchTab.setContentDescription(R.string.menu_stopwatch); 215 mTabsAdapter.addTab(mStopwatchTab, StopwatchFragment.class, STOPWATCH_TAB_INDEX); 216 217 mActionBar.setSelectedNavigationItem(selectedIndex); 218 mTabsAdapter.notifySelectedPage(selectedIndex); 219 } 220 } 221 222 @Override 223 protected void onCreate(Bundle icicle) { 224 super.onCreate(icicle); 225 226 getWindow().setBackgroundDrawable(null); 227 228 mSelectedTab = CLOCK_TAB_INDEX; 229 if (icicle != null) { 230 mSelectedTab = icicle.getInt(KEY_SELECTED_TAB, CLOCK_TAB_INDEX); 231 } 232 233 // Timer receiver may ask the app to go to the timer fragment if a timer expired 234 Intent i = getIntent(); 235 if (i != null) { 236 int tab = i.getIntExtra(SELECT_TAB_INTENT_EXTRA, -1); 237 if (tab != -1) { 238 mSelectedTab = tab; 239 } 240 } 241 initViews(); 242 setHomeTimeZone(); 243 244 // We need to update the system next alarm time on app startup because the 245 // user might have clear our data. 246 AlarmStateManager.updateNextAlarm(this); 247 ExtensionsFactory.init(getAssets()); 248 } 249 250 @Override 251 protected void onResume() { 252 super.onResume(); 253 254 mLastHourColor = Utils.getCurrentHourColor(); 255 getWindow().getDecorView().setBackgroundColor(mLastHourColor); 256 257 // We only want to show notifications for stopwatch/timer when the app is closed so 258 // that we don't have to worry about keeping the notifications in perfect sync with 259 // the app. 260 Intent stopwatchIntent = new Intent(getApplicationContext(), StopwatchService.class); 261 stopwatchIntent.setAction(Stopwatches.KILL_NOTIF); 262 startService(stopwatchIntent); 263 264 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); 265 SharedPreferences.Editor editor = prefs.edit(); 266 editor.putBoolean(Timers.NOTIF_APP_OPEN, true); 267 editor.apply(); 268 Intent timerIntent = new Intent(); 269 timerIntent.setAction(Timers.NOTIF_IN_USE_CANCEL); 270 sendBroadcast(timerIntent); 271 } 272 273 @Override 274 public void onPause() { 275 Intent intent = new Intent(getApplicationContext(), StopwatchService.class); 276 intent.setAction(Stopwatches.SHOW_NOTIF); 277 startService(intent); 278 279 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); 280 SharedPreferences.Editor editor = prefs.edit(); 281 editor.putBoolean(Timers.NOTIF_APP_OPEN, false); 282 editor.apply(); 283 Utils.showInUseNotifications(this); 284 285 super.onPause(); 286 } 287 288 @Override 289 protected void onSaveInstanceState(Bundle outState) { 290 super.onSaveInstanceState(outState); 291 outState.putInt(KEY_SELECTED_TAB, mActionBar.getSelectedNavigationIndex()); 292 } 293 294 @Override 295 public boolean onCreateOptionsMenu(Menu menu) { 296 // We only want to show it as a menu in landscape, and only for clock/alarm fragment. 297 mMenu = menu; 298 if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { 299 if (mActionBar.getSelectedNavigationIndex() == ALARM_TAB_INDEX || 300 mActionBar.getSelectedNavigationIndex() == CLOCK_TAB_INDEX) { 301 // Clear the menu so that it doesn't get duplicate items in case onCreateOptionsMenu 302 // was called multiple times. 303 menu.clear(); 304 getMenuInflater().inflate(R.menu.desk_clock_menu, menu); 305 } 306 // Always return true for landscape, regardless of whether we've inflated the menu, so 307 // that when we switch tabs this method will get called and we can inflate the menu. 308 return true; 309 } 310 return false; 311 } 312 313 @Override 314 public boolean onPrepareOptionsMenu(Menu menu) { 315 updateMenu(menu); 316 return true; 317 } 318 319 private void updateMenu(Menu menu) { 320 // Hide "help" if we don't have a URI for it. 321 MenuItem help = menu.findItem(R.id.menu_item_help); 322 if (help != null) { 323 Utils.prepareHelpMenuItem(this, help); 324 } 325 326 // Hide "lights out" for timer. 327 MenuItem nightMode = menu.findItem(R.id.menu_item_night_mode); 328 if (mActionBar.getSelectedNavigationIndex() == ALARM_TAB_INDEX) { 329 nightMode.setVisible(false); 330 } else if (mActionBar.getSelectedNavigationIndex() == CLOCK_TAB_INDEX) { 331 nightMode.setVisible(true); 332 } 333 } 334 335 @Override 336 public boolean onOptionsItemSelected(MenuItem item) { 337 if (processMenuClick(item)) { 338 return true; 339 } 340 341 return super.onOptionsItemSelected(item); 342 } 343 344 private boolean processMenuClick(MenuItem item) { 345 switch (item.getItemId()) { 346 case R.id.menu_item_settings: 347 startActivity(new Intent(DeskClock.this, SettingsActivity.class)); 348 return true; 349 case R.id.menu_item_help: 350 Intent i = item.getIntent(); 351 if (i != null) { 352 try { 353 startActivity(i); 354 } catch (ActivityNotFoundException e) { 355 // No activity found to match the intent - ignore 356 } 357 } 358 return true; 359 case R.id.menu_item_night_mode: 360 startActivity(new Intent(DeskClock.this, ScreensaverActivity.class)); 361 default: 362 break; 363 } 364 return true; 365 } 366 367 /** 368 * Insert the local time zone as the Home Time Zone if one is not set 369 */ 370 private void setHomeTimeZone() { 371 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); 372 String homeTimeZone = prefs.getString(SettingsActivity.KEY_HOME_TZ, ""); 373 if (!homeTimeZone.isEmpty()) { 374 return; 375 } 376 homeTimeZone = TimeZone.getDefault().getID(); 377 SharedPreferences.Editor editor = prefs.edit(); 378 editor.putString(SettingsActivity.KEY_HOME_TZ, homeTimeZone); 379 editor.apply(); 380 Log.v(LOG_TAG, "Setting home time zone to " + homeTimeZone); 381 } 382 383 public void registerPageChangedListener(DeskClockFragment frag) { 384 if (mTabsAdapter != null) { 385 mTabsAdapter.registerPageChangedListener(frag); 386 } 387 } 388 389 public void unregisterPageChangedListener(DeskClockFragment frag) { 390 if (mTabsAdapter != null) { 391 mTabsAdapter.unregisterPageChangedListener(frag); 392 } 393 } 394 395 396 /** 397 * Adapter for wrapping together the ActionBar's tab with the ViewPager 398 */ 399 400 private class TabsAdapter extends FragmentPagerAdapter 401 implements ActionBar.TabListener, ViewPager.OnPageChangeListener { 402 403 private static final String KEY_TAB_POSITION = "tab_position"; 404 405 final class TabInfo { 406 private final Class<?> clss; 407 private final Bundle args; 408 409 TabInfo(Class<?> _class, int position) { 410 clss = _class; 411 args = new Bundle(); 412 args.putInt(KEY_TAB_POSITION, position); 413 } 414 415 public int getPosition() { 416 return args.getInt(KEY_TAB_POSITION, 0); 417 } 418 } 419 420 private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>(); 421 ActionBar mMainActionBar; 422 Context mContext; 423 ViewPager mPager; 424 // Used for doing callbacks to fragments. 425 HashSet<String> mFragmentTags = new HashSet<String>(); 426 427 public TabsAdapter(Activity activity, ViewPager pager) { 428 super(activity.getFragmentManager()); 429 mContext = activity; 430 mMainActionBar = activity.getActionBar(); 431 mPager = pager; 432 mPager.setAdapter(this); 433 mPager.setOnPageChangeListener(this); 434 } 435 436 @Override 437 public Fragment getItem(int position) { 438 // Because this public method is called outside many times, 439 // check if it exits first before creating a new one. 440 final String name = makeFragmentName(R.id.desk_clock_pager, position); 441 Fragment fragment = getFragmentManager().findFragmentByTag(name); 442 if (fragment == null) { 443 TabInfo info = mTabs.get(getRtlPosition(position)); 444 fragment = Fragment.instantiate(mContext, info.clss.getName(), info.args); 445 } 446 447 return fragment; 448 449 } 450 451 /** 452 * Copied from: 453 * android/frameworks/support/v13/java/android/support/v13/app/FragmentPagerAdapter.java#94 454 * Create unique name for the fragment so fragment manager knows it exist. 455 */ 456 private String makeFragmentName(int viewId, int index) { 457 return "android:switcher:" + viewId + ":" + index; 458 } 459 460 @Override 461 public int getCount() { 462 return mTabs.size(); 463 } 464 465 public void addTab(ActionBar.Tab tab, Class<?> clss, int position) { 466 TabInfo info = new TabInfo(clss, position); 467 tab.setTag(info); 468 tab.setTabListener(this); 469 mTabs.add(info); 470 mMainActionBar.addTab(tab); 471 notifyDataSetChanged(); 472 } 473 474 @Override 475 public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 476 // Do nothing 477 } 478 479 @Override 480 public void onPageSelected(int position) { 481 // Set the page before doing the menu so that onCreateOptionsMenu knows what page it is. 482 mMainActionBar.setSelectedNavigationItem(getRtlPosition(position)); 483 notifyPageChanged(position); 484 485 // Only show the overflow menu for alarm and world clock. 486 if (mMenu != null) { 487 // Make sure the menu's been initialized. 488 if (position == ALARM_TAB_INDEX || position == CLOCK_TAB_INDEX) { 489 mMenu.setGroupVisible(R.id.menu_items, true); 490 onCreateOptionsMenu(mMenu); 491 } else { 492 mMenu.setGroupVisible(R.id.menu_items, false); 493 } 494 } 495 } 496 497 @Override 498 public void onPageScrollStateChanged(int state) { 499 // Do nothing 500 } 501 502 @Override 503 public void onTabReselected(Tab arg0, FragmentTransaction arg1) { 504 // Do nothing 505 } 506 507 @Override 508 public void onTabSelected(Tab tab, FragmentTransaction ft) { 509 TabInfo info = (TabInfo) tab.getTag(); 510 int position = info.getPosition(); 511 mSelectedTab = position; 512 if (mIsFirstLaunch && position == CLOCK_TAB_INDEX) { 513 mFab.setVisibility(View.VISIBLE); 514 mFab.setImageResource(R.drawable.ic_globe); 515 mIsFirstLaunch = false; 516 } else { 517 DeskClockFragment f = (DeskClockFragment) getItem(getRtlPosition(position)); 518 f.setFabAppearance(mFab); 519 } 520 mPager.setCurrentItem(getRtlPosition(position)); 521 } 522 523 @Override 524 public void onTabUnselected(Tab arg0, FragmentTransaction arg1) { 525 // Do nothing 526 } 527 528 public void notifySelectedPage(int page) { 529 notifyPageChanged(page); 530 } 531 532 private void notifyPageChanged(int newPage) { 533 for (String tag : mFragmentTags) { 534 final FragmentManager fm = getFragmentManager(); 535 DeskClockFragment f = (DeskClockFragment) fm.findFragmentByTag(tag); 536 if (f != null) { 537 f.onPageChanged(newPage); 538 } 539 } 540 } 541 542 public void registerPageChangedListener(DeskClockFragment frag) { 543 String tag = frag.getTag(); 544 if (mFragmentTags.contains(tag)) { 545 Log.wtf(LOG_TAG, "Trying to add an existing fragment " + tag); 546 } else { 547 mFragmentTags.add(frag.getTag()); 548 } 549 // Since registering a listener by the fragment is done sometimes after the page 550 // was already changed, make sure the fragment gets the current page 551 frag.onPageChanged(mMainActionBar.getSelectedNavigationIndex()); 552 } 553 554 public void unregisterPageChangedListener(DeskClockFragment frag) { 555 mFragmentTags.remove(frag.getTag()); 556 } 557 558 } 559 560 public static abstract class OnTapListener implements OnTouchListener { 561 private float mLastTouchX; 562 private float mLastTouchY; 563 private long mLastTouchTime; 564 private final TextView mMakePressedTextView; 565 private final int mPressedColor, mGrayColor; 566 private final float MAX_MOVEMENT_ALLOWED = 20; 567 private final long MAX_TIME_ALLOWED = 500; 568 569 public OnTapListener(Activity activity, TextView makePressedView) { 570 mMakePressedTextView = makePressedView; 571 mPressedColor = activity.getResources().getColor(Utils.getPressedColorId()); 572 mGrayColor = activity.getResources().getColor(Utils.getGrayColorId()); 573 } 574 575 @Override 576 public boolean onTouch(View v, MotionEvent e) { 577 switch (e.getAction()) { 578 case (MotionEvent.ACTION_DOWN): 579 mLastTouchTime = Utils.getTimeNow(); 580 mLastTouchX = e.getX(); 581 mLastTouchY = e.getY(); 582 if (mMakePressedTextView != null) { 583 mMakePressedTextView.setTextColor(mPressedColor); 584 } 585 break; 586 case (MotionEvent.ACTION_UP): 587 float xDiff = Math.abs(e.getX() - mLastTouchX); 588 float yDiff = Math.abs(e.getY() - mLastTouchY); 589 long timeDiff = (Utils.getTimeNow() - mLastTouchTime); 590 if (xDiff < MAX_MOVEMENT_ALLOWED && yDiff < MAX_MOVEMENT_ALLOWED 591 && timeDiff < MAX_TIME_ALLOWED) { 592 if (mMakePressedTextView != null) { 593 v = mMakePressedTextView; 594 } 595 processClick(v); 596 resetValues(); 597 return true; 598 } 599 resetValues(); 600 break; 601 case (MotionEvent.ACTION_MOVE): 602 xDiff = Math.abs(e.getX() - mLastTouchX); 603 yDiff = Math.abs(e.getY() - mLastTouchY); 604 if (xDiff >= MAX_MOVEMENT_ALLOWED || yDiff >= MAX_MOVEMENT_ALLOWED) { 605 resetValues(); 606 } 607 break; 608 default: 609 resetValues(); 610 } 611 return false; 612 } 613 614 private void resetValues() { 615 mLastTouchX = -1 * MAX_MOVEMENT_ALLOWED + 1; 616 mLastTouchY = -1 * MAX_MOVEMENT_ALLOWED + 1; 617 mLastTouchTime = -1 * MAX_TIME_ALLOWED + 1; 618 if (mMakePressedTextView != null) { 619 mMakePressedTextView.setTextColor(mGrayColor); 620 } 621 } 622 623 protected abstract void processClick(View v); 624 } 625 626 /** 627 * Called by the LabelDialogFormat class after the dialog is finished. * 628 */ 629 @Override 630 public void onDialogLabelSet(TimerObj timer, String label, String tag) { 631 Fragment frag = getFragmentManager().findFragmentByTag(tag); 632 if (frag instanceof TimerFragment) { 633 ((TimerFragment) frag).setLabel(timer, label); 634 } 635 } 636 637 /** 638 * Called by the LabelDialogFormat class after the dialog is finished. * 639 */ 640 @Override 641 public void onDialogLabelSet(Alarm alarm, String label, String tag) { 642 Fragment frag = getFragmentManager().findFragmentByTag(tag); 643 if (frag instanceof AlarmClockFragment) { 644 ((AlarmClockFragment) frag).setLabel(alarm, label); 645 } 646 } 647 648 public int getSelectedTab() { 649 return mSelectedTab; 650 } 651 652 private boolean isRtl() { 653 return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == 654 View.LAYOUT_DIRECTION_RTL; 655 } 656 657 private int getRtlPosition(int position) { 658 if (isRtl()) { 659 switch (position) { 660 case TIMER_TAB_INDEX: 661 return RTL_TIMER_TAB_INDEX; 662 case CLOCK_TAB_INDEX: 663 return RTL_CLOCK_TAB_INDEX; 664 case STOPWATCH_TAB_INDEX: 665 return RTL_STOPWATCH_TAB_INDEX; 666 case ALARM_TAB_INDEX: 667 return RTL_ALARM_TAB_INDEX; 668 default: 669 break; 670 } 671 } 672 return position; 673 } 674} 675