Add: tasker plugin support, injectable webview

This commit is contained in:
hyb1996
2017-04-02 12:41:00 +08:00
parent 121949f9c0
commit e7500e6e21
127 changed files with 5293 additions and 1667 deletions

View File

@@ -44,4 +44,6 @@ public abstract class Fragment extends android.support.v4.app.Fragment {
@Nullable
public abstract View createView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState);
}

View File

@@ -0,0 +1,31 @@
package com.stardust.pio;
/**
* Created by Stardust on 2017/4/1.
*/
public class PFile {
public static PFile open(String path, String mode) {
switch (mode){
case "r":
return new PReadableFile(path);
case "w":
return new PWritableFile();
}
return null;
}
public static void create(String path){
}
public static void createIfNotExists(String path){
}
public static void delete(String path){
}
}

View File

@@ -0,0 +1,106 @@
package com.stardust.pio;
import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Stardust on 2017/4/1.
*/
public class PReadableFile extends PFile {
private BufferedReader mBufferedReader;
private FileInputStream mFileInputStream;
private int mBufferingSize;
private String mEncoding;
public PReadableFile(String path) {
this(path, Charset.defaultCharset().name());
}
public PReadableFile(String path, String encoding) {
this(path, encoding, -1);
}
public PReadableFile(String path, String encoding, int bufferingSize) {
mEncoding = encoding;
mBufferingSize = bufferingSize;
try {
mFileInputStream = new FileInputStream(path);
} catch (FileNotFoundException e) {
throw new UncheckedIOException(e);
}
}
private void ensureBufferReader() {
if (mBufferedReader == null) {
try {
if (mBufferingSize == -1)
mBufferedReader = new BufferedReader(new InputStreamReader(mFileInputStream, mEncoding));
else
mBufferedReader = new BufferedReader(new InputStreamReader(mFileInputStream, mEncoding), mBufferingSize);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
public String read() {
try {
byte[] data = new byte[mFileInputStream.available()];
mFileInputStream.read(data);
return new String(data, mEncoding);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public String read(int size) {
ensureBufferReader();
try {
char[] chars = new char[size];
int len = mBufferedReader.read(chars);
return new String(chars, 0, len);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public String readline() {
ensureBufferReader();
try {
return mBufferedReader.readLine();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public String[] readlines() {
ensureBufferReader();
List<String> lines = new ArrayList<>();
try {
while (mBufferedReader.ready()) {
lines.add(mBufferedReader.readLine());
}
return lines.toArray(new String[lines.size()]);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public void close() {
try {
if (mBufferedReader != null) {
mBufferedReader.close();
} else {
mFileInputStream.close();
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}

View File

@@ -0,0 +1,15 @@
package com.stardust.pio;
/**
* Created by Stardust on 2017/4/1.
*/
public class PWritableFile extends PFile {
public PWritableFile(){
}
}

View File

@@ -0,0 +1,14 @@
package com.stardust.pio;
import java.io.IOException;
/**
* Created by Stardust on 2017/4/1.
*/
public class UncheckedIOException extends RuntimeException {
public UncheckedIOException(IOException cause) {
super(cause);
}
}

View File

@@ -13,6 +13,7 @@ import com.stardust.scriptdroid.droid.script.NodeJsJavaScriptEngine;
import com.stardust.scriptdroid.droid.script.RhinoJavaScriptEngine;
import com.stardust.scriptdroid.droid.script.file.ScriptFileList;
import com.stardust.scriptdroid.droid.script.file.SharedPrefScriptFileList;
import com.stardust.scriptdroid.droid.script.file.StorageScriptFileList;
import com.stardust.scriptdroid.layout_inspector.LayoutInspector;
import com.stardust.scriptdroid.record.accessibility.AccessibilityActionRecorder;
import com.stardust.scriptdroid.service.AccessibilityWatchDogService;

View File

@@ -62,7 +62,7 @@ public class Pref {
return def().getBoolean(getString(R.string.key_use_volume_control_record), false);
}
public static boolean isRunningVolumeControlEnable() {
public static boolean isRunningVolumeControlEnabled() {
return def().getBoolean(getString(R.string.key_use_volume_control_running), false);
}

View File

@@ -2,6 +2,7 @@ package com.stardust.scriptdroid.droid;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.Pref;
@@ -14,6 +15,7 @@ import com.stardust.scriptdroid.tool.FileUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Serializable;
import java.text.DateFormat;
import java.util.Date;
@@ -66,7 +68,7 @@ public class Droid {
private VolumeChangeObverseService.OnVolumeChangeListener mOnVolumeChangeListener = new VolumeChangeObverseService.OnVolumeChangeListener() {
@Override
public void onVolumeChange() {
if (Pref.isRunningVolumeControlEnable()) {
if (Pref.isRunningVolumeControlEnabled()) {
stopAllAndToast();
}
}
@@ -92,19 +94,17 @@ public class Droid {
}
public void runScriptFile(File file) {
runScriptFile(file, null);
runScriptFile(file, null, new RunningConfig());
}
public void runScriptFile(File file, OnRunFinishedListener listener) {
Timber.v(DateFormat.getTimeInstance().format(new Date()) + " " + App.getResString(R.string.text_start_running) + " " + file);
public void runScriptFile(File file, OnRunFinishedListener listener, RunningConfig config) {
Timber.v(DateFormat.getTimeInstance().format(new Date()) + " " + App.getResString(R.string.text_start_running) + " " + file);
listener = listener == null ? DEFAULT_LISTENER : listener;
try {
checkFile(file);
} catch (Exception e) {
listener.onException(e);
return;
int errorMsgId = PathChecker.check(file.getPath());
if(errorMsgId != PathChecker.CHECK_RESULT_OK){
listener.onException(new IOException(App.getResString(errorMsgId)));
}
runScript(FileUtils.readString(file), listener, RunningConfig.getDefault());
runScript(FileUtils.readString(file), listener, config.path(file.getPath()));
}
@@ -112,13 +112,15 @@ public class Droid {
runScriptFile(new File(path));
}
private void runScript(String script) {
public void runScript(String script) {
runScript(script, null, RunningConfig.getDefault());
}
public void runScript(final String script, OnRunFinishedListener listener, RunningConfig config) {
public void runScript(String script, OnRunFinishedListener listener, RunningConfig config) {
App.getApp().startService(new Intent(App.getApp(), VolumeChangeObverseService.class));
listener = listener == null ? DEFAULT_LISTENER : listener;
if (!TextUtils.isEmpty(config.prepareScript))
script = config.prepareScript + "\n" + script;
if (config.runInNewThread) {
if (script.startsWith(UI)) {
ScriptExecuteActivity.runScript(script, listener, config);
@@ -145,19 +147,6 @@ public class Droid {
RUNTIME.toast(App.getResString(R.string.text_no_running_script));
}
private void checkFile(File file) {
if (file == null) {
throw new NullPointerException("file = null");
}
if (!file.exists()) {
throw new RuntimeException(new FileNotFoundException(file.getAbsolutePath()));
}
if (!file.canRead()) {
throw new RuntimeException("file is not readable: path=" + file.getAbsolutePath());
}
}
private static class RunScriptRunnable implements Runnable {
private final String mScript;

View File

@@ -0,0 +1,64 @@
package com.stardust.scriptdroid.droid;
import android.app.Activity;
import android.os.Build;
import android.text.TextUtils;
import android.widget.Toast;
import com.stardust.scriptdroid.R;
import java.io.File;
import java.util.LinkedList;
import java.util.List;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
/**
* Created by Stardust on 2017/4/1.
*/
public class PathChecker {
public static final int CHECK_RESULT_OK = 0;
private Activity mActivity;
public PathChecker(Activity activity) {
mActivity = activity;
}
public static int check(final String path) {
if (TextUtils.isEmpty(path))
return R.string.text_path_is_empty;
if (!new File(path).exists())
return R.string.text_file_not_exists;
return CHECK_RESULT_OK;
}
public boolean checkAndToastError(String path) {
int result = checkWithStoragePermission(path);
if (result != CHECK_RESULT_OK) {
Toast.makeText(mActivity, result, Toast.LENGTH_SHORT).show();
return false;
}
return true;
}
private int checkWithStoragePermission(String path) {
if (!hasStorageReadPermission(mActivity)) {
return R.string.text_no_file_rw_permission;
}
return check(path);
}
private static boolean hasStorageReadPermission(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
activity.checkSelfPermission(READ_EXTERNAL_STORAGE) == PERMISSION_GRANTED;
}
return true;
}
}

View File

@@ -9,30 +9,27 @@ import android.content.Context;
public class RunningConfig {
private static final RunningConfig RUNNING_CONFIG = new RunningConfig();
public String path;
public String prepareScript = "";
public static RunningConfig getDefault() {
return RUNNING_CONFIG;
}
public boolean runInNewThread = true;
public Activity activity;
public Context context;
public RunningConfig runInNewThread(boolean runInNewThread) {
this.runInNewThread = runInNewThread;
return this;
}
public RunningConfig activity(Activity activity) {
this.activity = activity;
this.context = activity;
public RunningConfig path(String path) {
this.path = path;
return this;
}
public RunningConfig context(Context context) {
this.context = context;
public RunningConfig prepareScript(String script) {
this.prepareScript = script;
return this;
}
}

View File

@@ -1,5 +1,6 @@
package com.stardust.scriptdroid.droid.runtime;
import android.accessibilityservice.AccessibilityService;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
@@ -15,14 +16,17 @@ import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import com.jraska.console.timber.ConsoleTree;
import com.stardust.automator.AccessibilityEventCommandHost;
import com.stardust.scriptdroid.Pref;
import com.stardust.scriptdroid.accessibility.AccessibilityInfoProvider;
import com.stardust.scriptdroid.droid.runtime.action.ActionTarget;
import com.stardust.scriptdroid.droid.runtime.api.UiSelector;
import com.stardust.scriptdroid.tool.AccessibilityServiceTool;
import com.stardust.scriptdroid.service.AccessibilityWatchDogService;
import com.stardust.scriptdroid.tool.IntentTool;
@@ -63,6 +67,21 @@ public class DroidRuntime {
.build());
}
private static class PerformGlobalActionCommand implements AccessibilityEventCommandHost.Command {
boolean result;
private int mGlobalAction;
PerformGlobalActionCommand(int globalAction) {
mGlobalAction = globalAction;
}
@Override
public void execute(AccessibilityService service, AccessibilityEvent event) {
result = service.performGlobalAction(mGlobalAction);
}
}
private static final String TAG = "DroidRuntime";
private static DroidRuntime runtime = new DroidRuntime();
@@ -103,7 +122,7 @@ public class DroidRuntime {
return "";
}
public boolean openAppSetting(String packageName){
public boolean openAppSetting(String packageName) {
return IntentTool.goToAppSetting(App.getApp(), packageName);
}
@@ -176,6 +195,105 @@ public class DroidRuntime {
});
}
public boolean back() {
return performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
}
public boolean home() {
return performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME);
}
public boolean powerDialog() {
return performGlobalAction(AccessibilityService.GLOBAL_ACTION_POWER_DIALOG);
}
public boolean notifications() {
return performGlobalAction(AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS);
}
public boolean quickSettings() {
return performGlobalAction(AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS);
}
public boolean recents() {
return performGlobalAction(AccessibilityService.GLOBAL_ACTION_RECENTS);
}
public boolean splitScreen() {
return performGlobalAction(AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
}
public boolean swipeDown() {
return performGlobalAction(AccessibilityService.GESTURE_SWIPE_DOWN);
}
public boolean swipeDownLeft() {
return performGlobalAction(AccessibilityService.GESTURE_SWIPE_DOWN_AND_LEFT);
}
public boolean swipeDownRight() {
return performGlobalAction(AccessibilityService.GESTURE_SWIPE_DOWN_AND_RIGHT);
}
public boolean swipeDownUp() {
return performGlobalAction(AccessibilityService.GESTURE_SWIPE_DOWN_AND_UP);
}
public boolean swipeUp() {
return performGlobalAction(AccessibilityService.GESTURE_SWIPE_UP);
}
public boolean swipeUpLeft() {
return performGlobalAction(AccessibilityService.GESTURE_SWIPE_UP_AND_LEFT);
}
public boolean swipeUpRight() {
return performGlobalAction(AccessibilityService.GESTURE_SWIPE_UP_AND_RIGHT);
}
public boolean swipeUpDown() {
return performGlobalAction(AccessibilityService.GESTURE_SWIPE_UP_AND_DOWN);
}
public boolean swipeLeft() {
return performGlobalAction(AccessibilityService.GESTURE_SWIPE_LEFT);
}
public boolean swipeLeftRight() {
return performGlobalAction(AccessibilityService.GESTURE_SWIPE_LEFT_AND_RIGHT);
}
public boolean swipeLeftUp() {
return performGlobalAction(AccessibilityService.GESTURE_SWIPE_LEFT_AND_UP);
}
public boolean swipeLeftDown() {
return performGlobalAction(AccessibilityService.GESTURE_SWIPE_LEFT_AND_DOWN);
}
public boolean swipeRight() {
return performGlobalAction(AccessibilityService.GESTURE_SWIPE_RIGHT);
}
public boolean swipeRightLeft() {
return performGlobalAction(AccessibilityService.GESTURE_SWIPE_RIGHT_AND_LEFT);
}
public boolean swipeRightUp() {
return performGlobalAction(AccessibilityService.GESTURE_SWIPE_RIGHT_AND_UP);
}
public boolean swipeRightDown() {
return performGlobalAction(AccessibilityService.GESTURE_SWIPE_RIGHT_AND_DOWN);
}
private boolean performGlobalAction(final int action) {
ensureAccessibilityServiceEnabled();
PerformGlobalActionCommand command = new PerformGlobalActionCommand(action);
AccessibilityEventCommandHost.getInstance().executeAndWaitForEvent(command);
return command.result;
}
public boolean paste(ActionTarget target) {
return performAction(target.createAction(AccessibilityNodeInfo.ACTION_PASTE));
}

View File

@@ -0,0 +1,140 @@
package com.stardust.scriptdroid.droid.runtime.api;
import android.annotation.SuppressLint;
import android.util.Log;
import android.util.Pair;
import android.webkit.JavascriptInterface;
import android.webkit.ValueCallback;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.stardust.scriptdroid.droid.runtime.ScriptStopException;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import java.util.LinkedList;
import java.util.Queue;
/**
* Created by Stardust on 2017/4/1.
*/
public class InjectableWebClient extends WebViewClient {
private static final String TAG = "InjectableWebClient";
private Queue<Pair<String, ValueCallback<String>>> mToInjectJavaScripts = new LinkedList<>();
private final ValueCallback<String> defaultCallback = new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
Log.i(TAG, "onReceiveValue: " + value);
}
};
private WebView mWebView;
private Context mContext;
private Scriptable mScriptable;
private ScriptBridge mScriptBridge = new ScriptBridge();
public InjectableWebClient(Context context, Scriptable scriptable) {
mContext = context;
mScriptable = scriptable;
}
@Override
public void onPageFinished(WebView view, String url) {
mWebView = view;
setUpWebView(view);
while (!mToInjectJavaScripts.isEmpty()) {
Pair<String, ValueCallback<String>> pair = mToInjectJavaScripts.poll();
inject(view, pair.first, pair.second);
}
super.onPageFinished(view, url);
}
@SuppressLint("SetJavaScriptEnabled")
private void setUpWebView(WebView view) {
view.addJavascriptInterface(mScriptBridge, "rhino");
WebSettings webSettings = view.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowUniversalAccessFromFileURLs(true);
}
private void inject(WebView view, String script, ValueCallback<String> callback) {
view.evaluateJavascript(script, callback);
}
public void inject(String script, ValueCallback<String> callback) {
if (mWebView != null) {
inject(mWebView, script, callback);
return;
}
mToInjectJavaScripts.offer(new Pair<>(script, callback));
}
public void inject(String script) {
inject(script, defaultCallback);
}
public String injectAndWait(String script) {
InjectReturnCallback callback = new InjectReturnCallback();
inject(script, callback);
return callback.waitResult();
}
private class ScriptBridge {
private Object result;
@JavascriptInterface
public String eval(final String script) {
result = null;
mWebView.post(new Runnable() {
@Override
public void run() {
Log.v(TAG, "ScriptBridge.eval: " + script);
result = mContext.evaluateString(mScriptable, script, "<eval-local>", 1, null);
Log.v(TAG, "ScriptBridge.eval = " + result);
synchronized (ScriptBridge.this) {
ScriptBridge.this.notify();
}
}
});
synchronized (ScriptBridge.this) {
try {
ScriptBridge.this.wait();
} catch (InterruptedException e) {
throw new ScriptStopException(e);
}
}
return result.toString();
}
}
private static class InjectReturnCallback implements ValueCallback<String> {
private String result;
@Override
public void onReceiveValue(String value) {
result = value;
synchronized (this) {
this.notify();
}
}
String waitResult() {
synchronized (this) {
try {
this.wait();
} catch (InterruptedException e) {
throw new ScriptStopException(e);
}
}
return result;
}
}
}

View File

@@ -0,0 +1,39 @@
package com.stardust.scriptdroid.droid.runtime.api;
import android.content.Context;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.util.Pair;
import android.webkit.ValueCallback;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import org.mozilla.javascript.Scriptable;
/**
* Created by Stardust on 2017/4/1.
*/
public class InjectableWebView extends WebView {
private InjectableWebClient mInjectableWebClient;
public InjectableWebView(Context context, org.mozilla.javascript.Context jsCtx, Scriptable scriptable) {
super(context);
init(jsCtx, scriptable);
}
private void init(org.mozilla.javascript.Context jsCtx, Scriptable scriptable) {
mInjectableWebClient = new InjectableWebClient(jsCtx, scriptable);
setWebViewClient(mInjectableWebClient);
}
public void inject(String script, ValueCallback<String> callback) {
mInjectableWebClient.inject(script, callback);
}
public void inject(String script) {
mInjectableWebClient.inject(script);
}
}

View File

@@ -1,4 +1,4 @@
package com.stardust.scriptdroid.droid.runtime;
package com.stardust.scriptdroid.droid.runtime.api;
/**
* Created by Stardust on 2017/3/7.

View File

@@ -1,4 +1,4 @@
package com.stardust.scriptdroid.droid.runtime;
package com.stardust.scriptdroid.droid.runtime.api;
import android.accessibilityservice.AccessibilityService;
import android.support.annotation.NonNull;
@@ -12,6 +12,7 @@ import com.stardust.automator.UiObject;
import com.stardust.automator.UiObjectCollection;
import com.stardust.automator.filter.DfsFilter;
import com.stardust.scriptdroid.accessibility.AccessibilityInfoProvider;
import com.stardust.scriptdroid.droid.runtime.DroidRuntime;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ARGUMENT_COLUMN_INT;

View File

@@ -1,120 +0,0 @@
package com.stardust.scriptdroid.droid.script;
/**
* Created by Stardust on 2017/1/27.
*/
/*
public class DuktapeJavaScriptEngine extends JavaScriptEngine {
private final Map<Thread, DuktapeEngine> mThreadDuktapeEngineMap = new Hashtable<>();
private static final String INIT_SCRIPT = parse(Init.getInitScript());
private Map<String, Object> mVariableMap = new HashMap<>();
public DuktapeJavaScriptEngine(IDroidRuntime runtime) {
setRuntime(runtime);
}
private void setRuntime(IDroidRuntime runtime) {
set("droid", runtime);
}
@Override
public Object execute(String script) {
DuktapeEngine duktapeEngine = new DuktapeEngine();
init(duktapeEngine);
add(duktapeEngine, Thread.currentThread());
Object code = duktapeEngine.execute(parse(script));
if (!script.startsWith(Droid.UI) && !script.startsWith(Droid.STAY))
removeAndDestroy();
return code;
}
private static String parse(String script) {
try {
return JSTransformer.parse(new StringReader(script));
} catch (IOException e) {
//Should not happen
throw new RuntimeException(e);
}
}
private void init(DuktapeEngine duktapeEngine) {
duktapeEngine.put("context", App.getApp());
duktapeEngine.execute(INIT_SCRIPT);
for (Map.Entry<String, Object> variable : mVariableMap.entrySet()) {
duktapeEngine.put(variable.getKey(), variable.getValue());
}
}
@Override
public void removeAndDestroy() {
synchronized (mThreadDuktapeEngineMap) {
DuktapeEngine engine = mThreadDuktapeEngineMap.remove(Thread.currentThread());
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) {
synchronized (mThreadDuktapeEngineMap) {
mThreadDuktapeEngineMap.put(thread, duktapeEngine);
}
}
@Override
public int stopAll() {
int n;
synchronized (mThreadDuktapeEngineMap) {
for (Map.Entry<Thread, DuktapeEngine> entry : mThreadDuktapeEngineMap.entrySet()) {
forceStop(entry.getValue(), entry.getKey());
}
n = mThreadDuktapeEngineMap.size();
mThreadDuktapeEngineMap.clear();
}
return n;
}
private static final Field ptrField;
static {
Field field = null;
try {
field = DuktapeEngine.class.getDeclaredField("ptr");
field.setAccessible(true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
ptrField = field;
}
public static boolean isDestroyed(DuktapeEngine engine) {
try {
return ((long) ptrField.get(engine)) == 0;
} catch (IllegalAccessException e) {
return true;
}
}
}
*/

View File

@@ -1,10 +1,5 @@
package com.stardust.scriptdroid.droid.script;
import android.net.Uri;
import com.iwebpp.node.NodeContext;
import com.iwebpp.node.js.rhino.Host;
import com.iwebpp.nodeandroid.Toaster;
import com.stardust.scriptdroid.droid.Droid;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.droid.runtime.DroidRuntime;
@@ -17,12 +12,15 @@ import org.mozilla.javascript.ImporterTopLevel;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.commonjs.module.RequireBuilder;
import org.mozilla.javascript.commonjs.module.provider.ModuleSource;
import org.mozilla.javascript.commonjs.module.provider.SoftCachingModuleScriptProvider;
import org.mozilla.javascript.commonjs.module.provider.UrlModuleSourceProvider;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.util.ArrayList;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
@@ -83,16 +81,16 @@ public class RhinoJavaScriptEngine extends JavaScriptEngine {
ScriptableObject.putProperty(scope, "context", App.getApp());
ScriptableObject.putProperty(scope, "__engine__", "rhino");
for (Map.Entry<String, Object> variable : mVariableMap.entrySet()) {
ScriptableObject.putProperty(scope, variable.getKey(), variable.getValue());
ScriptableObject.putProperty(scope, variable.getKey(), Context.javaToJS(variable.getValue(), scope));
}
}
private void initRequireBuilder(Context context, Scriptable scope) {
List<URI> paths = Collections.singletonList(new File(ScriptFile.DEFAULT_FOLDER).toURI());
List<URI> list = Collections.singletonList(new File(ScriptFile.DEFAULT_DIRECTORY_PATH).toURI());
AssetAndUrlModuleSourceProvider provider = new AssetAndUrlModuleSourceProvider(App.getApp(), list);
new RequireBuilder()
.setModuleScriptProvider(new SoftCachingModuleScriptProvider(
new UrlModuleSourceProvider(paths, null)))
.setModuleScriptProvider(new SoftCachingModuleScriptProvider(provider))
.setSandboxed(true)
.createRequire(context, scope)
.install(scope);
@@ -139,7 +137,7 @@ public class RhinoJavaScriptEngine extends JavaScriptEngine {
}
public static class InterruptibleContextFactory extends ContextFactory {
private static class InterruptibleContextFactory extends ContextFactory {
@Override
protected void observeInstructionCount(Context cx, int instructionCount) {
@@ -156,4 +154,38 @@ public class RhinoJavaScriptEngine extends JavaScriptEngine {
return cx;
}
}
private static class AssetAndUrlModuleSourceProvider extends UrlModuleSourceProvider {
private static final String MODULES_PATH = "modules";
private android.content.Context mContext;
private List<String> mModules;
private final URI mBaseURI = URI.create("file:///android_asset/modules");
public AssetAndUrlModuleSourceProvider(android.content.Context context, List<URI> list) {
super(list, null);
mContext = context;
try {
mModules = Arrays.asList(mContext.getAssets().list(MODULES_PATH));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected ModuleSource loadFromPrivilegedLocations(String moduleId, Object validator) throws IOException, URISyntaxException {
String moduleIdWithExtension = moduleId;
if (!moduleIdWithExtension.endsWith(".js")) {
moduleIdWithExtension += ".js";
}
if (mModules.contains(moduleIdWithExtension)) {
return new ModuleSource(new InputStreamReader(mContext.getAssets().open(MODULES_PATH + "/" + moduleIdWithExtension)), null,
URI.create(moduleIdWithExtension), mBaseURI, validator);
}
return super.loadFromPrivilegedLocations(moduleId, validator);
}
}
}

View File

@@ -5,35 +5,100 @@ import android.os.Environment;
import com.stardust.scriptdroid.droid.Droid;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.tool.FileUtils;
import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.util.ArrayList;
/**
* Created by Stardust on 2017/1/23.
*/
public class ScriptFile {
public class ScriptFile extends File {
public static final String DEFAULT_FOLDER = Environment.getExternalStorageDirectory() + App.getApp().getString(R.string.folder_name);
public String name;
public static final String DEFAULT_DIRECTORY_PATH = Environment.getExternalStorageDirectory() + App.getApp().getString(R.string.folder_name);
public static final ScriptFile DEFAULT_DIRECTORY = new ScriptFile(DEFAULT_DIRECTORY_PATH);
public String path;
private String mSimplifyPath;
private String mSimplifiedName;
public ScriptFile(String name, String path) {
this.name = name;
this.path = path;
public ScriptFile(String path) {
super(path);
init();
}
private void init() {
mSimplifiedName = FileUtils.getNameWithoutExtension(getPath());
mSimplifyPath = getPath();
if (mSimplifyPath.startsWith(Environment.getExternalStorageDirectory().getPath())) {
mSimplifyPath = mSimplifyPath.substring(Environment.getExternalStorageDirectory().getPath().length());
}
}
public ScriptFile(ScriptFile parent, String child) {
super(parent, child);
init();
}
public void run() {
Droid.getInstance().runScriptFile(toFile());
Droid.getInstance().runScriptFile(this);
}
public File toFile() {
return new File(path);
public boolean renameTo(String newName) {
return renameTo(new File(getParent(), newName));
}
public void rename(String newName) {
File file = toFile();
file.renameTo(new File(file.getParent(), newName));
public String getSimplifiedPath() {
return mSimplifyPath;
}
@Override
public ScriptFile getParentFile() {
String p = this.getParent();
if (p == null)
return null;
return new ScriptFile(p);
}
@Override
public ScriptFile[] listFiles() {
return listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return file.isDirectory() || (file.getName().endsWith(".js") && !file.getName().startsWith("."));
}
});
}
@Override
public ScriptFile[] listFiles(FilenameFilter filter) {
String ss[] = list();
if (ss == null) return null;
ArrayList<ScriptFile> files = new ArrayList<>();
for (String s : ss)
if ((filter == null) || filter.accept(this, s))
files.add(new ScriptFile(this, s));
return files.toArray(new ScriptFile[files.size()]);
}
@Override
public ScriptFile[] listFiles(FileFilter filter) {
String ss[] = list();
if (ss == null) return null;
ArrayList<ScriptFile> files = new ArrayList<>();
for (String s : ss) {
ScriptFile f = new ScriptFile(this, s);
if ((filter == null) || filter.accept(f))
files.add(f);
}
return files.toArray(new ScriptFile[files.size()]);
}
public String getSimplifiedName() {
return mSimplifiedName;
}
}

View File

@@ -31,11 +31,12 @@ public abstract class ScriptFileList {
public boolean deleteFromFileSystem(int i) {
if (i < 0 || i >= size())
return false;
File file = new File(get(i).path);
File file = get(i);
remove(i);
return file.delete();
}
public abstract boolean containsPath(String path);
}

View File

@@ -57,14 +57,14 @@ public class SharedPrefScriptFileList extends ScriptFileList {
@Override
public void add(ScriptFile scriptFile) {
mScriptName.add(scriptFile.name);
mScriptPath.add(scriptFile.path);
mScriptName.add(scriptFile.getSimplifiedName());
mScriptPath.add(scriptFile.getSimplifiedPath());
syncWithSharedPref();
}
@Override
public ScriptFile get(int i) {
return new ScriptFile(mScriptName.get(i), mScriptPath.get(i));
return new ScriptFile(mScriptPath.get(i));
}
@Override

View File

@@ -0,0 +1,92 @@
package com.stardust.scriptdroid.droid.script.file;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Created by Stardust on 2017/3/27.
*/
public class StorageScriptFileList extends ScriptFileList {
private List<ScriptFile> mFiles;
private ScriptFile mFolder;
public StorageScriptFileList() {
this(ScriptFile.DEFAULT_DIRECTORY_PATH);
}
public ScriptFile getFolder() {
return mFolder;
}
public StorageScriptFileList(ScriptFile folder) {
if (!folder.isDirectory()) {
throw new IllegalArgumentException("file is not folder:" + folder);
}
mFolder = folder;
mFiles = Arrays.asList(folder.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return file.isDirectory() || (file.getName().endsWith(".js") && !file.getName().startsWith("."));
}
}));
Collections.sort(mFiles, new Comparator<ScriptFile>() {
@Override
public int compare(ScriptFile o1, ScriptFile o2) {
if (o1.isDirectory() != o2.isDirectory()) {
return o1.isDirectory() ? -1 : 1;
}
return 0;
}
});
}
public StorageScriptFileList(String path) {
this(new ScriptFile(path));
}
@Override
public void add(ScriptFile scriptFile) {
try {
scriptFile.createNewFile();
mFiles.add(scriptFile);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public ScriptFile get(int i) {
return mFiles.get(i);
}
@Override
public void remove(int i) {
throw new UnsupportedOperationException();
}
@Override
public void rename(int position, String newName, boolean renameFile) {
if (!renameFile)
throw new UnsupportedOperationException();
mFiles.get(position).renameTo(newName);
}
@Override
public int size() {
return mFiles.size();
}
@Override
public boolean containsPath(String path) {
return false;
}
}

View File

@@ -0,0 +1,14 @@
package com.stardust.scriptdroid.external;
/**
* Created by Stardust on 2017/4/1.
*/
public class CommonUtils {
public static final String EXTRA_KEY_PATH = "path";
public static final String EXTRA_KEY_PREPARE_SCRIPT = "script";
}

View File

@@ -47,7 +47,7 @@ public class HoverMenuAdapter implements io.mattcarroll.hover.HoverMenuAdapter {
private final Set<ContentChangeListener> mContentChangeListeners = new HashSet<>();
private View[] mViews;
public HoverMenuAdapter(@NonNull Context context) {
public HoverMenuAdapter(@NonNull HoverMenuService context) {
mContext = context;
mData.put(HoverMenuAdapter.ID_MAIN, new MainMenuNavigatorContent(context));

View File

@@ -40,6 +40,7 @@ import io.mattcarroll.hover.defaulthovermenu.window.WindowViewController;
public class HoverMenuService extends Service {
public static class ServiceStateChangedEvent {
ServiceStateChangedEvent(boolean state) {
this.state = state;
@@ -57,6 +58,7 @@ public class HoverMenuService extends Service {
public static final String MESSAGE_MENU_EXIT = "MESSAGE_MENU_EXIT";
private static boolean sIsRunning;
private static EventBus eventBus = new EventBus();
public static void startService(Context context) {
context.startService(new Intent(context, HoverMenuService.class));
@@ -72,6 +74,14 @@ public class HoverMenuService extends Service {
EventBus.getDefault().post(new ServiceStateChangedEvent(sIsRunning));
}
public static void postEvent(MessageEvent event) {
eventBus.post(event);
}
public static EventBus getEventBus() {
return eventBus;
}
private static final String TAG = "HoverMenuService";
@@ -194,15 +204,13 @@ public class HoverMenuService extends Service {
public void onMessageEvent(MessageEvent event) {
switch (event.message) {
case MESSAGE_SHOW_AND_EXPAND_MENU:
showView(mWindowHoverMenu.getHoverMenuView());
showAndExpandMenu();
break;
case MESSAGE_SHOW_LAYOUT_HIERARCHY:
mWindowHoverMenu.getHoverMenuView().setVisibility(View.GONE);
showView(mFloatingLayoutHierarchyView);
showLayoutHierarchy();
break;
case MESSAGE_SHOW_LAYOUT_BOUNDS:
mWindowHoverMenu.getHoverMenuView().setVisibility(View.GONE);
showView(mFloatingLayoutBoundsView);
showLayoutBounds();
break;
case MESSAGE_COLLAPSE_MENU:
mWindowHoverMenu.collapseMenu();
@@ -210,6 +218,21 @@ public class HoverMenuService extends Service {
}
}
private void showLayoutBounds() {
mWindowHoverMenu.getHoverMenuView().setVisibility(View.GONE);
showView(mFloatingLayoutBoundsView);
}
public void showLayoutHierarchy() {
mWindowHoverMenu.getHoverMenuView().setVisibility(View.GONE);
showView(mFloatingLayoutHierarchyView);
}
public void showAndExpandMenu() {
showView(mWindowHoverMenu.getHoverMenuView());
}
private void showView(View view) {
view.setVisibility(View.VISIBLE);
mWindowViewController.makeTouchable(view);

View File

@@ -50,12 +50,11 @@ public class MainMenuNavigatorContent implements NavigatorContent {
private String mCurrentPackage, mCurrentActivity;
private Context mContext;
public MainMenuNavigatorContent(Context context) {
mContext = context;
mView = View.inflate(context, R.layout.floating_window_main_menu, null);
ViewBinder.bind(this);
EventBus.getDefault().register(this);
HoverMenuService.getEventBus().register(this);
}
@ViewBinding.Click(R.id.layout_hierarchy)
@@ -63,7 +62,7 @@ public class MainMenuNavigatorContent implements NavigatorContent {
if (LayoutInspector.getInstance().getCapture() == null) {
Toast.makeText(mView.getContext(), R.string.text_no_accessibility_permission_to_capture, Toast.LENGTH_SHORT).show();
} else {
EventBus.getDefault().post(new MessageEvent(HoverMenuService.MESSAGE_SHOW_LAYOUT_HIERARCHY));
HoverMenuService.postEvent(new MessageEvent(HoverMenuService.MESSAGE_SHOW_LAYOUT_HIERARCHY));
}
}
@@ -72,7 +71,7 @@ public class MainMenuNavigatorContent implements NavigatorContent {
if (LayoutInspector.getInstance().getCapture() == null) {
Toast.makeText(mView.getContext(), R.string.text_no_accessibility_permission_to_capture, Toast.LENGTH_SHORT).show();
} else {
EventBus.getDefault().post(new MessageEvent(HoverMenuService.MESSAGE_SHOW_LAYOUT_BOUNDS));
HoverMenuService.postEvent(new MessageEvent(HoverMenuService.MESSAGE_SHOW_LAYOUT_BOUNDS));
}
}
@@ -85,7 +84,7 @@ public class MainMenuNavigatorContent implements NavigatorContent {
private void openMainActivity() {
App.getApp().startActivity(new Intent(App.getApp(), MainActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY | Intent.FLAG_ACTIVITY_NEW_TASK));
EventBus.getDefault().post(new MessageEvent(HoverMenuService.MESSAGE_COLLAPSE_MENU));
HoverMenuService.postEvent(new MessageEvent(HoverMenuService.MESSAGE_COLLAPSE_MENU));
}
@NonNull
@@ -129,7 +128,7 @@ public class MainMenuNavigatorContent implements NavigatorContent {
if (event.message.equals(HoverMenuService.MESSAGE_MENU_EXPANDING)) {
syncCurrentInfo();
} else if (event.message.equals(HoverMenuService.MESSAGE_MENU_EXIT)) {
EventBus.getDefault().unregister(this);
HoverMenuService.getEventBus().unregister(this);
}
}

View File

@@ -64,7 +64,7 @@ public class FloatingLayoutBoundsView extends LayoutBoundsView {
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
EventBus.getDefault().post(new MessageEvent(HoverMenuService.MESSAGE_SHOW_AND_EXPAND_MENU));
HoverMenuService.postEvent(new MessageEvent(HoverMenuService.MESSAGE_SHOW_AND_EXPAND_MENU));
setVisibility(GONE);
return true;
}

View File

@@ -104,7 +104,7 @@ public class FloatingScriptFileListView extends RecyclerView {
scriptFileOperations.add(new ScriptFileOperation.Rename() {
@Override
public void operate(final RecyclerView recyclerView, final ScriptFileList scriptFileList, final int position) {
String oldName = scriptFileList.get(position).name;
String oldName = scriptFileList.get(position).getSimplifiedName();
MaterialDialog dialog = new ThemeColorMaterialDialogBuilder(recyclerView.getContext())
.title(R.string.text_rename)
.checkBoxPrompt(App.getApp().getString(R.string.text_rename_file_meanwhile), false, null)
@@ -180,17 +180,8 @@ public class FloatingScriptFileListView extends RecyclerView {
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
ScriptFile scriptFile = mScriptFileList.get(position);
holder.name.setText(scriptFile.name);
holder.path.setText(trimFilePath(scriptFile.path));
}
private final String SD_CARD_PATH = Environment.getExternalStorageDirectory().toString();
private String trimFilePath(String path) {
if (path.startsWith(SD_CARD_PATH)) {
path = path.substring(SD_CARD_PATH.length());
}
return path;
holder.name.setText(scriptFile.getSimplifiedName());
holder.path.setText(scriptFile.getSimplifiedPath());
}
@Override

View File

@@ -40,25 +40,13 @@ public class ImportIntentActivity extends BaseActivity {
private void handleIntent() {
Intent intent = getIntent();
final String path = intent.getData().getPath();
if (!TextUtils.isEmpty(path)) {
new ThemeColorMaterialDialogBuilder(this)
.title(R.string.text_please_input_name)
.input(getString(R.string.text_name), FileUtils.getNameWithoutExtension(path), new MaterialDialog.InputCallback() {
@Override
public void onInput(@NonNull MaterialDialog dialog, CharSequence input) {
ScriptFileList.getImpl().add(new ScriptFile(input.toString(), path));
startMainActivity();
}
})
.show();
} else {
finish();
}
if (!TextUtils.isEmpty(path))
MainActivity.importScriptFile(this, path);
finish();
}
private void startMainActivity() {
startActivity(new Intent(this, MainActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK));
finish();
}
}

View File

@@ -4,12 +4,16 @@ import android.app.Activity;
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.external.shortcut.ShortcutActivity;
import com.stardust.scriptdroid.droid.Droid;
import com.stardust.scriptdroid.droid.PathChecker;
import com.stardust.scriptdroid.droid.RunningConfig;
import com.stardust.scriptdroid.external.CommonUtils;
import com.stardust.scriptdroid.R;
import java.io.File;
/**
* Created by Stardust on 2017/2/22.
*/
@@ -30,11 +34,20 @@ public class RunIntentActivity extends Activity {
private void handleIntent() {
Intent intent = getIntent();
String path = intent.getData().getPath();
if (!TextUtils.isEmpty(path)) {
startActivity(new Intent(this, ShortcutActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra("path", path));
String path = getPath(intent);
String script = intent.getStringExtra(CommonUtils.EXTRA_KEY_PREPARE_SCRIPT);
if (path == null && script != null) {
Droid.getInstance().runScript(script);
} else {
if (new PathChecker(this).checkAndToastError(path)) {
Droid.getInstance().runScriptFile(new File(path), null, new RunningConfig().prepareScript(script));
}
}
}
private String getPath(Intent intent) {
if (intent.getData() != null && intent.getData().getPath() != null)
return intent.getData().getPath();
return intent.getStringExtra(CommonUtils.EXTRA_KEY_PATH);
}
}

View File

@@ -1,73 +1,25 @@
package com.stardust.scriptdroid.external.shortcut;
import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.widget.Toast;
import com.stardust.scriptdroid.droid.Droid;
import com.stardust.scriptdroid.R;
import java.io.File;
import java.util.LinkedList;
import java.util.List;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import com.stardust.scriptdroid.droid.PathChecker;
import com.stardust.scriptdroid.external.CommonUtils;
/**
* Created by Stardust on 2017/1/23.
*/
public class ShortcutActivity extends Activity {
interface BooleanSupplier {
boolean getAsBoolean();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final String path = getIntent().getStringExtra("path");
if (!ensure(new BooleanSupplier() {
@Override
public boolean getAsBoolean() {
return !TextUtils.isEmpty(path);
}
}, R.string.text_path_is_empty))
return;
final File scriptFile = new File(path);
new Domino()
.then(new Tile() {
@Override
public boolean fall() {
return ShortcutActivity.this.ensure(new BooleanSupplier() {
@Override
public boolean getAsBoolean() {
return scriptFile.exists();
}
}, R.string.text_file_not_exists);
}
})
.then(new Tile() {
@Override
public boolean fall() {
return ShortcutActivity.this.ensure(new BooleanSupplier() {
@Override
public boolean getAsBoolean() {
return ShortcutActivity.this.hasStorageReadPermission();
}
}, R.string.text_no_file_rw_permission);
}
})
.then(new Tile() {
@Override
public boolean fall() {
ShortcutActivity.this.runScriptFile(path);
return true;
}
})
.fall();
final String path = getIntent().getStringExtra(CommonUtils.EXTRA_KEY_PATH);
if(new PathChecker(this).checkAndToastError(path)){
runScriptFile(path);
}
}
public void onStart() {
@@ -84,45 +36,4 @@ public class ShortcutActivity extends Activity {
}
}
private boolean ensure(BooleanSupplier bool, String message) {
boolean b = bool.getAsBoolean();
if (!b) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
return b;
}
private boolean ensure(BooleanSupplier bool, int resId) {
return ensure(bool, getString(resId));
}
private boolean hasStorageReadPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
checkSelfPermission(READ_EXTERNAL_STORAGE) == PERMISSION_GRANTED;
}
return true;
}
interface Tile {
boolean fall();
}
public static class Domino {
private List<Tile> mTiles = new LinkedList<>();
Domino then(Tile next) {
mTiles.add(next);
return this;
}
void fall() {
for (Tile tile : mTiles) {
if (!tile.fall())
break;
}
}
}
}

View File

@@ -0,0 +1,44 @@
package com.stardust.scriptdroid.external.tasker;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.util.Log;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.external.CommonUtils;
import com.stardust.scriptdroid.external.open.RunIntentActivity;
import com.stardust.scriptdroid.external.shortcut.ShortcutActivity;
import com.twofortyfouram.locale.sdk.client.receiver.AbstractPluginSettingReceiver;
/**
* Created by Stardust on 2017/3/27.
*/
public class FireSettingReceiver extends AbstractPluginSettingReceiver {
private static final String TAG = "FireSettingReceiver";
@Override
protected boolean isBundleValid(@NonNull Bundle bundle) {
Log.v(TAG, "isBundleValid: " + bundle);
return bundle.containsKey(CommonUtils.EXTRA_KEY_PATH);
}
@Override
protected boolean isAsync() {
Log.v(TAG, "isAsync");
return true;
}
@Override
protected void firePluginSetting(@NonNull Context context, @NonNull Bundle bundle) {
Log.v(TAG, "firePluginSetting" + bundle);
context.startActivity(new Intent(App.getApp(), RunIntentActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(CommonUtils.EXTRA_KEY_PATH, bundle.getString(CommonUtils.EXTRA_KEY_PATH))
.putExtra(CommonUtils.EXTRA_KEY_PREPARE_SCRIPT, bundle.getString(CommonUtils.EXTRA_KEY_PREPARE_SCRIPT)));
}
}

View File

@@ -0,0 +1,74 @@
package com.stardust.scriptdroid.external.tasker;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.script.file.ScriptFile;
import com.stardust.scriptdroid.external.CommonUtils;
import com.stardust.scriptdroid.ui.main.my_script_list.ScriptAndFolderListRecyclerView;
import com.twofortyfouram.locale.sdk.client.ui.activity.AbstractPluginActivity;
/**
* Created by Stardust on 2017/3/27.
*/
public class TaskPrefEditActivity extends AbstractPluginActivity {
private static final String TAG = "TaskPrefEditActivity";
private String mSelectedScriptFilePath;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.v(TAG, "onCreate");
setContentView(R.layout.activity_tasker_edit);
initScriptListRecyclerView();
}
private void initScriptListRecyclerView() {
ScriptAndFolderListRecyclerView scriptListRecyclerView = (ScriptAndFolderListRecyclerView) findViewById(R.id.script_list);
scriptListRecyclerView.setOnItemClickListener(new ScriptAndFolderListRecyclerView.OnScriptFileClickListener() {
@Override
public void onClick(ScriptFile file) {
mSelectedScriptFilePath = file.getPath();
finish();
}
});
}
@Override
public boolean isBundleValid(@NonNull Bundle bundle) {
boolean valid = bundle.getString(CommonUtils.EXTRA_KEY_PATH) != null;
Log.v(TAG, "isBundleValid: " + valid);
Context context;
return valid;
}
@Override
public void onPostCreateWithPreviousResult(@NonNull Bundle bundle, @NonNull String s) {
Log.v(TAG, "onPostCreateWithPreviousResult: bundle=" + bundle + " str=" + s);
mSelectedScriptFilePath = bundle.getString(CommonUtils.EXTRA_KEY_PATH);
}
@Nullable
@Override
public Bundle getResultBundle() {
Log.v(TAG, "getResultBundle");
Bundle bundle = new Bundle();
bundle.putString(CommonUtils.EXTRA_KEY_PATH, mSelectedScriptFilePath);
return bundle;
}
@NonNull
@Override
public String getResultBlurb(@NonNull Bundle bundle) {
Log.v(TAG, "getResultBlurb");
return bundle.getString(CommonUtils.EXTRA_KEY_PATH, getString(R.string.text_path_is_empty));
}
}

View File

@@ -0,0 +1,104 @@
package com.stardust.scriptdroid.scripts;
import com.stardust.scriptdroid.droid.script.file.ScriptFile;
import com.stardust.util.FileSorter;
import com.stardust.util.LimitedHashMap;
import org.greenrobot.eventbus.EventBus;
import org.junit.Assert;
import org.junit.Test;
import java.util.Arrays;
import java.util.Comparator;
/**
* Created by Stardust on 2017/3/31.
*/
public class StorageScriptProvider {
public static class DirectoryChangeEvent {
public ScriptFile directory;
public DirectoryChangeEvent(ScriptFile directory) {
this.directory = directory;
}
}
private static StorageScriptProvider instance = new StorageScriptProvider();
public static StorageScriptProvider getInstance() {
return instance;
}
private EventBus mDirectoryEventBus = new EventBus();
private LimitedHashMap<String, ScriptFile[]> mScriptFileCache = new LimitedHashMap<>(10);
private ScriptFile[] mInitialDirectoryScriptFiles = listAndSortFiles(ScriptFile.DEFAULT_DIRECTORY);
public void notifyDirectoryChanged(ScriptFile directory) {
if (directory.equals(ScriptFile.DEFAULT_DIRECTORY)) {
mInitialDirectoryScriptFiles = listAndSortFiles(ScriptFile.DEFAULT_DIRECTORY);
} else {
clearCache(directory);
}
mDirectoryEventBus.post(new DirectoryChangeEvent(directory));
}
public void notifyStoragePermissionGranted() {
mScriptFileCache.clear();
mInitialDirectoryScriptFiles = listAndSortFiles(ScriptFile.DEFAULT_DIRECTORY);
mDirectoryEventBus.post(new DirectoryChangeEvent(ScriptFile.DEFAULT_DIRECTORY));
}
public ScriptFile[] getInitialDirectoryScriptFiles() {
return mInitialDirectoryScriptFiles;
}
public ScriptFile[] getDirectoryScriptFiles(ScriptFile directory) {
if (directory.equals(ScriptFile.DEFAULT_DIRECTORY)) {
return mInitialDirectoryScriptFiles;
}
ScriptFile[] scriptFiles = getScriptFilesFromCache(directory);
if (scriptFiles == null) {
scriptFiles = getScriptFiles(directory);
}
return scriptFiles;
}
private void clearCache(ScriptFile directory) {
mScriptFileCache.remove(directory.getPath());
}
private ScriptFile[] getScriptFiles(ScriptFile directory) {
ScriptFile[] scriptFiles =listAndSortFiles(directory);
mScriptFileCache.put(directory.getPath(), scriptFiles);
return scriptFiles;
}
private ScriptFile[] listAndSortFiles(ScriptFile directory) {
ScriptFile[] scriptFiles = directory.listFiles();
if (scriptFiles == null)
scriptFiles = new ScriptFile[0];
else
FileSorter.sort(scriptFiles);
return scriptFiles;
}
private ScriptFile[] getScriptFilesFromCache(ScriptFile directory) {
return mScriptFileCache.get(directory.getPath());
}
public void registerDirectoryChangeListener(Object subscriber) {
mDirectoryEventBus.register(subscriber);
}
public void unregisterDirectoryChangeListener(Object subscriber) {
mDirectoryEventBus.unregister(subscriber);
}
}

View File

@@ -1,4 +1,4 @@
package com.stardust.scriptdroid.sample;
package com.stardust.scriptdroid.scripts.sample;
/**
* Created by Stardust on 2017/3/13.

View File

@@ -1,22 +1,14 @@
package com.stardust.scriptdroid.sample;
package com.stardust.scriptdroid.scripts.sample;
import android.content.Context;
import android.content.res.AssetManager;
import com.stardust.scriptdroid.Pref;
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.tool.FileUtils;
import com.stardust.util.MapEntries;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Created by Stardust on 2017/1/30.

View File

@@ -1,4 +1,4 @@
package com.stardust.scriptdroid.sample;
package com.stardust.scriptdroid.scripts.sample;
import java.util.ArrayList;
import java.util.List;

View File

@@ -5,6 +5,7 @@ import android.database.Cursor;
import android.net.Uri;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.droid.script.file.ScriptFile;
import java.io.File;
import java.io.FileInputStream;
@@ -152,6 +153,16 @@ public class FileUtils {
}
}
public static boolean copy(String pathFrom, String pathTo) {
try {
return copy(new FileInputStream(pathFrom), pathTo);
} catch (FileNotFoundException e) {
e.printStackTrace();
return false;
}
}
public static boolean copyAsset(String assetFile, String path) {
try {
return copy(App.getApp().getAssets().open(assetFile), path);
@@ -228,7 +239,7 @@ public class FileUtils {
public static File copyAssetToTmpFile(Context context, String path) {
String extension = getExtension(path);
String name = getNameWithoutExtension(path);
if(name.length() < 5){
if (name.length() < 5) {
name += name.hashCode();
}
try {
@@ -239,4 +250,14 @@ public class FileUtils {
throw new RuntimeException(e);
}
}
public static boolean deleteAll(File file) {
if (file.isFile())
return file.delete();
for (File child : file.listFiles()) {
if (!deleteAll(child))
return false;
}
return file.delete();
}
}

View File

@@ -61,9 +61,15 @@ public class IntentTool {
goToMail(context, sendTo, null, null);
}
public static void goToLink(Context context, String link) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(link)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
public static boolean goToLink(Context context, String link) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(link)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
return true;
} catch (ActivityNotFoundException ignored) {
return false;
}
}
public static void shareText(Context context, String text) {

View File

@@ -17,6 +17,7 @@ import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.scriptdroid.Pref;
import com.stardust.scriptdroid.droid.Droid;
import com.stardust.scriptdroid.droid.RunningConfig;
import com.stardust.scriptdroid.tool.FileUtils;
import com.stardust.scriptdroid.ui.edit.editor920.Editor920Activity;
import com.stardust.scriptdroid.ui.edit.sidemenu.EditSideMenuFragment;
@@ -253,7 +254,7 @@ public class EditActivity extends Editor920Activity {
private void run() {
Snackbar.make(mView, R.string.text_start_running, Snackbar.LENGTH_SHORT).show();
setMenuStatus(R.id.run, MenuDef.STATUS_DISABLED);
Droid.getInstance().runScriptFile(mFile, ON_RUN_FINISHED_LISTENER);
Droid.getInstance().runScriptFile(mFile, ON_RUN_FINISHED_LISTENER, RunningConfig.getDefault());
}
@ViewBinding.Click(R.id.undo)

View File

@@ -1,26 +0,0 @@
package com.stardust.scriptdroid.ui.edit;
import android.os.Bundle;
import android.support.annotation.Nullable;
import com.stardust.scriptdroid.ui.BaseActivity;
import xyz.iridiumion.iridiumhighlightingeditor.editor.HighlightingDefinition;
import xyz.iridiumion.iridiumhighlightingeditor.editor.IridiumHighlightingEditorJ;
import xyz.iridiumion.iridiumhighlightingeditor.highlightingdefinitions.definitions.JavaScriptHighlightingDefinition;
/**
* Created by Stardust on 2017/3/22.
*/
public class IridiumEditActivity extends BaseActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
IridiumHighlightingEditorJ editorJ = new IridiumHighlightingEditorJ(this);
editorJ.setText("1234567");
editorJ.loadHighlightingDefinition(new JavaScriptHighlightingDefinition());
setContentView(editorJ);
}
}

View File

@@ -10,8 +10,8 @@ import android.content.Intent;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.support.v4.widget.DrawerLayout;
@@ -33,6 +33,8 @@ import com.stardust.app.OnActivityResultDelegate;
import com.stardust.scriptdroid.Pref;
import com.stardust.scriptdroid.droid.script.file.ScriptFile;
import com.stardust.scriptdroid.droid.script.file.ScriptFileList;
import com.stardust.scriptdroid.external.open.ImportIntentActivity;
import com.stardust.scriptdroid.scripts.StorageScriptProvider;
import com.stardust.scriptdroid.service.AccessibilityWatchDogService;
import com.stardust.scriptdroid.tool.AccessibilityServiceTool;
import com.stardust.scriptdroid.tool.ImageSelector;
@@ -62,7 +64,9 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
private static final String EXTRA_ACTION = "EXTRA_ACTION";
private static final String ACTION_ON_ACTION_RECORD_STOPPED = "ACTION_ON_ACTION_RECORD_STOPPED";
private static final String ACTION_IMPORT_SCRIPT = "ACTION_IMPORT_SCRIPT";
private static final String ARGUMENT_SCRIPT = "ARGUMENT_SCRIPT";
private static final String ARGUMENT_PATH = "ARGUMENT_PATH";
private DrawerLayout mDrawerLayout;
@ViewBinding.Id(R.id.bottom_menu)
@@ -116,22 +120,6 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
}
}
private void addScriptFile(final String path) {
new ThemeColorMaterialDialogBuilder(this).title(R.string.text_name)
.inputType(InputType.TYPE_CLASS_TEXT)
.input(getString(R.string.text_please_input_name), FileUtils.getNameWithoutExtension(path), new MaterialDialog.InputCallback() {
@Override
public void onInput(@NonNull MaterialDialog dialog, CharSequence input) {
MainActivity.this.addScriptFile(input.toString(), path);
}
}).show();
}
private void addScriptFile(String name, String path) {
ScriptFileList.getImpl().add(new ScriptFile(name, path));
EventBus.getDefault().post(new MessageEvent(MyScriptListFragment.MESSAGE_SCRIPT_FILE_ADDED));
}
private void setUpUI() {
mDrawerLayout = (DrawerLayout) View.inflate(this, R.layout.activity_main, null);
setContentView(mDrawerLayout);
@@ -197,41 +185,18 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
@ViewBinding.Click(R.id.create_new_file)
private void createScriptFile() {
createScriptFileForScript(null);
getMyScriptListFragment().newScriptFile();
}
private void createScriptFileForScript(final String script) {
new ThemeColorMaterialDialogBuilder(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 = FileUtils.generateNotExistingPath(ScriptFile.DEFAULT_FOLDER + input, ".js");
MainActivity.this.createScriptFile(input.toString(), path, script);
}
})
.show();
}
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);
((MyScriptListFragment) mPagerAdapter.getStoredFragment(0)).editLatest();
} else {
Snackbar.make(mDrawerLayout, R.string.text_file_create_fail, Snackbar.LENGTH_LONG).show();
}
@ViewBinding.Click(R.id.create_new_directory)
private void createNewDirectory() {
getMyScriptListFragment().newDirectory();
}
@ViewBinding.Click(R.id.import_from_file)
private void showFileChooser() {
new FileChooserDialog.Builder(this)
.initialPath(ScriptFile.DEFAULT_FOLDER)
.initialPath(Environment.getExternalStorageDirectory().getPath())
.extensionsFilter(".js", ".txt")
.show();
}
@@ -260,7 +225,7 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
@Override
public void onFileSelection(@NonNull FileChooserDialog dialog, @NonNull File file) {
addScriptFile(file.getPath());
getMyScriptListFragment().importFile(file.getPath());
}
@Override
@@ -277,6 +242,9 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
case ACTION_ON_ACTION_RECORD_STOPPED:
handleRecordedScript(intent.getStringExtra(ARGUMENT_SCRIPT));
break;
case ACTION_IMPORT_SCRIPT:
getMyScriptListFragment().importFile(intent.getStringExtra(ARGUMENT_PATH));
break;
}
}
@@ -288,7 +256,7 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
@Override
public void onSelection(MaterialDialog dialog, View itemView, int position, CharSequence text) {
if (position == 0) {
createScriptFileForScript(script);
getMyScriptListFragment().newScriptFileForScript(script);
} else {
((ClipboardManager) getSystemService(CLIPBOARD_SERVICE))
.setPrimaryClip(ClipData.newPlainText("script", script));
@@ -307,9 +275,16 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
.show();
}
@Override
protected void onDestroy() {
super.onDestroy();
public static void importScriptFile(Context context, String path) {
context.startActivity(new Intent(context, MainActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP)
.putExtra(EXTRA_ACTION, ACTION_IMPORT_SCRIPT)
.putExtra(ARGUMENT_PATH, path));
}
private MyScriptListFragment getMyScriptListFragment() {
return ((MyScriptListFragment) mPagerAdapter.getStoredFragment(0));
}
@Override
@@ -325,6 +300,11 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
context.startActivity(intent);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
StorageScriptProvider.getInstance().notifyStoragePermissionGranted();
}
@ViewBinding.Click(R.id.toolbar)
public void OnToolbarClick() {
new ImageSelector(this, mActivityResultIntermediary, new ImageSelector.ImageSelectorCallback() {

View File

@@ -1,48 +1,55 @@
package com.stardust.scriptdroid.ui.main.my_script_list;
import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.support.design.widget.Snackbar;
import android.text.InputType;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.app.Fragment;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.script.file.ScriptFileList;
import com.stardust.scriptdroid.droid.script.file.SharedPrefScriptFileList;
import com.stardust.scriptdroid.tool.BackPressedHandler;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.stardust.scriptdroid.droid.script.file.ScriptFile;
import com.stardust.scriptdroid.scripts.StorageScriptProvider;
import com.stardust.scriptdroid.tool.FileUtils;
import com.stardust.scriptdroid.ui.main.operation.ScriptFileOperation;
import com.stardust.util.MessageEvent;
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
import com.stardust.view.ViewBinder;
import com.stardust.view.ViewBinding;
import com.stardust.widget.SimpleAdapterDataObserver;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import java.io.File;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
/**
* Created by Stardust on 2017/3/13.
*/
public class MyScriptListFragment extends Fragment implements BackPressedHandler {
public class MyScriptListFragment extends Fragment {
public static final String MESSAGE_SCRIPT_FILE_ADDED = "MESSAGE_SCRIPT_FILE_ADDED";
private ScriptListRecyclerView mScriptListRecyclerView;
private ScriptFileList mScriptFileList;
private ScriptAndFolderListRecyclerView mScriptListRecyclerView;
private View mNoScriptHint;
private View mProgressBar;
private MaterialDialog mScriptFileOperationDialog;
private MaterialDialog mDirectoryOperationDialog;
private ScriptFile mSelectedScriptFile;
private MaterialDialog.InputCallback mFileNameInputCallback = new InputCallback(false);
private MaterialDialog.InputCallback mDirectoryNameInputCallback = new InputCallback(true);
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventBus.getDefault().register(this);
if(!(getActivity() instanceof BaseActivity)){
throw new IllegalArgumentException("The fragment can only be used in BaseActivity");
}
}
@Nullable
@Override
public View createView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
@@ -53,8 +60,13 @@ public class MyScriptListFragment extends Fragment implements BackPressedHandler
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mScriptListRecyclerView = $(R.id.script_list);
mScriptFileList = ScriptFileList.getImpl();
mNoScriptHint = $(R.id.hint_no_script);
mProgressBar = $(R.id.progressBar);
initScriptListRecyclerView();
initDialogs();
}
private void initScriptListRecyclerView() {
mScriptListRecyclerView.getAdapter().registerAdapterDataObserver(new SimpleAdapterDataObserver() {
@Override
public void onSomethingChanged() {
@@ -65,49 +77,275 @@ public class MyScriptListFragment extends Fragment implements BackPressedHandler
}
}
});
mScriptListRecyclerView.setScriptFileList(mScriptFileList);
mScriptListRecyclerView.setOnItemClickListener(new ScriptAndFolderListRecyclerView.OnScriptFileClickListener() {
@Override
public void onClick(ScriptFile file) {
mSelectedScriptFile = file;
mScriptFileOperationDialog.show();
}
});
mScriptListRecyclerView.setOnItemLongClickListener(new ScriptAndFolderListRecyclerView.OnScriptFileLongClickListener() {
@Override
public void onLongClick(ScriptFile file) {
mSelectedScriptFile = file;
if (file.isDirectory()) {
mDirectoryOperationDialog.show();
} else {
mScriptFileOperationDialog.show();
}
}
});
}
//// FIXME: 2017/3/24
@Override
public boolean onBackPressed(Activity activity) {
if (mScriptListRecyclerView.getScriptFileOperationPopupMenu().isShowing()) {
mScriptListRecyclerView.getScriptFileOperationPopupMenu().dismiss();
return true;
}
return false;
private void initDialogs() {
mScriptFileOperationDialog = buildDialog(R.layout.dialog_script_file_operations);
mDirectoryOperationDialog = buildDialog(R.layout.dialog_directory_operations);
}
public ScriptListRecyclerView getScriptListRecyclerView() {
return mScriptListRecyclerView;
private MaterialDialog buildDialog(int layout) {
View view = View.inflate(getActivity(), layout, null);
ViewBinder.bind(this, view);
return new MaterialDialog.Builder(getActivity())
.customView(view, false)
.build();
}
@Subscribe
public void onMessageEvent(MessageEvent event) {
if (event.message.equals(MESSAGE_SCRIPT_FILE_ADDED)) {
mScriptListRecyclerView.getAdapter().notifyItemInserted(mScriptFileList.size() - 1);
public void newScriptFileForScript(final String script) {
showFileNameInputDialog("", new MaterialDialog.InputCallback() {
@Override
public void onInput(@NonNull MaterialDialog dialog, CharSequence input) {
createScriptFile(getCurrentDirectoryPath() + input + ".js", script);
}
});
}
private String getCurrentDirectoryPath() {
return getCurrentDirectory().getPath() + "/";
}
public void createScriptFile(String path, String script) {
if (FileUtils.createFileIfNotExists(path)) {
if (script != null) {
if (!FileUtils.writeString(path, script)) {
Snackbar.make(getView(), R.string.text_file_write_fail, Snackbar.LENGTH_LONG).show();
}
}
notifyScriptFileChanged();
ScriptFileOperation.edit(new ScriptFile(path));
} else {
Snackbar.make(getView(), R.string.text_create_fail, Snackbar.LENGTH_LONG).show();
}
}
public void newScriptFile() {
newScriptFileForScript(null);
}
public void importFile(final String pathFrom) {
showFileNameInputDialog(FileUtils.getNameWithoutExtension(pathFrom), new MaterialDialog.InputCallback() {
@Override
public void onInput(@NonNull MaterialDialog dialog, CharSequence input) {
final String pathTo = getCurrentDirectoryPath() + input + ".js";
new Thread(new Runnable() {
@Override
public void run() {
if (FileUtils.copy(pathFrom, pathTo)) {
showMessage(R.string.text_import_succeed);
} else {
showMessage(R.string.text_import_fail);
}
notifyScriptFileChanged();
}
}).start();
}
});
}
public void newDirectory() {
showNameInputDialog("", mDirectoryNameInputCallback, new MaterialDialog.InputCallback() {
@Override
public void onInput(@NonNull MaterialDialog dialog, CharSequence input) {
if (new ScriptFile(getCurrentDirectory(), input.toString()).mkdirs()) {
showMessage(R.string.text_already_create);
notifyScriptFileChanged();
} else {
showMessage(R.string.text_create_fail);
}
}
});
}
private ScriptFile getCurrentDirectory() {
return mScriptListRecyclerView.getCurrentDirectory();
}
private void showFileNameInputDialog(String prefix, final MaterialDialog.InputCallback callback) {
showNameInputDialog(prefix, mFileNameInputCallback, callback);
}
private void showNameInputDialog(String prefix, MaterialDialog.InputCallback textWatcher, final MaterialDialog.InputCallback callback) {
new ThemeColorMaterialDialogBuilder(getActivity()).title(R.string.text_name)
.inputType(InputType.TYPE_CLASS_TEXT)
.alwaysCallInputCallback()
.input(getString(R.string.text_please_input_name), prefix, false, textWatcher)
.onPositive(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
callback.onInput(dialog, dialog.getInputEditText().getText());
}
})
.show();
}
private void notifyScriptFileChanged() {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
StorageScriptProvider.getInstance().notifyDirectoryChanged(getCurrentDirectory());
}
});
}
@ViewBinding.Click(R.id.rename)
private void renameScriptFile() {
dismissDialogs();
String originalName = mSelectedScriptFile.getSimplifiedName();
showNameInputDialog(originalName, new InputCallback(mSelectedScriptFile.isDirectory(), originalName), new MaterialDialog.InputCallback() {
@Override
public void onInput(@NonNull MaterialDialog dialog, CharSequence input) {
mSelectedScriptFile.renameTo(input.toString());
onScriptFileOperated();
}
});
}
private void dismissDialogs() {
if (mDirectoryOperationDialog.isShowing())
mDirectoryOperationDialog.dismiss();
if (mScriptFileOperationDialog.isShowing())
mScriptFileOperationDialog.dismiss();
}
@ViewBinding.Click(R.id.open_by_other_apps)
private void openByOtherApps() {
dismissDialogs();
ScriptFileOperation.openByOtherApps(mSelectedScriptFile);
onScriptFileOperated();
}
private void onScriptFileOperated() {
mSelectedScriptFile = null;
mProgressBar.post(new Runnable() {
@Override
public void run() {
mProgressBar.setVisibility(View.GONE);
}
});
}
@ViewBinding.Click(R.id.create_shortcut)
private void createShortcut() {
dismissDialogs();
ScriptFileOperation.createShortcut(mSelectedScriptFile);
Snackbar.make(getView(), R.string.text_already_create, Snackbar.LENGTH_SHORT).show();
onScriptFileOperated();
}
@ViewBinding.Click(R.id.delete)
private void deleteScriptFile() {
dismissDialogs();
if (mSelectedScriptFile.isDirectory()) {
new MaterialDialog.Builder(getActivity())
.title(R.string.delete_confirm)
.positiveText(R.string.cancel)
.negativeText(R.string.ok)
.onNegative(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
doDeletingScriptFile();
}
})
.show();
} else {
doDeletingScriptFile();
}
}
private void doDeletingScriptFile() {
mProgressBar.setVisibility(View.VISIBLE);
new Thread(new Runnable() {
@Override
public void run() {
if (FileUtils.deleteAll(mSelectedScriptFile)) {
showMessage(R.string.text_already_delete);
notifyScriptFileChanged();
} else {
showMessage(R.string.text_already_delete);
}
onScriptFileOperated();
}
}).start();
}
private void showMessage(final int resId) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
Snackbar.make(getView(), resId, Snackbar.LENGTH_SHORT).show();
}
});
}
@Override
public void onResume() {
super.onResume();
EventBus.getDefault().register(mScriptListRecyclerView);
mScriptListRecyclerView.setFocusableInTouchMode(true);
mScriptListRecyclerView.requestFocus();
}
@Override
public void onPause() {
super.onPause();
EventBus.getDefault().unregister(mScriptListRecyclerView);
private class InputCallback implements MaterialDialog.InputCallback {
private boolean mIsDirectory = false;
private String mExcluded;
InputCallback(boolean isDirectory, String excluded) {
mIsDirectory = isDirectory;
mExcluded = excluded;
}
InputCallback(boolean isDirectory) {
mIsDirectory = isDirectory;
}
@Override
public void onInput(@NonNull MaterialDialog dialog, CharSequence input) {
EditText editText = dialog.getInputEditText();
if (editText == null)
return;
int errorResId = 0;
if (input == null || input.length() == 0) {
errorResId = R.string.text_name_should_not_be_empty;
} else if (!input.equals(mExcluded)) {
if (new File(getCurrentDirectory(), mIsDirectory ? input.toString() : input.toString() + ".js").exists()) {
errorResId = R.string.text_file_exists;
}
}
if (errorResId == 0) {
editText.setError(null);
dialog.getActionButton(DialogAction.POSITIVE).setEnabled(true);
} else {
editText.setError(getString(errorResId));
dialog.getActionButton(DialogAction.POSITIVE).setEnabled(false);
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
public void editLatest() {
ScriptFileOperation.Edit.getInstance().operate(mScriptListRecyclerView, mScriptFileList, mScriptFileList.size() - 1);
}
}

View File

@@ -0,0 +1,267 @@
package com.stardust.scriptdroid.ui.main.my_script_list;
import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.script.file.ScriptFile;
import com.stardust.scriptdroid.scripts.StorageScriptProvider;
import com.stardust.scriptdroid.ui.main.operation.ScriptFileOperation;
import org.greenrobot.eventbus.Subscribe;
/**
* Created by Stardust on 2017/3/27.
*/
public class ScriptAndFolderListRecyclerView extends RecyclerView {
public interface OnScriptFileClickListener {
void onClick(ScriptFile file);
}
public interface OnScriptFileLongClickListener {
void onLongClick(ScriptFile file);
}
private OnScriptFileClickListener mOnItemClickListener;
private OnScriptFileLongClickListener mOnItemLongClickListener;
private final OnClickListener mOnItemClickListenerProxy = new OnClickListener() {
@Override
public void onClick(View v) {
int position = getChildViewHolder(v).getAdapterPosition();
if (mCanGoBack && position == 0) {
goBack();
return;
}
ScriptFile file = mScriptFileList[getActualPosition(position)];
if (file.isDirectory()) {
setCurrentFolder(file, true);
} else if (mOnItemClickListener != null) {
mOnItemClickListener.onClick(file);
}
}
};
private final OnLongClickListener mOnItemLongClickListenerProxy = new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (mOnItemLongClickListener != null) {
int position = getChildViewHolder(v).getAdapterPosition();
mOnItemLongClickListener.onLongClick(mScriptFileList[getActualPosition(position)]);
return true;
}
return false;
}
};
private OnClickListener mOnRunClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
int position = getChildViewHolder((View) v.getParent()).getAdapterPosition();
mScriptFileList[getActualPosition(position)].run();
}
};
private OnClickListener mOnEditClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
int position = getChildViewHolder((View) v.getParent()).getAdapterPosition();
ScriptFileOperation.edit(mScriptFileList[getActualPosition(position)]);
}
};
private ScriptFile[] mScriptFileList;
private ScriptFile mCurrentFolder;
private ScriptFile mRootFolder;
private Adapter mAdapter;
private boolean mCanGoBack;
public ScriptAndFolderListRecyclerView(Context context) {
super(context);
init();
}
public ScriptAndFolderListRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public ScriptAndFolderListRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void setCurrentFolder(ScriptFile folder, boolean canGoBack) {
mCurrentFolder = folder;
mScriptFileList = StorageScriptProvider.getInstance().getDirectoryScriptFiles(folder);
mCanGoBack = canGoBack;
mAdapter.notifyDataSetChanged();
}
public void setRootFolder(ScriptFile folder) {
mRootFolder = folder;
setCurrentFolder(mRootFolder, false);
}
public void setOnItemClickListener(OnScriptFileClickListener onItemClickListener) {
mOnItemClickListener = onItemClickListener;
}
public void setOnItemLongClickListener(OnScriptFileLongClickListener onItemLongClickListener) {
mOnItemLongClickListener = onItemLongClickListener;
}
public ScriptFile getCurrentDirectory() {
return mCurrentFolder;
}
private void goBack() {
ScriptFile parent = mCurrentFolder.getParentFile();
setCurrentFolder(parent, !parent.equals(mRootFolder));
}
private void init() {
setLayoutManager(new LinearLayoutManager(getContext()));
addItemDecoration(new DividerItemDecoration(getContext(), VERTICAL));
mAdapter = new Adapter();
setAdapter(mAdapter);
setRootFolder(ScriptFile.DEFAULT_DIRECTORY);
}
@Override
protected Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putParcelable("superData", super.onSaveInstanceState());
bundle.putSerializable("current", mCurrentFolder);
bundle.putSerializable("root", mRootFolder);
return bundle;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
Bundle bundle = (Bundle) state;
mRootFolder = (ScriptFile) bundle.getSerializable("root");
mCurrentFolder = (ScriptFile) bundle.getSerializable("current");
setCurrentFolder(mCurrentFolder, !mCurrentFolder.equals(mRootFolder));
super.onRestoreInstanceState(bundle.getParcelable("superData"));
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
StorageScriptProvider.getInstance().registerDirectoryChangeListener(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
StorageScriptProvider.getInstance().unregisterDirectoryChangeListener(this);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
if (mCanGoBack) {
goBack();
return true;
}
}
return false;
}
@Subscribe
public void onDirectoryChange(StorageScriptProvider.DirectoryChangeEvent event) {
if (event.directory.equals(mCurrentFolder)) {
updateCurrentFolder();
}
}
private void updateCurrentFolder() {
setCurrentFolder(mCurrentFolder, mCanGoBack);
}
private class Adapter extends RecyclerView.Adapter<ViewHolder> {
private final int VIEW_TYPE_FOLDER = 1;
private final int VIEW_TYPE_FILE = 2;
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case VIEW_TYPE_FILE:
return new FileViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.script_list_recycler_view_item, parent, false));
case VIEW_TYPE_FOLDER:
return new ViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.script_list_recycler_view_folder, parent, false));
}
return null;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
if (mCanGoBack && position == 0) {
holder.name.setText("..");
holder.path.setText("");
} else
holder.bind(mScriptFileList[getActualPosition(position)]);
}
@Override
public int getItemCount() {
return mScriptFileList.length + (mCanGoBack ? 1 : 0);
}
@Override
public int getItemViewType(int position) {
if (mCanGoBack && position == 0) {
return VIEW_TYPE_FOLDER;
}
return mScriptFileList[getActualPosition(position)].isDirectory() ? VIEW_TYPE_FOLDER : VIEW_TYPE_FILE;
}
}
private int getActualPosition(int position) {
return mCanGoBack ? position - 1 : position;
}
private class ViewHolder extends RecyclerView.ViewHolder {
TextView name, path;
ViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(mOnItemClickListenerProxy);
itemView.setOnLongClickListener(mOnItemLongClickListenerProxy);
name = (TextView) itemView.findViewById(R.id.name);
path = (TextView) itemView.findViewById(R.id.path);
}
public void bind(ScriptFile file) {
name.setText(file.getSimplifiedName());
path.setText(file.getSimplifiedPath());
}
}
private class FileViewHolder extends ViewHolder {
FileViewHolder(View itemView) {
super(itemView);
itemView.findViewById(R.id.edit).setOnClickListener(mOnEditClickListener);
itemView.findViewById(R.id.run).setOnClickListener(mOnRunClickListener);
}
}
}

View File

@@ -12,6 +12,7 @@ import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.stardust.scriptdroid.droid.script.file.ScriptFile;
@@ -21,7 +22,6 @@ import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.ui.main.operation.ScriptFileOperation;
import com.stardust.scriptdroid.ui.main.operation.ScriptFileOperationPopupMenu;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import java.util.ArrayList;
@@ -33,13 +33,23 @@ import java.util.List;
public class ScriptListRecyclerView extends ThemeColorRecyclerView {
public interface OnItemClickListener {
void OnItemClick(View v, int position);
}
private ScriptFileList mScriptFileList;
private final OnClickListener mOnItemClickListener = new OnClickListener() {
private OnItemClickListener mOnItemClickListener;
private final OnClickListener mOnItemClickListenerProxy = new OnClickListener() {
@Override
public void onClick(View v) {
int position = getChildViewHolder(v).getAdapterPosition();
onItemClicked(v, position);
if (mOnItemClickListener != null) {
mOnItemClickListener.OnItemClick(v, position);
}
}
};
@@ -51,18 +61,15 @@ public class ScriptListRecyclerView extends ThemeColorRecyclerView {
}
};
private final OnClickListener mOnMoreIconClickListener = new OnClickListener() {
private final OnClickListener mOnRunIconClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
mOperateFileIndex = getChildViewHolder((View) v.getParent()).getAdapterPosition();
showOrDismissOperationPopupMenu(v);
int position = getChildViewHolder((View) v.getParent()).getAdapterPosition();
//onRunIconClick(v, position);
}
};
private ScriptFileOperationPopupMenu mScriptFileOperationPopupMenu;
private int mOperateFileIndex;
public ScriptListRecyclerView(Context context) {
super(context);
@@ -80,45 +87,19 @@ public class ScriptListRecyclerView extends ThemeColorRecyclerView {
init();
}
public ScriptFileOperationPopupMenu getScriptFileOperationPopupMenu() {
return mScriptFileOperationPopupMenu;
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
mOnItemClickListener = onItemClickListener;
}
private void init() {
setAdapter(new Adapter());
setLayoutManager(new LinearLayoutManager(getContext()));
addItemDecoration(new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL));
initScriptFileOperationPopupMenu();
}
private void initScriptFileOperationPopupMenu() {
mScriptFileOperationPopupMenu = new ScriptFileOperationPopupMenu(getContext(), getScriptFileOperations());
mScriptFileOperationPopupMenu.setOnItemClickListener(new ScriptFileOperationPopupMenu.OnItemClickListener() {
@Override
public void onClick(View view, int position, ScriptFileOperation operation) {
operation.operate(ScriptListRecyclerView.this, mScriptFileList, mOperateFileIndex);
mScriptFileOperationPopupMenu.dismiss();
}
});
}
protected List<ScriptFileOperation> getScriptFileOperations() {
List<ScriptFileOperation> scriptFileOperations = new ArrayList<>();
scriptFileOperations.add(new ScriptFileOperation.Run());
scriptFileOperations.add(new ScriptFileOperation.Rename());
scriptFileOperations.add(new ScriptFileOperation.OpenByOtherApp());
scriptFileOperations.add(new ScriptFileOperation.CreateShortcut());
scriptFileOperations.add(new ScriptFileOperation.Remove());
scriptFileOperations.add(new ScriptFileOperation.Delete());
return scriptFileOperations;
}
protected void onItemClicked(View v, int position) {
new ScriptFileOperation.Run().operate(ScriptListRecyclerView.this, mScriptFileList, position);
}
protected void onEditIconClick(View v, int position) {
new ScriptFileOperation.Edit().operate(ScriptListRecyclerView.this, mScriptFileList, position);
ScriptFileOperation.Edit.getInstance().operate(ScriptListRecyclerView.this, mScriptFileList, position);
}
public void setScriptFileList(ScriptFileList scriptFileList) {
@@ -126,14 +107,6 @@ public class ScriptListRecyclerView extends ThemeColorRecyclerView {
getAdapter().notifyDataSetChanged();
}
private void showOrDismissOperationPopupMenu(View v) {
if (mScriptFileOperationPopupMenu.isShowing()) {
mScriptFileOperationPopupMenu.dismiss();
} else {
mScriptFileOperationPopupMenu.show(v);
}
}
@Subscribe
public void showMessage(ScriptFileOperation.ShowMessageEvent event) {
Snackbar.make(this, event.messageResId, Snackbar.LENGTH_SHORT).show();
@@ -150,18 +123,10 @@ public class ScriptListRecyclerView extends ThemeColorRecyclerView {
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
ScriptFile scriptFile = mScriptFileList.get(position);
holder.name.setText(scriptFile.name);
holder.path.setText(trimFilePath(scriptFile.path));
holder.name.setText(scriptFile.getSimplifiedName());
holder.path.setText(scriptFile.getSimplifiedPath());
}
private final String SD_CARD_PATH = Environment.getExternalStorageDirectory().toString();
private String trimFilePath(String path) {
if (path.startsWith(SD_CARD_PATH)) {
path = path.substring(SD_CARD_PATH.length());
}
return path;
}
@Override
public int getItemCount() {
@@ -183,8 +148,8 @@ public class ScriptListRecyclerView extends ThemeColorRecyclerView {
name = (TextView) itemView.findViewById(R.id.name);
path = (TextView) itemView.findViewById(R.id.path);
ViewTool.$(itemView, R.id.edit).setOnClickListener(mOnEditIconClickListener);
ViewTool.$(itemView, R.id.more).setOnClickListener(mOnMoreIconClickListener);
itemView.setOnClickListener(mOnItemClickListener);
ViewTool.$(itemView, R.id.run).setOnClickListener(mOnRunIconClickListener);
itemView.setOnClickListener(mOnItemClickListenerProxy);
}
}

View File

@@ -9,10 +9,10 @@ import android.support.v7.widget.RecyclerView;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.scriptdroid.droid.script.file.ScriptFile;
import com.stardust.scriptdroid.droid.script.file.ScriptFileList;
import com.stardust.scriptdroid.external.CommonUtils;
import com.stardust.scriptdroid.external.shortcut.Shortcut;
import com.stardust.scriptdroid.external.shortcut.ShortcutActivity;
import com.stardust.scriptdroid.ui.edit.EditActivity;
import com.stardust.scriptdroid.ui.edit.IridiumEditActivity;
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
@@ -25,6 +25,7 @@ import org.greenrobot.eventbus.EventBus;
public abstract class ScriptFileOperation {
public static class ShowMessageEvent {
public int messageResId;
@@ -33,6 +34,25 @@ public abstract class ScriptFileOperation {
}
}
public static void openByOtherApps(ScriptFile scriptFile) {
Uri uri = Uri.parse("file://" + scriptFile.getPath());
App.getApp().startActivity(new Intent(Intent.ACTION_VIEW).setDataAndType(uri, "text/plain").addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
public static void createShortcut(ScriptFile scriptFile){
new Shortcut(App.getApp()).name(scriptFile.getSimplifiedName())
.targetClass(ShortcutActivity.class)
.icon(R.drawable.ic_robot_green)
.extras(new Intent().putExtra(CommonUtils.EXTRA_KEY_PATH, scriptFile.getPath()))
.send();
}
public static void edit(ScriptFile file) {
EditActivity.editFile(App.getApp(), file.getSimplifiedName(), file.getPath());
}
public abstract void operate(RecyclerView recyclerView, ScriptFileList scriptFileList, int position);
private String mName;
@@ -85,7 +105,7 @@ public abstract class ScriptFileOperation {
@Override
public void operate(RecyclerView recyclerView, ScriptFileList scriptFileList, int position) {
ScriptFile scriptFile = scriptFileList.get(position);
EditActivity.editFile(App.getApp(), scriptFile.name, scriptFile.path);
EditActivity.editFile(App.getApp(), scriptFile.getSimplifiedName(), scriptFile.getPath());
//脚本
//任务&控制台
//教程
@@ -102,7 +122,7 @@ public abstract class ScriptFileOperation {
@Override
public void operate(RecyclerView recyclerView, ScriptFileList scriptFileList, int position) {
ScriptFile scriptFile = scriptFileList.get(position);
Uri uri = Uri.parse("file://" + scriptFile.path);
Uri uri = Uri.parse("file://" + scriptFile.getPath());
App.getApp().startActivity(new Intent(Intent.ACTION_VIEW).setDataAndType(uri, "text/plain").addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
}
@@ -115,7 +135,7 @@ public abstract class ScriptFileOperation {
@Override
public void operate(final RecyclerView recyclerView, final ScriptFileList scriptFileList, final int position) {
String oldName = scriptFileList.get(position).name;
String oldName = scriptFileList.get(position).getSimplifiedName();
new ThemeColorMaterialDialogBuilder(recyclerView.getContext())
.title(R.string.text_rename)
.checkBoxPrompt(App.getApp().getString(R.string.text_rename_file_meanwhile), false, null)
@@ -139,10 +159,10 @@ public abstract class ScriptFileOperation {
@Override
public void operate(RecyclerView recyclerView, ScriptFileList scriptFileList, int position) {
ScriptFile scriptFile = scriptFileList.get(position);
new Shortcut(App.getApp()).name(scriptFile.name)
new Shortcut(App.getApp()).name(scriptFile.getSimplifiedName())
.targetClass(ShortcutActivity.class)
.icon(R.drawable.ic_robot_green)
.extras(new Intent().putExtra("path", scriptFile.path))
.extras(new Intent().putExtra("path", scriptFile.getPath()))
.send();
EventBus.getDefault().post(R.string.text_already_create);
}

View File

@@ -3,20 +3,17 @@ package com.stardust.scriptdroid.ui.main.sample_list;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.app.Fragment;
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.sample.Sample;
import com.stardust.scriptdroid.sample.SampleFileManager;
import com.stardust.scriptdroid.scripts.sample.Sample;
import com.stardust.scriptdroid.scripts.sample.SampleFileManager;
import com.stardust.scriptdroid.tool.FileUtils;
import com.stardust.scriptdroid.ui.edit.EditActivity;
import com.stardust.scriptdroid.ui.main.my_script_list.MyScriptListFragment;
@@ -72,9 +69,9 @@ public class SampleScriptListFragment extends Fragment {
}
private void copySampleToMyScripts(Sample sample) {
String path = ScriptFile.DEFAULT_FOLDER + sample.name + ".js";
String path = ScriptFile.DEFAULT_DIRECTORY_PATH + sample.name + ".js";
if (!ScriptFileList.getImpl().containsPath(path) && FileUtils.copyAsset(sample.path, path)) {
ScriptFileList.getImpl().add(new ScriptFile(sample.name, path));
ScriptFileList.getImpl().add(new ScriptFile(path));
EventBus.getDefault().post(new MessageEvent(MyScriptListFragment.MESSAGE_SCRIPT_FILE_ADDED));
Snackbar.make(mSampleScriptListRecyclerView, R.string.text_import_succeed, Snackbar.LENGTH_SHORT).show();
} else {

View File

@@ -16,8 +16,7 @@ import com.bignerdranch.expandablerecyclerview.ExpandableRecyclerAdapter;
import com.bignerdranch.expandablerecyclerview.ParentViewHolder;
import com.bignerdranch.expandablerecyclerview.model.Parent;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.sample.Sample;
import com.stardust.scriptdroid.ui.edit.EditActivity;
import com.stardust.scriptdroid.scripts.sample.Sample;
import com.stardust.widget.LevelBeamView;
import java.util.ArrayList;
@@ -87,9 +86,9 @@ public class SampleScriptListRecyclerView extends RecyclerView {
mOnItemClickListener = onItemClickListener;
}
public void setSamples(List<com.stardust.scriptdroid.sample.SampleGroup> samples) {
public void setSamples(List<com.stardust.scriptdroid.scripts.sample.SampleGroup> samples) {
mSampleGroups.clear();
for (com.stardust.scriptdroid.sample.SampleGroup sampleGroup : samples) {
for (com.stardust.scriptdroid.scripts.sample.SampleGroup sampleGroup : samples) {
mSampleGroups.add(new SampleGroup(sampleGroup));
}
mAdapter = new Adapter(mSampleGroups);
@@ -103,9 +102,9 @@ public class SampleScriptListRecyclerView extends RecyclerView {
private class SampleGroup implements Parent<Sample> {
private com.stardust.scriptdroid.sample.SampleGroup mSampleGroup;
private com.stardust.scriptdroid.scripts.sample.SampleGroup mSampleGroup;
SampleGroup(com.stardust.scriptdroid.sample.SampleGroup sampleGroup) {
SampleGroup(com.stardust.scriptdroid.scripts.sample.SampleGroup sampleGroup) {
mSampleGroup = sampleGroup;
}

View File

@@ -51,7 +51,9 @@ public class AboutActivity extends BaseActivity {
@ViewBinding.Click(R.id.github)
private void openGitHub() {
IntentTool.goToLink(this, getString(R.string.my_github));
if (!IntentTool.goToLink(this, getString(R.string.my_github))) {
Toast.makeText(this, R.string.text_no_brower, Toast.LENGTH_SHORT).show();
}
}
@ViewBinding.Click(R.id.qq)

View File

@@ -11,13 +11,11 @@ import android.view.View;
import android.widget.Toast;
import com.stardust.scriptdroid.Pref;
import com.stardust.scriptdroid.sample.SampleFileManager;
import com.stardust.scriptdroid.tool.IntentTool;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.stardust.scriptdroid.ui.error.IssueReportActivity;
import com.stardust.util.MapEntries;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.ui.main.MainActivity;
import com.stardust.theme.app.ColorSelectActivity;
import com.stardust.theme.util.ListBuilder;

View File

@@ -0,0 +1,81 @@
package com.stardust.util;
import com.stardust.scriptdroid.scripts.StorageScriptProvider;
import org.junit.Assert;
import org.junit.Test;
import java.io.File;
import java.util.Arrays;
import java.util.Comparator;
/**
* Created by Stardust on 2017/3/31.
*/
public class FileSorter {
public static void sort(File[] files) {
Arrays.sort(files, new Comparator<File>() {
@Override
public int compare(File o1, File o2) {
if (o1.isDirectory() != o2.isDirectory())
return o1.isDirectory() ? Integer.MIN_VALUE : Integer.MAX_VALUE;
return o1.getName().compareTo(o2.getName());
}
});
}
public static class TestSuite {
@Test
public void testEngFileSort() {
File file1 = new File("d:/a.txt");
File file2 = new File("e:/b.txt");
File file3 = new File("c:/c.txt");
File[] files = {file2, file3, file1};
sort(files);
Assert.assertArrayEquals(new File[]{file1, file2, file3}, files);
}
@Test
public void testEngFileSortWithDirectory() {
File dir1 = new File("d:/");
File dir2 = new File("e:/");
File file1 = new File("e:/a.txt");
File file2 = new File("e:/b.txt");
File file3 = new File("d:/c.txt");
Assert.assertTrue(dir1.isDirectory());
Assert.assertTrue(dir2.isDirectory());
File[] files = {file2, file3, dir1, file1, dir2};
sort(files);
Assert.assertArrayEquals(new File[]{dir1, dir2, file1, file2, file3}, files);
}
@Test
public void testCnFileSort() {
File file1 = new File("a.txt");
File file2 = new File("b.txt");
File file3 = new File("啊.txt");
File file4 = new File("啊啊.txt");
File[] files = {file2, file4, file3, file1};
sort(files);
Assert.assertArrayEquals(new File[]{file1, file2, file3, file4}, files);
}
@Test
public void testCnFileSortWithDirectory() {
File dir1 = new File("d:/整理/");
File dir2 = new File("d:/迅雷下载/");
File file1 = new File("d:/整理/a.txt");
File file2 = new File("啊.txt");
File file3 = new File("啊啊.txt");
Assert.assertTrue(dir1.isDirectory());
Assert.assertTrue(dir2.isDirectory());
File[] files = {file2, file3, dir1, file1, dir2};
sort(files);
Assert.assertArrayEquals(new File[]{dir1, dir2, file1, file2, file3}, files);
}
}
}

View File

@@ -0,0 +1,58 @@
package com.stardust.util;
import org.junit.Test;
import java.util.LinkedHashMap;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* Created by Stardust on 2017/3/31.
*/
public class LimitedHashMap<K, V> extends LinkedHashMap<K, V> {
private int mMaxSize;
public LimitedHashMap(int maxSize) {
super(4, 0.75f, true);
mMaxSize = maxSize;
}
@Override
protected boolean removeEldestEntry(Entry<K, V> eldest) {
return size() > mMaxSize;
}
public static class TestSuite {
@Test
public void testAutoRemove() {
LimitedHashMap<String, Integer> hashMap = new LimitedHashMap<>(5);
hashMap.put("a", 1);
hashMap.put("b", 2);
hashMap.put("c", 3);
hashMap.put("d", 4);
hashMap.put("e", 5);
hashMap.put("f", 6);
assertFalse(hashMap.containsKey("a"));
}
@Test
public void testAutoReorder() {
LimitedHashMap<String, Integer> hashMap = new LimitedHashMap<>(5);
hashMap.put("a", 1);
hashMap.put("b", 2);
hashMap.put("c", 3);
hashMap.put("d", 4);
hashMap.put("e", 5);
hashMap.get("a");
hashMap.put("f", 6);
assertTrue(hashMap.containsKey("a"));
assertFalse(hashMap.containsKey("b"));
}
}
}

View File

@@ -9,83 +9,114 @@ import java.lang.reflect.Method;
/**
* Created by Stardust on 2017/1/30.
*
* <p>
* 哈你说我为什么不用AA框架之前还不知道嘛所以现在用了。
*/
public class ViewBinder {
public static void bind(Object o) {
Method findViewById;
public interface ViewSupplier {
View findViewById(int id);
}
public static void bind(final Object o) {
final Method findViewById;
try {
findViewById = o.getClass().getMethod("findViewById", int.class);
findViewById.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new RuntimeException("You must implement findViewById to use view binding", e);
}
bind(o, new ViewSupplier() {
@Override
public View findViewById(int id) {
try {
return (View) findViewById.invoke(o, id);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
});
}
public static void bind(Object o, final View view) {
bind(o, new ViewSupplier() {
@Override
public View findViewById(int id) {
return view.findViewById(id);
}
});
}
public static void bind(Object o, ViewSupplier viewSupplier) {
Method[] methods = o.getClass().getDeclaredMethods();
bindId(o, findViewById);
bindId(o, viewSupplier);
for (Method method : methods) {
method.setAccessible(true);
bindClick(o, method, findViewById);
bindCheck(o, method, findViewById);
bindClick(o, method, viewSupplier);
bindCheck(o, method, viewSupplier);
}
}
private static void bindId(Object o, Method findViewById) {
public static void bindId(Object o, final View v) {
bindId(o, new ViewSupplier() {
@Override
public View findViewById(int id) {
return v.findViewById(id);
}
});
}
private static void bindId(Object o, ViewSupplier viewSupplier) {
for (Field field : o.getClass().getDeclaredFields()) {
field.setAccessible(true);
ViewBinding.Id id = field.getAnnotation(ViewBinding.Id.class);
if (id == null || id.value() == 0)
continue;
try {
field.set(o, findViewById.invoke(o, id.value()));
} catch (IllegalAccessException | InvocationTargetException e) {
field.set(o, viewSupplier.findViewById(id.value()));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
private static void bindCheck(final Object o, final Method method, Method findViewById) {
private static void bindCheck(final Object o, final Method method, ViewSupplier viewSupplier) {
ViewBinding.Check annotation = method.getAnnotation(ViewBinding.Check.class);
if (annotation == null || annotation.value() == 0)
return;
int id = annotation.value();
try {
CompoundButton button = (CompoundButton) findViewById.invoke(o, id);
button.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
try {
method.invoke(o, isChecked);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
CompoundButton button = (CompoundButton) viewSupplier.findViewById(id);
button.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
try {
method.invoke(o, isChecked);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
});
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
});
}
private static void bindClick(final Object o, final Method method, Method findViewById) {
private static void bindClick(final Object o, final Method method, ViewSupplier viewSupplier) {
ViewBinding.Click annotation = method.getAnnotation(ViewBinding.Click.class);
if (annotation == null || annotation.value() == 0)
return;
int id = annotation.value();
try {
View view = (View) findViewById.invoke(o, id);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
invokeMethod(o, method);
}
});
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
View view = viewSupplier.findViewById(id);
if (view == null)
return;
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
invokeMethod(o, method);
}
});
}
private static void invokeMethod(Object o, Method method) {

View File

@@ -78,7 +78,6 @@ public class LevelBeamView extends View {
@Override
protected void onDraw(Canvas canvas) {
Log.i(TAG, "onDraw");
super.onDraw(canvas);
for (int lvl = 0; lvl <= mLevel; lvl++) {
float LINE_X = mPaddingLeft + lvl * mLinesWidth;

View File

@@ -0,0 +1,13 @@
package com.stardust.widget;
import android.support.v7.widget.RecyclerView;
import android.view.View;
/**
* Created by Stardust on 2017/3/27.
*/
public interface OnItemClickListener {
void onItemClick(RecyclerView parent, View item, int position);
}