bootstrap之Orientation

Orientation

调整屏幕方向的操作。

package io.appium.android.bootstrap.handler;

import android.os.RemoteException;
import com.android.uiautomator.core.UiDevice;
import io.appium.android.bootstrap.*;
import org.json.JSONException;

import java.util.Hashtable;

/**
 * This handler is used to get or set the orientation of the device.
 *
 */
public class Orientation extends CommandHandler {

  /*
   * @param command The {@link AndroidCommand} used for this handler.
   *
   * @return {@link AndroidCommandResult}
   *
   * @throws JSONException
   *
   * @see io.appium.android.bootstrap.CommandHandler#execute(io.appium.android.
   * bootstrap.AndroidCommand)
   */
  @Override
  public AndroidCommandResult execute(final AndroidCommand command)
      throws JSONException {

    final Hashtable<String, Object> params = command.params();
    if (params.containsKey("orientation")) {
      // Set the rotation

      final String orientation = (String) params.get("orientation");
      try {
        return handleRotation(orientation);
      } catch (final Exception e) {
        return getErrorResult("Unable to rotate screen: " + e.getMessage());
      }
    } else {
      // Get the rotation
      return getRotation();
    }

  }

  /**
   * Returns the current rotation
   *
   * @return {@link AndroidCommandResult}
   */
  private AndroidCommandResult getRotation() {
    String res = null;
    final UiDevice d = UiDevice.getInstance();
    final OrientationEnum currentRotation = OrientationEnum.fromInteger(d
        .getDisplayRotation());
    Logger.debug("Current rotation: " + currentRotation);
    switch (currentRotation) {
      case ROTATION_0:
      case ROTATION_180:
        res = "PORTRAIT";
        break;
      case ROTATION_90:
      case ROTATION_270:
        res = "LANDSCAPE";
        break;
    }

    if (res != null) {
      return getSuccessResult(res);
    } else {
      return getErrorResult("Get orientation did not complete successfully");
    }
  }

  /**
   * Set the desired rotation
   *
   * @param orientation
   *          The rotation desired (LANDSCAPE or PORTRAIT)
   * @return {@link AndroidCommandResult}
   * @throws RemoteException
   * @throws InterruptedException
   */
  private AndroidCommandResult handleRotation(final String orientation)
      throws RemoteException, InterruptedException {
    final UiDevice d = UiDevice.getInstance();
    OrientationEnum desired;
    OrientationEnum current = OrientationEnum.fromInteger(d
        .getDisplayRotation());

    Logger.debug("Desired orientation: " + orientation);
    Logger.debug("Current rotation: " + current);

    if (orientation.equalsIgnoreCase("LANDSCAPE")) {
      switch (current) {
        case ROTATION_0:
          d.setOrientationRight();
          desired = OrientationEnum.ROTATION_270;
          break;
        case ROTATION_180:
          d.setOrientationLeft();
          desired = OrientationEnum.ROTATION_270;
          break;
        default:
          return getSuccessResult("Already in landscape mode.");
      }
    } else {
      switch (current) {
        case ROTATION_90:
        case ROTATION_270:
          d.setOrientationNatural();
          desired = OrientationEnum.ROTATION_0;
          break;
        default:
          return getSuccessResult("Already in portrait mode.");
      }
    }
    current = OrientationEnum.fromInteger(d.getDisplayRotation());
    // If the orientation has not changed,
    // busy wait until the TIMEOUT has expired
    final int TIMEOUT = 2000;
    final long then = System.currentTimeMillis();
    long now = then;
    while (current != desired && now - then < TIMEOUT) {
      Thread.sleep(100);
      now = System.currentTimeMillis();
      current = OrientationEnum.fromInteger(d.getDisplayRotation());
    }
    if (current != desired) {
      return getErrorResult("Set the orientation, but app refused to rotate.");
    }
    return getSuccessResult("Rotation (" + orientation + ") successful.");
  }
}

这个事件有点小复杂哈,当初研究uiautomator源码时就被它折腾的不行,也只实验了左和上的方向成功。没办法,既然又遇到了,那就只能纯理论讲啦。

execute方法中,首先判断参数中是否含有orientation,如果含有调用handleRotation,否则调用getRotation。

所以execute又分流到上面的2个方法中。

handleRotation

这种情况是参数里含有orientation,此时,我们来看看该方法中做了哪些事。

final UiDevice d = UiDevice.getInstance();
    OrientationEnum desired;
    OrientationEnum current = OrientationEnum.fromInteger(d
        .getDisplayRotation());

首先获取当前设备的方向,然后初始化一个私有变量,以备后用。其中OrientationEnum枚举类里定义了4个方向,fromInteger方法是根据整数值得到相应的枚举值,其中各个值的意思。

public enum OrientationEnum {
  ROTATION_0(0), ROTATION_90(1), ROTATION_180(2), ROTATION_270(3);

  public static OrientationEnum fromInteger(final int x) {
    switch (x) {
      case 0:
        return ROTATION_0;
      case 1:
        return ROTATION_90;
      case 2:
        return ROTATION_180;
      case 3:
        return ROTATION_270;
    }
    return null;
  }

ROTATION_0:你正常查看手机时,竖屏。此时屏幕的方向为0度。此时的power键在顶端。如下:

ROTATION_90:你将上面的屏幕向右顺时针旋转90度,此时设备旋转角度为90度,此时我的power键在右端。如果此时你的设备可以自动旋转屏幕的话,你屏幕里面的内容应该是什么样的?如下所示

ROTATION_180:从90度角再向下顺时针旋转90度,此时我的power键在下端,此时的角度为180.由于我的手机禁止了这种自由旋转,所以此时的屏幕展现在我的面前是这样一番景象:

ROTATION_270:从180度再顺时针想左旋转90度,此时我power键在左边。此时为270度,展现在我面前的图如下:

如果你理解了上面4个关键字的意思。那么下面理解代码就很简单啦。

handleRotation方法里做完初始化操作以后,就要判断客户端要求是横屏还是竖屏,如果是横屏,处理如下:

if (orientation.equalsIgnoreCase("LANDSCAPE")) {
      switch (current) {
        case ROTATION_0:
          d.setOrientationRight();
          desired = OrientationEnum.ROTATION_270;
          break;
        case ROTATION_180:
          d.setOrientationLeft();
          desired = OrientationEnum.ROTATION_270;
          break;
        default:
          return getSuccessResult("Already in landscape mode.");
      }
    }

如果是横屏的话,那么只需要处理屏幕处于0度和180度的情况,因为90度和270度都已经是横屏啦,自然不需要再处理。

如果是ROTATION_0,说明设备朝上,此时想要横屏,自然是顺时针向右旋转一下屏幕。此时,正常情况下可以旋转的话,屏幕里的视图应该是从左到右的,所以desired的值才会被设置为ROTATION_270.所以要分清屏幕的角度和视图的角度。下面就是向右顺时针旋转90度后,里面的视图是270度的,此时power键在右端。

如果是ROTATION_180度,说明设备拿反了,power键朝下。此时你向左顺时针旋转90度或者向右逆时针旋转90度,都能达到横屏的效果。源码里是向左旋转的,此时power键朝左,视图和上面是一样的,desired的值为ROTATION——270.

====================================================================================================================================

如果客户端传递过来的命令想要的是竖屏,就要走else里的代码块:

else {
      switch (current) {
        case ROTATION_90:
        case ROTATION_270:
          d.setOrientationNatural();
          desired = OrientationEnum.ROTATION_0;
          break;
        default:
          return getSuccessResult("Already in portrait mode.");
      }
    }

此时只要处理90度和270的情况。我们要把它变为ROTATION_0的情况,d.setOrientationNatural()是设置设备转到自然方向,该方向就是设备初始设置的方向。说明通常的设备横屏的2个方向可以旋转,竖屏方向就只有一个方向可以旋转。上面的处理完毕后,方法会做一个判断,判断是否旋转成功。

 current = OrientationEnum.fromInteger(d.getDisplayRotation());
    // If the orientation has not changed,
    // busy wait until the TIMEOUT has expired
    final int TIMEOUT = 2000;
    final long then = System.currentTimeMillis();
    long now = then;
    while (current != desired && now - then < TIMEOUT) {
      Thread.sleep(100);
      now = System.currentTimeMillis();
      current = OrientationEnum.fromInteger(d.getDisplayRotation());
    }
    if (current != desired) {
      return getErrorResult("Set the orientation, but app refused to rotate.");
    }
    return getSuccessResult("Rotation (" + orientation + ") successful.");

首先获得此时屏幕的方向,然后判断一下与预期的是否相同。如果不相同,等待2秒钟,再获取一次屏幕的方向,如果经过这么一次验证完毕后,当前的屏幕方向仍然和预期的不相同,那么就返回旋转失败的消息给客户端。如果相同的话,就返回旋转成功的消息给客户端。

到此为止handleRotation处理完毕,下面处理参数里不含有orientation的情况。

getRotation

该方法中就是根据当前的屏幕的方向得到横屏还是竖屏,将结果返回给客户端。很简单。

总结

通过上面的分析,说明客户端关于屏幕方向的命令有2种:

  • 获取屏幕的方向
  • 改变屏幕的方向

大家要特别主要选择方向的定义,设备的方向和里面视图的方向的区别。

bootstrap之Orientation,布布扣,bubuko.com

时间: 2024-10-09 18:54:05

bootstrap之Orientation的相关文章

手机自动化测试:appium源码分析之bootstrap四

手机自动化测试:appium源码分析之bootstrap四 Orientation是调整屏幕方向的操作 package io.appium.android.bootstrap.handler; import android.os.RemoteException;import com.android.uiautomator.core.UiDevice;import io.appium.android.bootstrap.*;import org.json.JSONException; import

bootstrap之Click大事

上一篇文章中谈到了bootstrap流程,本文开始把目光bootstrap它可以接受指令(从源代码视图的透视.因为appium该项目现在还处于不断更新,因此,一些指令已经实现.也许未来会实现一些.从视图的源代码的点的优点是对未来要知道将来也许appium哪些功能可以做). 在bootstrap项目中的io.appium.android.bootstrap.handler包中的类都是相应的相应的指令的类,里面都有execute方法来运行命令.先上上一篇文章中讲的map. private stati

Appuim源码剖析(Bootstrap)

Appuim源码剖析(Bootstrap) SkySeraph Jan. 26th 2017 Email:[email protected] 更多精彩请直接访问SkySeraph个人站点:www.skyseraph.com About Appuim Appium 是一个自动化测试开源工具,支持 iOS 平台和 Android 平台上的原生应用,web 应用和混合应用. 这里有很关键一点,跨平台.更多了解Appuim多平台支持相关信息,参考官方platform-support 相关概念 C/S 架

关于bootstrap的datepicker在meteor应用中的使用(不包含bootstrap框架)

1.安装bootstrap3-datepicker包 meteor add rajit:bootstrap3-datepicker 2.使用方法 Example In your handlebars template: <input type="text" class="form-control" id="my-datepicker"> In client-side JS code: Template.mytemplate.rende

appium框架之bootstrap

(闲来无事,做做测试..)最近弄了弄appium,感觉挺有意思,就深入研究了下. 看小弟这篇文章之前,先了解一下appium的架构,对你理解有好处,推荐下面这篇文章:testerhome appium是开源项目,可以获得源码:appium-master 在eclipse中用maven导入会发现有2个项目:bootstrap和sauce_appium_junit. sauce_appium_junit是一些测试用例的集合,帮助学习的.bootstrap就是appium架构中放在手机端的一个服务器.

手机自动化测试:appium源码分析之bootstrap一

手机自动化测试:appium源码分析之bootstrap一 前言: poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.poptest推出手机自动化测试的课程,讲解appuim的实际应用,培训全程用商业项目,   大家可以加qq群进行交流:195983133 开源的项目最大的好处是可以获得源代码,我们可以在源代码的基础上进行修改代码,编译,开发出符合我们要求的项目.如果想要做到可以修改源代码,首先要读懂代码了解代码.国内的一些公司

手机自动化测试:appium源码分析之bootstrap三

手机自动化测试:appium源码分析之bootstrap三 研究bootstrap源码,我们可以通过代码的结构,可以看出来appium的扩展思路和实现方式,从中可以添加我们自己要的功能,针对appium进行定制,poptest在2015年10月24日开设appium的课程,课程采用真实的商业项目进行培训,用现在互联网金融的业务.bootstrap代码中io.appium.android.bootstrap.handler包中的类都是对应的指令类,核心都是execute方法. 下面我们看下Hash

bootstrap响应式导航栏

参见:http://www.w3cschool.cc/bootstrap/bootstrap-navbar.html 为了给导航栏添加响应式特性,您要折叠的内容必须包裹在带有 classes .collapse..navbar-collapse 的 <div> 中.折叠起来的导航栏实际上是一个带有 class .navbar-toggle 及两个 data- 元素的按钮.第一个是 data-toggle,用于告诉 JavaScript 需要对按钮做什么,第二个是 data-target,指示要

Appium Android Bootstrap源码分析之命令解析执行

通过上一篇文章<Appium Android Bootstrap源码分析之控件AndroidElement>我们知道了Appium从pc端发送过来的命令如果是控件相关的话,最终目标控件在bootstrap中是以AndroidElement对象的方式呈现出来的,并且该控件对象会在AndroidElementHash维护的控件哈希表中保存起来.但是appium触发一个命令除了需要提供是否与控件相关这个信息外,还需要其他的一些信息,比如,这个是什么命令?这个就是我们这篇文章需要讨论的话题了. 下面我