Merge "Record all contextual card log to MetricsFeatureProvider" into qt-dev
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java
index 32f505e..74ff613 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java
@@ -20,6 +20,7 @@
 import static com.android.settings.slices.CustomSliceRegistry.CONTEXTUAL_NOTIFICATION_CHANNEL_SLICE_URI;
 import static com.android.settings.slices.CustomSliceRegistry.CONTEXTUAL_WIFI_SLICE_URI;
 
+import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.database.ContentObserver;
 import android.database.Cursor;
@@ -32,7 +33,9 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.settings.R;
+import com.android.settings.homepage.contextualcards.logging.ContextualCardLogUtils;
 import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.utils.AsyncLoaderCompat;
 
 import java.util.ArrayList;
@@ -162,10 +165,16 @@
             return visibleCards;
         } finally {
             if (!CardContentProvider.DELETE_CARD_URI.equals(mNotifyUri)) {
-                final ContextualCardFeatureProvider contextualCardFeatureProvider =
-                        FeatureFactory.getFactory(mContext)
-                                .getContextualCardFeatureProvider(mContext);
-                contextualCardFeatureProvider.logContextualCardDisplay(visibleCards, hiddenCards);
+                final MetricsFeatureProvider metricsFeatureProvider =
+                        FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
+
+                metricsFeatureProvider.action(mContext,
+                        SettingsEnums.ACTION_CONTEXTUAL_CARD_SHOW,
+                        ContextualCardLogUtils.buildCardListLog(visibleCards));
+
+                metricsFeatureProvider.action(mContext,
+                        SettingsEnums.ACTION_CONTEXTUAL_CARD_NOT_SHOW,
+                        ContextualCardLogUtils.buildCardListLog(hiddenCards));
             }
         }
     }
diff --git a/src/com/android/settings/homepage/contextualcards/logging/ContextualCardLogUtils.java b/src/com/android/settings/homepage/contextualcards/logging/ContextualCardLogUtils.java
new file mode 100644
index 0000000..585eca3
--- /dev/null
+++ b/src/com/android/settings/homepage/contextualcards/logging/ContextualCardLogUtils.java
@@ -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;
+        }
+    }
+}
diff --git a/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardController.java b/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardController.java
index 07e23de..0551377 100644
--- a/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardController.java
+++ b/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardController.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.homepage.contextualcards.slices;
 
+import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Build;
@@ -27,10 +28,11 @@
 import com.android.settings.homepage.contextualcards.CardDatabaseHelper;
 import com.android.settings.homepage.contextualcards.ContextualCard;
 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.logging.ContextualCardLogUtils;
 import com.android.settings.homepage.contextualcards.ContextualCardUpdateListener;
 import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.utils.ThreadUtils;
 
 /**
@@ -70,9 +72,13 @@
             dbHelper.markContextualCardAsDismissed(mContext, card.getName());
         });
         showFeedbackDialog(card);
-        final ContextualCardFeatureProvider contextualCardFeatureProvider =
-                FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider(mContext);
-        contextualCardFeatureProvider.logContextualCardDismiss(card);
+
+        final MetricsFeatureProvider metricsFeatureProvider =
+                FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
+
+        metricsFeatureProvider.action(mContext,
+                SettingsEnums.ACTION_CONTEXTUAL_CARD_DISMISS,
+                ContextualCardLogUtils.buildCardDismissLog(card));
     }
 
     @Override
diff --git a/src/com/android/settings/homepage/contextualcards/slices/SliceDeferredSetupCardRendererHelper.java b/src/com/android/settings/homepage/contextualcards/slices/SliceDeferredSetupCardRendererHelper.java
index 630839c..ea9e424 100644
--- a/src/com/android/settings/homepage/contextualcards/slices/SliceDeferredSetupCardRendererHelper.java
+++ b/src/com/android/settings/homepage/contextualcards/slices/SliceDeferredSetupCardRendererHelper.java
@@ -17,6 +17,7 @@
 package com.android.settings.homepage.contextualcards.slices;
 
 import android.app.PendingIntent;
+import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.util.Log;
 import android.view.View;
@@ -33,8 +34,9 @@
 
 import com.android.settings.R;
 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.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 /**
  * Card renderer helper for {@link ContextualCard} built as slice deferred setup card.
@@ -65,10 +67,14 @@
             } catch (PendingIntent.CanceledException e) {
                 Log.w(TAG, "Failed to start intent " + primaryAction.getTitle());
             }
-            final ContextualCardFeatureProvider contextualCardFeatureProvider =
-                    FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider(mContext);
-            contextualCardFeatureProvider.logContextualCardClick(card, 0 /* row */,
+            final String log = ContextualCardLogUtils.buildCardClickLog(card, 0 /* row */,
                     EventInfo.ACTION_TYPE_CONTENT, view.getAdapterPosition());
+
+            final MetricsFeatureProvider metricsFeatureProvider =
+                    FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
+
+            metricsFeatureProvider.action(mContext,
+                    SettingsEnums.ACTION_CONTEXTUAL_CARD_CLICK, log);
         });
     }
 
diff --git a/src/com/android/settings/homepage/contextualcards/slices/SliceFullCardRendererHelper.java b/src/com/android/settings/homepage/contextualcards/slices/SliceFullCardRendererHelper.java
index bb58e6d..a9a8346 100644
--- a/src/com/android/settings/homepage/contextualcards/slices/SliceFullCardRendererHelper.java
+++ b/src/com/android/settings/homepage/contextualcards/slices/SliceFullCardRendererHelper.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.homepage.contextualcards.slices;
 
+import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.view.View;
 import android.widget.LinearLayout;
@@ -26,8 +27,9 @@
 
 import com.android.settings.R;
 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.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 /**
  * Card renderer helper for {@link ContextualCard} built as slice full card.
@@ -54,11 +56,14 @@
         // Set this listener so we can log the interaction users make on the slice
         cardHolder.sliceView.setOnSliceActionListener(
                 (eventInfo, sliceItem) -> {
-                    final ContextualCardFeatureProvider contextualCardFeatureProvider =
-                            FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider(
-                                    mContext);
-                    contextualCardFeatureProvider.logContextualCardClick(card, eventInfo.rowIndex,
+                    final String log = ContextualCardLogUtils.buildCardClickLog(card, eventInfo.rowIndex,
                             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
diff --git a/src/com/android/settings/homepage/contextualcards/slices/SliceHalfCardRendererHelper.java b/src/com/android/settings/homepage/contextualcards/slices/SliceHalfCardRendererHelper.java
index 6bb2208..f774542 100644
--- a/src/com/android/settings/homepage/contextualcards/slices/SliceHalfCardRendererHelper.java
+++ b/src/com/android/settings/homepage/contextualcards/slices/SliceHalfCardRendererHelper.java
@@ -17,6 +17,7 @@
 package com.android.settings.homepage.contextualcards.slices;
 
 import android.app.PendingIntent;
+import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.util.Log;
 import android.view.View;
@@ -32,8 +33,9 @@
 
 import com.android.settings.R;
 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.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 /**
  * Card renderer helper for {@link ContextualCard} built as slice half card.
@@ -63,10 +65,14 @@
             } catch (PendingIntent.CanceledException e) {
                 Log.w(TAG, "Failed to start intent " + primaryAction.getTitle());
             }
-            final ContextualCardFeatureProvider contextualCardFeatureProvider =
-                    FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider(mContext);
-            contextualCardFeatureProvider.logContextualCardClick(card, 0 /* row */,
+            final String log = ContextualCardLogUtils.buildCardClickLog(card, 0 /* row */,
                     EventInfo.ACTION_TYPE_CONTENT, view.getAdapterPosition());
+
+            final MetricsFeatureProvider metricsFeatureProvider =
+                    FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
+
+            metricsFeatureProvider.action(mContext,
+                    SettingsEnums.ACTION_CONTEXTUAL_CARD_CLICK, log);
         });
     }
 
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java
index 40e6939..d3f6499 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java
@@ -22,12 +22,15 @@
 
 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.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
+import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.net.Uri;
 
@@ -182,8 +185,10 @@
 
         mContextualCardLoader.getDisplayableCards(new ArrayList<>());
 
-        verify(mFakeFeatureFactory.mContextualCardFeatureProvider).logContextualCardDisplay(
-                anyList(), anyList());
+        verify(mFakeFeatureFactory.metricsFeatureProvider).action(any(),
+                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
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/logging/ContextualCardLogUtilsTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/logging/ContextualCardLogUtilsTest.java
new file mode 100644
index 0000000..5207315
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/logging/ContextualCardLogUtilsTest.java
@@ -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);
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardControllerTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardControllerTest.java
index 94fbb97..51d2523 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardControllerTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardControllerTest.java
@@ -18,13 +18,15 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
 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.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
+import android.app.settings.SettingsEnums;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
@@ -96,7 +98,8 @@
         cr.close();
 
         assertThat(qryDismissed).isEqualTo(1);
-        verify(mFeatureFactory.mContextualCardFeatureProvider).logContextualCardDismiss(card);
+        verify(mFeatureFactory.metricsFeatureProvider).action(any(),
+                eq(SettingsEnums.ACTION_CONTEXTUAL_CARD_DISMISS), any(String.class));
     }
 
     @Test