Merge "Add log feature"
diff --git a/res/values/config.xml b/res/values/config.xml
index 6b0a10e..edd948f7 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -151,6 +151,9 @@
         com.android.settings.intelligence
     </string>
 
+    <!-- Settings intelligence interaction log intent action -->
+    <string name="config_settingsintelligence_log_action" translatable="false"></string>
+
     <!-- Emergency app package name -->
     <string name="config_emergency_package_name" translatable="false">
         com.android.emergency
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProvider.java b/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProvider.java
new file mode 100644
index 0000000..8583f38
--- /dev/null
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProvider.java
@@ -0,0 +1,39 @@
+/*
+ * 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;
+
+import android.content.Context;
+
+import java.util.List;
+
+/** Feature provider for the contextual card feature. */
+public interface ContextualCardFeatureProvider {
+
+    /** Homepage displays. */
+    public void logHomepageDisplay(Context context, Long latency);
+
+    /** When user clicks dismiss in contextual card */
+    public void logContextualCardDismiss(Context context, ContextualCard card);
+
+    /** After ContextualCardManager decides which cards will be displayed/hidden */
+    public void logContextualCardDisplay(Context context, List<ContextualCard> showedCards,
+            List<ContextualCard> hiddenCards);
+
+    /** When user clicks toggle/title area of a contextual card. */
+    public void logContextualCardClick(Context context, ContextualCard card, int row,
+            int tapTarget);
+}
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImpl.java b/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImpl.java
new file mode 100644
index 0000000..e437e2b
--- /dev/null
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImpl.java
@@ -0,0 +1,136 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.content.Intent;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.slice.widget.EventInfo;
+
+import com.android.settings.R;
+
+import java.util.List;
+
+public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureProvider {
+    private static final String TAG = "ContextualCardFeature";
+
+    // Contextual card interaction logs
+    // Settings Homepage shows
+    private static final int CONTEXTUAL_HOME_SHOW = 38;
+
+    // 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;
+
+    // Contextual card is clicked , log card name, score, tap area
+    private static final int CONTEXTUAL_CARD_CLICK = 42;
+
+    // SettingsLogBroadcastReceiver contracts
+    // contextual card name
+    private static final String EXTRA_CONTEXTUALCARD_NAME = "name";
+
+    // contextual card score
+    private static final String EXTRA_CONTEXTUALCARD_SCORE = "score";
+
+    // contextual card clicked row
+    private static final String EXTRA_CONTEXTUALCARD_ROW = "row";
+
+    // contextual card tap target
+    private static final String EXTRA_CONTEXTUALCARD_TAP_TARGET = "target";
+
+    // contextual homepage display latency
+    private static final String EXTRA_LATENCY = "latency";
+
+    // log type
+    private static final String EXTRA_CONTEXTUALCARD_ACTION_TYPE = "type";
+
+
+    // Contextual card tap target
+    private static final int TARGET_DEFAULT = 0;
+
+    // Click title area
+    private static final int TARGET_TITLE = 1;
+
+    // Click toggle
+    private static final int TARGET_TOGGLE = 2;
+
+    // Click slider
+    private static final int TARGET_SLIDER = 3;
+
+    @Override
+    public void logHomepageDisplay(Context context, Long latency) {
+    }
+
+    @Override
+    public void logContextualCardDismiss(Context context, ContextualCard card) {
+        final Intent intent = new Intent();
+        intent.putExtra(EXTRA_CONTEXTUALCARD_ACTION_TYPE, CONTEXTUAL_CARD_DISMISS);
+        intent.putExtra(EXTRA_CONTEXTUALCARD_NAME, card.getName());
+        intent.putExtra(EXTRA_CONTEXTUALCARD_SCORE, card.getRankingScore());
+        sendBroadcast(context, intent);
+    }
+
+    @Override
+    public void logContextualCardDisplay(Context context, List<ContextualCard> showCards,
+            List<ContextualCard> hiddenCards) {
+    }
+
+    @Override
+    public void logContextualCardClick(Context context, ContextualCard card, int row,
+            int actionType) {
+        final Intent intent = new Intent();
+        intent.putExtra(EXTRA_CONTEXTUALCARD_ACTION_TYPE, CONTEXTUAL_CARD_CLICK);
+        intent.putExtra(EXTRA_CONTEXTUALCARD_NAME, card.getName());
+        intent.putExtra(EXTRA_CONTEXTUALCARD_SCORE, card.getRankingScore());
+        intent.putExtra(EXTRA_CONTEXTUALCARD_ROW, row);
+        intent.putExtra(EXTRA_CONTEXTUALCARD_TAP_TARGET, actionTypeToTapTarget(actionType));
+        sendBroadcast(context, intent);
+    }
+
+    @VisibleForTesting
+    void sendBroadcast(final Context context, final Intent intent) {
+        intent.setPackage(context.getString(R.string.config_settingsintelligence_package_name));
+        final String action = context.getString(R.string.config_settingsintelligence_log_action);
+        if (!TextUtils.isEmpty(action)) {
+            intent.setAction(action);
+            context.sendBroadcast(intent);
+        }
+    }
+
+    private int actionTypeToTapTarget(int actionType) {
+        switch (actionType) {
+            case EventInfo.ACTION_TYPE_CONTENT:
+                return TARGET_TITLE;
+            case EventInfo.ACTION_TYPE_TOGGLE:
+                return TARGET_TOGGLE;
+            case EventInfo.ACTION_TYPE_SLIDER:
+                return TARGET_SLIDER;
+            default:
+                Log.w(TAG, "unknown type " + actionType);
+                return 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 3368580..4378be3 100644
--- a/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardController.java
+++ b/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardController.java
@@ -26,8 +26,10 @@
 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.ContextualCardUpdateListener;
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.utils.ThreadUtils;
 
 /**
@@ -67,6 +69,9 @@
             dbHelper.markContextualCardAsDismissed(mContext, card.getName());
         });
         showFeedbackDialog(card);
+        final ContextualCardFeatureProvider contexualCardFeatureProvider =
+                FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider();
+        contexualCardFeatureProvider.logContextualCardDismiss(mContext, card);
     }
 
     @Override
diff --git a/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRenderer.java b/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRenderer.java
index 74f25eb..a2d6e2b 100644
--- a/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRenderer.java
+++ b/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRenderer.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.net.Uri;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
 import android.view.View;
 import android.widget.Button;
@@ -38,10 +39,13 @@
 
 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.ContextualCardRenderer;
 import com.android.settings.homepage.contextualcards.ControllerRendererPool;
+import com.android.settings.overlay.FeatureFactory;
 
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Card renderer for {@link ContextualCard} built as slices.
@@ -58,6 +62,7 @@
     private final Context mContext;
     private final LifecycleOwner mLifecycleOwner;
     private final ControllerRendererPool mControllerRendererPool;
+    private final Set<ContextualCard> mCardSet;
 
     public SliceContextualCardRenderer(Context context, LifecycleOwner lifecycleOwner,
             ControllerRendererPool controllerRendererPool) {
@@ -65,6 +70,7 @@
         mLifecycleOwner = lifecycleOwner;
         mSliceLiveDataMap = new ArrayMap<>();
         mControllerRendererPool = controllerRendererPool;
+        mCardSet = new ArraySet<>();
     }
 
     @Override
@@ -99,6 +105,7 @@
             sliceLiveData = SliceLiveData.fromUri(mContext, uri);
             mSliceLiveDataMap.put(uri.toString(), sliceLiveData);
         }
+        mCardSet.add(card);
 
         sliceLiveData.removeObservers(mLifecycleOwner);
         sliceLiveData.observe(mLifecycleOwner, slice -> {
@@ -128,14 +135,27 @@
 
         final Button btnRemove = cardHolder.itemView.findViewById(R.id.remove);
         btnRemove.setOnClickListener(v -> {
-            mControllerRendererPool.getController(mContext, card.getCardType()).onDismissed(
-                    card);
+            mControllerRendererPool.getController(mContext, card.getCardType()).onDismissed(card);
         });
     }
 
     @Override
     public void onSliceAction(@NonNull EventInfo eventInfo, @NonNull SliceItem sliceItem) {
         //TODO(b/79698338): Log user interaction
+
+        // sliceItem.getSlice().getUri() is like
+        // content://android.settings.slices/action/wifi/_gen/0/_gen/0
+        // contextualCard.getSliceUri() is prefix of sliceItem.getSlice().getUri()
+        for (ContextualCard card : mCardSet) {
+            if (sliceItem.getSlice().getUri().toString().startsWith(
+                    card.getSliceUri().toString())) {
+                ContextualCardFeatureProvider contexualCardFeatureProvider =
+                        FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider();
+                contexualCardFeatureProvider.logContextualCardClick(mContext, card,
+                        eventInfo.rowIndex, eventInfo.actionType);
+                break;
+            }
+        }
     }
 
     public static class SliceViewHolder extends RecyclerView.ViewHolder {
diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java
index 02468b8..a52619b 100644
--- a/src/com/android/settings/overlay/FeatureFactory.java
+++ b/src/com/android/settings/overlay/FeatureFactory.java
@@ -28,6 +28,7 @@
 import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider;
 import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
 import com.android.settings.gestures.AssistGestureFeatureProvider;
+import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
 import com.android.settings.localepicker.LocaleFeatureProvider;
 import com.android.settings.panel.PanelFeatureProvider;
 import com.android.settings.search.SearchFeatureProvider;
@@ -108,6 +109,8 @@
 
     public abstract PanelFeatureProvider getPanelFeatureProvider();
 
+    public abstract ContextualCardFeatureProvider getContextualCardFeatureProvider();
+
     public static final class FactoryNotFoundException extends RuntimeException {
         public FactoryNotFoundException(Throwable throwable) {
             super("Unable to create factory. Did you misconfigure Proguard?", throwable);
diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java
index 8d6d4b6..3515ac0 100644
--- a/src/com/android/settings/overlay/FeatureFactoryImpl.java
+++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java
@@ -40,6 +40,8 @@
 import com.android.settings.fuelgauge.PowerUsageFeatureProviderImpl;
 import com.android.settings.gestures.AssistGestureFeatureProvider;
 import com.android.settings.gestures.AssistGestureFeatureProviderImpl;
+import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
+import com.android.settings.homepage.contextualcards.ContextualCardFeatureProviderImpl;
 import com.android.settings.localepicker.LocaleFeatureProvider;
 import com.android.settings.localepicker.LocaleFeatureProviderImpl;
 import com.android.settings.panel.PanelFeatureProvider;
@@ -75,6 +77,7 @@
     private SlicesFeatureProvider mSlicesFeatureProvider;
     private AccountFeatureProvider mAccountFeatureProvider;
     private PanelFeatureProvider mPanelFeatureProvider;
+    private ContextualCardFeatureProvider mContextualCardFeatureProvider;
 
     @Override
     public SupportFeatureProvider getSupportFeatureProvider(Context context) {
@@ -220,4 +223,11 @@
         }
         return mPanelFeatureProvider;
     }
+
+    public ContextualCardFeatureProvider getContextualCardFeatureProvider() {
+        if (mContextualCardFeatureProvider == null) {
+            mContextualCardFeatureProvider = new ContextualCardFeatureProviderImpl();
+        }
+        return mContextualCardFeatureProvider;
+    }
 }
diff --git a/tests/robotests/res/values-mcc999/config.xml b/tests/robotests/res/values-mcc999/config.xml
index 9bba3c3..c5c552e 100644
--- a/tests/robotests/res/values-mcc999/config.xml
+++ b/tests/robotests/res/values-mcc999/config.xml
@@ -73,6 +73,11 @@
         <item>fake_package/fake_service</item>
     </string-array>
 
+    <!-- Settings intelligence interaction log intent action -->
+    <string name="config_settingsintelligence_log_action" translatable="false">
+        aaa.bbb.ccc
+    </string>
+
     <!-- List of packages that should be whitelisted for slice uri access. Do not translate -->
     <string-array name="slice_whitelist_package_names" translatable="false">
         <item>com.android.settings.slice_whitelist_package</item>
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImplTest.java
new file mode 100644
index 0000000..08631f7
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImplTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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;
+
+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 com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class ContextualCardFeatureProviderImplTest {
+
+    private Context mContext;
+    private ContextualCardFeatureProviderImpl mImpl;
+
+    @Before
+    public void setUp() {
+        mContext = spy(RuntimeEnvironment.application);
+        mImpl = new ContextualCardFeatureProviderImpl();
+    }
+
+    @Test
+    public void sendBroadcast_emptyAction_notSendBroadcast() {
+        final Intent intent = new Intent();
+        mImpl.sendBroadcast(mContext, intent);
+
+        verify(mContext, never()).sendBroadcast(intent);
+    }
+
+    @Test
+    @Config(qualifiers = "mcc999")
+    public void sendBroadcast_hasAction_sendBroadcast() {
+        final Intent intent = new Intent();
+        mImpl.sendBroadcast(mContext, intent);
+
+        verify(mContext).sendBroadcast(intent);
+    }
+}
\ 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 29e309d..af3b2e8 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,9 +18,10 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 
 import android.content.ContentResolver;
 import android.content.ContentValues;
@@ -33,6 +34,7 @@
 import com.android.settings.homepage.contextualcards.ContextualCard;
 import com.android.settings.homepage.contextualcards.ContextualCardFeedbackDialog;
 import com.android.settings.homepage.contextualcards.ContextualCardsFragment;
+import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
@@ -57,6 +59,7 @@
     private CardContentProvider mProvider;
     private ContentResolver mResolver;
     private SliceContextualCardController mController;
+    private FakeFeatureFactory mFeatureFactory;
 
     @Before
     public void setUp() {
@@ -67,6 +70,7 @@
                 mProvider);
         mResolver = mContext.getContentResolver();
         mController = spy(new SliceContextualCardController(mContext));
+        mFeatureFactory = FakeFeatureFactory.setupForTest();
     }
 
     @Test
@@ -75,7 +79,8 @@
         mResolver.insert(providerUri, generateOneRow());
         doNothing().when(mController).showFeedbackDialog(any(ContextualCard.class));
 
-        mController.onDismissed(getTestSliceCard());
+        final ContextualCard card = getTestSliceCard();
+        mController.onDismissed(card);
 
         final String[] columns = {CardDatabaseHelper.CardColumns.CARD_DISMISSED};
         final String selection = CardDatabaseHelper.CardColumns.NAME + "=?";
@@ -86,6 +91,8 @@
         cr.close();
 
         assertThat(qryDismissed).isEqualTo(1);
+        verify(mFeatureFactory.mContextualCardFeatureProvider).logContextualCardDismiss(
+                mContext, card);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
index e14ef1f..978dd7d 100644
--- a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -28,6 +28,7 @@
 import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider;
 import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
 import com.android.settings.gestures.AssistGestureFeatureProvider;
+import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
 import com.android.settings.localepicker.LocaleFeatureProvider;
 import com.android.settings.overlay.DockUpdaterFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
@@ -63,6 +64,7 @@
     public final AssistGestureFeatureProvider assistGestureFeatureProvider;
     public final AccountFeatureProvider mAccountFeatureProvider;
     public final PanelFeatureProvider mPanelFeatureProvider;
+    public final ContextualCardFeatureProvider mContextualCardFeatureProvider;
 
     public SlicesFeatureProvider slicesFeatureProvider;
     public SearchFeatureProvider searchFeatureProvider;
@@ -105,6 +107,7 @@
         slicesFeatureProvider = mock(SlicesFeatureProvider.class);
         mAccountFeatureProvider = mock(AccountFeatureProvider.class);
         mPanelFeatureProvider = mock(PanelFeatureProvider.class);
+        mContextualCardFeatureProvider = mock(ContextualCardFeatureProvider.class);
     }
 
     @Override
@@ -191,4 +194,8 @@
     public PanelFeatureProvider getPanelFeatureProvider() {
         return mPanelFeatureProvider;
     }
+
+    public ContextualCardFeatureProvider getContextualCardFeatureProvider() {
+        return mContextualCardFeatureProvider;
+    }
 }