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

Commit 1f6a9ee7 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Implement UX for IME with freeform windows"

parents 97074c93 0d654cb2
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -1639,6 +1639,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
    @Override
    public void onRootViewScrollYChanged(int rootScrollY) {
        mRootScrollY = rootScrollY;
        if (mDecorCaptionView != null) {
            mDecorCaptionView.onRootViewScrollYChanged(rootScrollY);
        }
        updateColorViewTranslations();
    }

+16 −2
Original line number Diff line number Diff line
@@ -103,6 +103,7 @@ public class DecorCaptionView extends ViewGroup implements View.OnTouchListener,
    private final Rect mCloseRect = new Rect();
    private final Rect mMaximizeRect = new Rect();
    private View mClickTarget;
    private int mRootScrollY;

    public DecorCaptionView(Context context) {
        super(context);
@@ -154,10 +155,11 @@ public class DecorCaptionView extends ViewGroup implements View.OnTouchListener,
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            final int x = (int) ev.getX();
            final int y = (int) ev.getY();
            if (mMaximizeRect.contains(x, y)) {
            // Only offset y for containment tests because the actual views are already translated.
            if (mMaximizeRect.contains(x, y - mRootScrollY)) {
                mClickTarget = mMaximize;
            }
            if (mCloseRect.contains(x, y)) {
            if (mCloseRect.contains(x, y - mRootScrollY)) {
                mClickTarget = mClose;
            }
        }
@@ -417,4 +419,16 @@ public class DecorCaptionView extends ViewGroup implements View.OnTouchListener,
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        return false;
    }

    /**
     * Called when {@link android.view.ViewRootImpl} scrolls for adjustPan.
     */
    public void onRootViewScrollYChanged(int scrollY) {
        // Offset the caption opposite the root scroll. This keeps the caption at the
        // top of the window during adjustPan.
        if (mCaption != null) {
            mRootScrollY = scrollY;
            mCaption.setTranslationY(scrollY);
        }
    }
}
+4 −7
Original line number Diff line number Diff line
@@ -43,16 +43,15 @@ import android.util.Slog;
import android.view.BatchedInputEventReceiver;
import android.view.Choreographer;
import android.view.Display;
import android.view.InputApplicationHandle;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputWindowHandle;
import android.view.MotionEvent;
import android.view.WindowManager;

import com.android.internal.annotations.VisibleForTesting;
import android.view.InputApplicationHandle;
import android.view.InputWindowHandle;
import com.android.server.wm.WindowManagerService.H;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -356,12 +355,10 @@ class TaskPositioner {
                    + startY + "}");
        }
        mTask = win.getTask();
        // Use the dim bounds, not the original task bounds. The cursor
        // movement should be calculated relative to the visible bounds.
        // Also, use the dim bounds of the task which accounts for
        // Use the bounds of the task which accounts for
        // multiple app windows. Don't use any bounds from win itself as it
        // may not be the same size as the task.
        mTask.getDimBounds(mTmpRect);
        mTask.getBounds(mTmpRect);
        startDrag(resize, preserveOrientation, startX, startY, mTmpRect);
    }

+27 −8
Original line number Diff line number Diff line
@@ -842,6 +842,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
        // The offset from the layout containing frame to the actual containing frame.
        final int layoutXDiff;
        final int layoutYDiff;
        final WindowState imeWin = mWmService.mRoot.getCurrentInputMethodWindow();
        final boolean isImeTarget =
                imeWin != null && imeWin.isVisibleNow() && isInputMethodTarget();
        if (inFullscreenContainer || layoutInParentFrame()) {
            // We use the parent frame as the containing frame for fullscreen and child windows
            mWindowFrames.mContainingFrame.set(mWindowFrames.mParentFrame);
@@ -861,15 +864,19 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
                mWindowFrames.mContainingFrame.bottom =
                        mWindowFrames.mContainingFrame.top + frozen.height();
            }
            final WindowState imeWin = mWmService.mRoot.getCurrentInputMethodWindow();
            // IME is up and obscuring this window. Adjust the window position so it is visible.
            if (imeWin != null && imeWin.isVisibleNow() && isInputMethodTarget()) {
                if (inFreeformWindowingMode() && mWindowFrames.mContainingFrame.bottom
                        > mWindowFrames.mContentFrame.bottom) {
                    // In freeform we want to move the top up directly.
                    // TODO: Investigate why this is contentFrame not parentFrame.
                    mWindowFrames.mContainingFrame.top -= mWindowFrames.mContainingFrame.bottom
                            - mWindowFrames.mContentFrame.bottom;
            if (isImeTarget) {
                if (inFreeformWindowingMode()) {
                    // Push the freeform window up to make room for the IME. However, don't push
                    // it up past the top of the screen.
                    final int bottomOverlap = mWindowFrames.mContainingFrame.bottom
                            - mWindowFrames.mVisibleFrame.bottom;
                    if (bottomOverlap > 0) {
                        final int distanceToTop = Math.max(mWindowFrames.mContainingFrame.top
                                - mWindowFrames.mDisplayFrame.top, 0);
                        int offs = Math.min(bottomOverlap, distanceToTop);
                        mWindowFrames.mContainingFrame.top -= offs;
                    }
                } else if (!inPinnedWindowingMode() && mWindowFrames.mContainingFrame.bottom
                        > mWindowFrames.mParentFrame.bottom) {
                    // But in docked we want to behave like fullscreen and behave as if the task
@@ -955,9 +962,21 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
            final int left = Math.max(limitFrame.left + minVisibleWidth - width,
                    Math.min( mWindowFrames.mFrame.left, limitFrame.right - minVisibleWidth));
            mWindowFrames.mFrame.set(left, top, left + width, top + height);
            final int visBottom = mWindowFrames.mVisibleFrame.bottom;
            final int contentBottom = mWindowFrames.mContentFrame.bottom;
            mWindowFrames.mContentFrame.set( mWindowFrames.mFrame);
            mWindowFrames.mVisibleFrame.set(mWindowFrames.mContentFrame);
            mWindowFrames.mStableFrame.set(mWindowFrames.mContentFrame);
            if (isImeTarget && inFreeformWindowingMode()) {
                // After displacing a freeform window to make room for the ime, any part of
                // the window still covered by IME should be inset.
                if (contentBottom + layoutYDiff < mWindowFrames.mContentFrame.bottom) {
                    mWindowFrames.mContentFrame.bottom = contentBottom + layoutYDiff;
                }
                if (visBottom + layoutYDiff < mWindowFrames.mVisibleFrame.bottom) {
                    mWindowFrames.mVisibleFrame.bottom = visBottom + layoutYDiff;
                }
            }
        } else if (mAttrs.type == TYPE_DOCK_DIVIDER) {
            dc.getDockedDividerController().positionDockedStackedDivider(mWindowFrames.mFrame);
            mWindowFrames.mContentFrame.set(mWindowFrames.mFrame);
+69 −2
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.wm;

import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.DisplayCutout.fromBoundingRect;
@@ -41,6 +42,7 @@ import com.android.server.wm.utils.WmDisplayCutout;

import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;

/**
 * Tests for the {@link WindowState#computeFrameLw} method and other window frame machinery.
@@ -78,16 +80,21 @@ public class WindowFrameTests extends WindowTestsBase {
    }

    private static class TaskWithBounds extends Task {
        final Rect mBounds;
        Rect mBounds;
        final Rect mOverrideDisplayedBounds = new Rect();
        boolean mFullscreenForTest = true;

        TaskWithBounds(TaskStack stack, WindowManagerService wm, Rect bounds) {
            super(0, stack, 0, wm, 0, false, new TaskDescription(), null);
            mBounds = bounds;
            setBounds(bounds);
        }

        @Override
        public int setBounds(Rect bounds) {
            mBounds = bounds;
            return super.setBounds(bounds);
        }

        @Override
        public Rect getBounds() {
            return mBounds;
@@ -513,6 +520,66 @@ public class WindowFrameTests extends WindowTestsBase {
        assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetRight(), 0);
    }

    @Test
    public void testFreeformContentInsets() {
        // fullscreen task doesn't use bounds for computeFrame
        final Task task = new TaskWithBounds(mStubStack, mWm, null);
        WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT);
        w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
        task.setWindowingMode(WINDOWING_MODE_FREEFORM);
        ((TaskWithBounds) task).mFullscreenForTest = false;
        w.setWindowingMode(WINDOWING_MODE_FREEFORM);

        DisplayContent dc = mWm.getDefaultDisplayContentLocked();
        dc.mInputMethodTarget = w;
        WindowState mockIme = mock(WindowState.class);
        Mockito.doReturn(true).when(mockIme).isVisibleNow();
        dc.mInputMethodWindow = mockIme;

        // With no insets or system decor all the frames incoming from PhoneWindowManager
        // are identical.
        final Rect pf = new Rect(0, 0, 1000, 800);
        final Rect df = pf;
        final Rect of = df;
        final Rect cf = new Rect(pf);
        cf.bottom -= 400;
        final Rect vf = new Rect(cf);
        final Rect sf = new Rect(pf);
        final Rect dcf = pf;

        // First check that it only gets moved up enough to show window.
        final Rect winRect = new Rect(200, 200, 300, 500);

        task.setBounds(winRect);
        w.setBounds(winRect);
        w.getWindowFrames().setFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
        w.computeFrameLw();

        final Rect expected = new Rect(winRect.left, cf.bottom - winRect.height(),
                winRect.right, cf.bottom);
        assertEquals(expected, w.getFrameLw());
        assertEquals(expected, w.getContentFrameLw());
        assertEquals(expected, w.getVisibleFrameLw());

        // Now check that it won't get moved beyond the top and then has appropriate insets
        winRect.bottom = 600;
        task.setBounds(winRect);
        w.setBounds(winRect);
        w.getWindowFrames().setFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
        w.computeFrameLw();

        assertFrame(w, winRect.left, 0, winRect.right, winRect.height());
        expected.top = 0;
        expected.bottom = cf.bottom;
        assertContentFrame(w, expected);
        assertVisibleFrame(w, expected);

        // Check that it's moved back without ime insets
        w.getWindowFrames().setFrames(pf, df, of, pf, pf, dcf, sf, mEmptyRect);
        w.computeFrameLw();
        assertEquals(winRect, w.getFrameLw());
    }

    private WindowStateWithTask createWindow(Task task, int width, int height) {
        final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION);
        attrs.width = width;