| // Copyright 2013 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| package io.flutter.util; | |
| import android.app.Activity; | |
| import android.content.Context; | |
| import android.content.ContextWrapper; | |
| import android.os.Build; | |
| import android.view.View; | |
| import android.view.ViewGroup; | |
| import androidx.annotation.NonNull; | |
| import androidx.annotation.Nullable; | |
| public final class ViewUtils { | |
| /** | |
| * Retrieves the {@link Activity} from a given {@link Context}. | |
| * | |
| * <p>This method will recursively traverse up the context chain if it is a {@link ContextWrapper} | |
| * until it finds the first instance of the base context that is an {@link Activity}. | |
| */ | |
| @Nullable | |
| public static Activity getActivity(@Nullable Context context) { | |
| if (context == null) { | |
| return null; | |
| } | |
| if (context instanceof Activity) { | |
| return (Activity) context; | |
| } | |
| if (context instanceof ContextWrapper) { | |
| // Recurse up chain of base contexts until we find an Activity. | |
| return getActivity(((ContextWrapper) context).getBaseContext()); | |
| } | |
| return null; | |
| } | |
| /** | |
| * Generates a view id. | |
| * | |
| * <p>In API level 17 and above, this ID is unique. Below 17, the fallback id is used instead. | |
| * | |
| * @param fallbackId the fallback id. | |
| * @return the view id. | |
| */ | |
| public static int generateViewId(int fallbackId) { | |
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { | |
| return View.generateViewId(); | |
| } | |
| return fallbackId; | |
| } | |
| /** | |
| * Determines if the current view or any descendant view has focus. | |
| * | |
| * @param root The root view. | |
| * @return True if the current view or any descendant view has focus. | |
| */ | |
| public static boolean childHasFocus(@Nullable View root) { | |
| return traverseHierarchy(root, (View view) -> view.hasFocus()); | |
| } | |
| /** | |
| * Returns true if the root or any child view is an instance of the given types. | |
| * | |
| * @param root The root view. | |
| * @param viewTypes The types of views. | |
| * @return true if any child view is an instance of any of the given types. | |
| */ | |
| public static boolean hasChildViewOfType(@Nullable View root, Class<? extends View>[] viewTypes) { | |
| return traverseHierarchy( | |
| root, | |
| (View view) -> { | |
| for (int i = 0; i < viewTypes.length; i++) { | |
| final Class<? extends View> viewType = viewTypes[i]; | |
| if (viewType.isInstance(view)) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| }); | |
| } | |
| /** Allows to visit a view. */ | |
| public interface ViewVisitor { | |
| boolean run(@NonNull View view); | |
| } | |
| /** | |
| * Traverses the view hierarchy in pre-order and runs the visitor for each child view including | |
| * the root view. | |
| * | |
| * <p>If the visitor returns true, the traversal stops, and the method returns true. | |
| * | |
| * <p>If the visitor returns false, the traversal continues until all views are visited. | |
| * | |
| * @param root The root view. | |
| * @param visitor The visitor. | |
| * @return true if the visitor returned true for a given view. | |
| */ | |
| public static boolean traverseHierarchy(@Nullable View root, @NonNull ViewVisitor visitor) { | |
| if (root == null) { | |
| return false; | |
| } | |
| if (visitor.run(root)) { | |
| return true; | |
| } | |
| if (root instanceof ViewGroup) { | |
| final ViewGroup viewGroup = (ViewGroup) root; | |
| for (int idx = 0; idx < viewGroup.getChildCount(); idx++) { | |
| if (traverseHierarchy(viewGroup.getChildAt(idx), visitor)) { | |
| return true; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| } |