• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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