add: about page, settings, notification switch

This commit is contained in:
hyb1996
2017-02-03 10:41:23 +08:00
parent 636ce6d889
commit 5640b47663
47 changed files with 1392 additions and 307 deletions

View File

@@ -0,0 +1,89 @@
package com.stardust.scriptdroid;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Toast;
import com.stardust.scriptdroid.tool.IntentTool;
import com.stardust.view.ViewBinder;
import com.stardust.view.ViewBinding;
/**
* Created by Stardust on 2017/2/2.
*/
public class AboutActivity extends BaseActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setUpUI();
}
private void setUpUI() {
setContentView(R.layout.activity_about);
setUpToolbar();
ViewBinder.bind(this);
}
private void setUpToolbar() {
Toolbar toolbar = $(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
@ViewBinding.Click(R.id.github)
private void openGitHub() {
IntentTool.goToLink(this, getString(R.string.my_github));
}
@ViewBinding.Click(R.id.qq)
private void openQQToChatWithMe() {
String qq = getString(R.string.qq);
IntentTool.goToQQ(this, qq);
}
@ViewBinding.Click(R.id.email)
private void openEmailToSendMe() {
String email = getString(R.string.email);
IntentTool.goToMail(this, email);
}
@ViewBinding.Click(R.id.donate)
private void showDonateMeDialog() {
}
@ViewBinding.Click(R.id.share)
private void share() {
IntentTool.shareText(this, getString(R.string.share_app));
}
@ViewBinding.Click(R.id.icon)
private void lol() {
Toast.makeText(this, "略略略", Toast.LENGTH_LONG).show();
}
@ViewBinding.Click(R.id.developer)
private void hhh() {
Toast.makeText(this, "这是软件开发者(。・・)", Toast.LENGTH_LONG).show();
}
private void setClipText(CharSequence label, CharSequence text) {
ClipboardManager clipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
clipboardManager.setPrimaryClip(ClipData.newPlainText(label, text));
Toast.makeText(this, label + getString(R.string.text_already_copy_to_clip), Toast.LENGTH_SHORT).show();
}
}

View File

@@ -2,6 +2,8 @@ package com.stardust.scriptdroid;
import android.app.Application;
import com.stardust.util.CrashHandler;
/**
* Created by Stardust on 2017/1/27.
*/
@@ -16,6 +18,7 @@ public class App extends Application {
public void onCreate() {
super.onCreate();
Thread.setDefaultUncaughtExceptionHandler(new CrashHandler(ErrorReportActivity.class));
instance = this;
}
}

View File

@@ -0,0 +1,53 @@
package com.stardust.scriptdroid;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import com.stardust.scriptdroid.droid.runtime.action.ActionPerformService;
import com.stardust.scriptdroid.ui.AssistModeSwitchNotification;
/**
* Created by Stardust on 2017/2/2.
*/
public class AssistModeSwitchService extends Service {
public static PendingIntent getStartIntent() {
Intent intent = new Intent(App.getApp(), AssistModeSwitchService.class)
.putExtra("intentValid", true)
.putExtra("switch", "assistMode");
return PendingIntent.getService(App.getApp(), 0, intent, 0);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
boolean intentValid = intent.getBooleanExtra("intentValid", false);
if (intentValid) {
String action = intent.getStringExtra("action");
if (action != null) {
if (action.equals("assistMode")) {
ActionPerformService.setAssistModeEnable(!ActionPerformService.isAssistModeEnable());
} else {
AssistModeSwitchNotification.setEnable(false);
}
}
}
stopSelf();
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
public static PendingIntent getDeletePendingIntent() {
Intent deleteIntent = new Intent(App.getApp(), AssistModeSwitchService.class)
.putExtra("intentValid", true)
.putExtra("switch", "assistModeNotification");
return PendingIntent.getService(App.getApp(), 0, deleteIntent, 0);
}
}

View File

@@ -6,10 +6,11 @@ import android.support.v7.app.AppCompatActivity;
import android.view.View;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
/**
* Created by Stardust on 2017/1/23.
@@ -30,6 +31,10 @@ public class BaseActivity extends AppCompatActivity {
String[] requestPermissions = getRequestPermissions(permissions);
if (requestPermissions.length > 0)
requestPermissions(requestPermissions, PERMISSION_REQUEST_CODE);
} else {
int[] grantResults = new int[permissions.length];
Arrays.fill(grantResults, PERMISSION_GRANTED);
onRequestPermissionsResult(PERMISSION_REQUEST_CODE, permissions, grantResults);
}
}

View File

@@ -0,0 +1,100 @@
package com.stardust.scriptdroid;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.widget.Toolbar;
import android.widget.Toast;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import java.util.Timer;
import java.util.TimerTask;
/**
* Created by Stardust on 2017/2/2.
*/
public class ErrorReportActivity extends BaseActivity {
protected void onCreate(Bundle savedInstanceState) {
try {
super.onCreate(savedInstanceState);
setUpUI();
handleIntent();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
private void handleIntent() {
String message = getIntent().getStringExtra("message");
final String errorDetail = getIntent().getStringExtra("error");
new MaterialDialog.Builder(this)
.content(message)
.positiveText("退出")
.negativeText("复制调试信息")
.onPositive(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
exit();
}
})
.onNegative(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
copyToClip(getDeviceMessage() + errorDetail);
exitAfter(1000);
}
})
.cancelable(false)
.show();
}
private String getDeviceMessage() {
return "Android: " + Build.VERSION.SDK_INT + "\n";
}
private void exitAfter(long millis) {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
exit();
}
}, millis);
}
private void copyToClip(String text) {
((ClipboardManager) getSystemService(CLIPBOARD_SERVICE))
.setPrimaryClip(ClipData.newPlainText("Debug", text));
Toast.makeText(ErrorReportActivity.this, R.string.text_already_copy_to_clip, Toast.LENGTH_SHORT).show();
}
private void setUpUI() {
setContentView(R.layout.activity_error_report);
setUpToolbar();
}
private void setUpToolbar() {
Toolbar toolbar = $(R.id.toolbar);
toolbar.setTitle(getString(R.string.text_error_report));
setSupportActionBar(toolbar);
getSupportActionBar().setHomeButtonEnabled(false);
}
private void exit() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
finishAffinity();
} else {
finish();
}
}
}

View File

@@ -0,0 +1,48 @@
package com.stardust.scriptdroid;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.scriptdroid.droid.script.file.ScriptFile;
import com.stardust.scriptdroid.droid.script.file.SharedPrefScriptFileList;
import java.io.File;
/**
* Created by Stardust on 2017/2/2.
*/
public class ImportIntentActivity extends BaseActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handleIntent();
}
private void handleIntent() {
Intent intent = getIntent();
final String path = intent.getData().getPath();
if (!TextUtils.isEmpty(path)) {
new MaterialDialog.Builder(this)
.title(R.string.text_please_input_name)
.input(getString(R.string.text_name), new File(path).getName(), new MaterialDialog.InputCallback() {
@Override
public void onInput(@NonNull MaterialDialog dialog, CharSequence input) {
SharedPrefScriptFileList.getInstance().add(new ScriptFile(input.toString(), path));
startMainActivity();
}
})
.show();
}
}
private void startMainActivity() {
startActivity(new Intent(this, MainActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP));
}
}

View File

@@ -1,7 +1,6 @@
package com.stardust.scriptdroid;
import android.Manifest;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
@@ -12,17 +11,17 @@ import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.Toolbar;
import android.text.InputType;
import android.view.View;
import android.widget.Toast;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.afollestad.materialdialogs.folderselector.FileChooserDialog;
import com.stardust.app.NotRemindAgainDialog;
import com.stardust.scriptdroid.droid.runtime.action.ActionPerformService;
import com.stardust.scriptdroid.droid.script.file.ScriptFile;
import com.stardust.scriptdroid.droid.script.file.ScriptFileList;
import com.stardust.scriptdroid.droid.script.file.SharedPrefScriptFileList;
import com.stardust.scriptdroid.file.FileChooser;
import com.stardust.scriptdroid.file.FileUtils;
import com.stardust.scriptdroid.tool.BackPressedHandler;
import com.stardust.scriptdroid.ui.ScriptFileOperation;
import com.stardust.scriptdroid.ui.ScriptListRecyclerView;
import com.stardust.scriptdroid.ui.SlideMenuFragment;
@@ -32,22 +31,19 @@ import com.stardust.view.ViewBinding;
import com.stardust.view.accessibility.AccessibilityServiceUtils;
import java.io.File;
import java.io.InputStream;
public class MainActivity extends BaseActivity {
public class MainActivity extends BaseActivity implements FileChooserDialog.FileCallback {
private SlidingUpPanel mAddFilePanel;
private ScriptListRecyclerView mScriptListRecyclerView;
private ScriptFileList mScriptFileList;
private FileChooser mFileChooser;
private DrawerLayout mDrawerLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setUpUI();
setUpFileChooser();
checkPermissions();
}
@@ -72,19 +68,6 @@ public class MainActivity extends BaseActivity {
}
}
private void setUpFileChooser() {
mFileChooser = new FileChooser(this);
mFileChooser.setOnFileChoseListener(new FileChooser.OnFileChoseListener() {
@Override
public void onFileChose(InputStream inputStream) {
String path = FileUtils.getPath(inputStream);
if (path != null) {
MainActivity.this.addScriptFile(path);
}
}
});
}
private void addScriptFile(final String path) {
new MaterialDialog.Builder(this).title(R.string.text_name)
.inputType(InputType.TYPE_CLASS_TEXT)
@@ -159,19 +142,14 @@ public class MainActivity extends BaseActivity {
@ViewBinding.Click(R.id.import_from_file)
private void showFileChooser() {
mFileChooser.startFileManagerToChoose("*/*", new FileChooser.FileManagerNotFoundHandler() {
@Override
public void handle(ActivityNotFoundException exception, String mimeType) {
exception.printStackTrace();
Snackbar.make(mDrawerLayout, R.string.text_file_manager_not_found, Snackbar.LENGTH_SHORT).show();
}
});
new FileChooserDialog.Builder(this)
.extensionsFilter(".js", ".txt")
.show();
}
@ViewBinding.Click(R.id.setting)
private void startSettingActivity() {
//TODO create Setting Activity
Toast.makeText(this, "暂无", Toast.LENGTH_LONG).show();
startActivity(new Intent(this, SettingsActivity.class));
}
@ViewBinding.Click(R.id.exit)
@@ -179,10 +157,6 @@ public class MainActivity extends BaseActivity {
super.finish();
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
mFileChooser.onActivityResult(requestCode, resultCode, data);
}
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions, @NonNull int[] grantResults) {
@@ -191,4 +165,16 @@ public class MainActivity extends BaseActivity {
mScriptListRecyclerView.getAdapter().notifyDataSetChanged();
}
}
@Override
public void onFileSelection(@NonNull FileChooserDialog dialog, @NonNull File file) {
addScriptFile(file.getPath());
}
private BackPressedHandler mBackPressedHandler = new BackPressedHandler.DoublePressExit(this);
@Override
public void onBackPressed() {
mBackPressedHandler.onBackPressed();
}
}

View File

@@ -18,7 +18,7 @@ public class NonUiInitializer {
private static final Map<String, Integer> SAMPLES = new MapEntries<String, Integer>()
.entry("sample_open_wechat_moment.js", R.string.text_sample_open_what_moment)
.entry("sample_open_running_services.js", R.string.text_sample_open_running_services)
.entry("sample_simple_calculator.js", R.string.text_sample_simple_calculator)
.entry("sample_hello_big_sister.js", R.string.text_sample_hello_big_sister)
.map();
private static NonUiInitializer instance = new NonUiInitializer();
@@ -35,7 +35,7 @@ public class NonUiInitializer {
}
public void copySampleScriptFileIfNeeded(){
public void copySampleScriptFileIfNeeded() {
if (!Pref.def().getBoolean(Pref.SAMPLE_SCRIPTS_COPIED, false)) {
copySampleScriptFile();
}

View File

@@ -0,0 +1,99 @@
package com.stardust.scriptdroid;
import android.content.Intent;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceScreen;
import android.support.annotation.Nullable;
import android.support.v7.widget.Toolbar;
import android.view.View;
import com.stardust.util.MapEntries;
import java.util.Map;
import de.psdev.licensesdialog.LicensesDialog;
/**
* Created by Stardust on 2017/2/2.
*/
public class SettingsActivity extends BaseActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setUpUI();
}
private void setUpUI() {
setContentView(R.layout.activity_settings);
setUpToolbar();
getFragmentManager().beginTransaction().replace(R.id.fragment_setting, new PreferenceFragment()).commit();
}
private void setUpToolbar() {
Toolbar toolbar = $(R.id.toolbar);
toolbar.setTitle(R.string.text_setting);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
public static class PreferenceFragment extends android.preference.PreferenceFragment {
private Map<String, Runnable> ACTION_MAP;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
@Override
public void onStart() {
super.onStart();
ACTION_MAP = new MapEntries<String, Runnable>()
.entry(getString(R.string.text_about), new Runnable() {
@Override
public void run() {
startActivity(new Intent(getActivity(), AboutActivity.class));
}
})
.entry(getString(R.string.text_licenses), new Runnable() {
@Override
public void run() {
showLicenseDialog();
}
})
.map();
}
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
Runnable action = ACTION_MAP.get(preference.getTitle().toString());
if (action != null) {
action.run();
return true;
} else {
return super.onPreferenceTreeClick(preferenceScreen, preference);
}
}
private void showLicenseDialog() {
new LicensesDialog.Builder(getActivity())
.setNotices(R.raw.licenses)
.setIncludeOwnLicense(true)
.build()
.showAppCompat();
}
}
}

View File

@@ -1,7 +1,7 @@
package com.stardust.scriptdroid.droid;
import com.stardust.scriptdroid.droid.runtime.DroidRuntime;
import com.stardust.scriptdroid.droid.script.GBJDuktapeJavaScriptEngine;
import com.stardust.scriptdroid.droid.script.DuktapeJavaScriptEngine;
import com.stardust.scriptdroid.droid.script.JavaScriptEngine;
import com.stardust.scriptdroid.file.FileUtils;
@@ -19,7 +19,7 @@ public class Droid {
}
private static final DroidRuntime RUNTIME = DroidRuntime.getRuntime();
private static final JavaScriptEngine JAVA_SCRIPT_ENGINE = new GBJDuktapeJavaScriptEngine(RUNTIME);
private static final JavaScriptEngine JAVA_SCRIPT_ENGINE = new DuktapeJavaScriptEngine(RUNTIME);
private static final OnRunFinishedListener DEFAULT_LISTENER = new OnRunFinishedListener() {
@Override
public void onRunFinished(Object result, Exception e) {

View File

@@ -10,7 +10,6 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.UIActionPerformActivity;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.runtime.action.Action;
@@ -194,12 +193,7 @@ public class DroidRuntime implements IDroidRuntime {
}
public MaterialDialog show(final MaterialDialog.Builder dialog) {
UIActionPerformActivity.performAction(new Runnable() {
@Override
public void run() {
dialog.show();
}
});
return null;
}
}

View File

@@ -3,6 +3,7 @@ package com.stardust.scriptdroid.droid.runtime.action;
import android.accessibilityservice.AccessibilityService;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Intent;
import android.graphics.Rect;
import android.os.Build;
import android.preference.PreferenceManager;
@@ -27,6 +28,10 @@ import java.util.List;
public class ActionPerformService extends AccessibilityService {
public static final String ACTION_CHANGE_ASSIST_MODE = App.getApp().getPackageName() + ".ACTION_CHANGE_ASSIST_MODE";
private static final String TAG = "ActionPerformService";
private static final String KEY_ASSIST_MODE_ENABLE = "ASSIST_MODE_ENABLE";
private static ActionPerformService instance;
@@ -65,6 +70,7 @@ public class ActionPerformService extends AccessibilityService {
public static void setAssistModeEnable(boolean assistModeEnable) {
ActionPerformService.assistModeEnable = assistModeEnable;
PreferenceManager.getDefaultSharedPreferences(App.getApp()).edit().putBoolean(KEY_ASSIST_MODE_ENABLE, assistModeEnable).apply();
App.getApp().sendBroadcast(new Intent(ACTION_CHANGE_ASSIST_MODE).putExtra("enable", assistModeEnable));
}
@Override

View File

@@ -1,12 +1,19 @@
package com.stardust.scriptdroid.droid.script;
import android.util.Log;
import android.util.Pair;
import com.squareup.duktape.Duktape;
import com.efurture.script.JSTransformer;
import com.furture.react.DuktapeEngine;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.droid.runtime.api.IDroidRuntime;
import static com.stardust.scriptdroid.droid.script.JavaScriptEngine.Init.readInitScript;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Created by Stardust on 2017/1/27.
@@ -14,54 +21,100 @@ import static com.stardust.scriptdroid.droid.script.JavaScriptEngine.Init.readIn
public class DuktapeJavaScriptEngine implements JavaScriptEngine {
private static final String TAG = "DuktapeJavaScriptEngine";
private Duktape mDuktape = Duktape.create();
private boolean mRecycled = false;
private final List<Pair<DuktapeEngine, Thread>> mDuktapeEngineList = new ArrayList<>();
private static final String INIT_SCRIPT;
private Map<String, Object> mVariableMap = new HashMap<>();
static {
try {
INIT_SCRIPT = JSTransformer.parse(new StringReader(Init.INIT_SCRIPT));
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public DuktapeJavaScriptEngine(IDroidRuntime runtime) {
setRuntime(runtime);
execute(readInitScript());
}
public void setRuntime(IDroidRuntime runtime) {
private void setRuntime(IDroidRuntime runtime) {
set("droid", IDroidRuntime.class, runtime);
}
@Override
public Object execute(String script) {
return mDuktape.evaluate(script);
public Object execute(String script) throws IOException {
DuktapeEngine duktapeEngine = new DuktapeEngine();
init(duktapeEngine);
add(duktapeEngine, Thread.currentThread());
Object result;
try {
result = duktapeEngine.execute(JSTransformer.parse(new StringReader(script)));
} catch (IOException e) {
remove(duktapeEngine);
throw e;
}
remove(duktapeEngine);
return result;
}
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());
}
}
private void remove(DuktapeEngine duktapeEngine) {
duktapeEngine.destory();
synchronized (mDuktapeEngineList) {
Iterator<Pair<DuktapeEngine, Thread>> iterator = mDuktapeEngineList.iterator();
while (iterator.hasNext()) {
Pair<DuktapeEngine, Thread> pair = iterator.next();
if (pair.first == duktapeEngine) {
stop(pair.first, pair.second);
}
iterator.remove();
break;
}
}
}
private void stop(DuktapeEngine engine, Thread thread) {
try {
thread.interrupt();
} catch (Exception e) {
e.printStackTrace();
}
engine.destory();
}
private void add(DuktapeEngine duktapeEngine, Thread thread) {
synchronized (mDuktapeEngineList) {
mDuktapeEngineList.add(0, new Pair<>(duktapeEngine, thread));
}
}
@Override
public <T> void set(String varName, Class<T> c, T value) {
mDuktape.set(varName, c, value);
mVariableMap.put(varName, value);
}
@Override
public int stopAll() {
mDuktape.close();
return 1;
}
public void recycle() {
if (!mRecycled) {
mDuktape.close();
mRecycled = true;
}
}
@Override
public void finalize() throws Throwable {
try {
if (!mRecycled) {
Log.w(TAG, "Not recycled before finalize");
recycle();
int n;
synchronized (mDuktapeEngineList) {
for (Pair<DuktapeEngine, Thread> pair : mDuktapeEngineList) {
stop(pair.first, pair.second);
}
} catch (Throwable throwable) {
throwable.printStackTrace();
n = mDuktapeEngineList.size();
mDuktapeEngineList.clear();
}
super.finalize();
return n;
}
}

View File

@@ -1,120 +0,0 @@
package com.stardust.scriptdroid.droid.script;
import android.util.Pair;
import com.efurture.script.JSTransformer;
import com.furture.react.DuktapeEngine;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.droid.runtime.api.IDroidRuntime;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Created by Stardust on 2017/1/27.
*/
public class GBJDuktapeJavaScriptEngine implements JavaScriptEngine {
private final List<Pair<DuktapeEngine, Thread>> mDuktapeEngineList = new ArrayList<>();
private static final String INIT_SCRIPT;
private Map<String, Object> mVariableMap = new HashMap<>();
static {
try {
INIT_SCRIPT = JSTransformer.parse(new StringReader(Init.INIT_SCRIPT));
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public GBJDuktapeJavaScriptEngine(IDroidRuntime runtime) {
setRuntime(runtime);
}
private void setRuntime(IDroidRuntime runtime) {
set("droid", IDroidRuntime.class, runtime);
}
@Override
public Object execute(String script) throws IOException {
DuktapeEngine duktapeEngine = new DuktapeEngine();
init(duktapeEngine);
add(duktapeEngine, Thread.currentThread());
Object result;
try {
result = duktapeEngine.execute(JSTransformer.parse(new StringReader(script)));
} catch (IOException e) {
remove(duktapeEngine);
throw e;
}
remove(duktapeEngine);
return result;
}
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());
}
}
private void remove(DuktapeEngine duktapeEngine) {
duktapeEngine.destory();
synchronized (mDuktapeEngineList) {
Iterator<Pair<DuktapeEngine, Thread>> iterator = mDuktapeEngineList.iterator();
while (iterator.hasNext()) {
Pair<DuktapeEngine, Thread> pair = iterator.next();
if (pair.first == duktapeEngine) {
stop(pair.first, pair.second);
}
iterator.remove();
break;
}
}
}
private void stop(DuktapeEngine engine, Thread thread) {
try {
thread.interrupt();
} catch (Exception e) {
e.printStackTrace();
}
engine.destory();
}
private void add(DuktapeEngine duktapeEngine, Thread thread) {
synchronized (mDuktapeEngineList) {
mDuktapeEngineList.add(0, new Pair<>(duktapeEngine, thread));
}
}
@Override
public <T> void set(String varName, Class<T> c, T value) {
mVariableMap.put(varName, value);
}
@Override
public int stopAll() {
int n;
synchronized (mDuktapeEngineList) {
for (Pair<DuktapeEngine, Thread> pair : mDuktapeEngineList) {
stop(pair.first, pair.second);
}
n = mDuktapeEngineList.size();
mDuktapeEngineList.clear();
}
return n;
}
}

View File

@@ -0,0 +1,40 @@
package com.stardust.scriptdroid.tool;
import android.app.Activity;
import android.widget.Toast;
import com.stardust.scriptdroid.R;
/**
* Created by Stardust on 2017/2/3.
*/
public interface BackPressedHandler {
void onBackPressed();
class DoublePressExit implements BackPressedHandler {
private final Activity mActivity;
private long mLastPressedMillis;
private long mDoublePressInterval = 1000;
public DoublePressExit(Activity activity) {
mActivity = activity;
}
public DoublePressExit setDoublePressInterval(long doublePressInterval) {
mDoublePressInterval = doublePressInterval;
return this;
}
@Override
public void onBackPressed() {
if (System.currentTimeMillis() - mLastPressedMillis < mDoublePressInterval) {
mActivity.finish();
} else {
mLastPressedMillis = System.currentTimeMillis();
Toast.makeText(mActivity, R.string.text_press_again_to_exit, Toast.LENGTH_SHORT).show();
}
}
}
}

View File

@@ -0,0 +1,48 @@
package com.stardust.scriptdroid.tool;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.Nullable;
/**
* Intent工具用于Activity之间的跳转调起其他应用程序等
*/
public class IntentTool {
public static void goToQQ(Context context, String qq) {
try {
String url = "mqqwpa://im/chat?chat_type=wpa&uin=" + qq;
context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
} catch (Exception exception) {
exception.printStackTrace();
}
}
public static void goToMail(Context context, String sendTo, @Nullable String title, @Nullable String content) {
Uri uri = Uri.parse("mailto:" + sendTo);
String[] email = {sendTo};
Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
intent.putExtra(Intent.EXTRA_CC, email);
if (title != null)
intent.putExtra(Intent.EXTRA_SUBJECT, title);
if (content != null)
intent.putExtra(Intent.EXTRA_TEXT, content);
context.startActivity(Intent.createChooser(intent, "请选择邮件应用"));
}
public static void goToMail(Context context, String sendTo) {
goToMail(context, sendTo, null, null);
}
public static void goToLink(Context context, String link) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(link));
context.startActivity(intent);
}
public static void shareText(Context context, String text) {
context.startActivity(new Intent(Intent.ACTION_SEND)
.putExtra(Intent.EXTRA_TEXT, text)
.setType("text/plain"));
}
}

View File

@@ -0,0 +1,83 @@
package com.stardust.scriptdroid.ui;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.AssistModeSwitchService;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.runtime.action.ActionPerformService;
/**
* Created by Stardust on 2017/2/2.
*/
public class AssistModeSwitchNotification {
private static final int NOTIFY_ID = 11126;
private static final String KEY_ASSIST_MODE_NOTIFICATION = "KEY_ASSIST_MODE_NOTIFICATION";
private static boolean enable = false;
private static Notification notification;
static {
readPreference();
}
public static boolean isEnable() {
return enable;
}
public static void setEnable(boolean enable) {
if (AssistModeSwitchNotification.enable == enable)
return;
AssistModeSwitchNotification.enable = enable;
PreferenceManager.getDefaultSharedPreferences(App.getApp()).edit().putBoolean(KEY_ASSIST_MODE_NOTIFICATION, enable).apply();
if (enable) {
showNotification();
} else {
hideNotification();
}
}
public static void notifyAssistModeChanged() {
if (enable) {
setEnable(false);
setEnable(true);
}
}
private static void showNotification() {
notification = new NotificationCompat.Builder(App.getApp())
.setAutoCancel(false)
.setSmallIcon(R.drawable.ic_robot_head)
.setDeleteIntent(AssistModeSwitchService.getDeletePendingIntent())
.setContentText(ActionPerformService.isAssistModeEnable() ?
App.getApp().getString(R.string.text_assist_mode_enabled) :
App.getApp().getString(R.string.text_assist_mode_disabled))
.setContentIntent(AssistModeSwitchService.getStartIntent())
.build();
showNotification(notification);
}
private static void showNotification(Notification notification) {
NotificationManager notificationManager = (NotificationManager) App.getApp().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFY_ID, notification);
}
private static void hideNotification() {
NotificationManager notificationManager = (NotificationManager) App.getApp().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(NOTIFY_ID);
}
private static void readPreference() {
enable = PreferenceManager.getDefaultSharedPreferences(App.getApp()).getBoolean(KEY_ASSIST_MODE_NOTIFICATION, false);
setEnable(enable);
}
}

View File

@@ -1,6 +1,9 @@
package com.stardust.scriptdroid.ui;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
@@ -9,8 +12,6 @@ import android.support.v7.widget.SwitchCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.Toast;
import com.stardust.app.Fragment;
import com.stardust.scriptdroid.DocumentActivity;
@@ -21,7 +22,7 @@ import com.stardust.view.ViewBinder;
import com.stardust.view.ViewBinding;
import com.stardust.view.accessibility.AccessibilityServiceUtils;
import de.psdev.licensesdialog.LicensesDialog;
import static com.stardust.scriptdroid.droid.runtime.action.ActionPerformService.ACTION_CHANGE_ASSIST_MODE;
/**
* Created by Stardust on 2017/1/30.
@@ -35,7 +36,8 @@ public class SlideMenuFragment extends Fragment {
activity.getSupportFragmentManager().beginTransaction().replace(viewId, fragment).commit();
}
private SwitchCompat mAutoOperateServiceSwitch;
private SwitchCompat mAutoOperateServiceSwitch, mAssistServiceSwitch, mAssistServiceNotificationSwitch;
private Receiver mReceiver = new Receiver();
@Nullable
@Override
@@ -49,40 +51,68 @@ public class SlideMenuFragment extends Fragment {
if (mAutoOperateServiceSwitch == null) {
setUpSwitchCompat();
ViewBinder.bind(this);
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_CHANGE_ASSIST_MODE);
getContext().registerReceiver(mReceiver, filter);
}
syncSwitchStatus();
syncSwitchState();
}
private void syncSwitchStatus() {
private void syncSwitchState() {
mAutoOperateServiceSwitch.postDelayed(new Runnable() {
@Override
public void run() {
mAutoOperateServiceSwitch.setChecked(ActionPerformService.isEnable());
}
}, 450);
mAssistServiceSwitch.setChecked(ActionPerformService.isAssistModeEnable());
mAssistServiceNotificationSwitch.setChecked(AssistModeSwitchNotification.isEnable());
}
private void setUpSwitchCompat() {
mAutoOperateServiceSwitch = $(R.id.sw_auto_operate_service);
mAutoOperateServiceSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked && !ActionPerformService.isEnable()) {
AccessibilityServiceUtils.goToPermissionSetting(getContext());
} else if (!isChecked && ActionPerformService.isEnable()) {
ActionPerformService.disable();
}
}
});
mAssistServiceSwitch = $(R.id.sw_assist_service);
mAssistServiceNotificationSwitch = $(R.id.sw_assist_service_notification);
}
@ViewBinding.Click(R.id.licenses)
private void showLicenseDialog() {
new LicensesDialog.Builder(getActivity())
.setNotices(R.raw.licenses)
.setIncludeOwnLicense(true)
.build()
.showAppCompat();
@ViewBinding.Click(R.id.syntax_and_api)
private void startSyntaxHelpActivity() {
startActivity(new Intent(getContext(), DocumentActivity.class));
}
@ViewBinding.Click(R.id.auto_operate_service)
private void clickAutoOperateServiceSwitch() {
mAutoOperateServiceSwitch.toggle();
}
@ViewBinding.Check(R.id.sw_auto_operate_service)
private void setAutoOperateServiceEnable(boolean enable) {
if (enable && !ActionPerformService.isEnable()) {
AccessibilityServiceUtils.goToPermissionSetting(getContext());
} else if (!enable && ActionPerformService.isEnable()) {
ActionPerformService.disable();
}
}
@ViewBinding.Check(R.id.sw_assist_service)
private void setAssistServiceEnable(boolean enable) {
ActionPerformService.setAssistModeEnable(enable);
AssistModeSwitchNotification.notifyAssistModeChanged();
}
@ViewBinding.Click(R.id.assist_service)
private void toggleAssistServiceSwitch() {
mAssistServiceSwitch.toggle();
}
@ViewBinding.Check(R.id.sw_assist_service_notification)
private void setAssistServiceNotificationEnable(boolean enable) {
AssistModeSwitchNotification.setEnable(enable);
}
@ViewBinding.Click(R.id.assist_service_notification)
private void toggleAssistServiceNotificationSwitch() {
mAssistServiceNotificationSwitch.toggle();
}
@ViewBinding.Click(R.id.stop_all_running_scripts)
@@ -94,19 +124,23 @@ public class SlideMenuFragment extends Fragment {
Snackbar.make(getActivityContentView(), "没有正在运行的脚本", Snackbar.LENGTH_SHORT).show();
}
@ViewBinding.Click(R.id.syntax_and_api)
private void startSyntaxHelpActivity() {
startActivity(new Intent(getContext(), DocumentActivity.class));
@Override
public void onDestroy() {
getContext().unregisterReceiver(mReceiver);
super.onDestroy();
}
@ViewBinding.Click(R.id.about_app)
private void startAboutActivity() {
// TODO: 2017/1/30 startAboutActivity
Toast.makeText(getContext(), "暂无", Toast.LENGTH_LONG).show();
private class Receiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION_CHANGE_ASSIST_MODE)) {
AssistModeSwitchNotification.notifyAssistModeChanged();
mAssistServiceSwitch.setChecked(intent.getBooleanExtra("enable", false));
}
}
}
@ViewBinding.Click(R.id.auto_operate_service)
private void clickAutoOperateServiceSwitch() {
mAutoOperateServiceSwitch.setChecked(!mAutoOperateServiceSwitch.isChecked());
}
}