package com.stardust.view.accessibility; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; import android.util.Log; import android.view.accessibility.AccessibilityNodeInfo; import com.stardust.automator.BuildConfig; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Created by Stardust on 2017/3/22. */ public class AccessibilityNodeInfoAllocator { public static final AccessibilityNodeInfoAllocator NONE = new NoOpAllocator(); private static final String TAG = "AccessibilityAllocator"; private static final boolean DEBUG = BuildConfig.DEBUG; private static final AccessibilityNodeInfoAllocator GLOBAL = new AccessibilityNodeInfoAllocator(); public static AccessibilityNodeInfoAllocator getGlobal() { return GLOBAL; } private Map<AccessibilityNodeInfo, String> mAccessibilityNodeInfoList = new HashMap<>(); public AccessibilityNodeInfo getChild(AccessibilityNodeInfo parent, int i) { return add(parent.getChild(i)); } public AccessibilityNodeInfoCompat getChild(AccessibilityNodeInfoCompat parent, int i) { AccessibilityNodeInfoCompat compat = parent.getChild(i); add((AccessibilityNodeInfo) compat.getInfo()); return compat; } public AccessibilityNodeInfo getParent(AccessibilityNodeInfo n) { return add(n.getParent()); } public AccessibilityNodeInfoCompat getParent(AccessibilityNodeInfoCompat n) { AccessibilityNodeInfoCompat compat = n.getParent(); add((AccessibilityNodeInfo) compat.getInfo()); return compat; } public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(AccessibilityNodeInfo root, String text) { List<AccessibilityNodeInfo> list = root.findAccessibilityNodeInfosByText(text); addAll(list); return list; } public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(AccessibilityNodeInfoCompat root, String text) { List<AccessibilityNodeInfoCompat> list = root.findAccessibilityNodeInfosByText(text); addAll(list); return list; } public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(AccessibilityNodeInfo root, String id) { List<AccessibilityNodeInfo> list = root.findAccessibilityNodeInfosByViewId(id); addAll(list); return list; } public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByViewId(AccessibilityNodeInfoCompat root, String id) { List<AccessibilityNodeInfoCompat> list = root.findAccessibilityNodeInfosByViewId(id); addAll(list); return list; } public void recycle(AccessibilityNodeInfo nodeInfo) { nodeInfo.recycle(); mAccessibilityNodeInfoList.remove(nodeInfo); } public void recycle(AccessibilityNodeInfoCompat nodeInfo) { recycle((AccessibilityNodeInfo) nodeInfo.getInfo()); } public int recycleAll() { int notRecycledCount = 0; int size = mAccessibilityNodeInfoList.size(); for (Map.Entry<AccessibilityNodeInfo, String> pair : mAccessibilityNodeInfoList.entrySet()) { try { pair.getKey().recycle(); notRecycledCount++; if (DEBUG) Log.w(TAG, pair.getValue()); } catch (IllegalStateException ignored) { } } Log.v(TAG, "Total: " + size + " Not recycled: " + notRecycledCount); return notRecycledCount; } public AccessibilityNodeInfo add(@Nullable AccessibilityNodeInfo nodeInfo) { String stackTrace = DEBUG ? Arrays.toString(Thread.currentThread().getStackTrace()) : null; if (nodeInfo != null) mAccessibilityNodeInfoList.put(nodeInfo, stackTrace); return nodeInfo; } private void addAll(Collection<?> nodeInfos) { String stackTrace = DEBUG ? Arrays.toString(Thread.currentThread().getStackTrace()) : null; for (Object nodeInfo : nodeInfos) { if (nodeInfo instanceof AccessibilityNodeInfo) { mAccessibilityNodeInfoList.put((AccessibilityNodeInfo) nodeInfo, stackTrace); } else if (nodeInfo instanceof AccessibilityNodeInfoCompat) { mAccessibilityNodeInfoList.put((AccessibilityNodeInfo) ((AccessibilityNodeInfoCompat) nodeInfo).getInfo(), stackTrace); } } } public static void recycleList(AccessibilityNodeInfo root, List<AccessibilityNodeInfo> list) { for (AccessibilityNodeInfo nodeInfo : list) { if (nodeInfo != root && nodeInfo != null) { //// FIXME: 2017/5/1 Issue #180 nodeInfo.recycle(); } } } private static class NoOpAllocator extends AccessibilityNodeInfoAllocator { @Override public AccessibilityNodeInfoCompat getParent(AccessibilityNodeInfoCompat n) { return n.getParent(); } @Override public AccessibilityNodeInfo getParent(AccessibilityNodeInfo n) { return n.getParent(); } @Override public AccessibilityNodeInfoCompat getChild(AccessibilityNodeInfoCompat parent, int i) { return parent.getChild(i); } @Override public AccessibilityNodeInfo getChild(AccessibilityNodeInfo parent, int i) { return parent.getChild(i); } @Override public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByViewId(AccessibilityNodeInfoCompat root, String id) { return root.findAccessibilityNodeInfosByViewId(id); } @Override public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(AccessibilityNodeInfo root, String id) { return root.findAccessibilityNodeInfosByViewId(id); } @Override public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(AccessibilityNodeInfo root, String text) { return root.findAccessibilityNodeInfosByText(text); } @Override public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(AccessibilityNodeInfoCompat root, String text) { return root.findAccessibilityNodeInfosByText(text); } @Override public void recycle(AccessibilityNodeInfo nodeInfo) { super.recycle(nodeInfo); } @Override public void recycle(AccessibilityNodeInfoCompat nodeInfo) { super.recycle(nodeInfo); } @Override public int recycleAll() { return -1; } } }