Added WindowMagnification for Accessibility in System UI
Added support for window magnification as an accessibility feature. The UI can either be dragged to a new a location or arrow controls can move the window around. Test: adb shell settings put secure window_magnification 1 Bug: 136250281 Change-Id: If3098df098f2a1b88b2170e773dccc8f86946abf
This commit is contained in:
@@ -8415,6 +8415,12 @@ public final class Settings {
|
||||
*/
|
||||
public static final String PEOPLE_STRIP = "people_strip";
|
||||
|
||||
/**
|
||||
* Controls if window magnification is enabled.
|
||||
* @hide
|
||||
*/
|
||||
public static final String WINDOW_MAGNIFICATION = "window_magnification";
|
||||
|
||||
/**
|
||||
* Keys we no longer back up under the current schema, but want to continue to
|
||||
* process when restoring historical backup datasets.
|
||||
|
||||
@@ -734,7 +734,8 @@ public class SettingsBackupTest {
|
||||
Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
|
||||
Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
|
||||
Settings.Secure.FACE_UNLOCK_RE_ENROLL,
|
||||
Settings.Secure.TAP_GESTURE);
|
||||
Settings.Secure.TAP_GESTURE,
|
||||
Settings.Secure.WINDOW_MAGNIFICATION);
|
||||
|
||||
@Test
|
||||
public void systemSettingsBackedUpOrBlacklisted() {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
<vector android:height="96dp" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="96dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#757575" android:pathData="M15.54,5.54L13.77,7.3 12,5.54 10.23,7.3 8.46,5.54 12,2zM18.46,15.54l-1.76,-1.77L18.46,12l-1.76,-1.77 1.76,-1.77L22,12zM8.46,18.46l1.77,-1.76L12,18.46l1.77,-1.76 1.77,1.76L12,22zM5.54,8.46l1.76,1.77L5.54,12l1.76,1.77 -1.76,1.77L2,12z"/>
|
||||
<path android:fillColor="#757575" android:pathData="M12,12m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0"/>
|
||||
</vector>
|
||||
65
packages/SystemUI/res/layout/magnifier_controllers.xml
Normal file
65
packages/SystemUI/res/layout/magnifier_controllers.xml
Normal file
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="@dimen/magnification_controls_size"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_height="@dimen/magnification_controls_size"
|
||||
android:gravity="center">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:focusable="true"
|
||||
android:id="@+id/controller"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_control_magnification_grey" />
|
||||
|
||||
<View
|
||||
android:id="@+id/left_control"
|
||||
android:layout_width="@dimen/magnifier_left_right_controls_width"
|
||||
android:layout_height="@dimen/magnifier_left_right_controls_height"
|
||||
android:layout_alignLeft="@+id/controller"
|
||||
android:layout_centerVertical="true" />
|
||||
|
||||
<View
|
||||
android:id="@+id/up_control"
|
||||
android:layout_width="@dimen/magnifier_up_down_controls_width"
|
||||
android:layout_height="@dimen/magnifier_up_down_controls_height"
|
||||
android:layout_alignTop="@+id/controller"
|
||||
android:layout_centerHorizontal="true" />
|
||||
|
||||
<View
|
||||
android:id="@+id/right_control"
|
||||
android:layout_width="@dimen/magnifier_left_right_controls_width"
|
||||
android:layout_height="@dimen/magnifier_left_right_controls_height"
|
||||
android:layout_alignRight="@+id/controller"
|
||||
android:layout_centerVertical="true" />
|
||||
|
||||
<View
|
||||
android:id="@+id/down_control"
|
||||
android:layout_width="@dimen/magnifier_up_down_controls_width"
|
||||
android:layout_height="@dimen/magnifier_up_down_controls_height"
|
||||
android:layout_alignBottom="@+id/controller"
|
||||
android:layout_centerHorizontal="true" />
|
||||
</RelativeLayout>
|
||||
|
||||
</FrameLayout>
|
||||
71
packages/SystemUI/res/layout/window_magnifier_view.xml
Normal file
71
packages/SystemUI/res/layout/window_magnifier_view.xml
Normal file
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<SurfaceView
|
||||
android:layout_marginStart="@dimen/magnification_border_size"
|
||||
android:layout_marginTop="@dimen/magnification_border_size"
|
||||
android:id="@+id/surface_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:id="@+id/left_handle"
|
||||
android:layout_width="@dimen/magnification_border_size"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_above="@+id/drag_handle"
|
||||
android:background="@color/magnification_border_color" />
|
||||
|
||||
<View
|
||||
android:id="@+id/top_handle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/magnification_border_size"
|
||||
android:background="@color/magnification_border_color" />
|
||||
|
||||
<View
|
||||
android:id="@+id/right_handle"
|
||||
android:layout_width="@dimen/magnification_border_size"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_above="@+id/drag_handle"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:background="@color/magnification_border_color" />
|
||||
|
||||
<View
|
||||
android:id="@+id/bottom_handle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/magnification_border_size"
|
||||
android:layout_above="@+id/drag_handle"
|
||||
android:background="@color/magnification_border_color" />
|
||||
|
||||
<View
|
||||
android:id="@+id/drag_handle"
|
||||
android:layout_width="@dimen/magnification_drag_view_width"
|
||||
android:layout_height="@dimen/magnification_drag_view_height"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@color/magnification_border_color" />
|
||||
|
||||
</RelativeLayout>
|
||||
</FrameLayout>
|
||||
@@ -211,4 +211,5 @@
|
||||
<color name="GM2_green_500">#FF34A853</color>
|
||||
<color name="GM2_blue_500">#FF4285F4</color>
|
||||
|
||||
<color name="magnification_border_color">#FF9900</color>
|
||||
</resources>
|
||||
|
||||
@@ -295,6 +295,7 @@
|
||||
<item>com.android.systemui.SizeCompatModeActivityController</item>
|
||||
<item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
|
||||
<item>com.android.systemui.theme.ThemeOverlayController</item>
|
||||
<item>com.android.systemui.accessibility.WindowMagnification</item>
|
||||
</string-array>
|
||||
|
||||
<!-- SystemUI vender service, used in config_systemUIServiceComponents. -->
|
||||
|
||||
@@ -1139,4 +1139,16 @@
|
||||
<dimen name="qs_media_width">350dp</dimen>
|
||||
<dimen name="qs_media_padding">8dp</dimen>
|
||||
<dimen name="qs_media_corner_radius">10dp</dimen>
|
||||
|
||||
<dimen name="magnification_border_size">5dp</dimen>
|
||||
<dimen name="magnification_frame_move_short">5dp</dimen>
|
||||
<dimen name="magnification_frame_move_long">25dp</dimen>
|
||||
<dimen name="magnification_drag_view_width">100dp</dimen>
|
||||
<dimen name="magnification_drag_view_height">35dp</dimen>
|
||||
<dimen name="magnification_controls_size">90dp</dimen>
|
||||
<dimen name="magnifier_left_right_controls_width">35dp</dimen>
|
||||
<dimen name="magnifier_left_right_controls_height">45dp</dimen>
|
||||
<dimen name="magnifier_up_down_controls_width">45dp</dimen>
|
||||
<dimen name="magnifier_up_down_controls_height">40dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -32,4 +32,6 @@
|
||||
<!-- Ratio of "right" end of status bar that will swipe to QS. -->
|
||||
<integer name="qs_split_fraction">2</integer>
|
||||
|
||||
<integer name="magnification_default_scale">2</integer>
|
||||
|
||||
</resources>
|
||||
@@ -2492,4 +2492,12 @@
|
||||
|
||||
<!-- Title of the overlay warning the user to interact with the device or it will go to sleep. [CHAR LIMIT=25] -->
|
||||
<string name="inattentive_sleep_warning_title">Standby</string>
|
||||
|
||||
<!-- Window Magnification strings -->
|
||||
<!-- Title for Magnification Overlay Window [CHAR LIMIT=NONE] -->
|
||||
<string name="magnification_overlay_title">Magnification Overlay Window</string>
|
||||
<!-- Title for Magnification Window [CHAR LIMIT=NONE] -->
|
||||
<string name="magnification_window_title">Magnification Window</string>
|
||||
<!-- Title for Magnification Controls Window [CHAR LIMIT=NONE] -->
|
||||
<string name="magnification_controls_title">Magnification Window Controls</string>
|
||||
</resources>
|
||||
|
||||
@@ -27,6 +27,7 @@ import android.graphics.Rect;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManagerGlobal;
|
||||
|
||||
@@ -215,4 +216,23 @@ public class WindowManagerWrapper {
|
||||
public void removePinnedStackListener(PinnedStackListener listener) {
|
||||
mPinnedStackListenerForwarder.removeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mirrors a specified display. The SurfaceControl returned is the root of the mirrored
|
||||
* hierarchy.
|
||||
*
|
||||
* @param displayId The id of the display to mirror
|
||||
* @return The SurfaceControl for the root of the mirrored hierarchy.
|
||||
*/
|
||||
public SurfaceControl mirrorDisplay(final int displayId) {
|
||||
try {
|
||||
SurfaceControl outSurfaceControl = new SurfaceControl();
|
||||
WindowManagerGlobal.getWindowManagerService().mirrorDisplay(displayId,
|
||||
outSurfaceControl);
|
||||
return outSurfaceControl;
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Unable to reach window manager", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.accessibility;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.os.Handler;
|
||||
import android.provider.Settings;
|
||||
|
||||
import com.android.systemui.SystemUI;
|
||||
import com.android.systemui.dagger.qualifiers.MainHandler;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
* Class to handle changes to setting window_magnification value.
|
||||
*/
|
||||
@Singleton
|
||||
public class WindowMagnification extends SystemUI {
|
||||
private WindowMagnificationController mWindowMagnificationController;
|
||||
private final Handler mHandler;
|
||||
|
||||
@Inject
|
||||
public WindowMagnification(Context context, @MainHandler Handler mainHandler) {
|
||||
super(context);
|
||||
mHandler = mainHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
mContext.getContentResolver().registerContentObserver(
|
||||
Settings.Secure.getUriFor(Settings.Secure.WINDOW_MAGNIFICATION),
|
||||
true, new ContentObserver(mHandler) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
updateWindowMagnification();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateWindowMagnification() {
|
||||
try {
|
||||
boolean enable = Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Secure.WINDOW_MAGNIFICATION) != 0;
|
||||
if (enable) {
|
||||
enableMagnification();
|
||||
} else {
|
||||
disableMagnification();
|
||||
}
|
||||
} catch (Settings.SettingNotFoundException e) {
|
||||
disableMagnification();
|
||||
}
|
||||
}
|
||||
|
||||
private void enableMagnification() {
|
||||
if (mWindowMagnificationController == null) {
|
||||
mWindowMagnificationController = new WindowMagnificationController(mContext, mHandler);
|
||||
}
|
||||
mWindowMagnificationController.createWindowMagnification();
|
||||
}
|
||||
|
||||
private void disableMagnification() {
|
||||
if (mWindowMagnificationController != null) {
|
||||
mWindowMagnificationController.deleteWindowMagnification();
|
||||
}
|
||||
mWindowMagnificationController = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
* 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.accessibility;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.view.Display;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.shared.system.WindowManagerWrapper;
|
||||
|
||||
/**
|
||||
* Class to handle adding and removing a window magnification.
|
||||
*/
|
||||
public class WindowMagnificationController implements View.OnClickListener,
|
||||
View.OnLongClickListener, View.OnTouchListener, SurfaceHolder.Callback {
|
||||
private final int mBorderSize;
|
||||
private final int mMoveFrameAmountShort;
|
||||
private final int mMoveFrameAmountLong;
|
||||
|
||||
private final Context mContext;
|
||||
private final Point mDisplaySize = new Point();
|
||||
private final int mDisplayId;
|
||||
private final Handler mHandler;
|
||||
private final Rect mMagnificationFrame = new Rect();
|
||||
private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
|
||||
|
||||
private final WindowManager mWm;
|
||||
|
||||
private float mScale;
|
||||
|
||||
private final Rect mTmpRect = new Rect();
|
||||
|
||||
// The root of the mirrored content
|
||||
private SurfaceControl mMirrorSurface;
|
||||
|
||||
private boolean mIsPressedDown;
|
||||
|
||||
private View mLeftControl;
|
||||
private View mUpControl;
|
||||
private View mRightControl;
|
||||
private View mBottomControl;
|
||||
|
||||
private View mDragView;
|
||||
private View mLeftDrag;
|
||||
private View mTopDrag;
|
||||
private View mRightDrag;
|
||||
private View mBottomDrag;
|
||||
|
||||
private final PointF mLastDrag = new PointF();
|
||||
private final Point mMoveWindowOffset = new Point();
|
||||
|
||||
private View mMirrorView;
|
||||
private SurfaceView mMirrorSurfaceView;
|
||||
private View mControlsView;
|
||||
private View mOverlayView;
|
||||
|
||||
private MoveMirrorRunnable mMoveMirrorRunnable = new MoveMirrorRunnable();
|
||||
|
||||
WindowMagnificationController(Context context, Handler handler) {
|
||||
mContext = context;
|
||||
mHandler = handler;
|
||||
Display display = mContext.getDisplay();
|
||||
display.getSize(mDisplaySize);
|
||||
mDisplayId = mContext.getDisplayId();
|
||||
|
||||
mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
|
||||
Resources r = context.getResources();
|
||||
mBorderSize = (int) r.getDimension(R.dimen.magnification_border_size);
|
||||
mMoveFrameAmountShort = (int) r.getDimension(R.dimen.magnification_frame_move_short);
|
||||
mMoveFrameAmountLong = (int) r.getDimension(R.dimen.magnification_frame_move_long);
|
||||
|
||||
mScale = r.getInteger(R.integer.magnification_default_scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a magnification window if it doesn't already exist.
|
||||
*/
|
||||
void createWindowMagnification() {
|
||||
if (mMirrorView != null) {
|
||||
return;
|
||||
}
|
||||
createOverlayWindow();
|
||||
}
|
||||
|
||||
private void createOverlayWindow() {
|
||||
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
|
||||
WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT,
|
||||
WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
|
||||
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
|
||||
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
PixelFormat.TRANSPARENT);
|
||||
params.gravity = Gravity.TOP | Gravity.LEFT;
|
||||
params.token = new Binder();
|
||||
params.setTitle(mContext.getString(R.string.magnification_overlay_title));
|
||||
|
||||
mOverlayView = new View(mContext);
|
||||
mOverlayView.getViewTreeObserver().addOnWindowAttachListener(
|
||||
new ViewTreeObserver.OnWindowAttachListener() {
|
||||
@Override
|
||||
public void onWindowAttached() {
|
||||
mOverlayView.getViewTreeObserver().removeOnWindowAttachListener(this);
|
||||
createMirrorWindow();
|
||||
createControls();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowDetached() {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
mOverlayView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
|
||||
|
||||
mWm.addView(mOverlayView, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the magnification window.
|
||||
*/
|
||||
void deleteWindowMagnification() {
|
||||
if (mMirrorSurface != null) {
|
||||
mTransaction.remove(mMirrorSurface).apply();
|
||||
mMirrorSurface = null;
|
||||
}
|
||||
|
||||
if (mOverlayView != null) {
|
||||
mWm.removeView(mOverlayView);
|
||||
mOverlayView = null;
|
||||
}
|
||||
|
||||
if (mMirrorView != null) {
|
||||
mWm.removeView(mMirrorView);
|
||||
mMirrorView = null;
|
||||
}
|
||||
|
||||
if (mControlsView != null) {
|
||||
mWm.removeView(mControlsView);
|
||||
mControlsView = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void createMirrorWindow() {
|
||||
setInitialStartBounds();
|
||||
|
||||
// The window should be the size the mirrored surface will be but also add room for the
|
||||
// border and the drag handle.
|
||||
int dragViewHeight = (int) mContext.getResources().getDimension(
|
||||
R.dimen.magnification_drag_view_height);
|
||||
int windowWidth = mMagnificationFrame.width() + 2 * mBorderSize;
|
||||
int windowHeight = mMagnificationFrame.height() + dragViewHeight + 2 * mBorderSize;
|
||||
|
||||
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
|
||||
windowWidth, windowHeight,
|
||||
WindowManager.LayoutParams.TYPE_APPLICATION_PANEL,
|
||||
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
||||
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
PixelFormat.TRANSPARENT);
|
||||
params.gravity = Gravity.TOP | Gravity.LEFT;
|
||||
params.token = mOverlayView.getWindowToken();
|
||||
params.x = mMagnificationFrame.left;
|
||||
params.y = mMagnificationFrame.top;
|
||||
params.setTitle(mContext.getString(R.string.magnification_window_title));
|
||||
|
||||
mMirrorView = LayoutInflater.from(mContext).inflate(R.layout.window_magnifier_view, null);
|
||||
mMirrorSurfaceView = mMirrorView.findViewById(R.id.surface_view);
|
||||
// This places the SurfaceView's SurfaceControl above the ViewRootImpl's SurfaceControl to
|
||||
// ensure the mirrored area can get touch instead of going to the window
|
||||
mMirrorSurfaceView.setZOrderOnTop(true);
|
||||
|
||||
mMirrorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
|
||||
mWm.addView(mMirrorView, params);
|
||||
|
||||
SurfaceHolder holder = mMirrorSurfaceView.getHolder();
|
||||
holder.addCallback(this);
|
||||
holder.setFormat(PixelFormat.RGBA_8888);
|
||||
|
||||
addDragTouchListeners();
|
||||
}
|
||||
|
||||
private void createControls() {
|
||||
int controlsSize = (int) mContext.getResources().getDimension(
|
||||
R.dimen.magnification_controls_size);
|
||||
|
||||
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(controlsSize, controlsSize,
|
||||
WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL,
|
||||
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
||||
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
PixelFormat.RGBA_8888);
|
||||
lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
|
||||
lp.token = mOverlayView.getWindowToken();
|
||||
lp.setTitle(mContext.getString(R.string.magnification_controls_title));
|
||||
|
||||
mControlsView = LayoutInflater.from(mContext).inflate(R.layout.magnifier_controllers, null);
|
||||
mWm.addView(mControlsView, lp);
|
||||
|
||||
mLeftControl = mControlsView.findViewById(R.id.left_control);
|
||||
mUpControl = mControlsView.findViewById(R.id.up_control);
|
||||
mRightControl = mControlsView.findViewById(R.id.right_control);
|
||||
mBottomControl = mControlsView.findViewById(R.id.down_control);
|
||||
|
||||
mLeftControl.setOnClickListener(this);
|
||||
mUpControl.setOnClickListener(this);
|
||||
mRightControl.setOnClickListener(this);
|
||||
mBottomControl.setOnClickListener(this);
|
||||
|
||||
mLeftControl.setOnLongClickListener(this);
|
||||
mUpControl.setOnLongClickListener(this);
|
||||
mRightControl.setOnLongClickListener(this);
|
||||
mBottomControl.setOnLongClickListener(this);
|
||||
|
||||
mLeftControl.setOnTouchListener(this);
|
||||
mUpControl.setOnTouchListener(this);
|
||||
mRightControl.setOnTouchListener(this);
|
||||
mBottomControl.setOnTouchListener(this);
|
||||
}
|
||||
|
||||
private void setInitialStartBounds() {
|
||||
// Sets the initial frame area for the mirror and places it in the center of the display.
|
||||
int initSize = Math.min(mDisplaySize.x, mDisplaySize.y) / 2;
|
||||
int initX = mDisplaySize.x / 2 - initSize / 2;
|
||||
int initY = mDisplaySize.y / 2 - initSize / 2;
|
||||
mMagnificationFrame.set(initX, initY, initX + initSize, initY + initSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called once the surfaceView is created so the mirrored content can be placed as a
|
||||
* child of the surfaceView.
|
||||
*/
|
||||
private void createMirror() {
|
||||
mMirrorSurface = WindowManagerWrapper.getInstance().mirrorDisplay(mDisplayId);
|
||||
if (!mMirrorSurface.isValid()) {
|
||||
return;
|
||||
}
|
||||
mTransaction.show(mMirrorSurface)
|
||||
.reparent(mMirrorSurface, mMirrorSurfaceView.getSurfaceControl());
|
||||
|
||||
modifyWindowMagnification(mTransaction);
|
||||
mTransaction.apply();
|
||||
}
|
||||
|
||||
private void addDragTouchListeners() {
|
||||
mDragView = mMirrorView.findViewById(R.id.drag_handle);
|
||||
mLeftDrag = mMirrorView.findViewById(R.id.left_handle);
|
||||
mTopDrag = mMirrorView.findViewById(R.id.top_handle);
|
||||
mRightDrag = mMirrorView.findViewById(R.id.right_handle);
|
||||
mBottomDrag = mMirrorView.findViewById(R.id.bottom_handle);
|
||||
|
||||
mDragView.setOnTouchListener(this);
|
||||
mLeftDrag.setOnTouchListener(this);
|
||||
mTopDrag.setOnTouchListener(this);
|
||||
mRightDrag.setOnTouchListener(this);
|
||||
mBottomDrag.setOnTouchListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the placement of the mirrored content.
|
||||
*/
|
||||
private void modifyWindowMagnification(SurfaceControl.Transaction t) {
|
||||
Rect sourceBounds = getSourceBounds(mMagnificationFrame, mScale);
|
||||
// The final destination for the magnification surface should be at 0,0 since the
|
||||
// ViewRootImpl's position will change
|
||||
mTmpRect.set(0, 0, mMagnificationFrame.width(), mMagnificationFrame.height());
|
||||
|
||||
WindowManager.LayoutParams params =
|
||||
(WindowManager.LayoutParams) mMirrorView.getLayoutParams();
|
||||
params.x = mMagnificationFrame.left;
|
||||
params.y = mMagnificationFrame.top;
|
||||
mWm.updateViewLayout(mMirrorView, params);
|
||||
|
||||
t.setGeometry(mMirrorSurface, sourceBounds, mTmpRect, Surface.ROTATION_0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
setMoveOffset(v, mMoveFrameAmountShort);
|
||||
moveMirrorFromControls();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
mIsPressedDown = true;
|
||||
setMoveOffset(v, mMoveFrameAmountLong);
|
||||
mHandler.post(mMoveMirrorRunnable);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (v == mLeftControl || v == mUpControl || v == mRightControl || v == mBottomControl) {
|
||||
return handleControlTouchEvent(event);
|
||||
} else if (v == mDragView || v == mLeftDrag || v == mTopDrag || v == mRightDrag
|
||||
|| v == mBottomDrag) {
|
||||
return handleDragTouchEvent(event);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handleControlTouchEvent(MotionEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
mIsPressedDown = false;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handleDragTouchEvent(MotionEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mLastDrag.set(event.getRawX(), event.getRawY());
|
||||
return true;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
int xDiff = (int) (event.getRawX() - mLastDrag.x);
|
||||
int yDiff = (int) (event.getRawY() - mLastDrag.y);
|
||||
mMagnificationFrame.offset(xDiff, yDiff);
|
||||
mLastDrag.set(event.getRawX(), event.getRawY());
|
||||
modifyWindowMagnification(mTransaction);
|
||||
mTransaction.apply();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void setMoveOffset(View v, int moveFrameAmount) {
|
||||
mMoveWindowOffset.set(0, 0);
|
||||
|
||||
if (v == mLeftControl) {
|
||||
mMoveWindowOffset.x = -moveFrameAmount;
|
||||
} else if (v == mUpControl) {
|
||||
mMoveWindowOffset.y = -moveFrameAmount;
|
||||
} else if (v == mRightControl) {
|
||||
mMoveWindowOffset.x = moveFrameAmount;
|
||||
} else if (v == mBottomControl) {
|
||||
mMoveWindowOffset.y = moveFrameAmount;
|
||||
}
|
||||
}
|
||||
|
||||
private void moveMirrorFromControls() {
|
||||
mMagnificationFrame.offset(mMoveWindowOffset.x, mMoveWindowOffset.y);
|
||||
|
||||
modifyWindowMagnification(mTransaction);
|
||||
mTransaction.apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the desired source bounds. This will be the area under from the center of the
|
||||
* displayFrame, factoring in scale.
|
||||
*/
|
||||
private Rect getSourceBounds(Rect displayFrame, float scale) {
|
||||
int halfWidth = displayFrame.width() / 2;
|
||||
int halfHeight = displayFrame.height() / 2;
|
||||
int left = displayFrame.left + (halfWidth - (int) (halfWidth / scale));
|
||||
int right = displayFrame.right - (halfWidth - (int) (halfWidth / scale));
|
||||
int top = displayFrame.top + (halfHeight - (int) (halfHeight / scale));
|
||||
int bottom = displayFrame.bottom - (halfHeight - (int) (halfHeight / scale));
|
||||
return new Rect(left, top, right, bottom);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
createMirror();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
}
|
||||
|
||||
class MoveMirrorRunnable implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mIsPressedDown) {
|
||||
moveMirrorFromControls();
|
||||
mHandler.postDelayed(mMoveMirrorRunnable, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import com.android.systemui.ScreenDecorations;
|
||||
import com.android.systemui.SizeCompatModeActivityController;
|
||||
import com.android.systemui.SliceBroadcastRelayHandler;
|
||||
import com.android.systemui.SystemUI;
|
||||
import com.android.systemui.accessibility.WindowMagnification;
|
||||
import com.android.systemui.biometrics.AuthController;
|
||||
import com.android.systemui.globalactions.GlobalActionsComponent;
|
||||
import com.android.systemui.keyguard.KeyguardViewMediator;
|
||||
@@ -156,4 +157,10 @@ public abstract class SystemUIBinder {
|
||||
@IntoMap
|
||||
@ClassKey(VolumeUI.class)
|
||||
public abstract SystemUI bindVolumeUI(VolumeUI sysui);
|
||||
|
||||
/** Inject into WindowMagnification. */
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(WindowMagnification.class)
|
||||
public abstract SystemUI bindWindowMagnification(WindowMagnification sysui);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user