
org.robolectric.shadows.ShadowAccessibilityWindowInfo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of framework Show documentation
Show all versions of framework Show documentation
An alternative Android testing framework.
The newest version!
package org.robolectric.shadows;
import android.graphics.Rect;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.util.ReflectionHelpers;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import static android.os.Build.VERSION_CODES.LOLLIPOP;
/**
* Shadow of {@link android.view.accessibility.AccessibilityWindowInfo} that allows a test to set
* properties that are locked in the original class.
*/
@Implements(value = AccessibilityWindowInfo.class, minSdk = LOLLIPOP)
public class ShadowAccessibilityWindowInfo {
private static final Map obtainedInstances =
new HashMap<>();
private List children = null;
private AccessibilityWindowInfo parent = null;
private AccessibilityNodeInfo rootNode = null;
private Rect boundsInScreen = new Rect();
private int type = AccessibilityWindowInfo.TYPE_APPLICATION;
private int layer = 0;
private int id = 0;
private boolean isAccessibilityFocused = false;
private boolean isActive = false;
private boolean isFocused = false;
@RealObject
private AccessibilityWindowInfo mRealAccessibilityWindowInfo;
public void __constructor__() {}
@Implementation
public static AccessibilityWindowInfo obtain() {
final AccessibilityWindowInfo obtainedInstance =
ReflectionHelpers.callConstructor(AccessibilityWindowInfo.class);
StrictEqualityWindowWrapper wrapper = new StrictEqualityWindowWrapper(obtainedInstance);
obtainedInstances.put(wrapper, Thread.currentThread().getStackTrace());
return obtainedInstance;
}
@Implementation
public static AccessibilityWindowInfo obtain(AccessibilityWindowInfo window) {
final ShadowAccessibilityWindowInfo shadowInfo = Shadow.extract(window);
final AccessibilityWindowInfo obtainedInstance = shadowInfo.getClone();
StrictEqualityWindowWrapper wrapper = new StrictEqualityWindowWrapper(obtainedInstance);
obtainedInstances.put(wrapper, Thread.currentThread().getStackTrace());
return obtainedInstance;
}
private AccessibilityWindowInfo getClone() {
final AccessibilityWindowInfo newInfo =
ReflectionHelpers.callConstructor(AccessibilityWindowInfo.class);
final ShadowAccessibilityWindowInfo newShadow = Shadow.extract(newInfo);
newShadow.boundsInScreen = new Rect(boundsInScreen);
newShadow.parent = parent;
newShadow.rootNode = rootNode;
newShadow.type = type;
newShadow.layer = layer;
newShadow.id = id;
newShadow.isAccessibilityFocused = isAccessibilityFocused;
newShadow.isActive = isActive;
newShadow.isFocused = isFocused;
return newInfo;
}
/**
* Clear list of obtained instance objects. {@code areThereUnrecycledWindows} will always
* return false if called immediately afterwards.
*/
public static void resetObtainedInstances() {
obtainedInstances.clear();
}
/**
* Check for leaked objects that were {@code obtain}ed but never
* {@code recycle}d.
*
* @param printUnrecycledWindowsToSystemErr - if true, stack traces of calls
* to {@code obtain} that lack matching calls to {@code recycle} are
* dumped to System.err.
* @return {@code true} if there are unrecycled windows
*/
public static boolean areThereUnrecycledWindows(boolean printUnrecycledWindowsToSystemErr) {
if (printUnrecycledWindowsToSystemErr) {
for (final StrictEqualityWindowWrapper wrapper : obtainedInstances.keySet()) {
final ShadowAccessibilityWindowInfo shadow = Shadow.extract(wrapper.mInfo);
System.err.println(String.format(
"Leaked type = %d, id = %d. Stack trace:", shadow.getType(), shadow.getId()));
for (final StackTraceElement stackTraceElement : obtainedInstances.get(wrapper)) {
System.err.println(stackTraceElement.toString());
}
}
}
return (obtainedInstances.size() != 0);
}
@Override
@Implementation
public boolean equals(Object object) {
if (!(object instanceof AccessibilityWindowInfo)) {
return false;
}
final AccessibilityWindowInfo window = (AccessibilityWindowInfo) object;
final ShadowAccessibilityWindowInfo otherShadow = Shadow.extract(window);
boolean areEqual = (type == otherShadow.getType());
areEqual &= (parent == otherShadow.getParent());
areEqual &= (rootNode == otherShadow.getRoot());
areEqual &= (layer == otherShadow.getLayer());
areEqual &= (id == otherShadow.getId());
areEqual &= (isAccessibilityFocused == otherShadow.isAccessibilityFocused());
areEqual &= (isActive == otherShadow.isActive());
areEqual &= (isFocused == otherShadow.isFocused());
Rect anotherBounds = new Rect();
otherShadow.getBoundsInScreen(anotherBounds);
areEqual &= (boundsInScreen.equals(anotherBounds));
return areEqual;
}
@Override
@Implementation
public int hashCode() {
// This is 0 for a reason. If you change it, you will break the obtained instances map in
// a manner that is remarkably difficult to debug. Having a dynamic hash code keeps this
// object from being located in the map if it was mutated after being obtained.
return 0;
}
@Implementation
public int getType() {
return type;
}
@Implementation
public int getChildCount() {
if (children == null) {
return 0;
}
return children.size();
}
@Implementation
public AccessibilityWindowInfo getChild(int index) {
if (children == null) {
return null;
}
return children.get(index);
}
@Implementation
public AccessibilityWindowInfo getParent() {
return parent;
}
@Implementation
public AccessibilityNodeInfo getRoot() {
return (rootNode == null) ? null : AccessibilityNodeInfo.obtain(rootNode);
}
@Implementation
public boolean isActive() {
return isActive;
}
@Implementation
public int getId() {
return id;
}
@Implementation
public void getBoundsInScreen(Rect outBounds) {
if (boundsInScreen == null) {
outBounds.setEmpty();
} else {
outBounds.set(boundsInScreen);
}
}
@Implementation
public int getLayer() {
return layer;
}
@Implementation
public boolean isFocused() {
return isFocused;
}
@Implementation
public boolean isAccessibilityFocused() {
return isAccessibilityFocused;
}
@Implementation
public void recycle() {
// This shadow does not track recycling of windows.
}
public void setRoot(AccessibilityNodeInfo root) {
rootNode = root;
}
public void setType(int value) {
type = value;
}
public void setBoundsInScreen(Rect bounds) {
boundsInScreen.set(bounds);
}
public void setAccessibilityFocused(boolean value) {
isAccessibilityFocused = value;
}
public void setActive(boolean value) {
isActive = value;
}
public void setId(int value) {
id = value;
}
public void setLayer(int value) {
layer = value;
}
public void setFocused(boolean focused) {
isFocused = focused;
}
public void addChild(AccessibilityWindowInfo child) {
if (children == null) {
children = new LinkedList<>();
}
children.add(child);
((ShadowAccessibilityWindowInfo) Shadow.extract(child)).parent =
mRealAccessibilityWindowInfo;
}
/**
* Private class to keep different windows referring to the same window straight
* in the mObtainedInstances map.
*/
private static class StrictEqualityWindowWrapper {
public final AccessibilityWindowInfo mInfo;
public StrictEqualityWindowWrapper(AccessibilityWindowInfo info) {
mInfo = info;
}
@Override
public boolean equals(Object object) {
if (object == null) {
return false;
}
final StrictEqualityWindowWrapper wrapper = (StrictEqualityWindowWrapper) object;
return mInfo == wrapper.mInfo;
}
@Override
public int hashCode() {
return mInfo.hashCode();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy