Add: tasker plugin support, injectable webview
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
31
app/src/main/java/com/stardust/pio/PFile.java
Normal file
31
app/src/main/java/com/stardust/pio/PFile.java
Normal 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){
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
106
app/src/main/java/com/stardust/pio/PReadableFile.java
Normal file
106
app/src/main/java/com/stardust/pio/PReadableFile.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
15
app/src/main/java/com/stardust/pio/PWritableFile.java
Normal file
15
app/src/main/java/com/stardust/pio/PWritableFile.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package com.stardust.pio;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/4/1.
|
||||
*/
|
||||
|
||||
public class PWritableFile extends PFile {
|
||||
|
||||
public PWritableFile(){
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
14
app/src/main/java/com/stardust/pio/UncheckedIOException.java
Normal file
14
app/src/main/java/com/stardust/pio/UncheckedIOException.java
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.stardust.scriptdroid.droid.runtime;
|
||||
package com.stardust.scriptdroid.droid.runtime.api;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/7.
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
14
app/src/main/java/com/stardust/scriptdroid/external/CommonUtils.java
vendored
Normal file
14
app/src/main/java/com/stardust/scriptdroid/external/CommonUtils.java
vendored
Normal 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";
|
||||
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
44
app/src/main/java/com/stardust/scriptdroid/external/tasker/FireSettingReceiver.java
vendored
Normal file
44
app/src/main/java/com/stardust/scriptdroid/external/tasker/FireSettingReceiver.java
vendored
Normal 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)));
|
||||
}
|
||||
}
|
||||
74
app/src/main/java/com/stardust/scriptdroid/external/tasker/TaskPrefEditActivity.java
vendored
Normal file
74
app/src/main/java/com/stardust/scriptdroid/external/tasker/TaskPrefEditActivity.java
vendored
Normal 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));
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.stardust.scriptdroid.sample;
|
||||
package com.stardust.scriptdroid.scripts.sample;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/3/13.
|
||||
@@ -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.
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.stardust.scriptdroid.sample;
|
||||
package com.stardust.scriptdroid.scripts.sample;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
81
app/src/main/java/com/stardust/util/FileSorter.java
Normal file
81
app/src/main/java/com/stardust/util/FileSorter.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
58
app/src/main/java/com/stardust/util/LimitedHashMap.java
Normal file
58
app/src/main/java/com/stardust/util/LimitedHashMap.java
Normal 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"));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user