6.1.1 - 新增应用更新功能 修复编辑器页面/安卓10存储权限 异常消息资源化

This commit is contained in:
SuperMonster003
2022-05-31 21:23:36 +08:00
parent f68ad1c993
commit 069932c8d3
93 changed files with 2094 additions and 680 deletions

View File

@@ -1,30 +1,5 @@
package com.stardust.autojs.core.accessibility;
import android.os.Looper;
import android.os.SystemClock;
import androidx.annotation.NonNull;
import android.util.Log;
import android.view.accessibility.AccessibilityNodeInfo;
import com.stardust.autojs.BuildConfig;
import com.stardust.autojs.annotation.ScriptInterface;
import com.stardust.autojs.rhino.continuation.Continuation;
import com.stardust.autojs.runtime.exception.ScriptInterruptedException;
import com.stardust.automator.ActionArgument;
import com.stardust.automator.UiGlobalSelector;
import com.stardust.automator.UiObject;
import com.stardust.automator.UiObjectCollection;
import com.stardust.automator.filter.Filter;
import com.stardust.concurrent.VolatileBox;
import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS;
import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ARGUMENT_COLUMN_INT;
import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ARGUMENT_PROGRESS_VALUE;
@@ -57,6 +32,31 @@ import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.Acces
import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SET_PROGRESS;
import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SHOW_ON_SCREEN;
import android.os.Looper;
import android.os.SystemClock;
import android.util.Log;
import android.view.accessibility.AccessibilityNodeInfo;
import androidx.annotation.NonNull;
import com.stardust.app.GlobalAppContext;
import com.stardust.autojs.BuildConfig;
import com.stardust.autojs.R;
import com.stardust.autojs.annotation.ScriptInterface;
import com.stardust.autojs.runtime.exception.ScriptInterruptedException;
import com.stardust.automator.ActionArgument;
import com.stardust.automator.UiGlobalSelector;
import com.stardust.automator.UiObject;
import com.stardust.automator.UiObjectCollection;
import com.stardust.automator.filter.Filter;
import com.stardust.concurrent.VolatileBox;
import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Stardust on 2017/3/9.
*/
@@ -168,8 +168,7 @@ public class UiSelector extends UiGlobalSelector {
@NonNull
public UiObjectCollection untilFind() {
if (isUiThread()) {
// TODO: 2018/11/1 配置字符串
throw new IllegalThreadStateException("不能在ui线程执行阻塞操作, 请在子线程或子脚本执行untilFind()");
throw new IllegalThreadStateException(GlobalAppContext.getString(R.string.error_function_called_in_ui_thread, "untilFind"));
}
UiObjectCollection uiObjectCollection = find();
while (uiObjectCollection.empty()) {
@@ -189,8 +188,7 @@ public class UiSelector extends UiGlobalSelector {
@ScriptInterface
public UiObject findOne(long timeout) {
if (isUiThread()) {
// TODO: 2018/11/1 配置字符串
throw new IllegalThreadStateException("不能在ui线程执行阻塞操作, 请在子线程或子脚本执行findOne()");
throw new IllegalThreadStateException(GlobalAppContext.getString(R.string.error_function_called_in_ui_thread, "findOne"));
}
UiObjectCollection uiObjectCollection = find(1);
long start = SystemClock.uptimeMillis();

View File

@@ -2,6 +2,8 @@ package com.stardust.autojs.core.console;
import android.annotation.SuppressLint;
import com.stardust.app.GlobalAppContext;
import com.stardust.autojs.R;
import com.stardust.util.UiHandler;
import org.apache.log4j.Level;
@@ -49,7 +51,7 @@ public class GlobalConsole extends ConsoleImpl {
case android.util.Log.ASSERT:
return Level.FATAL;
}
throw new IllegalArgumentException("invalid level = " + level);
throw new IllegalArgumentException(GlobalAppContext.getString(R.string.error_illegal_argument, "level", level));
}
private String getLevelChar(int level) {

View File

@@ -2,6 +2,9 @@ package com.stardust.autojs.core.database;
import android.database.Cursor;
import com.stardust.app.GlobalAppContext;
import com.stardust.autojs.R;
public class CursorHelper {
@@ -12,7 +15,8 @@ public class CursorHelper {
case Cursor.FIELD_TYPE_INTEGER -> cursor.getInt(column);
case Cursor.FIELD_TYPE_NULL -> null;
case Cursor.FIELD_TYPE_BLOB -> cursor.getBlob(column);
default -> throw new IllegalArgumentException("unknown type");
default -> throw new IllegalArgumentException(GlobalAppContext
.getString(R.string.error_illegal_argument, "column", column));
};
}
}

View File

@@ -3,6 +3,8 @@ package com.stardust.autojs.core.eventloop;
import androidx.annotation.NonNull;
import com.stardust.app.GlobalAppContext;
import com.stardust.autojs.R;
import com.stardust.autojs.core.looper.Timer;
import com.stardust.autojs.runtime.ScriptBridges;
import com.stardust.autojs.runtime.exception.ScriptException;
@@ -40,7 +42,8 @@ public class EventEmitter {
private void ensureListenersNotAtLimit() {
if (mMaxListeners != 0 && mListenerWrappers.size() >= mMaxListeners) {
throw new ScriptException(new TooManyListenersException("max = " + mMaxListeners));
throw new ScriptException(new TooManyListenersException(GlobalAppContext
.getString(R.string.error_max_listeners_exceeded, mMaxListeners)));
}
}

View File

@@ -6,6 +6,8 @@ import android.media.Image;
import androidx.annotation.NonNull;
import com.stardust.app.GlobalAppContext;
import com.stardust.autojs.R;
import com.stardust.autojs.core.opencv.Mat;
import com.stardust.autojs.core.opencv.OpenCVHelper;
import com.stardust.pio.UncheckedIOException;
@@ -85,7 +87,7 @@ public class ImageWrapper implements Recyclable {
mHeight = bitmap.getHeight();
} else {
if (mat == null) {
throw new Error("Both bitmap and mat are null");
throw new Error(GlobalAppContext.getString(R.string.error_both_bitmap_and_mat_are_null));
}
mWidth = mat.cols();
mHeight = mat.rows();
@@ -204,7 +206,7 @@ public class ImageWrapper implements Recyclable {
public void ensureNotRecycled() {
if (isRecycled()) {
throw new IllegalStateException("Image has been recycled");
throw new IllegalStateException(GlobalAppContext.getString(R.string.error_image_has_been_recycled));
}
}

View File

@@ -4,6 +4,7 @@ import android.content.Context;
import androidx.annotation.NonNull;
import android.text.TextUtils;
import com.stardust.autojs.R;
import com.stardust.autojs.core.record.inputevent.EventFormatException;
import com.stardust.autojs.core.util.Shell;
@@ -91,7 +92,9 @@ public class InputEventObserver {
public void observe() {
if (mShell != null)
throw new IllegalStateException("observe() should be called only once");
throw new IllegalStateException(mContext
.getString(R.string.error_function_called_more_than_once,
"InputEventObserver.observe"));
mShell = new Shell(mContext, true);
mShell.setCallback(new Shell.SimpleCallback() {
@Override

View File

@@ -1,5 +1,8 @@
package com.stardust.autojs.core.internal;
import com.stardust.app.GlobalAppContext;
import com.stardust.autojs.R;
/**
* Created by Stardust on 2017/7/19.
*/
@@ -18,7 +21,8 @@ public class Functions {
} else if (func instanceof Func4) {
return ((Func4) func).call(args[0], args[1], args[2], args[3]);
}
throw new IllegalArgumentException("Unknown func: " + func);
throw new IllegalArgumentException(GlobalAppContext
.getString(R.string.error_illegal_argument, "func", func));
}
public interface Func {

View File

@@ -5,6 +5,8 @@ import android.os.Looper;
import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import com.stardust.app.GlobalAppContext;
import com.stardust.autojs.R;
import com.stardust.autojs.engine.RhinoJavaScriptEngine;
import com.stardust.autojs.runtime.ScriptRuntime;
import com.stardust.autojs.runtime.exception.ScriptInterruptedException;
@@ -88,7 +90,7 @@ public class TimerThread extends ThreadCompat {
public Timer getTimer() {
if (mTimer == null) {
throw new IllegalStateException("thread is not alive");
throw new IllegalStateException(GlobalAppContext.getString(R.string.error_thread_is_not_alive));
}
return mTimer;
}

View File

@@ -11,6 +11,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Looper;
import android.os.Message;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
@@ -39,6 +40,7 @@ import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.afollestad.materialdialogs.internal.MDButton;
import com.stardust.app.DialogUtils;
import com.stardust.autojs.R;
import com.stardust.autojs.core.eventloop.EventEmitter;
import com.stardust.util.UiHandler;
@@ -78,7 +80,7 @@ public class JsDialog {
Context context = mDialog.getContext();
if (!DialogUtils.isActivityContext(context)) {
Window window = mDialog.getWindow();
if (window != null){
if (window != null) {
int type;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -95,7 +97,8 @@ public class JsDialog {
case "positive" -> DialogAction.POSITIVE;
case "negative" -> DialogAction.NEGATIVE;
case "neutral" -> DialogAction.NEUTRAL;
default -> throw new IllegalArgumentException("unknown action " + action);
default -> throw new IllegalArgumentException(getContext()
.getString(R.string.error_illegal_argument, "action", action));
};
}

View File

@@ -325,7 +325,7 @@ public class DynamicLayoutInflater {
} else if (attr.length == 2) {
applyAttribute(context, setter, view, attr[0], attr[1], entry.getValue(), parent, attrs);
} else {
throw new InflateException("illegal attr name: " + entry.getKey());
throw new InflateException("Illegal attr name: " + entry.getKey());
}
}
setter.applyPendingAttributes(view, parent);

View File

@@ -4,6 +4,8 @@ import android.view.InflateException;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.stardust.app.GlobalAppContext;
import com.stardust.autojs.R;
import com.stardust.autojs.core.ui.inflater.ResourceParser;
import com.stardust.autojs.core.ui.inflater.util.Colors;
import com.stardust.autojs.core.ui.inflater.util.Dimensions;
@@ -44,7 +46,7 @@ public class ImageViewInflater<V extends ImageView> extends BaseViewInflater<V>
view.setMaxWidth(Dimensions.parseToIntPixel(value, view));
break;
case "path":
getDrawables().setupWithImage(view, wrapAsPath(value));
getDrawables().setupWithImage(view, wrapAsPath(value));
break;
case "scaleType":
view.setScaleType(parseScaleType(value));
@@ -59,13 +61,14 @@ public class ImageViewInflater<V extends ImageView> extends BaseViewInflater<V>
view.setImageTintMode(TINT_MODES.get(value));
break;
case "url":
getDrawables().setupWithImage(view, wrapAsUrl(value));
getDrawables().setupWithImage(view, wrapAsUrl(value));
break;
default:
return false;
}
return true;
}
private String wrapAsPath(String value) {
if (!value.startsWith("file://")) {
return "file://" + value;
@@ -80,8 +83,8 @@ public class ImageViewInflater<V extends ImageView> extends BaseViewInflater<V>
return value;
}
private ImageView.ScaleType parseScaleType(String value) {
switch (value.toLowerCase()) {
private ImageView.ScaleType parseScaleType(String scaleType) {
switch (scaleType.toLowerCase()) {
case "center":
return ImageView.ScaleType.CENTER;
case "center_crop":
@@ -99,6 +102,7 @@ public class ImageViewInflater<V extends ImageView> extends BaseViewInflater<V>
case "matrix":
return ImageView.ScaleType.MATRIX;
}
throw new InflateException("unknown scale type: " + value);
throw new InflateException(GlobalAppContext
.getString(R.string.error_illegal_argument, "scaleType", scaleType));
}
}

View File

@@ -7,6 +7,8 @@ import android.view.InflateException;
import android.view.View;
import android.view.ViewGroup;
import com.stardust.autojs.R;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -56,7 +58,7 @@ public class Dimensions {
}
Matcher m = DIMENSION_PATTERN.matcher(dimension);
if (!m.matches()) {
throw new InflateException("dimension cannot be resolved: " + dimension);
throw new InflateException(context.getString(R.string.error_illegal_argument, "dimension", dimension));
}
int unit = m.groupCount() == 2 ? UNITS.get(m.group(2), TypedValue.COMPLEX_UNIT_DIP) : TypedValue.COMPLEX_UNIT_DIP;
float value = Integer.parseInt(m.group(1));

View File

@@ -44,7 +44,7 @@ public class ValueMapper<V> {
public V get(String key) {
V v = mHashMap.get(key);
if (v == null) {
throw new InflateException(String.format("unknown value for %s: %s", mAttrName, key));
throw new InflateException(String.format("Unknown value for %s: %s", mAttrName, key));
}
return v;
}

View File

@@ -0,0 +1,135 @@
package com.stardust.autojs.core.ui.widget;
import android.content.Context;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.Button;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import com.google.android.material.snackbar.BaseTransientBottomBar;
import com.stardust.autojs.R;
/**
* Created by SuperMonster003 on May 30, 2022.
*/
public class CustomSnackbar extends BaseTransientBottomBar<CustomSnackbar> {
private final View mContent;
protected CustomSnackbar(@NonNull Context context, @NonNull ViewGroup parent, @NonNull View content, @NonNull com.google.android.material.snackbar.ContentViewCallback contentViewCallback) {
super(context, parent, content, contentViewCallback);
mContent = content;
}
@NonNull
public static CustomSnackbar make(@NonNull View view, @NonNull CharSequence text, @Duration int duration) {
return makeInternal(/* context= */ null, view, text, duration);
}
@SuppressWarnings("SameParameterValue")
private static CustomSnackbar makeInternal(@Nullable Context context, @NonNull View view, @NonNull CharSequence text, @Duration int duration) {
final ViewGroup parent = findSuitableParent(view);
if (parent == null) {
throw new IllegalArgumentException("No suitable parent found from the given view. Please provide a valid view.");
}
if (context == null) {
context = parent.getContext();
}
final LayoutInflater inflater = LayoutInflater.from(context);
final CustomSnackbarContentLayout content = (CustomSnackbarContentLayout) inflater.inflate(R.layout.snackbar_custom, parent, false);
final CustomSnackbar snackbar = new CustomSnackbar(context, parent, content, content);
snackbar.setText(text);
snackbar.setDuration(duration);
return snackbar;
}
// @Copied by SuperMonster003 on May 30, 2022.
// ! From: com.stardust.autojs.core.ui.widget.CustomSnackbar.findSuitableParent
@Nullable
private static ViewGroup findSuitableParent(View view) {
ViewGroup fallback = null;
do {
if (view instanceof CoordinatorLayout) {
// We've found a CoordinatorLayout, use it
return (ViewGroup) view;
} else if (view instanceof FrameLayout) {
if (view.getId() == android.R.id.content) {
// If we've hit the decor content view, then we didn't find a CoL in the
// hierarchy, so use it.
return (ViewGroup) view;
} else {
// It's not the content view but we'll use it as our fallback
fallback = (ViewGroup) view;
}
}
if (view != null) {
// Else, we will loop and crawl up the view hierarchy and try to find a parent
final ViewParent parent = view.getParent();
view = parent instanceof View ? (View) parent : null;
}
} while (view != null);
// If we reach here then we didn't find a CoL or a suitable content view so we'll fallback
return fallback;
}
@NonNull
public CustomSnackbar setText(@NonNull CharSequence message) {
((CustomSnackbarContentLayout) mContent).getCustomTextView().setText(message);
return this;
}
public CustomSnackbar setActionOne(@StringRes int resId, View.OnClickListener listener) {
return setActionOne(getContext().getText(resId), listener);
}
public CustomSnackbar setActionTwo(@StringRes int resId, View.OnClickListener listener) {
return setActionTwo(getContext().getText(resId), listener);
}
public CustomSnackbar setActionOne(@Nullable CharSequence text, @Nullable final View.OnClickListener listener) {
return setAction(text, listener, ((CustomSnackbarContentLayout) mContent).getActionOneView());
}
public CustomSnackbar setActionTwo(@Nullable CharSequence text, @Nullable final View.OnClickListener listener) {
return setAction(text, listener, ((CustomSnackbarContentLayout) mContent).getActionTwoView());
}
@NonNull
public CustomSnackbar setAction(@StringRes int resId, View.OnClickListener listener, Button view) {
return setAction(getContext().getText(resId), listener, view);
}
@NonNull
public CustomSnackbar setAction(@Nullable CharSequence text, @Nullable final View.OnClickListener listener, Button view) {
if (TextUtils.isEmpty(text)) {
view.setVisibility(View.GONE);
} else {
view.setVisibility(View.VISIBLE);
view.setText(text);
}
view.setOnClickListener(v -> {
if (listener != null) {
listener.onClick(v);
}
// Now dismiss the Snackbar
dispatchDismiss(BaseCallback.DISMISS_EVENT_ACTION);
});
return this;
}
}

View File

@@ -0,0 +1,81 @@
package com.stardust.autojs.core.ui.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.material.snackbar.ContentViewCallback;
import com.stardust.autojs.R;
/**
* Created by SuperMonster003 on May 30, 2022.
*/
public class CustomSnackbarContentLayout extends LinearLayout implements ContentViewCallback {
public CustomSnackbarContentLayout(@NonNull Context context) {
super(context);
}
public CustomSnackbarContentLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public Button getActionOneView() {
return findViewById(R.id.snackbar_action_one);
}
public Button getActionTwoView() {
return findViewById(R.id.snackbar_action_two);
}
public TextView getCustomTextView() {
return findViewById(R.id.snackbar_text);
}
@Override
public void animateContentIn(int delay, int duration) {
TextView mTextView = getCustomTextView();
Button mActionOneView = getActionOneView();
Button mActionTwoView = getActionTwoView();
mTextView.setAlpha(0f);
mTextView.animate().alpha(1f).setDuration(duration).setStartDelay(delay).start();
if (mActionOneView.getVisibility() == VISIBLE) {
mActionOneView.setAlpha(0f);
mActionOneView.animate().alpha(1f).setDuration(duration).setStartDelay(delay).start();
}
if (mActionTwoView.getVisibility() == VISIBLE) {
mActionTwoView.setAlpha(0f);
mActionTwoView.animate().alpha(1f).setDuration(duration).setStartDelay(delay).start();
}
}
@Override
public void animateContentOut(int delay, int duration) {
TextView mTextView = getCustomTextView();
Button mActionOneView = getActionOneView();
Button mActionTwoView = getActionTwoView();
mTextView.setAlpha(1f);
mTextView.animate().alpha(0f).setDuration(duration).setStartDelay(delay).start();
if (mActionOneView.getVisibility() == VISIBLE) {
mActionOneView.setAlpha(1f);
mActionOneView.animate().alpha(0f).setDuration(duration).setStartDelay(delay).start();
}
if (mActionTwoView.getVisibility() == VISIBLE) {
mActionTwoView.setAlpha(1f);
mActionTwoView.animate().alpha(0f).setDuration(duration).setStartDelay(delay).start();
}
}
}

View File

@@ -2,6 +2,8 @@ package com.stardust.autojs.core.util;
import android.util.Log;
import com.stardust.app.GlobalAppContext;
import com.stardust.autojs.R;
import com.stardust.autojs.runtime.api.AbstractShell;
import com.stardust.autojs.util.ProcessUtils;
import com.stardust.pio.UncheckedIOException;
@@ -178,7 +180,7 @@ public class ProcessShell extends AbstractShell {
public static Result execCommand(String[] commands, boolean isRoot) {
Result commandResult = new Result();
if (commands == null || commands.length == 0)
throw new IllegalArgumentException("command is empty");
throw new IllegalArgumentException(GlobalAppContext.getString(R.string.error_empty_shell_command));
Process process = null;
DataOutputStream os = null;
try {

View File

@@ -2,6 +2,8 @@ package com.stardust.autojs.engine;
import androidx.annotation.NonNull;
import com.stardust.app.GlobalAppContext;
import com.stardust.autojs.R;
import com.stardust.autojs.runtime.ScriptRuntime;
import com.stardust.autojs.script.JavaScriptSource;
import com.stardust.autojs.script.ScriptSource;
@@ -29,7 +31,7 @@ public abstract class JavaScriptEngine extends ScriptEngine.AbstractScriptEngine
public void setRuntime(ScriptRuntime runtime) {
if (mRuntime != null) {
throw new IllegalStateException("a runtime has been set");
throw new IllegalStateException(GlobalAppContext.getString(R.string.error_a_runtime_has_been_set));
}
mRuntime = runtime;
mRuntime.engines.setCurrentEngine(this);

View File

@@ -2,6 +2,8 @@ package com.stardust.autojs.engine;
import androidx.annotation.CallSuper;
import com.stardust.app.GlobalAppContext;
import com.stardust.autojs.R;
import com.stardust.autojs.execution.ScriptExecution;
import com.stardust.autojs.script.ScriptSource;
@@ -105,8 +107,10 @@ public interface ScriptEngine<S extends ScriptSource> {
}
public void setOnDestroyListener(OnDestroyListener onDestroyListener) {
if (mOnDestroyListener != null)
throw new SecurityException("setOnDestroyListener can be called only once");
if (mOnDestroyListener != null) {
throw new SecurityException(GlobalAppContext.getString(R.string.error_function_called_more_than_once,
"AbstractScriptEngine.setOnDestroyListener"));
}
mOnDestroyListener = onDestroyListener;
}

View File

@@ -3,6 +3,8 @@ package com.stardust.autojs.engine;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.stardust.app.GlobalAppContext;
import com.stardust.autojs.R;
import com.stardust.autojs.script.ScriptSource;
import com.stardust.util.Supplier;
@@ -64,16 +66,18 @@ public class ScriptEngineFactory {
@NonNull
public ScriptEngine createEngineByNameOrThrow(String name) {
ScriptEngine engine = createEngine(name);
if (engine == null)
throw new EngineNotFoundException("name: " + name);
if (engine == null) {
throw new EngineNotFoundException(GlobalAppContext.getString(R.string.error_illegal_argument, "name", name));
}
return engine;
}
@NonNull
public ScriptEngine createEngineOfSourceOrThrow(ScriptSource source) {
ScriptEngine engine = createEngineOfSource(source);
if (engine == null)
throw new EngineNotFoundException("source: " + source);
if (engine == null) {
throw new EngineNotFoundException(GlobalAppContext.getString(R.string.error_illegal_argument, "source", source));
}
return engine;
}

View File

@@ -3,6 +3,8 @@ package com.stardust.autojs.rhino;
import android.util.Log;
import com.android.dx.command.dexer.Main;
import com.stardust.app.GlobalAppContext;
import com.stardust.autojs.R;
import com.stardust.pio.PFiles;
import com.stardust.util.MD5;
@@ -94,7 +96,7 @@ public class AndroidClassLoader extends ClassLoader implements GeneratedClassLoa
public void loadJar(File jar) throws IOException {
Log.d(LOG_TAG, "loadJar: jar = " + jar);
if (!jar.exists() || !jar.canRead()) {
throw new FileNotFoundException("File does not exist or readable: " + jar.getPath());
throw new FileNotFoundException(GlobalAppContext.getString(R.string.file_not_exist_or_readable, jar.getPath()));
}
File dexFile = new File(mCacheDir, generateDexFileName(jar));
if (dexFile.exists()) {

View File

@@ -36,7 +36,7 @@ public class ScriptBridges {
private void checkBridges() {
if (mBridges == null)
throw new IllegalStateException("no bridges set");
throw new IllegalStateException("No bridges have been set");
}

View File

@@ -227,8 +227,9 @@ public class ScriptRuntime {
}
public void init() {
if (loopers != null)
throw new IllegalStateException("already initialized");
if (loopers != null) {
throw new IllegalStateException("Already initialized");
}
threads = new Threads(this);
timers = new Timers(this);
loopers = new Loopers(this);
@@ -243,7 +244,7 @@ public class ScriptRuntime {
public void setTopLevelScope(TopLevelScope topLevelScope) {
if (mTopLevelScope != null) {
throw new IllegalStateException("top level has already exists");
throw new IllegalStateException("Top level has already exists");
}
mTopLevelScope = topLevelScope;
}

View File

@@ -35,7 +35,7 @@ public class AccessibilityConfig {
public void addWhiteList(String packageName) {
if (mSealed)
throw new IllegalStateException("sealed");
throw new IllegalStateException("Sealed");
mWhiteList.add(packageName);
}

View File

@@ -6,11 +6,13 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import androidx.annotation.Nullable;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import android.util.Log;
import com.stardust.autojs.R;
import com.stardust.autojs.annotation.ScriptInterface;
import com.stardust.util.IntentUtil;
@@ -110,15 +112,21 @@ public class AppUtils {
@ScriptInterface
public void viewFile(String path) {
if (path == null)
throw new NullPointerException("path == null");
if (path == null) {
throw new NullPointerException(mContext
.getString(R.string.error_method_called_with_null_argument,
"AppUtils.viewFile", "path"));
}
IntentUtil.viewFile(mContext, path, mFileProviderAuthority);
}
@ScriptInterface
public void editFile(String path) {
if (path == null)
throw new NullPointerException("path == null");
if (path == null) {
throw new NullPointerException(mContext
.getString(R.string.error_method_called_with_null_argument,
"AppUtils.editFile", "path"));
}
IntentUtil.editFile(mContext, path, mFileProviderAuthority);
}

View File

@@ -206,7 +206,7 @@ public class Device {
public boolean isCharging() {
Intent intent = mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
if (intent == null) {
throw new ScriptException("Cannot retrieve the battery state");
throw new ScriptException(mContext.getString(R.string.error_cannot_retrieve_battery_state));
}
int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
return plugged == BatteryManager.BATTERY_PLUGGED_AC || plugged == BatteryManager.BATTERY_PLUGGED_USB;
@@ -310,7 +310,7 @@ public class Device {
private <T> T getSystemService(String service) {
Object systemService = mContext.getSystemService(service);
if (systemService == null) {
throw new RuntimeException("should never happen..." + service);
throw new RuntimeException("Should never happen... " + service);
}
return (T) systemService;
}

View File

@@ -161,7 +161,7 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver
mScriptRuntime.ensureAccessibilityServiceEnabled();
AccessibilityService service = mAccessibilityBridge.getService();
if (service == null)
throw new ScriptException("AccessibilityService = null");
throw new ScriptException(mContext.getString(R.string.error_no_accessibility_service));
return service;
}

View File

@@ -14,6 +14,10 @@ import android.util.Base64;
import android.util.Log;
import android.view.Gravity;
import androidx.annotation.NonNull;
import com.stardust.app.GlobalAppContext;
import com.stardust.autojs.R;
import com.stardust.autojs.annotation.ScriptVariable;
import com.stardust.autojs.core.image.ColorFinder;
import com.stardust.autojs.core.image.ImageWrapper;
@@ -99,7 +103,7 @@ public class Images {
public synchronized ImageWrapper captureScreen() {
if (mScreenCapturer == null) {
throw new SecurityException("No screen capture permission");
throw new SecurityException(mContext.getString(R.string.error_no_screen_capture_permission));
}
Image capture = mScreenCapturer.capture();
if (capture == mPreCapture && mPreCaptureImage != null) {
@@ -123,15 +127,12 @@ public class Images {
return false;
}
public ImageWrapper copy(ImageWrapper image) {
public ImageWrapper copy(@NonNull ImageWrapper image) {
return image.clone();
}
public boolean save(ImageWrapper image, String path, String format, int quality) throws IOException {
public boolean save(@NonNull ImageWrapper image, String path, String format, int quality) throws IOException {
Bitmap.CompressFormat compressFormat = parseImageFormat(format);
if (compressFormat == null) {
throw new IllegalArgumentException("unknown format " + format);
}
Bitmap bitmap = image.getBitmap();
FileOutputStream outputStream = new FileOutputStream(mScriptRuntime.files.path(path));
return bitmap.compress(compressFormat, quality, outputStream);
@@ -139,14 +140,17 @@ public class Images {
public static int pixel(ImageWrapper image, int x, int y) {
if (image == null) {
throw new NullPointerException("image = null");
throw new NullPointerException(GlobalAppContext
.getString(R.string.error_method_called_with_null_argument, "Images.pixel", "image"));
}
return image.pixel(x, y);
}
public static ImageWrapper concat(ImageWrapper img1, ImageWrapper img2, int direction) {
if (!Arrays.asList(Gravity.START, Gravity.END, Gravity.TOP, Gravity.BOTTOM).contains(direction)) {
throw new IllegalArgumentException("unknown direction " + direction);
throw new IllegalArgumentException(GlobalAppContext
.getString(R.string.error_illegal_argument,
"direction", direction));
}
int width;
int height;
@@ -175,13 +179,13 @@ public class Images {
return ImageWrapper.ofBitmap(bitmap);
}
public ImageWrapper rotate(ImageWrapper img, float x, float y, float degree) {
public ImageWrapper rotate(@NonNull ImageWrapper img, float x, float y, float degree) {
Matrix matrix = new Matrix();
matrix.postRotate(degree, x, y);
return ImageWrapper.ofBitmap(Bitmap.createBitmap(img.getBitmap(), 0, 0, img.getWidth(), img.getHeight(), matrix, true));
}
public ImageWrapper clip(ImageWrapper img, int x, int y, int w, int h) {
public ImageWrapper clip(@NonNull ImageWrapper img, int x, int y, int w, int h) {
return ImageWrapper.ofBitmap(Bitmap.createBitmap(img.getBitmap(), x, y, w, h));
}
@@ -199,11 +203,8 @@ public class Images {
return Base64.encodeToString(toBytes(wrapper, format, quality), Base64.NO_WRAP);
}
public byte[] toBytes(ImageWrapper wrapper, String format, int quality) {
public byte[] toBytes(@NonNull ImageWrapper wrapper, String format, int quality) {
Bitmap.CompressFormat compressFormat = parseImageFormat(format);
if (compressFormat == null) {
throw new IllegalArgumentException("Unknown format " + format);
}
Bitmap bitmap = wrapper.getBitmap();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
bitmap.compress(compressFormat, quality, outputStream);
@@ -219,7 +220,8 @@ public class Images {
case "png" -> Bitmap.CompressFormat.PNG;
case "jpeg", "jpg" -> Bitmap.CompressFormat.JPEG;
case "webp" -> Bitmap.CompressFormat.WEBP;
default -> null;
default -> throw new IllegalArgumentException(GlobalAppContext
.getString(R.string.error_illegal_argument, "format", format));
};
}
@@ -237,7 +239,7 @@ public class Images {
}
}
public static void saveBitmap(Bitmap bitmap, String path) {
public static void saveBitmap(@NonNull Bitmap bitmap, String path) {
try {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, new FileOutputStream(path));
} catch (FileNotFoundException e) {
@@ -279,10 +281,14 @@ public class Images {
public Point findImage(ImageWrapper image, ImageWrapper template, float weakThreshold, float threshold, Rect rect, int maxLevel) {
initOpenCvIfNeeded();
if (image == null) {
throw new NullPointerException("Param image is null");
throw new NullPointerException(GlobalAppContext
.getString(R.string.error_method_called_with_null_argument,
"Images.findImage", "image"));
}
if (template == null) {
throw new NullPointerException("Param template is null");
throw new NullPointerException(GlobalAppContext
.getString(R.string.error_method_called_with_null_argument,
"Images.findImage", "template"));
}
Mat src = image.getMat();
if (rect != null) {
@@ -307,10 +313,14 @@ public class Images {
public List<TemplateMatching.Match> matchTemplate(ImageWrapper image, ImageWrapper template, float weakThreshold, float threshold, Rect rect, int maxLevel, int limit) {
initOpenCvIfNeeded();
if (image == null) {
throw new NullPointerException("Param image is null");
throw new NullPointerException(GlobalAppContext
.getString(R.string.error_method_called_with_null_argument,
"Images.matchTemplate", "image"));
}
if (template == null) {
throw new NullPointerException("Param template is null");
throw new NullPointerException(GlobalAppContext
.getString(R.string.error_method_called_with_null_argument,
"Images.matchTemplate", "template"));
}
Mat src = image.getMat();
if (rect != null) {

View File

@@ -8,6 +8,8 @@ import android.hardware.SensorManager;
import androidx.annotation.NonNull;
import com.stardust.app.GlobalAppContext;
import com.stardust.autojs.R;
import com.stardust.autojs.core.eventloop.EventEmitter;
import com.stardust.autojs.core.looper.Loopers;
import com.stardust.autojs.runtime.ScriptBridges;
@@ -98,7 +100,9 @@ public class Sensors extends EventEmitter implements Loopers.LooperQuitHandler {
public SensorEventEmitter register(String sensorName, int delay) {
if (sensorName == null) {
throw new NullPointerException("sensorName = null");
throw new NullPointerException(GlobalAppContext
.getString(R.string.error_method_called_with_null_argument,
"Sensors.register", "sensorName"));
}
Sensor sensor = getSensor(sensorName);
if (sensor == null) {

View File

@@ -2,6 +2,8 @@ package com.stardust.autojs.runtime.api;
import androidx.annotation.NonNull;
import com.stardust.app.GlobalAppContext;
import com.stardust.autojs.R;
import com.stardust.autojs.core.looper.MainThreadProxy;
import com.stardust.autojs.core.looper.TimerThread;
import com.stardust.autojs.runtime.ScriptRuntime;
@@ -45,7 +47,7 @@ public class Threads {
TimerThread thread = createThread(runnable);
synchronized (mThreads) {
if (mExit) {
throw new IllegalStateException("Script is on exiting");
throw new IllegalStateException(GlobalAppContext.getString(R.string.error_script_is_on_exiting));
}
mThreads.add(thread);
thread.setName(mMainThread.getName() + " (Spawn-" + mSpawnCount + ")");

View File

@@ -56,7 +56,9 @@ public class RootTool {
if (key.equals(AUTO_DETECT.key)) {
return AUTO_DETECT;
}
throw new Error("Unknown RootMode number");
throw new IllegalArgumentException(GlobalAppContext
.getString(R.string.error_illegal_argument,
"RootMode", key));
}
@NonNull

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<view xmlns:android="http://schemas.android.com/apk/res/android"
class="com.stardust.autojs.core.ui.widget.CustomSnackbarContentLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:theme="@style/Theme.Holo">
<TextView
android:id="@+id/snackbar_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical|start"
android:paddingTop="@dimen/snackbar_padding_vertical"
android:paddingBottom="@dimen/snackbar_padding_vertical"
android:paddingLeft="@dimen/snackbar_padding_horizontal"
android:paddingRight="@dimen/snackbar_padding_horizontal"
android:ellipsize="end"
android:maxLines="@integer/snackbar_text_max_lines"
android:textSize="@dimen/snackbar_text_size"
android:textAlignment="viewStart"
android:textAppearance="@style/TextAppearance.Design.Snackbar.Message"/>
<Button
android:id="@+id/snackbar_action_two"
style="@style/Widget.AppCompat.Button.Borderless"
android:textSize="@dimen/snackbar_text_size"
android:textAllCaps="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/snackbar_extra_spacing_horizontal"
android:layout_gravity="center_vertical|end"
android:minWidth="@dimen/snackbar_min_width"
android:textColor="@color/colorAccent" />
<Button
android:id="@+id/snackbar_action_one"
style="@style/Widget.AppCompat.Button.Borderless"
android:textSize="@dimen/snackbar_text_size"
android:textAllCaps="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/snackbar_extra_spacing_horizontal"
android:layout_gravity="center_vertical|end"
android:minWidth="@dimen/snackbar_min_width"
android:textColor="@color/colorAccent"/>
</view>

View File

@@ -36,4 +36,20 @@
<string name="text_start_running">Running</string>
<string name="text_root_mode_description">If you have exotic root or abnormal state for root access, you can force set root to root or non-root.</string>
<string name="text_hidden_files_description">Whether the hidden files and folders show or not in file explorer. A file or folder is considered to be hidden if its name begins with a period character (\".\").</string>
<string name="text_updates_snack_bar_act_view">View</string>
<string name="text_updates_snack_bar_act_later">Later</string>
<string name="error_function_called_in_ui_thread">UI thread can\'t be blocked. Try running %s() in sub-thread or sub-script</string>
<string name="error_max_listeners_exceeded">Max listeners exceeded: %d</string>
<string name="error_both_bitmap_and_mat_are_null">Both bitmap and mat are null</string>
<string name="error_image_has_been_recycled">Image has been recycled</string>
<string name="error_function_called_more_than_once">%s() called more than once</string>
<string name="error_thread_is_not_alive">Thread is not alive</string>
<string name="error_empty_shell_command">Command is empty</string>
<string name="error_a_runtime_has_been_set">A runtime has been set</string>
<string name="file_not_exist_or_readable">File doesn\'t exist or readable: %s</string>
<string name="error_cannot_retrieve_battery_state">Can\'t retrieve the battery state</string>
<string name="error_no_screen_capture_permission">No screen capture permission</string>
<string name="error_no_accessibility_service">No accessibility service</string>
<string name="error_method_called_with_null_argument">%s() called with null argument: %s</string>
<string name="error_script_is_on_exiting">Script is on exiting</string>
</resources>

View File

@@ -37,4 +37,20 @@
<string name="entry_hidden_files_show">显示隐藏文件和文件夹</string>
<string name="text_root_mode_description">如果设备使用非常规 Root 方式或 Root 权限检测异常, 可设置强制 Root 或 强制非 Root 模式.</string>
<string name="text_hidden_files_description">设置是否在文件浏览器页面显示隐藏文件和文件夹 (文件名以 \".\" 开头)</string>
<string name="text_updates_snack_bar_act_view">查看更新</string>
<string name="text_updates_snack_bar_act_later">稍后提示</string>
<string name="error_function_called_in_ui_thread">UI 线程内无法执行阻塞操作. 可尝试在子线程或子脚本中执行 %s()</string>
<string name="error_max_listeners_exceeded">超出最大监听器数量限制: %d</string>
<string name="error_both_bitmap_and_mat_are_null">Bitmap 与 Mat 不能同时为 null</string>
<string name="error_image_has_been_recycled">图像资源已被回收</string>
<string name="error_function_called_more_than_once">%s() 调用不能超过一次</string>
<string name="error_thread_is_not_alive">线程处于非活动状态</string>
<string name="error_empty_shell_command">命令为空</string>
<string name="error_a_runtime_has_been_set">运行时已存在</string>
<string name="file_not_exist_or_readable">文件不存在或不可读: %s</string>
<string name="error_cannot_retrieve_battery_state">无法获取设备电池状态</string>
<string name="error_no_screen_capture_permission">缺少屏幕捕获权限</string>
<string name="error_no_accessibility_service">没有无障碍服务</string>
<string name="error_method_called_with_null_argument">%s() 传入的 %s 参数为空</string>
<string name="error_script_is_on_exiting">脚本正在退出中</string>
</resources>

View File

@@ -1,4 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="floaty_window_offset">12dp</dimen>
<dimen name="snackbar_padding_vertical">14dp</dimen>
<dimen name="snackbar_padding_horizontal">12dp</dimen>
<integer name="snackbar_text_max_lines">2</integer>
<dimen name="snackbar_extra_spacing_horizontal">0dp</dimen>
<dimen name="snackbar_min_width">48dp</dimen>
<dimen name="snackbar_text_size">14sp</dimen>
</resources>

View File

@@ -56,4 +56,20 @@
<string name="key_gesture_observing" translatable="false">key_gesture_observing</string>
<string name="text_root_mode_description">If you have exotic root or abnormal state for root access, you can force set root to root or non-root.</string>
<string name="text_hidden_files_description">Whether the hidden files and folders show or not in file explorer. A file or folder is considered to be hidden if its name begins with a period character (\".\").</string>
<string name="text_updates_snack_bar_act_view">View</string>
<string name="text_updates_snack_bar_act_later">Later</string>
<string name="error_function_called_in_ui_thread">UI thread can\'t be blocked. Try running %s() in sub-thread or sub-script</string>
<string name="error_max_listeners_exceeded">Max listeners exceeded: %d</string>
<string name="error_both_bitmap_and_mat_are_null">Both bitmap and mat are null</string>
<string name="error_image_has_been_recycled">Image has been recycled</string>
<string name="error_function_called_more_than_once">%s() called more than once</string>
<string name="error_thread_is_not_alive">Thread is not alive</string>
<string name="error_empty_shell_command">Command is empty</string>
<string name="error_a_runtime_has_been_set">A runtime has been set</string>
<string name="file_not_exist_or_readable">File doesn\'t exist or readable: %s</string>
<string name="error_cannot_retrieve_battery_state">Can\'t retrieve the battery state</string>
<string name="error_no_screen_capture_permission">No screen capture permission</string>
<string name="error_no_accessibility_service">No accessibility service</string>
<string name="error_method_called_with_null_argument">%s() called with null argument: %s</string>
<string name="error_script_is_on_exiting">Script is on exiting</string>
</resources>