1 /*
2  * Copyright (C) 2020 The Android Open Source Project
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 
17 package com.android.settings.testutils;
18 
19 import android.app.Activity;
20 import android.util.Log;
21 import android.view.View;
22 import android.view.ViewGroup;
23 
24 import androidx.fragment.app.FragmentActivity;
25 import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
26 import androidx.test.runner.lifecycle.Stage;
27 
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.function.Supplier;
31 
32 public class UiUtils {
33     private static final String TAG = "UI_UTILS";
34 
waitUntilCondition(long timeoutInMillis, Supplier<Boolean> condition)35     public static boolean waitUntilCondition(long timeoutInMillis, Supplier<Boolean> condition) {
36         long start = System.nanoTime();
37         while (System.nanoTime() - start < (timeoutInMillis * 1000000)) {
38             try {
39                 //Eat NPE from condition because there's a concurrency issue that when calling
40                 //findViewById when the view hierarchy is still rendering, it sometimes encounter
41                 //null views that may exist few milliseconds before, and causes a NPE.
42                 if (condition.get()) {
43                     return true;
44                 }
45             } catch (NullPointerException e) {
46                 e.printStackTrace();
47             }
48         }
49         Log.w(TAG, "Condition not match and timeout for waiting " + timeoutInMillis + "(ms).");
50         return false;
51     }
52 
waitForActivitiesInStage(long timeoutInMillis, Stage stage)53     public static boolean waitForActivitiesInStage(long timeoutInMillis, Stage stage) {
54         final Collection<Activity> activities = new ArrayList<>();
55         waitUntilCondition(Constants.ACTIVITY_LAUNCH_WAIT_TIMEOUT, () -> {
56             activities.addAll(
57                     ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(
58                             Stage.RESUMED));
59             return activities.size() > 0;
60         });
61 
62         return activities.size() > 0;
63     }
64 
dumpView(View view)65     public static void dumpView(View view) {
66         dumpViewRecursive(view, 0, 0, 0);
67     }
68 
getFirstViewFromActivity(Activity activity)69     public static View getFirstViewFromActivity(Activity activity) {
70         return ((FragmentActivity) activity).getSupportFragmentManager().getFragments().get(
71                 0).getView();
72     }
73 
dumpViewRecursive(View view, int layer, int index, int total)74     private static void dumpViewRecursive(View view, int layer, int index, int total) {
75         if (view instanceof ViewGroup) {
76             Log.i(TAG, "L[" + layer + "] PARENT -> " + (index + 1) + "/" + total + " >> "
77                     + view.toString());
78             System.out.println(
79                     TAG + " L[" + layer + "] PARENT -> " + (index + 1) + "/" + total + " >> "
80                             + view.toString());
81             for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
82                 dumpViewRecursive(((ViewGroup) view).getChildAt(i), layer + 1, i + 1,
83                         ((ViewGroup) view).getChildCount());
84             }
85         } else {
86             Log.i(TAG, "L[" + layer + "] =END=  -> " + (index + 1) + "/" + total + " >> "
87                     + view.toString());
88         }
89     }
90 }
91