2929
3030import android .graphics .Rect ;
3131
32- import android .content .ContentResolver ;
33-
3432import android .provider .Settings ;
3533
3634import android .view .accessibility .AccessibilityManager ;
4038import android .accessibilityservice .AccessibilityService ;
4139import android .os .Bundle ;
4240
41+ import android .view .Display ;
42+ import android .accessibilityservice .AccessibilityService .TakeScreenshotCallback ;
43+ import android .accessibilityservice .AccessibilityService .ScreenshotResult ;
44+ import android .hardware .HardwareBuffer ;
45+ import android .graphics .ColorSpace ;
46+ import android .graphics .Bitmap ;
47+ import java .io .OutputStream ;
48+ import java .io .IOException ;
49+ import java .lang .reflect .Field ;
50+
4351public class AccessibilityAPI {
4452
4553 private static final String LOG_TAG = "AccessibilityAPI" ;
@@ -54,18 +62,17 @@ public static void onReceive(TermuxApiReceiver apiReceiver, final Context contex
5462 context .startActivity (accessibilityIntent );
5563 }
5664
57- ResultReturner .returnData (apiReceiver , intent , out -> {
58- final ContentResolver contentResolver = context .getContentResolver ();
59- if (intent .hasExtra ("dump" )) {
60- out .print (dump ());
61- } else if (intent .hasExtra ("click" )) {
62- click (intent .getIntExtra ("x" , 0 ), intent .getIntExtra ("y" , 0 ), intent .getIntExtra ("duration" , 1 ));
63- } else if (intent .hasExtra ("type" )) {
64- type (intent .getStringExtra ("type" ));
65- } else if (intent .hasExtra ("global-action" )) {
66- performGlobalAction (intent .getStringExtra ("global-action" ));
67- }
68- });
65+ if (intent .hasExtra ("dump" )) {
66+ dump (apiReceiver , intent );
67+ } else if (intent .hasExtra ("click" )) {
68+ click (intent .getIntExtra ("x" , 0 ), intent .getIntExtra ("y" , 0 ), intent .getIntExtra ("duration" , 1 ));
69+ } else if (intent .hasExtra ("type" )) {
70+ type (intent .getStringExtra ("type" ));
71+ } else if (intent .hasExtra ("global-action" )) {
72+ performGlobalAction (intent .getStringExtra ("global-action" ));
73+ } else if (intent .hasExtra ("screenshot" )) {
74+ screenshot (apiReceiver , context , intent );
75+ }
6976 }
7077
7178 // [The Stack Overflow answer 14923144](https://stackoverflow.com/a/14923144)
@@ -91,10 +98,30 @@ private static void click(int x, int y, int millisecondsDuration) {
9198 }
9299
93100 // The aim of this function is to give a compatible output with `adb` `uiautomator dump`.
94- private static String dump () throws TransformerException , ParserConfigurationException {
95- // Create a DocumentBuilder
101+ private static void dump (TermuxApiReceiver apiReceiver , Intent intent ) {
102+ AccessibilityNodeInfo node = TermuxAccessibilityService .instance .getRootInActiveWindow ();
103+ // On Signal *App permissions* for instance
104+ if (node == null ) {
105+ ResultReturner .returnData (apiReceiver , intent , out -> {});
106+ return ;
107+ }
108+
109+ String swString = dumpAuxiliary (node );
110+
111+ ResultReturner .returnData (apiReceiver , intent , out -> {
112+ out .write (swString );
113+ });
114+ }
115+
116+ private static String dumpAuxiliary (AccessibilityNodeInfo node ) {
117+ // Create a DocumentBuilder
96118 DocumentBuilderFactory factory = DocumentBuilderFactory .newInstance ();
97- DocumentBuilder builder = factory .newDocumentBuilder ();
119+ DocumentBuilder builder = null ;
120+ try {
121+ builder = factory .newDocumentBuilder ();
122+ } catch (ParserConfigurationException parserConfigurationException ) {
123+ Logger .logDebug (LOG_TAG , "ParserConfigurationException" );
124+ }
98125
99126 // Create a new Document
100127 Document document = builder .newDocument ();
@@ -103,17 +130,16 @@ private static String dump() throws TransformerException, ParserConfigurationExc
103130 Element root = document .createElement ("hierarchy" );
104131 document .appendChild (root );
105132
106- AccessibilityNodeInfo node = TermuxAccessibilityService .instance .getRootInActiveWindow ();
107- // On Signal *App permissions* for instance
108- if (node == null ) {
109- return "" ;
110- }
111-
112133 dumpNodeAuxiliary (document , root , node );
113134
114135 // Write as XML
115136 TransformerFactory transformerFactory = TransformerFactory .newInstance ();
116- Transformer transformer = transformerFactory .newTransformer ();
137+ Transformer transformer = null ;
138+ try {
139+ transformer = transformerFactory .newTransformer ();
140+ } catch (TransformerException transformerException ) {
141+ Logger .logDebug (LOG_TAG , "TransformerException transformerFactory.newTransformer" );
142+ }
117143 transformer .setOutputProperty (OutputKeys .INDENT , "yes" );
118144 transformer .setOutputProperty ("{http://xml.apache.org/xslt}indent-amount" , "2" );
119145 // Necessary to not have surrogate pairs for emojis, see [Benjamin_Loison/Voice_assistant/issues/83#issue-3661619](https://codeberg.org/Benjamin_Loison/Voice_assistant/issues/83#issue-3661619)
@@ -122,10 +148,13 @@ private static String dump() throws TransformerException, ParserConfigurationExc
122148
123149 StringWriter sw = new StringWriter ();
124150 StreamResult result = new StreamResult (sw );
125- transformer .transform (source , result );
126-
127- return sw .toString ();
128- }
151+ try {
152+ transformer .transform (source , result );
153+ } catch (TransformerException transformerException ) {
154+ Logger .logDebug (LOG_TAG , "TransformerException transformer.transform" );
155+ }
156+ return sw .toString ();
157+ }
129158
130159 private static void dumpNodeAuxiliary (Document document , Element element , AccessibilityNodeInfo node ) {
131160 for (int i = 0 ; i < node .getChildCount (); i ++) {
@@ -194,7 +223,52 @@ private static void type(String toType) {
194223 focusedNode .performAction (AccessibilityNodeInfo .ACTION_SET_TEXT , arguments );
195224 }
196225
197- private static void performGlobalAction (String globalAction ) throws NoSuchFieldException , IllegalAccessException {
198- TermuxAccessibilityService .instance .performGlobalAction ((int )AccessibilityService .class .getDeclaredField ("GLOBAL_ACTION_" + globalAction .toUpperCase ()).get (null ));
226+ private static void performGlobalAction (String globalActionString ) {
227+ String fieldName = "GLOBAL_ACTION_" + globalActionString .toUpperCase ();
228+ Field field = null ;
229+ try {
230+ field = AccessibilityService .class .getDeclaredField (fieldName );
231+ } catch (NoSuchFieldException noSuchFieldException ) {
232+ Logger .logDebug (LOG_TAG , "NoSuchFieldException" );
233+ }
234+ Object globalActionObject = null ;
235+ try {
236+ globalActionObject = field .get (null );
237+ } catch (IllegalAccessException illegalAccessException ) {
238+ Logger .logDebug (LOG_TAG , "IllegalAccessException" );
239+ }
240+ int globalActionInt = (int )globalActionObject ;
241+ TermuxAccessibilityService .instance .performGlobalAction (globalActionInt );
199242 }
243+
244+ private static void screenshot (TermuxApiReceiver apiReceiver , final Context context , Intent intent ) {
245+ TermuxAccessibilityService .instance .takeScreenshot (
246+ Display .DEFAULT_DISPLAY ,
247+ context .getMainExecutor (),
248+ new TakeScreenshotCallback () {
249+
250+ @ Override
251+ public void onSuccess (ScreenshotResult screenshotResult ) {
252+ Logger .logDebug (LOG_TAG , "onSuccess" );
253+ HardwareBuffer buffer = screenshotResult .getHardwareBuffer ();
254+ ColorSpace colorSpace = screenshotResult .getColorSpace ();
255+
256+ Bitmap bitmap = Bitmap .wrapHardwareBuffer (buffer , colorSpace );
257+
258+ ResultReturner .returnData (apiReceiver , intent , new ResultReturner .BinaryOutput ()
259+ {
260+ @ Override
261+ public void writeResult (OutputStream out ) throws IOException {
262+ bitmap .compress (Bitmap .CompressFormat .PNG , 100 , out );
263+ }
264+ });
265+ }
266+
267+ @ Override
268+ public void onFailure (int errorCode ) {
269+ Logger .logDebug (LOG_TAG , "onFailure: " + errorCode );
270+ }
271+ }
272+ );
273+ }
200274}
0 commit comments