Forgotten

This commit is contained in:
hyb1996 2017-02-20 23:37:47 +08:00
parent 40d6285bed
commit 676ca14aca
17 changed files with 375 additions and 35 deletions

View File

@ -7,8 +7,8 @@ android {
applicationId "com.stardust.scriptdroid"
minSdkVersion 19
targetSdkVersion 23
versionCode 29
versionName "1.17.0216(1)"
versionCode 30
versionName "1.17.0217"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {

View File

@ -41,8 +41,8 @@ public class App extends Application {
return;
}
LeakCanary.install(this);
// if (!BuildConfig.DEBUG)
Thread.setDefaultUncaughtExceptionHandler(new CrashHandler(ErrorReportActivity.class));
if (!BuildConfig.DEBUG)
Thread.setDefaultUncaughtExceptionHandler(new CrashHandler(ErrorReportActivity.class));
instance = new WeakReference<>(this);
stateObserver = new StateObserver(PreferenceManager.getDefaultSharedPreferences(this));
registerActivityLifecycleCallback();

View File

@ -11,6 +11,7 @@ public class Pref {
private static final SharedPreferences DISPOSABLE_BOOLEAN = App.getApp().getSharedPreferences("DISPOSABLE_BOOLEAN", Context.MODE_PRIVATE);
public static final String SAMPLE_SCRIPTS_COPIED = "SAMPLE_SCRIPTS_COPIED";
private static final String KEY_MAX_TEXT_LENGTH_FOR_CODE_COMPLETION = "KEY_MAX_TEXT_LENGTH_FOR_CODE_COMPLETION";
public static SharedPreferences def() {
return PreferenceManager.getDefaultSharedPreferences(App.getApp());
@ -43,4 +44,12 @@ public class Pref {
private static String getString(int id) {
return App.getApp().getString(id);
}
public static int MaxTextLengthForCodeCompletion() {
try {
return Integer.parseInt(def().getString(App.getApp().getString(R.string.key_max_length_for_code_completion), "2000"));
} catch (NumberFormatException e) {
return 2000;
}
}
}

View File

@ -13,7 +13,6 @@ import com.afollestad.materialdialogs.MaterialDialog;
import com.jraska.console.Console;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.ui.console.ConsoleActivity;
import com.stardust.scriptdroid.droid.runtime.action.Action;
import com.stardust.scriptdroid.droid.runtime.action.ActionFactory;
import com.stardust.scriptdroid.droid.runtime.action.ActionPerformAccessibilityDelegate;
@ -22,8 +21,8 @@ import com.stardust.scriptdroid.droid.runtime.action.GetTextAction;
import com.stardust.scriptdroid.droid.runtime.api.IDroidRuntime;
import com.stardust.scriptdroid.service.AccessibilityWatchDogService;
import com.stardust.scriptdroid.tool.Shell;
import com.stardust.scriptdroid.ui.console.ConsoleActivity;
import java.util.Collections;
import java.util.List;
import timber.log.Timber;
@ -40,7 +39,6 @@ public class DroidRuntime implements IDroidRuntime {
private static DroidRuntime runtime = new DroidRuntime();
private final Object mActionPerformLock = new Object();
private boolean mActionPerformResult;
private Handler mUIHandler;
public static DroidRuntime getRuntime() {
@ -168,7 +166,7 @@ public class DroidRuntime implements IDroidRuntime {
Console.clear();
}
private boolean performAction(Action action) {
private <T> T performAction(Action action) {
if (AccessibilityWatchDogService.getInstance() == null) {
toast(App.getApp().getString(R.string.text_no_accessibility_permission));
throw new ScriptStopException(App.getApp().getString(R.string.text_no_accessibility_permission));
@ -182,25 +180,11 @@ public class DroidRuntime implements IDroidRuntime {
throw new ScriptStopException(App.getApp().getString(R.string.text_script_stopped), e);
}
}
return mActionPerformResult;
return (T) action.getResult();
}
public List<String> getTexts() {
if (AccessibilityWatchDogService.getInstance() == null) {
toast(App.getApp().getString(R.string.text_no_accessibility_permission));
throw new ScriptStopException(App.getApp().getString(R.string.text_no_accessibility_permission));
}
GetTextAction.result = Collections.EMPTY_LIST;
ActionPerformAccessibilityDelegate.setAction(new GetTextAction());
synchronized (mActionPerformLock) {
try {
mActionPerformLock.wait();
return GetTextAction.result;
} catch (InterruptedException e) {
ActionPerformAccessibilityDelegate.setAction(ActionPerformAccessibilityDelegate.NO_ACTION);
throw new ScriptStopException(App.getApp().getString(R.string.text_script_stopped), e);
}
}
return performAction(new GetTextAction());
}
@Override
@ -260,8 +244,7 @@ public class DroidRuntime implements IDroidRuntime {
};
}
public void notifyActionPerformed(boolean succeed) {
mActionPerformResult = succeed;
public void notifyActionPerformed() {
synchronized (mActionPerformLock) {
mActionPerformLock.notify();
}

View File

@ -8,8 +8,8 @@ import android.view.accessibility.AccessibilityNodeInfo;
public abstract class Action {
private boolean mPerformUtilSucceed = false;
private Object mResult;
public Action(boolean performUtilSucceed) {
mPerformUtilSucceed = performUtilSucceed;
@ -30,5 +30,11 @@ public abstract class Action {
return this;
}
public Object getResult() {
return mResult;
}
public void setResult(Object result) {
mResult = result;
}
}

View File

@ -37,18 +37,20 @@ public class ActionPerformAccessibilityDelegate implements AccessibilityDelegate
}
Log.i(TAG, "perform action:" + action);
if (action.perform(root)) {
onActionPerformed(true);
action.setResult(true);
onActionPerformed();
} else if (!action.performUtilSucceed()) {
onActionPerformed(false);
action.setResult(false);
onActionPerformed();
}
return false;
}
private void onActionPerformed(boolean succeed) {
private void onActionPerformed() {
synchronized (ActionPerformAccessibilityDelegate.class) {
action = NO_ACTION;
DroidRuntime.getRuntime().notifyActionPerformed(succeed);
DroidRuntime.getRuntime().notifyActionPerformed();
}
}

View File

@ -11,13 +11,12 @@ import java.util.List;
public class GetTextAction extends Action {
public static List<String> result;
@Override
public boolean perform(AccessibilityNodeInfo root) {
List<String> texts = new ArrayList<>();
getText(root, texts);
result = texts;
super.setResult(texts);
return true;
}
@ -34,4 +33,10 @@ public class GetTextAction extends Action {
}
}
}
@Override
public void setResult(Object result) {
if (result instanceof List)
super.setResult(result);
}
}

View File

@ -17,6 +17,7 @@ import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.jecelyin.editor.v2.common.Command;
import com.jecelyin.editor.v2.common.SaveListener;
import com.jecelyin.editor.v2.core.widget.TextView;
import com.jecelyin.editor.v2.ui.EditorDelegate;
import com.jecelyin.editor.v2.view.EditorView;
import com.jecelyin.editor.v2.view.menu.MenuDef;
@ -24,6 +25,7 @@ import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.Pref;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.Droid;
import com.stardust.scriptdroid.ui.edit.completion.InputMethodEnhanceBar;
import com.stardust.scriptdroid.ui.edit.sidemenu.AssistClipListRecyclerView;
import com.stardust.scriptdroid.ui.edit.sidemenu.EditSideMenuFragment;
import com.stardust.scriptdroid.ui.edit.sidemenu.FunctionListRecyclerView;
@ -156,8 +158,27 @@ public class EditActivity extends Editor920Activity {
private void setUpEditor() {
if (mFile != null) {
mEditorDelegate = new EditorDelegate(0, mFile, 0, null);
EditorView editorView = (EditorView) findViewById(R.id.editor);
final EditorView editorView = (EditorView) findViewById(R.id.editor);
mEditorDelegate.setEditorView(editorView);
InputMethodEnhanceBar inputMethodEnhanceBar = (InputMethodEnhanceBar) findViewById(R.id.input_method_enhance_bar);
inputMethodEnhanceBar.setEditTextBridge(new InputMethodEnhanceBar.EditTextBridge() {
@Override
public void appendText(CharSequence text) {
insertText(text);
}
@Override
public void backspace(int count) {
}
@Override
public TextView getEditText() {
return editorView.getEditText();
}
});
}
}

View File

@ -0,0 +1,158 @@
package com.stardust.scriptdroid.ui.edit.completion;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import com.jecelyin.editor.v2.core.widget.TextView;
import com.stardust.scriptdroid.Pref;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
/**
* Created by Stardust on 2017/2/17.
*/
public class CodeCompletion implements TextWatcher {
public static class CodeCompletionItem implements Comparable<CodeCompletionItem> {
String mDisplayText, mAppendText;
CodeCompletionItem(String displayText, String appendText) {
mDisplayText = displayText;
mAppendText = appendText;
}
CodeCompletionItem(String text) {
this(text, text);
}
public String getAppendText() {
return mAppendText;
}
public String getDisplayText() {
return mDisplayText;
}
@Override
public int compareTo(CodeCompletionItem o) {
return mDisplayText.compareTo(o.mDisplayText);
}
}
interface OnCodeCompletionChangeListener {
void OnCodeCompletionChange(Collection<CodeCompletionItem> list);
}
private static final String TAG = "CodeCompletion";
private OnCodeCompletionChangeListener mOnCodeCompletionChangeListener;
private TextView mEditText;
public CodeCompletion(OnCodeCompletionChangeListener listener) {
mOnCodeCompletionChangeListener = listener;
}
public void setEditText(TextView editText) {
mEditText = editText;
mEditText.addTextChangedListener(this);
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
Log.v(TAG, "beforeTextChanged: s=" + s + " start=" + start + " count=" + count + " after=" + after);
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Log.v(TAG, "onTextChanged: s=" + s + " start=" + start + " count=" + count);
}
@Override
public void afterTextChanged(Editable s) {
int position = mEditText.getSelectionStart();
String str = parseWordBefore(s, position);
if (!TextUtils.isEmpty(str))
searchCodeCompletion(str);
else
mOnCodeCompletionChangeListener.OnCodeCompletionChange(DEFAULT_CODE_COMPLETION_LIST);
}
private String parseWordBefore(Editable s, int position) {
int i;
for (i = position - 1; i >= 0; i--) {
if (position - i > KEY_WORD_LENGTH_MAX) {
return null;
}
if (!Character.isLetter(s.charAt(i))) {
break;
}
}
if (i < position - 1) {
return s.subSequence(i + 1, position).toString();
}
return null;
}
private static final String[] KEYWORDS = {"arguments", "break", "case", "catch", "class", "continue", "default", "do", "else", "eval", "export", "false", "for", "function", "if", "import", "in", "int", "new", "null", "package", "return", "switch", "this", "throw", "throws", "true", "try", "typeof", "var", "volatile", "while", "with", "Array", "Date", "hasOwnProperty", "Infinity", "isFinite", "isNaN", "isPrototypeOf", "length", "Math", "NaN", "name", "Number", "Object", "prototype", "String", "toString", "undefined", "valueOf"};
private static final int KEY_WORD_LENGTH_MAX = 15;
private static final String[] FUNCTIONS = {"launchApp", "click", "longClick", "scrollUp", "scrollDown", "toast", "launch", "input", "notStopped", "shell", "importClass"};
private boolean searchCodeCompletion(String str) {
Collection<CodeCompletionItem> c = searchWordCompletion(str);
c.addAll(searchCodeCompletion(str, FUNCTIONS));
c.addAll(searchKeyWordCompletion(str));
if (c.size() > 0) {
mOnCodeCompletionChangeListener.OnCodeCompletionChange(c);
return true;
} else {
mOnCodeCompletionChangeListener.OnCodeCompletionChange(DEFAULT_CODE_COMPLETION_LIST);
return false;
}
}
private Collection<CodeCompletionItem> searchWordCompletion(String str) {
if (mEditText.getEditableText().length() < Pref.MaxTextLengthForCodeCompletion()) {
return searchCodeCompletion(str, splitWord(mEditText.getEditableText().toString()));
}
return new TreeSet<>();
}
private Collection<CodeCompletionItem> searchCodeCompletion(String str, String[] words) {
Set<CodeCompletionItem> set = new TreeSet<>();
for (String word : words) {
// TODO: 2017/2/18 优化 字典树
if (word.startsWith(str) && str.length() < word.length()) {
set.add(new CodeCompletionItem(word, word.substring(str.length())));
}
}
return set;
}
private String[] splitWord(String s) {
// TODO: 2017/2/18 优化利用上次结果
return s.split("[\\W]");
}
private Collection<CodeCompletionItem> searchKeyWordCompletion(String str) {
return searchCodeCompletion(str, KEYWORDS);
}
private static final String[] DEFAULT_CODE_COMPLETIONS = new String[]{"=", "(", ")", ";", "{", "}", "\"", "!", "[", "]", ".", ","};
private static final List<CodeCompletionItem> DEFAULT_CODE_COMPLETION_LIST = new ArrayList<>();
static {
for (String str : DEFAULT_CODE_COMPLETIONS) {
DEFAULT_CODE_COMPLETION_LIST.add(new CodeCompletionItem(str));
}
}
}

View File

@ -0,0 +1,103 @@
package com.stardust.scriptdroid.ui.edit.completion;
import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.stardust.scriptdroid.R;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Created by Stardust on 2017/2/17.
*/
public class InputMethodEnhanceBar extends RecyclerView implements CodeCompletion.OnCodeCompletionChangeListener {
public InputMethodEnhanceBar(Context context) {
super(context);
init();
}
public InputMethodEnhanceBar(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public InputMethodEnhanceBar(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public interface EditTextBridge {
void appendText(CharSequence text);
void backspace(int count);
com.jecelyin.editor.v2.core.widget.TextView getEditText();
}
EditTextBridge mEditTextBridge;
private CodeCompletion mCodeCompletion = new CodeCompletion(this);
private List<CodeCompletion.CodeCompletionItem> mCodeCompletionList = new ArrayList<>();
private OnClickListener mOnCodeCompletionItemClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
int position = getChildViewHolder(v).getAdapterPosition();
mEditTextBridge.appendText(mCodeCompletionList.get(position).getAppendText());
}
};
private void init() {
setAdapter(new CodeCompletionAdapter());
setLayoutManager(new LinearLayoutManager(getContext(), HORIZONTAL, false));
}
public void setEditTextBridge(EditTextBridge editTextBridge) {
mEditTextBridge = editTextBridge;
mCodeCompletion.setEditText(mEditTextBridge.getEditText());
}
@Override
public void OnCodeCompletionChange(Collection<CodeCompletion.CodeCompletionItem> list) {
mCodeCompletionList.clear();
mCodeCompletionList.addAll(list);
getAdapter().notifyDataSetChanged();
}
private class CodeCompletionAdapter extends RecyclerView.Adapter<ViewHolder> {
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.input_method_enhance_bar_item, parent, false));
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
((TextView) holder.itemView).setText(mCodeCompletionList.get(position).getDisplayText());
}
@Override
public int getItemCount() {
return mCodeCompletionList.size();
}
}
private class ViewHolder extends RecyclerView.ViewHolder {
ViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(mOnCodeCompletionItemClickListener);
}
}
}

View File

@ -10,7 +10,8 @@
<com.jecelyin.editor.v2.view.EditorView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/editor"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="0dp"
android:layout_weight="1">
<com.jecelyin.editor.v2.core.widget.JecEditText
@ -35,6 +36,13 @@
android:indeterminate="true"
android:visibility="gone"/>
<com.stardust.scriptdroid.ui.edit.completion.InputMethodEnhanceBar
android:id="@+id/input_method_enhance_bar"
android:layout_width="match_parent"
android:layout_height="35dp"
android:layout_alignParentBottom="true"
android:background="#e7ebec"/>
</com.jecelyin.editor.v2.view.EditorView>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="@drawable/btn_selector"
android:gravity="center"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:textColor="@android:color/secondary_text_light"
android:textSize="16sp"/>

View File

@ -121,4 +121,7 @@
<string name="summary_use_volume_control_record">开启后每次音量变化会开始或停止脚本录制</string>
<string name="key_use_volume_control_record">key_use_volume_control_record</string>
<string name="text_mobile_qq_not_installed">未安装手机QQ</string>
<string name="text_edit">编辑</string>
<string name="key_max_length_for_code_completion">key_max_length_for_code_completion</string>
<string name="text_max_length_for_code_completion">代码补全最大文件长度</string>
</resources>

View File

@ -11,6 +11,15 @@
android:title="@string/text_use_volume_control_record"/>
</PreferenceCategory>
<PreferenceCategory
android:title="@string/text_edit">
<com.afollestad.materialdialogs.prefs.MaterialEditTextPreference
android:defaultValue="2000"
android:inputType="number"
android:key="@string/key_max_length_for_code_completion"
android:title="@string/text_max_length_for_code_completion"/>
</PreferenceCategory>
<PreferenceCategory
android:title="@string/text_others">

View File

@ -8,9 +8,32 @@ import org.junit.Test;
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
private int i = 0;
@Test
public void testSync() throws Exception {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
synchronized (this) {
i++;
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
synchronized (this) {
System.out.println(i);
}
}
}
}).start();
}

BIN
znymtjbl.exe Normal file

Binary file not shown.