diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java index c2f246f3922fc..58f5d2a5b43fa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java @@ -52,6 +52,9 @@ import android.widget.ImageView; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEvent; +import com.android.internal.logging.UiEventLogger; +import com.android.internal.logging.UiEventLoggerImpl; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; import com.android.systemui.R; @@ -64,6 +67,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface { private static final String TAG = KeyButtonView.class.getSimpleName(); private final boolean mPlaySounds; + private final UiEventLogger mUiEventLogger; private int mContentDescriptionRes; private long mDownTime; private int mCode; @@ -72,7 +76,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface { private boolean mIsVertical; private AudioManager mAudioManager; private boolean mGestureAborted; - private boolean mLongClicked; + @VisibleForTesting boolean mLongClicked; private OnClickListener mOnClickListener; private final KeyButtonRipple mRipple; private final OverviewProxyService mOverviewProxyService; @@ -82,6 +86,40 @@ public class KeyButtonView extends ImageView implements ButtonInterface { private float mDarkIntensity; private boolean mHasOvalBg = false; + @VisibleForTesting + public enum NavBarActionsEvent implements UiEventLogger.UiEventEnum { + + @UiEvent(doc = "The home button was pressed in the navigation bar.") + NAVBAR_HOME_BUTTON_TAP(533), + + @UiEvent(doc = "The back button was pressed in the navigation bar.") + NAVBAR_BACK_BUTTON_TAP(534), + + @UiEvent(doc = "The overview button was pressed in the navigation bar.") + NAVBAR_OVERVIEW_BUTTON_TAP(535), + + @UiEvent(doc = "The home button was long-pressed in the navigation bar.") + NAVBAR_HOME_BUTTON_LONGPRESS(536), + + @UiEvent(doc = "The back button was long-pressed in the navigation bar.") + NAVBAR_BACK_BUTTON_LONGPRESS(537), + + @UiEvent(doc = "The overview button was long-pressed in the navigation bar.") + NAVBAR_OVERVIEW_BUTTON_LONGPRESS(538), + + NONE(0); // an event we should not log + + private final int mId; + + NavBarActionsEvent(int id) { + mId = id; + } + + @Override + public int getId() { + return mId; + } + } private final Runnable mCheckLongPress = new Runnable() { public void run() { if (isPressed()) { @@ -104,12 +142,14 @@ public class KeyButtonView extends ImageView implements ButtonInterface { } public KeyButtonView(Context context, AttributeSet attrs, int defStyle) { - this(context, attrs, defStyle, InputManager.getInstance()); + this(context, attrs, defStyle, InputManager.getInstance(), new UiEventLoggerImpl()); } @VisibleForTesting - public KeyButtonView(Context context, AttributeSet attrs, int defStyle, InputManager manager) { + public KeyButtonView(Context context, AttributeSet attrs, int defStyle, InputManager manager, + UiEventLogger uiEventLogger) { super(context, attrs); + mUiEventLogger = uiEventLogger; TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.KeyButtonView, defStyle, 0); @@ -326,13 +366,48 @@ public class KeyButtonView extends ImageView implements ButtonInterface { sendEvent(action, flags, SystemClock.uptimeMillis()); } + private void logSomePresses(int action, int flags) { + boolean longPressSet = (flags & KeyEvent.FLAG_LONG_PRESS) != 0; + NavBarActionsEvent uiEvent = NavBarActionsEvent.NONE; + if (action == MotionEvent.ACTION_UP && mLongClicked) { + return; // don't log the up after a long press + } + if (action == MotionEvent.ACTION_DOWN && !longPressSet) { + return; // don't log a down unless it is also the long press marker + } + if ((flags & KeyEvent.FLAG_CANCELED) != 0 + || (flags & KeyEvent.FLAG_CANCELED_LONG_PRESS) != 0) { + return; // don't log various cancels + } + switch(mCode) { + case KeyEvent.KEYCODE_BACK: + uiEvent = longPressSet + ? NavBarActionsEvent.NAVBAR_BACK_BUTTON_LONGPRESS + : NavBarActionsEvent.NAVBAR_BACK_BUTTON_TAP; + break; + case KeyEvent.KEYCODE_HOME: + uiEvent = longPressSet + ? NavBarActionsEvent.NAVBAR_HOME_BUTTON_LONGPRESS + : NavBarActionsEvent.NAVBAR_HOME_BUTTON_TAP; + break; + case KeyEvent.KEYCODE_APP_SWITCH: + uiEvent = longPressSet + ? NavBarActionsEvent.NAVBAR_OVERVIEW_BUTTON_LONGPRESS + : NavBarActionsEvent.NAVBAR_OVERVIEW_BUTTON_TAP; + break; + } + if (uiEvent != NavBarActionsEvent.NONE) { + mUiEventLogger.log(uiEvent); + } + } + private void sendEvent(int action, int flags, long when) { mMetricsLogger.write(new LogMaker(MetricsEvent.ACTION_NAV_BUTTON_EVENT) .setType(MetricsEvent.TYPE_ACTION) .setSubtype(mCode) .addTaggedData(MetricsEvent.FIELD_NAV_ACTION, action) .addTaggedData(MetricsEvent.FIELD_FLAGS, flags)); - // TODO(b/122195391): Added logs to make sure sysui is sending back button events + logSomePresses(action, flags); if (mCode == KeyEvent.KEYCODE_BACK && flags != KeyEvent.FLAG_LONG_PRESS) { Log.i(TAG, "Back button event: " + KeyEvent.actionToString(action)); if (action == MotionEvent.ACTION_UP) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyButtonViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyButtonViewTest.java index 943674a7bf647..5e9d592ab7731 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyButtonViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyButtonViewTest.java @@ -14,21 +14,33 @@ package com.android.systemui.statusbar.policy; -import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_NAV_BUTTON_EVENT; -import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_FLAGS; -import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_NAV_ACTION; +import static android.view.KeyEvent.ACTION_DOWN; +import static android.view.KeyEvent.ACTION_UP; +import static android.view.KeyEvent.FLAG_CANCELED; +import static android.view.KeyEvent.FLAG_LONG_PRESS; +import static android.view.KeyEvent.KEYCODE_0; +import static android.view.KeyEvent.KEYCODE_APP_SWITCH; +import static android.view.KeyEvent.KEYCODE_BACK; +import static android.view.KeyEvent.KEYCODE_HOME; + +import static com.android.systemui.statusbar.policy.KeyButtonView.NavBarActionsEvent.NAVBAR_BACK_BUTTON_LONGPRESS; +import static com.android.systemui.statusbar.policy.KeyButtonView.NavBarActionsEvent.NAVBAR_BACK_BUTTON_TAP; +import static com.android.systemui.statusbar.policy.KeyButtonView.NavBarActionsEvent.NAVBAR_HOME_BUTTON_LONGPRESS; +import static com.android.systemui.statusbar.policy.KeyButtonView.NavBarActionsEvent.NAVBAR_HOME_BUTTON_TAP; +import static com.android.systemui.statusbar.policy.KeyButtonView.NavBarActionsEvent.NAVBAR_OVERVIEW_BUTTON_LONGPRESS; +import static com.android.systemui.statusbar.policy.KeyButtonView.NavBarActionsEvent.NAVBAR_OVERVIEW_BUTTON_TAP; import static junit.framework.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.hardware.input.InputManager; -import android.metrics.LogMaker; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; @@ -37,7 +49,7 @@ import android.view.KeyEvent; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.logging.UiEventLogger; import com.android.systemui.SysuiTestCase; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.recents.OverviewProxyService; @@ -46,12 +58,9 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatcher; import org.mockito.Captor; import org.mockito.MockitoAnnotations; -import java.util.Objects; - @RunWith(AndroidTestingRunner.class) @RunWithLooper @SmallTest @@ -60,6 +69,7 @@ public class KeyButtonViewTest extends SysuiTestCase { private KeyButtonView mKeyButtonView; private MetricsLogger mMetricsLogger; private BubbleController mBubbleController; + private UiEventLogger mUiEventLogger; private InputManager mInputManager = mock(InputManager.class); @Captor private ArgumentCaptor mInputEventCaptor; @@ -70,45 +80,74 @@ public class KeyButtonViewTest extends SysuiTestCase { mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class); mBubbleController = mDependency.injectMockDependency(BubbleController.class); mDependency.injectMockDependency(OverviewProxyService.class); + mUiEventLogger = mDependency.injectMockDependency(UiEventLogger.class); + TestableLooper.get(this).runWithLooper(() -> { - mKeyButtonView = new KeyButtonView(mContext, null, 0, mInputManager); + mKeyButtonView = new KeyButtonView(mContext, null, 0, mInputManager, mUiEventLogger); }); } @Test - public void testMetrics() { - int action = 42; - int flags = 0x141; - int code = KeyEvent.KEYCODE_ENTER; + public void testLogBackPress() { + checkmetrics(KEYCODE_BACK, ACTION_UP, 0, NAVBAR_BACK_BUTTON_TAP); + } + + @Test + public void testLogOverviewPress() { + checkmetrics(KEYCODE_APP_SWITCH, ACTION_UP, 0, NAVBAR_OVERVIEW_BUTTON_TAP); + } + + @Test + public void testLogHomePress() { + checkmetrics(KEYCODE_HOME, ACTION_UP, 0, NAVBAR_HOME_BUTTON_TAP); + } + + @Test + public void testLogBackLongPressLog() { + checkmetrics(KEYCODE_BACK, ACTION_DOWN, FLAG_LONG_PRESS, NAVBAR_BACK_BUTTON_LONGPRESS); + } + + @Test + public void testLogOverviewLongPress() { + checkmetrics(KEYCODE_APP_SWITCH, ACTION_DOWN, FLAG_LONG_PRESS, + NAVBAR_OVERVIEW_BUTTON_LONGPRESS); + } + + @Test + public void testLogHomeLongPress() { + checkmetrics(KEYCODE_HOME, ACTION_DOWN, FLAG_LONG_PRESS, NAVBAR_HOME_BUTTON_LONGPRESS); + } + + @Test + public void testNoLogKeyDown() { + checkmetrics(KEYCODE_BACK, ACTION_DOWN, 0, null); + } + + @Test + public void testNoLogTapAfterLong() { + mKeyButtonView.mLongClicked = true; + checkmetrics(KEYCODE_BACK, ACTION_UP, 0, null); + } + + @Test + public void testNoLogCanceled() { + checkmetrics(KEYCODE_HOME, ACTION_UP, FLAG_CANCELED, null); + } + + @Test + public void testNoLogArbitraryKeys() { + checkmetrics(KEYCODE_0, ACTION_UP, 0, null); + } + + private void checkmetrics(int code, int action, int flag, + KeyButtonView.NavBarActionsEvent expected) { mKeyButtonView.setCode(code); - mKeyButtonView.sendEvent(action, flags); - - verify(mMetricsLogger).write(argThat(new ArgumentMatcher() { - public String mReason; - - @Override - public boolean matches(LogMaker argument) { - return checkField("category", argument.getCategory(), ACTION_NAV_BUTTON_EVENT) - && checkField("type", argument.getType(), MetricsEvent.TYPE_ACTION) - && checkField("subtype", argument.getSubtype(), code) - && checkField("FIELD_FLAGS", argument.getTaggedData(FIELD_FLAGS), flags) - && checkField("FIELD_NAV_ACTION", argument.getTaggedData(FIELD_NAV_ACTION), - action); - } - - private boolean checkField(String field, Object val, Object val2) { - if (!Objects.equals(val, val2)) { - mReason = "Expected " + field + " " + val2 + " but was " + val; - return false; - } - return true; - } - - @Override - public String toString() { - return mReason; - } - })); + mKeyButtonView.sendEvent(action, flag); + if (expected == null) { + verify(mUiEventLogger, never()).log(any(KeyButtonView.NavBarActionsEvent.class)); + } else { + verify(mUiEventLogger, times(1)).log(expected); + } } @Test