Log contextual card is visible and homepage is displayed event

Bug: 79698338
Test: Robolectric
Change-Id: I89ac5ed2765729d8571e84d3a050f0aa07385caa
This commit is contained in:
Raff Tsai
2018-12-05 19:13:46 +08:00
parent e4128eca14
commit e58e0c6e0e
4 changed files with 126 additions and 28 deletions

View File

@@ -16,8 +16,10 @@
package com.android.settings.homepage.contextualcards; package com.android.settings.homepage.contextualcards;
import android.annotation.NonNull;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.UserHandle;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
@@ -25,6 +27,7 @@ import androidx.annotation.VisibleForTesting;
import androidx.slice.widget.EventInfo; import androidx.slice.widget.EventInfo;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.intelligence.ContextualCardProto.ContextualCardList;
import java.util.List; import java.util.List;
@@ -38,10 +41,6 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP
// Contextual card shows, log card name and rank // Contextual card shows, log card name and rank
private static final int CONTEXTUAL_CARD_SHOW = 39; 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 // Contextual card is dismissed, log card name
private static final int CONTEXTUAL_CARD_DISMISS = 41; private static final int CONTEXTUAL_CARD_DISMISS = 41;
@@ -67,6 +66,11 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP
// log type // log type
private static final String EXTRA_CONTEXTUALCARD_ACTION_TYPE = "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 // Contextual card tap target
private static final int TARGET_DEFAULT = 0; private static final int TARGET_DEFAULT = 0;
@@ -82,6 +86,10 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP
@Override @Override
public void logHomepageDisplay(Context context, Long latency) { 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 @Override
@@ -94,8 +102,13 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP
} }
@Override @Override
public void logContextualCardDisplay(Context context, List<ContextualCard> showCards, public void logContextualCardDisplay(Context context, List<ContextualCard> visibleCards,
List<ContextualCard> hiddenCards) { 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 @Override
@@ -116,7 +129,7 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP
final String action = context.getString(R.string.config_settingsintelligence_log_action); final String action = context.getString(R.string.config_settingsintelligence_log_action);
if (!TextUtils.isEmpty(action)) { if (!TextUtils.isEmpty(action)) {
intent.setAction(action); intent.setAction(action);
context.sendBroadcast(intent); context.sendBroadcastAsUser(intent, UserHandle.ALL);
} }
} }
@@ -133,4 +146,16 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP
return TARGET_DEFAULT; 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();
}
} }

View File

@@ -20,6 +20,9 @@ import static android.app.slice.Slice.HINT_ERROR;
import static androidx.slice.widget.SliceLiveData.SUPPORTED_SPECS; 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.ContentProviderClient;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
@@ -34,7 +37,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.slice.Slice; import androidx.slice.Slice;
import com.android.settings.slices.CustomSliceRegistry; import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.utils.AsyncLoaderCompat; import com.android.settingslib.utils.AsyncLoaderCompat;
import java.util.ArrayList; import java.util.ArrayList;
@@ -103,27 +106,50 @@ public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard>
return getFinalDisplayableCards(result); return getFinalDisplayableCards(result);
} }
// Get final displayed cards and log what cards will be displayed/hidden
@VisibleForTesting @VisibleForTesting
List<ContextualCard> getFinalDisplayableCards(List<ContextualCard> candidates) { List<ContextualCard> getFinalDisplayableCards(List<ContextualCard> candidates) {
List<ContextualCard> eligibleCards = filterEligibleCards(candidates); final List<ContextualCard> eligibleCards = filterEligibleCards(candidates);
eligibleCards = eligibleCards.stream().limit(DEFAULT_CARD_COUNT).collect( final List<ContextualCard> visibleCards = new ArrayList<>();
Collectors.toList()); final List<ContextualCard> hiddenCards = new ArrayList<>();
if (eligibleCards.size() <= 2 || getNumberOfLargeCard(eligibleCards) == 0) { final int size = eligibleCards.size();
return eligibleCards; 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() == DEFAULT_CARD_COUNT) { try {
eligibleCards.remove(eligibleCards.size() - 1); // 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 (getNumberOfLargeCard(eligibleCards) == 1) { if (visibleCards.size() == DEFAULT_CARD_COUNT) {
return eligibleCards; hiddenCards.add(visibleCards.remove(visibleCards.size() - 1));
} }
eligibleCards.remove(eligibleCards.size() - 1); if (getNumberOfLargeCard(visibleCards) == 1) {
// One large card with two small cards
return visibleCards;
}
return eligibleCards; hiddenCards.add(visibleCards.remove(visibleCards.size() - 1));
// Two large cards
return visibleCards;
} finally {
final ContextualCardFeatureProvider contextualCardFeatureProvider =
FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider();
contextualCardFeatureProvider.logContextualCardDisplay(mContext, visibleCards,
hiddenCards);
}
} }
@VisibleForTesting @VisibleForTesting
@@ -169,8 +195,8 @@ public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard>
private int getNumberOfLargeCard(List<ContextualCard> cards) { private int getNumberOfLargeCard(List<ContextualCard> cards) {
return (int) cards.stream() return (int) cards.stream()
.filter(card -> card.getSliceUri().equals(CustomSliceRegistry.WIFI_SLICE_URI) .filter(card -> card.getSliceUri().equals(WIFI_SLICE_URI)
|| card.getSliceUri().equals(CustomSliceRegistry.CONNECTED_DEVICE_SLICE_URI)) || card.getSliceUri().equals(CONNECTED_DEVICE_SLICE_URI))
.count(); .count();
} }

View File

@@ -16,8 +16,7 @@
package com.android.settings.homepage.contextualcards; package com.android.settings.homepage.contextualcards;
import static com.android.settings.homepage.contextualcards.ContextualCardLoader import static com.android.settings.homepage.contextualcards.ContextualCardLoader.CARD_CONTENT_LOADER_ID;
.CARD_CONTENT_LOADER_ID;
import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.groupingBy;
@@ -33,6 +32,7 @@ import androidx.annotation.VisibleForTesting;
import androidx.loader.app.LoaderManager; import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader; import androidx.loader.content.Loader;
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;
@@ -72,6 +72,7 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
private final List<LifecycleObserver> mLifecycleObservers; private final List<LifecycleObserver> mLifecycleObservers;
private ContextualCardUpdateListener mListener; private ContextualCardUpdateListener mListener;
private long mStartTime;
public ContextualCardManager(Context context, Lifecycle lifecycle) { public ContextualCardManager(Context context, Lifecycle lifecycle) {
mContext = context; mContext = context;
@@ -86,6 +87,7 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
} }
void loadContextualCards(ContextualCardsFragment fragment) { void loadContextualCards(ContextualCardsFragment fragment) {
mStartTime = System.currentTimeMillis();
final CardContentLoaderCallbacks cardContentLoaderCallbacks = final CardContentLoaderCallbacks cardContentLoaderCallbacks =
new CardContentLoaderCallbacks(mContext); new CardContentLoaderCallbacks(mContext);
cardContentLoaderCallbacks.setListener(this); cardContentLoaderCallbacks.setListener(this);
@@ -168,6 +170,10 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
@Override @Override
public void onFinishCardLoading(List<ContextualCard> cards) { public void onFinishCardLoading(List<ContextualCard> cards) {
onContextualCardUpdated(cards.stream().collect(groupingBy(ContextualCard::getCardType))); 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() { public ControllerRendererPool getControllerRendererPool() {

View File

@@ -16,13 +16,20 @@
package com.android.settings.homepage.contextualcards; 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.never;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import android.content.Context; import android.content.Context;
import android.content.Intent; 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 com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before; import org.junit.Before;
@@ -31,6 +38,9 @@ import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import java.util.ArrayList;
import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class) @RunWith(SettingsRobolectricTestRunner.class)
public class ContextualCardFeatureProviderImplTest { public class ContextualCardFeatureProviderImplTest {
@@ -48,7 +58,7 @@ public class ContextualCardFeatureProviderImplTest {
final Intent intent = new Intent(); final Intent intent = new Intent();
mImpl.sendBroadcast(mContext, intent); mImpl.sendBroadcast(mContext, intent);
verify(mContext, never()).sendBroadcast(intent); verify(mContext, never()).sendBroadcastAsUser(intent, UserHandle.ALL);
} }
@Test @Test
@@ -57,6 +67,37 @@ public class ContextualCardFeatureProviderImplTest {
final Intent intent = new Intent(); final Intent intent = new Intent();
mImpl.sendBroadcast(mContext, 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());
}
} }
} }