Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 091c5da3 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "add uievent logs for nav buttons" into rvc-dev am: a15a870d

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/11758280

Change-Id: Ic1e6aa11087183a4c74a2970c5acc13f8c312d03
parents 240da101 a15a870d
Loading
Loading
Loading
Loading
+79 −4
Original line number Diff line number Diff line
@@ -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) {
+81 −42
Original line number Diff line number Diff line
@@ -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<KeyEvent> 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;
        mKeyButtonView.setCode(code);
        mKeyButtonView.sendEvent(action, flags);
    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);
    }

        verify(mMetricsLogger).write(argThat(new ArgumentMatcher<LogMaker>() {
            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);
    @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);
    }

            private boolean checkField(String field, Object val, Object val2) {
                if (!Objects.equals(val, val2)) {
                    mReason = "Expected " + field + " " + val2 + " but was " + val;
                    return false;
    @Test
    public void testNoLogTapAfterLong() {
        mKeyButtonView.mLongClicked = true;
        checkmetrics(KEYCODE_BACK, ACTION_UP, 0, null);
    }
                return true;

    @Test
    public void testNoLogCanceled() {
        checkmetrics(KEYCODE_HOME, ACTION_UP, FLAG_CANCELED, null);
    }

            @Override
            public String toString() {
                return mReason;
    @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, flag);
        if (expected == null) {
            verify(mUiEventLogger, never()).log(any(KeyButtonView.NavBarActionsEvent.class));
        } else {
            verify(mUiEventLogger, times(1)).log(expected);
        }
        }));
    }

    @Test