api(floaty): floaty.rawWindow()

This commit is contained in:
hyb1996 2018-05-28 17:02:48 +08:00
parent ac0e9952c8
commit 44b7f0b39a
6 changed files with 310 additions and 43 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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<JsFloatyWindow> mWindows = new CopyOnWriteArraySet<>();
private CopyOnWriteArraySet<JsWindow> 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;
}

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</FrameLayout>