blob: 8ba5b9951c54301c261435bd2dba9bd40113c187 [file] [log] [blame]
Evan Rosky8d782e02019-10-14 15:43:53 -07001/*
2 * Copyright (C) 2019 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.wm;
18
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ValueAnimator;
Yunfan Chenc02a5ac2020-06-16 01:52:41 +000022import android.content.Context;
Evan Rosky8d782e02019-10-14 15:43:53 -070023import android.content.res.Configuration;
Tiger Huang227c6652020-03-19 22:28:43 +080024import android.graphics.Point;
Tony Huangd1cb4402020-04-27 11:36:00 +080025import android.graphics.Rect;
Evan Rosky8d782e02019-10-14 15:43:53 -070026import android.os.Handler;
27import android.os.RemoteException;
Yunfan Chenc02a5ac2020-06-16 01:52:41 +000028import android.os.ServiceManager;
Evan Rosky8d782e02019-10-14 15:43:53 -070029import android.util.Slog;
30import android.util.SparseArray;
31import android.view.IDisplayWindowInsetsController;
32import android.view.InsetsSource;
33import android.view.InsetsSourceControl;
34import android.view.InsetsState;
35import android.view.Surface;
36import android.view.SurfaceControl;
37import android.view.WindowInsets;
38import android.view.animation.Interpolator;
39import android.view.animation.PathInterpolator;
40
Yunfan Chenc02a5ac2020-06-16 01:52:41 +000041import com.android.internal.view.IInputMethodManager;
Evan Roskyaf9f27c2020-02-18 18:58:35 +000042import com.android.systemui.TransactionPool;
Evan Rosky8d782e02019-10-14 15:43:53 -070043import com.android.systemui.dagger.qualifiers.Main;
44
45import java.util.ArrayList;
46
47import javax.inject.Inject;
48import javax.inject.Singleton;
49
50/**
51 * Manages IME control at the display-level. This occurs when IME comes up in multi-window mode.
52 */
53@Singleton
Winson Chung95c9fca2020-01-22 09:48:40 -080054public class DisplayImeController implements DisplayController.OnDisplaysChangedListener {
Evan Rosky8d782e02019-10-14 15:43:53 -070055 private static final String TAG = "DisplayImeController";
56
Evan Roskyfdc71c42020-03-13 18:23:08 -070057 private static final boolean DEBUG = false;
58
Evan Rosky437a8792020-06-19 18:17:34 -070059 // NOTE: All these constants came from InsetsController.
Evan Roskyaf9f27c2020-02-18 18:58:35 +000060 public static final int ANIMATION_DURATION_SHOW_MS = 275;
61 public static final int ANIMATION_DURATION_HIDE_MS = 340;
Evan Rosky95729202020-02-21 10:16:08 -080062 public static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
Evan Rosky8d782e02019-10-14 15:43:53 -070063 private static final int DIRECTION_NONE = 0;
64 private static final int DIRECTION_SHOW = 1;
65 private static final int DIRECTION_HIDE = 2;
Evan Rosky437a8792020-06-19 18:17:34 -070066 private static final int FLOATING_IME_BOTTOM_INSET = -80;
Evan Rosky8d782e02019-10-14 15:43:53 -070067
68 SystemWindows mSystemWindows;
69 final Handler mHandler;
Evan Roskyaf9f27c2020-02-18 18:58:35 +000070 final TransactionPool mTransactionPool;
Evan Rosky8d782e02019-10-14 15:43:53 -070071
72 final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
73
74 final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>();
75
76 @Inject
Winson Chung95c9fca2020-01-22 09:48:40 -080077 public DisplayImeController(SystemWindows syswin, DisplayController displayController,
Evan Roskyaf9f27c2020-02-18 18:58:35 +000078 @Main Handler mainHandler, TransactionPool transactionPool) {
Evan Rosky8d782e02019-10-14 15:43:53 -070079 mHandler = mainHandler;
80 mSystemWindows = syswin;
Evan Roskyaf9f27c2020-02-18 18:58:35 +000081 mTransactionPool = transactionPool;
Evan Rosky8d782e02019-10-14 15:43:53 -070082 displayController.addDisplayWindowListener(this);
83 }
84
85 @Override
86 public void onDisplayAdded(int displayId) {
87 // Add's a system-ui window-manager specifically for ime. This type is special because
88 // WM will defer IME inset handling to it in multi-window scenarious.
89 PerDisplay pd = new PerDisplay(displayId,
90 mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation());
91 try {
92 mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, pd);
93 } catch (RemoteException e) {
94 Slog.w(TAG, "Unable to set insets controller on display " + displayId);
95 }
96 mImePerDisplay.put(displayId, pd);
97 }
98
99 @Override
100 public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
101 PerDisplay pd = mImePerDisplay.get(displayId);
102 if (pd == null) {
103 return;
104 }
105 if (mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation()
106 != pd.mRotation && isImeShowing(displayId)) {
Tiger Huang227c6652020-03-19 22:28:43 +0800107 pd.startAnimation(true, false /* forceRestart */);
Evan Rosky8d782e02019-10-14 15:43:53 -0700108 }
109 }
110
111 @Override
112 public void onDisplayRemoved(int displayId) {
113 try {
114 mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, null);
115 } catch (RemoteException e) {
116 Slog.w(TAG, "Unable to remove insets controller on display " + displayId);
117 }
118 mImePerDisplay.remove(displayId);
119 }
120
121 private boolean isImeShowing(int displayId) {
122 PerDisplay pd = mImePerDisplay.get(displayId);
123 if (pd == null) {
124 return false;
125 }
126 final InsetsSource imeSource = pd.mInsetsState.getSource(InsetsState.ITYPE_IME);
127 return imeSource != null && pd.mImeSourceControl != null && imeSource.isVisible();
128 }
129
130 private void dispatchPositionChanged(int displayId, int imeTop,
131 SurfaceControl.Transaction t) {
132 synchronized (mPositionProcessors) {
133 for (ImePositionProcessor pp : mPositionProcessors) {
134 pp.onImePositionChanged(displayId, imeTop, t);
135 }
136 }
137 }
138
Evan Rosky95729202020-02-21 10:16:08 -0800139 private void dispatchStartPositioning(int displayId, int hiddenTop, int shownTop,
Evan Rosky8d782e02019-10-14 15:43:53 -0700140 boolean show, SurfaceControl.Transaction t) {
141 synchronized (mPositionProcessors) {
142 for (ImePositionProcessor pp : mPositionProcessors) {
Evan Rosky95729202020-02-21 10:16:08 -0800143 pp.onImeStartPositioning(displayId, hiddenTop, shownTop, show, t);
Evan Rosky8d782e02019-10-14 15:43:53 -0700144 }
145 }
146 }
147
Evan Rosky95729202020-02-21 10:16:08 -0800148 private void dispatchEndPositioning(int displayId, boolean cancel,
Evan Rosky8d782e02019-10-14 15:43:53 -0700149 SurfaceControl.Transaction t) {
150 synchronized (mPositionProcessors) {
151 for (ImePositionProcessor pp : mPositionProcessors) {
Evan Rosky95729202020-02-21 10:16:08 -0800152 pp.onImeEndPositioning(displayId, cancel, t);
Evan Rosky8d782e02019-10-14 15:43:53 -0700153 }
154 }
155 }
156
157 /**
158 * Adds an {@link ImePositionProcessor} to be called during ime position updates.
159 */
160 public void addPositionProcessor(ImePositionProcessor processor) {
161 synchronized (mPositionProcessors) {
162 if (mPositionProcessors.contains(processor)) {
163 return;
164 }
165 mPositionProcessors.add(processor);
166 }
167 }
168
169 /**
170 * Removes an {@link ImePositionProcessor} to be called during ime position updates.
171 */
172 public void removePositionProcessor(ImePositionProcessor processor) {
173 synchronized (mPositionProcessors) {
174 mPositionProcessors.remove(processor);
175 }
176 }
177
178 class PerDisplay extends IDisplayWindowInsetsController.Stub {
179 final int mDisplayId;
180 final InsetsState mInsetsState = new InsetsState();
181 InsetsSourceControl mImeSourceControl = null;
182 int mAnimationDirection = DIRECTION_NONE;
183 ValueAnimator mAnimation = null;
184 int mRotation = Surface.ROTATION_0;
Evan Rosky95729202020-02-21 10:16:08 -0800185 boolean mImeShowing = false;
Evan Roskyaf3d6372020-05-14 19:01:11 -0700186 final Rect mImeFrame = new Rect();
Evan Rosky8d782e02019-10-14 15:43:53 -0700187
188 PerDisplay(int displayId, int initialRotation) {
189 mDisplayId = displayId;
190 mRotation = initialRotation;
191 }
192
193 @Override
194 public void insetsChanged(InsetsState insetsState) {
Evan Rosky653a7252020-05-28 17:12:26 -0700195 mHandler.post(() -> {
196 if (mInsetsState.equals(insetsState)) {
197 return;
198 }
Tony Huangd1cb4402020-04-27 11:36:00 +0800199
Evan Rosky653a7252020-05-28 17:12:26 -0700200 final InsetsSource newSource = insetsState.getSource(InsetsState.ITYPE_IME);
201 final Rect newFrame = newSource.getFrame();
202 final Rect oldFrame = mInsetsState.getSource(InsetsState.ITYPE_IME).getFrame();
Tony Huangd1cb4402020-04-27 11:36:00 +0800203
Evan Rosky653a7252020-05-28 17:12:26 -0700204 mInsetsState.set(insetsState, true /* copySources */);
205 if (mImeShowing && !newFrame.equals(oldFrame) && newSource.isVisible()) {
206 if (DEBUG) Slog.d(TAG, "insetsChanged when IME showing, restart animation");
207 startAnimation(mImeShowing, true /* forceRestart */);
208 }
209 });
Evan Rosky8d782e02019-10-14 15:43:53 -0700210 }
211
212 @Override
213 public void insetsControlChanged(InsetsState insetsState,
214 InsetsSourceControl[] activeControls) {
215 insetsChanged(insetsState);
216 if (activeControls != null) {
217 for (InsetsSourceControl activeControl : activeControls) {
218 if (activeControl == null) {
219 continue;
220 }
221 if (activeControl.getType() == InsetsState.ITYPE_IME) {
Tiger Huang227c6652020-03-19 22:28:43 +0800222 mHandler.post(() -> {
223 final Point lastSurfacePosition = mImeSourceControl != null
224 ? mImeSourceControl.getSurfacePosition() : null;
225 mImeSourceControl = activeControl;
226 if (!activeControl.getSurfacePosition().equals(lastSurfacePosition)
227 && mAnimation != null) {
228 startAnimation(mImeShowing, true /* forceRestart */);
229 }
230 });
Evan Rosky8d782e02019-10-14 15:43:53 -0700231 }
232 }
233 }
234 }
235
236 @Override
237 public void showInsets(int types, boolean fromIme) {
238 if ((types & WindowInsets.Type.ime()) == 0) {
239 return;
240 }
Evan Roskyfdc71c42020-03-13 18:23:08 -0700241 if (DEBUG) Slog.d(TAG, "Got showInsets for ime");
Evan Rosky653a7252020-05-28 17:12:26 -0700242 mHandler.post(() -> startAnimation(true /* show */, false /* forceRestart */));
Evan Rosky8d782e02019-10-14 15:43:53 -0700243 }
244
245 @Override
246 public void hideInsets(int types, boolean fromIme) {
247 if ((types & WindowInsets.Type.ime()) == 0) {
248 return;
249 }
Evan Roskyfdc71c42020-03-13 18:23:08 -0700250 if (DEBUG) Slog.d(TAG, "Got hideInsets for ime");
Evan Rosky653a7252020-05-28 17:12:26 -0700251 mHandler.post(() -> startAnimation(false /* show */, false /* forceRestart */));
Evan Rosky8d782e02019-10-14 15:43:53 -0700252 }
253
254 /**
255 * Sends the local visibility state back to window manager. Needed for legacy adjustForIme.
256 */
257 private void setVisibleDirectly(boolean visible) {
258 mInsetsState.getSource(InsetsState.ITYPE_IME).setVisible(visible);
259 try {
260 mSystemWindows.mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
261 } catch (RemoteException e) {
262 }
263 }
264
Evan Roskyaf3d6372020-05-14 19:01:11 -0700265 private int imeTop(float surfaceOffset) {
266 return mImeFrame.top + (int) surfaceOffset;
Evan Rosky8d782e02019-10-14 15:43:53 -0700267 }
268
Tiger Huang227c6652020-03-19 22:28:43 +0800269 private void startAnimation(final boolean show, final boolean forceRestart) {
Evan Rosky8d782e02019-10-14 15:43:53 -0700270 final InsetsSource imeSource = mInsetsState.getSource(InsetsState.ITYPE_IME);
271 if (imeSource == null || mImeSourceControl == null) {
272 return;
273 }
Evan Roskyaf3d6372020-05-14 19:01:11 -0700274 // Set frame, but only if the new frame isn't empty -- this maintains continuity
275 final Rect newFrame = imeSource.getFrame();
Evan Rosky437a8792020-06-19 18:17:34 -0700276 mImeFrame.set(newFrame);
277 final boolean isFloating = newFrame.height() == 0;
278 if (isFloating) {
279 // This is likely a "floating" or "expanded" IME, so to get animations, just
280 // pretend the ime has some size just below the screen.
Evan Roskyaf3d6372020-05-14 19:01:11 -0700281 mImeFrame.set(newFrame);
Evan Rosky437a8792020-06-19 18:17:34 -0700282 final int floatingInset = (int) (
283 mSystemWindows.mDisplayController.getDisplayLayout(mDisplayId).density()
284 * FLOATING_IME_BOTTOM_INSET);
285 mImeFrame.bottom -= floatingInset;
Evan Roskyaf3d6372020-05-14 19:01:11 -0700286 }
Evan Rosky653a7252020-05-28 17:12:26 -0700287 if (DEBUG) {
288 Slog.d(TAG, "Run startAnim show:" + show + " was:"
289 + (mAnimationDirection == DIRECTION_SHOW ? "SHOW"
290 : (mAnimationDirection == DIRECTION_HIDE ? "HIDE" : "NONE")));
291 }
292 if (!forceRestart && (mAnimationDirection == DIRECTION_SHOW && show)
293 || (mAnimationDirection == DIRECTION_HIDE && !show)) {
294 return;
295 }
296 boolean seek = false;
297 float seekValue = 0;
298 if (mAnimation != null) {
299 if (mAnimation.isRunning()) {
300 seekValue = (float) mAnimation.getAnimatedValue();
Tiger Huangef8f9a22020-04-08 00:57:38 +0800301 seek = true;
302 }
Evan Rosky653a7252020-05-28 17:12:26 -0700303 mAnimation.cancel();
304 }
305 final float defaultY = mImeSourceControl.getSurfacePosition().y;
306 final float x = mImeSourceControl.getSurfacePosition().x;
307 final float hiddenY = defaultY + mImeFrame.height();
308 final float shownY = defaultY;
309 final float startY = show ? hiddenY : shownY;
310 final float endY = show ? shownY : hiddenY;
311 if (mAnimationDirection == DIRECTION_NONE && mImeShowing && show) {
312 // IME is already showing, so set seek to end
313 seekValue = shownY;
314 seek = true;
315 }
316 mAnimationDirection = show ? DIRECTION_SHOW : DIRECTION_HIDE;
317 mImeShowing = show;
318 mAnimation = ValueAnimator.ofFloat(startY, endY);
319 mAnimation.setDuration(
320 show ? ANIMATION_DURATION_SHOW_MS : ANIMATION_DURATION_HIDE_MS);
321 if (seek) {
322 mAnimation.setCurrentFraction((seekValue - startY) / (endY - startY));
323 }
Evan Rosky8d782e02019-10-14 15:43:53 -0700324
Evan Rosky653a7252020-05-28 17:12:26 -0700325 mAnimation.addUpdateListener(animation -> {
326 SurfaceControl.Transaction t = mTransactionPool.acquire();
327 float value = (float) animation.getAnimatedValue();
328 t.setPosition(mImeSourceControl.getLeash(), x, value);
Evan Rosky437a8792020-06-19 18:17:34 -0700329 final float alpha = isFloating ? (value - hiddenY) / (shownY - hiddenY) : 1.f;
330 t.setAlpha(mImeSourceControl.getLeash(), alpha);
Evan Rosky653a7252020-05-28 17:12:26 -0700331 dispatchPositionChanged(mDisplayId, imeTop(value), t);
332 t.apply();
333 mTransactionPool.release(t);
334 });
335 mAnimation.setInterpolator(INTERPOLATOR);
336 mAnimation.addListener(new AnimatorListenerAdapter() {
337 private boolean mCancelled = false;
338 @Override
339 public void onAnimationStart(Animator animation) {
Evan Roskyaf9f27c2020-02-18 18:58:35 +0000340 SurfaceControl.Transaction t = mTransactionPool.acquire();
Evan Rosky653a7252020-05-28 17:12:26 -0700341 t.setPosition(mImeSourceControl.getLeash(), x, startY);
Evan Rosky437a8792020-06-19 18:17:34 -0700342 final float alpha = isFloating ? (startY - hiddenY) / (shownY - hiddenY) : 1.f;
343 t.setAlpha(mImeSourceControl.getLeash(), alpha);
Evan Rosky653a7252020-05-28 17:12:26 -0700344 if (DEBUG) {
345 Slog.d(TAG, "onAnimationStart d:" + mDisplayId + " top:"
346 + imeTop(hiddenY) + "->" + imeTop(shownY)
347 + " showing:" + (mAnimationDirection == DIRECTION_SHOW));
348 }
349 dispatchStartPositioning(mDisplayId, imeTop(hiddenY),
350 imeTop(shownY), mAnimationDirection == DIRECTION_SHOW,
351 t);
352 if (mAnimationDirection == DIRECTION_SHOW) {
353 t.show(mImeSourceControl.getLeash());
354 }
Evan Rosky8d782e02019-10-14 15:43:53 -0700355 t.apply();
Evan Roskyaf9f27c2020-02-18 18:58:35 +0000356 mTransactionPool.release(t);
Evan Rosky8d782e02019-10-14 15:43:53 -0700357 }
Evan Rosky653a7252020-05-28 17:12:26 -0700358 @Override
359 public void onAnimationCancel(Animator animation) {
360 mCancelled = true;
361 }
362 @Override
363 public void onAnimationEnd(Animator animation) {
364 if (DEBUG) Slog.d(TAG, "onAnimationEnd " + mCancelled);
365 SurfaceControl.Transaction t = mTransactionPool.acquire();
366 if (!mCancelled) {
367 t.setPosition(mImeSourceControl.getLeash(), x, endY);
Evan Rosky437a8792020-06-19 18:17:34 -0700368 t.setAlpha(mImeSourceControl.getLeash(), 1.f);
Evan Rosky653a7252020-05-28 17:12:26 -0700369 }
370 dispatchEndPositioning(mDisplayId, mCancelled, t);
371 if (mAnimationDirection == DIRECTION_HIDE && !mCancelled) {
372 t.hide(mImeSourceControl.getLeash());
Yunfan Chenc02a5ac2020-06-16 01:52:41 +0000373 final IInputMethodManager imms = getImms();
374 if (imms != null) {
375 try {
376 // Remove the IME surface to make the insets invisible for
377 // non-client controlled insets.
378 imms.removeImeSurface();
379 } catch (RemoteException e) {
380 Slog.e(TAG, "Failed to remove IME surface.", e);
381 }
382 }
Evan Rosky653a7252020-05-28 17:12:26 -0700383 }
384 t.apply();
385 mTransactionPool.release(t);
386
387 mAnimationDirection = DIRECTION_NONE;
388 mAnimation = null;
Evan Rosky8d782e02019-10-14 15:43:53 -0700389 }
390 });
Evan Rosky653a7252020-05-28 17:12:26 -0700391 if (!show) {
392 // When going away, queue up insets change first, otherwise any bounds changes
393 // can have a "flicker" of ime-provided insets.
394 setVisibleDirectly(false /* visible */);
395 }
396 mAnimation.start();
397 if (show) {
398 // When showing away, queue up insets change last, otherwise any bounds changes
399 // can have a "flicker" of ime-provided insets.
400 setVisibleDirectly(true /* visible */);
401 }
Evan Rosky8d782e02019-10-14 15:43:53 -0700402 }
403 }
404
405 /**
406 * Allows other things to synchronize with the ime position
407 */
408 public interface ImePositionProcessor {
409 /**
410 * Called when the IME position is starting to animate.
Evan Rosky95729202020-02-21 10:16:08 -0800411 *
412 * @param hiddenTop The y position of the top of the IME surface when it is hidden.
Yunfan Chenc02a5ac2020-06-16 01:52:41 +0000413 * @param shownTop The y position of the top of the IME surface when it is shown.
414 * @param showing {@code true} when we are animating from hidden to shown, {@code false}
415 * when animating from shown to hidden.
Evan Rosky8d782e02019-10-14 15:43:53 -0700416 */
Evan Rosky95729202020-02-21 10:16:08 -0800417 default void onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
Winson Chung95c9fca2020-01-22 09:48:40 -0800418 boolean showing, SurfaceControl.Transaction t) {}
Evan Rosky8d782e02019-10-14 15:43:53 -0700419
420 /**
421 * Called when the ime position changed. This is expected to be a synchronous call on the
422 * animation thread. Operations can be added to the transaction to be applied in sync.
Evan Rosky95729202020-02-21 10:16:08 -0800423 *
424 * @param imeTop The current y position of the top of the IME surface.
Evan Rosky8d782e02019-10-14 15:43:53 -0700425 */
Winson Chung95c9fca2020-01-22 09:48:40 -0800426 default void onImePositionChanged(int displayId, int imeTop,
427 SurfaceControl.Transaction t) {}
Evan Rosky8d782e02019-10-14 15:43:53 -0700428
429 /**
430 * Called when the IME position is done animating.
Evan Rosky95729202020-02-21 10:16:08 -0800431 *
432 * @param cancel {@code true} if this was cancelled. This implies another start is coming.
Evan Rosky8d782e02019-10-14 15:43:53 -0700433 */
Evan Rosky95729202020-02-21 10:16:08 -0800434 default void onImeEndPositioning(int displayId, boolean cancel,
Winson Chung95c9fca2020-01-22 09:48:40 -0800435 SurfaceControl.Transaction t) {}
Evan Rosky8d782e02019-10-14 15:43:53 -0700436 }
Yunfan Chenc02a5ac2020-06-16 01:52:41 +0000437
438 public IInputMethodManager getImms() {
439 return IInputMethodManager.Stub.asInterface(
440 ServiceManager.getService(Context.INPUT_METHOD_SERVICE));
441 }
Evan Rosky8d782e02019-10-14 15:43:53 -0700442}