6.1.1 - 新增应用更新功能 修复编辑器页面/安卓10存储权限 异常消息资源化
This commit is contained in:
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user