diff options
19 files changed, 1194 insertions, 1091 deletions
@@ -154,8 +154,7 @@ 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/IDisplayMagnificationMediator.aidl \ - core/java/android/view/IDisplayMagnificationController.aidl \ + core/java/android/view/IMagnificationCallbacks.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 425794a..06f7c54 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -145,6 +145,10 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framew $(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) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IDisplayMagnificationController.java) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IDisplayMagnificationController.P) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IDisplayMagnificationMediator.java) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IDisplayMagnificationMediator.P) # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST diff --git a/api/current.txt b/api/current.txt index 57d65ba..8faebfc 100644 --- a/api/current.txt +++ b/api/current.txt @@ -24753,7 +24753,6 @@ 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/view/IDisplayMagnificationMediator.aidl b/core/java/android/view/IDisplayMagnificationMediator.aidl deleted file mode 100644 index aa25dac..0000000 --- a/core/java/android/view/IDisplayMagnificationMediator.aidl +++ /dev/null @@ -1,31 +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.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/IDisplayMagnificationController.aidl b/core/java/android/view/IMagnificationCallbacks.aidl index efe2775..032d073 100644 --- a/core/java/android/view/IDisplayMagnificationController.aidl +++ b/core/java/android/view/IMagnificationCallbacks.aidl @@ -16,11 +16,13 @@ package android.view; +import android.graphics.Region; + /** * {@hide} */ -oneway interface IDisplayMagnificationController { - void onMagnifedFrameChanged(int left, int top, int right, int bottom); +oneway interface IMagnificationCallbacks { + void onMagnifedBoundsChanged(in Region bounds); void onRectangleOnScreenRequested(int left, int top, int right, int bottom); void onRotationChanged(int rotation); void onUserContextChanged(); diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 17f04e9..1ee2bb3 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -27,7 +27,7 @@ import android.graphics.Rect; import android.os.Bundle; import android.os.IRemoteCallback; import android.view.IApplicationToken; -import android.view.IDisplayMagnificationMediator; +import android.view.IMagnificationCallbacks; import android.view.IOnKeyguardExitResult; import android.view.IRotationWatcher; import android.view.IWindowSession; @@ -226,11 +226,6 @@ interface IWindowManager void setInputFilter(in IInputFilter filter); /** - * Gets the display magnification mediator. - */ - IDisplayMagnificationMediator getDisplayMagnificationMediator(); - - /** * Gets the frame of a window given its token. */ void getWindowFrame(IBinder token, out Rect outFrame); @@ -245,4 +240,30 @@ interface IWindowManager * credentials. */ void showAssistant(); + + /** + * Sets the display magnification callbacks. These callbacks notify + * the client for contextual changes related to display magnification. + * + * @param callbacks The magnification callbacks. + */ + void setMagnificationCallbacks(IMagnificationCallbacks callbacks); + + /** + * Sets the magnification spec to be applied to all windows that can be + * magnified. + * + * @param spec The current magnification spec. + */ + void setMagnificationSpec(in MagnificationSpec spec); + + /** + * Gets the magnification spec for a window given its token. If the + * window has a compatibility scale it is also folded in the returned + * magnification spec. + * + * @param windowToken The unique window token. + * @return The magnification spec if such or null. + */ + MagnificationSpec getCompatibleMagnificationSpecForWindow(in IBinder windowToken); } diff --git a/core/java/android/view/MagnificationSpec.java b/core/java/android/view/MagnificationSpec.java index 7fb5615..0ee6714 100644 --- a/core/java/android/view/MagnificationSpec.java +++ b/core/java/android/view/MagnificationSpec.java @@ -39,6 +39,9 @@ public class MagnificationSpec implements Parcelable { } public void initialize(float scale, float offsetX, float offsetY) { + if (scale < 1) { + throw new IllegalArgumentException("Scale must be greater than or equal to one!"); + } this.scale = scale; this.offsetX = offsetX; this.offsetY = offsetY; diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index f2c5eac..a972b75 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -812,6 +812,8 @@ public class Surface implements Parcelable { * * @param rotation The rotation. * @return The rotation symbolic name. + * + * @hide */ public static String rotationToString(int rotation) { switch (rotation) { diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 7bdb44c..47ef638 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -1134,4 +1134,23 @@ public interface WindowManagerPolicy { * {@link android.content.Intent#ACTION_ASSIST} */ public void showAssistant(); + + /** + * Returns whether a given window type can be magnified. + * + * @param windowType The window type. + * @return True if the window can be magnified. + */ + public boolean canMagnifyWindow(int windowType); + + /** + * Returns whether a given window type is considered a top level one. + * A top level window does not have a container, i.e. attached window, + * or if it has a container it is laid out as a top-level window, not + * as a child of its container. + * + * @param windowType The window type. + * @return True if the window is a top level one. + */ + public boolean isTopLevelWindow(int windowType); } diff --git a/graphics/java/android/graphics/Region.java b/graphics/java/android/graphics/Region.java index 27ea0f0..9773039 100644 --- a/graphics/java/android/graphics/Region.java +++ b/graphics/java/android/graphics/Region.java @@ -18,8 +18,15 @@ package android.graphics; import android.os.Parcel; import android.os.Parcelable; +import android.util.Pools.SynchronizedPool; public class Region implements Parcelable { + + private static final int MAX_POOL_SIZE = 10; + + private static final SynchronizedPool<Region> sPool = + new SynchronizedPool<Region>(MAX_POOL_SIZE); + /** * @hide */ @@ -291,6 +298,39 @@ public class Region implements Parcelable { return nativeToString(mNativeRegion); } + /** + * @return An instance from a pool if such or a new one. + * + * @hide + */ + public static Region obtain() { + Region region = sPool.acquire(); + return (region != null) ? region : new Region(); + } + + /** + * @return An instance from a pool if such or a new one. + * + * @param other Region to copy values from for initialization. + * + * @hide + */ + public static Region obtain(Region other) { + Region region = obtain(); + region.set(other); + return region; + } + + /** + * Recycles an instance. + * + * @hide + */ + public void recycle() { + setEmpty(); + sPool.release(this); + } + ////////////////////////////////////////////////////////////////////////// public static final Parcelable.Creator<Region> CREATOR diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 024c247..2601bd0 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -4544,6 +4544,28 @@ public class PhoneWindowManager implements WindowManagerPolicy { mKeyguardMediator.showAssistant(); } + @Override + public boolean canMagnifyWindow(int windowType) { + switch (windowType) { + 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 boolean isTopLevelWindow(int windowType) { + if (windowType >= WindowManager.LayoutParams.FIRST_SUB_WINDOW + && windowType <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { + return (windowType == WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG); + } + return true; + } + /** * Returns the human readable name of a window transition. * diff --git a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java index 45da1e1..9eb6834 100644 --- a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -18,7 +18,9 @@ package com.android.server.accessibility; import android.content.Context; import android.os.PowerManager; +import android.util.Pools.SimplePool; import android.util.Slog; +import android.view.Choreographer; import android.view.Display; import android.view.InputDevice; import android.view.InputEvent; @@ -27,6 +29,12 @@ import android.view.MotionEvent; import android.view.WindowManagerPolicy; import android.view.accessibility.AccessibilityEvent; +/** + * This class is an input filter for implementing accessibility features such + * as display magnification and explore by touch. + * + * NOTE: This class has to be created and poked only from the main thread. + */ class AccessibilityInputFilter extends InputFilter implements EventStreamTransformation { private static final String TAG = AccessibilityInputFilter.class.getSimpleName(); @@ -49,12 +57,31 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo */ static final int FLAG_FEATURE_TOUCH_EXPLORATION = 0x00000002; + private final Runnable mProcessBatchedEventsRunnable = new Runnable() { + @Override + public void run() { + final long frameTimeNanos = mChoreographer.getFrameTimeNanos(); + if (DEBUG) { + Slog.i(TAG, "Begin batch processing for frame: " + frameTimeNanos); + } + processBatchedEvents(frameTimeNanos); + if (DEBUG) { + Slog.i(TAG, "End batch processing."); + } + if (mEventQueue != null) { + scheduleProcessBatchedEvents(); + } + } + }; + private final Context mContext; private final PowerManager mPm; private final AccessibilityManagerService mAms; + private final Choreographer mChoreographer; + private int mCurrentDeviceId; private boolean mInstalled; @@ -65,11 +92,14 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo private ScreenMagnifier mScreenMagnifier; private EventStreamTransformation mEventHandler; + private MotionEventHolder mEventQueue; + AccessibilityInputFilter(Context context, AccessibilityManagerService service) { super(context.getMainLooper()); mContext = context; mAms = service; mPm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + mChoreographer = Choreographer.getInstance(); } @Override @@ -119,10 +149,61 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } mCurrentDeviceId = deviceId; } + batchMotionEvent((MotionEvent) event, policyFlags); + } + + private void scheduleProcessBatchedEvents() { + mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, + mProcessBatchedEventsRunnable, null); + } + + private void batchMotionEvent(MotionEvent event, int policyFlags) { + if (DEBUG) { + Slog.i(TAG, "Batching event: " + event + ", policyFlags: " + policyFlags); + } + if (mEventQueue == null) { + mEventQueue = MotionEventHolder.obtain(event, policyFlags); + scheduleProcessBatchedEvents(); + return; + } + if (mEventQueue.event.addBatch(event)) { + return; + } + MotionEventHolder holder = MotionEventHolder.obtain(event, policyFlags); + holder.next = mEventQueue; + mEventQueue.previous = holder; + mEventQueue = holder; + } + + private void processBatchedEvents(long frameNanos) { + MotionEventHolder current = mEventQueue; + while (current.next != null) { + current = current.next; + } + while (true) { + if (current == null) { + mEventQueue = null; + break; + } + if (current.event.getEventTimeNano() >= frameNanos) { + // Finished with this choreographer frame. Do the rest on the next one. + current.next = null; + break; + } + handleMotionEvent(current.event, current.policyFlags); + MotionEventHolder prior = current; + current = current.previous; + prior.recycle(); + } + } + + private void handleMotionEvent(MotionEvent event, int policyFlags) { + if (DEBUG) { + Slog.i(TAG, "Handling batched event: " + event + ", policyFlags: " + policyFlags); + } mPm.userActivity(event.getEventTime(), false); - MotionEvent rawEvent = (MotionEvent) event; - MotionEvent transformedEvent = MotionEvent.obtain(rawEvent); - mEventHandler.onMotionEvent(transformedEvent, rawEvent, policyFlags); + MotionEvent transformedEvent = MotionEvent.obtain(event); + mEventHandler.onMotionEvent(transformedEvent, event, policyFlags); transformedEvent.recycle(); } @@ -203,4 +284,34 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo public void onDestroy() { /* ignore */ } + + private static class MotionEventHolder { + private static final int MAX_POOL_SIZE = 32; + private static final SimplePool<MotionEventHolder> sPool = + new SimplePool<MotionEventHolder>(MAX_POOL_SIZE); + + public int policyFlags; + public MotionEvent event; + public MotionEventHolder next; + public MotionEventHolder previous; + + public static MotionEventHolder obtain(MotionEvent event, int policyFlags) { + MotionEventHolder holder = sPool.acquire(); + if (holder == null) { + holder = new MotionEventHolder(); + } + holder.event = MotionEvent.obtain(event); + holder.policyFlags = policyFlags; + return holder; + } + + public void recycle() { + event.recycle(); + event = null; + policyFlags = 0; + next = null; + previous = null; + sPool.release(this); + } + } } diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java index 1d0e3a3..3e3e7dc 100644 --- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -65,7 +65,6 @@ 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; @@ -159,8 +158,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private final MainHandler mMainHandler; - private IDisplayMagnificationMediator mMagnificationMediator; - private Service mUiAutomationService; private Service mQueryBridge; @@ -539,7 +536,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { accessibilityServiceInfo, true); mUiAutomationService.onServiceConnected(componentName, serviceClient.asBinder()); - updateInputFilterLocked(userState); + scheduleUpdateInputFilter(userState); scheduleSendStateToClientsLocked(userState); } } @@ -570,7 +567,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { userState.mTouchExplorationGrantedServices.add(service); // Update the internal state. performServiceManagementLocked(userState); - updateInputFilterLocked(userState); + scheduleUpdateInputFilter(userState); scheduleSendStateToClientsLocked(userState); } } @@ -736,7 +733,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { mTempStateChangeForCurrentUserMemento.clear(); // Update the internal state. performServiceManagementLocked(userState); - updateInputFilterLocked(userState); + scheduleUpdateInputFilter(userState); scheduleSendStateToClientsLocked(userState); } } @@ -890,7 +887,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { service.linkToOwnDeath(); userState.mServices.add(service); userState.mComponentNameToServiceMap.put(service.mComponentName, service); - updateInputFilterLocked(userState); + scheduleUpdateInputFilter(userState); tryEnableTouchExplorationLocked(service); } catch (RemoteException e) { /* do nothing */ @@ -912,7 +909,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { userState.mComponentNameToServiceMap.remove(service.mComponentName); service.unlinkToOwnDeath(); service.dispose(); - updateInputFilterLocked(userState); + scheduleUpdateInputFilter(userState); tryDisableTouchExplorationLocked(service); return removed; } @@ -1090,7 +1087,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } - private void updateInputFilterLocked(UserState userState) { + private void scheduleUpdateInputFilter(UserState userState) { + mMainHandler.obtainMessage(MainHandler.MSG_UPDATE_INPUT_FILTER, userState).sendToTarget(); + } + + private void updateInputFilter(UserState userState) { boolean setInputFilter = false; AccessibilityInputFilter inputFilter = null; synchronized (mLock) { @@ -1200,7 +1201,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { handleAccessibilityEnabledSettingChangedLocked(userState); performServiceManagementLocked(userState); - updateInputFilterLocked(userState); + scheduleUpdateInputFilter(userState); scheduleSendStateToClientsLocked(userState); } @@ -1355,6 +1356,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public static final int MSG_SEND_RECREATE_INTERNAL_STATE = 4; public static final int MSG_UPDATE_ACTIVE_WINDOW = 5; public static final int MSG_ANNOUNCE_NEW_USER_IF_NEEDED = 6; + public static final int MSG_UPDATE_INPUT_FILTER = 7; public MainHandler(Looper looper) { super(looper); @@ -1398,6 +1400,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { case MSG_ANNOUNCE_NEW_USER_IF_NEEDED: { announceNewUserIfNeeded(); } break; + case MSG_UPDATE_INPUT_FILTER: { + UserState userState = (UserState) msg.obj; + updateInputFilter(userState); + } break; } } @@ -2220,16 +2226,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private MagnificationSpec getCompatibleMagnificationSpec(int windowId) { try { - if (mMagnificationMediator == null) { - mMagnificationMediator = mWindowManagerService - .getDisplayMagnificationMediator(); - } IBinder windowToken = mGlobalWindowTokens.get(windowId); if (windowToken == null) { windowToken = getCurrentUserStateLocked().mWindowTokens.get(windowId); } if (windowToken != null) { - return mMagnificationMediator.getCompatibleMagnificationSpec(windowToken); + return mWindowManagerService.getCompatibleMagnificationSpecForWindow( + windowToken); } } catch (RemoteException re) { /* ignore */ @@ -2581,7 +2584,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { UserState userState = getCurrentUserStateLocked(); handleAccessibilityEnabledSettingChangedLocked(userState); performServiceManagementLocked(userState); - updateInputFilterLocked(userState); + scheduleUpdateInputFilter(userState); scheduleSendStateToClientsLocked(userState); } } @@ -2591,7 +2594,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { if (mUiAutomationService == null) { UserState userState = getCurrentUserStateLocked(); handleTouchExplorationEnabledSettingChangedLocked(userState); - updateInputFilterLocked(userState); + scheduleUpdateInputFilter(userState); scheduleSendStateToClientsLocked(userState); } } @@ -2601,7 +2604,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { if (mUiAutomationService == null) { UserState userState = getCurrentUserStateLocked(); handleDisplayMagnificationEnabledSettingChangedLocked(userState); - updateInputFilterLocked(userState); + scheduleUpdateInputFilter(userState); scheduleSendStateToClientsLocked(userState); } } diff --git a/services/java/com/android/server/accessibility/ScreenMagnifier.java b/services/java/com/android/server/accessibility/ScreenMagnifier.java index e5f0924..1bf2c42 100644 --- a/services/java/com/android/server/accessibility/ScreenMagnifier.java +++ b/services/java/com/android/server/accessibility/ScreenMagnifier.java @@ -16,25 +16,17 @@ package com.android.server.accessibility; -import android.animation.Animator; -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; import android.content.IntentFilter; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.PixelFormat; -import android.graphics.PorterDuff.Mode; import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.hardware.display.DisplayManager; -import android.hardware.display.DisplayManager.DisplayListener; +import android.graphics.Region; import android.os.AsyncTask; +import android.os.Binder; import android.os.Handler; import android.os.Message; import android.os.RemoteException; @@ -44,13 +36,9 @@ import android.provider.Settings; import android.text.TextUtils; import android.util.Property; import android.util.Slog; -import android.view.Display; -import android.view.DisplayInfo; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; -import android.view.Gravity; -import android.view.IDisplayMagnificationController; -import android.view.IDisplayMagnificationMediator; +import android.view.IMagnificationCallbacks; import android.view.IWindowManager; import android.view.MagnificationSpec; import android.view.MotionEvent; @@ -60,13 +48,9 @@ import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector.OnScaleGestureListener; import android.view.View; import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.animation.DecelerateInterpolator; -import android.view.animation.Interpolator; -import com.android.internal.R; import com.android.internal.os.SomeArgs; import java.util.Locale; @@ -110,7 +94,7 @@ import java.util.Locale; * * 6. The magnification scale will be persisted in settings and in the cloud. */ -public final class ScreenMagnifier extends IDisplayMagnificationController.Stub +public final class ScreenMagnifier extends IMagnificationCallbacks.Stub implements EventStreamTransformation { private static final String LOG_TAG = ScreenMagnifier.class.getSimpleName(); @@ -121,7 +105,6 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub private static final boolean DEBUG_PANNING = false; private static final boolean DEBUG_SCALING = false; private static final boolean DEBUG_MAGNIFICATION_CONTROLLER = false; - private static final boolean DEBUG_VIEWPORT_WINDOW = false; private static final int STATE_DELEGATING = 1; private static final int STATE_DETECTING = 2; @@ -131,22 +114,22 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub private static final float DEFAULT_MAGNIFICATION_SCALE = 2.0f; private static final int MULTI_TAP_TIME_SLOP_ADJUSTMENT = 50; - private static final int MESSAGE_ON_MAGNIFIED_FRAME_CHANGED = 1; + private static final int MESSAGE_ON_MAGNIFIED_BOUNDS_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 static final int DEFAULT_SCREEN_MAGNIFICATION_AUTO_UPDATE = 1; - private static final float DEFAULT_WINDOW_ANIMATION_SCALE = 1.0f; + + private static final int MY_PID = android.os.Process.myPid(); private final Rect mTempRect = new Rect(); private final Rect mTempRect1 = new Rect(); private final Context mContext; + private final IWindowManager mWindowManager; private final MagnificationController mMagnificationController; private final ScreenStateObserver mScreenStateObserver; - private final ViewportWindow mViewportWindow; private final DetectingStateHandler mDetectingStateHandler; private final MagnifiedContentInteractonStateHandler mMagnifiedContentInteractonStateHandler; @@ -161,11 +144,8 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub private final int mMultiTapDistanceSlop; private final long mLongAnimationDuration; - private final float mWindowAnimationScale; - - private final Rect mMagnifiedFrame = new Rect(); - private IDisplayMagnificationMediator mDisplayMagnifier; + private final Region mMagnifiedBounds = new Region(); private EventStreamTransformation mNext; @@ -178,18 +158,16 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub private long mDelegatingStateDownTime; + private boolean mUpdateMagnificationSpecOnNextBoundsChange; + 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(); + case MESSAGE_ON_MAGNIFIED_BOUNDS_CHANGED: { + Region bounds = (Region) message.obj; + handleOnMagnifiedBoundsChanged(bounds); + bounds.recycle(); } break; case MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED: { SomeArgs args = (SomeArgs) message.obj; @@ -207,23 +185,20 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub 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 = IWindowManager.Stub.asInterface( + ServiceManager.getService("window")); mAms = service; mLongAnimationDuration = context.getResources().getInteger( com.android.internal.R.integer.config_longAnimTime); mTapDistanceSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mMultiTapDistanceSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop(); - mWindowAnimationScale = Settings.Global.getFloat(context.getContentResolver(), - Settings.Global.WINDOW_ANIMATION_SCALE, DEFAULT_WINDOW_ANIMATION_SCALE); mDetectingStateHandler = new DetectingStateHandler(); mStateViewportDraggingHandler = new StateViewportDraggingHandler(); @@ -231,15 +206,10 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub context); mMagnificationController = new MagnificationController(mLongAnimationDuration); - mViewportWindow = new ViewportWindow(context, displayId); - mScreenStateObserver = new ScreenStateObserver(context, mMagnificationController, - mViewportWindow); + mScreenStateObserver = new ScreenStateObserver(context, mMagnificationController); try { - IWindowManager windowManagerService = IWindowManager.Stub.asInterface( - ServiceManager.getService("window")); - mDisplayMagnifier = windowManagerService.getDisplayMagnificationMediator(); - mDisplayMagnifier.addController(Display.DEFAULT_DISPLAY, this); + mWindowManager.setMagnificationCallbacks(this); } catch (RemoteException re) { /* ignore */ } @@ -248,18 +218,30 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub } @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(); + public void onMagnifedBoundsChanged(Region bounds) { + Region newBounds = Region.obtain(bounds); + mHandler.obtainMessage(MESSAGE_ON_MAGNIFIED_BOUNDS_CHANGED, newBounds).sendToTarget(); + if (MY_PID != Binder.getCallingPid()) { + bounds.recycle(); + } } - private void handleOnMagnifiedFrameChanged(int left, int top, int right, int bottom) { - mMagnifiedFrame.set(left, top, right, bottom); - mViewportWindow.setBounds(mMagnifiedFrame, mMagnificationController.isMagnifying()); + private void handleOnMagnifiedBoundsChanged(Region bounds) { + // If there was a rotation we have to update the center of the magnified + // region since the old offset X/Y may be out of its acceptable range for + // the new display width and height. + if (mUpdateMagnificationSpecOnNextBoundsChange) { + mUpdateMagnificationSpecOnNextBoundsChange = false; + MagnificationSpec spec = mMagnificationController.getMagnificationSpec(); + Rect magnifiedFrame = mTempRect; + mMagnifiedBounds.getBounds(magnifiedFrame); + final float scale = spec.scale; + final float centerX = (-spec.offsetX + magnifiedFrame.width() / 2) / scale; + final float centerY = (-spec.offsetY + magnifiedFrame.height() / 2) / scale; + mMagnificationController.setScaleAndMagnifiedRegionCenter(scale, centerX, + centerY, false); + } + mMagnifiedBounds.set(bounds); mAms.onMagnificationStateChanged(); } @@ -274,35 +256,35 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub } 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)) { + Rect magnifiedFrame = mTempRect; + mMagnifiedBounds.getBounds(magnifiedFrame); + if (!magnifiedFrame.intersects(left, top, right, bottom)) { return; } Rect magnifFrameInScreenCoords = mTempRect1; getMagnifiedFrameInContentCoords(magnifFrameInScreenCoords); final float scrollX; final float scrollY; - if (rectangle.width() > magnifFrameInScreenCoords.width()) { + if (right - left > magnifFrameInScreenCoords.width()) { final int direction = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()); if (direction == View.LAYOUT_DIRECTION_LTR) { - scrollX = rectangle.left - magnifFrameInScreenCoords.left; + scrollX = left - magnifFrameInScreenCoords.left; } else { - scrollX = rectangle.right - magnifFrameInScreenCoords.right; + scrollX = 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 if (left < magnifFrameInScreenCoords.left) { + scrollX = left - magnifFrameInScreenCoords.left; + } else if (right > magnifFrameInScreenCoords.right) { + scrollX = 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; + if (bottom - top > magnifFrameInScreenCoords.height()) { + scrollY = top - magnifFrameInScreenCoords.top; + } else if (top < magnifFrameInScreenCoords.top) { + scrollY = top - magnifFrameInScreenCoords.top; + } else if (bottom > magnifFrameInScreenCoords.bottom) { + scrollY = bottom - magnifFrameInScreenCoords.bottom; } else { scrollY = 0; } @@ -310,13 +292,6 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub 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(); @@ -324,12 +299,8 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub 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); + mUpdateMagnificationSpecOnNextBoundsChange = true; } } @@ -342,11 +313,17 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub resetMagnificationIfNeeded(); } + private void getMagnifiedFrameInContentCoords(Rect rect) { + MagnificationSpec spec = mMagnificationController.getMagnificationSpec(); + mMagnifiedBounds.getBounds(rect); + rect.offset((int) -spec.offsetX, (int) -spec.offsetY); + rect.scale(1.0f / spec.scale); + } + private void resetMagnificationIfNeeded() { if (mMagnificationController.isMagnifying() && isScreenMagnificationAutoUpdateEnabled(mContext)) { mMagnificationController.reset(true); - mViewportWindow.setShown(false, true); } } @@ -401,7 +378,11 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub @Override public void onDestroy() { mScreenStateObserver.destroy(); - mViewportWindow.destroy(); + try { + mWindowManager.setMagnificationCallbacks(null); + } catch (RemoteException re) { + /* ignore */ + } } private void handleMotionEventStateDelegating(MotionEvent event, @@ -423,7 +404,7 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub final float eventX = event.getX(); final float eventY = event.getY(); if (mMagnificationController.isMagnifying() - && mMagnifiedFrame.contains((int) eventX, (int) eventY)) { + && mMagnifiedBounds.contains((int) eventX, (int) eventY)) { final float scale = mMagnificationController.getScale(); final float scaledOffsetX = mMagnificationController.getOffsetX(); final float scaledOffsetY = mMagnificationController.getOffsetY(); @@ -619,7 +600,7 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub } final float eventX = event.getX(); final float eventY = event.getY(); - if (mMagnifiedFrame.contains((int) eventX, (int) eventY)) { + if (mMagnifiedBounds.contains((int) eventX, (int) eventY)) { if (mLastMoveOutsideMagnifiedRegion) { mLastMoveOutsideMagnifiedRegion = false; mMagnificationController.setMagnifiedRegionCenter(eventX, @@ -635,7 +616,6 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub case MotionEvent.ACTION_UP: { if (!mTranslationEnabledBeforePan) { mMagnificationController.reset(true); - mViewportWindow.setShown(false, true); } clear(); transitionToState(STATE_DETECTING); @@ -693,7 +673,7 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub switch (action) { case MotionEvent.ACTION_DOWN: { mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE); - if (!mMagnifiedFrame.contains((int) event.getX(), + if (!mMagnifiedBounds.contains((int) event.getX(), (int) event.getY())) { transitionToDelegatingStateAndClear(); return; @@ -735,7 +715,7 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub return; } mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD); - if (!mMagnifiedFrame.contains((int) event.getX(), (int) event.getY())) { + if (!mMagnifiedBounds.contains((int) event.getX(), (int) event.getY())) { transitionToDelegatingStateAndClear(); return; } @@ -861,10 +841,8 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub if (!mMagnificationController.isMagnifying()) { mMagnificationController.setScaleAndMagnifiedRegionCenter(getPersistedScale(), up.getX(), up.getY(), true); - mViewportWindow.setShown(true, true); } else { mMagnificationController.reset(true); - mViewportWindow.setShown(false, true); } } @@ -876,7 +854,6 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub mTranslationEnabledBeforePan = mMagnificationController.isMagnifying(); mMagnificationController.setScaleAndMagnifiedRegionCenter(getPersistedScale(), down.getX(), down.getY(), true); - mViewportWindow.setShown(true, true); transitionToState(STATE_VIEWPORT_DRAGGING); } } @@ -1043,10 +1020,12 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub } public void setScale(float scale, float pivotX, float pivotY, boolean animate) { + Rect magnifiedFrame = mTempRect; + mMagnifiedBounds.getBounds(magnifiedFrame); MagnificationSpec spec = mCurrentMagnificationSpec; 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 oldCenterX = (-spec.offsetX + magnifiedFrame.width() / 2) / oldScale; + final float oldCenterY = (-spec.offsetY + magnifiedFrame.height() / 2) / oldScale; final float normPivotX = (-spec.offsetX + pivotX) / oldScale; final float normPivotY = (-spec.offsetY + pivotY) / oldScale; final float offsetX = (oldCenterX - normPivotX) * (oldScale / scale); @@ -1099,24 +1078,30 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub public void updateMagnificationSpec(float scale, float magnifiedCenterX, float magnifiedCenterY) { + Rect magnifiedFrame = mTempRect; + mMagnifiedBounds.getBounds(magnifiedFrame); mCurrentMagnificationSpec.scale = scale; - final int viewportWidth = mMagnifiedFrame.width(); + final int viewportWidth = magnifiedFrame.width(); final float nonNormOffsetX = viewportWidth / 2 - magnifiedCenterX * scale; mCurrentMagnificationSpec.offsetX = Math.min(Math.max(nonNormOffsetX, getMinOffsetX()), 0); - final int viewportHeight = mMagnifiedFrame.height(); + final int viewportHeight = magnifiedFrame.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(); + Rect magnifiedFrame = mTempRect; + mMagnifiedBounds.getBounds(magnifiedFrame); + final float viewportWidth = magnifiedFrame.width(); return viewportWidth - viewportWidth * mCurrentMagnificationSpec.scale; } private float getMinOffsetY() { - final float viewportHeight = mMagnifiedFrame.height(); + Rect magnifiedFrame = mTempRect; + mMagnifiedBounds.getBounds(magnifiedFrame); + final float viewportHeight = magnifiedFrame.height(); return viewportHeight - viewportHeight * mCurrentMagnificationSpec.scale; } @@ -1138,7 +1123,7 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub mSentMagnificationSpec.scale = spec.scale; mSentMagnificationSpec.offsetX = spec.offsetX; mSentMagnificationSpec.offsetY = spec.offsetY; - mDisplayMagnifier.setMagnificationSpec(ScreenMagnifier.this, + mWindowManager.setMagnificationSpec( MagnificationSpec.obtain(spec)); } catch (RemoteException re) { /* ignore */ @@ -1146,12 +1131,11 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub } } - private static final class ScreenStateObserver extends BroadcastReceiver { + private final class ScreenStateObserver extends BroadcastReceiver { private static final int MESSAGE_ON_SCREEN_STATE_CHANGE = 1; private final Context mContext; private final MagnificationController mMagnificationController; - private final ViewportWindow mViewportWindow; private final Handler mHandler = new Handler() { @Override @@ -1166,10 +1150,9 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub }; public ScreenStateObserver(Context context, - MagnificationController magnificationController, ViewportWindow viewportWindow) { + MagnificationController magnificationController) { mContext = context; mMagnificationController = magnificationController; - mViewportWindow = viewportWindow; mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_SCREEN_OFF)); } @@ -1181,282 +1164,13 @@ public final class ScreenMagnifier extends IDisplayMagnificationController.Stub public void onReceive(Context context, Intent intent) { mHandler.obtainMessage(MESSAGE_ON_SCREEN_STATE_CHANGE, intent.getAction()).sendToTarget(); - } + } private void handleOnScreenStateChange(String action) { if (mMagnificationController.isMagnifying() && isScreenMagnificationAutoUpdateEnabled(mContext)) { mMagnificationController.reset(false); - mViewportWindow.setShown(false, false); - } - } - } - - 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 Rect mBounds = new Rect(); - - private final ValueAnimator mResizeFrameAnimator; - private final ValueAnimator mShowHideFrameAnimator; - - private final WindowManager mWindowManager; - private final DisplayManager mDisplayManager; - private final Display mDisplay; - private final DisplayInfo mDisplayInfo = new DisplayInfo(); - private final int mDisplayId; - - private final ContentView mWindowContent; - private final WindowManager.LayoutParams mWindowParams; - - private boolean mShown; - private int mAlpha; - - public ViewportWindow(Context context, int displayId) { - mWindowManager = (WindowManager) context.getSystemService(Service.WINDOW_SERVICE); - - mDisplayId = displayId; - mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE); - mDisplay = mDisplayManager.getDisplay(displayId); - mDisplayManager.registerDisplayListener(this, null); - updateDisplayInfo(); - - 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)) { - 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 */ - } - }); - - Property<ViewportWindow, Rect> property = Property.of(ViewportWindow.class, - Rect.class, PROPERTY_NAME_BOUNDS); - TypeEvaluator<Rect> evaluator = new TypeEvaluator<Rect>() { - private final Rect mReusableResultRect = new Rect(); - - @Override - public Rect evaluate(float fraction, Rect fromFrame, Rect toFrame) { - Rect result = mReusableResultRect; - result.left = (int) (fromFrame.left - + (toFrame.left - fromFrame.left) * fraction); - result.top = (int) (fromFrame.top - + (toFrame.top - fromFrame.top) * fraction); - result.right = (int) (fromFrame.right - + (toFrame.right - fromFrame.right) * fraction); - result.bottom = (int) (fromFrame.bottom - + (toFrame.bottom - fromFrame.bottom) * fraction); - return result; - } - }; - mResizeFrameAnimator = ObjectAnimator.ofObject(this, property, - evaluator, mBounds, mBounds); - mResizeFrameAnimator.setDuration(longAnimationDuration); - mResizeFrameAnimator.setInterpolator(interpolator); - } - - public boolean isShown() { - return mShown; - } - - public void setShown(boolean shown, boolean animate) { - if (isShown() == shown) { - return; - } - if (animate) { - if (mShowHideFrameAnimator.isRunning()) { - mShowHideFrameAnimator.reverse(); - } else { - if (shown) { - show(); - mShowHideFrameAnimator.start(); - } else { - mShowHideFrameAnimator.reverse(); - } - } - } else { - mShowHideFrameAnimator.cancel(); - if (shown) { - show(); - } else { - hide(); - } - } - } - - 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(getBounds(), bounds); - mResizeFrameAnimator.start(); - } else { - setBounds(bounds); - } - } - - private void hide() { - if (!mShown) { - return; - } - mShown = false; - mWindowManager.removeView(mWindowContent); - if (DEBUG_VIEWPORT_WINDOW) { - Slog.i(LOG_TAG, "ViewportWindow hidden."); - } - } - - @SuppressWarnings("unused") - // Called reflectively from an animator. - public int getAlpha() { - return mAlpha; - } - - @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 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); - } - } - - public void rotationChanged() { - mWindowParams.width = mDisplayInfo.logicalWidth; - mWindowParams.height = mDisplayInfo.logicalHeight; - if (mShown) { - mWindowManager.updateViewLayout(mWindowContent, mWindowParams); - } - } - - 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); - } - } - - private void updateDisplayInfo() { - if (!mDisplay.getDisplayInfo(mDisplayInfo)) { - Slog.e(LOG_TAG, "Display is not valid."); - } - } - - public void destroy() { - setShown(false, true); - mDisplayManager.unregisterDisplayListener(this); - } - - @Override - public void onDisplayChanged(int displayId) { - if (mDisplayId == displayId) { - updateDisplayInfo(); - } - } - - @Override - public void onDisplayAdded(int displayId) { - /* do nothing */ - } - - @Override - public void onDisplayRemoved(int displayId) { - /* do nothing */ } } } diff --git a/services/java/com/android/server/wm/DisplayMagnificationMediator.java b/services/java/com/android/server/wm/DisplayMagnificationMediator.java deleted file mode 100644 index 0e3a6e1..0000000 --- a/services/java/com/android/server/wm/DisplayMagnificationMediator.java +++ /dev/null @@ -1,614 +0,0 @@ -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<DisplayState> mDisplayStates = - new SparseArray<DisplayMagnificationMediator.DisplayState>(); - - 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(); - 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<WindowStateInfo> mTempWindowStateInfoList = - new ArrayList<WindowStateInfo>(); - - 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<WindowStateInfo> mWindowInfoInverseComparator = - new Comparator<WindowStateInfo>() { - @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<WindowStateInfo> 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<WindowStateInfo> 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<WindowStateInfo> sPool = - new SynchronizedPool<WindowStateInfo>(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/DisplayMagnifier.java b/services/java/com/android/server/wm/DisplayMagnifier.java new file mode 100644 index 0000000..cd5ae4b --- /dev/null +++ b/services/java/com/android/server/wm/DisplayMagnifier.java @@ -0,0 +1,732 @@ +/* + * 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; + +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.app.Service; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PixelFormat; +import android.graphics.Point; +import android.graphics.PorterDuff.Mode; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Region; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.util.Pools.SimplePool; +import android.util.Slog; +import android.util.SparseArray; +import android.util.TypedValue; +import android.view.IMagnificationCallbacks; +import android.view.MagnificationSpec; +import android.view.Surface; +import android.view.Surface.OutOfResourcesException; +import android.view.WindowManager; +import android.view.WindowManagerPolicy; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; + +import com.android.internal.R; +import com.android.internal.os.SomeArgs; +import com.android.internal.policy.impl.PhoneWindowManager; + +/** + * This class is a part of the window manager and encapsulates the + * functionality related to display magnification. + */ +final class DisplayMagnifier { + private static final String LOG_TAG = DisplayMagnifier.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 boolean DEBUG_VIEWPORT_WINDOW = false; + + private final Rect mTempRect1 = new Rect(); + private final Rect mTempRect2 = new Rect(); + + private final Region mTempRegion1 = new Region(); + private final Region mTempRegion2 = new Region(); + private final Region mTempRegion3 = new Region(); + private final Region mTempRegion4 = new Region(); + + private final Context mContext; + private final WindowManagerService mWindowManagerService; + private final MagnifiedViewport mMagnifedViewport; + private final Handler mHandler; + + private final IMagnificationCallbacks mCallbacks; + + private final long mLongAnimationDuration; + + public DisplayMagnifier(WindowManagerService windowManagerService, + IMagnificationCallbacks callbacks) { + mContext = windowManagerService.mContext; + mWindowManagerService = windowManagerService; + mCallbacks = callbacks; + mMagnifedViewport = new MagnifiedViewport(); + mHandler = new MyHandler(mWindowManagerService.mH.getLooper()); + mLongAnimationDuration = mContext.getResources().getInteger( + com.android.internal.R.integer.config_longAnimTime); + } + + public void setMagnificationSpecLocked(MagnificationSpec spec) { + mMagnifedViewport.updateMagnificationSpecLocked(spec); + mMagnifedViewport.recomputeBoundsLocked(); + mWindowManagerService.scheduleAnimationLocked(); + } + + public void onRectangleOnScreenRequestedLocked(Rect rectangle, boolean immediate) { + if (DEBUG_RECTANGLE_REQUESTED) { + Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle); + } + if (!mMagnifedViewport.isMagnifyingLocked()) { + return; + } + Rect magnifiedRegionBounds = mTempRect2; + mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds); + if (magnifiedRegionBounds.contains(rectangle)) { + return; + } + SomeArgs args = SomeArgs.obtain(); + args.argi1 = rectangle.left; + args.argi2 = rectangle.top; + args.argi3 = rectangle.right; + args.argi4 = rectangle.bottom; + mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED, + args).sendToTarget(); + } + + public void onWindowLayersChangedLocked() { + if (DEBUG_LAYERS) { + Slog.i(LOG_TAG, "Layers changed."); + } + mMagnifedViewport.recomputeBoundsLocked(); + mWindowManagerService.scheduleAnimationLocked(); + } + + public void onRotationChangedLocked(DisplayContent displayContent, int rotation) { + if (DEBUG_ROTATION) { + Slog.i(LOG_TAG, "Rotaton: " + Surface.rotationToString(rotation) + + " displayId: " + displayContent.getDisplayId()); + } + mMagnifedViewport.onRotationChangedLocked(); + mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED); + } + + public void onWindowTransitionLocked(WindowState windowState, int transition) { + if (DEBUG_WINDOW_TRANSITIONS) { + Slog.i(LOG_TAG, "Window transition: " + + PhoneWindowManager.windowTransitionToString(transition) + + " displayId: " + windowState.getDisplayId()); + } + final boolean magnifying = mMagnifedViewport.isMagnifyingLocked(); + 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.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED); + } + } + } + final int type = windowState.mAttrs.type; + 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 = mTempRect2; + mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked( + magnifiedRegionBounds); + Rect touchableRegionBounds = mTempRect1; + windowState.getTouchableRegion(mTempRegion1); + mTempRegion1.getBounds(touchableRegionBounds); + if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) { + try { + mCallbacks.onRectangleOnScreenRequested( + touchableRegionBounds.left, + touchableRegionBounds.top, + touchableRegionBounds.right, + touchableRegionBounds.bottom); + } catch (RemoteException re) { + /* ignore */ + } + } + } break; + } break; + } + } + } + + public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) { + MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked(); + if (spec != null && !spec.isNop()) { + WindowManagerPolicy policy = mWindowManagerService.mPolicy; + final int windowType = windowState.mAttrs.type; + if (!policy.isTopLevelWindow(windowType) && windowState.mAttachedWindow != null + && !policy.canMagnifyWindow(windowType)) { + return null; + } + if (!policy.canMagnifyWindow(windowState.mAttrs.type)) { + return null; + } + } + return spec; + } + + /** NOTE: This has to be called within a surface transaction. */ + public void drawMagnifiedRegionBorderIfNeededLocked() { + mMagnifedViewport.drawWindowIfNeededLocked(); + } + + private final class MagnifiedViewport { + + private static final int DEFAUTLT_BORDER_WIDTH_DIP = 5; + + private final SparseArray<WindowStateInfo> mTempWindowStateInfos = + new SparseArray<WindowStateInfo>(); + + private final float[] mTempFloats = new float[9]; + + private final RectF mTempRectF = new RectF(); + + private final Point mTempPoint = new Point(); + + private final Matrix mTempMatrix = new Matrix(); + + private final Region mMagnifiedBounds = new Region(); + private final Region mOldMagnifiedBounds = new Region(); + + private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain(); + + private final WindowManager mWindowManager; + + private final int mBorderWidth; + private final int mHalfBorderWidth; + + private ViewportWindow mWindow; + + private boolean mFullRedrawNeeded; + + public MagnifiedViewport() { + mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE); + mBorderWidth = (int) TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, DEFAUTLT_BORDER_WIDTH_DIP, + mContext.getResources().getDisplayMetrics()); + mHalfBorderWidth = (int) (mBorderWidth + 0.5) / 2; + mWindow = new ViewportWindow(mContext); + } + + public void updateMagnificationSpecLocked(MagnificationSpec spec) { + if (spec != null) { + mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY); + } else { + mMagnificationSpec.clear(); + } + // If this message is pending we are in a rotation animation and do not want + // to show the border. We will do so when the pending message is handled. + if (!mHandler.hasMessages(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) { + setMagnifiedRegionBorderShownLocked(isMagnifyingLocked(), true); + } + } + + public void recomputeBoundsLocked() { + mWindowManager.getDefaultDisplay().getRealSize(mTempPoint); + final int screenWidth = mTempPoint.x; + final int screenHeight = mTempPoint.y; + + Region magnifiedBounds = mMagnifiedBounds; + magnifiedBounds.set(0, 0, 0, 0); + + Region availableBounds = mTempRegion1; + availableBounds.set(0, 0, screenWidth, screenHeight); + + Region nonMagnifiedBounds = mTempRegion4; + nonMagnifiedBounds.set(0, 0, 0, 0); + + SparseArray<WindowStateInfo> visibleWindows = mTempWindowStateInfos; + visibleWindows.clear(); + getWindowsOnScreenLocked(visibleWindows); + + final int visibleWindowCount = visibleWindows.size(); + for (int i = visibleWindowCount - 1; i >= 0; i--) { + WindowStateInfo info = visibleWindows.valueAt(i); + if (info.mWindowState.mAttrs.type == WindowManager + .LayoutParams.TYPE_MAGNIFICATION_OVERLAY) { + continue; + } + + Region windowBounds = mTempRegion2; + Matrix matrix = mTempMatrix; + populateTransformationMatrix(info.mWindowState, matrix); + RectF windowFrame = mTempRectF; + + if (mWindowManagerService.mPolicy.canMagnifyWindow(info.mWindowState.mAttrs.type)) { + windowFrame.set(info.mWindowState.mFrame); + windowFrame.offset(-windowFrame.left, -windowFrame.top); + matrix.mapRect(windowFrame); + windowBounds.set((int) windowFrame.left, (int) windowFrame.top, + (int) windowFrame.right, (int) windowFrame.bottom); + magnifiedBounds.op(windowBounds, Region.Op.UNION); + magnifiedBounds.op(availableBounds, Region.Op.INTERSECT); + } else { + windowFrame.set(info.mTouchableRegion); + windowFrame.offset(-info.mWindowState.mFrame.left, + -info.mWindowState.mFrame.top); + matrix.mapRect(windowFrame); + windowBounds.set((int) windowFrame.left, (int) windowFrame.top, + (int) windowFrame.right, (int) windowFrame.bottom); + nonMagnifiedBounds.op(windowBounds, Region.Op.UNION); + windowBounds.op(magnifiedBounds, Region.Op.DIFFERENCE); + availableBounds.op(windowBounds, Region.Op.DIFFERENCE); + } + + Region accountedBounds = mTempRegion2; + accountedBounds.set(magnifiedBounds); + accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION); + accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT); + + if (accountedBounds.isRect()) { + Rect accountedFrame = mTempRect1; + accountedBounds.getBounds(accountedFrame); + if (accountedFrame.width() == screenWidth + && accountedFrame.height() == screenHeight) { + break; + } + } + } + + for (int i = visibleWindowCount - 1; i >= 0; i--) { + WindowStateInfo info = visibleWindows.valueAt(i); + info.recycle(); + visibleWindows.removeAt(i); + } + + magnifiedBounds.op(mHalfBorderWidth, mHalfBorderWidth, + screenWidth - mHalfBorderWidth, screenHeight - mHalfBorderWidth, + Region.Op.INTERSECT); + + if (!mOldMagnifiedBounds.equals(magnifiedBounds)) { + Region bounds = Region.obtain(); + bounds.set(magnifiedBounds); + mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED, + bounds).sendToTarget(); + + mWindow.setBounds(magnifiedBounds); + Rect dirtyRect = mTempRect1; + if (mFullRedrawNeeded) { + mFullRedrawNeeded = false; + dirtyRect.set(mHalfBorderWidth, mHalfBorderWidth, + screenWidth - mHalfBorderWidth, screenHeight - mHalfBorderWidth); + mWindow.invalidate(dirtyRect); + } else { + Region dirtyRegion = mTempRegion3; + dirtyRegion.set(magnifiedBounds); + dirtyRegion.op(mOldMagnifiedBounds, Region.Op.UNION); + dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT); + dirtyRegion.getBounds(dirtyRect); + mWindow.invalidate(dirtyRect); + } + + mOldMagnifiedBounds.set(magnifiedBounds); + } + } + + private void populateTransformationMatrix(WindowState windowState, Matrix outMatrix) { + mTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx; + mTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx; + mTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDsDy; + mTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDtDy; + mTempFloats[Matrix.MTRANS_X] = windowState.mShownFrame.left; + mTempFloats[Matrix.MTRANS_Y] = windowState.mShownFrame.top; + mTempFloats[Matrix.MPERSP_0] = 0; + mTempFloats[Matrix.MPERSP_1] = 0; + mTempFloats[Matrix.MPERSP_2] = 1; + outMatrix.setValues(mTempFloats); + } + + private void getWindowsOnScreenLocked(SparseArray<WindowStateInfo> outWindowStates) { + DisplayContent displayContent = mWindowManagerService.getDefaultDisplayContentLocked(); + WindowList windowList = displayContent.getWindowList(); + final int windowCount = windowList.size(); + for (int i = 0; i < windowCount; i++) { + WindowState windowState = windowList.get(i); + if ((windowState.isOnScreen() || windowState.mAttrs.type == WindowManager + .LayoutParams.TYPE_UNIVERSE_BACKGROUND) + && !windowState.mWinAnimator.mEnterAnimationPending) { + outWindowStates.put(windowState.mLayer, WindowStateInfo.obtain(windowState)); + } + } + } + + public void onRotationChangedLocked() { + // If we are magnifying, hide the magnified border window immediately so + // the user does not see strange artifacts during rotation. The screenshot + // used for rotation has already the border. After the rotation is complete + // we will show the border. + if (isMagnifyingLocked()) { + setMagnifiedRegionBorderShownLocked(false, false); + final long delay = (long) (mLongAnimationDuration + * mWindowManagerService.mWindowAnimationScale); + Message message = mHandler.obtainMessage( + MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED); + mHandler.sendMessageDelayed(message, delay); + } + recomputeBoundsLocked(); + mWindow.updateSize(); + } + + public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) { + if (shown) { + mFullRedrawNeeded = true; + mOldMagnifiedBounds.set(0, 0, 0, 0); + } + mWindow.setShown(shown, animate); + } + + public void getMagnifiedFrameInContentCoordsLocked(Rect rect) { + MagnificationSpec spec = mMagnificationSpec; + mMagnifiedBounds.getBounds(rect); + rect.offset((int) -spec.offsetX, (int) -spec.offsetY); + rect.scale(1.0f / spec.scale); + } + + public boolean isMagnifyingLocked() { + return mMagnificationSpec.scale > 1.0f; + } + + public MagnificationSpec getMagnificationSpecLocked() { + return mMagnificationSpec; + } + + /** NOTE: This has to be called within a surface transaction. */ + public void drawWindowIfNeededLocked() { + recomputeBoundsLocked(); + mWindow.drawIfNeeded(); + } + + private final class ViewportWindow { + private static final String SURFACE_TITLE = "Magnification Overlay"; + + private static final String PROPERTY_NAME_ALPHA = "alpha"; + + private static final int MIN_ALPHA = 0; + private static final int MAX_ALPHA = 255; + + private final Point mTempPoint = new Point(); + private final Region mBounds = new Region(); + private final Rect mDirtyRect = new Rect(); + private final Paint mPaint = new Paint(); + + private final ValueAnimator mShowHideFrameAnimator; + private final Surface mSurface; + + private boolean mShown; + private int mAlpha; + + private boolean mInvalidated; + + public ViewportWindow(Context context) { + Surface surface = null; + try { + mWindowManager.getDefaultDisplay().getRealSize(mTempPoint); + surface = new Surface(mWindowManagerService.mFxSession, SURFACE_TITLE, + mTempPoint.x, mTempPoint.y, PixelFormat.TRANSLUCENT, Surface.HIDDEN); + } catch (OutOfResourcesException oore) { + /* ignore */ + } + mSurface = surface; + mSurface.setLayerStack(mWindowManager.getDefaultDisplay().getLayerStack()); + mSurface.setLayer(mWindowManagerService.mPolicy.windowTypeToLayerLw( + WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY) + * WindowManagerService.TYPE_LAYER_MULTIPLIER); + mSurface.setPosition(0, 0); + + TypedValue typedValue = new TypedValue(); + context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight, + typedValue, true); + final int borderColor = context.getResources().getColor(typedValue.resourceId); + + mPaint.setStyle(Paint.Style.STROKE); + mPaint.setStrokeWidth(mBorderWidth); + mPaint.setColor(borderColor); + + 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); + mInvalidated = true; + } + + public void setShown(boolean shown, boolean animate) { + synchronized (mWindowManagerService.mWindowMap) { + if (mShown == shown) { + return; + } + mShown = shown; + if (animate) { + if (mShowHideFrameAnimator.isRunning()) { + mShowHideFrameAnimator.reverse(); + } else { + if (shown) { + mShowHideFrameAnimator.start(); + } else { + mShowHideFrameAnimator.reverse(); + } + } + } else { + mShowHideFrameAnimator.cancel(); + if (shown) { + setAlpha(MAX_ALPHA); + } else { + setAlpha(MIN_ALPHA); + } + } + if (DEBUG_VIEWPORT_WINDOW) { + Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown); + } + } + } + + @SuppressWarnings("unused") + // Called reflectively from an animator. + public int getAlpha() { + synchronized (mWindowManagerService.mWindowMap) { + return mAlpha; + } + } + + public void setAlpha(int alpha) { + synchronized (mWindowManagerService.mWindowMap) { + if (mAlpha == alpha) { + return; + } + mAlpha = alpha; + invalidate(null); + if (DEBUG_VIEWPORT_WINDOW) { + Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha); + } + } + } + + public void setBounds(Region bounds) { + synchronized (mWindowManagerService.mWindowMap) { + if (mBounds.equals(bounds)) { + return; + } + mBounds.set(bounds); + invalidate(mDirtyRect); + if (DEBUG_VIEWPORT_WINDOW) { + Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds); + } + } + } + + public void updateSize() { + synchronized (mWindowManagerService.mWindowMap) { + mWindowManager.getDefaultDisplay().getRealSize(mTempPoint); + mSurface.setSize(mTempPoint.x, mTempPoint.y); + invalidate(mDirtyRect); + } + } + + public void invalidate(Rect dirtyRect) { + if (dirtyRect != null) { + mDirtyRect.set(dirtyRect); + } else { + mDirtyRect.setEmpty(); + } + mInvalidated = true; + mWindowManagerService.scheduleAnimationLocked(); + } + + /** NOTE: This has to be called within a surface transaction. */ + public void drawIfNeeded() { + synchronized (mWindowManagerService.mWindowMap) { + if (!mInvalidated) { + return; + } + mInvalidated = false; + Canvas canvas = null; + try { + // Empty dirty rectangle means unspecified. + if (mDirtyRect.isEmpty()) { + mBounds.getBounds(mDirtyRect); + } + mDirtyRect.inset(- mHalfBorderWidth, - mHalfBorderWidth); + canvas = mSurface.lockCanvas(mDirtyRect); + if (DEBUG_VIEWPORT_WINDOW) { + Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect); + } + } catch (IllegalArgumentException iae) { + /* ignore */ + } catch (OutOfResourcesException oore) { + /* ignore */ + } + if (canvas == null) { + return; + } + if (DEBUG_VIEWPORT_WINDOW) { + Slog.i(LOG_TAG, "Bounds: " + mBounds); + } + canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR); + mPaint.setAlpha(mAlpha); + Path path = mBounds.getBoundaryPath(); + canvas.drawPath(path, mPaint); + + mSurface.unlockCanvasAndPost(canvas); + + if (mAlpha > 0) { + mSurface.show(); + } else { + mSurface.hide(); + } + } + } + } + } + + private static final class WindowStateInfo { + private static final int MAX_POOL_SIZE = 30; + + private static final SimplePool<WindowStateInfo> sPool = + new SimplePool<WindowStateInfo>(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 class MyHandler extends Handler { + public static final int MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED = 1; + public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2; + public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3; + public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4; + public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5; + + public MyHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message message) { + switch (message.what) { + case MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED: { + Region bounds = (Region) message.obj; + try { + mCallbacks.onMagnifedBoundsChanged(bounds); + } catch (RemoteException re) { + /* ignore */ + } finally { + bounds.recycle(); + } + } break; + case MESSAGE_NOTIFY_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; + try { + mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom); + } catch (RemoteException re) { + /* ignore */ + } finally { + args.recycle(); + } + } break; + case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: { + try { + mCallbacks.onUserContextChanged(); + } catch (RemoteException re) { + /* ignore */ + } + } break; + case MESSAGE_NOTIFY_ROTATION_CHANGED: { + final int rotation = message.arg1; + try { + mCallbacks.onRotationChanged(rotation); + } catch (RemoteException re) { + /* ignore */ + } + } break; + case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : { + synchronized (mWindowManagerService.mWindowMap) { + if (mMagnifedViewport.isMagnifyingLocked()) { + mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true); + mWindowManagerService.scheduleAnimationLocked(); + } + } + } break; + } + } + } +} diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java index 74f51d7..a9f3c0f 100644 --- a/services/java/com/android/server/wm/WindowAnimator.java +++ b/services/java/com/android/server/wm/WindowAnimator.java @@ -570,6 +570,10 @@ public class WindowAnimator { mAnimating |= dimAnimator.updateSurface(isDimmingLocked(displayId), mCurrentTime, !mService.okToDisplay()); } + //TODO (multidisplay): Magnification is supported only for the default display. + if (mService.mDisplayMagnifier != null && displayId == Display.DEFAULT_DISPLAY) { + mService.mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked(); + } } if (mService.mWatermark != null) { diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 653f075..1640cac 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -115,8 +115,8 @@ import android.view.Display; import android.view.DisplayInfo; import android.view.Gravity; import android.view.IApplicationToken; -import android.view.IDisplayMagnificationMediator; import android.view.IInputFilter; +import android.view.IMagnificationCallbacks; import android.view.IOnKeyguardExitResult; import android.view.IRotationWatcher; import android.view.IWindow; @@ -127,6 +127,7 @@ 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; @@ -424,7 +425,7 @@ public class WindowManagerService extends IWindowManager.Stub IInputMethodManager mInputMethodManager; - DisplayMagnificationMediator mMagnificationMediator; + DisplayMagnifier mDisplayMagnifier; final SurfaceSession mFxSession; Watermark mWatermark; @@ -2323,8 +2324,10 @@ public class WindowManagerService extends IWindowManager.Stub if (win.mWinAnimator.applyAnimationLocked(transit, false)) { win.mExiting = true; } - if (mMagnificationMediator != null) { - mMagnificationMediator.onWindowTransitionLw(win, transit); + //TODO (multidisplay): Magnification is supported only for the default display. + if (mDisplayMagnifier != null + && win.getDisplayId() == Display.DEFAULT_DISPLAY) { + mDisplayMagnifier.onWindowTransitionLocked(win, transit); } } if (win.mExiting || win.mWinAnimator.isAnimating()) { @@ -2619,11 +2622,11 @@ public class WindowManagerService extends IWindowManager.Stub public void onRectangleOnScreenRequested(IBinder token, Rect rectangle, boolean immediate) { synchronized (mWindowMap) { - if (mMagnificationMediator != null) { + if (mDisplayMagnifier != null) { WindowState window = mWindowMap.get(token); - if (window != null) { - mMagnificationMediator.onRectangleOnScreenRequestedLw(window, rectangle, - immediate); + //TODO (multidisplay): Magnification is supported only for the default display. + if (window != null && window.getDisplayId() == Display.DEFAULT_DISPLAY) { + mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle, immediate); } } } @@ -2850,8 +2853,10 @@ public class WindowManagerService extends IWindowManager.Stub } winAnimator.destroySurfaceLocked(); } - if (mMagnificationMediator != null) { - mMagnificationMediator.onWindowTransitionLw(win, transit); + //TODO (multidisplay): Magnification is supported only for the default + if (mDisplayMagnifier != null + && win.getDisplayId() == Display.DEFAULT_DISPLAY) { + mDisplayMagnifier.onWindowTransitionLocked(win, transit); } } } @@ -3004,15 +3009,64 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public IDisplayMagnificationMediator getDisplayMagnificationMediator() { + public void setMagnificationSpec(MagnificationSpec spec) { if (!checkCallingPermission(android.Manifest.permission.MAGNIFY_DISPLAY, - "getDisplayMagnificationMediator()")) { - throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission."); + "setMagnificationSpec()")) { + throw new SecurityException("Requires MAGNIFY_DISPLAY permission."); + } + synchronized (mWindowMap) { + if (mDisplayMagnifier != null) { + mDisplayMagnifier.setMagnificationSpecLocked(spec); + } else { + throw new IllegalStateException("Magnification callbacks not set!"); + } + } + if (Binder.getCallingPid() != android.os.Process.myPid()) { + spec.recycle(); + } + } + + @Override + public MagnificationSpec getCompatibleMagnificationSpecForWindow(IBinder windowToken) { + if (!checkCallingPermission(android.Manifest.permission.MAGNIFY_DISPLAY, + "getCompatibleMagnificationSpecForWindow()")) { + throw new SecurityException("Requires MAGNIFY_DISPLAY permission."); + } + synchronized (mWindowMap) { + WindowState windowState = mWindowMap.get(windowToken); + if (windowState == null) { + return null; + } + MagnificationSpec spec = null; + if (mDisplayMagnifier != null) { + spec = mDisplayMagnifier.getMagnificationSpecForWindowLocked(windowState); + } + if ((spec == null || spec.isNop()) && windowState.mGlobalScale == 1.0f) { + return null; + } + spec = (spec == null) ? MagnificationSpec.obtain() : MagnificationSpec.obtain(spec); + spec.scale *= windowState.mGlobalScale; + return spec; + } + } + + @Override + public void setMagnificationCallbacks(IMagnificationCallbacks callbacks) { + if (!checkCallingPermission(android.Manifest.permission.MAGNIFY_DISPLAY, + "setMagnificationCallbacks()")) { + throw new SecurityException("Requires MAGNIFY_DISPLAY permission."); } - if (mMagnificationMediator == null) { - mMagnificationMediator = new DisplayMagnificationMediator(this); + synchronized (mWindowMap) { + if (mDisplayMagnifier == null) { + mDisplayMagnifier = new DisplayMagnifier(this, callbacks); + } else { + if (callbacks == null) { + mDisplayMagnifier = null; + } else { + throw new IllegalStateException("Magnification callbacks already set!"); + } + } } - return mMagnificationMediator; } private boolean applyAnimationLocked(AppWindowToken atoken, @@ -3156,8 +3210,10 @@ public class WindowManagerService extends IWindowManager.Stub if (win.isVisibleNow()) { win.mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_EXIT, false); - if (mMagnificationMediator != null) { - mMagnificationMediator.onWindowTransitionLw(win, + //TODO (multidisplay): Magnification is supported only for the default + if (mDisplayMagnifier != null + && win.getDisplayId() == Display.DEFAULT_DISPLAY) { + mDisplayMagnifier.onWindowTransitionLocked(win, WindowManagerPolicy.TRANSIT_EXIT); } changed = true; @@ -3912,8 +3968,10 @@ public class WindowManagerService extends IWindowManager.Stub delayed = runningAppAnimation = true; } WindowState window = wtoken.findMainWindow(); - if (window != null && mMagnificationMediator != null) { - mMagnificationMediator.onWindowTransitionLw(window, transit); + //TODO (multidisplay): Magnification is supported only for the default display. + if (window != null && mDisplayMagnifier != null + && window.getDisplayId() == Display.DEFAULT_DISPLAY) { + mDisplayMagnifier.onWindowTransitionLocked(window, transit); } changed = true; } @@ -3932,8 +3990,10 @@ public class WindowManagerService extends IWindowManager.Stub if (!runningAppAnimation) { win.mWinAnimator.applyAnimationLocked( WindowManagerPolicy.TRANSIT_ENTER, true); - if (mMagnificationMediator != null) { - mMagnificationMediator.onWindowTransitionLw(win, + //TODO (multidisplay): Magnification is supported only for the default + if (mDisplayMagnifier != null + && win.getDisplayId() == Display.DEFAULT_DISPLAY) { + mDisplayMagnifier.onWindowTransitionLocked(win, WindowManagerPolicy.TRANSIT_ENTER); } } @@ -3944,8 +4004,10 @@ public class WindowManagerService extends IWindowManager.Stub if (!runningAppAnimation) { win.mWinAnimator.applyAnimationLocked( WindowManagerPolicy.TRANSIT_EXIT, false); - if (mMagnificationMediator != null) { - mMagnificationMediator.onWindowTransitionLw(win, + //TODO (multidisplay): Magnification is supported only for the default + if (mDisplayMagnifier != null + && win.getDisplayId() == Display.DEFAULT_DISPLAY) { + mDisplayMagnifier.onWindowTransitionLocked(win, WindowManagerPolicy.TRANSIT_EXIT); } } @@ -5571,8 +5633,10 @@ public class WindowManagerService extends IWindowManager.Stub } } - if (mMagnificationMediator != null) { - mMagnificationMediator.onRotationChangedLw(Display.DEFAULT_DISPLAY, rotation); + //TODO (multidisplay): Magnification is supported only for the default display. + if (mDisplayMagnifier != null + && displayContent.getDisplayId() == Display.DEFAULT_DISPLAY) { + mDisplayMagnifier.onRotationChangedLocked(getDefaultDisplayContentLocked(), rotation); } return true; @@ -7467,9 +7531,10 @@ public class WindowManagerService extends IWindowManager.Stub // "Assigned layer " + curLayer + " to " + w.mClient.asBinder()); } - if (mMagnificationMediator != null && anyLayerChanged) { - mMagnificationMediator.onWindowLayersChangedLw( - windows.get(windows.size() - 1).getDisplayId()); + //TODO (multidisplay): Magnification is supported only for the default display. + if (mDisplayMagnifier != null && anyLayerChanged + && windows.get(windows.size() - 1).getDisplayId() == Display.DEFAULT_DISPLAY) { + mDisplayMagnifier.onWindowLayersChangedLocked(); } } diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java index e03e40d..9acdd49 100644 --- a/services/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/java/com/android/server/wm/WindowStateAnimator.java @@ -15,6 +15,7 @@ import android.graphics.Rect; import android.graphics.Region; import android.os.Debug; import android.util.Slog; +import android.view.Display; import android.view.DisplayInfo; import android.view.MagnificationSpec; import android.view.Surface; @@ -919,9 +920,11 @@ class WindowStateAnimator { if (screenAnimation) { tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix()); } - if (mService.mMagnificationMediator != null) { - MagnificationSpec spec = mService.mMagnificationMediator - .getMagnificationSpecLw(mWin); + //TODO (multidisplay): Magnification is supported only for the default display. + if (mService.mDisplayMagnifier != null + && mWin.getDisplayId() == Display.DEFAULT_DISPLAY) { + MagnificationSpec spec = mService.mDisplayMagnifier + .getMagnificationSpecForWindowLocked(mWin); if (spec != null && !spec.isNop()) { tmpMatrix.postScale(spec.scale, spec.scale); tmpMatrix.postTranslate(spec.offsetX, spec.offsetY); @@ -997,8 +1000,11 @@ class WindowStateAnimator { final boolean applyUniverseTransformation = (mAnimator.mUniverseBackground != null && mWin.mAttrs.type != WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND && mWin.mBaseLayer < mAnimator.mAboveUniverseLayer); - MagnificationSpec spec = (mService.mMagnificationMediator != null) - ? mService.mMagnificationMediator.getMagnificationSpecLw(mWin) : null; + MagnificationSpec spec = null; + //TODO (multidisplay): Magnification is supported only for the default display. + if (mService.mDisplayMagnifier != null && mWin.getDisplayId() == Display.DEFAULT_DISPLAY) { + spec = mService.mDisplayMagnifier.getMagnificationSpecForWindowLocked(mWin); + } if (applyUniverseTransformation || spec != null) { final Rect frame = mWin.mFrame; final float tmpFloats[] = mService.mTmpFloats; @@ -1498,8 +1504,10 @@ class WindowStateAnimator { transit = WindowManagerPolicy.TRANSIT_SHOW; } applyAnimationLocked(transit, true); - if (mService.mMagnificationMediator != null) { - mService.mMagnificationMediator.onWindowTransitionLw(mWin, transit); + //TODO (multidisplay): Magnification is supported only for the default display. + if (mService.mDisplayMagnifier != null + && mWin.getDisplayId() == Display.DEFAULT_DISPLAY) { + mService.mDisplayMagnifier.onWindowTransitionLocked(mWin, transit); } } |