com.tascape.qa.th.android.driver.AdbDevice.java Source code

Java tutorial

Introduction

Here is the source code for com.tascape.qa.th.android.driver.AdbDevice.java

Source

/*
 * Copyright 2015 - 2016 Nebula Bay.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.tascape.qa.th.android.driver;

import com.android.uiautomator.stub.IUiDevice;
import com.google.common.collect.Lists;
import com.tascape.qa.th.driver.EntityDriver;
import com.tascape.qa.th.android.comm.Adb;
import com.tascape.qa.th.exception.EntityDriverException;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author linsong wang
 */
class AdbDevice extends EntityDriver {
    private static final Logger LOG = LoggerFactory.getLogger(AdbDevice.class);

    private Adb adb;

    private String version;

    public void setAdb(Adb adb) throws IOException {
        this.adb = adb;
    }

    public Adb getAdb() {
        return adb;
    }

    @Override
    public String getName() {
        try {
            return getPropValue("ro.product.brand") + "-" + getPropValue("ro.product.model");
        } catch (IOException ex) {
            LOG.warn(ex.getMessage());
            return "na";
        }
    }

    @Override
    public String getVersion() {
        if (StringUtils.isBlank(version)) {
            try {
                return getPropValue("ro.build.version.release") + "-" + getPropValue("ro.build.version.sdk ");
            } catch (IOException ex) {
                LOG.warn(ex.getMessage());
                version = "";
            }
        }
        return version;
    }

    public List<String> getProp() throws IOException {
        List<String> props = this.adb.shell(Lists.newArrayList("getprop"));
        props.forEach(p -> LOG.debug(p));
        return props;
    }

    public boolean grantPermission(String packageName, String permission) throws IOException {
        List<String> res = this.adb.shell(Lists.newArrayList("pm", packageName, permission));
        LOG.debug("{}", res);
        return res.stream().filter(l -> l.contains("Success")).findAny().isPresent();
    }

    public boolean uninstall(String packageName) throws IOException {
        List<String> res = this.adb.adb(Lists.newArrayList("uninstall", packageName));
        LOG.debug("{}", res);
        return res.stream().filter(l -> l.contains("Success")).findAny().isPresent();
    }

    public String getAppVersion(String packageName) throws IOException, EntityDriverException {
        List<String> res = this.adb.shell(Lists.newArrayList("dumpsys", "package", packageName));
        res.forEach(l -> LOG.debug(l));
        String versionName = "";
        String versionCode = "";
        for (int i = 0, j = res.size(); i < j; i++) {
            String line = res.get(i);
            if (line.contains("versionName")) {
                versionName = line.trim().split("=")[1];
            } else if (line.contains("versionCode")) {
                versionCode = line.trim().split(" ")[0].split("=")[1];
            }
            if (!versionName.isEmpty() && !versionCode.isEmpty()) {
                break;
            }
        }
        if (versionName.isEmpty() || versionCode.isEmpty()) {
            throw new EntityDriverException("Cannot find app version");
        }
        return versionName + "-" + versionCode;
    }

    public String getSystemLanguage() throws IOException {
        List<String> res = this.getProp("persist.sys.language");
        res.addAll(this.getProp("ro.product.locale.language"));
        return res.stream().filter(s -> StringUtils.isNotBlank(s)).findFirst().get();
    }

    public List<String> getProp(String name) throws IOException {
        List<String> res = this.adb.shell(Lists.newArrayList("getprop", name));
        LOG.debug("{}", res);
        return res;
    }

    public String getPropValue(String name) throws IOException {
        List<String> res = this.getProp(name);
        return res.stream().filter(s -> !(s.startsWith("*") && s.endsWith("*"))).findFirst().get();
    }

    /**
     * Gets event output lines.
     *
     * @param device such as /dev/input/event0
     *
     * @return output event log lines
     *
     * @throws IOException in case of IO issue
     */
    public List<String> logTouchEvents(String device) throws IOException {
        return this.adb.shell(Arrays.asList(new Object[] { "getevent", "-lt", device }));
    }

    public String recordScreen(int seconds, int bitRate) throws IOException {
        String mp4 = "sr-" + UUID.randomUUID() + ".mp4";
        this.adb.shellAsync(Arrays.asList(new Object[] { "screenrecord", "--time-limit", seconds,
                IUiDevice.TMP_DIR + mp4, "--bit-rate", bitRate }), seconds * 1000L);
        return mp4;
    }

    public File getScreenRecord(String name) throws IOException {
        File mp4 = this.getLogPath().resolve(name).toFile();

        // todo
        this.getAdb().pull(IUiDevice.TMP_DIR + name, mp4);
        return mp4;
    }

    /**
     * Dumps window hierarchy into xml file. This does not work when 'uiautomator' process is running on device.
     *
     * @return the hierarchy xml file
     *
     * @throws IOException any error
     */
    public File dumpWindowHierarchy() throws IOException {
        String f = "/data/local/tmp/uidump.xml";
        adb.shell(Lists.newArrayList("rm", f)).forEach(l -> LOG.debug(l));
        adb.shell(Lists.newArrayList("uiautomator", "dump", f)).forEach(l -> LOG.debug(l));
        File xml = this.getLogPath().resolve("ui-" + System.currentTimeMillis() + ".xml").toFile();
        this.adb.pull(f, xml);
        LOG.debug("Save WindowHierarchy into {}", xml.getAbsolutePath());
        return xml;
    }

    /**
     * The input macro can emulate all sort of events, as described in its documentation.
     * <pre>
     * Usage: input [source] command [arg...]
     *
     * The sources are:
     *   trackball
     *   joystick
     *   touchnavigation
     *   mouse
     *   keyboard
     *   gamepad
     *   touchpad
     *   dpad
     *   stylus
     *   touchscreen
     *
     * The commands and default sources are:
     *   text 'string' (Default: touchscreen) [delay]
     *   keyevent [--longpress] 'key code number or name' ... (Default: keyboard)
     *   tap x y (Default: touchscreen)
     *   swipe x1 y1 x2 y2 [duration(ms)] (Default: touchscreen)
     *   press (Default: trackball)
     *   roll dx dy (Default: trackball)
     * </pre>
     *
     * @param arguments arguments
     *
     * @return adb stdout
     *
     * @throws IOException in case of any issue
     */
    public List<String> input(final List<Object> arguments) throws IOException {
        List<Object> args = new ArrayList<>(arguments);
        args.add(0, "input");
        return adb.shell(args);
    }

    /**
     * Sends keyboard key event to device. Shortcut to input(keyevent, ...).
     *
     * @param key key value, see class KeyEvent
     *
     * @return adb stdout
     *
     * @throws IOException in case of any issue
     */
    public List<String> inputKeyEvent(int key) throws IOException {
        return this.input(Lists.newArrayList("keyevent", key + ""));
    }

    /**
     * Sends text to device. Shortcut to input(text, ...).
     *
     * @param text text sent to device
     *
     * @return adb stdout
     *
     * @throws IOException in case of any issue
     */
    public List<String> inputText(String text) throws IOException {
        return this.input(Lists.newArrayList("text", text));
    }

    /**
     * Sends touchscreen tap event to device. Shortcut to input(tap, ...).
     *
     * @param x x
     * @param y y
     *
     * @return adb stdout
     *
     * @throws IOException in case of any issue
     */
    public List<String> inputTap(int x, int y) throws IOException {
        return this.input(Lists.newArrayList("tap", x, y));
    }

    /**
     * Emulates touchscreen interaction with sendevent in Android.
     *
     * http://ktnr74.blogspot.com/2013/06/emulating-touchscreen-interaction-with.html.
     * busybox usleep 50000: wait at least 50 milliseconds
     *
     * @param device such as /dev/input/event0
     * @param type   For touch events only 2 event types are used:
     *               EV_ABS (3)
     *               EV_SYN (0)
     * @param code   Touching the display (in case of Type A protocol) will result in an input report (sequence of input
     *               events) containing the following event codes:
     *               ABS_MT_TRACKING_ID (57) - ID of the touch (important for multi-touch reports), value -1 to release
     *               ABS_MT_POSITION_X (53) - x coordinate of the touch
     *               ABS_MT_POSITION_Y (54) - y coordinate of the touch
     *               ABS_MT_TOUCH_MAJOR (48) - basically width of your finger tip in pixels, use 5
     *               ABS_MT_PRESSURE (58) - pressure of the touch, value 50
     *               SYN_MT_REPORT (2) - end of separate touch data, value 0
     *               SYN_REPORT (0) - end of report, value 0
     * @param value  event value
     *
     * @throws IOException in case of any issue
     */
    public void sendEvent(String device, int type, int code, int value) throws IOException {
        this.adb.shell(Lists.newArrayList("sendevent", "" + type, "" + code, "" + value));
    }

    /**
     *
     * @param eventLogFile log
     *
     * @return event timestamps
     *
     * @throws java.io.FileNotFoundException if no file found
     * @throws java.io.IOException           if io issue
     */
    public List<Long> getTouchEvents(File eventLogFile) throws FileNotFoundException, IOException {
        List<Long> events = new ArrayList<>();
        BufferedReader bis = new BufferedReader(new FileReader(eventLogFile));
        String line = "";

        Pattern patternEvent = Pattern.compile("\\[(.+?)\\].+");
        while (line != null) {
            Matcher matcherEvent = patternEvent.matcher(line);
            if (matcherEvent.matches()) {
                LOG.trace("start time {}", line);
                String ts = matcherEvent.group(1);
                Double d = Double.parseDouble(ts);
                events.add((long) (d * 1000000));
                break;
            }
            line = bis.readLine();
        }

        Pattern patternButtonUp = Pattern.compile("\\[(.+?)\\] EV_KEY.+?BTN_TOUCH.+?UP.+");
        while (line != null) {
            Matcher matcherUp = patternButtonUp.matcher(line);
            if (matcherUp.matches()) {
                String ts = matcherUp.group(1);
                Double d = Double.parseDouble(ts);
                events.add((long) (d * 1000000));
            }
            line = bis.readLine();
        }

        if (!events.isEmpty()) {
            long start = events.get(0);
            for (int i = 0; i < events.size(); i++) {
                long time = events.get(i);
                events.set(i, time - start);
            }
            events.remove(0);
        }
        return events;
    }

    @Override
    public void reset() throws Exception {
    }

    public String getSerial() {
        return this.adb.getSerial();
    }

    private boolean bufferedImagesEqual(BufferedImage img1, BufferedImage img2) {
        if (img1.getWidth() != img2.getWidth() || img1.getHeight() != img2.getHeight()) {
            return false;
        }

        boolean equal = true;
        for (int x = 100; x < img1.getWidth() - 100; x++) {
            for (int y = 100; y < img1.getHeight() - 100; y++) {
                if (img1.getRGB(x, y) != img2.getRGB(x, y)) {
                    equal = false;
                }
            }
        }
        return equal;
    }

    public static void main(String[] args) throws Exception {
        Adb adb = new Adb();
        AdbDevice device = new AdbDevice();
        device.setAdb(adb);
    }
}