All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.qa.automation.android.view.ViewFetcher Maven / Gradle / Ivy

package com.qa.automation.android.view;

import android.content.Context;
import android.os.SystemClock;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.WindowManager;
import android.webkit.WebView;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;


/**
 * Contains view methods. Examples are getViews(),
 * getCurrentTextViews(), getCurrentImageViews().
 *
 * @author Renas Reda, [email protected]
 */
class ViewFetcher {

    private static Class windowManager;

    static {
        try {
            String windowManagerClassName;
            if (android.os.Build.VERSION.SDK_INT >= 17) {
                windowManagerClassName = "android.view.WindowManagerGlobal";
            } else {
                windowManagerClassName = "android.view.WindowManagerImpl";
            }
            windowManager = Class.forName(windowManagerClassName);

        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (SecurityException e) {
            e.printStackTrace();
        }
    }

    private String windowManagerString;
    private Context mcontext;
    private Sleeper sleeper;


    /**
     * Constructs this object.
     *
     * @param context the instance.
     * @param sleeper the sleeper
     */
    public ViewFetcher(Context context, Sleeper sleeper) {
        this.mcontext = context;
        this.sleeper = sleeper;
        setWindowManagerString();
    }

    /**
     * Returns the absolute top parent {@code View} in for a given {@code View}.
     *
     * @param view the {@code View} whose top parent is requested
     * @return the top parent {@code View}
     */
    public View getTopParent(View view) {
        final ViewParent viewParent = view.getParent();
        if (viewParent != null
                && viewParent instanceof View) {
            return getTopParent((View) viewParent);
        } else {
            return view;
        }
    }

    /**
     * Returns the scroll or list parent view
     *
     * @param view the view who's parent should be returned
     * @return the parent scroll view, list view or null
     */
    public View getScrollOrListParent(View view) {

        if (!(view instanceof android.widget.AbsListView) && !(view instanceof android.widget.ScrollView) && !(view instanceof WebView)) {
            try {
                return getScrollOrListParent((View) view.getParent());
            } catch (Exception e) {
                return null;
            }
        } else {
            return view;
        }
    }

    /**
     * Returns views from the shown DecorViews.
     *
     * @param onlySufficientlyVisible if only sufficiently visible views should be returned
     * @return all the views contained in the DecorViews
     */
    public ArrayList getAllViews(boolean onlySufficientlyVisible) {
        final View[] views = getWindowDecorViews();
        final ArrayList allViews = new ArrayList();
        final View[] nonDecorViews = getNonDecorViews(views);
        View view = null;

        if (nonDecorViews != null) {
            for (View nonDecorView : nonDecorViews) {
                view = nonDecorView;
                try {
                    addChildren(allViews, (ViewGroup) view, onlySufficientlyVisible);
                } catch (Exception ignored) {
                }
                if (view != null) allViews.add(view);
            }
        }

        if (views != null && views.length > 0) {
            view = getRecentDecorView(views);
            try {
                addChildren(allViews, (ViewGroup) view, onlySufficientlyVisible);
            } catch (Exception ignored) {
            }

            if (view != null) allViews.add(view);
        }

        return allViews;
    }

    /**
     * Returns the most recent DecorView
     *
     * @param views the views to check
     * @return the most recent DecorView
     */
    public final View getRecentDecorView(View[] views) {
        if (views == null)
            return null;

        final View[] decorViews = new View[views.length];
        int i = 0;
        View view;

        for (View view1 : views) {
            view = view1;
            if (isDecorView(view)) {
                decorViews[i] = view;
                i++;
            }
        }
        return getRecentContainer(decorViews);
    }

    /**
     * Returns the most recent view container
     *
     * @param views the views to check
     * @return the most recent view container
     */

    private View getRecentContainer(View[] views) {
        View container = null;
        long drawingTime = 0;
        View view;

        for (View view1 : views) {
            view = view1;
            if (view != null && view.isShown() && view.hasWindowFocus() && view.getDrawingTime() > drawingTime) {
                container = view;
                drawingTime = view.getDrawingTime();
            }
        }
        return container;
    }

    /**
     * Returns all views that are non DecorViews
     *
     * @param views the views to check
     * @return the non DecorViews
     */

    private View[] getNonDecorViews(View[] views) {
        View[] decorViews = null;

        if (views != null) {
            decorViews = new View[views.length];

            int i = 0;
            View view;

            for (View view1 : views) {
                view = view1;
                if (!isDecorView(view)) {
                    decorViews[i] = view;
                    i++;
                }
            }
        }
        return decorViews;
    }

    /**
     * Returns whether a view is a DecorView
     *
     * @return true if view is a DecorView, false otherwise
     */
    private boolean isDecorView(View view) {
        if (view == null) {
            return false;
        }

        final String nameOfClass = view.getClass().getName();
        return (nameOfClass.equals("com.android.internal.policy.impl.PhoneWindow$DecorView") ||
                nameOfClass.equals("com.android.internal.policy.impl.MultiPhoneWindow$MultiPhoneDecorView") ||
                nameOfClass.equals("com.android.internal.policy.PhoneWindow$DecorView"));
    }

    /**
     * Extracts all {@code View}s located in the currently active {@code Activity}, recursively.
     *
     * @param parent                  the {@code View} whose children should be returned, or {@code null} for all
     * @param onlySufficientlyVisible if only sufficiently visible views should be returned
     * @return all {@code View}s located in the currently active {@code Activity}, never {@code null}
     */
    public ArrayList getViews(View parent, boolean onlySufficientlyVisible) {
        final ArrayList views = new ArrayList();
        final View parentToUse;

        if (parent == null) {
            return getAllViews(onlySufficientlyVisible);
        } else {
            parentToUse = parent;

            views.add(parentToUse);

            if (parentToUse instanceof ViewGroup) {
                addChildren(views, (ViewGroup) parentToUse, onlySufficientlyVisible);
            }
        }
        return views;
    }

    /**
     * Adds all children of {@code viewGroup} (recursively) into {@code views}.
     *
     * @param views                   an {@code ArrayList} of {@code View}s
     * @param viewGroup               the {@code ViewGroup} to extract children from
     * @param onlySufficientlyVisible if only sufficiently visible views should be returned
     */

    private void addChildren(ArrayList views, ViewGroup viewGroup, boolean onlySufficientlyVisible) {
        if (viewGroup != null) {
            for (int i = 0; i < viewGroup.getChildCount(); i++) {
                final View child = viewGroup.getChildAt(i);

                if (onlySufficientlyVisible && isViewSufficientlyShown(child)) {
                    views.add(child);
                } else if (!onlySufficientlyVisible && child != null) {
                    views.add(child);
                }

                if (child instanceof ViewGroup) {
                    addChildren(views, (ViewGroup) child, onlySufficientlyVisible);
                }
            }
        }
    }

    /**
     * Returns true if the view is sufficiently shown
     *
     * @param view the view to check
     * @return true if the view is sufficiently shown
     */
    public final boolean isViewSufficientlyShown(View view) {
        final int[] xyView = new int[2];
        final int[] xyParent = new int[2];

        if (view == null)
            return false;

        final float viewHeight = view.getHeight();
        final View parent = getScrollOrListParent(view);
        view.getLocationOnScreen(xyView);

        if (parent == null) {
            xyParent[1] = 0;
        } else {
            parent.getLocationOnScreen(xyParent);
        }

        if (xyView[1] + (viewHeight / 2.0f) > getScrollListWindowHeight(view))
            return false;

        else if (xyView[1] + (viewHeight / 2.0f) < xyParent[1])
            return false;

        return true;
    }

    /**
     * Returns the height of the scroll or list view parent
     *
     * @param view the view who's parents height should be returned
     * @return the height of the scroll or list view parent
     */
    @SuppressWarnings("deprecation")
    public float getScrollListWindowHeight(View view) {
        final int[] xyParent = new int[2];
        View parent = getScrollOrListParent(view);
        final float windowHeight;

        if (parent == null) {
            WindowManager windowManager = (WindowManager)
                    mcontext.getSystemService(Context.WINDOW_SERVICE);

            windowHeight = windowManager.getDefaultDisplay().getHeight();
        } else {
            parent.getLocationOnScreen(xyParent);
            windowHeight = xyParent[1] + parent.getHeight();
        }
        parent = null;
        return windowHeight;
    }

    /**
     * Returns an {@code ArrayList} of {@code View}s of the specified {@code Class} located in the current
     * {@code Activity}.
     *
     * @param                the type parameter
     * @param classToFilterBy   return all instances of this class, e.g. {@code Button.class} or {@code GridView.class}
     * @param includeSubclasses include instances of the subclasses in the {@code ArrayList} that will be returned
     * @return an {@code ArrayList} of {@code View}s of the specified {@code Class} located in the current {@code Activity}
     */
    public  ArrayList getCurrentViews(Class classToFilterBy, boolean includeSubclasses) {
        return getCurrentViews(classToFilterBy, includeSubclasses, null);
    }

    /**
     * Returns an {@code ArrayList} of {@code View}s of the specified {@code Class} located under the specified {@code parent}.
     *
     * @param                the type parameter
     * @param classToFilterBy   return all instances of this class, e.g. {@code Button.class} or {@code GridView.class}
     * @param includeSubclasses include instances of subclasses in {@code ArrayList} that will be returned
     * @param parent            the parent {@code View} for where to start the traversal
     * @return an {@code ArrayList} of {@code View}s of the specified {@code Class} located under the specified {@code parent}
     */
    public  ArrayList getCurrentViews(Class classToFilterBy, boolean includeSubclasses, View parent) {
        ArrayList filteredViews = new ArrayList();
        List allViews = getViews(parent, true);
        for (View view : allViews) {
            if (view == null) {
                continue;
            }
            Class classOfView = view.getClass();
            if (includeSubclasses && classToFilterBy.isAssignableFrom(classOfView) || !includeSubclasses && classToFilterBy == classOfView) {
                filteredViews.add(classToFilterBy.cast(view));
            }
        }
        allViews = null;
        return filteredViews;
    }

    /**
     * Tries to guess which view is the most likely to be interesting. Returns
     * the most recently drawn view, which presumably will be the one that the
     * user was most recently interacting with.
     *
     * @param    the type parameter
     * @param views the views
     * @return most recently drawn view, or null if no views were passed
     */
    public final  T getFreshestView(ArrayList views) {
        final int[] locationOnScreen = new int[2];
        T viewToReturn = null;
        long drawingTime = 0;
        if (views == null) {
            return null;
        }
        for (T view : views) {
            if (view != null) {
                view.getLocationOnScreen(locationOnScreen);

                if (locationOnScreen[0] < 0)
                    continue;

                if (view.getDrawingTime() > drawingTime && view.getHeight() > 0) {
                    drawingTime = view.getDrawingTime();
                    viewToReturn = view;
                }

            }
        }
        views = null;
        return viewToReturn;
    }

    /**
     * Waits for a RecyclerView and returns it.
     *
     * @param                the type parameter
     * @param recyclerViewIndex the index of the RecyclerView
     * @param timeOut           the time out
     * @return {@code ViewGroup} if RecycleView is displayed
     */


    public  ViewGroup getRecyclerView(int recyclerViewIndex, int timeOut) {
        final long endTime = SystemClock.uptimeMillis() + timeOut;

        while (SystemClock.uptimeMillis() < endTime) {
            View recyclerView = getRecyclerView(true, recyclerViewIndex);
            if (recyclerView != null) {
                return (ViewGroup) recyclerView;
            }
        }
        return null;
    }

    /**
     * Returns a RecyclerView or null if none is found
     *
     * @param shouldSleep       the should sleep
     * @param recyclerViewIndex the recycler view index
     * @return a RecyclerView
     */
    public View getRecyclerView(boolean shouldSleep, int recyclerViewIndex) {
        Set uniqueViews = new HashSet();
        if (shouldSleep) {
            sleeper.sleep();
        }

        @SuppressWarnings("unchecked")
        ArrayList views = ViewUtils.filterViewsToSet(new Class[]{ViewGroup.class}, getAllViews(false));
        views = ViewUtils.removeInvisibleViews(views);

        for (View view : views) {

            if (isViewType(view.getClass(), "widget.RecyclerView")) {
                uniqueViews.add(view);
            }

            if (uniqueViews.size() > recyclerViewIndex) {
                return (ViewGroup) view;
            }
        }
        return null;
    }

    /**
     * Returns a Set of all RecyclerView or empty Set if none is found
     *
     * @param shouldSleep the should sleep
     * @return a Set of RecyclerViews
     */
    public List getAllRecyclerViews(boolean shouldSleep) {
        List viewsToReturn = new ArrayList();
        if (shouldSleep) {
            sleeper.sleep();
        }

        @SuppressWarnings("unchecked")
        ArrayList views = ViewUtils.filterViewsToSet(new Class[]{ViewGroup.class}, getAllViews(true));
        views = ViewUtils.removeInvisibleViews(views);

        for (View view : views) {

            if (isViewType(view.getClass(), "widget.RecyclerView")) {
                viewsToReturn.add(view);
            }

        }
        return viewsToReturn;
    }

    private boolean isViewType(Class aClass, String typeName) {
        return aClass.getName().contains(typeName) || aClass.getSuperclass() != null && isViewType(aClass.getSuperclass(), typeName);

    }

    /**
     * Returns an identical View to the one specified.
     *
     * @param view the view to find
     * @return identical view of the specified view
     */
    public View getIdenticalView(View view) {
        if (view == null) {
            return null;
        }
        View viewToReturn = null;
        List visibleViews = ViewUtils.removeInvisibleViews(getCurrentViews(view.getClass(), true));

        for (View v : visibleViews) {
            if (areViewsIdentical(v, view)) {
                viewToReturn = v;
                break;
            }
        }

        return viewToReturn;
    }

    /**
     * Compares if the specified views are identical. This is used instead of View.compare
     * as it always returns false in cases where the View tree is refreshed.
     *
     * @param firstView  the first view
     * @param secondView the second view
     * @return true if views are equal
     */

    private boolean areViewsIdentical(View firstView, View secondView) {
        return !(firstView.getId() != secondView.getId() || !firstView.getClass().isAssignableFrom(secondView.getClass())) && (!(firstView.getParent() != null && firstView.getParent() instanceof View && secondView.getParent() != null && secondView.getParent() instanceof View) || areViewsIdentical((View) firstView.getParent(), (View) secondView.getParent()));

    }

    /**
     * Returns the WindowDecorViews shown on the screen.
     *
     * @return the WindowDecorViews shown on the screen
     */
    @SuppressWarnings("unchecked")
    public View[] getWindowDecorViews() {

        Field viewsField;
        Field instanceField;
        try {
            viewsField = windowManager.getDeclaredField("mViews");
            instanceField = windowManager.getDeclaredField(windowManagerString);
            viewsField.setAccessible(true);
            instanceField.setAccessible(true);
            Object instance = instanceField.get(null);
            View[] result;
            ArrayList viewList = (ArrayList) viewsField.get(instance);
            if (android.os.Build.VERSION.SDK_INT >= 19) {
                result = (viewList.toArray(new View[viewList.size()]));
            } else {
                result = (View[]) viewsField.get(instance);
            }
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Sets the window manager string.
     */
    private void setWindowManagerString() {

        if (android.os.Build.VERSION.SDK_INT >= 17) {
            windowManagerString = "sDefaultWindowManager";

        } else if (android.os.Build.VERSION.SDK_INT >= 13) {
            windowManagerString = "sWindowManager";

        } else {
            windowManagerString = "mWindowManager";
        }
    }


}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy