Record all contextual card log to MetricsFeatureProvider
Use ContextualCardLogUtils to serialize contextual card event to string, and records the string using regular MetricFeatureProvider logging APIs. Bug: 124701288 Test: Robolectric, integrating test with SettingsIntelligence Change-Id: Ie139b4f4b8a2b0f0dcc4bb8df9bdec8f5fd824a6
This commit is contained in:
@@ -20,6 +20,7 @@ import static com.android.settings.slices.CustomSliceRegistry.BLUETOOTH_DEVICES_
|
|||||||
import static com.android.settings.slices.CustomSliceRegistry.CONTEXTUAL_NOTIFICATION_CHANNEL_SLICE_URI;
|
import static com.android.settings.slices.CustomSliceRegistry.CONTEXTUAL_NOTIFICATION_CHANNEL_SLICE_URI;
|
||||||
import static com.android.settings.slices.CustomSliceRegistry.CONTEXTUAL_WIFI_SLICE_URI;
|
import static com.android.settings.slices.CustomSliceRegistry.CONTEXTUAL_WIFI_SLICE_URI;
|
||||||
|
|
||||||
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.ContentObserver;
|
import android.database.ContentObserver;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
@@ -32,7 +33,9 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.homepage.contextualcards.logging.ContextualCardLogUtils;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
|
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||||
import com.android.settingslib.utils.AsyncLoaderCompat;
|
import com.android.settingslib.utils.AsyncLoaderCompat;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -162,10 +165,16 @@ public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard>
|
|||||||
return visibleCards;
|
return visibleCards;
|
||||||
} finally {
|
} finally {
|
||||||
if (!CardContentProvider.DELETE_CARD_URI.equals(mNotifyUri)) {
|
if (!CardContentProvider.DELETE_CARD_URI.equals(mNotifyUri)) {
|
||||||
final ContextualCardFeatureProvider contextualCardFeatureProvider =
|
final MetricsFeatureProvider metricsFeatureProvider =
|
||||||
FeatureFactory.getFactory(mContext)
|
FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
|
||||||
.getContextualCardFeatureProvider(mContext);
|
|
||||||
contextualCardFeatureProvider.logContextualCardDisplay(visibleCards, hiddenCards);
|
metricsFeatureProvider.action(mContext,
|
||||||
|
SettingsEnums.ACTION_CONTEXTUAL_CARD_SHOW,
|
||||||
|
ContextualCardLogUtils.buildCardListLog(visibleCards));
|
||||||
|
|
||||||
|
metricsFeatureProvider.action(mContext,
|
||||||
|
SettingsEnums.ACTION_CONTEXTUAL_CARD_NOT_SHOW,
|
||||||
|
ContextualCardLogUtils.buildCardListLog(hiddenCards));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,263 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.homepage.contextualcards.logging;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.slice.widget.EventInfo;
|
||||||
|
|
||||||
|
import com.android.settings.homepage.contextualcards.ContextualCard;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utils of building contextual card to string, and parse string back to {@link CardLog}
|
||||||
|
*/
|
||||||
|
public class ContextualCardLogUtils {
|
||||||
|
|
||||||
|
private static final String TAG = "ContextualCardLogUtils";
|
||||||
|
|
||||||
|
private static final class TapTarget {
|
||||||
|
static int TARGET_DEFAULT = 0;
|
||||||
|
static int TARGET_TITLE = 1;
|
||||||
|
static int TARGET_TOGGLE = 2;
|
||||||
|
static int TARGET_SLIDER = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log data for a general contextual card event
|
||||||
|
*/
|
||||||
|
public static class CardLog {
|
||||||
|
private final String mSliceUri;
|
||||||
|
private final double mRankingScore;
|
||||||
|
|
||||||
|
public CardLog(Builder builder) {
|
||||||
|
mSliceUri = builder.mSliceUri;
|
||||||
|
mRankingScore = builder.mRankingScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSliceUri() {
|
||||||
|
return mSliceUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getRankingScore() {
|
||||||
|
return mRankingScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
private String mSliceUri;
|
||||||
|
private double mRankingScore;
|
||||||
|
|
||||||
|
public Builder setSliceUri(String sliceUri) {
|
||||||
|
mSliceUri = sliceUri;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setRankingScore(double rankingScore) {
|
||||||
|
mRankingScore = rankingScore;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public CardLog build() {
|
||||||
|
return new CardLog(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log data for a contextual card click event
|
||||||
|
*/
|
||||||
|
public static class CardClickLog extends CardLog {
|
||||||
|
private final int mSliceRow;
|
||||||
|
private final int mSliceTapTarget;
|
||||||
|
private final int mUiPosition;
|
||||||
|
|
||||||
|
public CardClickLog(Builder builder) {
|
||||||
|
super(builder);
|
||||||
|
mSliceRow = builder.mSliceRow;
|
||||||
|
mSliceTapTarget = builder.mSliceTapTarget;
|
||||||
|
mUiPosition = builder.mUiPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSliceRow() {
|
||||||
|
return mSliceRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSliceTapTarget() {
|
||||||
|
return mSliceTapTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getUiPosition() {
|
||||||
|
return mUiPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder extends CardLog.Builder {
|
||||||
|
private int mSliceRow;
|
||||||
|
private int mSliceTapTarget;
|
||||||
|
private int mUiPosition;
|
||||||
|
|
||||||
|
public Builder setSliceRow(int sliceRow) {
|
||||||
|
mSliceRow = sliceRow;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setSliceTapTarget(int sliceTapTarget) {
|
||||||
|
mSliceTapTarget = sliceTapTarget;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setUiPosition(int uiPosition) {
|
||||||
|
mUiPosition = uiPosition;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public CardClickLog build() {
|
||||||
|
return new CardClickLog(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize {@link ContextualCard} click event to string
|
||||||
|
*
|
||||||
|
* @param card Clicked Contextual card.
|
||||||
|
* @param sliceRow A Slice can contains multiple row, which row are we clicked
|
||||||
|
* @param tapTarget Integer value of {@link TapTarget}
|
||||||
|
* @param uiPosition Contextual card position in Listview
|
||||||
|
*/
|
||||||
|
public static String buildCardClickLog(ContextualCard card, int sliceRow, int tapTarget,
|
||||||
|
int uiPosition) {
|
||||||
|
final StringBuilder log = new StringBuilder();
|
||||||
|
log.append(card.getTextSliceUri()).append("|")
|
||||||
|
.append(card.getRankingScore()).append("|")
|
||||||
|
.append(sliceRow).append("|")
|
||||||
|
.append(actionTypeToTapTarget(tapTarget)).append("|")
|
||||||
|
.append(uiPosition);
|
||||||
|
return log.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse string to a {@link CardClickLog}
|
||||||
|
*/
|
||||||
|
public static CardClickLog parseCardClickLog(String clickLog) {
|
||||||
|
if (clickLog != null) {
|
||||||
|
final String[] parts = clickLog.split("\\|");
|
||||||
|
if (parts.length < 5) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final CardClickLog.Builder builder = new CardClickLog.Builder();
|
||||||
|
builder.setSliceRow(Integer.parseInt(parts[2]))
|
||||||
|
.setSliceTapTarget(Integer.parseInt(parts[3]))
|
||||||
|
.setUiPosition(Integer.parseInt(parts[4]))
|
||||||
|
.setSliceUri(parts[0])
|
||||||
|
.setRankingScore(Double.parseDouble(parts[1]));
|
||||||
|
return builder.build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "error parsing log", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize {@link ContextualCard} to string
|
||||||
|
*
|
||||||
|
* @param card Contextual card.
|
||||||
|
*/
|
||||||
|
public static String buildCardDismissLog(ContextualCard card) {
|
||||||
|
final StringBuilder log = new StringBuilder();
|
||||||
|
log.append(card.getTextSliceUri())
|
||||||
|
.append("|")
|
||||||
|
.append(card.getRankingScore());
|
||||||
|
return log.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse string to a {@link CardLog}
|
||||||
|
*/
|
||||||
|
public static CardLog parseCardDismissLog(String dismissLog) {
|
||||||
|
if (dismissLog != null) {
|
||||||
|
final String[] parts = dismissLog.split("\\|");
|
||||||
|
if (parts.length < 2) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final CardLog.Builder builder = new CardLog.Builder();
|
||||||
|
builder.setSliceUri(parts[0])
|
||||||
|
.setRankingScore(Double.parseDouble(parts[1]));
|
||||||
|
return builder.build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "error parsing log", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize List of {@link ContextualCard} to string
|
||||||
|
*/
|
||||||
|
public static String buildCardListLog(List<ContextualCard> cards) {
|
||||||
|
final StringBuilder log = new StringBuilder();
|
||||||
|
log.append(cards.size());
|
||||||
|
for (ContextualCard card : cards) {
|
||||||
|
log.append("|").append(card.getTextSliceUri())
|
||||||
|
.append("|").append(card.getRankingScore());
|
||||||
|
}
|
||||||
|
return log.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse string to a List of {@link CardLog}
|
||||||
|
*/
|
||||||
|
public static List<CardLog> parseCardListLog(String listLog) {
|
||||||
|
final List<CardLog> logList = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
final String[] parts = listLog.split("\\|");
|
||||||
|
if (Integer.parseInt(parts[0]) < 0) {
|
||||||
|
return logList;
|
||||||
|
}
|
||||||
|
final int size = parts.length;
|
||||||
|
for (int i = 1; i < size; ) {
|
||||||
|
final CardLog.Builder builder = new CardLog.Builder();
|
||||||
|
builder.setSliceUri(parts[i++])
|
||||||
|
.setRankingScore(Double.parseDouble(parts[i++]));
|
||||||
|
logList.add(builder.build());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "error parsing log", e);
|
||||||
|
return logList;
|
||||||
|
}
|
||||||
|
return logList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int actionTypeToTapTarget(int actionType) {
|
||||||
|
switch (actionType) {
|
||||||
|
case EventInfo.ACTION_TYPE_CONTENT:
|
||||||
|
return TapTarget.TARGET_TITLE;
|
||||||
|
case EventInfo.ACTION_TYPE_TOGGLE:
|
||||||
|
return TapTarget.TARGET_TOGGLE;
|
||||||
|
case EventInfo.ACTION_TYPE_SLIDER:
|
||||||
|
return TapTarget.TARGET_SLIDER;
|
||||||
|
default:
|
||||||
|
Log.w(TAG, "unknown type " + actionType);
|
||||||
|
return TapTarget.TARGET_DEFAULT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.settings.homepage.contextualcards.slices;
|
package com.android.settings.homepage.contextualcards.slices;
|
||||||
|
|
||||||
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
@@ -27,10 +28,11 @@ import com.android.settings.R;
|
|||||||
import com.android.settings.homepage.contextualcards.CardDatabaseHelper;
|
import com.android.settings.homepage.contextualcards.CardDatabaseHelper;
|
||||||
import com.android.settings.homepage.contextualcards.ContextualCard;
|
import com.android.settings.homepage.contextualcards.ContextualCard;
|
||||||
import com.android.settings.homepage.contextualcards.ContextualCardController;
|
import com.android.settings.homepage.contextualcards.ContextualCardController;
|
||||||
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
|
|
||||||
import com.android.settings.homepage.contextualcards.ContextualCardFeedbackDialog;
|
import com.android.settings.homepage.contextualcards.ContextualCardFeedbackDialog;
|
||||||
|
import com.android.settings.homepage.contextualcards.logging.ContextualCardLogUtils;
|
||||||
import com.android.settings.homepage.contextualcards.ContextualCardUpdateListener;
|
import com.android.settings.homepage.contextualcards.ContextualCardUpdateListener;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
|
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||||
import com.android.settingslib.utils.ThreadUtils;
|
import com.android.settingslib.utils.ThreadUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -70,9 +72,13 @@ public class SliceContextualCardController implements ContextualCardController {
|
|||||||
dbHelper.markContextualCardAsDismissed(mContext, card.getName());
|
dbHelper.markContextualCardAsDismissed(mContext, card.getName());
|
||||||
});
|
});
|
||||||
showFeedbackDialog(card);
|
showFeedbackDialog(card);
|
||||||
final ContextualCardFeatureProvider contextualCardFeatureProvider =
|
|
||||||
FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider(mContext);
|
final MetricsFeatureProvider metricsFeatureProvider =
|
||||||
contextualCardFeatureProvider.logContextualCardDismiss(card);
|
FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
|
||||||
|
|
||||||
|
metricsFeatureProvider.action(mContext,
|
||||||
|
SettingsEnums.ACTION_CONTEXTUAL_CARD_DISMISS,
|
||||||
|
ContextualCardLogUtils.buildCardDismissLog(card));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
package com.android.settings.homepage.contextualcards.slices;
|
package com.android.settings.homepage.contextualcards.slices;
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -33,8 +34,9 @@ import androidx.slice.widget.EventInfo;
|
|||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.homepage.contextualcards.ContextualCard;
|
import com.android.settings.homepage.contextualcards.ContextualCard;
|
||||||
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
|
import com.android.settings.homepage.contextualcards.logging.ContextualCardLogUtils;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
|
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Card renderer helper for {@link ContextualCard} built as slice deferred setup card.
|
* Card renderer helper for {@link ContextualCard} built as slice deferred setup card.
|
||||||
@@ -65,10 +67,14 @@ class SliceDeferredSetupCardRendererHelper {
|
|||||||
} catch (PendingIntent.CanceledException e) {
|
} catch (PendingIntent.CanceledException e) {
|
||||||
Log.w(TAG, "Failed to start intent " + primaryAction.getTitle());
|
Log.w(TAG, "Failed to start intent " + primaryAction.getTitle());
|
||||||
}
|
}
|
||||||
final ContextualCardFeatureProvider contextualCardFeatureProvider =
|
final String log = ContextualCardLogUtils.buildCardClickLog(card, 0 /* row */,
|
||||||
FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider(mContext);
|
|
||||||
contextualCardFeatureProvider.logContextualCardClick(card, 0 /* row */,
|
|
||||||
EventInfo.ACTION_TYPE_CONTENT, view.getAdapterPosition());
|
EventInfo.ACTION_TYPE_CONTENT, view.getAdapterPosition());
|
||||||
|
|
||||||
|
final MetricsFeatureProvider metricsFeatureProvider =
|
||||||
|
FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
|
||||||
|
|
||||||
|
metricsFeatureProvider.action(mContext,
|
||||||
|
SettingsEnums.ACTION_CONTEXTUAL_CARD_CLICK, log);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.settings.homepage.contextualcards.slices;
|
package com.android.settings.homepage.contextualcards.slices;
|
||||||
|
|
||||||
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
@@ -26,8 +27,9 @@ import androidx.slice.widget.SliceView;
|
|||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.homepage.contextualcards.ContextualCard;
|
import com.android.settings.homepage.contextualcards.ContextualCard;
|
||||||
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
|
import com.android.settings.homepage.contextualcards.logging.ContextualCardLogUtils;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
|
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Card renderer helper for {@link ContextualCard} built as slice full card.
|
* Card renderer helper for {@link ContextualCard} built as slice full card.
|
||||||
@@ -54,11 +56,14 @@ class SliceFullCardRendererHelper {
|
|||||||
// Set this listener so we can log the interaction users make on the slice
|
// Set this listener so we can log the interaction users make on the slice
|
||||||
cardHolder.sliceView.setOnSliceActionListener(
|
cardHolder.sliceView.setOnSliceActionListener(
|
||||||
(eventInfo, sliceItem) -> {
|
(eventInfo, sliceItem) -> {
|
||||||
final ContextualCardFeatureProvider contextualCardFeatureProvider =
|
final String log = ContextualCardLogUtils.buildCardClickLog(card, eventInfo.rowIndex,
|
||||||
FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider(
|
|
||||||
mContext);
|
|
||||||
contextualCardFeatureProvider.logContextualCardClick(card, eventInfo.rowIndex,
|
|
||||||
eventInfo.actionType, cardHolder.getAdapterPosition());
|
eventInfo.actionType, cardHolder.getAdapterPosition());
|
||||||
|
|
||||||
|
final MetricsFeatureProvider metricsFeatureProvider =
|
||||||
|
FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
|
||||||
|
|
||||||
|
metricsFeatureProvider.action(mContext,
|
||||||
|
SettingsEnums.ACTION_CONTEXTUAL_CARD_CLICK, log);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Customize slice view for Settings
|
// Customize slice view for Settings
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
package com.android.settings.homepage.contextualcards.slices;
|
package com.android.settings.homepage.contextualcards.slices;
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -32,8 +33,9 @@ import androidx.slice.widget.EventInfo;
|
|||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.homepage.contextualcards.ContextualCard;
|
import com.android.settings.homepage.contextualcards.ContextualCard;
|
||||||
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
|
import com.android.settings.homepage.contextualcards.logging.ContextualCardLogUtils;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
|
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Card renderer helper for {@link ContextualCard} built as slice half card.
|
* Card renderer helper for {@link ContextualCard} built as slice half card.
|
||||||
@@ -63,10 +65,14 @@ class SliceHalfCardRendererHelper {
|
|||||||
} catch (PendingIntent.CanceledException e) {
|
} catch (PendingIntent.CanceledException e) {
|
||||||
Log.w(TAG, "Failed to start intent " + primaryAction.getTitle());
|
Log.w(TAG, "Failed to start intent " + primaryAction.getTitle());
|
||||||
}
|
}
|
||||||
final ContextualCardFeatureProvider contextualCardFeatureProvider =
|
final String log = ContextualCardLogUtils.buildCardClickLog(card, 0 /* row */,
|
||||||
FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider(mContext);
|
|
||||||
contextualCardFeatureProvider.logContextualCardClick(card, 0 /* row */,
|
|
||||||
EventInfo.ACTION_TYPE_CONTENT, view.getAdapterPosition());
|
EventInfo.ACTION_TYPE_CONTENT, view.getAdapterPosition());
|
||||||
|
|
||||||
|
final MetricsFeatureProvider metricsFeatureProvider =
|
||||||
|
FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
|
||||||
|
|
||||||
|
metricsFeatureProvider.action(mContext,
|
||||||
|
SettingsEnums.ACTION_CONTEXTUAL_CARD_CLICK, log);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -22,12 +22,15 @@ import static com.android.settings.homepage.contextualcards.ContextualCardLoader
|
|||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyList;
|
import static org.mockito.ArgumentMatchers.anyList;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.doReturn;
|
import static org.mockito.Mockito.doReturn;
|
||||||
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.app.settings.SettingsEnums;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
@@ -182,8 +185,10 @@ public class ContextualCardLoaderTest {
|
|||||||
|
|
||||||
mContextualCardLoader.getDisplayableCards(new ArrayList<>());
|
mContextualCardLoader.getDisplayableCards(new ArrayList<>());
|
||||||
|
|
||||||
verify(mFakeFeatureFactory.mContextualCardFeatureProvider).logContextualCardDisplay(
|
verify(mFakeFeatureFactory.metricsFeatureProvider).action(any(),
|
||||||
anyList(), anyList());
|
eq(SettingsEnums.ACTION_CONTEXTUAL_CARD_SHOW), any(String.class));
|
||||||
|
verify(mFakeFeatureFactory.metricsFeatureProvider).action(any(),
|
||||||
|
eq(SettingsEnums.ACTION_CONTEXTUAL_CARD_NOT_SHOW), any(String.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.homepage.contextualcards.logging;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import com.android.settings.homepage.contextualcards.ContextualCard;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class ContextualCardLogUtilsTest {
|
||||||
|
|
||||||
|
private static final String TEST_URI = "content://test/test2";
|
||||||
|
private static final double TEST_SCORE = 0.12345f;
|
||||||
|
|
||||||
|
private static final ContextualCard TEST_CARD =
|
||||||
|
new ContextualCard.Builder()
|
||||||
|
.setSliceUri(Uri.parse(TEST_URI))
|
||||||
|
.setRankingScore(TEST_SCORE)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parseCardDismissLog_notValid_returnNull() {
|
||||||
|
assertThat(ContextualCardLogUtils.parseCardDismissLog(TEST_URI + "|" + TEST_URI)).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parseCardDismissLog_isValid_returnCorrectData() {
|
||||||
|
final String log = ContextualCardLogUtils.buildCardDismissLog(TEST_CARD);
|
||||||
|
|
||||||
|
final ContextualCardLogUtils.CardLog cardLog = ContextualCardLogUtils.parseCardDismissLog(
|
||||||
|
log);
|
||||||
|
|
||||||
|
assertThat(cardLog.getSliceUri()).isEqualTo(TEST_URI);
|
||||||
|
assertThat(cardLog.getRankingScore()).isEqualTo(TEST_SCORE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parseCardClickLog_isValid_returnCorrectData() {
|
||||||
|
final int row = 1;
|
||||||
|
final int target = 2;
|
||||||
|
final int position = 3;
|
||||||
|
final String log = ContextualCardLogUtils.buildCardClickLog(TEST_CARD, row, target,
|
||||||
|
position);
|
||||||
|
|
||||||
|
final ContextualCardLogUtils.CardClickLog cardClickLog =
|
||||||
|
ContextualCardLogUtils.parseCardClickLog(log);
|
||||||
|
|
||||||
|
assertThat(cardClickLog.getSliceUri()).isEqualTo(TEST_URI);
|
||||||
|
assertThat(cardClickLog.getRankingScore()).isEqualTo(TEST_SCORE);
|
||||||
|
assertThat(cardClickLog.getSliceRow()).isEqualTo(row);
|
||||||
|
assertThat(cardClickLog.getSliceTapTarget()).isEqualTo(
|
||||||
|
ContextualCardLogUtils.actionTypeToTapTarget(target));
|
||||||
|
assertThat(cardClickLog.getUiPosition()).isEqualTo(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parseCardClickList_isValid_returnCorrectData() {
|
||||||
|
final ContextualCard testcard =
|
||||||
|
new ContextualCard.Builder()
|
||||||
|
.setSliceUri(Uri.parse("testtest"))
|
||||||
|
.setRankingScore(-1d)
|
||||||
|
.build();
|
||||||
|
final List<ContextualCard> cardList = new ArrayList<>();
|
||||||
|
cardList.add(TEST_CARD);
|
||||||
|
cardList.add(testcard);
|
||||||
|
final String log = ContextualCardLogUtils.buildCardListLog(cardList);
|
||||||
|
|
||||||
|
final List<ContextualCardLogUtils.CardLog> cardClickLogList =
|
||||||
|
ContextualCardLogUtils.parseCardListLog(log);
|
||||||
|
|
||||||
|
assertThat(cardClickLogList.size()).isEqualTo(2);
|
||||||
|
assertThat(cardClickLogList.get(0).getSliceUri()).isEqualTo(TEST_URI);
|
||||||
|
assertThat(cardClickLogList.get(0).getRankingScore()).isEqualTo(TEST_SCORE);
|
||||||
|
assertThat(cardClickLogList.get(1).getSliceUri()).isEqualTo("testtest");
|
||||||
|
assertThat(cardClickLogList.get(1).getRankingScore()).isEqualTo(-1d);
|
||||||
|
}
|
||||||
|
}
|
@@ -18,13 +18,15 @@ package com.android.settings.homepage.contextualcards.slices;
|
|||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.Mockito.any;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.doNothing;
|
import static org.mockito.Mockito.doNothing;
|
||||||
import static org.mockito.Mockito.doReturn;
|
import static org.mockito.Mockito.doReturn;
|
||||||
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.app.settings.SettingsEnums;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -96,7 +98,8 @@ public class SliceContextualCardControllerTest {
|
|||||||
cr.close();
|
cr.close();
|
||||||
|
|
||||||
assertThat(qryDismissed).isEqualTo(1);
|
assertThat(qryDismissed).isEqualTo(1);
|
||||||
verify(mFeatureFactory.mContextualCardFeatureProvider).logContextualCardDismiss(card);
|
verify(mFeatureFactory.metricsFeatureProvider).action(any(),
|
||||||
|
eq(SettingsEnums.ACTION_CONTEXTUAL_CARD_DISMISS), any(String.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Reference in New Issue
Block a user