|
|
@@ -4,7 +4,6 @@ import android.accessibilityservice.AccessibilityServiceInfo;
|
|
|
import android.content.pm.ApplicationInfo;
|
|
|
import android.content.pm.PackageManager;
|
|
|
import android.graphics.PixelFormat;
|
|
|
-import android.graphics.Rect;
|
|
|
import android.os.Build;
|
|
|
import android.util.Log;
|
|
|
import android.view.Gravity;
|
|
|
@@ -14,23 +13,17 @@ import android.view.accessibility.AccessibilityEvent;
|
|
|
import android.view.accessibility.AccessibilityNodeInfo;
|
|
|
import android.widget.FrameLayout;
|
|
|
|
|
|
+import androidx.annotation.NonNull;
|
|
|
import androidx.appcompat.view.ContextThemeWrapper;
|
|
|
|
|
|
-import com.android.volley.Request;
|
|
|
-import com.android.volley.RequestQueue;
|
|
|
-import com.android.volley.toolbox.JsonObjectRequest;
|
|
|
-import com.android.volley.toolbox.StringRequest;
|
|
|
-import com.android.volley.toolbox.Volley;
|
|
|
import com.example.modifier.databinding.FloatingWindowBinding;
|
|
|
|
|
|
import org.json.JSONArray;
|
|
|
import org.json.JSONException;
|
|
|
import org.json.JSONObject;
|
|
|
|
|
|
-import java.util.ArrayList;
|
|
|
-import java.util.HashMap;
|
|
|
-import java.util.List;
|
|
|
-import java.util.Map;
|
|
|
+import java.net.URISyntaxException;
|
|
|
+import java.util.Optional;
|
|
|
import java.util.concurrent.ScheduledExecutorService;
|
|
|
import java.util.concurrent.ScheduledFuture;
|
|
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
|
|
@@ -45,6 +38,8 @@ public class ModifierService extends android.accessibilityservice.AccessibilityS
|
|
|
|
|
|
public static final String NAME = BuildConfig.APPLICATION_ID + ".ModifierService";
|
|
|
|
|
|
+ private static ModifierService instance;
|
|
|
+
|
|
|
private ScheduledExecutorService mExecutor = new ScheduledThreadPoolExecutor(8);
|
|
|
|
|
|
private Socket mSocket;
|
|
|
@@ -52,11 +47,18 @@ public class ModifierService extends android.accessibilityservice.AccessibilityS
|
|
|
private boolean canSend = false;
|
|
|
private FloatingWindowBinding binding;
|
|
|
|
|
|
- public ModifierService() {
|
|
|
- Log.i(TAG, "Creating ModifierService");
|
|
|
+ public static ModifierService getInstance() {
|
|
|
+ return instance;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void connect(String server) {
|
|
|
+ if (mSocket != null) {
|
|
|
+ mSocket.disconnect();
|
|
|
+ }
|
|
|
try {
|
|
|
+ canSend = getSharedPreferences(BuildConfig.APPLICATION_ID, MODE_PRIVATE).getBoolean("canSend", false);
|
|
|
mSocketOpts.query = "model=" + Build.MANUFACTURER + " " + Build.MODEL + "&name=" + Build.DEVICE + "&id=" + Utils.getUniqueID() + "&canSend=" + canSend;
|
|
|
- mSocket = IO.socket("http://192.168.50.135:3000", mSocketOpts);
|
|
|
+ mSocket = IO.socket(server, mSocketOpts);
|
|
|
mSocket.on("message", this);
|
|
|
mSocket.on(Socket.EVENT_CONNECT, args -> {
|
|
|
Log.i(TAG, "Connected to server");
|
|
|
@@ -71,6 +73,8 @@ public class ModifierService extends android.accessibilityservice.AccessibilityS
|
|
|
e.printStackTrace();
|
|
|
}
|
|
|
});
|
|
|
+
|
|
|
+ mSocket.connect();
|
|
|
} catch (Exception e) {
|
|
|
e.printStackTrace();
|
|
|
}
|
|
|
@@ -80,13 +84,13 @@ public class ModifierService extends android.accessibilityservice.AccessibilityS
|
|
|
public void onCreate() {
|
|
|
super.onCreate();
|
|
|
Log.i(TAG, "Starting ModifierService");
|
|
|
- mSocketOpts.query = "model=" + Build.MANUFACTURER + " " + Build.MODEL + "&name=" + Build.DEVICE + "&id=" + Utils.getUniqueID() + "&canSend=" + canSend;
|
|
|
- canSend = getSharedPreferences(BuildConfig.APPLICATION_ID, MODE_PRIVATE).getBoolean("canSend", false);
|
|
|
- mSocket.connect();
|
|
|
+ String server = getSharedPreferences(BuildConfig.APPLICATION_ID, MODE_PRIVATE).getString("server", "http://192.168.6.215:3000");
|
|
|
+ connect(server);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public void onAccessibilityEvent(AccessibilityEvent event) {
|
|
|
+// traverseNode(getRootInActiveWindow(), new TraverseResult());
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@@ -112,35 +116,48 @@ public class ModifierService extends android.accessibilityservice.AccessibilityS
|
|
|
JSONArray data = json.optJSONArray("data");
|
|
|
String id = json.optString("id");
|
|
|
if (data != null) {
|
|
|
+ JSONArray success = new JSONArray();
|
|
|
+ JSONArray fail = new JSONArray();
|
|
|
for (int i = 0; i < data.length(); i++) {
|
|
|
JSONObject task = data.optJSONObject(i);
|
|
|
- if (task != null) {
|
|
|
- String to = task.optString("number");
|
|
|
- String body = task.optString("message");
|
|
|
- send(to, body);
|
|
|
-
|
|
|
+ String to = task.optString("number");
|
|
|
+ String body = task.optString("message");
|
|
|
+ int taskId = task.optInt("id");
|
|
|
+ try {
|
|
|
+ if (send(to, body)) {
|
|
|
+ success.put(taskId);
|
|
|
+ } else {
|
|
|
+ fail.put(taskId);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ fail.put(taskId);
|
|
|
}
|
|
|
}
|
|
|
JSONObject res = new JSONObject();
|
|
|
try {
|
|
|
+ res.put("id", id);
|
|
|
res.put("status", 0);
|
|
|
+ res.put("data", new JSONObject()
|
|
|
+ .put("success", success)
|
|
|
+ .put("fail", fail));
|
|
|
} catch (JSONException e) {
|
|
|
}
|
|
|
- mSocket.emit(id, res);
|
|
|
+ mSocket.emit("callback", res);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private void send(String to, String body) {
|
|
|
+ private boolean send(String to, String body) {
|
|
|
Log.i(TAG, "Sending SMS to " + to + ": " + body);
|
|
|
String cmd = "am start -a android.intent.action.SENDTO -d sms:" + to + " --es sms_body '" + body + "' --ez exit_on_sent true";
|
|
|
try {
|
|
|
Utils.runAsRoot(cmd);
|
|
|
|
|
|
Log.i(TAG, "Command executed successfully, waiting for app to open...");
|
|
|
- ScheduledFuture<?> f = mExecutor.schedule(() -> {
|
|
|
+ ScheduledFuture<Boolean> f = mExecutor.schedule(() -> {
|
|
|
Log.i(TAG, "Getting root node...");
|
|
|
AccessibilityNodeInfo root = getRootInActiveWindow();
|
|
|
String packageName = root.getPackageName().toString();
|
|
|
@@ -153,59 +170,66 @@ public class ModifierService extends android.accessibilityservice.AccessibilityS
|
|
|
e.printStackTrace();
|
|
|
}
|
|
|
Log.i(TAG, "App: " + appName + " (" + packageName + ")");
|
|
|
- traverseNode(root);
|
|
|
- Log.i(TAG, "!!!!!!!!!!!!!!!!");
|
|
|
+ TraverseResult result = new TraverseResult();
|
|
|
+ traverseNode(root, result);
|
|
|
+ if (result.isRcsCapable()) {
|
|
|
+ if (result.getSendBtn() == null) {
|
|
|
+ Log.i(TAG, "Send button not found");
|
|
|
+ } else {
|
|
|
+ Log.i(TAG, "Clicking send button");
|
|
|
+ result.getSendBtn().performAction(AccessibilityNodeInfo.ACTION_CLICK);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ Log.i(TAG, "RCS not detected");
|
|
|
+ }
|
|
|
+ return false;
|
|
|
}, 1, java.util.concurrent.TimeUnit.SECONDS);
|
|
|
synchronized (f) {
|
|
|
Log.i(TAG, "Waiting for task to complete...");
|
|
|
- f.get();
|
|
|
- Log.i(TAG, "Task completed");
|
|
|
+ return f.get();
|
|
|
}
|
|
|
} catch (Exception e) {
|
|
|
e.printStackTrace();
|
|
|
}
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
- private Map<String, Object> traverseNode(AccessibilityNodeInfo node) {
|
|
|
+ private void traverseNode(AccessibilityNodeInfo node, @NonNull TraverseResult result) {
|
|
|
if (node == null) {
|
|
|
- return null;
|
|
|
+ return;
|
|
|
}
|
|
|
- Map<String, Object> map = new HashMap<>();
|
|
|
- map.put("class", node.getClassName());
|
|
|
- map.put("text", node.getText());
|
|
|
- map.put("actions", node.getActionList().stream().map(AccessibilityNodeInfo.AccessibilityAction::getId).toArray());
|
|
|
- Rect rect = new Rect();
|
|
|
- node.getBoundsInScreen(rect);
|
|
|
- map.put("bounds", rect);
|
|
|
|
|
|
+ String className = node.getClassName().toString();
|
|
|
String name = node.getViewIdResourceName();
|
|
|
- Log.i(TAG, "Node: class=" + node.getClassName() + ", text=" + node.getText() + ", name=" + name);
|
|
|
+ String text = Optional.ofNullable(node.getText()).map(CharSequence::toString).orElse(null);
|
|
|
+ String id = node.getViewIdResourceName();
|
|
|
+
|
|
|
+// Log.i(TAG, "Node: class=" + className + ", text=" + text + ", name=" + name + ", id=" + id);
|
|
|
|
|
|
if ("Compose:Draft:Send".equals(name)) {
|
|
|
- Log.i(TAG, "Found send button Node: class=" + node.getClassName() + ", text=" + node.getText() + ", name=" + name);
|
|
|
- node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
|
|
|
+ result.setSendBtn(node);
|
|
|
}
|
|
|
|
|
|
- node.getViewIdResourceName();
|
|
|
+ if (text != null && (text.contains("RCS 聊天") || text.contains("RCS chat"))) {
|
|
|
+ result.setRcsCapable(true);
|
|
|
+ }
|
|
|
|
|
|
if (node.getChildCount() != 0) {
|
|
|
- List<Map<String, Object>> children = new ArrayList<>();
|
|
|
for (int i = 0; i < node.getChildCount(); i++) {
|
|
|
- children.add(traverseNode(node.getChild(i)));
|
|
|
- map.put("children", children);
|
|
|
+ traverseNode(node.getChild(i), result);
|
|
|
}
|
|
|
}
|
|
|
- return map;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
protected void onServiceConnected() {
|
|
|
super.onServiceConnected();
|
|
|
|
|
|
+ instance = this;
|
|
|
+
|
|
|
AccessibilityServiceInfo info = new AccessibilityServiceInfo();
|
|
|
- info.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED |
|
|
|
- AccessibilityEvent.TYPE_VIEW_FOCUSED |
|
|
|
- AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
|
|
|
+ info.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
|
|
|
info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;
|
|
|
info.notificationTimeout = 100;
|
|
|
this.setServiceInfo(info);
|
|
|
@@ -221,7 +245,7 @@ public class ModifierService extends android.accessibilityservice.AccessibilityS
|
|
|
layoutParams.x = 0;
|
|
|
layoutParams.y = 800;
|
|
|
layoutParams.gravity = Gravity.START | Gravity.TOP;
|
|
|
- ContextThemeWrapper newContext = new ContextThemeWrapper(getApplicationContext(), R.style.Theme_Modifier);
|
|
|
+ ContextThemeWrapper newContext = new ContextThemeWrapper(getApplicationContext(), R.style.FloatingWindow);
|
|
|
LayoutInflater inflater = LayoutInflater.from(newContext);
|
|
|
binding = FloatingWindowBinding.inflate(inflater, mLayout, true);
|
|
|
windowManager.addView(mLayout, layoutParams);
|
|
|
@@ -252,6 +276,4 @@ public class ModifierService extends android.accessibilityservice.AccessibilityS
|
|
|
mSocket.emit("message", data);
|
|
|
});
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
}
|