diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/floating/layoutinspector/LayoutHierarchyFloatyWindow.java b/app/src/main/java/com/stardust/scriptdroid/ui/floating/layoutinspector/LayoutHierarchyFloatyWindow.java index ba91de85..c0c678ff 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/floating/layoutinspector/LayoutHierarchyFloatyWindow.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/floating/layoutinspector/LayoutHierarchyFloatyWindow.java @@ -1,6 +1,7 @@ package com.stardust.scriptdroid.ui.floating.layoutinspector; import android.content.Context; +import android.support.annotation.NonNull; import android.view.ContextThemeWrapper; import android.view.KeyEvent; import android.view.View; diff --git a/autojs/src/main/assets/modules/__dialogs__.js b/autojs/src/main/assets/modules/__dialogs__.js index 8ac68ce6..bfde2044 100644 --- a/autojs/src/main/assets/modules/__dialogs__.js +++ b/autojs/src/main/assets/modules/__dialogs__.js @@ -89,20 +89,112 @@ module.exports = function(__runtime__, scope){ if(isUiThread() && !callback){ return new Promise(function(resolve, reject){ rtDialogs().singleChoice(title, index, items, function(r){ - resolve.apply(null, toJsArray(r)); + resolve.apply(null, javaArrayToJsArray(r)); }); }); } if(callback){ return rtDialogs().multiChoice(title, index, items, function(r){ - callback(toJsArray(r)); + callback(javaArrayToJsArray(r)); }); } - return toJsArray(rtDialogs().multiChoice(title, index, items, null)); + return javaArrayToJsArray(rtDialogs().multiChoice(title, index, items, null)); } - function toJsArray(javaArray){ + var propertySetters = { + "title": null, + "titleColor": {adapter: parseColor}, + "buttonRippleColor": {adapter: parseColor}, + "icon": null, + "content": null, + "contentColor": {adapter: parseColor}, + "contentLineSpacing": null, + "items": null, + "itemsColor": {adapter: parseColor}, + "positive": {method: "positiveText"}, + "positiveColor": {adapter: parseColor}, + "neutral": {method: "neutralText"}, + "neutralColor": {adapter: parseColor}, + "negative": {method: "negativeText"}, + "negativeColor": {adapter: parseColor}, + "cancelable": null, + "canceledOnTouchOutside": null, + "checkBoxPrompt": null, + "checkBoxChecked": null + }; + + dialogs.build = function(properties){ + var builder = __runtime__.dialogs.newBuilder(); + for(var name in properties){ + if(!properties.hasOwnProperty(name)){ + continue; + } + applyDialogProperty(builder, name, properties[name]); + } + applyOtherDialogProperties(builder, properties); + return ui.run(()=> builder.build()); + } + + function applyDialogProperty(builder, name, value){ + if(!propertySetters.hasOwnProperty(name)){ + return; + } + var propertySetter = propertySetters[name]; + if(propertySetter == null){ + propertySetter = {method: name}; + } + if(propertySetter.adapter){ + value = propertySetter.adapter(value); + } + builder[propertySetter.method].call(builder, value); + } + + function applyOtherDialogProperties(builder, properties){ + if(properties.inputHint != undefined || properties.inputPrefill != undefined){ + builder.input(wrapNonNullString(properties.inputHint), wrapNonNullString(properties.inputPrefill), + function(dialog, input){ + input = input.toString(); + dialog.emit("input_change", dialog, input); + }, true); + } + if(properties.items != undefined){ + var itemsSelectMode = properties.itemSelectMode; + if(itemsSelectMode == undefined || itemsSelectMode == 'select'){ + builder.itemsCallback(function(dialog, view, position, text){ + dialog.emit("item_select", position, text.toString(), dialog); + }); + }else if(itemsSelectMode == 'singleChoice'){ + builder.itemsCallbackSingleChoice(properties.itemsSelectedIndex == undefined ? -1 : properties.itemsSelectedIndex, + function(dialog, view, which, text){ + dialog.emit("single_choice", which, text.toString(), dialog) + }); + }else if(itemsSelectMode == 'multiChoice'){ + builder.itemsCallbackMultiChoice(properties.itemsSelectedIndex == undefined ? -1 : properties.itemsSelectedIndex, + function(dialog, view, indices, texts){ + dialog.emit("multi_choice", toJsArray(indices, (l, i)=> parseInt(l.get(i)), + toJsArray(texts, (l, i)=> l.get(i).toString())), dialog); + }); + }else{ + throw new Error("unknown itemsSelecteMode " + itemsSelectMode); + } + } + if(properties.progress != undefined){ + var progress = properties.progress; + var indeterminate = (progress.max == -1); + builder.progress(indeterminate, progress.max, progress.showMinMax); + builder.progressIndeterminateStyle(progress.horizontal); + } + } + + function wrapNonNullString(str){ + if(str == null || str == undefined){ + return ""; + } + return str; + } + + function javaArrayToJsArray(javaArray){ var jsArray = []; var len = javaArray.length; for (var i = 0;i < len;i++){ @@ -111,6 +203,15 @@ module.exports = function(__runtime__, scope){ return jsArray; } + function toJsArray(object, adapter){ + var jsArray = []; + var len = javaArray.length; + for (var i = 0;i < len;i++){ + jsArray.push(adapter(object, i)); + } + return jsArray; + } + function rtDialogs(){ var d = __runtime__.dialogs; if(!isUiThread()){ @@ -124,6 +225,13 @@ module.exports = function(__runtime__, scope){ return android.os.Looper.myLooper() == android.os.Looper.getMainLooper(); } + function parseColor(c){ + if(typeof(c) == 'string'){ + return colors.parseColor(c); + } + return c; + } + scope.rawInput = dialogs.rawInput.bind(dialogs); scope.alert = dialogs.alert.bind(dialogs); diff --git a/autojs/src/main/java/com/stardust/autojs/core/ui/BlockedMaterialDialog.java b/autojs/src/main/java/com/stardust/autojs/core/ui/dialog/BlockedMaterialDialog.java similarity index 97% rename from autojs/src/main/java/com/stardust/autojs/core/ui/BlockedMaterialDialog.java rename to autojs/src/main/java/com/stardust/autojs/core/ui/dialog/BlockedMaterialDialog.java index 70287922..ed748eb8 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/ui/BlockedMaterialDialog.java +++ b/autojs/src/main/java/com/stardust/autojs/core/ui/dialog/BlockedMaterialDialog.java @@ -1,12 +1,10 @@ -package com.stardust.autojs.core.ui; +package com.stardust.autojs.core.ui.dialog; import android.app.Activity; import android.content.Context; import android.content.ContextWrapper; -import android.content.DialogInterface; import android.os.Looper; import android.support.annotation.Nullable; -import android.view.View; import android.view.WindowManager; import com.afollestad.materialdialogs.DialogAction; @@ -18,8 +16,6 @@ import com.stardust.concurrent.VolatileDispose; import com.stardust.util.ArrayUtils; import com.stardust.util.UiHandler; -import org.jdeferred.impl.DeferredObject; - /** * Created by Stardust on 2017/5/8. */ diff --git a/autojs/src/main/java/com/stardust/autojs/core/ui/dialog/JsDialog.java b/autojs/src/main/java/com/stardust/autojs/core/ui/dialog/JsDialog.java new file mode 100644 index 00000000..62c6af6b --- /dev/null +++ b/autojs/src/main/java/com/stardust/autojs/core/ui/dialog/JsDialog.java @@ -0,0 +1,106 @@ +package com.stardust.autojs.core.ui.dialog; + +import android.content.Context; +import android.os.Looper; +import android.view.Window; +import android.view.WindowManager; + +import com.afollestad.materialdialogs.MaterialDialog; +import com.stardust.app.DialogUtils; +import com.stardust.autojs.core.eventloop.EventEmitter; +import com.stardust.util.UiHandler; + +/** + * Created by Stardust on 2018/4/17. + */ + +public class JsDialog extends MaterialDialog { + + private final EventEmitter mEmitter; + private final UiHandler mUiHandler; + + public JsDialog(Builder builder, EventEmitter emitter, UiHandler uiHandler) { + super(builder); + mEmitter = emitter; + mUiHandler = uiHandler; + } + + public EventEmitter once(String eventName, Object listener) { + return mEmitter.once(eventName, listener); + } + + public EventEmitter on(String eventName, Object listener) { + return mEmitter.on(eventName, listener); + } + + public EventEmitter addListener(String eventName, Object listener) { + return mEmitter.addListener(eventName, listener); + } + + public boolean emit(String eventName, Object... args) { + return mEmitter.emit(eventName, args); + } + + public String[] eventNames() { + return mEmitter.eventNames(); + } + + public int listenerCount(String eventName) { + return mEmitter.listenerCount(eventName); + } + + public Object[] listeners(String eventName) { + return mEmitter.listeners(eventName); + } + + public EventEmitter prependListener(String eventName, Object listener) { + return mEmitter.prependListener(eventName, listener); + } + + public EventEmitter prependOnceListener(String eventName, Object listener) { + return mEmitter.prependOnceListener(eventName, listener); + } + + public EventEmitter removeAllListeners() { + return mEmitter.removeAllListeners(); + } + + public EventEmitter removeAllListeners(String eventName) { + return mEmitter.removeAllListeners(eventName); + } + + public EventEmitter removeListener(String eventName, Object listener) { + return mEmitter.removeListener(eventName, listener); + } + + public EventEmitter setMaxListeners(int n) { + return mEmitter.setMaxListeners(n); + } + + public int getMaxListeners() { + return mEmitter.getMaxListeners(); + } + + public static int defaultMaxListeners() { + return EventEmitter.defaultMaxListeners(); + } + + @Override + public void show() { + checkWindowType(); + if (Looper.myLooper() == Looper.getMainLooper()) { + super.show(); + } else { + mUiHandler.post(super::show); + } + } + + private void checkWindowType() { + Context context = getContext(); + if (!DialogUtils.isActivityContext(context)) { + Window window = getWindow(); + if (window != null) + window.setType(WindowManager.LayoutParams.TYPE_PHONE); + } + } +} diff --git a/autojs/src/main/java/com/stardust/autojs/core/ui/dialog/JsDialogBuilder.java b/autojs/src/main/java/com/stardust/autojs/core/ui/dialog/JsDialogBuilder.java new file mode 100644 index 00000000..2a99ef69 --- /dev/null +++ b/autojs/src/main/java/com/stardust/autojs/core/ui/dialog/JsDialogBuilder.java @@ -0,0 +1,134 @@ +package com.stardust.autojs.core.ui.dialog; + +import android.content.Context; +import android.content.DialogInterface; +import android.content.res.ColorStateList; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.widget.CompoundButton; +import android.widget.EditText; + +import com.afollestad.materialdialogs.DialogAction; +import com.afollestad.materialdialogs.GravityEnum; +import com.afollestad.materialdialogs.MaterialDialog; +import com.afollestad.materialdialogs.StackingBehavior; +import com.afollestad.materialdialogs.Theme; +import com.stardust.autojs.core.eventloop.EventEmitter; +import com.stardust.autojs.runtime.ScriptBridges; +import com.stardust.util.UiHandler; + +import java.text.NumberFormat; +import java.util.Collection; + +/** + * Created by Stardust on 2018/4/17. + */ + +public class JsDialogBuilder extends MaterialDialog.Builder { + + private final EventEmitter mEmitter; + private final UiHandler mUiHandler; + + public JsDialogBuilder(@NonNull Context context, ScriptBridges bridges, UiHandler uiHandler) { + super(context); + mEmitter = new EventEmitter(bridges); + this.mUiHandler = uiHandler; + setUpEvents(); + } + + private void setUpEvents() { + showListener(dialog -> emit("show", dialog)); + onAny((dialog, which) -> { + switch (which) { + case NEUTRAL: + emit("neutral", dialog); + emit("any", "neutral", dialog); + break; + case NEGATIVE: + emit("negative", dialog); + emit("any", "negative", dialog); + break; + case POSITIVE: + EditText editText = dialog.getInputEditText(); + if (editText != null) { + emit("input", editText.getText().toString()); + } + emit("positive", dialog); + emit("any", "positive", dialog); + break; + } + }); + dismissListener(dialog -> emit("dismiss", dialog)); + cancelListener(dialog -> emit("cancel", dialog)); + } + + @Override + public MaterialDialog build() { + return new JsDialog(this, mEmitter, mUiHandler); + } + + public EventEmitter once(String eventName, Object listener) { + return mEmitter.once(eventName, listener); + } + + public EventEmitter on(String eventName, Object listener) { + return mEmitter.on(eventName, listener); + } + + public EventEmitter addListener(String eventName, Object listener) { + return mEmitter.addListener(eventName, listener); + } + + public boolean emit(String eventName, Object... args) { + return mEmitter.emit(eventName, args); + } + + public String[] eventNames() { + return mEmitter.eventNames(); + } + + public int listenerCount(String eventName) { + return mEmitter.listenerCount(eventName); + } + + public Object[] listeners(String eventName) { + return mEmitter.listeners(eventName); + } + + public EventEmitter prependListener(String eventName, Object listener) { + return mEmitter.prependListener(eventName, listener); + } + + public EventEmitter prependOnceListener(String eventName, Object listener) { + return mEmitter.prependOnceListener(eventName, listener); + } + + public EventEmitter removeAllListeners() { + return mEmitter.removeAllListeners(); + } + + public EventEmitter removeAllListeners(String eventName) { + return mEmitter.removeAllListeners(eventName); + } + + public EventEmitter removeListener(String eventName, Object listener) { + return mEmitter.removeListener(eventName, listener); + } + + public EventEmitter setMaxListeners(int n) { + return mEmitter.setMaxListeners(n); + } + + public int getMaxListeners() { + return mEmitter.getMaxListeners(); + } + + public static int defaultMaxListeners() { + return EventEmitter.defaultMaxListeners(); + } + +} diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/api/Dialogs.java b/autojs/src/main/java/com/stardust/autojs/runtime/api/Dialogs.java index dc48284e..40a12883 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/api/Dialogs.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/Dialogs.java @@ -10,7 +10,8 @@ import com.afollestad.materialdialogs.Theme; import com.stardust.autojs.R; import com.stardust.autojs.annotation.ScriptInterface; import com.stardust.autojs.annotation.ScriptVariable; -import com.stardust.autojs.core.ui.BlockedMaterialDialog; +import com.stardust.autojs.core.ui.dialog.BlockedMaterialDialog; +import com.stardust.autojs.core.ui.dialog.JsDialogBuilder; import com.stardust.autojs.runtime.ScriptBridges; import com.stardust.util.ArrayUtils; import com.stardust.util.UiHandler; @@ -138,6 +139,17 @@ public class Dialogs { .theme(Theme.LIGHT); } + @ScriptInterface + public MaterialDialog.Builder newBuilder() { + Context context = mAppUtils.getCurrentActivity(); + if (context == null || ((Activity) context).isFinishing()) { + context = getContext(); + } + return new JsDialogBuilder(context, mScriptBridges, mUiHandler) + .theme(Theme.LIGHT); + + } + public class NonUiDialogs { public String rawInput(String title, String prefill, Object callback) { diff --git a/automator/src/main/java/com/stardust/view/accessibility/LayoutInspector.java b/automator/src/main/java/com/stardust/view/accessibility/LayoutInspector.java index 2f5c38d1..e53e4308 100644 --- a/automator/src/main/java/com/stardust/view/accessibility/LayoutInspector.java +++ b/automator/src/main/java/com/stardust/view/accessibility/LayoutInspector.java @@ -50,6 +50,7 @@ public class LayoutInspector { l.onCaptureAvailable(mCapture); } }); + } public void addCaptureAvailableListener(CaptureAvailableListener l){