From 62aafaf06b36e4cb9a38c56bffb7f607ee77c077 Mon Sep 17 00:00:00 2001 From: hyb1996 <946994919@qq.com> Date: Wed, 19 Sep 2018 14:40:05 +0800 Subject: [PATCH] fix: app memory leak; feat: app foreground service --- .idea/caches/build_file_checksums.ser | Bin 733 -> 733 bytes app/build.gradle | 18 +- app/src/main/AndroidManifest.xml | 1 + app/src/main/java/org/autojs/autojs/App.java | 15 + app/src/main/java/org/autojs/autojs/Pref.java | 4 + .../foreground/ForegroundService.java | 58 ++++ .../tasker/TaskerScriptEditActivity.java | 5 + .../model/autocomplete/AutoCompletion.java | 3 + .../autojs/ui/common/ScriptOperations.java | 1 + .../autojs/autojs/ui/edit/EditActivity.java | 1 + .../org/autojs/autojs/ui/edit/EditorView.java | 7 +- .../autojs/ui/edit/editor/CodeEditor.java | 5 + .../ui/edit/editor/JavaScriptHighlighter.java | 14 +- .../ui/edit/editor/TextViewRedoUndoer.java | 290 ------------------ .../ui/edit/editor/TextViewUndoRedo.java | 9 +- .../autojs/ui/main/drawer/DrawerFragment.java | 21 ++ app/src/main/res/values/strings.xml | 5 + .../execution/RunnableScriptExecution.java | 2 + .../autojs/rhino/AndroidClassLoader.java | 1 + .../stardust/autojs/runtime/api/Threads.java | 2 +- .../stardust/autojs/script/JsBeautifier.java | 8 +- common/release/output.json | 2 +- 22 files changed, 167 insertions(+), 305 deletions(-) create mode 100644 app/src/main/java/org/autojs/autojs/external/foreground/ForegroundService.java delete mode 100644 app/src/main/java/org/autojs/autojs/ui/edit/editor/TextViewRedoUndoer.java diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 259a89dcb7414dbe3332924ae967a0d3230dabae..4c2daf666ae5e458ff1e8b893dcae8e74665c0b2 100644 GIT binary patch delta 98 zcmV-o0GFUZm;~?+6d#eCcMwm;T6tvrM|vL8oNnq--X@bH0hAEkV!1BLQ>o_L zjPfRp6JofN#{qc|(?GG}j>(=SBE90{q%m=$lTZR45IwePWly%vn~smDYADU$q?3LE EU**p)3IG5A delta 98 zcmV-o0GFUZm;~$oaio!)cM#Jlvh5fTox6Vvn~%obxlNNJ0hADGR$^Rwntfn5 zjHH&5+;#|)#{qc|z*m9*T8^o%-Pn(JPkk`7lTZR45IofbHLa|VVXDhfaHl!SdXs(v EUjo-HWdHyG diff --git a/app/build.gradle b/app/build.gradle index 41d8fa47..7c4f2275 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,8 +8,8 @@ android { applicationId "org.autojs.autojs" minSdkVersion 17 targetSdkVersion 23 - versionCode 420 - versionName "4.0.3 Alpha" + versionCode 421 + versionName "4.0.3 Alpha2" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" multiDexEnabled true ndk { @@ -24,7 +24,7 @@ android { } release { shrinkResources false - minifyEnabled false + minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } @@ -83,10 +83,10 @@ dependencies { }) annotationProcessor 'com.jakewharton:butterknife-compiler:8.6.0' // Android supports - compile 'com.android.support:appcompat-v7:25.4.0' - compile 'com.android.support:cardview-v7:25.4.0' - compile 'com.android.support:design:25.4.0' - compile 'com.android.support:multidex:1.0.2' + compile 'com.android.support:appcompat-v7:27.1.1' + compile 'com.android.support:cardview-v7:27.1.1' + compile 'com.android.support:design:27.1.1' + compile 'com.android.support:multidex:1.0.3' // Personal libraries compile 'com.github.hyb1996:MutableTheme:0.2.2' // Material Dialogs @@ -146,6 +146,10 @@ dependencies { compile('com.afollestad.material-dialogs:commons:0.9.2.3', { exclude group: 'com.android.support' }) + debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.1' + releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.1' + // Optional, if you use support library fragments: + debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.1' compile project(':automator') compile project(':common') compile project(':autojs') diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ef88f4bd..c0c87487 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -116,6 +116,7 @@ android:theme="@style/IssueReporterTheme"/> + diff --git a/app/src/main/java/org/autojs/autojs/App.java b/app/src/main/java/org/autojs/autojs/App.java index d8f07b60..dabe48ab 100644 --- a/app/src/main/java/org/autojs/autojs/App.java +++ b/app/src/main/java/org/autojs/autojs/App.java @@ -1,8 +1,11 @@ package org.autojs.autojs; +import android.app.Application; +import android.content.Context; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.support.multidex.MultiDex; import android.support.multidex.MultiDexApplication; import android.view.View; import android.widget.ImageView; @@ -14,6 +17,7 @@ import com.raizlabs.android.dbflow.config.DatabaseConfig; import com.raizlabs.android.dbflow.config.FlowConfig; import com.raizlabs.android.dbflow.config.FlowManager; import com.raizlabs.android.dbflow.runtime.DirectModelNotifier; +import com.squareup.leakcanary.LeakCanary; import com.stardust.app.GlobalAppContext; import com.stardust.autojs.core.ui.inflater.ImageLoader; import com.stardust.autojs.core.ui.inflater.util.Drawables; @@ -67,6 +71,11 @@ public class App extends MultiDexApplication { } + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(base); + } + private void setUpDebugEnvironment() { CrashHandler crashHandler = new CrashHandler(ErrorReportActivity.class); @@ -77,6 +86,12 @@ public class App extends MultiDexApplication { crashHandler.setBuglyHandler(Thread.getDefaultUncaughtExceptionHandler()); Thread.setDefaultUncaughtExceptionHandler(crashHandler); + if (LeakCanary.isInAnalyzerProcess(this)) { + // This process is dedicated to LeakCanary for heap analysis. + // You should not init your app in this process. + return; + } + LeakCanary.install(this); } private void init() { diff --git a/app/src/main/java/org/autojs/autojs/Pref.java b/app/src/main/java/org/autojs/autojs/Pref.java index e63bc533..90a2c01f 100644 --- a/app/src/main/java/org/autojs/autojs/Pref.java +++ b/app/src/main/java/org/autojs/autojs/Pref.java @@ -201,4 +201,8 @@ public class Pref { getString(R.string.default_value_script_dir_path)); return new File(Environment.getExternalStorageDirectory(), dir).getPath(); } + + public static boolean isForegroundServiceEnabled() { + return def().getBoolean(getString(R.string.key_foreground_servie), false); + } } diff --git a/app/src/main/java/org/autojs/autojs/external/foreground/ForegroundService.java b/app/src/main/java/org/autojs/autojs/external/foreground/ForegroundService.java new file mode 100644 index 00000000..563f2e8b --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/external/foreground/ForegroundService.java @@ -0,0 +1,58 @@ +package org.autojs.autojs.external.foreground; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.os.IBinder; +import android.support.annotation.Nullable; +import android.support.annotation.RequiresApi; +import android.support.v4.app.NotificationCompat; + +import org.autojs.autojs.R; + +public class ForegroundService extends Service { + private static final int NOTIFICATION_ID = 117; + private static final String CHANEL_ID = "foreground"; + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + startForeground(); + return START_STICKY; + } + + private void startForeground(){ + startForeground(NOTIFICATION_ID, buildNotification()); + } + + private Notification buildNotification() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + createNotificationChannel(); + } + return new NotificationCompat.Builder(this, CHANEL_ID) + .setContentTitle(getString(R.string.foreground_notification_title)) + .setContentText(getString(R.string.foreground_notification_text)) + .setSmallIcon(R.drawable.autojs_material) + .build(); + } + + @RequiresApi(api = Build.VERSION_CODES.O) + private void createNotificationChannel(){ + NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + assert manager != null; + CharSequence name = getString(R.string.foreground_notification_channel_name); + String description = getString(R.string.foreground_notification_channel_name); + NotificationChannel channel = new NotificationChannel(CHANEL_ID, name, NotificationManager.IMPORTANCE_HIGH); + channel.setDescription(description); + manager.createNotificationChannel(channel); + } +} diff --git a/app/src/main/java/org/autojs/autojs/external/tasker/TaskerScriptEditActivity.java b/app/src/main/java/org/autojs/autojs/external/tasker/TaskerScriptEditActivity.java index 0b5afb01..4c0de0a1 100644 --- a/app/src/main/java/org/autojs/autojs/external/tasker/TaskerScriptEditActivity.java +++ b/app/src/main/java/org/autojs/autojs/external/tasker/TaskerScriptEditActivity.java @@ -63,4 +63,9 @@ public class TaskerScriptEditActivity extends BaseActivity { TaskerScriptEditActivity.super.finish(); } + @Override + protected void onDestroy() { + mEditorView.destroy(); + super.onDestroy(); + } } diff --git a/app/src/main/java/org/autojs/autojs/model/autocomplete/AutoCompletion.java b/app/src/main/java/org/autojs/autojs/model/autocomplete/AutoCompletion.java index 7bbbb675..ca95c75f 100644 --- a/app/src/main/java/org/autojs/autojs/model/autocomplete/AutoCompletion.java +++ b/app/src/main/java/org/autojs/autojs/model/autocomplete/AutoCompletion.java @@ -161,4 +161,7 @@ public class AutoCompletion { } + public void shutdown(){ + mExecutorService.shutdownNow(); + } } diff --git a/app/src/main/java/org/autojs/autojs/ui/common/ScriptOperations.java b/app/src/main/java/org/autojs/autojs/ui/common/ScriptOperations.java index 535d4bbb..cf18a459 100644 --- a/app/src/main/java/org/autojs/autojs/ui/common/ScriptOperations.java +++ b/app/src/main/java/org/autojs/autojs/ui/common/ScriptOperations.java @@ -318,6 +318,7 @@ public class ScriptOperations { String fileName = DownloadManager.parseFileNameLocally(url); return new FileChooserDialogBuilder(mContext) .title(R.string.text_select_save_path) + .dir(Pref.getScriptDirPath()) .chooseDir() .singleChoice() .map(saveDir -> new File(saveDir, fileName).getPath()) diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/EditActivity.java b/app/src/main/java/org/autojs/autojs/ui/edit/EditActivity.java index d293c100..0ec43fef 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/EditActivity.java +++ b/app/src/main/java/org/autojs/autojs/ui/edit/EditActivity.java @@ -180,6 +180,7 @@ EditActivity extends BaseActivity implements OnActivityResultDelegate.DelegateHo @Override protected void onDestroy() { + mEditorView.destroy(); super.onDestroy(); } diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/EditorView.java b/app/src/main/java/org/autojs/autojs/ui/edit/EditorView.java index 3ba09bdb..3f9b827c 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/EditorView.java +++ b/app/src/main/java/org/autojs/autojs/ui/edit/EditorView.java @@ -392,7 +392,7 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC Snackbar.make(this, R.string.text_start_running, Snackbar.LENGTH_SHORT).show(); } ScriptExecution execution = Scripts.runWithBroadcastSender(mFile); - if(execution == null){ + if (execution == null) { return null; } mScriptExecutionId = execution.getId(); @@ -671,4 +671,9 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC super.onRestoreInstanceState(superData); setMenuItemStatus(R.id.run, mScriptExecutionId == ScriptExecution.NO_ID); } + + public void destroy() { + mEditor.destroy(); + mAutoCompletion.shutdown(); + } } diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditor.java b/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditor.java index 83b3db16..0c6eeceb 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditor.java +++ b/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditor.java @@ -373,6 +373,11 @@ public class CodeEditor extends HVScrollView { mCodeEditText.removeAllBreakpoints(); } + public void destroy(){ + mJavaScriptHighlighter.shutdown(); + mJsBeautifier.shutdown(); + } + @Override protected void onDraw(Canvas canvas) { int codeWidth = getWidth() - getPaddingLeft() - getPaddingRight(); diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/editor/JavaScriptHighlighter.java b/app/src/main/java/org/autojs/autojs/ui/edit/editor/JavaScriptHighlighter.java index 20752a55..7037a3ff 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/editor/JavaScriptHighlighter.java +++ b/app/src/main/java/org/autojs/autojs/ui/edit/editor/JavaScriptHighlighter.java @@ -2,10 +2,12 @@ package org.autojs.autojs.ui.edit.editor; import android.text.Editable; +import android.text.TextWatcher; import android.util.TimingLogger; import com.stardust.autojs.rhino.TokenStream; import com.stardust.pio.UncheckedIOException; + import org.autojs.autojs.ui.edit.theme.Theme; import org.autojs.autojs.ui.widget.SimpleTextWatcher; @@ -46,7 +48,7 @@ public class JavaScriptHighlighter implements SimpleTextWatcher.AfterTextChanged @Override public String toString() { - return super.toString() + "{count = " + mCount + ", length = " + mText.length() + "}"; + return super.toString() + "{count = " + mCount + ", length = " + mText.length() + "}"; } public int getCharCount() { @@ -60,16 +62,18 @@ public class JavaScriptHighlighter implements SimpleTextWatcher.AfterTextChanged private Theme mTheme; private CodeEditText mCodeEditText; - private ThreadPoolExecutor mExecutorService = new ThreadPoolExecutor(3, 6, + private ThreadPoolExecutor mExecutorService = new ThreadPoolExecutor(3, 6, 2L, TimeUnit.MINUTES, new LinkedBlockingQueue<>()); private AtomicInteger mRunningHighlighterId = new AtomicInteger(); private TimingLogger mLogger = new TimingLogger(CodeEditText.LOG_TAG, "highlight"); + private final TextWatcher mTextWatcher; public JavaScriptHighlighter(Theme theme, CodeEditText codeEditText) { mExecutorService.allowCoreThreadTimeOut(true); mTheme = theme; mCodeEditText = codeEditText; - codeEditText.addTextChangedListener(new SimpleTextWatcher(this)); + mTextWatcher = new SimpleTextWatcher(this); + codeEditText.addTextChangedListener(mTextWatcher); } @Override @@ -112,5 +116,9 @@ public class JavaScriptHighlighter implements SimpleTextWatcher.AfterTextChanged mCodeEditText.updateHighlightTokens(highlightTokens); } + public void shutdown() { + mCodeEditText.removeTextChangedListener(mTextWatcher); + mExecutorService.shutdownNow(); + } } diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/editor/TextViewRedoUndoer.java b/app/src/main/java/org/autojs/autojs/ui/edit/editor/TextViewRedoUndoer.java deleted file mode 100644 index 06144eb0..00000000 --- a/app/src/main/java/org/autojs/autojs/ui/edit/editor/TextViewRedoUndoer.java +++ /dev/null @@ -1,290 +0,0 @@ -package org.autojs.autojs.ui.edit.editor; -/* - * Copyright 2016. SHENQINCI(沈钦赐)<946736079@qq.com> - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -import android.support.annotation.NonNull; -import android.text.Editable; -import android.text.TextWatcher; -import android.widget.EditText; - -import java.util.Stack; - -/** - * 撤销和恢复撤销 - * Created by 沈钦赐 on 16/6/23. - */ -public class TextViewRedoUndoer { - //操作序号(一次编辑可能对应多个操作,如替换文字,就是删除+插入) - int index; - //撤销栈 - Stack history = new Stack<>(); - //恢复栈 - Stack historyBack = new Stack<>(); - - private Editable editable; - private EditText editText; - //自动操作标志,防止重复回调,导致无限撤销 - private boolean flag = false; - - private int mInitialHistoryStackSize; - private boolean mEnabled = true; - - public TextViewRedoUndoer(@NonNull EditText editText) { - this.editable = editText.getText(); - this.editText = editText; - editText.addTextChangedListener(new Watcher()); - } - - protected void onEditableChanged(Editable s) { - - } - - protected void onTextChanged(Editable s) { - if (history.size() < mInitialHistoryStackSize) { - mInitialHistoryStackSize = 0; - } - } - - - /** - * 清理记录 - * Clear history. - */ - public final void clearHistory() { - history.clear(); - historyBack.clear(); - mInitialHistoryStackSize = 0; - } - - public boolean canUndo() { - return !history.empty(); - } - - /** - * 撤销 - * Undo. - */ - public final void undo() { - if (history.empty()) return; - //锁定操作 - flag = true; - Action action = history.pop(); - historyBack.push(action); - if (action.isAdd) { - //撤销添加 - editable.delete(action.startCursor, action.startCursor + action.actionTarget.length()); - editText.setSelection(action.startCursor, action.startCursor); - } else { - //插销删除 - editable.insert(action.startCursor, action.actionTarget); - if (action.endCursor == action.startCursor) { - editText.setSelection(action.startCursor + action.actionTarget.length()); - } else { - editText.setSelection(action.startCursor, action.endCursor); - } - } - //释放操作 - flag = false; - //判断是否是下一个动作是否和本动作是同一个操作,直到不同为止 - if (!history.empty() && history.peek().index == action.index) { - undo(); - } - } - - public boolean canRedo() { - return !historyBack.empty(); - } - - /** - * 恢复 - * Redo. - */ - public final void redo() { - if (historyBack.empty()) return; - flag = true; - Action action = historyBack.pop(); - history.push(action); - if (action.isAdd) { - //恢复添加 - editable.insert(action.startCursor, action.actionTarget); - if (action.endCursor == action.startCursor) { - editText.setSelection(action.startCursor + action.actionTarget.length()); - } else { - editText.setSelection(action.startCursor, action.endCursor); - } - } else { - //恢复删除 - editable.delete(action.startCursor, action.startCursor + action.actionTarget.length()); - editText.setSelection(action.startCursor, action.startCursor); - } - flag = false; - //判断是否是下一个动作是否和本动作是同一个操作 - if (!historyBack.empty() && historyBack.peek().index == action.index) - redo(); - } - - /** - * 首次设置文本 - * Set default text. - */ - public final void setDefaultText(CharSequence text) { - clearHistory(); - flag = true; - editable.replace(0, editable.length(), text); - flag = false; - } - - public boolean isTextChanged() { - return history.size() != mInitialHistoryStackSize; - } - - public void markTextAsUnchanged() { - mInitialHistoryStackSize = history.size(); - } - - public void setEnabled(boolean enabled) { - mEnabled = enabled; - } - - public boolean isEnabled() { - return mEnabled; - } - - private class Watcher implements TextWatcher { - - /** - * Before text changed. - * - * @param s the s - * @param start the start 起始光标 - * @param count the endCursor 选择数量 - * @param after the after 替换增加的文字数 - */ - @Override - public final void beforeTextChanged(CharSequence s, int start, int count, int after) { - if (!editText.isEnabled() || !mEnabled) { - return; - } - if (flag) return; - int end = start + count; - if (end > start && end <= s.length()) { - CharSequence charSequence = s.subSequence(start, end); - //删除了文字 - if (charSequence.length() > 0) { - Action action = new Action(charSequence, start, false); - if (count > 1) { - //如果一次超过一个字符,说名用户选择了,然后替换或者删除操作 - action.setSelectCount(count); - } else if (count == 1 && count == after) { - //一个字符替换 - action.setSelectCount(count); - } - //还有一种情况:选择一个字符,然后删除(暂时没有考虑这种情况) - - history.push(action); - historyBack.clear(); - action.setIndex(++index); - } - } - } - - /** - * On text changed. - * - * @param s the s - * @param start the start 起始光标 - * @param before the before 选择数量 - * @param count the endCursor 添加的数量 - */ - @Override - public final void onTextChanged(CharSequence s, int start, int before, int count) { - if (!editText.isEnabled() || !mEnabled) { - return; - } - if (flag) return; - int end = start + count; - if (end > start) { - CharSequence charSequence = s.subSequence(start, end); - //添加文字 - if (charSequence.length() > 0) { - Action action = new Action(charSequence, start, true); - - history.push(action); - historyBack.clear(); - if (before > 0) { - //文字替换(先删除再增加),删除和增加是同一个操作,所以不需要增加序号 - action.setIndex(index); - } else { - action.setIndex(++index); - } - } - } - } - - @Override - public final void afterTextChanged(Editable s) { - if (!editText.isEnabled() || !mEnabled) { - return; - } - if (flag) return; - if (s != editable) { - editable = s; - onEditableChanged(s); - } - TextViewRedoUndoer.this.onTextChanged(s); - } - - } - - private class Action { - /** - * 改变字符. - */ - CharSequence actionTarget; - /** - * 光标位置. - */ - int startCursor; - int endCursor; - /** - * 标志增加操作. - */ - boolean isAdd; - /** - * 操作序号. - */ - int index; - - - public Action(CharSequence actionTag, int startCursor, boolean add) { - this.actionTarget = actionTag; - this.startCursor = startCursor; - this.endCursor = startCursor; - this.isAdd = add; - } - - public void setSelectCount(int count) { - this.endCursor = endCursor + count; - } - - public void setIndex(int index) { - this.index = index; - } - } - - -} \ No newline at end of file diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/editor/TextViewUndoRedo.java b/app/src/main/java/org/autojs/autojs/ui/edit/editor/TextViewUndoRedo.java index 01b6a173..c9c54751 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/editor/TextViewUndoRedo.java +++ b/app/src/main/java/org/autojs/autojs/ui/edit/editor/TextViewUndoRedo.java @@ -9,6 +9,7 @@ import java.util.LinkedList; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; +import android.os.Handler; import android.text.Editable; import android.text.Selection; import android.text.TextWatcher; @@ -46,6 +47,10 @@ public class TextViewUndoRedo { private int mInitialHistoryStackSize; + private Handler mHandler = new Handler(); + private int mTextChangeId = 0; + private boolean mTextChanging = false; + // =================================================================== // /** @@ -404,7 +409,6 @@ public class TextViewUndoRedo { if (mIsUndoOrRedo || !mEnabled) { return; } - mBeforeChange = s.subSequence(start, start + count); } @@ -415,7 +419,10 @@ public class TextViewUndoRedo { } mAfterChange = s.subSequence(start, start + count); + mTextChangeId++; mEditHistory.add(new EditItem(start, mBeforeChange, mAfterChange)); + int textChangeId = mTextChangeId; + //TODO 增加连续输入文字当成一次撤销的功能 } public void afterTextChanged(Editable s) { diff --git a/app/src/main/java/org/autojs/autojs/ui/main/drawer/DrawerFragment.java b/app/src/main/java/org/autojs/autojs/ui/main/drawer/DrawerFragment.java index 5d7ccae0..65b09364 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/drawer/DrawerFragment.java +++ b/app/src/main/java/org/autojs/autojs/ui/main/drawer/DrawerFragment.java @@ -1,5 +1,6 @@ package org.autojs.autojs.ui.main.drawer; +import android.annotation.SuppressLint; import android.content.Intent; import android.graphics.drawable.Drawable; import android.os.Build; @@ -22,6 +23,7 @@ import com.stardust.notification.NotificationListenerService; import org.autojs.autojs.Pref; import org.autojs.autojs.R; +import org.autojs.autojs.external.foreground.ForegroundService; import org.autojs.autojs.network.GlideApp; import org.autojs.autojs.network.UserService; import org.autojs.autojs.tool.EmptyObservers; @@ -107,6 +109,8 @@ public class DrawerFragment extends android.support.v4.app.Fragment { }; private DrawerMenuItem mNotificationPermissionItem = new DrawerMenuItem(R.drawable.ic_ali_notification, R.string.text_notification_permission, 0, this::goToNotificationServiceSettings); + private DrawerMenuItem mForegroundServiceItem = new DrawerMenuItem(R.drawable.ic_service_green, R.string.text_foreground_service, R.string.key_foreground_servie, this::toggleForegroundService); + private DrawerMenuItem mFloatingWindowItem = new DrawerMenuItem(R.drawable.ic_robot_64, R.string.text_floating_window, 0, this::showOrDismissFloatingWindow); private DrawerMenuItem mCheckForUpdatesItem = new DrawerMenuItem(R.drawable.ic_check_for_updates, R.string.text_check_for_updates, this::checkForUpdates); @@ -142,6 +146,10 @@ public class DrawerFragment extends android.support.v4.app.Fragment { setChecked(mFloatingWindowItem, true); } setChecked(mConnectionItem, DevPluginService.getInstance().isConnected()); + if(Pref.isForegroundServiceEnabled()){ + GlobalAppContext.get().startService(new Intent(getContext(), ForegroundService.class)); + setChecked(mForegroundServiceItem, true); + } } private void initMenuItems() { @@ -150,6 +158,7 @@ public class DrawerFragment extends android.support.v4.app.Fragment { mAccessibilityServiceItem, mStableModeItem, mNotificationPermissionItem, + mForegroundServiceItem, new DrawerMenuGroup(R.string.text_script_record), mFloatingWindowItem, @@ -165,6 +174,7 @@ public class DrawerFragment extends android.support.v4.app.Fragment { } + @SuppressLint("CheckResult") @Click(R.id.avatar) void loginOrShowUserInfo() { UserService.getInstance() @@ -251,6 +261,17 @@ public class DrawerFragment extends android.support.v4.app.Fragment { } } + + private void toggleForegroundService(DrawerMenuItemViewHolder holder) { + boolean checked = holder.getSwitchCompat().isChecked(); + if(checked){ + GlobalAppContext.get().startService(new Intent(getContext(), ForegroundService.class)); + }else { + GlobalAppContext.get().stopService(new Intent(getContext(), ForegroundService.class)); + } + } + + private void inputRemoteHost() { String host = Pref.getServerAddressOrDefault(WifiTool.getRouterIp(getActivity())); new MaterialDialog.Builder(getActivity()) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4e89bd90..0bebb257 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -398,4 +398,9 @@ 新建项目 js文件 无效项目 + 前台服务 + key_foreground_service + 前台服务通知 + Auto.js保持运行中 + 点击进入主界面 diff --git a/autojs/src/main/java/com/stardust/autojs/execution/RunnableScriptExecution.java b/autojs/src/main/java/com/stardust/autojs/execution/RunnableScriptExecution.java index 5073803b..ce87eebc 100644 --- a/autojs/src/main/java/com/stardust/autojs/execution/RunnableScriptExecution.java +++ b/autojs/src/main/java/com/stardust/autojs/execution/RunnableScriptExecution.java @@ -6,6 +6,7 @@ import com.stardust.autojs.engine.ScriptEngine; import com.stardust.autojs.engine.ScriptEngineManager; import com.stardust.autojs.runtime.exception.ScriptInterruptedException; import com.stardust.autojs.script.ScriptSource; +import com.stardust.lang.ThreadCompat; /** * Created by Stardust on 2017/5/1. @@ -24,6 +25,7 @@ public class RunnableScriptExecution extends ScriptExecution.AbstractScriptExecu @Override public void run() { + ThreadCompat.currentThread().setName("ScriptThread-" + getId() + "[" + getSource() + "]"); execute(); } diff --git a/autojs/src/main/java/com/stardust/autojs/rhino/AndroidClassLoader.java b/autojs/src/main/java/com/stardust/autojs/rhino/AndroidClassLoader.java index 0e9363bc..4538c348 100644 --- a/autojs/src/main/java/com/stardust/autojs/rhino/AndroidClassLoader.java +++ b/autojs/src/main/java/com/stardust/autojs/rhino/AndroidClassLoader.java @@ -3,6 +3,7 @@ package com.stardust.autojs.rhino; import android.util.Log; import com.android.dx.command.dexer.Main; +import com.android.dx.dex.file.DexFile; import net.lingala.zip4j.core.ZipFile; import net.lingala.zip4j.exception.ZipException; diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/api/Threads.java b/autojs/src/main/java/com/stardust/autojs/runtime/api/Threads.java index abb91107..d1a1428c 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/api/Threads.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/Threads.java @@ -46,7 +46,7 @@ public class Threads { TimerThread thread = createThread(runnable); synchronized (mThreads) { mThreads.add(thread); - thread.setName(thread.getName() + " (Spawn-" + mSpawnCount + ")"); + thread.setName(mMainThread.getName() + " (Spawn-" + mSpawnCount + ")"); mSpawnCount++; } thread.start(); diff --git a/autojs/src/main/java/com/stardust/autojs/script/JsBeautifier.java b/autojs/src/main/java/com/stardust/autojs/script/JsBeautifier.java index 15b02ffa..5b114468 100644 --- a/autojs/src/main/java/com/stardust/autojs/script/JsBeautifier.java +++ b/autojs/src/main/java/com/stardust/autojs/script/JsBeautifier.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.Collections; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** @@ -35,7 +36,7 @@ public class JsBeautifier { void onException(Exception e); } - private Executor mExecutor = Executors.newSingleThreadExecutor(); + private ExecutorService mExecutor = Executors.newSingleThreadExecutor(); private Context mContext; private Function mJsBeautifyFunction; private org.mozilla.javascript.Context mScriptContext; @@ -122,4 +123,9 @@ public class JsBeautifier { } } + public void shutdown(){ + mExecutor.shutdownNow(); + mView = null; + } + } diff --git a/common/release/output.json b/common/release/output.json index 9b80d418..25bb428d 100644 --- a/common/release/output.json +++ b/common/release/output.json @@ -1 +1 @@ -[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":416},"path":"commonRelease-4.0.2 Alpha11.apk","properties":{"packageId":"org.autojs.autojs","split":"","minSdkVersion":"17"}}] \ No newline at end of file +[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":421},"path":"commonRelease-4.0.3 Alpha2.apk","properties":{"packageId":"org.autojs.autojs","split":"","minSdkVersion":"17"}}] \ No newline at end of file