Fixes folder logging. am: b53764bd4b
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/11902346
Change-Id: Ib9b34794311a41e0b54c19d1c0097f6d36680917
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index e496807..2fd807d 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -34,6 +34,9 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
+import com.android.launcher3.logger.LauncherAtom.FolderIcon;
+import com.android.launcher3.logger.LauncherAtom.FromState;
+import com.android.launcher3.logger.LauncherAtom.ToState;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.InstanceIdSequence;
import com.android.launcher3.logging.StatsLogManager;
@@ -174,6 +177,9 @@
private Optional<ContainerInfo> mContainerInfo = Optional.empty();
private int mSrcState = LAUNCHER_UICHANGED__SRC_STATE__HOME;
private int mDstState = LAUNCHER_UICHANGED__DST_STATE__BACKGROUND;
+ private Optional<FromState> mFromState = Optional.empty();
+ private Optional<ToState> mToState = Optional.empty();
+ private Optional<String> mEditText = Optional.empty();
@Override
public StatsLogger withItemInfo(ItemInfo itemInfo) {
@@ -220,25 +226,33 @@
}
@Override
+ public StatsLogger withFromState(FromState fromState) {
+ this.mFromState = Optional.of(fromState);
+ return this;
+ }
+
+ @Override
+ public StatsLogger withToState(ToState toState) {
+ this.mToState = Optional.of(toState);
+ return this;
+ }
+
+ @Override
+ public StatsLogger withEditText(String editText) {
+ this.mEditText = Optional.of(editText);
+ return this;
+ }
+
+ @Override
public void log(EventEnum event) {
if (!Utilities.ATLEAST_R) {
return;
}
- LauncherAtom.ItemInfo.Builder itemInfoBuilder =
- (LauncherAtom.ItemInfo.Builder) mItemInfo.buildProto().toBuilder();
- mRank.ifPresent(itemInfoBuilder::setRank);
- if (mContainerInfo.isPresent()) {
- // User already provided container info;
- // default container info from item info will be ignored.
- itemInfoBuilder.setContainerInfo(mContainerInfo.get());
- write(event, mInstanceId, itemInfoBuilder.build(), mSrcState, mDstState);
- return;
- }
-
if (mItemInfo.container < 0) {
// Item is not within a folder. Write to StatsLog in same thread.
- write(event, mInstanceId, itemInfoBuilder.build(), mSrcState, mDstState);
+ write(event, mInstanceId, applyOverwrites(mItemInfo.buildProto()), mSrcState,
+ mDstState);
} else {
// Item is inside the folder, fetch folder info in a BG thread
// and then write to StatsLog.
@@ -248,17 +262,33 @@
public void execute(LauncherAppState app, BgDataModel dataModel,
AllAppsList apps) {
FolderInfo folderInfo = dataModel.folders.get(mItemInfo.container);
- LauncherAtom.ItemInfo.Builder atomInfoBuilder =
- (LauncherAtom.ItemInfo.Builder) mItemInfo
- .buildProto(folderInfo).toBuilder();
- mRank.ifPresent(atomInfoBuilder::setRank);
- write(event, mInstanceId, atomInfoBuilder.build(), mSrcState,
- mDstState);
+ write(event, mInstanceId,
+ applyOverwrites(mItemInfo.buildProto(folderInfo)),
+ mSrcState, mDstState);
}
});
}
}
+ private LauncherAtom.ItemInfo applyOverwrites(LauncherAtom.ItemInfo atomInfo) {
+ LauncherAtom.ItemInfo.Builder itemInfoBuilder =
+ (LauncherAtom.ItemInfo.Builder) atomInfo.toBuilder();
+
+ mRank.ifPresent(itemInfoBuilder::setRank);
+ mContainerInfo.ifPresent(itemInfoBuilder::setContainerInfo);
+
+ if (mFromState.isPresent() || mToState.isPresent() || mEditText.isPresent()) {
+ FolderIcon.Builder folderIconBuilder = (FolderIcon.Builder) itemInfoBuilder
+ .getFolderIcon()
+ .toBuilder();
+ mFromState.ifPresent(folderIconBuilder::setFromLabelState);
+ mToState.ifPresent(folderIconBuilder::setToLabelState);
+ mEditText.ifPresent(folderIconBuilder::setLabelInfo);
+ itemInfoBuilder.setFolderIcon(folderIconBuilder);
+ }
+ return itemInfoBuilder.build();
+ }
+
private void write(EventEnum event, InstanceId instanceId, LauncherAtom.ItemInfo atomInfo,
int srcState, int dstState) {
if (IS_VERBOSE) {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index fb58f21..a8dca12 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -415,7 +415,7 @@
// Always enter the spring loaded mode
mLauncher.getStateManager().goToState(SPRING_LOADED);
- mStatsLogManager.logger().withItemInfo(dragObject.originalDragInfo)
+ mStatsLogManager.logger().withItemInfo(dragObject.dragInfo)
.withInstanceId(dragObject.logInstanceId)
.log(LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED);
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 301f79c..e950f3c 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -81,7 +81,10 @@
import com.android.launcher3.dragndrop.DragController.DragListener;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.logger.LauncherAtom.FromState;
+import com.android.launcher3.logger.LauncherAtom.ToState;
import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.FolderInfo.FolderListener;
@@ -99,6 +102,7 @@
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
+import java.util.StringJoiner;
import java.util.stream.Collectors;
/**
@@ -109,6 +113,12 @@
View.OnFocusChangeListener, DragListener, ExtendedEditText.OnBackKeyListener {
private static final String TAG = "Launcher.Folder";
private static final boolean DEBUG = false;
+
+ /**
+ * Used for separating folder title when logging together.
+ */
+ private static final CharSequence FOLDER_LABEL_DELIMITER = "~";
+
/**
* We avoid measuring {@link #mContent} with a 0 width or height, as this
* results in CellLayout being measured as UNSPECIFIED, which it does not support.
@@ -155,6 +165,8 @@
protected final Launcher mLauncher;
protected DragController mDragController;
public FolderInfo mInfo;
+ private CharSequence mFromTitle;
+ private FromState mFromLabelState;
@Thunk FolderIcon mFolderIcon;
@@ -335,7 +347,6 @@
Log.d(TAG, "onBackKey newTitle=" + newTitle);
}
mInfo.setTitle(newTitle);
- mInfo.fromCustom = mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
mInfo.setOption(FLAG_MANUAL_FOLDER_NAME, !mInfo.getAcceptedSuggestionIndex().isPresent(),
mLauncher.getModelWriter());
mFolderIcon.onTitleChanged(newTitle);
@@ -415,6 +426,8 @@
void bind(FolderInfo info) {
mInfo = info;
+ mFromTitle = info.title;
+ mFromLabelState = info.getFromLabelState();
ArrayList<WorkspaceItemInfo> children = info.contents;
Collections.sort(children, ITEM_POS_COMPARATOR);
updateItemLocationsInDatabaseBatch(true);
@@ -1442,10 +1455,38 @@
public void onFocusChange(View v, boolean hasFocus) {
if (v == mFolderName) {
if (hasFocus) {
+ mFromLabelState = mInfo.getFromLabelState();
+ mFromTitle = mInfo.title;
startEditingFolderName();
} else {
- mStatsLogManager.logger().withItemInfo(mInfo).log(LAUNCHER_FOLDER_LABEL_UPDATED);
- logFolderLabelState();
+ StatsLogger statsLogger = mStatsLogManager.logger()
+ .withItemInfo(mInfo)
+ .withFromState(mFromLabelState);
+
+ // If the folder label is suggested, it is logged to improve prediction model.
+ // When both old and new labels are logged together delimiter is used.
+ StringJoiner labelInfoBuilder = new StringJoiner(FOLDER_LABEL_DELIMITER);
+ if (mFromLabelState.equals(FromState.FROM_SUGGESTED)) {
+ labelInfoBuilder.add(mFromTitle);
+ }
+
+ ToState toLabelState;
+ if (mFromTitle != null && mFromTitle.equals(mInfo.title)) {
+ toLabelState = ToState.UNCHANGED;
+ } else {
+ toLabelState = mInfo.getToLabelState();
+ if (toLabelState.toString().startsWith("TO_SUGGESTION")) {
+ labelInfoBuilder.add(mInfo.title);
+ }
+ }
+ statsLogger.withToState(toLabelState);
+
+ if (labelInfoBuilder.length() > 0) {
+ statsLogger.withEditText(labelInfoBuilder.toString());
+ }
+
+ statsLogger.log(LAUNCHER_FOLDER_LABEL_UPDATED);
+ logFolderLabelState(mFromLabelState, toLabelState);
mFolderName.dispatchBackKey();
}
}
@@ -1650,8 +1691,8 @@
* @deprecated This method is only used for log validation and soon will be removed.
*/
@Deprecated
- public void logFolderLabelState() {
+ public void logFolderLabelState(FromState fromState, ToState toState) {
mLauncher.getUserEventDispatcher()
- .logLauncherEvent(mInfo.getFolderLabelStateLauncherEvent());
+ .logLauncherEvent(mInfo.getFolderLabelStateLauncherEvent(fromState, toState));
}
}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 7af4664..ce37506 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -20,7 +20,7 @@
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
import static com.android.launcher3.folder.PreviewItemManager.INITIAL_ITEM_ANIMATION_DURATION;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_AUTO_LABELED;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -63,6 +63,8 @@
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.icons.DotRenderer;
+import com.android.launcher3.logger.LauncherAtom.FromState;
+import com.android.launcher3.logger.LauncherAtom.ToState;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.AppInfo;
@@ -428,7 +430,6 @@
mPreviewItemManager.hidePreviewItem(finalIndex, false);
mFolder.showItem(item);
setLabelSuggestion(nameInfos, instanceId);
- mFolder.logFolderLabelState();
invalidate();
}, DROP_IN_ANIMATION_DURATION);
}
@@ -447,12 +448,25 @@
if (nameInfos == null || nameInfos[0] == null || isEmpty(nameInfos[0].getLabel())) {
return;
}
- mInfo.setTitle(nameInfos[0].getLabel());
- StatsLogManager.newInstance(getContext()).logger().withItemInfo(mInfo)
- .withInstanceId(instanceId).log(LAUNCHER_FOLDER_LABEL_UPDATED);
+ CharSequence newTitle = nameInfos[0].getLabel();
+ FromState fromState = mInfo.getFromLabelState();
+
+ mInfo.setTitle(newTitle);
onTitleChanged(mInfo.title);
mFolder.mFolderName.setText(mInfo.title);
mFolder.mLauncher.getModelWriter().updateItemInDatabase(mInfo);
+
+ // Logging for folder creation flow
+ StatsLogManager.newInstance(getContext()).logger()
+ .withInstanceId(instanceId)
+ .withItemInfo(mInfo)
+ .withFromState(fromState)
+ .withToState(ToState.TO_SUGGESTION0)
+ // When LAUNCHER_FOLDER_LABEL_UPDATED event.edit_text does not have delimiter,
+ // event is assumed to be folder creation on the server side.
+ .withEditText(newTitle.toString())
+ .log(LAUNCHER_FOLDER_AUTO_LABELED);
+ mFolder.logFolderLabelState(fromState, ToState.TO_SUGGESTION0);
}
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 82d61da..be8edc7 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -21,6 +21,8 @@
import com.android.launcher3.R;
import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
+import com.android.launcher3.logger.LauncherAtom.FromState;
+import com.android.launcher3.logger.LauncherAtom.ToState;
import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.ResourceBasedOverride;
@@ -66,8 +68,10 @@
+ "resulting in a new folder creation")
LAUNCHER_ITEM_DROP_FOLDER_CREATED(386),
- @UiEvent(doc = "User action resulted in or manually updated the folder label to "
- + "new/same value.")
+ @UiEvent(doc = "Folder's label is automatically assigned.")
+ LAUNCHER_FOLDER_AUTO_LABELED(591),
+
+ @UiEvent(doc = "User manually updated the folder label.")
LAUNCHER_FOLDER_LABEL_UPDATED(460),
@UiEvent(doc = "User long pressed on the workspace empty space.")
@@ -246,6 +250,27 @@
}
/**
+ * Sets FromState field of log message.
+ */
+ default StatsLogger withFromState(FromState fromState) {
+ return this;
+ }
+
+ /**
+ * Sets ToState field of log message.
+ */
+ default StatsLogger withToState(ToState toState) {
+ return this;
+ }
+
+ /**
+ * Sets editText field of log message.
+ */
+ default StatsLogger withEditText(String editText) {
+ return this;
+ }
+
+ /**
* Sets the final value for container related fields of log message.
*
* By default container related fields are derived from {@link ItemInfo}, this method would
diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java
index 8f577b5..08eb383 100644
--- a/src/com/android/launcher3/model/data/FolderInfo.java
+++ b/src/com/android/launcher3/model/data/FolderInfo.java
@@ -54,7 +54,6 @@
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalInt;
-import java.util.StringJoiner;
import java.util.stream.IntStream;
@@ -88,20 +87,6 @@
public Intent suggestedFolderNames;
- // Represents the title before current.
- // Primarily used for logging purpose.
- private CharSequence mPreviousTitle;
-
- // True if the title before was manually entered, suggested otherwise.
- // Primarily used for logging purpose.
- public boolean fromCustom;
-
- /**
- * Used for separating {@link #mPreviousTitle} and {@link #title} when concatenating them
- * for logging.
- */
- private static final CharSequence FOLDER_LABEL_DELIMITER = "=>";
-
/**
* The apps and shortcuts
*/
@@ -207,21 +192,16 @@
return getDefaultItemInfoBuilder()
.setFolderIcon(LauncherAtom.FolderIcon.newBuilder().setCardinality(contents.size()))
.setRank(rank)
- .setAttribute(fromCustom ? MANUAL_LABEL : SUGGESTED_LABEL)
+ .setAttribute(hasOption(FLAG_MANUAL_FOLDER_NAME) ? MANUAL_LABEL : SUGGESTED_LABEL)
.setContainerInfo(getContainerInfo())
.build();
}
@Override
public void setTitle(CharSequence title) {
- mPreviousTitle = this.title;
this.title = title;
}
- public CharSequence getPreviousTitle() {
- return mPreviousTitle;
- }
-
@Override
public ItemInfo makeShallowCopy() {
FolderInfo folderInfo = new FolderInfo();
@@ -235,30 +215,7 @@
*/
@Override
public LauncherAtom.ItemInfo buildProto() {
- FromState fromFolderLabelState = getFromFolderLabelState();
- ToState toFolderLabelState = getToFolderLabelState();
- LauncherAtom.FolderIcon.Builder folderIconBuilder = LauncherAtom.FolderIcon.newBuilder()
- .setCardinality(contents.size())
- .setFromLabelState(fromFolderLabelState)
- .setToLabelState(toFolderLabelState);
-
- // If the folder label is suggested, it is logged to improve prediction model.
- // When both old and new labels are logged together delimiter is used.
- StringJoiner labelInfoBuilder = new StringJoiner(FOLDER_LABEL_DELIMITER);
- if (fromFolderLabelState.equals(FromState.FROM_SUGGESTED)) {
- labelInfoBuilder.add(mPreviousTitle);
- }
- if (toFolderLabelState.toString().startsWith("TO_SUGGESTION")) {
- labelInfoBuilder.add(title);
- }
- if (labelInfoBuilder.length() > 0) {
- folderIconBuilder.setLabelInfo(labelInfoBuilder.toString());
- }
-
- return getDefaultItemInfoBuilder()
- .setFolderIcon(folderIconBuilder)
- .setContainerInfo(getContainerInfo())
- .build();
+ return buildProto(null);
}
/**
@@ -280,15 +237,27 @@
}
- private LauncherAtom.ToState getToFolderLabelState() {
+ /**
+ * Returns {@link FromState} based on current {@link #title}.
+ */
+ public LauncherAtom.FromState getFromLabelState() {
+ return title == null
+ ? LauncherAtom.FromState.FROM_STATE_UNSPECIFIED
+ : title.length() == 0
+ ? LauncherAtom.FromState.FROM_EMPTY
+ : hasOption(FLAG_MANUAL_FOLDER_NAME)
+ ? LauncherAtom.FromState.FROM_CUSTOM
+ : LauncherAtom.FromState.FROM_SUGGESTED;
+ }
+
+ /**
+ * Returns {@link ToState} based on current {@link #title}.
+ */
+ public LauncherAtom.ToState getToLabelState() {
if (title == null) {
return LauncherAtom.ToState.TO_STATE_UNSPECIFIED;
}
- if (title.equals(mPreviousTitle)) {
- return LauncherAtom.ToState.UNCHANGED;
- }
-
if (!FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
return title.length() > 0
? LauncherAtom.ToState.TO_CUSTOM_WITH_SUGGESTIONS_DISABLED
@@ -335,17 +304,6 @@
// fall through
}
return LauncherAtom.ToState.TO_STATE_UNSPECIFIED;
-
- }
-
- private LauncherAtom.FromState getFromFolderLabelState() {
- return mPreviousTitle == null
- ? LauncherAtom.FromState.FROM_STATE_UNSPECIFIED
- : mPreviousTitle.length() == 0
- ? LauncherAtom.FromState.FROM_EMPTY
- : fromCustom
- ? LauncherAtom.FromState.FROM_CUSTOM
- : LauncherAtom.FromState.FROM_SUGGESTED;
}
private Optional<String[]> getSuggestedLabels() {
@@ -368,7 +326,8 @@
* @deprecated This method is used only for validation purpose and soon will be removed.
*/
@Deprecated
- public LauncherLogProto.LauncherEvent getFolderLabelStateLauncherEvent() {
+ public LauncherLogProto.LauncherEvent getFolderLabelStateLauncherEvent(FromState fromState,
+ ToState toState) {
return LauncherLogProto.LauncherEvent.newBuilder()
.setAction(LauncherLogProto.Action
.newBuilder()
@@ -377,8 +336,8 @@
.newBuilder()
.setType(Target.Type.ITEM)
.setItemType(LauncherLogProto.ItemType.EDITTEXT)
- .setFromFolderLabelState(convertFolderLabelState(getFromFolderLabelState()))
- .setToFolderLabelState(convertFolderLabelState(getToFolderLabelState())))
+ .setFromFolderLabelState(convertFolderLabelState(fromState))
+ .setToFolderLabelState(convertFolderLabelState(toState)))
.addSrcTarget(Target.newBuilder()
.setType(Target.Type.CONTAINER)
.setContainerType(LauncherLogProto.ContainerType.FOLDER)