1/*
2 * Copyright (C) 2017 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
17package com.android.systemui.pip.phone;
18
19import static android.view.WindowManager.INPUT_CONSUMER_PIP;
20
21import android.os.Looper;
22import android.os.RemoteException;
23import android.util.Log;
24import android.view.BatchedInputEventReceiver;
25import android.view.Choreographer;
26import android.view.InputChannel;
27import android.view.InputEvent;
28import android.view.IWindowManager;
29import android.view.MotionEvent;
30
31import com.android.systemui.recents.misc.Utilities;
32
33import java.io.PrintWriter;
34
35/**
36 * Manages the input consumer that allows the SystemUI to control the PiP.
37 */
38public class InputConsumerController {
39
40    private static final String TAG = InputConsumerController.class.getSimpleName();
41
42    /**
43     * Listener interface for callers to subscribe to touch events.
44     */
45    public interface TouchListener {
46        boolean onTouchEvent(MotionEvent ev);
47    }
48
49    /**
50     * Listener interface for callers to learn when this class is registered or unregistered with
51     * window manager
52     */
53    public interface RegistrationListener {
54        void onRegistrationChanged(boolean isRegistered);
55    }
56
57    /**
58     * Input handler used for the PiP input consumer. Input events are batched and consumed with the
59     * SurfaceFlinger vsync.
60     */
61    private final class PipInputEventReceiver extends BatchedInputEventReceiver {
62
63        public PipInputEventReceiver(InputChannel inputChannel, Looper looper) {
64            super(inputChannel, looper, Choreographer.getSfInstance());
65        }
66
67        @Override
68        public void onInputEvent(InputEvent event) {
69            boolean handled = true;
70            try {
71                // To be implemented for input handling over Pip windows
72                if (mListener != null && event instanceof MotionEvent) {
73                    MotionEvent ev = (MotionEvent) event;
74                    handled = mListener.onTouchEvent(ev);
75                }
76            } finally {
77                finishInputEvent(event, handled);
78            }
79        }
80    }
81
82    private IWindowManager mWindowManager;
83
84    private PipInputEventReceiver mInputEventReceiver;
85    private TouchListener mListener;
86    private RegistrationListener mRegistrationListener;
87
88    public InputConsumerController(IWindowManager windowManager) {
89        mWindowManager = windowManager;
90        registerInputConsumer();
91    }
92
93    /**
94     * Sets the touch listener.
95     */
96    public void setTouchListener(TouchListener listener) {
97        mListener = listener;
98    }
99
100    /**
101     * Sets the registration listener.
102     */
103    public void setRegistrationListener(RegistrationListener listener) {
104        mRegistrationListener = listener;
105        if (mRegistrationListener != null) {
106            mRegistrationListener.onRegistrationChanged(mInputEventReceiver != null);
107        }
108    }
109
110    /**
111     * Check if the InputConsumer is currently registered with WindowManager
112     *
113     * @return {@code true} if registered, {@code false} if not.
114     */
115    public boolean isRegistered() {
116        return mInputEventReceiver != null;
117    }
118
119    /**
120     * Registers the input consumer.
121     */
122    public void registerInputConsumer() {
123        if (mInputEventReceiver == null) {
124            final InputChannel inputChannel = new InputChannel();
125            try {
126                mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
127                mWindowManager.createInputConsumer(INPUT_CONSUMER_PIP, inputChannel);
128            } catch (RemoteException e) {
129                Log.e(TAG, "Failed to create PIP input consumer", e);
130            }
131            mInputEventReceiver = new PipInputEventReceiver(inputChannel, Looper.myLooper());
132            if (mRegistrationListener != null) {
133                mRegistrationListener.onRegistrationChanged(true /* isRegistered */);
134            }
135        }
136    }
137
138    /**
139     * Unregisters the input consumer.
140     */
141    public void unregisterInputConsumer() {
142        if (mInputEventReceiver != null) {
143            try {
144                mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
145            } catch (RemoteException e) {
146                Log.e(TAG, "Failed to destroy PIP input consumer", e);
147            }
148            mInputEventReceiver.dispose();
149            mInputEventReceiver = null;
150            if (mRegistrationListener != null) {
151                mRegistrationListener.onRegistrationChanged(false /* isRegistered */);
152            }
153        }
154    }
155
156    public void dump(PrintWriter pw, String prefix) {
157        final String innerPrefix = prefix + "  ";
158        pw.println(prefix + TAG);
159        pw.println(innerPrefix + "registered=" + (mInputEventReceiver != null));
160    }
161}
162