Home | History | Annotate | Download | only in view
      1 /*
      2  * Copyright (C) 2015 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.actions.view;
     18 
     19 import android.content.Context;
     20 import android.os.Bundle;
     21 import android.os.ResultReceiver;
     22 import android.os.SystemClock;
     23 import android.util.Log;
     24 import android.view.View;
     25 import android.view.inputmethod.InputMethodManager;
     26 
     27 import java.util.concurrent.CountDownLatch;
     28 import java.util.concurrent.TimeUnit;
     29 import java.util.concurrent.atomic.AtomicInteger;
     30 
     31 import io.appium.droiddriver.UiElement;
     32 import io.appium.droiddriver.exceptions.ActionException;
     33 import io.appium.droiddriver.exceptions.DroidDriverException;
     34 import io.appium.droiddriver.util.InstrumentationUtils;
     35 import io.appium.droiddriver.util.Logs;
     36 
     37 /**
     38  * Closes soft keyboard. Based on the <a href="https://code.google.com/p/android-test-kit/wiki/Espresso">Espresso</a>
     39  * code under the same name.
     40  */
     41 public class CloseKeyboardAction extends ViewAction {
     42   /** Defaults timeoutMillis to 2000 */
     43   public static final CloseKeyboardAction DEFAULT_INSTANCE = new CloseKeyboardAction(2000L, 1000L);
     44 
     45   private final long keyboardDismissalDelayMillis;
     46 
     47   /**
     48    * @param timeoutMillis                the value returned by {@link #getTimeoutMillis}
     49    * @param keyboardDismissalDelayMillis <a href="https://code.google.com/p/android-test-kit/issues/detail?id=79#c7">a
     50    *                                     delay for the soft keyboard to finish closing</a>
     51    */
     52   public CloseKeyboardAction(long timeoutMillis, long keyboardDismissalDelayMillis) {
     53     super(timeoutMillis);
     54     this.keyboardDismissalDelayMillis = keyboardDismissalDelayMillis;
     55   }
     56 
     57   protected boolean perform(View view, UiElement element) {
     58     InputMethodManager imm = (InputMethodManager) InstrumentationUtils.getTargetContext()
     59         .getSystemService(Context.INPUT_METHOD_SERVICE);
     60     final AtomicInteger resultCodeHolder = new AtomicInteger();
     61     final CountDownLatch latch = new CountDownLatch(1);
     62 
     63     ResultReceiver resultReceiver = new ResultReceiver(null) {
     64       @Override
     65       protected void onReceiveResult(int resultCode, Bundle resultData) {
     66         resultCodeHolder.set(resultCode);
     67         latch.countDown();
     68       }
     69     };
     70 
     71     if (!imm.hideSoftInputFromWindow(view.getWindowToken(), 0, resultReceiver)) {
     72       Logs.log(Log.INFO, "InputMethodManager.hideSoftInputFromWindow returned false");
     73       // Soft keyboard is not shown if hideSoftInputFromWindow returned false
     74       return true;
     75     }
     76 
     77     try {
     78       if (!latch.await(getTimeoutMillis(), TimeUnit.MILLISECONDS)) {
     79         throw new ActionException("Timed out after " + getTimeoutMillis() + " milliseconds" +
     80             " waiting for resultCode from InputMethodManager.hideSoftInputFromWindow");
     81       }
     82     } catch (InterruptedException e) {
     83       throw DroidDriverException.propagate(e);
     84     }
     85 
     86     int resultCode = resultCodeHolder.get();
     87     if (resultCode != InputMethodManager.RESULT_UNCHANGED_HIDDEN
     88         && resultCode != InputMethodManager.RESULT_HIDDEN) {
     89       throw new ActionException("resultCode from InputMethodManager.hideSoftInputFromWindow="
     90           + resultCode);
     91     }
     92 
     93     // Wait for the soft keyboard to finish closing
     94     SystemClock.sleep(keyboardDismissalDelayMillis);
     95     return true;
     96   }
     97 
     98   @Override
     99   public String toString() {
    100     return getClass().getSimpleName();
    101   }
    102 }
    103