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

Commit d1144eb8 authored by Jason Long's avatar Jason Long
Browse files

Add basic AnchoredWindow.

Bug: 34637800
Test: Manual
Change-Id: I82c9ec44296c3423e3f8d3b55832e344a5094cfd
parent b922d1f9
Loading
Loading
Loading
Loading
+133 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.server.autofill;

import static com.android.server.autofill.Helper.DEBUG;

import android.content.Context;
import android.graphics.Rect;
import android.util.Slog;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.widget.FrameLayout;

/**
 * A window above the application that is smartly anchored to a rectangular region.
 */
final class AnchoredWindow {
    private static final String TAG = "AutoFill";

    private final WindowManager mWm;
    private final View mRootView;
    private final View mView;
    private final int mWidth;
    private final int mHeight;
    private boolean mIsShowing = false;

    /**
     * Constructor.
     *
     * @param wm window manager that draws the view on a window
     * @param view singleton view in the window
     * @param width requested width of the view
     * @param height requested height of the view
     */
    AnchoredWindow(WindowManager wm, View view, int width, int height) {
        mWm = wm;
        mRootView = wrapView(view, width, height);
        mView = view;
        mWidth = width;
        mHeight = height;
    }

    /**
     * Shows the window.
     *
     * @param bounds the rectangular region this window should be anchored to
     */
    void show(Rect bounds) {
        LayoutParams params = createBaseLayoutParams();
        params.x = bounds.left;
        params.y = bounds.bottom;

        if (!mIsShowing) {
            if (DEBUG) Slog.d(TAG, "adding view " + mView);
            mWm.addView(mRootView, params);
        } else {
            if (DEBUG) Slog.d(TAG, "updating view " + mView);
            mWm.updateViewLayout(mRootView, params);
        }
        mIsShowing = true;
    }

    /**
     * Hides the window.
     */
    void hide() {
        if (DEBUG) Slog.d(TAG, "removing view " + mView);
        if (mIsShowing) {
            mWm.removeView(mRootView);
        }
        mIsShowing = false;
    }

    /**
     * Wraps a view with a SelfRemovingView and sets its requested width and height.
     */
    private View wrapView(View view, int width, int height) {
        ViewGroup viewGroup = new SelfRemovingView(view.getContext());
        viewGroup.addView(view, new ViewGroup.LayoutParams(width, height));
        return viewGroup;
    }

    private static LayoutParams createBaseLayoutParams() {
        LayoutParams params = new LayoutParams();
        // TODO(b/33197203): LayoutParams.TYPE_AUTOFILL
        params.type = LayoutParams.TYPE_SYSTEM_ALERT;
        params.flags =
                LayoutParams.SOFT_INPUT_STATE_UNCHANGED
                | LayoutParams.FLAG_LAYOUT_IN_SCREEN
                | LayoutParams.FLAG_LAYOUT_NO_LIMITS
                | LayoutParams.FLAG_NOT_FOCUSABLE
                | LayoutParams.FLAG_NOT_TOUCH_MODAL
                | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
        params.gravity = Gravity.TOP | Gravity.LEFT;
        params.width = LayoutParams.WRAP_CONTENT;
        params.height = LayoutParams.WRAP_CONTENT;
        return params;
    }

    /** FrameLayout that listens for touch events removes itself if the touch event is outside. */
    private final class SelfRemovingView extends FrameLayout {
        public SelfRemovingView(Context context) {
            super(context);
        }

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
                hide();
                return true;
            } else {
                return super.onTouchEvent(event);
            }
        }
    }
}