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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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