blob: 2fb5c7a039c52b0daa796f4339be6039a10396a5 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.leanback.app;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.os.Bundle;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.leanback.testutils.PollingCheck;
import androidx.leanback.widget.GuidedAction;
import androidx.leanback.widget.GuidedActionsStylist;
import androidx.leanback.widget.VerticalGridView;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.RecyclerView;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.List;
@LargeTest
@RunWith(AndroidJUnit4.class)
public class GuidedStepSupportFragmentTest extends GuidedStepSupportFragmentTestBase {
private static final int ON_DESTROY_TIMEOUT = 5000;
@Test
public void nextAndBack() throws Throwable {
final String firstFragmentName = generateMethodTestName("first");
final String secondFragmentName = generateMethodTestName("second");
GuidedStepTestSupportFragment.Provider first = mockProvider(firstFragmentName);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
List actions = (List) invocation.getArguments()[0];
actions.add(new GuidedAction.Builder().id(1000).title("OK").build());
return null;
}
}).when(first).onCreateActions(any(List.class), nullable(Bundle.class));
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
GuidedAction action = (GuidedAction) invocation.getArguments()[0];
GuidedStepTestSupportFragment.Provider obj = (GuidedStepTestSupportFragment.Provider)
invocation.getMock();
if (action.getId() == 1000) {
GuidedStepSupportFragment.add(obj.getFragmentManager(),
new GuidedStepTestSupportFragment(secondFragmentName));
}
return null;
}
}).when(first).onGuidedActionClicked(any(GuidedAction.class));
GuidedStepTestSupportFragment.Provider second = mockProvider(secondFragmentName);
GuidedStepSupportFragmentTestActivity activity = launchTestActivity(firstFragmentName);
verify(first, times(1)).onCreate(nullable(Bundle.class));
verify(first, times(1)).onCreateGuidance(nullable(Bundle.class));
verify(first, times(1)).onCreateActions(any(List.class), nullable(Bundle.class));
verify(first, times(1)).onCreateButtonActions(any(List.class), nullable(Bundle.class));
verify(first, times(1)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
nullable(Bundle.class), any(View.class));
verify(first, times(1)).onViewStateRestored(nullable(Bundle.class));
verify(first, times(1)).onStart();
verify(first, times(1)).onResume();
sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
verify(first, times(1)).onGuidedActionClicked(any(GuidedAction.class));
PollingCheck.waitFor(new EnterTransitionFinish(second));
verify(first, times(1)).onPause();
verify(first, times(1)).onStop();
verify(first, times(1)).onDestroyView();
verify(second, times(1)).onCreate(nullable(Bundle.class));
verify(second, times(1)).onCreateGuidance(nullable(Bundle.class));
verify(second, times(1)).onCreateActions(any(List.class), nullable(Bundle.class));
verify(second, times(1)).onCreateButtonActions(any(List.class), nullable(Bundle.class));
verify(second, times(1)).onCreateView(any(LayoutInflater.class), nullable(ViewGroup.class),
nullable(Bundle.class), any(View.class));
verify(second, times(1)).onViewStateRestored(nullable(Bundle.class));
verify(second, times(1)).onStart();
verify(second, times(1)).onResume();
sendKey(KeyEvent.KEYCODE_BACK);
PollingCheck.waitFor(new EnterTransitionFinish(first));
verify(second, times(1)).onPause();
verify(second, times(1)).onStop();
verify(second, times(1)).onDestroyView();
verify(second, times(1)).onDestroy();
verify(first, times(1)).onCreateActions(any(List.class), nullable(Bundle.class));
verify(first, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
nullable(Bundle.class), any(View.class));
verify(first, times(2)).onViewStateRestored(nullable(Bundle.class));
verify(first, times(2)).onStart();
verify(first, times(2)).onResume();
sendKey(KeyEvent.KEYCODE_BACK);
PollingCheck.waitFor(new PollingCheck.ActivityDestroy(activity));
verify(first, timeout(ON_DESTROY_TIMEOUT).times(1)).onDestroy();
assertTrue(activity.isDestroyed());
}
@Test
public void restoreFragments() throws Throwable {
final String firstFragmentName = generateMethodTestName("first");
final String secondFragmentName = generateMethodTestName("second");
GuidedStepTestSupportFragment.Provider first = mockProvider(firstFragmentName);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
List actions = (List) invocation.getArguments()[0];
actions.add(new GuidedAction.Builder().id(1000).title("OK").build());
actions.add(new GuidedAction.Builder().id(1001).editable(true).title("text")
.build());
actions.add(new GuidedAction.Builder().id(1002).editable(true).title("text")
.autoSaveRestoreEnabled(false).build());
return null;
}
}).when(first).onCreateActions(any(List.class), nullable(Bundle.class));
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
GuidedAction action = (GuidedAction) invocation.getArguments()[0];
GuidedStepTestSupportFragment.Provider obj = (GuidedStepTestSupportFragment.Provider)
invocation.getMock();
if (action.getId() == 1000) {
GuidedStepSupportFragment.add(obj.getFragmentManager(),
new GuidedStepTestSupportFragment(secondFragmentName));
}
return null;
}
}).when(first).onGuidedActionClicked(any(GuidedAction.class));
GuidedStepTestSupportFragment.Provider second = mockProvider(secondFragmentName);
final GuidedStepSupportFragmentTestActivity activity = launchTestActivity(firstFragmentName);
first.getFragment().findActionById(1001).setTitle("modified text");
first.getFragment().findActionById(1002).setTitle("modified text");
sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
PollingCheck.waitFor(new EnterTransitionFinish(second));
activityTestRule.runOnUiThread(new Runnable() {
@Override
public void run() {
activity.recreate();
}
});
PollingCheck.waitFor(new EnterTransitionFinish(second));
verify(first, times(1)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
nullable(Bundle.class), any(View.class));
verify(first, times(1)).onDestroy();
verify(second, times(2)).onCreate(nullable(Bundle.class));
verify(second, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
nullable(Bundle.class), any(View.class));
verify(second, times(1)).onDestroy();
sendKey(KeyEvent.KEYCODE_BACK);
PollingCheck.waitFor(new EnterTransitionFinish(first));
verify(second, times(2)).onPause();
verify(second, times(2)).onStop();
verify(second, times(2)).onDestroyView();
verify(second, times(2)).onDestroy();
assertEquals("modified text", first.getFragment().findActionById(1001).getTitle());
assertEquals("text", first.getFragment().findActionById(1002).getTitle());
verify(first, times(2)).onCreate(nullable(Bundle.class));
verify(first, times(2)).onCreateActions(any(List.class), nullable(Bundle.class));
verify(first, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
nullable(Bundle.class), any(View.class));
}
@Test
public void finishGuidedStepSupportFragment_finishes_activity() throws Throwable {
final String firstFragmentName = generateMethodTestName("first");
GuidedStepTestSupportFragment.Provider first = mockProvider(firstFragmentName);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
List actions = (List) invocation.getArguments()[0];
actions.add(new GuidedAction.Builder().id(1001).title("Finish activity").build());
return null;
}
}).when(first).onCreateActions(any(List.class), nullable(Bundle.class));
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
GuidedAction action = (GuidedAction) invocation.getArguments()[0];
GuidedStepTestSupportFragment.Provider obj = (GuidedStepTestSupportFragment.Provider)
invocation.getMock();
if (action.getId() == 1001) {
obj.getFragment().finishGuidedStepSupportFragments();
}
return null;
}
}).when(first).onGuidedActionClicked(any(GuidedAction.class));
final GuidedStepSupportFragmentTestActivity activity = launchTestActivity(firstFragmentName);
View viewFinish = first.getFragment().getActionItemView(0);
assertTrue(viewFinish.hasFocus());
sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
PollingCheck.waitFor(new PollingCheck.ActivityDestroy(activity));
verify(first, timeout(ON_DESTROY_TIMEOUT).times(1)).onDestroy();
}
@Test
public void finishGuidedStepSupportFragment_finishes_fragments() throws Throwable {
final String firstFragmentName = generateMethodTestName("first");
GuidedStepTestSupportFragment.Provider first = mockProvider(firstFragmentName);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
List actions = (List) invocation.getArguments()[0];
actions.add(new GuidedAction.Builder().id(1001).title("Finish fragments").build());
return null;
}
}).when(first).onCreateActions(any(List.class), nullable(Bundle.class));
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
GuidedAction action = (GuidedAction) invocation.getArguments()[0];
GuidedStepTestSupportFragment.Provider obj = (GuidedStepTestSupportFragment.Provider)
invocation.getMock();
if (action.getId() == 1001) {
obj.getFragment().finishGuidedStepSupportFragments();
}
return null;
}
}).when(first).onGuidedActionClicked(any(GuidedAction.class));
final GuidedStepSupportFragmentTestActivity activity = launchTestActivity(firstFragmentName,
false /*asRoot*/);
View viewFinish = first.getFragment().getActionItemView(0);
assertTrue(viewFinish.hasFocus());
sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
// fragment should be destroyed, activity should not destroyed
waitOnDestroy(first, 1);
assertFalse(activity.isDestroyed());
}
@Test
public void subActions() throws Throwable {
final String firstFragmentName = generateMethodTestName("first");
final String secondFragmentName = generateMethodTestName("second");
final boolean[] expandSubActionInOnCreateView = new boolean[] {false};
GuidedStepTestSupportFragment.Provider first = mockProvider(firstFragmentName);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
GuidedStepTestSupportFragment.Provider obj = (GuidedStepTestSupportFragment.Provider)
invocation.getMock();
if (expandSubActionInOnCreateView[0]) {
obj.getFragment().expandAction(obj.getFragment().findActionById(1000), false);
}
return null;
}
}).when(first).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
nullable(Bundle.class), any(View.class));
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
List actions = (List) invocation.getArguments()[0];
List<GuidedAction> subActions = new ArrayList<GuidedAction>();
subActions.add(new GuidedAction.Builder().id(2000).title("item1").build());
subActions.add(new GuidedAction.Builder().id(2001).title("item2").build());
actions.add(new GuidedAction.Builder().id(1000).subActions(subActions)
.title("list").build());
return null;
}
}).when(first).onCreateActions(any(List.class), nullable(Bundle.class));
doAnswer(new Answer<Boolean>() {
@Override
public Boolean answer(InvocationOnMock invocation) {
GuidedStepTestSupportFragment.Provider obj = (GuidedStepTestSupportFragment.Provider)
invocation.getMock();
GuidedAction action = (GuidedAction) invocation.getArguments()[0];
if (action.getId() == 2000) {
return true;
} else if (action.getId() == 2001) {
GuidedStepSupportFragment.add(obj.getFragmentManager(),
new GuidedStepTestSupportFragment(secondFragmentName));
return false;
}
return false;
}
}).when(first).onSubGuidedActionClicked(any(GuidedAction.class));
GuidedStepTestSupportFragment.Provider second = mockProvider(secondFragmentName);
final GuidedStepSupportFragmentTestActivity activity = launchTestActivity(firstFragmentName);
// after clicked, it sub actions list should expand
View viewForList = first.getFragment().getActionItemView(0);
assertTrue(viewForList.hasFocus());
sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
PollingCheck.waitFor(new ExpandTransitionFinish(first));
assertFalse(viewForList.hasFocus());
sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
ArgumentCaptor<GuidedAction> actionCapture = ArgumentCaptor.forClass(GuidedAction.class);
verify(first, times(1)).onSubGuidedActionClicked(actionCapture.capture());
assertEquals(2000, actionCapture.getValue().getId());
// after clicked a sub action, it sub actions list should close
PollingCheck.waitFor(new ExpandTransitionFinish(first));
assertTrue(viewForList.hasFocus());
sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
PollingCheck.waitFor(new ExpandTransitionFinish(first));
assertFalse(viewForList.hasFocus());
sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
ArgumentCaptor<GuidedAction> actionCapture2 = ArgumentCaptor.forClass(GuidedAction.class);
verify(first, times(2)).onSubGuidedActionClicked(actionCapture2.capture());
assertEquals(2001, actionCapture2.getValue().getId());
PollingCheck.waitFor(new EnterTransitionFinish(second));
verify(second, times(1)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
nullable(Bundle.class), any(View.class));
// test expand sub action when return to first fragment
expandSubActionInOnCreateView[0] = true;
sendKey(KeyEvent.KEYCODE_BACK);
PollingCheck.waitFor(new EnterTransitionFinish(first));
verify(first, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
nullable(Bundle.class), any(View.class));
assertTrue(first.getFragment().isExpanded());
sendKey(KeyEvent.KEYCODE_BACK);
PollingCheck.waitFor(new ExpandTransitionFinish(first));
assertFalse(first.getFragment().isExpanded());
sendKey(KeyEvent.KEYCODE_BACK);
PollingCheck.waitFor(new PollingCheck.ActivityDestroy(activity));
verify(first, timeout(ON_DESTROY_TIMEOUT).times(1)).onDestroy();
}
@Test
public void setActionsWhenSubActionsExpanded() throws Throwable {
final String firstFragmentName = generateMethodTestName("first");
GuidedStepTestSupportFragment.Provider first = mockProvider(firstFragmentName);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
List actions = (List) invocation.getArguments()[0];
List<GuidedAction> subActions = new ArrayList<GuidedAction>();
subActions.add(new GuidedAction.Builder().id(2000).title("item1").build());
actions.add(new GuidedAction.Builder().id(1000).subActions(subActions)
.title("list").build());
return null;
}
}).when(first).onCreateActions(any(List.class), nullable(Bundle.class));
doAnswer(new Answer<Boolean>() {
@Override
public Boolean answer(InvocationOnMock invocation) {
GuidedStepTestSupportFragment.Provider obj = (GuidedStepTestSupportFragment.Provider)
invocation.getMock();
GuidedAction action = (GuidedAction) invocation.getArguments()[0];
if (action.getId() == 2000) {
List<GuidedAction> newActions = new ArrayList<GuidedAction>();
newActions.add(new GuidedAction.Builder().id(1001).title("item2").build());
obj.getFragment().setActions(newActions);
return false;
}
return false;
}
}).when(first).onSubGuidedActionClicked(any(GuidedAction.class));
final GuidedStepSupportFragmentTestActivity activity = launchTestActivity(firstFragmentName);
// after clicked, it sub actions list should expand
View firstView = first.getFragment().getActionItemView(0);
assertTrue(firstView.hasFocus());
sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
PollingCheck.waitFor(new ExpandTransitionFinish(first));
assertFalse(firstView.hasFocus());
sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
ArgumentCaptor<GuidedAction> actionCapture = ArgumentCaptor.forClass(GuidedAction.class);
verify(first, times(1)).onSubGuidedActionClicked(actionCapture.capture());
// after clicked a sub action, whole action list is replaced.
PollingCheck.waitFor(new ExpandTransitionFinish(first));
assertFalse(first.getFragment().isExpanded());
View newFirstView = first.getFragment().getActionItemView(0);
assertTrue(newFirstView.hasFocus());
assertTrue(newFirstView.getVisibility() == View.VISIBLE);
GuidedActionsStylist.ViewHolder vh = (GuidedActionsStylist.ViewHolder) first.getFragment()
.getGuidedActionsStylist().getActionsGridView().getChildViewHolder(newFirstView);
assertEquals(1001, vh.getAction().getId());
}
@Test
public void buttonActionsRtl() throws Throwable {
final String firstFragmentName = generateMethodTestName("first");
GuidedStepTestSupportFragment.Provider first = mockProvider(firstFragmentName);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
List actions = (List) invocation.getArguments()[0];
actions.add(new GuidedAction.Builder().id(1000).title("action").build());
return null;
}
}).when(first).onCreateActions(any(List.class), nullable(Bundle.class));
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
List actions = (List) invocation.getArguments()[0];
actions.add(new GuidedAction.Builder().id(1001).title("button action").build());
return null;
}
}).when(first).onCreateButtonActions(any(List.class), nullable(Bundle.class));
final GuidedStepSupportFragmentTestActivity activity = launchTestActivity(firstFragmentName,
true, View.LAYOUT_DIRECTION_RTL);
assertEquals(View.LAYOUT_DIRECTION_RTL, first.getFragment().getView().getLayoutDirection());
View firstView = first.getFragment().getActionItemView(0);
assertTrue(firstView.hasFocus());
}
@Test
public void recyclerViewDiffTest() throws Throwable {
final String firstFragmentName = generateMethodTestName("first");
final GuidedStepTestSupportFragment.Provider first = mockProvider(firstFragmentName);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
List actions = (List) invocation.getArguments()[0];
actions.add(new GuidedAction.Builder().id(1000).title("action1").build());
actions.add(new GuidedAction.Builder().id(1001).title("action2").build());
return null;
}
}).when(first).onCreateActions(any(List.class), nullable(Bundle.class));
launchTestActivity(firstFragmentName, true);
final ArrayList<RecyclerView.ViewHolder> changeList = new ArrayList();
VerticalGridView rv = first.getFragment().mActionsStylist.getActionsGridView();
rv.setItemAnimator(new DefaultItemAnimator() {
@Override
public void onChangeStarting(RecyclerView.ViewHolder item, boolean oldItem) {
if (!oldItem) {
changeList.add(item);
}
super.onChangeStarting(item, oldItem);
}
});
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
List actions = new ArrayList();
actions.add(new GuidedAction.Builder().id(1001).title("action2x").build());
actions.add(new GuidedAction.Builder().id(1000).title("action1x").build());
first.getFragment().setActions(actions);
}
});
// should causes two change animation.
PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
@Override
public boolean canProceed() {
return changeList.size() == 2;
}
});
}
}