From 152e9bb81aa5b2ab4637f4b2dae04b3ce89fa891 Mon Sep 17 00:00:00 2001 From: Svetoslav Ganov Date: Fri, 12 Oct 2012 20:15:29 -0700 Subject: Refactoring of the screen magnification feature. 1. The screen magnification feature was implemented entirely as a part of the accessibility manager. To achieve that the window manager had to implement a bunch of hooks for an external client to observe its internal state. This was problematic since it dilutes the window manager interface and allows code that is deeply coupled with the window manager to reside outside of it. Also the observer callbacks were IPCs which cannot be called with the window manager's lock held. To avoid that the window manager had to post messages requesting notification of interested parties which makes the code consuming the callbacks to run asynchronously of the window manager. This causes timing issues and adds unnecessary complexity. Now the magnification logic is split in two halves. The first half that is responsible to track the magnified portion of the screen and serve as a policy which windows can be magnified and it is a part of the window manager. This part exposes higher level APIs allowing interested parties with the right permissions to control the magnification of a given display. The APIs also allow a client to be registered for callbacks on interesting changes such as resize of the magnified region, etc. This part servers as a mediator between magnification controllers and the window manager. The second half is a controller that is responsible to drive the magnification state based on touch interactions. It also presents a highlight when magnified to suggest the magnified potion of the screen. The controller is responsible for auto zooming out in case the user context changes - rotation, new actitivity. The controller also auto pans if a dialog appears and it does not interesect the magnified frame. bug:7410464 2. By design screen magnification and touch exploration work separately and together. If magnification is enabled the user sees a larger version of the widgets and a sub section of the screen content. Accessibility services use the introspection APIs to "see" what is on the screen so they can speak it, navigate to the next item in response to a gesture, etc. Hence, the information returned to accessibility services has to reflect what a sighted user would see on the screen. Therefore, if the screen is magnified we need to adjust the bounds and position of the infos describing views in a magnified window such that the info bounds are equivalent to what the user sees. To improve performance we keep accessibility node info caches in the client process. However, when magnification state changes we have to clear these caches since the bounds of the cached infos no longer reflect the screen content which just got smaller or larger. This patch propagates not only the window scale as before but also the X/Y pan and the bounds of the magnified portion of the screen to the introspected app. This information is used to adjust the bounds of the node infos coming from this window such that the reported bounds are the same as the user sees not as the app thinks they are. Note that if magnification is enabled we zoom the content and pan it along the X and Y axis. Also recomputed is the isVisibleToUser property of the reported info since in a magnified state the user sees a subset of the window content and the views not in the magnified viewport should be reported as not visible to the user. bug:7344059 Change-Id: I6f7832c7a6a65c5368b390eb1f1518d0c7afd7d2 --- Android.mk | 3 +- CleanSpec.mk | 5 + api/current.txt | 1 + .../accessibilityservice/AccessibilityService.java | 9 + .../IAccessibilityServiceClient.aidl | 2 + .../IAccessibilityServiceConnection.aidl | 21 +- .../view/AccessibilityInteractionController.java | 120 +- .../view/IDisplayContentChangeListener.aidl | 33 - .../view/IDisplayMagnificationController.aidl | 27 + .../view/IDisplayMagnificationMediator.aidl | 31 + core/java/android/view/IWindowManager.aidl | 32 +- core/java/android/view/MagnificationSpec.aidl | 20 + core/java/android/view/MagnificationSpec.java | 120 ++ core/java/android/view/Surface.java | 26 + core/java/android/view/ViewRootImpl.java | 23 +- core/java/android/view/WindowInfo.aidl | 20 - core/java/android/view/WindowInfo.java | 175 --- core/java/android/view/WindowManagerPolicy.java | 8 - .../AccessibilityInteractionClient.java | 73 +- .../IAccessibilityInteractionConnection.aidl | 11 +- .../com/android/internal/policy/PolicyManager.java | 2 - .../internal/policy/impl/PhoneWindowManager.java | 78 +- .../com/android/internal/policy/impl/Policy.java | 3 - .../accessibility/AccessibilityInputFilter.java | 4 +- .../accessibility/AccessibilityManagerService.java | 153 ++- .../server/accessibility/ScreenMagnifier.java | 1331 +++++++------------- .../java/com/android/server/wm/DisplayContent.java | 11 - .../server/wm/DisplayMagnificationMediator.java | 616 +++++++++ .../com/android/server/wm/MagnificationSpec.java | 46 - .../android/server/wm/WindowManagerService.java | 371 +----- .../java/com/android/server/wm/WindowState.java | 15 - .../com/android/server/wm/WindowStateAnimator.java | 23 +- .../src/android/view/IWindowManagerImpl.java | 34 +- 33 files changed, 1716 insertions(+), 1731 deletions(-) delete mode 100644 core/java/android/view/IDisplayContentChangeListener.aidl create mode 100644 core/java/android/view/IDisplayMagnificationController.aidl create mode 100644 core/java/android/view/IDisplayMagnificationMediator.aidl create mode 100644 core/java/android/view/MagnificationSpec.aidl create mode 100644 core/java/android/view/MagnificationSpec.java delete mode 100644 core/java/android/view/WindowInfo.aidl delete mode 100644 core/java/android/view/WindowInfo.java create mode 100644 services/java/com/android/server/wm/DisplayMagnificationMediator.java delete mode 100644 services/java/com/android/server/wm/MagnificationSpec.java diff --git a/Android.mk b/Android.mk index f338fa1eeafc..dc45b1c8da0a 100644 --- a/Android.mk +++ b/Android.mk @@ -154,7 +154,8 @@ LOCAL_SRC_FILES += \ core/java/android/view/accessibility/IAccessibilityManager.aidl \ core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \ core/java/android/view/IApplicationToken.aidl \ - core/java/android/view/IDisplayContentChangeListener.aidl \ + core/java/android/view/IDisplayMagnificationMediator.aidl \ + core/java/android/view/IDisplayMagnificationController.aidl \ core/java/android/view/IInputFilter.aidl \ core/java/android/view/IInputFilterHost.aidl \ core/java/android/view/IOnKeyguardExitResult.aidl \ diff --git a/CleanSpec.mk b/CleanSpec.mk index 01d09f62cd20..425794a90290 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -141,6 +141,11 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/ui/*.ogg) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/telephony/java/com/android/internal/telephony/IExtendedNetworkService.java) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/telephony/java/com/android/internal/telephony/IExtendedNetworkService.P) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/ImageProcessing_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IDisplayContentChangeListener.java) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IDisplayContentChangeListener.P) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IWindowManager.java) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IWindowManager.P) + # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ diff --git a/api/current.txt b/api/current.txt index 454e5da9a080..2be13a0380d6 100644 --- a/api/current.txt +++ b/api/current.txt @@ -24746,6 +24746,7 @@ package android.view { method public android.graphics.Canvas lockCanvas(android.graphics.Rect) throws java.lang.IllegalArgumentException, android.view.Surface.OutOfResourcesException; method public void readFromParcel(android.os.Parcel); method public void release(); + method public static java.lang.String rotationToString(int); method public deprecated void unlockCanvas(android.graphics.Canvas); method public void unlockCanvasAndPost(android.graphics.Canvas); method public void writeToParcel(android.os.Parcel, int); diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index b0bad0721218..ecf3b19c12e3 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -548,6 +548,7 @@ public abstract class AccessibilityService extends Service { private static final int DO_ON_INTERRUPT = 20; private static final int DO_ON_ACCESSIBILITY_EVENT = 30; private static final int DO_ON_GESTURE = 40; + private static final int DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE = 50; private final HandlerCaller mCaller; @@ -580,6 +581,11 @@ public abstract class AccessibilityService extends Service { mCaller.sendMessage(message); } + public void clearAccessibilityNodeInfoCache() { + Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE); + mCaller.sendMessage(message); + } + public void executeMessage(Message message) { switch (message.what) { case DO_ON_ACCESSIBILITY_EVENT : @@ -611,6 +617,9 @@ public abstract class AccessibilityService extends Service { final int gestureId = message.arg1; mCallback.onGesture(gestureId); return; + case DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE: + AccessibilityInteractionClient.getInstance().clearCache(); + return; default : Log.w(LOG_TAG, "Unknown message type " + message.what); } diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl index d459fd546264..5d684e358f56 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl @@ -33,4 +33,6 @@ import android.view.accessibility.AccessibilityEvent; void onInterrupt(); void onGesture(int gesture); + + void clearAccessibilityNodeInfoCache(); } diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index dd50f3c38104..f33f503ef1ff 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -18,6 +18,7 @@ package android.accessibilityservice; import android.os.Bundle; import android.accessibilityservice.AccessibilityServiceInfo; +import android.view.MagnificationSpec; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.IAccessibilityInteractionConnectionCallback; @@ -44,9 +45,9 @@ interface IAccessibilityServiceConnection { * @param callback Callback which to receive the result. * @param flags Additional flags. * @param threadId The id of the calling thread. - * @return The current window scale, where zero means a failure. + * @return Whether the call succeeded. */ - float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId, + boolean findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId, long accessibilityNodeId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, long threadId); @@ -66,9 +67,9 @@ interface IAccessibilityServiceConnection { * @param interactionId The id of the interaction for matching with the callback result. * @param callback Callback which to receive the result. * @param threadId The id of the calling thread. - * @return The current window scale, where zero means a failure. + * @return Whether the call succeeded. */ - float findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId, + boolean findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId, String text, int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId); @@ -88,9 +89,9 @@ interface IAccessibilityServiceConnection { * @param interactionId The id of the interaction for matching with the callback result. * @param callback Callback which to receive the result. * @param threadId The id of the calling thread. - * @return The current window scale, where zero means a failure. + * @return Whether the call succeeded. */ - float findAccessibilityNodeInfoByViewId(int accessibilityWindowId, long accessibilityNodeId, + boolean findAccessibilityNodeInfoByViewId(int accessibilityWindowId, long accessibilityNodeId, int viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId); @@ -110,9 +111,9 @@ interface IAccessibilityServiceConnection { * @param interactionId The id of the interaction for matching with the callback result. * @param callback Callback which to receive the result. * @param threadId The id of the calling thread. - * @return The current window scale, where zero means a failure. + * @return Whether the call succeeded. */ - float findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType, + boolean findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType, int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId); /** @@ -131,9 +132,9 @@ interface IAccessibilityServiceConnection { * @param interactionId The id of the interaction for matching with the callback result. * @param callback Callback which to receive the result. * @param threadId The id of the calling thread. - * @return The current window scale, where zero means a failure. + * @return Whether the call succeeded. */ - float focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction, + boolean focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction, int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId); /** diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index 9bee4bf9737d..ba82d7920ab4 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -18,6 +18,7 @@ package android.view; import static android.view.accessibility.AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS; +import android.graphics.Point; import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; @@ -26,6 +27,7 @@ import android.os.Message; import android.os.Process; import android.os.RemoteException; import android.util.SparseLongArray; +import android.view.View.AttachInfo; import android.view.accessibility.AccessibilityInteractionClient; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeProvider; @@ -62,7 +64,10 @@ final class AccessibilityInteractionController { private final ArrayList mTempArrayList = new ArrayList(); + private final Point mTempPoint = new Point(); private final Rect mTempRect = new Rect(); + private final Rect mTempRect1 = new Rect(); + private final Rect mTempRect2 = new Rect(); public AccessibilityInteractionController(ViewRootImpl viewRootImpl) { Looper looper = viewRootImpl.mHandler.getLooper(); @@ -86,7 +91,7 @@ final class AccessibilityInteractionController { public void findAccessibilityNodeInfoByAccessibilityIdClientThread( long accessibilityNodeId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, - long interrogatingTid) { + long interrogatingTid, MagnificationSpec spec) { Message message = mHandler.obtainMessage(); message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID; message.arg1 = flags; @@ -96,6 +101,7 @@ final class AccessibilityInteractionController { args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId); args.argi3 = interactionId; args.arg1 = callback; + args.arg2 = spec; message.obj = args; // If the interrogation is performed by the same thread as the main UI @@ -119,6 +125,7 @@ final class AccessibilityInteractionController { final int interactionId = args.argi3; final IAccessibilityInteractionConnectionCallback callback = (IAccessibilityInteractionConnectionCallback) args.arg1; + final MagnificationSpec spec = (MagnificationSpec) args.arg2; args.recycle(); @@ -142,7 +149,10 @@ final class AccessibilityInteractionController { } finally { try { mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false; - applyApplicationScaleIfNeeded(infos); + applyAppScaleAndMagnificationSpecIfNeeded(infos, spec); + if (spec != null) { + spec.recycle(); + } callback.setFindAccessibilityNodeInfosResult(infos, interactionId); infos.clear(); } catch (RemoteException re) { @@ -153,7 +163,7 @@ final class AccessibilityInteractionController { public void findAccessibilityNodeInfoByViewIdClientThread(long accessibilityNodeId, int viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback, - int flags, int interrogatingPid, long interrogatingTid) { + int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { Message message = mHandler.obtainMessage(); message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID; message.arg1 = flags; @@ -163,6 +173,7 @@ final class AccessibilityInteractionController { args.argi1 = viewId; args.argi2 = interactionId; args.arg1 = callback; + args.arg2 = spec; message.obj = args; @@ -187,6 +198,7 @@ final class AccessibilityInteractionController { final int interactionId = args.argi2; final IAccessibilityInteractionConnectionCallback callback = (IAccessibilityInteractionConnectionCallback) args.arg1; + final MagnificationSpec spec = (MagnificationSpec) args.arg2; args.recycle(); @@ -212,7 +224,10 @@ final class AccessibilityInteractionController { } finally { try { mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false; - applyApplicationScaleIfNeeded(info); + applyAppScaleAndMagnificationSpecIfNeeded(info, spec); + if (spec != null) { + spec.recycle(); + } callback.setFindAccessibilityNodeInfoResult(info, interactionId); } catch (RemoteException re) { /* ignore - the other side will time out */ @@ -222,7 +237,7 @@ final class AccessibilityInteractionController { public void findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeId, String text, int interactionId, IAccessibilityInteractionConnectionCallback callback, - int flags, int interrogatingPid, long interrogatingTid) { + int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { Message message = mHandler.obtainMessage(); message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT; message.arg1 = flags; @@ -230,10 +245,10 @@ final class AccessibilityInteractionController { SomeArgs args = SomeArgs.obtain(); args.arg1 = text; args.arg2 = callback; + args.arg3 = spec; args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId); args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId); args.argi3 = interactionId; - message.obj = args; // If the interrogation is performed by the same thread as the main UI @@ -255,6 +270,7 @@ final class AccessibilityInteractionController { final String text = (String) args.arg1; final IAccessibilityInteractionConnectionCallback callback = (IAccessibilityInteractionConnectionCallback) args.arg2; + final MagnificationSpec spec = (MagnificationSpec) args.arg3; final int accessibilityViewId = args.argi1; final int virtualDescendantId = args.argi2; final int interactionId = args.argi3; @@ -310,7 +326,10 @@ final class AccessibilityInteractionController { } finally { try { mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false; - applyApplicationScaleIfNeeded(infos); + applyAppScaleAndMagnificationSpecIfNeeded(infos, spec); + if (spec != null) { + spec.recycle(); + } callback.setFindAccessibilityNodeInfosResult(infos, interactionId); } catch (RemoteException re) { /* ignore - the other side will time out */ @@ -320,7 +339,7 @@ final class AccessibilityInteractionController { public void findFocusClientThread(long accessibilityNodeId, int focusType, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid, - long interrogatingTid) { + long interrogatingTid, MagnificationSpec spec) { Message message = mHandler.obtainMessage(); message.what = PrivateHandler.MSG_FIND_FOCUS; message.arg1 = flags; @@ -331,6 +350,7 @@ final class AccessibilityInteractionController { args.argi2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId); args.argi3 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId); args.arg1 = callback; + args.arg2 = spec; message.obj = args; @@ -356,7 +376,7 @@ final class AccessibilityInteractionController { final int virtualDescendantId = args.argi3; final IAccessibilityInteractionConnectionCallback callback = (IAccessibilityInteractionConnectionCallback) args.arg1; - + final MagnificationSpec spec = (MagnificationSpec) args.arg2; args.recycle(); AccessibilityNodeInfo focused = null; @@ -407,7 +427,10 @@ final class AccessibilityInteractionController { } finally { try { mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false; - applyApplicationScaleIfNeeded(focused); + applyAppScaleAndMagnificationSpecIfNeeded(focused, spec); + if (spec != null) { + spec.recycle(); + } callback.setFindAccessibilityNodeInfoResult(focused, interactionId); } catch (RemoteException re) { /* ignore - the other side will time out */ @@ -417,7 +440,7 @@ final class AccessibilityInteractionController { public void focusSearchClientThread(long accessibilityNodeId, int direction, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid, - long interrogatingTid) { + long interrogatingTid, MagnificationSpec spec) { Message message = mHandler.obtainMessage(); message.what = PrivateHandler.MSG_FOCUS_SEARCH; message.arg1 = flags; @@ -427,6 +450,7 @@ final class AccessibilityInteractionController { args.argi2 = direction; args.argi3 = interactionId; args.arg1 = callback; + args.arg2 = spec; message.obj = args; @@ -451,6 +475,7 @@ final class AccessibilityInteractionController { final int interactionId = args.argi3; final IAccessibilityInteractionConnectionCallback callback = (IAccessibilityInteractionConnectionCallback) args.arg1; + final MagnificationSpec spec = (MagnificationSpec) args.arg2; args.recycle(); @@ -476,7 +501,10 @@ final class AccessibilityInteractionController { } finally { try { mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false; - applyApplicationScaleIfNeeded(next); + applyAppScaleAndMagnificationSpecIfNeeded(next, spec); + if (spec != null) { + spec.recycle(); + } callback.setFindAccessibilityNodeInfoResult(next, interactionId); } catch (RemoteException re) { /* ignore - the other side will time out */ @@ -572,38 +600,84 @@ final class AccessibilityInteractionController { return foundView; } - private void applyApplicationScaleIfNeeded(List infos) { + private void applyAppScaleAndMagnificationSpecIfNeeded(List infos, + MagnificationSpec spec) { if (infos == null) { return; } final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale; - if (applicationScale != 1.0f) { + if (shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) { final int infoCount = infos.size(); for (int i = 0; i < infoCount; i++) { AccessibilityNodeInfo info = infos.get(i); - applyApplicationScaleIfNeeded(info); + applyAppScaleAndMagnificationSpecIfNeeded(info, spec); } } } - private void applyApplicationScaleIfNeeded(AccessibilityNodeInfo info) { + private void applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info, + MagnificationSpec spec) { if (info == null) { return; } + final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale; + if (!shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) { + return; + } + + Rect boundsInParent = mTempRect; + Rect boundsInScreen = mTempRect1; + + info.getBoundsInParent(boundsInParent); + info.getBoundsInScreen(boundsInScreen); if (applicationScale != 1.0f) { - Rect bounds = mTempRect; + boundsInParent.scale(applicationScale); + boundsInScreen.scale(applicationScale); + } + if (spec != null) { + boundsInParent.scale(spec.scale); + // boundsInParent must not be offset. + boundsInScreen.scale(spec.scale); + boundsInScreen.offset((int) spec.offsetX, (int) spec.offsetY); + } + info.setBoundsInParent(boundsInParent); + info.setBoundsInScreen(boundsInScreen); + + if (spec != null) { + AttachInfo attachInfo = mViewRootImpl.mAttachInfo; + if (attachInfo.mDisplay == null) { + return; + } - info.getBoundsInParent(bounds); - bounds.scale(applicationScale); - info.setBoundsInParent(bounds); + final float scale = attachInfo.mApplicationScale * spec.scale; - info.getBoundsInScreen(bounds); - bounds.scale(applicationScale); - info.setBoundsInScreen(bounds); + Rect visibleWinFrame = mTempRect1; + visibleWinFrame.left = (int) (attachInfo.mWindowLeft * scale + spec.offsetX); + visibleWinFrame.top = (int) (attachInfo.mWindowTop * scale + spec.offsetY); + visibleWinFrame.right = (int) (visibleWinFrame.left + mViewRootImpl.mWidth * scale); + visibleWinFrame.bottom = (int) (visibleWinFrame.top + mViewRootImpl.mHeight * scale); + + attachInfo.mDisplay.getRealSize(mTempPoint); + final int displayWidth = mTempPoint.x; + final int displayHeight = mTempPoint.y; + + Rect visibleDisplayFrame = mTempRect2; + visibleDisplayFrame.set(0, 0, displayWidth, displayHeight); + + visibleWinFrame.intersect(visibleDisplayFrame); + + if (!visibleWinFrame.intersects(boundsInScreen.left, boundsInScreen.top, + boundsInScreen.right, boundsInScreen.bottom)) { + info.setVisibleToUser(false); + } } } + private boolean shouldApplyAppScaleAndMagnificationSpec(float appScale, + MagnificationSpec spec) { + return (appScale != 1.0f || (spec != null && !spec.isNop())); + } /** * This class encapsulates a prefetching strategy for the accessibility APIs for diff --git a/core/java/android/view/IDisplayContentChangeListener.aidl b/core/java/android/view/IDisplayContentChangeListener.aidl deleted file mode 100644 index ef7edeab3894..000000000000 --- a/core/java/android/view/IDisplayContentChangeListener.aidl +++ /dev/null @@ -1,33 +0,0 @@ -/* -** Copyright 2012, 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.os.IBinder; -import android.view.WindowInfo; -import android.graphics.Rect; - -/** - * Interface for observing content changes on a display. - * - * {@hide} - */ -oneway interface IDisplayContentChangeListener { - void onWindowTransition(int displayId, int transition, in WindowInfo info); - void onRectangleOnScreenRequested(int displayId, in Rect rectangle, boolean immediate); - void onWindowLayersChanged(int displayId); - void onRotationChanged(int rotation); -} diff --git a/core/java/android/view/IDisplayMagnificationController.aidl b/core/java/android/view/IDisplayMagnificationController.aidl new file mode 100644 index 000000000000..efe277536354 --- /dev/null +++ b/core/java/android/view/IDisplayMagnificationController.aidl @@ -0,0 +1,27 @@ +/* +** Copyright 2012, 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; + +/** + * {@hide} + */ +oneway interface IDisplayMagnificationController { + void onMagnifedFrameChanged(int left, int top, int right, int bottom); + void onRectangleOnScreenRequested(int left, int top, int right, int bottom); + void onRotationChanged(int rotation); + void onUserContextChanged(); +} diff --git a/core/java/android/view/IDisplayMagnificationMediator.aidl b/core/java/android/view/IDisplayMagnificationMediator.aidl new file mode 100644 index 000000000000..aa25dacb609d --- /dev/null +++ b/core/java/android/view/IDisplayMagnificationMediator.aidl @@ -0,0 +1,31 @@ +/* +** Copyright 2012, 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.IDisplayMagnificationController; +import android.view.MagnificationSpec; + +/** + * {@hide} + */ +interface IDisplayMagnificationMediator { + void addController(int displayId, in IDisplayMagnificationController controller); + void removeController(in IDisplayMagnificationController controller); + void setMagnificationSpec(in IDisplayMagnificationController controller, + in MagnificationSpec spec); + MagnificationSpec getCompatibleMagnificationSpec(in IBinder windowToken); +} diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 0fe2a8e3f830..17f04e959cd2 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -27,17 +27,17 @@ import android.graphics.Rect; import android.os.Bundle; import android.os.IRemoteCallback; import android.view.IApplicationToken; -import android.view.IDisplayContentChangeListener; +import android.view.IDisplayMagnificationMediator; import android.view.IOnKeyguardExitResult; import android.view.IRotationWatcher; import android.view.IWindowSession; import android.view.KeyEvent; import android.view.InputEvent; +import android.view.MagnificationSpec; import android.view.MotionEvent; import android.view.InputChannel; import android.view.InputDevice; import android.view.IInputFilter; -import android.view.WindowInfo; /** * System private interface to the window manager. @@ -220,40 +220,20 @@ interface IWindowManager */ IBinder getFocusedWindowToken(); - /** - * Gets the compatibility scale of e window given its token. - */ - float getWindowCompatibilityScale(IBinder windowToken); - /** * Sets an input filter for manipulating the input event stream. */ void setInputFilter(in IInputFilter filter); /** - * Sets the scale and offset for implementing accessibility magnification. - */ - void magnifyDisplay(int dipslayId, float scale, float offsetX, float offsetY); - - /** - * Adds a listener for display content changes. - */ - void addDisplayContentChangeListener(int displayId, IDisplayContentChangeListener listener); - - /** - * Removes a listener for display content changes. - */ - void removeDisplayContentChangeListener(int displayId, IDisplayContentChangeListener listener); - - /** - * Gets the info for a window given its token. + * Gets the display magnification mediator. */ - WindowInfo getWindowInfo(IBinder token); + IDisplayMagnificationMediator getDisplayMagnificationMediator(); /** - * Gets the infos for all visible windows. + * Gets the frame of a window given its token. */ - void getVisibleWindowsForDisplay(int displayId, out List outInfos); + void getWindowFrame(IBinder token, out Rect outFrame); /** * Device is in safe mode. diff --git a/core/java/android/view/MagnificationSpec.aidl b/core/java/android/view/MagnificationSpec.aidl new file mode 100644 index 000000000000..d5fbdef16e20 --- /dev/null +++ b/core/java/android/view/MagnificationSpec.aidl @@ -0,0 +1,20 @@ +/* +** +** Copyright 2012, 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; + +parcelable MagnificationSpec; diff --git a/core/java/android/view/MagnificationSpec.java b/core/java/android/view/MagnificationSpec.java new file mode 100644 index 000000000000..7fb56158da5e --- /dev/null +++ b/core/java/android/view/MagnificationSpec.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2012 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.os.Parcel; +import android.os.Parcelable; +import android.util.Pools.SynchronizedPool; + +/** + * This class represents spec for performing screen magnification. + * + * @hide + */ +public class MagnificationSpec implements Parcelable { + private static final int MAX_POOL_SIZE = 20; + private static final SynchronizedPool sPool = + new SynchronizedPool(MAX_POOL_SIZE); + + public float scale = 1.0f; + public float offsetX; + public float offsetY; + + private MagnificationSpec() { + /* do nothing - reducing visibility */ + } + + public void initialize(float scale, float offsetX, float offsetY) { + this.scale = scale; + this.offsetX = offsetX; + this.offsetY = offsetY; + } + + public boolean isNop() { + return scale == 1.0f && offsetX == 0 && offsetY == 0; + } + + public static MagnificationSpec obtain(MagnificationSpec other) { + MagnificationSpec info = obtain(); + info.scale = other.scale; + info.offsetX = other.offsetX; + info.offsetY = other.offsetY; + return info; + } + + public static MagnificationSpec obtain() { + MagnificationSpec spec = sPool.acquire(); + return (spec != null) ? spec : new MagnificationSpec(); + } + + public void recycle() { + clear(); + sPool.release(this); + } + + public void clear() { + scale = 1.0f; + offsetX = 0.0f; + offsetY = 0.0f; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeFloat(scale); + parcel.writeFloat(offsetX); + parcel.writeFloat(offsetY); + recycle(); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(""); + return builder.toString(); + } + + private void initFromParcel(Parcel parcel) { + scale = parcel.readFloat(); + offsetX = parcel.readFloat(); + offsetY = parcel.readFloat(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public MagnificationSpec[] newArray(int size) { + return new MagnificationSpec[size]; + } + + @Override + public MagnificationSpec createFromParcel(Parcel parcel) { + MagnificationSpec spec = MagnificationSpec.obtain(); + spec.initFromParcel(parcel); + return spec; + } + }; +} diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 550a740195d7..f2c5eac2bd1c 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -807,6 +807,32 @@ public class Surface implements Parcelable { } } + /** + * Returns a human readable representation of a rotation. + * + * @param rotation The rotation. + * @return The rotation symbolic name. + */ + public static String rotationToString(int rotation) { + switch (rotation) { + case Surface.ROTATION_0: { + return "ROTATION_0"; + } + case Surface.ROTATION_90: { + return "ROATATION_90"; + } + case Surface.ROTATION_180: { + return "ROATATION_180"; + } + case Surface.ROTATION_270: { + return "ROATATION_270"; + } + default: { + throw new IllegalArgumentException("Invalid rotation: " + rotation); + } + } + } + /** * A Canvas class that can handle the compatibility mode. * This does two things differently. diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index e66e30fe382b..5ad7bb3cdd8d 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -5416,12 +5416,13 @@ public final class ViewRootImpl implements ViewParent, @Override public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, - int interrogatingPid, long interrogatingTid) { + int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null && viewRootImpl.mView != null) { viewRootImpl.getAccessibilityInteractionController() .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityNodeId, - interactionId, callback, flags, interrogatingPid, interrogatingTid); + interactionId, callback, flags, interrogatingPid, interrogatingTid, + spec); } else { // We cannot make the call and notify the caller so it does not wait. try { @@ -5455,12 +5456,13 @@ public final class ViewRootImpl implements ViewParent, @Override public void findAccessibilityNodeInfoByViewId(long accessibilityNodeId, int viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, - int interrogatingPid, long interrogatingTid) { + int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null && viewRootImpl.mView != null) { viewRootImpl.getAccessibilityInteractionController() .findAccessibilityNodeInfoByViewIdClientThread(accessibilityNodeId, viewId, - interactionId, callback, flags, interrogatingPid, interrogatingTid); + interactionId, callback, flags, interrogatingPid, interrogatingTid, + spec); } else { // We cannot make the call and notify the caller so it does not wait. try { @@ -5474,12 +5476,13 @@ public final class ViewRootImpl implements ViewParent, @Override public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, - int interrogatingPid, long interrogatingTid) { + int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null && viewRootImpl.mView != null) { viewRootImpl.getAccessibilityInteractionController() .findAccessibilityNodeInfosByTextClientThread(accessibilityNodeId, text, - interactionId, callback, flags, interrogatingPid, interrogatingTid); + interactionId, callback, flags, interrogatingPid, interrogatingTid, + spec); } else { // We cannot make the call and notify the caller so it does not wait. try { @@ -5493,12 +5496,12 @@ public final class ViewRootImpl implements ViewParent, @Override public void findFocus(long accessibilityNodeId, int focusType, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, - int interrogatingPid, long interrogatingTid) { + int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null && viewRootImpl.mView != null) { viewRootImpl.getAccessibilityInteractionController() .findFocusClientThread(accessibilityNodeId, focusType, interactionId, callback, - flags, interrogatingPid, interrogatingTid); + flags, interrogatingPid, interrogatingTid, spec); } else { // We cannot make the call and notify the caller so it does not wait. try { @@ -5512,12 +5515,12 @@ public final class ViewRootImpl implements ViewParent, @Override public void focusSearch(long accessibilityNodeId, int direction, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, - int interrogatingPid, long interrogatingTid) { + int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null && viewRootImpl.mView != null) { viewRootImpl.getAccessibilityInteractionController() .focusSearchClientThread(accessibilityNodeId, direction, interactionId, - callback, flags, interrogatingPid, interrogatingTid); + callback, flags, interrogatingPid, interrogatingTid, spec); } else { // We cannot make the call and notify the caller so it does not wait. try { diff --git a/core/java/android/view/WindowInfo.aidl b/core/java/android/view/WindowInfo.aidl deleted file mode 100644 index 23e927a1ee20..000000000000 --- a/core/java/android/view/WindowInfo.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* -** -** Copyright 2012, 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; - -parcelable WindowInfo; diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java deleted file mode 100644 index 7d16e147d293..000000000000 --- a/core/java/android/view/WindowInfo.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2012 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.graphics.Rect; -import android.os.IBinder; -import android.os.Parcel; -import android.os.Parcelable; - -/** - * Information the state of a window. - * - * @hide - */ -public class WindowInfo implements Parcelable { - - private static final int MAX_POOL_SIZE = 20; - - private static int UNDEFINED = -1; - - private static Object sPoolLock = new Object(); - private static WindowInfo sPool; - private static int sPoolSize; - - private WindowInfo mNext; - private boolean mInPool; - - public IBinder token; - - public final Rect frame = new Rect(); - - public final Rect touchableRegion = new Rect(); - - public int type = UNDEFINED; - - public float compatibilityScale = UNDEFINED; - - public boolean visible; - - public int displayId = UNDEFINED; - - public int layer = UNDEFINED; - - private WindowInfo() { - /* do nothing - reduce visibility */ - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel parcel, int flags) { - parcel.writeStrongBinder(token); - parcel.writeParcelable(frame, 0); - parcel.writeParcelable(touchableRegion, 0); - parcel.writeInt(type); - parcel.writeFloat(compatibilityScale); - parcel.writeInt(visible ? 1 : 0); - parcel.writeInt(displayId); - parcel.writeInt(layer); - recycle(); - } - - private void initFromParcel(Parcel parcel) { - token = parcel.readStrongBinder(); - frame.set((Rect) parcel.readParcelable(null)); - touchableRegion.set((Rect) parcel.readParcelable(null)); - type = parcel.readInt(); - compatibilityScale = parcel.readFloat(); - visible = (parcel.readInt() == 1); - displayId = parcel.readInt(); - layer = parcel.readInt(); - } - - public static WindowInfo obtain(WindowInfo other) { - WindowInfo info = obtain(); - info.token = other.token; - info.frame.set(other.frame); - info.touchableRegion.set(other.touchableRegion); - info.type = other.type; - info.compatibilityScale = other.compatibilityScale; - info.visible = other.visible; - info.displayId = other.displayId; - info.layer = other.layer; - return info; - } - - public static WindowInfo obtain() { - synchronized (sPoolLock) { - if (sPoolSize > 0) { - WindowInfo info = sPool; - sPool = info.mNext; - info.mNext = null; - info.mInPool = false; - sPoolSize--; - return info; - } else { - return new WindowInfo(); - } - } - } - - public void recycle() { - if (mInPool) { - throw new IllegalStateException("Already recycled."); - } - clear(); - synchronized (sPoolLock) { - if (sPoolSize < MAX_POOL_SIZE) { - mNext = sPool; - sPool = this; - mInPool = true; - sPoolSize++; - } - } - } - - private void clear() { - token = null; - frame.setEmpty(); - touchableRegion.setEmpty(); - type = UNDEFINED; - compatibilityScale = UNDEFINED; - visible = false; - displayId = UNDEFINED; - layer = UNDEFINED; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("Window [token:").append((token != null) ? token.hashCode() : null); - builder.append(", displayId:").append(displayId); - builder.append(", type:").append(type); - builder.append(", visible:").append(visible); - builder.append(", layer:").append(layer); - builder.append(", compatibilityScale:").append(compatibilityScale); - builder.append(", frame:").append(frame); - builder.append(", touchableRegion:").append(touchableRegion); - builder.append("]"); - return builder.toString(); - } - - /** - * @see Parcelable.Creator - */ - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - public WindowInfo createFromParcel(Parcel parcel) { - WindowInfo info = WindowInfo.obtain(); - info.initFromParcel(parcel); - return info; - } - - public WindowInfo[] newArray(int size) { - return new WindowInfo[size]; - } - }; -} diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 26739b3d5026..7bdb44c80595 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -1111,14 +1111,6 @@ public interface WindowManagerPolicy { */ public void setLastInputMethodWindowLw(WindowState ime, WindowState target); - /** - * Returns whether magnification can be applied to the given window type. - * - * @param attrs The window's LayoutParams. - * @return Whether magnification can be applied. - */ - public boolean canMagnifyWindowLw(WindowManager.LayoutParams attrs); - /** * Called when the current user changes. Guaranteed to be called before the broadcast * of the new user id is made to all listeners. diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index 20b5f17405cb..67df684c0777 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -17,7 +17,6 @@ package android.view.accessibility; import android.accessibilityservice.IAccessibilityServiceConnection; -import android.graphics.Rect; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -102,8 +101,6 @@ public final class AccessibilityInteractionClient private Message mSameThreadMessage; - private final Rect mTempBounds = new Rect(); - // The connection cache is shared between all interrogating threads. private static final SparseArray sConnectionCache = new SparseArray(); @@ -194,14 +191,14 @@ public final class AccessibilityInteractionClient return cachedInfo; } final int interactionId = mInteractionIdCounter.getAndIncrement(); - final float windowScale = connection.findAccessibilityNodeInfoByAccessibilityId( + final boolean success = connection.findAccessibilityNodeInfoByAccessibilityId( accessibilityWindowId, accessibilityNodeId, interactionId, this, prefetchFlags, Thread.currentThread().getId()); // If the scale is zero the call has failed. - if (windowScale > 0) { + if (success) { List infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); - finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, windowScale); + finalizeAndCacheAccessibilityNodeInfos(infos, connectionId); if (infos != null && !infos.isEmpty()) { return infos.get(0); } @@ -242,15 +239,13 @@ public final class AccessibilityInteractionClient IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); - final float windowScale = - connection.findAccessibilityNodeInfoByViewId(accessibilityWindowId, - accessibilityNodeId, viewId, interactionId, this, - Thread.currentThread().getId()); - // If the scale is zero the call has failed. - if (windowScale > 0) { + final boolean success =connection.findAccessibilityNodeInfoByViewId( + accessibilityWindowId, accessibilityNodeId, viewId, interactionId, this, + Thread.currentThread().getId()); + if (success) { AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( interactionId); - finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale); + finalizeAndCacheAccessibilityNodeInfo(info, connectionId); return info; } } else { @@ -290,14 +285,13 @@ public final class AccessibilityInteractionClient IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); - final float windowScale = connection.findAccessibilityNodeInfosByText( + final boolean success = connection.findAccessibilityNodeInfosByText( accessibilityWindowId, accessibilityNodeId, text, interactionId, this, Thread.currentThread().getId()); - // If the scale is zero the call has failed. - if (windowScale > 0) { + if (success) { List infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); - finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, windowScale); + finalizeAndCacheAccessibilityNodeInfos(infos, connectionId); return infos; } } else { @@ -336,14 +330,13 @@ public final class AccessibilityInteractionClient IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); - final float windowScale = connection.findFocus(accessibilityWindowId, + final boolean success = connection.findFocus(accessibilityWindowId, accessibilityNodeId, focusType, interactionId, this, Thread.currentThread().getId()); - // If the scale is zero the call has failed. - if (windowScale > 0) { + if (success) { AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( interactionId); - finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale); + finalizeAndCacheAccessibilityNodeInfo(info, connectionId); return info; } } else { @@ -381,14 +374,13 @@ public final class AccessibilityInteractionClient IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); - final float windowScale = connection.focusSearch(accessibilityWindowId, + final boolean success = connection.focusSearch(accessibilityWindowId, accessibilityNodeId, direction, interactionId, this, Thread.currentThread().getId()); - // If the scale is zero the call has failed. - if (windowScale > 0) { + if (success) { AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( interactionId); - finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale); + finalizeAndCacheAccessibilityNodeInfo(info, connectionId); return info; } } else { @@ -603,37 +595,15 @@ public final class AccessibilityInteractionClient } } - /** - * Applies compatibility scale to the info bounds if it is not equal to one. - * - * @param info The info whose bounds to scale. - * @param scale The scale to apply. - */ - private void applyCompatibilityScaleIfNeeded(AccessibilityNodeInfo info, float scale) { - if (scale == 1.0f) { - return; - } - Rect bounds = mTempBounds; - info.getBoundsInParent(bounds); - bounds.scale(scale); - info.setBoundsInParent(bounds); - - info.getBoundsInScreen(bounds); - bounds.scale(scale); - info.setBoundsInScreen(bounds); - } - /** * Finalize an {@link AccessibilityNodeInfo} before passing it to the client. * * @param info The info. * @param connectionId The id of the connection to the system. - * @param windowScale The source window compatibility scale. */ - private void finalizeAndCacheAccessibilityNodeInfo(AccessibilityNodeInfo info, int connectionId, - float windowScale) { + private void finalizeAndCacheAccessibilityNodeInfo(AccessibilityNodeInfo info, + int connectionId) { if (info != null) { - applyCompatibilityScaleIfNeeded(info, windowScale); info.setConnectionId(connectionId); info.setSealed(true); sAccessibilityNodeInfoCache.add(info); @@ -645,15 +615,14 @@ public final class AccessibilityInteractionClient * * @param infos The {@link AccessibilityNodeInfo}s. * @param connectionId The id of the connection to the system. - * @param windowScale The source window compatibility scale. */ private void finalizeAndCacheAccessibilityNodeInfos(List infos, - int connectionId, float windowScale) { + int connectionId) { if (infos != null) { final int infosCount = infos.size(); for (int i = 0; i < infosCount; i++) { AccessibilityNodeInfo info = infos.get(i); - finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale); + finalizeAndCacheAccessibilityNodeInfo(info, connectionId); } } } diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl index 9b393002d9bf..c313b0798e3f 100644 --- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl +++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl @@ -17,6 +17,7 @@ package android.view.accessibility; import android.os.Bundle; +import android.view.MagnificationSpec; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.IAccessibilityInteractionConnectionCallback; @@ -30,23 +31,23 @@ oneway interface IAccessibilityInteractionConnection { void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, - long interrogatingTid); + long interrogatingTid, in MagnificationSpec spec); void findAccessibilityNodeInfoByViewId(long accessibilityNodeId, int viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, - long interrogatingTid); + long interrogatingTid, in MagnificationSpec spec); void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, - long interrogatingTid); + long interrogatingTid, in MagnificationSpec spec); void findFocus(long accessibilityNodeId, int focusType, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, - long interrogatingTid); + long interrogatingTid, in MagnificationSpec spec); void focusSearch(long accessibilityNodeId, int direction, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, - long interrogatingTid); + long interrogatingTid, in MagnificationSpec spec); void performAccessibilityAction(long accessibilityNodeId, int action, in Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, diff --git a/core/java/com/android/internal/policy/PolicyManager.java b/core/java/com/android/internal/policy/PolicyManager.java index 5274e545a8de..462b3a99850c 100644 --- a/core/java/com/android/internal/policy/PolicyManager.java +++ b/core/java/com/android/internal/policy/PolicyManager.java @@ -22,8 +22,6 @@ import android.view.LayoutInflater; import android.view.Window; import android.view.WindowManagerPolicy; -import com.android.internal.policy.IPolicy; - /** * {@hide} */ diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index e978a8f0ed14..6262e4bf54ce 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -4519,19 +4519,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { mLastInputMethodTargetWindow = target; } - @Override - public boolean canMagnifyWindowLw(WindowManager.LayoutParams attrs) { - switch (attrs.type) { - case WindowManager.LayoutParams.TYPE_INPUT_METHOD: - case WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG: - case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR: - case WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY: { - return false; - } - } - return true; - } - @Override public void setCurrentUserLw(int newUserId) { if (mKeyguardMediator != null) { @@ -4552,6 +4539,71 @@ public class PhoneWindowManager implements WindowManagerPolicy { mKeyguardMediator.showAssistant(); } + /** + * Returns the human readable name of a window transition. + * + * @param transition The window transition. + * @return The transition symbolic name. + */ + public static String windowTransitionToString(int transition) { + switch (transition) { + case WindowManagerPolicy.TRANSIT_UNSET: { + return "TRANSIT_UNSET"; + } + case WindowManagerPolicy.TRANSIT_NONE: { + return "TRANSIT_NONE"; + } + case WindowManagerPolicy.TRANSIT_ENTER: { + return "TRANSIT_ENTER"; + } + case WindowManagerPolicy.TRANSIT_EXIT: { + return "TRANSIT_EXIT"; + } + case WindowManagerPolicy.TRANSIT_SHOW: { + return "TRANSIT_SHOW"; + } + case WindowManagerPolicy.TRANSIT_EXIT_MASK: { + return "TRANSIT_EXIT_MASK"; + } + case WindowManagerPolicy.TRANSIT_PREVIEW_DONE: { + return "TRANSIT_PREVIEW_DONE"; + } + case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN: { + return "TRANSIT_ACTIVITY_OPEN"; + } + case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE: { + return "TRANSIT_ACTIVITY_CLOSE"; + } + case WindowManagerPolicy.TRANSIT_TASK_OPEN: { + return "TRANSIT_TASK_OPEN"; + } + case WindowManagerPolicy.TRANSIT_TASK_CLOSE: { + return "TRANSIT_TASK_CLOSE"; + } + case WindowManagerPolicy.TRANSIT_TASK_TO_FRONT: { + return "TRANSIT_TASK_TO_FRONT"; + } + case WindowManagerPolicy.TRANSIT_TASK_TO_BACK: { + return "TRANSIT_TASK_TO_BACK"; + } + case WindowManagerPolicy.TRANSIT_WALLPAPER_CLOSE: { + return "TRANSIT_WALLPAPER_CLOSE"; + } + case WindowManagerPolicy.TRANSIT_WALLPAPER_OPEN: { + return "TRANSIT_WALLPAPER_OPEN"; + } + case WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_OPEN: { + return "TRANSIT_WALLPAPER_INTRA_OPEN"; + } + case WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_CLOSE: { + return "TRANSIT_WALLPAPER_INTRA_CLOSE"; + } + default: { + return ""; + } + } + } + @Override public void dump(String prefix, PrintWriter pw, String[] args) { pw.print(prefix); pw.print("mSafeMode="); pw.print(mSafeMode); diff --git a/policy/src/com/android/internal/policy/impl/Policy.java b/policy/src/com/android/internal/policy/impl/Policy.java index 153ef0f21878..42bfc5fb2474 100644 --- a/policy/src/com/android/internal/policy/impl/Policy.java +++ b/policy/src/com/android/internal/policy/impl/Policy.java @@ -24,9 +24,6 @@ import android.view.Window; import android.view.WindowManagerPolicy; import com.android.internal.policy.IPolicy; -import com.android.internal.policy.impl.PhoneLayoutInflater; -import com.android.internal.policy.impl.PhoneWindow; -import com.android.internal.policy.impl.PhoneWindowManager; /** * {@hide} diff --git a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java index eb414fae2a25..45da1e11f4f8 100644 --- a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -19,6 +19,7 @@ package com.android.server.accessibility; import android.content.Context; import android.os.PowerManager; import android.util.Slog; +import android.view.Display; import android.view.InputDevice; import android.view.InputEvent; import android.view.InputFilter; @@ -169,7 +170,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo private void enableFeatures() { if ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0) { - mEventHandler = mScreenMagnifier = new ScreenMagnifier(mContext); + mEventHandler = mScreenMagnifier = new ScreenMagnifier(mContext, + Display.DEFAULT_DISPLAY, mAms); mEventHandler.setNext(this); } if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) { diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java index 65bfa7e6a65f..3e2540db6a53 100644 --- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -65,12 +65,13 @@ import android.text.TextUtils.SimpleStringSplitter; import android.util.Slog; import android.util.SparseArray; import android.view.Display; +import android.view.IDisplayMagnificationMediator; import android.view.IWindow; import android.view.IWindowManager; import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; -import android.view.WindowInfo; +import android.view.MagnificationSpec; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityInteractionClient; @@ -158,6 +159,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private final MainHandler mMainHandler; + private IDisplayMagnificationMediator mMagnificationMediator; + private Service mUiAutomationService; private Service mQueryBridge; @@ -621,6 +624,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return false; } focus.getBoundsInScreen(outBounds); + + MagnificationSpec spec = service.getCompatibleMagnificationSpec(focus.getWindowId()); + if (spec != null && !spec.isNop()) { + outBounds.offset((int) -spec.offsetX, (int) -spec.offsetY); + outBounds.scale(1 / spec.scale); + } + // Clip to the window rectangle. Rect windowBounds = mTempRect; getActiveWindowBounds(windowBounds); @@ -628,6 +638,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { // Clip to the screen rectangle. mDefaultDisplay.getRealSize(mTempPoint); outBounds.intersect(0, 0, mTempPoint.x, mTempPoint.y); + return true; } finally { client.removeConnection(connectionId); @@ -648,19 +659,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { token = getCurrentUserStateLocked().mWindowTokens.get(windowId); } } - WindowInfo info = null; try { - info = mWindowManagerService.getWindowInfo(token); - if (info != null) { - outBounds.set(info.frame); + mWindowManagerService.getWindowFrame(token, outBounds); + if (!outBounds.isEmpty()) { return true; } } catch (RemoteException re) { /* ignore */ - } finally { - if (info != null) { - info.recycle(); - } } return false; } @@ -677,6 +682,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { mSecurityPolicy.onTouchInteractionEnd(); } + void onMagnificationStateChanged() { + notifyClearAccessibilityNodeInfoCacheLocked(); + } + private void switchUser(int userId) { synchronized (mLock) { // The user switched so we do not need to restore the current user @@ -762,6 +771,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return false; } + private void notifyClearAccessibilityNodeInfoCacheLocked() { + UserState state = getCurrentUserStateLocked(); + for (int i = state.mServices.size() - 1; i >= 0; i--) { + Service service = state.mServices.get(i); + service.notifyClearAccessibilityNodeInfoCache(); + } + } + /** * Removes an AccessibilityInteractionConnection. * @@ -1438,9 +1455,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { class Service extends IAccessibilityServiceConnection.Stub implements ServiceConnection, DeathRecipient { - // We pick the MSB to avoid collision since accessibility event types are + // We pick the MSBs to avoid collision since accessibility event types are // used as message types allowing us to remove messages per event type. private static final int MSG_ON_GESTURE = 0x80000000; + private static final int MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE = 0x40000000; final int mUserId; @@ -1494,6 +1512,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final int gestureId = message.arg1; notifyGestureInternal(gestureId); } break; + case MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE: { + notifyClearAccessibilityNodeInfoCacheInternal(); + } break; default: { final int eventType = type; notifyAccessibilityEventInternal(eventType); @@ -1636,7 +1657,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } @Override - public float findAccessibilityNodeInfoByViewId(int accessibilityWindowId, + public boolean findAccessibilityNodeInfoByViewId(int accessibilityWindowId, long accessibilityNodeId, int viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException { @@ -1647,17 +1668,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { .resolveCallingUserIdEnforcingPermissionsLocked( UserHandle.getCallingUserId()); if (resolvedUserId != mCurrentUserId) { - return -1; + return false; } mSecurityPolicy.enforceCanRetrieveWindowContent(this); final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this); if (!permissionGranted) { - return 0; + return false; } else { resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); connection = getConnectionLocked(resolvedWindowId); if (connection == null) { - return 0; + return false; } } } @@ -1665,10 +1686,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0; final int interrogatingPid = Binder.getCallingPid(); final long identityToken = Binder.clearCallingIdentity(); + MagnificationSpec spec = getCompatibleMagnificationSpec(resolvedWindowId); try { connection.findAccessibilityNodeInfoByViewId(accessibilityNodeId, viewId, - interactionId, callback, flags, interrogatingPid, interrogatingTid); - return getCompatibilityScale(resolvedWindowId); + interactionId, callback, flags, interrogatingPid, interrogatingTid, spec); + return true; } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error findAccessibilityNodeInfoByViewId()."); @@ -1676,11 +1698,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } finally { Binder.restoreCallingIdentity(identityToken); } - return 0; + return false; } @Override - public float findAccessibilityNodeInfosByText(int accessibilityWindowId, + public boolean findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId, String text, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException { @@ -1691,18 +1713,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { .resolveCallingUserIdEnforcingPermissionsLocked( UserHandle.getCallingUserId()); if (resolvedUserId != mCurrentUserId) { - return -1; + return false; } mSecurityPolicy.enforceCanRetrieveWindowContent(this); resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); final boolean permissionGranted = mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); if (!permissionGranted) { - return 0; + return false; } else { connection = getConnectionLocked(resolvedWindowId); if (connection == null) { - return 0; + return false; } } } @@ -1710,11 +1732,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0; final int interrogatingPid = Binder.getCallingPid(); final long identityToken = Binder.clearCallingIdentity(); + MagnificationSpec spec = getCompatibleMagnificationSpec(resolvedWindowId); try { connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text, - interactionId, callback, flags, interrogatingPid, - interrogatingTid); - return getCompatibilityScale(resolvedWindowId); + interactionId, callback, flags, interrogatingPid, interrogatingTid, spec); + return true; } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfosByText()"); @@ -1722,12 +1744,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } finally { Binder.restoreCallingIdentity(identityToken); } - return 0; + return false; } @Override - public float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId, - long accessibilityNodeId, int interactionId, + public boolean findAccessibilityNodeInfoByAccessibilityId( + int accessibilityWindowId, long accessibilityNodeId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, long interrogatingTid) throws RemoteException { final int resolvedWindowId; @@ -1737,18 +1759,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { .resolveCallingUserIdEnforcingPermissionsLocked( UserHandle.getCallingUserId()); if (resolvedUserId != mCurrentUserId) { - return -1; + return false; } mSecurityPolicy.enforceCanRetrieveWindowContent(this); resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); final boolean permissionGranted = mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); if (!permissionGranted) { - return 0; + return false; } else { connection = getConnectionLocked(resolvedWindowId); if (connection == null) { - return 0; + return false; } } } @@ -1756,10 +1778,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0); final int interrogatingPid = Binder.getCallingPid(); final long identityToken = Binder.clearCallingIdentity(); + MagnificationSpec spec = getCompatibleMagnificationSpec(resolvedWindowId); try { connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId, - interactionId, callback, allFlags, interrogatingPid, interrogatingTid); - return getCompatibilityScale(resolvedWindowId); + interactionId, callback, allFlags, interrogatingPid, interrogatingTid, + spec); + return true; } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfoByAccessibilityId()"); @@ -1767,11 +1791,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } finally { Binder.restoreCallingIdentity(identityToken); } - return 0; + return false; } @Override - public float findFocus(int accessibilityWindowId, long accessibilityNodeId, + public boolean findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException { @@ -1782,18 +1806,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { .resolveCallingUserIdEnforcingPermissionsLocked( UserHandle.getCallingUserId()); if (resolvedUserId != mCurrentUserId) { - return -1; + return false; } mSecurityPolicy.enforceCanRetrieveWindowContent(this); resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); final boolean permissionGranted = mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); if (!permissionGranted) { - return 0; + return false; } else { connection = getConnectionLocked(resolvedWindowId); if (connection == null) { - return 0; + return false; } } } @@ -1801,10 +1825,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0; final int interrogatingPid = Binder.getCallingPid(); final long identityToken = Binder.clearCallingIdentity(); + MagnificationSpec spec = getCompatibleMagnificationSpec(resolvedWindowId); try { connection.findFocus(accessibilityNodeId, focusType, interactionId, callback, - flags, interrogatingPid, interrogatingTid); - return getCompatibilityScale(resolvedWindowId); + flags, interrogatingPid, interrogatingTid, spec); + return true; } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error calling findAccessibilityFocus()"); @@ -1812,11 +1837,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } finally { Binder.restoreCallingIdentity(identityToken); } - return 0; + return false; } @Override - public float focusSearch(int accessibilityWindowId, long accessibilityNodeId, + public boolean focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException { @@ -1827,18 +1852,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { .resolveCallingUserIdEnforcingPermissionsLocked( UserHandle.getCallingUserId()); if (resolvedUserId != mCurrentUserId) { - return -1; + return false; } mSecurityPolicy.enforceCanRetrieveWindowContent(this); resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); final boolean permissionGranted = mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); if (!permissionGranted) { - return 0; + return false; } else { connection = getConnectionLocked(resolvedWindowId); if (connection == null) { - return 0; + return false; } } } @@ -1846,10 +1871,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0; final int interrogatingPid = Binder.getCallingPid(); final long identityToken = Binder.clearCallingIdentity(); + MagnificationSpec spec = getCompatibleMagnificationSpec(resolvedWindowId); try { connection.focusSearch(accessibilityNodeId, direction, interactionId, callback, - flags, interrogatingPid, interrogatingTid); - return getCompatibilityScale(resolvedWindowId); + flags, interrogatingPid, interrogatingTid, spec); + return true; } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error calling accessibilityFocusSearch()"); @@ -1857,7 +1883,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } finally { Binder.restoreCallingIdentity(identityToken); } - return 0; + return false; } @Override @@ -2082,6 +2108,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { mHandler.obtainMessage(MSG_ON_GESTURE, gestureId, 0).sendToTarget(); } + public void notifyClearAccessibilityNodeInfoCache() { + mHandler.sendEmptyMessage(MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE); + } + private void notifyGestureInternal(int gestureId) { IAccessibilityServiceClient listener = mServiceInterface; if (listener != null) { @@ -2094,6 +2124,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } + private void notifyClearAccessibilityNodeInfoCacheInternal() { + IAccessibilityServiceClient listener = mServiceInterface; + if (listener != null) { + try { + listener.clearAccessibilityNodeInfoCache(); + } catch (RemoteException re) { + Slog.e(LOG_TAG, "Error during requesting accessibility info cache" + + " to be cleared.", re); + } + } + } + private void sendDownAndUpKeyEvents(int keyCode) { final long token = Binder.clearCallingIdentity(); @@ -2176,20 +2218,23 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return accessibilityWindowId; } - private float getCompatibilityScale(int windowId) { + private MagnificationSpec getCompatibleMagnificationSpec(int windowId) { try { - IBinder windowToken = mGlobalWindowTokens.get(windowId); - if (windowToken != null) { - return mWindowManagerService.getWindowCompatibilityScale(windowToken); + if (mMagnificationMediator == null) { + mMagnificationMediator = mWindowManagerService + .getDisplayMagnificationMediator(); } - windowToken = getCurrentUserStateLocked().mWindowTokens.get(windowId); + IBinder windowToken = mGlobalWindowTokens.get(windowId); + if (windowToken == null) { + windowToken = getCurrentUserStateLocked().mWindowTokens.get(windowId); + } if (windowToken != null) { - return mWindowManagerService.getWindowCompatibilityScale(windowToken); + return mMagnificationMediator.getCompatibleMagnificationSpec(windowToken); } } catch (RemoteException re) { /* ignore */ } - return 1.0f; + return null; } } diff --git a/services/java/com/android/server/accessibility/ScreenMagnifier.java b/services/java/com/android/server/accessibility/ScreenMagnifier.java index 482bff5aa7e4..e5f092409aaf 100644 --- a/services/java/com/android/server/accessibility/ScreenMagnifier.java +++ b/services/java/com/android/server/accessibility/ScreenMagnifier.java @@ -21,6 +21,7 @@ import android.animation.Animator.AnimatorListener; import android.animation.ObjectAnimator; import android.animation.TypeEvaluator; import android.animation.ValueAnimator; +import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -48,20 +49,19 @@ import android.view.DisplayInfo; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.Gravity; -import android.view.IDisplayContentChangeListener; +import android.view.IDisplayMagnificationController; +import android.view.IDisplayMagnificationMediator; import android.view.IWindowManager; +import android.view.MagnificationSpec; import android.view.MotionEvent; import android.view.MotionEvent.PointerCoords; import android.view.MotionEvent.PointerProperties; import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector.OnScaleGestureListener; -import android.view.Surface; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; -import android.view.WindowInfo; import android.view.WindowManager; -import android.view.WindowManagerPolicy; import android.view.accessibility.AccessibilityEvent; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; @@ -69,9 +69,6 @@ import android.view.animation.Interpolator; import com.android.internal.R; import com.android.internal.os.SomeArgs; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; import java.util.Locale; /** @@ -91,7 +88,7 @@ import java.util.Locale; * moving finger will be magnified to fit the screen. For example, if the * screen was not magnified and the user triple taps and holds the screen * would magnify and the viewport will follow the user's finger. When the - * finger goes up the screen will clear zoom out. If the same user interaction + * finger goes up the screen will zoom out. If the same user interaction * is performed when the screen is magnified, the viewport movement will * be the same but when the finger goes up the screen will stay magnified. * In other words, the initial magnified state is sticky. @@ -106,26 +103,25 @@ import java.util.Locale; * to pan the viewport. Note that in this mode the content is panned as * opposed to the viewport dragging mode in which the viewport is moved. * - * 5. When in a permanent magnified state the user can use three or more + * 5. When in a permanent magnified state the user can use two or more * fingers to change the magnification scale which will become the current * default magnification scale. The next time the user magnifies the same * magnification scale would be used. * * 6. The magnification scale will be persisted in settings and in the cloud. */ -public final class ScreenMagnifier implements EventStreamTransformation { +public final class ScreenMagnifier extends IDisplayMagnificationController.Stub + implements EventStreamTransformation { + + private static final String LOG_TAG = ScreenMagnifier.class.getSimpleName(); private static final boolean DEBUG_STATE_TRANSITIONS = false; private static final boolean DEBUG_DETECTING = false; - private static final boolean DEBUG_TRANSFORMATION = false; + private static final boolean DEBUG_SET_MAGNIFICATION_SPEC = false; private static final boolean DEBUG_PANNING = false; private static final boolean DEBUG_SCALING = false; - private static final boolean DEBUG_VIEWPORT_WINDOW = false; - private static final boolean DEBUG_WINDOW_TRANSITIONS = false; - private static final boolean DEBUG_ROTATION = false; private static final boolean DEBUG_MAGNIFICATION_CONTROLLER = false; - - private static final String LOG_TAG = ScreenMagnifier.class.getSimpleName(); + private static final boolean DEBUG_VIEWPORT_WINDOW = false; private static final int STATE_DELEGATING = 1; private static final int STATE_DETECTING = 2; @@ -133,27 +129,30 @@ public final class ScreenMagnifier implements EventStreamTransformation { private static final int STATE_MAGNIFIED_INTERACTION = 4; private static final float DEFAULT_MAGNIFICATION_SCALE = 2.0f; - private static final int DEFAULT_SCREEN_MAGNIFICATION_AUTO_UPDATE = 1; - private static final float DEFAULT_WINDOW_ANIMATION_SCALE = 1.0f; - private static final int MULTI_TAP_TIME_SLOP_ADJUSTMENT = 50; - private final IWindowManager mWindowManagerService = IWindowManager.Stub.asInterface( - ServiceManager.getService("window")); - private final WindowManager mWindowManager; - private final DisplayProvider mDisplayProvider; + private static final int MESSAGE_ON_MAGNIFIED_FRAME_CHANGED = 1; + private static final int MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED = 2; + private static final int MESSAGE_ON_USER_CONTEXT_CHANGED = 3; + private static final int MESSAGE_ON_ROTATION_CHANGED = 4; + private static final int MESSAGE_SHOW_VIEWPORT_FRAME = 5; - private final DetectingStateHandler mDetectingStateHandler = new DetectingStateHandler(); - private final MagnifiedContentInteractonStateHandler mMagnifiedContentInteractonStateHandler; - private final StateViewportDraggingHandler mStateViewportDraggingHandler = - new StateViewportDraggingHandler(); + private static final int DEFAULT_SCREEN_MAGNIFICATION_AUTO_UPDATE = 1; + private static final float DEFAULT_WINDOW_ANIMATION_SCALE = 1.0f; - private final Interpolator mInterpolator = new DecelerateInterpolator(2.5f); + private final Rect mTempRect = new Rect(); + private final Rect mTempRect1 = new Rect(); + private final Context mContext; private final MagnificationController mMagnificationController; - private final DisplayContentObserver mDisplayContentObserver; private final ScreenStateObserver mScreenStateObserver; - private final Viewport mViewport; + private final ViewportWindow mViewportWindow; + + private final DetectingStateHandler mDetectingStateHandler; + private final MagnifiedContentInteractonStateHandler mMagnifiedContentInteractonStateHandler; + private final StateViewportDraggingHandler mStateViewportDraggingHandler; + + private final AccessibilityManagerService mAms; private final int mTapTimeSlop = ViewConfiguration.getTapTimeout(); private final int mMultiTapTimeSlop = @@ -161,11 +160,12 @@ public final class ScreenMagnifier implements EventStreamTransformation { private final int mTapDistanceSlop; private final int mMultiTapDistanceSlop; - private final int mShortAnimationDuration; - private final int mLongAnimationDuration; + private final long mLongAnimationDuration; private final float mWindowAnimationScale; - private final Context mContext; + private final Rect mMagnifiedFrame = new Rect(); + + private IDisplayMagnificationMediator mDisplayMagnifier; private EventStreamTransformation mNext; @@ -178,12 +178,46 @@ public final class ScreenMagnifier implements EventStreamTransformation { private long mDelegatingStateDownTime; - public ScreenMagnifier(Context context) { + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message message) { + switch (message.what) { + case MESSAGE_ON_MAGNIFIED_FRAME_CHANGED: { + SomeArgs args = (SomeArgs) message.obj; + final int left = args.argi1; + final int top = args.argi2; + final int right = args.argi3; + final int bottom = args.argi4; + handleOnMagnifiedFrameChanged(left, top, right, bottom); + args.recycle(); + } break; + case MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED: { + SomeArgs args = (SomeArgs) message.obj; + final int left = args.argi1; + final int top = args.argi2; + final int right = args.argi3; + final int bottom = args.argi4; + handleOnRectangleOnScreenRequested(left, top, right, bottom); + args.recycle(); + } break; + case MESSAGE_ON_USER_CONTEXT_CHANGED: { + handleOnUserContextChanged(); + } break; + case MESSAGE_ON_ROTATION_CHANGED: { + final int rotation = message.arg1; + handleOnRotationChanged(rotation); + } break; + case MESSAGE_SHOW_VIEWPORT_FRAME: { + mViewportWindow.setShown(true, true); + } break; + } + } + }; + + public ScreenMagnifier(Context context, int displayId, AccessibilityManagerService service) { mContext = context; - mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + mAms = service; - mShortAnimationDuration = context.getResources().getInteger( - com.android.internal.R.integer.config_shortAnimTime); mLongAnimationDuration = context.getResources().getInteger( com.android.internal.R.integer.config_longAnimTime); mTapDistanceSlop = ViewConfiguration.get(context).getScaledTouchSlop(); @@ -191,22 +225,131 @@ public final class ScreenMagnifier implements EventStreamTransformation { mWindowAnimationScale = Settings.Global.getFloat(context.getContentResolver(), Settings.Global.WINDOW_ANIMATION_SCALE, DEFAULT_WINDOW_ANIMATION_SCALE); - mMagnificationController = new MagnificationController(mShortAnimationDuration); - mDisplayProvider = new DisplayProvider(context, mWindowManager); - mViewport = new Viewport(mContext, mWindowManager, mWindowManagerService, - mDisplayProvider, mInterpolator, mShortAnimationDuration); - mDisplayContentObserver = new DisplayContentObserver(mContext, mViewport, - mMagnificationController, mWindowManagerService, mDisplayProvider, - mLongAnimationDuration, mWindowAnimationScale); - mScreenStateObserver = new ScreenStateObserver(mContext, mViewport, - mMagnificationController); - + mDetectingStateHandler = new DetectingStateHandler(); + mStateViewportDraggingHandler = new StateViewportDraggingHandler(); mMagnifiedContentInteractonStateHandler = new MagnifiedContentInteractonStateHandler( context); + mMagnificationController = new MagnificationController(mLongAnimationDuration); + mViewportWindow = new ViewportWindow(context, displayId); + mScreenStateObserver = new ScreenStateObserver(context, mMagnificationController, + mViewportWindow); + + try { + IWindowManager windowManagerService = IWindowManager.Stub.asInterface( + ServiceManager.getService("window")); + mDisplayMagnifier = windowManagerService.getDisplayMagnificationMediator(); + mDisplayMagnifier.addController(Display.DEFAULT_DISPLAY, this); + } catch (RemoteException re) { + /* ignore */ + } + transitionToState(STATE_DETECTING); } + @Override + public void onMagnifedFrameChanged(int left, int top, int right, int bottom) { + SomeArgs args = SomeArgs.obtain(); + args.argi1 = left; + args.argi2 = top; + args.argi3 = right; + args.argi4 = bottom; + mHandler.obtainMessage(MESSAGE_ON_MAGNIFIED_FRAME_CHANGED, args).sendToTarget(); + } + + private void handleOnMagnifiedFrameChanged(int left, int top, int right, int bottom) { + mMagnifiedFrame.set(left, top, right, bottom); + mViewportWindow.setBounds(mMagnifiedFrame, mMagnificationController.isMagnifying()); + mAms.onMagnificationStateChanged(); + } + + @Override + public void onRectangleOnScreenRequested(int left, int top, int right, int bottom) { + SomeArgs args = SomeArgs.obtain(); + args.argi1 = left; + args.argi2 = top; + args.argi3 = right; + args.argi4 = bottom; + mHandler.obtainMessage(MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED, args).sendToTarget(); + } + + private void handleOnRectangleOnScreenRequested(int left, int top, int right, int bottom) { + Rect rectangle = mTempRect; + rectangle.set(left, top, right, bottom); + if (!Rect.intersects(rectangle, mMagnifiedFrame)) { + return; + } + Rect magnifFrameInScreenCoords = mTempRect1; + getMagnifiedFrameInContentCoords(magnifFrameInScreenCoords); + final float scrollX; + final float scrollY; + if (rectangle.width() > magnifFrameInScreenCoords.width()) { + final int direction = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()); + if (direction == View.LAYOUT_DIRECTION_LTR) { + scrollX = rectangle.left - magnifFrameInScreenCoords.left; + } else { + scrollX = rectangle.right - magnifFrameInScreenCoords.right; + } + } else if (rectangle.left < magnifFrameInScreenCoords.left) { + scrollX = rectangle.left - magnifFrameInScreenCoords.left; + } else if (rectangle.right > magnifFrameInScreenCoords.right) { + scrollX = rectangle.right - magnifFrameInScreenCoords.right; + } else { + scrollX = 0; + } + if (rectangle.height() > magnifFrameInScreenCoords.height()) { + scrollY = rectangle.top - magnifFrameInScreenCoords.top; + } else if (rectangle.top < magnifFrameInScreenCoords.top) { + scrollY = rectangle.top - magnifFrameInScreenCoords.top; + } else if (rectangle.bottom > magnifFrameInScreenCoords.bottom) { + scrollY = rectangle.bottom - magnifFrameInScreenCoords.bottom; + } else { + scrollY = 0; + } + final float scale = mMagnificationController.getScale(); + mMagnificationController.offsetMagnifiedRegionCenter(scrollX * scale, scrollY * scale); + } + + private void getMagnifiedFrameInContentCoords(Rect rect) { + MagnificationSpec spec = mMagnificationController.getMagnificationSpec(); + rect.set(mMagnifiedFrame); + rect.offset((int) -spec.offsetX, (int) -spec.offsetY); + rect.scale(1.0f / spec.scale); + } + + @Override + public void onRotationChanged(int rotation) { + mHandler.obtainMessage(MESSAGE_ON_ROTATION_CHANGED, rotation, 0).sendToTarget(); + } + + private void handleOnRotationChanged(int rotation) { + resetMagnificationIfNeeded(); + mViewportWindow.setShown(false, false); + mViewportWindow.rotationChanged(); + if (mMagnificationController.isMagnifying()) { + final long delay = (long) (2 * mLongAnimationDuration * mWindowAnimationScale); + Message message = mHandler.obtainMessage(MESSAGE_SHOW_VIEWPORT_FRAME); + mHandler.sendMessageDelayed(message, delay); + } + } + + @Override + public void onUserContextChanged() { + mHandler.sendEmptyMessage(MESSAGE_ON_USER_CONTEXT_CHANGED); + } + + private void handleOnUserContextChanged() { + resetMagnificationIfNeeded(); + } + + private void resetMagnificationIfNeeded() { + if (mMagnificationController.isMagnifying() + && isScreenMagnificationAutoUpdateEnabled(mContext)) { + mMagnificationController.reset(true); + mViewportWindow.setShown(false, true); + } + } + @Override public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { @@ -257,12 +400,8 @@ public final class ScreenMagnifier implements EventStreamTransformation { @Override public void onDestroy() { - mMagnificationController.setScaleAndMagnifiedRegionCenter(1.0f, - 0, 0, true); - mViewport.setFrameShown(false, true); - mDisplayProvider.destroy(); - mDisplayContentObserver.destroy(); mScreenStateObserver.destroy(); + mViewportWindow.destroy(); } private void handleMotionEventStateDelegating(MotionEvent event, @@ -284,10 +423,10 @@ public final class ScreenMagnifier implements EventStreamTransformation { final float eventX = event.getX(); final float eventY = event.getY(); if (mMagnificationController.isMagnifying() - && mViewport.getBounds().contains((int) eventX, (int) eventY)) { + && mMagnifiedFrame.contains((int) eventX, (int) eventY)) { final float scale = mMagnificationController.getScale(); - final float scaledOffsetX = mMagnificationController.getScaledOffsetX(); - final float scaledOffsetY = mMagnificationController.getScaledOffsetY(); + final float scaledOffsetX = mMagnificationController.getOffsetX(); + final float scaledOffsetY = mMagnificationController.getOffsetY(); final int pointerCount = event.getPointerCount(); PointerCoords[] coords = getTempPointerCoordsWithMinSize(pointerCount); PointerProperties[] properties = getTempPointerPropertiesWithMinSize(pointerCount); @@ -412,16 +551,11 @@ public final class ScreenMagnifier implements EventStreamTransformation { if (mCurrentState != STATE_MAGNIFIED_INTERACTION) { return true; } - final float scale = mMagnificationController.getScale(); - final float scrollX = distanceX / scale; - final float scrollY = distanceY / scale; - final float centerX = mMagnificationController.getMagnifiedRegionCenterX() + scrollX; - final float centerY = mMagnificationController.getMagnifiedRegionCenterY() + scrollY; if (DEBUG_PANNING) { - Slog.i(LOG_TAG, "Panned content by scrollX: " + scrollX - + " scrollY: " + scrollY); + Slog.i(LOG_TAG, "Panned content by scrollX: " + distanceX + + " scrollY: " + distanceY); } - mMagnificationController.setMagnifiedRegionCenter(centerX, centerY, false); + mMagnificationController.offsetMagnifiedRegionCenter(distanceX, distanceY); return true; } @@ -485,7 +619,7 @@ public final class ScreenMagnifier implements EventStreamTransformation { } final float eventX = event.getX(); final float eventY = event.getY(); - if (mViewport.getBounds().contains((int) eventX, (int) eventY)) { + if (mMagnifiedFrame.contains((int) eventX, (int) eventY)) { if (mLastMoveOutsideMagnifiedRegion) { mLastMoveOutsideMagnifiedRegion = false; mMagnificationController.setMagnifiedRegionCenter(eventX, @@ -501,7 +635,7 @@ public final class ScreenMagnifier implements EventStreamTransformation { case MotionEvent.ACTION_UP: { if (!mTranslationEnabledBeforePan) { mMagnificationController.reset(true); - mViewport.setFrameShown(false, true); + mViewportWindow.setShown(false, true); } clear(); transitionToState(STATE_DETECTING); @@ -559,7 +693,7 @@ public final class ScreenMagnifier implements EventStreamTransformation { switch (action) { case MotionEvent.ACTION_DOWN: { mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE); - if (!mViewport.getBounds().contains((int) event.getX(), + if (!mMagnifiedFrame.contains((int) event.getX(), (int) event.getY())) { transitionToDelegatingStateAndClear(); return; @@ -601,7 +735,7 @@ public final class ScreenMagnifier implements EventStreamTransformation { return; } mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD); - if (!mViewport.getBounds().contains((int) event.getX(), (int) event.getY())) { + if (!mMagnifiedFrame.contains((int) event.getX(), (int) event.getY())) { transitionToDelegatingStateAndClear(); return; } @@ -727,10 +861,10 @@ public final class ScreenMagnifier implements EventStreamTransformation { if (!mMagnificationController.isMagnifying()) { mMagnificationController.setScaleAndMagnifiedRegionCenter(getPersistedScale(), up.getX(), up.getY(), true); - mViewport.setFrameShown(true, true); + mViewportWindow.setShown(true, true); } else { mMagnificationController.reset(true); - mViewport.setFrameShown(false, true); + mViewportWindow.setShown(false, true); } } @@ -742,7 +876,7 @@ public final class ScreenMagnifier implements EventStreamTransformation { mTranslationEnabledBeforePan = mMagnificationController.isMagnifying(); mMagnificationController.setScaleAndMagnifiedRegionCenter(getPersistedScale(), down.getX(), down.getY(), true); - mViewport.setFrameShown(true, true); + mViewportWindow.setShown(true, true); transitionToState(STATE_VIEWPORT_DRAGGING); } } @@ -837,443 +971,35 @@ public final class ScreenMagnifier implements EventStreamTransformation { } } - private static final class ScreenStateObserver extends BroadcastReceiver { - - private static final int MESSAGE_ON_SCREEN_STATE_CHANGE = 1; - - private final Handler mHandler = new Handler() { - @Override - public void handleMessage(Message message) { - switch (message.what) { - case MESSAGE_ON_SCREEN_STATE_CHANGE: { - String action = (String) message.obj; - handleOnScreenStateChange(action); - } break; - } - } - }; - - private final Context mContext; - private final Viewport mViewport; - private final MagnificationController mMagnificationController; - - public ScreenStateObserver(Context context, Viewport viewport, - MagnificationController magnificationController) { - mContext = context; - mViewport = viewport; - mMagnificationController = magnificationController; - mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_SCREEN_OFF)); - } - - public void destroy() { - mContext.unregisterReceiver(this); - } - - @Override - public void onReceive(Context context, Intent intent) { - mHandler.obtainMessage(MESSAGE_ON_SCREEN_STATE_CHANGE, - intent.getAction()).sendToTarget(); - } - - private void handleOnScreenStateChange(String action) { - if (action.equals(Intent.ACTION_SCREEN_OFF) - && mMagnificationController.isMagnifying() - && isScreenMagnificationAutoUpdateEnabled(mContext)) { - mMagnificationController.reset(false); - mViewport.setFrameShown(false, false); - } - } - } - - private static final class DisplayContentObserver { - - private static final int MESSAGE_SHOW_VIEWPORT_FRAME = 1; - private static final int MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED = 3; - private static final int MESSAGE_ON_WINDOW_TRANSITION = 4; - private static final int MESSAGE_ON_ROTATION_CHANGED = 5; - private static final int MESSAGE_ON_WINDOW_LAYERS_CHANGED = 6; - - private final Handler mHandler = new MyHandler(); - - private final Rect mTempRect = new Rect(); - - private final IDisplayContentChangeListener mDisplayContentChangeListener; - - private final Context mContext; - private final Viewport mViewport; - private final MagnificationController mMagnificationController; - private final IWindowManager mWindowManagerService; - private final DisplayProvider mDisplayProvider; - private final long mLongAnimationDuration; - private final float mWindowAnimationScale; - - public DisplayContentObserver(Context context, Viewport viewport, - MagnificationController magnificationController, - IWindowManager windowManagerService, DisplayProvider displayProvider, - long longAnimationDuration, float windowAnimationScale) { - mContext = context; - mViewport = viewport; - mMagnificationController = magnificationController; - mWindowManagerService = windowManagerService; - mDisplayProvider = displayProvider; - mLongAnimationDuration = longAnimationDuration; - mWindowAnimationScale = windowAnimationScale; - - mDisplayContentChangeListener = new IDisplayContentChangeListener.Stub() { - @Override - public void onWindowTransition(int displayId, int transition, WindowInfo info) { - mHandler.obtainMessage(MESSAGE_ON_WINDOW_TRANSITION, - transition, 0, WindowInfo.obtain(info)).sendToTarget(); - } - - @Override - public void onRectangleOnScreenRequested(int dsiplayId, Rect rectangle, - boolean immediate) { - SomeArgs args = SomeArgs.obtain(); - args.argi1 = rectangle.left; - args.argi2 = rectangle.top; - args.argi3 = rectangle.right; - args.argi4 = rectangle.bottom; - mHandler.obtainMessage(MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED, 0, - immediate ? 1 : 0, args).sendToTarget(); - } - - @Override - public void onRotationChanged(int rotation) throws RemoteException { - mHandler.obtainMessage(MESSAGE_ON_ROTATION_CHANGED, rotation, 0) - .sendToTarget(); - } - - @Override - public void onWindowLayersChanged(int displayId) throws RemoteException { - mHandler.sendEmptyMessage(MESSAGE_ON_WINDOW_LAYERS_CHANGED); - } - }; - - try { - mWindowManagerService.addDisplayContentChangeListener( - mDisplayProvider.getDisplay().getDisplayId(), - mDisplayContentChangeListener); - } catch (RemoteException re) { - /* ignore */ - } - } - - public void destroy() { - try { - mWindowManagerService.removeDisplayContentChangeListener( - mDisplayProvider.getDisplay().getDisplayId(), - mDisplayContentChangeListener); - } catch (RemoteException re) { - /* ignore*/ - } - } - - private void handleOnRotationChanged(int rotation) { - if (DEBUG_ROTATION) { - Slog.i(LOG_TAG, "Rotation: " + rotationToString(rotation)); - } - resetMagnificationIfNeeded(); - mViewport.setFrameShown(false, false); - mViewport.rotationChanged(); - mViewport.recomputeBounds(false); - if (mMagnificationController.isMagnifying()) { - final long delay = (long) (2 * mLongAnimationDuration * mWindowAnimationScale); - Message message = mHandler.obtainMessage(MESSAGE_SHOW_VIEWPORT_FRAME); - mHandler.sendMessageDelayed(message, delay); - } - } - - private void handleOnWindowTransition(int transition, WindowInfo info) { - if (DEBUG_WINDOW_TRANSITIONS) { - Slog.i(LOG_TAG, "Window transitioning: " - + windowTransitionToString(transition)); - } - try { - final boolean magnifying = mMagnificationController.isMagnifying(); - if (magnifying) { - switch (transition) { - case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN: - case WindowManagerPolicy.TRANSIT_TASK_OPEN: - case WindowManagerPolicy.TRANSIT_TASK_TO_FRONT: - case WindowManagerPolicy.TRANSIT_WALLPAPER_OPEN: - case WindowManagerPolicy.TRANSIT_WALLPAPER_CLOSE: - case WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_OPEN: { - resetMagnificationIfNeeded(); - } - } - } - if (info.type == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR - || info.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD - || info.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG - || info.type == WindowManager.LayoutParams.TYPE_KEYGUARD - || info.type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) { - switch (transition) { - case WindowManagerPolicy.TRANSIT_ENTER: - case WindowManagerPolicy.TRANSIT_SHOW: - case WindowManagerPolicy.TRANSIT_EXIT: - case WindowManagerPolicy.TRANSIT_HIDE: { - mViewport.recomputeBounds(mMagnificationController.isMagnifying()); - } break; - } - } - switch (transition) { - case WindowManagerPolicy.TRANSIT_ENTER: - case WindowManagerPolicy.TRANSIT_SHOW: { - if (!magnifying || !isScreenMagnificationAutoUpdateEnabled(mContext)) { - break; - } - final int type = info.type; - switch (type) { - // TODO: Are these all the windows we want to make - // visible when they appear on the screen? - // Do we need to take some of them out? - case WindowManager.LayoutParams.TYPE_APPLICATION: - case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL: - case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA: - case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL: - case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG: - case WindowManager.LayoutParams.TYPE_SEARCH_BAR: - case WindowManager.LayoutParams.TYPE_PHONE: - case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT: - case WindowManager.LayoutParams.TYPE_TOAST: - case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: - case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE: - case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG: - case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG: - case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR: - case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY: - case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: - case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY: { - Rect magnifiedRegionBounds = mMagnificationController - .getMagnifiedRegionBounds(); - Rect touchableRegion = info.touchableRegion; - if (!magnifiedRegionBounds.intersect(touchableRegion)) { - ensureRectangleInMagnifiedRegionBounds( - magnifiedRegionBounds, touchableRegion); - } - } break; - } break; - } - } - } finally { - if (info != null) { - info.recycle(); - } - } - } - - private void handleOnRectangleOnScreenRequested(Rect rectangle, boolean immediate) { - if (!mMagnificationController.isMagnifying()) { - return; - } - Rect magnifiedRegionBounds = mMagnificationController.getMagnifiedRegionBounds(); - if (magnifiedRegionBounds.contains(rectangle)) { - return; - } - ensureRectangleInMagnifiedRegionBounds(magnifiedRegionBounds, rectangle); - } - - private void ensureRectangleInMagnifiedRegionBounds(Rect magnifiedRegionBounds, - Rect rectangle) { - if (!Rect.intersects(rectangle, mViewport.getBounds())) { - return; - } - final float scrollX; - final float scrollY; - if (rectangle.width() > magnifiedRegionBounds.width()) { - final int direction = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()); - if (direction == View.LAYOUT_DIRECTION_LTR) { - scrollX = rectangle.left - magnifiedRegionBounds.left; - } else { - scrollX = rectangle.right - magnifiedRegionBounds.right; - } - } else if (rectangle.left < magnifiedRegionBounds.left) { - scrollX = rectangle.left - magnifiedRegionBounds.left; - } else if (rectangle.right > magnifiedRegionBounds.right) { - scrollX = rectangle.right - magnifiedRegionBounds.right; - } else { - scrollX = 0; - } - if (rectangle.height() > magnifiedRegionBounds.height()) { - scrollY = rectangle.top - magnifiedRegionBounds.top; - } else if (rectangle.top < magnifiedRegionBounds.top) { - scrollY = rectangle.top - magnifiedRegionBounds.top; - } else if (rectangle.bottom > magnifiedRegionBounds.bottom) { - scrollY = rectangle.bottom - magnifiedRegionBounds.bottom; - } else { - scrollY = 0; - } - final float viewportCenterX = mMagnificationController.getMagnifiedRegionCenterX() - + scrollX; - final float viewportCenterY = mMagnificationController.getMagnifiedRegionCenterY() - + scrollY; - mMagnificationController.setMagnifiedRegionCenter(viewportCenterX, viewportCenterY, - true); - } - - private void resetMagnificationIfNeeded() { - if (mMagnificationController.isMagnifying() - && isScreenMagnificationAutoUpdateEnabled(mContext)) { - mMagnificationController.reset(true); - mViewport.setFrameShown(false, true); - } - } - - private String windowTransitionToString(int transition) { - switch (transition) { - case WindowManagerPolicy.TRANSIT_UNSET: { - return "TRANSIT_UNSET"; - } - case WindowManagerPolicy.TRANSIT_NONE: { - return "TRANSIT_NONE"; - } - case WindowManagerPolicy.TRANSIT_ENTER: { - return "TRANSIT_ENTER"; - } - case WindowManagerPolicy.TRANSIT_EXIT: { - return "TRANSIT_EXIT"; - } - case WindowManagerPolicy.TRANSIT_SHOW: { - return "TRANSIT_SHOW"; - } - case WindowManagerPolicy.TRANSIT_EXIT_MASK: { - return "TRANSIT_EXIT_MASK"; - } - case WindowManagerPolicy.TRANSIT_PREVIEW_DONE: { - return "TRANSIT_PREVIEW_DONE"; - } - case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN: { - return "TRANSIT_ACTIVITY_OPEN"; - } - case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE: { - return "TRANSIT_ACTIVITY_CLOSE"; - } - case WindowManagerPolicy.TRANSIT_TASK_OPEN: { - return "TRANSIT_TASK_OPEN"; - } - case WindowManagerPolicy.TRANSIT_TASK_CLOSE: { - return "TRANSIT_TASK_CLOSE"; - } - case WindowManagerPolicy.TRANSIT_TASK_TO_FRONT: { - return "TRANSIT_TASK_TO_FRONT"; - } - case WindowManagerPolicy.TRANSIT_TASK_TO_BACK: { - return "TRANSIT_TASK_TO_BACK"; - } - case WindowManagerPolicy.TRANSIT_WALLPAPER_CLOSE: { - return "TRANSIT_WALLPAPER_CLOSE"; - } - case WindowManagerPolicy.TRANSIT_WALLPAPER_OPEN: { - return "TRANSIT_WALLPAPER_OPEN"; - } - case WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_OPEN: { - return "TRANSIT_WALLPAPER_INTRA_OPEN"; - } - case WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_CLOSE: { - return "TRANSIT_WALLPAPER_INTRA_CLOSE"; - } - default: { - return ""; - } - } - } - - private String rotationToString(int rotation) { - switch (rotation) { - case Surface.ROTATION_0: { - return "ROTATION_0"; - } - case Surface.ROTATION_90: { - return "ROATATION_90"; - } - case Surface.ROTATION_180: { - return "ROATATION_180"; - } - case Surface.ROTATION_270: { - return "ROATATION_270"; - } - default: { - throw new IllegalArgumentException("Invalid rotation: " - + rotation); - } - } - } - - private final class MyHandler extends Handler { - @Override - public void handleMessage(Message message) { - final int action = message.what; - switch (action) { - case MESSAGE_SHOW_VIEWPORT_FRAME: { - mViewport.setFrameShown(true, true); - } break; - case MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED: { - SomeArgs args = (SomeArgs) message.obj; - try { - mTempRect.set(args.argi1, args.argi2, args.argi3, args.argi4); - final boolean immediate = (message.arg1 == 1); - handleOnRectangleOnScreenRequested(mTempRect, immediate); - } finally { - args.recycle(); - } - } break; - case MESSAGE_ON_WINDOW_TRANSITION: { - final int transition = message.arg1; - WindowInfo info = (WindowInfo) message.obj; - handleOnWindowTransition(transition, info); - } break; - case MESSAGE_ON_ROTATION_CHANGED: { - final int rotation = message.arg1; - handleOnRotationChanged(rotation); - } break; - case MESSAGE_ON_WINDOW_LAYERS_CHANGED: { - mViewport.recomputeBounds(mMagnificationController.isMagnifying()); - } break; - default: { - throw new IllegalArgumentException("Unknown message: " + action); - } - } - } - } - } - private final class MagnificationController { - private static final String PROPERTY_NAME_ACCESSIBILITY_TRANSFORMATION = - "accessibilityTransformation"; + private static final String PROPERTY_NAME_MAGNIFICATION_SPEC = + "magnificationSpec"; - private final MagnificationSpec mSentMagnificationSpec = new MagnificationSpec(); + private final MagnificationSpec mSentMagnificationSpec = MagnificationSpec.obtain(); - private final MagnificationSpec mCurrentMagnificationSpec = new MagnificationSpec(); + private final MagnificationSpec mCurrentMagnificationSpec = MagnificationSpec.obtain(); private final Rect mTempRect = new Rect(); private final ValueAnimator mTransformationAnimator; - public MagnificationController(int animationDuration) { + public MagnificationController(long animationDuration) { Property property = Property.of(MagnificationController.class, MagnificationSpec.class, - PROPERTY_NAME_ACCESSIBILITY_TRANSFORMATION); + PROPERTY_NAME_MAGNIFICATION_SPEC); TypeEvaluator evaluator = new TypeEvaluator() { - private final MagnificationSpec mTempTransformationSpec = new MagnificationSpec(); + private final MagnificationSpec mTempTransformationSpec = + MagnificationSpec.obtain(); @Override public MagnificationSpec evaluate(float fraction, MagnificationSpec fromSpec, MagnificationSpec toSpec) { MagnificationSpec result = mTempTransformationSpec; - result.mScale = fromSpec.mScale - + (toSpec.mScale - fromSpec.mScale) * fraction; - result.mMagnifiedRegionCenterX = fromSpec.mMagnifiedRegionCenterX - + (toSpec.mMagnifiedRegionCenterX - fromSpec.mMagnifiedRegionCenterX) - * fraction; - result.mMagnifiedRegionCenterY = fromSpec.mMagnifiedRegionCenterY - + (toSpec.mMagnifiedRegionCenterY - fromSpec.mMagnifiedRegionCenterY) - * fraction; - result.mScaledOffsetX = fromSpec.mScaledOffsetX - + (toSpec.mScaledOffsetX - fromSpec.mScaledOffsetX) + result.scale = fromSpec.scale + + (toSpec.scale - fromSpec.scale) * fraction; + result.offsetX = fromSpec.offsetX + (toSpec.offsetX - fromSpec.offsetX) * fraction; - result.mScaledOffsetY = fromSpec.mScaledOffsetY - + (toSpec.mScaledOffsetY - fromSpec.mScaledOffsetY) + result.offsetY = fromSpec.offsetY + (toSpec.offsetY - fromSpec.offsetY) * fraction; return result; } @@ -1281,61 +1007,48 @@ public final class ScreenMagnifier implements EventStreamTransformation { mTransformationAnimator = ObjectAnimator.ofObject(this, property, evaluator, mSentMagnificationSpec, mCurrentMagnificationSpec); mTransformationAnimator.setDuration((long) (animationDuration)); - mTransformationAnimator.setInterpolator(mInterpolator); + mTransformationAnimator.setInterpolator(new DecelerateInterpolator(2.5f)); } public boolean isMagnifying() { - return mCurrentMagnificationSpec.mScale > 1.0f; + return mCurrentMagnificationSpec.scale > 1.0f; } public void reset(boolean animate) { if (mTransformationAnimator.isRunning()) { mTransformationAnimator.cancel(); } - mCurrentMagnificationSpec.reset(); + mCurrentMagnificationSpec.clear(); if (animate) { - animateAccessibilityTranformation(mSentMagnificationSpec, + animateMangificationSpec(mSentMagnificationSpec, mCurrentMagnificationSpec); } else { - setAccessibilityTransformation(mCurrentMagnificationSpec); + setMagnificationSpec(mCurrentMagnificationSpec); } - } - - public Rect getMagnifiedRegionBounds() { - mTempRect.set(mViewport.getBounds()); - mTempRect.offset((int) -mCurrentMagnificationSpec.mScaledOffsetX, - (int) -mCurrentMagnificationSpec.mScaledOffsetY); - mTempRect.scale(1.0f / mCurrentMagnificationSpec.mScale); - return mTempRect; + Rect bounds = mTempRect; + bounds.setEmpty(); + mAms.onMagnificationStateChanged(); } public float getScale() { - return mCurrentMagnificationSpec.mScale; - } - - public float getMagnifiedRegionCenterX() { - return mCurrentMagnificationSpec.mMagnifiedRegionCenterX; - } - - public float getMagnifiedRegionCenterY() { - return mCurrentMagnificationSpec.mMagnifiedRegionCenterY; + return mCurrentMagnificationSpec.scale; } - public float getScaledOffsetX() { - return mCurrentMagnificationSpec.mScaledOffsetX; + public float getOffsetX() { + return mCurrentMagnificationSpec.offsetX; } - public float getScaledOffsetY() { - return mCurrentMagnificationSpec.mScaledOffsetY; + public float getOffsetY() { + return mCurrentMagnificationSpec.offsetY; } public void setScale(float scale, float pivotX, float pivotY, boolean animate) { MagnificationSpec spec = mCurrentMagnificationSpec; - final float oldScale = spec.mScale; - final float oldCenterX = spec.mMagnifiedRegionCenterX; - final float oldCenterY = spec.mMagnifiedRegionCenterY; - final float normPivotX = (-spec.mScaledOffsetX + pivotX) / oldScale; - final float normPivotY = (-spec.mScaledOffsetY + pivotY) / oldScale; + final float oldScale = spec.scale; + final float oldCenterX = (-spec.offsetX + mMagnifiedFrame.width() / 2) / oldScale; + final float oldCenterY = (-spec.offsetY + mMagnifiedFrame.height() / 2) / oldScale; + final float normPivotX = (-spec.offsetX + pivotX) / oldScale; + final float normPivotY = (-spec.offsetY + pivotY) / oldScale; final float offsetX = (oldCenterX - normPivotX) * (oldScale / scale); final float offsetY = (oldCenterY - normPivotY) * (oldScale / scale); final float centerX = normPivotX + offsetX; @@ -1344,16 +1057,26 @@ public final class ScreenMagnifier implements EventStreamTransformation { } public void setMagnifiedRegionCenter(float centerX, float centerY, boolean animate) { - setScaleAndMagnifiedRegionCenter(mCurrentMagnificationSpec.mScale, centerX, centerY, + setScaleAndMagnifiedRegionCenter(mCurrentMagnificationSpec.scale, centerX, centerY, animate); } + public void offsetMagnifiedRegionCenter(float offsetX, float offsetY) { + final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX; + mCurrentMagnificationSpec.offsetX = Math.min(Math.max(nonNormOffsetX, + getMinOffsetX()), 0); + final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY; + mCurrentMagnificationSpec.offsetY = Math.min(Math.max(nonNormOffsetY, + getMinOffsetY()), 0); + setMagnificationSpec(mCurrentMagnificationSpec); + } + public void setScaleAndMagnifiedRegionCenter(float scale, float centerX, float centerY, boolean animate) { - if (Float.compare(mCurrentMagnificationSpec.mScale, scale) == 0 - && Float.compare(mCurrentMagnificationSpec.mMagnifiedRegionCenterX, + if (Float.compare(mCurrentMagnificationSpec.scale, scale) == 0 + && Float.compare(mCurrentMagnificationSpec.offsetX, centerX) == 0 - && Float.compare(mCurrentMagnificationSpec.mMagnifiedRegionCenterY, + && Float.compare(mCurrentMagnificationSpec.offsetY, centerY) == 0) { return; } @@ -1361,149 +1084,192 @@ public final class ScreenMagnifier implements EventStreamTransformation { mTransformationAnimator.cancel(); } if (DEBUG_MAGNIFICATION_CONTROLLER) { - Slog.i(LOG_TAG, "scale: " + scale + " centerX: " + centerX - + " centerY: " + centerY); + Slog.i(LOG_TAG, "scale: " + scale + " offsetX: " + centerX + + " offsetY: " + centerY); } - mCurrentMagnificationSpec.initialize(scale, centerX, centerY); + updateMagnificationSpec(scale, centerX, centerY); if (animate) { - animateAccessibilityTranformation(mSentMagnificationSpec, + animateMangificationSpec(mSentMagnificationSpec, mCurrentMagnificationSpec); } else { - setAccessibilityTransformation(mCurrentMagnificationSpec); + setMagnificationSpec(mCurrentMagnificationSpec); } + mAms.onMagnificationStateChanged(); + } + + public void updateMagnificationSpec(float scale, float magnifiedCenterX, + float magnifiedCenterY) { + mCurrentMagnificationSpec.scale = scale; + final int viewportWidth = mMagnifiedFrame.width(); + final float nonNormOffsetX = viewportWidth / 2 - magnifiedCenterX * scale; + mCurrentMagnificationSpec.offsetX = Math.min(Math.max(nonNormOffsetX, + getMinOffsetX()), 0); + final int viewportHeight = mMagnifiedFrame.height(); + final float nonNormOffsetY = viewportHeight / 2 - magnifiedCenterY * scale; + mCurrentMagnificationSpec.offsetY = Math.min(Math.max(nonNormOffsetY, + getMinOffsetY()), 0); + } + + private float getMinOffsetX() { + final float viewportWidth = mMagnifiedFrame.width(); + return viewportWidth - viewportWidth * mCurrentMagnificationSpec.scale; } - private void animateAccessibilityTranformation(MagnificationSpec fromSpec, + private float getMinOffsetY() { + final float viewportHeight = mMagnifiedFrame.height(); + return viewportHeight - viewportHeight * mCurrentMagnificationSpec.scale; + } + + private void animateMangificationSpec(MagnificationSpec fromSpec, MagnificationSpec toSpec) { mTransformationAnimator.setObjectValues(fromSpec, toSpec); mTransformationAnimator.start(); } - @SuppressWarnings("unused") - // Called from an animator. - public MagnificationSpec getAccessibilityTransformation() { + public MagnificationSpec getMagnificationSpec() { return mSentMagnificationSpec; } - public void setAccessibilityTransformation(MagnificationSpec transformation) { - if (DEBUG_TRANSFORMATION) { - Slog.i(LOG_TAG, "Transformation scale: " + transformation.mScale - + " offsetX: " + transformation.mScaledOffsetX - + " offsetY: " + transformation.mScaledOffsetY); + public void setMagnificationSpec(MagnificationSpec spec) { + if (DEBUG_SET_MAGNIFICATION_SPEC) { + Slog.i(LOG_TAG, "Sending: " + spec); } try { - mSentMagnificationSpec.updateFrom(transformation); - mWindowManagerService.magnifyDisplay(mDisplayProvider.getDisplay().getDisplayId(), - transformation.mScale, transformation.mScaledOffsetX, - transformation.mScaledOffsetY); + mSentMagnificationSpec.scale = spec.scale; + mSentMagnificationSpec.offsetX = spec.offsetX; + mSentMagnificationSpec.offsetY = spec.offsetY; + mDisplayMagnifier.setMagnificationSpec(ScreenMagnifier.this, + MagnificationSpec.obtain(spec)); } catch (RemoteException re) { /* ignore */ } } + } - private class MagnificationSpec { - - private static final float DEFAULT_SCALE = 1.0f; - - public float mScale = DEFAULT_SCALE; - - public float mMagnifiedRegionCenterX; - - public float mMagnifiedRegionCenterY; - - public float mScaledOffsetX; - - public float mScaledOffsetY; + private static final class ScreenStateObserver extends BroadcastReceiver { + private static final int MESSAGE_ON_SCREEN_STATE_CHANGE = 1; - public void initialize(float scale, float magnifiedRegionCenterX, - float magnifiedRegionCenterY) { - mScale = scale; + private final Context mContext; + private final MagnificationController mMagnificationController; + private final ViewportWindow mViewportWindow; - final int viewportWidth = mViewport.getBounds().width(); - final int viewportHeight = mViewport.getBounds().height(); - final float minMagnifiedRegionCenterX = (viewportWidth / 2) / scale; - final float minMagnifiedRegionCenterY = (viewportHeight / 2) / scale; - final float maxMagnifiedRegionCenterX = viewportWidth - minMagnifiedRegionCenterX; - final float maxMagnifiedRegionCenterY = viewportHeight - minMagnifiedRegionCenterY; + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message message) { + switch (message.what) { + case MESSAGE_ON_SCREEN_STATE_CHANGE: { + String action = (String) message.obj; + handleOnScreenStateChange(action); + } break; + } + } + }; - mMagnifiedRegionCenterX = Math.min(Math.max(magnifiedRegionCenterX, - minMagnifiedRegionCenterX), maxMagnifiedRegionCenterX); - mMagnifiedRegionCenterY = Math.min(Math.max(magnifiedRegionCenterY, - minMagnifiedRegionCenterY), maxMagnifiedRegionCenterY); + public ScreenStateObserver(Context context, + MagnificationController magnificationController, ViewportWindow viewportWindow) { + mContext = context; + mMagnificationController = magnificationController; + mViewportWindow = viewportWindow; + mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_SCREEN_OFF)); + } - mScaledOffsetX = -(mMagnifiedRegionCenterX * scale - viewportWidth / 2); - mScaledOffsetY = -(mMagnifiedRegionCenterY * scale - viewportHeight / 2); - } + public void destroy() { + mContext.unregisterReceiver(this); + } - public void updateFrom(MagnificationSpec other) { - mScale = other.mScale; - mMagnifiedRegionCenterX = other.mMagnifiedRegionCenterX; - mMagnifiedRegionCenterY = other.mMagnifiedRegionCenterY; - mScaledOffsetX = other.mScaledOffsetX; - mScaledOffsetY = other.mScaledOffsetY; + @Override + public void onReceive(Context context, Intent intent) { + mHandler.obtainMessage(MESSAGE_ON_SCREEN_STATE_CHANGE, + intent.getAction()).sendToTarget(); } - public void reset() { - mScale = DEFAULT_SCALE; - mMagnifiedRegionCenterX = 0; - mMagnifiedRegionCenterY = 0; - mScaledOffsetX = 0; - mScaledOffsetY = 0; + private void handleOnScreenStateChange(String action) { + if (mMagnificationController.isMagnifying() + && isScreenMagnificationAutoUpdateEnabled(mContext)) { + mMagnificationController.reset(false); + mViewportWindow.setShown(false, false); } } } - private static final class Viewport { + private static final class ViewportWindow implements DisplayListener { + private static final String WINDOW_TITLE = "Magnification Overlay"; private static final String PROPERTY_NAME_ALPHA = "alpha"; - private static final String PROPERTY_NAME_BOUNDS = "bounds"; private static final int MIN_ALPHA = 0; - private static final int MAX_ALPHA = 255; - private final ArrayList mTempWindowInfoList = new ArrayList(); + private final Rect mBounds = new Rect(); + + private final ValueAnimator mResizeFrameAnimator; + private final ValueAnimator mShowHideFrameAnimator; - private final Rect mTempRect1 = new Rect(); - private final Rect mTempRect2 = new Rect(); - private final Rect mTempRect3 = new Rect(); + private final WindowManager mWindowManager; + private final DisplayManager mDisplayManager; + private final Display mDisplay; + private final DisplayInfo mDisplayInfo = new DisplayInfo(); + private final int mDisplayId; - private final IWindowManager mWindowManagerService; - private final DisplayProvider mDisplayProvider; + private final ContentView mWindowContent; + private final WindowManager.LayoutParams mWindowParams; - private final ViewportWindow mViewportFrame; + private boolean mShown; + private int mAlpha; - private final ValueAnimator mResizeFrameAnimator; + public ViewportWindow(Context context, int displayId) { + mWindowManager = (WindowManager) context.getSystemService(Service.WINDOW_SERVICE); - private final ValueAnimator mShowHideFrameAnimator; + mDisplayId = displayId; + mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE); + mDisplay = mDisplayManager.getDisplay(displayId); + mDisplayManager.registerDisplayListener(this, null); + updateDisplayInfo(); - public Viewport(Context context, WindowManager windowManager, - IWindowManager windowManagerService, DisplayProvider displayInfoProvider, - Interpolator animationInterpolator, long animationDuration) { - mWindowManagerService = windowManagerService; - mDisplayProvider = displayInfoProvider; - mViewportFrame = new ViewportWindow(context, windowManager, displayInfoProvider); - - mShowHideFrameAnimator = ObjectAnimator.ofInt(mViewportFrame, PROPERTY_NAME_ALPHA, - MIN_ALPHA, MAX_ALPHA); - mShowHideFrameAnimator.setInterpolator(animationInterpolator); - mShowHideFrameAnimator.setDuration(animationDuration); + ViewGroup.LayoutParams contentParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + mWindowContent = new ContentView(context); + mWindowContent.setLayoutParams(contentParams); + mWindowContent.setBackgroundColor(R.color.transparent); + + mWindowParams = new WindowManager.LayoutParams( + WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY); + mWindowParams.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + mWindowParams.setTitle(WINDOW_TITLE); + mWindowParams.gravity = Gravity.CENTER; + mWindowParams.width = mDisplayInfo.logicalWidth; + mWindowParams.height = mDisplayInfo.logicalHeight; + mWindowParams.format = PixelFormat.TRANSLUCENT; + + Interpolator interpolator = new DecelerateInterpolator(2.5f); + final long longAnimationDuration = context.getResources().getInteger( + com.android.internal.R.integer.config_longAnimTime); + + mShowHideFrameAnimator = ObjectAnimator.ofInt(this, PROPERTY_NAME_ALPHA, + MIN_ALPHA, MAX_ALPHA); + mShowHideFrameAnimator.setInterpolator(interpolator); + mShowHideFrameAnimator.setDuration(longAnimationDuration); mShowHideFrameAnimator.addListener(new AnimatorListener() { @Override public void onAnimationEnd(Animator animation) { if (mShowHideFrameAnimator.getAnimatedValue().equals(MIN_ALPHA)) { - mViewportFrame.hide(); + hide(); } } + @Override public void onAnimationStart(Animator animation) { /* do nothing - stub */ } + @Override public void onAnimationCancel(Animator animation) { /* do nothing - stub */ } + @Override public void onAnimationRepeat(Animator animation) { /* do nothing - stub */ @@ -1514,6 +1280,7 @@ public final class ScreenMagnifier implements EventStreamTransformation { Rect.class, PROPERTY_NAME_BOUNDS); TypeEvaluator evaluator = new TypeEvaluator() { private final Rect mReusableResultRect = new Rect(); + @Override public Rect evaluate(float fraction, Rect fromFrame, Rect toFrame) { Rect result = mReusableResultRect; @@ -1528,103 +1295,18 @@ public final class ScreenMagnifier implements EventStreamTransformation { return result; } }; - mResizeFrameAnimator = ObjectAnimator.ofObject(mViewportFrame, property, - evaluator, mViewportFrame.mBounds, mViewportFrame.mBounds); - mResizeFrameAnimator.setDuration((long) (animationDuration)); - mResizeFrameAnimator.setInterpolator(animationInterpolator); - - recomputeBounds(false); + mResizeFrameAnimator = ObjectAnimator.ofObject(this, property, + evaluator, mBounds, mBounds); + mResizeFrameAnimator.setDuration(longAnimationDuration); + mResizeFrameAnimator.setInterpolator(interpolator); } - private final Comparator mWindowInfoInverseComparator = - new Comparator() { - @Override - public int compare(WindowInfo lhs, WindowInfo rhs) { - if (lhs.layer != rhs.layer) { - return rhs.layer - lhs.layer; - } - if (lhs.touchableRegion.top != rhs.touchableRegion.top) { - return rhs.touchableRegion.top - lhs.touchableRegion.top; - } - if (lhs.touchableRegion.left != rhs.touchableRegion.left) { - return rhs.touchableRegion.left - lhs.touchableRegion.left; - } - if (lhs.touchableRegion.right != rhs.touchableRegion.right) { - return rhs.touchableRegion.right - lhs.touchableRegion.right; - } - if (lhs.touchableRegion.bottom != rhs.touchableRegion.bottom) { - return rhs.touchableRegion.bottom - lhs.touchableRegion.bottom; - } - return 0; - } - }; - - public void recomputeBounds(boolean animate) { - Rect magnifiedFrame = mTempRect1; - magnifiedFrame.set(0, 0, 0, 0); - - DisplayInfo displayInfo = mDisplayProvider.getDisplayInfo(); - - Rect availableFrame = mTempRect2; - availableFrame.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight); - - ArrayList infos = mTempWindowInfoList; - infos.clear(); - int windowCount = 0; - try { - mWindowManagerService.getVisibleWindowsForDisplay( - mDisplayProvider.getDisplay().getDisplayId(), infos); - Collections.sort(infos, mWindowInfoInverseComparator); - windowCount = infos.size(); - for (int i = 0; i < windowCount; i++) { - WindowInfo info = infos.get(i); - if (info.type == WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY) { - continue; - } - Rect windowFrame = mTempRect3; - windowFrame.set(info.touchableRegion); - if (isWindowMagnified(info.type)) { - magnifiedFrame.union(windowFrame); - magnifiedFrame.intersect(availableFrame); - } else { - subtract(windowFrame, magnifiedFrame); - subtract(availableFrame, windowFrame); - } - if (availableFrame.equals(magnifiedFrame)) { - break; - } - } - } catch (RemoteException re) { - /* ignore */ - } finally { - for (int i = windowCount - 1; i >= 0; i--) { - infos.remove(i).recycle(); - } - } - - final int displayWidth = mDisplayProvider.getDisplayInfo().logicalWidth; - final int displayHeight = mDisplayProvider.getDisplayInfo().logicalHeight; - magnifiedFrame.intersect(0, 0, displayWidth, displayHeight); - - resize(magnifiedFrame, animate); + public boolean isShown() { + return mShown; } - private boolean isWindowMagnified(int type) { - return (type != WindowManager.LayoutParams.TYPE_NAVIGATION_BAR - && type != WindowManager.LayoutParams.TYPE_INPUT_METHOD - && type != WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG); - } - - public void rotationChanged() { - mViewportFrame.rotationChanged(); - } - - public Rect getBounds() { - return mViewportFrame.getBounds(); - } - - public void setFrameShown(boolean shown, boolean animate) { - if (mViewportFrame.isShown() == shown) { + public void setShown(boolean shown, boolean animate) { + if (isShown() == shown) { return; } if (animate) { @@ -1632,7 +1314,7 @@ public final class ScreenMagnifier implements EventStreamTransformation { mShowHideFrameAnimator.reverse(); } else { if (shown) { - mViewportFrame.show(); + show(); mShowHideFrameAnimator.start(); } else { mShowHideFrameAnimator.reverse(); @@ -1641,221 +1323,140 @@ public final class ScreenMagnifier implements EventStreamTransformation { } else { mShowHideFrameAnimator.cancel(); if (shown) { - mViewportFrame.show(); + show(); } else { - mViewportFrame.hide(); + hide(); } } } - private void resize(Rect bounds, boolean animate) { - if (mViewportFrame.getBounds().equals(bounds)) { + private void show() { + if (mShown) { + return; + } + mShown = true; + mWindowManager.addView(mWindowContent, mWindowParams); + if (DEBUG_VIEWPORT_WINDOW) { + Slog.i(LOG_TAG, "ViewportWindow shown."); + } + } + + public void setBounds(Rect bounds, boolean animate) { + if (getBounds().equals(bounds)) { return; } if (animate) { if (mResizeFrameAnimator.isRunning()) { mResizeFrameAnimator.cancel(); } - mResizeFrameAnimator.setObjectValues(mViewportFrame.mBounds, bounds); + mResizeFrameAnimator.setObjectValues(getBounds(), bounds); mResizeFrameAnimator.start(); } else { - mViewportFrame.setBounds(bounds); + setBounds(bounds); } } - private boolean subtract(Rect lhs, Rect rhs) { - if (lhs.right < rhs.left || lhs.left > rhs.right - || lhs.bottom < rhs.top || lhs.top > rhs.bottom) { - return false; - } - if (lhs.left < rhs.left) { - lhs.right = rhs.left; - } - if (lhs.top < rhs.top) { - lhs.bottom = rhs.top; - } - if (lhs.right > rhs.right) { - lhs.left = rhs.right; + private void hide() { + if (!mShown) { + return; } - if (lhs.bottom > rhs.bottom) { - lhs.top = rhs.bottom; + mShown = false; + mWindowManager.removeView(mWindowContent); + if (DEBUG_VIEWPORT_WINDOW) { + Slog.i(LOG_TAG, "ViewportWindow hidden."); } - return true; } - private static final class ViewportWindow { - private static final String WINDOW_TITLE = "Magnification Overlay"; - - private final WindowManager mWindowManager; - private final DisplayProvider mDisplayProvider; - - private final ContentView mWindowContent; - private final WindowManager.LayoutParams mWindowParams; - - private final Rect mBounds = new Rect(); - private boolean mShown; - private int mAlpha; - - public ViewportWindow(Context context, WindowManager windowManager, - DisplayProvider displayProvider) { - mWindowManager = windowManager; - mDisplayProvider = displayProvider; - - ViewGroup.LayoutParams contentParams = new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); - mWindowContent = new ContentView(context); - mWindowContent.setLayoutParams(contentParams); - mWindowContent.setBackgroundColor(R.color.transparent); - - mWindowParams = new WindowManager.LayoutParams( - WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY); - mWindowParams.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; - mWindowParams.setTitle(WINDOW_TITLE); - mWindowParams.gravity = Gravity.CENTER; - mWindowParams.width = displayProvider.getDisplayInfo().logicalWidth; - mWindowParams.height = displayProvider.getDisplayInfo().logicalHeight; - mWindowParams.format = PixelFormat.TRANSLUCENT; - } - - public boolean isShown() { - return mShown; - } + @SuppressWarnings("unused") + // Called reflectively from an animator. + public int getAlpha() { + return mAlpha; + } - public void show() { - if (mShown) { - return; - } - mShown = true; - mWindowManager.addView(mWindowContent, mWindowParams); - if (DEBUG_VIEWPORT_WINDOW) { - Slog.i(LOG_TAG, "ViewportWindow shown."); - } + @SuppressWarnings("unused") + // Called reflectively from an animator. + public void setAlpha(int alpha) { + if (mAlpha == alpha) { + return; } - - public void hide() { - if (!mShown) { - return; - } - mShown = false; - mWindowManager.removeView(mWindowContent); - if (DEBUG_VIEWPORT_WINDOW) { - Slog.i(LOG_TAG, "ViewportWindow hidden."); - } + mAlpha = alpha; + if (mShown) { + mWindowContent.invalidate(); } - - @SuppressWarnings("unused") - // Called reflectively from an animator. - public int getAlpha() { - return mAlpha; + if (DEBUG_VIEWPORT_WINDOW) { + Slog.i(LOG_TAG, "ViewportFrame set alpha: " + alpha); } + } - @SuppressWarnings("unused") - // Called reflectively from an animator. - public void setAlpha(int alpha) { - if (mAlpha == alpha) { - return; - } - mAlpha = alpha; - if (mShown) { - mWindowContent.invalidate(); - } - if (DEBUG_VIEWPORT_WINDOW) { - Slog.i(LOG_TAG, "ViewportFrame set alpha: " + alpha); - } - } + public Rect getBounds() { + return mBounds; + } - public Rect getBounds() { - return mBounds; + public void setBounds(Rect bounds) { + if (mBounds.equals(bounds)) { + return; } - - public void rotationChanged() { - mWindowParams.width = mDisplayProvider.getDisplayInfo().logicalWidth; - mWindowParams.height = mDisplayProvider.getDisplayInfo().logicalHeight; - if (mShown) { - mWindowManager.updateViewLayout(mWindowContent, mWindowParams); - } + mBounds.set(bounds); + if (mShown) { + mWindowContent.invalidate(); } - - public void setBounds(Rect bounds) { - if (mBounds.equals(bounds)) { - return; - } - mBounds.set(bounds); - if (mShown) { - mWindowContent.invalidate(); - } - if (DEBUG_VIEWPORT_WINDOW) { - Slog.i(LOG_TAG, "ViewportFrame set bounds: " + bounds); - } + if (DEBUG_VIEWPORT_WINDOW) { + Slog.i(LOG_TAG, "ViewportFrame set bounds: " + bounds); } + } - private final class ContentView extends View { - private final Drawable mHighlightFrame; - - public ContentView(Context context) { - super(context); - mHighlightFrame = context.getResources().getDrawable( - R.drawable.magnified_region_frame); - } - - @Override - public void onDraw(Canvas canvas) { - canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR); - mHighlightFrame.setBounds(mBounds); - mHighlightFrame.setAlpha(mAlpha); - mHighlightFrame.draw(canvas); - } + public void rotationChanged() { + mWindowParams.width = mDisplayInfo.logicalWidth; + mWindowParams.height = mDisplayInfo.logicalHeight; + if (mShown) { + mWindowManager.updateViewLayout(mWindowContent, mWindowParams); } } - } - - private static class DisplayProvider implements DisplayListener { - private final WindowManager mWindowManager; - private final DisplayManager mDisplayManager; - private final Display mDefaultDisplay; - private final DisplayInfo mDefaultDisplayInfo = new DisplayInfo(); - public DisplayProvider(Context context, WindowManager windowManager) { - mWindowManager = windowManager; - mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE); - mDefaultDisplay = mWindowManager.getDefaultDisplay(); - mDisplayManager.registerDisplayListener(this, null); - updateDisplayInfo(); - } + private final class ContentView extends View { + private final Drawable mHighlightFrame; - public DisplayInfo getDisplayInfo() { - return mDefaultDisplayInfo; - } + public ContentView(Context context) { + super(context); + mHighlightFrame = context.getResources().getDrawable( + R.drawable.magnified_region_frame); + } - public Display getDisplay() { - return mDefaultDisplay; + @Override + public void onDraw(Canvas canvas) { + canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR); + mHighlightFrame.setBounds(mBounds); + mHighlightFrame.setAlpha(mAlpha); + mHighlightFrame.draw(canvas); + } } private void updateDisplayInfo() { - if (!mDefaultDisplay.getDisplayInfo(mDefaultDisplayInfo)) { - Slog.e(LOG_TAG, "Default display is not valid."); + if (!mDisplay.getDisplayInfo(mDisplayInfo)) { + Slog.e(LOG_TAG, "Display is not valid."); } } public void destroy() { + setShown(false, true); mDisplayManager.unregisterDisplayListener(this); } @Override - public void onDisplayAdded(int displayId) { - /* do noting */ + public void onDisplayChanged(int displayId) { + if (mDisplayId == displayId) { + updateDisplayInfo(); + } } @Override - public void onDisplayRemoved(int displayId) { - // Having no default display + public void onDisplayAdded(int displayId) { + /* do nothing */ } @Override - public void onDisplayChanged(int displayId) { - updateDisplayInfo(); + public void onDisplayRemoved(int displayId) { + /* do nothing */ } } } diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java index 68cdbfc0e678..59e4b0e6f730 100644 --- a/services/java/com/android/server/wm/DisplayContent.java +++ b/services/java/com/android/server/wm/DisplayContent.java @@ -16,10 +16,8 @@ package com.android.server.wm; -import android.os.RemoteCallbackList; import android.view.Display; import android.view.DisplayInfo; -import android.view.IDisplayContentChangeListener; import java.io.PrintWriter; import java.util.ArrayList; @@ -43,12 +41,6 @@ class DisplayContent { * from mDisplayWindows; */ private WindowList mWindows = new WindowList(); - // Specification for magnifying the display content. - MagnificationSpec mMagnificationSpec; - - // Callback for observing content changes on a display. - RemoteCallbackList mDisplayContentChangeListeners; - // This protects the following display size properties, so that // getDisplaySize() doesn't need to acquire the global lock. This is // needed because the window manager sometimes needs to use ActivityThread @@ -128,9 +120,6 @@ class DisplayContent { pw.print("-"); pw.print(mDisplayInfo.largestNominalAppWidth); pw.print("x"); pw.println(mDisplayInfo.largestNominalAppHeight); pw.print(subPrefix); pw.print("layoutNeeded="); pw.print(layoutNeeded); - if (mMagnificationSpec != null) { - pw.print(" mMagnificationSpec="); pw.print(mMagnificationSpec); - } pw.println(); } } diff --git a/services/java/com/android/server/wm/DisplayMagnificationMediator.java b/services/java/com/android/server/wm/DisplayMagnificationMediator.java new file mode 100644 index 000000000000..8621c5cd396b --- /dev/null +++ b/services/java/com/android/server/wm/DisplayMagnificationMediator.java @@ -0,0 +1,616 @@ +package com.android.server.wm; + +import android.Manifest; +import android.content.Context; +import android.content.pm.PackageManager; +import android.graphics.Rect; +import android.graphics.Region; +import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManager.DisplayListener; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.Process; +import android.os.RemoteException; +import android.util.Slog; +import android.util.SparseArray; +import android.util.Pools.SynchronizedPool; +import android.view.DisplayInfo; +import android.view.IDisplayMagnificationController; +import android.view.IDisplayMagnificationMediator; +import android.view.MagnificationSpec; +import android.view.Surface; +import android.view.WindowManager; +import android.view.WindowManagerPolicy; + +import com.android.internal.os.SomeArgs; +import com.android.internal.policy.impl.PhoneWindowManager; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; + +final class DisplayMagnificationMediator extends IDisplayMagnificationMediator.Stub + implements DisplayListener { + private static final String LOG_TAG = DisplayMagnificationMediator.class.getSimpleName(); + + private static final boolean DEBUG_WINDOW_TRANSITIONS = false; + private static final boolean DEBUG_ROTATION = false; + private static final boolean DEBUG_LAYERS = false; + private static final boolean DEBUG_RECTANGLE_REQUESTED = false; + + private static final int MESSAGE_NOTIFY_MAGNIFIED_FRAME_CHANGED = 1; + private static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2; + private static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3; + private static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4; + + private static final String METHOD_SIGNATURE_ADD_CONTROLLER = + "addController(int, IDisplayMagnificationController)"; + private static final String METHOD_SIGNATURE_REMOVE_CONTROLLER = + "removeController(IDisplayMagnificationController, int)"; + private static final String METHOD_SIGNATURE_SET_MAGNIFICATION_SPEC = + "setMagnificationSpec(IDisplayMagnificationController, MagnificationSpec)"; + + private final Rect mTempRect = new Rect(); + private final Rect mTempRect1 = new Rect(); + private final Region mTempRegion = new Region(); + + private final SparseArray mDisplayStates = + new SparseArray(); + + private final Context mContext; + private final WindowManagerService mWindowManagerService; + + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message message) { + switch (message.what) { + case MESSAGE_NOTIFY_MAGNIFIED_FRAME_CHANGED: { + SomeArgs args = (SomeArgs) message.obj; + IDisplayMagnificationController client = + (IDisplayMagnificationController) args.arg1; + final int left = args.argi1; + final int top = args.argi2; + final int right = args.argi3; + final int bottom = args.argi4; + try { + client.onMagnifedFrameChanged(left, top, right, bottom); + } catch (RemoteException re) { + /* ignore */ + } finally { + args.recycle(); + } + } break; + case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: { + SomeArgs args = (SomeArgs) message.obj; + IDisplayMagnificationController client = + (IDisplayMagnificationController) args.arg1; + final int left = args.argi1; + final int top = args.argi2; + final int right = args.argi3; + final int bottom = args.argi4; + try { + client.onRectangleOnScreenRequested(left, top, right, bottom); + } catch (RemoteException re) { + /* ignore */ + } finally { + args.recycle(); + } + } break; + case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: { + IDisplayMagnificationController client = + (IDisplayMagnificationController) message.obj; + try { + client.onUserContextChanged(); + } catch (RemoteException re) { + /* ignore */ + } + } break; + case MESSAGE_NOTIFY_ROTATION_CHANGED: { + IDisplayMagnificationController client = + (IDisplayMagnificationController) message.obj; + final int rotation = message.arg1; + try { + client.onRotationChanged(rotation); + } catch (RemoteException re) { + /* ignore */ + } + } break; + } + } + }; + + public DisplayMagnificationMediator(WindowManagerService windowManagerService) { + mContext = windowManagerService.mContext; + mWindowManagerService = windowManagerService; + DisplayManager displayManager = (DisplayManager) + mContext.getSystemService(Context.DISPLAY_SERVICE); + displayManager.registerDisplayListener(this, mHandler); + } + + @Override + public void addController(int displayId, IDisplayMagnificationController controller) { + enforceCallingPermission(Manifest.permission.MAGNIFY_DISPLAY, + METHOD_SIGNATURE_ADD_CONTROLLER); + synchronized (mWindowManagerService.mWindowMap) { + DisplayState displayState = mDisplayStates.get(displayId); + if (displayState != null) { + displayState.clearLw(); + } + mDisplayStates.remove(displayId); + mDisplayStates.put(displayId, new DisplayState(displayId, controller)); + } + } + + @Override + public void removeController(IDisplayMagnificationController controller) { + enforceCallingPermission(Manifest.permission.MAGNIFY_DISPLAY, + METHOD_SIGNATURE_REMOVE_CONTROLLER); + synchronized (mWindowManagerService.mWindowMap) { + final int displayStateCount = mDisplayStates.size(); + for (int i = 0; i < displayStateCount; i++) { + DisplayState displayState = mDisplayStates.valueAt(i); + if (displayState.mClient.asBinder() == controller.asBinder()) { + displayState.clearLw(); + mDisplayStates.removeAt(i); + return; + } + } + } + } + + @Override + public void setMagnificationSpec(IDisplayMagnificationController controller, + MagnificationSpec spec) { + enforceCallingPermission(Manifest.permission.MAGNIFY_DISPLAY, + METHOD_SIGNATURE_SET_MAGNIFICATION_SPEC); + synchronized (mWindowManagerService.mWindowMap) { + DisplayState displayState = null; + final int displayStateCount = mDisplayStates.size(); + for (int i = 0; i < displayStateCount; i++) { + DisplayState candidate = mDisplayStates.valueAt(i); + if (candidate.mClient.asBinder() == controller.asBinder()) { + displayState = candidate; + break; + } + } + if (displayState == null) { + Slog.e(LOG_TAG, "Setting magnification spec for unregistered controller " + + controller); + return; + } + displayState.mMagnificationSpec.initialize(spec.scale, spec.offsetX, + spec.offsetY); + spec.recycle(); + } + synchronized (mWindowManagerService.mLayoutToAnim) { + mWindowManagerService.scheduleAnimationLocked(); + } + } + + @Override + public MagnificationSpec getCompatibleMagnificationSpec(IBinder windowToken) { + synchronized (mWindowManagerService.mWindowMap) { + WindowState windowState = mWindowManagerService.mWindowMap.get(windowToken); + if (windowState == null) { + return null; + } + MagnificationSpec spec = getMagnificationSpecLw(windowState); + if ((spec == null || spec.isNop()) && windowState.mGlobalScale == 1.0f) { + return null; + } + if (spec == null) { + spec = MagnificationSpec.obtain(); + } else { + spec = MagnificationSpec.obtain(spec); + } + spec.scale *= windowState.mGlobalScale; + return spec; + } + } + + @Override + public void onDisplayAdded(int displayId) { + /* do nothing */ + } + + @Override + public void onDisplayRemoved(int displayId) { + synchronized (mWindowManagerService.mWindowMap) { + DisplayState displayState = mDisplayStates.get(displayId); + if (displayState != null) { + displayState.clearLw(); + mDisplayStates.remove(displayId); + } + } + } + + @Override + public void onDisplayChanged(int displayId) { + /* do nothing */ + } + + public void onRectangleOnScreenRequestedLw(WindowState windowState, Rect rectangle, + boolean immediate) { + DisplayState displayState = mDisplayStates.get(windowState.getDisplayId()); + if (displayState == null) { + return; + } + if (DEBUG_RECTANGLE_REQUESTED) { + Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle + + " displayId: " + windowState.getDisplayId()); + } + if (!displayState.isMagnifyingLw()) { + return; + } + Rect magnifiedRegionBounds = mTempRect1; + displayState.getMagnifiedFrameInContentCoordsLw(magnifiedRegionBounds); + if (magnifiedRegionBounds.contains(rectangle)) { + return; + } + SomeArgs args = SomeArgs.obtain(); + args.arg1 = displayState.mClient; + args.argi1 = rectangle.left; + args.argi2 = rectangle.top; + args.argi3 = rectangle.right; + args.argi4 = rectangle.bottom; + mHandler.obtainMessage(MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED, + args).sendToTarget(); + } + + public void onWindowLayersChangedLw(int displayId) { + DisplayState displayState = mDisplayStates.get(displayId); + if (displayState == null) { + return; + } + if (DEBUG_LAYERS) { + Slog.i(LOG_TAG, "Layers changed displayId: " + displayId); + } + displayState.mViewport.recomputeBoundsLw(); + } + + public void onRotationChangedLw(int displayId, int rotation) { + DisplayState displayState = mDisplayStates.get(displayId); + if (displayState == null) { + return; + } + if (DEBUG_ROTATION) { + Slog.i(LOG_TAG, "Rotaton: " + Surface.rotationToString(rotation) + + " displayId: " + displayId); + } + displayState.mViewport.recomputeBoundsLw(); + mHandler.obtainMessage(MESSAGE_NOTIFY_ROTATION_CHANGED, rotation, 0, + displayState.mClient).sendToTarget(); + } + + public void onWindowTransitionLw(WindowState windowState, int transition) { + DisplayState displayState = mDisplayStates.get(windowState.getDisplayId()); + if (displayState == null) { + return; + } + if (DEBUG_WINDOW_TRANSITIONS) { + Slog.i(LOG_TAG, "Window transition: " + + PhoneWindowManager.windowTransitionToString(transition) + + " displayId: " + windowState.getDisplayId()); + } + final boolean magnifying = displayState.isMagnifyingLw(); + if (magnifying) { + switch (transition) { + case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN: + case WindowManagerPolicy.TRANSIT_TASK_OPEN: + case WindowManagerPolicy.TRANSIT_TASK_TO_FRONT: + case WindowManagerPolicy.TRANSIT_WALLPAPER_OPEN: + case WindowManagerPolicy.TRANSIT_WALLPAPER_CLOSE: + case WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_OPEN: { + mHandler.obtainMessage(MESSAGE_NOTIFY_USER_CONTEXT_CHANGED, + displayState.mClient).sendToTarget(); + } + } + } + final int type = windowState.mAttrs.type; + if (type == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR + || type == WindowManager.LayoutParams.TYPE_INPUT_METHOD + || type == WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG + || type == WindowManager.LayoutParams.TYPE_KEYGUARD + || type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) { + switch (transition) { + case WindowManagerPolicy.TRANSIT_ENTER: + case WindowManagerPolicy.TRANSIT_SHOW: + case WindowManagerPolicy.TRANSIT_EXIT: + case WindowManagerPolicy.TRANSIT_HIDE: { + displayState.mViewport.recomputeBoundsLw(); + } break; + } + } + switch (transition) { + case WindowManagerPolicy.TRANSIT_ENTER: + case WindowManagerPolicy.TRANSIT_SHOW: { + if (!magnifying) { + break; + } + switch (type) { + case WindowManager.LayoutParams.TYPE_APPLICATION: + case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL: + case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA: + case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL: + case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG: + case WindowManager.LayoutParams.TYPE_SEARCH_BAR: + case WindowManager.LayoutParams.TYPE_PHONE: + case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT: + case WindowManager.LayoutParams.TYPE_TOAST: + case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: + case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE: + case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG: + case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG: + case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR: + case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY: + case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: + case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY: { + Rect magnifiedRegionBounds = mTempRect1; + displayState.getMagnifiedFrameInContentCoordsLw(magnifiedRegionBounds); + Rect touchableRegionBounds = mTempRect; + windowState.getTouchableRegion(mTempRegion); + mTempRegion.getBounds(touchableRegionBounds); + if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = displayState.mClient; + args.argi1 = touchableRegionBounds.left; + args.argi2 = touchableRegionBounds.top; + args.argi3 = touchableRegionBounds.right; + args.argi4 = touchableRegionBounds.bottom; + mHandler.obtainMessage(MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED, + args).sendToTarget(); + } + } break; + } break; + } + } + } + + public MagnificationSpec getMagnificationSpecLw(WindowState windowState) { + DisplayState displayState = mDisplayStates.get(windowState.getDisplayId()); + if (displayState == null) { + return null; + } + MagnificationSpec spec = displayState.mMagnificationSpec; + if (spec != null && !spec.isNop()) { + if (windowState.mAttachedWindow != null) { + if (!canMagnifyWindow(windowState.mAttachedWindow.mAttrs.type)) { + return null; + } + } + if (!canMagnifyWindow(windowState.mAttrs.type)) { + return null; + } + } + return spec; + } + + private void enforceCallingPermission(String permission, String function) { + if (Process.myPid() == Binder.getCallingPid()) { + return; + } + if (mContext.checkCallingPermission(permission) != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("The caller does not have " + permission + + " required to call " + function); + } + } + + private static boolean canMagnifyWindow(int type) { + switch (type) { + case WindowManager.LayoutParams.TYPE_INPUT_METHOD: + case WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG: + case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR: + case WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY: { + return false; + } + } + return true; + } + + private static final class Viewport { + + private final ArrayList mTempWindowStateInfoList = + new ArrayList(); + + private final Rect mTempRect1 = new Rect(); + private final Rect mTempRect2 = new Rect(); + private final Rect mTempRect3 = new Rect(); + + private final Rect mBounds = new Rect(); + private final Handler mHandler; + private final IDisplayMagnificationController mClient; + private final WindowManagerService mWindowManagerService; + private final DisplayInfo mDisplayInfo; + + private final int mDisplayId; + + public Viewport(Context context, int displayId, Handler handler, + IDisplayMagnificationController client, WindowManagerService windowManagerService) { + mDisplayId = displayId; + mHandler = handler; + mWindowManagerService = windowManagerService; + mDisplayInfo = mWindowManagerService.getDisplayContentLocked(displayId) + .getDisplayInfo(); + mClient = client; + recomputeBoundsLw(); + } + + private final Comparator mWindowInfoInverseComparator = + new Comparator() { + @Override + public int compare(WindowStateInfo lhs, WindowStateInfo rhs) { + if (lhs.mWindowState.mLayer != rhs.mWindowState.mLayer) { + return rhs.mWindowState.mLayer - lhs.mWindowState.mLayer; + } + if (lhs.mTouchableRegion.top != rhs.mTouchableRegion.top) { + return rhs.mTouchableRegion.top - lhs.mTouchableRegion.top; + } + if (lhs.mTouchableRegion.left != rhs.mTouchableRegion.left) { + return rhs.mTouchableRegion.left - lhs.mTouchableRegion.left; + } + if (lhs.mTouchableRegion.right != rhs.mTouchableRegion.right) { + return rhs.mTouchableRegion.right - lhs.mTouchableRegion.right; + } + if (lhs.mTouchableRegion.bottom != rhs.mTouchableRegion.bottom) { + return rhs.mTouchableRegion.bottom - lhs.mTouchableRegion.bottom; + } + return 0; + } + }; + + public void recomputeBoundsLw() { + Rect magnifiedFrame = mBounds; + magnifiedFrame.set(0, 0, 0, 0); + + Rect oldmagnifiedFrame = mTempRect3; + oldmagnifiedFrame.set(magnifiedFrame); + + Rect availableFrame = mTempRect1; + availableFrame.set(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight); + + ArrayList visibleWindows = mTempWindowStateInfoList; + visibleWindows.clear(); + getVisibleWindowsLw(visibleWindows); + + Collections.sort(visibleWindows, mWindowInfoInverseComparator); + + final int visibleWindowCount = visibleWindows.size(); + for (int i = 0; i < visibleWindowCount; i++) { + WindowStateInfo info = visibleWindows.get(i); + if (info.mWindowState.mAttrs.type == WindowManager + .LayoutParams.TYPE_MAGNIFICATION_OVERLAY) { + continue; + } + Rect windowFrame = mTempRect2; + windowFrame.set(info.mTouchableRegion); + if (canMagnifyWindow(info.mWindowState.mAttrs.type)) { + magnifiedFrame.union(windowFrame); + magnifiedFrame.intersect(availableFrame); + } else { + subtract(windowFrame, magnifiedFrame); + subtract(availableFrame, windowFrame); + } + if (availableFrame.equals(magnifiedFrame)) { + break; + } + } + for (int i = visibleWindowCount - 1; i >= 0; i--) { + visibleWindows.remove(i).recycle(); + } + + final int displayWidth = mDisplayInfo.logicalWidth; + final int displayHeight = mDisplayInfo.logicalHeight; + magnifiedFrame.intersect(0, 0, displayWidth, displayHeight); + + if (!oldmagnifiedFrame.equals(magnifiedFrame)) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = mClient; + args.argi1 = magnifiedFrame.left; + args.argi2 = magnifiedFrame.top; + args.argi3 = magnifiedFrame.right; + args.argi4 = magnifiedFrame.bottom; + mHandler.obtainMessage(MESSAGE_NOTIFY_MAGNIFIED_FRAME_CHANGED, args) + .sendToTarget(); + } + } + + private void getVisibleWindowsLw(ArrayList outWindowStates) { + DisplayContent displayContent = mWindowManagerService.getDisplayContentLocked( + mDisplayId); + WindowList windowList = displayContent.getWindowList(); + final int windowCount = windowList.size(); + for (int i = 0; i < windowCount; i++) { + WindowState windowState = windowList.get(i); + if (windowState.isVisibleLw() || windowState.mAttrs.type == WindowManager + .LayoutParams.TYPE_UNIVERSE_BACKGROUND) { + outWindowStates.add(WindowStateInfo.obtain(windowState)); + } + } + } + + public Rect getBoundsLw() { + return mBounds; + } + + private static boolean subtract(Rect lhs, Rect rhs) { + if (lhs.right < rhs.left || lhs.left > rhs.right + || lhs.bottom < rhs.top || lhs.top > rhs.bottom) { + return false; + } + if (lhs.left < rhs.left) { + lhs.right = rhs.left; + } + if (lhs.top < rhs.top) { + lhs.bottom = rhs.top; + } + if (lhs.right > rhs.right) { + lhs.left = rhs.right; + } + if (lhs.bottom > rhs.bottom) { + lhs.top = rhs.bottom; + } + return true; + } + + private static final class WindowStateInfo { + private static final int MAX_POOL_SIZE = 30; + + private static final SynchronizedPool sPool = + new SynchronizedPool(MAX_POOL_SIZE); + + private static final Region mTempRegion = new Region(); + + public WindowState mWindowState; + public final Rect mTouchableRegion = new Rect(); + + public static WindowStateInfo obtain(WindowState windowState) { + WindowStateInfo info = sPool.acquire(); + if (info == null) { + info = new WindowStateInfo(); + } + info.mWindowState = windowState; + windowState.getTouchableRegion(mTempRegion); + mTempRegion.getBounds(info.mTouchableRegion); + return info; + } + + public void recycle() { + mWindowState = null; + mTouchableRegion.setEmpty(); + sPool.release(this); + } + } + } + + private final class DisplayState { + final int mDisplayId; + final MagnificationSpec mMagnificationSpec; + final Viewport mViewport; + final IDisplayMagnificationController mClient; + + DisplayState(int displayId, IDisplayMagnificationController client) { + mDisplayId = displayId; + mClient = client; + mMagnificationSpec = MagnificationSpec.obtain(); + mViewport = new Viewport(mContext, mDisplayId, mHandler, + mClient, mWindowManagerService); + } + + public boolean isMagnifyingLw() { + return mMagnificationSpec.scale > 1.0f; + } + + private void getMagnifiedFrameInContentCoordsLw(Rect rect) { + MagnificationSpec spec = mMagnificationSpec; + rect.set(mViewport.getBoundsLw()); + rect.offset((int) -spec.offsetX, (int) -spec.offsetY); + rect.scale(1.0f / spec.scale); + } + + public void clearLw() { + mMagnificationSpec.recycle(); + } + } +} diff --git a/services/java/com/android/server/wm/MagnificationSpec.java b/services/java/com/android/server/wm/MagnificationSpec.java deleted file mode 100644 index 31aae66b694d..000000000000 --- a/services/java/com/android/server/wm/MagnificationSpec.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2012 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.wm; - -public class MagnificationSpec { - public float mScale = 1.0f; - public float mOffsetX; - public float mOffsetY; - - public void initialize(float scale, float offsetX, float offsetY) { - mScale = scale; - mOffsetX = offsetX; - mOffsetY = offsetY; - } - - public boolean isNop() { - return mScale == 1.0f && mOffsetX == 0 && mOffsetY == 0; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append(""); - return builder.toString(); - } -} diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 69964e478949..dc1121b5ab41 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -91,7 +91,6 @@ import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.PowerManager; import android.os.Process; -import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ServiceManager; import android.os.StrictMode; @@ -114,7 +113,7 @@ import android.view.Display; import android.view.DisplayInfo; import android.view.Gravity; import android.view.IApplicationToken; -import android.view.IDisplayContentChangeListener; +import android.view.IDisplayMagnificationMediator; import android.view.IInputFilter; import android.view.IOnKeyguardExitResult; import android.view.IRotationWatcher; @@ -126,12 +125,12 @@ import android.view.InputDevice; import android.view.InputEvent; import android.view.InputEventReceiver; import android.view.KeyEvent; +import android.view.MagnificationSpec; import android.view.MotionEvent; import android.view.Surface; import android.view.SurfaceSession; import android.view.View; import android.view.ViewTreeObserver; -import android.view.WindowInfo; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.WindowManagerPolicy; @@ -424,6 +423,8 @@ public class WindowManagerService extends IWindowManager.Stub IInputMethodManager mInputMethodManager; + DisplayMagnificationMediator mMagnificationMediator; + final SurfaceSession mFxSession; Watermark mWatermark; StrictModeFlash mStrictModeFlash; @@ -2374,7 +2375,9 @@ public class WindowManagerService extends IWindowManager.Stub if (win.mWinAnimator.applyAnimationLocked(transit, false)) { win.mExiting = true; } - scheduleNotifyWindowTranstionIfNeededLocked(win, transit); + if (mMagnificationMediator != null) { + mMagnificationMediator.onWindowTransitionLw(win, transit); + } } if (win.mExiting || win.mWinAnimator.isAnimating()) { // The exit animation is running... wait for it! @@ -2666,49 +2669,13 @@ public class WindowManagerService extends IWindowManager.Stub public void onRectangleOnScreenRequested(IBinder token, Rect rectangle, boolean immediate) { synchronized (mWindowMap) { - WindowState window = mWindowMap.get(token); - if (window != null) { - scheduleNotifyRectangleOnScreenRequestedIfNeededLocked(window, rectangle, - immediate); - } - } - } - - private void scheduleNotifyRectangleOnScreenRequestedIfNeededLocked(WindowState window, - Rect rectangle, boolean immediate) { - DisplayContent displayContent = window.mDisplayContent; - if (displayContent.mDisplayContentChangeListeners != null - && displayContent.mDisplayContentChangeListeners.getRegisteredCallbackCount() > 0) { - mH.obtainMessage(H.NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED, displayContent.getDisplayId(), - immediate? 1 : 0, new Rect(rectangle)).sendToTarget(); - } - } - - private void handleNotifyRectangleOnScreenRequested(int displayId, Rect rectangle, - boolean immediate) { - RemoteCallbackList callbacks = null; - synchronized (mWindowMap) { - DisplayContent displayContent = getDisplayContentLocked(displayId); - if (displayContent == null) { - return; - } - callbacks = displayContent.mDisplayContentChangeListeners; - if (callbacks == null) { - return; - } - } - final int callbackCount = callbacks.beginBroadcast(); - try { - for (int i = 0; i < callbackCount; i++) { - try { - callbacks.getBroadcastItem(i).onRectangleOnScreenRequested(displayId, - rectangle, immediate); - } catch (RemoteException re) { - /* ignore */ + if (mMagnificationMediator != null) { + WindowState window = mWindowMap.get(token); + if (window != null) { + mMagnificationMediator.onRectangleOnScreenRequestedLw(window, rectangle, + immediate); } } - } finally { - callbacks.finishBroadcast(); } } @@ -2933,7 +2900,9 @@ public class WindowManagerService extends IWindowManager.Stub } winAnimator.destroySurfaceLocked(false); } - scheduleNotifyWindowTranstionIfNeededLocked(win, transit); + if (mMagnificationMediator != null) { + mMagnificationMediator.onWindowTransitionLw(win, transit); + } } } @@ -3072,106 +3041,31 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public float getWindowCompatibilityScale(IBinder windowToken) { - if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO, - "getWindowCompatibilityScale()")) { - throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission."); - } - synchronized (mWindowMap) { - WindowState windowState = mWindowMap.get(windowToken); - return (windowState != null) ? windowState.mGlobalScale : 1.0f; - } - } - - @Override - public WindowInfo getWindowInfo(IBinder token) { + public void getWindowFrame(IBinder token, Rect outBounds) { if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO, "getWindowInfo()")) { throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission."); } synchronized (mWindowMap) { - WindowState window = mWindowMap.get(token); - if (window != null) { - return getWindowInfoForWindowStateLocked(window); + WindowState windowState = mWindowMap.get(token); + if (windowState != null) { + outBounds.set(windowState.mFrame); + } else { + outBounds.setEmpty(); } - return null; } } @Override - public void getVisibleWindowsForDisplay(int displayId, List outInfos) { - if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO, - "getWindowInfos()")) { + public IDisplayMagnificationMediator getDisplayMagnificationMediator() { + if (!checkCallingPermission(android.Manifest.permission.MAGNIFY_DISPLAY, + "getDisplayMagnificationMediator()")) { throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission."); } - synchronized (mWindowMap) { - DisplayContent displayContent = getDisplayContentLocked(displayId); - if (displayContent == null) { - return; - } - WindowList windows = displayContent.getWindowList(); - final int windowCount = windows.size(); - for (int i = 0; i < windowCount; i++) { - WindowState window = windows.get(i); - if (window.isVisibleLw() || window.mAttrs.type == TYPE_UNIVERSE_BACKGROUND) { - WindowInfo info = getWindowInfoForWindowStateLocked(window); - outInfos.add(info); - } - } - } - } - - @Override - public void magnifyDisplay(int displayId, float scale, float offsetX, float offsetY) { - if (!checkCallingPermission( - android.Manifest.permission.MAGNIFY_DISPLAY, "magnifyDisplay()")) { - throw new SecurityException("Requires MAGNIFY_DISPLAY permission"); + if (mMagnificationMediator == null) { + mMagnificationMediator = new DisplayMagnificationMediator(this); } - synchronized (mWindowMap) { - MagnificationSpec spec = getDisplayMagnificationSpecLocked(displayId); - if (spec != null) { - final boolean scaleChanged = spec.mScale != scale; - final boolean offsetChanged = spec.mOffsetX != offsetX || spec.mOffsetY != offsetY; - if (!scaleChanged && !offsetChanged) { - return; - } - spec.initialize(scale, offsetX, offsetY); - // If the offset has changed we need to re-add the input windows - // since the offsets have to be propagated to the input system. - if (offsetChanged) { - // TODO(multidisplay): Input only occurs on the default display. - if (displayId == Display.DEFAULT_DISPLAY) { - mInputMonitor.updateInputWindowsLw(true); - } - } - scheduleAnimationLocked(); - } - } - } - - MagnificationSpec getDisplayMagnificationSpecLocked(int displayId) { - DisplayContent displayContent = getDisplayContentLocked(displayId); - if (displayContent != null) { - if (displayContent.mMagnificationSpec == null) { - displayContent.mMagnificationSpec = new MagnificationSpec(); - } - return displayContent.mMagnificationSpec; - } - return null; - } - - private WindowInfo getWindowInfoForWindowStateLocked(WindowState window) { - WindowInfo info = WindowInfo.obtain(); - info.token = window.mToken.token; - info.frame.set(window.mFrame); - info.type = window.mAttrs.type; - info.displayId = window.getDisplayId(); - info.compatibilityScale = window.mGlobalScale; - info.visible = window.isVisibleLw() || info.type == TYPE_UNIVERSE_BACKGROUND; - info.layer = window.mLayer; - window.getTouchableRegion(mTempRegion); - mTempRegion.getBounds(info.touchableRegion); - return info; + return mMagnificationMediator; } private boolean applyAnimationLocked(AppWindowToken atoken, @@ -3315,8 +3209,10 @@ public class WindowManagerService extends IWindowManager.Stub if (win.isVisibleNow()) { win.mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_EXIT, false); - scheduleNotifyWindowTranstionIfNeededLocked(win, - WindowManagerPolicy.TRANSIT_EXIT); + if (mMagnificationMediator != null) { + mMagnificationMediator.onWindowTransitionLw(win, + WindowManagerPolicy.TRANSIT_EXIT); + } changed = true; win.mDisplayContent.layoutNeeded = true; } @@ -4071,8 +3967,8 @@ public class WindowManagerService extends IWindowManager.Stub delayed = runningAppAnimation = true; } WindowState window = wtoken.findMainWindow(); - if (window != null) { - scheduleNotifyWindowTranstionIfNeededLocked(window, transit); + if (window != null && mMagnificationMediator != null) { + mMagnificationMediator.onWindowTransitionLw(window, transit); } changed = true; } @@ -4091,8 +3987,10 @@ public class WindowManagerService extends IWindowManager.Stub if (!runningAppAnimation) { win.mWinAnimator.applyAnimationLocked( WindowManagerPolicy.TRANSIT_ENTER, true); - scheduleNotifyWindowTranstionIfNeededLocked(win, - WindowManagerPolicy.TRANSIT_ENTER); + if (mMagnificationMediator != null) { + mMagnificationMediator.onWindowTransitionLw(win, + WindowManagerPolicy.TRANSIT_ENTER); + } } changed = true; win.mDisplayContent.layoutNeeded = true; @@ -4101,8 +3999,10 @@ public class WindowManagerService extends IWindowManager.Stub if (!runningAppAnimation) { win.mWinAnimator.applyAnimationLocked( WindowManagerPolicy.TRANSIT_EXIT, false); - scheduleNotifyWindowTranstionIfNeededLocked(win, - WindowManagerPolicy.TRANSIT_EXIT); + if (mMagnificationMediator != null) { + mMagnificationMediator.onWindowTransitionLw(win, + WindowManagerPolicy.TRANSIT_EXIT); + } } changed = true; win.mDisplayContent.layoutNeeded = true; @@ -5727,7 +5627,9 @@ public class WindowManagerService extends IWindowManager.Stub } } - scheduleNotifyRotationChangedIfNeededLocked(displayContent, rotation); + if (mMagnificationMediator != null) { + mMagnificationMediator.onRotationChangedLw(Display.DEFAULT_DISPLAY, rotation); + } return true; } @@ -6109,146 +6011,6 @@ public class WindowManagerService extends IWindowManager.Stub return success; } - @Override - public void addDisplayContentChangeListener(int displayId, - IDisplayContentChangeListener listener) { - if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO, - "addDisplayContentChangeListener()")) { - throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission"); - } - synchronized(mWindowMap) { - DisplayContent displayContent = getDisplayContentLocked(displayId); - if (displayContent != null) { - if (displayContent.mDisplayContentChangeListeners == null) { - displayContent.mDisplayContentChangeListeners = - new RemoteCallbackList(); - displayContent.mDisplayContentChangeListeners.register(listener); - } - } - } - } - - @Override - public void removeDisplayContentChangeListener(int displayId, - IDisplayContentChangeListener listener) { - if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO, - "removeDisplayContentChangeListener()")) { - throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission"); - } - synchronized(mWindowMap) { - DisplayContent displayContent = getDisplayContentLocked(displayId); - if (displayContent != null) { - if (displayContent.mDisplayContentChangeListeners != null) { - displayContent.mDisplayContentChangeListeners.unregister(listener); - if (displayContent.mDisplayContentChangeListeners - .getRegisteredCallbackCount() == 0) { - displayContent.mDisplayContentChangeListeners = null; - } - } - } - } - } - - void scheduleNotifyWindowTranstionIfNeededLocked(WindowState window, int transition) { - DisplayContent displayContent = window.mDisplayContent; - if (displayContent.mDisplayContentChangeListeners != null) { - WindowInfo info = getWindowInfoForWindowStateLocked(window); - mH.obtainMessage(H.NOTIFY_WINDOW_TRANSITION, transition, 0, info).sendToTarget(); - } - } - - private void handleNotifyWindowTranstion(int transition, WindowInfo info) { - RemoteCallbackList callbacks = null; - synchronized (mWindowMap) { - DisplayContent displayContent = getDisplayContentLocked(info.displayId); - if (displayContent == null) { - return; - } - callbacks = displayContent.mDisplayContentChangeListeners; - if (callbacks == null) { - return; - } - } - final int callbackCount = callbacks.beginBroadcast(); - try { - for (int i = 0; i < callbackCount; i++) { - try { - callbacks.getBroadcastItem(i).onWindowTransition(info.displayId, - transition, info); - } catch (RemoteException re) { - /* ignore */ - } - } - } finally { - callbacks.finishBroadcast(); - } - } - - private void scheduleNotifyRotationChangedIfNeededLocked(DisplayContent displayContent, - int rotation) { - if (displayContent.mDisplayContentChangeListeners != null - && displayContent.mDisplayContentChangeListeners.getRegisteredCallbackCount() > 0) { - mH.obtainMessage(H.NOTIFY_ROTATION_CHANGED, displayContent.getDisplayId(), - rotation).sendToTarget(); - } - } - - private void handleNotifyRotationChanged(int displayId, int rotation) { - RemoteCallbackList callbacks = null; - synchronized (mWindowMap) { - DisplayContent displayContent = getDisplayContentLocked(displayId); - if (displayContent == null) { - return; - } - callbacks = displayContent.mDisplayContentChangeListeners; - if (callbacks == null) { - return; - } - } - try { - final int watcherCount = callbacks.beginBroadcast(); - for (int i = 0; i < watcherCount; i++) { - try { - callbacks.getBroadcastItem(i).onRotationChanged(rotation); - } catch (RemoteException re) { - /* ignore */ - } - } - } finally { - callbacks.finishBroadcast(); - } - } - - private void scheduleNotifyWindowLayersChangedIfNeededLocked(DisplayContent displayContent) { - if (displayContent.mDisplayContentChangeListeners != null - && displayContent.mDisplayContentChangeListeners.getRegisteredCallbackCount() > 0) { - mH.obtainMessage(H.NOTIFY_WINDOW_LAYERS_CHANGED, displayContent) .sendToTarget(); - } - } - - private void handleNotifyWindowLayersChanged(DisplayContent displayContent) { - RemoteCallbackList callbacks = null; - synchronized (mWindowMap) { - callbacks = displayContent.mDisplayContentChangeListeners; - if (callbacks == null) { - return; - } - } - try { - final int watcherCount = callbacks.beginBroadcast(); - for (int i = 0; i < watcherCount; i++) { - try { - callbacks.getBroadcastItem(i).onWindowLayersChanged( - displayContent.getDisplayId()); - } catch (RemoteException re) { - /* ignore */ - } - } - } finally { - callbacks.finishBroadcast(); - } - } - public void addWindowChangeListener(WindowChangeListener listener) { synchronized(mWindowMap) { mWindowChangeListeners.add(listener); @@ -6892,16 +6654,12 @@ public class WindowManagerService extends IWindowManager.Stub public static final int UPDATE_ANIM_PARAMETERS = 25; public static final int SHOW_STRICT_MODE_VIOLATION = 26; public static final int DO_ANIMATION_CALLBACK = 27; - public static final int NOTIFY_ROTATION_CHANGED = 28; - public static final int NOTIFY_WINDOW_TRANSITION = 29; - public static final int NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 30; - public static final int NOTIFY_WINDOW_LAYERS_CHANGED = 31; - public static final int DO_DISPLAY_ADDED = 32; - public static final int DO_DISPLAY_REMOVED = 33; - public static final int DO_DISPLAY_CHANGED = 34; + public static final int DO_DISPLAY_ADDED = 28; + public static final int DO_DISPLAY_REMOVED = 29; + public static final int DO_DISPLAY_CHANGED = 30; - public static final int CLIENT_FREEZE_TIMEOUT = 35; + public static final int CLIENT_FREEZE_TIMEOUT = 31; public static final int ANIMATOR_WHAT_OFFSET = 100000; public static final int SET_TRANSPARENT_REGION = ANIMATOR_WHAT_OFFSET + 1; @@ -7351,34 +7109,6 @@ public class WindowManagerService extends IWindowManager.Stub break; } - case NOTIFY_ROTATION_CHANGED: { - final int displayId = msg.arg1; - final int rotation = msg.arg2; - handleNotifyRotationChanged(displayId, rotation); - break; - } - - case NOTIFY_WINDOW_TRANSITION: { - final int transition = msg.arg1; - WindowInfo info = (WindowInfo) msg.obj; - handleNotifyWindowTranstion(transition, info); - break; - } - - case NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: { - final int displayId = msg.arg1; - final boolean immediate = (msg.arg2 == 1); - Rect rectangle = (Rect) msg.obj; - handleNotifyRectangleOnScreenRequested(displayId, rectangle, immediate); - break; - } - - case NOTIFY_WINDOW_LAYERS_CHANGED: { - DisplayContent displayContent = (DisplayContent) msg.obj; - handleNotifyWindowLayersChanged(displayContent); - break; - } - case DO_DISPLAY_ADDED: synchronized (mWindowMap) { handleDisplayAddedLocked(msg.arg1); @@ -7823,8 +7553,9 @@ public class WindowManagerService extends IWindowManager.Stub // "Assigned layer " + curLayer + " to " + w.mClient.asBinder()); } - if (anyLayerChanged) { - scheduleNotifyWindowLayersChangedIfNeededLocked(getDefaultDisplayContentLocked()); + if (mMagnificationMediator != null && anyLayerChanged) { + mMagnificationMediator.onWindowLayersChangedLw( + windows.get(windows.size() - 1).getDisplayId()); } } diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java index 3b5b1ebcc245..541e8594bb8a 100644 --- a/services/java/com/android/server/wm/WindowState.java +++ b/services/java/com/android/server/wm/WindowState.java @@ -517,21 +517,6 @@ final class WindowState implements WindowManagerPolicy.WindowState { } } - MagnificationSpec getWindowMagnificationSpecLocked() { - MagnificationSpec spec = mDisplayContent.mMagnificationSpec; - if (spec != null && !spec.isNop()) { - if (mAttachedWindow != null) { - if (!mPolicy.canMagnifyWindowLw(mAttachedWindow.mAttrs)) { - return null; - } - } - if (!mPolicy.canMagnifyWindowLw(mAttrs)) { - return null; - } - } - return spec; - } - @Override public Rect getFrameLw() { return mFrame; diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java index 945c5e3df0b7..3c9424a7e0be 100644 --- a/services/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/java/com/android/server/wm/WindowStateAnimator.java @@ -16,6 +16,7 @@ import android.graphics.Region; import android.os.Debug; import android.util.Slog; import android.view.DisplayInfo; +import android.view.MagnificationSpec; import android.view.Surface; import android.view.SurfaceSession; import android.view.WindowManager; @@ -918,10 +919,13 @@ class WindowStateAnimator { if (screenAnimation) { tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix()); } - MagnificationSpec spec = mWin.getWindowMagnificationSpecLocked(); - if (spec != null && !spec.isNop()) { - tmpMatrix.postScale(spec.mScale, spec.mScale); - tmpMatrix.postTranslate(spec.mOffsetX, spec.mOffsetY); + if (mService.mMagnificationMediator != null) { + MagnificationSpec spec = mService.mMagnificationMediator + .getMagnificationSpecLw(mWin); + if (spec != null && !spec.isNop()) { + tmpMatrix.postScale(spec.scale, spec.scale); + tmpMatrix.postTranslate(spec.offsetX, spec.offsetY); + } } // "convert" it into SurfaceFlinger's format @@ -994,7 +998,8 @@ class WindowStateAnimator { final boolean applyUniverseTransformation = (mAnimator.mUniverseBackground != null && mWin.mAttrs.type != WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND && mWin.mBaseLayer < mAnimator.mAboveUniverseLayer); - MagnificationSpec spec = mWin.getWindowMagnificationSpecLocked(); + MagnificationSpec spec = (mService.mMagnificationMediator != null) + ? mService.mMagnificationMediator.getMagnificationSpecLw(mWin) : null; if (applyUniverseTransformation || spec != null) { final Rect frame = mWin.mFrame; final float tmpFloats[] = mService.mTmpFloats; @@ -1008,8 +1013,8 @@ class WindowStateAnimator { } if (spec != null && !spec.isNop()) { - tmpMatrix.postScale(spec.mScale, spec.mScale); - tmpMatrix.postTranslate(spec.mOffsetX, spec.mOffsetY); + tmpMatrix.postScale(spec.scale, spec.scale); + tmpMatrix.postTranslate(spec.offsetX, spec.offsetY); } tmpMatrix.getValues(tmpFloats); @@ -1494,7 +1499,9 @@ class WindowStateAnimator { transit = WindowManagerPolicy.TRANSIT_SHOW; } applyAnimationLocked(transit, true); - mService.scheduleNotifyWindowTranstionIfNeededLocked(mWin, transit); + if (mService.mMagnificationMediator != null) { + mService.mMagnificationMediator.onWindowTransitionLw(mWin, transit); + } } // TODO(cmautner): Move back to WindowState? diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java index 3e625f98367d..141b32377e1e 100644 --- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java +++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java @@ -22,6 +22,7 @@ import com.android.internal.view.IInputMethodClient; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; +import android.graphics.Rect; import android.os.Bundle; import android.os.IBinder; import android.os.IRemoteCallback; @@ -30,13 +31,11 @@ import android.util.DisplayMetrics; import android.view.Display; import android.view.Gravity; import android.view.IApplicationToken; -import android.view.IDisplayContentChangeListener; import android.view.IInputFilter; import android.view.IOnKeyguardExitResult; import android.view.IRotationWatcher; import android.view.IWindowManager; import android.view.IWindowSession; -import android.view.WindowInfo; import java.util.List; @@ -446,7 +445,7 @@ public class IWindowManagerImpl implements IWindowManager { public void lockNow(Bundle options) { // TODO Auto-generated method stub } - + @Override public boolean isSafeModeEnabled() { return false; @@ -463,44 +462,19 @@ public class IWindowManagerImpl implements IWindowManager { return null; } - @Override - public float getWindowCompatibilityScale(IBinder windowToken) throws RemoteException { - // TODO Auto-generated method stub - return 0; - } - @Override public void setInputFilter(IInputFilter filter) throws RemoteException { // TODO Auto-generated method stub } @Override - public void magnifyDisplay(int dipslayId, float scale, float offsetX, float offsetY) - throws RemoteException { - // TODO Auto-generated method stub - } - - @Override - public void addDisplayContentChangeListener(int displayId, - IDisplayContentChangeListener listener) throws RemoteException { - // TODO Auto-generated method stub - } - - @Override - public void removeDisplayContentChangeListener(int displayId, - IDisplayContentChangeListener listener) throws RemoteException { - // TODO Auto-generated method stub - } - - @Override - public WindowInfo getWindowInfo(IBinder token) throws RemoteException { + public IDisplayMagnificationMediator getDisplayMagnificationMediator() { // TODO Auto-generated method stub return null; } @Override - public void getVisibleWindowsForDisplay(int displayId, List outInfos) - throws RemoteException { + public void getWindowFrame(IBinder token, Rect outFrame) { // TODO Auto-generated method stub } } -- cgit v1.2.3-59-g8ed1b