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 17 package io.appium.droiddriver.util; 18 19 import android.app.Activity; 20 import android.os.Looper; 21 import android.util.Log; 22 23 import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry; 24 import androidx.test.runner.lifecycle.Stage; 25 26 import java.util.Iterator; 27 import java.util.concurrent.Callable; 28 29 /** Static helper methods for retrieving activities. */ 30 public class ActivityUtils { 31 private static final Callable<Activity> GET_RUNNING_ACTIVITY = 32 new Callable<Activity>() { 33 @Override 34 public Activity call() { 35 Iterator<Activity> activityIterator = 36 ActivityLifecycleMonitorRegistry.getInstance() 37 .getActivitiesInStage(Stage.RESUMED) 38 .iterator(); 39 return activityIterator.hasNext() ? activityIterator.next() : null; 40 } 41 }; 42 private static Supplier<Activity> runningActivitySupplier = 43 new Supplier<Activity>() { 44 @Override 45 public Activity get() { 46 try { 47 // If this is called on main (UI) thread, don't call runOnMainSync 48 if (Looper.myLooper() == Looper.getMainLooper()) { 49 return GET_RUNNING_ACTIVITY.call(); 50 } 51 52 return InstrumentationUtils.runOnMainSyncWithTimeout(GET_RUNNING_ACTIVITY); 53 } catch (Exception e) { 54 Logs.log(Log.WARN, e); 55 return null; 56 } 57 } 58 }; 59 60 /** 61 * Sets the Supplier for the running (a.k.a. resumed or foreground) activity. If a custom runner 62 * is used, this method must be called appropriately, otherwise {@link #getRunningActivity} won't 63 * work. 64 */ setRunningActivitySupplier(Supplier<Activity> activitySupplier)65 public static synchronized void setRunningActivitySupplier(Supplier<Activity> activitySupplier) { 66 runningActivitySupplier = Preconditions.checkNotNull(activitySupplier); 67 } 68 69 /** Shorthand to {@link #getRunningActivity(long)} with {@code timeoutMillis=30_000}. */ getRunningActivity()70 public static Activity getRunningActivity() { 71 return getRunningActivity(30_000L); 72 } 73 74 /** 75 * Waits for idle on main looper, then gets the running (a.k.a. resumed or foreground) activity. 76 * 77 * @return the currently running activity, or null if no activity has focus. 78 */ getRunningActivity(long timeoutMillis)79 public static Activity getRunningActivity(long timeoutMillis) { 80 // It's safe to check running activity only when the main looper is idle. 81 // If the AUT is in background, its main looper should be idle already. 82 // If the AUT is in foreground, its main looper should be idle eventually. 83 if (InstrumentationUtils.tryWaitForIdleSync(timeoutMillis)) { 84 return getRunningActivityNoWait(); 85 } 86 return null; 87 } 88 89 /** 90 * Gets the running (a.k.a. resumed or foreground) activity without waiting for idle on main 91 * looper. 92 * 93 * @return the currently running activity, or null if no activity has focus. 94 */ getRunningActivityNoWait()95 public static synchronized Activity getRunningActivityNoWait() { 96 return runningActivitySupplier.get(); 97 } 98 99 public interface Supplier<T> { 100 /** 101 * Retrieves an instance of the appropriate type. The returned object may or may not be a new 102 * instance, depending on the implementation. 103 * 104 * @return an instance of the appropriate type 105 */ get()106 T get(); 107 } 108 } 109