Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 39dcad9b authored by Yi-Ling Chuang's avatar Yi-Ling Chuang
Browse files

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
parent 4eddb1b1
Loading
Loading
Loading
Loading
+55 −4
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import androidx.loader.content.Loader;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;

import java.util.ArrayList;
import java.util.List;
@@ -56,7 +57,9 @@ import java.util.stream.Collectors;
 * get the page refreshed.
 */
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";

@@ -68,6 +71,9 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
    final List<ContextualCard> mContextualCards;
    @VisibleForTesting
    long mStartTime;
    boolean mIsFirstLaunch;
    @VisibleForTesting
    List<String> mSavedCards;

    private final Context mContext;
    private final ControllerRendererPool mControllerRendererPool;
@@ -76,12 +82,20 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo

    private ContextualCardUpdateListener mListener;

    public ContextualCardManager(Context context, Lifecycle lifecycle) {
    public ContextualCardManager(Context context, Lifecycle lifecycle, Bundle savedInstanceState) {
        mContext = context;
        mLifecycle = lifecycle;
        mContextualCards = new ArrayList<>();
        mLifecycleObservers = new ArrayList<>();
        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 (@ContextualCard.CardType int cardType : SETTINGS_CARDS) {
            setupController(cardType);
@@ -172,13 +186,34 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
    @Override
    public void onFinishCardLoading(List<ContextualCard> cards) {
        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) {
            onContextualCardUpdated(
                    cards.stream().collect(groupingBy(ContextualCard::getCardType)));
            onContextualCardUpdated(cards.stream()
                    .collect(groupingBy(ContextualCard::getCardType)));
        }
        final long totalTime = System.currentTimeMillis() - mStartTime;
        FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider()
                .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() {
@@ -189,6 +224,22 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
        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
            LoaderManager.LoaderCallbacks<List<ContextualCard>> {

+2 −1
Original line number Diff line number Diff line
@@ -42,7 +42,8 @@ public class ContextualCardsFragment extends InstrumentedFragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mContextualCardManager = new ContextualCardManager(getContext(), getSettingsLifecycle());
        mContextualCardManager = new ContextualCardManager(getContext(), getSettingsLifecycle(),
                savedInstanceState);
    }

    @Override
+71 −2
Original line number Diff line number Diff line
@@ -42,13 +42,16 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@RunWith(RobolectricTestRunner.class)
public class ContextualCardManagerTest {

    private static final String TEST_SLICE_URI = "context://test/test";
    private static final String TEST_SLICE_NAME = "test_name";

    @Mock
    ContextualCardUpdateListener mListener;
@@ -61,7 +64,8 @@ public class ContextualCardManagerTest {
        MockitoAnnotations.initMocks(this);
        mContext = RuntimeEnvironment.application;
        final ContextualCardsFragment fragment = new ContextualCardsFragment();
        mManager = new ContextualCardManager(mContext, fragment.getSettingsLifecycle());
        mManager = new ContextualCardManager(mContext, fragment.getSettingsLifecycle(),
                null /* bundle */);
    }

    @Test
@@ -135,9 +139,74 @@ public class ContextualCardManagerTest {
        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) {
        return new ContextualCard.Builder()
                .setName("test_name")
                .setName(TEST_SLICE_NAME)
                .setCardType(ContextualCard.CardType.SLICE)
                .setSliceUri(Uri.parse(sliceUri))
                .build();
    }