Added getTexts and action recorder, fixed github issue report token

This commit is contained in:
hyb1996 2017-02-15 18:10:17 +08:00
parent 04b4303899
commit e2d5efa710
82 changed files with 1392 additions and 518 deletions

View File

@ -7,8 +7,8 @@ android {
applicationId "com.stardust.scriptdroid"
minSdkVersion 19
targetSdkVersion 23
versionCode 22
versionName "1.17.0213"
versionCode 25
versionName "1.17.0215"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
@ -25,6 +25,10 @@ android {
dataBinding {
enabled = true
}
lintOptions {
disable 'MissingTranslation'
disable 'ExtraTranslation'
}
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
def file = output.outputFile

View File

@ -17,8 +17,9 @@
android:theme="@style/AppTheme"
tools:replace="android:label">
<activity
android:name=".MainActivity"
android:name=".ui.main.MainActivity"
android:label="@string/_app_name"
android:launchMode="singleTask"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
@ -28,7 +29,7 @@
</activity>
<activity
android:name=".ShortcutActivity"
android:name=".external.shortcut.ShortcutActivity"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
@ -38,7 +39,7 @@
</activity>
<service
android:name=".droid.runtime.action.ActionPerformService"
android:name=".service.AccessibilityWatchDogService"
android:enabled="true"
android:exported="true"
android:label="@string/_app_name"
@ -53,7 +54,7 @@
</service>
<service
android:name="com.stardust.scriptdroid.tile.BoundsAssistEnableTileService"
android:name=".external.tile.BoundsAssistEnableTileService"
android:icon="@drawable/ic_robot_head"
android:label="脚本辅助"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
@ -73,21 +74,22 @@
android:resource="@xml/provider_paths"/>
</provider>
<activity android:name=".EditActivity"/>
<activity android:name=".DocumentActivity"/>
<activity android:name=".AboutActivity"/>
<activity android:name=".SettingsActivity"/>
<activity android:name=".droid.ConsoleActivity"/>
<activity android:name=".ErrorReportActivity"/>
<activity android:name=".ui.edit.EditActivity"/>
<activity android:name=".ui.help.DocumentActivity"/>
<activity android:name=".ui.settings.AboutActivity"/>
<activity android:name=".ui.settings.SettingsActivity"/>
<activity android:name=".ui.console.ConsoleActivity"/>
<activity android:name=".ui.error.ErrorReportActivity"/>
<activity
android:name=".IssueReportActivity"
android:name=".ui.error.IssueReportActivity"
android:theme="@style/IssueReportTheme"/>
<activity android:name=".droid.script.ScriptExecuteActivity"/>
<service android:name=".AssistModeSwitchService"/>
<service android:name=".external.notification.bounds_assist.BoundsAssistSwitchNotificationHandleService"/>
<service android:name=".external.notification.record.ActionRecordNotificationHandleService"/>
<activity
android:name=".EditAndRunIntentActivity"
android:name=".ui.edit.EditAndRunIntentActivity"
android:icon="@drawable/ic_edit_green_48dp"
android:label="编辑与运行脚本">
@ -126,7 +128,7 @@
</activity>
<activity
android:name=".ImportIntentActivity"
android:name=".ui.edit.ImportIntentActivity"
android:icon="@drawable/ic_folder_open_outline_green"
android:label="导入脚本文件">

View File

@ -120,4 +120,8 @@ var clearConsole = function(){
var shell = function(cmd, root){
root = root ? 1 : 0;
droid.shell(cmd, root);
}
var getTexts = function(){
return droid.getTexts();
}

View File

@ -5,21 +5,27 @@ import android.app.Application;
import android.os.Bundle;
import android.preference.PreferenceManager;
import com.stardust.scriptdroid.droid.runtime.action.ActionPerformAccessibilityDelegate;
import com.stardust.scriptdroid.record.AccessibilityRecorderDelegate;
import com.stardust.scriptdroid.service.AccessibilityWatchDogService;
import com.stardust.scriptdroid.ui.error.ErrorReportActivity;
import com.stardust.util.CrashHandler;
import com.stardust.util.StateObserver;
import java.lang.ref.WeakReference;
/**
* Created by Stardust on 2017/1/27.
*/
public class App extends Application {
private static App instance;
private static WeakReference<App> instance;
private static StateObserver stateObserver;
private static Activity currentActivity;
private static WeakReference<Activity> currentActivity;
public static App getApp() {
return instance;
return instance.get();
}
public static StateObserver getStateObserver() {
@ -29,13 +35,24 @@ public class App extends Application {
public void onCreate() {
super.onCreate();
Thread.setDefaultUncaughtExceptionHandler(new CrashHandler(ErrorReportActivity.class));
instance = this;
if (!BuildConfig.DEBUG)
Thread.setDefaultUncaughtExceptionHandler(new CrashHandler(ErrorReportActivity.class));
instance = new WeakReference<>(this);
stateObserver = new StateObserver(PreferenceManager.getDefaultSharedPreferences(this));
registerActivityLifecycleCallback();
initAccessibilityServiceDelegates();
}
private void initAccessibilityServiceDelegates() {
AccessibilityWatchDogService.addDelegateIfNeeded(100, ActionPerformAccessibilityDelegate.class);
AccessibilityWatchDogService.addDelegateIfNeeded(200, AccessibilityRecorderDelegate.getInstance());
}
private void registerActivityLifecycleCallback() {
registerActivityLifecycleCallbacks(new SimpleActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
currentActivity = activity;
currentActivity = new WeakReference<>(activity);
}
@ -46,14 +63,14 @@ public class App extends Application {
@Override
public void onActivityResumed(Activity activity) {
currentActivity = activity;
currentActivity = new WeakReference<>(activity);
}
});
}
public static Activity currentActivity() {
return currentActivity;
return currentActivity.get();
}

View File

@ -1,71 +0,0 @@
package com.stardust.scriptdroid;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import com.heinrichreimersoftware.androidissuereporter.IssueReporterActivity;
import com.heinrichreimersoftware.androidissuereporter.model.github.GithubTarget;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Created by Stardust on 2017/2/13.
*/
public class IssueReportActivity extends IssueReporterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handleIntent();
findViewById(com.heinrichreimersoftware.androidissuereporter.R.id.air_optionAnonymous).performClick();
((EditText) findViewById(R.id.air_inputTitle)).setText(R.string.text_bug_report);
getSupportActionBar().setTitle(R.string.text_report_bug);
FloatingActionButton send = (FloatingActionButton) this.findViewById(com.heinrichreimersoftware.androidissuereporter.R.id.air_buttonSend);
try {
final Method reportIssue = IssueReporterActivity.class.getDeclaredMethod("reportIssue");
reportIssue.setAccessible(true);
send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
reportIssue.invoke(IssueReportActivity.this);
Toast.makeText(IssueReportActivity.this, R.string.text_report_succeed, Toast.LENGTH_SHORT).show();
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
Toast.makeText(IssueReportActivity.this, R.string.text_report_fail, Toast.LENGTH_SHORT).show();
}
exit();
}
});
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
private void handleIntent() {
final String errorDetail = getIntent().getStringExtra("error");
((EditText) findViewById(R.id.air_inputDescription)).setText(errorDetail);
}
private void exit() {
finishAffinity();
}
@Override
protected GithubTarget getTarget() {
return new GithubTarget("hyb1996", "NoRootScriptDroid");
}
@Override
protected String getGuestToken() {
return "cd403d68a9f3a3590a14408d055c55180e7af7d3";
}
}

View File

@ -1,4 +1,4 @@
package com.stardust.scriptdroid.droid.assist;
package com.stardust.scriptdroid.bounds_assist;
/**
* Created by Stardust on 2017/2/4.

View File

@ -1,4 +1,4 @@
package com.stardust.scriptdroid.droid.assist;
package com.stardust.scriptdroid.bounds_assist;
import android.graphics.Rect;
import android.preference.PreferenceManager;
@ -17,7 +17,7 @@ import com.stardust.scriptdroid.R;
*/
public class BoundsAssistant {
public static final String KEY_ASSIST_MODE_ENABLE = "ASSIST_MODE_ENABLE";
public static final String KEY_BOUNDS_ASSIST_ENABLE = "ASSIST_MODE_ENABLE";
private static boolean assistModeEnable;
@ -32,7 +32,7 @@ public class BoundsAssistant {
showAssistModeInfoDialog();
}
BoundsAssistant.assistModeEnable = assistModeEnable;
App.getStateObserver().setState(KEY_ASSIST_MODE_ENABLE, assistModeEnable);
App.getStateObserver().setState(KEY_BOUNDS_ASSIST_ENABLE, assistModeEnable);
}
private static void showAssistModeInfoDialog() {
@ -59,21 +59,25 @@ public class BoundsAssistant {
}
}
private static Rect getBoundsInScreen(AccessibilityNodeInfo nodeInfo) {
public static Rect getBoundsInScreen(AccessibilityNodeInfo nodeInfo) {
Rect rect = new Rect();
nodeInfo.getBoundsInScreen(rect);
return rect;
}
private static void saveAndAlertBounds(Rect rect) {
String str = rect.toString().replace('-', ',').replace(" ", "").substring(4);
String str = boundsToString(rect);
boundsAssistClipList.add(str);
Toast.makeText(App.getApp(), str, Toast.LENGTH_SHORT).show();
}
public static String boundsToString(Rect rect) {
return rect.toString().replace('-', ',').replace(" ", "").substring(4);
}
static {
assistModeEnable = PreferenceManager.getDefaultSharedPreferences(App.getApp()).getBoolean(KEY_ASSIST_MODE_ENABLE, false);
assistModeEnable = PreferenceManager.getDefaultSharedPreferences(App.getApp()).getBoolean(KEY_BOUNDS_ASSIST_ENABLE, false);
}
}

View File

@ -1,4 +1,4 @@
package com.stardust.scriptdroid.droid.assist;
package com.stardust.scriptdroid.bounds_assist;
import android.content.Context;
import android.content.SharedPreferences;

View File

@ -13,14 +13,17 @@ import com.afollestad.materialdialogs.MaterialDialog;
import com.jraska.console.Console;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.ConsoleActivity;
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.ActionPerformService;
import com.stardust.scriptdroid.droid.runtime.action.ActionPerformAccessibilityDelegate;
import com.stardust.scriptdroid.droid.runtime.action.ActionTarget;
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 java.util.Collections;
import java.util.List;
import timber.log.Timber;
@ -166,22 +169,40 @@ public class DroidRuntime implements IDroidRuntime {
}
private boolean performAction(Action action) {
if (ActionPerformService.getInstance() == null) {
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));
}
ActionPerformService.setAction(action);
ActionPerformAccessibilityDelegate.setAction(action);
synchronized (mActionPerformLock) {
try {
mActionPerformLock.wait();
} catch (InterruptedException e) {
ActionPerformService.setActions(ActionPerformService.NO_ACTION);
ActionPerformAccessibilityDelegate.setAction(ActionPerformAccessibilityDelegate.NO_ACTION);
throw new ScriptStopException(App.getApp().getString(R.string.text_script_stopped), e);
}
}
return mActionPerformResult;
}
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);
}
}
}
@Override
public void toast(final String text) {
mUIHandler.post(new Runnable() {

View File

@ -0,0 +1,55 @@
package com.stardust.scriptdroid.droid.runtime.action;
import android.accessibilityservice.AccessibilityService;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import com.stardust.scriptdroid.droid.runtime.DroidRuntime;
import com.stardust.scriptdroid.service.AccessibilityDelegate;
/**
* Created by Stardust on 2017/1/21.
*/
public class ActionPerformAccessibilityDelegate implements AccessibilityDelegate {
private static final String TAG = "ActionPerformDelegate";
public static final Action NO_ACTION = null;
private static Action action;
public static void setAction(Action action) {
synchronized (ActionPerformAccessibilityDelegate.class) {
ActionPerformAccessibilityDelegate.action = action;
}
}
@Override
public boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event) {
if (action == NO_ACTION)
return false;
AccessibilityNodeInfo root = service.getRootInActiveWindow();
if (root == null) {
Log.v(TAG, "root = null");
}
Log.i(TAG, "perform action:" + action);
if (action.perform(root)) {
onActionPerformed(true);
} else if (!action.performUtilSucceed()) {
onActionPerformed(false);
}
return false;
}
private void onActionPerformed(boolean succeed) {
synchronized (ActionPerformAccessibilityDelegate.class) {
action = NO_ACTION;
DroidRuntime.getRuntime().notifyActionPerformed(succeed);
}
}
}

View File

@ -1,123 +0,0 @@
package com.stardust.scriptdroid.droid.runtime.action;
import android.accessibilityservice.AccessibilityService;
import android.os.Build;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.droid.assist.BoundsAssistant;
import com.stardust.scriptdroid.droid.runtime.DroidRuntime;
import com.stardust.view.accessibility.AccessibilityServiceUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* Created by Stardust on 2017/1/21.
*/
public class ActionPerformService extends AccessibilityService {
private static final String TAG = "ActionPerformService";
private static volatile ActionPerformService instance;
@SuppressWarnings("unchecked")
public static final List<Action> NO_ACTION = Collections.EMPTY_LIST;
public static final int STATE_INACTIVE = -1;
public static final int STATE_PERFORM = 0;
public static final List<Action> actions = new ArrayList<>();
private static int state = STATE_INACTIVE;
public static void setActions(Collection<Action> collection) {
synchronized (actions) {
actions.clear();
actions.addAll(collection);
state = actions.size() > 0 ? STATE_PERFORM : STATE_INACTIVE;
}
}
public static void setAction(Action action) {
setActions(Collections.singletonList(action));
}
public static ActionPerformService getInstance() {
return instance;
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
Log.v(TAG, "onAccessibilityEvent: state=" + state + " event=" + event);
BoundsAssistant.performAssistance(event);
if (state == STATE_INACTIVE)
return;
AccessibilityNodeInfo root = getRootInActiveWindow();
if (root == null) {
Log.v(TAG, "root = null");
}
Action action = nextAction();
if (action == null) {
onActionPerformed(true);
} else {
Log.i(TAG, "perform action:" + action);
if (action.perform(root)) {
state++;
} else if (!action.performUtilSucceed()) {
onActionPerformed(false);
}
}
}
private void onActionPerformed(boolean succeed) {
state = STATE_INACTIVE;
synchronized (actions) {
actions.clear();
}
DroidRuntime.getRuntime().notifyActionPerformed(succeed);
}
private Action nextAction() {
synchronized (actions) {
if (state >= actions.size()) {
return null;
}
return actions.get(state);
}
}
@Override
public void onInterrupt() {
}
@Override
public void onServiceConnected() {
// FIXME: 2017/2/12 有时在无障碍中开启服务后这里不会调用服务也不会运行安卓的BUG???
Log.v(TAG, "onServiceConnected");
}
@Override
public void onCreate() {
super.onCreate();
Log.v(TAG, "onCreate");
instance = this;
}
public static void disable() {
if (instance != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
instance.disableSelf();
} else {
AccessibilityServiceUtils.goToAccessibilitySetting(App.getApp());
}
}
public static boolean isEnable() {
return AccessibilityServiceUtils.isAccessibilityServiceEnabled(App.getApp(), ActionPerformService.class);
}
}

View File

@ -0,0 +1,37 @@
package com.stardust.scriptdroid.droid.runtime.action;
import android.view.accessibility.AccessibilityNodeInfo;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Stardust on 2017/2/14.
*/
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;
return true;
}
private void getText(AccessibilityNodeInfo nodeInfo, List<String> texts) {
CharSequence text = nodeInfo.getText();
if (text != null && text.length() != 0) {
texts.add(text.toString());
}
for (int i = 0; i < nodeInfo.getChildCount(); i++) {
AccessibilityNodeInfo child = nodeInfo.getChild(i);
if (child != null) {
getText(child, texts);
child.recycle();
}
}
}
}

View File

@ -11,6 +11,8 @@ import java.io.StringReader;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
/**
* Created by Stardust on 2017/1/27.
@ -70,7 +72,7 @@ public class DuktapeJavaScriptEngine implements JavaScriptEngine {
public void removeAndStop(Thread thread) {
synchronized (mThreadDuktapeEngineMap) {
DuktapeEngine engine = mThreadDuktapeEngineMap.remove(thread);
stop(engine, thread);
forceStop(engine, thread);
}
}
@ -84,14 +86,21 @@ public class DuktapeJavaScriptEngine implements JavaScriptEngine {
}
}
private void stop(DuktapeEngine engine, Thread thread) {
if (engine != null)
engine.destory();
private void forceStop(final DuktapeEngine engine, Thread thread) {
try {
thread.interrupt();
} catch (Exception e) {
e.printStackTrace();
}
if (engine != null) {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
engine.destory();
}
}, 1000);
}
}
private void add(DuktapeEngine duktapeEngine, Thread thread) {
@ -110,7 +119,7 @@ public class DuktapeJavaScriptEngine implements JavaScriptEngine {
int n;
synchronized (mThreadDuktapeEngineMap) {
for (Map.Entry<Thread, DuktapeEngine> entry : mThreadDuktapeEngineMap.entrySet()) {
stop(entry.getValue(), entry.getKey());
forceStop(entry.getValue(), entry.getKey());
}
n = mThreadDuktapeEngineMap.size();
mThreadDuktapeEngineMap.clear();

View File

@ -1,4 +1,4 @@
package com.stardust.scriptdroid.ui;
package com.stardust.scriptdroid.external.notification.bounds_assist;
import android.app.Notification;
import android.app.NotificationManager;
@ -7,9 +7,8 @@ import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.AssistModeSwitchService;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.assist.BoundsAssistant;
import com.stardust.scriptdroid.bounds_assist.BoundsAssistant;
import com.stardust.util.StateObserver;
@ -17,36 +16,35 @@ import com.stardust.util.StateObserver;
* Created by Stardust on 2017/2/2.
*/
public class AssistModeSwitchNotification {
public class BoundsAssistSwitchNotification {
private static final int NOTIFY_ID = 11126;
public static final String KEY_ASSIST_MODE_NOTIFICATION = "KEY_ASSIST_MODE_NOTIFICATION";
public static final String KEY_BOUNDS_ASSIST_SWITCH_NOTIFICATION_ENABLE = "KEY_BOUNDS_ASSIST_SWITCH_NOTIFICATION_ENABLE";
private static boolean enable = false;
private static Notification notification;
public static boolean isEnable() {
return enable;
}
static {
App.getStateObserver().register(KEY_ASSIST_MODE_NOTIFICATION, new StateObserver.OnBooleanStateChangedListener() {
App.getStateObserver().register(KEY_BOUNDS_ASSIST_SWITCH_NOTIFICATION_ENABLE, new StateObserver.OnStateChangedListener() {
@Override
public void onStateChanged(Boolean newState) {
public void onStateChanged(boolean newState) {
setEnable(newState);
}
@Override
public void initState(Boolean state) {
public void initState(boolean state) {
enable = state;
if (enable) {
showNotification();
}
}
});
App.getStateObserver().register(BoundsAssistant.KEY_ASSIST_MODE_ENABLE, new StateObserver.OnBooleanStateChangedListener() {
App.getStateObserver().register(BoundsAssistant.KEY_BOUNDS_ASSIST_ENABLE, new StateObserver.OnStateChangedListener() {
@Override
public void onStateChanged(Boolean newState) {
public void onStateChanged(boolean newState) {
if (enable) {
setEnable(false);
setEnable(true);
@ -54,17 +52,17 @@ public class AssistModeSwitchNotification {
}
@Override
public void initState(Boolean state) {
public void initState(boolean state) {
}
});
}
public static void setEnable(boolean enable) {
if (AssistModeSwitchNotification.enable == enable)
if (BoundsAssistSwitchNotification.enable == enable)
return;
AssistModeSwitchNotification.enable = enable;
PreferenceManager.getDefaultSharedPreferences(App.getApp()).edit().putBoolean(KEY_ASSIST_MODE_NOTIFICATION, enable).apply();
BoundsAssistSwitchNotification.enable = enable;
PreferenceManager.getDefaultSharedPreferences(App.getApp()).edit().putBoolean(KEY_BOUNDS_ASSIST_SWITCH_NOTIFICATION_ENABLE, enable).apply();
if (enable) {
showNotification();
} else {
@ -74,14 +72,14 @@ public class AssistModeSwitchNotification {
private static void showNotification() {
notification = new NotificationCompat.Builder(App.getApp())
Notification notification = new NotificationCompat.Builder(App.getApp())
.setAutoCancel(false)
.setSmallIcon(R.drawable.ic_robot_head)
.setDeleteIntent(AssistModeSwitchService.getDeletePendingIntent())
.setDeleteIntent(BoundsAssistSwitchNotificationHandleService.getDeletePendingIntent())
.setContentText(BoundsAssistant.isAssistModeEnable() ?
App.getApp().getString(R.string.text_assist_mode_enabled) :
App.getApp().getString(R.string.text_assist_mode_disabled))
.setContentIntent(AssistModeSwitchService.getStartIntent())
.setContentIntent(BoundsAssistSwitchNotificationHandleService.getStartIntent())
.build();
showNotification(notification);
}

View File

@ -1,4 +1,4 @@
package com.stardust.scriptdroid;
package com.stardust.scriptdroid.external.notification.bounds_assist;
import android.app.PendingIntent;
import android.app.Service;
@ -6,17 +6,18 @@ import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import com.stardust.scriptdroid.droid.assist.BoundsAssistant;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.bounds_assist.BoundsAssistant;
import static com.stardust.scriptdroid.ui.AssistModeSwitchNotification.KEY_ASSIST_MODE_NOTIFICATION;
import static com.stardust.scriptdroid.external.notification.bounds_assist.BoundsAssistSwitchNotification.KEY_BOUNDS_ASSIST_SWITCH_NOTIFICATION_ENABLE;
/**
* Created by Stardust on 2017/2/2.
*/
public class AssistModeSwitchService extends Service {
public class BoundsAssistSwitchNotificationHandleService extends Service {
private static final String EXTRA_INTENT_VALID = "intentValid";
private static final String EXTRA_INTENT_VALID = "BoundsAssistSwitchNotificationHandleService.intentValid";
private static final String EXTRA_ACTION = "action";
private static final int ACTION_TOGGLE_ASSIST_MODE = 1;
@ -24,7 +25,7 @@ public class AssistModeSwitchService extends Service {
public static PendingIntent getStartIntent() {
Intent intent = new Intent(App.getApp(), AssistModeSwitchService.class)
Intent intent = new Intent(App.getApp(), BoundsAssistSwitchNotificationHandleService.class)
.putExtra(EXTRA_INTENT_VALID, true)
.putExtra(EXTRA_ACTION, ACTION_TOGGLE_ASSIST_MODE);
return PendingIntent.getService(App.getApp(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
@ -32,7 +33,7 @@ public class AssistModeSwitchService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
boolean intentValid = intent.getBooleanExtra("intentValid", false);
boolean intentValid = intent.getBooleanExtra(EXTRA_INTENT_VALID, false);
if (intentValid) {
performAction(intent.getIntExtra(EXTRA_ACTION, 0));
}
@ -46,7 +47,7 @@ public class AssistModeSwitchService extends Service {
BoundsAssistant.setAssistModeEnable(!BoundsAssistant.isAssistModeEnable());
break;
case ACTION_CANCEL_ASSIST_MODE_NOTIFICATION:
App.getStateObserver().setState(KEY_ASSIST_MODE_NOTIFICATION, false);
App.getStateObserver().setState(KEY_BOUNDS_ASSIST_SWITCH_NOTIFICATION_ENABLE, false);
break;
}
}
@ -58,7 +59,7 @@ public class AssistModeSwitchService extends Service {
}
public static PendingIntent getDeletePendingIntent() {
Intent deleteIntent = new Intent(App.getApp(), AssistModeSwitchService.class)
Intent deleteIntent = new Intent(App.getApp(), BoundsAssistSwitchNotificationHandleService.class)
.putExtra(EXTRA_INTENT_VALID, true)
.putExtra(EXTRA_ACTION, ACTION_CANCEL_ASSIST_MODE_NOTIFICATION);
return PendingIntent.getService(App.getApp(), 0, deleteIntent, PendingIntent.FLAG_ONE_SHOT);

View File

@ -0,0 +1,123 @@
package com.stardust.scriptdroid.external.notification.record;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.widget.Toast;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.record.AccessibilityRecorderDelegate;
import com.stardust.scriptdroid.service.AccessibilityWatchDogService;
import com.stardust.scriptdroid.ui.main.MainActivity;
import static com.stardust.scriptdroid.external.notification.record.ActionRecordSwitchView.PAUSED;
import static com.stardust.scriptdroid.external.notification.record.ActionRecordSwitchView.RECORDING;
import static com.stardust.scriptdroid.external.notification.record.ActionRecordSwitchView.STOPPED;
/**
* Created by Stardust on 2017/2/15.
*/
public class ActionRecordNotificationHandleService extends Service {
private static final String EXTRA_INTENT_VALID = "ActionRecordNotificationHandleService.intentValid";
private static final String EXTRA_ACTION = "action";
private static final int ACTION_STOP = 17771;
private static final int ACTION_START_OR_PAUSE = 17772;
private static final int ACTION_DELETE = 17773;
public static PendingIntent getStartOrPauseIntent() {
Intent intent = new Intent(App.getApp(), ActionRecordNotificationHandleService.class)
.putExtra(EXTRA_INTENT_VALID, true)
.putExtra(EXTRA_ACTION, ACTION_START_OR_PAUSE);
return PendingIntent.getService(App.getApp(), ACTION_START_OR_PAUSE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
public static PendingIntent getStopIntent() {
Intent intent = new Intent(App.getApp(), ActionRecordNotificationHandleService.class)
.putExtra(EXTRA_INTENT_VALID, true)
.putExtra(EXTRA_ACTION, ACTION_STOP);
return PendingIntent.getService(App.getApp(), ACTION_STOP, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
public static PendingIntent getDeleteIntent() {
Intent intent = new Intent(App.getApp(), ActionRecordNotificationHandleService.class)
.putExtra(EXTRA_INTENT_VALID, true)
.putExtra(EXTRA_ACTION, ACTION_DELETE);
return PendingIntent.getService(App.getApp(), ACTION_DELETE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
boolean intentValid = intent.getBooleanExtra(EXTRA_INTENT_VALID, false);
if (intentValid) {
performAction(intent.getIntExtra(EXTRA_ACTION, 0));
}
stopSelf();
return super.onStartCommand(intent, flags, startId);
}
private void performAction(int action) {
switch (action) {
case ACTION_STOP:
stopRecord();
break;
case ACTION_START_OR_PAUSE:
startOrPauseRecord();
break;
case ACTION_DELETE:
stopRecordIfNeeded();
}
collapseNotificationBar();
}
private void collapseNotificationBar() {
sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
}
private void stopRecordIfNeeded() {
if (AccessibilityRecorderDelegate.getInstance().getState() != STOPPED) {
stopRecord();
}
}
private void startOrPauseRecord() {
int state = AccessibilityRecorderDelegate.getInstance().getState();
if (state == PAUSED) {
AccessibilityRecorderDelegate.getInstance().resumeRecord();
ActionRecordSwitchView.getInstance().setState(RECORDING);
} else if (state == RECORDING) {
AccessibilityRecorderDelegate.getInstance().pauseRecord();
ActionRecordSwitchView.getInstance().setState(PAUSED);
} else {
//state == STOPPED
if (AccessibilityWatchDogService.getInstance() == null) {
Toast.makeText(this, R.string.text_need_enable_accessibility_service, Toast.LENGTH_SHORT).show();
return;
}
AccessibilityRecorderDelegate.getInstance().startRecord();
ActionRecordSwitchView.getInstance().setState(RECORDING);
Toast.makeText(this, R.string.text_start_record, Toast.LENGTH_SHORT).show();
}
}
private void stopRecord() {
if (AccessibilityRecorderDelegate.getInstance().getState() != STOPPED) {
String script = AccessibilityRecorderDelegate.getInstance().stopRecord();
ActionRecordSwitchView.getInstance().setState(STOPPED);
MainActivity.onActionRecordStopped(this, script);
} else {
Toast.makeText(App.getApp(), R.string.text_not_recording, Toast.LENGTH_SHORT).show();
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}

View File

@ -0,0 +1,36 @@
package com.stardust.scriptdroid.external.notification.record;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.support.v4.app.NotificationCompat;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
/**
* Created by Stardust on 2017/2/14.
*/
public class ActionRecordSwitchNotification {
private static final int NOTIFY_ID = 22236;
private static NotificationCompat.Builder builder;
public static void showOrUpdateNotification() {
if (builder == null) {
builder = new NotificationCompat.Builder(App.getApp())
.setAutoCancel(false)
.setSmallIcon(R.drawable.ic_robot_head)
.setDeleteIntent(ActionRecordNotificationHandleService.getDeleteIntent())
.setCustomContentView(ActionRecordSwitchView.getInstance());
}
showNotification(builder.build());
}
private static void showNotification(Notification notification) {
NotificationManager notificationManager = (NotificationManager) App.getApp().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFY_ID, notification);
}
}

View File

@ -0,0 +1,54 @@
package com.stardust.scriptdroid.external.notification.record;
import android.widget.RemoteViews;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
/**
* Created by Stardust on 2017/2/14.
*/
public class ActionRecordSwitchView extends RemoteViews {
private static ActionRecordSwitchView instance;
public static final int STOPPED = 0;
public static final int PAUSED = 1;
public static final int RECORDING = 2;
public static ActionRecordSwitchView getInstance() {
if (instance == null) {
instance = new ActionRecordSwitchView();
}
return instance;
}
private ActionRecordSwitchView() {
super(App.getApp().getPackageName(), R.layout.remote_views_record_switch);
setUpOnClick();
}
public void setState(int state) {
switch (state) {
case STOPPED:
setImageViewResource(R.id.img_start_or_pause, R.drawable.ic_play_arrow_grey600_48dp);
setTextViewText(R.id.text_start_or_pause, App.getApp().getString(R.string.text_start_record));
break;
case RECORDING:
setImageViewResource(R.id.img_start_or_pause, R.drawable.ic_pause_grey600_48dp);
setTextViewText(R.id.text_start_or_pause, App.getApp().getString(R.string.text_pause_record));
break;
case PAUSED:
setImageViewResource(R.id.img_start_or_pause, R.drawable.ic_play_arrow_grey600_48dp);
setTextViewText(R.id.text_start_or_pause, App.getApp().getString(R.string.text_resume_record));
break;
}
ActionRecordSwitchNotification.showOrUpdateNotification();
}
private void setUpOnClick() {
setOnClickPendingIntent(R.id.stop, ActionRecordNotificationHandleService.getStopIntent());
setOnClickPendingIntent(R.id.start_or_pause, ActionRecordNotificationHandleService.getStartOrPauseIntent());
}
}

View File

@ -1,4 +1,4 @@
package com.stardust.scriptdroid.shortcut;
package com.stardust.scriptdroid.external.shortcut;
import android.content.Context;
import android.content.Intent;

View File

@ -1,18 +1,17 @@
package com.stardust.scriptdroid;
package com.stardust.scriptdroid.external.shortcut;
import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Toast;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.Droid;
import java.io.File;
import java.util.LinkedList;
import java.util.List;
import java.util.function.BooleanSupplier;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;

View File

@ -1,11 +1,11 @@
package com.stardust.scriptdroid.tile;
package com.stardust.scriptdroid.external.tile;
import android.os.Build;
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
import android.support.annotation.RequiresApi;
import com.stardust.scriptdroid.droid.assist.BoundsAssistant;
import com.stardust.scriptdroid.bounds_assist.BoundsAssistant;
/**
* Created by Stardust on 2017/1/26.

View File

@ -1,67 +0,0 @@
package com.stardust.scriptdroid.file;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.Intent;
import android.net.Uri;
import com.stardust.scriptdroid.R;
import java.io.FileNotFoundException;
import java.io.InputStream;
import static android.app.Activity.RESULT_OK;
/**
* Created by Stardust on 2017/1/23.
*/
public class FileChooser {
public interface FileManagerNotFoundHandler {
void handle(ActivityNotFoundException e, String mimeType);
}
public interface OnFileChoseListener {
void onFileChose(InputStream inputStream);
}
private static final int FILE_CHOOSE = 1209;
private Activity mActivity;
private OnFileChoseListener mOnFileChoseListener;
public FileChooser(Activity activity) {
mActivity = activity;
}
public void setOnFileChoseListener(OnFileChoseListener onFileChoseListener) {
mOnFileChoseListener = onFileChoseListener;
}
public void startFileManagerToChoose(String mimeType, FileManagerNotFoundHandler handler) {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType(mimeType);
intent.addCategory(Intent.CATEGORY_OPENABLE);
try {
mActivity.startActivityForResult(Intent.createChooser(intent, mActivity.getString(R.string.text_choose_file)), FILE_CHOOSE);
} catch (ActivityNotFoundException ex) {
handler.handle(ex, mimeType);
}
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == FILE_CHOOSE && resultCode == RESULT_OK) {
Uri uri = data.getData();
ContentResolver cr = mActivity.getContentResolver();
try {
InputStream inputStream = cr.openInputStream(uri);
mOnFileChoseListener.onFileChose(inputStream);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
}

View File

@ -176,4 +176,26 @@ public class FileUtils {
}
public static boolean writeString(String path, String text) {
return writeString(new File(path), text);
}
public static boolean writeString(File file, String text) {
try {
return writeString(new FileOutputStream(file), text);
} catch (FileNotFoundException e) {
e.printStackTrace();
return false;
}
}
public static boolean writeString(OutputStream outputStream, String text) {
try {
outputStream.write(text.getBytes());
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
}

View File

@ -16,7 +16,7 @@ import java.util.Map;
/**
* Created by Stardust on 2017/1/30.
*/
public class SampleFileTool {
public class SampleFileManager {
private static final Map<String, Integer> SAMPLES = new MapEntries<>(new LinkedHashMap<String, Integer>())
.entry("sample_open_running_services.js", R.string.text_sample_open_running_services)
@ -27,9 +27,9 @@ public class SampleFileTool {
.entry("sample_alipay_scan.js", R.string.text_sample_alipay_scan)
.map();
private static SampleFileTool instance = new SampleFileTool();
private static SampleFileManager instance = new SampleFileManager();
public static SampleFileTool getInstance() {
public static SampleFileManager getInstance() {
return instance;
}

View File

@ -0,0 +1,95 @@
package com.stardust.scriptdroid.record;
import android.accessibilityservice.AccessibilityService;
import android.view.accessibility.AccessibilityEvent;
import com.stardust.scriptdroid.service.AccessibilityDelegate;
import static com.stardust.scriptdroid.external.notification.record.ActionRecordSwitchView.PAUSED;
import static com.stardust.scriptdroid.external.notification.record.ActionRecordSwitchView.RECORDING;
import static com.stardust.scriptdroid.external.notification.record.ActionRecordSwitchView.STOPPED;
/**
* Created by Stardust on 2017/2/14.
*/
public class AccessibilityRecorderDelegate implements AccessibilityDelegate {
private static final int PRIORITY = 200;
private static final long RECORD_TIME_OUT = 10 * 60 * 1000;
private static AccessibilityRecorderDelegate instance;
private int mState = STOPPED;
public static AccessibilityRecorderDelegate getInstance() {
if (instance == null) {
instance = new AccessibilityRecorderDelegate();
}
return instance;
}
private ActionRecorder mRecorder = new ActionRecorder();
private long mRecordStartMillis;
public void startRecord() {
if (mState != STOPPED) {
throw new IllegalStateException("Recording");
}
mState = RECORDING;
mRecorder = new ActionRecorder();
mRecordStartMillis = System.currentTimeMillis();
}
public String stopRecord() {
if (mState == STOPPED) {
throw new IllegalStateException("Not recording");
}
mState = STOPPED;
String script = mRecorder.getScript();
mRecorder = null;
return script;
}
public void pauseRecord() {
if (mState != RECORDING) {
throw new IllegalStateException("Not recording");
}
mState = PAUSED;
}
public void resumeRecord() {
if (mState != PAUSED) {
throw new IllegalStateException("Not paused");
}
mRecorder.onResume();
mState = RECORDING;
}
public void stopRecordIfNeeded() {
if (mRecorder != null) {
mRecorder = null;
}
}
public int getState() {
return mState;
}
@Override
public boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event) {
if (mState == RECORDING) {
mRecorder.record(event);
checkTimeOut();
}
return false;
}
private void checkTimeOut() {
if (System.currentTimeMillis() - mRecordStartMillis > RECORD_TIME_OUT) {
stopRecord();
}
}
}

View File

@ -0,0 +1,102 @@
package com.stardust.scriptdroid.record;
import android.os.Build;
import android.util.SparseArray;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import com.stardust.util.SparseArrayEntries;
import static com.stardust.scriptdroid.bounds_assist.BoundsAssistant.boundsToString;
import static com.stardust.scriptdroid.bounds_assist.BoundsAssistant.getBoundsInScreen;
/**
* Created by Stardust on 2017/2/14.
*/
public class ActionRecorder {
private static final SparseArray<EventToScriptConverter> CONVERTER_MAP = new SparseArrayEntries<EventToScriptConverter>()
.entry(AccessibilityEvent.TYPE_VIEW_CLICKED, new DoUtilSucceedConverter("click"))
.entry(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED, new DoUtilSucceedConverter("longClick"))
.entry(AccessibilityEvent.TYPE_VIEW_SCROLLED, new DoOnceConverter("//scroll???"))
.sparseArray();
static {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
CONVERTER_MAP.put(AccessibilityEvent.TYPE_VIEW_CONTEXT_CLICKED, new DoOnceConverter("contextClick"));
}
}
private StringBuilder mScript = new StringBuilder();
private boolean mFirstAction = true;
public void record(AccessibilityEvent event) {
EventToScriptConverter converter = CONVERTER_MAP.get(event.getEventType());
if (converter != null) {
if (mFirstAction) {
mFirstAction = false;
return;
}
converter.onAccessibilityEvent(event, mScript);
mScript.append("\n");
}
}
public String getScript() {
return mScript.toString();
}
public void onResume() {
mFirstAction = true;
}
interface EventToScriptConverter {
void onAccessibilityEvent(AccessibilityEvent event, StringBuilder sb);
}
private static abstract class BoundsEventConverter implements EventToScriptConverter {
@Override
public void onAccessibilityEvent(AccessibilityEvent event, StringBuilder sb) {
AccessibilityNodeInfo source = event.getSource();
String bounds = boundsToString(getBoundsInScreen(source));
source.recycle();
onAccessibilityEvent(event, bounds, sb);
}
protected abstract void onAccessibilityEvent(AccessibilityEvent event, String bounds, StringBuilder sb);
}
private static class DoOnceConverter extends BoundsEventConverter {
private String mActionFunction;
DoOnceConverter(String actionFunction) {
mActionFunction = actionFunction;
}
@Override
protected void onAccessibilityEvent(AccessibilityEvent event, String bounds, StringBuilder sb) {
sb.append(mActionFunction).append(bounds).append(";");
}
}
private static class DoUtilSucceedConverter extends BoundsEventConverter {
private String mActionFunction;
DoUtilSucceedConverter(String actionFunction) {
mActionFunction = actionFunction;
}
@Override
protected void onAccessibilityEvent(AccessibilityEvent event, String bounds, StringBuilder sb) {
sb.append("while(!").append(mActionFunction).append(bounds).append(");");
}
}
}

View File

@ -0,0 +1,14 @@
package com.stardust.scriptdroid.service;
import android.accessibilityservice.AccessibilityService;
import android.view.accessibility.AccessibilityEvent;
/**
* Created by Stardust on 2017/2/14.
*/
public interface AccessibilityDelegate {
boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event);
}

View File

@ -0,0 +1,99 @@
package com.stardust.scriptdroid.service;
import android.accessibilityservice.AccessibilityService;
import android.os.Build;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import com.stardust.scriptdroid.App;
import com.stardust.view.accessibility.AccessibilityServiceUtils;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* Created by Stardust on 2017/2/14.
*/
public class AccessibilityWatchDogService extends AccessibilityService {
private static final String TAG = "AccessibilityWatchDog";
private static final SortedMap<Integer, AccessibilityDelegate> mDelegates = new TreeMap<>();
private static AccessibilityWatchDogService instance;
public static void addDelegate(AccessibilityDelegate delegate, int uniquePriority) {
synchronized (mDelegates) {
mDelegates.put(uniquePriority, delegate);
}
}
public static boolean containsPriority(int priority) {
synchronized (mDelegates) {
return mDelegates.containsKey(priority);
}
}
public static AccessibilityDelegate getDelegate(int priority) {
synchronized (mDelegates) {
return mDelegates.get(priority);
}
}
public static void addDelegateIfNeeded(int priority, Class<? extends AccessibilityDelegate> delegateClass) {
try {
addDelegateIfNeeded(priority, delegateClass.newInstance());
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public static void addDelegateIfNeeded(int priority, AccessibilityDelegate delegate) {
synchronized (mDelegates) {
if (!mDelegates.containsKey(priority)) {
mDelegates.put(priority, delegate);
}
}
}
public static boolean isEnable() {
return AccessibilityServiceUtils.isAccessibilityServiceEnabled(App.getApp(), AccessibilityWatchDogService.class);
}
public static AccessibilityWatchDogService getInstance() {
return instance;
}
@Override
public synchronized void onAccessibilityEvent(AccessibilityEvent event) {
Log.v(TAG, "onAccessibilityEvent: " + event);
synchronized (mDelegates) {
for (Map.Entry<Integer, AccessibilityDelegate> entry : mDelegates.entrySet()) {
if (entry.getValue().onAccessibilityEvent(this, event))
break;
}
}
}
@Override
public void onInterrupt() {
}
@Override
protected void onServiceConnected() {
super.onServiceConnected();
// FIXME: 2017/2/12 有时在无障碍中开启服务后这里不会调用服务也不会运行安卓的BUG???
Log.v(TAG, "onServiceConnected");
instance = this;
}
public static void disable() {
if (instance != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
instance.disableSelf();
} else {
AccessibilityServiceUtils.goToAccessibilitySetting(App.getApp());
}
}
}

View File

@ -47,4 +47,5 @@ public class IntentTool {
.putExtra(Intent.EXTRA_TEXT, text)
.setType("text/plain"));
}
}

View File

@ -1,4 +1,4 @@
package com.stardust.scriptdroid;
package com.stardust.scriptdroid.ui;
import android.os.Build;
import android.os.Bundle;
@ -9,6 +9,8 @@ import android.support.v7.widget.Toolbar;
import android.view.View;
import com.stardust.scriptdroid.R;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

View File

@ -1,4 +1,4 @@
package com.stardust.scriptdroid.droid;
package com.stardust.scriptdroid.ui.console;
import android.content.Context;
import android.graphics.Color;
@ -10,7 +10,7 @@ import android.view.MenuItem;
import android.widget.TextView;
import com.jraska.console.Console;
import com.stardust.scriptdroid.BaseActivity;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.stardust.scriptdroid.R;
/**

View File

@ -1,4 +1,4 @@
package com.stardust.scriptdroid;
package com.stardust.scriptdroid.ui.edit;
import android.content.Context;
import android.content.Intent;
@ -18,12 +18,14 @@ import com.jecelyin.editor.v2.common.SaveListener;
import com.jecelyin.editor.v2.ui.EditorDelegate;
import com.jecelyin.editor.v2.view.EditorView;
import com.jecelyin.editor.v2.view.menu.MenuDef;
import com.stardust.scriptdroid.Pref;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.Droid;
import com.stardust.scriptdroid.editor920.Editor920Activity;
import com.stardust.scriptdroid.ui.AssistClipListRecyclerView;
import com.stardust.scriptdroid.ui.EditSideMenuFragment;
import com.stardust.scriptdroid.ui.FunctionListRecyclerView;
import com.stardust.scriptdroid.widget.ToolbarMenuItem;
import com.stardust.scriptdroid.ui.edit.sidemenu.AssistClipListRecyclerView;
import com.stardust.scriptdroid.ui.edit.sidemenu.EditSideMenuFragment;
import com.stardust.scriptdroid.ui.edit.sidemenu.FunctionListRecyclerView;
import com.stardust.scriptdroid.ui.edit.editor920.Editor920Activity;
import com.stardust.widget.ToolbarMenuItem;
import com.stardust.util.SparseArrayEntries;
import com.stardust.view.ViewBinder;
import com.stardust.view.ViewBinding;

View File

@ -1,12 +1,13 @@
package com.stardust.scriptdroid;
package com.stardust.scriptdroid.ui.edit;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.widget.Toast;
import com.stardust.scriptdroid.BaseActivity;
import com.stardust.scriptdroid.EditActivity;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.stardust.scriptdroid.R;
/**
* Created by Stardust on 2017/2/2.
@ -17,7 +18,12 @@ public class EditAndRunIntentActivity extends BaseActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handleIntent();
try {
handleIntent();
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, R.string.edit_and_run_handle_intent_error, Toast.LENGTH_LONG).show();
}
}
private void handleIntent() {

View File

@ -1,4 +1,4 @@
package com.stardust.scriptdroid;
package com.stardust.scriptdroid.ui.edit;
import android.content.Intent;
import android.os.Bundle;
@ -7,8 +7,11 @@ import android.support.annotation.Nullable;
import android.text.TextUtils;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.script.file.ScriptFile;
import com.stardust.scriptdroid.droid.script.file.SharedPrefScriptFileList;
import com.stardust.scriptdroid.ui.main.MainActivity;
import java.io.File;

View File

@ -1,4 +1,4 @@
package com.stardust.scriptdroid.editor920;
package com.stardust.scriptdroid.ui.edit.editor920;
import android.support.annotation.NonNull;

View File

@ -1,4 +1,4 @@
package com.stardust.scriptdroid.ui;
package com.stardust.scriptdroid.ui.edit.sidemenu;
import android.bug.WrapContentLinearLayoutManager;
import android.content.Context;
@ -11,9 +11,9 @@ import android.view.ViewGroup;
import android.widget.TextView;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.assist.BoundsAssistClipList;
import com.stardust.scriptdroid.droid.assist.SharedPrefBoundsAssistClipList;
import com.stardust.scriptdroid.widget.ExpandableRecyclerView;
import com.stardust.scriptdroid.bounds_assist.BoundsAssistClipList;
import com.stardust.scriptdroid.bounds_assist.SharedPrefBoundsAssistClipList;
import com.stardust.widget.ExpandableRecyclerView;
/**
* Created by Stardust on 2017/2/4.

View File

@ -1,4 +1,4 @@
package com.stardust.scriptdroid.ui;
package com.stardust.scriptdroid.ui.edit.sidemenu;
import android.content.Intent;
import android.os.Bundle;
@ -10,14 +10,15 @@ import android.view.View;
import android.view.ViewGroup;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.DocumentActivity;
import com.stardust.scriptdroid.ui.help.DocumentActivity;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.ConsoleActivity;
import com.stardust.scriptdroid.droid.assist.BoundsAssistant;
import com.stardust.scriptdroid.ui.console.ConsoleActivity;
import com.stardust.scriptdroid.bounds_assist.BoundsAssistant;
import com.stardust.scriptdroid.external.notification.bounds_assist.BoundsAssistSwitchNotification;
import com.stardust.view.ViewBinder;
import com.stardust.view.ViewBinding;
import static com.stardust.scriptdroid.ui.AssistModeSwitchNotification.KEY_ASSIST_MODE_NOTIFICATION;
import static com.stardust.scriptdroid.external.notification.bounds_assist.BoundsAssistSwitchNotification.KEY_BOUNDS_ASSIST_SWITCH_NOTIFICATION_ENABLE;
/**
* Created by Stardust on 2017/2/4.
@ -72,14 +73,14 @@ public class EditSideMenuFragment extends com.stardust.app.Fragment {
private void syncSwitchState() {
mAssistServiceSwitch.setChecked(BoundsAssistant.isAssistModeEnable());
mAssistServiceNotificationSwitch.setChecked(AssistModeSwitchNotification.isEnable());
mAssistServiceNotificationSwitch.setChecked(BoundsAssistSwitchNotification.isEnable());
}
private void setUpSwitchCompat() {
mAssistServiceSwitch = $(R.id.sw_assist_service);
mAssistServiceNotificationSwitch = $(R.id.sw_assist_service_notification);
App.getStateObserver().register(BoundsAssistant.KEY_ASSIST_MODE_ENABLE, mAssistServiceSwitch);
App.getStateObserver().register(KEY_ASSIST_MODE_NOTIFICATION, mAssistServiceNotificationSwitch);
App.getStateObserver().register(BoundsAssistant.KEY_BOUNDS_ASSIST_ENABLE, mAssistServiceSwitch);
App.getStateObserver().register(KEY_BOUNDS_ASSIST_SWITCH_NOTIFICATION_ENABLE, mAssistServiceNotificationSwitch);
}
@ViewBinding.Click(R.id.syntax_and_api)
@ -104,7 +105,7 @@ public class EditSideMenuFragment extends com.stardust.app.Fragment {
@ViewBinding.Check(R.id.sw_assist_service_notification)
private void setAssistServiceNotificationEnable(boolean enable) {
AssistModeSwitchNotification.setEnable(enable);
BoundsAssistSwitchNotification.setEnable(enable);
}
@ViewBinding.Click(R.id.assist_service_notification)

View File

@ -1,4 +1,4 @@
package com.stardust.scriptdroid.ui;
package com.stardust.scriptdroid.ui.edit.sidemenu;
import android.bug.WrapContentLinearLayoutManager;
import android.content.Context;
@ -13,7 +13,7 @@ import android.widget.TextView;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.file.FileUtils;
import com.stardust.scriptdroid.widget.ExpandableRecyclerView;
import com.stardust.widget.ExpandableRecyclerView;
import java.text.Collator;
import java.util.ArrayList;

View File

@ -1,4 +1,4 @@
package com.stardust.scriptdroid;
package com.stardust.scriptdroid.ui.error;
import android.content.ClipData;
import android.content.ClipboardManager;
@ -13,6 +13,8 @@ import android.widget.Toast;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.stardust.scriptdroid.R;
import java.util.Timer;
import java.util.TimerTask;

View File

@ -0,0 +1,131 @@
package com.stardust.scriptdroid.ui.error;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import com.heinrichreimersoftware.androidissuereporter.IssueReporterActivity;
import com.heinrichreimersoftware.androidissuereporter.model.github.GithubTarget;
import com.stardust.scriptdroid.R;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Created by Stardust on 2017/2/13.
*/
public class IssueReportActivity extends IssueReporterActivity {
private boolean mCrash = false;
private Method mReportIssue, mValidateInput;
private boolean mReportFailed = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handleIntent();
setUpToobar();
hookSendClick();
}
private void hookSendClick() {
FloatingActionButton send = (FloatingActionButton) this.findViewById(com.heinrichreimersoftware.androidissuereporter.R.id.air_buttonSend);
send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
reportIssue();
} catch (Exception e) {
mReportFailed = true;
e.printStackTrace();
finish();
}
}
});
}
private void setUpToobar() {
getSupportActionBar().setTitle(R.string.text_issue_report);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
Toolbar toolbar = (Toolbar) findViewById(R.id.air_toolbar);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
private void handleIntent() {
final String errorDetail = getIntent().getStringExtra("error");
if (errorDetail != null) {
((EditText) findViewById(R.id.air_inputDescription)).setText(errorDetail);
((EditText) findViewById(R.id.air_inputTitle)).setText(R.string.text_crash_en);
mCrash = true;
}
}
private boolean validateInput() {
if (mValidateInput == null) {
try {
mValidateInput = IssueReporterActivity.class.getDeclaredMethod("validateInput");
mValidateInput.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
try {
return mValidateInput != null && (Boolean) mValidateInput.invoke(this);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return false;
}
private void reportIssue() {
if (mReportIssue == null) {
try {
mReportIssue = IssueReporterActivity.class.getDeclaredMethod("reportIssue");
mReportIssue.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
try {
mReportIssue.invoke(this);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
@Override
public void finish() {
if (mCrash) {
if (!mReportFailed) {
Toast.makeText(IssueReportActivity.this, R.string.text_report_succeed, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(IssueReportActivity.this, R.string.text_report_fail, Toast.LENGTH_SHORT).show();
}
finishAffinity();
} else {
super.finish();
}
}
@Override
protected GithubTarget getTarget() {
return new GithubTarget("hyb1996", "NoRootScriptDroid");
}
@Override
protected String getGuestToken() {
return "f32d789662645640ff22e240b80f5d76117181a1";
}
}

View File

@ -1,11 +1,13 @@
package com.stardust.scriptdroid;
package com.stardust.scriptdroid.ui.help;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.view.View;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.file.FileUtils;
import com.stardust.view.MarkdownView;
import com.stardust.widget.MarkdownView;
/**
* Created by Stardust on 2017/2/1.

View File

@ -1,8 +1,10 @@
package com.stardust.scriptdroid;
package com.stardust.scriptdroid.ui.main;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@ -16,32 +18,41 @@ import android.support.v7.widget.Toolbar;
import android.text.InputType;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.afollestad.materialdialogs.folderselector.FileChooserDialog;
import com.stardust.app.NotRemindAgainDialog;
import com.stardust.scriptdroid.droid.runtime.action.ActionPerformService;
import com.stardust.scriptdroid.BuildConfig;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.runtime.action.ActionPerformAccessibilityDelegate;
import com.stardust.scriptdroid.droid.script.file.ScriptFile;
import com.stardust.scriptdroid.droid.script.file.ScriptFileList;
import com.stardust.scriptdroid.droid.script.file.SharedPrefScriptFileList;
import com.stardust.scriptdroid.external.notification.record.ActionRecordSwitchNotification;
import com.stardust.scriptdroid.file.FileUtils;
import com.stardust.scriptdroid.file.SampleFileTool;
import com.stardust.scriptdroid.file.SampleFileManager;
import com.stardust.scriptdroid.service.AccessibilityWatchDogService;
import com.stardust.scriptdroid.tool.BackPressedHandler;
import com.stardust.scriptdroid.ui.ScriptFileOperation;
import com.stardust.scriptdroid.ui.ScriptListRecyclerView;
import com.stardust.scriptdroid.ui.SlideMenuFragment;
import com.stardust.scriptdroid.ui.SlidingUpPanel;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.stardust.scriptdroid.ui.main.operation.ScriptFileOperation;
import com.stardust.scriptdroid.ui.settings.SettingsActivity;
import com.stardust.view.ViewBinder;
import com.stardust.view.ViewBinding;
import com.stardust.view.accessibility.AccessibilityServiceUtils;
import com.stardust.widget.SlidingUpPanel;
import java.io.File;
public class MainActivity extends BaseActivity implements FileChooserDialog.FileCallback {
private static final String EXTRA_ACTION = "EXTRA_ACTION";
public static final String ACTION_NOTIFY_SCRIPT_LIST_CHANGE = "ACTION_NOTIFY_SCRIPT_LIST_CHANGE";
private static final String ACTION_ON_ACTION_RECORD_STOPPED = "ACTION_ON_ACTION_RECORD_STOPPED";
private static final String ARGUMENT_SCRIPT = "ARGUMENT_SCRIPT";
private SlidingUpPanel mAddFilePanel;
private ScriptListRecyclerView mScriptListRecyclerView;
@ -55,6 +66,7 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
setUpUI();
checkPermissions();
registerReceivers();
handleIntent(getIntent());
}
private void registerReceivers() {
@ -68,7 +80,7 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
}
private void goToAccessibilityPermissionSettingIfDisabled() {
if (!AccessibilityServiceUtils.isAccessibilityServiceEnabled(this, ActionPerformService.class)) {
if (!AccessibilityServiceUtils.isAccessibilityServiceEnabled(this, ActionPerformAccessibilityDelegate.class)) {
new NotRemindAgainDialog.Builder(this, "goToAccessibilityPermissionSettingIfDisabled")
.title(R.string.text_alert)
.content(R.string.explain_accessibility_permission)
@ -142,19 +154,29 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
@ViewBinding.Click(R.id.create_new_file)
private void createScriptFile() {
createScriptFileForScript(null);
}
private void createScriptFileForScript(final String script) {
new MaterialDialog.Builder(this).title(R.string.text_name)
.inputType(InputType.TYPE_CLASS_TEXT)
.input(getString(R.string.text_please_input_name), "", new MaterialDialog.InputCallback() {
@Override
public void onInput(@NonNull MaterialDialog dialog, CharSequence input) {
String path = ScriptFile.DEFAULT_FOLDER + input + ".js";
MainActivity.this.createScriptFile(input.toString(), path);
MainActivity.this.createScriptFile(input.toString(), path, script);
}
}).show();
}
private void createScriptFile(String name, String path) {
private void createScriptFile(String name, String path, String script) {
if (FileUtils.createFileIfNotExists(path)) {
if (script != null) {
if (!FileUtils.writeString(path, script)) {
Snackbar.make(mDrawerLayout, R.string.text_file_write_fail, Snackbar.LENGTH_LONG).show();
}
}
addScriptFile(name, path);
new ScriptFileOperation.Edit().operate(mScriptListRecyclerView, mScriptFileList, mScriptFileList.size() - 1);
} else {
@ -170,6 +192,16 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
.show();
}
@ViewBinding.Click(R.id.record)
private void startScriptRecord() {
if (AccessibilityWatchDogService.getInstance() == null) {
Snackbar.make(mDrawerLayout, R.string.text_need_enable_accessibility_service, Snackbar.LENGTH_SHORT).show();
return;
}
ActionRecordSwitchNotification.showOrUpdateNotification();
Snackbar.make(mDrawerLayout, R.string.hint_start_record, Snackbar.LENGTH_SHORT).show();
}
@ViewBinding.Click(R.id.setting)
private void startSettingActivity() {
startActivity(new Intent(this, SettingsActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
@ -184,7 +216,7 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == PERMISSION_REQUEST_CODE && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
SampleFileTool.getInstance().copySampleScriptFileIfNeeded();
SampleFileManager.getInstance().copySampleScriptFileIfNeeded();
mScriptListRecyclerView.getAdapter().notifyDataSetChanged();
}
}
@ -194,6 +226,42 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
addScriptFile(file.getPath());
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
handleIntent(intent);
}
private void handleIntent(Intent intent) {
String action = intent.getStringExtra(EXTRA_ACTION);
if (action == null)
return;
switch (action) {
case ACTION_ON_ACTION_RECORD_STOPPED:
handleRecordedScript(intent.getStringExtra(ARGUMENT_SCRIPT));
break;
}
}
private void handleRecordedScript(final String script) {
new MaterialDialog.Builder(this)
.title(R.string.text_recorded)
.items(getString(R.string.text_new_file), getString(R.string.text_copy_to_clip))
.itemsCallback(new MaterialDialog.ListCallback() {
@Override
public void onSelection(MaterialDialog dialog, View itemView, int position, CharSequence text) {
if (position == 0) {
createScriptFileForScript(script);
} else {
((ClipboardManager) getSystemService(CLIPBOARD_SERVICE))
.setPrimaryClip(ClipData.newPlainText("script", script));
Toast.makeText(MainActivity.this, R.string.text_already_copy_to_clip, Toast.LENGTH_SHORT).show();
}
}
})
.show();
}
private BackPressedHandler mBackPressedHandler = new BackPressedHandler.DoublePressExit(this);
@ -214,6 +282,14 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
unregisterReceiver(mReceiver);
}
public static void onActionRecordStopped(Context context, String script) {
Intent intent = new Intent(context, MainActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
.putExtra(EXTRA_ACTION, ACTION_ON_ACTION_RECORD_STOPPED)
.putExtra(ARGUMENT_SCRIPT, script);
context.startActivity(intent);
}
private class Receiver extends BroadcastReceiver {
@Override

View File

@ -1,4 +1,4 @@
package com.stardust.scriptdroid.ui;
package com.stardust.scriptdroid.ui.main;
import android.content.Context;
import android.os.Environment;
@ -16,11 +16,13 @@ import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.script.file.ScriptFile;
import com.stardust.scriptdroid.droid.script.file.ScriptFileList;
import com.stardust.scriptdroid.ui.main.operation.ScriptFileOperation;
import com.stardust.scriptdroid.ui.main.operation.ScriptFileOperationPopupMenu;
import com.stardust.scriptdroid.tool.ClassTool;
import static com.stardust.scriptdroid.tool.ViewTool.$;
import static com.stardust.scriptdroid.ui.ScriptFileOperation.*;
import static com.stardust.scriptdroid.ui.main.operation.ScriptFileOperation.*;
/**
* Created by Stardust on 2017/1/23.

View File

@ -1,4 +1,4 @@
package com.stardust.scriptdroid.ui;
package com.stardust.scriptdroid.ui.main;
import android.content.Intent;
import android.os.Bundle;
@ -12,17 +12,18 @@ import android.view.ViewGroup;
import com.stardust.app.Fragment;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.DocumentActivity;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.ConsoleActivity;
import com.stardust.scriptdroid.bounds_assist.BoundsAssistant;
import com.stardust.scriptdroid.droid.Droid;
import com.stardust.scriptdroid.droid.assist.BoundsAssistant;
import com.stardust.scriptdroid.droid.runtime.action.ActionPerformService;
import com.stardust.scriptdroid.external.notification.bounds_assist.BoundsAssistSwitchNotification;
import com.stardust.scriptdroid.service.AccessibilityWatchDogService;
import com.stardust.scriptdroid.ui.console.ConsoleActivity;
import com.stardust.scriptdroid.ui.help.DocumentActivity;
import com.stardust.view.ViewBinder;
import com.stardust.view.ViewBinding;
import com.stardust.view.accessibility.AccessibilityServiceUtils;
import static com.stardust.scriptdroid.ui.AssistModeSwitchNotification.KEY_ASSIST_MODE_NOTIFICATION;
import static com.stardust.scriptdroid.external.notification.bounds_assist.BoundsAssistSwitchNotification.KEY_BOUNDS_ASSIST_SWITCH_NOTIFICATION_ENABLE;
/**
* Created by Stardust on 2017/1/30.
@ -59,19 +60,19 @@ public class SlideMenuFragment extends Fragment {
mAutoOperateServiceSwitch.postDelayed(new Runnable() {
@Override
public void run() {
mAutoOperateServiceSwitch.setChecked(ActionPerformService.isEnable());
mAutoOperateServiceSwitch.setChecked(AccessibilityWatchDogService.isEnable());
}
}, 450);
mAssistServiceSwitch.setChecked(BoundsAssistant.isAssistModeEnable());
mAssistServiceNotificationSwitch.setChecked(AssistModeSwitchNotification.isEnable());
mAssistServiceNotificationSwitch.setChecked(BoundsAssistSwitchNotification.isEnable());
}
private void setUpSwitchCompat() {
mAutoOperateServiceSwitch = $(R.id.sw_auto_operate_service);
mAssistServiceSwitch = $(R.id.sw_assist_service);
mAssistServiceNotificationSwitch = $(R.id.sw_assist_service_notification);
App.getStateObserver().register(BoundsAssistant.KEY_ASSIST_MODE_ENABLE, mAssistServiceSwitch);
App.getStateObserver().register(KEY_ASSIST_MODE_NOTIFICATION, mAssistServiceNotificationSwitch);
App.getStateObserver().register(BoundsAssistant.KEY_BOUNDS_ASSIST_ENABLE, mAssistServiceSwitch);
App.getStateObserver().register(KEY_BOUNDS_ASSIST_SWITCH_NOTIFICATION_ENABLE, mAssistServiceNotificationSwitch);
}
@ -92,10 +93,10 @@ public class SlideMenuFragment extends Fragment {
@ViewBinding.Check(R.id.sw_auto_operate_service)
private void setAutoOperateServiceEnable(boolean enable) {
if (enable && !ActionPerformService.isEnable()) {
if (enable && !AccessibilityWatchDogService.isEnable()) {
AccessibilityServiceUtils.goToAccessibilitySetting(getContext());
} else if (!enable && ActionPerformService.isEnable()) {
ActionPerformService.disable();
} else if (!enable && AccessibilityWatchDogService.isEnable()) {
AccessibilityWatchDogService.disable();
}
}
@ -111,7 +112,7 @@ public class SlideMenuFragment extends Fragment {
@ViewBinding.Check(R.id.sw_assist_service_notification)
private void setAssistServiceNotificationEnable(boolean enable) {
AssistModeSwitchNotification.setEnable(enable);
BoundsAssistSwitchNotification.setEnable(enable);
}
@ViewBinding.Click(R.id.assist_service_notification)

View File

@ -1,4 +1,4 @@
package com.stardust.scriptdroid.ui;
package com.stardust.scriptdroid.ui.main.operation;
import android.content.Context;
import android.content.Intent;
@ -8,12 +8,13 @@ import android.support.design.widget.Snackbar;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.EditActivity;
import com.stardust.scriptdroid.ui.edit.EditActivity;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.ShortcutActivity;
import com.stardust.scriptdroid.external.shortcut.ShortcutActivity;
import com.stardust.scriptdroid.droid.script.file.ScriptFile;
import com.stardust.scriptdroid.droid.script.file.ScriptFileList;
import com.stardust.scriptdroid.shortcut.Shortcut;
import com.stardust.scriptdroid.external.shortcut.Shortcut;
import com.stardust.scriptdroid.ui.main.ScriptListRecyclerView;
import java.util.ArrayList;
import java.util.List;

View File

@ -1,4 +1,4 @@
package com.stardust.scriptdroid.ui;
package com.stardust.scriptdroid.ui.main.operation;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;

View File

@ -1,16 +1,21 @@
package com.stardust.scriptdroid;
package com.stardust.scriptdroid.ui.settings;
import android.annotation.SuppressLint;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.stardust.scriptdroid.BuildConfig;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.tool.IntentTool;
import com.stardust.view.ViewBinder;
import com.stardust.view.ViewBinding;
@ -23,6 +28,8 @@ import moe.feng.alipay.zerosdk.AlipayZeroSdk;
public class AboutActivity extends BaseActivity {
private int mLolClickCount = 0;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -61,6 +68,7 @@ public class AboutActivity extends BaseActivity {
@ViewBinding.Click(R.id.qq)
private void openQQToChatWithMe() {
Toast.makeText(this, R.string.text_qq_group_id_copied, Toast.LENGTH_SHORT).show();
String qq = getString(R.string.qq);
IntentTool.goToQQ(this, qq);
}
@ -99,7 +107,19 @@ public class AboutActivity extends BaseActivity {
@ViewBinding.Click(R.id.icon)
private void lol() {
mLolClickCount++;
Toast.makeText(this, R.string.text_lll, Toast.LENGTH_LONG).show();
if (mLolClickCount >= 5) {
new MaterialDialog.Builder(this)
.title("Crash Test")
.positiveText("Crash")
.onPositive(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
throw new RuntimeException("Crash Test");
}
}).show();
}
}
@ViewBinding.Click(R.id.developer)

View File

@ -1,5 +1,7 @@
package com.stardust.scriptdroid;
package com.stardust.scriptdroid.ui.settings;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Intent;
import android.os.Bundle;
import android.preference.Preference;
@ -9,7 +11,11 @@ import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Toast;
import com.stardust.scriptdroid.file.SampleFileTool;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.file.SampleFileManager;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.stardust.scriptdroid.ui.error.IssueReportActivity;
import com.stardust.scriptdroid.ui.main.MainActivity;
import com.stardust.util.MapEntries;
import java.util.Map;
@ -66,7 +72,7 @@ public class SettingsActivity extends BaseActivity {
.entry(getString(R.string.text_re_import_samples), new Runnable() {
@Override
public void run() {
int failCount = SampleFileTool.getInstance().copySampleScriptFile();
int failCount = SampleFileManager.getInstance().copySampleScriptFile();
if (failCount <= 0) {
Toast.makeText(getActivity(), R.string.text_re_import_succeed, Toast.LENGTH_SHORT).show();
notifyScriptListChanged();
@ -74,6 +80,19 @@ public class SettingsActivity extends BaseActivity {
Toast.makeText(getActivity(), R.string.text_fail, Toast.LENGTH_SHORT).show();
}
})
.entry(getString(R.string.text_issue_report), new Runnable() {
@Override
public void run() {
startActivity(new Intent(getActivity(), IssueReportActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
})
.entry(getString(R.string.text_join_qq_group), new Runnable() {
@Override
public void run() {
((ClipboardManager) getActivity().getSystemService(CLIPBOARD_SERVICE)).setPrimaryClip(ClipData.newPlainText("qq", "556928653"));
Toast.makeText(getActivity(), R.string.text_qq_group_id_copied, Toast.LENGTH_SHORT).show();
}
})
.entry(getString(R.string.text_about), new Runnable() {
@Override
public void run() {

View File

@ -15,30 +15,18 @@ import java.util.concurrent.CopyOnWriteArrayList;
public class StateObserver {
public interface OnStateChangedListener<T> {
public interface OnStateChangedListener {
void onStateChanged(T newState);
void onStateChanged(boolean newState);
void initState(T state);
void initState(boolean state);
}
public static abstract class SimpleOnStateChangedListener<T> implements OnStateChangedListener<T> {
public static abstract class SimpleOnStateChangedListener<T> implements OnStateChangedListener {
@Override
public void initState(T state) {
onStateChanged(state);
}
}
public interface OnBooleanStateChangedListener extends OnStateChangedListener<Boolean> {
}
public static abstract class SimpleOnBooleanStateChangedListener implements OnBooleanStateChangedListener {
@Override
public void initState(Boolean state) {
public void initState(boolean state) {
onStateChanged(state);
}
}
@ -53,9 +41,9 @@ public class StateObserver {
public void register(final String key, SwitchCompat switchCompat) {
final WeakReference<SwitchCompat> switchCompatWeakReference = new WeakReference<>(switchCompat);
register(key, new SimpleOnBooleanStateChangedListener() {
register(key, new SimpleOnStateChangedListener() {
@Override
public void onStateChanged(Boolean newState) {
public void onStateChanged(boolean newState) {
if (switchCompatWeakReference.get() != null) {
switchCompatWeakReference.get().setChecked(newState);
} else {
@ -65,10 +53,8 @@ public class StateObserver {
});
}
public <T> void register(String key, OnStateChangedListener<T> listener) {
T initialState = readState(key, listener);
if (initialState != null)
listener.initState(initialState);
public void register(String key, OnStateChangedListener listener) {
initState(key, listener);
synchronized (mKeyStateListenersMap) {
List<OnStateChangedListener> listeners = getListenerListOrCreateIfNotExists(key);
listeners.add(listener);
@ -76,7 +62,7 @@ public class StateObserver {
}
private <T> void unregister(String key, OnStateChangedListener<T> stateChangedListener) {
private void unregister(String key, OnStateChangedListener stateChangedListener) {
synchronized (mKeyStateListenersMap) {
List<OnStateChangedListener> listeners = mKeyStateListenersMap.get(key);
if (listeners == null) {
@ -86,15 +72,13 @@ public class StateObserver {
}
}
public <T> void setState(String key, T state) {
public void setState(String key, boolean state) {
synchronized (mKeyStateListenersMap) {
List<OnStateChangedListener> listeners = mKeyStateListenersMap.get(key);
if (listeners == null || listeners.isEmpty())
return;
if (listeners.get(0) instanceof OnBooleanStateChangedListener) {
mSharedPreferences.edit().putBoolean(key, (Boolean) state).apply();
notifyBooleanStateChanged(listeners, (Boolean) state);
}
mSharedPreferences.edit().putBoolean(key, state).apply();
notifyBooleanStateChanged(listeners, state);
}
}
@ -104,12 +88,10 @@ public class StateObserver {
}
}
@SuppressWarnings("unchecked")
protected <T> T readState(String key, OnStateChangedListener<T> listener) {
if (listener instanceof OnBooleanStateChangedListener) {
return mSharedPreferences.contains(key) ? (T) Boolean.valueOf(mSharedPreferences.getBoolean(key, false)) : null;
private void initState(String key, OnStateChangedListener listener) {
if (mSharedPreferences.contains(key)) {
listener.initState(mSharedPreferences.getBoolean(key, false));
}
return null;
}
private List<OnStateChangedListener> getListenerListOrCreateIfNotExists(String key) {

View File

@ -1,8 +0,0 @@
package com.stardust.util.function;
/**
* Created by Stardust on 2017/1/24.
*/
public class ArrayOptional {
}

View File

@ -1,8 +0,0 @@
package com.stardust.util.function;
/**
* Created by Stardust on 2017/1/26.
*/
public class Domino {
}

View File

@ -1,15 +0,0 @@
package com.stardust.util.function;
import android.support.annotation.RequiresApi;
import java.util.Optional;
import java.util.function.Consumer;
/**
* Created by Stardust on 2017/1/24.
*/
public class FunctionTool {
}

View File

@ -1,24 +0,0 @@
package com.stardust.util.function;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Stardust on 2017/1/26.
*/
public class ListTool {
@FunctionalInterface
public interface ChildSupplier<T> {
T getChild(int i);
}
public static <T> List<T> toList(ChildSupplier<T> supplier, int size) {
ArrayList<T> arrayList = new ArrayList<T>(size);
for (int i = 0; i < size - 1; i++) {
arrayList.add(supplier.getChild(i));
}
return arrayList;
}
}

View File

@ -1,4 +1,4 @@
package com.stardust.scriptdroid.widget;
package com.stardust.widget;
import android.content.Context;
import android.support.annotation.Nullable;

View File

@ -1,4 +1,4 @@
package com.stardust.view;
package com.stardust.widget;
import android.content.Context;
import android.os.Build;

View File

@ -1,4 +1,4 @@
package com.stardust.scriptdroid.ui;
package com.stardust.widget;
import android.annotation.TargetApi;
import android.content.Context;

View File

@ -1,4 +1,4 @@
package com.stardust.scriptdroid.widget;
package com.stardust.widget;
import android.annotation.TargetApi;
import android.content.Context;

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<color android:color="#cccccc"/>
</item>
<item>
<color android:color="@android:color/transparent"/>
</item>
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 608 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 835 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@ -22,7 +22,7 @@
<view
android:id="@+id/console"
class="com.stardust.scriptdroid.droid.ConsoleActivity$ConsoleView"
class="com.stardust.scriptdroid.ui.console.ConsoleActivity$ConsoleView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

View File

@ -26,7 +26,7 @@
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
<com.stardust.view.MarkdownView
<com.stardust.widget.MarkdownView
android:id="@+id/markdown"
android:layout_width="match_parent"
android:layout_height="match_parent"

View File

@ -6,7 +6,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".EditActivity">
tools:context=".ui.edit.EditActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
@ -27,7 +27,7 @@
android:layout_gravity="right"
android:orientation="horizontal">
<com.stardust.scriptdroid.widget.ToolbarMenuItem
<com.stardust.widget.ToolbarMenuItem
android:id="@+id/run"
android:layout_width="40dp"
android:layout_height="match_parent"
@ -35,21 +35,21 @@
app:icon_color="@android:color/white"
app:text="@string/text_run"/>
<com.stardust.scriptdroid.widget.ToolbarMenuItem
<com.stardust.widget.ToolbarMenuItem
android:id="@+id/undo"
android:layout_width="40dp"
android:layout_height="match_parent"
app:icon="@drawable/ic_undo_white_48dp"
app:text="@string/text_undo"/>
<com.stardust.scriptdroid.widget.ToolbarMenuItem
<com.stardust.widget.ToolbarMenuItem
android:id="@+id/redo"
android:layout_width="40dp"
android:layout_height="match_parent"
app:icon="@drawable/ic_redo_white_48dp"
app:text="@string/text_redo"/>
<com.stardust.scriptdroid.widget.ToolbarMenuItem
<com.stardust.widget.ToolbarMenuItem
android:id="@+id/save"
android:layout_width="40dp"
android:layout_height="match_parent"

View File

@ -5,7 +5,7 @@
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.stardust.scriptdroid.MainActivity">
tools:context=".ui.main.MainActivity">
<android.support.design.widget.CoordinatorLayout
@ -41,7 +41,7 @@
<include layout="@layout/content_main"/>
<com.stardust.scriptdroid.ui.SlidingUpPanel
<com.stardust.widget.SlidingUpPanel
android:id="@+id/bottom_menu"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -107,9 +107,35 @@
</LinearLayout>
<LinearLayout
android:id="@+id/record"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="60dp"
android:layout_height="60dp"
android:padding="10dp"
android:src="@drawable/ic_video_record"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/text_script_record"
android:textColor="@color/colorPrimary"
android:textSize="16sp"/>
</LinearLayout>
</LinearLayout>
</com.stardust.scriptdroid.ui.SlidingUpPanel>
</com.stardust.widget.SlidingUpPanel>
</android.support.design.widget.CoordinatorLayout>

View File

@ -7,11 +7,11 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.stardust.scriptdroid.MainActivity"
tools:context=".ui.main.MainActivity"
tools:showIn="@layout/activity_main">
<com.stardust.scriptdroid.ui.ScriptListRecyclerView
<com.stardust.scriptdroid.ui.main.ScriptListRecyclerView
android:id="@+id/script_list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

View File

@ -116,12 +116,12 @@
</RelativeLayout>
<com.stardust.scriptdroid.ui.FunctionListRecyclerView
<com.stardust.scriptdroid.ui.edit.sidemenu.FunctionListRecyclerView
android:id="@+id/function_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<com.stardust.scriptdroid.ui.AssistClipListRecyclerView
<com.stardust.scriptdroid.ui.edit.sidemenu.AssistClipListRecyclerView
android:id="@+id/assist_clip_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

View File

@ -0,0 +1,68 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="64dp"
android:background="@android:color/white"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_margin="8dp"
android:src="@drawable/ic_android_eat_js"/>
<LinearLayout
android:id="@+id/start_or_pause"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_margin="4dp"
android:background="@drawable/btn_selector"
android:clickable="true"
android:gravity="center_horizontal"
android:orientation="vertical"
android:padding="4dp">
<ImageView
android:id="@+id/img_start_or_pause"
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/ic_play_arrow_grey600_48dp"/>
<TextView
android:id="@+id/text_start_or_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/text_start_record"
android:textColor="#777777"
android:textSize="12sp"/>
</LinearLayout>
<LinearLayout
android:id="@+id/stop"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_margin="4dp"
android:background="@drawable/btn_selector"
android:clickable="true"
android:gravity="center_horizontal"
android:orientation="vertical"
android:padding="4dp">
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/ic_stop_grey600_48dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/text_stop_record"
android:textColor="#777777"
android:textSize="12sp"/>
</LinearLayout>
</LinearLayout>

View File

@ -8,7 +8,7 @@
<view
android:id="@+id/operation_list"
class="com.stardust.scriptdroid.ui.ScriptFileOperationPopupMenu$ScriptFileOperationListRecyclerView"
class="com.stardust.scriptdroid.ui.main.operation.ScriptFileOperationPopupMenu$ScriptFileOperationListRecyclerView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>

View File

@ -1,6 +1,6 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.stardust.scriptdroid.MainActivity">
tools:context=".ui.main.MainActivity">
</menu>

View File

@ -25,6 +25,18 @@
* `notStopped` 若当前脚本处于运行状态时返回`true`, 否则返回`false`。对于某些循环, 例如`while(true)`,请用`while(notStopped())`代替,以免死循环造成的脚本无法正常停止。
* `isStoppd` 若当前脚本处于停止状态时返回`true`, 否则返回`false`。
* `shell(cmd, root=false)` 执行shell命令cmd, 其中参数root表示是否以root权限执行默认为false。例如`shell("input keyevent 26", true); //锁屏`。
* `getTexts()` 获取屏幕上的文字列表, 返回一个java.util.List<String>。例如:
```
launchApp("微信");
while(!click("通讯录"));
var texts = getTexts();
for(var i = 0; i < texts.size(); i++){
log(texts.get(i));
}
openConsole();
```
###四、全局变量
* `context` ApplicationContext参见安卓[android.content.Context](https://developer.android.com/reference/android/content/Context.html)
> 这里的context由于是ApplicationContext是不可见的不能用于dialog和其他UI相关。如果要显示弹窗或者视图请启动UI模式代码的第一行为`"ui";`既可并使用activity代替。

View File

@ -0,0 +1,36 @@
<resources>
<string name="air_title_report_issue">错误报告</string>
<string name="air_title_issue">问题</string>
<string name="air_title_login">登录</string>
<string name="air_label_issue_title">标题</string>
<string name="air_label_issue_description">描述</string>
<string name="air_label_device_info">设备信息</string>
<string name="air_label_use_account">使用Github账号发送</string>
<string name="air_label_use_email">使用Email发送</string>
<string name="air_label_use_guest">匿名发送</string>
<string name="air_label_username">用户名</string>
<string name="air_label_password">密码</string>
<string name="air_label_email">邮箱</string>
<string name="air_label_email_optional">联系邮箱 (选填)</string>
<string name="air_error_no_title">请输入问题</string>
<string name="air_error_no_description">请输入详细描述</string>
<string name="air_error_no_username">请输入有效的Github用户名</string>
<string name="air_error_no_password">请输入正确的密码</string>
<string name="air_error_no_email">请输入邮箱</string>
<string name="air_dialog_title_loading">正在上传到GitHub…</string>
<string name="air_dialog_title_failed">无法发送错误报告</string>
<string name="air_dialog_description_failed_wrong_credentials">错误的用户名或密码</string>
<string name="air_dialog_description_failed_invalid_token">无效的access token. 请联系软件开发者.</string>
<string name="air_dialog_description_failed_issues_not_available">Issues不可用请联系软件开发者.</string>
<string name="air_dialog_description_failed_unknown">未知错误</string>
<string name="air_dialog_action_failed">OK</string>
<plurals name="air_error_short_description">
<item quantity="one">描述至少要 %d 个字.</item>
<item quantity="other">描述至少要 %d 个字.</item>
</plurals>
</resources>

View File

@ -99,4 +99,20 @@
<string name="text_bug_report">Bug Report</string>
<string name="text_report_fail">提交失败</string>
<string name="text_report_succeed">提交成功</string>
<string name="edit_and_run_handle_intent_error">无法处理文件</string>
<string name="text_crash_en">Crash</string>
<string name="text_issue_report">问题反馈</string>
<string name="text_script_record">录制脚本</string>
<string name="hint_start_record">在通知栏中开始录制</string>
<string name="text_start_record">开始录制</string>
<string name="text_stop_record">停止录制</string>
<string name="text_not_recording">录制未开始</string>
<string name="text_pause_record">暂停录制</string>
<string name="text_resume_record">继续录制</string>
<string name="text_recorded">录制结束</string>
<string name="text_copy_to_clip">复制到剪贴板</string>
<string name="text_file_write_fail">文件写入失败</string>
<string name="text_need_enable_accessibility_service">需要打开\"自动操作服务“才能录制脚本</string>
<string name="text_join_qq_group">加入QQ交流群</string>
<string name="text_qq_group_id_copied">已复制群号到剪贴板</string>
</resources>

View File

@ -11,6 +11,12 @@
<PreferenceCategory
android:title="@string/text_about">
<Preference
android:title="@string/text_issue_report"/>
<Preference
android:title="@string/text_join_qq_group"/>
<Preference
android:title="@string/text_about"/>

View File

@ -2,6 +2,11 @@ package com.stardust.scriptdroid;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* Example local unit test, which will execute on the development machine (host).
*
@ -9,7 +14,70 @@ import org.junit.Test;
*/
public class ExampleUnitTest {
@Test
public void testStack() throws Exception {
public void testSync() throws Exception {
final Sync sync = new Sync();
//sync.print(11);
add(sync, 11, 123);
//Thread.sleep(1);
sync.print(11);
add(sync, 11, 456);
sync.print(11);
}
private void add(final Sync sync, final int key, final int i) {
new Thread(new Runnable() {
@Override
public void run() {
sync.add(key, i);
}
}).start();
}
private static class Sync {
private final Map<Integer, List<Integer>> mMap = new TreeMap<>();
Sync() {
for (int i = 0; i < 10; i++) {
List<Integer> list = new ArrayList<>();
for (int j = 0; j < 10; j++) {
list.add(j * i);
}
mMap.put(i, list);
}
}
public Sync add(int key, int i) {
synchronized (mMap) {
List<Integer> list = ensureList(key);
list.add(i);
System.out.println("add:" + key + " " + i);
}
return this;
}
private List<Integer> ensureList(int key) {
List<Integer> list = mMap.get(key);
if (list == null) {
list = new ArrayList<>();
mMap.put(key, list);
}
return list;
}
public void print(int key) {
synchronized (mMap) {
List<Integer> list = mMap.get(key);
System.out.print(key + ":");
if (list == null) {
System.out.print("null");
} else {
for (Integer i : list) {
System.out.print(i + " ");
}
}
System.out.println();
}
}
}
}