add: module floaty; events.on('exit')

This commit is contained in:
hyb1996 2017-12-06 13:18:36 +08:00
parent 346487b0ef
commit e33ce733d5
12 changed files with 448 additions and 25 deletions

View File

@ -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];

View 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;
}

View File

@ -83,6 +83,8 @@ module.exports = function(__runtime__, scope){
return view;
}
ui.__decorate__ = decorate;
var proxy = __runtime__.ui;
proxy.__proxy__ = {
set: function(name, value){

View File

@ -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;

View File

@ -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);
}
}
}

View File

@ -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();
}
}

View File

@ -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();
}

View 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());
}
}
}
}

View File

@ -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();
}
}

View File

@ -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();

View 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>

View File

@ -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>