6.1.1 - 新增应用更新功能 修复编辑器页面/安卓10存储权限 异常消息资源化
This commit is contained in:
@@ -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">
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
597
app/src/main/java/org/autojs/autojs/network/UpdateChecker.java
Normal file
597
app/src/main/java/org/autojs/autojs/network/UpdateChecker.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
59
app/src/main/java/org/autojs/autojs/tool/UpdateUtils.java
Normal file
59
app/src/main/java/org/autojs/autojs/tool/UpdateUtils.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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()) &&
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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" +
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
// }
|
||||
// });
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
BIN
app/src/main/res/drawable/ic_about.png
Normal file
BIN
app/src/main/res/drawable/ic_about.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
@@ -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>
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user