Loading src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImpl.java +31 −6 Original line number Diff line number Diff line Loading @@ -16,8 +16,10 @@ package com.android.settings.homepage.contextualcards; import android.annotation.NonNull; import android.content.Context; import android.content.Intent; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; Loading @@ -25,6 +27,7 @@ import androidx.annotation.VisibleForTesting; import androidx.slice.widget.EventInfo; import com.android.settings.R; import com.android.settings.intelligence.ContextualCardProto.ContextualCardList; import java.util.List; Loading @@ -38,10 +41,6 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP // Contextual card shows, log card name and rank private static final int CONTEXTUAL_CARD_SHOW = 39; // Contextual card is eligible to be shown, but doesn't rank high // enough, log card name and score private static final int CONTEXTUAL_CARD_NOT_SHOW = 40; // Contextual card is dismissed, log card name private static final int CONTEXTUAL_CARD_DISMISS = 41; Loading @@ -67,6 +66,11 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP // log type private static final String EXTRA_CONTEXTUALCARD_ACTION_TYPE = "type"; // displayed contextual cards private static final String EXTRA_CONTEXTUALCARD_VISIBLE = "visible"; // hidden contextual cards private static final String EXTRA_CONTEXTUALCARD_HIDDEN = "hidden"; // Contextual card tap target private static final int TARGET_DEFAULT = 0; Loading @@ -82,6 +86,10 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP @Override public void logHomepageDisplay(Context context, Long latency) { final Intent intent = new Intent(); intent.putExtra(EXTRA_CONTEXTUALCARD_ACTION_TYPE, CONTEXTUAL_HOME_SHOW); intent.putExtra(EXTRA_LATENCY, latency); sendBroadcast(context, intent); } @Override Loading @@ -94,8 +102,13 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP } @Override public void logContextualCardDisplay(Context context, List<ContextualCard> showCards, public void logContextualCardDisplay(Context context, List<ContextualCard> visibleCards, List<ContextualCard> hiddenCards) { final Intent intent = new Intent(); intent.putExtra(EXTRA_CONTEXTUALCARD_ACTION_TYPE, CONTEXTUAL_CARD_SHOW); intent.putExtra(EXTRA_CONTEXTUALCARD_VISIBLE, serialize(visibleCards)); intent.putExtra(EXTRA_CONTEXTUALCARD_HIDDEN, serialize(hiddenCards)); sendBroadcast(context, intent); } @Override Loading @@ -116,7 +129,7 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP final String action = context.getString(R.string.config_settingsintelligence_log_action); if (!TextUtils.isEmpty(action)) { intent.setAction(action); context.sendBroadcast(intent); context.sendBroadcastAsUser(intent, UserHandle.ALL); } } Loading @@ -133,4 +146,16 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP return TARGET_DEFAULT; } } @VisibleForTesting @NonNull byte[] serialize(List<ContextualCard> cards) { final ContextualCardList.Builder builder = ContextualCardList.newBuilder(); cards.stream().forEach(card -> builder.addCard( com.android.settings.intelligence.ContextualCardProto.ContextualCard.newBuilder() .setSliceUri(card.getSliceUri().toString()) .setCardName(card.getName()) .build())); return builder.build().toByteArray(); } } src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java +43 −17 Original line number Diff line number Diff line Loading @@ -20,6 +20,9 @@ import static android.app.slice.Slice.HINT_ERROR; import static androidx.slice.widget.SliceLiveData.SUPPORTED_SPECS; import static com.android.settings.slices.CustomSliceRegistry.CONNECTED_DEVICE_SLICE_URI; import static com.android.settings.slices.CustomSliceRegistry.WIFI_SLICE_URI; import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.Context; Loading @@ -34,7 +37,7 @@ import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.slice.Slice; import com.android.settings.slices.CustomSliceRegistry; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.utils.AsyncLoaderCompat; import java.util.ArrayList; Loading Loading @@ -103,27 +106,50 @@ public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard> return getFinalDisplayableCards(result); } // Get final displayed cards and log what cards will be displayed/hidden @VisibleForTesting List<ContextualCard> getFinalDisplayableCards(List<ContextualCard> candidates) { List<ContextualCard> eligibleCards = filterEligibleCards(candidates); eligibleCards = eligibleCards.stream().limit(DEFAULT_CARD_COUNT).collect( Collectors.toList()); final List<ContextualCard> eligibleCards = filterEligibleCards(candidates); final List<ContextualCard> visibleCards = new ArrayList<>(); final List<ContextualCard> hiddenCards = new ArrayList<>(); final int size = eligibleCards.size(); for (int i = 0; i < size; i++) { if (i < DEFAULT_CARD_COUNT) { visibleCards.add(eligibleCards.get(i)); } else { hiddenCards.add(eligibleCards.get(i)); } } if (eligibleCards.size() <= 2 || getNumberOfLargeCard(eligibleCards) == 0) { return eligibleCards; try { // The maximum cards are four small cards OR // one large card with two small cards OR // two large cards if (visibleCards.size() <= 2 || getNumberOfLargeCard(visibleCards) == 0) { // four small cards return visibleCards; } if (eligibleCards.size() == DEFAULT_CARD_COUNT) { eligibleCards.remove(eligibleCards.size() - 1); if (visibleCards.size() == DEFAULT_CARD_COUNT) { hiddenCards.add(visibleCards.remove(visibleCards.size() - 1)); } if (getNumberOfLargeCard(eligibleCards) == 1) { return eligibleCards; if (getNumberOfLargeCard(visibleCards) == 1) { // One large card with two small cards return visibleCards; } eligibleCards.remove(eligibleCards.size() - 1); hiddenCards.add(visibleCards.remove(visibleCards.size() - 1)); return eligibleCards; // Two large cards return visibleCards; } finally { final ContextualCardFeatureProvider contextualCardFeatureProvider = FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider(); contextualCardFeatureProvider.logContextualCardDisplay(mContext, visibleCards, hiddenCards); } } @VisibleForTesting Loading Loading @@ -169,8 +195,8 @@ public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard> private int getNumberOfLargeCard(List<ContextualCard> cards) { return (int) cards.stream() .filter(card -> card.getSliceUri().equals(CustomSliceRegistry.WIFI_SLICE_URI) || card.getSliceUri().equals(CustomSliceRegistry.CONNECTED_DEVICE_SLICE_URI)) .filter(card -> card.getSliceUri().equals(WIFI_SLICE_URI) || card.getSliceUri().equals(CONNECTED_DEVICE_SLICE_URI)) .count(); } Loading src/com/android/settings/homepage/contextualcards/ContextualCardManager.java +8 −2 Original line number Diff line number Diff line Loading @@ -16,8 +16,7 @@ package com.android.settings.homepage.contextualcards; import static com.android.settings.homepage.contextualcards.ContextualCardLoader .CARD_CONTENT_LOADER_ID; import static com.android.settings.homepage.contextualcards.ContextualCardLoader.CARD_CONTENT_LOADER_ID; import static java.util.stream.Collectors.groupingBy; Loading @@ -33,6 +32,7 @@ import androidx.annotation.VisibleForTesting; import androidx.loader.app.LoaderManager; 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; Loading Loading @@ -72,6 +72,7 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo private final List<LifecycleObserver> mLifecycleObservers; private ContextualCardUpdateListener mListener; private long mStartTime; public ContextualCardManager(Context context, Lifecycle lifecycle) { mContext = context; Loading @@ -86,6 +87,7 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo } void loadContextualCards(ContextualCardsFragment fragment) { mStartTime = System.currentTimeMillis(); final CardContentLoaderCallbacks cardContentLoaderCallbacks = new CardContentLoaderCallbacks(mContext); cardContentLoaderCallbacks.setListener(this); Loading Loading @@ -168,6 +170,10 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo @Override public void onFinishCardLoading(List<ContextualCard> cards) { onContextualCardUpdated(cards.stream().collect(groupingBy(ContextualCard::getCardType))); final long elapsedTime = System.currentTimeMillis() - mStartTime; final ContextualCardFeatureProvider contextualCardFeatureProvider = FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider(); contextualCardFeatureProvider.logHomepageDisplay(mContext, elapsedTime); } public ControllerRendererPool getControllerRendererPool() { Loading tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImplTest.java +43 −2 Original line number Diff line number Diff line Loading @@ -16,13 +16,20 @@ package com.android.settings.homepage.contextualcards; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Matchers.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.UserHandle; import com.android.settings.intelligence.ContextualCardProto.ContextualCardList; import com.android.settings.testutils.SettingsRobolectricTestRunner; import org.junit.Before; Loading @@ -31,6 +38,9 @@ import org.junit.runner.RunWith; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import java.util.ArrayList; import java.util.List; @RunWith(SettingsRobolectricTestRunner.class) public class ContextualCardFeatureProviderImplTest { Loading @@ -48,7 +58,7 @@ public class ContextualCardFeatureProviderImplTest { final Intent intent = new Intent(); mImpl.sendBroadcast(mContext, intent); verify(mContext, never()).sendBroadcast(intent); verify(mContext, never()).sendBroadcastAsUser(intent, UserHandle.ALL); } @Test Loading @@ -57,6 +67,37 @@ public class ContextualCardFeatureProviderImplTest { final Intent intent = new Intent(); mImpl.sendBroadcast(mContext, intent); verify(mContext).sendBroadcast(intent); verify(mContext).sendBroadcastAsUser(intent, UserHandle.ALL); } @Test @Config(qualifiers = "mcc999") public void logContextualCardDisplay_hasAction_sendBroadcast() { mImpl.logContextualCardDisplay(mContext, new ArrayList<>(), new ArrayList<>()); verify(mContext).sendBroadcastAsUser(any(Intent.class), any()); } @Test public void serialize_hasSizeTwo_returnSizeTwo() { final List<ContextualCard> cards = new ArrayList<>(); cards.add(new ContextualCard.Builder() .setName("name1") .setSliceUri(Uri.parse("uri1")) .build()); cards.add(new ContextualCard.Builder() .setName("name2") .setSliceUri(Uri.parse("uri2")) .build()); final byte[] data = mImpl.serialize(cards); try { assertThat(ContextualCardList .parseFrom(data).getCardCount()).isEqualTo(cards.size()); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } } } No newline at end of file Loading
src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImpl.java +31 −6 Original line number Diff line number Diff line Loading @@ -16,8 +16,10 @@ package com.android.settings.homepage.contextualcards; import android.annotation.NonNull; import android.content.Context; import android.content.Intent; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; Loading @@ -25,6 +27,7 @@ import androidx.annotation.VisibleForTesting; import androidx.slice.widget.EventInfo; import com.android.settings.R; import com.android.settings.intelligence.ContextualCardProto.ContextualCardList; import java.util.List; Loading @@ -38,10 +41,6 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP // Contextual card shows, log card name and rank private static final int CONTEXTUAL_CARD_SHOW = 39; // Contextual card is eligible to be shown, but doesn't rank high // enough, log card name and score private static final int CONTEXTUAL_CARD_NOT_SHOW = 40; // Contextual card is dismissed, log card name private static final int CONTEXTUAL_CARD_DISMISS = 41; Loading @@ -67,6 +66,11 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP // log type private static final String EXTRA_CONTEXTUALCARD_ACTION_TYPE = "type"; // displayed contextual cards private static final String EXTRA_CONTEXTUALCARD_VISIBLE = "visible"; // hidden contextual cards private static final String EXTRA_CONTEXTUALCARD_HIDDEN = "hidden"; // Contextual card tap target private static final int TARGET_DEFAULT = 0; Loading @@ -82,6 +86,10 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP @Override public void logHomepageDisplay(Context context, Long latency) { final Intent intent = new Intent(); intent.putExtra(EXTRA_CONTEXTUALCARD_ACTION_TYPE, CONTEXTUAL_HOME_SHOW); intent.putExtra(EXTRA_LATENCY, latency); sendBroadcast(context, intent); } @Override Loading @@ -94,8 +102,13 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP } @Override public void logContextualCardDisplay(Context context, List<ContextualCard> showCards, public void logContextualCardDisplay(Context context, List<ContextualCard> visibleCards, List<ContextualCard> hiddenCards) { final Intent intent = new Intent(); intent.putExtra(EXTRA_CONTEXTUALCARD_ACTION_TYPE, CONTEXTUAL_CARD_SHOW); intent.putExtra(EXTRA_CONTEXTUALCARD_VISIBLE, serialize(visibleCards)); intent.putExtra(EXTRA_CONTEXTUALCARD_HIDDEN, serialize(hiddenCards)); sendBroadcast(context, intent); } @Override Loading @@ -116,7 +129,7 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP final String action = context.getString(R.string.config_settingsintelligence_log_action); if (!TextUtils.isEmpty(action)) { intent.setAction(action); context.sendBroadcast(intent); context.sendBroadcastAsUser(intent, UserHandle.ALL); } } Loading @@ -133,4 +146,16 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP return TARGET_DEFAULT; } } @VisibleForTesting @NonNull byte[] serialize(List<ContextualCard> cards) { final ContextualCardList.Builder builder = ContextualCardList.newBuilder(); cards.stream().forEach(card -> builder.addCard( com.android.settings.intelligence.ContextualCardProto.ContextualCard.newBuilder() .setSliceUri(card.getSliceUri().toString()) .setCardName(card.getName()) .build())); return builder.build().toByteArray(); } }
src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java +43 −17 Original line number Diff line number Diff line Loading @@ -20,6 +20,9 @@ import static android.app.slice.Slice.HINT_ERROR; import static androidx.slice.widget.SliceLiveData.SUPPORTED_SPECS; import static com.android.settings.slices.CustomSliceRegistry.CONNECTED_DEVICE_SLICE_URI; import static com.android.settings.slices.CustomSliceRegistry.WIFI_SLICE_URI; import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.Context; Loading @@ -34,7 +37,7 @@ import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.slice.Slice; import com.android.settings.slices.CustomSliceRegistry; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.utils.AsyncLoaderCompat; import java.util.ArrayList; Loading Loading @@ -103,27 +106,50 @@ public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard> return getFinalDisplayableCards(result); } // Get final displayed cards and log what cards will be displayed/hidden @VisibleForTesting List<ContextualCard> getFinalDisplayableCards(List<ContextualCard> candidates) { List<ContextualCard> eligibleCards = filterEligibleCards(candidates); eligibleCards = eligibleCards.stream().limit(DEFAULT_CARD_COUNT).collect( Collectors.toList()); final List<ContextualCard> eligibleCards = filterEligibleCards(candidates); final List<ContextualCard> visibleCards = new ArrayList<>(); final List<ContextualCard> hiddenCards = new ArrayList<>(); final int size = eligibleCards.size(); for (int i = 0; i < size; i++) { if (i < DEFAULT_CARD_COUNT) { visibleCards.add(eligibleCards.get(i)); } else { hiddenCards.add(eligibleCards.get(i)); } } if (eligibleCards.size() <= 2 || getNumberOfLargeCard(eligibleCards) == 0) { return eligibleCards; try { // The maximum cards are four small cards OR // one large card with two small cards OR // two large cards if (visibleCards.size() <= 2 || getNumberOfLargeCard(visibleCards) == 0) { // four small cards return visibleCards; } if (eligibleCards.size() == DEFAULT_CARD_COUNT) { eligibleCards.remove(eligibleCards.size() - 1); if (visibleCards.size() == DEFAULT_CARD_COUNT) { hiddenCards.add(visibleCards.remove(visibleCards.size() - 1)); } if (getNumberOfLargeCard(eligibleCards) == 1) { return eligibleCards; if (getNumberOfLargeCard(visibleCards) == 1) { // One large card with two small cards return visibleCards; } eligibleCards.remove(eligibleCards.size() - 1); hiddenCards.add(visibleCards.remove(visibleCards.size() - 1)); return eligibleCards; // Two large cards return visibleCards; } finally { final ContextualCardFeatureProvider contextualCardFeatureProvider = FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider(); contextualCardFeatureProvider.logContextualCardDisplay(mContext, visibleCards, hiddenCards); } } @VisibleForTesting Loading Loading @@ -169,8 +195,8 @@ public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard> private int getNumberOfLargeCard(List<ContextualCard> cards) { return (int) cards.stream() .filter(card -> card.getSliceUri().equals(CustomSliceRegistry.WIFI_SLICE_URI) || card.getSliceUri().equals(CustomSliceRegistry.CONNECTED_DEVICE_SLICE_URI)) .filter(card -> card.getSliceUri().equals(WIFI_SLICE_URI) || card.getSliceUri().equals(CONNECTED_DEVICE_SLICE_URI)) .count(); } Loading
src/com/android/settings/homepage/contextualcards/ContextualCardManager.java +8 −2 Original line number Diff line number Diff line Loading @@ -16,8 +16,7 @@ package com.android.settings.homepage.contextualcards; import static com.android.settings.homepage.contextualcards.ContextualCardLoader .CARD_CONTENT_LOADER_ID; import static com.android.settings.homepage.contextualcards.ContextualCardLoader.CARD_CONTENT_LOADER_ID; import static java.util.stream.Collectors.groupingBy; Loading @@ -33,6 +32,7 @@ import androidx.annotation.VisibleForTesting; import androidx.loader.app.LoaderManager; 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; Loading Loading @@ -72,6 +72,7 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo private final List<LifecycleObserver> mLifecycleObservers; private ContextualCardUpdateListener mListener; private long mStartTime; public ContextualCardManager(Context context, Lifecycle lifecycle) { mContext = context; Loading @@ -86,6 +87,7 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo } void loadContextualCards(ContextualCardsFragment fragment) { mStartTime = System.currentTimeMillis(); final CardContentLoaderCallbacks cardContentLoaderCallbacks = new CardContentLoaderCallbacks(mContext); cardContentLoaderCallbacks.setListener(this); Loading Loading @@ -168,6 +170,10 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo @Override public void onFinishCardLoading(List<ContextualCard> cards) { onContextualCardUpdated(cards.stream().collect(groupingBy(ContextualCard::getCardType))); final long elapsedTime = System.currentTimeMillis() - mStartTime; final ContextualCardFeatureProvider contextualCardFeatureProvider = FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider(); contextualCardFeatureProvider.logHomepageDisplay(mContext, elapsedTime); } public ControllerRendererPool getControllerRendererPool() { Loading
tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImplTest.java +43 −2 Original line number Diff line number Diff line Loading @@ -16,13 +16,20 @@ package com.android.settings.homepage.contextualcards; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Matchers.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.UserHandle; import com.android.settings.intelligence.ContextualCardProto.ContextualCardList; import com.android.settings.testutils.SettingsRobolectricTestRunner; import org.junit.Before; Loading @@ -31,6 +38,9 @@ import org.junit.runner.RunWith; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import java.util.ArrayList; import java.util.List; @RunWith(SettingsRobolectricTestRunner.class) public class ContextualCardFeatureProviderImplTest { Loading @@ -48,7 +58,7 @@ public class ContextualCardFeatureProviderImplTest { final Intent intent = new Intent(); mImpl.sendBroadcast(mContext, intent); verify(mContext, never()).sendBroadcast(intent); verify(mContext, never()).sendBroadcastAsUser(intent, UserHandle.ALL); } @Test Loading @@ -57,6 +67,37 @@ public class ContextualCardFeatureProviderImplTest { final Intent intent = new Intent(); mImpl.sendBroadcast(mContext, intent); verify(mContext).sendBroadcast(intent); verify(mContext).sendBroadcastAsUser(intent, UserHandle.ALL); } @Test @Config(qualifiers = "mcc999") public void logContextualCardDisplay_hasAction_sendBroadcast() { mImpl.logContextualCardDisplay(mContext, new ArrayList<>(), new ArrayList<>()); verify(mContext).sendBroadcastAsUser(any(Intent.class), any()); } @Test public void serialize_hasSizeTwo_returnSizeTwo() { final List<ContextualCard> cards = new ArrayList<>(); cards.add(new ContextualCard.Builder() .setName("name1") .setSliceUri(Uri.parse("uri1")) .build()); cards.add(new ContextualCard.Builder() .setName("name2") .setSliceUri(Uri.parse("uri2")) .build()); final byte[] data = mImpl.serialize(cards); try { assertThat(ContextualCardList .parseFrom(data).getCardCount()).isEqualTo(cards.size()); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } } } No newline at end of file