1/* 2 * Copyright (C) 2017 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 android.support.v17.leanback.widget.picker; 18 19import static org.hamcrest.CoreMatchers.is; 20import static org.hamcrest.MatcherAssert.assertThat; 21import static org.junit.Assert.assertEquals; 22 23import android.content.Context; 24import android.content.Intent; 25import android.support.test.InstrumentationRegistry; 26import android.support.test.filters.LargeTest; 27import android.support.test.rule.ActivityTestRule; 28import android.support.test.runner.AndroidJUnit4; 29import android.support.v17.leanback.test.R; 30import android.view.KeyEvent; 31import android.view.View; 32import android.view.ViewGroup; 33 34import org.junit.Before; 35import org.junit.Rule; 36import org.junit.Test; 37import org.junit.runner.RunWith; 38 39import java.util.Arrays; 40import java.util.List; 41 42@LargeTest 43@RunWith(AndroidJUnit4.class) 44public class TimePickerTest { 45 46 private static final String TAG = "TimePickerTest"; 47 private static final long TRANSITION_LENGTH = 1000; 48 private static final long UPDATE_LENGTH = 1000; 49 50 View mViewAbove; 51 TimePicker mTimePicker12HourView; 52 TimePicker mTimePicker24HourView; 53 View mViewBelow; 54 55 @Rule 56 public ActivityTestRule<TimePickerActivity> mActivityTestRule = 57 new ActivityTestRule<>(TimePickerActivity.class, false, false); 58 private TimePickerActivity mActivity; 59 private Context mContext; 60 61 @Before 62 public void setUp() throws Exception { 63 mContext = InstrumentationRegistry.getTargetContext(); 64 } 65 66 public void initActivity(Intent intent) throws Throwable { 67 mActivity = mActivityTestRule.launchActivity(intent); 68 mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 69 mTimePicker12HourView = (TimePicker) mActivity.findViewById(R.id.time_picker12); 70 mTimePicker12HourView.setActivatedVisibleItemCount(3); 71 mTimePicker12HourView.setOnClickListener(new View.OnClickListener() { 72 @Override 73 public void onClick(View v) { 74 mTimePicker12HourView.setActivated(!mTimePicker12HourView.isActivated()); 75 } 76 }); 77 78 if (intent.getIntExtra(TimePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 79 R.layout.timepicker_with_other_widgets) == R.layout.timepicker_with_other_widgets) { 80 mViewAbove = mActivity.findViewById(R.id.above_picker); 81 mViewBelow = mActivity.findViewById(R.id.below_picker); 82 mTimePicker24HourView = (TimePicker) mActivity.findViewById(R.id.time_picker24); 83 mTimePicker24HourView.setActivatedVisibleItemCount(3); 84 mTimePicker24HourView.setOnClickListener(new View.OnClickListener() { 85 @Override 86 public void onClick(View v) { 87 mTimePicker24HourView.setActivated(!mTimePicker24HourView.isActivated()); 88 } 89 }); 90 } else if (intent.getIntExtra(TimePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 91 R.layout.timepicker_with_other_widgets) == R.layout.timepicker_alone) { 92 // A layout with only a TimePicker widget that is initially activated. 93 mActivityTestRule.runOnUiThread(new Runnable() { 94 @Override 95 public void run() { 96 mTimePicker12HourView.setActivated(true); 97 } 98 }); 99 Thread.sleep(500); 100 } 101 } 102 103 @Test 104 public void testSetHourIn24hFormat() throws Throwable { 105 Intent intent = new Intent(); 106 intent.putExtra(TimePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 107 R.layout.timepicker_with_other_widgets); 108 initActivity(intent); 109 110 mActivityTestRule.runOnUiThread(new Runnable() { 111 @Override 112 public void run() { 113 mTimePicker24HourView.setHour(0); 114 } 115 }); 116 Thread.sleep(UPDATE_LENGTH); 117 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 118 mTimePicker24HourView.getHour(), is(0)); 119 120 mActivityTestRule.runOnUiThread(new Runnable() { 121 @Override 122 public void run() { 123 mTimePicker24HourView.setHour(11); 124 } 125 }); 126 Thread.sleep(UPDATE_LENGTH); 127 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 128 mTimePicker24HourView.getHour(), is(11)); 129 130 mActivityTestRule.runOnUiThread(new Runnable() { 131 @Override 132 public void run() { 133 mTimePicker24HourView.setHour(12); 134 } 135 }); 136 Thread.sleep(UPDATE_LENGTH); 137 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 138 mTimePicker24HourView.getHour(), is(12)); 139 140 mActivityTestRule.runOnUiThread(new Runnable() { 141 @Override 142 public void run() { 143 mTimePicker24HourView.setHour(13); 144 } 145 }); 146 Thread.sleep(UPDATE_LENGTH); 147 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 148 mTimePicker24HourView.getHour(), is(13)); 149 150 mActivityTestRule.runOnUiThread(new Runnable() { 151 @Override 152 public void run() { 153 mTimePicker24HourView.setHour(23); 154 } 155 }); 156 Thread.sleep(UPDATE_LENGTH); 157 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 158 mTimePicker24HourView.getHour(), is(23)); 159 } 160 161 @Test 162 public void testSetHourIn12hFormat() throws Throwable { 163 Intent intent = new Intent(); 164 intent.putExtra(TimePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 165 R.layout.timepicker_with_other_widgets); 166 initActivity(intent); 167 mActivityTestRule.runOnUiThread(new Runnable() { 168 @Override 169 public void run() { 170 mTimePicker12HourView.setHour(0); 171 } 172 }); 173 Thread.sleep(UPDATE_LENGTH); 174 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 175 mTimePicker12HourView.getHour(), is(0)); 176 177 mActivityTestRule.runOnUiThread(new Runnable() { 178 @Override 179 public void run() { 180 mTimePicker12HourView.setHour(11); 181 } 182 }); 183 Thread.sleep(UPDATE_LENGTH); 184 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 185 mTimePicker12HourView.getHour(), is(11)); 186 187 mActivityTestRule.runOnUiThread(new Runnable() { 188 @Override 189 public void run() { 190 mTimePicker12HourView.setHour(12); 191 } 192 }); 193 Thread.sleep(UPDATE_LENGTH); 194 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 195 mTimePicker12HourView.getHour(), is(12)); 196 197 mActivityTestRule.runOnUiThread(new Runnable() { 198 @Override 199 public void run() { 200 mTimePicker12HourView.setHour(13); 201 } 202 }); 203 Thread.sleep(UPDATE_LENGTH); 204 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 205 mTimePicker12HourView.getHour(), is(13)); 206 207 mActivityTestRule.runOnUiThread(new Runnable() { 208 @Override 209 public void run() { 210 mTimePicker12HourView.setHour(23); 211 } 212 }); 213 Thread.sleep(UPDATE_LENGTH); 214 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 215 mTimePicker12HourView.getHour(), is(23)); 216 } 217 218 @Test 219 public void testSetMinuteIn24hFormat() throws Throwable { 220 Intent intent = new Intent(); 221 intent.putExtra(TimePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 222 R.layout.timepicker_with_other_widgets); 223 initActivity(intent); 224 mActivityTestRule.runOnUiThread(new Runnable() { 225 @Override 226 public void run() { 227 mTimePicker24HourView.setMinute(0); 228 } 229 }); 230 Thread.sleep(UPDATE_LENGTH); 231 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 232 mTimePicker24HourView.getMinute(), is(0)); 233 234 mActivityTestRule.runOnUiThread(new Runnable() { 235 @Override 236 public void run() { 237 mTimePicker24HourView.setMinute(11); 238 } 239 }); 240 Thread.sleep(UPDATE_LENGTH); 241 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 242 mTimePicker24HourView.getMinute(), is(11)); 243 244 mActivityTestRule.runOnUiThread(new Runnable() { 245 @Override 246 public void run() { 247 mTimePicker24HourView.setMinute(59); 248 } 249 }); 250 Thread.sleep(UPDATE_LENGTH); 251 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 252 mTimePicker24HourView.getMinute(), is(59)); 253 } 254 255 @Test 256 public void testSetMinuteIn12hFormat() throws Throwable { 257 Intent intent = new Intent(); 258 intent.putExtra(TimePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 259 R.layout.timepicker_with_other_widgets); 260 initActivity(intent); 261 mActivityTestRule.runOnUiThread(new Runnable() { 262 @Override 263 public void run() { 264 mTimePicker12HourView.setMinute(0); 265 } 266 }); 267 Thread.sleep(UPDATE_LENGTH); 268 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 269 mTimePicker12HourView.getMinute(), is(0)); 270 271 mActivityTestRule.runOnUiThread(new Runnable() { 272 @Override 273 public void run() { 274 mTimePicker12HourView.setMinute(11); 275 } 276 }); 277 Thread.sleep(UPDATE_LENGTH); 278 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 279 mTimePicker12HourView.getMinute(), is(11)); 280 281 mActivityTestRule.runOnUiThread(new Runnable() { 282 @Override 283 public void run() { 284 mTimePicker12HourView.setMinute(59); 285 } 286 }); 287 Thread.sleep(UPDATE_LENGTH); 288 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 289 mTimePicker12HourView.getMinute(), is(59)); 290 291 } 292 293 @Test 294 public void testAmToPmTransition() throws Throwable { 295 Intent intent = new Intent(); 296 intent.putExtra(TimePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 297 R.layout.timepicker_with_other_widgets); 298 initActivity(intent); 299 mActivityTestRule.runOnUiThread(new Runnable() { 300 @Override 301 public void run() { 302 mTimePicker12HourView.setHour(0); 303 mTimePicker12HourView.setMinute(47); 304 } 305 }); 306 Thread.sleep(UPDATE_LENGTH); 307 assertThat("TimePicker in 12-hour mode returns a different hour in getHour()", 308 mTimePicker12HourView.getHour(), is(0)); 309 assertThat("TimePicker in 12-hour mode returns a different hour in getMinute()", 310 mTimePicker12HourView.getMinute(), is(47)); 311 312 // traverse to the AM/PM column of 12 hour TimePicker widget 313 sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); 314 Thread.sleep(TRANSITION_LENGTH); 315 // Click once to activate 316 sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); 317 Thread.sleep(TRANSITION_LENGTH); 318 sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); 319 Thread.sleep(TRANSITION_LENGTH); 320 sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); 321 Thread.sleep(TRANSITION_LENGTH); 322 // scroll down to PM value 323 sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); 324 Thread.sleep(TRANSITION_LENGTH); 325 // Click now to deactivate 326 sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); 327 Thread.sleep(TRANSITION_LENGTH); 328 329 assertThat("TimePicker in 24-hour mode returns a different hour in getHour() returns", 330 mTimePicker12HourView.getHour(), is(12)); 331 assertThat("TimePicker in 24-hour mode returns a different hour in getHour() returns", 332 mTimePicker12HourView.getMinute(), is(47)); 333 } 334 335 @Test 336 public void testPmToAmTransition() throws Throwable { 337 Intent intent = new Intent(); 338 intent.putExtra(TimePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 339 R.layout.timepicker_with_other_widgets); 340 initActivity(intent); 341 mActivityTestRule.runOnUiThread(new Runnable() { 342 @Override 343 public void run() { 344 mTimePicker12HourView.setHour(12); 345 mTimePicker12HourView.setMinute(47); 346 } 347 }); 348 Thread.sleep(UPDATE_LENGTH); 349 assertThat("TimePicker in 12-hour mode returns a different hour in getHour()", 350 mTimePicker12HourView.getHour(), is(12)); 351 assertThat("TimePicker in 12-hour mode returns a different hour in getMinute()", 352 mTimePicker12HourView.getMinute(), is(47)); 353 354 // traverse to the AM/PM column of 12 hour TimePicker widget 355 sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); 356 Thread.sleep(TRANSITION_LENGTH); 357 // Click once to activate 358 sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); 359 Thread.sleep(TRANSITION_LENGTH); 360 sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); 361 Thread.sleep(TRANSITION_LENGTH); 362 sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); 363 Thread.sleep(TRANSITION_LENGTH); 364 // scroll down to PM value 365 sendKeys(KeyEvent.KEYCODE_DPAD_UP); 366 Thread.sleep(TRANSITION_LENGTH); 367 // Click now to deactivate 368 sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); 369 Thread.sleep(TRANSITION_LENGTH); 370 371 assertThat("TimePicker in 24-hour mode returns a different hour in getHour() returns", 372 mTimePicker12HourView.getHour(), is(0)); 373 assertThat("TimePicker in 24-hour mode returns a different hour in getHour() returns", 374 mTimePicker12HourView.getMinute(), is(47)); 375 } 376 377 @Test 378 public void test12To24HourFormatTransition() throws Throwable { 379 Intent intent = new Intent(); 380 intent.putExtra(TimePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 381 R.layout.timepicker_with_other_widgets); 382 initActivity(intent); 383 mActivityTestRule.runOnUiThread(new Runnable() { 384 @Override 385 public void run() { 386 mTimePicker12HourView.setHour(14); 387 mTimePicker12HourView.setMinute(47); 388 } 389 }); 390 Thread.sleep(UPDATE_LENGTH); 391 assertThat("TimePicker should be in 12-hour format.", mTimePicker12HourView.is24Hour(), 392 is(false)); 393 mActivityTestRule.runOnUiThread(new Runnable() { 394 @Override 395 public void run() { 396 mTimePicker12HourView.setIs24Hour(true); 397 } 398 }); 399 Thread.sleep(UPDATE_LENGTH); 400 assertThat("TimePicker should now be in 24-hour format.", mTimePicker12HourView.is24Hour(), 401 is(true)); 402 // The hour and minute should not be changed. 403 assertThat("TimePicker in 24-hour mode returns a different hour in getHour() returns", 404 mTimePicker12HourView.getHour(), is(14)); 405 assertThat("TimePicker in 24-hour mode returns a different hour in getHour() returns", 406 mTimePicker12HourView.getMinute(), is(47)); 407 } 408 409 @Test 410 public void test24To12HourFormatTransition() throws Throwable { 411 Intent intent = new Intent(); 412 intent.putExtra(TimePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 413 R.layout.timepicker_with_other_widgets); 414 initActivity(intent); 415 mActivityTestRule.runOnUiThread(new Runnable() { 416 @Override 417 public void run() { 418 mTimePicker24HourView.setHour(14); 419 mTimePicker24HourView.setMinute(47); 420 } 421 }); 422 Thread.sleep(UPDATE_LENGTH); 423 assertThat("TimePicker should be in 12-hour format.", mTimePicker24HourView.is24Hour(), 424 is(true)); 425 mActivityTestRule.runOnUiThread(new Runnable() { 426 @Override 427 public void run() { 428 mTimePicker24HourView.setIs24Hour(false); 429 } 430 }); 431 Thread.sleep(UPDATE_LENGTH); 432 assertThat("TimePicker should now be in 24-hour format.", mTimePicker24HourView.is24Hour(), 433 is(false)); 434 // The hour and minute should not be changed. 435 assertThat("TimePicker in 24-hour mode returns a different hour in getHour() returns", 436 mTimePicker24HourView.getHour(), is(14)); 437 assertThat("TimePicker in 24-hour mode returns a different hour in getHour() returns", 438 mTimePicker24HourView.getMinute(), is(47)); 439 } 440 441 @Test 442 public void testInitiallyActiveTimePicker() 443 throws Throwable { 444 Intent intent = new Intent(); 445 intent.putExtra(TimePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 446 R.layout.timepicker_alone); 447 initActivity(intent); 448 449 mActivityTestRule.runOnUiThread(new Runnable() { 450 @Override 451 public void run() { 452 mTimePicker12HourView.setHour(14); 453 mTimePicker12HourView.setMinute(47); 454 } 455 }); 456 Thread.sleep(UPDATE_LENGTH); 457 458 ViewGroup mTimePickerInnerView = (ViewGroup) mTimePicker12HourView.findViewById( 459 R.id.picker); 460 461 assertThat("The first column of TimePicker should initially hold focus", 462 mTimePickerInnerView.getChildAt(0).hasFocus(), is(true)); 463 464 // focus on first column 465 sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); 466 Thread.sleep(TRANSITION_LENGTH); 467 assertThat("The first column of TimePicker should still hold focus after scrolling down", 468 mTimePickerInnerView.getChildAt(0).hasFocus(), is(true)); 469 470 // focus on second column 471 sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); 472 Thread.sleep(TRANSITION_LENGTH); 473 assertThat("The second column of TimePicker should hold focus after scrolling right", 474 mTimePickerInnerView.getChildAt(2).hasFocus(), is(true)); 475 476 sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); 477 Thread.sleep(TRANSITION_LENGTH); 478 assertThat("The second column of TimePicker should still hold focus after scrolling down", 479 mTimePickerInnerView.getChildAt(2).hasFocus(), is(true)); 480 481 // focus on third column 482 sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); 483 Thread.sleep(TRANSITION_LENGTH); 484 assertThat("The third column of TimePicker should hold focus after scrolling right", 485 mTimePickerInnerView.getChildAt(3).hasFocus(), is(true)); 486 487 sendKeys(KeyEvent.KEYCODE_DPAD_UP); 488 Thread.sleep(TRANSITION_LENGTH); 489 assertThat("The third column of TimePicker should still hold focus after scrolling down", 490 mTimePickerInnerView.getChildAt(3).hasFocus(), is(true)); 491 } 492 493 @Test 494 public void testExtractSeparatorsForDifferentLocales() throws Throwable { 495 // A typical time pattern for different locales in 12-hour format 496 TimePicker timePicker = new TimePicker(mContext, null) { 497 @Override 498 String getBestHourMinutePattern() { 499 return "h:mm a"; 500 } 501 }; 502 List<CharSequence> actualSeparators = timePicker.extractSeparators(); 503 List<String> expectedSeparators = Arrays.asList(new String[] {"", ":", "", ""}); 504 assertEquals(expectedSeparators, actualSeparators); 505 506 // time pattern for ja_JP in 12 hour format 507 timePicker = new TimePicker(mContext, null) { 508 @Override 509 String getBestHourMinutePattern() { 510 return "aK:mm"; 511 } 512 513 @Override 514 public boolean is24Hour() { 515 return false; 516 } 517 }; 518 actualSeparators = timePicker.extractSeparators(); 519 expectedSeparators = Arrays.asList(new String[] {"", "", ":", ""}); 520 assertEquals(expectedSeparators, actualSeparators); 521 522 // time pattern for fr_CA in 24 hour format 523 timePicker = new TimePicker(mContext, null) { 524 @Override 525 String getBestHourMinutePattern() { 526 return "HH 'h' mm"; 527 } 528 529 @Override 530 public boolean is24Hour() { 531 return true; 532 } 533 }; 534 actualSeparators = timePicker.extractSeparators(); 535 expectedSeparators = Arrays.asList(new String[] {"", "h", ""}); 536 assertEquals(expectedSeparators, actualSeparators); 537 538 // time pattern for hsb_DE in 24 hour format 539 timePicker = new TimePicker(mContext, null) { 540 @Override 541 String getBestHourMinutePattern() { 542 return "H:mm 'hodz'"; 543 } 544 545 @Override 546 public boolean is24Hour() { 547 return true; 548 } 549 }; 550 actualSeparators = timePicker.extractSeparators(); 551 expectedSeparators = Arrays.asList(new String[] {"", ":", "hodz"}); 552 assertEquals(expectedSeparators, actualSeparators); 553 554 // time pattern for ko_KR in 12 hour format 555 timePicker = new TimePicker(mContext, null) { 556 @Override 557 String getBestHourMinutePattern() { 558 return "a h:mm"; 559 } 560 561 @Override 562 public boolean is24Hour() { 563 return false; 564 } 565 }; 566 actualSeparators = timePicker.extractSeparators(); 567 expectedSeparators = Arrays.asList(new String[] {"", "", ":", ""}); 568 assertEquals(expectedSeparators, actualSeparators); 569 570 // time pattern for fa_IR in 24 hour format 571 timePicker = new TimePicker(mContext, null) { 572 @Override 573 String getBestHourMinutePattern() { 574 return "H:mm"; 575 } 576 577 @Override 578 public boolean is24Hour() { 579 return true; 580 } 581 }; 582 actualSeparators = timePicker.extractSeparators(); 583 expectedSeparators = Arrays.asList(new String[] {"", ":", ""}); 584 assertEquals(expectedSeparators, actualSeparators); 585 } 586 587 private void sendKeys(int ...keys) { 588 for (int i = 0; i < keys.length; i++) { 589 InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(keys[i]); 590 } 591 } 592} 593