• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 package com.android.quickstep.util;
17 
18 import static android.view.MotionEvent.ACTION_CANCEL;
19 import static android.view.MotionEvent.ACTION_DOWN;
20 import static android.view.MotionEvent.ACTION_UP;
21 
22 import android.content.Context;
23 import android.util.Log;
24 import android.view.InputEvent;
25 import android.view.KeyEvent;
26 import android.view.MotionEvent;
27 
28 import com.android.quickstep.InputConsumer;
29 import com.android.quickstep.SimpleOrientationTouchTransformer;
30 import com.android.systemui.shared.system.InputConsumerController;
31 
32 import java.util.function.Supplier;
33 
34 /**
35  * Utility class which manages proxying input events from {@link InputConsumerController}
36  * to an {@link InputConsumer}
37  */
38 public class InputConsumerProxy {
39 
40     private static final String TAG = "InputConsumerProxy";
41 
42     private final Context mContext;
43     private final Supplier<Integer> mRotationSupplier;
44     private final InputConsumerController mInputConsumerController;
45     private Runnable mCallback;
46     private Supplier<InputConsumer> mConsumerSupplier;
47 
48     // The consumer is created lazily on demand.
49     private InputConsumer mInputConsumer;
50 
51     private boolean mDestroyed = false;
52     private boolean mTouchInProgress = false;
53     private boolean mDestroyPending = false;
54 
InputConsumerProxy(Context context, Supplier<Integer> rotationSupplier, InputConsumerController inputConsumerController, Runnable callback, Supplier<InputConsumer> consumerSupplier)55     public InputConsumerProxy(Context context, Supplier<Integer> rotationSupplier,
56             InputConsumerController inputConsumerController,
57             Runnable callback, Supplier<InputConsumer> consumerSupplier) {
58         mContext = context;
59         mRotationSupplier = rotationSupplier;
60         mInputConsumerController = inputConsumerController;
61         mCallback = callback;
62         mConsumerSupplier = consumerSupplier;
63     }
64 
enable()65     public void enable() {
66         if (mDestroyed) {
67             return;
68         }
69         mInputConsumerController.setInputListener(this::onInputConsumerEvent);
70     }
71 
onInputConsumerEvent(InputEvent ev)72     private boolean onInputConsumerEvent(InputEvent ev) {
73         if (ev instanceof MotionEvent) {
74             MotionEvent event = (MotionEvent) ev;
75             int action = event.getActionMasked();
76             boolean isHoverEvent = action == MotionEvent.ACTION_HOVER_ENTER
77                     || action == MotionEvent.ACTION_HOVER_MOVE
78                     || action == MotionEvent.ACTION_HOVER_EXIT;
79             if (isHoverEvent) {
80                 onInputConsumerHoverEvent(event);
81             } else {
82                 onInputConsumerMotionEvent(event);
83             }
84         } else if (ev instanceof KeyEvent) {
85             initInputConsumerIfNeeded();
86             mInputConsumer.onKeyEvent((KeyEvent) ev);
87             return true;
88         }
89         return false;
90     }
91 
onInputConsumerMotionEvent(MotionEvent ev)92     private boolean onInputConsumerMotionEvent(MotionEvent ev) {
93         int action = ev.getAction();
94 
95         // Just to be safe, verify that ACTION_DOWN comes before any other action,
96         // and ignore any ACTION_DOWN after the first one (though that should not happen).
97         if (!mTouchInProgress && action != ACTION_DOWN) {
98             Log.w(TAG, "Received non-down motion before down motion: " + action);
99             return false;
100         }
101         if (mTouchInProgress && action == ACTION_DOWN) {
102             Log.w(TAG, "Received down motion while touch was already in progress");
103             return false;
104         }
105 
106         if (action == ACTION_DOWN) {
107             mTouchInProgress = true;
108             initInputConsumerIfNeeded();
109         } else if (action == ACTION_CANCEL || action == ACTION_UP) {
110             // Finish any pending actions
111             mTouchInProgress = false;
112             if (mDestroyPending) {
113                 destroy();
114             }
115         }
116         if (mInputConsumer != null) {
117             SimpleOrientationTouchTransformer.INSTANCE.get(mContext).transform(ev,
118                     mRotationSupplier.get());
119             mInputConsumer.onMotionEvent(ev);
120         }
121 
122         return true;
123     }
124 
onInputConsumerHoverEvent(MotionEvent ev)125     private void onInputConsumerHoverEvent(MotionEvent ev) {
126         initInputConsumerIfNeeded();
127         if (mInputConsumer != null) {
128             SimpleOrientationTouchTransformer.INSTANCE.get(mContext).transform(ev,
129                     mRotationSupplier.get());
130             mInputConsumer.onHoverEvent(ev);
131         }
132     }
133 
destroy()134     public void destroy() {
135         if (mTouchInProgress) {
136             mDestroyPending = true;
137             return;
138         }
139         mDestroyPending = false;
140         mDestroyed = true;
141         mInputConsumerController.setInputListener(null);
142     }
143 
unregisterCallback()144     public void unregisterCallback() {
145         mCallback = null;
146     }
147 
initInputConsumerIfNeeded()148     private void initInputConsumerIfNeeded() {
149         if (mInputConsumer == null) {
150             if (mCallback != null) {
151                 mCallback.run();
152             }
153             mInputConsumer = mConsumerSupplier.get();
154             mConsumerSupplier = null;
155         }
156     }
157 }
158