Remove DashboardSummary and all related classes.
- Condition is already supported in PersonalSettingsFragment - Suggestion is already supported in PersonalSettingsFragment - Static/dynamic tiles are supported in TopLevelSettings Change-Id: I51882e3bd0919ad95109baefac683d98667c11e3 Fixes: 110405144 Test: robotests
This commit is contained in:
@@ -1,282 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 com.android.settings.dashboard;
|
||||
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Bundle;
|
||||
import android.service.settings.suggestions.Suggestion;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.dashboard.suggestions.SuggestionAdapter;
|
||||
import com.android.settings.homepage.conditional.ConditionManager;
|
||||
import com.android.settings.homepage.conditional.ConditionalCard;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||
import com.android.settings.widget.RoundedHomepageIcon;
|
||||
import com.android.settingslib.drawer.CategoryKey;
|
||||
import com.android.settingslib.drawer.Tile;
|
||||
import com.android.settingslib.utils.IconCache;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
@Config(shadows = SettingsShadowResources.SettingsShadowTheme.class)
|
||||
public class DashboardAdapterTest {
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private SettingsActivity mContext;
|
||||
@Mock
|
||||
private ConditionalCard mCondition;
|
||||
@Mock
|
||||
private Resources mResources;
|
||||
@Mock
|
||||
private WindowManager mWindowManager;
|
||||
@Mock
|
||||
private ConditionManager mConditionManager;
|
||||
|
||||
private ActivityInfo mActivityInfo;
|
||||
private DashboardAdapter mDashboardAdapter;
|
||||
private List<ConditionalCard> mConditionList;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
FakeFeatureFactory.setupForTest();
|
||||
mActivityInfo = new ActivityInfo();
|
||||
mActivityInfo.packageName = "pkg";
|
||||
mActivityInfo.name = "class";
|
||||
mActivityInfo.metaData = new Bundle();
|
||||
mActivityInfo.metaData.putString(META_DATA_PREFERENCE_TITLE, "test-title");
|
||||
|
||||
when(mContext.getSystemService(Context.WINDOW_SERVICE)).thenReturn(mWindowManager);
|
||||
when(mContext.getResources()).thenReturn(mResources);
|
||||
when(mResources.getQuantityString(any(int.class), any(int.class), any())).thenReturn("");
|
||||
|
||||
mConditionList = new ArrayList<>();
|
||||
mConditionList.add(mCondition);
|
||||
mDashboardAdapter = new DashboardAdapter(mContext, null /* savedInstanceState */,
|
||||
mConditionManager, null /* suggestionControllerMixin */, null /* lifecycle */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSuggestionClosed_notOnlySuggestion_updateSuggestionOnly() {
|
||||
final DashboardAdapter adapter =
|
||||
spy(new DashboardAdapter(mContext, null /* savedInstanceState */,
|
||||
mConditionManager,
|
||||
null /* suggestionControllerMixin */,
|
||||
null /* lifecycle */));
|
||||
final List<Suggestion> suggestions = makeSuggestions("pkg1", "pkg2", "pkg3");
|
||||
adapter.setSuggestions(suggestions);
|
||||
|
||||
final RecyclerView data = mock(RecyclerView.class);
|
||||
when(data.getResources()).thenReturn(mResources);
|
||||
when(data.getContext()).thenReturn(mContext);
|
||||
when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
|
||||
final View itemView = mock(View.class);
|
||||
when(itemView.findViewById(R.id.suggestion_list)).thenReturn(data);
|
||||
when(itemView.findViewById(android.R.id.summary)).thenReturn(mock(TextView.class));
|
||||
when(itemView.findViewById(android.R.id.title)).thenReturn(mock(TextView.class));
|
||||
final DashboardAdapter.SuggestionContainerHolder holder =
|
||||
new DashboardAdapter.SuggestionContainerHolder(itemView);
|
||||
|
||||
adapter.onBindSuggestion(holder, 0);
|
||||
|
||||
reset(adapter); // clear interactions tracking
|
||||
|
||||
final Suggestion suggestionToRemove = suggestions.get(1);
|
||||
adapter.onSuggestionClosed(suggestionToRemove);
|
||||
|
||||
assertThat(suggestions.size()).isEqualTo(2);
|
||||
assertThat(suggestions.contains(suggestionToRemove)).isFalse();
|
||||
verify(adapter).notifyDashboardDataChanged(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSuggestionClosed_onlySuggestion_updateDashboardData() {
|
||||
final DashboardAdapter adapter =
|
||||
spy(new DashboardAdapter(mContext, null /* savedInstanceState */,
|
||||
mConditionManager,
|
||||
null /* suggestionControllerMixin */, null /* lifecycle */));
|
||||
final List<Suggestion> suggestions = makeSuggestions("pkg1");
|
||||
adapter.setSuggestions(suggestions);
|
||||
final DashboardData dashboardData = adapter.mDashboardData;
|
||||
reset(adapter); // clear interactions tracking
|
||||
|
||||
adapter.onSuggestionClosed(suggestions.get(0));
|
||||
|
||||
assertThat(adapter.mDashboardData).isNotEqualTo(dashboardData);
|
||||
verify(adapter).notifyDashboardDataChanged(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSuggestionClosed_notInSuggestionList_shouldNotUpdateSuggestionList() {
|
||||
final DashboardAdapter adapter =
|
||||
spy(new DashboardAdapter(mContext, null /* savedInstanceState */,
|
||||
mConditionManager,
|
||||
null /* suggestionControllerMixin */, null /* lifecycle */));
|
||||
final List<Suggestion> suggestions = makeSuggestions("pkg1");
|
||||
adapter.setSuggestions(suggestions);
|
||||
|
||||
reset(adapter); // clear interactions tracking
|
||||
|
||||
adapter.onSuggestionClosed(mock(Suggestion.class));
|
||||
|
||||
verify(adapter, never()).setSuggestions(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBindSuggestion_shouldSetSuggestionAdapterAndNoCrash() {
|
||||
mDashboardAdapter = new DashboardAdapter(mContext, null /* savedInstanceState */,
|
||||
mConditionManager, null /* suggestionControllerMixin */, null /* lifecycle */);
|
||||
final List<Suggestion> suggestions = makeSuggestions("pkg1");
|
||||
|
||||
mDashboardAdapter.setSuggestions(suggestions);
|
||||
|
||||
final RecyclerView data = mock(RecyclerView.class);
|
||||
when(data.getResources()).thenReturn(mResources);
|
||||
when(data.getContext()).thenReturn(mContext);
|
||||
when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
|
||||
final View itemView = mock(View.class);
|
||||
when(itemView.findViewById(R.id.suggestion_list)).thenReturn(data);
|
||||
when(itemView.findViewById(android.R.id.summary)).thenReturn(mock(TextView.class));
|
||||
when(itemView.findViewById(android.R.id.title)).thenReturn(mock(TextView.class));
|
||||
final DashboardAdapter.SuggestionContainerHolder holder =
|
||||
new DashboardAdapter.SuggestionContainerHolder(itemView);
|
||||
|
||||
mDashboardAdapter.onBindSuggestion(holder, 0);
|
||||
|
||||
verify(data).setAdapter(any(SuggestionAdapter.class));
|
||||
// should not crash
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBindTile_internalTile_shouldNotUseGenericBackgroundIcon() {
|
||||
final Context context = RuntimeEnvironment.application;
|
||||
final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile, null);
|
||||
final DashboardAdapter.DashboardItemHolder holder =
|
||||
new DashboardAdapter.DashboardItemHolder(view);
|
||||
final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
|
||||
doReturn(Icon.createWithResource(context, R.drawable.ic_settings))
|
||||
.when(tile).getIcon(context);
|
||||
final IconCache iconCache = mock(IconCache.class);
|
||||
when(iconCache.getIcon(tile.getIcon(context)))
|
||||
.thenReturn(context.getDrawable(R.drawable.ic_settings));
|
||||
|
||||
mDashboardAdapter = new DashboardAdapter(context, null /* savedInstanceState */,
|
||||
mConditionManager, null /* suggestionControllerMixin */, null /* lifecycle */);
|
||||
ReflectionHelpers.setField(mDashboardAdapter, "mCache", iconCache);
|
||||
mDashboardAdapter.onBindTile(holder, tile);
|
||||
|
||||
verify(iconCache, never()).updateIcon(any(Icon.class), any(Drawable.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBindTile_externalTile_shouldUpdateIcon() {
|
||||
final Context context = spy(RuntimeEnvironment.application);
|
||||
final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile, null);
|
||||
final DashboardAdapter.DashboardItemHolder holder =
|
||||
new DashboardAdapter.DashboardItemHolder(view);
|
||||
final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
|
||||
final Icon icon = Icon.createWithResource(context, R.drawable.ic_settings);
|
||||
doReturn(icon).when(tile).getIcon(context);
|
||||
|
||||
final IconCache iconCache = new IconCache(context);
|
||||
|
||||
mDashboardAdapter = new DashboardAdapter(context, null /* savedInstanceState */,
|
||||
mConditionManager,
|
||||
null /* suggestionControllerMixin */, null /* lifecycle */);
|
||||
ReflectionHelpers.setField(mDashboardAdapter, "mCache", iconCache);
|
||||
|
||||
doReturn("another.package").when(context).getPackageName();
|
||||
mDashboardAdapter.onBindTile(holder, tile);
|
||||
|
||||
assertThat(iconCache.getIcon(tile.getIcon(context)))
|
||||
.isInstanceOf(RoundedHomepageIcon.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBindTile_externalTile_usingRoundedHomepageIcon_shouldNotUpdateIcon() {
|
||||
final Context context = RuntimeEnvironment.application;
|
||||
final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile, null);
|
||||
final DashboardAdapter.DashboardItemHolder holder =
|
||||
new DashboardAdapter.DashboardItemHolder(view);
|
||||
final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
|
||||
doReturn(mock(Icon.class)).when(tile).getIcon(context);
|
||||
when(tile.getIcon(context).getResPackage()).thenReturn("another.package");
|
||||
|
||||
final IconCache iconCache = mock(IconCache.class);
|
||||
when(iconCache.getIcon(tile.getIcon(context))).thenReturn(mock(RoundedHomepageIcon.class));
|
||||
|
||||
mDashboardAdapter = new DashboardAdapter(context, null /* savedInstanceState */,
|
||||
mConditionManager, null /* suggestionControllerMixin */, null /* lifecycle */);
|
||||
ReflectionHelpers.setField(mDashboardAdapter, "mCache", iconCache);
|
||||
|
||||
mDashboardAdapter.onBindTile(holder, tile);
|
||||
|
||||
verify(iconCache, never()).updateIcon(eq(tile.getIcon(context)),
|
||||
any(RoundedHomepageIcon.class));
|
||||
}
|
||||
|
||||
private List<Suggestion> makeSuggestions(String... pkgNames) {
|
||||
final List<Suggestion> suggestions = new ArrayList<>();
|
||||
for (String pkgName : pkgNames) {
|
||||
final Suggestion suggestion = new Suggestion.Builder(pkgName)
|
||||
.setPendingIntent(mock(PendingIntent.class))
|
||||
.build();
|
||||
suggestions.add(suggestion);
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
}
|
@@ -1,417 +0,0 @@
|
||||
/*
|
||||
* 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 com.android.settings.dashboard;
|
||||
|
||||
import static com.android.settings.dashboard.DashboardData.STABLE_ID_CONDITION_CONTAINER;
|
||||
import static com.android.settings.dashboard.DashboardData.STABLE_ID_CONDITION_FOOTER;
|
||||
import static com.android.settings.dashboard.DashboardData.STABLE_ID_SUGGESTION_CONDITION_DIVIDER;
|
||||
import static com.android.settings.dashboard.DashboardData.STABLE_ID_SUGGESTION_CONTAINER;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.service.settings.suggestions.Suggestion;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.ListUpdateCallback;
|
||||
|
||||
import com.android.settings.homepage.conditional.AirplaneModeConditionCard;
|
||||
import com.android.settings.homepage.conditional.ConditionalCard;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settingslib.drawer.CategoryKey;
|
||||
import com.android.settingslib.drawer.DashboardCategory;
|
||||
import com.android.settingslib.drawer.Tile;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class DashboardDataTest {
|
||||
|
||||
private static final String TEST_SUGGESTION_TITLE = "Use fingerprint";
|
||||
private static final int TEST_TILE_ID = 12345;
|
||||
|
||||
private DashboardData mDashboardDataWithOneConditions;
|
||||
private DashboardData mDashboardDataWithTwoConditions;
|
||||
private DashboardData mDashboardDataWithNoItems;
|
||||
private DashboardCategory mDashboardCategory;
|
||||
@Mock
|
||||
private Tile mTestCategoryTile;
|
||||
@Mock
|
||||
private ConditionalCard mTestCondition;
|
||||
@Mock
|
||||
private ConditionalCard mSecondCondition; // condition used to test insert in DiffUtil
|
||||
private Suggestion mTestSuggestion;
|
||||
|
||||
@Before
|
||||
public void SetUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mDashboardCategory = new DashboardCategory(CategoryKey.CATEGORY_HOMEPAGE);
|
||||
|
||||
// Build suggestions
|
||||
final List<Suggestion> suggestions = new ArrayList<>();
|
||||
mTestSuggestion = new Suggestion.Builder("pkg")
|
||||
.setTitle(TEST_SUGGESTION_TITLE)
|
||||
.setPendingIntent(mock(PendingIntent.class))
|
||||
.build();
|
||||
suggestions.add(mTestSuggestion);
|
||||
|
||||
// Build oneItemConditions
|
||||
final List<ConditionalCard> oneItemConditions = new ArrayList<>();
|
||||
oneItemConditions.add(mTestCondition);
|
||||
|
||||
// Build twoItemConditions
|
||||
final List<ConditionalCard> twoItemsConditions = new ArrayList<>();
|
||||
twoItemsConditions.add(mTestCondition);
|
||||
twoItemsConditions.add(mSecondCondition);
|
||||
|
||||
// Build category
|
||||
when(mTestCategoryTile.getId()).thenReturn(TEST_TILE_ID);
|
||||
|
||||
mDashboardCategory.addTile(mTestCategoryTile);
|
||||
|
||||
// Build DashboardData
|
||||
mDashboardDataWithOneConditions = new DashboardData.Builder()
|
||||
.setConditions(oneItemConditions)
|
||||
.setCategory(mDashboardCategory)
|
||||
.setSuggestions(suggestions)
|
||||
.setConditionExpanded(true)
|
||||
.build();
|
||||
|
||||
mDashboardDataWithTwoConditions = new DashboardData.Builder()
|
||||
.setConditions(twoItemsConditions)
|
||||
.setCategory(mDashboardCategory)
|
||||
.setSuggestions(suggestions)
|
||||
.setConditionExpanded(true)
|
||||
.build();
|
||||
|
||||
mDashboardDataWithNoItems = new DashboardData.Builder()
|
||||
.setConditions(null)
|
||||
.setCategory(null)
|
||||
.setSuggestions(null)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildItemsData_shouldSetstableId() {
|
||||
final List<DashboardData.Item> items = mDashboardDataWithOneConditions.getItemList();
|
||||
|
||||
// suggestion, separator, condition, footer, 1 tile
|
||||
assertThat(items).hasSize(5);
|
||||
|
||||
assertThat(items.get(0).id).isEqualTo(STABLE_ID_SUGGESTION_CONTAINER);
|
||||
assertThat(items.get(1).id).isEqualTo(STABLE_ID_SUGGESTION_CONDITION_DIVIDER);
|
||||
assertThat(items.get(2).id).isEqualTo(STABLE_ID_CONDITION_CONTAINER);
|
||||
assertThat(items.get(3).id).isEqualTo(STABLE_ID_CONDITION_FOOTER);
|
||||
assertThat(items.get(4).id).isEqualTo(TEST_TILE_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildItemsData_containsAllData() {
|
||||
final Object[] expectedObjects = {
|
||||
mDashboardDataWithOneConditions.getSuggestions(),
|
||||
null /* divider */,
|
||||
mDashboardDataWithOneConditions.getConditions(),
|
||||
null /* footer */, mTestCategoryTile};
|
||||
final int expectedSize = expectedObjects.length;
|
||||
|
||||
assertThat(mDashboardDataWithOneConditions.getItemList()).hasSize(expectedSize);
|
||||
|
||||
for (int i = 0; i < expectedSize; i++) {
|
||||
final Object item = mDashboardDataWithOneConditions.getItemEntityByPosition(i);
|
||||
if (item instanceof List) {
|
||||
assertThat(item).isEqualTo(expectedObjects[i]);
|
||||
} else if (item instanceof DashboardData.ConditionHeaderData) {
|
||||
DashboardData.ConditionHeaderData i1 = (DashboardData.ConditionHeaderData) item;
|
||||
DashboardData.ConditionHeaderData i2 =
|
||||
(DashboardData.ConditionHeaderData) expectedObjects[i];
|
||||
assertThat(i1.title).isEqualTo(i2.title);
|
||||
assertThat(i1.conditionCount).isEqualTo(i2.conditionCount);
|
||||
} else {
|
||||
assertThat(item).isSameAs(expectedObjects[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPositionByEntity_selfInstance_returnPositionFound() {
|
||||
final int position = mDashboardDataWithOneConditions
|
||||
.getPositionByEntity(mDashboardDataWithOneConditions.getConditions());
|
||||
assertThat(position).isNotEqualTo(DashboardData.POSITION_NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPositionByEntity_notExisted_returnNotFound() {
|
||||
final ConditionalCard condition = mock(AirplaneModeConditionCard.class);
|
||||
final int position = mDashboardDataWithOneConditions.getPositionByEntity(condition);
|
||||
assertThat(position).isEqualTo(DashboardData.POSITION_NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPositionByTile_selfInstance_returnPositionFound() {
|
||||
final int position = mDashboardDataWithOneConditions.getPositionByTile(mTestCategoryTile);
|
||||
assertThat(position).isNotEqualTo(DashboardData.POSITION_NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPositionByTile_equalTitle_returnPositionFound() {
|
||||
final Tile tile = mock(Tile.class);
|
||||
when(tile.getId()).thenReturn(TEST_TILE_ID);
|
||||
|
||||
final int position = mDashboardDataWithOneConditions.getPositionByTile(tile);
|
||||
|
||||
assertThat(position).isNotEqualTo(DashboardData.POSITION_NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPositionByTile_notExisted_returnNotFound() {
|
||||
final Tile tile = mock(Tile.class);
|
||||
when(tile.getId()).thenReturn(123);
|
||||
final int position = mDashboardDataWithOneConditions.getPositionByTile(tile);
|
||||
assertThat(position).isEqualTo(DashboardData.POSITION_NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDiffUtil_DataEqual_noResultData() {
|
||||
List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
|
||||
testDiffUtil(mDashboardDataWithOneConditions,
|
||||
mDashboardDataWithOneConditions, testResultData);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDiffUtil_InsertOneCondition_ResultDataOneChanged() {
|
||||
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
|
||||
// Item in position 3 is the condition container containing the list of conditions, which
|
||||
// gets 1 more item
|
||||
testResultData.add(new ListUpdateResult.ResultData(
|
||||
ListUpdateResult.ResultData.TYPE_OPERATION_CHANGE, 2, 1));
|
||||
|
||||
testDiffUtil(mDashboardDataWithOneConditions,
|
||||
mDashboardDataWithTwoConditions, testResultData);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDiffUtil_RemoveOneSuggestion_causeItemRemoveAndChange() {
|
||||
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
|
||||
// removed suggestion and the divider
|
||||
testResultData.add(new ListUpdateResult.ResultData(
|
||||
ListUpdateResult.ResultData.TYPE_OPERATION_REMOVE, 0, 2));
|
||||
testResultData.add(new ListUpdateResult.ResultData(
|
||||
ListUpdateResult.ResultData.TYPE_OPERATION_CHANGE, 2, 1));
|
||||
// Build DashboardData
|
||||
final List<ConditionalCard> oneItemConditions = new ArrayList<>();
|
||||
|
||||
oneItemConditions.add(mTestCondition);
|
||||
final List<Suggestion> suggestions = new ArrayList<>();
|
||||
suggestions.add(mTestSuggestion);
|
||||
|
||||
final DashboardData oldData = new DashboardData.Builder()
|
||||
.setConditions(oneItemConditions)
|
||||
.setCategory(mDashboardCategory)
|
||||
.setSuggestions(suggestions)
|
||||
.setConditionExpanded(false)
|
||||
.build();
|
||||
final DashboardData newData = new DashboardData.Builder()
|
||||
.setConditions(oneItemConditions)
|
||||
.setSuggestions(null)
|
||||
.setCategory(mDashboardCategory)
|
||||
.setConditionExpanded(false)
|
||||
.build();
|
||||
|
||||
testDiffUtil(oldData, newData, testResultData);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDiffUtil_DeleteAllData_ResultDataOneDeleted() {
|
||||
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
|
||||
testResultData.add(new ListUpdateResult.ResultData(
|
||||
ListUpdateResult.ResultData.TYPE_OPERATION_REMOVE, 0, 5));
|
||||
|
||||
testDiffUtil(mDashboardDataWithOneConditions, mDashboardDataWithNoItems, testResultData);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDiffUtil_typeSuggestedContainer_ResultDataNothingChanged() {
|
||||
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
|
||||
|
||||
DashboardData prevData = new DashboardData.Builder()
|
||||
.setConditions(null)
|
||||
.setCategory(null)
|
||||
.setSuggestions(Collections.singletonList(mTestSuggestion))
|
||||
.build();
|
||||
DashboardData currentData = new DashboardData.Builder()
|
||||
.setConditions(null)
|
||||
.setCategory(null)
|
||||
.setSuggestions(Collections.singletonList(mTestSuggestion))
|
||||
.build();
|
||||
testDiffUtil(prevData, currentData, testResultData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test when using the
|
||||
* {@link com.android.settings.dashboard.DashboardData.ItemsDataDiffCallback}
|
||||
* to transfer List from {@paramref baseDashboardData} to {@paramref diffDashboardData},
|
||||
* whether
|
||||
* the transform data result is equals to {@paramref testResultData}
|
||||
* <p>
|
||||
* The steps are described below:
|
||||
* 1. Calculate a {@link androidx.recyclerview.widget.DiffUtil.DiffResult} from
|
||||
* {@paramref baseDashboardData} to {@paramref diffDashboardData}
|
||||
* <p>
|
||||
* 2. Dispatch the {@link androidx.recyclerview.widget.DiffUtil.DiffResult} calculated from step
|
||||
* 1
|
||||
* into {@link ListUpdateResult}
|
||||
* <p>
|
||||
* 3. Get result data(a.k.a. baseResultData) from {@link ListUpdateResult} and compare it to
|
||||
* {@paramref testResultData}
|
||||
* <p>
|
||||
* Because baseResultData and {@paramref testResultData} don't have sequence. When do the
|
||||
* comparison, we will sort them first and then compare the inside data from them one by one.
|
||||
*/
|
||||
private void testDiffUtil(DashboardData baseDashboardData, DashboardData diffDashboardData,
|
||||
List<ListUpdateResult.ResultData> testResultData) {
|
||||
final DiffUtil.DiffResult diffUtilResult = DiffUtil.calculateDiff(
|
||||
new DashboardData.ItemsDataDiffCallback(
|
||||
baseDashboardData.getItemList(), diffDashboardData.getItemList()));
|
||||
|
||||
// Dispatch to listUpdateResult, then listUpdateResult will have result data
|
||||
final ListUpdateResult listUpdateResult = new ListUpdateResult();
|
||||
diffUtilResult.dispatchUpdatesTo(listUpdateResult);
|
||||
|
||||
final List<ListUpdateResult.ResultData> baseResultData = listUpdateResult.getResultData();
|
||||
assertThat(testResultData.size()).isEqualTo(baseResultData.size());
|
||||
|
||||
// Sort them so we can compare them one by one using a for loop
|
||||
Collections.sort(baseResultData);
|
||||
Collections.sort(testResultData);
|
||||
final int size = baseResultData.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
// Refer to equals method in ResultData
|
||||
assertThat(baseResultData.get(i)).isEqualTo(testResultData.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class contains the result about how the changes made to convert one
|
||||
* list to another list. It implements ListUpdateCallback to record the result data.
|
||||
*/
|
||||
private static class ListUpdateResult implements ListUpdateCallback {
|
||||
final private List<ResultData> mResultData;
|
||||
|
||||
public ListUpdateResult() {
|
||||
mResultData = new ArrayList<>();
|
||||
}
|
||||
|
||||
private List<ResultData> getResultData() {
|
||||
return mResultData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInserted(int position, int count) {
|
||||
mResultData.add(new ResultData(ResultData.TYPE_OPERATION_INSERT, position, count));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoved(int position, int count) {
|
||||
mResultData.add(new ResultData(ResultData.TYPE_OPERATION_REMOVE, position, count));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMoved(int fromPosition, int toPosition) {
|
||||
mResultData.add(
|
||||
new ResultData(ResultData.TYPE_OPERATION_MOVE, fromPosition, toPosition));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChanged(int position, int count, Object payload) {
|
||||
mResultData.add(new ResultData(ResultData.TYPE_OPERATION_CHANGE, position, count));
|
||||
}
|
||||
|
||||
/**
|
||||
* This class contains general type and field to record the operation data generated
|
||||
* in {@link ListUpdateCallback}. Please refer to {@link ListUpdateCallback} for more info.
|
||||
* <p>
|
||||
* The following are examples about the data stored in this class:
|
||||
* <p>
|
||||
* "The data starts from position(arg1) with count number(arg2) is changed(operation)"
|
||||
* or "The data is moved(operation) from position1(arg1) to position2(arg2)"
|
||||
*/
|
||||
private static class ResultData implements Comparable<ResultData> {
|
||||
|
||||
private static final int TYPE_OPERATION_INSERT = 0;
|
||||
private static final int TYPE_OPERATION_REMOVE = 1;
|
||||
private static final int TYPE_OPERATION_MOVE = 2;
|
||||
private static final int TYPE_OPERATION_CHANGE = 3;
|
||||
|
||||
private final int operation;
|
||||
private final int arg1;
|
||||
private final int arg2;
|
||||
|
||||
private ResultData(int operation, int arg1, int arg2) {
|
||||
this.operation = operation;
|
||||
this.arg1 = arg1;
|
||||
this.arg2 = arg2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj instanceof ResultData)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ResultData targetData = (ResultData) obj;
|
||||
|
||||
return operation == targetData.operation && arg1 == targetData.arg1
|
||||
&& arg2 == targetData.arg2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NonNull ResultData resultData) {
|
||||
if (this.operation != resultData.operation) {
|
||||
return operation - resultData.operation;
|
||||
}
|
||||
|
||||
if (arg1 != resultData.arg1) {
|
||||
return arg1 - resultData.arg1;
|
||||
}
|
||||
|
||||
return arg2 - resultData.arg2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "op:" + operation + ",arg1:" + arg1 + ",arg2:" + arg2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* 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 com.android.settings.dashboard;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settingslib.drawer.CategoryKey;
|
||||
import com.android.settingslib.drawer.Tile;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class DashboardItemAnimatorTest {
|
||||
|
||||
private DashboardItemAnimator mDashboardItemAnimator;
|
||||
private ViewHolder mViewHolder;
|
||||
|
||||
@Before
|
||||
public void SetUp() {
|
||||
mDashboardItemAnimator = new DashboardItemAnimator();
|
||||
mViewHolder = new ViewHolder(new TextView(RuntimeEnvironment.application));
|
||||
final ActivityInfo activityInfo = new ActivityInfo();
|
||||
activityInfo.packageName = "pkg";
|
||||
activityInfo.name = "class";
|
||||
mViewHolder.itemView.setTag(new Tile(activityInfo, CategoryKey.CATEGORY_HOMEPAGE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnimateChange_NoPositionChange_NoPendingAnimation() {
|
||||
final boolean hasPendingAnimation =
|
||||
mDashboardItemAnimator.animateChange(mViewHolder, mViewHolder, 0, 1, 0, 1);
|
||||
assertThat(hasPendingAnimation).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnimateChange_HasPositionChange_HasPendingAnimation() {
|
||||
final boolean hasPendingAnimation =
|
||||
mDashboardItemAnimator.animateChange(mViewHolder, mViewHolder, 0, 0, 1, 1);
|
||||
assertThat(hasPendingAnimation).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnimateChange_HasRunningAnimationWhileNoPositionChange_NoPendingAnimation() {
|
||||
// Set pending move animations
|
||||
mDashboardItemAnimator.animateMove(mViewHolder, 0, 0, 1, 1);
|
||||
|
||||
final boolean hasPendingAnimation =
|
||||
mDashboardItemAnimator.animateChange(mViewHolder, mViewHolder, 0, 1, 0, 1);
|
||||
assertThat(hasPendingAnimation).isFalse();
|
||||
}
|
||||
|
||||
// Sample viewholder to use for test
|
||||
static final class ViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,185 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 com.android.settings.dashboard;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.android.settings.homepage.conditional.ConditionManager;
|
||||
import com.android.settings.homepage.conditional.FocusRecyclerView;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settingslib.drawer.CategoryKey;
|
||||
import com.android.settingslib.drawer.DashboardCategory;
|
||||
import com.android.settingslib.suggestions.SuggestionControllerMixinCompat;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class DashboardSummaryTest {
|
||||
|
||||
@Mock
|
||||
private DashboardAdapter mAdapter;
|
||||
@Mock
|
||||
private DashboardFeatureProvider mDashboardFeatureProvider;
|
||||
@Mock
|
||||
private FocusRecyclerView mDashboard;
|
||||
@Mock
|
||||
private LinearLayoutManager mLayoutManager;
|
||||
@Mock
|
||||
private ConditionManager mConditionManager;
|
||||
@Mock
|
||||
private SummaryLoader mSummaryLoader;
|
||||
@Mock
|
||||
private SuggestionControllerMixinCompat mSuggestionControllerMixin;
|
||||
|
||||
private Context mContext;
|
||||
private DashboardSummary mSummary;
|
||||
private FakeFeatureFactory mFeatureFactory;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mSummary = spy(new DashboardSummary());
|
||||
ReflectionHelpers.setField(mSummary, "mAdapter", mAdapter);
|
||||
ReflectionHelpers.setField(mSummary, "mDashboardFeatureProvider",
|
||||
mDashboardFeatureProvider);
|
||||
ReflectionHelpers.setField(mSummary, "mDashboard", mDashboard);
|
||||
ReflectionHelpers.setField(mSummary, "mLayoutManager", mLayoutManager);
|
||||
ReflectionHelpers.setField(mSummary, "mConditionManager", mConditionManager);
|
||||
ReflectionHelpers.setField(mSummary, "mSummaryLoader", mSummaryLoader);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onAttach_suggestionDisabled_shouldNotStartSuggestionControllerMixin() {
|
||||
when(mFeatureFactory.suggestionsFeatureProvider.isSuggestionEnabled(any(Context.class)))
|
||||
.thenReturn(false);
|
||||
|
||||
mSummary.onAttach(mContext);
|
||||
final SuggestionControllerMixinCompat mixin = ReflectionHelpers
|
||||
.getField(mSummary, "mSuggestionControllerMixin");
|
||||
assertThat(mixin).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onAttach_suggestionEnabled_shouldStartSuggestionControllerMixin() {
|
||||
when(mFeatureFactory.suggestionsFeatureProvider.isSuggestionEnabled(any(Context.class)))
|
||||
.thenReturn(true);
|
||||
|
||||
mSummary.onAttach(mContext);
|
||||
final SuggestionControllerMixinCompat mixin = ReflectionHelpers
|
||||
.getField(mSummary, "mSuggestionControllerMixin");
|
||||
assertThat(mixin).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateCategory_shouldGetCategoryFromFeatureProvider() {
|
||||
ReflectionHelpers.setField(mSummary, "mSuggestionControllerMixin",
|
||||
mSuggestionControllerMixin);
|
||||
|
||||
when(mSuggestionControllerMixin.isSuggestionLoaded()).thenReturn(true);
|
||||
doReturn(mock(FragmentActivity.class)).when(mSummary).getActivity();
|
||||
mSummary.onAttach(mContext);
|
||||
mSummary.updateCategory();
|
||||
|
||||
verify(mSummaryLoader).updateSummaryToCache(nullable(DashboardCategory.class));
|
||||
verify(mDashboardFeatureProvider).getTilesForCategory(CategoryKey.CATEGORY_HOMEPAGE);
|
||||
verify(mAdapter).setCategory(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateCategory_shouldGetCategoryFromFeatureProvider_evenIfSuggestionDisabled() {
|
||||
when(mFeatureFactory.suggestionsFeatureProvider.isSuggestionEnabled(any(Context.class)))
|
||||
.thenReturn(false);
|
||||
|
||||
doReturn(mock(FragmentActivity.class)).when(mSummary).getActivity();
|
||||
mSummary.onAttach(mContext);
|
||||
mSummary.updateCategory();
|
||||
|
||||
verify(mSummaryLoader).updateSummaryToCache(nullable(DashboardCategory.class));
|
||||
verify(mDashboardFeatureProvider).getTilesForCategory(CategoryKey.CATEGORY_HOMEPAGE);
|
||||
verify(mAdapter).setCategory(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onConditionChanged_PositionAtTop_ScrollToTop() {
|
||||
when(mLayoutManager.findFirstCompletelyVisibleItemPosition()).thenReturn(1);
|
||||
mSummary.onConditionsChanged();
|
||||
mSummary.onConditionsChanged();
|
||||
verify(mDashboard).scrollToPosition(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onConditionChanged_PositionNotTop_RemainPosition() {
|
||||
when(mLayoutManager.findFirstCompletelyVisibleItemPosition()).thenReturn(2);
|
||||
mSummary.onConditionsChanged();
|
||||
mSummary.onConditionsChanged();
|
||||
verify(mDashboard, never()).scrollToPosition(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onConditionChanged_firstCall_shouldIgnore() {
|
||||
mSummary.onConditionsChanged();
|
||||
verify(mAdapter, never()).setConditions(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onConditionChanged_secondCall_shouldSetConditionsOnAdapter() {
|
||||
mSummary.onConditionsChanged();
|
||||
mSummary.onConditionsChanged();
|
||||
verify(mAdapter).setConditions(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onCategoryChanged_noRebuildOnFirstCall() {
|
||||
doReturn(mock(FragmentActivity.class)).when(mSummary).getActivity();
|
||||
doNothing().when(mSummary).rebuildUI();
|
||||
mSummary.onCategoriesChanged();
|
||||
verify(mSummary, never()).rebuildUI();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onCategoryChanged_rebuildOnSecondCall() {
|
||||
doReturn(mock(FragmentActivity.class)).when(mSummary).getActivity();
|
||||
doNothing().when(mSummary).rebuildUI();
|
||||
mSummary.onCategoriesChanged();
|
||||
mSummary.onCategoriesChanged();
|
||||
verify(mSummary).rebuildUI();
|
||||
}
|
||||
}
|
@@ -1,345 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 com.android.settings.dashboard.suggestions;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.service.settings.suggestions.Suggestion;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.android.internal.logging.nano.MetricsProto;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.dashboard.DashboardAdapter;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.shadow.ShadowCardView;
|
||||
import com.android.settingslib.Utils;
|
||||
import com.android.settingslib.suggestions.SuggestionControllerMixinCompat;
|
||||
import com.android.settingslib.utils.IconCache;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
@Config(shadows = ShadowCardView.class)
|
||||
public class SuggestionAdapterTest {
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private SettingsActivity mActivity;
|
||||
@Mock
|
||||
private SuggestionControllerMixinCompat mSuggestionControllerMixin;
|
||||
@Mock
|
||||
private Resources mResources;
|
||||
@Mock
|
||||
private WindowManager mWindowManager;
|
||||
|
||||
private FakeFeatureFactory mFeatureFactory;
|
||||
private Context mContext;
|
||||
private SuggestionAdapter mSuggestionAdapter;
|
||||
private DashboardAdapter.DashboardItemHolder mSuggestionHolder;
|
||||
private List<Suggestion> mOneSuggestion;
|
||||
private List<Suggestion> mTwoSuggestions;
|
||||
private SuggestionAdapter.CardConfig mConfig;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
when(mActivity.getSystemService(Context.WINDOW_SERVICE)).thenReturn(mWindowManager);
|
||||
when(mActivity.getResources()).thenReturn(mResources);
|
||||
when(mResources.getDimensionPixelOffset(R.dimen.suggestion_card_inner_margin))
|
||||
.thenReturn(10);
|
||||
when(mResources.getDimensionPixelOffset(R.dimen.suggestion_card_outer_margin))
|
||||
.thenReturn(20);
|
||||
mConfig = spy(SuggestionAdapter.CardConfig.get(mActivity));
|
||||
|
||||
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||
|
||||
final Suggestion suggestion1 = new Suggestion.Builder("id1")
|
||||
.setTitle("Test suggestion 1")
|
||||
.build();
|
||||
final Suggestion suggestion2 = new Suggestion.Builder("id2")
|
||||
.setTitle("Test suggestion 2")
|
||||
.build();
|
||||
mOneSuggestion = new ArrayList<>();
|
||||
mOneSuggestion.add(suggestion1);
|
||||
mTwoSuggestions = new ArrayList<>();
|
||||
mTwoSuggestions.add(suggestion1);
|
||||
mTwoSuggestions.add(suggestion2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getItemCount_shouldReturnListSize() {
|
||||
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
|
||||
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
||||
mSuggestionAdapter.setSuggestions(mOneSuggestion);
|
||||
assertThat(mSuggestionAdapter.getItemCount()).isEqualTo(1);
|
||||
|
||||
mSuggestionAdapter.setSuggestions(mTwoSuggestions);
|
||||
assertThat(mSuggestionAdapter.getItemCount()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getItemViewType_shouldReturnSuggestionTile() {
|
||||
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
|
||||
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
||||
mSuggestionAdapter.setSuggestions(mOneSuggestion);
|
||||
assertThat(mSuggestionAdapter.getItemViewType(0))
|
||||
.isEqualTo(R.layout.suggestion_tile);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getItemType_hasButton_shouldReturnSuggestionWithButton() {
|
||||
final List<Suggestion> suggestions = new ArrayList<>();
|
||||
suggestions.add(new Suggestion.Builder("id")
|
||||
.setFlags(Suggestion.FLAG_HAS_BUTTON)
|
||||
.setTitle("123")
|
||||
.setSummary("456")
|
||||
.build());
|
||||
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
|
||||
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
||||
mSuggestionAdapter.setSuggestions(suggestions);
|
||||
|
||||
assertThat(mSuggestionAdapter.getItemViewType(0))
|
||||
.isEqualTo(R.layout.suggestion_tile_with_button);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBindViewHolder_shouldLog() {
|
||||
final View view = spy(LayoutInflater.from(mContext).inflate(
|
||||
R.layout.suggestion_tile, new LinearLayout(mContext), true));
|
||||
mSuggestionHolder = new DashboardAdapter.DashboardItemHolder(view);
|
||||
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
|
||||
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
||||
mSuggestionAdapter.setSuggestions(mOneSuggestion);
|
||||
doReturn("sans").when(mContext).getString(anyInt());
|
||||
|
||||
// Bind twice
|
||||
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
||||
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
||||
|
||||
// Log once
|
||||
verify(mFeatureFactory.metricsFeatureProvider).action(
|
||||
mContext, MetricsProto.MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
|
||||
mOneSuggestion.get(0).getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBindViewHolder_itemViewShouldHandleClick()
|
||||
throws PendingIntent.CanceledException {
|
||||
final List<Suggestion> suggestions = makeSuggestions("pkg1");
|
||||
setupSuggestions(mActivity, suggestions);
|
||||
|
||||
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
||||
mSuggestionHolder.itemView.performClick();
|
||||
|
||||
verify(mSuggestionControllerMixin).launchSuggestion(suggestions.get(0));
|
||||
verify(suggestions.get(0).getPendingIntent()).send();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBindViewHolder_hasButton_buttonShouldHandleClick()
|
||||
throws PendingIntent.CanceledException {
|
||||
final List<Suggestion> suggestions = new ArrayList<>();
|
||||
final PendingIntent pendingIntent = mock(PendingIntent.class);
|
||||
suggestions.add(new Suggestion.Builder("id")
|
||||
.setFlags(Suggestion.FLAG_HAS_BUTTON)
|
||||
.setTitle("123")
|
||||
.setSummary("456")
|
||||
.setPendingIntent(pendingIntent)
|
||||
.build());
|
||||
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
|
||||
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
||||
mSuggestionAdapter.setSuggestions(suggestions);
|
||||
mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
|
||||
new FrameLayout(RuntimeEnvironment.application),
|
||||
mSuggestionAdapter.getItemViewType(0));
|
||||
doReturn("sans").when(mContext).getString(anyInt());
|
||||
|
||||
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
||||
mSuggestionHolder.itemView.findViewById(android.R.id.primary).performClick();
|
||||
|
||||
verify(mSuggestionControllerMixin).launchSuggestion(suggestions.get(0));
|
||||
verify(pendingIntent).send();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSuggestions_shouldReturnSuggestionWhenMatch() {
|
||||
final List<Suggestion> suggestions = makeSuggestions("pkg1");
|
||||
setupSuggestions(mActivity, suggestions);
|
||||
|
||||
assertThat(mSuggestionAdapter.getSuggestion(0)).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBindViewHolder_closeButtonShouldHandleClick()
|
||||
throws PendingIntent.CanceledException {
|
||||
final List<Suggestion> suggestions = makeSuggestions("pkg1");
|
||||
final SuggestionAdapter.Callback callback = mock(SuggestionAdapter.Callback.class);
|
||||
mSuggestionAdapter = new SuggestionAdapter(mActivity, mSuggestionControllerMixin,
|
||||
null /* savedInstanceState */, callback, null /* lifecycle */);
|
||||
mSuggestionAdapter.setSuggestions(suggestions);
|
||||
mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
|
||||
new FrameLayout(RuntimeEnvironment.application),
|
||||
mSuggestionAdapter.getItemViewType(0));
|
||||
|
||||
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
||||
mSuggestionHolder.itemView.findViewById(R.id.close_button).performClick();
|
||||
|
||||
final Suggestion suggestion = suggestions.get(0);
|
||||
verify(mFeatureFactory.suggestionsFeatureProvider).dismissSuggestion(
|
||||
mActivity, mSuggestionControllerMixin, suggestion);
|
||||
verify(callback).onSuggestionClosed(suggestion);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBindViewHolder_iconNotTintable_shouldNotTintIcon()
|
||||
throws PendingIntent.CanceledException {
|
||||
final Icon icon = mock(Icon.class);
|
||||
final Suggestion suggestion = new Suggestion.Builder("pkg1")
|
||||
.setPendingIntent(mock(PendingIntent.class))
|
||||
.setIcon(icon)
|
||||
.build();
|
||||
final List<Suggestion> suggestions = new ArrayList<>();
|
||||
suggestions.add(suggestion);
|
||||
mSuggestionAdapter = new SuggestionAdapter(mActivity, mSuggestionControllerMixin,
|
||||
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
||||
mSuggestionAdapter.setSuggestions(suggestions);
|
||||
mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
|
||||
new FrameLayout(RuntimeEnvironment.application),
|
||||
mSuggestionAdapter.getItemViewType(0));
|
||||
IconCache cache = mock(IconCache.class);
|
||||
final Drawable drawable = mock(Drawable.class);
|
||||
when(cache.getIcon(icon)).thenReturn(drawable);
|
||||
ReflectionHelpers.setField(mSuggestionAdapter, "mCache", cache);
|
||||
|
||||
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
||||
|
||||
verify(drawable, never()).setTint(anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBindViewHolder_iconTintable_shouldTintIcon()
|
||||
throws PendingIntent.CanceledException {
|
||||
final Icon icon = mock(Icon.class);
|
||||
final int FLAG_ICON_TINTABLE = 1 << 1;
|
||||
final Suggestion suggestion = new Suggestion.Builder("pkg1")
|
||||
.setPendingIntent(mock(PendingIntent.class))
|
||||
.setIcon(icon)
|
||||
.setFlags(FLAG_ICON_TINTABLE)
|
||||
.build();
|
||||
final List<Suggestion> suggestions = new ArrayList<>();
|
||||
suggestions.add(suggestion);
|
||||
mSuggestionAdapter = new SuggestionAdapter(mActivity, mSuggestionControllerMixin,
|
||||
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
||||
mSuggestionAdapter.setSuggestions(suggestions);
|
||||
mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
|
||||
new FrameLayout(RuntimeEnvironment.application),
|
||||
mSuggestionAdapter.getItemViewType(0));
|
||||
IconCache cache = mock(IconCache.class);
|
||||
final Drawable drawable = mock(Drawable.class);
|
||||
when(cache.getIcon(icon)).thenReturn(drawable);
|
||||
ReflectionHelpers.setField(mSuggestionAdapter, "mCache", cache);
|
||||
TypedArray typedArray = mock(TypedArray.class);
|
||||
final ColorStateList colorAccentState = Utils.getColorAccent(mContext);
|
||||
when(mActivity.obtainStyledAttributes(any())).thenReturn(typedArray);
|
||||
when(typedArray.getColorStateList(anyInt())).thenReturn(colorAccentState);
|
||||
|
||||
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
||||
|
||||
verify(drawable).setTintList(colorAccentState);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBindViewHolder_closeButtonShouldHaveContentDescription()
|
||||
throws PendingIntent.CanceledException {
|
||||
final List<Suggestion> suggestions = makeSuggestions("pkg1");
|
||||
setupSuggestions(mActivity, suggestions);
|
||||
|
||||
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
||||
|
||||
assertThat(
|
||||
mSuggestionHolder.itemView.findViewById(R.id.close_button).getContentDescription())
|
||||
.isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setCardLayout_twoCards_shouldSetCardWidthToHalfScreenMinusPadding() {
|
||||
final List<Suggestion> suggestions = makeSuggestions("pkg1");
|
||||
setupSuggestions(mContext, suggestions);
|
||||
doReturn(200).when(mConfig).getScreenWidth();
|
||||
|
||||
mConfig.setCardLayout(mSuggestionHolder, 0);
|
||||
|
||||
/*
|
||||
* card width = (screen width - left margin - inner margin - right margin) / 2
|
||||
* = (200 - 20 - 10 - 20) / 2
|
||||
* = 75
|
||||
*/
|
||||
assertThat(mSuggestionHolder.itemView.getLayoutParams().width).isEqualTo(75);
|
||||
}
|
||||
|
||||
private void setupSuggestions(Context context, List<Suggestion> suggestions) {
|
||||
mSuggestionAdapter = new SuggestionAdapter(context, mSuggestionControllerMixin,
|
||||
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
||||
mSuggestionAdapter.setSuggestions(suggestions);
|
||||
mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
|
||||
new FrameLayout(RuntimeEnvironment.application),
|
||||
mSuggestionAdapter.getItemViewType(0));
|
||||
}
|
||||
|
||||
private List<Suggestion> makeSuggestions(String... pkgNames) {
|
||||
final List<Suggestion> suggestions = new ArrayList<>();
|
||||
for (String pkgName : pkgNames) {
|
||||
final Suggestion suggestion = new Suggestion.Builder(pkgName)
|
||||
.setPendingIntent(mock(PendingIntent.class))
|
||||
.build();
|
||||
suggestions.add(suggestion);
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user