Don't have cards auto-filled in the foreground when a card gets dismissed.
Currently if users dismiss a card or navigate back to the homepage, new suggestions will be auto filled in. We should only add new cards upon next visit. Also fix suggestion cards undismissable problem. Fixes: 121175037 Bug: 120628661 Test: robotests Change-Id: I01d74aaaa21c8408e5cecafef04a7d52c97bccc5
This commit is contained in:
@@ -35,6 +35,7 @@ import androidx.loader.content.Loader;
|
|||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||||
|
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -56,7 +57,9 @@ import java.util.stream.Collectors;
|
|||||||
* get the page refreshed.
|
* get the page refreshed.
|
||||||
*/
|
*/
|
||||||
public class ContextualCardManager implements ContextualCardLoader.CardContentLoaderListener,
|
public class ContextualCardManager implements ContextualCardLoader.CardContentLoaderListener,
|
||||||
ContextualCardUpdateListener {
|
ContextualCardUpdateListener, LifecycleObserver, OnSaveInstanceState {
|
||||||
|
|
||||||
|
private static final String KEY_CONTEXTUAL_CARDS = "key_contextual_cards";
|
||||||
|
|
||||||
private static final String TAG = "ContextualCardManager";
|
private static final String TAG = "ContextualCardManager";
|
||||||
|
|
||||||
@@ -68,6 +71,9 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
|
|||||||
final List<ContextualCard> mContextualCards;
|
final List<ContextualCard> mContextualCards;
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
long mStartTime;
|
long mStartTime;
|
||||||
|
boolean mIsFirstLaunch;
|
||||||
|
@VisibleForTesting
|
||||||
|
List<String> mSavedCards;
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final ControllerRendererPool mControllerRendererPool;
|
private final ControllerRendererPool mControllerRendererPool;
|
||||||
@@ -76,12 +82,20 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
|
|||||||
|
|
||||||
private ContextualCardUpdateListener mListener;
|
private ContextualCardUpdateListener mListener;
|
||||||
|
|
||||||
public ContextualCardManager(Context context, Lifecycle lifecycle) {
|
public ContextualCardManager(Context context, Lifecycle lifecycle, Bundle savedInstanceState) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mLifecycle = lifecycle;
|
mLifecycle = lifecycle;
|
||||||
mContextualCards = new ArrayList<>();
|
mContextualCards = new ArrayList<>();
|
||||||
mLifecycleObservers = new ArrayList<>();
|
mLifecycleObservers = new ArrayList<>();
|
||||||
mControllerRendererPool = new ControllerRendererPool();
|
mControllerRendererPool = new ControllerRendererPool();
|
||||||
|
mLifecycle.addObserver(this);
|
||||||
|
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
mIsFirstLaunch = true;
|
||||||
|
mSavedCards = null;
|
||||||
|
} else {
|
||||||
|
mSavedCards = savedInstanceState.getStringArrayList(KEY_CONTEXTUAL_CARDS);
|
||||||
|
}
|
||||||
//for data provided by Settings
|
//for data provided by Settings
|
||||||
for (@ContextualCard.CardType int cardType : SETTINGS_CARDS) {
|
for (@ContextualCard.CardType int cardType : SETTINGS_CARDS) {
|
||||||
setupController(cardType);
|
setupController(cardType);
|
||||||
@@ -172,13 +186,34 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
|
|||||||
@Override
|
@Override
|
||||||
public void onFinishCardLoading(List<ContextualCard> cards) {
|
public void onFinishCardLoading(List<ContextualCard> cards) {
|
||||||
final long loadTime = System.currentTimeMillis() - mStartTime;
|
final long loadTime = System.currentTimeMillis() - mStartTime;
|
||||||
|
final List<ContextualCard> cardsToKeep = getCardsToKeep(cards);
|
||||||
|
|
||||||
|
//navigate back to the homepage or after card dismissal
|
||||||
|
if (!mIsFirstLaunch) {
|
||||||
|
onContextualCardUpdated(cardsToKeep.stream()
|
||||||
|
.collect(groupingBy(ContextualCard::getCardType)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//only log homepage display upon a fresh launch
|
||||||
if (loadTime <= ContextualCardLoader.CARD_CONTENT_LOADER_TIMEOUT_MS) {
|
if (loadTime <= ContextualCardLoader.CARD_CONTENT_LOADER_TIMEOUT_MS) {
|
||||||
onContextualCardUpdated(
|
onContextualCardUpdated(cards.stream()
|
||||||
cards.stream().collect(groupingBy(ContextualCard::getCardType)));
|
.collect(groupingBy(ContextualCard::getCardType)));
|
||||||
}
|
}
|
||||||
final long totalTime = System.currentTimeMillis() - mStartTime;
|
final long totalTime = System.currentTimeMillis() - mStartTime;
|
||||||
FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider()
|
FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider()
|
||||||
.logHomepageDisplay(mContext, totalTime);
|
.logHomepageDisplay(mContext, totalTime);
|
||||||
|
|
||||||
|
mIsFirstLaunch = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
|
final ArrayList<String> cards = mContextualCards.stream()
|
||||||
|
.map(ContextualCard::getName)
|
||||||
|
.collect(Collectors.toCollection(ArrayList::new));
|
||||||
|
|
||||||
|
outState.putStringArrayList(KEY_CONTEXTUAL_CARDS, cards);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ControllerRendererPool getControllerRendererPool() {
|
public ControllerRendererPool getControllerRendererPool() {
|
||||||
@@ -189,6 +224,22 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
|
|||||||
mListener = listener;
|
mListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<ContextualCard> getCardsToKeep(List<ContextualCard> cards) {
|
||||||
|
if (mSavedCards != null) {
|
||||||
|
//screen rotate
|
||||||
|
final List<ContextualCard> cardsToKeep = cards.stream()
|
||||||
|
.filter(card -> mSavedCards.contains(card.getName()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
mSavedCards = null;
|
||||||
|
return cardsToKeep;
|
||||||
|
} else {
|
||||||
|
//navigate back to the homepage or after dismissing a card
|
||||||
|
return cards.stream()
|
||||||
|
.filter(card -> mContextualCards.contains(card))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static class CardContentLoaderCallbacks implements
|
static class CardContentLoaderCallbacks implements
|
||||||
LoaderManager.LoaderCallbacks<List<ContextualCard>> {
|
LoaderManager.LoaderCallbacks<List<ContextualCard>> {
|
||||||
|
|
||||||
|
@@ -42,7 +42,8 @@ public class ContextualCardsFragment extends InstrumentedFragment {
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
mContextualCardManager = new ContextualCardManager(getContext(), getSettingsLifecycle());
|
mContextualCardManager = new ContextualCardManager(getContext(), getSettingsLifecycle(),
|
||||||
|
savedInstanceState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -42,13 +42,16 @@ import org.robolectric.RobolectricTestRunner;
|
|||||||
import org.robolectric.RuntimeEnvironment;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(RobolectricTestRunner.class)
|
||||||
public class ContextualCardManagerTest {
|
public class ContextualCardManagerTest {
|
||||||
|
|
||||||
private static final String TEST_SLICE_URI = "context://test/test";
|
private static final String TEST_SLICE_URI = "context://test/test";
|
||||||
|
private static final String TEST_SLICE_NAME = "test_name";
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
ContextualCardUpdateListener mListener;
|
ContextualCardUpdateListener mListener;
|
||||||
@@ -61,7 +64,8 @@ public class ContextualCardManagerTest {
|
|||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mContext = RuntimeEnvironment.application;
|
mContext = RuntimeEnvironment.application;
|
||||||
final ContextualCardsFragment fragment = new ContextualCardsFragment();
|
final ContextualCardsFragment fragment = new ContextualCardsFragment();
|
||||||
mManager = new ContextualCardManager(mContext, fragment.getSettingsLifecycle());
|
mManager = new ContextualCardManager(mContext, fragment.getSettingsLifecycle(),
|
||||||
|
null /* bundle */);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -135,9 +139,74 @@ public class ContextualCardManagerTest {
|
|||||||
verify(manager, never()).onContextualCardUpdated(anyMap());
|
verify(manager, never()).onContextualCardUpdated(anyMap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onFinishCardLoading_newLaunch_twoLoadedCards_shouldShowTwoCards() {
|
||||||
|
mManager.mStartTime = System.currentTimeMillis();
|
||||||
|
mManager.setListener(mListener);
|
||||||
|
final List<ContextualCard> cards = new ArrayList<>();
|
||||||
|
cards.add(buildContextualCard(TEST_SLICE_URI));
|
||||||
|
cards.add(buildContextualCard(TEST_SLICE_URI));
|
||||||
|
|
||||||
|
mManager.onFinishCardLoading(cards);
|
||||||
|
|
||||||
|
assertThat(mManager.mContextualCards).hasSize(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onFinishCardLoading_hasSavedCard_shouldOnlyShowSavedCard() {
|
||||||
|
mManager.setListener(mListener);
|
||||||
|
final List<String> savedCardNames = new ArrayList<>();
|
||||||
|
savedCardNames.add(TEST_SLICE_NAME);
|
||||||
|
mManager.mIsFirstLaunch = false;
|
||||||
|
mManager.mSavedCards = savedCardNames;
|
||||||
|
final ContextualCard newCard =
|
||||||
|
new ContextualCard.Builder()
|
||||||
|
.setName("test_name2")
|
||||||
|
.setCardType(ContextualCard.CardType.SLICE)
|
||||||
|
.setSliceUri(Uri.parse("content://test/test2"))
|
||||||
|
.build();
|
||||||
|
final List<ContextualCard> loadedCards = new ArrayList<>();
|
||||||
|
loadedCards.add(buildContextualCard(TEST_SLICE_URI));
|
||||||
|
loadedCards.add(newCard);
|
||||||
|
|
||||||
|
mManager.onFinishCardLoading(loadedCards);
|
||||||
|
|
||||||
|
final List<String> actualCards = mManager.mContextualCards.stream()
|
||||||
|
.map(ContextualCard::getName)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
final List<String> expectedCards = Arrays.asList(TEST_SLICE_NAME);
|
||||||
|
assertThat(actualCards).containsExactlyElementsIn(expectedCards);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onFinishCardLoading_reloadData_shouldOnlyShowOldCard() {
|
||||||
|
mManager.setListener(mListener);
|
||||||
|
mManager.mIsFirstLaunch = false;
|
||||||
|
//old card
|
||||||
|
mManager.mContextualCards.add(buildContextualCard(TEST_SLICE_URI));
|
||||||
|
final ContextualCard newCard =
|
||||||
|
new ContextualCard.Builder()
|
||||||
|
.setName("test_name2")
|
||||||
|
.setCardType(ContextualCard.CardType.SLICE)
|
||||||
|
.setSliceUri(Uri.parse("content://test/test2"))
|
||||||
|
.build();
|
||||||
|
final List<ContextualCard> loadedCards = new ArrayList<>();
|
||||||
|
loadedCards.add(buildContextualCard(TEST_SLICE_URI));
|
||||||
|
loadedCards.add(newCard);
|
||||||
|
|
||||||
|
mManager.onFinishCardLoading(loadedCards);
|
||||||
|
|
||||||
|
final List<String> actualCards = mManager.mContextualCards.stream()
|
||||||
|
.map(ContextualCard::getName)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
final List<String> expectedCards = Arrays.asList(TEST_SLICE_NAME);
|
||||||
|
assertThat(actualCards).containsExactlyElementsIn(expectedCards);
|
||||||
|
}
|
||||||
|
|
||||||
private ContextualCard buildContextualCard(String sliceUri) {
|
private ContextualCard buildContextualCard(String sliceUri) {
|
||||||
return new ContextualCard.Builder()
|
return new ContextualCard.Builder()
|
||||||
.setName("test_name")
|
.setName(TEST_SLICE_NAME)
|
||||||
|
.setCardType(ContextualCard.CardType.SLICE)
|
||||||
.setSliceUri(Uri.parse(sliceUri))
|
.setSliceUri(Uri.parse(sliceUri))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user