• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 
17 package com.android.systemui.accessibility;
18 
19 import static android.view.WindowManager.LayoutParams;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.content.Context;
24 import android.graphics.PixelFormat;
25 import android.graphics.Point;
26 import android.graphics.Rect;
27 import android.util.Log;
28 import android.util.MathUtils;
29 import android.view.Gravity;
30 import android.view.LayoutInflater;
31 import android.view.View;
32 import android.view.WindowManager;
33 
34 import com.android.systemui.R;
35 
36 /**
37  * Contains a movable control UI to manipulate mirrored window's position, size and scale. The
38  * window type of the UI is {@link LayoutParams#TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY} to
39  * ensure it won't be magnified. It is not movable to the navigation bar.
40  */
41 public abstract class MirrorWindowControl {
42     private static final String TAG = "MirrorWindowControl";
43     private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG) | false;
44 
45     /**
46      * A delegate handling a mirrored window's offset.
47      */
48     public interface MirrorWindowDelegate {
49         /**
50          * Moves the window with specified offset.
51          *
52          * @param xOffset the amount in pixels to offset the window in the X coordinate, in current
53          *                display pixels.
54          * @param yOffset the amount in pixels to offset the window in the Y coordinate, in current
55          *                display pixels.
56          */
move(int xOffset, int yOffset)57         void move(int xOffset, int yOffset);
58     }
59 
60     protected final Context mContext;
61     private final Rect mDraggableBound = new Rect();
62     final Point mTmpPoint = new Point();
63 
64     @Nullable
65     protected MirrorWindowDelegate mMirrorWindowDelegate;
66     protected View mControlsView;
67     /**
68      * The left top position of the control UI. Initialized when the control UI is visible.
69      *
70      * @see #setDefaultPosition(LayoutParams)
71      */
72     private final Point mControlPosition = new Point();
73     private final WindowManager mWindowManager;
74 
MirrorWindowControl(Context context)75     MirrorWindowControl(Context context) {
76         mContext = context;
77         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
78     }
79 
setWindowDelegate(@ullable MirrorWindowDelegate windowDelegate)80     public void setWindowDelegate(@Nullable MirrorWindowDelegate windowDelegate) {
81         mMirrorWindowDelegate = windowDelegate;
82     }
83 
84     /**
85      * Shows the control UI.
86      *
87      * {@link LayoutParams#TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY}  window.
88      */
showControl()89     public final void showControl() {
90         if (mControlsView != null) {
91             Log.w(TAG, "control view is visible");
92             return;
93         }
94         final Point  viewSize = mTmpPoint;
95         mControlsView = onCreateView(LayoutInflater.from(mContext), viewSize);
96 
97         final LayoutParams lp = new LayoutParams();
98         final int defaultSize = mContext.getResources().getDimensionPixelSize(
99                 R.dimen.magnification_controls_size);
100         lp.width = viewSize.x <= 0 ? defaultSize : viewSize.x;
101         lp.height = viewSize.y <= 0 ? defaultSize : viewSize.y;
102         setDefaultParams(lp);
103         setDefaultPosition(lp);
104         mWindowManager.addView(mControlsView, lp);
105         updateDraggableBound(lp.width, lp.height);
106     }
107 
setDefaultParams(LayoutParams lp)108     private void setDefaultParams(LayoutParams lp) {
109         lp.gravity = Gravity.TOP | Gravity.LEFT;
110         lp.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
111                 | LayoutParams.FLAG_NOT_FOCUSABLE;
112         lp.type = LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
113         lp.format = PixelFormat.RGBA_8888;
114         lp.setTitle(getWindowTitle());
115     }
116 
setDefaultPosition(LayoutParams layoutParams)117     private void setDefaultPosition(LayoutParams layoutParams) {
118         final Point displaySize = mTmpPoint;
119         mContext.getDisplay().getSize(displaySize);
120         layoutParams.x = displaySize.x - layoutParams.width;
121         layoutParams.y = displaySize.y - layoutParams.height;
122         mControlPosition.set(layoutParams.x, layoutParams.y);
123     }
124 
125     /**
126      * Removes the UI from the scene.
127      */
destroyControl()128     public final void destroyControl() {
129         if (mControlsView != null) {
130             mWindowManager.removeView(mControlsView);
131             mControlsView = null;
132         }
133     }
134 
135     /**
136      * Moves the control view with specified offset.
137      *
138      * @param xOffset the amount in pixels to offset the UI in the X coordinate, in current
139      *                display pixels.
140      * @param yOffset the amount in pixels to offset the UI in the Y coordinate, in current
141      *                display pixels.
142      */
move(int xOffset, int yOffset)143     public void move(int xOffset, int yOffset) {
144         if (mControlsView == null) {
145             Log.w(TAG, "control view is not available yet or destroyed");
146             return;
147         }
148         final Point nextPosition = mTmpPoint;
149         nextPosition.set(mControlPosition.x, mControlPosition.y);
150         mTmpPoint.offset(xOffset, yOffset);
151         setPosition(mTmpPoint);
152     }
153 
setPosition(Point point)154     private void setPosition(Point point) {
155         constrainFrameToDraggableBound(point);
156         if (point.equals(mControlPosition)) {
157             return;
158         }
159         mControlPosition.set(point.x, point.y);
160         LayoutParams lp = (LayoutParams) mControlsView.getLayoutParams();
161         lp.x = mControlPosition.x;
162         lp.y = mControlPosition.y;
163         mWindowManager.updateViewLayout(mControlsView, lp);
164     }
165 
constrainFrameToDraggableBound(Point point)166     private void constrainFrameToDraggableBound(Point point) {
167         point.x = MathUtils.constrain(point.x, mDraggableBound.left, mDraggableBound.right);
168         point.y = MathUtils.constrain(point.y, mDraggableBound.top, mDraggableBound.bottom);
169     }
170 
updateDraggableBound(int viewWidth, int viewHeight)171     private void updateDraggableBound(int viewWidth, int viewHeight) {
172         final Point size = mTmpPoint;
173         mContext.getDisplay().getSize(size);
174         mDraggableBound.set(0, 0, size.x - viewWidth, size.y - viewHeight);
175         if (DBG) {
176             Log.d(TAG, "updateDraggableBound :" + mDraggableBound);
177         }
178     }
179 
getWindowTitle()180     abstract String getWindowTitle();
181 
182     /**
183      * Called when the UI is going to show.
184      *
185      * @param inflater The LayoutInflater object used to inflate the view.
186      * @param viewSize The {@link Point} to specify view's width with {@link Point#x)} and height
187      *                with {@link Point#y)} .The value should be greater than 0, otherwise will
188      *                 fall back to the default size.
189      * @return the View for the control's UI.
190      */
191     @NonNull
onCreateView(@onNull LayoutInflater inflater, @NonNull Point viewSize)192     abstract View onCreateView(@NonNull LayoutInflater inflater, @NonNull Point viewSize);
193 }
194