From 7232332dc963c2ce55dda1a3a0367080d5494503 Mon Sep 17 00:00:00 2001 From: Blazej Magnowski Date: Fri, 24 Jul 2015 11:49:40 -0700 Subject: [PATCH] System to track touch and sensor events before unlocking the phone Added a new component which tracks touch and sensor events and also events like showing the bouncer tapping a notification and others. The collection is enabled when the screen is turned on and is disabled after the phone is unlocked. The data is saved in a protobuf file in internal storage in a directory called "good_touches". There is also an option to collect events which end with the screen turning off. These are saved in the "bad_touches" file. Everything is hidden behind the ENABLE_ANALYTICS flag which is set by default to false and can be turned on only if Build.IS_DEBUGGABLE is true. Also behind the ENFORCE_BOUNCER flag the class shows the bouncer before expanding a notification, showing quick settings or launching an affordance from one of the bottom corners. Change-Id: Iaeae0fb7a0d9c707daf7a270201fa5b1cd84c74a --- packages/SystemUI/Android.mk | 4 +- .../analytics/LockedPhoneAnalytics.java | 453 ++++++++++++++++++ .../analytics/SensorLoggerSession.java | 160 +++++++ .../keyguard/KeyguardViewMediator.java | 2 + .../ActivatableNotificationView.java | 5 + .../systemui/statusbar/DragDownHelper.java | 5 + .../statusbar/ExpandableNotificationRow.java | 4 + .../statusbar/phone/KeyguardBouncer.java | 7 + .../phone/NotificationPanelView.java | 41 +- .../statusbar/phone/PhoneStatusBar.java | 13 +- .../statusbar/phone/StatusBarWindowView.java | 4 + .../statusbar/phone/touch_analytics.proto | 136 ++++++ .../stack/NotificationStackScrollLayout.java | 11 + packages/SystemUI/tests/Android.mk | 5 + 14 files changed, 846 insertions(+), 4 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/analytics/LockedPhoneAnalytics.java create mode 100644 packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java create mode 100644 packages/SystemUI/src/com/android/systemui/statusbar/phone/touch_analytics.proto diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk index 51fea2a124ec0..314b3c4bf3267 100644 --- a/packages/SystemUI/Android.mk +++ b/packages/SystemUI/Android.mk @@ -3,13 +3,15 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := $(call all-java-files-under, src) \ +LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-proto-files-under,src) \ src/com/android/systemui/EventLogTags.logtags LOCAL_STATIC_JAVA_LIBRARIES := Keyguard LOCAL_JAVA_LIBRARIES := telephony-common LOCAL_PACKAGE_NAME := SystemUI +LOCAL_PROTOC_OPTIMIZE_TYPE := nano +LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors LOCAL_CERTIFICATE := platform LOCAL_PRIVILEGED_MODULE := true diff --git a/packages/SystemUI/src/com/android/systemui/analytics/LockedPhoneAnalytics.java b/packages/SystemUI/src/com/android/systemui/analytics/LockedPhoneAnalytics.java new file mode 100644 index 0000000000000..e458862295e11 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/analytics/LockedPhoneAnalytics.java @@ -0,0 +1,453 @@ +/* + * Copyright (C) 2015 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.systemui.analytics; + +import android.content.Context; +import android.database.ContentObserver; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Handler; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.Log; +import android.view.MotionEvent; + +import com.android.systemui.statusbar.StatusBarState; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +import static com.android.systemui.statusbar.phone.TouchAnalyticsProto.Session; +import static com.android.systemui.statusbar.phone.TouchAnalyticsProto.Session.PhoneEvent; + +/** + * Tracks touch, sensor and phone events when the lockscreen is on. If the phone is unlocked + * the data containing these events is saved to a file. This data is collected + * to analyze how a human interaction looks like. + * + * A session starts when the screen is turned on. + * A session ends when the screen is turned off or user unlocks the phone. + */ +public class LockedPhoneAnalytics implements SensorEventListener { + private static final String TAG = "LockedPhoneAnalytics"; + private static final String ANALYTICS_ENABLE = "locked_phone_analytics_enable"; + private static final String ENFORCE_BOUNCER = "locked_phone_analytics_enforce_bouncer"; + private static final String COLLECT_BAD_TOCUHES = "locked_phone_analytics_collect_bad_touches"; + + private static final long TIMEOUT_MILLIS = 11000; // 11 seconds. + public static final boolean DEBUG = false; + + private static final int[] SENSORS = new int[] { + Sensor.TYPE_ACCELEROMETER, + Sensor.TYPE_GYROSCOPE, + Sensor.TYPE_PROXIMITY, + Sensor.TYPE_LIGHT, + Sensor.TYPE_ROTATION_VECTOR, + }; + + private final Handler mHandler = new Handler(); + protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange) { + updateConfiguration(); + } + }; + + private final SensorManager mSensorManager; + private final Context mContext; + + // Err on the side of caution, so logging is not started after a crash even tough the screen + // is off. + private SensorLoggerSession mCurrentSession = null; + + private boolean mEnableAnalytics = false; + private boolean mEnforceBouncer = false; + private boolean mTimeoutActive = false; + private boolean mCollectBadTouches = false; + private boolean mBouncerOn = false; + private boolean mCornerSwiping = false; + private boolean mTrackingStarted = false; + + private int mState = StatusBarState.SHADE; + + private static LockedPhoneAnalytics sInstance = null; + + private LockedPhoneAnalytics(Context context) { + mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + mContext = context; + + mContext.getContentResolver().registerContentObserver( + Settings.Secure.getUriFor(ANALYTICS_ENABLE), false, + mSettingsObserver, + UserHandle.USER_ALL); + + mContext.getContentResolver().registerContentObserver( + Settings.Secure.getUriFor(ENFORCE_BOUNCER), false, + mSettingsObserver, + UserHandle.USER_ALL); + + mContext.getContentResolver().registerContentObserver( + Settings.Secure.getUriFor(COLLECT_BAD_TOCUHES), false, + mSettingsObserver, + UserHandle.USER_ALL); + + updateConfiguration(); + } + + public static LockedPhoneAnalytics getInstance(Context context) { + if (sInstance == null) { + sInstance = new LockedPhoneAnalytics(context); + } + return sInstance; + } + + private void updateConfiguration() { + mEnableAnalytics = Build.IS_DEBUGGABLE && 0 != Settings.Secure.getInt( + mContext.getContentResolver(), + ANALYTICS_ENABLE, 0); + mEnforceBouncer = mEnableAnalytics && 0 != Settings.Secure.getInt( + mContext.getContentResolver(), + ENFORCE_BOUNCER, 0); + mCollectBadTouches = mEnableAnalytics && 0 != Settings.Secure.getInt( + mContext.getContentResolver(), + COLLECT_BAD_TOCUHES, 0); + } + + private boolean sessionEntrypoint() { + if ((mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) + && mEnableAnalytics && mCurrentSession == null) { + onSessionStart(); + return true; + } + return false; + } + + private void sessionExitpoint(int result) { + if (mEnableAnalytics && mCurrentSession != null) { + onSessionEnd(result); + } + } + + private void onSessionStart() { + mBouncerOn = false; + mCornerSwiping = false; + mTrackingStarted = false; + mCurrentSession = new SensorLoggerSession(System.currentTimeMillis(), System.nanoTime()); + for (int sensorType : SENSORS) { + Sensor s = mSensorManager.getDefaultSensor(sensorType); + if (s != null) { + mSensorManager.registerListener(this, s, SensorManager.SENSOR_DELAY_GAME); + } + } + } + + private void onSessionEnd(int result) { + SensorLoggerSession session = mCurrentSession; + mCurrentSession = null; + + session.end(System.currentTimeMillis(), result); + queueSession(session); + } + + private void queueSession(final SensorLoggerSession currentSession) { + AsyncTask.execute(new Runnable() { + @Override + public void run() { + byte[] b = Session.toByteArray(currentSession.toProto()); + String dir = mContext.getFilesDir().getAbsolutePath(); + if (currentSession.getResult() != Session.SUCCESS) { + if (!mCollectBadTouches) { + return; + } + dir += "/bad_touches"; + } else { + dir += "/good_touches"; + } + + File file = new File(dir); + file.mkdir(); + File touch = new File(file, "trace_" + System.currentTimeMillis()); + + try { + new FileOutputStream(touch).write(b); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }); + } + + + @Override + public synchronized void onSensorChanged(SensorEvent event) { + if (mEnableAnalytics && mCurrentSession != null) { + mCurrentSession.addSensorEvent(event, System.nanoTime()); + enforceTimeout(); + } + } + + private void enforceTimeout() { + if (mTimeoutActive) { + if (System.currentTimeMillis() - mCurrentSession.getStartTimestampMillis() + > TIMEOUT_MILLIS) { + onSessionEnd(Session.UNKNOWN); + if (DEBUG) { + Log.i(TAG, "Analytics timed out."); + } + } + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + } + + public boolean shouldEnforceBouncer() { + return mEnforceBouncer; + } + + public void setStatusBarState(int state) { + mState = state; + } + + public void onScreenOn() { + if (sessionEntrypoint()) { + if (DEBUG) { + Log.d(TAG, "onScreenOn"); + } + addEvent(PhoneEvent.ON_SCREEN_ON); + } + } + + public void onScreenOnFromTouch() { + if (sessionEntrypoint()) { + if (DEBUG) { + Log.d(TAG, "onScreenOnFromTouch"); + } + addEvent(PhoneEvent.ON_SCREEN_ON_FROM_TOUCH); + } + } + + public void onScreenOff() { + if (DEBUG) { + Log.d(TAG, "onScreenOff"); + } + addEvent(PhoneEvent.ON_SCREEN_OFF); + sessionExitpoint(Session.FAILURE); + } + + public void onSucccessfulUnlock() { + if (DEBUG) { + Log.d(TAG, "onSuccessfulUnlock"); + } + addEvent(PhoneEvent.ON_SUCCESSFUL_UNLOCK); + sessionExitpoint(Session.SUCCESS); + } + + public void onBouncerShown() { + if (!mBouncerOn) { + if (DEBUG) { + Log.d(TAG, "onBouncerShown"); + } + mBouncerOn = true; + addEvent(PhoneEvent.ON_BOUNCER_SHOWN); + } + } + + public void onBouncerHidden() { + if (mBouncerOn) { + if (DEBUG) { + Log.d(TAG, "onBouncerHidden"); + } + mBouncerOn = false; + addEvent(PhoneEvent.ON_BOUNCER_HIDDEN); + } + } + + public void onQsDown() { + if (DEBUG) { + Log.d(TAG, "onQsDown"); + } + addEvent(PhoneEvent.ON_QS_DOWN); + } + + public void setQsExpanded(boolean expanded) { + if (DEBUG) { + Log.d(TAG, "setQsExpanded = " + expanded); + } + if (expanded) { + addEvent(PhoneEvent.SET_QS_EXPANDED_TRUE); + } else { + addEvent(PhoneEvent.SET_QS_EXPANDED_FALSE); + } + } + + public void onTrackingStarted() { + if (DEBUG) { + Log.d(TAG, "onTrackingStarted"); + } + mTrackingStarted = true; + addEvent(PhoneEvent.ON_TRACKING_STARTED); + } + + public void onTrackingStopped() { + if (mTrackingStarted) { + if (DEBUG) { + Log.d(TAG, "onTrackingStopped"); + } + mTrackingStarted = false; + addEvent(PhoneEvent.ON_TRACKING_STOPPED); + } + } + + public void onNotificationActive() { + if (DEBUG) { + Log.d(TAG, "onNotificationActive"); + } + addEvent(PhoneEvent.ON_NOTIFICATION_ACTIVE); + } + + + public void onNotificationDoubleTap() { + if (DEBUG) { + Log.d(TAG, "onNotificationDoubleTap"); + } + addEvent(PhoneEvent.ON_NOTIFICATION_DOUBLE_TAP); + } + + public void setNotificationExpanded() { + if (DEBUG) { + Log.d(TAG, "setNotificationExpanded"); + } + addEvent(PhoneEvent.SET_NOTIFICATION_EXPANDED); + } + + public void onNotificatonStartDraggingDown() { + if (DEBUG) { + Log.d(TAG, "onNotificationStartDraggingDown"); + } + addEvent(PhoneEvent.ON_NOTIFICATION_START_DRAGGING_DOWN); + } + + public void onNotificatonStopDraggingDown() { + if (DEBUG) { + Log.d(TAG, "onNotificationStopDraggingDown"); + } + addEvent(PhoneEvent.ON_NOTIFICATION_STOP_DRAGGING_DOWN); + } + + public void onNotificationDismissed() { + if (DEBUG) { + Log.d(TAG, "onNotificationDismissed"); + } + addEvent(PhoneEvent.ON_NOTIFICATION_DISMISSED); + } + + public void onNotificatonStartDismissing() { + if (DEBUG) { + Log.d(TAG, "onNotificationStartDismissing"); + } + addEvent(PhoneEvent.ON_NOTIFICATION_START_DISMISSING); + } + + public void onNotificatonStopDismissing() { + if (DEBUG) { + Log.d(TAG, "onNotificationStopDismissing"); + } + addEvent(PhoneEvent.ON_NOTIFICATION_STOP_DISMISSING); + } + + public void onCameraOn() { + if (DEBUG) { + Log.d(TAG, "onCameraOn"); + } + addEvent(PhoneEvent.ON_CAMERA_ON); + } + + public void onLeftAffordanceOn() { + if (DEBUG) { + Log.d(TAG, "onLeftAffordanceOn"); + } + addEvent(PhoneEvent.ON_LEFT_AFFORDANCE_ON); + } + + public void onAffordanceSwipingStarted(boolean rightCorner) { + if (DEBUG) { + Log.d(TAG, "onAffordanceSwipingStarted"); + } + mCornerSwiping = true; + if (rightCorner) { + addEvent(PhoneEvent.ON_RIGHT_AFFORDANCE_SWIPING_STARTED); + } else { + addEvent(PhoneEvent.ON_LEFT_AFFORDANCE_SWIPING_STARTED); + } + } + + public void onAffordanceSwipingAborted() { + if (mCornerSwiping) { + if (DEBUG) { + Log.d(TAG, "onAffordanceSwipingAborted"); + } + mCornerSwiping = false; + addEvent(PhoneEvent.ON_AFFORDANCE_SWIPING_ABORTED); + } + } + + public void onUnlockHintStarted() { + if (DEBUG) { + Log.d(TAG, "onUnlockHintStarted"); + } + addEvent(PhoneEvent.ON_UNLOCK_HINT_STARTED); + } + + public void onCameraHintStarted() { + if (DEBUG) { + Log.d(TAG, "onCameraHintStarted"); + } + addEvent(PhoneEvent.ON_CAMERA_HINT_STARTED); + } + + public void onLeftAffordanceHintStarted() { + if (DEBUG) { + Log.d(TAG, "onLeftAffordanceHintStarted"); + } + addEvent(PhoneEvent.ON_LEFT_AFFORDANCE_HINT_STARTED); + } + + public void onTouchEvent(MotionEvent ev, int width, int height) { + if (!mBouncerOn && mCurrentSession != null) { + if (DEBUG) { + Log.v(TAG, "onTouchEvent(ev.action=" + + MotionEvent.actionToString(ev.getAction()) + ")"); + } + mCurrentSession.addMotionEvent(ev); + mCurrentSession.setTouchArea(width, height); + enforceTimeout(); + } + } + + private void addEvent(int eventType) { + if (mEnableAnalytics && mCurrentSession != null) { + mCurrentSession.addPhoneEvent(eventType, System.nanoTime()); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java b/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java new file mode 100644 index 0000000000000..09383f6aa8d1f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2015 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.systemui.analytics; + +import android.os.Build; +import android.util.Log; +import android.view.MotionEvent; + +import java.util.ArrayList; + +import static com.android.systemui.statusbar.phone.TouchAnalyticsProto.Session; +import static com.android.systemui.statusbar.phone.TouchAnalyticsProto.Session.TouchEvent; +import static com.android.systemui.statusbar.phone.TouchAnalyticsProto.Session.SensorEvent; +import static com.android.systemui.statusbar.phone.TouchAnalyticsProto.Session.PhoneEvent; + +/** + * Collects touch, sensor and phone events and converts the data to + * TouchAnalyticsProto.Session. + */ +public class SensorLoggerSession { + private static final String TAG = "SensorLoggerSession"; + + private final long mStartTimestampMillis; + private final long mStartSystemTimeNanos; + + private long mEndTimestampMillis; + private int mType; + + private ArrayList mMotionEvents = new ArrayList<>(); + private ArrayList mSensorEvents = new ArrayList<>(); + private ArrayList mPhoneEvents = new ArrayList<>(); + private int mTouchAreaHeight; + private int mTouchAreaWidth; + private int mResult = Session.UNKNOWN; + + public SensorLoggerSession(long startTimestampMillis, long startSystemTimeNanos) { + mStartTimestampMillis = startTimestampMillis; + mStartSystemTimeNanos = startSystemTimeNanos; + mType = Session.REAL; + } + + public void end(long endTimestampMillis, int result) { + mResult = result; + mEndTimestampMillis = endTimestampMillis; + + if (LockedPhoneAnalytics.DEBUG) { + Log.d(TAG, "Ending session result=" + result + " it lasted for " + + (float) (mEndTimestampMillis - mStartTimestampMillis) / 1000f + "s"); + } + } + + public void addMotionEvent(MotionEvent motionEvent) { + TouchEvent event = motionEventToProto(motionEvent); + mMotionEvents.add(event); + } + + public void addSensorEvent(android.hardware.SensorEvent eventOrig, long systemTimeNanos) { + SensorEvent event = sensorEventToProto(eventOrig, systemTimeNanos); + mSensorEvents.add(event); + } + + public void addPhoneEvent(int eventType, long systemTimeNanos) { + PhoneEvent event = phoneEventToProto(eventType, systemTimeNanos); + mPhoneEvents.add(event); + } + + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Session{"); + sb.append("mStartTimestampMillis=").append(mStartTimestampMillis); + sb.append(", mStartSystemTimeNanos=").append(mStartSystemTimeNanos); + sb.append(", mEndTimestampMillis=").append(mEndTimestampMillis); + sb.append(", mResult=").append(mResult); + sb.append(", mTouchAreaHeight=").append(mTouchAreaHeight); + sb.append(", mTouchAreaWidth=").append(mTouchAreaWidth); + sb.append(", mMotionEvents=[size=").append(mMotionEvents.size()).append("]"); + sb.append(", mSensorEvents=[size=").append(mSensorEvents.size()).append("]"); + sb.append(", mPhoneEvents=[size=").append(mPhoneEvents.size()).append("]"); + sb.append('}'); + return sb.toString(); + } + + public Session toProto() { + Session proto = new Session(); + proto.setStartTimestampMillis(mStartTimestampMillis); + proto.setDurationMillis(mEndTimestampMillis - mStartTimestampMillis); + proto.setBuild(Build.FINGERPRINT); + proto.setResult(mResult); + proto.setType(mType); + proto.sensorEvents = mSensorEvents.toArray(proto.sensorEvents); + proto.touchEvents = mMotionEvents.toArray(proto.touchEvents); + proto.phoneEvents = mPhoneEvents.toArray(proto.phoneEvents); + proto.setTouchAreaWidth(mTouchAreaWidth); + proto.setTouchAreaHeight(mTouchAreaHeight); + return proto; + } + + private PhoneEvent phoneEventToProto(int eventType, long sysTimeNanos) { + PhoneEvent proto = new PhoneEvent(); + proto.setType(eventType); + proto.setTimeOffsetNanos(sysTimeNanos - mStartSystemTimeNanos); + return proto; + } + + private SensorEvent sensorEventToProto(android.hardware.SensorEvent ev, long sysTimeNanos) { + SensorEvent proto = new SensorEvent(); + proto.setType(ev.sensor.getType()); + proto.setTimeOffsetNanos(sysTimeNanos - mStartSystemTimeNanos); + proto.setTimestamp(ev.timestamp); + proto.values = ev.values.clone(); + return proto; + } + + private TouchEvent motionEventToProto(MotionEvent ev) { + int count = ev.getPointerCount(); + TouchEvent proto = new TouchEvent(); + proto.setTimeOffsetNanos(ev.getEventTimeNano() - mStartSystemTimeNanos); + proto.setAction(ev.getActionMasked()); + proto.setActionIndex(ev.getActionIndex()); + proto.pointers = new TouchEvent.Pointer[count]; + for (int i = 0; i < count; i++) { + TouchEvent.Pointer p = new TouchEvent.Pointer(); + p.setX(ev.getX(i)); + p.setY(ev.getY(i)); + p.setSize(ev.getSize(i)); + p.setPressure(ev.getPressure(i)); + p.setId(ev.getPointerId(i)); + proto.pointers[i] = p; + } + return proto; + } + + public void setTouchArea(int width, int height) { + mTouchAreaWidth = width; + mTouchAreaHeight = height; + } + + public int getResult() { + return mResult; + } + + public long getStartTimestampMillis() { + return mStartTimestampMillis; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index aafa9915ead5b..a28e18894e88e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -69,6 +69,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.SystemUI; import com.android.systemui.statusbar.phone.FingerprintUnlockController; +import com.android.systemui.analytics.LockedPhoneAnalytics; import com.android.systemui.statusbar.phone.PhoneStatusBar; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; @@ -1240,6 +1241,7 @@ public class KeyguardViewMediator extends SystemUI { case START_KEYGUARD_EXIT_ANIM: StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj; handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration); + LockedPhoneAnalytics.getInstance(mContext).onSucccessfulUnlock(); break; case KEYGUARD_DONE_PENDING_TIMEOUT: Log.w(TAG, "Timeout while waiting for activity drawn!"); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java index 7f17885ab6873..c1dfec3f954bc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java @@ -34,6 +34,7 @@ import android.view.animation.LinearInterpolator; import android.view.animation.PathInterpolator; import com.android.systemui.R; +import com.android.systemui.analytics.LockedPhoneAnalytics; /** * Base class for both {@link ExpandableNotificationRow} and {@link NotificationOverflowContainer} @@ -128,6 +129,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private final int mNormalColor; private final int mLowPriorityColor; private boolean mIsBelowSpeedBump; + private LockedPhoneAnalytics mLockedPhoneAnalytics; public ActivatableNotificationView(Context context, AttributeSet attrs) { super(context, attrs); @@ -151,6 +153,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView R.color.notification_ripple_color_low_priority); mNormalRippleColor = context.getColor( R.color.notification_ripple_untinted_color); + mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(context); } @Override @@ -219,6 +222,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView makeActive(); postDelayed(mTapTimeoutRunnable, DOUBLETAP_TIMEOUT_MS); } else { + mLockedPhoneAnalytics.onNotificationDoubleTap(); boolean performed = performClick(); if (performed) { removeCallbacks(mTapTimeoutRunnable); @@ -238,6 +242,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } private void makeActive() { + mLockedPhoneAnalytics.onNotificationActive(); startActivateAnimation(false /* reverse */); mActivated = true; if (mOnActivatedListener != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java index 15a092c37612d..e2304c1005172 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java @@ -29,6 +29,7 @@ import android.view.animation.Interpolator; import com.android.systemui.ExpandHelper; import com.android.systemui.Gefingerpoken; import com.android.systemui.R; +import com.android.systemui.analytics.LockedPhoneAnalytics; /** * A utility class to enable the downward swipe on the lockscreen to go to the full shade and expand @@ -54,6 +55,7 @@ public class DragDownHelper implements Gefingerpoken { private ExpandableView mStartingChild; private Interpolator mInterpolator; private float mLastHeight; + private LockedPhoneAnalytics mLockedPhoneAnalytics; public DragDownHelper(Context context, View host, ExpandHelper.Callback callback, DragDownCallback dragDownCallback) { @@ -65,6 +67,7 @@ public class DragDownHelper implements Gefingerpoken { mCallback = callback; mDragDownCallback = dragDownCallback; mHost = host; + mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(context); } @Override @@ -84,6 +87,7 @@ public class DragDownHelper implements Gefingerpoken { case MotionEvent.ACTION_MOVE: final float h = y - mInitialTouchY; if (h > mTouchSlop && h > Math.abs(x - mInitialTouchX)) { + mLockedPhoneAnalytics.onNotificatonStartDraggingDown(); mDraggingDown = true; captureStartingChild(mInitialTouchX, mInitialTouchY); mInitialTouchY = y; @@ -201,6 +205,7 @@ public class DragDownHelper implements Gefingerpoken { } private void stopDragging() { + mLockedPhoneAnalytics.onNotificatonStopDraggingDown(); if (mStartingChild != null) { cancelExpansion(mStartingChild); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index b88e5ca7f5cbf..62559674344ac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -34,6 +34,7 @@ import android.view.animation.LinearInterpolator; import android.widget.ImageView; import com.android.systemui.R; +import com.android.systemui.analytics.LockedPhoneAnalytics; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.PhoneStatusBar; import com.android.systemui.statusbar.stack.NotificationChildrenContainer; @@ -109,6 +110,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { !mChildrenExpanded); } }; + private LockedPhoneAnalytics mLockedPhoneAnalytics; public NotificationContentView getPrivateLayout() { return mPrivateLayout; @@ -307,6 +309,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { public ExpandableNotificationRow(Context context, AttributeSet attrs) { super(context, attrs); + mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(context); } /** @@ -494,6 +497,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { * @param userExpanded whether the user wants this notification to be expanded */ public void setUserExpanded(boolean userExpanded) { + mLockedPhoneAnalytics.setNotificationExpanded(); if (userExpanded && !mExpandable) return; final boolean wasExpanded = isExpanded(); mHasUserChangedExpansion = true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index 8b96e5f52ae35..676f114eb9b17 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -31,6 +31,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.R; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.DejankUtils; +import com.android.systemui.analytics.LockedPhoneAnalytics; import static com.android.keyguard.KeyguardHostView.OnDismissAction; import static com.android.keyguard.KeyguardSecurityModel.SecurityMode; @@ -49,6 +50,7 @@ public class KeyguardBouncer { private ViewGroup mRoot; private boolean mShowingSoon; private int mBouncerPromptReason; + private LockedPhoneAnalytics mLockedPhoneAnalytics; private KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { @Override @@ -66,9 +68,11 @@ public class KeyguardBouncer { mContainer = container; mWindowManager = windowManager; KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback); + mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(mContext); } public void show(boolean resetSecuritySelection) { + mLockedPhoneAnalytics.onBouncerShown(); ensureView(); if (resetSecuritySelection) { // showPrimarySecurityScreen() updates the current security method. This is needed in @@ -128,6 +132,7 @@ public class KeyguardBouncer { } public void hide(boolean destroyView) { + mLockedPhoneAnalytics.onBouncerHidden(); cancelShowRunnable(); if (mKeyguardView != null) { mKeyguardView.cancelDismissAction(); @@ -157,6 +162,7 @@ public class KeyguardBouncer { public void reset() { cancelShowRunnable(); inflateView(); + mLockedPhoneAnalytics.onBouncerHidden(); } public void onScreenTurnedOff() { @@ -244,6 +250,7 @@ public class KeyguardBouncer { // We need to show it in case it is secure. If not, it will get dismissed in any case. mRoot.setVisibility(View.VISIBLE); + mLockedPhoneAnalytics.onBouncerShown(); mKeyguardView.requestFocus(); mKeyguardView.onResume(); return true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 710c335ecafe2..babca34f0511d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -56,6 +56,7 @@ import com.android.systemui.statusbar.GestureRecorder; import com.android.systemui.statusbar.KeyguardAffordanceView; import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.analytics.LockedPhoneAnalytics; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; @@ -203,6 +204,7 @@ public class NotificationPanelView extends PanelView implements private int mLastOrientation = -1; private boolean mClosingWithAlphaFadeOut; private boolean mHeadsUpAnimatingAway; + private LockedPhoneAnalytics mLockedPhoneAnalytics; private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() { @Override @@ -219,6 +221,7 @@ public class NotificationPanelView extends PanelView implements public NotificationPanelView(Context context, AttributeSet attrs) { super(context, attrs); setWillNotDraw(!DEBUG); + mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(context); } public void setStatusBar(PhoneStatusBar bar) { @@ -820,6 +823,7 @@ public class NotificationPanelView extends PanelView implements private void handleQsDown(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN && shouldQuickSettingsIntercept(event.getX(), event.getY(), -1)) { + mLockedPhoneAnalytics.onQsDown(); mQsTracking = true; onQsExpansionStarted(); mInitialHeightOnTouch = mQsExpansionHeight; @@ -987,6 +991,7 @@ public class NotificationPanelView extends PanelView implements mQsExpanded = expanded; updateQsState(); requestPanelHeightUpdate(); + mLockedPhoneAnalytics.setQsExpanded(expanded); mNotificationStackScroller.setInterceptDelegateEnabled(expanded); mStatusBar.setQsExpanded(expanded); mQsPanel.setExpanded(expanded); @@ -1313,6 +1318,10 @@ public class NotificationPanelView extends PanelView implements R.string.accessibility_desc_quick_settings)); mLastAnnouncementWasQuickSettings = true; } + if (mQsFullyExpanded && mLockedPhoneAnalytics.shouldEnforceBouncer()) { + mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */, + false /* dismissShade */, true /* afterKeyguardGone */); + } if (DEBUG) { invalidate(); } @@ -1840,6 +1849,7 @@ public class NotificationPanelView extends PanelView implements @Override protected void onTrackingStarted() { + mLockedPhoneAnalytics.onTrackingStarted(); super.onTrackingStarted(); if (mQsFullyExpanded) { mQsExpandImmediate = true; @@ -1853,6 +1863,7 @@ public class NotificationPanelView extends PanelView implements @Override protected void onTrackingStopped(boolean expand) { + mLockedPhoneAnalytics.onTrackingStopped(); super.onTrackingStopped(expand); if (expand) { mNotificationStackScroller.setOverScrolledPixels( @@ -1951,11 +1962,35 @@ public class NotificationPanelView extends PanelView implements if (start) { EventLogTags.writeSysuiLockscreenGesture( EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_DIALER, lengthDp, velocityDp); - mKeyguardBottomArea.launchLeftAffordance(); + + mLockedPhoneAnalytics.onLeftAffordanceOn(); + if (mLockedPhoneAnalytics.shouldEnforceBouncer()) { + mStatusBar.executeRunnableDismissingKeyguard(new Runnable() { + @Override + public void run() { + mKeyguardBottomArea.launchLeftAffordance(); + } + }, null, true /* dismissShade */, false /* afterKeyguardGone */); + } + else { + mKeyguardBottomArea.launchLeftAffordance(); + } } else { EventLogTags.writeSysuiLockscreenGesture( EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_CAMERA, lengthDp, velocityDp); - mSecureCameraLaunchManager.startSecureCameraLaunch(); + + mLockedPhoneAnalytics.onCameraOn(); + if (mLockedPhoneAnalytics.shouldEnforceBouncer()) { + mStatusBar.executeRunnableDismissingKeyguard(new Runnable() { + @Override + public void run() { + mSecureCameraLaunchManager.startSecureCameraLaunch(); + } + }, null, true /* dismissShade */, false /* afterKeyguardGone */); + } + else { + mSecureCameraLaunchManager.startSecureCameraLaunch(); + } } mStatusBar.startLaunchTransitionTimeout(); mBlockTouches = true; @@ -1999,6 +2034,7 @@ public class NotificationPanelView extends PanelView implements @Override public void onSwipingStarted(boolean rightIcon) { + mLockedPhoneAnalytics.onAffordanceSwipingStarted(rightIcon); boolean camera = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? !rightIcon : rightIcon; if (camera) { @@ -2012,6 +2048,7 @@ public class NotificationPanelView extends PanelView implements @Override public void onSwipingAborted() { + mLockedPhoneAnalytics.onAffordanceSwipingAborted(); mKeyguardBottomArea.unbindCameraPrewarmService(false /* launched */); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index b7943530b39a8..447619264c335 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -130,6 +130,7 @@ import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.SignalClusterView; import com.android.systemui.statusbar.SpeedBumpView; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.analytics.LockedPhoneAnalytics; import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener; import com.android.systemui.statusbar.policy.AccessibilityController; import com.android.systemui.statusbar.policy.BatteryController; @@ -588,6 +589,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private HashSet mHeadsUpEntriesToRemoveOnSwitch = new HashSet<>(); private RankingMap mLatestRankingMap; private boolean mNoAnimationOnNextBarModeChange; + private LockedPhoneAnalytics mLockedPhoneAnalytics; @Override public void start() { @@ -635,6 +637,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, notifyUserAboutHiddenNotifications(); mScreenPinningRequest = new ScreenPinningRequest(mContext); + mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(mContext); } // ================================================================================ @@ -3755,6 +3758,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } mState = state; mGroupManager.setStatusBarState(state); + mLockedPhoneAnalytics.setStatusBarState(state); mStatusBarWindowManager.setStatusBarState(state); updateDozing(); } @@ -3776,6 +3780,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } public void onUnlockHintStarted() { + mLockedPhoneAnalytics.onUnlockHintStarted(); mKeyguardIndicationController.showTransientIndication(R.string.keyguard_unlock); } @@ -3785,14 +3790,17 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } public void onCameraHintStarted() { + mLockedPhoneAnalytics.onCameraHintStarted(); mKeyguardIndicationController.showTransientIndication(R.string.camera_hint); } public void onVoiceAssistHintStarted() { + mLockedPhoneAnalytics.onLeftAffordanceHintStarted(); mKeyguardIndicationController.showTransientIndication(R.string.voice_hint); } public void onPhoneHintStarted() { + mLockedPhoneAnalytics.onLeftAffordanceHintStarted(); mKeyguardIndicationController.showTransientIndication(R.string.phone_hint); } @@ -3867,7 +3875,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, row.setUserExpanded(true); } boolean fullShadeNeedsBouncer = !userAllowsPrivateNotificationsInPublic(mCurrentUserId) - || !mShowLockscreenNotifications; + || !mShowLockscreenNotifications || mLockedPhoneAnalytics.shouldEnforceBouncer(); if (isLockscreenPublicMode() && fullShadeNeedsBouncer) { mLeaveOpenOnKeyguardHide = true; showBouncer(); @@ -3912,6 +3920,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mDeviceInteractive = false; mWakeUpComingFromTouch = false; mWakeUpTouchLocation = null; + mLockedPhoneAnalytics.onScreenOff(); mStackScroller.setAnimationsEnabled(false); updateVisibleToUser(); } @@ -3921,6 +3930,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mStackScroller.setAnimationsEnabled(true); mNotificationPanel.setTouchDisabled(false); updateVisibleToUser(); + mLockedPhoneAnalytics.onScreenOn(); } public void onScreenTurningOn() { @@ -4049,6 +4059,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mWakeUpTouchLocation = new PointF(event.getX(), event.getY()); mNotificationPanel.setTouchDisabled(false); mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested(); + mLockedPhoneAnalytics.onScreenOnFromTouch(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index 0e22aa825b35a..bbf981f3de4c4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -39,6 +39,7 @@ import com.android.systemui.R; import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.analytics.LockedPhoneAnalytics; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; @@ -55,12 +56,14 @@ public class StatusBarWindowView extends FrameLayout { private PhoneStatusBar mService; private final Paint mTransparentSrcPaint = new Paint(); + private LockedPhoneAnalytics mLockedPhoneAnalytics; public StatusBarWindowView(Context context, AttributeSet attrs) { super(context, attrs); setMotionEventSplittingEnabled(false); mTransparentSrcPaint.setColor(0); mTransparentSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); + mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(context); } @Override @@ -197,6 +200,7 @@ public class StatusBarWindowView extends FrameLayout { @Override public boolean dispatchTouchEvent(MotionEvent ev) { + mLockedPhoneAnalytics.onTouchEvent(ev, getWidth(), getHeight()); if (mBrightnessMirror != null && mBrightnessMirror.getVisibility() == VISIBLE) { // Disallow new pointers while the brightness mirror is visible. This is so that you // can't touch anything other than the brightness slider while the mirror is showing diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/touch_analytics.proto b/packages/SystemUI/src/com/android/systemui/statusbar/phone/touch_analytics.proto new file mode 100644 index 0000000000000..afc8f77f68962 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/touch_analytics.proto @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2015 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 + */ + +syntax = "proto2"; + +package systemui; + +option java_package = "com.android.systemui.statusbar.phone"; +option java_outer_classname = "TouchAnalyticsProto"; + +message Session { + message TouchEvent { + message BoundingBox { + optional float width = 1; + optional float height = 2; + } + + enum Action { + // Keep in sync with MotionEvent. + DOWN = 0; + UP = 1; + MOVE = 2; + CANCEL = 3; + OUTSIDE = 4; + POINTER_DOWN = 5; + POINTER_UP = 6; + } + + message Pointer { + optional float x = 1; + optional float y = 2; + optional float size = 3; + optional float pressure = 4; + optional int32 id = 5; + optional float removed_length = 6; + optional BoundingBox removed_boundingBox = 7; + } + + optional uint64 timeOffsetNanos = 1; + optional Action action = 2; + optional int32 actionIndex = 3; + repeated Pointer pointers = 4; + optional bool removed_redacted = 5; + optional BoundingBox removed_boundingBox = 6; + } + + message SensorEvent { + enum Type { + ACCELEROMETER = 1; + GYROSCOPE = 4; + LIGHT = 5; + PROXIMITY = 8; + ROTATION_VECTOR = 11; + } + + optional Type type = 1; + optional uint64 timeOffsetNanos = 2; + repeated float values = 3; + optional uint64 timestamp = 4; + } + + message PhoneEvent { + enum Type { + ON_SCREEN_ON = 0; + ON_SCREEN_ON_FROM_TOUCH = 1; + ON_SCREEN_OFF = 2; + ON_SUCCESSFUL_UNLOCK = 3; + ON_BOUNCER_SHOWN = 4; + ON_BOUNCER_HIDDEN = 5; + ON_QS_DOWN = 6; + SET_QS_EXPANDED_TRUE = 7; + SET_QS_EXPANDED_FALSE = 8; + ON_TRACKING_STARTED = 9; + ON_TRACKING_STOPPED = 10; + ON_NOTIFICATION_ACTIVE = 11; + ON_NOTIFICATION_INACTIVE = 12; + ON_NOTIFICATION_DOUBLE_TAP = 13; + SET_NOTIFICATION_EXPANDED = 14; + RESET_NOTIFICATION_EXPANDED = 15; + ON_NOTIFICATION_START_DRAGGING_DOWN = 16; + ON_NOTIFICATION_STOP_DRAGGING_DOWN = 17; + ON_NOTIFICATION_DISMISSED = 18; + ON_NOTIFICATION_START_DISMISSING = 19; + ON_NOTIFICATION_STOP_DISMISSING = 20; + ON_RIGHT_AFFORDANCE_SWIPING_STARTED = 21; + ON_LEFT_AFFORDANCE_SWIPING_STARTED = 22; + ON_AFFORDANCE_SWIPING_ABORTED = 23; + ON_CAMERA_ON = 24; + ON_LEFT_AFFORDANCE_ON = 25; + ON_UNLOCK_HINT_STARTED = 26; + ON_CAMERA_HINT_STARTED = 27; + ON_LEFT_AFFORDANCE_HINT_STARTED = 28; + } + + optional Type type = 1; + optional uint64 timeOffsetNanos = 2; + } + + enum Result { + FAILURE = 0; + SUCCESS = 1; + UNKNOWN = 2; + } + + enum Type { + RESERVED_1 = 0; + RESERVED_2 = 1; + RANDOM_WAKEUP = 2; + REAL = 3; + } + + optional uint64 startTimestampMillis = 1; + optional uint64 durationMillis = 2; + optional string build = 3; + optional Result result = 4; + repeated TouchEvent touchEvents = 5; + repeated SensorEvent sensorEvents = 6; + + optional int32 touchAreaWidth = 9; + optional int32 touchAreaHeight = 10; + optional Type type = 11; + repeated PhoneEvent phoneEvents = 12; +} \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 82c1aa8577cdd..e5bf768d2c0cb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -47,6 +47,7 @@ import com.android.systemui.statusbar.NotificationOverflowContainer; import com.android.systemui.statusbar.SpeedBumpView; import com.android.systemui.statusbar.StackScrollerDecorView; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.analytics.LockedPhoneAnalytics; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.PhoneStatusBar; import com.android.systemui.statusbar.phone.ScrimController; @@ -231,6 +232,7 @@ public class NotificationStackScrollLayout extends ViewGroup private boolean mForceNoOverlappingRendering; private NotificationOverflowContainer mOverflowContainer; private final ArrayList> mTmpList = new ArrayList<>(); + private LockedPhoneAnalytics mLockedPhoneAnalytics; public NotificationStackScrollLayout(Context context) { this(context, null); @@ -264,6 +266,7 @@ public class NotificationStackScrollLayout extends ViewGroup mDebugPaint.setStrokeWidth(2); mDebugPaint.setStyle(Paint.Style.STROKE); } + mLockedPhoneAnalytics = LockedPhoneAnalytics.getInstance(context); } @Override @@ -595,6 +598,12 @@ public class NotificationStackScrollLayout extends ViewGroup veto.performClick(); } if (DEBUG) Log.v(TAG, "onChildDismissed: " + v); + + mLockedPhoneAnalytics.onNotificationDismissed(); + if (mLockedPhoneAnalytics.shouldEnforceBouncer()) { + mPhoneStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */, + false /* dismissShade */, true /* afterKeyguardGone */); + } } @Override @@ -622,6 +631,7 @@ public class NotificationStackScrollLayout extends ViewGroup } public void onBeginDrag(View v) { + mLockedPhoneAnalytics.onNotificatonStartDismissing(); setSwipingInProgress(true); mAmbientState.onBeginDrag(v); if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) { @@ -648,6 +658,7 @@ public class NotificationStackScrollLayout extends ViewGroup } public void onDragCancelled(View v) { + mLockedPhoneAnalytics.onNotificatonStopDismissing(); setSwipingInProgress(false); } diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk index 6a7201c26a4ff..d6d17cb10c2fb 100644 --- a/packages/SystemUI/tests/Android.mk +++ b/packages/SystemUI/tests/Android.mk @@ -17,9 +17,14 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests +LOCAL_PROTOC_OPTIMIZE_TYPE := nano +LOCAL_PROTOC_FLAGS := -I$(LOCAL_PATH)/.. +LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors + LOCAL_AAPT_FLAGS := --auto-add-overlay --extra-packages com.android.systemui:com.android.keyguard LOCAL_SRC_FILES := $(call all-java-files-under, src) \ $(call all-java-files-under, ../src) \ + $(call all-proto-files-under, ../src) \ src/com/android/systemui/EventLogTags.logtags LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res \