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

Commit c1c2bfa7 authored by thiruram's avatar thiruram
Browse files

Initial draft of smart folder logging to clearcut pipeline.

* Adds additional fields to launcher_log.proto to capture smart folder related information.
* Uses ProtoLite to generate log object using builder pattern and converts to nano version before writing to clearcut. Hence not making drastic change to existing logging pattern.

Change-Id: I89b10da8d4e35e3abc7ddb553046946f91b43445
parent 421946a8
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -32,7 +32,7 @@ android_library {
}

java_library_static {
    name: "launcher-log-protos-lite",
    name: "launcher_log_protos_lite",
    srcs: [
        "protos/*.proto",
        "proto_overrides/*.proto",
@@ -45,4 +45,5 @@ java_library_static {
            "proto_overrides",
        ],
    },
    static_libs: ["libprotobuf-java-lite"],
}
+11 −3
Original line number Diff line number Diff line
@@ -48,7 +48,9 @@ LOCAL_STATIC_ANDROID_LIBRARIES := \
    androidx.preference_preference \
    iconloader_base

LOCAL_STATIC_JAVA_LIBRARIES := LauncherPluginLib
LOCAL_STATIC_JAVA_LIBRARIES := \
    LauncherPluginLib \
    launcher_log_protos_lite

LOCAL_SRC_FILES := \
    $(call all-proto-files-under, protos) \
@@ -144,7 +146,10 @@ LOCAL_USE_AAPT2 := true
LOCAL_AAPT2_ONLY := true
LOCAL_MODULE_TAGS := optional

LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
LOCAL_STATIC_JAVA_LIBRARIES := \
    SystemUISharedLib \
    launcherprotosnano \
    launcher_log_protos_lite
ifneq (,$(wildcard frameworks/base))
  LOCAL_PRIVATE_PLATFORM_APIS := true
else
@@ -213,7 +218,10 @@ include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE_TAGS := optional

LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
LOCAL_STATIC_JAVA_LIBRARIES := \
    SystemUISharedLib \
    launcherprotosnano \
    launcher_log_protos_lite
ifneq (,$(wildcard frameworks/base))
  LOCAL_PRIVATE_PLATFORM_APIS := true
else
+31 −1
Original line number Diff line number Diff line
@@ -58,6 +58,35 @@ message Target {
  optional TipType tip_type = 17;
  optional int32 search_query_length = 18;
  optional bool is_work_app = 19;
  optional FromFolderLabelState from_folder_label_state = 20;
  optional ToFolderLabelState to_folder_label_state = 21;

  // Note: proto does not support duplicate enum values, even if they belong to different enum type.
  // Hence "FROM" and "TO" prefix added.
  enum FromFolderLabelState{
    FROM_FOLDER_LABEL_STATE_UNSPECIFIED = 0;
    FROM_EMPTY = 1;
    FROM_CUSTOM = 2;
    FROM_SUGGESTED = 3;
  }

  enum ToFolderLabelState{
    TO_FOLDER_LABEL_STATE_UNSPECIFIED = 0;
    TO_SUGGESTION0_WITH_VALID_PRIMARY = 1;
    TO_SUGGESTION1_WITH_VALID_PRIMARY = 2;
    TO_SUGGESTION1_WITH_EMPTY_PRIMARY = 3;
    TO_SUGGESTION2_WITH_VALID_PRIMARY = 4;
    TO_SUGGESTION2_WITH_EMPTY_PRIMARY = 5;
    TO_SUGGESTION3_WITH_VALID_PRIMARY = 6;
    TO_SUGGESTION3_WITH_EMPTY_PRIMARY = 7;
    TO_EMPTY_WITH_VALID_SUGGESTIONS = 8;
    TO_EMPTY_WITH_EMPTY_SUGGESTIONS = 9;
    TO_EMPTY_WITH_SUGGESTIONS_DISABLED = 10;
    TO_CUSTOM_WITH_VALID_SUGGESTIONS = 11;
    TO_CUSTOM_WITH_EMPTY_SUGGESTIONS = 12;
    TO_CUSTOM_WITH_SUGGESTIONS_DISABLED = 13;
    UNCHANGED = 14;
  }
}

// Used to define what type of item a Target would represent.
@@ -141,7 +170,8 @@ message Action {
    AUTOMATED = 1;
    COMMAND = 2;
    TIP = 3;
    // SOFT_KEYBOARD, HARD_KEYBOARD, ASSIST
    SOFT_KEYBOARD = 4;
    // HARD_KEYBOARD, ASSIST
  }

  enum Touch {
+154 −23
Original line number Diff line number Diff line
@@ -16,9 +16,23 @@

package com.android.launcher3.folder;

import static android.text.TextUtils.isEmpty;

import static androidx.core.util.Preconditions.checkNotNull;

import static com.android.launcher3.FolderInfo.FLAG_MANUAL_FOLDER_NAME;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_CUSTOM;
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_EMPTY;
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_SUGGESTED;

import static java.util.Arrays.asList;
import static java.util.Arrays.stream;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -75,22 +89,24 @@ import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.logging.LoggerUtils;
import com.android.launcher3.pageindicators.PageIndicatorDots;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.userevent.LauncherLogProto.Action;
import com.android.launcher3.userevent.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.LauncherLogProto.ItemType;
import com.android.launcher3.userevent.LauncherLogProto.LauncherEvent;
import com.android.launcher3.userevent.LauncherLogProto.Target;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.ClipPathView;
import com.android.launcher3.widget.PendingAddShortcutInfo;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * Represents a set of icons chosen by the user or generated by the system.
@@ -188,6 +204,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
    @Thunk int mScrollHintDir = SCROLL_NONE;
    @Thunk int mCurrentScrollDir = SCROLL_NONE;

    private String mPreviousLabel;
    private boolean mIsPreviousLabelSuggested;

    /**
     * Used to inflate the Workspace from XML.
     *
@@ -302,7 +321,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
    public void startEditingFolderName() {
        post(() -> {
            if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
                if (TextUtils.isEmpty(mFolderName.getText())) {
                if (isEmpty(mFolderName.getText())) {
                    FolderNameInfo[] nameInfos =
                            (FolderNameInfo[]) mInfo.suggestedFolderNames.getParcelableArrayExtra(
                                    FolderInfo.EXTRA_FOLDER_SUGGESTIONS);
@@ -326,7 +345,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
        }

        mInfo.title = newTitle;
        mInfo.setOption(FolderInfo.FLAG_MANUAL_FOLDER_NAME, mFolderName.isEnteredCompose(),
        mInfo.setOption(FLAG_MANUAL_FOLDER_NAME, mFolderName.isEnteredCompose(),
                mLauncher.getModelWriter());
        mFolderIcon.onTitleChanged(newTitle);
        mLauncher.getModelWriter().updateItemInDatabase(mInfo);
@@ -337,7 +356,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
            // suggested, apply different hint.
            mFolderName.setHint("");
        } else {
            if (TextUtils.isEmpty(mInfo.title)) {
            if (isEmpty(mInfo.title)) {
                mFolderName.setHint(R.string.folder_hint_text);
                mFolderName.setText("");
            } else {
@@ -425,8 +444,10 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
        mItemsInvalidated = true;
        mInfo.addListener(this);

        if (!TextUtils.isEmpty(mInfo.title)) {
        if (!isEmpty(mInfo.title)) {
            mFolderName.setText(mInfo.title);
            mPreviousLabel = mInfo.title.toString();
            mIsPreviousLabelSuggested = !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
            mFolderName.setHint(null);
        } else {
            mFolderName.setText("");
@@ -452,8 +473,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
        if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
            mInfo.suggestedFolderNames = new Intent().putExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS,
                    nameInfos);
            if (TextUtils.isEmpty(mFolderName.getText().toString())
                    && !mInfo.hasOption(FolderInfo.FLAG_MANUAL_FOLDER_NAME)) {
            if (isEmpty(mFolderName.getText().toString())
                    && !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME)) {
                showLabelSuggestion(nameInfos);
            }
        }
@@ -469,14 +490,14 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
        }
        // Open the Folder and Keyboard when the first or second suggestion is valid non-empty
        // string.
        boolean shouldOpen = nameInfos.length > 0 && nameInfos[0] != null && !TextUtils.isEmpty(
        boolean shouldOpen = nameInfos.length > 0 && nameInfos[0] != null && !isEmpty(
                nameInfos[0].getLabel())
                || nameInfos.length > 1 && nameInfos[1] != null && !TextUtils.isEmpty(
                || nameInfos.length > 1 && nameInfos[1] != null && !isEmpty(
                nameInfos[1].getLabel());
        CharSequence firstLabel = nameInfos[0].getLabel();

        if (shouldOpen) {
            if (!TextUtils.isEmpty(firstLabel)) {
            if (!isEmpty(firstLabel)) {
                mFolderName.setHint("");
                mFolderName.setText(firstLabel);
                mInfo.title = firstLabel;
@@ -484,7 +505,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
            animateOpen(mInfo.contents, 0, true);
            mFolderName.showKeyboard();
            mFolderName.displayCompletions(
                    Arrays.asList(nameInfos).subList(1, nameInfos.length).stream()
                    asList(nameInfos).subList(1, nameInfos.length).stream()
                            .filter(Objects::nonNull)
                            .map(s -> s.getLabel().toString())
                            .collect(Collectors.toList()));
@@ -636,9 +657,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo

                if (!skipUserEventLog) {
                    mLauncher.getUserEventDispatcher().logActionOnItem(
                        Touch.TAP,
                        Direction.NONE,
                        ItemType.FOLDER_ICON, mInfo.cellX, mInfo.cellY);
                            LauncherLogProto.Action.Touch.TAP,
                            LauncherLogProto.Action.Direction.NONE,
                            LauncherLogProto.ItemType.FOLDER_ICON, mInfo.cellX, mInfo.cellY);
                }


@@ -1420,6 +1441,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
            if (hasFocus) {
                startEditingFolderName();
            } else {
                logEditFolderLabel();
                mFolderName.dispatchBackKey();
            }
        }
@@ -1433,11 +1455,12 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
    }

    @Override
    public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
    public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
            LauncherLogProto.Target targetParent) {
        target.gridX = info.cellX;
        target.gridY = info.cellY;
        target.pageIndex = mContent.getCurrentPage();
        targetParent.containerType = ContainerType.FOLDER;
        targetParent.containerType = LauncherLogProto.ContainerType.FOLDER;
    }

    private class OnScrollHintListener implements OnAlarmListener {
@@ -1535,7 +1558,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo

    @Override
    public int getLogContainerType() {
        return ContainerType.FOLDER;
        return LauncherLogProto.ContainerType.FOLDER;
    }

    /**
@@ -1570,7 +1593,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
                    }
                } else {
                    mLauncher.getUserEventDispatcher().logActionTapOutside(
                            LoggerUtils.newContainerTarget(ContainerType.FOLDER));
                            LoggerUtils.newContainerTarget(LauncherLogProto.ContainerType.FOLDER));
                    close(true);
                    return true;
                }
@@ -1600,4 +1623,112 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
            super.draw(canvas);
        }
    }

    private void logEditFolderLabel() {
        LauncherEvent launcherEvent = LauncherEvent.newBuilder()
                .setAction(Action.newBuilder().setType(Action.Type.SOFT_KEYBOARD))
                .addSrcTarget(newEditTextTargetBuilder()
                        .setFromFolderLabelState(getFromFolderLabelState())
                        .setToFolderLabelState(getToFolderLabelState()))
                .addSrcTarget(newFolderTargetBuilder())
                .addSrcTarget(newParentContainerTarget())
                .build();
        mLauncher.getUserEventDispatcher().logLauncherEvent(launcherEvent);
        mPreviousLabel = mFolderName.getText().toString();
        mIsPreviousLabelSuggested = !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
    }

    private Target.FromFolderLabelState getFromFolderLabelState() {
        return mPreviousLabel == null
                ? FROM_FOLDER_LABEL_STATE_UNSPECIFIED
                : mPreviousLabel.isEmpty()
                ? FROM_EMPTY
                : mIsPreviousLabelSuggested
                ? FROM_SUGGESTED
                : FROM_CUSTOM;
    }

    private Target.ToFolderLabelState getToFolderLabelState() {
        String newLabel =
                checkNotNull(mFolderName.getText().toString(),
                        "Expected valid folder label, but found null");

        Optional<String[]> suggestedLabels = Optional.ofNullable(
                (FolderNameInfo[]) mInfo.suggestedFolderNames
                        .getParcelableArrayExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS))
                .map(folderNameInfoArray ->
                        stream(folderNameInfoArray)
                                .map(FolderNameInfo::getLabel)
                                .map(CharSequence::toString)
                                .toArray(String[]::new));


        int accepted_suggestion_index = suggestedLabels
                .map(folderNameInfoArray ->
                        IntStream.range(0, folderNameInfoArray.length)
                                .filter(index -> newLabel.equalsIgnoreCase(
                                        folderNameInfoArray[index]))
                                .findFirst()
                                .orElse(-1)
                ).orElse(-1);

        boolean hasValidPrimary = suggestedLabels
                .map(labels -> labels.length > 0 && !isEmpty(labels[0]))
                .orElse(false);
        String primarySuffix = hasValidPrimary
                ? "_WITH_VALID_PRIMARY"
                : "_WITH_EMPTY_PRIMARY";

        boolean isEmptySuggestions = suggestedLabels
                .map(labels -> stream(labels).allMatch(TextUtils::isEmpty))
                .orElse(true);
        boolean isSuggestionsEnabled = FeatureFlags.FOLDER_NAME_SUGGEST.get();
        String suggestionsSuffix =  isSuggestionsEnabled
                ? isEmptySuggestions
                    ? "_WITH_EMPTY_SUGGESTIONS"
                    : "_WITH_VALID_SUGGESTIONS"
                : "_WITH_SUGGESTIONS_DISABLED";

        return newLabel.equals(mPreviousLabel)
                ? Target.ToFolderLabelState.UNCHANGED
                : newLabel.isEmpty()
                    ? Target.ToFolderLabelState.valueOf("TO_EMPTY" + suggestionsSuffix)
                    : accepted_suggestion_index >= 0
                        ? Target.ToFolderLabelState.valueOf("TO_SUGGESTION"
                            + accepted_suggestion_index
                            + primarySuffix)
                        : Target.ToFolderLabelState.valueOf("TO_CUSTOM" + suggestionsSuffix);
    }


    private Target.Builder newEditTextTargetBuilder() {
        return Target.newBuilder().setType(Target.Type.ITEM).setItemType(ItemType.EDITTEXT);
    }

    private Target.Builder newFolderTargetBuilder() {
        return Target.newBuilder()
                .setType(Target.Type.CONTAINER)
                .setContainerType(ContainerType.FOLDER)
                .setPageIndex(mInfo.screenId)
                .setGridX(mInfo.cellX)
                .setGridY(mInfo.cellY)
                .setCardinality(mInfo.contents.size());
    }

    private Target.Builder newParentContainerTarget() {
        Target.Builder builder = Target.newBuilder().setType(Target.Type.CONTAINER);

        switch (mInfo.container) {
            case CONTAINER_HOTSEAT:
                return builder.setContainerType(ContainerType.HOTSEAT);
            case CONTAINER_DESKTOP:
                return builder.setContainerType(ContainerType.WORKSPACE);
            default:
                throw new AssertionError(String
                        .format("Expected container to be either %s or %s but found %s.",
                                CONTAINER_HOTSEAT,
                                CONTAINER_DESKTOP,
                                mInfo.container));
        }
    }
}
+20 −2
Original line number Diff line number Diff line
@@ -69,8 +69,7 @@ import java.util.UUID;
public class UserEventDispatcher implements ResourceBasedOverride {

    private static final String TAG = "UserEvent";
    private static final boolean IS_VERBOSE =
            FeatureFlags.IS_DOGFOOD_BUILD && Utilities.isPropertyEnabled(LogConfig.USEREVENT);
    private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.USEREVENT);
    private static final String UUID_STORAGE = "uuid";

    public static UserEventDispatcher newInstance(Context context,
@@ -372,6 +371,25 @@ public class UserEventDispatcher implements ResourceBasedOverride {
        dispatchUserEvent(event, null);
    }

    /**
     * Logs proto lite version of LauncherEvent object to clearcut.
     */
    public void logLauncherEvent(
                com.android.launcher3.userevent.LauncherLogProto.LauncherEvent launcherEvent) {

        if (mPreviousHomeGesture) {
            mPreviousHomeGesture = false;
        }
        mAppOrTaskLaunch = false;
        launcherEvent.toBuilder()
            .setElapsedContainerMillis(SystemClock.uptimeMillis() - mElapsedContainerMillis)
            .setElapsedSessionMillis(SystemClock.uptimeMillis() - mElapsedSessionMillis).build();
        if (!IS_VERBOSE) {
            return;
        }
        Log.d(TAG, launcherEvent.toString());
    }

    public void logDeepShortcutsOpen(View icon) {
        LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(icon);
        if (icon == null || !(icon.getTag() instanceof ItemInfo || provider == null)) {
Loading