Log contextual card is visible and homepage is displayed event
Bug: 79698338 Test: Robolectric Change-Id: I89ac5ed2765729d8571e84d3a050f0aa07385caa
This commit is contained in:
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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() {
|
||||||
|
@@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user