Loading Android.bp +2 −1 Original line number Diff line number Diff line Loading @@ -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", Loading @@ -45,4 +45,5 @@ java_library_static { "proto_overrides", ], }, static_libs: ["libprotobuf-java-lite"], } Android.mk +11 −3 Original line number Diff line number Diff line Loading @@ -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) \ Loading Loading @@ -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 Loading Loading @@ -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 Loading protos/launcher_log.proto +31 −1 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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 { Loading src/com/android/launcher3/folder/Folder.java +154 −23 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading Loading @@ -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. * Loading Loading @@ -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); Loading @@ -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); Loading @@ -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 { Loading Loading @@ -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(""); Loading @@ -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); } } Loading @@ -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; Loading @@ -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())); Loading Loading @@ -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); } Loading Loading @@ -1420,6 +1441,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo if (hasFocus) { startEditingFolderName(); } else { logEditFolderLabel(); mFolderName.dispatchBackKey(); } } Loading @@ -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 { Loading Loading @@ -1535,7 +1558,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo @Override public int getLogContainerType() { return ContainerType.FOLDER; return LauncherLogProto.ContainerType.FOLDER; } /** Loading Loading @@ -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; } Loading Loading @@ -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)); } } } src/com/android/launcher3/logging/UserEventDispatcher.java +20 −2 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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 Loading
Android.bp +2 −1 Original line number Diff line number Diff line Loading @@ -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", Loading @@ -45,4 +45,5 @@ java_library_static { "proto_overrides", ], }, static_libs: ["libprotobuf-java-lite"], }
Android.mk +11 −3 Original line number Diff line number Diff line Loading @@ -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) \ Loading Loading @@ -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 Loading Loading @@ -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 Loading
protos/launcher_log.proto +31 −1 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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 { Loading
src/com/android/launcher3/folder/Folder.java +154 −23 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading Loading @@ -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. * Loading Loading @@ -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); Loading @@ -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); Loading @@ -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 { Loading Loading @@ -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(""); Loading @@ -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); } } Loading @@ -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; Loading @@ -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())); Loading Loading @@ -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); } Loading Loading @@ -1420,6 +1441,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo if (hasFocus) { startEditingFolderName(); } else { logEditFolderLabel(); mFolderName.dispatchBackKey(); } } Loading @@ -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 { Loading Loading @@ -1535,7 +1558,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo @Override public int getLogContainerType() { return ContainerType.FOLDER; return LauncherLogProto.ContainerType.FOLDER; } /** Loading Loading @@ -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; } Loading Loading @@ -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)); } } }
src/com/android/launcher3/logging/UserEventDispatcher.java +20 −2 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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