1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 15package androidx.leanback.app.wizard; 16 17import static org.junit.Assert.assertTrue; 18 19import android.content.Context; 20import android.content.Intent; 21import android.content.res.Resources; 22import android.support.test.InstrumentationRegistry; 23import android.support.test.filters.LargeTest; 24import android.support.test.rule.ActivityTestRule; 25import android.support.test.runner.AndroidJUnit4; 26import android.view.KeyEvent; 27 28import androidx.leanback.app.GuidedStepFragment; 29import androidx.leanback.test.R; 30import androidx.leanback.widget.GuidanceStylist; 31import androidx.leanback.widget.GuidedAction; 32 33import org.junit.Before; 34import org.junit.Rule; 35import org.junit.Test; 36import org.junit.runner.RunWith; 37 38import java.util.ArrayList; 39import java.util.Arrays; 40import java.util.Collections; 41import java.util.List; 42 43@LargeTest 44@RunWith(AndroidJUnit4.class) 45public class GuidedStepAttributesTest { 46 static final long TRANSITION_LENGTH = 1000; 47 48 static final String TAG = "GuidedStepAttributesTest"; 49 50 @Rule 51 public ActivityTestRule<GuidedStepAttributesTestActivity> activityTestRule = 52 new ActivityTestRule<>(GuidedStepAttributesTestActivity.class, false, false); 53 54 GuidedStepAttributesTestActivity mActivity; 55 56 private void initActivity(Intent intent) { 57 mActivity = activityTestRule.launchActivity(intent); 58 try { 59 Thread.sleep(2000); 60 } catch(InterruptedException e) { 61 e.printStackTrace(); 62 } 63 } 64 65 Context mContext; 66 @Before 67 public void setUp() { 68 mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();; 69 } 70 71 public static void sendKey(int keyCode) { 72 InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(keyCode); 73 } 74 75 @Test 76 public void testFocusDisabledOnActions() throws Throwable { 77 78 Intent intent = new Intent(); 79 Resources res = mContext.getResources(); 80 81 final int NUM_SEARCH_ACTIONS = 10; 82 final List<Integer> ACTIONS_WITH_DISABLED_FOCUS = new ArrayList<>( 83 Arrays.asList(1, 3, 4, 5, 8)); 84 final int ACTION_ID_SEARCH = 1; 85 List<Integer> EXPECTED_ACTIONS_ID_AFTER_EACH_SELECT = new ArrayList<>(); 86 87 // we will traverse actions from top to bottom and then back to the top 88 for(int i = 0; i < NUM_SEARCH_ACTIONS; i++) { 89 if (!ACTIONS_WITH_DISABLED_FOCUS.contains(i)) 90 EXPECTED_ACTIONS_ID_AFTER_EACH_SELECT.add(i); 91 } 92 for(int i = EXPECTED_ACTIONS_ID_AFTER_EACH_SELECT.size(); i-- != 0;) { 93 EXPECTED_ACTIONS_ID_AFTER_EACH_SELECT.add(EXPECTED_ACTIONS_ID_AFTER_EACH_SELECT.get(i)); 94 } 95 96 97 String title = "Guided Actions Focusable Test"; 98 String breadcrumb = "Focusable Test Demo"; 99 String description = ""; 100 GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description, 101 breadcrumb, null); 102 103 List<GuidedAction> actionList = new ArrayList<>(); 104 for (int i = 0; i < NUM_SEARCH_ACTIONS; i++ ) { 105 actionList.add(new GuidedAction.Builder(mContext) 106 .id(ACTION_ID_SEARCH) 107 .title(res.getString(R.string.search) + "" + i) 108 .description(res.getString(R.string.search_description) + i) 109 .build() 110 ); 111 } 112 for(int action_id : ACTIONS_WITH_DISABLED_FOCUS ) 113 actionList.get(action_id).setFocusable(false); 114 115 GuidedStepAttributesTestFragment.GUIDANCE = guidance; 116 GuidedStepAttributesTestFragment.ACTION_LIST = actionList; 117 118 initActivity(intent); 119 120 int lastSelectedActionId = -1; 121 int selectIndex = 0; 122 GuidedStepFragment mFragment = (GuidedStepFragment) mActivity.getGuidedStepTestFragment(); 123 int prevSelectedActionPosition = -1; 124 int nextSelectedActionPosition = mFragment.getSelectedActionPosition(); 125 while ( nextSelectedActionPosition != prevSelectedActionPosition ) { 126 lastSelectedActionId = mFragment.getSelectedActionPosition(); 127 assertTrue(res.getString(R.string.focusable_test_error_message, 128 actionList.get(lastSelectedActionId).getTitle()), 129 lastSelectedActionId == EXPECTED_ACTIONS_ID_AFTER_EACH_SELECT.get(selectIndex)); 130 selectIndex++; 131 sendKey(KeyEvent.KEYCODE_DPAD_DOWN); 132 prevSelectedActionPosition = nextSelectedActionPosition; 133 nextSelectedActionPosition = mFragment.getSelectedActionPosition(); 134 Thread.sleep(TRANSITION_LENGTH); 135 } 136 137 prevSelectedActionPosition = -1; 138 while ( nextSelectedActionPosition != prevSelectedActionPosition ) { 139 lastSelectedActionId = mFragment.getSelectedActionPosition(); 140 assertTrue(res.getString(R.string.focusable_test_error_message, 141 actionList.get(lastSelectedActionId).getTitle()), 142 lastSelectedActionId == EXPECTED_ACTIONS_ID_AFTER_EACH_SELECT.get(selectIndex)); 143 selectIndex++; 144 sendKey(KeyEvent.KEYCODE_DPAD_UP); 145 prevSelectedActionPosition = nextSelectedActionPosition; 146 nextSelectedActionPosition = mFragment.getSelectedActionPosition(); 147 Thread.sleep(TRANSITION_LENGTH); 148 } 149 150 } 151 152 // Note: do not remove final or sRevertCallback gets null from 2nd test on! 153 static final GuidedStepAttributesTestFragment.Callback sRevertCallback = new 154 GuidedStepAttributesTestFragment.Callback() { 155 @Override 156 public void onActionClicked(GuidedStepFragment fragment, long id) { 157 List<GuidedAction> allActions = fragment.getActions(); 158 for(int i = 1; i < allActions.size(); i++) { 159 GuidedAction action = allActions.get(i); 160 action.setEnabled(!action.isEnabled()); 161 fragment.notifyActionChanged(fragment.findActionPositionById(action.getId())); 162 } 163 } 164 }; 165 166 /** 167 * Creates a number of enabled and disable actions and tests whether the flag is correctly set 168 * by clicking on each individual action and checking whether the click event is triggered. 169 * @throws Throwable 170 */ 171 @Test 172 public void testDisabledActions() throws Throwable { 173 174 Intent intent = new Intent(); 175 Resources res = mContext.getResources(); 176 177 final int NUM_SEARCH_ACTIONS = 10; 178 final List<Integer> DISABLED_ACTIONS = new ArrayList<>( 179 Arrays.asList(1, 3, 5, 7)); 180 final int ACTION_ID_REVERT_BUTTON = 0; 181 final int ACTION_ID_SEARCH_BEGIN = ACTION_ID_REVERT_BUTTON + 1; 182 int ACTION_ID_SEARCH_END = ACTION_ID_SEARCH_BEGIN; 183 184 // sequence of clicked actions simulated in the test 185 List<Integer> CLICK_SEQUENCE = new ArrayList<>(); 186 187 // Expected Clicked sequence can be different from focused ones since some of the actions 188 // are disabled hence not clickable 189 List<Integer> EXPECTED_FOCUSED_SEQUENCE = new ArrayList<>(); 190 List<Integer> EXPECTED_CLICKED_SEQUENCE = new ArrayList<>(); 191 // Expected actions state according to list of DISABLED_ACTIONS: false for disabled actions 192 List<Boolean> EXPECTED_ACTIONS_STATE = new ArrayList<>( 193 Arrays.asList(new Boolean[NUM_SEARCH_ACTIONS]) 194 ); 195 Collections.fill(EXPECTED_ACTIONS_STATE, Boolean.TRUE); 196 197 for(int i = 0; i < NUM_SEARCH_ACTIONS; i++) { 198 CLICK_SEQUENCE.add(i + 1); 199 } 200 for(int clickedActionId : CLICK_SEQUENCE) { 201 EXPECTED_FOCUSED_SEQUENCE.add(clickedActionId); 202 if (!DISABLED_ACTIONS.contains(clickedActionId - 1)) 203 EXPECTED_CLICKED_SEQUENCE.add(clickedActionId); 204 else 205 EXPECTED_CLICKED_SEQUENCE.add(-1); 206 } 207 208 String title = "Guided Actions Enabled Test"; 209 String breadcrumb = "Enabled Test Demo"; 210 String description = ""; 211 GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description, 212 breadcrumb, null); 213 214 List<GuidedAction> actionList = new ArrayList<>(); 215 actionList.add(new GuidedAction.Builder(mContext) 216 .id(ACTION_ID_REVERT_BUTTON) 217 .title(res.getString(R.string.invert_title)) 218 .description(res.getString(R.string.revert_description)) 219 .build() 220 ); 221 222 for (int i = 0; i < NUM_SEARCH_ACTIONS; i++ ) { 223 actionList.add(new GuidedAction.Builder(mContext) 224 .id(ACTION_ID_SEARCH_END++) 225 .title(res.getString(R.string.search) + "" + i) 226 .description(res.getString(R.string.search_description) + i) 227 .build() 228 ); 229 } 230 for(int action_id : DISABLED_ACTIONS ) { 231 if ( action_id >= 0 && action_id < NUM_SEARCH_ACTIONS ) { 232 actionList.get(action_id + 1).setEnabled(false); 233 EXPECTED_ACTIONS_STATE.set(action_id, Boolean.FALSE); 234 } 235 } 236 237 GuidedStepAttributesTestFragment.clear(); 238 GuidedStepAttributesTestFragment.GUIDANCE = guidance; 239 GuidedStepAttributesTestFragment.ACTION_LIST = actionList; 240 GuidedStepAttributesTestFragment.setActionClickCallback(ACTION_ID_REVERT_BUTTON, 241 sRevertCallback); 242 243 initActivity(intent); 244 245 examineEnabledAndDisabledActions(actionList, CLICK_SEQUENCE, EXPECTED_FOCUSED_SEQUENCE, 246 EXPECTED_CLICKED_SEQUENCE); 247 } 248 249 /** 250 * Toggles Enabled flags in oll the actions of the prior test, and tests whether they are 251 * correctly reverted. 252 */ 253 @Test 254 public void testToggleEnabledFlags() throws Throwable { 255 256 Intent intent = new Intent(); 257 Resources res = mContext.getResources(); 258 259 final int NUM_SEARCH_ACTIONS = 10; 260 final List<Integer> DISABLED_ACTIONS = new ArrayList<>( 261 Arrays.asList(1, 3, 5, 7)); 262 final int ACTION_ID_REVERT_BUTTON = 0; 263 final int ACTION_ID_SEARCH_BEGIN = ACTION_ID_REVERT_BUTTON + 1; 264 int ACTION_ID_SEARCH_END = ACTION_ID_SEARCH_BEGIN; 265 266 // sequence of clicked actions simulated in the test 267 List<Integer> CLICK_SEQUENCE = new ArrayList<>(); 268 269 // Expected Clicked sequence can be different from focused ones since some of the actions 270 // are disabled hence not clickable 271 List<Integer> EXPECTED_FOCUSED_SEQUENCE = new ArrayList<>(); 272 List<Integer> EXPECTED_CLICKED_SEQUENCE = new ArrayList<>(); 273 // Expected actions state according to list of DISABLED_ACTIONS: false for disabled actions 274 List<Boolean> EXPECTED_ACTIONS_STATE = new ArrayList<>( 275 Arrays.asList(new Boolean[NUM_SEARCH_ACTIONS]) 276 ); 277 Collections.fill(EXPECTED_ACTIONS_STATE, Boolean.FALSE); 278 279 for(int i = 0; i < NUM_SEARCH_ACTIONS; i++) { 280 CLICK_SEQUENCE.add(i + 1); 281 } 282 for(int clickedActionId : CLICK_SEQUENCE) { 283 EXPECTED_FOCUSED_SEQUENCE.add(clickedActionId); 284 if (DISABLED_ACTIONS.contains(clickedActionId - 1)) 285 EXPECTED_CLICKED_SEQUENCE.add(clickedActionId); 286 else 287 EXPECTED_CLICKED_SEQUENCE.add(-1); 288 } 289 290 String title = "Guided Actions Enabled Test"; 291 String breadcrumb = "Toggle Enabled Flag Test Demo"; 292 String description = ""; 293 GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description, 294 breadcrumb, null); 295 296 List<GuidedAction> actionList = new ArrayList<>(); 297 actionList.add(new GuidedAction.Builder(mContext) 298 .id(ACTION_ID_REVERT_BUTTON) 299 .title(res.getString(R.string.invert_title)) 300 .description(res.getString(R.string.revert_description)) 301 .build() 302 ); 303 304 for (int i = 0; i < NUM_SEARCH_ACTIONS; i++ ) { 305 actionList.add(new GuidedAction.Builder(mContext) 306 .id(ACTION_ID_SEARCH_END++) 307 .title(res.getString(R.string.search) + "" + i) 308 .description(res.getString(R.string.search_description) + i) 309 .build() 310 ); 311 } 312 for(int action_id : DISABLED_ACTIONS ) { 313 if ( action_id >= 0 && action_id < NUM_SEARCH_ACTIONS ) { 314 actionList.get(action_id + 1).setEnabled(false); 315 EXPECTED_ACTIONS_STATE.set(action_id, Boolean.TRUE); 316 } 317 } 318 319 GuidedStepAttributesTestFragment.clear(); 320 GuidedStepAttributesTestFragment.GUIDANCE = guidance; 321 GuidedStepAttributesTestFragment.ACTION_LIST = actionList; 322 GuidedStepAttributesTestFragment.setActionClickCallback(ACTION_ID_REVERT_BUTTON, 323 sRevertCallback); 324 325 initActivity(intent); 326 327 final GuidedStepFragment mFragment = (GuidedStepFragment) 328 mActivity.getGuidedStepTestFragment(); 329 330 mActivity.runOnUiThread(new Runnable() { 331 @Override 332 public void run() { 333 mFragment.setSelectedActionPosition(0); 334 } 335 }); 336 Thread.sleep(TRANSITION_LENGTH); 337 sendKey(KeyEvent.KEYCODE_DPAD_CENTER); 338 Thread.sleep(TRANSITION_LENGTH); 339 examineEnabledAndDisabledActions(actionList, CLICK_SEQUENCE, EXPECTED_FOCUSED_SEQUENCE, 340 EXPECTED_CLICKED_SEQUENCE); 341 } 342 343 private void examineEnabledAndDisabledActions( 344 List<GuidedAction> actionList, List<Integer> CLICK_SEQUENCE, 345 List<Integer> EXPECTED_FOCUSED_SEQUENCE, 346 List<Integer> EXPECTED_CLICKED_SEQUENCE) 347 throws Throwable { 348 349 final GuidedStepFragment mFragment = (GuidedStepFragment) 350 mActivity.getGuidedStepTestFragment(); 351 352 for(int i = 0; i < CLICK_SEQUENCE.size(); i++) { 353 GuidedStepAttributesTestFragment.LAST_SELECTED_ACTION_ID = 354 GuidedStepAttributesTestFragment.LAST_CLICKED_ACTION_ID = -1; 355 final int id = CLICK_SEQUENCE.get(i); 356 mActivity.runOnUiThread(new Runnable() { 357 @Override 358 public void run() { 359 mFragment.setSelectedActionPosition(id); 360 } 361 }); 362 Thread.sleep(TRANSITION_LENGTH); 363 364 sendKey(KeyEvent.KEYCODE_DPAD_CENTER); 365 Thread.sleep(TRANSITION_LENGTH); 366 367 assertTrue(mContext.getResources().getString( 368 R.string.enabled_test_wrong_focus_error_message), 369 GuidedStepAttributesTestFragment.LAST_SELECTED_ACTION_ID 370 == EXPECTED_FOCUSED_SEQUENCE.get(i) 371 ); 372 assertTrue(mContext.getResources().getString( 373 R.string.enabled_test_wrong_click_error_message), 374 GuidedStepAttributesTestFragment.LAST_CLICKED_ACTION_ID 375 == EXPECTED_CLICKED_SEQUENCE.get(i) 376 ); 377 assertTrue(mContext.getResources().getString( 378 R.string.enabled_test_wrong_flag_error_message), 379 (GuidedStepAttributesTestFragment.LAST_CLICKED_ACTION_ID == -1) 380 ? !actionList.get(id).isEnabled() 381 : actionList.get(id).isEnabled() 382 ); 383 } 384 } 385 386 @Test 387 public void testCheckedActions() throws Throwable { 388 389 Intent intent = new Intent(); 390 Resources res = mContext.getResources(); 391 392 final int NUM_RADIO_ACTIONS = 3; 393 final int NUM_CHECK_BOX_ACTIONS = 3; 394 final int INITIALLY_CHECKED_RADIO_ACTION = 0; 395 final List<Integer> INITIALLY_CHECKED_CHECKBOX_ACTIONS = new ArrayList<>( 396 Arrays.asList(1, 2) 397 ); 398 399 List<Integer> CLICK_SEQUENCE = new ArrayList<>(); 400 for(int i = 0; i < NUM_RADIO_ACTIONS + NUM_CHECK_BOX_ACTIONS; i++) { 401 CLICK_SEQUENCE.add(i); 402 } 403 404 List<Boolean> EXPECTED_ACTIONS_STATE_AFTER_EACH_CLICK = new ArrayList<>( 405 Arrays.asList(new Boolean[CLICK_SEQUENCE.size()]) 406 ); 407 Collections.fill(EXPECTED_ACTIONS_STATE_AFTER_EACH_CLICK, Boolean.FALSE); 408 409 // initial state of actions before any clicks happen 410 EXPECTED_ACTIONS_STATE_AFTER_EACH_CLICK.set(INITIALLY_CHECKED_RADIO_ACTION, true); 411 for(int checkedCheckBox : INITIALLY_CHECKED_CHECKBOX_ACTIONS) { 412 EXPECTED_ACTIONS_STATE_AFTER_EACH_CLICK.set(NUM_RADIO_ACTIONS + checkedCheckBox, true); 413 } 414 415 String title = "Guided Actions Checked Test"; 416 String breadcrumb = "Checked Test Demo"; 417 String description = ""; 418 GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description, 419 breadcrumb, null); 420 421 List<GuidedAction> actionList = new ArrayList<>(); 422 actionList.add(new GuidedAction.Builder(mContext) 423 .title(res.getString(R.string.radio_actions_info_title)) 424 .description(res.getString(R.string.radio_actions_info_desc)) 425 .infoOnly(true) 426 .enabled(true) 427 .focusable(false) 428 .build() 429 ); 430 431 int firstRadioActionIndex = actionList.size(); 432 for(int i = 0; i < NUM_RADIO_ACTIONS; i++) { 433 actionList.add(new GuidedAction.Builder(mContext) 434 .title(res.getString(R.string.checkbox_title) + i) 435 .description(res.getString(R.string.checkbox_desc) + i) 436 .checkSetId(GuidedAction.DEFAULT_CHECK_SET_ID) 437 .build() 438 ); 439 if (i == INITIALLY_CHECKED_RADIO_ACTION) 440 actionList.get(firstRadioActionIndex + i).setChecked(true); 441 } 442 443 actionList.add(new GuidedAction.Builder(mContext) 444 .title(res.getString(R.string.checkbox_actions_info_title)) 445 .description(res.getString(R.string.checkbox_actions_info_desc)) 446 .infoOnly(true) 447 .enabled(true) 448 .focusable(false) 449 .build() 450 ); 451 int firstCheckBoxActionIndex = actionList.size(); 452 for(int i = 0; i < NUM_CHECK_BOX_ACTIONS; i++) { 453 actionList.add(new GuidedAction.Builder(mContext) 454 .title(res.getString(R.string.checkbox_title) + i) 455 .description(res.getString(R.string.checkbox_desc) + i) 456 .checkSetId(GuidedAction.CHECKBOX_CHECK_SET_ID) 457 .build() 458 ); 459 } 460 for(int i = 0; i < INITIALLY_CHECKED_CHECKBOX_ACTIONS.size(); i++ ) { 461 actionList.get(firstCheckBoxActionIndex + INITIALLY_CHECKED_CHECKBOX_ACTIONS.get(i)) 462 .setChecked(true); 463 } 464 465 GuidedStepAttributesTestFragment.GUIDANCE = guidance; 466 GuidedStepAttributesTestFragment.ACTION_LIST = actionList; 467 initActivity(intent); 468 469 examineCheckedAndUncheckedActions(actionList, EXPECTED_ACTIONS_STATE_AFTER_EACH_CLICK, 470 NUM_RADIO_ACTIONS, NUM_CHECK_BOX_ACTIONS); 471 } 472 473 private void updateExpectedActionsStateAfterClick( 474 List<Boolean> EXPECTED_ACTIONS_STATE_AFTER_EACH_CLICK, int NUM_RADIO_ACTIONS, 475 int NUM_CHECK_BOX_ACTIONS, int clickedActionIndex) { 476 477 if (clickedActionIndex < NUM_RADIO_ACTIONS) { 478 for(int i = 0; i < NUM_RADIO_ACTIONS; i++) 479 EXPECTED_ACTIONS_STATE_AFTER_EACH_CLICK.set(i, false); 480 EXPECTED_ACTIONS_STATE_AFTER_EACH_CLICK.set(clickedActionIndex, true); 481 } 482 else if (clickedActionIndex < NUM_RADIO_ACTIONS + NUM_CHECK_BOX_ACTIONS) { 483 EXPECTED_ACTIONS_STATE_AFTER_EACH_CLICK.set(clickedActionIndex, 484 !EXPECTED_ACTIONS_STATE_AFTER_EACH_CLICK.get(clickedActionIndex)); 485 } 486 } 487 488 private void verifyIfActionsStateIsCorrect(List<GuidedAction> actionList, 489 List<Boolean> EXPECTED_ACTIONS_STATE_AFTER_EACH_CLICK) { 490 491 int actionIndex = 0; 492 for(GuidedAction checkAction : actionList) { 493 if (checkAction.infoOnly()) 494 continue; 495 assertTrue("Action " + actionIndex + " is " + (!checkAction.isChecked() ? "un-" : "") 496 + "checked while it shouldn't be!", checkAction.isChecked() 497 == EXPECTED_ACTIONS_STATE_AFTER_EACH_CLICK.get(actionIndex)); 498 actionIndex++; 499 } 500 } 501 502 private void examineCheckedAndUncheckedActions(List<GuidedAction> actionList, 503 List<Boolean> EXPECTED_ACTIONS_STATE_AFTER_EACH_CLICK, 504 int NUM_RADIO_ACTIONS, 505 int NUM_CHECK_BOX_ACTIONS) throws Throwable { 506 507 final GuidedStepFragment guidedStepCheckedFragment = (GuidedStepFragment) 508 mActivity.getGuidedStepTestFragment(); 509 final int firstRadioActionIndex = 1; 510 final int firstCheckBoxActionIndex = firstRadioActionIndex + NUM_RADIO_ACTIONS + 1; 511 for(int actionId = 0; actionId < NUM_RADIO_ACTIONS; actionId++) { 512 final int id = actionId; 513 mActivity.runOnUiThread(new Runnable() { 514 @Override 515 public void run() { 516 guidedStepCheckedFragment 517 .setSelectedActionPosition(firstRadioActionIndex + id); 518 } 519 }); 520 Thread.sleep(TRANSITION_LENGTH); 521 522 sendKey(KeyEvent.KEYCODE_DPAD_CENTER); 523 Thread.sleep(TRANSITION_LENGTH); 524 updateExpectedActionsStateAfterClick(EXPECTED_ACTIONS_STATE_AFTER_EACH_CLICK, 525 NUM_RADIO_ACTIONS, NUM_CHECK_BOX_ACTIONS, actionId); 526 verifyIfActionsStateIsCorrect(actionList, EXPECTED_ACTIONS_STATE_AFTER_EACH_CLICK); 527 } 528 529 for(int actionId = 0; actionId < NUM_CHECK_BOX_ACTIONS; actionId++) { 530 final int id = actionId; 531 mActivity.runOnUiThread(new Runnable() { 532 @Override 533 public void run() { 534 guidedStepCheckedFragment 535 .setSelectedActionPosition(firstCheckBoxActionIndex + id); 536 } 537 }); 538 Thread.sleep(TRANSITION_LENGTH); 539 540 sendKey(KeyEvent.KEYCODE_DPAD_CENTER); 541 Thread.sleep(TRANSITION_LENGTH); 542 updateExpectedActionsStateAfterClick(EXPECTED_ACTIONS_STATE_AFTER_EACH_CLICK, 543 NUM_RADIO_ACTIONS, NUM_CHECK_BOX_ACTIONS, NUM_RADIO_ACTIONS + actionId); 544 verifyIfActionsStateIsCorrect(actionList, EXPECTED_ACTIONS_STATE_AFTER_EACH_CLICK); 545 } 546 } 547 548 @Test 549 public void testActionWithTwoSubActions() throws Throwable { 550 ExpectedSubActionResult result = setUpActionsForSubActionsTest(); 551 552 final int actionPos = 0; 553 final GuidedAction selectedAction = result.actionList.get(actionPos); 554 List<Integer> expectedFocusedSeq = result.expectedFocusedSeq.get(actionPos); 555 List<Integer> expectedClickedSeq = result.expectedClickedSeq.get(actionPos); 556 557 traverseSubActionsAndVerifyFocusAndClickEvents(selectedAction, actionPos, expectedFocusedSeq, 558 expectedClickedSeq); 559 } 560 561 @Test 562 public void testActionWithOneSubAction() throws Throwable { 563 ExpectedSubActionResult result = setUpActionsForSubActionsTest(); 564 565 final int actionPos = 1; 566 final GuidedAction selectedAction = result.actionList.get(actionPos); 567 List<Integer> expectedFocusedSeq = result.expectedFocusedSeq.get(actionPos); 568 List<Integer> expectedClickedSeq = result.expectedClickedSeq.get(actionPos); 569 570 traverseSubActionsAndVerifyFocusAndClickEvents(selectedAction, actionPos, expectedFocusedSeq, 571 expectedClickedSeq); 572 } 573 574 @Test 575 public void testActionWithZeroSubActions() throws Throwable { 576 ExpectedSubActionResult result = setUpActionsForSubActionsTest(); 577 578 final int actionPos = 2; 579 final GuidedAction selectedAction = result.actionList.get(actionPos); 580 List<Integer> expectedFocusedSeq = result.expectedFocusedSeq.get(actionPos); 581 List<Integer> expectedClickedSeq = result.expectedClickedSeq.get(actionPos); 582 583 traverseSubActionsAndVerifyFocusAndClickEvents(selectedAction, actionPos, expectedFocusedSeq, 584 expectedClickedSeq); 585 } 586 587 @Test 588 public void testActionWithThreeSubActions() throws Throwable { 589 ExpectedSubActionResult result = setUpActionsForSubActionsTest(); 590 591 final int actionPos = 3; 592 final GuidedAction selectedAction = result.actionList.get(actionPos); 593 List<Integer> expectedFocusedSeq = result.expectedFocusedSeq.get(actionPos); 594 List<Integer> expectedClickedSeq = result.expectedClickedSeq.get(actionPos); 595 596 traverseSubActionsAndVerifyFocusAndClickEvents(selectedAction, actionPos, expectedFocusedSeq, 597 expectedClickedSeq); 598 } 599 600 /** 601 * Traverses the list of sub actions of a gudied action. It also verifies the correct action 602 * or sub action is focused or clicked as the traversal is performed. 603 * @param selectedAction The action of interest 604 * @param actionPos The position of selectedAction within the array of guidedactions 605 * @param expectedFocusedSeq The actual actions IDs used as a reference to verify focused actions 606 * @param expectedClickedSeq The actual action IDs used as a reference to verify clicked actions 607 * @throws Throwable 608 */ 609 private void traverseSubActionsAndVerifyFocusAndClickEvents(GuidedAction selectedAction, 610 int actionPos, 611 List<Integer> expectedFocusedSeq, 612 List<Integer> expectedClickedSeq) 613 throws Throwable{ 614 615 final GuidedStepFragment mFragment = 616 (GuidedStepFragment) mActivity.getGuidedStepTestFragment(); 617 int focusStep = 0, clickStep = 0; 618 GuidedStepAttributesTestFragment.LAST_SELECTED_ACTION_ID = 619 GuidedStepAttributesTestFragment.LAST_CLICKED_ACTION_ID = -1; 620 621 622 final int pos = actionPos; 623 mActivity.runOnUiThread(new Runnable() { 624 @Override 625 public void run() { 626 mFragment.setSelectedActionPosition(pos); 627 } 628 }); 629 Thread.sleep(TRANSITION_LENGTH); 630 631 if (mFragment.getSelectedActionPosition() != actionPos) { 632 assertTrue(mContext.getResources().getString( 633 R.string.subaction_test_wrong_focus_error_message), 634 GuidedStepAttributesTestFragment.LAST_SELECTED_ACTION_ID 635 == expectedFocusedSeq.get(focusStep++) 636 ); 637 } else { 638 // If the currently focused position is the same as the position of the action of interest, 639 // then GuidedStepFragment won't received onGuidedActionFocused callback. Since the first 640 // element in the expectedFocusSeq is always the id of this action, we need to move focusStep 641 // one step forward. 642 focusStep++; 643 } 644 if (selectedAction.hasSubActions()) { 645 // Following for loop clicks on a specific action and scrolls & clicks through 646 // all its subactions 647 for (int j = 0; j < selectedAction.getSubActions().size(); j++) { 648 sendKey(KeyEvent.KEYCODE_DPAD_CENTER); 649 Thread.sleep(TRANSITION_LENGTH); 650 assertTrue(mContext.getResources().getString( 651 R.string.subaction_test_wrong_focus_error_message), 652 GuidedStepAttributesTestFragment.LAST_SELECTED_ACTION_ID 653 == expectedFocusedSeq.get(focusStep++) 654 ); 655 assertTrue(mContext.getResources().getString( 656 R.string.subaction_test_wrong_click_error_message), 657 GuidedStepAttributesTestFragment.LAST_CLICKED_ACTION_ID 658 == expectedClickedSeq.get(clickStep++) 659 ); 660 661 for (int k = 0; k < j; k++) { 662 sendKey(KeyEvent.KEYCODE_DPAD_DOWN); 663 Thread.sleep(TRANSITION_LENGTH); 664 assertTrue(mContext.getResources().getString( 665 R.string.subaction_test_wrong_focus_error_message), 666 GuidedStepAttributesTestFragment.LAST_SELECTED_ACTION_ID 667 == expectedFocusedSeq.get(focusStep++) 668 ); 669 } 670 sendKey(KeyEvent.KEYCODE_DPAD_CENTER); 671 Thread.sleep(TRANSITION_LENGTH); 672 673 assertTrue(mContext.getResources().getString( 674 R.string.subaction_test_wrong_focus_error_message), 675 GuidedStepAttributesTestFragment.LAST_SELECTED_ACTION_ID 676 == expectedFocusedSeq.get(focusStep++) 677 ); 678 assertTrue(mContext.getResources().getString( 679 R.string.subaction_test_wrong_click_error_message), 680 GuidedStepAttributesTestFragment.LAST_CLICKED_ACTION_ID 681 == expectedClickedSeq.get(clickStep++) 682 ); 683 } 684 } else { 685 sendKey(KeyEvent.KEYCODE_DPAD_CENTER); 686 Thread.sleep(TRANSITION_LENGTH); 687 assertTrue(mContext.getResources().getString( 688 R.string.subaction_test_wrong_focus_error_message), 689 GuidedStepAttributesTestFragment.LAST_SELECTED_ACTION_ID 690 == expectedFocusedSeq.get(focusStep++) 691 ); 692 assertTrue(mContext.getResources().getString( 693 R.string.subaction_test_wrong_click_error_message), 694 GuidedStepAttributesTestFragment.LAST_CLICKED_ACTION_ID 695 == expectedClickedSeq.get(clickStep++) 696 ); 697 } 698 } 699 700 static class ExpectedSubActionResult { 701 List<List<Integer>> expectedFocusedSeq; // Expected sequence of action (or subaction) ids to receive focus events; 702 // Each entry corresponds to an action item in the guidedactions pane 703 List<List<Integer>> expectedClickedSeq; // Expected sequence of action (or subaction) ids to receive click events; 704 // Each entry corresponds to an action item in the guidedactions pane 705 List<GuidedAction> actionList; // List of GuidedActions in the guidedactions pane 706 } 707 708 /** 709 * Populates a sample list of actions and subactions in the guidedactions pane. 710 * @return An object holding the expected sequence of action and subactions IDs that receive 711 * focus and click events as well as the list of GuidedActions. 712 */ 713 private ExpectedSubActionResult setUpActionsForSubActionsTest() { 714 Intent intent = new Intent(); 715 Resources res = mContext.getResources(); 716 717 ExpectedSubActionResult result = new ExpectedSubActionResult(); 718 result.expectedFocusedSeq = new ArrayList<>(); 719 result.expectedClickedSeq = new ArrayList<>(); 720 721 final int NUM_REGULAR_ACTIONS = 4; 722 final int[] NUM_SUBACTIONS_PER_ACTION = {2, 1, 0, 3}; 723 final int[] REGULAR_ACTIONS_INDEX = new int[NUM_REGULAR_ACTIONS]; 724 final int[] BEGIN_SUBACTION_INDEX_PER_ACTION = new int[NUM_REGULAR_ACTIONS]; 725 final int[] END_SUBACTION_INDEX_PER_ACTION = new int[NUM_REGULAR_ACTIONS]; 726 // Actions and SubActions are assigned unique sequential IDs 727 int lastIndex = 0; 728 for(int i = 0; i < NUM_REGULAR_ACTIONS; i++) { 729 REGULAR_ACTIONS_INDEX[i] = lastIndex; 730 lastIndex++; 731 BEGIN_SUBACTION_INDEX_PER_ACTION[i] = lastIndex; 732 END_SUBACTION_INDEX_PER_ACTION[i] = (lastIndex += NUM_SUBACTIONS_PER_ACTION[i]); 733 } 734 735 for (int i = 0; i < NUM_REGULAR_ACTIONS; i++) { 736 List<Integer> expectedFocusSeqForEachAction = new ArrayList<>(); 737 List<Integer> expectedClickedSeqForEachAction = new ArrayList<>(); 738 expectedFocusSeqForEachAction.add(REGULAR_ACTIONS_INDEX[i]); 739 740 if (NUM_SUBACTIONS_PER_ACTION[i] > 0) { 741 for (int j = BEGIN_SUBACTION_INDEX_PER_ACTION[i]; 742 j < END_SUBACTION_INDEX_PER_ACTION[i]; j++) { 743 expectedClickedSeqForEachAction.add(REGULAR_ACTIONS_INDEX[i]); 744 for (int k = BEGIN_SUBACTION_INDEX_PER_ACTION[i]; k <= j; k++) { 745 expectedFocusSeqForEachAction.add(k); 746 } 747 expectedClickedSeqForEachAction.add(j); 748 expectedFocusSeqForEachAction.add(REGULAR_ACTIONS_INDEX[i]); 749 } 750 } else { 751 expectedClickedSeqForEachAction.add(REGULAR_ACTIONS_INDEX[i]); 752 expectedFocusSeqForEachAction.add(REGULAR_ACTIONS_INDEX[i]); 753 } 754 result.expectedFocusedSeq.add(expectedFocusSeqForEachAction); 755 result.expectedClickedSeq.add(expectedClickedSeqForEachAction); 756 } 757 758 String title = "Guided SubActions Test"; 759 String breadcrumb = "SubActions Test Demo"; 760 String description = ""; 761 GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description, 762 breadcrumb, null); 763 764 List<GuidedAction> actionList = new ArrayList<>(); 765 766 lastIndex = 0; 767 for (int i = 0; i < NUM_REGULAR_ACTIONS; i++ ) { 768 GuidedAction action = new GuidedAction.Builder(mContext) 769 .id(lastIndex++) 770 .title(res.getString(R.string.dropdown_action_title, i)) 771 .description(res.getString(R.string.dropdown_action_desc, i)) 772 .build(); 773 if (NUM_SUBACTIONS_PER_ACTION[i] > 0) { 774 List<GuidedAction> subActions = new ArrayList<>(); 775 action.setSubActions(subActions); 776 for(int j = 0; j < NUM_SUBACTIONS_PER_ACTION[i]; j++) { 777 subActions.add(new GuidedAction.Builder(mContext) 778 .id(lastIndex++) 779 .title(res.getString(R.string.subaction_title, j)) 780 .description("") 781 .build() 782 ); 783 } 784 } 785 actionList.add(action); 786 } 787 result.actionList = actionList; 788 789 GuidedStepAttributesTestFragment.clear(); 790 GuidedStepAttributesTestFragment.GUIDANCE = guidance; 791 GuidedStepAttributesTestFragment.ACTION_LIST = actionList; 792 793 initActivity(intent); 794 return result; 795 } 796} 797