Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5f8fe2a4 authored by Fan Zhang's avatar Fan Zhang Committed by android-build-merger
Browse files

Merge "Record all contextual card log to MetricsFeatureProvider" into qt-dev

am: dc4f7ce2

Change-Id: Ib4122ef0cb900ba5afff73e25e1f0ffe6bcb50c1
parents 7924c897 dc4f7ce2
Loading
Loading
Loading
Loading
+13 −4
Original line number Diff line number Diff line
@@ -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_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.NonNull;
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 @@ public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard>
            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));
            }
        }
    }
+263 −0
Original line number Diff line number Diff line
/*
 * 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;
        }
    }
}
+10 −4
Original line number Diff line number Diff line
@@ -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.R;
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 @@ public class SliceContextualCardController implements ContextualCardController {
            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
+10 −4
Original line number Diff line number Diff line
@@ -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 androidx.slice.widget.EventInfo;

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 @@ class SliceDeferredSetupCardRendererHelper {
            } 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);
        });
    }

+10 −5
Original line number Diff line number Diff line
@@ -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 androidx.slice.widget.SliceView;

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 @@ class SliceFullCardRendererHelper {
        // 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
Loading