Commit 152e9bb8 authored by Svetoslav Ganov's avatar Svetoslav Ganov

Refactoring of the screen magnification feature.

1. The screen magnification feature was implemented entirely as a part of the accessibility
   manager. To achieve that the window manager had to implement a bunch of hooks for an
   external client to observe its internal state. This was problematic since it dilutes
   the window manager interface and allows code that is deeply coupled with the window
   manager to reside outside of it. Also the observer callbacks were IPCs which cannot
   be called with the window manager's lock held. To avoid that the window manager had
   to post messages requesting notification of interested parties which makes the code
   consuming the callbacks to run asynchronously of the window manager. This causes timing
   issues and adds unnecessary complexity.

   Now the magnification logic is split in two halves. The first half that is responsible
   to track the magnified portion of the screen and serve as a policy which windows can be
   magnified and it is a part of the window manager. This part exposes higher level APIs
   allowing interested parties with the right permissions to control the magnification
   of a given display. The APIs also allow a client to be registered for callbacks on
   interesting changes such as resize of the magnified region, etc. This part servers
   as a mediator between magnification controllers and the window manager.

   The second half is a controller that is responsible to drive the magnification
   state based on touch interactions. It also presents a highlight when magnified to
   suggest the magnified potion of the screen. The controller is responsible for auto
   zooming out in case the user context changes - rotation, new actitivity. The controller
   also auto pans if a dialog appears and it does not interesect the magnified frame.

bug:7410464

2. By design screen magnification and touch exploration work separately and together. If
   magnification is enabled the user sees a larger version of the widgets and a sub section
   of the screen content. Accessibility services use the introspection APIs to "see" what
   is on the screen so they can speak it, navigate to the next item in response to a
   gesture, etc. Hence, the information returned to accessibility services has to reflect
   what a sighted user would see on the screen. Therefore, if the screen is magnified
   we need to adjust the bounds and position of the infos describing views in a magnified
   window such that the info bounds are equivalent to what the user sees.

   To improve performance we keep accessibility node info caches in the client process.
   However, when magnification state changes we have to clear these caches since the
   bounds of the cached infos no longer reflect the screen content which just got smaller
   or larger.

   This patch propagates not only the window scale as before but also the X/Y pan and the
   bounds of the magnified portion of the screen to the introspected app. This information
   is used to adjust the bounds of the node infos coming from this window such that the
   reported bounds are the same as the user sees not as the app thinks they are. Note that
   if magnification is enabled we zoom the content and pan it along the X and Y axis. Also
   recomputed is the isVisibleToUser property of the reported info since in a magnified
   state the user sees a subset of the window content and the views not in the magnified
   viewport should be reported as not visible to the user.

bug:7344059

Change-Id: I6f7832c7a6a65c5368b390eb1f1518d0c7afd7d2
parent 4d58730f
......@@ -154,7 +154,8 @@ LOCAL_SRC_FILES += \
core/java/android/view/accessibility/IAccessibilityManager.aidl \
core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \
core/java/android/view/IApplicationToken.aidl \
core/java/android/view/IDisplayContentChangeListener.aidl \
core/java/android/view/IDisplayMagnificationMediator.aidl \
core/java/android/view/IDisplayMagnificationController.aidl \
core/java/android/view/IInputFilter.aidl \
core/java/android/view/IInputFilterHost.aidl \
core/java/android/view/IOnKeyguardExitResult.aidl \
......
......@@ -141,6 +141,11 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/ui/*.ogg)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/telephony/java/com/android/internal/telephony/IExtendedNetworkService.java)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/telephony/java/com/android/internal/telephony/IExtendedNetworkService.P)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/ImageProcessing_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IDisplayContentChangeListener.java)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IDisplayContentChangeListener.P)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IWindowManager.java)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IWindowManager.P)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
......@@ -24746,6 +24746,7 @@ package android.view {
method public android.graphics.Canvas lockCanvas(android.graphics.Rect) throws java.lang.IllegalArgumentException, android.view.Surface.OutOfResourcesException;
method public void readFromParcel(android.os.Parcel);
method public void release();
method public static java.lang.String rotationToString(int);
method public deprecated void unlockCanvas(android.graphics.Canvas);
method public void unlockCanvasAndPost(android.graphics.Canvas);
method public void writeToParcel(android.os.Parcel, int);
......@@ -548,6 +548,7 @@ public abstract class AccessibilityService extends Service {
private static final int DO_ON_INTERRUPT = 20;
private static final int DO_ON_ACCESSIBILITY_EVENT = 30;
private static final int DO_ON_GESTURE = 40;
private static final int DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE = 50;
private final HandlerCaller mCaller;
......@@ -580,6 +581,11 @@ public abstract class AccessibilityService extends Service {
mCaller.sendMessage(message);
}
public void clearAccessibilityNodeInfoCache() {
Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE);
mCaller.sendMessage(message);
}
public void executeMessage(Message message) {
switch (message.what) {
case DO_ON_ACCESSIBILITY_EVENT :
......@@ -611,6 +617,9 @@ public abstract class AccessibilityService extends Service {
final int gestureId = message.arg1;
mCallback.onGesture(gestureId);
return;
case DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE:
AccessibilityInteractionClient.getInstance().clearCache();
return;
default :
Log.w(LOG_TAG, "Unknown message type " + message.what);
}
......
......@@ -33,4 +33,6 @@ import android.view.accessibility.AccessibilityEvent;
void onInterrupt();
void onGesture(int gesture);
void clearAccessibilityNodeInfoCache();
}
......@@ -18,6 +18,7 @@ package android.accessibilityservice;
import android.os.Bundle;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.view.MagnificationSpec;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
......@@ -44,9 +45,9 @@ interface IAccessibilityServiceConnection {
* @param callback Callback which to receive the result.
* @param flags Additional flags.
* @param threadId The id of the calling thread.
* @return The current window scale, where zero means a failure.
* @return Whether the call succeeded.
*/
float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
boolean findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, long threadId);
......@@ -66,9 +67,9 @@ interface IAccessibilityServiceConnection {
* @param interactionId The id of the interaction for matching with the callback result.
* @param callback Callback which to receive the result.
* @param threadId The id of the calling thread.
* @return The current window scale, where zero means a failure.
* @return Whether the call succeeded.
*/
float findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId,
boolean findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId,
String text, int interactionId, IAccessibilityInteractionConnectionCallback callback,
long threadId);
......@@ -88,9 +89,9 @@ interface IAccessibilityServiceConnection {
* @param interactionId The id of the interaction for matching with the callback result.
* @param callback Callback which to receive the result.
* @param threadId The id of the calling thread.
* @return The current window scale, where zero means a failure.
* @return Whether the call succeeded.
*/
float findAccessibilityNodeInfoByViewId(int accessibilityWindowId, long accessibilityNodeId,
boolean findAccessibilityNodeInfoByViewId(int accessibilityWindowId, long accessibilityNodeId,
int viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback,
long threadId);
......@@ -110,9 +111,9 @@ interface IAccessibilityServiceConnection {
* @param interactionId The id of the interaction for matching with the callback result.
* @param callback Callback which to receive the result.
* @param threadId The id of the calling thread.
* @return The current window scale, where zero means a failure.
* @return Whether the call succeeded.
*/
float findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType,
boolean findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType,
int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId);
/**
......@@ -131,9 +132,9 @@ interface IAccessibilityServiceConnection {
* @param interactionId The id of the interaction for matching with the callback result.
* @param callback Callback which to receive the result.
* @param threadId The id of the calling thread.
* @return The current window scale, where zero means a failure.
* @return Whether the call succeeded.
*/
float focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction,
boolean focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction,
int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId);
/**
......
/*
** Copyright 2012, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License")
** 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
**
......@@ -16,18 +16,12 @@
package android.view;
import android.os.IBinder;
import android.view.WindowInfo;
import android.graphics.Rect;
/**
* Interface for observing content changes on a display.
*
* {@hide}
*/
oneway interface IDisplayContentChangeListener {
void onWindowTransition(int displayId, int transition, in WindowInfo info);
void onRectangleOnScreenRequested(int displayId, in Rect rectangle, boolean immediate);
void onWindowLayersChanged(int displayId);
oneway interface IDisplayMagnificationController {
void onMagnifedFrameChanged(int left, int top, int right, int bottom);
void onRectangleOnScreenRequested(int left, int top, int right, int bottom);
void onRotationChanged(int rotation);
void onUserContextChanged();
}
/*
** 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);
}
......@@ -27,17 +27,17 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.IRemoteCallback;
import android.view.IApplicationToken;
import android.view.IDisplayContentChangeListener;
import android.view.IDisplayMagnificationMediator;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
import android.view.IWindowSession;
import android.view.KeyEvent;
import android.view.InputEvent;
import android.view.MagnificationSpec;
import android.view.MotionEvent;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.IInputFilter;
import android.view.WindowInfo;
/**
* System private interface to the window manager.
......@@ -220,40 +220,20 @@ interface IWindowManager
*/
IBinder getFocusedWindowToken();
/**
* Gets the compatibility scale of e window given its token.
*/
float getWindowCompatibilityScale(IBinder windowToken);
/**
* Sets an input filter for manipulating the input event stream.
*/
void setInputFilter(in IInputFilter filter);
/**
* Sets the scale and offset for implementing accessibility magnification.
*/
void magnifyDisplay(int dipslayId, float scale, float offsetX, float offsetY);
/**
* Adds a listener for display content changes.
*/
void addDisplayContentChangeListener(int displayId, IDisplayContentChangeListener listener);
/**
* Removes a listener for display content changes.
*/
void removeDisplayContentChangeListener(int displayId, IDisplayContentChangeListener listener);
/**
* Gets the info for a window given its token.
* Gets the display magnification mediator.
*/
WindowInfo getWindowInfo(IBinder token);
IDisplayMagnificationMediator getDisplayMagnificationMediator();
/**
* Gets the infos for all visible windows.
* Gets the frame of a window given its token.
*/
void getVisibleWindowsForDisplay(int displayId, out List<WindowInfo> outInfos);
void getWindowFrame(IBinder token, out Rect outFrame);
/**
* Device is in safe mode.
......
......@@ -17,4 +17,4 @@
package android.view;
parcelable WindowInfo;
parcelable MagnificationSpec;
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.view;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Pools.SynchronizedPool;
/**
* This class represents spec for performing screen magnification.
*
* @hide
*/
public class MagnificationSpec implements Parcelable {
private static final int MAX_POOL_SIZE = 20;
private static final SynchronizedPool<MagnificationSpec> sPool =
new SynchronizedPool<MagnificationSpec>(MAX_POOL_SIZE);
public float scale = 1.0f;
public float offsetX;
public float offsetY;
private MagnificationSpec() {
/* do nothing - reducing visibility */
}
public void initialize(float scale, float offsetX, float offsetY) {
this.scale = scale;
this.offsetX = offsetX;
this.offsetY = offsetY;
}
public boolean isNop() {
return scale == 1.0f && offsetX == 0 && offsetY == 0;
}
public static MagnificationSpec obtain(MagnificationSpec other) {
MagnificationSpec info = obtain();
info.scale = other.scale;
info.offsetX = other.offsetX;
info.offsetY = other.offsetY;
return info;
}
public static MagnificationSpec obtain() {
MagnificationSpec spec = sPool.acquire();
return (spec != null) ? spec : new MagnificationSpec();
}
public void recycle() {
clear();
sPool.release(this);
}
public void clear() {
scale = 1.0f;
offsetX = 0.0f;
offsetY = 0.0f;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeFloat(scale);
parcel.writeFloat(offsetX);
parcel.writeFloat(offsetY);
recycle();
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("<scale:");
builder.append(scale);
builder.append(",offsetX:");
builder.append(offsetX);
builder.append(",offsetY:");
builder.append(offsetY);
builder.append(">");
return builder.toString();
}
private void initFromParcel(Parcel parcel) {
scale = parcel.readFloat();
offsetX = parcel.readFloat();
offsetY = parcel.readFloat();
}
public static final Creator<MagnificationSpec> CREATOR = new Creator<MagnificationSpec>() {
@Override
public MagnificationSpec[] newArray(int size) {
return new MagnificationSpec[size];
}
@Override
public MagnificationSpec createFromParcel(Parcel parcel) {
MagnificationSpec spec = MagnificationSpec.obtain();
spec.initFromParcel(parcel);
return spec;
}
};
}
......@@ -807,6 +807,32 @@ public class Surface implements Parcelable {
}
}
/**
* Returns a human readable representation of a rotation.
*
* @param rotation The rotation.
* @return The rotation symbolic name.
*/
public static String rotationToString(int rotation) {
switch (rotation) {
case Surface.ROTATION_0: {
return "ROTATION_0";
}
case Surface.ROTATION_90: {
return "ROATATION_90";
}
case Surface.ROTATION_180: {
return "ROATATION_180";
}
case Surface.ROTATION_270: {
return "ROATATION_270";
}
default: {
throw new IllegalArgumentException("Invalid rotation: " + rotation);
}
}
}
/**
* A Canvas class that can handle the compatibility mode.
* This does two things differently.
......
......@@ -5416,12 +5416,13 @@ public final class ViewRootImpl implements ViewParent,
@Override
public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId,
int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid) {
int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController()
.findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityNodeId,
interactionId, callback, flags, interrogatingPid, interrogatingTid);
interactionId, callback, flags, interrogatingPid, interrogatingTid,
spec);
} else {
// We cannot make the call and notify the caller so it does not wait.
try {
......@@ -5455,12 +5456,13 @@ public final class ViewRootImpl implements ViewParent,
@Override
public void findAccessibilityNodeInfoByViewId(long accessibilityNodeId, int viewId,
int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid) {
int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController()
.findAccessibilityNodeInfoByViewIdClientThread(accessibilityNodeId, viewId,
interactionId, callback, flags, interrogatingPid, interrogatingTid);
interactionId, callback, flags, interrogatingPid, interrogatingTid,
spec);
} else {
// We cannot make the call and notify the caller so it does not wait.
try {
......@@ -5474,12 +5476,13 @@ public final class ViewRootImpl implements ViewParent,
@Override
public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text,
int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid) {
int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController()
.findAccessibilityNodeInfosByTextClientThread(accessibilityNodeId, text,
interactionId, callback, flags, interrogatingPid, interrogatingTid);
interactionId, callback, flags, interrogatingPid, interrogatingTid,
spec);
} else {
// We cannot make the call and notify the caller so it does not wait.
try {
......@@ -5493,12 +5496,12 @@ public final class ViewRootImpl implements ViewParent,
@Override
public void findFocus(long accessibilityNodeId, int focusType, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid) {
int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController()
.findFocusClientThread(accessibilityNodeId, focusType, interactionId, callback,
flags, interrogatingPid, interrogatingTid);
flags, interrogatingPid, interrogatingTid, spec);
} else {
// We cannot make the call and notify the caller so it does not wait.
try {
......@@ -5512,12 +5515,12 @@ public final class ViewRootImpl implements ViewParent,
@Override
public void focusSearch(long accessibilityNodeId, int direction, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid) {
int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController()
.focusSearchClientThread(accessibilityNodeId, direction, interactionId,
callback, flags, interrogatingPid, interrogatingTid);
callback, flags, interrogatingPid, interrogatingTid, spec);
} else {
// We cannot make the call and notify the caller so it does not wait.
try {
......
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.view;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Information the state of a window.
*
* @hide
*/
public class WindowInfo implements Parcelable {
private static final int MAX_POOL_SIZE = 20;
private static int UNDEFINED = -1;
private static Object sPoolLock = new Object();
private static WindowInfo sPool;
private static int sPoolSize;
private WindowInfo mNext;
private boolean mInPool;
public IBinder token;
public final Rect frame = new Rect();
public final Rect touchableRegion = new Rect();
public int type = UNDEFINED;
public float compatibilityScale = UNDEFINED;
public boolean visible;
public int displayId = UNDEFINED;
public int layer = UNDEFINED;
private WindowInfo() {
/* do nothing - reduce visibility */
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeStrongBinder(token);
parcel.writeParcelable(frame, 0);
parcel.writeParcelable(touchableRegion, 0);
parcel.writeInt(type);
parcel.writeFloat(compatibilityScale);
parcel.writeInt(visible ? 1 : 0);
parcel.writeInt(displayId);
parcel.writeInt(layer);
recycle();
}
private void initFromParcel(Parcel parcel) {
token = parcel.readStrongBinder();
frame.set((Rect) parcel.readParcelable(null));
touchableRegion.set((Rect) parcel.readParcelable(null));
type = parcel.readInt();
compatibilityScale = parcel.readFloat();
visible = (parcel.readInt() == 1);
displayId = parcel.readInt();
layer = parcel.readInt();
}
public static WindowInfo obtain(WindowInfo other) {
WindowInfo info = obtain();
info.token = other.token;
info.frame.set(other.frame);
info.touchableRegion.set(other.touchableRegion);
info.type = other.type;
info.compatibilityScale = other.compatibilityScale;
info.visible = other.visible;
info.displayId = other.displayId;
info.layer = other.layer;
return info;
}
public static WindowInfo obtain() {
synchronized (sPoolLock) {
if (sPoolSize > 0) {
WindowInfo info = sPool;
sPool = info.mNext;
info.mNext = null;
info.mInPool = false;
sPoolSize--;
return info;
} else {
return new WindowInfo();
}
}
}
public void recycle() {
if (mInPool) {
throw new IllegalStateException("Already recycled.");
}
clear();
synchronized (sPoolLock) {
if (sPoolSize < MAX_POOL_SIZE) {
mNext = sPool;
sPool = this;
mInPool = true;
sPoolSize++;
}
}
}
private void clear() {
token = null;
frame.setEmpty();
touchableRegion.setEmpty();
type = UNDEFINED;
compatibilityScale = UNDEFINED;
visible = false;
displayId = UNDEFINED;
layer = UNDEFINED;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Window [token:").append((token != null) ? token.hashCode() : null);
builder.append(", displayId:").append(displayId);
builder.append(", type:").append(type);
builder.append(", visible:").append(visible);
builder.append(", layer:").append(layer);
builder.append(", compatibilityScale:").append(compatibilityScale);
builder.append(", frame:").append(frame);
builder.append(", touchableRegion:").append(touchableRegion);
builder.append("]");
return builder.toString();
}
/**
* @see Parcelable.Creator
*/
public static final Parcelable.Creator<WindowInfo> CREATOR =
new Parcelable.Creator<WindowInfo>() {
public WindowInfo createFromParcel(Parcel parcel) {
WindowInfo info = WindowInfo.obtain();
info.initFromParcel(parcel);
return info;
}
public WindowInfo[] newArray(int size) {
return new WindowInfo[size];
}