mirror of
https://github.com/TonyJiangWJ/Auto.js.git
synced 2026-06-21 21:01:43 +08:00
add: module floaty; events.on('exit')
This commit is contained in:
parent
346487b0ef
commit
e33ce733d5
@ -65,7 +65,7 @@ require("__general__")(__runtime__, this);
|
||||
|
||||
(function(scope){
|
||||
var modules = ['app', 'automator', 'console', 'dialogs', 'io', 'selector', 'shell', 'web', 'ui',
|
||||
"images", "timers", "events", "engines", "RootAutomator", "http", "storages"];
|
||||
"images", "timers", "events", "engines", "RootAutomator", "http", "storages", "floaty"];
|
||||
var len = modules.length;
|
||||
for(var i = 0; i < len; i++) {
|
||||
var m = modules[i];
|
||||
|
||||
37
autojs/src/main/assets/modules/__floaty__.js
Normal file
37
autojs/src/main/assets/modules/__floaty__.js
Normal file
@ -0,0 +1,37 @@
|
||||
|
||||
module.exports = function(__runtime__, scope){
|
||||
var floaty = {};
|
||||
|
||||
floaty.window = function(layout){
|
||||
if(typeof(layout) == 'xml'){
|
||||
layout = layout.toString();
|
||||
}
|
||||
return wrap(__runtime__.floaty.window(layout));
|
||||
}
|
||||
|
||||
function wrap(window){
|
||||
var proxyObject = new com.stardust.autojs.rhino.ProxyJavaObject(scope, window, window.getClass());
|
||||
proxyObject.__proxy__ = {
|
||||
set: function(name, value){
|
||||
window[name] = value;
|
||||
},
|
||||
get: function(name) {
|
||||
var value = window[name];
|
||||
if(typeof(value) == 'undefined'){
|
||||
value = window.getView(name);
|
||||
if(!value){
|
||||
value = undefined;
|
||||
}else{
|
||||
value = scope.ui.__decorate__(value);
|
||||
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
};
|
||||
return proxyObject;
|
||||
}
|
||||
|
||||
return floaty;
|
||||
}
|
||||
|
||||
@ -83,6 +83,8 @@ module.exports = function(__runtime__, scope){
|
||||
return view;
|
||||
}
|
||||
|
||||
ui.__decorate__ = decorate;
|
||||
|
||||
var proxy = __runtime__.ui;
|
||||
proxy.__proxy__ = {
|
||||
set: function(name, value){
|
||||
|
||||
@ -148,16 +148,13 @@ public class StardustConsole extends AbstractConsole {
|
||||
return;
|
||||
}
|
||||
startFloatyService();
|
||||
mUiHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
FloatyService.addWindow(mFloatyWindow);
|
||||
// SecurityException: https://github.com/hyb1996-guest/AutoJsIssueReport/issues/4781
|
||||
} catch (WindowManager.BadTokenException | SecurityException e) {
|
||||
e.printStackTrace();
|
||||
mUiHandler.toast(R.string.text_no_floating_window_permission);
|
||||
}
|
||||
mUiHandler.post(() -> {
|
||||
try {
|
||||
FloatyService.addWindow(mFloatyWindow);
|
||||
// SecurityException: https://github.com/hyb1996-guest/AutoJsIssueReport/issues/4781
|
||||
} catch (WindowManager.BadTokenException | SecurityException e) {
|
||||
e.printStackTrace();
|
||||
mUiHandler.toast(R.string.text_no_floating_window_permission);
|
||||
}
|
||||
});
|
||||
mShown = true;
|
||||
|
||||
@ -1,5 +1,13 @@
|
||||
package com.stardust.autojs.core.floaty;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.stardust.autojs.R;
|
||||
import com.stardust.enhancedfloaty.FloatyService;
|
||||
import com.stardust.enhancedfloaty.ResizableFloaty;
|
||||
import com.stardust.enhancedfloaty.ResizableFloatyWindow;
|
||||
|
||||
@ -7,7 +15,86 @@ import com.stardust.enhancedfloaty.ResizableFloatyWindow;
|
||||
* Created by Stardust on 2017/12/5.
|
||||
*/
|
||||
|
||||
public class FloatyWindow {
|
||||
public class FloatyWindow extends ResizableFloatyWindow {
|
||||
|
||||
private static final Object LOCK = new Object();
|
||||
private View mView;
|
||||
private boolean mCreated = false;
|
||||
private View mMoveCursor;
|
||||
private View mResizer;
|
||||
|
||||
|
||||
public FloatyWindow(View view) {
|
||||
super(new MyFloaty(view));
|
||||
mView = view;
|
||||
}
|
||||
|
||||
public void waitFor() {
|
||||
synchronized (LOCK) {
|
||||
if (mCreated) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
LOCK.wait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(FloatyService service, WindowManager manager) {
|
||||
super.onCreate(service, manager);
|
||||
synchronized (LOCK) {
|
||||
mCreated = true;
|
||||
LOCK.notify();
|
||||
}
|
||||
View root = (View) mView.getParent().getParent();
|
||||
mMoveCursor = root.findViewById(R.id.move_cursor);
|
||||
mResizer = root.findViewById(R.id.resizer);
|
||||
}
|
||||
|
||||
public void setAdjustEnabled(boolean enabled) {
|
||||
if (!enabled) {
|
||||
mMoveCursor.setVisibility(View.GONE);
|
||||
mResizer.setVisibility(View.GONE);
|
||||
} else {
|
||||
mMoveCursor.setVisibility(View.VISIBLE);
|
||||
mResizer.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAdjustEnabled() {
|
||||
return mMoveCursor.getVisibility() == View.VISIBLE;
|
||||
}
|
||||
|
||||
private static class MyFloaty implements ResizableFloaty {
|
||||
|
||||
|
||||
private View mContentView;
|
||||
|
||||
|
||||
public MyFloaty(View view) {
|
||||
mContentView = view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View inflateView(FloatyService floatyService, ResizableFloatyWindow resizableFloatyWindow) {
|
||||
View view = View.inflate(mContentView.getContext(), R.layout.floaty_window, null);
|
||||
((FrameLayout) view.findViewById(R.id.container)).addView(mContentView);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View getResizerView(View view) {
|
||||
return view.findViewById(R.id.resizer);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View getMoveCursorView(View view) {
|
||||
return view.findViewById(R.id.move_cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,70 @@
|
||||
package com.stardust.autojs.rhino;
|
||||
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.NativeFunction;
|
||||
import org.mozilla.javascript.NativeJavaObject;
|
||||
import org.mozilla.javascript.NativeObject;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
import org.mozilla.javascript.UniqueTag;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/12/6.
|
||||
*/
|
||||
|
||||
public class ProxyJavaObject extends NativeJavaObject {
|
||||
|
||||
private NativeFunction mGetter;
|
||||
private NativeFunction mSetter;
|
||||
|
||||
public ProxyJavaObject() {
|
||||
}
|
||||
|
||||
public ProxyJavaObject(Scriptable scope, Object javaObject, Class<?> staticType) {
|
||||
super(scope, javaObject, staticType);
|
||||
}
|
||||
|
||||
public ProxyJavaObject(Scriptable scope, Object javaObject, Class<?> staticType, boolean isAdapter) {
|
||||
super(scope, javaObject, staticType, isAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String name, Scriptable start, Object value) {
|
||||
if (name.equals("__proxy__")) {
|
||||
NativeObject proxy = (NativeObject) value;
|
||||
Object getter = proxy.get("get", start);
|
||||
if (getter instanceof NativeFunction) {
|
||||
mGetter = (NativeFunction) getter;
|
||||
}
|
||||
Object setter = proxy.get("set", start);
|
||||
if (setter instanceof NativeFunction) {
|
||||
mSetter = (NativeFunction) setter;
|
||||
}
|
||||
} else if (mSetter != null) {
|
||||
mSetter.call(Context.getCurrentContext(), start, start, new Object[]{name, value});
|
||||
} else {
|
||||
super.put(name, start, value);
|
||||
}
|
||||
}
|
||||
|
||||
public Object getWithoutProxy(String name, Scriptable start) {
|
||||
return super.get(name, start);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(String name, Scriptable start) {
|
||||
Object value = super.get(name, start);
|
||||
if (value != null && value != UniqueTag.NOT_FOUND) {
|
||||
return value;
|
||||
}
|
||||
if (mGetter != null) {
|
||||
value = mGetter.call(Context.getCurrentContext(), start, start, new Object[]{name});
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDefaultValue(Class<?> typeHint) {
|
||||
return toString();
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ import com.stardust.autojs.runtime.api.Console;
|
||||
import com.stardust.autojs.runtime.api.Device;
|
||||
import com.stardust.autojs.runtime.api.Engines;
|
||||
import com.stardust.autojs.runtime.api.Events;
|
||||
import com.stardust.autojs.runtime.api.Floaty;
|
||||
import com.stardust.autojs.runtime.api.Loopers;
|
||||
import com.stardust.autojs.runtime.api.Threads;
|
||||
import com.stardust.autojs.runtime.api.Timers;
|
||||
@ -154,6 +155,9 @@ public class ScriptRuntime {
|
||||
@ScriptVariable
|
||||
public final Threads threads;
|
||||
|
||||
@ScriptVariable
|
||||
public final Floaty floaty;
|
||||
|
||||
private Images images;
|
||||
|
||||
private static WeakReference<Context> applicationContext;
|
||||
@ -182,6 +186,7 @@ public class ScriptRuntime {
|
||||
dialogs = new Dialogs(app, mUiHandler, bridges);
|
||||
threads = new Threads(this);
|
||||
device = new Device(mUiHandler.getContext());
|
||||
floaty = new Floaty(mUiHandler, ui);
|
||||
}
|
||||
|
||||
public void init() {
|
||||
@ -313,23 +318,31 @@ public class ScriptRuntime {
|
||||
}
|
||||
|
||||
public void onExit() {
|
||||
//悬浮窗需要第一时间关闭以免出现恶意脚本全屏悬浮窗屏蔽屏幕并且在exit中写死循环的问题
|
||||
ignoresException(floaty::closeAll);
|
||||
try {
|
||||
threads.shutDownAll();
|
||||
events.emit("exit");
|
||||
} catch (Exception ignored) {
|
||||
console.error("exception on exit: " + ignored);
|
||||
}
|
||||
ignoresException(threads::shutDownAll);
|
||||
ignoresException(events::recycle);
|
||||
ignoresException(loopers::quitAll);
|
||||
ignoresException(() -> {
|
||||
if (mRootShell != null) mRootShell.exitAndWaitFor();
|
||||
mRootShell = null;
|
||||
mShellSupplier = null;
|
||||
});
|
||||
ignoresException(() -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
images.releaseScreenCapturer();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (events != null) {
|
||||
events.recycle();
|
||||
}
|
||||
if (loopers != null) {
|
||||
loopers.quitAll();
|
||||
}
|
||||
if (mRootShell != null) {
|
||||
mRootShell.exitAndWaitFor();
|
||||
}
|
||||
mRootShell = null;
|
||||
mShellSupplier = null;
|
||||
private void ignoresException(Runnable r) {
|
||||
try {
|
||||
r.run();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
159
autojs/src/main/java/com/stardust/autojs/runtime/api/Floaty.java
Normal file
159
autojs/src/main/java/com/stardust/autojs/runtime/api/Floaty.java
Normal file
@ -0,0 +1,159 @@
|
||||
package com.stardust.autojs.runtime.api;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Looper;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.View;
|
||||
|
||||
import com.stardust.autojs.R;
|
||||
import com.stardust.autojs.core.floaty.FloatyWindow;
|
||||
import com.stardust.autojs.core.ui.JsLayoutInflater;
|
||||
import com.stardust.autojs.core.ui.JsViewHelper;
|
||||
import com.stardust.autojs.rhino.ProxyObject;
|
||||
import com.stardust.enhancedfloaty.FloatyService;
|
||||
import com.stardust.util.UiHandler;
|
||||
|
||||
import org.mozilla.javascript.NativeJavaObject;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
import org.mozilla.javascript.ScriptableObject;
|
||||
import org.mozilla.javascript.UniqueTag;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/12/5.
|
||||
*/
|
||||
|
||||
public class Floaty {
|
||||
|
||||
private JsLayoutInflater mJsLayoutInflater;
|
||||
private Context mContext;
|
||||
private UiHandler mUiHandler;
|
||||
private Set<JsFloatyWindow> mWindows = new HashSet<>();
|
||||
|
||||
public Floaty(UiHandler uiHandler, UI ui) {
|
||||
mUiHandler = uiHandler;
|
||||
mContext = new ContextThemeWrapper(mUiHandler.getContext(), R.style.AppTheme);
|
||||
mJsLayoutInflater = ui.getJsLayoutInflater();
|
||||
}
|
||||
|
||||
public JsFloatyWindow window(String xml) {
|
||||
return window(inflate(xml));
|
||||
}
|
||||
|
||||
public JsFloatyWindow window(View view) {
|
||||
JsFloatyWindow window = new JsFloatyWindow(view);
|
||||
mWindows.add(window);
|
||||
return window;
|
||||
}
|
||||
|
||||
private View inflate(String xml) {
|
||||
return mJsLayoutInflater.inflate(mContext, xml);
|
||||
}
|
||||
|
||||
public void closeAll() {
|
||||
Iterator<JsFloatyWindow> iterator = mWindows.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
JsFloatyWindow window = iterator.next();
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
window.mWindow.close();
|
||||
} else {
|
||||
mUiHandler.post(() -> window.mWindow.close());
|
||||
}
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
public class JsFloatyWindow {
|
||||
|
||||
private Map<String, View> mViewCache = new HashMap<>();
|
||||
private View mView;
|
||||
private FloatyWindow mWindow;
|
||||
|
||||
public JsFloatyWindow(View view) {
|
||||
mWindow = new FloatyWindow(view);
|
||||
mUiHandler.post(() -> {
|
||||
mUiHandler.getContext().startService(new Intent(mUiHandler.getContext(), FloatyService.class));
|
||||
FloatyService.addWindow(mWindow);
|
||||
});
|
||||
mWindow.waitFor();
|
||||
setSize(mWindow.getWindowBridge().getScreenWidth() / 2, mWindow.getWindowBridge().getScreenHeight() / 2);
|
||||
mView = view;
|
||||
}
|
||||
|
||||
public View getView(String id) {
|
||||
View v = mViewCache.get(id);
|
||||
if (v != null) {
|
||||
return v;
|
||||
}
|
||||
v = JsViewHelper.findViewByStringId(mView, id);
|
||||
if (v != null) {
|
||||
mViewCache.put(id, v);
|
||||
return v;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public int getX() {
|
||||
return mWindow.getWindowBridge().getX();
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return mWindow.getWindowBridge().getY();
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return mWindow.getWindowBridge().getWidth();
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return mWindow.getWindowBridge().getHeight();
|
||||
}
|
||||
|
||||
public void setSize(int w, int h) {
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
mWindow.getWindowBridge().updateMeasure(w, h);
|
||||
} else {
|
||||
mUiHandler.post(() -> mWindow.getWindowBridge().updateMeasure(w, h));
|
||||
}
|
||||
}
|
||||
|
||||
public void setPosition(int x, int y) {
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
mWindow.getWindowBridge().updatePosition(x, y);
|
||||
} else {
|
||||
mUiHandler.post(() -> mWindow.getWindowBridge().updatePosition(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
public void setAdjustEnabled(boolean enabled) {
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
mWindow.setAdjustEnabled(enabled);
|
||||
} else {
|
||||
mUiHandler.post(() -> mWindow.setAdjustEnabled(enabled));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAdjustEnabled() {
|
||||
return mWindow.isAdjustEnabled();
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (!mWindows.remove(this)) {
|
||||
return;
|
||||
}
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
mWindow.close();
|
||||
} else {
|
||||
mUiHandler.post(() -> mWindow.close());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -38,7 +38,6 @@ public class Loopers {
|
||||
Looper l = Looper.myLooper();
|
||||
if (l != null && shouldQuitLooper()) {
|
||||
if (mLooperQuitHandler != null && mLooperQuitHandler.shouldQuit()) {
|
||||
mScriptRuntime.events.emit("exit");
|
||||
l.quit();
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,10 +21,12 @@ public class UI extends ProxyObject {
|
||||
|
||||
private Context mContext;
|
||||
private Map<String, Object> mProperties = new ConcurrentHashMap<>();
|
||||
private JsLayoutInflater mJsLayoutInflater;
|
||||
|
||||
public UI(Context context, JsLayoutInflater layoutInflater) {
|
||||
mContext = context;
|
||||
mProperties.put("layoutInflater", layoutInflater);
|
||||
mJsLayoutInflater = layoutInflater;
|
||||
}
|
||||
|
||||
public UI(Context context) {
|
||||
@ -32,6 +34,10 @@ public class UI extends ProxyObject {
|
||||
}
|
||||
|
||||
|
||||
public JsLayoutInflater getJsLayoutInflater() {
|
||||
return mJsLayoutInflater;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClassName() {
|
||||
return UI.class.getSimpleName();
|
||||
|
||||
42
autojs/src/main/res/layout/floaty_window.xml
Normal file
42
autojs/src/main/res/layout/floaty_window.xml
Normal file
@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="12dp">
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/resizer"
|
||||
android:layout_width="25dp"
|
||||
android:layout_height="25dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:background="@drawable/circle_cool_black"
|
||||
android:padding="6dp"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/ic_resizer"
|
||||
android:tint="@android:color/white"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/move_cursor"
|
||||
android:layout_width="25dp"
|
||||
android:layout_height="25dp"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:background="@drawable/circle_cool_black"
|
||||
android:padding="5dp"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/ic_move_cursor"
|
||||
android:tint="@android:color/white"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</RelativeLayout>
|
||||
@ -5,4 +5,15 @@
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorButtonNormal">#cc181818</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
|
||||
</style>
|
||||
|
||||
<color name="colorPrimary">#009688</color>
|
||||
<color name="colorPrimaryDark">#009688</color>
|
||||
<color name="colorAccent">#03a9f4</color>
|
||||
</resources>
|
||||
Loading…
Reference in New Issue
Block a user