1 /*
2  * Copyright (C) 2020 The Android Open Source Project
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 com.android.server.accessibility.utils;
18 
19 import android.view.MotionEvent;
20 
21 import java.util.regex.Matcher;
22 import java.util.regex.Pattern;
23 
24 /**
25  * This class helps parse a gesture event log into its individual MotionEvents
26  */
27 public class GestureLogParser {
28     /** Gets a MotionEvent from a log line */
getMotionEventFromLogLine(String line)29     public static MotionEvent getMotionEventFromLogLine(String line) {
30         final int downTime;
31         final int eventTime;
32         int action;
33         final int pointerCount;
34 
35         final MotionEvent.PointerProperties[] properties;
36         final MotionEvent.PointerCoords[] pointerCoords;
37         final int metaState;
38         final int buttonState = 0;
39         final int xPrecision = 1;
40         final int yPrecision = 1;
41         final int deviceId;
42         final int edgeFlags;
43         final int source;
44         final int flags;
45         final int actionIndex;
46 
47         downTime = findInt(line, "downTime=(\\d+)");
48         eventTime = findInt(line, "eventTime=(\\d+)");
49         action = stringToAction(findString(line, "action=(\\w+)"));
50 
51         // For pointer indices
52         Pattern p = Pattern.compile("action=(\\w+)\\((\\d)");
53         Matcher matcher = p.matcher(line);
54         if (matcher.find()) {
55             actionIndex = Integer.decode(matcher.group(2));
56             action = action | (actionIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
57         }
58 
59         pointerCount = findInt(line, "pointerCount=(\\d+)");
60         metaState = findInt(line, "metaState=(\\d+)");
61         deviceId = findInt(line, "deviceId=(\\d+)");
62         edgeFlags = Integer.decode(findString(line, "edgeFlags=(\\w+)"));
63         source = Integer.decode(findString(line, "source=(\\w+)"));
64         flags = Integer.decode(findString(line, "flags=(\\w+)"));
65         properties = findProperties(line, pointerCount);
66         pointerCoords = findCoordinates(line, pointerCount);
67 
68         return MotionEvent.obtain(downTime, eventTime, action,
69                 pointerCount, properties, pointerCoords, metaState, buttonState,
70                 xPrecision, yPrecision, deviceId, edgeFlags, source, flags);
71     }
72 
findInt(String eventText, String pattern)73     private static int findInt(String eventText, String pattern) {
74         final Pattern p = Pattern.compile(pattern);
75         final Matcher matcher = p.matcher(eventText);
76         matcher.find();
77         return Integer.decode(matcher.group(1));
78     }
79 
findFloat(String eventText, String pattern)80     private static float findFloat(String eventText, String pattern) {
81         final Pattern p = Pattern.compile(pattern);
82         final Matcher matcher = p.matcher(eventText);
83         matcher.find();
84         return Float.parseFloat(matcher.group(1));
85     }
86 
findString(String eventText, String pattern)87     private static String findString(String eventText, String pattern) {
88         final Pattern p = Pattern.compile(pattern);
89         final Matcher matcher = p.matcher(eventText);
90         matcher.find();
91         return matcher.group(1);
92     }
93 
findCoordinates(String eventText, int pointerCount)94     private static MotionEvent.PointerCoords[] findCoordinates(String eventText, int pointerCount) {
95         if (pointerCount == 0) {
96             return null;
97         }
98 
99         final MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[pointerCount];
100         float x;
101         float y;
102         for (int i = 0; i < pointerCount; i++) {
103 
104             x = findFloat(eventText, "x\\[" + i + "\\]=([\\d.]+)");
105             y = findFloat(eventText, "y\\[" + i + "\\]=([\\d.]+)");
106 
107             MotionEvent.PointerCoords pointerCoords = new MotionEvent.PointerCoords();
108             pointerCoords.x = x;
109             pointerCoords.y = y;
110             pointerCoords.pressure = 1;
111             pointerCoords.size = 1;
112 
113             coords[i] = pointerCoords;
114         }
115         return coords;
116     }
117 
findProperties( String eventText, int pointerCount)118     private static MotionEvent.PointerProperties[] findProperties(
119             String eventText, int pointerCount) {
120         if (pointerCount == 0) {
121             return null;
122         }
123 
124         final MotionEvent.PointerProperties[] props =
125                 new MotionEvent.PointerProperties[pointerCount];
126         int id;
127         for (int i = 0; i < pointerCount; i++) {
128             id = findInt(eventText, "id\\[" + i + "\\]=([\\d])");
129             MotionEvent.PointerProperties pointerProps = new MotionEvent.PointerProperties();
130             pointerProps.id = id;
131             pointerProps.toolType = MotionEvent.TOOL_TYPE_FINGER;
132             props[i] = pointerProps;
133         }
134         return props;
135     }
136 
stringToAction(String action)137     private static int stringToAction(String action) {
138         switch (action) {
139             case "ACTION_DOWN":
140                 return MotionEvent.ACTION_DOWN;
141             case "ACTION_UP":
142                 return MotionEvent.ACTION_UP;
143             case "ACTION_CANCEL":
144                 return MotionEvent.ACTION_CANCEL;
145             case "ACTION_OUTSIDE":
146                 return MotionEvent.ACTION_OUTSIDE;
147             case "ACTION_MOVE":
148                 return MotionEvent.ACTION_MOVE;
149             case "ACTION_HOVER_MOVE":
150                 return MotionEvent.ACTION_HOVER_MOVE;
151             case "ACTION_SCROLL":
152                 return MotionEvent.ACTION_SCROLL;
153             case "ACTION_HOVER_ENTER":
154                 return MotionEvent.ACTION_HOVER_ENTER;
155             case "ACTION_HOVER_EXIT":
156                 return MotionEvent.ACTION_HOVER_EXIT;
157             case "ACTION_BUTTON_PRESS":
158                 return MotionEvent.ACTION_BUTTON_PRESS;
159             case "ACTION_BUTTON_RELEASE":
160                 return MotionEvent.ACTION_BUTTON_RELEASE;
161             case "ACTION_POINTER_DOWN":
162                 return MotionEvent.ACTION_POINTER_DOWN;
163             case "ACTION_POINTER_UP":
164                 return MotionEvent.ACTION_POINTER_UP;
165             default:
166                 return -1;
167         }
168     }
169 }
170