Overview
Overview
Note
This document is the document of the API v2 based on Node.js added in Pro 9 (the API v1 is still available). If you want to view the documentation of the old API v1, please switch in the menu bar.
This document is being improved; to join the internal test, please join telegram group https://t.me/joinchat/ExZ-DlDaQmn7Mswy. It is normal for the beta version to have bugs, please give positive feedback and tolerance. It is forbidden to discuss sensitive and illegal topics in the closed beta group, otherwise it will be permanently blocked.
CloudControl Pro 9 is a brand new version of CloudControl Pro. In addition to new functions such as editor and packaging, the most important thing is to bring Node.js-based engine and brand-new API v2 (p. The API v1 is still available), accompanied by a huge npm ecosystem (close to 2 million npm packages), and still supports interaction with Android/Java (that is, Android/Java API can be used in Node.js).
The difference between the API v2 and the API v1
The advantages of Node.js (API v2) over Rhino (API v1) are:
- The JavaScript execution performance of the Node.js engine is more than 100 times that of Rhino
- The code using the Node.js engine encryption strength is high, currently cannot be restored
- Node.js supports language standards above ES2021, Rhino only supports ES5 and some ES6 features
- The Node.js engine itself has very few bugs, while the module system and language implementation of the Rhino engine have many bugs
- The API v2 corresponding to Node.js is better designed and more standard
- You can use third-party npm packages
- Node.js has a lot of network information
The disadvantages of Node.js (API v2) compared to Rhino (API v1) are:
- The API v2 corresponding to Node.js has a high threshold for getting started, and requires a certain understanding of Promise and asynchrony, especially for novices
- The documentation of the API v2 is difficult to read and is currently being improved
- Rhino and the API v1 community have many source codes, materials, and examples
- The API v1 is more convenient to use
How to choose an engine when you are new to CloudControl Pro
What if you are:
- Novices who have no programming foundation and do not want to learn programming in depth
- As long as the code can run, do not pursue maintainability and readability
- I only want to use the automatic operation, the function of the picture and color part
- Do not pursue the latest language standards, and can tolerate non-standard parts and bugs in the engine and API design itself
Then it is recommended that you use the Rhino engine and the API v1 to get started faster. You don't need special configuration, the code is executed with this engine by default.
What if you are:
- Computer major or have certain development experience
- It is the first time to learn programming, but I want to learn industry standards and norms, so as to lay the foundation for further study or learning Android/JavaScript/Web in the future
- Have certain code literacy and pursuit
- High software security and encryption requirements
- Want to use npm package to meet requirements such as connecting to mysql
- Pursue higher JS running performance
- Love programming, or love to explore, love to learn
Then it is recommended that you use the Node.js engine and the API v2, and you only need to understand the Rhino engine and the API v1.
Tips
The choice of engine is not absolute, you can use the Rhino engine while using the Node.js engine, or look at another engine/API after learning for a while.
Quick start
This section describes how to use the Node.js engine and the API v2.
Run code with Node.js engine
For backwards compatibility, code in Pro 9 still defaults to the old Rhino engine. To use the new Node.js engine, use either of the following:
- Add
"nodejs";
to the header of the file, for example:
"nodejs";
// Print the version of nodejs
console.log(`Node.js version: ${process.version}`);
- The file ends with
.node.js
or.mjs
. The ES Module function will be enabled at the end of.mjs
, see https://nodejs.org/api/esm.html
Using Node.js built-in modules
In Node.js, you can use dozens of modules that come with it, such as:
fs
: file system, used to read and write files (similar to the files module in Pro 8)http
,https
: http(s) request and service, used to send http(s) request or build http serverworker_threads
: Worker threads for executing tasks in parallel (similar to the threads module in Pro 8) *...
See Node.js 16.x documentation for all Node.js built-in modules and their documentation.
Here is an example of reading a text file using the built-in fs module:
"nodejs";
const fs = require("fs");
// Use readFile to read files, see https://nodejs.org/dist/latest-v16.x/docs/api/fs.html#fsreadfilepath-options-callback
fs.readFile('/sdcard/script/test.txt', {'encoding': 'utf-8'}, (err, data) => {
if (err) {
console.error("Failed to read file:", err);
} else {
console.log("read file successfully:", data);
}
});
Use Pro 9 built-in modules
As a supplement to the built-in modules of Node.js, Pro 9 migrated some modules of Pro 8 to the API of Pro 9, such as:
app
: Used to start other applications, obtain other application information, send broadcasts, emails, etc.ui
: used to display custom interface, web interfaceaccessibility
: Used to complete automation tasks using accessibility APIs *...
In Pro 9, all modules need to userequire()
to import before they can be used, unlike Pro 8 Just use global variables directly. For example, you cannot directly useapp
or$app
variable, but you need to useconst app = require('app')
to import modules.
A list of all modules can be viewed on the right side of this document or in the upper right menu.
The API of each module may be different from Pro 8. Most APIs are designed to be asynchronous rather than synchronous blocking, and some global functions and variables are designed as functions within the module. For example, requestScreenCapture
function is a synchronous function in Pro 8 version, which requests screenshot permission, and it will be blocked until the user operates, so it cannot be executed in the UI thread. It is an asynchronous function in Pro 9, returning a Promise, You need to use await
or then
to get the result; setClip()
, getClip()
are global functions in Pro 8, and belong to modules in Pro 9][clip_manager](https://g. pro.autojs.org/docs/v9/modules/clip_manager.html).
The following is an example of using the media_projection module to request screenshot permission and using the image module to find images:
// Use the destructuring syntax to import some functions and variables of the module
// Of course, you can also use const image = require("image") and then use image.readImage(), but it is relatively cumbersome
const {readImage, findImage} = require("image");
const {requestScreenCapture} = require('media_projection');
// Main function, marked with async to use await to wait for the result
async function main() {
// request screenshot permission
const capturer = await requestScreenCapture();
// read the image you are looking for
const template = await readImage("./template.png");
// Get the screenshot of the next frame
const capture = await capturer. nextImage();
// template matching template in screenshot
const result = await findImage(capture, template);
// print the result
console.log('findImage: ', result);
// stop screenshot
capturer. stop();
// Recycle image
template. recycle();
}
// Execute the main function
main();
Use npm to install third-party modules
There are a large number of third-party modules on npm, most of which can be used in Pro 9. Modules need to be installed with the npm command before using them.
-
The npm package requires a project to install. In the file management of Pro 9, click the menu in the lower right corner, select the project, and select the Node.js project in the template.
-
On the new project page, fill in the application name and package name (the package name must contain English ".", such as com.example), and click OK
-
In the project folder, click the project icon in the toolbar, click Terminal
-
Enter "npm i --no-bin-links module name" to install the npm package. After installation, you can use the module in the project code
Taking the uuid module for generating UUID as an example, the whole process is as follows:
You can refer to documentation of uuid module to use this module in main.js:
"nodejs";
const uuid = require("uuid");
console.log("uuid:", uuid.v4());
Follow-up needs to install other modules, also in the terminal, through the cd
command to enter the corresponding project directory.
To search for modules, please search in npm official website.
The reason to use the
--no-bin-links
option is because many npm modules will link some executable scripts to thenode_modules/.bin
directory during installation, but the Android internal storage partition (sdcard) file The system does not support symbolic links, so we need to use this option to disable it. But at the same time, we often use these executable files in npm scripts, such as running the webpack command after installing webpack, and running the react-scripts command after installing react. At this time, only js files with specific paths can be executed instead, such as Usenode node_modules/webpack/bin/webpack.js
instead. Another solution is to migrate the default script folder to the app's private directory. The file system here supports conforming links. You can modify the default script folder to "~" in the settings, but it should be noted that the private directory will be uninstalled when the APP is uninstalled. Therefore,
Install npm global modules
The built-in npm of Pro 9 can also install global modules, such as typescript compiling ts files, webpack-cli packaging js files, etc.
Execute npm i -g typescript
in the terminal to install the typescript module, and then execute the tsc
command in the terminal to compile the ts file.
Attention! Do not upgrade the built-in npm version, otherwise you may encounter unexpected problems; in addition, you cannot use
--no-bin-links
parameter, otherwise the corresponding command will not be found.
Call Java/Android API
Pro 9 provides a global object $autojs, providing some special APIs, such as calling Java APIs.
E.g:
"nodejs";
// Get the $java object for interacting with Java
const $java = $autojs.java;
// load Java/Android class
const StringBuilder = $java. findClass('java. lang. StringBuilder');
// Create an object of this class
const sb = new StringBuilder();
// call the method of this class
sb.append("Hello");
sb.append(2);
console.log(sb.toString());
In addition to findClass, $java provides APIs such as switching threads when calling Java methods, see $java object documentation.
In addition to this relatively primitive way, Pro 9 provides the rhino
module, which is used to provide a way of interacting with Java similar to the rhino engine in Pro 8:
"nodejs";
// After calling install, you can directly access Java classes by java.*, android.*, etc.
require('rhino').install();
const StringBuilder = java.lang.StringBuilder;
const sb = new StringBuilder();
sb.append(android.util.Base64.decode("YXV0b2pz", 0));
console.log(sb.toString());
Functions such as importClass/importPackage are not supported yet; JavaAdapter is not supported either.
For more information on Java/Android interaction, please wait for the subsequent separate chapters to expand.
Thread and Thread Model
Single-threaded and multi-threaded
Node.js usage follows a single-threaded model with an event loop, and it's the same in Pro 9, so you can't use the threads module to start new threads like in Pro 8.
In most cases, you do not need to use threads. Some time-consuming operations, such as findImage and click, are encapsulated as asynchronous operations and can be executed in parallel. When calling some Java APIs, if these APIs are asynchronous operations, you can also specify Java functions to execute threads, such as:
"nodejs";
require('rhino').install();
const path = require('path');
const BitmapFactory = android. graphics. BitmapFactory;
// test.png file in the current directory
const file = path.join(__dirname, "./test.png");
async function main() {
// Call BitmapFactory.decodeFile(file) to decode the image file into Bitmap
// This is a time-consuming operation, we specify to execute it on the io thread
const bitmap = await BitmapFactory.decodeFile.invoke(null, [file], 'io');
console.log(bitmap);
bitmap.recycle();
}
main();
If none of the above can meet your needs, and you need pure JavaScript computing logic to run in a separate thread, then you need to use the worker_threads
module of Node.js, see [Node.js documentation](https://nodejs.org/dist /latest-v16.x/docs/api/worker_threads.html) and related information on the Internet. Unlike the threads sub-thread in Pro 8, worker_thread can share all public and global variables with the main thread, and requires additional communication, so it will not be expanded here.
At present, sub-threads in worker_threads cannot access autojs-related APIs and modules, such as $autojs, and can only access Node.js built-in modules and objects .
UI thread
By default, the Node.js engine runs on a non-UI thread, but it cannot operate interface-related content; therefore Pro 9 provides the option of the UI thread, by using the string "ui-thread" or "ui" in the file header to ID, for example:
"nodejs ui";
const {isUiThread} = require("ui");
console.log(isUiThread());
There is a difference between "ui" and "ui-thread":
ui
: Used to display the interface (Activity), such as displaying a Web page for user operations after startup, see the documentation of the UI module.ui-thread
: Do not display a new page at startup, but the code runs on the UI thread, which is generally used to display and control the floating window in the code without interface, see the documentation of the floating window module.
In addition, if you occasionally need to operate UI elements in a non-UI thread, such as displaying and controlling dialog boxes, you can use the aforementioned method of switching threads when calling Java APIs. For example view.setText.invoke(view, ["hello"], "ui")
.
Guidelines for reading module documentation
The documentation of the module is generated by code, and reading the documentation requires some skills, otherwise the documentation may be difficult to understand.
Taking the app module as an example, after opening the app module documentation, you will see a list:
Interface
: Interface, the first time you read the document, skip this part directly.Variables
: Variables of this module. We seepackageName
in this list, which means that the app module has a variable calledpackageName
. It can be used in the following ways:
"nodejs";
const app = require("app");
console.log(app.packageName);
Click on the document of the variable, and you can see that there is a constant
mark in front of it, indicating that this is a constant and its value cannot be modified, that is, app.packageName = "xxx"
will report an error; the type is string, a string. Combined with the variable name, we can know that this is the package name of the current app. Of course, there will be Chinese comments for variables later, but they haven’t been written yet, so we have to guess through the variable names.
Function
: The function of this module. We see many functions in this list, such aseditFile
,startActivity
. These are the functions of the app module, takestartActivity
as an example, click on its document to see:
startActivity(target: string | IntentOptionsWithRoot): Promise<void>
It has a parameter target, similar to string or IntentOptionsWithRoot. We all know that string is a string, which is similar to the usage of app.startActivity("console")
in Pro 8. What about IntentOptionsWithRoot?
Click IntentOptionsWithRoot to see the documentation for IntentOptionsWithRoot. First look at the Properties column, which describes the properties of the interface. There is a boolean attribute root
, preceded by an Optional tag, indicating that it is an optional attribute; then look at the previous inheritance relationship, indicating that IntentOptionsWithRoot inherits from IntentOptions, jump to IntentOptions, you can see that it has many attributes, such as action of type string. In fact, you don’t need to jump to the past, check Inherited in the upper right corner, you can see all inherited attributes.
Taken together, we can know that IntentOptionsWithRoot requires us to pass in an object, which can have optional attributes such as root and action, so we can write like this:
"nodejs";
const app = require("app");
app.startActivity({
"root": false,
"action": "android.intent.action.VIEW",
"data": "http://smartcloudscript.com",
});