From 44b7f0b39a4ef93b214de2d7272bb0d1ecad0fb0 Mon Sep 17 00:00:00 2001 From: hyb1996 <946994919@qq.com> Date: Mon, 28 May 2018 17:02:48 +0800 Subject: [PATCH] api(floaty): floaty.rawWindow() --- autojs/src/main/assets/modules/__floaty__.js | 12 +- ...ow.java => BaseResizableFloatyWindow.java} | 41 ++--- .../autojs/core/floaty/RawWindow.java | 123 +++++++++++++ .../ui/inflater/inflaters/Exceptions.java | 3 + .../stardust/autojs/runtime/api/Floaty.java | 168 ++++++++++++++++-- autojs/src/main/res/layout/raw_window.xml | 6 + 6 files changed, 310 insertions(+), 43 deletions(-) rename autojs/src/main/java/com/stardust/autojs/core/floaty/{FloatyWindow.java => BaseResizableFloatyWindow.java} (86%) create mode 100644 autojs/src/main/java/com/stardust/autojs/core/floaty/RawWindow.java create mode 100644 autojs/src/main/res/layout/raw_window.xml diff --git a/autojs/src/main/assets/modules/__floaty__.js b/autojs/src/main/assets/modules/__floaty__.js index 2fc1ddb3..b74881b3 100644 --- a/autojs/src/main/assets/modules/__floaty__.js +++ b/autojs/src/main/assets/modules/__floaty__.js @@ -9,10 +9,16 @@ module.exports = function(runtime, global){ return wrap(runtime.floaty.window(layout)); } - floaty.__view_cache__ = {}; + floaty.rawWindow = function(layout){ + if(typeof(layout) == 'xml'){ + layout = layout.toString(); + } + return wrap(runtime.floaty.rawWindow(layout)); + } function wrap(window){ var proxyObject = new com.stardust.autojs.rhino.ProxyJavaObject(global, window, window.getClass()); + var viewCache = {}; proxyObject.__proxy__ = { set: function(name, value){ window[name] = value; @@ -20,12 +26,12 @@ module.exports = function(runtime, global){ get: function(name) { var value = window[name]; if(typeof(value) == 'undefined'){ - value = floaty.__view_cache__[name]; + value = viewCache[name]; if(!value){ value = window.findView(name); if(value){ value = ui.__decorate__(value); - floaty.__view_cache__[name] = value; + viewCache[name] = value; } } if(!value){ diff --git a/autojs/src/main/java/com/stardust/autojs/core/floaty/FloatyWindow.java b/autojs/src/main/java/com/stardust/autojs/core/floaty/BaseResizableFloatyWindow.java similarity index 86% rename from autojs/src/main/java/com/stardust/autojs/core/floaty/FloatyWindow.java rename to autojs/src/main/java/com/stardust/autojs/core/floaty/BaseResizableFloatyWindow.java index d15d596b..8ad0dde7 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/floaty/FloatyWindow.java +++ b/autojs/src/main/java/com/stardust/autojs/core/floaty/BaseResizableFloatyWindow.java @@ -10,6 +10,10 @@ import android.view.WindowManager; import android.widget.FrameLayout; import com.stardust.autojs.R; +import com.stardust.autojs.core.ui.inflater.inflaters.Exceptions; +import com.stardust.autojs.runtime.exception.ScriptInterruptedException; +import com.stardust.concurrent.VolatileBox; +import com.stardust.concurrent.VolatileDispose; import com.stardust.enhancedfloaty.FloatyService; import com.stardust.enhancedfloaty.ResizableFloaty; import com.stardust.enhancedfloaty.ResizableFloatyWindow; @@ -21,7 +25,7 @@ import com.stardust.enhancedfloaty.gesture.ResizeGesture; * Created by Stardust on 2017/12/5. */ -public class FloatyWindow extends ResizableFloatyWindow { +public class BaseResizableFloatyWindow extends ResizableFloatyWindow { public interface ViewSupplier { @@ -29,8 +33,7 @@ public class FloatyWindow extends ResizableFloatyWindow { } - private final Object mLock = new Object(); - private boolean mCreated = false; + private VolatileDispose mInflateException = new VolatileDispose<>(); private View mCloseButton; private static final String TAG = "ResizableFloatyWindow"; private WindowManager mWindowManager; @@ -43,26 +46,17 @@ public class FloatyWindow extends ResizableFloatyWindow { private MyFloaty mFloaty; - public FloatyWindow(Context context, ViewSupplier viewSupplier) { + public BaseResizableFloatyWindow(Context context, ViewSupplier viewSupplier) { this(new MyFloaty(context, viewSupplier)); } - private FloatyWindow(MyFloaty floaty) { + private BaseResizableFloatyWindow(MyFloaty floaty) { super(floaty); mFloaty = floaty; } - public void waitFor() { - synchronized (mLock) { - if (mCreated) { - return; - } - try { - mLock.wait(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } + public RuntimeException waitForCreation() { + return mInflateException.blockedGetOrThrow(ScriptInterruptedException.class); } @Override @@ -72,14 +66,16 @@ public class FloatyWindow extends ResizableFloatyWindow { if (this.mFloaty == null) { throw new IllegalStateException("Must start this service by static method ResizableExpandableFloatyWindow.startService"); } else { - this.initWindowView(service); + try { + this.initWindowView(service); + } catch (RuntimeException e) { + mInflateException.setAndNotify(e); + return; + } this.mWindowBridge = new WindowBridge.DefaultImpl(this.mWindowLayoutParams, this.mWindowManager, this.mWindowView); this.initGesture(); } - synchronized (mLock) { - mCreated = true; - mLock.notify(); - } + mInflateException.setAndNotify(Exceptions.NO_EXCEPTION); } public void setOnCloseButtonClickListener(View.OnClickListener listener) { @@ -161,7 +157,8 @@ public class FloatyWindow extends ResizableFloatyWindow { } public void close() { - this.mWindowManager.removeView(this.mWindowView); + if (mWindowView != null) + this.mWindowManager.removeView(this.mWindowView); FloatyService.removeWindow(this); } diff --git a/autojs/src/main/java/com/stardust/autojs/core/floaty/RawWindow.java b/autojs/src/main/java/com/stardust/autojs/core/floaty/RawWindow.java new file mode 100644 index 00000000..d7d9e7a7 --- /dev/null +++ b/autojs/src/main/java/com/stardust/autojs/core/floaty/RawWindow.java @@ -0,0 +1,123 @@ +package com.stardust.autojs.core.floaty; + +import android.graphics.PixelFormat; +import android.os.Build; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; + +import com.stardust.autojs.R; +import com.stardust.autojs.core.ui.inflater.inflaters.Exceptions; +import com.stardust.autojs.runtime.exception.ScriptInterruptedException; +import com.stardust.concurrent.VolatileBox; +import com.stardust.concurrent.VolatileDispose; +import com.stardust.enhancedfloaty.FloatyService; +import com.stardust.enhancedfloaty.FloatyWindow; +import com.stardust.enhancedfloaty.WindowBridge; + +public class RawWindow implements FloatyWindow { + + + public interface RawFloaty { + + View inflateWindowView(FloatyService service, ViewGroup parent); + } + + private WindowBridge mWindowBridge; + private VolatileDispose mInflateException = new VolatileDispose<>(); + private WindowManager mWindowManager; + private ViewGroup mWindowView; + private View mWindowContent; + private RawFloaty mRawFloaty; + private WindowManager.LayoutParams mWindowLayoutParams; + + public RawWindow(RawFloaty rawFloaty) { + mRawFloaty = rawFloaty; + } + + @Override + public void onCreate(FloatyService floatyService, WindowManager windowManager) { + mWindowManager = windowManager; + mWindowView = (ViewGroup) View.inflate(floatyService, R.layout.raw_window, null); + mWindowLayoutParams = createWindowLayoutParams(); + try { + mWindowContent = mRawFloaty.inflateWindowView(floatyService, mWindowView); + mWindowManager.addView(mWindowView, mWindowLayoutParams); + } catch (RuntimeException e) { + mInflateException.setAndNotify(e); + return; + } + mWindowBridge = new WindowBridge.DefaultImpl(mWindowLayoutParams, windowManager, mWindowView); + mInflateException.setAndNotify(Exceptions.NO_EXCEPTION); + } + + public RuntimeException waitForCreation() { + return mInflateException.blockedGetOrThrow(ScriptInterruptedException.class); + } + + + protected WindowManager.LayoutParams createWindowLayoutParams() { + int flags = + WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_FULLSCREEN + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + flags |= WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; + } + WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams( + WindowManager.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, + flags, + PixelFormat.TRANSLUCENT); + layoutParams.gravity = Gravity.TOP | Gravity.START; + return layoutParams; + } + + + public void disableWindowFocus() { + mWindowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + mWindowManager.updateViewLayout(mWindowView, mWindowLayoutParams); + } + + public void requestWindowFocus() { + mWindowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + mWindowManager.updateViewLayout(mWindowView, mWindowLayoutParams); + mWindowView.requestFocus(); + } + + public void setTouchable(boolean touchable) { + if (touchable) { + mWindowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + } else { + mWindowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + } + mWindowManager.updateViewLayout(mWindowView, mWindowLayoutParams); + } + + public WindowBridge getWindowBridge() { + return mWindowBridge; + } + + public ViewGroup getWindowView() { + return mWindowView; + } + + public View getWindowContent() { + return mWindowContent; + } + + @Override + public void onServiceDestroy(FloatyService floatyService) { + close(); + } + + @Override + public void close() { + if (mWindowView != null) + mWindowManager.removeView(mWindowView); + FloatyService.removeWindow(this); + } +} diff --git a/autojs/src/main/java/com/stardust/autojs/core/ui/inflater/inflaters/Exceptions.java b/autojs/src/main/java/com/stardust/autojs/core/ui/inflater/inflaters/Exceptions.java index b7ea2c5d..29635bf6 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/ui/inflater/inflaters/Exceptions.java +++ b/autojs/src/main/java/com/stardust/autojs/core/ui/inflater/inflaters/Exceptions.java @@ -8,6 +8,9 @@ import android.view.View; public class Exceptions { + public static final RuntimeException NO_EXCEPTION = new RuntimeException(); + + public interface ExceptionHandler { boolean handleUnsupportedException(UnsupportedOperationException e, View v, String attrName, String value); } diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/api/Floaty.java b/autojs/src/main/java/com/stardust/autojs/runtime/api/Floaty.java index 9fa072ed..97572cee 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/api/Floaty.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/Floaty.java @@ -5,16 +5,16 @@ import android.content.Intent; import android.os.Looper; import android.view.ContextThemeWrapper; import android.view.View; -import android.view.ViewGroup; import com.stardust.autojs.R; -import com.stardust.autojs.core.floaty.FloatyWindow; +import com.stardust.autojs.core.floaty.BaseResizableFloatyWindow; +import com.stardust.autojs.core.floaty.RawWindow; import com.stardust.autojs.core.ui.JsViewHelper; import com.stardust.autojs.core.ui.inflater.DynamicLayoutInflater; +import com.stardust.autojs.core.ui.inflater.inflaters.Exceptions; import com.stardust.autojs.runtime.ScriptRuntime; import com.stardust.autojs.runtime.exception.ScriptInterruptedException; import com.stardust.autojs.util.FloatingPermission; -import com.stardust.concurrent.VolatileDispose; import com.stardust.enhancedfloaty.FloatyService; import com.stardust.util.UiHandler; import com.stardust.util.ViewUtil; @@ -30,7 +30,7 @@ public class Floaty { private DynamicLayoutInflater mLayoutInflater; private Context mContext; private UiHandler mUiHandler; - private CopyOnWriteArraySet mWindows = new CopyOnWriteArraySet<>(); + private CopyOnWriteArraySet mWindows = new CopyOnWriteArraySet<>(); private ScriptRuntime mRuntime; public Floaty(UiHandler uiHandler, UI ui, ScriptRuntime runtime) { @@ -40,51 +40,175 @@ public class Floaty { mLayoutInflater = ui.getLayoutInflater(); } - public JsFloatyWindow window(String xml) { + public JsResizableWindow window(String xml) { try { FloatingPermission.waitForPermissionGranted(mContext); } catch (InterruptedException e) { throw new ScriptInterruptedException(); } - JsFloatyWindow window = new JsFloatyWindow((context, parent) -> mLayoutInflater.inflate(xml, parent)); + JsResizableWindow window = new JsResizableWindow((context, parent) -> mLayoutInflater.inflate(xml, parent)); addWindow(window); return window; } - public JsFloatyWindow window(View view) { + public JsResizableWindow window(View view) { try { FloatingPermission.waitForPermissionGranted(view.getContext()); } catch (InterruptedException e) { throw new ScriptInterruptedException(); } - JsFloatyWindow window = new JsFloatyWindow((context, parent) -> view); + JsResizableWindow window = new JsResizableWindow((context, parent) -> view); addWindow(window); return window; } - private synchronized void addWindow(JsFloatyWindow window) { + public JsRawWindow rawWindow(String xml) { + try { + FloatingPermission.waitForPermissionGranted(mContext); + } catch (InterruptedException e) { + throw new ScriptInterruptedException(); + } + JsRawWindow window = new JsRawWindow((context, parent) -> mLayoutInflater.inflate(xml, parent)); + addWindow(window); + return window; + } + + public JsRawWindow rawWindow(View view) { + try { + FloatingPermission.waitForPermissionGranted(mContext); + } catch (InterruptedException e) { + throw new ScriptInterruptedException(); + } + JsRawWindow window = new JsRawWindow((context, parent) -> view); + addWindow(window); + return window; + } + + private synchronized void addWindow(JsWindow window) { mWindows.add(window); } - private synchronized boolean removeWindow(JsFloatyWindow window) { + private synchronized boolean removeWindow(JsWindow window) { return mWindows.remove(window); } public synchronized void closeAll() { - for (JsFloatyWindow window : mWindows) { + for (JsWindow window : mWindows) { window.close(false); } mWindows.clear(); } - public class JsFloatyWindow { + public interface JsWindow { + void close(boolean removeFromWindows); + } + + public class JsRawWindow implements JsWindow { + + private RawWindow mWindow; + private boolean mExitOnClose; + + public JsRawWindow(RawWindow.RawFloaty floaty) { + mWindow = new RawWindow(floaty); + mUiHandler.post(() -> { + mUiHandler.getContext().startService(new Intent(mUiHandler.getContext(), FloatyService.class)); + FloatyService.addWindow(mWindow); + }); + RuntimeException exception = mWindow.waitForCreation(); + if (exception != Exceptions.NO_EXCEPTION && exception != null) { + throw exception; + } + } + + public View findView(String id) { + return JsViewHelper.findViewByStringId(mWindow.getWindowContent(), id); + } + + public int getX() { + return mWindow.getWindowBridge().getX(); + } + + public int getY() { + return mWindow.getWindowBridge().getY(); + } + + public int getWidth() { + return mWindow.getWindowView().getWidth(); + } + + public int getHeight() { + return mWindow.getWindowView().getHeight(); + } + + public void setSize(int w, int h) { + runWithWindow(() -> { + mWindow.getWindowBridge().updateMeasure(w, h); + ViewUtil.setViewMeasure(mWindow.getWindowView(), w, h); + } + ); + } + + public void setTouchable(boolean touchable) { + runWithWindow(() -> mWindow.setTouchable(touchable)); + } + + private void runWithWindow(Runnable r) { + if (mWindow == null) + return; + if (Looper.myLooper() == Looper.getMainLooper()) { + r.run(); + return; + } + mUiHandler.post(() -> { + if (mWindow == null) + return; + r.run(); + }); + } + + public void setPosition(int x, int y) { + runWithWindow(() -> mWindow.getWindowBridge().updatePosition(x, y)); + } + + public void exitOnClose() { + mExitOnClose = true; + } + + public void requestFocus() { + mWindow.requestWindowFocus(); + } + + public void disableFocus() { + mWindow.disableWindowFocus(); + } + + public void close() { + close(true); + } + + public void close(boolean removeFromWindows) { + if (removeFromWindows && !removeWindow(this)) { + return; + } + runWithWindow(() -> { + mWindow.close(); + mWindow = null; + if (mExitOnClose) { + mRuntime.exit(); + } + }); + } + + } + + public class JsResizableWindow implements JsWindow { private View mView; - private volatile FloatyWindow mWindow; + private volatile BaseResizableFloatyWindow mWindow; private boolean mExitOnClose = false; - public JsFloatyWindow(FloatyWindow.ViewSupplier supplier) { - mWindow = new FloatyWindow(mContext, (context, parent) -> { + public JsResizableWindow(BaseResizableFloatyWindow.ViewSupplier supplier) { + mWindow = new BaseResizableFloatyWindow(mContext, (context, parent) -> { mView = supplier.inflate(context, parent); return mView; }); @@ -92,7 +216,10 @@ public class Floaty { mUiHandler.getContext().startService(new Intent(mUiHandler.getContext(), FloatyService.class)); FloatyService.addWindow(mWindow); }); - mWindow.waitFor(); + RuntimeException exception = mWindow.waitForCreation(); + if (exception != Exceptions.NO_EXCEPTION && exception != null) { + throw exception; + } mWindow.setOnCloseButtonClickListener(v -> close()); //setSize(mWindow.getWindowBridge().getScreenWidth() / 2, mWindow.getWindowBridge().getScreenHeight() / 2); } @@ -118,9 +245,14 @@ public class Floaty { } public void setSize(int w, int h) { - runWithWindow(() -> ViewUtil.setViewMeasure(mWindow.getRootView(), w, h)); + runWithWindow(() -> { + mWindow.getWindowBridge().updateMeasure(w, h); + ViewUtil.setViewMeasure(mWindow.getRootView(), w, h); + } + ); } + private void runWithWindow(Runnable r) { if (mWindow == null) return; @@ -163,7 +295,7 @@ public class Floaty { close(true); } - void close(boolean removeFromWindows) { + public void close(boolean removeFromWindows) { if (removeFromWindows && !removeWindow(this)) { return; } diff --git a/autojs/src/main/res/layout/raw_window.xml b/autojs/src/main/res/layout/raw_window.xml new file mode 100644 index 00000000..14e14421 --- /dev/null +++ b/autojs/src/main/res/layout/raw_window.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file