1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.view; 18 19 import android.content.Context; 20 import android.os.Build; 21 import android.os.Handler; 22 23 import com.android.window.flags.Flags; 24 25 import java.util.ArrayList; 26 import java.util.List; 27 28 /** 29 * Compatibility processor for InputEvents that allows events to be adjusted before and 30 * after it is sent to the application. 31 * 32 * {@hide} 33 */ 34 public class InputEventCompatProcessor { 35 36 protected Context mContext; 37 protected int mTargetSdkVersion; 38 private final LetterboxScrollProcessor mLetterboxScrollProcessor; 39 40 /** List of events to be used to return the processed events */ 41 private final List<InputEvent> mProcessedEvents; 42 InputEventCompatProcessor(Context context)43 public InputEventCompatProcessor(Context context) { 44 this(context, null); 45 } 46 InputEventCompatProcessor(Context context, Handler handler)47 public InputEventCompatProcessor(Context context, Handler handler) { 48 mContext = context; 49 mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion; 50 if (Flags.scrollingFromLetterbox()) { 51 mLetterboxScrollProcessor = new LetterboxScrollProcessor(mContext, handler); 52 } else { 53 mLetterboxScrollProcessor = null; 54 } 55 56 mProcessedEvents = new ArrayList<>(); 57 } 58 59 60 /** 61 * Processes the InputEvent for compatibility before it is sent to the app, allowing for the 62 * generation of more than one event if necessary. 63 * 64 * @param inputEvent The InputEvent to process. 65 * @return The list of adjusted events, or null if no adjustments are needed. The list is empty 66 * if the event should be ignored. Do not keep a reference to the output as the list is reused. 67 */ processInputEventForCompatibility(InputEvent inputEvent)68 public List<InputEvent> processInputEventForCompatibility(InputEvent inputEvent) { 69 mProcessedEvents.clear(); 70 71 // Process the event for StylusButtonCompatibility. 72 final InputEvent stylusCompatEvent = processStylusButtonCompatibility(inputEvent); 73 74 // Process the event for LetterboxScrollCompatibility. 75 List<MotionEvent> letterboxScrollCompatEvents = processLetterboxScrollCompatibility( 76 stylusCompatEvent != null ? stylusCompatEvent : inputEvent); 77 78 // If no adjustments are needed for LetterboxCompatibility. 79 if (letterboxScrollCompatEvents == null) { 80 // If stylus compatibility made adjustments, return that adjusted event. 81 if (stylusCompatEvent != null) { 82 mProcessedEvents.add(stylusCompatEvent); 83 return mProcessedEvents; 84 } 85 // Otherwise, return null to indicate no adjustments. 86 return null; 87 } 88 89 // Otherwise if LetterboxCompatibility made adjustments, return the list of adjusted events. 90 mProcessedEvents.addAll(letterboxScrollCompatEvents); 91 return mProcessedEvents; 92 } 93 94 /** 95 * Processes the InputEvent for compatibility before it is finished by calling 96 * InputEventReceiver#finishInputEvent(). 97 * 98 * @param inputEvent The InputEvent to process. 99 * @return The InputEvent to finish, or null if it should not be finished. 100 */ processInputEventBeforeFinish(InputEvent inputEvent)101 public InputEvent processInputEventBeforeFinish(InputEvent inputEvent) { 102 if (mLetterboxScrollProcessor != null && inputEvent instanceof MotionEvent motionEvent) { 103 // LetterboxScrollProcessor may have generated events while processing motion events. 104 return mLetterboxScrollProcessor.processMotionEventBeforeFinish(motionEvent); 105 } 106 107 // No changes needed 108 return inputEvent; 109 } 110 111 processLetterboxScrollCompatibility(InputEvent inputEvent)112 private List<MotionEvent> processLetterboxScrollCompatibility(InputEvent inputEvent) { 113 if (mLetterboxScrollProcessor != null 114 && inputEvent instanceof MotionEvent motionEvent 115 && motionEvent.getAction() != MotionEvent.ACTION_OUTSIDE) { 116 return mLetterboxScrollProcessor.processMotionEvent(motionEvent); 117 } 118 return null; 119 } 120 121 processStylusButtonCompatibility(InputEvent inputEvent)122 private InputEvent processStylusButtonCompatibility(InputEvent inputEvent) { 123 if (mTargetSdkVersion < Build.VERSION_CODES.M && inputEvent instanceof MotionEvent) { 124 MotionEvent motion = (MotionEvent) inputEvent; 125 final int mask = 126 MotionEvent.BUTTON_STYLUS_PRIMARY | MotionEvent.BUTTON_STYLUS_SECONDARY; 127 final int buttonState = motion.getButtonState(); 128 final int compatButtonState = (buttonState & mask) >> 4; 129 if (compatButtonState != 0) { 130 motion.setButtonState(buttonState | compatButtonState); 131 } 132 return motion; 133 } 134 return null; 135 } 136 } 137