前言

有感于各路大神对 STF 细节功能的挖掘:
STF 框架之 minicap 工具
[STF 系列] 无痛接入基于 LDAP 的单点登录
发现除了 minicap 之外,对于 STF 的设备管理的触摸功能,他们用的是类似的依赖工具,叫 minitouch,Git 地址:https://github.com/openstf/minitouch

minitouch 介绍

跟 minicap 一样,minitouch 也是用 NDK 开发的,跟 minicap 使用方法类似,不过它只要上传一个 minitouch 文件就可以了。对应的文件路径树跟 minicap 一样就不重复介绍 (不过它只需要对应不同的 CPU 的 ABI,而不需要对应 SDK 版本)。实际测试这个触摸操作和 minicap 一样,实时性很高没什么卡顿。

使用概述

minitouch 的 output 信息

之后进行触摸操作的时候我发现,部分设备是可以准确的执行到正确的坐标,而部分设备坐标存在巨大的偏差。于是有仔细的看下文档,仔细看了 minitouch 的 output 部分信息:

When you first open a connection to the socket, you'll get some protocol metadata which you'll need need to read from the socket. Other than that there will be no responses of any kind.

首先,当 socket 连接建立的时候,socket 的首次 output 信息会返回 minitouch 对应的头信息,我尝试用一段 C# 代码对它进行读取

static void Main(string[] args)
        {
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socket.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1111));
            byte[] chunk = new byte[1024];
            socket.Receive(chunk);
            Console.WriteLine(System.Text.Encoding.Default.GetString(chunk));
            Console.ReadLine();
        }

执行后可以看到返回的数据为:

官方文档中对它的描述分别为:

这边要重点说的是 max-x 和 max-y,这边返回的值有可能和你的设备的真实大小并不是一样的,比如我用三星 GalaxyNote10.1 的平板调试,读取到的 max-x 和 max-y 分别为 4095x4095,而设备的实际分辨率为 1600x2560。
所以我注意看了下官方文档有写了:

It's also very important to note that the maximum X and Y coordinates may, but usually do not, match the display size. You'll need to work out a good way to map display coordinates to touch coordinates if required, possibly by using percentages for screen coordinates.
文档里有说到通常不匹配显示大小,你需要自己用一个方法来映射坐标,建议是用百分比来显示

于是我做了个实验,对设备发送了

d 0 2048 2048 50\n      
c\n

然后在设备上发现,设备被点击的坐标是 800,1280,果然分别是真实坐标的一半。
那么就是说我们在实际应用中需要给设备分别换算出真实设备大小和 minitouch 映射大小的宽度和高度的百分比参数,来进行正确的坐标映射。
需要对设备进行操作的时候,将要点击的坐标除以这个百分比就可以得到要给设备发送的正确坐标。
用 C# 做了个简单的实现:

public class MiniTouchStream
    {
        private String IP = "127.0.0.1";
        private int PORT = 1111;
        private Socket socket;
        private Banner banner = new Banner();
        private AndroidDevice device;

        public Banner Banner
        {
            get
            {
                return banner;
            }
        }

        public MiniTouchStream(AndroidDevice device)
        {
            this.device = device;
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socket.Connect(new IPEndPoint(IPAddress.Parse(IP), PORT));
            ParseBanner(socket);
        }

        private void ParseBanner(Socket socket)
        {
            byte[] chunk = new byte[64];
            socket.Receive(chunk);
            string[] result = Encoding.Default.GetString(chunk).Split(new char[2] { '\n', ' ' }).ToArray();
            banner.Version = Convert.ToInt32(result[1]);
            banner.MaxContacts = Convert.ToInt32(result[3]);
            banner.MaxX = Convert.ToInt32(result[4]);
            banner.MaxY = Convert.ToInt32(result[5]);
            banner.MaxPressure = Convert.ToInt32(result[6]);
            banner.Pid = Convert.ToInt32(result[8]);
            banner.PercentX = (double)device.Width / banner.MaxX;
            banner.PercentY = (double)device.Height / banner.MaxY;
        }

        public void TouchDown(Point downpoint)
        {
            Point realpoint = PointConvert(downpoint);
            ExecuteTouch(string.Format("d 0 {0} {1} 50\n", realpoint.X.ToString(), realpoint.Y.ToString()));
        }

        public void TouchUp()
        {
            ExecuteTouch(string.Format("u 0\n"));
        }

        public void TouchMove(Point movepoint)
        {
            Point realpoint = PointConvert(movepoint);
            ExecuteTouch(string.Format("m 0 {0} {1} 50\n", realpoint.X.ToString(), realpoint.Y.ToString()));
        }

        public void ExecuteTouch(string touchcommand)
        {
            byte[] inbuff = Encoding.ASCII.GetBytes(touchcommand);
            socket.Send(inbuff);
            string ccommand = "c\n";
            inbuff = Encoding.ASCII.GetBytes(ccommand);
            socket.Send(inbuff);   
        }

        private Point PointConvert(Point point)
        {
            Point realpoint = new Point((int)(point.X / banner.PercentX) * device.Scale, (int)(point.Y / banner.PercentY) * device.Scale);
            return realpoint;
        }
    }

最后贴个结合 minicap 和 minitouch 简单写的一个小 demo,
C#demo
第一次启动可能会稍微慢点,耐心等下


↙↙↙阅读原文可查看相关链接,并与作者交流