From 6829c0659e5c34cd8cf91868f280a366d7471287 Mon Sep 17 00:00:00 2001 From: hyb1996 <946994919@qq.com> Date: Wed, 26 Apr 2017 13:48:59 +0800 Subject: [PATCH] Dying --- app/build.gradle | 8 +- app/src/main/AndroidManifest.xml | 1 + app/src/main/assets/js/jsbeautify.js | 973 ++++++++++++++++++ app/src/main/assets/numbers/0.png | Bin 0 -> 18327 bytes app/src/main/assets/numbers/1.png | Bin 0 -> 18026 bytes app/src/main/assets/numbers/2.png | Bin 0 -> 18257 bytes app/src/main/assets/numbers/3.png | Bin 0 -> 18252 bytes app/src/main/assets/numbers/4.png | Bin 0 -> 18153 bytes app/src/main/assets/numbers/5.png | Bin 0 -> 18253 bytes app/src/main/assets/numbers/6.png | Bin 0 -> 18348 bytes app/src/main/assets/numbers/7.png | Bin 0 -> 18118 bytes app/src/main/assets/numbers/8.png | Bin 0 -> 18370 bytes app/src/main/assets/numbers/9.png | Bin 0 -> 18315 bytes .../assets/sample/Shell命令/小米6666脚本.js | 29 + .../main/java/com/stardust/mi666/Cracker.java | 68 ++ app/src/main/java/com/stardust/mi666/OCR.java | 81 ++ .../main/java/com/stardust/mi666/Solver.java | 55 + .../com/stardust/mi666/ocr/ColorDetector.java | 53 + .../main/java/com/stardust/mi666/ocr/OCR.java | 15 + .../com/stardust/mi666/ocr/SimpleOCR.java | 142 +++ .../mi666/ocr/SimpleTextDetector.java | 34 + .../mi666/ocr/SimpleTextSplitter.java | 52 + .../stardust/mi666/ocr/TemplateMatching.java | 109 ++ .../java/com/stardust/scriptdroid/App.java | 31 +- .../java/com/stardust/scriptdroid/Pref.java | 4 +- .../floating_window/ConsoleFloaty.java | 77 ++ .../floating_window/FloatingConsole.java | 16 + .../content/RecordNavigatorContent.java | 2 +- .../content/ScriptListNavigatorContent.java | 1 + .../view/FloatingLayoutHierarchyView.java | 5 +- .../stardust/scriptdroid/tool/BitmapTool.java | 71 ++ .../scriptdroid/tool/DrawableSaver.java | 9 +- .../stardust/scriptdroid/tool/ImagePHash.java | 162 +++ .../scriptdroid/tool/ImageSelector.java | 5 +- .../stardust/scriptdroid/tool/IntentTool.java | 20 + .../scriptdroid/tool/JsBeautifierFactory.java | 25 + .../tool/MaterialDialogFactory.java | 26 + .../com/stardust/scriptdroid/tool/Shell.java | 161 +++ .../scriptdroid/tool/UpdateChecker.java | 49 +- .../scriptdroid/tool/VersionInfo.java | 129 +++ .../stardust/scriptdroid/ui/BaseActivity.java | 24 +- .../scriptdroid/ui/console/TimberConsole.java | 22 +- .../scriptdroid/ui/edit/EditActivity.java | 56 + .../ui/edit/completion/CodeCompletion.java | 2 +- .../edit/sidemenu/HelpSideMenuFragment.java | 24 + .../scriptdroid/ui/main/MainActivity.java | 62 +- .../ui/settings/AboutActivity.java | 5 +- .../ui/update/UpdateCheckDialog.java | 61 +- .../ui/update/UpdateInfoDialogBuilder.java | 146 +++ .../scriptdroid/ui/update/VersionGuard.java | 94 ++ .../com/stardust/view/ResizableFloaty.java | 410 -------- .../com/stardust/widget/ViewSwitcher.java | 63 -- .../res/drawable-hdpi/ic_close_white_24dp.png | Bin 0 -> 221 bytes .../ic_settings_ethernet_white_24dp.png | Bin 0 -> 367 bytes .../res/drawable-mdpi/ic_close_white_24dp.png | Bin 0 -> 175 bytes .../ic_settings_ethernet_white_24dp.png | Bin 0 -> 269 bytes .../drawable-xhdpi/ic_close_white_24dp.png | Bin 0 -> 257 bytes .../ic_settings_ethernet_white_24dp.png | Bin 0 -> 456 bytes .../drawable-xxhdpi/ic_close_white_24dp.png | Bin 0 -> 347 bytes .../ic_settings_ethernet_white_24dp.png | Bin 0 -> 690 bytes .../drawable-xxxhdpi/ic_close_white_24dp.png | Bin 0 -> 436 bytes .../ic_settings_ethernet_white_24dp.png | Bin 0 -> 930 bytes .../res/drawable/circle_color_primary.xml | 8 + .../drawable/floating_console_bg_bottom.xml | 7 + .../res/drawable/floating_console_bg_top.xml | 9 + app/src/main/res/drawable/ic_move_cursor.png | Bin 0 -> 3853 bytes app/src/main/res/drawable/ic_resizer.png | Bin 9692 -> 20585 bytes .../main/res/layout/dialog_update_info.xml | 70 ++ .../res/layout/dialog_update_info_btn.xml | 9 + .../res/layout/floating_console_expand.xml | 90 ++ .../res/layout/floating_window_collapse.xml | 2 +- .../res/layout/fragment_help_side_menu.xml | 8 + .../res/layout/resizable_floaty_container.xml | 23 - app/src/main/res/menu/menu_editor.xml | 21 +- app/src/main/res/values-en/strings.xml | 3 + app/src/main/res/values/strings.xml | 11 + app/src/main/res/values/styles.xml | 1 + .../src/main/assets/javascript_engine_init.js | 37 +- .../autojs/runtime/ScriptRuntime.java | 15 +- .../api/{Shell.java => AbstractShell.java} | 27 +- .../autojs/runtime/api/SlowShell.java | 28 + .../simple_action/SimpleActionAutomator.java | 9 +- .../stardust/autojs/script/JsBeautifier.java | 91 ++ .../stardust/net/AutoHttpURLConnection.java | 62 ++ .../main/java/com/stardust/util/Callback.java | 10 + .../java/com/stardust/util/DownloadTask.java | 107 ++ .../java/com/stardust/util/IntentUtil.java | 9 + .../java/com/stardust/util/NetworkUtils.java | 20 + .../java/com/stardust/util/ScreenMetrics.java | 35 + .../main/java/com/stardust/util/Shell.java | 6 +- .../main/java/com/stardust/util/ViewUtil.java | 14 + version.json | 4 +- 92 files changed, 3439 insertions(+), 679 deletions(-) create mode 100644 app/src/main/assets/js/jsbeautify.js create mode 100644 app/src/main/assets/numbers/0.png create mode 100644 app/src/main/assets/numbers/1.png create mode 100644 app/src/main/assets/numbers/2.png create mode 100644 app/src/main/assets/numbers/3.png create mode 100644 app/src/main/assets/numbers/4.png create mode 100644 app/src/main/assets/numbers/5.png create mode 100644 app/src/main/assets/numbers/6.png create mode 100644 app/src/main/assets/numbers/7.png create mode 100644 app/src/main/assets/numbers/8.png create mode 100644 app/src/main/assets/numbers/9.png create mode 100644 app/src/main/assets/sample/Shell命令/小米6666脚本.js create mode 100644 app/src/main/java/com/stardust/mi666/Cracker.java create mode 100644 app/src/main/java/com/stardust/mi666/OCR.java create mode 100644 app/src/main/java/com/stardust/mi666/Solver.java create mode 100644 app/src/main/java/com/stardust/mi666/ocr/ColorDetector.java create mode 100644 app/src/main/java/com/stardust/mi666/ocr/OCR.java create mode 100644 app/src/main/java/com/stardust/mi666/ocr/SimpleOCR.java create mode 100644 app/src/main/java/com/stardust/mi666/ocr/SimpleTextDetector.java create mode 100644 app/src/main/java/com/stardust/mi666/ocr/SimpleTextSplitter.java create mode 100644 app/src/main/java/com/stardust/mi666/ocr/TemplateMatching.java create mode 100644 app/src/main/java/com/stardust/scriptdroid/external/floating_window/ConsoleFloaty.java create mode 100644 app/src/main/java/com/stardust/scriptdroid/external/floating_window/FloatingConsole.java create mode 100644 app/src/main/java/com/stardust/scriptdroid/tool/BitmapTool.java create mode 100644 app/src/main/java/com/stardust/scriptdroid/tool/ImagePHash.java create mode 100644 app/src/main/java/com/stardust/scriptdroid/tool/IntentTool.java create mode 100644 app/src/main/java/com/stardust/scriptdroid/tool/JsBeautifierFactory.java create mode 100644 app/src/main/java/com/stardust/scriptdroid/tool/MaterialDialogFactory.java create mode 100644 app/src/main/java/com/stardust/scriptdroid/tool/Shell.java create mode 100644 app/src/main/java/com/stardust/scriptdroid/tool/VersionInfo.java create mode 100644 app/src/main/java/com/stardust/scriptdroid/ui/edit/sidemenu/HelpSideMenuFragment.java create mode 100644 app/src/main/java/com/stardust/scriptdroid/ui/update/UpdateInfoDialogBuilder.java create mode 100644 app/src/main/java/com/stardust/scriptdroid/ui/update/VersionGuard.java delete mode 100644 app/src/main/java/com/stardust/view/ResizableFloaty.java delete mode 100644 app/src/main/java/com/stardust/widget/ViewSwitcher.java create mode 100644 app/src/main/res/drawable-hdpi/ic_close_white_24dp.png create mode 100644 app/src/main/res/drawable-hdpi/ic_settings_ethernet_white_24dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_close_white_24dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_settings_ethernet_white_24dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_close_white_24dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_settings_ethernet_white_24dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_close_white_24dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_settings_ethernet_white_24dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_close_white_24dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_settings_ethernet_white_24dp.png create mode 100644 app/src/main/res/drawable/circle_color_primary.xml create mode 100644 app/src/main/res/drawable/floating_console_bg_bottom.xml create mode 100644 app/src/main/res/drawable/floating_console_bg_top.xml create mode 100644 app/src/main/res/drawable/ic_move_cursor.png create mode 100644 app/src/main/res/layout/dialog_update_info.xml create mode 100644 app/src/main/res/layout/dialog_update_info_btn.xml create mode 100644 app/src/main/res/layout/floating_console_expand.xml create mode 100644 app/src/main/res/layout/fragment_help_side_menu.xml delete mode 100644 app/src/main/res/layout/resizable_floaty_container.xml rename autojs/src/main/java/com/stardust/autojs/runtime/api/{Shell.java => AbstractShell.java} (56%) create mode 100644 autojs/src/main/java/com/stardust/autojs/runtime/api/SlowShell.java create mode 100644 autojs/src/main/java/com/stardust/autojs/script/JsBeautifier.java create mode 100644 common/src/main/java/com/stardust/net/AutoHttpURLConnection.java create mode 100644 common/src/main/java/com/stardust/util/Callback.java create mode 100644 common/src/main/java/com/stardust/util/DownloadTask.java create mode 100644 common/src/main/java/com/stardust/util/NetworkUtils.java create mode 100644 common/src/main/java/com/stardust/util/ScreenMetrics.java diff --git a/app/build.gradle b/app/build.gradle index 4988d0ab..8a6d71b3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,15 +8,14 @@ android { applicationId "com.stardust.scriptdroid" minSdkVersion 19 targetSdkVersion 23 - versionCode 116 - versionName "2.0.7 Alpha3" + versionCode 118 + versionName "2.0.8Alpha" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" multiDexEnabled true - } buildTypes { release { - shrinkResources true + shrinkResources false minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } @@ -76,6 +75,7 @@ dependencies { compile 'me.zhanghai.android.materialprogressbar:library:1.3.0' compile group:'com.twofortyfouram', name:'android-plugin-client-sdk-for-locale', version:'[4.0.2, 5.0[' compile 'com.android.volley:volley:1.0.0' + compile 'com.github.hyb1996:EnhancedFloaty:0.11' compile(name: 'libtermexec-release', ext: 'aar') compile(name: 'emulatorview-release', ext: 'aar') compile(name: 'term-debug', ext: 'aar') diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 24b32e70..19671223 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,6 +11,7 @@ + + http://jsbeautifier.org/ + + Originally converted to javascript by Vital, + + You are free to use this in any way you want, in case you find this useful or working for you. + + Usage: + js_beautify(js_source_text); + js_beautify(js_source_text, options); + + The options are: + indent_size (default 4) �� indentation size, + indent_char (default space) �� character to indent with, + preserve_newlines (default true) �� whether existing line breaks should be preserved, + indent_level (default 0) �� initial indentation level, you probably won't need this ever, + + space_after_anon_function (default false) �� if true, then space is added between "function ()" + (jslint is happy about this); if false, then the common "function()" output is used. + + e.g + + js_beautify(js_source_text, {indent_size: 1, indent_char: '\t'}); + + +*/ + +function js_beautify(js_source_text, options) { + + var input, output, token_text, last_type, last_text, last_last_text, last_word, flags, flag_store, indent_string; + var whitespace, wordchar, punct, parser_pos, line_starters, digits; + var prefix, token_type, do_block_just_closed; + var indent_level, wanted_newline, just_added_newline, n_newlines; + + + options = options || {}; + var opt_indent_size = options.indent_size || 4; + var opt_indent_char = options.indent_char || ' '; + var opt_preserve_newlines = typeof options.preserve_newlines === 'undefined' ? true : options.preserve_newlines; + var opt_indent_level = options.indent_level || 0; // starting indentation + var opt_space_after_anon_function = options.space_after_anon_function === 'undefined' ? false : options.space_after_anon_function; + var opt_keep_array_indentation = typeof options.keep_array_indentation === 'undefined' ? true : options.keep_array_indentation; + + just_added_newline = false; + + + function trim_output() { + while (output.length && (output[output.length - 1] === ' ' || output[output.length - 1] === indent_string)) { + output.pop(); + } + } + + function print_newline(ignore_repeated) { + + if (opt_keep_array_indentation && is_array(flags.mode)) { + return; + } + + ignore_repeated = typeof ignore_repeated === 'undefined' ? true : ignore_repeated; + + flags.if_line = false; + trim_output(); + + if (!output.length) { + return; // no newline on start of file + } + + if (output[output.length - 1] !== "\n" || !ignore_repeated) { + just_added_newline = true; + output.push("\n"); + } + for (var i = 0; i < indent_level; i += 1) { + output.push(indent_string); + } + } + + + + function print_single_space() { + var last_output = ' '; + if (output.length) { + last_output = output[output.length - 1]; + } + if (last_output !== ' ' && last_output !== '\n' && last_output !== indent_string) { // prevent occassional duplicate space + output.push(' '); + } + } + + + function print_token() { + just_added_newline = false; + output.push(token_text); + } + + function indent() { + indent_level += 1; + } + + + function unindent() { + if (indent_level) { + indent_level -= 1; + } + } + + + function remove_indent() { + if (output.length && output[output.length - 1] === indent_string) { + output.pop(); + } + } + + function print_javadoc_comment() { + var lines = token_text.split('\n'); + output.push(lines[0]); + for (var i = 1; i < lines.length; i++) { + print_newline(); + output.push(' '); + output.push(lines[i].replace(/^\s+/, '')); + } + } + + + function set_mode(mode) { + if (flags) { + flag_store.push(flags); + } + flags = { + mode: mode, + var_line: false, + var_line_tainted: false, + if_line: false, + in_case: false, + indentation_baseline: -1 + }; + } + + function is_expression(mode) { + return mode === '[EXPRESSION]' || mode === '[INDENTED-EXPRESSION]' || mode === '(EXPRESSION)'; + } + + function is_array(mode) { + return mode === '[EXPRESSION]' || mode === '[INDENTED-EXPRESSION]'; + } + + function restore_mode() { + do_block_just_closed = flags.mode === 'DO_BLOCK'; + if (flag_store.length > 0) { + flags = flag_store.pop(); + } + } + + + function in_array(what, arr) { + for (var i = 0; i < arr.length; i += 1) { + if (arr[i] === what) { + return true; + } + } + return false; + } + + // Walk backwards from the colon to find a '?' (colon is part of a ternary op) + // or a '{' (colon is part of a class literal). Along the way, keep track of + // the blocks and expressions we pass so we only trigger on those chars in our + // own level, and keep track of the colons so we only trigger on the matching '?'. + + + function is_ternary_op() { + var level = 0, + colon_count = 0; + for (var i = output.length - 1; i >= 0; i--) { + switch (output[i]) { + case ':': + if (level === 0) { + colon_count++; + } + break; + case '?': + if (level === 0) { + if (colon_count === 0) { + return true; + } else { + colon_count--; + } + } + break; + case '{': + if (level === 0) { + return false; + } + level--; + break; + case '(': + case '[': + level--; + break; + case ')': + case ']': + case '}': + level++; + break; + } + } + } + + + function get_next_token() { + n_newlines = 0; + + if (parser_pos >= input.length) { + return ['', 'TK_EOF']; + } + + var c = input.charAt(parser_pos); + parser_pos += 1; + + + var keep_whitespace = opt_keep_array_indentation && is_array(flags.mode); + wanted_newline = false; + + if (keep_whitespace) { + + // + // slight mess to allow nice preservation of array indentation and reindent that correctly + // first time when we get to the arrays: + // var a = [ + // ....'something' + // we make note of whitespace_count = 4 into flags.indentation_baseline + // so we know that 4 whitespaces in original source match indent_level of reindented source + // + // and afterwards, when we get to + // 'something, + // .......'something else' + // we know that this should be indented to indent_level + (7 - indentation_baseline) spaces + // + var whitespace_count = 0; + + while (in_array(c, whitespace)) { + + if (c === "\n") { + trim_output(); + output.push("\n"); + just_added_newline = true; + whitespace_count = 0; + } else { + if (c === '\t') { + whitespace_count += 4; + } else { + whitespace_count += 1; + } + } + + if (parser_pos >= input.length) { + return ['', 'TK_EOF']; + } + + c = input.charAt(parser_pos); + parser_pos += 1; + + } + if (flags.indentation_baseline === -1) { + flags.indentation_baseline = whitespace_count; + } + + if (just_added_newline) { + for (var i = 0; i < indent_level + 1; i += 1) { + output.push(indent_string); + } + if (flags.indentation_baseline !== -1) { + for (var i = 0; i < whitespace_count - flags.indentation_baseline; i++) { + output.push(' '); + } + } + } + + } else { + while (in_array(c, whitespace)) { + + if (c === "\n") { + n_newlines += 1; + } + + + if (parser_pos >= input.length) { + return ['', 'TK_EOF']; + } + + c = input.charAt(parser_pos); + parser_pos += 1; + + } + + if (opt_preserve_newlines) { + if (n_newlines > 1) { + for (var i = 0; i < n_newlines; i += 1) { + print_newline(i === 0); + just_added_newline = true; + } + } + } + wanted_newline = n_newlines > 0; + } + + + if (in_array(c, wordchar)) { + if (parser_pos < input.length) { + while (in_array(input.charAt(parser_pos), wordchar)) { + c += input.charAt(parser_pos); + parser_pos += 1; + if (parser_pos === input.length) { + break; + } + } + } + + // small and surprisingly unugly hack for 1E-10 representation + if (parser_pos !== input.length && c.match(/^[0-9]+[Ee]$/) && (input.charAt(parser_pos) === '-' || input.charAt(parser_pos) === '+')) { + + var sign = input.charAt(parser_pos); + parser_pos += 1; + + var t = get_next_token(parser_pos); + c += sign + t[0]; + return [c, 'TK_WORD']; + } + + if (c === 'in') { // hack for 'in' operator + return [c, 'TK_OPERATOR']; + } + if (wanted_newline && last_type !== 'TK_OPERATOR' && !flags.if_line && (opt_preserve_newlines || last_text !== 'var')) { + print_newline(); + } + return [c, 'TK_WORD']; + } + + if (c === '(' || c === '[') { + return [c, 'TK_START_EXPR']; + } + + if (c === ')' || c === ']') { + return [c, 'TK_END_EXPR']; + } + + if (c === '{') { + return [c, 'TK_START_BLOCK']; + } + + if (c === '}') { + return [c, 'TK_END_BLOCK']; + } + + if (c === ';') { + return [c, 'TK_SEMICOLON']; + } + + if (c === '/') { + var comment = ''; + // peek for comment /* ... */ + if (input.charAt(parser_pos) === '*') { + parser_pos += 1; + if (parser_pos < input.length) { + while (! (input.charAt(parser_pos) === '*' && input.charAt(parser_pos + 1) && input.charAt(parser_pos + 1) === '/') && parser_pos < input.length) { + comment += input.charAt(parser_pos); + parser_pos += 1; + if (parser_pos >= input.length) { + break; + } + } + } + parser_pos += 2; + return ['/*' + comment + '*/', 'TK_BLOCK_COMMENT']; + } + // peek for comment // ... + if (input.charAt(parser_pos) === '/') { + comment = c; + while (input.charAt(parser_pos) !== "\x0d" && input.charAt(parser_pos) !== "\x0a") { + comment += input.charAt(parser_pos); + parser_pos += 1; + if (parser_pos >= input.length) { + break; + } + } + parser_pos += 1; + if (wanted_newline) { + print_newline(); + } + return [comment, 'TK_COMMENT']; + } + + } + + if (c === "'" || // string + c === '"' || // string + (c === '/' && ((last_type === 'TK_WORD' && in_array(last_text, ['return', 'do'])) || (last_type === 'TK_START_EXPR' || last_type === 'TK_START_BLOCK' || last_type === 'TK_END_BLOCK' || last_type === 'TK_OPERATOR' || last_type === 'TK_EOF' || last_type === 'TK_SEMICOLON')))) { // regexp + var sep = c; + var esc = false; + var resulting_string = c; + + if (parser_pos < input.length) { + if (sep === '/') { + // + // handle regexp separately... + // + var in_char_class = false; + while (esc || in_char_class || input.charAt(parser_pos) !== sep) { + resulting_string += input.charAt(parser_pos); + if (!esc) { + esc = input.charAt(parser_pos) === '\\'; + if (input.charAt(parser_pos) === '[') { + in_char_class = true; + } else if (input.charAt(parser_pos) === ']') { + in_char_class = false; + } + } else { + esc = false; + } + parser_pos += 1; + if (parser_pos >= input.length) { + // incomplete string/rexp when end-of-file reached. + // bail out with what had been received so far. + return [resulting_string, 'TK_STRING']; + } + } + + } else { + // + // and handle string also separately + // + while (esc || input.charAt(parser_pos) !== sep) { + resulting_string += input.charAt(parser_pos); + if (!esc) { + esc = input.charAt(parser_pos) === '\\'; + } else { + esc = false; + } + parser_pos += 1; + if (parser_pos >= input.length) { + // incomplete string/rexp when end-of-file reached. + // bail out with what had been received so far. + return [resulting_string, 'TK_STRING']; + } + } + } + + + + } + + parser_pos += 1; + + resulting_string += sep; + + if (sep === '/') { + // regexps may have modifiers /regexp/MOD , so fetch those, too + while (parser_pos < input.length && in_array(input.charAt(parser_pos), wordchar)) { + resulting_string += input.charAt(parser_pos); + parser_pos += 1; + } + } + return [resulting_string, 'TK_STRING']; + } + + if (c === '#') { + // Spidermonkey-specific sharp variables for circular references + // https://developer.mozilla.org/En/Sharp_variables_in_JavaScript + // http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935 + var sharp = '#'; + if (parser_pos < input.length && in_array(input.charAt(parser_pos), digits)) { + do { + c = input.charAt(parser_pos); + sharp += c; + parser_pos += 1; + } while (parser_pos < input.length && c !== '#' && c !== '='); + if (c === '#') { + return [sharp, 'TK_WORD']; + } else { + return [sharp, 'TK_OPERATOR']; + } + } + } + + if (c === '<' && input.substring(parser_pos - 1, parser_pos + 3) === '') { + parser_pos += 2; + if (wanted_newline) { + print_newline(); + } + return ['-->', 'TK_COMMENT']; + } + + if (in_array(c, punct)) { + while (parser_pos < input.length && in_array(c + input.charAt(parser_pos), punct)) { + c += input.charAt(parser_pos); + parser_pos += 1; + if (parser_pos >= input.length) { + break; + } + } + + return [c, 'TK_OPERATOR']; + } + + return [c, 'TK_UNKNOWN']; + } + + //---------------------------------- + indent_string = ''; + while (opt_indent_size > 0) { + indent_string += opt_indent_char; + opt_indent_size -= 1; + } + + indent_level = opt_indent_level; + + input = js_source_text; + + last_word = ''; // last 'TK_WORD' passed + last_type = 'TK_START_EXPR'; // last token type + last_text = ''; // last token text + last_last_text = ''; // pre-last token text + output = []; + + do_block_just_closed = false; + + whitespace = "\n\r\t ".split(''); + wordchar = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$'.split(''); + digits = '0123456789'.split(''); + + // 5Wq4qS{4$j?8*=`(V3tdt&>P4x`WDXRXhS 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 literal 0 HcmV?d00001 diff --git a/app/src/main/assets/numbers/6.png b/app/src/main/assets/numbers/6.png new file mode 100644 index 0000000000000000000000000000000000000000..d5a756a3d500ec64d9ecdae161c2529015ff5a00 GIT binary patch literal 18348 zcmeI4c{J4D|Ht39LMb7NWE#l`c&H0@3`+ooVo|$vT%&9cjlb8f2=o|Nr=mf z0{|dljx(|4zRGf+%SC5#|Ax-0NaVhVF>o#{0GJ~^@e}|~B`g8}aZ9qXv9+})olR$X z(isqQV`B(|Np~kxNC4p1m}uwg95pJ7Z5buacQkr7kP}M@2B6BBqUDkbz5GPQSj#zrU~V^V83TT?t*| zHJsY{K?|f*BVVWN1axf=>S3NKg}=b zxhONipdt~#C(JowzTplaD9%W&3V`Sc0>(?FtA!vILdl5wlGcpvjDz_&0id)px;l1a zJz6F|`k}n<`+?TBWo@@9IYxN7Rg&&v9ngf>cx7o^mykuxTmX7r5BE~OdJWK_*kyw)Uki^33ImdxYWo|vcXl=iG0FrkdzW}+Yylb_0ne5(S9FtD z@@Dae{$ocuI^%{*lg?O|>nSb~pEIXV#qCTHqWviL8~PQ!^=RM-*ZZ}}e*oBZ$}ldg zGe;atJK!u9){ek9gC7~FDg7F zCcLg(R2shw6SBZ9>>0F1R4y(|7>dVf91lu^bce{;K<||^(*!e&c1F75l;6XcGPij)W0{>X7r?k3Gk4J$Bf1$3M>Us%5f-PG?QWa7}`uv$oGdh0-K^zvyzoqe0TW zqT3Eg-lpt}h%j@ua5g@#(yM$;szB69Z2tk|?G@0d^H$eX{H4b3`nh3pBAnM}s=iyc zbXk(hwiWZFw@2ZY&wFsZW0UwYl?asi9CI&R6AqSUmxisEBqvy?_C!!0yzCIY5|jyv zjm(RfA7OR;^MgAbc`}%LA#E07Da&osPDpLgBpGxHpFNE-ic7v=Q=O6YSoA^&=JaNB zYO;?t-0pCOw1)R7mFjp|XV13$R6ZTPk1-GKn@!AimLA&w|Lio~d=_sVnkT-yQa7 z*YuOax@XdtxMga*BsF{K;oZonrnI|h_d1GKRiE%WiB+AEDa8Fw@8bUS${c${G+4r$7r+Za(P73&i#XGcvxl1@AA zUm4vg|E{3^`lI30_?z*~S7LfHFQ!PEYy4;P;+hPLPz zD+e|N#s$tEPx`#}Q{|WNk0q}-?_~!!iR=)#CD18Q6f7TnRme+7NjOgU>}*YuP?4Ln z;B&DHjpm9;xF|8B=%sS+6~+kYW)HPfbqgy*6Blk)_=KoZwOHD)AnZ`BXp3YZ{oI&E zcU7)vNUIqoBwvZVI8JVCkIbsos0-?EZLZm|cd%xme#n9e1DI)&HlRCD=tdg-+-=Wgo*PWOp{F z+>m?ST~nFltLLKW60uw7;7O_2D+ZnWAx75lMOK~ZG)qP4_Xpw+TA3E7gr?Z!M;@r( zQ|VcGCFYBDo3pLM`<(8DwUbD|hBRb>6gGyTVhs`$#3-QrJhyIAJ(H1nKKtY4_4%*coHlKLvU`|AZESqGA+MsZ2iKFDY>+vXy>{o4-XAU zWGM`Wb}DSAZg|N)a}`!iapBmRkdT`L{GWL?ZFey-Zs zQ1ZezU})Zc#f58M_Mhl2yuQZI$*ah8&oHh2-M$y$@@@(RC9eZtC!0=bzEo0jMT}~z6}l9MVRXg^(HrCYPWfJEY+gO@_PWknD_YL&4rsT2bEf7_ z^PTFJxV?G9qCGgyntDu$cP1>Anyi1OsQ=Be)I`N?ie|-m-vaxQ?F8>K&2XorIio_>6)?s}S2)PKKM zA&;i|VoAPA!SF|)kv=(J@pl%@uKwYEm&S`5zr3COY5v>2*S_YEN~x_Mca-j^z2h>! zf1cRkq^~EwJv#iV-mJ>(iSl~o(C|bhB?SrjqW8p_s8{J%(xq!F0v*3`dRP~`TE4V> z-dO8A{%l~**wNaOY)-}yv;ShoB}|!enNnd5M}P3#cvel*p8ov)f|jx(rMQ*jhF`6R z>OK+5(jQulV!L*K9fyw|82YSIe;N${7zEkgneA+8fg#YT>UbjEjil~JWpIz70H9~! z$G{W3NNk83$%9POhxXqrfkMbceW;VBCBl+nO!6e-{Fx*>|4sG;e=mYA5o&-H*Ym@0 z15in9Jj9Pmp|LQ2`q0U^81DOtW;hfw*@f+;4_!Br5aMiU4Kb!ONf1qSEf@izr47;6 zRYz)SYinq$K~M;!CLDo=qY*F^8l!>0XrLh9A5g3~_njV-=#H^9S^qs9*V2c2ve^s_ z9PaDutL}?dr!zg^NL^iBI06MnpSOg}S!6wsbkcqf> zH@Y`l9}1mF^z-q3TvW!-L^RfSc3g^ZKRg4DR7b%7BC@pPEu&KZsARECeYjWhePn-3 z$g<}!NN`&ci|)-NkW76@G`8}eE`mt-8HeG`q)hT565u2XiOQ9*xEGK7<0UYDeE;|< z|K&1%y8k+QHrai8a(>ZHc>XFQ`H}x3oA6A@CQD&zhV;1ghcRZ7@N7EMo=&G=r>boF z+Y|T8(?nH4RyvVsM7l3)l^z@{nV#?IXe1Lnn}p>mwP6SiZZX;;5E!Hm28~#Q(7_-O zGlER@1jVrA);#{Zirzw`yip%cmOoPQN%y63MkEG;qSG!`3ABaqBZu-vh$lgUI3 z+TD$S)7B@y-DzqC$Y)=%Yk8Tt3={C5bZ;?I!(J%_I+iN>q^r$SBk`g;%--JR`=XOh-= za4Y1`D-(s9xiY7s{wf29*Mm>&)fDo7VIo*~pa0)9{JT~4KQRr{RZQ^2(>zE-Ec{0S z|7iGkGdDG#zpktwtM&Jls7EAV-04gzo{c3_@g5{NgXW>3pB$^P0X1or_)PW=5Yo#_TkU3g1py1~+Er2~!3#-hGw12xZ3O?(n# zCZXDK4~--&cWB@yaBpj}ABo~@Lgs!}uqO6A6gSJH2JHBw&}CYP)|x4t;rK3`DyJDo zyUBfGs+D_i}blYoHmHu<=C^8tDJxIjR7n|xfn z`GCB9Tp%F4O+GH(d_Z14E)WpjCLb4XJ|Hh27YGP%laGrxACQ-i3j~C>$;ZW;56H{M z1p>m`25fq?Kf`M7xV0eShjKtOn#d|bTwfV_NMARxR= zJ}%yTKwdsB5D?xb9~W;vATJ*m2ncVJkBc`Ske81O1cbNA$Hkiu$jiqC0>azm!ynI|BAiPaJF5Y}VUOp}m z5Z)$FT;kKWfs$z4dp~`-H+imHNuT%^CJ+M7))D~xRRADpKLCt<+Jm+dt<&>+2Uo&4CEvSp+`(m=^1G%AB~EmQ{v$lE#fae zZ+RM?wY_31tdi1m&(ys#fp%_41Sa!nnZOZYnGZ+Rf&=QBH*XmlXZN#`KI_&F>5nET z-fG``j8)n;nC8^6n^`MncUE0=A7swPVe_mNBNsb1$<6)98695`X^k71h552defUFE z^w~{wplKT-gTlh+E)ajmJlP$px;cQZdP_hiLxGTUno{5xE-W$-kT*c1rDd%M-XEw0 zpVgndz*NmL>#&i?K|^D+`R9(KI1EH(@`TUG?Mo8 zm+Gz^UYnAf+}S8`%v5DbnG7?`S#N#m&FWp2x3*uA9xxF0=n8pb6(1%M(@Q(+plSd3 zG1@>gnDDWfVzwsl*=4OJn+x`Fw>Yzuy+_pd2`JH*wkS)@^XRQ!!+NyaTQ}#rw~g~w zRaeehEL`5E@^GxxVE)suAK#L?F6LbK6*9VVKS@Har$*#LeB-Lsmm;Wd$>z2zS6zMV zmYv~&_m>)d%e*2Ju=b{oM|yePYi4e41CTo0 zqWGeX7|xki^{dhZPZL;ZqP~Xj=!2w&>l)$h4}=0ul+C`qDDD>-qz9lX2B?ot7G%z7)_%arD1?WW92} zlWg3s!U|D+QTmO>@R!aZZ%a=`pWEdab4oB(?7He_oVgUs`;GWqbL;0For{k>3z%)~ t#Y7o-`wR8$$t!ONc^~!AJa}9l$O#{_nYGCSJ8|2yx#=d8Y@;3f{trs!sowwq literal 0 HcmV?d00001 diff --git a/app/src/main/assets/numbers/7.png b/app/src/main/assets/numbers/7.png new file mode 100644 index 0000000000000000000000000000000000000000..dc46da63fc2f9941a262078ba0ced1fd00d011bf GIT binary patch literal 18118 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 Ra{6ItEA!1}`6eD={|9|Ns~7+P literal 0 HcmV?d00001 diff --git a/app/src/main/assets/numbers/9.png b/app/src/main/assets/numbers/9.png new file mode 100644 index 0000000000000000000000000000000000000000..a17fa2120a8a99df9cd3534b5f48663d9861b71b GIT binary patch literal 18315 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 PtJ%`r#w^>!WB>mH#LbdX literal 0 HcmV?d00001 diff --git a/app/src/main/assets/sample/Shell命令/小米6666脚本.js b/app/src/main/assets/sample/Shell命令/小米6666脚本.js new file mode 100644 index 00000000..9327b560 --- /dev/null +++ b/app/src/main/assets/sample/Shell命令/小米6666脚本.js @@ -0,0 +1,29 @@ +importClass(com.stardust.mi666.Cracker); +var cracker = new Cracker(); +const path = "/sdcard/666.png"; +var count = 1; +sleep(2000); +while(notStopped()){ + Screencap(path); + sleep(2000); + toast("你已经被耍了" + count + "次"); + var coords = cracker.crack(path); + for each(var coord in coords){ + Tap(coord[0], coord[1]); + } + sleep(9000); + 关闭(); + sleep(2000); + console.log(++count); + 再玩一次(); + sleep(10000); +} + + +function 关闭(){ + Tap(933, 660); +} + +function 再玩一次(){ + Tap(521, 1615); +} \ No newline at end of file diff --git a/app/src/main/java/com/stardust/mi666/Cracker.java b/app/src/main/java/com/stardust/mi666/Cracker.java new file mode 100644 index 00000000..e2dc1f90 --- /dev/null +++ b/app/src/main/java/com/stardust/mi666/Cracker.java @@ -0,0 +1,68 @@ +package com.stardust.mi666; + +import com.stardust.util.ScreenMetrics; + +import java.util.List; + +/** + * Created by Stardust on 2017/4/24. + */ + +public class Cracker { + + private OCR mOCR; + private Solver mSolver; + private int mX, mY, mIntervalX, mIntervalY, mWidth, mHeight; + + private void init(int x, int y, int intervalX, int intervalY, int w, int h) { + mOCR = new OCR(x, y, intervalX, intervalY, w, h); + mSolver = new Solver(); + mX = x; + mY = y; + mIntervalY = intervalY; + mIntervalX = intervalX; + mWidth = w; + mHeight = h; + } + + public Cracker() { + if (ScreenMetrics.getScreenWidth() == 1080 && ScreenMetrics.getScreenHeight() == 1920) { + init(68, 724, 238, 236, 228, 68); + } else { + double scaleX = 1080 / ScreenMetrics.getScreenWidth(); + double scaleY = 1920 / ScreenMetrics.getScreenHeight(); + init((int) (68 * scaleX), (int) (724 * scaleY), (int) (238 * scaleX), (int) (236 * scaleY), (int) (228 * scaleX), (int) (68 * scaleY)); + } + } + + public int[][] crack(String path) { + List numbers = mOCR.detect(path); + mSolver.solve(numbers); + if (!mSolver.isSolved()) { + return new int[0][]; + } + List result = mSolver.getResult(); + boolean[] used = new boolean[numbers.size()]; + int[][] coordinates = new int[result.size()][2]; + for (int i = 0; i < coordinates.length; i++) { + int index = indexOf(numbers, used, result.get(i)); + int row = index / 4; + int col = index % 4; + int x = mX + mIntervalX * col + mWidth / 2; + int y = mY + mIntervalY * row + mHeight / 2; + coordinates[i][0] = x; + coordinates[i][1] = y; + } + return coordinates; + } + + private int indexOf(List numbers, boolean[] used, int integer) { + for (int i = 0; i < numbers.size(); i++) { + if (numbers.get(i) == integer && !used[i]) { + used[i] = true; + return i; + } + } + return -1; + } +} diff --git a/app/src/main/java/com/stardust/mi666/OCR.java b/app/src/main/java/com/stardust/mi666/OCR.java new file mode 100644 index 00000000..3d29edc7 --- /dev/null +++ b/app/src/main/java/com/stardust/mi666/OCR.java @@ -0,0 +1,81 @@ +package com.stardust.mi666; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.util.Log; + +import com.stardust.mi666.ocr.ColorDetector; +import com.stardust.mi666.ocr.SimpleTextSplitter; +import com.stardust.mi666.ocr.TemplateMatching; +import com.stardust.pio.UncheckedIOException; +import com.stardust.util.ViewUtil; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Stardust on 2017/4/24. + */ + +public class OCR { + + private static com.stardust.mi666.ocr.OCR ocr; + private static SimpleTextSplitter textSplitter; + + static { + ColorDetector colorDetector = new ColorDetector.SimpleColorDetector(0xff9f826f); + ocr = new TemplateMatching.TemplateMatchingOCR(1, colorDetector); + textSplitter = new SimpleTextSplitter(colorDetector); + } + + private int mX, mY, mIntervalX, mIntervalY, mWidth, mHeight; + + public OCR(int x, int y, int intervalX, int intervalY, int w, int h) { + mX = x; + mY = y; + mIntervalY = intervalY; + mIntervalX = intervalX; + mWidth = w; + mHeight = h; + } + + public static void init(Context context) { + try { + for (int i = 0; i < 10; i++) { + Bitmap number = BitmapFactory.decodeStream(context.getAssets().open("numbers/" + i + ".png")); + ocr.addChar((char) ('0' + i), number); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public List detect(String path) { + Bitmap screenshot = BitmapFactory.decodeFile(path); + try { + List numbers = new ArrayList<>(16); + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + Bitmap block = getBlockAt(screenshot, i, j); + List bitmaps = textSplitter.split(block); + StringBuilder sb = new StringBuilder(); + for (Bitmap num : bitmaps) { + sb.append(ocr.detect(num)); + } + numbers.add(Integer.parseInt(sb.toString())); + Log.i("TestOCR", "result = " + sb); + } + } + return numbers; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + private Bitmap getBlockAt(Bitmap bitmap, int row, int col) { + return Bitmap.createBitmap(bitmap, mX + col * mIntervalX, mY + row * mIntervalY, mWidth, mHeight); + } +} diff --git a/app/src/main/java/com/stardust/mi666/Solver.java b/app/src/main/java/com/stardust/mi666/Solver.java new file mode 100644 index 00000000..58897f29 --- /dev/null +++ b/app/src/main/java/com/stardust/mi666/Solver.java @@ -0,0 +1,55 @@ +package com.stardust.mi666; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Stardust on 2017/4/24. + */ + +public class Solver { + + private boolean mSolved = false; + private List mResult = new ArrayList<>(); + private List mTmpList = new ArrayList<>(); + private int[] value; + + + void solve(int sum, int index) { + if (mSolved || index < 0) return; + if (sum == value[index] && mTmpList.size() == 5) { + mSolved = true; + mTmpList.add(value[index]); + mResult.addAll(mTmpList); + return; + } + mTmpList.add(value[index]); + solve(sum - value[index], index - 1); + mTmpList.remove(mTmpList.size() - 1); + solve(sum, index - 1); + } + + public void solve(int[] num) { + mResult.clear(); + mTmpList.clear(); + mSolved = false; + value = num; + solve(6666, 15); + } + + public boolean isSolved() { + return mSolved; + } + + public List getResult() { + return mResult; + } + + public void solve(List numbers) { + int[] num = new int[numbers.size()]; + for (int i = 0; i < num.length; i++) { + num[i] = numbers.get(i); + } + solve(num); + } +} diff --git a/app/src/main/java/com/stardust/mi666/ocr/ColorDetector.java b/app/src/main/java/com/stardust/mi666/ocr/ColorDetector.java new file mode 100644 index 00000000..9472b374 --- /dev/null +++ b/app/src/main/java/com/stardust/mi666/ocr/ColorDetector.java @@ -0,0 +1,53 @@ +package com.stardust.mi666.ocr; + +import android.graphics.Color; + +/** + * Created by Stardust on 2017/4/23. + */ + +public interface ColorDetector { + + boolean isCharPixel(int color); + + class ColorDistanceDetector implements ColorDetector { + + private int mColor; + private double mThreshold; + + public ColorDistanceDetector(int color, double threshold) { + mColor = color; + mThreshold = threshold; + } + + public static double distance(int c1, int c2) { + double meanR = (Color.red(c1) + Color.red(c2)) / 2; + int r = Color.red(c1) - Color.red(c2); + int g = Color.green(c1) - Color.green(c2); + int b = Color.blue(c1) - Color.blue(c2); + double weightR = 2 + meanR / 256; + double weightG = 4.0; + double weightB = 2 + (255 - meanR) / 256; + return Math.sqrt(weightR * r * r + weightG * g * g + weightB * b * b); + } + + @Override + public boolean isCharPixel(int color) { + return distance(color, mColor) < mThreshold; + } + } + + class SimpleColorDetector implements ColorDetector { + + private int mColor; + + public SimpleColorDetector(int color) { + mColor = color; + } + + @Override + public boolean isCharPixel(int color) { + return mColor == color; + } + } +} diff --git a/app/src/main/java/com/stardust/mi666/ocr/OCR.java b/app/src/main/java/com/stardust/mi666/ocr/OCR.java new file mode 100644 index 00000000..81757171 --- /dev/null +++ b/app/src/main/java/com/stardust/mi666/ocr/OCR.java @@ -0,0 +1,15 @@ +package com.stardust.mi666.ocr; + +import android.graphics.Bitmap; + +/** + * Created by Stardust on 2017/4/23. + */ + +public interface OCR { + + void addChar(char ch, Bitmap bitmap); + + char detect(Bitmap bitmap); + +} diff --git a/app/src/main/java/com/stardust/mi666/ocr/SimpleOCR.java b/app/src/main/java/com/stardust/mi666/ocr/SimpleOCR.java new file mode 100644 index 00000000..8f6e8cf9 --- /dev/null +++ b/app/src/main/java/com/stardust/mi666/ocr/SimpleOCR.java @@ -0,0 +1,142 @@ +package com.stardust.mi666.ocr; + +import android.graphics.Bitmap; +import android.util.Log; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by Stardust on 2017/4/23. + */ + +public class SimpleOCR implements OCR { + + private static class PixelLine { + int start; + int end; + + PixelLine(int start, int end) { + this.start = start; + this.end = end; + } + } + + private static class PixelLines { + int start = -1; + List lines = new ArrayList<>(); + + + void addLine(PixelLine line) { + lines.add(line); + if (start == -1 || start > line.start) { + start = line.start; + } + } + + int distanceTo(PixelLines another) { + if (lines.size() != another.lines.size()) { + return lineWidthSum() + another.lineWidthSum(); + } + int offset = another.start - start; + int sum = 0; + for (int i = 0; i < lines.size(); i++) { + sum += Math.abs(lines.get(i).start + offset - another.lines.get(i).start); + sum += Math.abs(lines.get(i).end + offset - another.lines.get(i).end); + } + return sum; + } + + private int lineWidthSum() { + int sum = 0; + for (PixelLine line : lines) { + sum += line.end - line.start; + } + return sum; + } + } + + + public static class CharFeature { + + private List mPixelLinesPerRow; + + public CharFeature(int rowNumber) { + mPixelLinesPerRow = new ArrayList<>(rowNumber); + for (int i = 0; i < rowNumber; i++) { + mPixelLinesPerRow.add(new PixelLines()); + } + } + + void addLine(int row, PixelLine line) { + mPixelLinesPerRow.get(row).addLine(line); + } + + int distanceTo(CharFeature feature) { + int sum = 0; + if (feature.mPixelLinesPerRow.size() != mPixelLinesPerRow.size()) { + for (PixelLines lines : mPixelLinesPerRow) { + sum += lines.lines.size(); + } + return sum; + } + for (int i = 0; i < mPixelLinesPerRow.size(); i++) { + sum += mPixelLinesPerRow.get(i).distanceTo(feature.mPixelLinesPerRow.get(i)); + } + return sum; + } + + } + + private static final String TAG = "SimpleOCR"; + private Map mChars = new HashMap<>(); + private ColorDetector mColorDetector; + + public SimpleOCR(ColorDetector colorDetector) { + mColorDetector = colorDetector; + } + + public void addChar(char c, Bitmap ch) { + mChars.put(c, getCharFeature(ch)); + } + + public CharFeature getCharFeature(Bitmap bitmap) { + CharFeature feature = new CharFeature(bitmap.getHeight()); + for (int y = 0; y < bitmap.getHeight(); y++) { + int mPixelStart = -1; + for (int x = 0; x < bitmap.getWidth(); x++) { + if (mColorDetector.isCharPixel(bitmap.getPixel(x, y))) { + if (mPixelStart == -1) { + mPixelStart = x; + } + } else if (mPixelStart >= 0) { + feature.addLine(y, new PixelLine(mPixelStart, x)); + mPixelStart = -1; + } + } + if (mPixelStart >= 0) { + feature.addLine(y, new PixelLine(mPixelStart, bitmap.getWidth())); + } + } + return feature; + } + + public char detect(Bitmap bitmap) { + CharFeature feature = getCharFeature(bitmap); + int min = Integer.MAX_VALUE; + char detectedChar = ' '; + for (Map.Entry ch : mChars.entrySet()) { + int distance = ch.getValue().distanceTo(feature); + Log.d(TAG, "ch=" + ch.getKey() + " distance=" + distance); + if (distance < min) { + min = distance; + detectedChar = ch.getKey(); + } + } + return detectedChar; + } + + +} diff --git a/app/src/main/java/com/stardust/mi666/ocr/SimpleTextDetector.java b/app/src/main/java/com/stardust/mi666/ocr/SimpleTextDetector.java new file mode 100644 index 00000000..05641ddd --- /dev/null +++ b/app/src/main/java/com/stardust/mi666/ocr/SimpleTextDetector.java @@ -0,0 +1,34 @@ +package com.stardust.mi666.ocr; + +import android.graphics.Bitmap; + +/** + * Created by Stardust on 2017/4/23. + */ + +public class SimpleTextDetector { + + private ColorDetector mColorDetector; + + public SimpleTextDetector(ColorDetector colorDetector) { + mColorDetector = colorDetector; + } + + public Bitmap detect(Bitmap bitmap) { + int minX = bitmap.getWidth(); + int minY = bitmap.getHeight(); + int maxX = 0; + int maxY = 0; + for (int i = 0; i < bitmap.getWidth(); i++) { + for (int j = 0; j < bitmap.getHeight(); j++) { + if (mColorDetector.isCharPixel(bitmap.getPixel(i, j))) { + minX = Math.min(minX, i); + minY = Math.min(minY, j); + maxX = Math.max(maxX, i); + maxY = Math.max(maxY, j); + } + } + } + return Bitmap.createBitmap(bitmap, minX, minY, maxX - minX, maxY - minY); + } +} diff --git a/app/src/main/java/com/stardust/mi666/ocr/SimpleTextSplitter.java b/app/src/main/java/com/stardust/mi666/ocr/SimpleTextSplitter.java new file mode 100644 index 00000000..54258087 --- /dev/null +++ b/app/src/main/java/com/stardust/mi666/ocr/SimpleTextSplitter.java @@ -0,0 +1,52 @@ +package com.stardust.mi666.ocr; + +import android.graphics.Bitmap; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Stardust on 2017/4/24. + */ + +public class SimpleTextSplitter { + + private ColorDetector mColorDetector; + private SimpleTextDetector mSimpleTextDetector; + + public SimpleTextSplitter(ColorDetector colorDetector) { + mColorDetector = colorDetector; + mSimpleTextDetector = new SimpleTextDetector(colorDetector); + } + + public List split(Bitmap bitmap) { + Bitmap text = mSimpleTextDetector.detect(bitmap); + int[] projection = new int[text.getWidth()]; + for (int i = 0; i < text.getWidth(); i++) { + for (int j = 0; j < text.getHeight(); j++) { + if (mColorDetector.isCharPixel(text.getPixel(i, j))) { + projection[i]++; + } + } + } + int start = -1; + List numbers = new ArrayList<>(); + for (int i = 0; i < text.getWidth(); i++) { + if (projection[i] > 0) { + if (start == -1) { + start = i; + } + } else { + if (start >= 0) { + numbers.add(Bitmap.createBitmap(text, start, 0, i - start, text.getHeight())); + start = -1; + } + } + } + if (start >= 0) { + numbers.add(Bitmap.createBitmap(text, start, 0, text.getWidth() - start, text.getHeight())); + } + return numbers; + } + +} diff --git a/app/src/main/java/com/stardust/mi666/ocr/TemplateMatching.java b/app/src/main/java/com/stardust/mi666/ocr/TemplateMatching.java new file mode 100644 index 00000000..97f7c7ee --- /dev/null +++ b/app/src/main/java/com/stardust/mi666/ocr/TemplateMatching.java @@ -0,0 +1,109 @@ +package com.stardust.mi666.ocr; + +import android.graphics.Bitmap; +import android.util.Log; + +import com.stardust.scriptdroid.tool.BitmapTool; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by Stardust on 2017/4/23. + */ + +public class TemplateMatching { + + private static final String LOG_TAG = "TemplateMatching"; + + private int mBlockSize; + private ColorDetector mColorDetector; + + + public TemplateMatching(int blockSize, ColorDetector colorDetector) { + mBlockSize = blockSize; + mColorDetector = colorDetector; + } + + public int[][] getFeature(Bitmap bitmap) { + int w = bitmap.getWidth() / mBlockSize; + int h = bitmap.getHeight() / mBlockSize; + int[][] mat = new int[w][h]; + for (int i = 0; i < bitmap.getWidth(); i++) { + for (int j = 0; j < bitmap.getHeight(); j++) { + if (mColorDetector.isCharPixel(bitmap.getPixel(i, j))) { + mat[i * w / bitmap.getWidth()][j * h / bitmap.getHeight()]++; + } + } + } + return mat; + } + + public int distance(int[][] f, int[][] template, int offsetX, int offsetY) { + int sum = 0; + for (int i = 0; i < template.length; i++) { + for (int j = 0; j < template[i].length; j++) { + if (i >= f.length || j >= f[i].length) { + sum += template[i].length - j; + break; + } + sum += Math.abs(template[i][j] - f[i + offsetX][j + offsetY]); + + } + } + return sum; + } + + public int minDistance(int[][] f, int[][] template) { + int min = Integer.MAX_VALUE; + int i = 0; + while (i < template.length && i + template.length - 1 < f.length) { + int j = 0; + while (j < template[i].length && j + template[i].length - 1 < f[i].length) { + int d = distance(f, template, i, j); + if (d < min) { + min = d; + } + j++; + } + i++; + } + return min; + } + + public static class TemplateMatchingOCR implements OCR { + + private Map mChars = new HashMap<>(); + private TemplateMatching mTemplateMatching; + private SimpleTextDetector mSimpleTextDetector; + + public TemplateMatchingOCR(int blockSize, ColorDetector colorDetector) { + mTemplateMatching = new TemplateMatching(blockSize, colorDetector); + mSimpleTextDetector = new SimpleTextDetector(colorDetector); + } + + @Override + public void addChar(char ch, Bitmap bitmap) { + mChars.put(ch, mTemplateMatching.getFeature(mSimpleTextDetector.detect(bitmap))); + } + + @Override + public char detect(Bitmap bitmap) { + int min = Integer.MAX_VALUE; + char ch = ' '; + for (Map.Entry entry : mChars.entrySet()) { + int[][] f = mTemplateMatching.getFeature(BitmapTool.scaleBitmap(bitmap, entry.getValue().length, entry.getValue()[0].length)); + int d = mTemplateMatching.distance(f, entry.getValue(), 0, 0); + Log.d(LOG_TAG, "char: " + entry.getKey() + " distance: " + d); + if (d < min) { + if (entry.getKey() == '1' && d > 30) { + continue; + } + min = d; + ch = entry.getKey(); + } + } + return ch; + } + } +} diff --git a/app/src/main/java/com/stardust/scriptdroid/App.java b/app/src/main/java/com/stardust/scriptdroid/App.java index b11a9e50..d6b43dcb 100644 --- a/app/src/main/java/com/stardust/scriptdroid/App.java +++ b/app/src/main/java/com/stardust/scriptdroid/App.java @@ -10,13 +10,17 @@ import android.support.annotation.Keep; import com.squareup.leakcanary.LeakCanary; import com.stardust.app.SimpleActivityLifecycleCallbacks; import com.stardust.app.VolumeChangeObserver; +import com.stardust.mi666.OCR; import com.stardust.scriptdroid.autojs.AutoJs; import com.stardust.scriptdroid.service.AccessibilityWatchDogService; import com.stardust.scriptdroid.tool.CrashHandler; -import com.stardust.scriptdroid.tool.UpdateChecker; +import com.stardust.scriptdroid.tool.JsBeautifierFactory; import com.stardust.scriptdroid.ui.error.ErrorReportActivity; import com.stardust.theme.ThemeColor; import com.stardust.theme.ThemeColorManager; +import com.stardust.util.ScreenMetrics; + +import java.lang.ref.WeakReference; /** * Created by Stardust on 2017/1/27. @@ -26,23 +30,25 @@ public class App extends Application { private static final String TAG = "App"; - private static App instance; - private static Activity currentActivity; + private static WeakReference instance; + private static WeakReference currentActivity; public static App getApp() { - return instance; + return instance.get(); } private VolumeChangeObserver mVolumeChangeObserver = new VolumeChangeObserver(); public void onCreate() { super.onCreate(); - instance = this; + instance = new WeakReference<>(this); setUpDebugEnvironment(); init(); registerActivityLifecycleCallback(); + } + private void setUpDebugEnvironment() { if (LeakCanary.isInAnalyzerProcess(this)) { return; @@ -56,8 +62,10 @@ public class App extends Application { ThemeColorManager.setDefaultThemeColor(new ThemeColor(getResources().getColor(R.color.colorPrimary), getResources().getColor(R.color.colorPrimaryDark), getResources().getColor(R.color.colorAccent))); ThemeColorManager.init(this); AutoJs.initInstance(this); + JsBeautifierFactory.initJsBeautify(this, "js/jsbeautify.js"); initVolumeChangeObserver(); startService(new Intent(this, AccessibilityWatchDogService.class)); + OCR.init(this); } private void initVolumeChangeObserver() { @@ -74,10 +82,6 @@ public class App extends Application { private void registerActivityLifecycleCallback() { registerActivityLifecycleCallbacks(new SimpleActivityLifecycleCallbacks() { - @Override - public void onActivityCreated(Activity activity, Bundle savedInstanceState) { - currentActivity = activity; - } @Override public void onActivityPaused(Activity activity) { @@ -86,20 +90,17 @@ public class App extends Application { @Override public void onActivityResumed(Activity activity) { - currentActivity = activity; + ScreenMetrics.initIfNeeded(activity); + currentActivity = new WeakReference<>(activity); } - @Override - public void onActivityStopped(Activity activity) { - currentActivity = null; - } }); } @Keep public static Activity currentActivity() { - return currentActivity; + return currentActivity.get(); } public static String getResString(int id) { diff --git a/app/src/main/java/com/stardust/scriptdroid/Pref.java b/app/src/main/java/com/stardust/scriptdroid/Pref.java index 5cd8f2df..f443edb6 100644 --- a/app/src/main/java/com/stardust/scriptdroid/Pref.java +++ b/app/src/main/java/com/stardust/scriptdroid/Pref.java @@ -46,7 +46,7 @@ public class Pref { } public static boolean isFirstGoToAccessibilitySetting() { - return getDisposableBoolean("isFirstGoToAccessibilitySetting", true); + return getDisposableBoolean("I miss you so much ...", true); } public static int oldVersion() { @@ -77,7 +77,7 @@ public class Pref { return App.getResString(id); } - public static int MaxTextLengthForCodeCompletion() { + public static int getMaxTextLengthForCodeCompletion() { try { return Integer.parseInt(def().getString(App.getApp().getString(R.string.key_max_length_for_code_completion), "2000")); } catch (NumberFormatException e) { diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floating_window/ConsoleFloaty.java b/app/src/main/java/com/stardust/scriptdroid/external/floating_window/ConsoleFloaty.java new file mode 100644 index 00000000..efc88cd0 --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/external/floating_window/ConsoleFloaty.java @@ -0,0 +1,77 @@ +package com.stardust.scriptdroid.external.floating_window; + +import android.content.Context; +import android.content.ContextWrapper; +import android.support.annotation.Nullable; +import android.view.ContextThemeWrapper; +import android.view.View; + +import com.stardust.enhancedfloaty.ResizableFloaty; +import com.stardust.enhancedfloaty.ResizableFloatyService; + +import com.stardust.scriptdroid.R; +/** + * Created by Stardust on 2017/4/20. + */ + +public class ConsoleFloaty extends ResizableFloaty { + + private ContextWrapper mContextWrapper; + private View mResizer, mMoveCursor; + + @Override + public View inflateCollapsedView(ResizableFloatyService service) { + ensureContextWrapper(service); + return View.inflate(mContextWrapper, R.layout.floating_window_collapse, null); + } + + private void ensureContextWrapper(Context context) { + if (mContextWrapper == null) { + mContextWrapper = new ContextThemeWrapper(context, R.style.AppTheme); + } + } + + @Override + public View inflateExpandedView(ResizableFloatyService service) { + ensureContextWrapper(service); + View view = View.inflate(mContextWrapper, R.layout.floating_console_expand, null); + setListeners(view, service); + return view; + } + + private void setListeners(final View view, final ResizableFloatyService service) { + view.findViewById(R.id.close).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + service.stopSelf(); + service.stopForeground(true); + } + }); + view.findViewById(R.id.move_or_resize).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if(mMoveCursor.getVisibility() == View.VISIBLE){ + mMoveCursor.setVisibility(View.GONE); + mResizer.setVisibility(View.GONE); + }else { + mMoveCursor.setVisibility(View.VISIBLE); + mResizer.setVisibility(View.VISIBLE); + } + } + }); + } + + @Nullable + @Override + public View getResizerView(View expandedView) { + mResizer = expandedView.findViewById(R.id.resizer); + return mResizer; + } + + @Nullable + @Override + public View getMoveCursorView(View expandedView) { + mMoveCursor = expandedView.findViewById(R.id.move_cursor); + return mMoveCursor; + } +} diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floating_window/FloatingConsole.java b/app/src/main/java/com/stardust/scriptdroid/external/floating_window/FloatingConsole.java new file mode 100644 index 00000000..32832c84 --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/external/floating_window/FloatingConsole.java @@ -0,0 +1,16 @@ +package com.stardust.scriptdroid.external.floating_window; + +import android.content.Context; + +import com.stardust.enhancedfloaty.ResizableFloatyService; + +/** + * Created by Stardust on 2017/4/18. + */ + +public class FloatingConsole { + + public static void startFloatingService(Context context) { + ResizableFloatyService.startService(context, new ConsoleFloaty()); + } +} diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floating_window/content/RecordNavigatorContent.java b/app/src/main/java/com/stardust/scriptdroid/external/floating_window/content/RecordNavigatorContent.java index b1009b36..1ca65178 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floating_window/content/RecordNavigatorContent.java +++ b/app/src/main/java/com/stardust/scriptdroid/external/floating_window/content/RecordNavigatorContent.java @@ -186,7 +186,7 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat @Override public void onStop() { - MainActivity.onActionRecordStopped(mContext, mRecorder.getCode()); + MainActivity.onRecordStop(mContext, mRecorder.getCode()); mRecorder = null; } diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floating_window/content/ScriptListNavigatorContent.java b/app/src/main/java/com/stardust/scriptdroid/external/floating_window/content/ScriptListNavigatorContent.java index 9a7fc773..5f7032a6 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floating_window/content/ScriptListNavigatorContent.java +++ b/app/src/main/java/com/stardust/scriptdroid/external/floating_window/content/ScriptListNavigatorContent.java @@ -96,6 +96,7 @@ public class ScriptListNavigatorContent implements NavigatorContent { int position = mFloatingScriptFileListView.getChildViewHolder((View) v.getParent()).getAdapterPosition(); ScriptFile file = mFloatingScriptFileListView.getAdapter().getScriptFileAt(position); EditActivity.editFile(v.getContext(), file); + HoverMenuService.postEvent(new MessageEvent(HoverMenuService.MESSAGE_COLLAPSE_MENU)); } }); diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floating_window/view/FloatingLayoutHierarchyView.java b/app/src/main/java/com/stardust/scriptdroid/external/floating_window/view/FloatingLayoutHierarchyView.java index 9b6fe66d..b6f241c3 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floating_window/view/FloatingLayoutHierarchyView.java +++ b/app/src/main/java/com/stardust/scriptdroid/external/floating_window/view/FloatingLayoutHierarchyView.java @@ -62,14 +62,15 @@ public class FloatingLayoutHierarchyView extends LayoutHierarchyView { .customView(mNodeInfoView, false) .theme(Theme.LIGHT) .build(); - mNodeInfoDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + if (mNodeInfoDialog.getWindow() != null) + mNodeInfoDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); } } @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; } diff --git a/app/src/main/java/com/stardust/scriptdroid/tool/BitmapTool.java b/app/src/main/java/com/stardust/scriptdroid/tool/BitmapTool.java new file mode 100644 index 00000000..2df02e28 --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/tool/BitmapTool.java @@ -0,0 +1,71 @@ +package com.stardust.scriptdroid.tool; + +import android.graphics.Bitmap; +import android.graphics.Matrix; + +/** + * Created by Stardust on 2017/4/22. + */ + +public class BitmapTool { + + public static Bitmap scaleBitmap(Bitmap origin, int newWidth, int newHeight) { + if (origin == null) { + return null; + } + int height = origin.getHeight(); + int width = origin.getWidth(); + float scaleWidth = ((float) newWidth) / width; + float scaleHeight = ((float) newHeight) / height; + Matrix matrix = new Matrix(); + matrix.postScale(scaleWidth, scaleHeight);// 使用后乘 + return Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false); + } + + /* + + public static MatOfDMatch matchesBitmaps(Bitmap bitmap1, Bitmap bitmap2) { + FeatureDetector detector = FeatureDetector.create(FeatureDetector.BRISK); + DescriptorExtractor descriptor = DescriptorExtractor.create(DescriptorExtractor.ORB); + DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMING); + Mat descriptors1 = computeDescriptors(bitmap1, detector, descriptor); + Mat descriptors2 = computeDescriptors(bitmap2, detector, descriptor); + MatOfDMatch matches = new MatOfDMatch(); + matcher.match(descriptors1, descriptors2, matches); + return matches; + } + + private static Mat computeDescriptors(Bitmap bitmap, FeatureDetector detector, DescriptorExtractor descriptor) { + Mat mat = bitmapToMat(bitmap.copy(bitmap.getConfig(), true)); + Mat descriptors = new Mat(); + MatOfKeyPoint keyPoints1 = new MatOfKeyPoint(); + detector.detect(mat, keyPoints1); + descriptor.compute(mat, keyPoints1, descriptors); + return descriptors; + } + + private static Mat bitmapToMat(Bitmap bmp) { + Mat mat = new Mat(bmp.getWidth(), bmp.getHeight(), CvType.CV_8UC1); + Utils.bitmapToMat(bmp, mat); + return mat; + } + + public static Core.MinMaxLocResult templateMatching(Bitmap bitmap, Bitmap tmp, int matchMethod) { + Mat img = bitmapToMat(bitmap); + Mat template = bitmapToMat(tmp); + + // / Create the result matrix + int result_cols = img.cols() - template.cols() + 1; + int result_rows = img.rows() - template.rows() + 1; + Mat result = new Mat(result_rows, result_cols, CvType.CV_32FC1); + + // / Do the Matching and Normalize + Imgproc.matchTemplate(img, template, result, matchMethod); + Core.normalize(result, result, 0, 1, Core.NORM_MINMAX, -1, new Mat()); + + // / Localizing the best match with minMaxLoc + Core.MinMaxLocResult mmr = Core.minMaxLoc(result); + return mmr; + } + */ +} diff --git a/app/src/main/java/com/stardust/scriptdroid/tool/DrawableSaver.java b/app/src/main/java/com/stardust/scriptdroid/tool/DrawableSaver.java index 54f69067..ff19010e 100644 --- a/app/src/main/java/com/stardust/scriptdroid/tool/DrawableSaver.java +++ b/app/src/main/java/com/stardust/scriptdroid/tool/DrawableSaver.java @@ -14,7 +14,6 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.concurrent.Callable; /** * Created by Stardust on 2017/4/3. @@ -24,14 +23,14 @@ public abstract class DrawableSaver { private static final String PREFIX = "saved_drawable_"; - protected Drawable mOriginalDrawble; + protected Drawable mOriginalDrawable; private Context mContext; private String mName; - public DrawableSaver(Context context, String name, Drawable originalDrawble) { + public DrawableSaver(Context context, String name, Drawable originalDrawable) { mContext = context; mName = PREFIX + name; - mOriginalDrawble = originalDrawble; + mOriginalDrawable = originalDrawable; readImageAndApply(); } @@ -89,7 +88,7 @@ public abstract class DrawableSaver { } public void reset() { - applyDrawableToView(mOriginalDrawble); + applyDrawableToView(mOriginalDrawable); mContext.deleteFile(mName); } diff --git a/app/src/main/java/com/stardust/scriptdroid/tool/ImagePHash.java b/app/src/main/java/com/stardust/scriptdroid/tool/ImagePHash.java new file mode 100644 index 00000000..e0781249 --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/tool/ImagePHash.java @@ -0,0 +1,162 @@ +package com.stardust.scriptdroid.tool; + +/** + * Created by Stardust on 2017/4/23. + */ + +import android.graphics.Bitmap; + +import com.stardust.mi666.ocr.ColorDetector; +import com.stardust.mi666.ocr.SimpleTextDetector; + +import java.util.HashMap; +import java.util.Map; + +public class ImagePHash { + + private static final String LOG_TAG = "ImagePHash"; + + private int width = 32; + private int height; + private int smallerWidth = 8; + private int smallerHeight; + + public ImagePHash() { + this(32, 8); + } + + public ImagePHash(int width, int smallerWidth) { + this(width, width, smallerWidth, smallerWidth); + } + + public ImagePHash(int width, int height, int smallerWidth, int smallerHeight) { + this.width = width; + this.height = height; + this.smallerWidth = smallerWidth; + this.smallerHeight = smallerHeight; + initCoefficients(); + } + + public int distance(boolean[] s1, boolean[] s2) { + int counter = 0; + for (int k = 0; k < s1.length; k++) { + if (s1[k] != s2[k]) { + counter++; + } + } + return counter; + } + + + public boolean[] getHash(Bitmap img) { + double[][] vals = new double[width][height]; + img = BitmapTool.scaleBitmap(img, width, height); + + for (int x = 0; x < img.getWidth(); x++) { + for (int y = 0; y < img.getHeight(); y++) { + vals[x][y] = getBlue(img, x, y); + } + } + + double[][] dctVals = applyDCT(vals); + + double total = 0; + + for (int x = 0; x < smallerWidth; x++) { + for (int y = 0; y < smallerHeight; y++) { + total += dctVals[x][y]; + } + } + total -= dctVals[0][0]; + + double avg = total / (double) ((smallerWidth * smallerHeight) - 1); + boolean[] hash = new boolean[smallerWidth * smallerHeight]; + int i = 0; + for (int x = 0; x < smallerWidth; x++) { + for (int y = 0; y < smallerHeight; y++) { + if (x != 0 && y != 0) { + hash[i++] = dctVals[x][y] > avg; + } + } + } + return hash; + } + + + private static int getBlue(Bitmap img, int x, int y) { + return img.getPixel(x, y) & 0xff; + } + + private double[] c; + private double[] c2; + + private void initCoefficients() { + c = new double[width]; + c2 = new double[height]; + for (int i = 1; i < width; i++) { + c[i] = 1; + } + for (int i = 1; i < height; i++) { + c2[i] = 1; + } + c[0] = 1 / Math.sqrt(2.0); + c2[0] = 1 / Math.sqrt(2.0); + } + + private double[][] applyDCT(double[][] f) { + double[][] F = new double[width][height]; + for (int u = 0; u < width; u++) { + for (int v = 0; v < height; v++) { + double sum = 0.0; + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + sum += Math.cos(((2 * i + 1) / (2.0 * width)) * u * Math.PI) * Math.cos(((2 * j + 1) / (2.0 * height)) * v * Math.PI) * (f[i][j]); + } + } + sum *= ((c[u] * c2[v]) / 4.0); + F[u][v] = sum; + } + } + return F; + } + + public static class OCR implements com.stardust.mi666.ocr.OCR { + + private SimpleTextDetector mSimpleTextDetector; + private Map mChars = new HashMap<>(); + private ImagePHash mImagePHash; + + public OCR(ColorDetector colorDetector, int size, int smallerSize) { + mSimpleTextDetector = new SimpleTextDetector(colorDetector); + mImagePHash = new ImagePHash(size, smallerSize); + } + + public OCR(ColorDetector colorDetector, ImagePHash pHash) { + mSimpleTextDetector = new SimpleTextDetector(colorDetector); + mImagePHash = pHash; + } + + @Override + public void addChar(char ch, Bitmap bitmap) { + mChars.put(ch, mImagePHash.getHash(mSimpleTextDetector.detect(bitmap))); + } + + @Override + public char detect(Bitmap bitmap) { + bitmap = mSimpleTextDetector.detect(bitmap); + int min = Integer.MAX_VALUE; + boolean[] h = mImagePHash.getHash(bitmap); + char ch = ' '; + for (Map.Entry entry : mChars.entrySet()) { + int d = mImagePHash.distance(h, entry.getValue()); + //Log.i(LOG_TAG, "char: " + entry.getKey() + " distance: " + d); + if (d < min) { + min = d; + ch = entry.getKey(); + } + } + return ch; + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/stardust/scriptdroid/tool/ImageSelector.java b/app/src/main/java/com/stardust/scriptdroid/tool/ImageSelector.java index cf5347fc..b0f05bd8 100644 --- a/app/src/main/java/com/stardust/scriptdroid/tool/ImageSelector.java +++ b/app/src/main/java/com/stardust/scriptdroid/tool/ImageSelector.java @@ -2,9 +2,6 @@ package com.stardust.scriptdroid.tool; import android.app.Activity; import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; -import android.provider.MediaStore; import com.stardust.app.OnActivityResultDelegate; import com.stardust.scriptdroid.R; @@ -22,6 +19,8 @@ public class ImageSelector implements OnActivityResultDelegate { void onImageSelected(ImageSelector selector, InputStream path); } + private static final String TAG = ImageSelector.class.getSimpleName(); + private static final int REQUEST_CODE = "LOVE EATING".hashCode() >> 16; private Activity mActivity; private ImageSelectorCallback mCallback; diff --git a/app/src/main/java/com/stardust/scriptdroid/tool/IntentTool.java b/app/src/main/java/com/stardust/scriptdroid/tool/IntentTool.java new file mode 100644 index 00000000..06f60b19 --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/tool/IntentTool.java @@ -0,0 +1,20 @@ +package com.stardust.scriptdroid.tool; + +import android.content.Context; +import android.widget.Toast; + +import com.stardust.scriptdroid.R; +import com.stardust.util.IntentUtil; + +/** + * Created by Stardust on 2017/4/12. + */ + +public class IntentTool { + + public static void browse(Context context, String url){ + if (!IntentUtil.browse(context, url)) { + Toast.makeText(context, R.string.text_no_brower, Toast.LENGTH_SHORT).show(); + } + } +} diff --git a/app/src/main/java/com/stardust/scriptdroid/tool/JsBeautifierFactory.java b/app/src/main/java/com/stardust/scriptdroid/tool/JsBeautifierFactory.java new file mode 100644 index 00000000..9637f2a1 --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/tool/JsBeautifierFactory.java @@ -0,0 +1,25 @@ +package com.stardust.scriptdroid.tool; + +import android.annotation.SuppressLint; +import android.app.Application; + +import com.stardust.autojs.script.JsBeautifier; + +/** + * Created by Stardust on 2017/4/18. + */ + +public class JsBeautifierFactory { + + @SuppressLint("StaticFieldLeak") + private static JsBeautifier jsBeautifier; + + public static JsBeautifier getJsBeautify() { + return jsBeautifier; + } + + public static void initJsBeautify(Application context, String path) { + jsBeautifier = new JsBeautifier(context, path); + jsBeautifier.prepare(); + } +} diff --git a/app/src/main/java/com/stardust/scriptdroid/tool/MaterialDialogFactory.java b/app/src/main/java/com/stardust/scriptdroid/tool/MaterialDialogFactory.java new file mode 100644 index 00000000..fe9f1a2f --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/tool/MaterialDialogFactory.java @@ -0,0 +1,26 @@ +package com.stardust.scriptdroid.tool; + +import android.content.Context; + +import com.afollestad.materialdialogs.MaterialDialog; +import com.stardust.scriptdroid.R; + +/** + * Created by Stardust on 2017/4/18. + */ + +public class MaterialDialogFactory { + public static MaterialDialog createProgress(Context context) { + return new MaterialDialog.Builder(context) + .progress(true, 0) + .cancelable(false) + .content(R.string.text_processing) + .build(); + } + + public static MaterialDialog showProgress(Context context) { + MaterialDialog dialog = createProgress(context); + dialog.show(); + return dialog; + } +} diff --git a/app/src/main/java/com/stardust/scriptdroid/tool/Shell.java b/app/src/main/java/com/stardust/scriptdroid/tool/Shell.java new file mode 100644 index 00000000..63d99b8c --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/tool/Shell.java @@ -0,0 +1,161 @@ +package com.stardust.scriptdroid.tool; + +import android.content.Context; +import android.preference.PreferenceManager; + +import com.stardust.pio.UncheckedIOException; +import com.stardust.scriptdroid.App; + +import java.io.IOException; + +import jackpal.androidterm.ShellTermSession; +import jackpal.androidterm.emulatorview.TermSession; +import jackpal.androidterm.util.TermSettings; + +/** + * Created by Stardust on 2017/4/24. + */ + +public class Shell { + + private TermSession mTermSession; + private String mOutput; + + public Shell(boolean root) { + this(App.getApp(), root ? "su\n" : "sh\n"); + } + + public Shell(Context context, String initialCommand) { + TermSettings settings = new TermSettings(context.getResources(), PreferenceManager.getDefaultSharedPreferences(context)); + try { + mTermSession = new MyShellTermSession(settings, initialCommand); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public String execAndWaitFor(String command) { + mTermSession.write(command + "\n"); + mOutput = null; + synchronized (this) { + try { + wait(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + return mOutput; + } + + public String execAndWaitFor(String command, int millis) { + mTermSession.write(command + "\n"); + mOutput = null; + synchronized (this) { + try { + wait(millis); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + return mOutput; + } + + public void exec(String command) { + mTermSession.write(command); + } + + public void execute(String command) { + mTermSession.write(command); + } + + + public void Tap(int x, int y) { + execute("input tap " + x + " " + y); + } + + public void Swipe(int x1, int y1, int x2, int y2) { + execute("input swipe " + x1 + " " + y1 + " " + x2 + " " + y2); + } + + public void Swipe(int x1, int y1, int x2, int y2, long duration) { + execute("input swipe " + x1 + " " + y1 + " " + x2 + " " + y2 + " " + duration); + } + + public void KeyCode(int keyCode) { + execute("input keyevent " + keyCode); + } + + public void KeyCode(String keyCode) { + execute("input keyevent " + keyCode); + } + + public void Home() { + KeyCode(3); + } + + public void Back() { + KeyCode(4); + } + + public void Power() { + KeyCode(26); + } + + public void Up() { + KeyCode(19); + } + + public void Down() { + KeyCode(20); + } + + public void Left() { + KeyCode(21); + } + + public void Right() { + KeyCode(22); + } + + public void OK() { + KeyCode(23); + } + + public void VolumeUp() { + KeyCode(24); + } + + public void VolumeDown() { + KeyCode(25); + } + + public void Menu() { + KeyCode(1); + } + + public void Camera() { + KeyCode(27); + } + + public void Text(String text) { + execute("input text " + text); + } + + private class MyShellTermSession extends ShellTermSession { + + public MyShellTermSession(TermSettings settings, String initialCommand) throws IOException { + super(settings, initialCommand); + } + + + @Override + protected void processInput(byte[] data, int offset, int count) { + mOutput = new String(data, offset, count); + synchronized (Shell.this) { + Shell.this.notifyAll(); + } + appendToEmulator(data, offset, count); + } + } + +} diff --git a/app/src/main/java/com/stardust/scriptdroid/tool/UpdateChecker.java b/app/src/main/java/com/stardust/scriptdroid/tool/UpdateChecker.java index 93a69bc6..a85b130e 100644 --- a/app/src/main/java/com/stardust/scriptdroid/tool/UpdateChecker.java +++ b/app/src/main/java/com/stardust/scriptdroid/tool/UpdateChecker.java @@ -13,7 +13,7 @@ import com.android.volley.toolbox.StringRequest; import com.android.volley.toolbox.Volley; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; -import com.stardust.pio.UncheckedIOException; +import com.stardust.scriptdroid.BuildConfig; import java.lang.reflect.Type; import java.util.List; @@ -28,37 +28,40 @@ public class UpdateChecker implements Response.Listener, Response.ErrorL private RequestQueue mRequestQueue; private static final String UPDATE_URL = "https://raw.githubusercontent.com/hyb1996/NoRootScriptDroid/master/version.json"; - public interface Callback { - void onSuccess(CheckResult result); + void onSuccess(UpdateInfo result); void onError(Exception exception); } - private Callback mCallback; - private final int mTimeOut = 3000; + public static UpdateInfo savedResult; - public UpdateChecker(Context context, Callback callback) { - mCallback = callback; + + private final int mTimeOut = 3000; + private Callback mCallback; + + public UpdateChecker(Context context) { mRequestQueue = Volley.newRequestQueue(context); } - private CheckResult parse(String json) { + private UpdateInfo parse(String json) { Gson gson = new Gson(); - Type type = new TypeToken() { + Type type = new TypeToken() { }.getType(); - CheckResult result = gson.fromJson(json, type); + UpdateInfo result = gson.fromJson(json, type); Log.i(LOG_TAG, result.toString()); return result; } - public void check() { + public void check(final Callback callback) { + mCallback = callback; StringRequest request = new StringRequest(Request.Method.GET, UPDATE_URL, this, this); request.setRetryPolicy(new DefaultRetryPolicy(mTimeOut, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT)); request.setTag("update-check"); + request.setShouldCache(false); mRequestQueue.add(request); } @@ -66,12 +69,16 @@ public class UpdateChecker implements Response.Listener, Response.ErrorL public void onErrorResponse(VolleyError error) { error.printStackTrace(); mCallback.onError(error); + mCallback = null; } @Override public void onResponse(String response) { try { - CheckResult result = parse(response); + UpdateInfo result = parse(response); + savedResult = result; + if (mCallback == null) + return; if (result.isValid()) { mCallback.onSuccess(result); } else { @@ -81,13 +88,14 @@ public class UpdateChecker implements Response.Listener, Response.ErrorL e.printStackTrace(); mCallback.onError(e); } + mCallback = null; } public void cancel() { mRequestQueue.cancelAll("update-check"); } - public static class CheckResult { + public static class UpdateInfo { public int versionCode; public String releaseNotes; @@ -95,25 +103,34 @@ public class UpdateChecker implements Response.Listener, Response.ErrorL public List downloads; public List oldVersions; public int deprecated; + public String downloadUrl; public boolean isValid() { return downloads != null && !downloads.isEmpty() && versionCode > 0 && !TextUtils.isEmpty(versionName) && !TextUtils.isEmpty(releaseNotes); } + public OldVersion getOldVersion(int versionCode) { + for (OldVersion oldVersion : oldVersions) { + if (oldVersion.versionCode == versionCode) { + return oldVersion; + } + } + return null; + } + @Override public String toString() { - return "CheckResult{" + + return "UpdateInfo{" + "versionCode=" + versionCode + ", releaseNotes='" + releaseNotes + '\'' + ", versionName='" + versionName + '\'' + ", downloads=" + downloads + ", oldVersions=" + oldVersions + ", deprecated=" + deprecated + + ", downloadUrl='" + downloadUrl + '\'' + '}'; } - - } public static class OldVersion { diff --git a/app/src/main/java/com/stardust/scriptdroid/tool/VersionInfo.java b/app/src/main/java/com/stardust/scriptdroid/tool/VersionInfo.java new file mode 100644 index 00000000..2a1898f8 --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/tool/VersionInfo.java @@ -0,0 +1,129 @@ +package com.stardust.scriptdroid.tool; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; + +import com.stardust.scriptdroid.BuildConfig; +import com.stardust.util.NetworkUtils; + +/** + * Created by Stardust on 2017/4/9. + */ + +public class VersionInfo { + + + private static final String KEY_DEPRECATED = "Still loving you...Can we go back..."; + private static final String KEY_DEPRECATED_VERSION_CODE = "I miss you so much tonight...Baby don't let me cry..."; + + public interface OnReceiveUpdateResultCallback { + void onReceive(UpdateChecker.UpdateInfo info, boolean isCurrentVersionDeprecated); + } + + + private static VersionInfo instance = new VersionInfo(); + + public static VersionInfo getInstance() { + return instance; + } + + private boolean mDeprecated = false; + private UpdateChecker.UpdateInfo mUpdateInfo; + private final int mReconnectTimes = 2; + private int mReconnectCount = 0; + private OnReceiveUpdateResultCallback mOnReceiveUpdateResultCallback; + private SharedPreferences mSharedPreferences; + + public void readDeprecatedFromPref(Context context) { + mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + if (mSharedPreferences.getInt(KEY_DEPRECATED_VERSION_CODE, 0) < BuildConfig.VERSION_CODE) { + mSharedPreferences.edit().remove(KEY_DEPRECATED_VERSION_CODE) + .putBoolean(KEY_DEPRECATED, false) + .apply(); + } + mDeprecated = mSharedPreferences.getBoolean(KEY_DEPRECATED, false); + } + + + public void readDeprecatedFromPrefIfNeeded(Context context) { + if (mSharedPreferences == null) { + readDeprecatedFromPref(context); + } + } + + public boolean isCurrentVersionDeprecated() { + return mDeprecated; + } + + public UpdateChecker.UpdateInfo getUpdateInfo() { + return mUpdateInfo; + } + + public String getCurrentVersionIssues() { + if (mUpdateInfo == null) + return null; + UpdateChecker.OldVersion oldVersion = mUpdateInfo.getOldVersion(BuildConfig.VERSION_CODE); + if (oldVersion == null) + return null; + return oldVersion.issues; + } + + public void checkUpdateIfNeeded(Context context) { + if (mUpdateInfo == null) { + checkUpdateIfUsingWifi(context); + } + } + + public void setOnReceiveUpdateResultCallback(OnReceiveUpdateResultCallback onReceiveUpdateResultCallback) { + mOnReceiveUpdateResultCallback = onReceiveUpdateResultCallback; + } + + private void checkUpdateIfUsingWifi(Context context) { + if (NetworkUtils.isWifiAvailable(context)) { + checkUpdate(context); + } + + } + + public void checkUpdate(Context context) { + mReconnectCount = 0; + checkUpdateInner(context); + } + + private void checkUpdateInner(final Context context) { + mReconnectCount++; + new UpdateChecker(context).check(new UpdateChecker.Callback() { + + @Override + public void onSuccess(UpdateChecker.UpdateInfo result) { + if (result.isValid()) { + setUpdateInfo(result); + } else if (mReconnectCount < mReconnectTimes) { + checkUpdate(context); + } + } + + @Override + public void onError(Exception exception) { + if (mReconnectCount < mReconnectTimes) { + checkUpdate(context); + } + } + }); + } + + private void setUpdateInfo(UpdateChecker.UpdateInfo result) { + mDeprecated = BuildConfig.VERSION_CODE <= result.deprecated; + mUpdateInfo = result; + if (mDeprecated) { + mSharedPreferences.edit().putBoolean(KEY_DEPRECATED, mDeprecated) + .putInt(KEY_DEPRECATED_VERSION_CODE, BuildConfig.VERSION_CODE) + .apply(); + } + if (mOnReceiveUpdateResultCallback != null) { + mOnReceiveUpdateResultCallback.onReceive(result, mDeprecated); + } + } + +} diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/BaseActivity.java b/app/src/main/java/com/stardust/scriptdroid/ui/BaseActivity.java index e8e25fd7..eba39324 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/BaseActivity.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/BaseActivity.java @@ -1,8 +1,10 @@ package com.stardust.scriptdroid.ui; import android.app.Activity; +import android.content.DialogInterface; import android.os.Build; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.RequiresApi; import android.support.v7.app.AppCompatActivity; @@ -10,8 +12,14 @@ import android.support.v7.widget.Toolbar; import android.view.View; +import com.afollestad.materialdialogs.DialogAction; +import com.afollestad.materialdialogs.MaterialDialog; +import com.stardust.pio.PFile; +import com.stardust.scriptdroid.App; import com.stardust.scriptdroid.R; -import com.stardust.scriptdroid.external.tasker.TaskPrefEditActivity; +import com.stardust.scriptdroid.tool.UpdateChecker; +import com.stardust.scriptdroid.tool.VersionInfo; +import com.stardust.scriptdroid.ui.update.UpdateCheckDialog; import com.stardust.theme.ThemeColorManager; import com.stardust.util.BackPressedHandler; @@ -42,6 +50,20 @@ public abstract class BaseActivity extends AppCompatActivity { if ((getWindow().getDecorView().getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0) { ThemeColorManager.addActivityStatusBar(this); } + + } + + @Override + protected void onResume() { + super.onResume(); + } + + + + @Override + protected void onPause() { + super.onPause(); + VersionInfo.getInstance().setOnReceiveUpdateResultCallback(null); } @SuppressWarnings("unchecked") diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/console/TimberConsole.java b/app/src/main/java/com/stardust/scriptdroid/ui/console/TimberConsole.java index f232e355..0343c6c0 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/console/TimberConsole.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/console/TimberConsole.java @@ -22,15 +22,19 @@ import timber.log.Timber; public class TimberConsole implements Console { static { - Timber.plant(new ConsoleTree.Builder() - .minPriority(Log.VERBOSE) - .verboseColor(0xff909090) - .debugColor(0xffc88b48) - .infoColor(0xffc9c9c9) - .warnColor(0xffa97db6) - .errorColor(0xffff534e) - .assertColor(0xffff5540) - .build()); + try { + Timber.plant(new ConsoleTree.Builder() + .minPriority(Log.VERBOSE) + .verboseColor(0xff909090) + .debugColor(0xffc88b48) + .infoColor(0xffc9c9c9) + .warnColor(0xffa97db6) + .errorColor(0xffff534e) + .assertColor(0xffff5540) + .build()); + } catch (Exception e) { + // FIXME: 2017/4/26 java.lang.NoClassDefFoundError: com.jraska.console.timber.ConsoleTree at android4.4 + } } @Override diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/edit/EditActivity.java b/app/src/main/java/com/stardust/scriptdroid/ui/edit/EditActivity.java index 775620f2..9fb52656 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/edit/EditActivity.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/edit/EditActivity.java @@ -11,6 +11,8 @@ import android.support.design.widget.Snackbar; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; import android.util.SparseArray; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.inputmethod.InputMethod; @@ -19,17 +21,24 @@ import com.afollestad.materialdialogs.MaterialDialog; import com.stardust.autojs.engine.JavaScriptEngine; import com.stardust.autojs.ScriptExecutionListener; import com.stardust.autojs.script.FileScriptSource; +import com.stardust.autojs.script.JsBeautifier; import com.stardust.autojs.script.ScriptSource; import com.stardust.autojs.script.StringScriptSource; +import com.stardust.enhancedfloaty.ResizableFloatyService; import com.stardust.pio.PFile; import com.stardust.scriptdroid.Pref; import com.stardust.scriptdroid.autojs.AutoJs; +import com.stardust.scriptdroid.external.floating_window.FloatingConsole; import com.stardust.scriptdroid.scripts.ScriptFile; +import com.stardust.scriptdroid.tool.JsBeautifierFactory; +import com.stardust.scriptdroid.tool.MaterialDialogFactory; import com.stardust.scriptdroid.ui.BaseActivity; +import com.stardust.scriptdroid.ui.console.ConsoleActivity; import com.stardust.scriptdroid.ui.edit.editor920.Editor920Activity; import com.stardust.scriptdroid.ui.edit.editor920.Editor920Utils; import com.stardust.scriptdroid.ui.edit.sidemenu.EditSideMenuFragment; import com.stardust.scriptdroid.ui.edit.sidemenu.FunctionListRecyclerView; +import com.stardust.scriptdroid.ui.help.HelpCatalogueActivity; import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder; import com.stardust.util.SparseArrayEntries; import com.stardust.view.ViewBinding; @@ -56,6 +65,7 @@ import timber.log.Timber; public class EditActivity extends Editor920Activity { + public static class InputMethodEnhanceBarBridge implements InputMethodEnhanceBar.EditTextBridge { private Editor920Activity mEditor920Activity; @@ -152,6 +162,7 @@ public class EditActivity extends Editor920Activity { } } }; + private JsBeautifier mJsBeautifier = JsBeautifierFactory.getJsBeautify(); public void onCreate(Bundle b) { super.onCreate(b); @@ -314,6 +325,51 @@ public class EditActivity extends Editor920Activity { menuItem.setEnabled(!disabled); } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_editor, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_console: + startActivity(new Intent(getContext(), ConsoleActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + return true; + case R.id.action_help: + HelpCatalogueActivity.showMainCatalogue(this); + return true; + case R.id.action_beautify: + beautifyCode(); + return true; + + } + return super.onOptionsItemSelected(item); + } + + private void beautifyCode() { + final MaterialDialog dialog = MaterialDialogFactory.showProgress(this); + mJsBeautifier.beautify(mEditorDelegate.getText(), new JsBeautifier.Callback() { + @Override + public void onSuccess(final String beautifiedCode) { + runOnUiThread(new Runnable() { + @Override + public void run() { + mEditorDelegate.mEditText.setText(beautifiedCode); + dialog.dismiss(); + } + }); + } + + @Override + public void onException(Exception e) { + e.printStackTrace(); + } + }); + } + @Override public void finish() { if (!mReadOnly && mEditorDelegate.isChanged()) { diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/edit/completion/CodeCompletion.java b/app/src/main/java/com/stardust/scriptdroid/ui/edit/completion/CodeCompletion.java index b07dd5d3..fdd1197c 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/edit/completion/CodeCompletion.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/edit/completion/CodeCompletion.java @@ -122,7 +122,7 @@ public class CodeCompletion implements TextWatcher { } private Collection searchWordCompletion(String str) { - if (mEditText.getEditableText().length() < Pref.MaxTextLengthForCodeCompletion()) { + if (mEditText.getEditableText().length() < Pref.getMaxTextLengthForCodeCompletion()) { return searchCodeCompletion(str, splitWord(mEditText.getEditableText().toString())); } return new TreeSet<>(); diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/edit/sidemenu/HelpSideMenuFragment.java b/app/src/main/java/com/stardust/scriptdroid/ui/edit/sidemenu/HelpSideMenuFragment.java new file mode 100644 index 00000000..57c94f4c --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/ui/edit/sidemenu/HelpSideMenuFragment.java @@ -0,0 +1,24 @@ +package com.stardust.scriptdroid.ui.edit.sidemenu; + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.stardust.app.Fragment; +import com.stardust.scriptdroid.R; + +/** + * Created by Stardust on 2017/4/18. + */ + +public class HelpSideMenuFragment extends Fragment { + + + @Nullable + @Override + public View createView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_help_side_menu, container, false); + } +} diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/main/MainActivity.java b/app/src/main/java/com/stardust/scriptdroid/ui/main/MainActivity.java index 595c7fce..b46cb8f7 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/main/MainActivity.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/main/MainActivity.java @@ -25,26 +25,27 @@ import com.afollestad.materialdialogs.MaterialDialog; import com.stardust.app.FragmentPagerAdapterBuilder; import com.stardust.app.NotAskAgainDialog; import com.stardust.app.OnActivityResultDelegate; +import com.stardust.scriptdroid.BuildConfig; +import com.stardust.scriptdroid.R; import com.stardust.scriptdroid.scripts.ScriptFile; import com.stardust.scriptdroid.scripts.StorageScriptProvider; import com.stardust.scriptdroid.service.AccessibilityWatchDogService; import com.stardust.scriptdroid.tool.AccessibilityServiceTool; import com.stardust.scriptdroid.tool.DrawableSaver; import com.stardust.scriptdroid.ui.BaseActivity; -import com.stardust.scriptdroid.ui.main.script_list.MyScriptListFragment; import com.stardust.scriptdroid.ui.main.sample_list.SampleScriptListFragment; +import com.stardust.scriptdroid.ui.main.script_list.MyScriptListFragment; import com.stardust.scriptdroid.ui.main.script_list.ScriptFileChooserDialogBuilder; import com.stardust.scriptdroid.ui.main.task.TaskManagerFragment; import com.stardust.scriptdroid.ui.settings.SettingsActivity; +import com.stardust.scriptdroid.ui.update.VersionGuard; import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder; import com.stardust.util.BackPressedHandler; import com.stardust.util.MessageEvent; -import com.stardust.view.ViewBinding; -import com.stardust.widget.SlidingUpPanel; -import com.stardust.scriptdroid.BuildConfig; -import com.stardust.scriptdroid.R; import com.stardust.view.ViewBinder; +import com.stardust.view.ViewBinding; import com.stardust.view.accessibility.AccessibilityServiceUtils; +import com.stardust.widget.SlidingUpPanel; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; @@ -68,6 +69,7 @@ public class MainActivity extends BaseActivity { private OnActivityResultDelegate.Intermediary mActivityResultIntermediary = new OnActivityResultDelegate.Intermediary(); private DrawableSaver mDrawerHeaderBackgroundSaver, mAppbarBackgroundSaver; + private VersionGuard mVersionGuard; @Override protected void onCreate(Bundle savedInstanceState) { @@ -77,8 +79,11 @@ public class MainActivity extends BaseActivity { registerBackPressHandlers(); handleIntent(getIntent()); EventBus.getDefault().register(this); + mVersionGuard = new VersionGuard(this); } + + private void registerBackPressHandlers() { registerBackPressedHandler(new BackPressedHandler() { @Override @@ -101,7 +106,7 @@ public class MainActivity extends BaseActivity { private void showAccessibilitySettingPromptIfDisabled() { if (!AccessibilityServiceUtils.isAccessibilityServiceEnabled(this, AccessibilityWatchDogService.class)) { - new NotAskAgainDialog.Builder(this, "showAccessibilitySettingPromptIfDisabled") + new NotAskAgainDialog.Builder(this, "Eating...love you...miss you...17.4.12") .title(R.string.text_need_to_enable_accessibility_service) .content(R.string.explain_accessibility_permission) .positiveText(R.string.text_go_to_setting) @@ -279,24 +284,6 @@ public class MainActivity extends BaseActivity { return ((MyScriptListFragment) mPagerAdapter.getStoredFragment(0)); } - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - mActivityResultIntermediary.onActivityResult(requestCode, resultCode, data); - } - - public static void onActionRecordStopped(Context context, String script) { - Intent intent = new Intent(context, MainActivity.class) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .putExtra(EXTRA_ACTION, ACTION_ON_ACTION_RECORD_STOPPED) - .putExtra(ARGUMENT_SCRIPT, script); - context.startActivity(intent); - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - StorageScriptProvider.getDefault().notifyStoragePermissionGranted(); - } - @ViewBinding.Click(R.id.toolbar) public void OnToolbarClick() { mAppbarBackgroundSaver.select(this, mActivityResultIntermediary); @@ -310,9 +297,36 @@ public class MainActivity extends BaseActivity { } } + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + mActivityResultIntermediary.onActivityResult(requestCode, resultCode, data); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + StorageScriptProvider.getDefault().notifyStoragePermissionGranted(); + } + + + @Override + protected void onResume() { + super.onResume(); + mVersionGuard.checkDeprecateAndUpdate(); + } + @Override protected void onDestroy() { super.onDestroy(); EventBus.getDefault().unregister(this); } + + + public static void onRecordStop(Context context, String script) { + Intent intent = new Intent(context, MainActivity.class) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + .putExtra(EXTRA_ACTION, ACTION_ON_ACTION_RECORD_STOPPED) + .putExtra(ARGUMENT_SCRIPT, script); + context.startActivity(intent); + } + } \ No newline at end of file diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/settings/AboutActivity.java b/app/src/main/java/com/stardust/scriptdroid/ui/settings/AboutActivity.java index 020ffd24..7b5d69fa 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/settings/AboutActivity.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/settings/AboutActivity.java @@ -12,6 +12,7 @@ import android.widget.Toast; import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; +import com.stardust.scriptdroid.tool.IntentTool; import com.stardust.scriptdroid.ui.BaseActivity; import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder; import com.stardust.util.IntentUtil; @@ -51,9 +52,7 @@ public class AboutActivity extends BaseActivity { @ViewBinding.Click(R.id.github) private void openGitHub() { - if (!IntentUtil.browse(this, getString(R.string.my_github))) { - Toast.makeText(this, R.string.text_no_brower, Toast.LENGTH_SHORT).show(); - } + IntentTool.browse(this, getString(R.string.my_github)); } @ViewBinding.Click(R.id.qq) diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/update/UpdateCheckDialog.java b/app/src/main/java/com/stardust/scriptdroid/ui/update/UpdateCheckDialog.java index 6dd015d0..ce32fada 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/update/UpdateCheckDialog.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/update/UpdateCheckDialog.java @@ -1,7 +1,6 @@ package com.stardust.scriptdroid.ui.update; import android.content.Context; -import android.os.AsyncTask; import android.support.annotation.NonNull; import android.widget.Toast; @@ -12,8 +11,7 @@ import com.stardust.scriptdroid.R; import com.stardust.scriptdroid.tool.UpdateChecker; import com.stardust.util.IntentUtil; -import java.util.Timer; -import java.util.TimerTask; +import java.util.LinkedList; /** * Created by Stardust on 2017/4/9. @@ -21,7 +19,6 @@ import java.util.TimerTask; public class UpdateCheckDialog implements UpdateChecker.Callback { - private MaterialDialog mResultDialog; private MaterialDialog mProgressDialog; private Context mContext; private UpdateChecker mUpdateChecker; @@ -29,70 +26,42 @@ public class UpdateCheckDialog implements UpdateChecker.Callback { public UpdateCheckDialog(Context context) { mContext = context; - mUpdateChecker = new UpdateChecker(context, this); + mUpdateChecker = new UpdateChecker(context); } public void show() { + if (mProgressDialog != null) + throw new IllegalStateException(); mProgressDialog = new MaterialDialog.Builder(mContext) .content(mContext.getString(R.string.text_checking_update)) .progress(true, 0) .cancelable(false) .show(); - mUpdateChecker.check(); + mUpdateChecker.check(this); } @Override - public void onSuccess(UpdateChecker.CheckResult result) { + public void onSuccess(UpdateChecker.UpdateInfo result) { mProgressDialog.dismiss(); if (BuildConfig.VERSION_CODE >= result.versionCode) { Toast.makeText(mContext, R.string.text_is_latest_version, Toast.LENGTH_SHORT).show(); return; } - mResultDialog = buildDialog(result); - mResultDialog.show(); - String updateSite = result.downloads.get(0).name; - final String updateUrl = result.downloads.get(0).url; - mResultDialog = new MaterialDialog.Builder(mContext) - .title(mContext.getString(R.string.text_new_version) + " " + result.versionName) - .content(result.releaseNotes) - .positiveText(updateSite) - .onPositive(new MaterialDialog.SingleButtonCallback() { - @Override - public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { - IntentUtil.browse(mContext, updateUrl); - } - }) + new UpdateInfoDialogBuilder(mContext, result) .show(); - } - - private MaterialDialog buildDialog(final UpdateChecker.CheckResult result) { - String updateSite = result.downloads.get(0).name; - final String updateUrl = result.downloads.get(0).url; - MaterialDialog.Builder builder = new MaterialDialog.Builder(mContext) - .title(mContext.getString(R.string.text_new_version) + " " + result.versionName) - .content(result.releaseNotes) - .positiveText(updateSite) - .onPositive(new MaterialDialog.SingleButtonCallback() { - @Override - public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { - IntentUtil.browse(mContext, updateUrl); - } - }); - if (result.downloads.size() > 1) { - builder.negativeText(result.downloads.get(1).name) - .onNegative(new MaterialDialog.SingleButtonCallback() { - @Override - public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { - IntentUtil.browse(mContext, result.downloads.get(1).url); - } - }); - } - return builder.build(); + clear(); } @Override public void onError(Exception exception) { mProgressDialog.dismiss(); Toast.makeText(mContext, R.string.text_check_update_error, Toast.LENGTH_SHORT).show(); + clear(); } + + private void clear() { + mProgressDialog = null; + mContext = null; + } + } diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/update/UpdateInfoDialogBuilder.java b/app/src/main/java/com/stardust/scriptdroid/ui/update/UpdateInfoDialogBuilder.java new file mode 100644 index 00000000..c02b57de --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/ui/update/UpdateInfoDialogBuilder.java @@ -0,0 +1,146 @@ +package com.stardust.scriptdroid.ui.update; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.support.annotation.NonNull; +import android.text.TextUtils; +import android.view.View; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; + +import com.afollestad.materialdialogs.MaterialDialog; +import com.stardust.scriptdroid.BuildConfig; +import com.stardust.scriptdroid.R; +import com.stardust.scriptdroid.scripts.StorageScriptProvider; +import com.stardust.scriptdroid.tool.IntentTool; +import com.stardust.scriptdroid.tool.UpdateChecker; +import com.stardust.util.DownloadTask; +import com.stardust.util.IntentUtil; +import com.stardust.widget.CommonMarkdownView; + +/** + * Created by Stardust on 2017/4/9. + */ + +public class UpdateInfoDialogBuilder extends MaterialDialog.Builder { + + private static final String KEY_DO_NOT_ASK_AGAIN_FOR_VERSION = "I cannot forget you...cannot help missing you..."; + private View mView; + private SharedPreferences mSharedPreferences; + private UpdateChecker.UpdateInfo mUpdateInfo; + + public UpdateInfoDialogBuilder(@NonNull Context context, UpdateChecker.UpdateInfo info) { + super(context); + mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + updateInfo(info); + } + + public UpdateInfoDialogBuilder updateInfo(UpdateChecker.UpdateInfo info) { + mUpdateInfo = info; + mView = View.inflate(context, R.layout.dialog_update_info, null); + setReleaseNotes(mView, info); + setCurrentVersionIssues(mView, info); + setUpdateDownloadButtons(mView, info); + title(context.getString(R.string.text_new_version) + " " + info.versionName); + customView(mView, false); + return this; + } + + public UpdateInfoDialogBuilder showDoNotAskAgain() { + mView.findViewById(R.id.do_not_ask_again_container).setVisibility(View.VISIBLE); + CheckBox checkBox = (CheckBox) mView.findViewById(R.id.do_not_ask_again); + checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + mSharedPreferences.edit().putBoolean(KEY_DO_NOT_ASK_AGAIN_FOR_VERSION + mUpdateInfo.versionCode, isChecked).apply(); + } + }); + return this; + } + + @Override + public MaterialDialog show() { + if (mSharedPreferences.getBoolean(KEY_DO_NOT_ASK_AGAIN_FOR_VERSION + mUpdateInfo.versionCode, false)) { + return null; + } + return super.show(); + } + + private void setCurrentVersionIssues(View view, UpdateChecker.UpdateInfo info) { + TextView issues = (TextView) view.findViewById(R.id.issues); + UpdateChecker.OldVersion currentVersion = info.getOldVersion(BuildConfig.VERSION_CODE); + if (currentVersion == null) { + issues.setVisibility(View.GONE); + } else { + issues.setText(currentVersion.issues); + } + } + + private void setUpdateDownloadButtons(View view, UpdateChecker.UpdateInfo info) { + LinearLayout downloads = (LinearLayout) view.findViewById(R.id.downloads); + setDirectlyDownloadButton(downloads, info); + for (final UpdateChecker.Download download : info.downloads) { + Button button = (Button) View.inflate(getContext(), R.layout.dialog_update_info_btn, null); + button.setText(download.name); + downloads.addView(button); + button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + IntentTool.browse(v.getContext(), download.url); + } + }); + } + } + + private void setDirectlyDownloadButton(LinearLayout container, final UpdateChecker.UpdateInfo info) { + if (TextUtils.isEmpty(info.downloadUrl)) { + return; + } + Button button = (Button) View.inflate(getContext(), R.layout.dialog_update_info_btn, null); + button.setText(R.string.text_directly_download); + button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + directlyDownload(info.downloadUrl); + } + }); + container.addView(button); + } + + private void directlyDownload(String downloadUrl) { + final MaterialDialog dialog = new MaterialDialog.Builder(getContext()) + .title(R.string.text_downloading) + .progress(false, 100) + .show(); + final String path = StorageScriptProvider.DEFAULT_DIRECTORY_PATH + "AutoJs.apk"; + final DownloadTask task = new DownloadTask() { + @Override + protected void onProgressUpdate(Integer... values) { + super.onProgressUpdate(values); + dialog.setProgress(values[0]); + } + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + dialog.dismiss(); + if (!result) { + Toast.makeText(getContext(), R.string.text_download_failed, Toast.LENGTH_SHORT).show(); + } else { + IntentUtil.installApk(getContext(), path); + } + } + }; + task.execute(downloadUrl, path); + } + + private void setReleaseNotes(View view, UpdateChecker.UpdateInfo info) { + CommonMarkdownView markdownView = (CommonMarkdownView) view.findViewById(R.id.release_notes); + markdownView.loadMarkdown(info.releaseNotes); + } +} diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/update/VersionGuard.java b/app/src/main/java/com/stardust/scriptdroid/ui/update/VersionGuard.java new file mode 100644 index 00000000..5eb90324 --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/ui/update/VersionGuard.java @@ -0,0 +1,94 @@ +package com.stardust.scriptdroid.ui.update; + +import android.app.Activity; +import android.content.DialogInterface; +import android.support.annotation.NonNull; + +import com.afollestad.materialdialogs.DialogAction; +import com.afollestad.materialdialogs.MaterialDialog; +import com.stardust.scriptdroid.R; +import com.stardust.scriptdroid.tool.UpdateChecker; +import com.stardust.scriptdroid.tool.VersionInfo; + +/** + * Created by Stardust on 2017/4/12. + */ + +public class VersionGuard { + + + private Activity mActivity; + private MaterialDialog mDeprecatedDialog; + private VersionInfo mVersionInfo = VersionInfo.getInstance(); + + public VersionGuard(Activity activity) { + mActivity = activity; + } + + public void checkDeprecateAndUpdate() { + mVersionInfo.readDeprecatedFromPrefIfNeeded(mActivity); + if (mVersionInfo.isCurrentVersionDeprecated()) { + showDeprecatedDialogIfNeeded(); + } else { + checkUpdateIfNeeded(); + } + } + + private void checkUpdateIfNeeded() { + if (mVersionInfo.getUpdateInfo() == null) { + mVersionInfo.setOnReceiveUpdateResultCallback(new VersionInfo.OnReceiveUpdateResultCallback() { + @Override + public void onReceive(UpdateChecker.UpdateInfo info, boolean isCurrentVersionDeprecated) { + mVersionInfo.setOnReceiveUpdateResultCallback(null); + if (isCurrentVersionDeprecated) { + showDeprecatedDialogIfNeeded(); + } else { + showUpdateInfo(info); + } + } + }); + mVersionInfo.checkUpdateIfNeeded(mActivity); + } + } + + private void showUpdateInfo(UpdateChecker.UpdateInfo info) { + new UpdateInfoDialogBuilder(mActivity, info) + .showDoNotAskAgain() + .show(); + } + + private void showDeprecatedDialogIfNeeded() { + if (mDeprecatedDialog != null && mDeprecatedDialog.isShowing()) + return; + String content = mActivity.getString(R.string.warning_version_too_old); + String issues = mVersionInfo.getCurrentVersionIssues(); + if (issues != null) { + content += "\n" + issues; + } + mDeprecatedDialog = new MaterialDialog.Builder(mActivity) + .title(R.string.text_version_too_old) + .content(content) + .positiveText(R.string.text_update) + .negativeText(R.string.text_exit) + .cancelable(false) + .autoDismiss(false) + .onAny(new MaterialDialog.SingleButtonCallback() { + @Override + public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { + if (which == DialogAction.POSITIVE) { + new UpdateCheckDialog(mActivity) + .show(); + } else { + mActivity.finish(); + } + } + }) + .dismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + mDeprecatedDialog = null; + } + }) + .show(); + } +} diff --git a/app/src/main/java/com/stardust/view/ResizableFloaty.java b/app/src/main/java/com/stardust/view/ResizableFloaty.java deleted file mode 100644 index fa45f38d..00000000 --- a/app/src/main/java/com/stardust/view/ResizableFloaty.java +++ /dev/null @@ -1,410 +0,0 @@ -package com.stardust.view; - -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.graphics.PixelFormat; -import android.os.IBinder; -import android.support.annotation.Nullable; -import android.support.v4.view.GestureDetectorCompat; -import android.util.DisplayMetrics; -import android.view.GestureDetector; -import android.view.Gravity; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.RelativeLayout; - -import com.stardust.scriptdroid.R; -import com.stardust.util.ViewUtil; -import com.stardust.widget.ViewSwitcher; - -/** - * Created by Stardust on 2017/3/11. - */ - -public class ResizableFloaty { - - private static final String TAG = "ResizableFloaty"; - - private View mCollapsedView, mExpandedView; - private static ResizableFloaty floaty; - - public ResizableFloaty(View collapsedView, View expandedView) { - mExpandedView = expandedView; - mCollapsedView = collapsedView; - } - - public static void startService(Context context, View collapsedView, View expandedView) { - floaty = new ResizableFloaty(collapsedView, expandedView); - context.startService(new Intent(context, FloatingWindowService.class)); - } - - interface WindowBridge { - int getX(); - - int getY(); - - void updatePosition(int x, int y); - - int getWidth(); - - int getHeight(); - - void updateMeasure(int width, int height); - - int getScreenWidth(); - - int getScreenHeight(); - } - - public static class FloatingWindowService extends Service { - - private WindowManager mWindowManager; - private WindowManager.LayoutParams mWindowLayoutParams; - private RelativeLayout mWindowView; - private ViewSwitcher mCollapseExpandViewSwitcher; - private View mResizer; - private ResizableFloaty mFloaty = floaty; - private ResizeGesture mResizeGesture; - private DragGesture mDragGesture; - private ViewStack mViewStack = new ViewStack(new ViewStack.CurrentViewSetter() { - @Override - public void setCurrentView(View v) { - mCollapseExpandViewSwitcher.setSecondView(v); - } - }); - - private WindowBridge mWindowBridge = new WindowBridge() { - DisplayMetrics mDisplayMetrics; - - @Override - public int getX() { - return mWindowLayoutParams.x; - } - - @Override - public int getY() { - return mWindowLayoutParams.y; - } - - @Override - public void updatePosition(int x, int y) { - mWindowLayoutParams.x = x; - mWindowLayoutParams.y = y; - mWindowManager.updateViewLayout(mWindowView, mWindowLayoutParams); - } - - @Override - public int getWidth() { - return mWindowView.getWidth(); - } - - @Override - public int getHeight() { - return mWindowView.getHeight(); - } - - @Override - public void updateMeasure(int width, int height) { - mWindowLayoutParams.width = width; - mWindowLayoutParams.height = height; - mWindowManager.updateViewLayout(mWindowView, mWindowLayoutParams); - } - - @Override - public int getScreenWidth() { - ensureDisplayMetrics(); - return mDisplayMetrics.widthPixels; - } - - @Override - public int getScreenHeight() { - ensureDisplayMetrics(); - return mDisplayMetrics.heightPixels; - } - - private void ensureDisplayMetrics() { - if (mDisplayMetrics == null) { - mDisplayMetrics = new DisplayMetrics(); - mWindowManager.getDefaultDisplay().getMetrics(mDisplayMetrics); - } - } - }; - - @Nullable - @Override - public IBinder onBind(Intent intent) { - return null; - } - - @Override - public void onCreate() { - super.onCreate(); - mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE); - mWindowLayoutParams = createWindowLayoutParams(); - initWindowView(); - initGesture(); - setUpListeners(); - } - - private void setUpListeners() { - mDragGesture.setOnDraggedViewClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - enableWindowFocus(); - expand(); - } - }); - mWindowView.setOnKeyListener(new View.OnKeyListener() { - @Override - public boolean onKey(View v, int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) { - onBackPressed(); - return true; - } - if (keyCode == KeyEvent.KEYCODE_HOME) { - onHomePressed(); - return true; - } - return false; - } - - - }); - } - - private void expand() { - mCollapseExpandViewSwitcher.showSecond(); - mResizeGesture.setResizeEnabled(true); - mDragGesture.setKeepToSide(false); - } - - private void onBackPressed() { - if (mViewStack.canGoBack()) { - mViewStack.goBack(); - } else { - collapse(); - } - } - - private void onHomePressed() { - mViewStack.goBackToFirst(); - collapse(); - } - - private void collapse() { - mCollapseExpandViewSwitcher.showFirst(); - disableWindowFocus(); - mResizeGesture.setResizeEnabled(false); - mDragGesture.setKeepToSide(true); - } - - private void disableWindowFocus() { - mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; - mWindowManager.updateViewLayout(mWindowView, mWindowLayoutParams); - } - - private void initGesture() { - mResizeGesture = ResizeGesture.enableResize(mResizer, mWindowBridge); - mDragGesture = DragGesture.enableDrag(mWindowView, mWindowBridge); - mResizeGesture.setResizeEnabled(false); - mDragGesture.setKeepToSide(true); - } - - private void enableWindowFocus() { - mWindowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; - mWindowManager.updateViewLayout(mWindowView, mWindowLayoutParams); - mWindowView.requestFocus(); - } - - private void initWindowView() { - mWindowView = (RelativeLayout) View.inflate(getApplicationContext(), R.layout.resizable_floaty_container, null); - mWindowView.setFocusableInTouchMode(true); - mCollapseExpandViewSwitcher = (ViewSwitcher) mWindowView.findViewById(R.id.container); - mResizer = mWindowView.findViewById(R.id.resizer); - ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - mCollapseExpandViewSwitcher.addView(floaty.mCollapsedView, params); - mCollapseExpandViewSwitcher.addView(floaty.mExpandedView, params); - mViewStack.setRootView(floaty.mExpandedView); - mWindowManager.addView(mWindowView, mWindowLayoutParams); - } - - private WindowManager.LayoutParams createWindowLayoutParams() { - WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams( - WindowManager.LayoutParams.WRAP_CONTENT, - WindowManager.LayoutParams.WRAP_CONTENT, - WindowManager.LayoutParams.TYPE_PHONE, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, - PixelFormat.TRANSLUCENT); - layoutParams.gravity = Gravity.TOP | Gravity.START; - return layoutParams; - } - } - - public static class DragGesture extends GestureDetector.SimpleOnGestureListener { - - public static DragGesture enableDrag(final View view, WindowBridge bridge) { - final DragGesture gestureListener = new DragGesture(bridge, view) { - @Override - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - view.setAlpha(0.8f); - return super.onScroll(e1, e2, distanceX, distanceY); - } - - }; - final GestureDetectorCompat gestureDetector = new GestureDetectorCompat(view.getContext(), gestureListener); - view.setOnTouchListener(new View.OnTouchListener() { - @Override - public boolean onTouch(View v, MotionEvent event) { - gestureDetector.onTouchEvent(event); - if (event.getAction() == MotionEvent.ACTION_UP) { - view.setAlpha(1.0f); - if (!gestureListener.mFlung && gestureListener.isKeepToSide()) { - gestureListener.keepToSide(); - } - } - return true; - } - }); - return gestureListener; - } - - private WindowBridge mWindowBridge; - private boolean mKeepToSide; - private View.OnClickListener mOnClickListener; - private View mView; - - private int initialX; - private int initialY; - private float initialTouchX; - private float initialTouchY; - - private boolean mFlung = false; - - public DragGesture(WindowBridge windowBridge, View view) { - mWindowBridge = windowBridge; - mView = view; - } - - public void setKeepToSide(boolean keepToSide) { - mKeepToSide = keepToSide; - } - - public boolean isKeepToSide() { - return mKeepToSide; - } - - @Override - public boolean onDown(MotionEvent event) { - initialX = mWindowBridge.getX(); - initialY = mWindowBridge.getY(); - initialTouchX = event.getRawX(); - initialTouchY = event.getRawY(); - mFlung = false; - return false; - } - - @Override - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - mWindowBridge.updatePosition(initialX + (int) ((e2.getRawX() - initialTouchX)), - initialY + (int) ((e2.getRawY() - initialTouchY))); - return false; - } - - @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - mFlung = true; - if (mKeepToSide) - keepToSide(); - return false; - } - - public void keepToSide() { - int newX = mWindowBridge.getX(); - if (newX > mWindowBridge.getScreenWidth() / 2) - mWindowBridge.updatePosition(mWindowBridge.getScreenWidth(), mWindowBridge.getY()); - else - mWindowBridge.updatePosition(0, mWindowBridge.getY()); - } - - @Override - public boolean onSingleTapConfirmed(MotionEvent e) { - if (mOnClickListener != null) - mOnClickListener.onClick(mView); - return super.onSingleTapConfirmed(e); - } - - public void setOnDraggedViewClickListener(View.OnClickListener onClickListener) { - mOnClickListener = onClickListener; - } - } - - public static class ResizeGesture extends GestureDetector.SimpleOnGestureListener { - - public static ResizeGesture enableResize(View resizer, WindowBridge windowBridge) { - ResizeGesture resizeGesture = new ResizeGesture(windowBridge, resizer); - final GestureDetector detector = new GestureDetector(resizer.getContext(), resizeGesture); - resizer.setOnTouchListener(new View.OnTouchListener() { - @Override - public boolean onTouch(View v, MotionEvent event) { - detector.onTouchEvent(event); - return true; - } - }); - return resizeGesture; - } - - private WindowBridge mWindowBridge; - private float initialTouchX; - private float initialTouchY; - private int mInitialWidth, mInitialHeight; - private View mResizerView; - private int mMinHeight = 200, mMinWidth = 200; - private final int mStatusBarHeight; - - - public ResizeGesture(WindowBridge windowBridge, View resizerView) { - mWindowBridge = windowBridge; - mResizerView = resizerView; - mStatusBarHeight = ViewUtil.getStatusBarHeight(resizerView.getContext()); - } - - public void setMinHeight(int minHeight) { - mMinHeight = minHeight; - } - - public void setMinWidth(int minWidth) { - mMinWidth = minWidth; - } - - public void setResizeEnabled(boolean enabled) { - mResizerView.setVisibility(enabled ? View.VISIBLE : View.GONE); - } - - @Override - public boolean onDown(MotionEvent event) { - initialTouchX = event.getRawX(); - initialTouchY = event.getRawY(); - mInitialWidth = mWindowBridge.getWidth(); - mInitialHeight = mWindowBridge.getHeight(); - return false; - } - - @Override - public boolean onScroll(MotionEvent e1, final MotionEvent e2, float distanceX, float distanceY) { - int newWidth = mInitialWidth + (int) ((e2.getRawX() - initialTouchX)); - int newHeight = mInitialHeight + (int) ((e2.getRawY() - initialTouchY)); - newWidth = Math.max(mMinWidth, newWidth); - newHeight = Math.max(mMinHeight, newHeight); - newWidth = Math.min(mWindowBridge.getScreenWidth() - mWindowBridge.getX() - mResizerView.getWidth(), newWidth); - newHeight = Math.min(mWindowBridge.getScreenHeight() - mWindowBridge.getY() - mResizerView.getHeight() - mStatusBarHeight, newHeight); - mWindowBridge.updateMeasure(newWidth, newHeight); - return true; - } - } - - -} diff --git a/app/src/main/java/com/stardust/widget/ViewSwitcher.java b/app/src/main/java/com/stardust/widget/ViewSwitcher.java deleted file mode 100644 index aa99e07d..00000000 --- a/app/src/main/java/com/stardust/widget/ViewSwitcher.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.stardust.widget; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; - -/** - * Created by Stardust on 2017/3/11. - */ - -public class ViewSwitcher extends android.widget.ViewSwitcher { - - private View mCurrentView; - - public ViewSwitcher(Context context) { - super(context); - } - - public ViewSwitcher(Context context, View first, View second) { - super(context); - addView(first); - addView(second); - } - - public ViewSwitcher(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public View getCurrentView() { - return mCurrentView; - } - - public int getCurrentViewIndex() { - return mCurrentView == getChildAt(0) ? 0 : 1; - } - - public void showFirst() { - ensureCurrentView(); - if (mCurrentView != getChildAt(0)) { - showPrevious(); - mCurrentView = getChildAt(0); - } - } - - private void ensureCurrentView() { - if (mCurrentView == null) { - mCurrentView = getChildAt(0); - } - } - - public void showSecond() { - ensureCurrentView(); - if (mCurrentView != getChildAt(1)) { - showNext(); - mCurrentView = getChildAt(1); - } - } - - public void setSecondView(View v) { - removeViewAt(1); - addView(v); - } -} diff --git a/app/src/main/res/drawable-hdpi/ic_close_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_close_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..ceb1a1eebf2b2cc9a008f42010e144f4dab968de GIT binary patch literal 221 zcmV<303!d1P)og+*{ z>6z1@lfD*AYSPav7dXZl^}i`LqQ=|5D`%^h+bqMQ6`FBL^Ke=BM1TU^ANt|1j)vqn+8o| z7aDLk^=z7%;+g5LKb`vO>wlDUIzuVow9dGES~d;5!^Whlx!?oV2C?V`_~B+%bQ{n` zqDO#M3U~sT)kHV!!7EKDYOKJ{wLgij*n*GFNMg?V0!~~iMPuKvB63ci;}UqQ&Pat!bplm$Iy#<(NE#Oh$M|&M@Ikv N002ovPDHLkV1gb+pJxC7 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_close_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_close_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..af7f8288da6854204dcc4e6678b9053cd72032c4 GIT binary patch literal 175 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_+iGEW!B5R21KC-3Avpdi4K$<+1X zay^SH!@6{thMdKZi+L1hS^BMZUAdKCT}(HRw{NeWg~AlBHtr3jj>2B5kK^|J))MUC zlz7ydwCsb8xn}W?^r?}5wtsjdmvX{3=JbT!J9sKPrYmk={m|7^>A|Z*7j^D$`>}ry Zqi^Z9GQN4ct^!@a;OXk;vd$@?2>?I&LQeny literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_settings_ethernet_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_settings_ethernet_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..40430c1312dfe4e6a9a3564c7419ec298bd9d63a GIT binary patch literal 269 zcmV+o0rLKdP)0nRGMSgRHH`D20Ruc#L$(lmTdMen|@?j-yrZjMzE}Y`k zM7|kQ$91|=bBkvqN%VQZqrsGvQc_AuDJ4UmaId!~iTtgWFY?8xI&RXHnmarj&Nq`9 zI7t`I@M1h|Rux({Qo$BJEaxYOXqrg}bKbF^-&`ter-NOE^HkAqJ{2tJ%m4NXfG?$L Tz4X)p00000NkvXXu0mjfvg>e* literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_close_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_close_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..b7c7ffd0e795ba76ed3a062566c9016448795f7a GIT binary patch literal 257 zcmV+c0sj7pP) zOA5m<3jYBNi%A&<@ZO?$P9};P4Y5CjG5$M&YXI45J{s}~# zf|&?x1_gn4B7+hS@X!l}&!voFhmZP^sujifL@~PKMMM~{6xH}^g$q7WOzwCQ5vHTU z6`v~H@rlA8e;CUh_(b84zg=+ih`wG<)HiJjzSlQx5#CnjMR;A)R^jtaTa9;7rSy)7O%~`cm?ZjXImW?6TYRT<;U^@VKiSj`soFk00000NkvXX Hu0mjfhD&W| literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_settings_ethernet_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_settings_ethernet_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..b68e29233dea1c828a56ea804c9578511abb5c15 GIT binary patch literal 456 zcmV;(0XP1MP)Z$^*9e09bZVs^B5ZfCZb&0&6@GoR%tjM({|hIB-L7S*qa`!Aw!$g5a*f zT7g0L1?T?`9McB8v|g&|h}VF&sXqf_76G5_ltMx7ce2}8z;}BiFlqs?;;__JAUWz6 z;H{1Q2u%DNNcQ#NC;Dbj@Bict-T>Mr%a7^3n#Xj=1HrkXi*~)si*{9Tt!z|0?qXD& z6+F}`A9n9+4!a{(0Pk$-{kY!c3vsvMkH8EXyh{JTRaptiwcq@D(P;=ehLbl?EMKm*m7(@7_siS}}k zNFtnck{HL4mc!ypcyUoqJV~4rM^fS3C#iAnkyJU3biCmDy`VZLOv=LXld^HVq0F_+^MClRuC7($3pPY(gq((7hSj%L@m^c=$|3fN2mtTKSIUU zS`kEXX&+ieDrnQRTFo3+fsZj}CimVz7x$c9RtGYf`^y!N$K&yMJRVOp9AN`u=|&DC z(F)P-=O^#jZk4lx_bk)pB4`U=0GOxSIOh=G0GOlIHfSxcDYL?WQOCeeJL5wSGGG@2Kr;s@o?EU*i4U^|Owa&d!((UdrWII@ob z&0w6MECHI!h(m|a1oTD)by7rg7jf(;ngVUrf|{8{Ge**RP>KmO(=`1rD8)FM88Qg& z-NYv}&uFL|ltWXb1F4gHSweHWQqUlpfPN&T#^iJDY0z*n9MnsIW*Dh!kZ=P{iDNpb zjRKlHQum+)kI*cza}@^(J*(g=i+HHBbqzXG?V&Dv&+*Sb{?-~nDg`AOi?LbUzZjo} zUX02NGib(1InPP+63rBi)ehGVis8-T*av6|?2eir{SlkR&hXYc8E2qfMHA3t9uTp$ zuEd?N_bY4`dpqK6t$3SdiY9{qqpfAMSz>Cmwc>A<5v!mannl`8{z{*!{CYPqd@S{u zUN&6+_W(37d~grhT*o{_^TXibZlQo?)b7G+4X-Hki*ty91FTSHf|Om5u$j*QeB}UQ zXgBi!e5A$2rRP4DdBax3!fm`|i9LvUkoF+!5KGt7jo1gd*9Z9^ALN64kPq@fK4^9M Y7mq70q6I+&CIA2c07*qoM6N<$f)IH$*#H0l literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_close_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_close_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..39641921925f090e33df2767a4ee5e6d5911194f GIT binary patch literal 436 zcmV;l0ZaagP)32ETvhYyB39D^lU%DgQgv&#U%8l^-CA%qY@2qA=!B5=cm zUgehujQG*l{{`@rPr!f|fEk^>KI9WteE@ilV6HEl&_rJ@p_zU*;T}RirIc{5NocNLm*7JGdV(AM zYYDFOvk5~8{Z*t_>U=!XvoehUSEh=adIga45oe)B9kGgT}7UT3Cirmr(oHPv^YQ1-p=Hlh5u;xggf zX{&yw+El-OrrKQJRl@b7x{HLmNkj95`a#LLnW{Veb2C+!`ppt#r)=g4@?c8RY{yJj~W@W|h^mU4q`i)2y~Rw@J`jIh$1%|In!~{Tb{nMqaxlgb+dq eA%qY@zJ@mriVM?qfwL0;0000K_dOpA9yO8hV)pMTb$p3`nIF92u zj^j9v<2ZPT#U+q%6TN@Dbk36rI|+SGa1l@wnPnLXf-UOb(sn{pDp}0ITtZF{WD;T^GpQPvW(V=7~XYg z%hVn;;7DfB9z!hbakLrI2MO?a4ebpQmY#z~(MIs{59mevoKnQx^xqogk$eL>&{n8J zEUunav}^JSIEPLAwrD{tuayjbe{pu$fNFk1>qa8jjc7k|LYROOK1J(CBHAF@Bqc!u zJYGQ?BNUxtg2 z7YWu9X_((TgfY5F3k}t%e z`qd_{S^LqZ4PCRAF^l#vVlp7fn5B#E1aG1Z*uQ1~PblCpnY>1~xS$dQhpckn+a literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/circle_color_primary.xml b/app/src/main/res/drawable/circle_color_primary.xml new file mode 100644 index 00000000..50194cc2 --- /dev/null +++ b/app/src/main/res/drawable/circle_color_primary.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/floating_console_bg_bottom.xml b/app/src/main/res/drawable/floating_console_bg_bottom.xml new file mode 100644 index 00000000..3ff8cadf --- /dev/null +++ b/app/src/main/res/drawable/floating_console_bg_bottom.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/floating_console_bg_top.xml b/app/src/main/res/drawable/floating_console_bg_top.xml new file mode 100644 index 00000000..7aee34aa --- /dev/null +++ b/app/src/main/res/drawable/floating_console_bg_top.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_move_cursor.png b/app/src/main/res/drawable/ic_move_cursor.png new file mode 100644 index 0000000000000000000000000000000000000000..c558d96a52436113b90bfbc98206f65e9e20fb13 GIT binary patch literal 3853 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/ic_resizer.png b/app/src/main/res/drawable/ic_resizer.png index a2ed94d22a396bc313692b0ae19371510315af0b..9150e64439f9506325f0543bdff590e66c9de304 100644 GIT binary patch literal 20585 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 Aga7~l literal 9692 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 diff --git a/app/src/main/res/layout/dialog_update_info.xml b/app/src/main/res/layout/dialog_update_info.xml new file mode 100644 index 00000000..a34ff141 --- /dev/null +++ b/app/src/main/res/layout/dialog_update_info.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_update_info_btn.xml b/app/src/main/res/layout/dialog_update_info_btn.xml new file mode 100644 index 00000000..dbe47a12 --- /dev/null +++ b/app/src/main/res/layout/dialog_update_info_btn.xml @@ -0,0 +1,9 @@ + +