¿Cómo realizar un arrastre (basado en las coordenadas X, Y del mouse) en Android usando AccessibilityService?

39

¿Quiero saber cómo realizar un arrastre en Android basado en las coordenadas X, Y del mouse? considere como dos ejemplos simples, el Team Viewer / QuickSupport dibujando el "patrón de contraseña" en el teléfono inteligente remoto y el Pen of Windows Paint, respectivamente.

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

Todo lo que puedo hacer es simular el tacto (con dispatchGesture()y también AccessibilityNodeInfo.ACTION_CLICK).

Encontré estos enlaces relevantes, pero no sé si pueden ser útiles:

A continuación se muestra mi código de trabajo que se utiliza para enviar las coordenadas del mouse (dentro del PictureBoxcontrol) al teléfono remoto y simular el tacto.

Aplicación de formularios de Windows:

private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
    foreach (ListViewItem item in lvConnections.SelectedItems)
    {
        // Remote screen resolution
        string[] tokens = item.SubItems[5].Text.Split('x'); // Ex: 1080x1920

        int xClick = (e.X * int.Parse(tokens[0].ToString())) / (pictureBox1.Size.Width);
        int yClick = (e.Y * int.Parse(tokens[1].ToString())) / (pictureBox1.Size.Height);

        Client client = (Client)item.Tag;

        if (e.Button == MouseButtons.Left)
            client.sock.Send(Encoding.UTF8.GetBytes("TOUCH" + xClick + "<|>" + yClick + Environment.NewLine));
    }
}

Editar:

Mi último intento fue una "pantalla de deslizamiento" utilizando las coordenadas del mouse (aplicación de formularios Windows Forms de C #) y una rutina personalizada de Android (con referencia al código de "pantalla de deslizar" vinculado anteriormente), respectivamente:

private Point mdownPoint = new Point();

private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
    foreach (ListViewItem item in lvConnections.SelectedItems)
    {
        // Remote screen resolution
        string[] tokens = item.SubItems[5].Text.Split('x'); // Ex: 1080x1920

        Client client = (Client)item.Tag;

        if (e.Button == MouseButtons.Left)
        {
            xClick = (e.X * int.Parse(tokens[0].ToString())) / (pictureBox1.Size.Width); 
            yClick = (e.Y * int.Parse(tokens[1].ToString())) / (pictureBox1.Size.Height);

            // Saving start position:

            mdownPoint.X = xClick; 
            mdownPoint.Y = yClick; 

            client.sock.Send(Encoding.UTF8.GetBytes("TOUCH" + xClick + "<|>" + yClick + Environment.NewLine));
        }
    }
}

private void PictureBox1_MouseMove(object sender, MouseEventArgs e)
{
    foreach (ListViewItem item in lvConnections.SelectedItems)
    {
        // Remote screen resolution
        string[] tokens = item.SubItems[5].Text.Split('x'); // Ex: 1080x1920

        Client client = (Client)item.Tag;

        if (e.Button == MouseButtons.Left)
        {
            xClick = (e.X * int.Parse(tokens[0].ToString())) / (pictureBox1.Size.Width);
            yClick = (e.Y * int.Parse(tokens[1].ToString())) / (pictureBox1.Size.Height);

            client.sock.Send(Encoding.UTF8.GetBytes("MOUSESWIPESCREEN" + mdownPoint.X + "<|>" + mdownPoint.Y + "<|>" + xClick + "<|>" + yClick + Environment.NewLine));
        }
    }
}

Servicio de accesibilidad de Android :

public void Swipe(int x1, int y1, int x2, int y2, int time) {

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
    System.out.println(" ======= Swipe =======");

    GestureDescription.Builder gestureBuilder = new GestureDescription.Builder();
    Path path = new Path();
    path.moveTo(x1, y1);
    path.lineTo(x2, y2);

    gestureBuilder.addStroke(new GestureDescription.StrokeDescription(path, 100, time));
    dispatchGesture(gestureBuilder.build(), new GestureResultCallback() {
        @Override
        public void onCompleted(GestureDescription gestureDescription) {
            System.out.println("SWIPE Gesture Completed :D");
            super.onCompleted(gestureDescription);
        }
    }, null);
}

}

que produce el siguiente resultado (pero todavía no puede dibujar "contraseña de patrón" como TeamViewer, por ejemplo). Pero como se dice en el comentario a continuación, creo que con un enfoque similar, esto se puede lograr usando gestos continuos probablemente. Cualquier sugerencia en esta dirección será bienvenida.

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí


Edición 2:

Definitivamente, la solución son los gestos continuos como se dijo en la edición anterior .

Y a continuación hay un supuesto código fijo que encontré aquí =>

Servicio de accesibilidad de Android:

// Simulates an L-shaped drag path: 200 pixels right, then 200 pixels down.
Path path = new Path();
path.moveTo(200,200);
path.lineTo(400,200);

final GestureDescription.StrokeDescription sd = new GestureDescription.StrokeDescription(path, 0, 500, true);

// The starting point of the second path must match
// the ending point of the first path.
Path path2 = new Path();
path2.moveTo(400,200);
path2.lineTo(400,400);

final GestureDescription.StrokeDescription sd2 = sd.continueStroke(path2, 0, 500, false); // 0.5 second

HongBaoService.mService.dispatchGesture(new GestureDescription.Builder().addStroke(sd).build(), new AccessibilityService.GestureResultCallback(){

@Override
public void onCompleted(GestureDescription gestureDescription){
super.onCompleted(gestureDescription);
HongBaoService.mService.dispatchGesture(new GestureDescription.Builder().addStroke(sd2).build(),null,null);
}

@Override
public void onCancelled(GestureDescription gestureDescription){
super.onCancelled(gestureDescription);
}
},null);

Entonces, mi duda es: ¿cómo enviar correctamente las coordenadas del mouse para el código anterior, de la forma en que puede realizar el arrastre en cualquier dirección? ¿Alguna idea?


Edición 3:

Encontré dos rutinas que se usan para realizar el arrastre, pero están usando UiAutomation + injectInputEvent(). AFAIK, la inyección de eventos funciona solo en una aplicación del sistema como se dice aquí y aquí y no la quiero.

Estas son las rutinas encontradas:

Luego, para lograr mi objetivo, creo que la segunda rutina es más apropiada para usar (siguiendo la lógica, excluyendo la inyección de eventos) con el código mostrado en Edit 2 y enviando todos los puntos de pictureBox1_MouseDowny pictureBox1_MouseMove(C # Windows Forms Application) respectivamente para llenar Point[]dinámicamente y al pictureBox1_MouseUpenviar cmd para ejecutar la rutina y usar esta matriz llena. Si tiene una idea para la primera rutina, avíseme: D.

Si después de leer esta edición tiene una posible solución, muéstreme una respuesta, por favor, mientras que intentaré probar esta idea.

BrowJr
fuente
1
TeamViewer no está utilizando el marco de accesibilidad, muy probablemente. Tienen acuerdos especiales con los fabricantes de dispositivos, por lo que su producto no está disponible para todos los dispositivos.
CommonsWare
@CommonsWare gracias. Pero creo que StrokeDescription.continueStroke()puede ser una solución probable. Consulte la sección Gestos continuos aquí .
BrowJr
2
En cuanto a su primer acercamiento. pictureBox1_MouseDownNo debe enviar las coordenadas. Solo debe almacenar las coordenadas iniciales, y luego pictureBox1_MouseUpenviarlas, porque eso marca el final del movimiento del mouse
Greggz

Respuestas:

1

Aquí hay un ejemplo de una solución basada en la edición 3 de la pregunta.


Aplicación C # de Windows Froms " formMain.cs ":

using System.Net.Sockets;

private List<Point> lstPoints;

private void pictureBox1_MouseDown(object sender, MouseEventArgs e) 
{
    if (e.Button == MouseButtons.Left)
    {
        lstPoints = new List<Point>();
        lstPoints.Add(new Point(e.X, e.Y));
    }
}

private void PictureBox1_MouseMove(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
    {
        lstPoints.Add(new Point(e.X, e.Y));
    }
}

private void PictureBox1_MouseUp(object sender, MouseEventArgs e)
{
    lstPoints.Add(new Point(e.X, e.Y));

    StringBuilder sb = new StringBuilder();

    foreach (Point obj in lstPoints)
    {
        sb.Append(Convert.ToString(obj) + ":");
    }

    serverSocket.Send("MDRAWEVENT" + sb.ToString() + Environment.NewLine);
}

servicio de Android " SocketBackground.java ":

import java.net.Socket;

String xline;

while (clientSocket.isConnected()) {

    BufferedReader xreader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream(), StandardCharsets.UTF_8));

    if (xreader.ready()) {

        while ((xline = xreader.readLine()) != null) {
                xline = xline.trim();

            if (xline != null && !xline.trim().isEmpty()) {

                if (xline.contains("MDRAWEVENT")) {

                    String coordinates = xline.replace("MDRAWEVENT", "");
                    String[] tokens = coordinates.split(Pattern.quote(":"));
                    Point[] moviments = new Point[tokens.length];

                    for (int i = 0; i < tokens.length; i++) {
                       String[] coordinates = tokens[i].replace("{", "").replace("}", "").split(",");

                       int x = Integer.parseInt(coordinates[0].split("=")[1]);
                       int y = Integer.parseInt(coordinates[1].split("=")[1]);

                       moviments[i] = new Point(x, y);
                    }

                    MyAccessibilityService.instance.mouseDraw(moviments, 2000);
                }
            }
        }
    }
}

android AccessibilityService" MyAccessibilityService.java ":

public void mouseDraw(Point[] segments, int time) {
    if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

        Path path = new Path();
        path.moveTo(segments[0].x, segments[0].y);

        for (int i = 1; i < segments.length; i++) {

            path.lineTo(segments[i].x, segments[i].y);

            GestureDescription.StrokeDescription sd = new GestureDescription.StrokeDescription(path, 0, time);

            dispatchGesture(new GestureDescription.Builder().addStroke(sd).build(), new AccessibilityService.GestureResultCallback() {

                @Override
                public void onCompleted(GestureDescription gestureDescription) {
                    super.onCompleted(gestureDescription);
                }

                @Override
                public void onCancelled(GestureDescription gestureDescription) {
                    super.onCancelled(gestureDescription);
                }
            }, null);
        }
    }
}
BrowJr
fuente
0

¿Has tratado de usar AutoIt scripting?

Puede guardar coordenadas dentro de ventanas / pantallas específicas. Puede mantener presionado el clic del mouse mientras dibuja el patrón.

¡También tengo algunos códigos / scripts de ejemplo para ti si los quieres!


EDITAR:

De acuerdo con este tutorial , puede usar Auto-IT en C #.

Sigue estos pasos:

  1. Instalar Auto-IT
  2. Agregue Auto-IT como referencia en el administrador de referencias (AutoItX3.dll)
  3. Luego importe la biblioteca que agregó con: Using AutoItX3Lib;
  4. Cree un nuevo objeto AutoItX3 llamado 'auto': AutoItX3 auto = new AutoItX3();
  5. Ahora puede ejecutar los comandos de Auto It.

Este es el ejemplo completo para ejecutar un clic del mouse:

Using AutoItX3Lib;
AutoItX3 auto = new AutoItX3();
auto.MouseClick("left", 78, 1133, 1, 35)


Con el AutoIt Window Info Toolpuede verificar las coordenadas que desea utilizar.

Tenga en cuenta que existen diferencias entre los modos de coordenadas del mouse:

por ejemplo: auto.AutoItSetOption("MouseCoordMode", 1)usará coordenadas de pantalla absolutas. Ver fuente aquí .


Para mantener presionado el mouse, puede verificar la función MouseDown

JaFizz
fuente
1
Esto no ayudó. Su sugerencia es exactamente lo que mi aplicación C # Windows Form ya hace.
BrowJr