fix: app memory leak; feat: app foreground service
This commit is contained in:
BIN
.idea/caches/build_file_checksums.ser
generated
BIN
.idea/caches/build_file_checksums.ser
generated
Binary file not shown.
@@ -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')
|
||||
|
||||
@@ -116,6 +116,7 @@
|
||||
android:theme="@style/IssueReporterTheme"/>
|
||||
|
||||
|
||||
<service android:name=".external.foreground.ForegroundService"/>
|
||||
<service android:name=".external.ScriptExecutionIntentService"/>
|
||||
|
||||
<activity android:name=".external.tasker.TaskPrefEditActivity_"/>
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
58
app/src/main/java/org/autojs/autojs/external/foreground/ForegroundService.java
vendored
Normal file
58
app/src/main/java/org/autojs/autojs/external/foreground/ForegroundService.java
vendored
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -63,4 +63,9 @@ public class TaskerScriptEditActivity extends BaseActivity {
|
||||
TaskerScriptEditActivity.super.finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
mEditorView.destroy();
|
||||
super.onDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,4 +161,7 @@ public class AutoCompletion {
|
||||
}
|
||||
|
||||
|
||||
public void shutdown(){
|
||||
mExecutorService.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -180,6 +180,7 @@ EditActivity extends BaseActivity implements OnActivityResultDelegate.DelegateHo
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
mEditorView.destroy();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Action> history = new Stack<>();
|
||||
//恢复栈
|
||||
Stack<Action> 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -398,4 +398,9 @@
|
||||
<string name="text_new_project">新建项目</string>
|
||||
<string name="text_js_file">js文件</string>
|
||||
<string name="text_invalid_project">无效项目</string>
|
||||
<string name="text_foreground_service">前台服务</string>
|
||||
<string name="key_foreground_servie">key_foreground_service</string>
|
||||
<string name="foreground_notification_channel_name">前台服务通知</string>
|
||||
<string name="foreground_notification_title">Auto.js保持运行中</string>
|
||||
<string name="foreground_notification_text">点击进入主界面</string>
|
||||
</resources>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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"}}]
|
||||
[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":421},"path":"commonRelease-4.0.3 Alpha2.apk","properties":{"packageId":"org.autojs.autojs","split":"","minSdkVersion":"17"}}]
|
||||
Reference in New Issue
Block a user