Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 769c9a5e authored by Evan Rosky's avatar Evan Rosky Committed by Android (Google) Code Review
Browse files

Merge "IME to systemui"

parents 22f2eb72 8d782e0c
Loading
Loading
Loading
Loading
+48 −0
Original line number Diff line number Diff line
/**
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.view;

import android.view.InsetsSourceControl;
import android.view.InsetsState;

/**
 * Singular controller of insets to use when there isn't another obvious controller available.
 * Specifically, this will take over insets control in multi-window.
 * @hide
 */
oneway interface IDisplayWindowInsetsController {

    /**
     * @see IWindow#insetsChanged
     */
    void insetsChanged(in InsetsState insetsState);

    /**
     * @see IWindow#insetsControlChanged
     */
    void insetsControlChanged(in InsetsState insetsState, in InsetsSourceControl[] activeControls);

    /**
     * @see IWindow#showInsets
     */
    void showInsets(int types, boolean fromIme);

    /**
     * @see IWindow#hideInsets
     */
    void hideInsets(int types, boolean fromIme);
}
+14 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.os.ParcelFileDescriptor;
import android.view.IApplicationToken;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IDockedStackListener;
import android.view.IDisplayWindowInsetsController;
import android.view.IDisplayWindowListener;
import android.view.IDisplayFoldListener;
import android.view.IDisplayWindowRotationController;
@@ -49,6 +50,7 @@ import android.view.IWindowSession;
import android.view.IWindowSessionCallback;
import android.view.KeyEvent;
import android.view.InputEvent;
import android.view.InsetsState;
import android.view.MagnificationSpec;
import android.view.MotionEvent;
import android.view.InputChannel;
@@ -711,4 +713,16 @@ interface IWindowManager
     * @return true if the display was successfully mirrored.
     */
    boolean mirrorDisplay(int displayId, out SurfaceControl outSurfaceControl);

    /**
     * When in multi-window mode, the provided displayWindowInsetsController will control insets
     * animations.
     */
    void setDisplayWindowInsetsController(
            int displayId, in IDisplayWindowInsetsController displayWindowInsetsController);

    /**
     * Called when a remote process modifies insets on a display window container.
     */
    void modifyDisplayWindowInsets(int displayId, in InsetsState state);
}
+3 −0
Original line number Diff line number Diff line
@@ -121,6 +121,7 @@ import com.android.systemui.util.leak.GarbageMonitor;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.util.leak.LeakReporter;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.wm.DisplayImeController;
import com.android.systemui.wm.DisplayWindowController;
import com.android.systemui.wm.SystemWindows;

@@ -321,6 +322,7 @@ public class Dependency {
    @Inject Lazy<StatusBar> mStatusBar;
    @Inject Lazy<DisplayWindowController> mDisplayWindowController;
    @Inject Lazy<SystemWindows> mSystemWindows;
    @Inject Lazy<DisplayImeController> mDisplayImeController;

    @Inject
    public Dependency() {
@@ -509,6 +511,7 @@ public class Dependency {
        mProviders.put(StatusBar.class, mStatusBar::get);
        mProviders.put(DisplayWindowController.class, mDisplayWindowController::get);
        mProviders.put(SystemWindows.class, mSystemWindows::get);
        mProviders.put(DisplayImeController.class, mDisplayImeController::get);

        // TODO(b/118592525): to support multi-display , we start to add something which is
        //                    per-display, while others may be global. I think it's time to add
+333 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.wm;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.res.Configuration;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Slog;
import android.util.SparseArray;
import android.view.IDisplayWindowInsetsController;
import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.WindowInsets;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;

import com.android.systemui.dagger.qualifiers.Main;

import java.util.ArrayList;

import javax.inject.Inject;
import javax.inject.Singleton;

/**
 * Manages IME control at the display-level. This occurs when IME comes up in multi-window mode.
 */
@Singleton
public class DisplayImeController implements DisplayWindowController.DisplayWindowListener {
    private static final String TAG = "DisplayImeController";

    static final int ANIMATION_DURATION_SHOW_MS = 275;
    static final int ANIMATION_DURATION_HIDE_MS = 340;
    static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
    private static final int DIRECTION_NONE = 0;
    private static final int DIRECTION_SHOW = 1;
    private static final int DIRECTION_HIDE = 2;

    SystemWindows mSystemWindows;
    final Handler mHandler;

    final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();

    final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>();

    @Inject
    DisplayImeController(SystemWindows syswin, DisplayWindowController displayController,
            @Main Handler mainHandler) {
        mHandler = mainHandler;
        mSystemWindows = syswin;
        displayController.addDisplayWindowListener(this);
    }

    @Override
    public void onDisplayAdded(int displayId) {
        // Add's a system-ui window-manager specifically for ime. This type is special because
        // WM will defer IME inset handling to it in multi-window scenarious.
        PerDisplay pd = new PerDisplay(displayId,
                mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation());
        try {
            mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, pd);
        } catch (RemoteException e) {
            Slog.w(TAG, "Unable to set insets controller on display " + displayId);
        }
        mImePerDisplay.put(displayId, pd);
    }

    @Override
    public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
        PerDisplay pd = mImePerDisplay.get(displayId);
        if (pd == null) {
            return;
        }
        if (mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation()
                != pd.mRotation && isImeShowing(displayId)) {
            pd.startAnimation(true);
        }
    }

    @Override
    public void onDisplayRemoved(int displayId) {
        try {
            mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, null);
        } catch (RemoteException e) {
            Slog.w(TAG, "Unable to remove insets controller on display " + displayId);
        }
        mImePerDisplay.remove(displayId);
    }

    private boolean isImeShowing(int displayId) {
        PerDisplay pd = mImePerDisplay.get(displayId);
        if (pd == null) {
            return false;
        }
        final InsetsSource imeSource = pd.mInsetsState.getSource(InsetsState.ITYPE_IME);
        return imeSource != null && pd.mImeSourceControl != null && imeSource.isVisible();
    }

    private void dispatchPositionChanged(int displayId, int imeTop,
            SurfaceControl.Transaction t) {
        synchronized (mPositionProcessors) {
            for (ImePositionProcessor pp : mPositionProcessors) {
                pp.onImePositionChanged(displayId, imeTop, t);
            }
        }
    }

    private void dispatchStartPositioning(int displayId, int imeTop, int finalImeTop,
            boolean show, SurfaceControl.Transaction t) {
        synchronized (mPositionProcessors) {
            for (ImePositionProcessor pp : mPositionProcessors) {
                pp.onImeStartPositioning(displayId, imeTop, finalImeTop, show, t);
            }
        }
    }

    private void dispatchEndPositioning(int displayId, int imeTop, boolean show,
            SurfaceControl.Transaction t) {
        synchronized (mPositionProcessors) {
            for (ImePositionProcessor pp : mPositionProcessors) {
                pp.onImeEndPositioning(displayId, imeTop, show, t);
            }
        }
    }

    /**
     * Adds an {@link ImePositionProcessor} to be called during ime position updates.
     */
    public void addPositionProcessor(ImePositionProcessor processor) {
        synchronized (mPositionProcessors) {
            if (mPositionProcessors.contains(processor)) {
                return;
            }
            mPositionProcessors.add(processor);
        }
    }

    /**
     * Removes an {@link ImePositionProcessor} to be called during ime position updates.
     */
    public void removePositionProcessor(ImePositionProcessor processor) {
        synchronized (mPositionProcessors) {
            mPositionProcessors.remove(processor);
        }
    }

    class PerDisplay extends IDisplayWindowInsetsController.Stub {
        final int mDisplayId;
        final InsetsState mInsetsState = new InsetsState();
        InsetsSourceControl mImeSourceControl = null;
        int mAnimationDirection = DIRECTION_NONE;
        ValueAnimator mAnimation = null;
        int mRotation = Surface.ROTATION_0;

        PerDisplay(int displayId, int initialRotation) {
            mDisplayId = displayId;
            mRotation = initialRotation;
        }

        @Override
        public void insetsChanged(InsetsState insetsState) {
            if (mInsetsState.equals(insetsState)) {
                return;
            }
            mInsetsState.set(insetsState, true /* copySources */);
        }

        @Override
        public void insetsControlChanged(InsetsState insetsState,
                InsetsSourceControl[] activeControls) {
            insetsChanged(insetsState);
            if (activeControls != null) {
                for (InsetsSourceControl activeControl : activeControls) {
                    if (activeControl == null) {
                        continue;
                    }
                    if (activeControl.getType() == InsetsState.ITYPE_IME) {
                        mImeSourceControl = activeControl;
                    }
                }
            }
        }

        @Override
        public void showInsets(int types, boolean fromIme) {
            if ((types & WindowInsets.Type.ime()) == 0) {
                return;
            }
            startAnimation(true /* show */);
        }

        @Override
        public void hideInsets(int types, boolean fromIme) {
            if ((types & WindowInsets.Type.ime()) == 0) {
                return;
            }
            startAnimation(false /* show */);
        }

        /**
         * Sends the local visibility state back to window manager. Needed for legacy adjustForIme.
         */
        private void setVisibleDirectly(boolean visible) {
            mInsetsState.getSource(InsetsState.ITYPE_IME).setVisible(visible);
            try {
                mSystemWindows.mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
            } catch (RemoteException e) {
            }
        }

        private int imeTop(InsetsSource imeSource, float surfaceOffset) {
            return imeSource.getFrame().top + (int) surfaceOffset;
        }

        private void startAnimation(final boolean show) {
            final InsetsSource imeSource = mInsetsState.getSource(InsetsState.ITYPE_IME);
            if (imeSource == null || mImeSourceControl == null) {
                return;
            }
            if ((mAnimationDirection == DIRECTION_SHOW && show)
                    || (mAnimationDirection == DIRECTION_HIDE && !show)) {
                return;
            }
            if (mAnimationDirection != DIRECTION_NONE) {
                mAnimation.end();
                mAnimationDirection = DIRECTION_NONE;
            }
            mAnimationDirection = show ? DIRECTION_SHOW : DIRECTION_HIDE;
            mHandler.post(() -> {
                final float defaultY = mImeSourceControl.getSurfacePosition().y;
                final float x = mImeSourceControl.getSurfacePosition().x;
                final float startY = show ? defaultY + imeSource.getFrame().height() : defaultY;
                final float endY = show ? defaultY : defaultY + imeSource.getFrame().height();
                mAnimation = ValueAnimator.ofFloat(startY, endY);
                mAnimation.setDuration(
                        show ? ANIMATION_DURATION_SHOW_MS : ANIMATION_DURATION_HIDE_MS);

                mAnimation.addUpdateListener(animation -> {
                    SurfaceControl.Transaction t = new SurfaceControl.Transaction();
                    float value = (float) animation.getAnimatedValue();
                    t.setPosition(mImeSourceControl.getLeash(), x, value);
                    dispatchPositionChanged(mDisplayId, imeTop(imeSource, value), t);
                    t.apply();
                    t.close();
                });
                mAnimation.setInterpolator(INTERPOLATOR);
                mAnimation.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationStart(Animator animation) {
                        SurfaceControl.Transaction t = new SurfaceControl.Transaction();
                        t.setPosition(mImeSourceControl.getLeash(), x, startY);
                        dispatchStartPositioning(mDisplayId, imeTop(imeSource, startY),
                                imeTop(imeSource, endY), mAnimationDirection == DIRECTION_SHOW,
                                t);
                        if (mAnimationDirection == DIRECTION_SHOW) {
                            t.show(mImeSourceControl.getLeash());
                        }
                        t.apply();
                        t.close();
                    }
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        SurfaceControl.Transaction t = new SurfaceControl.Transaction();
                        t.setPosition(mImeSourceControl.getLeash(), x, endY);
                        dispatchEndPositioning(mDisplayId, imeTop(imeSource, endY),
                                mAnimationDirection == DIRECTION_SHOW, t);
                        if (mAnimationDirection == DIRECTION_HIDE) {
                            t.hide(mImeSourceControl.getLeash());
                        }
                        t.apply();
                        t.close();

                        mAnimationDirection = DIRECTION_NONE;
                        mAnimation = null;
                    }
                });
                if (!show) {
                    // When going away, queue up insets change first, otherwise any bounds changes
                    // can have a "flicker" of ime-provided insets.
                    setVisibleDirectly(false /* visible */);
                }
                mAnimation.start();
                if (show) {
                    // When showing away, queue up insets change last, otherwise any bounds changes
                    // can have a "flicker" of ime-provided insets.
                    setVisibleDirectly(true /* visible */);
                }
            });
        }
    }

    /**
     * Allows other things to synchronize with the ime position
     */
    public interface ImePositionProcessor {
        /**
         * Called when the IME position is starting to animate.
         */
        void onImeStartPositioning(int displayId, int imeTop, int finalImeTop, boolean showing,
                SurfaceControl.Transaction t);

        /**
         * Called when the ime position changed. This is expected to be a synchronous call on the
         * animation thread. Operations can be added to the transaction to be applied in sync.
         */
        void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t);

        /**
         * Called when the IME position is done animating.
         */
        void onImeEndPositioning(int displayId, int imeTop, boolean showing,
                SurfaceControl.Transaction t);
    }
}
+97 −6
Original line number Diff line number Diff line
@@ -205,6 +205,7 @@ import android.view.Display;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.IDisplayWindowInsetsController;
import android.view.ISystemGestureExclusionListener;
import android.view.IWindow;
import android.view.InputChannel;
@@ -218,6 +219,7 @@ import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.SurfaceSession;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants.PointerEventListener;

@@ -570,6 +572,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
     */
    WindowState mInputMethodTarget;

    InsetsControlTarget mInputMethodControlTarget;

    /** If true hold off on modifying the animation layer of mInputMethodTarget */
    boolean mInputMethodTargetWaitingAnim;

@@ -598,6 +602,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
    private final float mWindowCornerRadius;

    private final SparseArray<ShellRoot> mShellRoots = new SparseArray<>();
    RemoteInsetsControlTarget mRemoteInsetsControlTarget = null;
    private final IBinder.DeathRecipient mRemoteInsetsDeath =
            () -> {
                synchronized (mWmService.mGlobalLock) {
                    mRemoteInsetsControlTarget = null;
                }
            };

    private RootWindowContainer mRootWindowContainer;

@@ -1156,6 +1167,22 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        mShellRoots.remove(windowType);
    }

    void setRemoteInsetsController(IDisplayWindowInsetsController controller) {
        if (mRemoteInsetsControlTarget != null) {
            mRemoteInsetsControlTarget.mRemoteInsetsController.asBinder().unlinkToDeath(
                    mRemoteInsetsDeath, 0);
            mRemoteInsetsControlTarget = null;
        }
        if (controller != null) {
            try {
                controller.asBinder().linkToDeath(mRemoteInsetsDeath, 0);
                mRemoteInsetsControlTarget = new RemoteInsetsControlTarget(controller);
            } catch (RemoteException e) {
                return;
            }
        }
    }

    /** Changes the display the input window token is housed on to this one. */
    void reParentWindowToken(WindowToken token) {
        final DisplayContent prevDc = token.getDisplayContent();
@@ -3383,6 +3410,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        }
    }

    boolean isImeAttachedToApp() {
        return (mInputMethodTarget != null && mInputMethodTarget.mActivityRecord != null
                && mInputMethodTarget.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
                // An activity with override bounds should be letterboxed inside its parent bounds,
                // so it doesn't fill the screen.
                && mInputMethodTarget.mActivityRecord.matchParentBounds());
    }

    private void setInputMethodTarget(WindowState target, boolean targetWaitingAnim) {
        if (target == mInputMethodTarget && mInputMethodTargetWaitingAnim == targetWaitingAnim) {
            return;
@@ -3391,7 +3426,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        mInputMethodTarget = target;
        mInputMethodTargetWaitingAnim = targetWaitingAnim;
        assignWindowLayers(false /* setLayoutNeeded */);
        mInsetsStateController.onImeTargetChanged(target);
        mInputMethodControlTarget = computeImeControlTarget();
        mInsetsStateController.onImeTargetChanged(mInputMethodControlTarget);
        updateImeParent();
    }

@@ -3416,11 +3452,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        // Attach it to app if the target is part of an app and such app is covering the entire
        // screen. If it's not covering the entire screen the IME might extend beyond the apps
        // bounds.
        if (mInputMethodTarget != null && mInputMethodTarget.mActivityRecord != null
                && mInputMethodTarget.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
                // An activity with override bounds should be letterboxed inside its parent bounds,
                // so it doesn't fill the screen.
                && mInputMethodTarget.mActivityRecord.matchParentBounds()) {
        if (isImeAttachedToApp()) {
            return mInputMethodTarget.mActivityRecord.getSurfaceControl();
        }

@@ -3428,6 +3460,19 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        return mWindowContainers.getSurfaceControl();
    }

    /**
     * Computes which control-target the IME should be attached to.
     */
    @VisibleForTesting
    InsetsControlTarget computeImeControlTarget() {
        if (!isImeAttachedToApp() && mRemoteInsetsControlTarget != null) {
            return mRemoteInsetsControlTarget;
        }

        // Otherwise, we just use the ime target
        return mInputMethodTarget;
    }

    void setLayoutNeeded() {
        if (DEBUG_LAYOUT) Slog.w(TAG_WM, "setLayoutNeeded: callers=" + Debug.getCallers(3));
        mLayoutNeeded = true;
@@ -6688,4 +6733,50 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
    Context getDisplayUiContext() {
        return mDisplayPolicy.getSystemUiContext();
    }

    class RemoteInsetsControlTarget implements InsetsControlTarget {
        private final IDisplayWindowInsetsController mRemoteInsetsController;

        RemoteInsetsControlTarget(IDisplayWindowInsetsController controller) {
            mRemoteInsetsController = controller;
        }

        void notifyInsetsChanged() {
            try {
                mRemoteInsetsController.insetsChanged(
                        getInsetsStateController().getRawInsetsState());
            } catch (RemoteException e) {
                Slog.w(TAG, "Failed to deliver inset state change", e);
            }
        }

        @Override
        public void notifyInsetsControlChanged() {
            final InsetsStateController stateController = getInsetsStateController();
            try {
                mRemoteInsetsController.insetsControlChanged(stateController.getRawInsetsState(),
                        stateController.getControlsForDispatch(this));
            } catch (RemoteException e) {
                Slog.w(TAG, "Failed to deliver inset state change", e);
            }
        }

        @Override
        public void showInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) {
            try {
                mRemoteInsetsController.showInsets(types, fromIme);
            } catch (RemoteException e) {
                Slog.w(TAG, "Failed to deliver showInsets", e);
            }
        }

        @Override
        public void hideInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) {
            try {
                mRemoteInsetsController.hideInsets(types, fromIme);
            } catch (RemoteException e) {
                Slog.w(TAG, "Failed to deliver showInsets", e);
            }
        }
    }
}
Loading