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

@@ -2,10 +2,25 @@
xmlns:tools="http://schemas.android.com/tools"
package="org.autojs.autojs">
<!--
@Hint by SuperMonster003 on May 27, 2022.
! WRT: android.permission.WRITE_EXTERNAL_STORAGE
! MSV<API>: android:maxSdkVersion="API"
! LGC: android:requestLegacyExternalStorage="true" (since API 29)
! MAN: android.permission.MANAGE_EXTERNAL_STORAGE (since API 30)
!
! Storage permission requirement on different API targets.
! [ XXX ] means optional but recommended.
!
! Target API == 28: WRT
! Target API == 29: WRT + [ MSV<29> ] / WRT + [ MSV<28> ] + LGC
! Target API >= 30: WRT + [ MSV<29> ] + LGC + MAN
-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
android:maxSdkVersion="29" />
<uses-permission
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
@@ -42,6 +57,7 @@
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true"
android:requestLegacyExternalStorage="true"
tools:replace="android:label, android:allowBackup"
tools:ignore="DataExtractionRules">

View File

@@ -4,6 +4,20 @@
******
# v6.1.1
###### 2022/05/31
* `新增` 检查更新/下载更新/更新提示功能 (参阅 设置页面) (暂不支持安卓 7.x 系统)
* `修复` 应用在安卓 10 系统无法读写外部存储的问题 _[`issue #17`](https://github.com/SuperMonster003/AutoJs6/issues/17)_
* `修复` 编辑器页面长按时可能导致应用崩溃的问题 _[`issue #18`](https://github.com/SuperMonster003/AutoJs6/issues/18)_
* `修复` 编辑器页面长按菜单 "删除行" 和 "复制行" 功能无效的问题
* `修复` 编辑器页面选项菜单中 "粘贴" 功能缺失的问题
* `优化` 部分异常消息字符串资源化 (en / zh)
* `优化` 调整内容未保存对话框的按钮布局并增加颜色区分
* `优化` 附加 github-api 版本 1.306
* `优化` 替换 retrofit2-rxjava2-adapter 版本 1.0.0 为 adapter-rxjava2 版本 2.9.0
# v6.1.0
###### 2022/05/26 - 包名变更 谨慎升级
@@ -82,14 +96,14 @@
* `优化` 主页抽屉增加前台服务状态开关的同步
* `优化` 主页抽屉展开时立即按需同步开关状态
* `优化` 显示指针位置增加状态检测及结果提示
* `优化` 支持 64 位操作系统 (Ref to TonyJiangWJ)
* `优化` 支持 64 位操作系统 (Ref to [TonyJiangWJ](https://github.com/TonyJiangWJ))
* `优化` 悬浮窗初始化时同时应用透明度设置 (无需点击后再应用透明度)
* `优化` 重置文件内容时增加是否为示例代码文件的检测并增加结果提示
* `优化` 转移打包插件下载地址 GitHub -> JsDelivr
* `优化` 附加 Zeugma Solutions LocaleHelper 版本 1.5.1
* `优化` 降级 Android Material 版本 1.6.0-alpha02 -> 1.5.0
* `优化` 升级 Kotlinx Coroutines 版本 1.6.0-native-mt -> 1.6.0
* `优化` 升级 OpenCV 版本 3.4.3 -> 4.5.4 -> 4.5.5 (Ref to TonyJiangWJ)
* `优化` 升级 OpenCV 版本 3.4.3 -> 4.5.4 -> 4.5.5 (Ref to [TonyJiangWJ](https://github.com/TonyJiangWJ))
* `优化` 升级 Okhttp3 版本 3.10.0 -> 5.0.0-alpha.4 -> 5.0.0-alpha.6
* `优化` 升级 Android Gradle 插件版本 7.2.0-beta01 -> 7.3.0-alpha06
* `优化` 升级 Auto.js-ApkBuilder 版本 1.0.1 -> 1.0.3
@@ -107,7 +121,7 @@
* `修复` toast.dismiss() 可能无效的问题
* `修复` 客户端模式及服务端模式开关可能无法正常工作的问题
* `修复` 客户端模式及服务端模式开关状态不能正常刷新的问题
* `修复` Android 7 解析 UI 模式 text 元素异常 (Ref to TonyJiangWJ) _[`issue #4`](https://github.com/SuperMonster003/AutoJs6/issues/4)_ _[`#9`](https://github.com/SuperMonster003/AutoJs6/issues/9)_
* `修复` Android 7 解析 UI 模式 text 元素异常 (Ref to [TonyJiangWJ](https://github.com/TonyJiangWJ)) _[`issue #4`](https://github.com/SuperMonster003/AutoJs6/issues/4)_ _[`#9`](https://github.com/SuperMonster003/AutoJs6/issues/9)_
* `优化` 忽略 sleep() 的 ScriptInterruptedException 异常
* `优化` 附加 Androidx AppCompat (Legacy) 版本 1.0.2
* `优化` 升级 Androidx AppCompat 版本 1.4.0 -> 1.4.1
@@ -122,8 +136,8 @@
###### 2022/01/01
* `新增` 连接 VSCode 插件支持客户端 (LAN) 及服务端 (LAN/ADB) 方式 (Ref to Auto.js Pro)
* `新增` 内置 base64 模块 (Ref to Auto.js Pro)
* `新增` 连接 VSCode 插件支持客户端 (LAN) 及服务端 (LAN/ADB) 方式 (Ref to [Auto.js Pro](https://g.pro.autojs.org/))
* `新增` 内置 base64 模块 (Ref to [Auto.js Pro](https://g.pro.autojs.org/))
* `新增` 增加 isInteger/isNullish/isPlainObject/isPrimitive/isReference 全局方法
* `新增` 增加 polyfill (Object.getOwnPropertyDescriptors)
* `新增` 增加 polyfill (Array.prototype.flat)
@@ -159,7 +173,7 @@
* `优化` 升级 JSON 模块至 2017-06-12 版本并整合 cycle.js
* `优化` 移除 Activity 前置时的自动检查更新功能并移除检查更新相关按钮
* `优化` AppOpsKt#isOpPermissionGranted 内部代码逻辑
* `优化` ResourceMonitor 使用 ReentrantLock 增强安全性 (Ref to TonyJiangWJ)
* `优化` ResourceMonitor 使用 ReentrantLock 增强安全性 (Ref to [TonyJiangWJ](https://github.com/TonyJiangWJ))
* `优化` 使用 Maven Central 等仓库替换 JCenter 仓库
* `优化` 抽离并移除重复的本地库文件
* `优化` 本地化 CrashReport 版本 2.6.6

View File

@@ -17,7 +17,7 @@ console.log('code: ' + morse.code);
console.log('pattern: ' + morse.pattern);
// Call vibrate() if you need your device play this morse code by vibration.
// zh-CN: Call vibrate() if you need your device play this morse code by vibration.
// zh-CN: 可调用 vibrate() 方法使设备按摩斯电码模式震动.
// morse.vibrate();

View File

@@ -2,6 +2,7 @@ package com.stardust.theme.preference;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Typeface;
import android.preference.PreferenceCategory;
import android.util.AttributeSet;
import android.view.View;
@@ -54,8 +55,10 @@ public class ThemeColorPreferenceCategory extends PreferenceCategory implements
public View onCreateView(ViewGroup parent) {
View view = super.onCreateView(parent);
mTitleTextView = view.findViewById(android.R.id.title);
if (mColor != Color.TRANSPARENT)
if (mColor != Color.TRANSPARENT) {
mTitleTextView.setTextColor(mColor);
}
mTitleTextView.setTypeface(null, Typeface.BOLD);
return view;
}

View File

@@ -3,6 +3,7 @@ package org.autojs.autojs;
import android.content.SharedPreferences;
import android.os.Environment;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceManager;
import com.stardust.app.GlobalAppContext;
@@ -10,6 +11,9 @@ import com.stardust.autojs.runtime.accessibility.AccessibilityConfig;
import org.autojs.autojs.autojs.key.GlobalKeyObserver;
import org.autojs.autojs6.R;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import java.io.File;
import java.util.Objects;
@@ -21,6 +25,7 @@ public class Pref {
private static final String KEY_SERVER_ADDRESS = "KEY_SERVER_ADDRESS";
private static final String KEY_FLOATING_MENU_SHOWN = "KEY_FLOATING_MENU_SHOWN";
private static final String KEY_LAST_UPDATED_CHECKED = "KEY_LAST_UPDATED_CHECKED";
private static final String KEY_APP_LANG_INDEX = "KEY_APP_LANG_INDEX";
private static final String KEY_EDITOR_THEME = "editor.theme";
private static final String KEY_EDITOR_TEXT_SIZE = "editor.textSize";
@@ -51,6 +56,29 @@ public class Pref {
return def().getBoolean(getString(R.string.key_use_volume_control_running), true);
}
public static boolean isAutoCheckForUpdatesEnabled() {
return def().getBoolean(getString(R.string.key_auto_check_for_updates), true);
}
public static void refreshLastUpdatesCheckedTimestamp() {
def().edit().putLong(KEY_LAST_UPDATED_CHECKED, System.currentTimeMillis()).apply();
}
public static long getLastUpdatesCheckedTimestamp() {
return def().getLong(KEY_LAST_UPDATED_CHECKED, -1);
}
@Nullable
public static String getLastUpdatesCheckedTimeString() {
long ts = getLastUpdatesCheckedTimestamp();
if (ts < 0) {
return null;
}
DateTime dt = new DateTime(ts);
DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyy/MM/dd HH:mm");
return fmt.print(dt);
}
private static String getString(int id) {
return GlobalAppContext.getString(id);
}

View File

@@ -39,10 +39,10 @@ public class TinySign {
private static void doDir(String prefix, File dir, ZipOutputStream zos, DigestOutputStream dos, Manifest m) throws IOException {
zos.putNextEntry(new ZipEntry(prefix));
zos.closeEntry();
File[] arr$ = dir.listFiles();
File[] files = dir.listFiles();
if (arr$ != null) {
for (File f : arr$) {
if (files != null) {
for (File f : files) {
if (f.isFile()) {
doFile(prefix + f.getName(), f, zos, dos, m);
} else {
@@ -72,13 +72,8 @@ public class TinySign {
private static Manifest generateSF(Manifest manifest) throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest md = MessageDigest.getInstance("SHA1");
PrintStream print = new PrintStream(new DigestOutputStream(new OutputStream() {
public void write(byte[] arg0) {
}
public void write(byte[] arg0, int arg1, int arg2) {
}
public void write(int arg0) {
@Override
public void write(int b) {
}
}, md), true, "UTF-8");
Manifest sf = new Manifest();
@@ -164,10 +159,10 @@ public class TinySign {
}
private static void zipAndSha1(File dir, ZipOutputStream zos, DigestOutputStream dos, Manifest m) throws IOException {
File[] arr$ = dir.listFiles();
File[] files = dir.listFiles();
if (arr$ != null) {
for (File f : arr$) {
if (files != null) {
for (File f : files) {
if (!f.getName().startsWith("META-INF")) {
if (f.isFile()) {
doFile(f.getName(), f, zos, dos, m);
@@ -191,8 +186,8 @@ public class TinySign {
public void write(byte[] buffer) throws IOException {
try {
this.mSignature.update(buffer);
} catch (SignatureException var3) {
throw new IOException("SignatureException: " + var3);
} catch (SignatureException e) {
throw new IOException("SignatureException: " + e);
}
this.out.write(buffer);
@@ -201,8 +196,8 @@ public class TinySign {
public void write(byte[] b, int off, int len) throws IOException {
try {
this.mSignature.update(b, off, len);
} catch (SignatureException var5) {
throw new IOException("SignatureException: " + var5);
} catch (SignatureException e) {
throw new IOException("SignatureException: " + e);
}
this.out.write(b, off, len);
@@ -211,8 +206,8 @@ public class TinySign {
public void write(int b) throws IOException {
try {
this.mSignature.update((byte) b);
} catch (SignatureException var3) {
throw new IOException("SignatureException: " + var3);
} catch (SignatureException e) {
throw new IOException("SignatureException: " + e);
}
this.out.write(b);

View File

@@ -6,6 +6,7 @@ import android.graphics.Bitmap;
import android.os.Bundle;
import org.autojs.autojs.tool.BitmapTool;
import org.autojs.autojs6.R;
/**
* Created by Stardust on 2017/1/20.
@@ -54,7 +55,7 @@ public class Shortcut {
public Shortcut iconRes(Intent.ShortcutIconResource icon) {
if (mIcon != null) {
throw new IllegalStateException("Cannot set both iconRes and icon");
throw new IllegalStateException(mContext.getString(R.string.error_set_both_icon_res_and_icon));
}
mIconRes = icon;
return this;
@@ -67,7 +68,7 @@ public class Shortcut {
public Shortcut icon(Bitmap icon) {
if (mIconRes != null) {
throw new IllegalStateException("Cannot set both iconRes and icon");
throw new IllegalStateException(mContext.getString(R.string.error_set_both_icon_res_and_icon));
}
if (icon.getByteCount() > 1024 * 500) {
mIcon = BitmapTool.scaleBitmap(icon, 200, 200);

View File

@@ -2,6 +2,10 @@ package org.autojs.autojs.model.explorer;
import androidx.annotation.NonNull;
import com.stardust.app.GlobalAppContext;
import org.autojs.autojs6.R;
public class ExplorerChangeEvent {
@@ -73,6 +77,6 @@ public class ExplorerChangeEvent {
case CHILDREN_CHANGE:
return "CHILDREN_CHANGE";
}
throw new IllegalArgumentException("action = " + action);
throw new IllegalArgumentException(GlobalAppContext.getString(R.string.error_illegal_argument, "action", action));
}
}

View File

@@ -2,11 +2,12 @@ package org.autojs.autojs.model.explorer;
import androidx.annotation.NonNull;
import com.stardust.app.GlobalAppContext;
import com.stardust.pio.PFile;
import com.stardust.util.ObjectHelper;
import com.stardust.util.Objects;
import org.autojs.autojs.model.script.ScriptFile;
import org.autojs.autojs6.R;
import java.io.File;
import java.util.Arrays;
@@ -24,7 +25,11 @@ public class ExplorerFileItem implements ExplorerItem {
private final ExplorerPage mParent;
public ExplorerFileItem(PFile file, ExplorerPage parent) {
ObjectHelper.requireNonNull(file, "file");
if (file == null) {
throw new NullPointerException(GlobalAppContext
.getString(R.string.error_method_called_with_null_argument,
"ExplorerFileItem.constructor", "file"));
}
mFile = file;
mParent = parent;
}

View File

@@ -0,0 +1,597 @@
package org.autojs.autojs.network;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.text.Html;
import android.text.Spanned;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.afollestad.materialdialogs.internal.MDButton;
import com.google.android.material.snackbar.BaseTransientBottomBar;
import com.google.gson.GsonBuilder;
import com.stardust.app.GlobalAppContext;
import com.stardust.autojs.core.ui.widget.CustomSnackbar;
import com.stardust.util.IntentUtil;
import org.autojs.autojs.Pref;
import org.autojs.autojs.external.fileprovider.AppFileProvider;
import org.autojs.autojs.network.api.UpdateCheckerApi;
import org.autojs.autojs.network.download.DownloadManager;
import org.autojs.autojs.network.entity.VersionInfo;
import org.autojs.autojs.tool.SimpleObserver;
import org.autojs.autojs6.R;
import org.kohsuke.github.GHAsset;
import org.kohsuke.github.GHRelease;
import org.kohsuke.github.GHRepository;
import org.kohsuke.github.GHUser;
import org.kohsuke.github.GitHub;
import org.kohsuke.github.MarkdownMode;
import org.kohsuke.github.PagedIterable;
import java.io.BufferedReader;
import java.io.File;
import java.io.Reader;
import java.util.Properties;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
import okhttp3.ResponseBody;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* Created by Stardust on 2017/9/20.
* Modified by SuperMonster003 as of Feb 26, 2022.
*/
public class UpdateChecker {
private MaterialDialog mUpdateDialog;
private MaterialDialog mPendingDialog;
public enum PromptMode {NONE, DIALOG, SNACKBAR}
private static final String TAG = UpdateChecker.class.getSimpleName();
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final Context mContext;
private final View mView;
private final String mBaseUrl;
private final String mUrl;
private final PromptMode mPromptMode;
private final SimpleObserver<ResponseBody> mCallback;
private final Executor mGitHubExecutor = Executors.newSingleThreadExecutor();
private GitHub mGitHubConnection;
private UpdateCheckerApi checkerApi;
private UpdateChecker(Context context, View view, String baseUrl, String url, PromptMode promptMode, SimpleObserver<ResponseBody> callback) {
mContext = context;
mView = view;
mBaseUrl = baseUrl;
mUrl = url;
mPromptMode = promptMode;
mCallback = callback;
}
public void checkNow() {
mPendingDialog = getPendingDialog(mContext, R.string.text_checking_update);
if (mPromptMode == PromptMode.DIALOG) {
mPendingDialog.show();
}
getCheckerApi()
.checkForUpdates(mUrl)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SimpleObserver<>() {
@Override
public void onNext(ResponseBody responseBody) {
if (mPendingDialog.isShowing()) {
mPendingDialog.dismiss();
}
Pref.refreshLastUpdatesCheckedTimestamp();
if (mCallback != null) {
mCallback.onNext(responseBody);
return;
}
try {
Properties prop = new Properties();
prop.load(responseBody.byteStream());
String versionNameKey = mContext.getString(R.string.key_app_property_version_name);
String versionName = prop.getProperty(versionNameKey);
String versionCodeKey = mContext.getString(R.string.key_app_property_version_code);
int versionCode = Integer.parseInt(prop.getProperty(versionCodeKey));
VersionInfo versionInfo = new VersionInfo(versionName, versionCode);
switch (mPromptMode) {
case DIALOG -> {
if (versionInfo.isNewer()) {
showDialog(versionInfo);
} else {
GlobalAppContext.toast(R.string.text_is_latest_version);
}
}
case SNACKBAR -> {
if (versionInfo.isNewer()) {
showSnackBar(versionInfo);
}
}
default -> throw new IllegalStateException(mContext.getString(
R.string.error_illegal_argument,
"promptMode", mPromptMode));
}
} catch (Exception e) {
e.printStackTrace();
GlobalAppContext.toast(mContext.getString(R.string.error_failed_to_parse_version_info));
}
}
@Override
public void onError(@NonNull Throwable e) {
e.printStackTrace();
if (mPendingDialog.isShowing()) {
mPendingDialog.dismiss();
}
if (mCallback != null) {
mCallback.onError(e);
return;
}
if (mPromptMode == PromptMode.DIALOG) {
new MaterialDialog.Builder(mContext)
.title(R.string.error_check_for_update)
.content(e.toString())
.positiveText(R.string.text_cancel)
.canceledOnTouchOutside(false)
.build()
.show();
}
}
});
}
private void showDialog(@NonNull VersionInfo versionInfo) {
showDialog(versionInfo, null);
}
private void showDialog(@NonNull VersionInfo versionInfo, Context context) {
// TODO by SuperMonster003 on May 30, 2022.
// ! A. Updates ignorance (and settings).
// ! B. VersionInfo: *.properties / *.json.
Context ctx = context != null ? context : mContext;
String propVersion = versionInfo.getVersionName();
if (propVersion == null) {
new MaterialDialog.Builder(ctx)
.title(R.string.error_check_for_update)
.content(R.string.error_parse_version_info)
.positiveText(R.string.dialog_button_back)
.build()
.show();
return;
}
mUpdateDialog = new MaterialDialog.Builder(ctx)
.title(propVersion)
.content(R.string.text_getting_release_notes)
.neutralText(R.string.dialog_button_ignore_current_update)
.neutralColor(ctx.getColor(R.color.dialog_button_warn))
.negativeText(R.string.dialog_button_back)
.negativeColor(ctx.getColor(R.color.dialog_button_default))
.positiveText(R.string.dialog_button_update_now)
.positiveColor(ctx.getColor(R.color.dialog_button_unavailable))
.autoDismiss(false)
.cancelable(false)
.build();
mPendingDialog = getPendingDialog(ctx, R.string.text_preparing);
MDButton neutralButton = mUpdateDialog.getActionButton(DialogAction.NEUTRAL);
neutralButton.setOnClickListener(v -> {
// TODO by SuperMonster003 on May 30, 2022.
// ! Updates ignorance.
new MaterialDialog.Builder(ctx)
.title(R.string.text_prompt)
.content(R.string.text_under_development_content)
.positiveText(R.string.dialog_button_back)
.build()
.show();
});
MDButton negativeButton = mUpdateDialog.getActionButton(DialogAction.NEGATIVE);
negativeButton.setOnClickListener(v -> mUpdateDialog.dismiss());
MDButton positiveButton = mUpdateDialog.getActionButton(DialogAction.POSITIVE);
positiveButton.setOnClickListener(null);
mUpdateDialog.show();
// TODO by SuperMonster003 on May 31, 2022.
// ! Android 7.x (N and N_MR1) compatibility.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
mUpdateDialog.dismiss();
new MaterialDialog.Builder(ctx)
.title(R.string.error_check_for_update)
.content(R.string.error_sdk_lower_than_o_not_supported_yet)
.positiveText(R.string.text_cancel)
.cancelable(false)
.build()
.show();
return;
}
mGitHubExecutor.execute(() -> {
GitHub github = connectToGitHubIfNeeded();
if (github == null) {
setDialogContent(mUpdateDialog, R.string.error_cannot_connect_to_github);
return;
}
String userName = ctx.getString(R.string.developer_full_name);
String repoName = ctx.getString(R.string.app_name);
GHRepository repo = getGitHubRepo(github, userName, repoName);
if (repo == null) {
setDialogContent(mUpdateDialog, ctx.getString(R.string.error_invalid_github_repo, repoName));
return;
}
GHRelease release = getGitHubRelease(repo);
if (release == null) {
setDialogContent(mUpdateDialog, R.string.error_get_github_latest_release);
return;
}
String releaseTag = release.getTagName();
if (!checkReleaseTagMatchesProp(releaseTag, propVersion)) {
setDialogContent(mUpdateDialog, R.string.error_corresponding_github_release_may_not_published);
return;
}
String rawHtmlContent = getRawHtmlFromRelease(repo, release);
Spanned htmlContent = Html.fromHtml(rawHtmlContent, Html.FROM_HTML_MODE_COMPACT);
if (htmlContent.toString().isEmpty()) {
setDialogContent(mUpdateDialog, R.string.text_empty_release_note);
} else {
setDialogContent(mUpdateDialog, htmlContent);
}
PagedIterable<GHAsset> assets = getGitHubAssets(release);
if (assets == null) {
setDialogContent(mUpdateDialog, R.string.error_empty_github_release_assets);
return;
}
setDialogUpdateButton(ctx, assets, versionInfo);
});
}
@Nullable
private PagedIterable<GHAsset> getGitHubAssets(GHRelease release) {
try {
PagedIterable<GHAsset> assets = release.listAssets();
if (assets.toArray().length > 0) {
return assets;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@SuppressWarnings("ResultOfMethodCallIgnored")
@SuppressLint("CheckResult")
private void download(Context ctx, String downloadUrl, String fileName, VersionInfo versionInfo) {
File downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
final String path = new File(downloadDir, fileName).getPath();
Consumer<File> onNext = file -> {
mUpdateDialog = null;
IntentUtil.installApkOrToast(ctx, file.getPath(), AppFileProvider.AUTHORITY);
};
Consumer<Throwable> onError = e -> {
Log.d(TAG, "onError: printing stack trace");
e.printStackTrace();
String msg = e.getMessage();
MaterialDialog d = new MaterialDialog.Builder(ctx)
.title(R.string.text_download_failed)
.content(msg == null ? ctx.getString(R.string.error_unknown) : msg)
.negativeText(R.string.text_cancel)
.negativeColor(ctx.getColor(R.color.dialog_button_default))
.onNegative((dialog, which) -> {
dialog.dismiss();
mUpdateDialog = null;
})
.positiveText("")
.positiveColor(ctx.getColor(R.color.dialog_button_failure))
.autoDismiss(false)
.cancelable(false)
.build();
if (mPendingDialog != null) {
mPendingDialog.dismiss();
}
if (mUpdateDialog != null) {
d.getActionButton(DialogAction.NEGATIVE).setText(R.string.dialog_button_quit);
d.getActionButton(DialogAction.NEGATIVE).setTextColor(ctx.getColor(R.color.dialog_button_caution));
d.getActionButton(DialogAction.POSITIVE).setText(R.string.dialog_button_retry);
d.getActionButton(DialogAction.POSITIVE).setOnClickListener(v -> {
d.dismiss();
mUpdateDialog.show();
});
}
d.show();
};
DownloadManager.getInstance().downloadWithProgress(ctx, downloadUrl, path, versionInfo)
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(onNext, onError);
}
private void setDialogUpdateButton(@NonNull Context ctx, PagedIterable<GHAsset> ghAssets, VersionInfo versionInfo) {
MDButton positiveButton = mUpdateDialog.getActionButton(DialogAction.POSITIVE);
positiveButton.setTextColor(ctx.getColor(R.color.dialog_button_attraction));
positiveButton.setOnClickListener(v -> {
if (!mUpdateDialog.isShowing()) {
return;
}
mUpdateDialog.dismiss();
mPendingDialog.show();
mGitHubExecutor.execute(() -> {
String[] abiList = Build.SUPPORTED_ABIS;
String abiBackup = "universal";
String url = null;
String urlBackup = null;
String fileName = null;
String fileNameBackup = null;
try {
assets:
for (GHAsset ghAsset : ghAssets) {
String assetName = ghAsset.getName();
String assetUrl = ghAsset.getBrowserDownloadUrl();
for (String abi : abiList) {
String regex = ".*\\b" + abi + "\\b.*";
if (assetName.matches(regex)) {
url = assetUrl;
fileName = assetName;
break assets;
}
}
if (assetName.contains(abiBackup)) {
urlBackup = assetUrl;
fileNameBackup = assetName;
}
}
} catch (Exception ignore) {
// Ignored.
}
mPendingDialog.dismiss();
if (url == null && urlBackup == null) {
mHandler.post(() -> new MaterialDialog.Builder(ctx)
.content(R.string.error_cannot_get_download_url)
.positiveText(R.string.text_cancel)
.positiveColor(ctx.getColor(R.color.dialog_button_default))
.canceledOnTouchOutside(false)
.build()
.show());
return;
}
if (url != null) {
downloadWithHandler(ctx, url, fileName, versionInfo);
} else {
GlobalAppContext.toast(R.string.text_github_backup_url_used, Toast.LENGTH_LONG);
downloadWithHandler(ctx, urlBackup, fileNameBackup, versionInfo);
}
});
});
}
private void downloadWithHandler(Context ctx, String url, String fileName, VersionInfo versionInfo) {
mHandler.post(() -> download(ctx, url, fileName, versionInfo));
}
private String getRawHtmlFromRelease(GHRepository repo, GHRelease release) {
Reader reader;
try {
reader = repo.renderMarkdown(release.getBody(), MarkdownMode.MARKDOWN);
} catch (Exception e) {
e.printStackTrace();
return null;
}
StringBuilder textBuilder = new StringBuilder();
try (Reader r = new BufferedReader(reader)) {
int c;
while ((c = r.read()) != -1) {
textBuilder.append((char) c);
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
return textBuilder.toString();
}
private GHRelease getGitHubRelease(GHRepository repo) {
try {
return repo.getLatestRelease();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private GHRepository getGitHubRepo(GitHub github, String userName, String repoName) {
try {
GHUser user = github.getUser(userName);
return user.getRepository(repoName);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private boolean checkReleaseTagMatchesProp(String releaseTag, String propVersion) {
return releaseTag.endsWith(propVersion);
}
private void setDialogContent(MaterialDialog dialog, String content) {
mHandler.post(() -> dialog.setContent(content));
}
private void setDialogContent(MaterialDialog dialog, Spanned content) {
mHandler.post(() -> dialog.setContent(content));
}
private void setDialogContent(MaterialDialog dialog, int resId) {
mHandler.post(() -> dialog.setContent(resId));
}
private GitHub connectToGitHubIfNeeded() {
try {
if (mGitHubConnection == null) {
// FIXME by SuperMonster003 on May 31, 2022.
// ! Given that java.time package was added only in Android O (API 26),
// ! API 24 and 25 will throw an java.lang.NoClassDefFoundError here.
mGitHubConnection = GitHub.connectAnonymously();
}
return mGitHubConnection;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private void showSnackBar(@NonNull VersionInfo versionInfo) {
String content = mContext.getString(R.string.text_new_version_found) + ": " + versionInfo.getVersionName();
CustomSnackbar snackbar = CustomSnackbar.make(mView, content, BaseTransientBottomBar.LENGTH_INDEFINITE);
snackbar.setAnimationMode(BaseTransientBottomBar.ANIMATION_MODE_SLIDE)
.setActionOne(R.string.text_updates_snack_bar_act_view, v -> showDialog(versionInfo, mView.getContext()))
.setActionTwo(R.string.text_updates_snack_bar_act_later, null)
.show();
}
private MaterialDialog getPendingDialog(Context context, int content) {
// TODO by SuperMonster003 on May 29, 2022.
// ! Add a "CANCEL" button for interruption.
// ! Concurrent programming may be needed.
return new MaterialDialog.Builder(context)
.content(content)
.autoDismiss(false)
.cancelable(false)
.build();
}
public static class Builder {
private final Context mContext;
private View mView;
private String mBaseUrl;
private String mUrl;
private PromptMode mPromptMode = PromptMode.NONE;
private SimpleObserver<ResponseBody> mCallback;
public Builder() {
this(GlobalAppContext.get());
}
public Builder(Context context) {
mContext = context;
}
public Builder(View view) {
this();
mView = view;
}
public Builder setBaseUrl(String baseUrl) {
this.mBaseUrl = baseUrl;
return this;
}
public Builder setUrl(String url) {
this.mUrl = url;
return this;
}
public Builder setPromptMode(PromptMode promptMode) {
this.mPromptMode = promptMode;
return this;
}
public Builder setCallback(SimpleObserver<ResponseBody> callback) {
this.mCallback = callback;
return this;
}
public UpdateChecker build() {
ensureUrl();
return new UpdateChecker(mContext, mView, mBaseUrl, mUrl, mPromptMode, mCallback);
}
private void ensureUrl() {
String regexUrl = "^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]";
if (mBaseUrl != null) {
if (!mBaseUrl.matches(regexUrl)) {
System.out.println("Base URL: " + mBaseUrl);
throw new IllegalArgumentException(mContext.getString(R.string.error_illegal_url_argument));
}
} else {
if (!mUrl.matches(regexUrl)) {
System.out.println("URL: " + mUrl);
throw new IllegalArgumentException(mContext.getString(R.string.error_illegal_relative_url_argument_without_base));
}
}
}
}
@NonNull
private UpdateCheckerApi getCheckerApi() {
if (checkerApi == null) {
checkerApi = new Retrofit.Builder()
.baseUrl(mBaseUrl)
.addConverterFactory(GsonConverterFactory.create(new GsonBuilder().setLenient().create()))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
.create(UpdateCheckerApi.class);
}
return checkerApi;
}
}

View File

@@ -1,45 +0,0 @@
package org.autojs.autojs.network;
import androidx.annotation.NonNull;
import com.google.gson.GsonBuilder;
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import org.autojs.autojs.network.api.UpdateCheckApi;
import org.autojs.autojs.network.entity.VersionInfo;
import io.reactivex.Observable;
import io.reactivex.schedulers.Schedulers;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* Created by Stardust on 2017/9/20.
* Modified by SuperMonster003 as of Feb 26, 2022.
*/
public class VersionService {
private static final String baseUrl = "https://raw.githubusercontent.com/";
private static Retrofit mRetrofit;
public static Observable<VersionInfo> checkForUpdates() {
Retrofit mRetrofit = getRetrofit();
return mRetrofit.create(UpdateCheckApi.class)
.checkForUpdates()
.subscribeOn(Schedulers.io());
}
@NonNull
private static Retrofit getRetrofit() {
if (mRetrofit == null) {
mRetrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create(new GsonBuilder().setLenient().create()))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
return mRetrofit;
}
}

View File

@@ -1,19 +1,23 @@
package org.autojs.autojs.network.api;
import org.autojs.autojs.network.entity.VersionInfo;
import java.io.InputStream;
import io.reactivex.Observable;
import okhttp3.ResponseBody;
import retrofit2.http.GET;
import retrofit2.http.Headers;
import retrofit2.http.Streaming;
import retrofit2.http.Url;
/**
* Created by Stardust on 2017/9/20.
* Modified by SuperMonster003 as of Feb 28, 2022.
*/
public interface UpdateCheckApi {
public interface UpdateCheckerApi {
@GET("/SuperMonster003/AutoJs6/master/project-versions.json")
@Streaming
@GET()
@Headers("Cache-Control: no-cache")
Observable<VersionInfo> checkForUpdates();
Observable<ResponseBody> checkForUpdates(@Url String url);
}

View File

@@ -1,19 +1,25 @@
package org.autojs.autojs.network.download;
import com.stardust.app.GlobalAppContext;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.ColorStateList;
import android.util.Log;
import android.widget.ProgressBar;
import androidx.annotation.Nullable;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import com.afollestad.materialdialogs.internal.MDButton;
import com.stardust.app.GlobalAppContext;
import com.stardust.concurrent.VolatileBox;
import com.stardust.pio.PFiles;
import org.autojs.autojs6.R;
import org.autojs.autojs.network.api.DownloadApi;
import org.autojs.autojs.network.entity.VersionInfo;
import org.autojs.autojs.tool.SimpleObserver;
import org.autojs.autojs.tool.UpdateUtils;
import org.autojs.autojs6.R;
import java.io.File;
import java.io.FileOutputStream;
@@ -21,17 +27,21 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import io.reactivex.subjects.PublishSubject;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
/**
* Created by Stardust on 2017/10/20.
@@ -40,32 +50,38 @@ public class DownloadManager {
private static final String LOG_TAG = "DownloadManager";
private static DownloadManager sInstance;
private static final int RETRY_COUNT = 3;
private final DownloadApi mDownloadApi;
private final ConcurrentHashMap<String, VolatileBox<Boolean>> mDownloadStatuses = new ConcurrentHashMap<>();
private Disposable mDisposable;
public DownloadManager() {
Retrofit mRetrofit = new Retrofit.Builder()
.baseUrl("https://www.autojs.org")
.baseUrl(UpdateUtils.BASE_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(new OkHttpClient.Builder()
.addInterceptor(chain -> {
Request request = chain.request();
Response response = chain.proceed(request);
int tryCount = 0;
while (!response.isSuccessful() && tryCount < RETRY_COUNT) {
tryCount++;
response = chain.proceed(request);
}
return response;
})
.build()
)
.client(getOkHttpClient())
.build();
mDownloadApi = mRetrofit.create(DownloadApi.class);
}
private OkHttpClient getOkHttpClient() {
Interceptor interceptor = chain -> {
Request request = chain.request();
Response response = chain.proceed(request);
int tryCount = 0;
while (!response.isSuccessful() && tryCount < RETRY_COUNT) {
tryCount++;
response = chain.proceed(request);
}
return response;
};
return new OkHttpClient.Builder()
.addInterceptor(interceptor)
.build();
}
public static DownloadManager getInstance() {
if (sInstance == null) {
@@ -74,6 +90,11 @@ public class DownloadManager {
return sInstance;
}
public void disposeIfNeeded() {
if (mDisposable != null) {
mDisposable.dispose();
}
}
public static String parseFileNameLocally(String url) {
int i = url.lastIndexOf('-');
@@ -89,7 +110,7 @@ public class DownloadManager {
@SuppressWarnings("ResultOfMethodCallIgnored")
@SuppressLint("CheckResult")
public Observable<Integer> download(String url, String path) {
public Observable<ProgressInfo> download(String url, String path) {
DownloadTask task = new DownloadTask(url, path);
mDownloadApi.download(url)
.subscribeOn(Schedulers.io())
@@ -98,26 +119,73 @@ public class DownloadManager {
}
public Observable<File> downloadWithProgress(Context context, String url, String path) {
String fileName = DownloadManager.parseFileNameLocally(url);
return download(url, path, createDownloadProgressDialog(context, url, fileName));
String content = context.getString(R.string.text_file_name) + ": " + DownloadManager.parseFileNameLocally(url);
return downloadWithProgress(context, url, path, content);
}
private MaterialDialog createDownloadProgressDialog(Context context, String url, String fileName) {
return new MaterialDialog.Builder(context)
.progress(false, 100)
.title(fileName)
public Observable<File> downloadWithProgress(Context context, String url, String path, VersionInfo versionInfo) {
return download(context, url, path, createDownloadProgressDialog(context, url, versionInfo, null));
}
public Observable<File> downloadWithProgress(Context context, String url, String path, String content) {
return download(context, url, path, createDownloadProgressDialog(context, url, null, content));
}
private MaterialDialog createDownloadProgressDialog(Context context, String url, @Nullable VersionInfo versionInfo, @Nullable String content) {
MaterialDialog d = new MaterialDialog.Builder(context)
.title(context.getString(R.string.text_downloading))
.positiveText(R.string.dialog_button_cancel_download)
.onPositive((dialog, which) -> {
dialog.dismiss();
DownloadManager.getInstance().cancelDownload(url);
})
.progress(false, 100, true)
.cancelable(false)
.positiveText(R.string.text_cancel_download)
.onPositive((dialog, which) -> DownloadManager.getInstance().cancelDownload(url))
.autoDismiss(false)
.show();
String contentText = versionInfo != null ? versionInfo.toString() : content;
if (contentText != null) {
d.setContent(contentText);
}
d.setProgressNumberFormat(context.getString(R.string.text_half_ellipsis));
ProgressBar progressBar = d.getProgressBar();
progressBar.setProgressTintList(ColorStateList.valueOf(context.getColor(R.color.dialog_progress_download_tint)));
progressBar.setProgressBackgroundTintList(ColorStateList.valueOf(context.getColor(R.color.dialog_progress_download_bg_tint)));
MDButton positiveButton = d.getActionButton(DialogAction.POSITIVE);
positiveButton.setTextColor(context.getColor(R.color.dialog_progress_download_act_btn));
return d;
}
private Observable<File> download(String url, String path, MaterialDialog progressDialog) {
private Observable<File> download(Context context, String url, String path, MaterialDialog progressDialog) {
PublishSubject<File> subject = PublishSubject.create();
DownloadManager.getInstance().download(url, path)
DownloadManager downloadMgr = DownloadManager.getInstance();
downloadMgr.download(url, path)
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(progressDialog::setProgress)
.doOnNext(o -> {
// 10,000 KB (around but less than 10 MB)
int megaThreshold = 10000 * (1 << 10);
if (o.getTotalBytes() > megaThreshold) {
progressDialog.setProgressNumberFormat(String.format(Locale.getDefault(),
context.getString(R.string.format_dialog_progress_number_format_mega_bytes),
o.getReadMegaBytes(), o.getTotalMegaBytes()));
} else {
progressDialog.setProgressNumberFormat(String.format(Locale.getDefault(),
context.getString(R.string.format_dialog_progress_number_format_kilo_bytes),
o.getReadKiloBytes(), o.getTotalKiloBytes()));
}
progressDialog.setProgress(o.getProgress());
})
.subscribe(new SimpleObserver<>() {
@Override
public void onSubscribe(Disposable disposable) {
mDisposable = disposable;
}
@Override
public void onComplete() {
progressDialog.dismiss();
@@ -129,6 +197,8 @@ public class DownloadManager {
public void onError(Throwable error) {
Log.e(LOG_TAG, "Download failed", error);
progressDialog.dismiss();
disposeIfNeeded();
getOkHttpClient().dispatcher().cancelAll();
subject.onError(error);
}
});
@@ -142,6 +212,11 @@ public class DownloadManager {
}
}
public boolean isCancelled(String url) {
VolatileBox<Boolean> status = mDownloadStatuses.get(url);
return status != null && !status.get();
}
private class DownloadTask {
private final String mUrl;
@@ -149,24 +224,27 @@ public class DownloadManager {
private final VolatileBox<Boolean> mStatus;
private InputStream mInputStream;
private FileOutputStream mFileOutputStream;
private final PublishSubject<Integer> mProgress;
private final PublishSubject<ProgressInfo> mProgress;
public DownloadTask(String url, String path) {
mUrl = url;
mPath = path;
mStatus = new VolatileBox<>(true);
VolatileBox<Boolean> previous = mDownloadStatuses.put(mUrl, mStatus);
if (previous != null)
if (previous != null) {
previous.set(false);
}
mProgress = PublishSubject.create();
}
private void startImpl(ResponseBody body) throws IOException {
private void startImpl(ResponseBody body) throws Exception {
byte[] buffer = new byte[4096];
mFileOutputStream = new FileOutputStream(mPath);
mInputStream = body.byteStream();
long total = body.contentLength();
long read = 0;
ProgressInfo o = new ProgressInfo(total);
while (true) {
if (!mStatus.get()) {
onCancel();
@@ -176,52 +254,51 @@ public class DownloadManager {
if (len == -1) {
break;
}
read += len;
o.incrementRead(len);
mFileOutputStream.write(buffer, 0, len);
if (total > 0) {
mProgress.onNext((int) (100 * read / total));
if (o.getTotalBytes() > 0) {
mProgress.onNext(o);
}
}
mProgress.onComplete();
recycle();
}
public void start(ResponseBody body) {
try {
PFiles.ensureDir(mPath);
startImpl(body);
} catch (Exception e) {
mProgress.onError(e);
}
public void start(ResponseBody body) throws Exception {
PFiles.ensureDir(mPath);
startImpl(body);
}
private void onCancel() {
GlobalAppContext.toast(R.string.text_download_cancelled);
recycle();
// TODO: 2017/12/6 notify?
}
public void recycle() {
// FIXME by SuperMonster003 on May 31, 2022.
// ! Seems like none of the ways below could stop the downloading process.
// ! Even worse, progress may stuck at around 99% and suspend.
// disposeIfNeeded();
// getOkHttpClient().dispatcher().cancelAll();
// body.close();
mDownloadStatuses.remove(mUrl);
if (mInputStream != null) {
try {
mInputStream.close();
} catch (IOException ignored) {
}
try {
mInputStream.close();
} catch (IOException ignored) {
// Ignored.
}
if (mFileOutputStream != null) {
try {
mFileOutputStream.close();
} catch (IOException ignored) {
}
try {
mFileOutputStream.close();
} catch (IOException ignored) {
// Ignored.
}
}
public PublishSubject<Integer> progress() {
public PublishSubject<ProgressInfo> progress() {
return mProgress;
}
}
}

View File

@@ -0,0 +1,58 @@
package org.autojs.autojs.network.download;
/**
* Created by SuperMonster003 on May 30, 2022.
*/
public class ProgressInfo {
private long mRead = 0;
private long mTotal;
public ProgressInfo(long contentLength) {
mTotal = contentLength;
}
public long getTotalBytes() {
return mTotal;
}
public float getTotalKiloBytes() {
return (float) (mTotal / Math.pow(2, 10));
}
public float getTotalMegaBytes() {
return (float) (mTotal / Math.pow(2, 20));
}
public void setTotal(long total) {
mTotal = total;
}
public void incrementTotal(long i) {
mTotal += i;
}
public long getReadBytes() {
return mRead;
}
public float getReadKiloBytes() {
return (float) (mRead / Math.pow(2, 10));
}
public float getReadMegaBytes() {
return (float) (mRead / Math.pow(2, 20));
}
public void setRead(long read) {
mRead = read;
}
public void incrementRead(long i) {
mRead += i;
}
public int getProgress() {
return (int) (mRead * 100 / mTotal);
}
}

View File

@@ -2,27 +2,63 @@ package org.autojs.autojs.network.entity;
import androidx.annotation.NonNull;
import com.stardust.app.GlobalAppContext;
import org.autojs.autojs6.BuildConfig;
import org.autojs.autojs6.R;
import java.text.MessageFormat;
/**
* Created by Stardust on 2017/9/20.
* Modified by SuperMonster003 as of May 29, 2022.
*/
public class VersionInfo {
public int appVersionCode;
public String appVersionName;
private String mVersionName;
private int mVersionCode;
public VersionInfo(@NonNull String propertiesFileRawString) {
String regexVersionName = "VERSION_NAME=.+";
String regexVersionCode = "VERSION_BUILD=.+";
for (String string : propertiesFileRawString.split("\n")) {
if (string.matches(regexVersionName)) {
mVersionName = string.split("=")[1];
} else if (string.matches(regexVersionCode)) {
mVersionCode = Integer.parseInt(string.split("=")[1]);
}
if (mVersionName != null && mVersionCode > 0) {
break;
}
}
}
public VersionInfo(@NonNull String versionName, int versionCode) {
mVersionName = versionName;
mVersionCode = versionCode;
}
public boolean isNewer() {
return appVersionCode > BuildConfig.VERSION_CODE;
return mVersionCode > BuildConfig.VERSION_CODE;
}
public boolean isNotIgnored() {
// TODO by SuperMonster003 on May 30, 2022.
return true;
}
@NonNull
@Override
public String toString() {
return "UpdateInfo{" +
"appVersionCode=" + appVersionCode +
", appVersionName='" + appVersionName + '\'' +
'}';
return MessageFormat.format("{0}: {1} ({2})", GlobalAppContext.getString(R.string.text_version), mVersionName, mVersionCode);
}
public String getVersionName() {
return mVersionName;
}
public int getVersionCode() {
return mVersionCode;
}
}

View File

@@ -21,6 +21,7 @@ import com.stardust.autojs.runtime.api.Device;
import com.stardust.util.MapBuilder;
import org.autojs.autojs6.BuildConfig;
import org.autojs.autojs6.R;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
@@ -167,7 +168,7 @@ abstract public class JsonSocket extends Socket {
} else if (value instanceof JsonElement) {
data.add(key, (JsonElement) value);
} else {
throw new IllegalArgumentException("cannot put value " + value + " into json");
throw new IllegalArgumentException(GlobalAppContext.getString(R.string.error_put_value_into_json, value));
}
}

View File

@@ -6,10 +6,12 @@ import android.content.Intent;
import androidx.annotation.NonNull;
import com.stardust.app.GlobalAppContext;
import com.stardust.autojs.execution.ExecutionConfig;
import org.autojs.autojs.external.ScriptIntents;
import org.autojs.autojs.storage.database.BaseModel;
import org.autojs.autojs6.R;
import org.joda.time.DateTime;
import org.joda.time.DateTimeConstants;
import org.joda.time.LocalDateTime;
@@ -100,7 +102,7 @@ public class TimedTask extends BaseModel {
dayOfWeek++;
nextTimeMillis += TimeUnit.DAYS.toMillis(1);
}
throw new IllegalStateException("Should not happen! timeFlag = " + mTimeFlag + ", dayOfWeek = " + DateTime.now().getDayOfWeek());
return -1;
}
public static long getDayOfWeekTimeFlag(int dayOfWeek) {
@@ -127,7 +129,7 @@ public class TimedTask extends BaseModel {
return FLAG_FRIDAY;
}
throw new IllegalArgumentException("dayOfWeek = " + dayOfWeek);
throw new IllegalArgumentException(GlobalAppContext.getString(R.string.error_illegal_argument, "dayOfWeek", dayOfWeek));
}
public long getMillis() {

View File

@@ -8,10 +8,16 @@ import org.autojs.autojs.ui.log.LogActivity_;
public class ConsoleTool {
public static void launch() {
LogActivity_.intent(GlobalAppContext.get())
.flags(Intent.FLAG_ACTIVITY_NEW_TASK)
.start();
public static boolean launch() {
try {
LogActivity_.intent(GlobalAppContext.get())
.flags(Intent.FLAG_ACTIVITY_NEW_TASK)
.start();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}

View File

@@ -0,0 +1,59 @@
package org.autojs.autojs.tool;
import android.content.Context;
import android.view.View;
import com.stardust.autojs.annotation.ScriptInterface;
import org.autojs.autojs.network.UpdateChecker;
import org.autojs.autojs.network.UpdateChecker.PromptMode;
import okhttp3.ResponseBody;
/**
* Created by SuperMonster003 on May 29, 2022.
*/
public class UpdateUtils {
public static String BASE_URL = "https://raw.githubusercontent.com/";
public static String RELATIVE_URL = "/SuperMonster003/AutoJs6/master/version.properties";
public static String URL = BASE_URL + RELATIVE_URL.substring(1);
@ScriptInterface
public static UpdateChecker getDialogChecker(Context context, String url, SimpleObserver<ResponseBody> callback) {
return getBuilder(context, url, callback)
.setPromptMode(PromptMode.DIALOG)
.build();
}
public static UpdateChecker getDialogChecker(Context context) {
return getDialogChecker(context, null, null);
}
@ScriptInterface
public static UpdateChecker getSnackbarChecker(View view, String url, SimpleObserver<ResponseBody> callback) {
return getBuilder(view, url, callback)
.setPromptMode(PromptMode.SNACKBAR)
.build();
}
public static UpdateChecker getSnackbarChecker(View view) {
return getSnackbarChecker(view, null, null);
}
private static UpdateChecker.Builder getBuilder(Context context, String url, SimpleObserver<ResponseBody> callback) {
return new UpdateChecker.Builder(context)
.setBaseUrl(BASE_URL)
.setUrl(url != null ? url : RELATIVE_URL)
.setCallback(callback);
}
private static UpdateChecker.Builder getBuilder(View view, String url, SimpleObserver<ResponseBody> callback) {
return new UpdateChecker.Builder(view)
.setBaseUrl(BASE_URL)
.setUrl(url != null ? url : RELATIVE_URL)
.setCallback(callback);
}
}

View File

@@ -29,12 +29,12 @@ import com.stardust.pio.PFiles;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.ViewById;
import org.autojs.autojs6.R;
import org.autojs.autojs.storage.file.TmpScriptFiles;
import org.autojs.autojs.theme.dialog.ThemeColorMaterialDialogBuilder;
import org.autojs.autojs.tool.Observers;
import org.autojs.autojs.ui.BaseActivity;
import org.autojs.autojs.ui.main.MainActivity_;
import org.autojs.autojs6.R;
import java.io.File;
import java.io.IOException;
@@ -158,11 +158,29 @@ public class EditActivity extends BaseActivity implements OnActivityResultDelega
Log.d(LOG_TAG, "onActionModeStarted: " + mode);
Menu menu = mode.getMenu();
MenuItem item = menu.getItem(menu.size() - 1);
menu.add(item.getGroupId(), R.id.action_delete_line, 10000, R.string.text_delete_line);
menu.add(item.getGroupId(), R.id.action_copy_line, 20000, R.string.text_copy_line);
addMenuItem(menu, item.getGroupId(), R.id.action_delete_line, 10000, R.string.text_delete_line, () -> mEditorMenu.deleteLine());
addMenuItem(menu, item.getGroupId(), R.id.action_copy_line, 20000, R.string.text_copy_line, () -> mEditorMenu.copyLine());
super.onActionModeStarted(mode);
}
private void addMenuItem(Menu menu, int groupId, int itemId, int order, int titleRes, Runnable runnable) {
try {
menu.add(groupId, itemId, order, titleRes).setOnMenuItemClickListener(item -> {
try {
runnable.run();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
});
} catch (Exception e) {
// @Example android.content.res.Resources.NotFoundException
// ! on MIUI devices (maybe more)
e.printStackTrace();
}
}
@Override
public void onSupportActionModeStarted(@NonNull androidx.appcompat.view.ActionMode mode) {
Log.d(LOG_TAG, "onSupportActionModeStarted: mode = " + mode);
@@ -215,14 +233,17 @@ public class EditActivity extends BaseActivity implements OnActivityResultDelega
new ThemeColorMaterialDialogBuilder(this)
.title(R.string.text_prompt)
.content(R.string.edit_exit_without_save_warn)
.positiveText(R.string.text_cancel)
.negativeText(R.string.text_save_and_exit)
.neutralText(R.string.text_exit_directly)
.onNegative((dialog, which) -> {
.neutralText(R.string.text_back)
.neutralColor(getColor(R.color.dialog_button_default))
.negativeText(R.string.text_exit_directly)
.negativeColor(getColor(R.color.dialog_button_caution))
.positiveText(R.string.text_save_and_exit)
.positiveColor(getColor(R.color.dialog_button_hint))
.onNegative((dialog, which) -> finishAndRemoveFromRecents())
.onPositive((dialog, which) -> {
mEditorView.saveFile();
finishAndRemoveFromRecents();
})
.onNeutral((dialog, which) -> finishAndRemoveFromRecents())
.show();
}

View File

@@ -16,6 +16,7 @@ import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.autojs.script.JavaScriptSource;
import com.stardust.pio.PFiles;
import org.autojs.autojs.tool.ConsoleTool;
import org.autojs.autojs6.R;
import org.autojs.autojs.model.indices.AndroidClass;
import org.autojs.autojs.model.indices.ClassSearchingItem;
@@ -23,7 +24,6 @@ import org.autojs.autojs.ui.project.BuildActivity;
import org.autojs.autojs.ui.project.BuildActivity_;
import org.autojs.autojs.ui.common.NotAskAgainDialog;
import org.autojs.autojs.ui.edit.editor.CodeEditor;
import org.autojs.autojs.ui.log.LogActivity_;
import org.autojs.autojs.theme.dialog.ThemeColorMaterialDialogBuilder;
import com.stardust.util.ClipboardUtil;
@@ -54,12 +54,10 @@ public class EditorMenu {
public boolean onOptionsItemSelected(MenuItem item) {
int itemId = item.getItemId();
if (itemId == R.id.action_log) {
showLog();
return true;
return ConsoleTool.launch();
}
if (itemId == R.id.action_force_stop) {
forceStop();
return true;
return tryDoing(mEditorView::forceStop);
}
return onEditOptionsSelected(item)
|| onJumpOptionsSelected(item)
@@ -79,8 +77,7 @@ public class EditorMenu {
.content(R.string.hint_long_click_run_to_debug)
.positiveText(R.string.text_ok)
.show();
mEditorView.debug();
return true;
return tryDoing(mEditorView::debug);
}
if (itemId == R.id.action_remove_all_breakpoints) {
mEditor.removeAllBreakpoints();
@@ -118,24 +115,20 @@ public class EditorMenu {
private boolean onMoreOptionsSelected(MenuItem item) {
int itemId = item.getItemId();
if (itemId == R.id.action_console) {
showConsole();
return true;
return tryDoing(mEditorView::showConsole);
}
if (itemId == R.id.action_import_java_class) {
importJavaPackageOrClass();
return true;
}
if (itemId == R.id.action_editor_text_size) {
mEditorView.selectTextSize();
return true;
return tryDoing(mEditorView::selectTextSize);
}
if (itemId == R.id.action_editor_theme) {
mEditorView.selectEditorTheme();
return true;
return tryDoing(mEditorView::selectEditorTheme);
}
if (itemId == R.id.action_open_by_other_apps) {
openByOtherApps();
return true;
return tryDoing(mEditorView::openByOtherApps);
}
if (itemId == R.id.action_info) {
showInfo();
@@ -212,20 +205,19 @@ public class EditorMenu {
return true;
}
if (itemId == R.id.action_copy_line) {
copyLine();
return true;
return copyLine();
}
if (itemId == R.id.action_delete_line) {
deleteLine();
return true;
return deleteLine();
}
if (itemId == R.id.action_paste) {
return paste();
}
if (itemId == R.id.action_clear) {
mEditor.setText("");
return true;
return tryDoing(() -> mEditor.setText(""));
}
if (itemId == R.id.action_beautify) {
beautifyCode();
return true;
return tryDoing(mEditorView::beautifyCode);
}
return false;
}
@@ -255,11 +247,12 @@ public class EditorMenu {
}
private void showInfo() {
Observable.zip(Observable.just(mEditor.getText()), mEditor.getLineCount(), (text, lineCount) -> {
String size = PFiles.getHumanReadableSize(text.length());
return String.format(Locale.getDefault(), mContext.getString(R.string.format_editor_info),
text.length(), lineCount, size);
})
Observable
.zip(Observable.just(mEditor.getText()), mEditor.getLineCount(), (text, lineCount) -> {
String size = PFiles.getHumanReadableSize(text.length());
return String.format(Locale.getDefault(), mContext.getString(R.string.format_editor_info),
text.length(), lineCount, size);
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::showInfo);
@@ -272,20 +265,21 @@ public class EditorMenu {
.show();
}
private void copyLine() {
mEditor.copyLine();
protected boolean copyLine() {
return tryDoing(mEditor::copyLine);
}
private void deleteLine() {
mEditor.deleteLine();
protected boolean deleteLine() {
return tryDoing(mEditor::deleteLine);
}
private void paste() {
CharSequence clip = getClip();
if (clip != null) {
mEditor.insert(clip.toString());
}
protected boolean paste() {
return tryDoing(() -> {
CharSequence clip = getClip();
if (clip != null) {
mEditor.insert(clip.toString());
}
});
}
@Nullable
@@ -309,25 +303,14 @@ public class EditorMenu {
Snackbar.make(mEditorView, R.string.text_already_copied_to_clip, Snackbar.LENGTH_SHORT).show();
}
private void showLog() {
LogActivity_.intent(mContext).start();
}
private void showConsole() {
mEditorView.showConsole();
}
private void forceStop() {
mEditorView.forceStop();
}
private void openByOtherApps() {
mEditorView.openByOtherApps();
}
private void beautifyCode() {
mEditorView.beautifyCode();
private boolean tryDoing(Runnable callable) {
try {
callable.run();
return true;
} catch (Exception ignore) {
// Ignored.
}
return false;
}
}

View File

@@ -15,6 +15,8 @@ import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.Scroller;
import org.autojs.autojs6.R;
import java.util.List;
@@ -194,40 +196,34 @@ public class HVScrollView extends FrameLayout {
@Override
public void addView(View child) {
if (getChildCount() > 0) {
throw new IllegalStateException("ScrollView can host only one direct child");
}
ensureNotMoreThanOneChild();
super.addView(child);
}
@Override
public void addView(View child, int index) {
if (getChildCount() > 0) {
throw new IllegalStateException("ScrollView can host only one direct child");
}
ensureNotMoreThanOneChild();
super.addView(child, index);
}
@Override
public void addView(View child, ViewGroup.LayoutParams params) {
if (getChildCount() > 0) {
throw new IllegalStateException("ScrollView can host only one direct child");
}
ensureNotMoreThanOneChild();
super.addView(child, params);
}
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
if (getChildCount() > 0) {
throw new IllegalStateException("ScrollView can host only one direct child");
}
ensureNotMoreThanOneChild();
super.addView(child, index, params);
}
private void ensureNotMoreThanOneChild() {
if (getChildCount() > 0) {
throw new IllegalStateException(getContext().getString(R.string.error_only_one_child_for_scroll_view));
}
}
/**
* @return Returns true this ScrollView can be scrolled
*/

View File

@@ -23,7 +23,6 @@ import java.util.concurrent.atomic.AtomicInteger;
public class JavaScriptHighlighter implements SimpleTextWatcher.AfterTextChangedListener {
public static class HighlightTokens {
public final int[] colors;
@@ -90,7 +89,6 @@ public class JavaScriptHighlighter implements SimpleTextWatcher.AfterTextChanged
updateTokens(s.toString());
}
public void setTheme(Theme theme) {
mTheme = theme;
}
@@ -112,7 +110,6 @@ public class JavaScriptHighlighter implements SimpleTextWatcher.AfterTextChanged
} catch (IOException neverHappen) {
throw new UncheckedIOException(neverHappen);
}
});
}

View File

@@ -2,6 +2,8 @@ package org.autojs.autojs.ui.edit.toolbar;
import android.os.Bundle;
import android.os.Handler;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import android.text.TextUtils;
@@ -69,7 +71,7 @@ public class DebugToolbarFragment extends ToolbarFragment implements DebugCallba
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mEditorView = findEditorView(view);
mDebugger = DebuggerSingleton.get();

View File

@@ -1,6 +1,8 @@
package org.autojs.autojs.ui.edit.toolbar;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.view.View;
@@ -17,9 +19,12 @@ public class SearchToolbarFragment extends ToolbarFragment {
public static final String ARGUMENT_SHOW_REPLACE_ITEM = "show_replace_item";
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
boolean showReplaceItem = getArguments().getBoolean(ARGUMENT_SHOW_REPLACE_ITEM, false);
boolean showReplaceItem = false;
if (getArguments() != null) {
showReplaceItem = getArguments().getBoolean(ARGUMENT_SHOW_REPLACE_ITEM, false);
}
view.findViewById(R.id.replace).setVisibility(showReplaceItem ? View.VISIBLE : View.GONE);
}

View File

@@ -9,6 +9,7 @@ import androidx.fragment.app.Fragment;
import android.view.View;
import org.autojs.autojs.ui.edit.EditorView;
import org.autojs.autojs6.R;
import java.util.List;
@@ -48,7 +49,7 @@ public abstract class ToolbarFragment extends Fragment implements View.OnClickLi
view = (View) view.getParent();
}
if (!(view instanceof EditorView)) {
throw new IllegalStateException("cannot find EditorView from child: " + view);
throw new IllegalStateException(getString(R.string.error_cannot_find_editor_view_from_child_view, view));
}
return (EditorView) view;
}

View File

@@ -38,6 +38,7 @@ import com.heinrichreimersoftware.androidissuereporter.model.github.GithubLogin;
import com.heinrichreimersoftware.androidissuereporter.model.github.GithubTarget;
import com.heinrichreimersoftware.androidissuereporter.util.ColorUtils;
import com.heinrichreimersoftware.androidissuereporter.util.ThemeUtils;
import com.stardust.app.GlobalAppContext;
import com.stardust.theme.ThemeColorManager;
import org.autojs.autojs6.BuildConfig;
@@ -235,7 +236,8 @@ public abstract class AbstractIssueReporterActivity extends BaseActivity {
sendBugReport(new GithubLogin(username, password), null);
} else {
if (TextUtils.isEmpty(token))
throw new IllegalStateException("You must provide a GitHub API Token.");
throw new IllegalStateException(GlobalAppContext
.getString(org.autojs.autojs6.R.string.error_github_api_token_needed));
String email = null;
if (!TextUtils.isEmpty(inputEmail.getText()) &&

View File

@@ -48,9 +48,12 @@ import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Click;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.ViewById;
import org.autojs.autojs.Pref;
import org.autojs.autojs.autojs.AutoJs;
import org.autojs.autojs.external.foreground.MainActivityForegroundService;
import org.autojs.autojs.model.explorer.Explorers;
import org.autojs.autojs.network.UpdateChecker;
import org.autojs.autojs.tool.UpdateUtils;
import org.autojs.autojs.ui.BaseActivity;
import org.autojs.autojs.ui.common.NotAskAgainDialog;
import org.autojs.autojs.ui.doc.DocsFragment_;
@@ -101,6 +104,7 @@ public class MainActivity extends BaseActivity implements OnActivityResultDelega
@Override
protected void onPostResume() {
restartIfNeeded();
autoCheckForUpdatesIfNeeded();
super.onPostResume();
}
@@ -111,6 +115,19 @@ public class MainActivity extends BaseActivity implements OnActivityResultDelega
}
}
private void autoCheckForUpdatesIfNeeded() {
if (Pref.isAutoCheckForUpdatesEnabled()) {
long minCheckedInterval = 2 * 60 * 60 * 1000; // 2 hours
long lastChecked = Pref.getLastUpdatesCheckedTimestamp();
if (System.currentTimeMillis() - lastChecked > minCheckedInterval) {
View rootView = findViewById(android.R.id.content);
UpdateChecker checker = UpdateUtils.getSnackbarChecker(rootView);
checker.checkNow();
}
}
}
public void applyThemeColor() {
if (ThemeColor.fromPreferences(PreferenceManager.getDefaultSharedPreferences(this), null) == null) {
ThemeColor defaultThemeColor = new ThemeColor(

View File

@@ -189,7 +189,10 @@ public class DrawerFragment extends androidx.fragment.app.Fragment {
new DrawerMenuGroup(R.string.text_appearance),
new DrawerMenuItem(R.drawable.ic_night_mode, R.string.text_night_mode, R.string.key_night_mode, this::toggleNightMode),
new DrawerMenuItem(R.drawable.ic_personalize, R.string.text_theme_color, this::openThemeColorSettings)
new DrawerMenuItem(R.drawable.ic_personalize, R.string.text_theme_color, this::launchThemeColorSettings),
new DrawerMenuGroup(R.string.text_about),
new DrawerMenuItem(R.drawable.ic_about, R.string.text_about_app_and_developer, this::launchAboutAppAndDeveloper)
)));
mDrawerMenu.setAdapter(mDrawerMenuAdapter);
mDrawerMenu.setLayoutManager(new LinearLayoutManager(mContext));
@@ -266,7 +269,6 @@ public class DrawerFragment extends androidx.fragment.app.Fragment {
syncForegroundServiceState();
})
.onPositive((dialog, which) -> MainActivityForegroundService.start(mContext))
.canceledOnTouchOutside(false)
.cancelable(false)
.show();
}
@@ -307,7 +309,6 @@ public class DrawerFragment extends androidx.fragment.app.Fragment {
syncUsageStatsPermissionState();
})
.onPositive((dialog, which) -> requestAppUsagePermission())
.canceledOnTouchOutside(false)
.cancelable(false)
.show();
}
@@ -350,10 +351,14 @@ public class DrawerFragment extends androidx.fragment.app.Fragment {
return ((PowerManager) mContext.getSystemService(Context.POWER_SERVICE)).isIgnoringBatteryOptimizations(mPackageName);
}
public void openThemeColorSettings(DrawerMenuItemViewHolder holder) {
public void launchThemeColorSettings(DrawerMenuItemViewHolder holder) {
SettingsActivity.selectThemeColor(getActivity());
}
public void launchAboutAppAndDeveloper(DrawerMenuItemViewHolder holder) {
SettingsActivity.launchAboutAppAndDeveloper(requireActivity());
}
public void toggleNightMode(@NonNull DrawerMenuItemViewHolder holder) {
((BaseActivity) requireActivity()).setNightModeEnabled(holder.getSwitchCompat().isChecked());
}
@@ -402,7 +407,6 @@ public class DrawerFragment extends androidx.fragment.app.Fragment {
syncWriteSecuritySettingsState();
})
.onPositive((dialog, which) -> grantWriteSecureSettingsAccess())
.canceledOnTouchOutside(false)
.cancelable(false)
.show();
}
@@ -430,7 +434,6 @@ public class DrawerFragment extends androidx.fragment.app.Fragment {
syncProjectMediaAccessState();
})
.onPositive((dialog, which) -> grantProjectMediaAccess())
.canceledOnTouchOutside(false)
.cancelable(false)
.show();
}

View File

@@ -3,6 +3,7 @@ package org.autojs.autojs.ui.main.task;
import android.content.Context;
import com.bignerdranch.expandablerecyclerview.model.Parent;
import com.stardust.app.GlobalAppContext;
import com.stardust.autojs.execution.ScriptExecution;
import org.autojs.autojs6.R;
@@ -69,7 +70,7 @@ public abstract class TaskGroup implements Parent<Task> {
} else if (task instanceof IntentTask) {
mTasks.add(new Task.PendingTask((IntentTask) task));
} else {
throw new IllegalArgumentException("task = " + task);
throw new IllegalArgumentException(GlobalAppContext.getString(R.string.error_illegal_argument, "task", task));
}
return pos;
}
@@ -100,7 +101,7 @@ public abstract class TaskGroup implements Parent<Task> {
} else if (task instanceof IntentTask) {
((Task.PendingTask) mTasks.get(i)).setIntentTask((IntentTask) task);
} else {
throw new IllegalArgumentException("task = " + task);
throw new IllegalArgumentException(GlobalAppContext.getString(R.string.error_illegal_argument, "task", task));
}
}
return i;

View File

@@ -126,7 +126,7 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
}
private void downloadPlugin() {
IntentUtil.browse(this, "https://cdn.jsdelivr.net/gh/SuperMonster002/Hello-Sockpuppet@master/" +
IntentUtil.browse(this, "https://raw.githubusercontent.com/SuperMonster002/Hello-Sockpuppet/master/" +
"%5B" + "auto.js" + "%5D" +
"%5B" + "apk_builder_plugin_4.1.1_alpha2" + "%5D" +
"%5B" + "arm-v7a" + "%5D" +

View File

@@ -11,11 +11,11 @@ import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Click;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.ViewById;
import org.autojs.autojs.tool.UpdateUtils;
import org.autojs.autojs6.BuildConfig;
import org.autojs.autojs6.R;
import org.autojs.autojs.ui.BaseActivity;
import org.autojs.autojs.ui.error.IssueReporterActivity;
import org.autojs.autojs.ui.update.UpdateCheckDialog;
import de.psdev.licensesdialog.LicenseResolver;
import de.psdev.licensesdialog.LicensesDialog;
@@ -113,8 +113,8 @@ public class AboutActivity extends BaseActivity {
@SuppressLint("NonConstantResourceId")
@Click(R.id.about_functions_button_update)
void checkForUpdated() {
new UpdateCheckDialog(this).show();
void checkForUpdates() {
UpdateUtils.getDialogChecker(this).checkNow();
}
@SuppressLint("NonConstantResourceId")

View File

@@ -0,0 +1,60 @@
package org.autojs.autojs.ui.settings;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.Preference;
import android.util.AttributeSet;
import androidx.preference.PreferenceManager;
import org.autojs.autojs.Pref;
import org.autojs.autojs6.R;
/**
* Created by SuperMonster003 on May 31, 2022.
*/
public class CheckForUpdatesPreference extends Preference implements SharedPreferences.OnSharedPreferenceChangeListener {
public CheckForUpdatesPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
public CheckForUpdatesPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
SharedPreferences mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
mSharedPreferences.registerOnSharedPreferenceChangeListener(this);
}
public CheckForUpdatesPreference(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CheckForUpdatesPreference(Context context) {
super(context);
init();
}
@Override
protected void onAttachedToActivity() {
setSummaryIfNeeded();
super.onAttachedToActivity();
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
setSummaryIfNeeded();
}
private void setSummaryIfNeeded() {
String lastChecked = Pref.getLastUpdatesCheckedTimeString();
if (lastChecked != null) {
setSummary(getContext().getString(R.string.text_last_updates_checked_time, lastChecked));
}
}
}

View File

@@ -1,6 +1,7 @@
package org.autojs.autojs.ui.settings;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@@ -23,10 +24,10 @@ import com.stardust.util.MapBuilder;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.EActivity;
import org.autojs.autojs.Pref;
import org.autojs.autojs.tool.UpdateUtils;
import org.autojs.autojs6.R;
import org.autojs.autojs.ui.BaseActivity;
import org.autojs.autojs.ui.common.NotAskAgainDialog;
import org.autojs.autojs.ui.update.UpdateCheckDialog;
import java.util.ArrayList;
import java.util.Collection;
@@ -66,70 +67,6 @@ public class SettingsActivity extends BaseActivity {
.add(new Pair<>(R.color.theme_color_default, R.string.theme_color_default))
.list();
public static void selectThemeColor(Context context) {
List<ColorSelectActivity.ColorItem> colorItems = new ArrayList<>(COLOR_ITEMS.size());
for (Pair<Integer, Integer> item : COLOR_ITEMS) {
colorItems.add(new ColorSelectActivity.ColorItem(context.getString(item.second), ContextCompat.getColor(context, item.first)));
}
ColorSelectActivity.startColorSelect(context, context.getString(R.string.mt_color_picker_title), colorItems);
}
@NonNull
private static LinkedHashMap<String, Runnable> getAvailableLanguages(@NonNull Context context) {
LinkedHashMap<String, Runnable> map = new LinkedHashMap<>();
map.put(context.getString(R.string.text_app_language_follow_system),
((BaseActivity) context)::setLocaleFollowSystem);
map.put(context.getString(R.string.text_app_language_simplified_chinese),
() -> ((BaseActivity) context).updateLocale(Locale.SIMPLIFIED_CHINESE));
map.put(context.getString(R.string.text_app_language_english),
() -> ((BaseActivity) context).updateLocale(Locale.ENGLISH));
return map;
}
public static void selectAppLanguage(Context context) {
LinkedHashMap<String, Runnable> languagesMap = getAvailableLanguages(context);
Set<String> languagesKey = languagesMap.keySet();
Collection<Runnable> languagesRunnable = languagesMap.values();
new MaterialDialog.Builder(context)
.title(R.string.text_app_language)
.items(languagesKey)
.itemsCallbackSingleChoice(Pref.getAppLanguageIndex(), (dialog, itemView, position, text) -> true)
.positiveText(R.string.text_ok)
.onPositive((dialog, which) -> {
int index = dialog.getSelectedIndex();
Runnable run = (Runnable) languagesRunnable.toArray()[index];
if (run != null) {
Pref.setAppLanguageIndex(index);
GlobalAppContext.post(run);
}
dialog.dismiss();
})
.negativeText(R.string.text_cancel)
.onNegative((dialog, which) -> dialog.dismiss())
.neutralText(R.string.text_dialog_button_go_to_settings)
.onNeutral((dialog, which) -> {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setClassName("com.android.settings", "com.android.settings.LanguageSettings");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
})
.autoDismiss(false)
.show();
showImperfectHint(context);
}
private static void showImperfectHint(Context context) {
new NotAskAgainDialog.Builder(context, "SettingsActivity.select_app_language_imperfect_hint")
.title(R.string.text_notice)
.content(R.string.text_imperfect_hint_for_app_language)
.positiveText(R.string.text_ok)
.onPositive((dialog, which) -> dialog.dismiss())
.autoDismiss(false)
.show();
}
@AfterViews
void setUpUI() {
setUpToolbar();
@@ -163,14 +100,28 @@ public class SettingsActivity extends BaseActivity {
@Override
public void onStart() {
super.onStart();
Activity mActivity = getActivity();
ACTION_MAP = new MapBuilder<String, Runnable>()
.put(getString(R.string.text_theme_color), () -> selectThemeColor(getActivity()))
.put(getString(R.string.text_check_for_updates), () -> new UpdateCheckDialog(getActivity()).show())
.put(getString(R.string.text_about_app_and_developer), () -> startActivity(new Intent(getActivity(), AboutActivity_.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)))
.put(getString(R.string.text_app_language), () -> selectAppLanguage(getActivity()))
.put(getString(R.string.text_theme_color), () -> selectThemeColor(mActivity))
.put(getString(R.string.text_about_app_and_developer), () -> launchAboutAppAndDeveloper(mActivity))
.put(getString(R.string.text_app_language), () -> selectAppLanguage(mActivity))
.put(getString(R.string.text_check_for_updates), () -> checkForUpdates(mActivity))
.put(getString(R.string.text_manage_ignored_updates), () -> manageIgnoredUpdates(mActivity))
.build();
}
private void manageIgnoredUpdates(Activity mActivity) {
// TODO by SuperMonster003 on May 31, 2022.
// ! Updates ignorance.
new MaterialDialog.Builder(mActivity)
.title(R.string.text_prompt)
.content(R.string.text_under_development_content)
.positiveText(R.string.dialog_button_back)
.build()
.show();
}
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
CharSequence title = preference.getTitle();
if (title != null) {
@@ -185,4 +136,76 @@ public class SettingsActivity extends BaseActivity {
}
public static void checkForUpdates(Context context) {
UpdateUtils.getDialogChecker(context).checkNow();
}
public static void selectThemeColor(Context context) {
List<ColorSelectActivity.ColorItem> colorItems = new ArrayList<>(COLOR_ITEMS.size());
for (Pair<Integer, Integer> item : COLOR_ITEMS) {
colorItems.add(new ColorSelectActivity.ColorItem(context.getString(item.second), ContextCompat.getColor(context, item.first)));
}
ColorSelectActivity.startColorSelect(context, context.getString(R.string.mt_color_picker_title), colorItems);
}
public static void selectAppLanguage(Context context) {
LinkedHashMap<String, Runnable> languagesMap = getAvailableLanguages(context);
Set<String> languagesKey = languagesMap.keySet();
Collection<Runnable> languagesRunnable = languagesMap.values();
new MaterialDialog.Builder(context)
.title(R.string.text_app_language)
.items(languagesKey)
.itemsCallbackSingleChoice(Pref.getAppLanguageIndex(), (dialog, itemView, position, text) -> true)
.positiveText(R.string.text_ok)
.onPositive((dialog, which) -> {
int index = dialog.getSelectedIndex();
Runnable run = (Runnable) languagesRunnable.toArray()[index];
if (run != null) {
Pref.setAppLanguageIndex(index);
GlobalAppContext.post(run);
}
dialog.dismiss();
})
.negativeText(R.string.text_cancel)
.onNegative((dialog, which) -> dialog.dismiss())
.neutralText(R.string.dialog_button_go_to_settings)
.onNeutral((dialog, which) -> {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setClassName("com.android.settings", "com.android.settings.LanguageSettings");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
})
.autoDismiss(false)
.show();
showImperfectHint(context);
}
public static void launchAboutAppAndDeveloper(@NonNull Context context) {
context.startActivity(new Intent(context, AboutActivity_.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
@NonNull
private static LinkedHashMap<String, Runnable> getAvailableLanguages(@NonNull Context context) {
LinkedHashMap<String, Runnable> map = new LinkedHashMap<>();
map.put(context.getString(R.string.text_app_language_follow_system),
((BaseActivity) context)::setLocaleFollowSystem);
map.put(context.getString(R.string.text_app_language_simplified_chinese),
() -> ((BaseActivity) context).updateLocale(Locale.SIMPLIFIED_CHINESE));
map.put(context.getString(R.string.text_app_language_english),
() -> ((BaseActivity) context).updateLocale(Locale.ENGLISH));
return map;
}
private static void showImperfectHint(Context context) {
new NotAskAgainDialog.Builder(context, "SettingsActivity.select_app_language_imperfect_hint")
.title(R.string.text_notice)
.content(R.string.text_imperfect_hint_for_app_language)
.positiveText(R.string.text_ok)
.onPositive((dialog, which) -> dialog.dismiss())
.autoDismiss(false)
.show();
}
}

View File

@@ -1,53 +0,0 @@
package org.autojs.autojs.ui.update;
import android.content.Context;
import com.afollestad.materialdialogs.MaterialDialog;
import org.autojs.autojs6.R;
/**
* Created by Stardust on 2017/9/20.
*/
public class UpdateCheckDialog {
private final MaterialDialog mProgress;
private final Context mContext;
public UpdateCheckDialog(Context context) {
mContext = context;
mProgress = new MaterialDialog.Builder(context)
.title(R.string.text_under_development_title)
.content(R.string.text_under_development_content)
.positiveText(R.string.text_ok)
.build();
// mProgress = new MaterialDialog.Builder(context)
// .progress(true, 0)
// .content(R.string.text_checking_update)
// .build();
}
public void show() {
mProgress.show();
// VersionService.checkForUpdates()
// .observeOn(AndroidSchedulers.mainThread())
// .subscribe(new SimpleObserver<>() {
// @Override
// public void onNext(@NonNull VersionInfo versionInfo) {
// mProgress.dismiss();
// if (versionInfo.isNewer()) {
// new UpdateInfoDialogBuilder(mContext, versionInfo).show();
// } else {
// GlobalAppContext.toast(R.string.text_is_latest_version);
// }
// }
//
// @Override
// public void onError(@NonNull Throwable e) {
// e.printStackTrace();
// mProgress.dismiss();
// GlobalAppContext.toast(R.string.text_check_update_error);
// }
// });
}
}

View File

@@ -1,67 +0,0 @@
package org.autojs.autojs.ui.update;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import androidx.preference.PreferenceManager;
import androidx.annotation.NonNull;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.app.GlobalAppContext;
import com.stardust.util.IntentUtil;
import org.autojs.autojs.Pref;
import org.autojs.autojs6.R;
import org.autojs.autojs.external.fileprovider.AppFileProvider;
import org.autojs.autojs.network.download.DownloadManager;
import org.autojs.autojs.network.entity.VersionInfo;
import java.io.File;
import io.reactivex.android.schedulers.AndroidSchedulers;
/**
* Created by Stardust on 2017/4/9.
*/
public class UpdateInfoDialogBuilder extends MaterialDialog.Builder {
private static final String KEY_IGNORED_VERSION_PREFIX = "Ignored version: ";
private final SharedPreferences mSharedPreferences;
private VersionInfo mVersionInfo;
public UpdateInfoDialogBuilder(@NonNull Context context, VersionInfo info) {
super(context);
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
updateInfo(info);
}
public void updateInfo(VersionInfo info) {
mVersionInfo = info;
title(GlobalAppContext.getString(R.string.text_new_version_found));
content(info.appVersionName + "\n" + info.appVersionCode);
}
@Override
public MaterialDialog show() {
if (mSharedPreferences.getBoolean(KEY_IGNORED_VERSION_PREFIX + mVersionInfo.appVersionCode, false)) {
return null;
}
return super.show();
}
@SuppressWarnings("ResultOfMethodCallIgnored")
@SuppressLint("CheckResult")
private void directlyDownload(String downloadUrl) {
final String path = new File(Pref.getScriptDirPath(), "autojs6-latest.apk").getPath();
DownloadManager.getInstance().downloadWithProgress(getContext(), downloadUrl, path)
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(file -> IntentUtil.installApkOrToast(getContext(), file.getPath(), AppFileProvider.AUTHORITY),
error -> {
error.printStackTrace();
GlobalAppContext.toast(R.string.text_download_failed);
});
}
}

View File

@@ -2,9 +2,12 @@ package org.autojs.autojs.ui.viewmodel;
import android.content.SharedPreferences;
import com.stardust.app.GlobalAppContext;
import org.autojs.autojs.model.explorer.ExplorerItem;
import org.autojs.autojs.model.explorer.ExplorerPage;
import org.autojs.autojs.model.explorer.ExplorerSorter;
import org.autojs.autojs6.R;
import java.util.ArrayList;
import java.util.Comparator;
@@ -118,7 +121,7 @@ public class ExplorerItemList {
case SORT_TYPE_TYPE:
return ExplorerSorter.TYPE;
}
throw new IllegalArgumentException("unknown type " + sortType);
throw new IllegalArgumentException(GlobalAppContext.getString(R.string.error_illegal_argument, "sortType", sortType));
}
public int groupCount() {

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -30,5 +30,5 @@
android:scrollbars="vertical"
android:textColor="@android:color/secondary_text_light"
android:textSize="16sp"
tools:text="错误"/>
tools:text="@string/text_error"/>
</LinearLayout>

View File

@@ -26,7 +26,7 @@
android:layout_height="?attr/actionBarSize"
android:theme="@style/ToolBarStyle"
android:title="@string/app_name"
app:layout_scrollFlags="scroll|enterAlways"
app:layout_scrollFlags="enterAlways"
app:popupTheme="@style/AppTheme.PopupOverlay" />
<com.google.android.material.tabs.TabLayout

View File

@@ -1,149 +1,154 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_force_stop"
android:title="@string/text_force_stop"
app:showAsAction="never"/>
android:id="@+id/action_force_stop"
android:title="@string/text_force_stop"
app:showAsAction="never" />
<item android:title="@string/text_edit">
<menu>
<item
android:id="@+id/action_find_or_replace"
android:title="@string/text_find_or_replace"
app:showAsAction="never"/>
android:id="@+id/action_find_or_replace"
android:title="@string/text_find_or_replace"
app:showAsAction="never" />
<item
android:id="@+id/action_copy_all"
android:title="@string/text_copy_all"
app:showAsAction="never"/>
android:id="@+id/action_copy_all"
android:title="@string/text_copy_all"
app:showAsAction="never" />
<item
android:id="@+id/action_copy_line"
android:title="@string/text_copy_line"
app:showAsAction="never"/>
android:id="@+id/action_copy_line"
android:title="@string/text_copy_line"
app:showAsAction="never" />
<item
android:id="@+id/action_delete_line"
android:title="@string/text_delete_line"
app:showAsAction="never"/>
android:id="@+id/action_paste"
android:title="@string/text_paste"
app:showAsAction="never" />
<item
android:id="@+id/action_clear"
android:title="@string/text_clear"
app:showAsAction="never"/>
android:id="@+id/action_delete_line"
android:title="@string/text_delete_line"
app:showAsAction="never" />
<item
android:id="@+id/action_beautify"
android:title="@string/text_code_beautify"
app:showAsAction="never"/>
android:id="@+id/action_clear"
android:title="@string/text_clear"
app:showAsAction="never" />
<item
android:id="@+id/action_beautify"
android:title="@string/text_code_beautify"
app:showAsAction="never" />
</menu>
</item>
<item
android:title="@string/text_jump"
app:showAsAction="never">
android:title="@string/text_jump"
app:showAsAction="never">
<menu>
<item
android:id="@+id/action_jump_to_line"
android:title="@string/text_jump_to_line"
app:showAsAction="never"/>
android:id="@+id/action_jump_to_line"
android:title="@string/text_jump_to_line"
app:showAsAction="never" />
<item
android:id="@+id/action_jump_to_start"
android:title="@string/text_jump_to_start"
app:showAsAction="never"/>
android:id="@+id/action_jump_to_start"
android:title="@string/text_jump_to_start"
app:showAsAction="never" />
<item
android:id="@+id/action_jump_to_end"
android:title="@string/text_jump_to_end"
app:showAsAction="never"/>
android:id="@+id/action_jump_to_end"
android:title="@string/text_jump_to_end"
app:showAsAction="never" />
<item
android:id="@+id/action_jump_to_line_start"
android:title="@string/text_jump_to_line_start"
app:showAsAction="never"/>
android:id="@+id/action_jump_to_line_start"
android:title="@string/text_jump_to_line_start"
app:showAsAction="never" />
<item
android:id="@+id/action_jump_to_line_end"
android:title="@string/text_jump_to_line_end"
app:showAsAction="never"/>
android:id="@+id/action_jump_to_line_end"
android:title="@string/text_jump_to_line_end"
app:showAsAction="never" />
</menu>
</item>
<item
android:title="@string/text_debug"
app:showAsAction="never">
android:title="@string/text_debug"
app:showAsAction="never">
<menu>
<item
android:id="@+id/action_breakpoint"
android:title="@string/text_set_breakpoint"
app:showAsAction="never"/>
android:id="@+id/action_breakpoint"
android:title="@string/text_set_breakpoint"
app:showAsAction="never" />
<item
android:id="@+id/action_launch_debugger"
android:title="@string/text_launch_debugger"
app:showAsAction="never"/>
android:id="@+id/action_launch_debugger"
android:title="@string/text_launch_debugger"
app:showAsAction="never" />
<item
android:id="@+id/action_remove_all_breakpoints"
android:title="@string/text_remove_all_breakpoints"
app:showAsAction="never"/>
android:id="@+id/action_remove_all_breakpoints"
android:title="@string/text_remove_all_breakpoints"
app:showAsAction="never" />
</menu>
</item>
<item
android:id="@+id/action_build_apk"
android:title="@string/text_build_apk"
app:showAsAction="never"/>
android:id="@+id/action_build_apk"
android:title="@string/text_build_apk"
app:showAsAction="never" />
<item
android:id="@+id/action_log"
android:title="@string/text_log"
app:showAsAction="never"/>
android:id="@+id/action_log"
android:title="@string/text_log"
app:showAsAction="never" />
<item android:title="@string/text_more">
<menu>
<item
android:id="@+id/action_console"
android:title="@string/text_console"
app:showAsAction="never"/>
android:id="@+id/action_console"
android:title="@string/text_console"
app:showAsAction="never" />
<item
android:id="@+id/action_import_java_class"
android:title="@string/text_find_java_classes"
app:showAsAction="never"/>
android:id="@+id/action_import_java_class"
android:title="@string/text_find_java_classes"
app:showAsAction="never" />
<item
android:id="@+id/action_info"
android:title="@string/text_info"
app:showAsAction="never"/>
android:id="@+id/action_info"
android:title="@string/text_info"
app:showAsAction="never" />
<item
android:id="@+id/action_editor_text_size"
android:title="@string/text_text_size"
app:showAsAction="never"/>
android:id="@+id/action_editor_text_size"
android:title="@string/text_text_size"
app:showAsAction="never" />
<item
android:id="@+id/action_editor_theme"
android:title="@string/text_editor_theme"
app:showAsAction="never"/>
android:id="@+id/action_editor_theme"
android:title="@string/text_editor_theme"
app:showAsAction="never" />
<item
android:id="@+id/action_open_by_other_apps"
android:title="@string/text_open_by_other_apps"
app:showAsAction="never"/>
android:id="@+id/action_open_by_other_apps"
android:title="@string/text_open_by_other_apps"
app:showAsAction="never" />
</menu>
</item>

View File

@@ -77,12 +77,12 @@
<string name="text_build_failed">Build failed</string>
<string name="text_build_successfully">Build succeeded</string>
<string name="text_cancel">Cancel</string>
<string name="text_cancel_download">Cancel downloading</string>
<string name="dialog_button_cancel_download">Cancel</string>
<string name="text_cannot_read_file">Can\'t read file</string>
<string name="text_change_working_dir">Change working directory</string>
<string name="text_checking_update">Checking for updates</string>
<string name="text_checking_update" tools:ignore="TypographyEllipsis">Checking for updates...</string>
<string name="text_check_for_updates">Check for updates</string>
<string name="text_check_update_error">Check for updates failed</string>
<string name="error_check_for_update">Checking for updates failed</string>
<string name="text_class_or_package_name">Class / Package name</string>
<string name="text_clear">Clear</string>
<string name="text_clear_file_selection">Clear file selection</string>
@@ -102,6 +102,7 @@
<string name="text_copy_command">Copy cmd</string>
<string name="text_copy_debug_info">Copy debugging log</string>
<string name="text_copy_line">Copy line</string>
<string name="text_paste">Paste</string>
<string name="text_copy_to_clip">Copy to clipboard</string>
<string name="text_copy_value">Copy value</string>
<string name="text_create_fail">Create failed</string>
@@ -123,7 +124,7 @@
<string name="text_delete_line">Delete line</string>
<string name="text_detail">Details</string>
<string name="text_developer_details_under_development">Developer details is under development</string>
<string name="text_dialog_button_go_to_settings">System Settings</string>
<string name="dialog_button_go_to_settings">System Settings</string>
<string name="text_directly_download">Download now</string>
<string name="text_directory">Directory</string>
<string name="text_display_over_other_app">Display over other apps</string>
@@ -365,7 +366,7 @@
<string name="text_under_development_content">The feature will be ready to use in the next few updates</string>
<string name="text_under_development_title">Under development</string>
<string name="text_undo">Undo</string>
<string name="text_update">Update</string>
<string name="text_updates">Updates</string>
<string name="text_usage_stats_permission">Usage stats access</string>
<string name="text_usage_stats_permission_description">Provides access to device usage history and statistics which results in currentPackage() a more accurate result</string>
<string name="text_username">Username</string>
@@ -406,6 +407,42 @@
<string name="theme_color_yellow">Yellow</string>
<string name="theme_color_yellow_green">Yellow green</string>
<string name="text_refresh_explorer_may_needed">An explorer page swipe-to-refresh may be needed</string>
<string name="error_set_both_icon_res_and_icon">Can\'t set both iconRes and icon</string>
<string name="error_only_one_child_for_scroll_view">ScrollView can host only one direct child</string>
<string name="error_illegal_url_argument">URL must starts with a protocol like \"https://\" and so forth</string>
<string name="error_illegal_relative_url_argument_without_base">Relative URL can\'t be used without base URL</string>
<string name="dialog_button_ignore_current_update">Ignore</string>
<string name="dialog_button_back">@string/text_back</string>
<string name="dialog_button_update_now">Update</string>
<string name="text_getting_release_notes" tools:ignore="TypographyEllipsis">Retrieving release notes...</string>
<string name="error_cannot_connect_to_github">Can\'t connect to GitHub</string>
<string name="error_invalid_github_repo">Invalid GitHub repository: %s</string>
<string name="error_get_github_latest_release">Can\'t get latest GitHub release</string>
<string name="error_corresponding_github_release_may_not_published">Corresponding GitHub release may have not been published yet</string>
<string name="error_parse_version_info">Can\'t parse version information</string>
<string name="text_empty_release_note">No release note</string>
<string name="error_cannot_get_download_url">Can\'t get downloading URL</string>
<string name="error_empty_github_release_assets">No GitHub release assets to download</string>
<string name="text_github_backup_url_used">Backup URL has been used</string>
<string name="error_unknown">Unknown error</string>
<string name="text_downloading">Downloading</string>
<string name="text_file_name">File name</string>
<string name="text_version">Version</string>
<string name="dialog_button_quit">Quit</string>
<string name="dialog_button_retry">Retry</string>
<string name="text_preparing" tools:ignore="TypographyEllipsis">Preparing...</string>
<string name="error_download_interrupted">Download interrupted</string>
<string name="text_download_cancelled">Download cancelled</string>
<string name="text_manage_ignored_updates">Manage ignored updates</string>
<string name="text_auto_check_for_updates_and_show_snackbar">Check for updates automatically and show a snackbar on homepage</string>
<string name="text_auto_check_for_updates">Auto check for updates</string>
<string name="text_last_updates_checked_time">Last checked: %s</string>
<string name="error_failed_to_parse_version_info">Failed to parse version info</string>
<string name="error_put_value_into_json">Can\'t put value %s into JSON</string>
<string name="error_github_api_token_needed">You must provide a GitHub API Token</string>
<string name="error_cannot_find_editor_view_from_child_view">Cann\'t find EditorView from child view: %s</string>
<string name="error_unknown_type">Unknown type: %s</string>
<string name="error_sdk_lower_than_o_not_supported_yet">Current function is not supported by Android SDK versions lower than 26 (Android 8.0) yet</string>
<plurals name="air_error_short_description" key="air_error_short_description">
<item quantity="one">The description must have at least %d character</item>
<item quantity="other">The description must have at least %d characters</item>

View File

@@ -77,12 +77,12 @@
<string name="text_build_failed">打包失败</string>
<string name="text_build_successfully">打包成功</string>
<string name="text_cancel">取消</string>
<string name="text_cancel_download">取消下载</string>
<string name="dialog_button_cancel_download">取消下载</string>
<string name="text_cannot_read_file">无法读取文件</string>
<string name="text_change_working_dir">更改工作路径 (脚本文件夹)</string>
<string name="text_checking_update">正在检查更新</string>
<string name="text_checking_update" tools:ignore="TypographyEllipsis">正在检查更新...</string>
<string name="text_check_for_updates">检查更新</string>
<string name="text_check_update_error">检查更新失败</string>
<string name="error_check_for_update">检查更新失败</string>
<string name="text_class_or_package_name">类/包名</string>
<string name="text_clear">清空</string>
<string name="text_clear_file_selection">清空文件选择</string>
@@ -102,6 +102,7 @@
<string name="text_copy_command">复制指令</string>
<string name="text_copy_debug_info">复制调试信息</string>
<string name="text_copy_line">复制行</string>
<string name="text_paste">粘贴</string>
<string name="text_copy_to_clip">复制到剪贴板</string>
<string name="text_copy_value">复制值</string>
<string name="text_create_fail">创建失败</string>
@@ -123,7 +124,7 @@
<string name="text_delete_line">删除行</string>
<string name="text_detail">详情</string>
<string name="text_developer_details_under_development" tools:ignore="TypographyEllipsis">\"开发者详情\" 正在开发中...</string>
<string name="text_dialog_button_go_to_settings">系统设置</string>
<string name="dialog_button_go_to_settings">系统设置</string>
<string name="text_directly_download">直接下载</string>
<string name="text_directory">文件夹</string>
<string name="text_display_over_other_app">显示在其他应用上层</string>
@@ -193,7 +194,7 @@
<string name="text_invalid_project">无效项目</string>
<string name="text_in_progress">处理中</string>
<string name="text_issue_report">问题反馈</string>
<string name="text_is_latest_version">暂无更新版本</string>
<string name="text_is_latest_version">已是最新版本</string>
<string name="text_js_file">*.js 文件</string>
<string name="text_jump">跳转</string>
<string name="text_jump_to_end">跳转到文件末尾</string>
@@ -365,7 +366,7 @@
<string name="text_under_development_content">将在后续版本实现当前功能</string>
<string name="text_under_development_title">开发中</string>
<string name="text_undo">撤销</string>
<string name="text_update">更新</string>
<string name="text_updates">更新</string>
<string name="text_usage_stats_permission">查看使用情况权限</string>
<string name="text_usage_stats_permission_description">\"查看使用情况\" 权限可获取本设备过去使用应用的情况,从而使 \"获取当前应用\" (currentPackage) 结果更准确</string>
<string name="text_username">用户名</string>
@@ -406,6 +407,42 @@
<string name="theme_color_yellow">黄色</string>
<string name="theme_color_yellow_green">黄绿</string>
<string name="text_refresh_explorer_may_needed">可能需要下拉刷新文件浏览器页面</string>
<string name="error_set_both_icon_res_and_icon">不支持同时设置 iconRes 与 icon</string>
<string name="error_only_one_child_for_scroll_view">ScrollView 只能拥有至多一个直接子控件</string>
<string name="error_illegal_url_argument">URL 需以协议标识开头 (如 "https://" 等)</string>
<string name="error_illegal_relative_url_argument_without_base">使用相对 URL 时必须指定 Base URL</string>
<string name="dialog_button_ignore_current_update">忽略此版本</string>
<string name="dialog_button_back">@string/text_back</string>
<string name="dialog_button_update_now">立即更新</string>
<string name="text_getting_release_notes" tools:ignore="TypographyEllipsis">正在获取版本信息...</string>
<string name="error_cannot_connect_to_github">无法连接到 GitHub</string>
<string name="error_invalid_github_repo">无效的 GitHub 仓库: %s</string>
<string name="error_get_github_latest_release">无法获取最新的 GitHub 发行版</string>
<string name="error_corresponding_github_release_may_not_published">对应的 GitHub 发行版可能还未发布</string>
<string name="error_parse_version_info">无法解析版本信息</string>
<string name="text_empty_release_note">无版本信息</string>
<string name="error_cannot_get_download_url">无法获取下载链接</string>
<string name="error_empty_github_release_assets">无可供下载的 GitHub 发行版资源</string>
<string name="text_github_backup_url_used">备用 URL 已使用</string>
<string name="error_unknown">未知错误</string>
<string name="text_downloading">下载中</string>
<string name="text_file_name">文件名</string>
<string name="text_version">版本</string>
<string name="dialog_button_quit">放弃</string>
<string name="dialog_button_retry">重试</string>
<string name="text_preparing" tools:ignore="TypographyEllipsis">准备中...</string>
<string name="error_download_interrupted">下载中断</string>
<string name="text_download_cancelled">下载已取消</string>
<string name="text_manage_ignored_updates">管理已忽略更新</string>
<string name="text_auto_check_for_updates_and_show_snackbar">自动检查更新并在首页下方显示通知条</string>
<string name="text_auto_check_for_updates">自动检查更新</string>
<string name="text_last_updates_checked_time">上次检查: %s</string>
<string name="error_failed_to_parse_version_info">无法解析版本信息</string>
<string name="error_put_value_into_json">无法将值 %s 存入 JSON</string>
<string name="error_github_api_token_needed">需要提供 GitHub API 令牌</string>
<string name="error_cannot_find_editor_view_from_child_view">无法在子控件 %s 中找到 EditorView</string>
<string name="error_unknown_type">未知类型: %s</string>
<string name="error_sdk_lower_than_o_not_supported_yet">当前功能暂不支持安卓 SDK 版本低于 26 (Android 8.0) 的系统</string>
<plurals name="air_error_short_description" key="air_error_short_description" tools:ignore="UnusedQuantity">
<item quantity="one">描述至少需要 %d 个字</item>
<item quantity="other">描述至少需要 %d 个字</item>

View File

@@ -36,6 +36,46 @@
<color name="github_color_fg_muted">#57606A</color>
<color name="tint_1st_developer_identifier">#2B7A85</color>
<color name="tint_2nd_developer_identifier">#CC7A34</color>
<color name="title_material_toolbar">#ffffff</color>
<color name="dialog_button_default">#03A9F4</color>
<color name="dialog_button_caution">#FF3D00</color>
<color name="dialog_button_warn">#F57C00</color>
<color name="dialog_button_attraction">#7B1FA2</color>
<color name="dialog_button_hint">#0DA798</color>
<color name="dialog_button_reset">#A1887F</color>
<color name="dialog_button_unavailable">#BDBDBD</color>
<color name="dialog_button_finish">#009624</color>
<color name="dialog_button_success">@color/dialog_button_finish</color>
<color name="dialog_button_error">#003C8F</color>
<color name="dialog_button_failure">@color/dialog_button_error</color>
<color name="dialog_progress_download_tint">#FF6F00</color>
<color name="dialog_progress_download_bg_tint">#FFECB3</color>
<color name="dialog_progress_download_act_btn">#C43E00</color>
<color name="dialog_progress_files_tint">#F9A825</color>
<color name="dialog_progress_files_bg_tint">#FFF59D</color>
<color name="dialog_progress_files_act_btn">#C17900</color>
<color name="dialog_progress_backup_tint">#455A64</color>
<color name="dialog_progress_backup_bg_tint">#ECEFF1</color>
<color name="dialog_progress_backup_act_btn">#1C313A</color>
<color name="dialog_progress_restore_tint">#AB47BC</color>
<color name="dialog_progress_restore_bg_tint">#F3E5F5</color>
<color name="dialog_progress_restore_act_btn">#790E8B</color>
<color name="dialog_progress_indeterminate_tint">#00897B</color>
<color name="dialog_progress_indeterminate_bg_tint">#B2DFDB</color>
<color name="dialog_progress_indeterminate_act_btn">#005B4F</color>
<color name="dialog_progress_finish_tint">#00C853</color>
<color name="dialog_progress_finish_bg_tint">#DCEDC8</color>
<color name="dialog_progress_finish_act_btn">#009624</color>
<color name="dialog_progress_success_tint">#00C853</color>
<color name="dialog_progress_success_bg_tint">#DCEDC8</color>
<color name="dialog_progress_success_act_btn">#009624</color>
<color name="dialog_progress_error_tint">#1565C0</color>
<color name="dialog_progress_error_bg_tint">#BBDEFB</color>
<color name="dialog_progress_error_act_btn">#003C8F</color>
<color name="dialog_progress_failure_tint">#1565C0</color>
<color name="dialog_progress_failure_bg_tint">#BBDEFB</color>
<color name="dialog_progress_failure_act_btn">#003C8F</color>
</resources>

View File

@@ -105,12 +105,12 @@
<string name="text_build_failed">Build failed</string>
<string name="text_build_successfully">Build succeeded</string>
<string name="text_cancel">Cancel</string>
<string name="text_cancel_download">Cancel downloading</string>
<string name="dialog_button_cancel_download">Cancel</string>
<string name="text_cannot_read_file">Can\'t read file</string>
<string name="text_change_working_dir">Change working directory</string>
<string name="text_checking_update">Checking for updates</string>
<string name="text_checking_update" tools:ignore="TypographyEllipsis">Checking for updates...</string>
<string name="text_check_for_updates">Check for updates</string>
<string name="text_check_update_error">Check for updates failed</string>
<string name="error_check_for_update">Checking for updates failed</string>
<string name="text_class_or_package_name">Class / Package name</string>
<string name="text_clear">Clear</string>
<string name="text_clear_file_selection">Clear file selection</string>
@@ -130,6 +130,7 @@
<string name="text_copy_command">Copy cmd</string>
<string name="text_copy_debug_info">Copy debugging log</string>
<string name="text_copy_line">Copy line</string>
<string name="text_paste">Paste</string>
<string name="text_copy_to_clip">Copy to clipboard</string>
<string name="text_copy_value">Copy value</string>
<string name="text_create_fail">Create failed</string>
@@ -151,7 +152,7 @@
<string name="text_delete_line">Delete line</string>
<string name="text_detail">Details</string>
<string name="text_developer_details_under_development">Developer details is under development</string>
<string name="text_dialog_button_go_to_settings">System Settings</string>
<string name="dialog_button_go_to_settings">System Settings</string>
<string name="text_directly_download">Download now</string>
<string name="text_directory">Directory</string>
<string name="text_display_over_other_app">Display over other apps</string>
@@ -400,7 +401,7 @@
<string name="text_under_development_content">The feature will be ready to use in the next few updates</string>
<string name="text_under_development_title">Under development</string>
<string name="text_undo">Undo</string>
<string name="text_update">Update</string>
<string name="text_updates">Updates</string>
<string name="text_usage_stats_permission">Usage stats access</string>
<string name="text_usage_stats_permission_description">Provides access to device usage history and statistics which results in currentPackage() a more accurate result</string>
<string name="text_username">Username</string>
@@ -421,6 +422,47 @@
<string name="text_write_secure_settings_description">Secure system settings, containing system preferences that applications can read but are not allowed to write.\nThese are for preferences that the user must explicitly modify through the UI of a system app.\nWith secure system settings permission, normal applications can directly modify the secure settings (such as accessibility service).</string>
<string name="text_write_system_settings">Write system settings</string>
<string name="text_refresh_explorer_may_needed">An explorer page swipe-to-refresh may be needed</string>
<string name="error_set_both_icon_res_and_icon">Can\'t set both iconRes and icon</string>
<string name="error_only_one_child_for_scroll_view">ScrollView can host only one direct child</string>
<string name="error_illegal_url_argument">URL must starts with a protocol like \"https://\" and so forth</string>
<string name="error_illegal_relative_url_argument_without_base">Relative URL can\'t be used without base URL</string>
<string name="dialog_button_ignore_current_update">Ignore</string>
<string name="dialog_button_back">@string/text_back</string>
<string name="dialog_button_update_now">Update</string>
<string name="text_getting_release_notes" tools:ignore="TypographyEllipsis">Retrieving release notes...</string>
<string name="error_cannot_connect_to_github">Can\'t connect to GitHub</string>
<string name="error_invalid_github_repo">Invalid GitHub repository: %s</string>
<string name="error_get_github_latest_release">Can\'t get latest GitHub release</string>
<string name="error_corresponding_github_release_may_not_published">Corresponding GitHub release may have not been published yet</string>
<string name="error_parse_version_info">Can\'t parse version information</string>
<string name="text_empty_release_note">No release note</string>
<string name="error_cannot_get_download_url">Can\'t get downloading URL</string>
<string name="error_empty_github_release_assets">No GitHub release assets to download</string>
<string name="text_github_backup_url_used">Backup URL has been used</string>
<string name="error_unknown">Unknown error</string>
<string name="text_downloading">Downloading</string>
<string name="text_file_name">File name</string>
<string name="text_version">Version</string>
<string name="dialog_button_quit">Quit</string>
<string name="dialog_button_retry">Retry</string>
<string name="text_half_ellipsis" translatable="false" formatted="true">...</string>
<string name="text_preparing" tools:ignore="TypographyEllipsis">Preparing...</string>
<string name="error_download_interrupted">Download interrupted</string>
<string name="text_download_cancelled">Download cancelled</string>
<string name="format_dialog_progress_number_format_mega_bytes" translatable="false">%.2fMB/%.2fMB</string>
<string name="format_dialog_progress_number_format_kilo_bytes" translatable="false">%.1fKB/%.1fKB</string>
<string name="text_manage_ignored_updates">Manage ignored updates</string>
<string name="text_auto_check_for_updates">Auto check for updates</string>
<string name="text_last_updates_checked_time">Last checked: %s</string>
<string name="key_app_property_version_name" translatable="false">VERSION_NAME</string>
<string name="key_app_property_version_code" translatable="false">VERSION_BUILD</string>
<string name="error_failed_to_parse_version_info">Failed to parse version info</string>
<string name="error_put_value_into_json">Can\'t put value %s into JSON</string>
<string name="error_github_api_token_needed">You must provide a GitHub API Token</string>
<string name="text_auto_check_for_updates_and_show_snackbar">Check for updates automatically and show a snackbar on homepage</string>
<string name="error_cannot_find_editor_view_from_child_view">Cann\'t find EditorView from child view: %s</string>
<string name="error_unknown_type">Unknown type: %s</string>
<string name="error_sdk_lower_than_o_not_supported_yet">Current function is not supported by Android SDK versions lower than 26 (Android 8.0) yet</string>
<plurals name="air_error_short_description" key="air_error_short_description">
<item quantity="one">The description must have at least %d character</item>
<item quantity="other">The description must have at least %d characters</item>

View File

@@ -79,4 +79,7 @@
<style name="MtAppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.ActionBar">
<item name="android:textColorPrimary">#212121</item>
</style>
<style name="TextAppearance.Design.Snackbar.Message" parent="android:TextAppearance" />
</resources>

View File

@@ -41,7 +41,7 @@
android:key="@string/key_root_mode"
android:layout="@layout/preference_custom"
android:dialogMessage="@string/text_root_mode_description"
android:title="@string/text_root_mode_title"/>
android:title="@string/text_root_mode_title" />
<org.autojs.autojs.ui.settings.SetHiddenFilesPreference
android:defaultValue="@string/value_hidden_files_default"
@@ -50,7 +50,7 @@
android:key="@string/key_hidden_files"
android:layout="@layout/preference_custom"
android:dialogMessage="@string/text_hidden_files_description"
android:title="@string/text_hidden_files_title"/>
android:title="@string/text_hidden_files_title" />
</com.stardust.theme.preference.ThemeColorPreferenceCategory>
@@ -130,11 +130,28 @@
<com.stardust.theme.preference.ThemeColorPreferenceCategory
android:layout="@layout/preference_category_custom"
android:title="@string/text_about">
android:title="@string/text_updates">
<org.autojs.autojs.ui.settings.CheckForUpdatesPreference
android:layout="@layout/preference_custom"
android:title="@string/text_check_for_updates" />
<com.stardust.theme.preference.ThemeColorSwitchPreference
android:defaultValue="true"
android:key="@string/key_auto_check_for_updates"
android:layout="@layout/preference_custom"
android:title="@string/text_auto_check_for_updates"
android:summary="@string/text_auto_check_for_updates_and_show_snackbar" />
<Preference
android:layout="@layout/preference_custom"
android:title="@string/text_check_for_updates" />
android:title="@string/text_manage_ignored_updates" />
</com.stardust.theme.preference.ThemeColorPreferenceCategory>
<com.stardust.theme.preference.ThemeColorPreferenceCategory
android:layout="@layout/preference_category_custom"
android:title="@string/text_about">
<Preference
android:layout="@layout/preference_custom"