Add logging for bubble UI events.
Bug: 123543171
Test: manual on local device using statsd_testdrive
Change-Id: I4d344d94e39f52fdd743b99390616c23c86a3a45
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 427662a..937588b 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -5263,6 +5263,7 @@
PERMISSION_IGNORED = 12;
SWIPE_LEFT = 13;
SWIPE_RIGHT = 14;
+ STACK_EXPANDED = 15;
}
optional Action action = 6;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index c107058..c67e3ff 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -27,7 +27,9 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.service.notification.StatusBarNotification;
import android.util.Log;
+import android.util.StatsLog;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -54,6 +56,9 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
/**
* Renders bubbles in a stack and handles animating expanded and collapsed states.
*/
@@ -240,16 +245,20 @@
// Previously expanded, notify that this bubble is no longer expanded
notifyExpansionChanged(mExpandedBubble, false /* expanded */);
}
+ BubbleView prevBubble = mExpandedBubble;
mExpandedBubble = bubbleToExpand;
if (!mIsExpanded) {
// If we weren't previously expanded we should animate open.
animateExpansion(true /* expand */);
+ logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
} else {
// Otherwise just update the views
// TODO: probably animate / page to expanded one
updateExpandedBubble();
updatePointerPosition();
requestUpdate();
+ logBubbleEvent(prevBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
+ logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
}
mExpandedBubble.getEntry().setShowInShadeWhenBubble(false);
notifyExpansionChanged(mExpandedBubble, true /* expanded */);
@@ -278,6 +287,7 @@
new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
ViewClippingUtil.setClippingDeactivated(bubbleView, true, mClippingParameters);
requestUpdate();
+ logBubbleEvent(bubbleView, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
}
/**
@@ -300,6 +310,7 @@
if (wasExpanded != mIsExpanded) {
notifyExpansionChanged(mExpandedBubble, mIsExpanded);
}
+ logBubbleEvent(bubbleView, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
}
/**
@@ -308,6 +319,8 @@
public void stackDismissed() {
collapseStack();
mBubbleContainer.removeAllViews();
+ logBubbleEvent(null /* no bubble associated with bubble stack dismiss */,
+ StatsLog.BUBBLE_UICHANGED__ACTION__STACK_DISMISSED);
}
/**
@@ -331,6 +344,7 @@
entry.setShowInShadeWhenBubble(false);
requestUpdate();
}
+ logBubbleEvent(bubbleView, StatsLog.BUBBLE_UICHANGED__ACTION__UPDATED);
}
/**
@@ -366,6 +380,7 @@
// TODO: Save opened bubble & move it to top of stack
animateExpansion(false /* shouldExpand */);
notifyExpansionChanged(mExpandedBubble, mIsExpanded);
+ logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
}
}
@@ -382,6 +397,7 @@
if (!mIsExpanded) {
mExpandedBubble = getTopBubble();
setExpandedBubble(mExpandedBubble);
+ logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED);
}
}
@@ -574,6 +590,9 @@
.setStiffness(SpringForce.STIFFNESS_LOW)
.setDampingRatio(SPRING_DAMPING_RATIO),
/* destination */ null);
+
+ logBubbleEvent(null /* no bubble associated with bubble stack move */,
+ StatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
}
/**
@@ -733,4 +752,72 @@
viewState.headsUpIsVisible = false;
viewState.applyToView(view);
}
+
+ /**
+ * @return the number of bubbles in the stack view.
+ */
+ private int getBubbleCount() {
+ return mBubbleContainer.getChildCount();
+ }
+
+ /**
+ * Finds the bubble index within the stack.
+ *
+ * @param bubbleView the view of the bubble.
+ * @return the index of the bubble view within the bubble stack. The range of the position
+ * is between 0 and the bubble count minus 1.
+ */
+ private int getBubbleIndex(BubbleView bubbleView) {
+ return mBubbleContainer.indexOfChild(bubbleView);
+ }
+
+ /**
+ * @return the normalized x-axis position of the bubble stack rounded to 4 decimal places.
+ */
+ private float getNormalizedXPosition() {
+ return new BigDecimal(getPosition().x / mDisplaySize.x)
+ .setScale(4, RoundingMode.CEILING.HALF_UP)
+ .floatValue();
+ }
+
+ /**
+ * @return the normalized y-axis position of the bubble stack rounded to 4 decimal places.
+ */
+ private float getNormalizedYPosition() {
+ return new BigDecimal(getPosition().y / mDisplaySize.y)
+ .setScale(4, RoundingMode.CEILING.HALF_UP)
+ .floatValue();
+ }
+
+ /**
+ * Logs the bubble UI event.
+ *
+ * @param bubble the bubble that is being interacted on. Null value indicates that
+ * the user interaction is not specific to one bubble.
+ * @param action the user interaction enum.
+ */
+ private void logBubbleEvent(@Nullable BubbleView bubble, int action) {
+ if (bubble == null) {
+ StatsLog.write(StatsLog.BUBBLE_UI_CHANGED,
+ null /* package name */,
+ null /* notification channel */,
+ 0 /* notification ID */,
+ 0 /* bubble position */,
+ getBubbleCount(),
+ action,
+ getNormalizedXPosition(),
+ getNormalizedYPosition());
+ } else {
+ StatusBarNotification notification = bubble.getEntry().notification;
+ StatsLog.write(StatsLog.BUBBLE_UI_CHANGED,
+ notification.getPackageName(),
+ notification.getNotification().getChannelId(),
+ notification.getId(),
+ getBubbleIndex(bubble),
+ getBubbleCount(),
+ action,
+ getNormalizedXPosition(),
+ getNormalizedYPosition());
+ }
+ }
}