1/* 2 * Copyright (C) 2013 DroidDriver committers 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package io.appium.droiddriver.instrumentation; 18 19import android.os.Build; 20import android.view.View; 21 22import java.lang.reflect.Field; 23import java.lang.reflect.InvocationTargetException; 24import java.lang.reflect.Method; 25import java.util.Arrays; 26import java.util.List; 27 28import io.appium.droiddriver.exceptions.DroidDriverException; 29 30/** 31 * Class to find the root view. 32 */ 33public class RootFinder { 34 35 private static final String VIEW_FIELD_NAME = "mViews"; 36 private static final Field viewsField; 37 private static final Object windowManagerObj; 38 39 static { 40 String windowManagerClassName = 41 Build.VERSION.SDK_INT >= 17 ? "android.view.WindowManagerGlobal" 42 : "android.view.WindowManagerImpl"; 43 String instanceMethod = Build.VERSION.SDK_INT >= 17 ? "getInstance" : "getDefault"; 44 try { 45 Class<?> clazz = Class.forName(windowManagerClassName); 46 Method getMethod = clazz.getMethod(instanceMethod); 47 windowManagerObj = getMethod.invoke(null); 48 viewsField = clazz.getDeclaredField(VIEW_FIELD_NAME); 49 viewsField.setAccessible(true); 50 } catch (InvocationTargetException ite) { 51 throw new DroidDriverException(String.format("could not invoke: %s on %s", instanceMethod, 52 windowManagerClassName), ite.getCause()); 53 } catch (ClassNotFoundException cnfe) { 54 throw new DroidDriverException(String.format("could not find class: %s", 55 windowManagerClassName), cnfe); 56 } catch (NoSuchFieldException nsfe) { 57 throw new DroidDriverException(String.format("could not find field: %s on %s", 58 VIEW_FIELD_NAME, windowManagerClassName), nsfe); 59 } catch (NoSuchMethodException nsme) { 60 throw new DroidDriverException(String.format("could not find method: %s on %s", 61 instanceMethod, windowManagerClassName), nsme); 62 } catch (RuntimeException re) { 63 throw new DroidDriverException(String.format( 64 "reflective setup failed using obj: %s method: %s field: %s", windowManagerClassName, 65 instanceMethod, VIEW_FIELD_NAME), re); 66 } catch (IllegalAccessException iae) { 67 throw new DroidDriverException(String.format( 68 "reflective setup failed using obj: %s method: %s field: %s", windowManagerClassName, 69 instanceMethod, VIEW_FIELD_NAME), iae); 70 } 71 } 72 73 /** 74 * @return a list of {@link View}s. 75 */ 76 @SuppressWarnings("unchecked") 77 public static List<View> getRootViews() { 78 List<View> views = null; 79 80 try { 81 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 82 views = (List<View>) viewsField.get(windowManagerObj); 83 } else { 84 views = Arrays.asList((View[]) viewsField.get(windowManagerObj)); 85 } 86 return views; 87 } catch (RuntimeException re) { 88 throw new DroidDriverException(String.format("Reflective access to %s on %s failed.", 89 viewsField, windowManagerObj), re); 90 } catch (IllegalAccessException iae) { 91 throw new DroidDriverException(String.format("Reflective access to %s on %s failed.", 92 viewsField, windowManagerObj), iae); 93 } 94 } 95} 96