Home | History | Annotate | Download | only in instrumentation
      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 com.google.android.droiddriver.instrumentation;
     18 
     19 import android.app.Instrumentation;
     20 import android.os.SystemClock;
     21 import android.util.Log;
     22 import android.view.View;
     23 
     24 import com.google.android.droiddriver.actions.InputInjector;
     25 import com.google.android.droiddriver.base.BaseDroidDriver;
     26 import com.google.android.droiddriver.base.DroidDriverContext;
     27 import com.google.android.droiddriver.exceptions.DroidDriverException;
     28 import com.google.android.droiddriver.exceptions.TimeoutException;
     29 import com.google.android.droiddriver.util.ActivityUtils;
     30 import com.google.android.droiddriver.util.Logs;
     31 
     32 /**
     33  * Implementation of DroidDriver that is driven via instrumentation.
     34  */
     35 public class InstrumentationDriver extends BaseDroidDriver<View, ViewElement> {
     36   private final DroidDriverContext<View, ViewElement> context;
     37   private final InputInjector injector;
     38   private final InstrumentationUiDevice uiDevice;
     39 
     40   public InstrumentationDriver(Instrumentation instrumentation) {
     41     context = new DroidDriverContext<View, ViewElement>(instrumentation, this);
     42     injector = new InstrumentationInputInjector(instrumentation);
     43     uiDevice = new InstrumentationUiDevice(context);
     44   }
     45 
     46   @Override
     47   public InputInjector getInjector() {
     48     return injector;
     49   }
     50 
     51   @Override
     52   protected ViewElement newRootElement() {
     53     return context.newRootElement(findRootView());
     54   }
     55 
     56   @Override
     57   protected ViewElement newUiElement(View rawElement, ViewElement parent) {
     58     return new ViewElement(context, rawElement, parent);
     59   }
     60 
     61   private static class FindRootViewRunnable implements Runnable {
     62     View rootView;
     63     Throwable exception;
     64 
     65     @Override
     66     public void run() {
     67       try {
     68         View[] views = RootFinder.getRootViews();
     69         if (views.length > 1) {
     70           Logs.log(Log.VERBOSE, "views.length=" + views.length);
     71           for (View view : views) {
     72             if (view.hasWindowFocus()) {
     73               rootView = view;
     74               return;
     75             }
     76           }
     77         }
     78         // Fall back to DecorView.
     79         rootView = ActivityUtils.getRunningActivity().getWindow().getDecorView();
     80       } catch (Throwable e) {
     81         exception = e;
     82         Logs.log(Log.ERROR, e);
     83       }
     84     }
     85   }
     86 
     87   private View findRootView() {
     88     waitForRunningActivity();
     89     FindRootViewRunnable findRootViewRunnable = new FindRootViewRunnable();
     90     context.runOnMainSync(findRootViewRunnable);
     91     if (findRootViewRunnable.exception != null) {
     92       throw new DroidDriverException(findRootViewRunnable.exception);
     93     }
     94     return findRootViewRunnable.rootView;
     95   }
     96 
     97   private void waitForRunningActivity() {
     98     long timeoutMillis = getPoller().getTimeoutMillis();
     99     long end = SystemClock.uptimeMillis() + timeoutMillis;
    100     while (true) {
    101       if (ActivityUtils.getRunningActivity() != null) {
    102         return;
    103       }
    104       long remainingMillis = end - SystemClock.uptimeMillis();
    105       if (remainingMillis < 0) {
    106         throw new TimeoutException(String.format(
    107             "Timed out after %d milliseconds waiting for foreground activity", timeoutMillis));
    108       }
    109       SystemClock.sleep(Math.min(250, remainingMillis));
    110     }
    111   }
    112 
    113   @Override
    114   public InstrumentationUiDevice getUiDevice() {
    115     return uiDevice;
    116   }
    117 }
    118