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

Commit f5918914 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add InputEvent Compatibility Processor"

parents 7b1e7a38 3ce67d10
Loading
Loading
Loading
Loading
+81 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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 android.view;

import android.content.Context;
import android.os.Build;

import java.util.ArrayList;
import java.util.List;

/**
 * Compatibility processor for InputEvents that allows events to be adjusted before and
 * after it is sent to the application.
 *
 * {@hide}
 */
public class InputEventCompatProcessor {

    protected Context mContext;
    protected int mTargetSdkVersion;

    /** List of events to be used to return the processed events */
    private List<InputEvent> mProcessedEvents;

    public InputEventCompatProcessor(Context context) {
        mContext = context;
        mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
        mProcessedEvents = new ArrayList<>();
    }

    /**
     * Processes the InputEvent for compatibility before it is sent to the app, allowing for the
     * generation of more than one event if necessary.
     *
     * @param e The InputEvent to process
     * @return The list of adjusted events, or null if no adjustments are needed. Do not keep a
     *         reference to the output as the list is reused.
     */
    public List<InputEvent> processInputEventForCompatibility(InputEvent e) {
        if (mTargetSdkVersion < Build.VERSION_CODES.M && e instanceof MotionEvent) {
            mProcessedEvents.clear();
            MotionEvent motion = (MotionEvent) e;
            final int mask =
                    MotionEvent.BUTTON_STYLUS_PRIMARY | MotionEvent.BUTTON_STYLUS_SECONDARY;
            final int buttonState = motion.getButtonState();
            final int compatButtonState = (buttonState & mask) >> 4;
            if (compatButtonState != 0) {
                motion.setButtonState(buttonState | compatButtonState);
            }
            mProcessedEvents.add(motion);
            return mProcessedEvents;
        }
        return null;
    }

    /**
     * Processes the InputEvent for compatibility before it is finished by calling
     * InputEventReceiver#finishInputEvent().
     *
     * @param e The InputEvent to process
     * @return The InputEvent to finish, or null if it should not be finished
     */
    public InputEvent processInputEventBeforeFinish(InputEvent e) {
        // No changes needed
        return e;
    }
}
+61 −16
Original line number Diff line number Diff line
@@ -124,6 +124,7 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;

@@ -543,6 +544,8 @@ public final class ViewRootImpl implements ViewParent,

    private boolean mNeedsRendererSetup;

    private final InputEventCompatProcessor mInputCompatProcessor;

    /**
     * Consistency verifier for debugging purposes.
     */
@@ -598,6 +601,25 @@ public final class ViewRootImpl implements ViewParent,
        mChoreographer = Choreographer.getInstance();
        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);

        String processorOverrideName = context.getResources().getString(
                                    R.string.config_inputEventCompatProcessorOverrideClassName);
        if (processorOverrideName.isEmpty()) {
            // No compatibility processor override, using default.
            mInputCompatProcessor = new InputEventCompatProcessor(context);
        } else {
            InputEventCompatProcessor compatProcessor = null;
            try {
                final Class<? extends InputEventCompatProcessor> klass =
                        (Class<? extends InputEventCompatProcessor>) Class.forName(
                                processorOverrideName);
                compatProcessor = klass.getConstructor(Context.class).newInstance(context);
            } catch (Exception e) {
                Log.e(TAG, "Unable to create the InputEventCompatProcessor. ", e);
            } finally {
                mInputCompatProcessor = compatProcessor;
            }
        }

        if (!sCompatibilityDone) {
            sAlwaysAssignFocus = mTargetSdkVersion < Build.VERSION_CODES.P;

@@ -7166,6 +7188,7 @@ public final class ViewRootImpl implements ViewParent,
        public static final int FLAG_FINISHED_HANDLED = 1 << 3;
        public static final int FLAG_RESYNTHESIZED = 1 << 4;
        public static final int FLAG_UNHANDLED = 1 << 5;
        public static final int FLAG_MODIFIED_FOR_COMPATIBILITY = 1 << 6;

        public QueuedInputEvent mNext;

@@ -7258,7 +7281,6 @@ public final class ViewRootImpl implements ViewParent,
    @UnsupportedAppUsage
    void enqueueInputEvent(InputEvent event,
            InputEventReceiver receiver, int flags, boolean processImmediately) {
        adjustInputEventForCompatibility(event);
        QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);

        // Always enqueue the input event in order, regardless of its time stamp.
@@ -7361,7 +7383,22 @@ public final class ViewRootImpl implements ViewParent,

        if (q.mReceiver != null) {
            boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
            boolean modified = (q.mFlags & QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY) != 0;
            if (modified) {
                Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventBeforeFinish");
                InputEvent processedEvent;
                try {
                    processedEvent =
                            mInputCompatProcessor.processInputEventBeforeFinish(q.mEvent);
                } finally {
                    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
                }
                if (processedEvent != null) {
                    q.mReceiver.finishInputEvent(processedEvent, handled);
                }
            } else {
                q.mReceiver.finishInputEvent(q.mEvent, handled);
            }
        } else {
            q.mEvent.recycleIfNeededAfterDispatch();
        }
@@ -7369,19 +7406,6 @@ public final class ViewRootImpl implements ViewParent,
        recycleQueuedInputEvent(q);
    }

    private void adjustInputEventForCompatibility(InputEvent e) {
        if (mTargetSdkVersion < Build.VERSION_CODES.M && e instanceof MotionEvent) {
            MotionEvent motion = (MotionEvent) e;
            final int mask =
                MotionEvent.BUTTON_STYLUS_PRIMARY | MotionEvent.BUTTON_STYLUS_SECONDARY;
            final int buttonState = motion.getButtonState();
            final int compatButtonState = (buttonState & mask) >> 4;
            if (compatButtonState != 0) {
                motion.setButtonState(buttonState | compatButtonState);
            }
        }
    }

    static boolean isTerminalInputEvent(InputEvent event) {
        if (event instanceof KeyEvent) {
            final KeyEvent keyEvent = (KeyEvent)event;
@@ -7452,8 +7476,29 @@ public final class ViewRootImpl implements ViewParent,

        @Override
        public void onInputEvent(InputEvent event) {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility");
            List<InputEvent> processedEvents;
            try {
                processedEvents =
                    mInputCompatProcessor.processInputEventForCompatibility(event);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
            if (processedEvents != null) {
                if (processedEvents.isEmpty()) {
                    // InputEvent consumed by mInputCompatProcessor
                    finishInputEvent(event, true);
                } else {
                    for (int i = 0; i < processedEvents.size(); i++) {
                        enqueueInputEvent(
                                processedEvents.get(i), this,
                                QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);
                    }
                }
            } else {
                enqueueInputEvent(event, this, 0, true);
            }
        }

        @Override
        public void onBatchedInputEventPending() {
+5 −0
Original line number Diff line number Diff line
@@ -3597,4 +3597,9 @@
    <!-- Component name for default assistant on this device -->
    <string name="config_defaultAssistantComponentName">#+UNSET</string>

    <!-- Class name for the InputEvent compatibility processor override.
         Empty string means use the default compatibility processor
         (android.view.InputEventCompatProcessor). -->
    <string name="config_inputEventCompatProcessorOverrideClassName" translatable="false"></string>

</resources>
+1 −0
Original line number Diff line number Diff line
@@ -302,6 +302,7 @@
  <java-symbol type="bool" name="config_enableWallpaperService" />
  <java-symbol type="bool" name="config_checkWallpaperAtBoot" />
  <java-symbol type="string" name="config_wallpaperManagerServiceName" />
  <java-symbol type="string" name="config_inputEventCompatProcessorOverrideClassName" />
  <java-symbol type="bool" name="config_enableUpdateableTimeZoneRules" />
  <java-symbol type="bool" name="config_timeZoneRulesUpdateTrackingEnabled" />
  <java-symbol type="string" name="config_timeZoneRulesUpdaterPackage" />