Is it possible to programmatically enumerate all of the android.view.Window
s, or decor views within an application?
Dialogs
for example wil
I'm not entirely sure this answers the actual question, but it's a better way of getting all root views as suggested in the accepted answer.
As mentioned there, I've also managed to accomplish this only using reflection, except this code supports All versions from API 14 and above (I haven't checked below):
public static List<View> getWindowManagerViews() {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH &&
Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
// get the list from WindowManagerImpl.mViews
Class wmiClass = Class.forName("android.view.WindowManagerImpl");
Object wmiInstance = wmiClass.getMethod("getDefault").invoke(null);
return viewsFromWM(wmiClass, wmiInstance);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
// get the list from WindowManagerGlobal.mViews
Class wmgClass = Class.forName("android.view.WindowManagerGlobal");
Object wmgInstance = wmgClass.getMethod("getInstance").invoke(null);
return viewsFromWM(wmgClass, wmgInstance);
}
} catch (Exception e) {
e.printStackTrace();
}
return new ArrayList<View>();
}
private static List<View> viewsFromWM(Class wmClass, Object wmInstance) throws Exception {
Field viewsField = wmClass.getDeclaredField("mViews");
viewsField.setAccessible(true);
Object views = viewsField.get(wmInstance);
if (views instanceof List) {
return (List<View>) viewsField.get(wmInstance);
} else if (views instanceof View[]) {
return Arrays.asList((View[])viewsField.get(wmInstance));
}
return new ArrayList<View>();
}
You can use @hidden APIs directly, without using reflection by accessing the class files and adding then to your android.jar in Android SDK. Here's how: https://devmaze.wordpress.com/2011/01/18/using-com-android-internal-part-1-introduction/
And the source for the android.jar for specific android version (19,21,22,23,24) can be grabbed here: https://github.com/anggrayudi/android-hidden-api
So then you can use the WindowManagerGlobal class directly to get all the root views like,
private void logRootViews() {
WindowManagerGlobal windowManagerGlobal = WindowManagerGlobal.getInstance();
String[] rootViewNames = windowManagerGlobal.getViewRootNames();
for (String viewName : rootViewNames) {
View rootView = windowManagerGlobal.getRootView(viewName);
Log.i("", "Root view is: " + viewName + ": " + rootView);
/*do what you want with the rootView*/
}
}
Output:
Root view is: com.example.paintsample/com.example.paintsample.PaintSample/android.view.ViewRootImpl@41deeff0: com.android.internal.policy.impl.PhoneWindow$DecorView{41dcc278 V.E..... R....... 0,0-768,1184}
Root view is: PopupWindow:42887380/android.view.ViewRootImpl@42891820: android.widget.PopupWindow$PopupViewContainer{42891450 V.E..... ........ 0,0-424,618}
Every solution is in Java above and I made the solution for Kotlin converting the Andrew Lavers's answer-
try
{
val wmgClass = Class.forName("android.view.WindowManagerGlobal")
val wagInstance = wmgClass.getMethod("getInstance").invoke(null)
val getViewRootNames: Method = wmgClass.getMethod("getViewRootNames")
val getRootView: Method = wmgClass.getMethod("getRootView", String::class.java)
val rootViewNames = getViewRootNames.invoke(wagInstance) as Array<String>
for (viewName in rootViewNames) {
val rootView = getRootView.invoke(wagInstance, viewName) as View
}
} catch (exception: java.lang.Exception {}
The Hierarchyviewer tool that comes with the SDK is worth its weight in gold.
I've found a way to do it via reflection on the @hidden WindowManagerGlobal
. At least so far I know this works for android-18.
private void logRootViews() {
try {
Class wmgClass = Class.forName("android.view.WindowManagerGlobal");
Object wmgInstnace = wmgClass.getMethod("getInstance").invoke(null, (Object[])null);
Method getViewRootNames = wmgClass.getMethod("getViewRootNames");
Method getRootView = wmgClass.getMethod("getRootView", String.class);
String[] rootViewNames = (String[])getViewRootNames.invoke(wmgInstnace, (Object[])null);
for(String viewName : rootViewNames) {
View rootView = (View)getRootView.invoke(wmgInstnace, viewName);
Log.i(TAG, "Found root view: " + viewName + ": " + rootView);
}
} catch (Exception e) {
e.printStackTrace();
}
}
Output:
Found root view: com.example.paintsample/com.example.paintsample.PaintSample/android.view.ViewRootImpl@41deeff0: com.android.internal.policy.impl.PhoneWindow$DecorView{41dcc278 V.E..... R....... 0,0-768,1184}
Found root view: PopupWindow:42887380/android.view.ViewRootImpl@42891820: android.widget.PopupWindow$PopupViewContainer{42891450 V.E..... ........ 0,0-424,618}
Bounty is still up for grabs of course for anyone who can find a better way :)