From 652893274402c48ea3d8aa775aeec2d4e320124f Mon Sep 17 00:00:00 2001 From: buddygr Date: Thu, 4 Jan 2024 10:10:17 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8D=87=E7=BA=A7targetSdk=E5=88=B033=20?= =?UTF-8?q?=E6=9B=B4=E6=96=B0SL4A=E5=BA=93=E5=88=B0=E6=9C=80=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/FUNDING.yml | 4 - .gitignore | 38 +- LICENSE | 404 +- README.md | 26 +- build.gradle | 96 +- doc/cn/APIs.rst | 4214 ++++++++--------- doc/en/APIs.rst | 4070 ++++++++-------- proguard-rules.pro | 34 +- src/main/AndroidManifest.xml | 35 +- .../org/qpython/qsl4a/QPyScriptService.java | 323 +- src/main/java/org/qpython/qsl4a/QSL4APP.java | 162 +- .../java/org/qpython/qsl4a/QSL4AScript.java | 64 +- .../org/qpython/qsl4a/codec/Base64Codec.java | 2100 ++++---- .../qpython/qsl4a/codec/BinaryDecoder.java | 84 +- .../qpython/qsl4a/codec/BinaryEncoder.java | 84 +- .../org/qpython/qsl4a/codec/CharEncoding.java | 250 +- .../java/org/qpython/qsl4a/codec/Decoder.java | 110 +- .../qpython/qsl4a/codec/DecoderException.java | 176 +- .../java/org/qpython/qsl4a/codec/Encoder.java | 92 +- .../qpython/qsl4a/codec/EncoderException.java | 180 +- .../qpython/qsl4a/codec/StreamGobbler.java | 620 +-- .../org/qpython/qsl4a/codec/StringUtils.java | 554 +-- .../org/qpython/qsl4a/qsl4a/Analytics.java | 180 +- .../org/qpython/qsl4a/qsl4a/AndroidProxy.java | 189 +- .../qpython/qsl4a/qsl4a/BaseApplication.java | 102 +- .../org/qpython/qsl4a/qsl4a/Constants.java | 202 +- .../java/org/qpython/qsl4a/qsl4a/Exec.java | 140 +- .../qsl4a/qsl4a/FeaturedInterpreters.java | 212 +- .../qsl4a/qsl4a/ForegroundService.java | 190 +- .../qpython/qsl4a/qsl4a/IntentBuilders.java | 314 +- .../java/org/qpython/qsl4a/qsl4a/LogUtil.java | 358 +- .../org/qpython/qsl4a/qsl4a/MainThread.java | 108 +- .../qsl4a/qsl4a/NotificationIdFactory.java | 74 +- .../java/org/qpython/qsl4a/qsl4a/Process.java | 410 +- .../qpython/qsl4a/qsl4a/ScriptLauncher.java | 200 +- .../qpython/qsl4a/qsl4a/ScriptProcess.java | 84 +- .../qsl4a/qsl4a/ScriptStorageAdapter.java | 272 +- .../org/qpython/qsl4a/qsl4a/SimpleServer.java | 518 +- .../qsl4a/qsl4a/SingleThreadExecutor.java | 67 +- .../org/qpython/qsl4a/qsl4a/StringUtils.java | 70 +- .../java/org/qpython/qsl4a/qsl4a/Version.java | 78 +- .../bluetooth/BluetoothDiscoveryHelper.java | 96 - .../org/qpython/qsl4a/qsl4a/event/Event.java | 114 +- .../qsl4a/qsl4a/exception/Sl4aException.java | 68 +- .../qsl4a/facade/ActivityResultFacade.java | 654 +-- .../qsl4a/qsl4a/facade/AndroidFacade.java | 1832 +++---- .../facade/ApplicationManagerFacade.java | 383 +- .../qsl4a/facade/BatteryManagerFacade.java | 448 +- .../qsl4a/qsl4a/facade/BluetoothFacade.java | 1289 ++--- .../qsl4a/qsl4a/facade/CameraFacade.java | 580 ++- .../qsl4a/qsl4a/facade/CipherFacade.java | 465 +- .../qsl4a/facade/CommonIntentsFacade.java | 597 ++- .../qsl4a/qsl4a/facade/ContactsFacade.java | 612 +-- .../qsl4a/facade/DocumentFileFacade.java | 614 +-- .../qsl4a/qsl4a/facade/EventFacade.java | 750 +-- .../qsl4a/qsl4a/facade/EventServer.java | 238 +- .../qsl4a/facade/FacadeConfiguration.java | 370 +- .../qsl4a/qsl4a/facade/FacadeManager.java | 158 +- .../qsl4a/facade/FacadeManagerFactory.java | 84 +- .../qsl4a/qsl4a/facade/FloatViewFacade.java | 415 +- .../qpython/qsl4a/qsl4a/facade/FtpFacade.java | 117 + .../qsl4a/qsl4a/facade/HarmonyOsFacade.java | 116 + .../qsl4a/qsl4a/facade/JpegProvider.java | 8 +- .../qsl4a/qsl4a/facade/LocationFacade.java | 490 +- .../qsl4a/qsl4a/facade/MediaPlayerFacade.java | 492 +- .../qsl4a/facade/MediaRecorderFacade.java | 884 ++-- .../qsl4a/qsl4a/facade/MjpegServer.java | 120 +- .../qsl4a/qsl4a/facade/PhoneFacade.java | 992 ++-- .../qsl4a/qsl4a/facade/PreferencesFacade.java | 212 +- .../qsl4a/facade/QPyInterfaceFacade.java | 423 +- .../qsl4a/facade/SensorManagerFacade.java | 1033 ++-- .../qsl4a/qsl4a/facade/SettingsFacade.java | 775 +-- .../qsl4a/facade/SignalStrengthFacade.java | 236 +- .../qpython/qsl4a/qsl4a/facade/SmsFacade.java | 492 +- .../qsl4a/facade/SpeechRecognitionFacade.java | 169 +- .../qsl4a/facade/TextToSpeechFacade.java | 165 +- .../qsl4a/facade/ToneGeneratorFacade.java | 186 +- .../qsl4a/qsl4a/facade/VideoFacade.java | 83 +- .../qsl4a/qsl4a/facade/WakeLockFacade.java | 244 +- .../qsl4a/qsl4a/facade/WebCamFacade.java | 796 ++-- .../qsl4a/qsl4a/facade/WifiFacade.java | 406 +- .../qsl4a/facade/ui/AlertDialogTask.java | 674 +-- .../qsl4a/facade/ui/DatePickerDialogTask.java | 188 +- .../qsl4a/qsl4a/facade/ui/DialogTask.java | 164 +- .../qsl4a/qsl4a/facade/ui/FullScreenTask.java | 983 ++-- .../qsl4a/facade/ui/ProgressDialogTask.java | 108 +- .../qsl4a/facade/ui/SeekBarDialogTask.java | 320 +- .../qsl4a/facade/ui/TimePickerDialogTask.java | 182 +- .../qsl4a/qsl4a/facade/ui/UiConfig.java | 115 +- .../qsl4a/qsl4a/facade/ui/UiFacade.java | 1616 +++---- .../qsl4a/qsl4a/facade/ui/ViewInflater.java | 2322 ++++----- .../qsl4a/facade/usb/USBHostSerialFacade.java | 2326 ++++----- .../qsl4a/facade/usb/deviceids/CH34xIds.java | 48 +- .../qsl4a/facade/usb/deviceids/CP210xIds.java | 310 +- .../qsl4a/facade/usb/deviceids/CP2130Ids.java | 32 +- .../facade/usb/deviceids/FTDISioIds.java | 1174 ++--- .../qsl4a/facade/usb/deviceids/Helpers.java | 54 +- .../qsl4a/facade/usb/deviceids/PL2303Ids.java | 158 +- .../qsl4a/facade/usb/deviceids/XdcVcpIds.java | 50 +- .../qsl4a/qsl4a/future/FutureActivity.java | 321 +- .../qsl4a/future/FutureActivityTask.java | 270 +- .../future/FutureActivityTaskExecutor.java | 125 +- .../qsl4a/qsl4a/future/FutureResult.java | 130 +- .../interpreter/ExternalClassLoader.java | 42 +- .../interpreter/InProcessInterpreter.java | 26 +- .../qsl4a/qsl4a/interpreter/Interpreter.java | 366 +- .../interpreter/InterpreterConfiguration.java | 646 +-- .../interpreter/InterpreterConstants.java | 122 +- .../interpreter/InterpreterDescriptor.java | 262 +- .../qsl4a/interpreter/InterpreterProcess.java | 250 +- .../interpreter/InterpreterPropertyNames.java | 122 +- .../qsl4a/interpreter/InterpreterUtils.java | 72 +- .../qsl4a/interpreter/MyInterpreter.java | 80 +- .../interpreter/html/HtmlActivityTask.java | 828 ++-- .../interpreter/html/HtmlInterpreter.java | 186 +- .../interpreter/shell/ShellInterpreter.java | 136 +- .../qsl4a/qsl4a/jsonrpc/JsonBuilder.java | 612 +-- .../qsl4a/qsl4a/jsonrpc/JsonRpcResult.java | 118 +- .../qsl4a/qsl4a/jsonrpc/JsonRpcServer.java | 240 +- .../qsl4a/qsl4a/jsonrpc/RpcReceiver.java | 35 +- .../qsl4a/jsonrpc/RpcReceiverManager.java | 194 +- .../jsonrpc/RpcReceiverManagerFactory.java | 50 +- .../qsl4a/language/BeanShellLanguage.java | 150 +- .../qsl4a/qsl4a/language/HtmlLanguage.java | 154 +- .../qsl4a/language/JavaScriptLanguage.java | 74 +- .../qsl4a/qsl4a/language/Language.java | 374 +- .../qsl4a/qsl4a/language/LuaLanguage.java | 70 +- .../qsl4a/qsl4a/language/PerlLanguage.java | 90 +- .../qsl4a/qsl4a/language/PhpLanguage.java | 102 +- .../qsl4a/qsl4a/language/PythonLanguage.java | 110 +- .../qsl4a/qsl4a/language/RubyLanguage.java | 70 +- .../qsl4a/qsl4a/language/ShellLanguage.java | 50 +- .../qsl4a/qsl4a/language/SleepLanguage.java | 82 +- .../qsl4a/language/SquirrelLanguage.java | 116 +- .../qsl4a/language/SupportedLanguages.java | 154 +- .../qsl4a/qsl4a/language/TclLanguage.java | 120 +- .../qpython/qsl4a/qsl4a/rpc/Converter.java | 58 +- .../qsl4a/qsl4a/rpc/MethodDescriptor.java | 1048 ++-- .../qsl4a/qsl4a/rpc/ParameterDescriptor.java | 84 +- .../java/org/qpython/qsl4a/qsl4a/rpc/Rpc.java | 86 +- .../qpython/qsl4a/qsl4a/rpc/RpcDefault.java | 78 +- .../qsl4a/qsl4a/rpc/RpcDeprecated.java | 74 +- .../org/qpython/qsl4a/qsl4a/rpc/RpcError.java | 51 +- .../qpython/qsl4a/qsl4a/rpc/RpcMinSdk.java | 64 +- .../org/qpython/qsl4a/qsl4a/rpc/RpcName.java | 72 +- .../qpython/qsl4a/qsl4a/rpc/RpcOptional.java | 76 +- .../qpython/qsl4a/qsl4a/rpc/RpcParameter.java | 90 +- .../qsl4a/qsl4a/rpc/RpcStartEvent.java | 72 +- .../qpython/qsl4a/qsl4a/rpc/RpcStopEvent.java | 72 +- .../EventGenerationControllingObserver.java | 176 +- .../qsl4a/qsl4a/trigger/ScriptTrigger.java | 84 +- .../qpython/qsl4a/qsl4a/trigger/Trigger.java | 98 +- .../qsl4a/trigger/TriggerRepository.java | 408 +- .../qpython/qsl4a/qsl4a/util/FileUtils.java | 407 +- .../qpython/qsl4a/qsl4a/util/HtmlUtil.java | 86 +- .../qsl4a/qsl4a/{ => util}/IoUtils.java | 120 +- .../qsl4a/qsl4a/util/PermissionUtil.java | 85 + .../qpython/qsl4a/qsl4a/util/SPFUtils.java | 322 +- .../qsl4a/qsl4a/util/VisibleForTesting.java | 54 +- .../res/drawable-xxhdpi/img_home_logo_3.png | Bin 937 -> 0 bytes src/main/res/drawable/toolbar_button_bg.xml | 30 +- src/main/res/layout/activity_run_splash.xml | 37 - src/main/res/layout/widget_run_control.xml | 26 - src/main/res/values-zh/strings.xml | 37 +- src/main/res/values/colors.xml | 30 +- src/main/res/values/strings.xml | 37 +- src/main/res/values/styles.xml | 23 +- tests/AndroidFacade-Clipboard.md | 64 +- 168 files changed, 29563 insertions(+), 27953 deletions(-) delete mode 100644 .github/FUNDING.yml delete mode 100644 src/main/java/org/qpython/qsl4a/qsl4a/bluetooth/BluetoothDiscoveryHelper.java create mode 100644 src/main/java/org/qpython/qsl4a/qsl4a/facade/FtpFacade.java create mode 100644 src/main/java/org/qpython/qsl4a/qsl4a/facade/HarmonyOsFacade.java rename src/main/java/org/qpython/qsl4a/qsl4a/{ => util}/IoUtils.java (92%) create mode 100644 src/main/java/org/qpython/qsl4a/qsl4a/util/PermissionUtil.java delete mode 100644 src/main/res/drawable-xxhdpi/img_home_logo_3.png delete mode 100644 src/main/res/layout/activity_run_splash.xml delete mode 100644 src/main/res/layout/widget_run_control.xml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 51f1a32..0000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,4 +0,0 @@ -# These are supported funding model platforms - -patreon: riverfor -custom: ["https://paypal.me/qpyriver"] diff --git a/.gitignore b/.gitignore index 94e6b2b..cfc8895 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,19 @@ -*.iml -.gradle -build -__MACOSX -.idea -keystore -.DS_Store -local.properties -*.apk -qbaselib.iml -.* -!/.gitignore -gradle-wrapper.properties -Android.mk -qpython/src/main/jni/python/libpython2.7.b -qpysdk/src/main/obj -values-fr -values-tr -build.gradle.new +*.iml +.gradle +build +__MACOSX +.idea +keystore +.DS_Store +local.properties +*.apk +qbaselib.iml +.* +!/.gitignore +gradle-wrapper.properties +Android.mk +qpython/src/main/jni/python/libpython2.7.b +qpysdk/src/main/obj +values-fr +values-tr +build.gradle.new diff --git a/LICENSE b/LICENSE index d645695..75b5248 100644 --- a/LICENSE +++ b/LICENSE @@ -1,202 +1,202 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index bc31610..f169ee1 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ -# About -QPysl4a is forked from https://www.github.com/damonkohler/sl4a/, and it provides the Android Layer APIs for script engine. - -It is not a standalone project, it will be included by QPython / QPython3 as a submodule. - -# APIs Documentation -If you are looking for a solution to drive Android with Python, then maybe QPysl4a is exactly what you need. With the help of QPysl4a, you could call many Android APIs with Python code. - -Here is what we have provided: [QPysl4a APIs](doc/en/APIs.rst), [Chinese version](doc/cn/APIs.rst) - - -## More information -Please visit [QPython Project Readme](https://github.com/qpython-android/qpython/blob/master/README.md) +# About +QPysl4a is forked from https://www.github.com/damonkohler/sl4a/, and it provides the Android Layer APIs for script engine. + +It is not a standalone project, it will be included by QPython / QPython3 as a submodule. + +# APIs Documentation +If you are looking for a solution to drive Android with Python, then maybe QPysl4a is exactly what you need. With the help of QPysl4a, you could call many Android APIs with Python code. + +Here is what we have provided: [QPysl4a APIs](doc/en/APIs.rst), [Chinese version](doc/cn/APIs.rst) + + +## More information +Please visit [QPython Project Readme](https://github.com/qpython-android/qpython/blob/master/README.md) diff --git a/build.gradle b/build.gradle index 68ae8ba..febb34d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,47 +1,49 @@ -apply plugin: 'com.android.library' - -android { - compileSdkVersion rootProject.ext.compileSdkVersion - buildToolsVersion rootProject.ext.buildToolsVersion - - defaultConfig { - minSdkVersion rootProject.ext.minSdkVersion - targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 1 - versionName "1.0" - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - -// defaultPublishConfig 'release' -// publishNonDefault true -// productFlavors { -// library { -// /* This strange empty flavour is actually needed -// for the below to be successful -// debugCompile project(path: ':common', configuration: "libraryDebug") -// releaseCompile project(path: ':common', configuration: "libraryRelease") -// */ -// } -// } - - dataBinding { - enabled = true - } - - lintOptions { - abortOnError false - } -} - -dependencies { - api rootProject.ext.libSupportDesign - //api rootProject.ext.libSupportV7 - api rootProject.ext.libGoogleGuava - api project(':qbaselib') -} - +apply plugin: 'com.android.library' + +android { + compileSdkVersion rootProject.ext.compileSdkVersion + + defaultConfig { + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + +// defaultPublishConfig 'release' +// publishNonDefault true +// productFlavors { +// library { +// /* This strange empty flavour is actually needed +// for the below to be successful +// debugCompile project(path: ':common', configuration: "libraryDebug") +// releaseCompile project(path: ':common', configuration: "libraryRelease") +// */ +// } +// } + + dataBinding { + enabled = true + } + + lintOptions { + abortOnError false + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + api rootProject.ext.libSupportDesign + //api rootProject.ext.libSupportV7 + api rootProject.ext.libGoogleGuava + implementation 'com.google.zxing:core:3.4.1' + api project(path: ':qbaselib') +} + diff --git a/doc/cn/APIs.rst b/doc/cn/APIs.rst index 577b8e1..438da63 100644 --- a/doc/cn/APIs.rst +++ b/doc/cn/APIs.rst @@ -1,2107 +1,2107 @@ -QPysl4a APIs -============ -安卓的脚本层(简称为SL4A,以前叫做安卓环境或者ASE)是一个在安卓设备上用不同编程语言创建和运行的开发库。在整合了它到QPython项目中之后,QPython团队开始开发和扩展维护它来适应QPython的需要,这就是QPysl4a项目。 - -.. toctree:: - :maxdepth: 2 - - -AndroidFacade -=============== - -Clipboard APIs ----------------- -.. py:function:: setClipboard(text) - - 向剪贴板写入信息 - - :param str text: text - -.. py:function:: getClipboard() - - 读取剪贴板中的文本 - - :return: The text in the clipboard - - -:: - - # python - - from androidhelper import Android - droid = Android() - - #setClipboard - droid.setClipboard("Hello World") - - #getClipboard - clipboard = droid.getClipboard().result - - -Intent & startActivity APIs ----------------------------------- -.. py:function:: makeIntent(action, uri, type, extras, categories, packagename, classname, flags) - - Starts an activity and returns the result - - :param str action: action - :param str uri(Optional): uri - :param str type(Optional): MIME type/subtype of the URI - :param object extras(Optional): a Map of extras to add to the Intent - :param list categories(Optional): a List of categories to add to the Intent - :param str packagename(Optional): name of package. If used, requires classname to be useful - :param str classname(Optional): name of class. If used, requires packagename to be useful - :param int flags(Optional): Intent flags - - :return: An object representing an Intent - - -:: - - sample code to show makeIntent - - -.. py:function:: getIntent() - - 获取启动脚本的 intent - -:: - - sample code to show getIntent - - -.. py:function:: startActivityForResult(action, uri, type, extras, packagename, classname) - - 运行一个 activity 并返回结果 - - :param str action: action - :param str uri(Optional): uri - :param str type(Optional): MIME type/subtype of the URI - :param object extras(Optional): a Map of extras to add to the Intent - :param str packagename(Optional): name of package. If used, requires classname to be useful - :param str classname(Optional): name of class. If used, requires packagename to be useful - - :return: A Map representation of the result Intent - - -:: - - sample code to show startActivityForResult - - -.. py:function:: startActivityForResultIntent(intent) - - Starts an activity and returns the result - - :param Intent intent: Intent in the format as returned from makeIntent - - :return: A Map representation of the result Intent - - -:: - - sample code to show startActivityForResultIntent - -.. py:function:: startActivityIntent(intent, wait) - - Starts an activity - - :param Intent intent: Intent in the format as returned from makeIntent - :param bool wait(Optional): block until the user exits the started activity - -:: - - sample code to show startActivityIntent - - -.. py:function:: startActivity(action, uri, type, extras, wait, packagename, classname) - - Starts an activity - - :param str action: action - :param str uri(Optional): uri - :param str type(Optional): MIME type/subtype of the URI - :param object extras(Optional): a Map of extras to add to the Intent - :param bool wait(Optional): block until the user exits the started activity - :param str packagename(Optional): name of package. If used, requires classname to be useful - :param str classname(Optional): name of class. If used, requires packagename to be useful - -:: - - sample code to show startActivityForResultIntent - - -SendBroadcast APIs -------------------- -.. py:function:: sendBroadcast(action, uri, type, extras, packagename, classname) - - Send a broadcast - - :param str action: action - :param str uri(Optional): uri - :param str type(Optional): MIME type/subtype of the URI - :param object extras(Optional): a Map of extras to add to the Intent - :param str packagename(Optional): name of package. If used, requires classname to be useful - :param str classname(Optional): name of class. If used, requires packagename to be useful - - -:: - - sample code to show sendBroadcast - -.. py:function:: sendBroadcastIntent(intent) - - Send a broadcast - - :param Intent intent: Intent in the format as returned from makeIntent - -:: - - sample code to show sendBroadcastIntent - - -Vibrate ----------- -.. py:function:: vibrate(duration) - - 使手机震动指定的时间 - - :param int duration: duration in milliseconds - -:: - - # python - - from androidhelper import Android - droid = Android() - droid.vibrate(1000) - - -NetworkStatus ---------------- -.. py:function:: getNetworkStatus() - - Returns the status of network connection - -:: - - sample code to show getNetworkStatus - -PackageVersion APIs ------------------------------- -.. py:function:: requiredVersion(requiredVersion) - - 检测当前 sl4a 的版本是否大于制定的版本 - - :param int requiredVersion: requiredVersion - - :return: true or false - - -.. py:function:: getPackageVersionCode(packageName) - - 返回指定包的版本号 - - :param str packageName: packageName - - :return: Package version code - -.. py:function:: getPackageVersion(packageName) - - Returns package version name - - :param str packageName: packageName - - :return: Package version name - - -:: - - sample code to show getPackageVersionCode & getPackageVersion - - -System APIs --------------------------------- -.. py:function:: getConstants(classname) - - Get list of constants (static final fields) for a class - - :param str classname: classname - - :return: list - -:: - - sample code to show getConstants - -.. py:function:: environment() - - A map of various useful environment details - - :return: environment map object includes id, display, offset, TZ, SDK, download, appcache, availblocks, blocksize, blockcount, sdcard - -:: - - sample code to show environment - -.. py:function:: log(message) - - Writes message to logcat - - :param str message: message - -:: - - sample code to show log - - -SendEmail ----------- -.. py:function:: sendEmail(to, subject, body, attachmentUri) - - Launches an activity that sends an e-mail message to a given recipient - - :param str to: A comma separated list of recipients - :param str subject: subject - :param str body: mail body - :param str attachmentUri(Optional): message - -:: - - sample code to show sendEmail - - -Toast, getInput, getPassword, notify APIs ------------------------------------------------- -.. py:function:: makeToast(message) - - Displays a short-duration Toast notification - - :param str message: message - -:: - - sample code to show makeToast - -.. py:function:: getInput(title, message) - - Queries the user for a text input - - :param str title: title of the input box - :param str message: message to display above the input box - -:: - - sample code to show getInput - -.. py:function:: getPassword(title, message) - - Queries the user for a password - - :param str title: title of the input box - :param str message: message to display above the input box - -:: - - sample code to show getPassword - -.. py:function:: notify(title, message, url) - - Displays a notification that will be canceled when the user clicks on it - - :param str title: title - :param str message: message - :param str url(optional): url - -:: - - import androidhelper - droid = androidhelper.Android() - droid.notify('Hello','QPython','http://qr.qpython.com.cn') # you could set the 3rd parameter None also - - - -ApplicationManagerFacade -========================= - -Manager APIs -------------- - -.. py:function:: getLaunchableApplications() - - Returns a list of all launchable application class names - - :return: map object - -:: - - sample code to show getLaunchableApplications - - -.. py:function:: launch(classname) - - Start activity with the given class name - - :param str classname: classname - -:: - - sample code to show launch - -.. py:function:: getRunningPackages() - - Returns a list of packages running activities or services - - :return: List of packages running activities - -:: - - sample code to show getRunningPackages - -.. py:function:: forceStopPackage(packageName) - - Force stops a package - - :param str packageName: packageName - -:: - - sample code to show forceStopPackage - - -CameraFacade -========================= - -.. py:function:: cameraCapturePicture(targetPath) - - Take a picture and save it to the specified path - - :return: A map of Booleans autoFocus and takePicture where True indicates success - -.. py:function:: cameraInteractiveCapturePicture(targetPath) - - Starts the image capture application to take a picture and saves it to the specified path - -CommonIntentsFacade -========================= - -Barcode ----------- -.. py:function:: scanBarcode() - - Starts the barcode scanner - - :return: A Map representation of the result Intent - -View APIs ----------- -.. py:function:: pick(uri) - - Display content to be picked by URI (e.g. contacts) - - :return: A map of result values - -.. py:function:: view(uri, type, extras) - - Start activity with view action by URI (i.e. browser, contacts, etc.) - -.. py:function:: viewMap(query) - - Opens a map search for query (e.g. pizza, 123 My Street) - -.. py:function:: viewContacts() - - Opens the list of contacts - -.. py:function:: viewHtml(path) - - Opens the browser to display a local HTML file - -.. py:function:: search(query) - - Starts a search for the given query - -ContactsFacade -========================= - -.. py:function:: pickContact() - - Displays a list of contacts to pick from - - :return: A map of result values - -.. py:function:: pickPhone() - - Displays a list of phone numbers to pick from - - :return: The selected phone number - -.. py:function:: contactsGetAttributes() - - Returns a List of all possible attributes for contacts - - :return: a List of contacts as Maps - -.. py:function:: contactsGetIds() - - Returns a List of all contact IDs - -.. py:function:: contactsGet(attributes) - - Returns a List of all contacts - -.. py:function:: contactsGetById(id) - - Returns contacts by ID - -.. py:function:: contactsGetCount() - - Returns the number of contacts - -.. py:function:: queryContent(uri, attributes, selection, selectionArgs, order) - - Content Resolver Query - - :return: result of query as Maps - -.. py:function:: queryAttributes(uri) - - Content Resolver Query Attributes - - :return: a list of available columns for a given content uri - -EventFacade -========================= - -.. py:function:: eventClearBuffer() - - Clears all events from the event buffer - -.. py:function:: eventRegisterForBroadcast(category, enqueue) - - Registers a listener for a new broadcast signal - -.. py:function:: eventUnregisterForBroadcast(category) - - Stop listening for a broadcast signal - -.. py:function:: eventGetBrodcastCategories() - - Lists all the broadcast signals we are listening for - -.. py:function:: eventPoll(number_of_events) - - Returns and removes the oldest n events (i.e. location or sensor update, etc.) from the event buffer - - :return: A List of Maps of event properties - -.. py:function:: eventWaitFor(eventName, timeout) - - Blocks until an event with the supplied name occurs. The returned event is not removed from the buffer - - :return: Map of event properties - -.. py:function:: eventWait(timeout) - - Blocks until an event occurs. The returned event is removed from the buffer - - :return: Map of event properties - -.. py:function:: eventPost(name, data, enqueue) - - Post an event to the event queue - -.. py:function:: rpcPostEvent(name, data) - - Post an event to the event queue - -.. py:function:: receiveEvent() - - Returns and removes the oldest event (i.e. location or sensor update, etc.) from the event buffer - - :return: Map of event properties - -.. py:function:: waitForEvent(eventName, timeout) - - Blocks until an event with the supplied name occurs. The returned event is not removed from the buffer - - :return: Map of event properties - -.. py:function:: startEventDispatcher(port) - - Opens up a socket where you can read for events posted - -.. py:function:: stopEventDispatcher() - - Stops the event server, you can't read in the port anymore - - -LocationFacade -========================= - -Providers APIs ------------------ - -.. py:function:: locationProviders() - - Returns availables providers on the phone - -.. py:function:: locationProviderEnabled(provider) - - Ask if provider is enabled - -Location APIs ------------------ -.. py:function:: startLocating(minDistance, minUpdateDistance) - - Starts collecting location data - -.. py:function:: readLocation() - - Returns the current location as indicated by all available providers - - :return: A map of location information by provider - -.. py:function:: stopLocating() - - Stops collecting location data - -.. py:function:: getLastKnownLocation() - - Returns the last known location of the device - - :return: A map of location information by provider - - -GEO ------------ -.. py:function:: geocode(latitude, longitude, maxResults) - - Returns a list of addresses for the given latitude and longitude - - :return: A list of addresses - - -*sample code* - -:: - - #qpy:console - import time - from androidhelper import Android - droid = Android() - droid.startLocating() - locproviders = droid.locationProviders().result - print("locproviders:"+repr(locproviders)) - - gpsprovider = droid.locationProviderEnabled('gps').result - print("gpsprovider:"+repr(gpsprovider)) - i = 0 - while i<100: - event = droid.eventWaitFor('location', 10000).result - print("Event:"+repr(event)) - - location = droid.readLocation().result - - if len(location) > 0: - print('Location:'+repr(location)) - - else: - location = droid.getLastKnownLocation().result - print("Last location:"+repr(lastloc)) - - loc = location['network'] # change to gps if you can get it - addr = droid.geocode(loc['latitude'], loc['longitude']).result - print("addr:"+repr(addr)) - - time.sleep(1) - i = i+1 - droid.stopLocating() - -PhoneFacade -========================= - -PhoneStat APIs ----------------- - -.. py:function:: startTrackingPhoneState() - - Starts tracking phone state - -.. py:function:: readPhoneState() - - Returns the current phone state and incoming number - - :return: A Map of "state" and "incomingNumber" - -.. py:function:: stopTrackingPhoneState() - - Stops tracking phone state - - -Call & Dia APIs ----------------- - -.. py:function:: phoneCall(uri) - - Calls a contact/phone number by URI - -.. py:function:: phoneCallNumber(number) - - Calls a phone number - -.. py:function:: phoneDial(uri) - - Dials a contact/phone number by URI - -.. py:function:: phoneDialNumber(number) - - Dials a phone number - - - -Get information APIs ------------------------- -.. py:function:: getCellLocation() - - Returns the current cell location - -.. py:function:: getNetworkOperator() - - Returns the numeric name (MCC+MNC) of current registered operator - -.. py:function:: getNetworkOperatorName() - - Returns the alphabetic name of current registered operator - -.. py:function:: getNetworkType() - - Returns a the radio technology (network type) currently in use on the device - -.. py:function:: getPhoneType() - - Returns the device phone type - -.. py:function:: getSimCountryIso() - - Returns the ISO country code equivalent for the SIM provider's country code - -.. py:function:: getSimOperator() - - Returns the MCC+MNC (mobile country code + mobile network code) of the provider of the SIM. 5 or 6 decimal digits - -.. py:function:: getSimOperatorName() - - Returns the Service Provider Name (SPN) - -.. py:function:: getSimSerialNumber() - - Returns the serial number of the SIM, if applicable. Return null if it is unavailable - -.. py:function:: getSimState() - - Returns the state of the device SIM card - -.. py:function:: getSubscriberId() - - Returns the unique subscriber ID, for example, the IMSI for a GSM phone. Return null if it is unavailable - -.. py:function:: getVoiceMailAlphaTag() - - Retrieves the alphabetic identifier associated with the voice mail number - -.. py:function:: getVoiceMailNumber() - - Returns the voice mail number. Return null if it is unavailable - -.. py:function:: checkNetworkRoaming() - - Returns true if the device is considered roaming on the current network, for GSM purposes - -.. py:function:: getDeviceId() - - Returns the unique device ID, for example, the IMEI for GSM and the MEID for CDMA phones. Return null if device ID is not available - -.. py:function:: getDeviceSoftwareVersion() - - Returns the software version number for the device, for example, the IMEI/SV for GSM phones. Return null if the software version is not available - -.. py:function:: getLine1Number() - - Returns the phone number string for line 1, for example, the MSISDN for a GSM phone. Return null if it is unavailable - -.. py:function:: getNeighboringCellInfo() - - Returns the neighboring cell information of the device - -MediaRecorderFacade -========================= - - -Audio --------- - -.. py:function:: recorderStartMicrophone(targetPath) - - Records audio from the microphone and saves it to the given location - -Video APIs ------------ - -.. py:function:: recorderStartVideo(targetPath, duration, videoSize) - - Records video from the camera and saves it to the given location. - Duration specifies the maximum duration of the recording session. - If duration is 0 this method will return and the recording will only be stopped - when recorderStop is called or when a scripts exits. - Otherwise it will block for the time period equal to the duration argument. - videoSize: 0=160x120, 1=320x240, 2=352x288, 3=640x480, 4=800x480. - - -.. py:function:: recorderCaptureVideo(targetPath, duration, recordAudio) - - Records video (and optionally audio) from the camera and saves it to the given location. - Duration specifies the maximum duration of the recording session. - If duration is not provided this method will return immediately and the recording will only be stopped - when recorderStop is called or when a scripts exits. - Otherwise it will block for the time period equal to the duration argument. - -.. py:function:: startInteractiveVideoRecording(path) - - Starts the video capture application to record a video and saves it to the specified path - - -Stop --------- -.. py:function:: recorderStop() - - Stops a previously started recording - - -SensorManagerFacade -========================= - -Start & Stop -------------- -.. py:function:: startSensingTimed(sensorNumber, delayTime) - - Starts recording sensor data to be available for polling - -.. py:function:: startSensingThreshold(ensorNumber, threshold, axis) - - Records to the Event Queue sensor data exceeding a chosen threshold - -.. py:function:: startSensing(sampleSize) - - Starts recording sensor data to be available for polling - -.. py:function:: stopSensing() - - Stops collecting sensor data - -Read data APIs ---------------- -.. py:function:: readSensors() - - Returns the most recently recorded sensor data - -.. py:function:: sensorsGetAccuracy() - - Returns the most recently received accuracy value - -.. py:function:: sensorsGetLight() - - Returns the most recently received light value - -.. py:function:: sensorsReadAccelerometer() - - Returns the most recently received accelerometer values - - :return: a List of Floats [(acceleration on the) X axis, Y axis, Z axis] - -.. py:function:: sensorsReadMagnetometer() - - Returns the most recently received magnetic field values - - :return: a List of Floats [(magnetic field value for) X axis, Y axis, Z axis] - -.. py:function:: sensorsReadOrientation() - - Returns the most recently received orientation values - - :return: a List of Doubles [azimuth, pitch, roll] - -*sample code* -:: - - Droid = androidhelper.Android() - Droid.startSensingTimed(1, 250) - sensor = Droid.sensorsReadOrientation().result - Droid.stopSensing() - - -SettingsFacade -========================= - -Screen ----------- - -.. py:function:: setScreenTimeout(value) - - Sets the screen timeout to this number of seconds - - :return: The original screen timeout - -.. py:function:: getScreenTimeout() - - Gets the screen timeout - - :return: the current screen timeout in seconds - -AirplanerMode ---------------------- - -.. py:function:: checkAirplaneMode() - - Checks the airplane mode setting - - :return: True if airplane mode is enabled - -.. py:function:: toggleAirplaneMode(enabled) - - Toggles airplane mode on and off - - :return: True if airplane mode is enabled - -Ringer Silent Mode ---------------------- - -.. py:function:: checkRingerSilentMode() - - Checks the ringer silent mode setting - - :return: True if ringer silent mode is enabled - -.. py:function:: toggleRingerSilentMode(enabled) - - Toggles ringer silent mode on and off - - :return: True if ringer silent mode is enabled - -Vibrate Mode ---------------------- - -.. py:function:: toggleVibrateMode(enabled) - - Toggles vibrate mode on and off. If ringer=true then set Ringer setting, else set Notification setting - - :return: True if vibrate mode is enabled - -.. py:function:: getVibrateMode(ringer) - - Checks Vibration setting. If ringer=true then query Ringer setting, else query Notification setting - - :return: True if vibrate mode is enabled - -Ringer & Media Volume ---------------------- - -.. py:function:: getMaxRingerVolume() - - Returns the maximum ringer volume - -.. py:function:: getRingerVolume() - - Returns the current ringer volume - -.. py:function:: setRingerVolume(volume) - - Sets the ringer volume - -.. py:function:: getMaxMediaVolume() - - Returns the maximum media volume - -.. py:function:: getMediaVolume() - - Returns the current media volume - -.. py:function:: setMediaVolume(volume) - - Sets the media volume - -Screen Brightness ---------------------- - -.. py:function:: getScreenBrightness() - - Returns the screen backlight brightness - - :return: the current screen brightness between 0 and 255 - -.. py:function:: setScreenBrightness(value) - - Sets the the screen backlight brightness - - :return: the original screen brightness - -.. py:function:: checkScreenOn() - - Checks if the screen is on or off (requires API level 7) - - :return: True if the screen is currently on - - -SmsFacade -========================= - -.. py:function:: smsSend(destinationAddress, text) - - 发送一条短信 - - :param str destinationAddress: typically a phone number - :param str text: - -.. py:function:: smsGetMessageCount(unreadOnly, folder) - - 返回短信息的数目 - - :param bool unreadOnly: typically a phone number - :param str folder(optional): default "inbox" - -.. py:function:: smsGetMessageIds(unreadOnly, folder) - - 返回所有信息的 id - - :param bool unreadOnly: typically a phone number - :param str folder(optional): default "inbox" - -.. py:function:: smsGetMessages(unreadOnly, folder, attributes) - - 返回所有信息的列表 - - :param bool unreadOnly: typically a phone number - :param str folder: default "inbox" - :param list attributes(optional): attributes - - :return: a List of messages as Maps - -.. py:function:: smsGetMessageById(id, attributes) - - Returns message attributes - - :param int id: message ID - :param list attributes(optional): attributes - - :return: a List of messages as Maps - -.. py:function:: smsGetAttributes() - - 返回指定信息的属性 - -.. py:function:: smsDeleteMessage(id) - - 删除指定的短信息 - - :param int id: message ID - - :return: True if the message was deleted - -.. py:function:: smsMarkMessageRead(ids, read) - - 将短信息标记为已读 - - :param list ids: List of message IDs to mark as read - :param bool read: true or false - - :return: number of messages marked read - -SpeechRecognitionFacade -========================= - -.. py:function:: recognizeSpeech(prompt, language, languageModel) - - Recognizes user's speech and returns the most likely result - - :param str prompt(optional): text prompt to show to the user when asking them to speak - :param str language(optional): language override to inform the recognizer that it should expect speech in a language different than the one set in the java.util.Locale.getDefault() - :param str languageModel(optional): informs the recognizer which speech model to prefer (see android.speech.RecognizeIntent) - - :return: An empty string in case the speech cannot be recongnized - - -ToneGeneratorFacade -========================= - -.. py:function:: generateDtmfTones(phoneNumber, toneDuration) - - Generate DTMF tones for the given phone number - - :param str phoneNumber: phone number - :param int toneDuration(optional): default 100, duration of each tone in milliseconds - - -WakeLockFacade -========================= - -.. py:function:: wakeLockAcquireFull() - - Acquires a full wake lock (CPU on, screen bright, keyboard bright) - -.. py:function:: wakeLockAcquirePartial() - - Acquires a partial wake lock (CPU on) - -.. py:function:: wakeLockAcquireBright() - - Acquires a bright wake lock (CPU on, screen bright) - -.. py:function:: wakeLockAcquireDim() - - Acquires a dim wake lock (CPU on, screen dim) - -.. py:function:: wakeLockRelease() - - Releases the wake lock - -WifiFacade -========================= - -.. py:function:: wifiGetScanResults() - - Returns the list of access points found during the most recent Wifi scan - -.. py:function:: wifiLockAcquireFull() - - Acquires a full Wifi lock - -.. py:function:: wifiLockAcquireScanOnly() - - Acquires a scan only Wifi lock - -.. py:function:: wifiLockRelease() - - Releases a previously acquired Wifi lock - -.. py:function:: wifiStartScan() - - Starts a scan for Wifi access points - - :return: True if the scan was initiated successfully - -.. py:function:: checkWifiState() - - Checks Wifi state - - :return: True if Wifi is enabled - -.. py:function:: toggleWifiState(enabled) - - Toggle Wifi on and off - - :param bool enabled(optional): enabled - - :return: True if Wifi is enabled - -.. py:function:: wifiDisconnect() - - Disconnects from the currently active access point - - :return: True if the operation succeeded - -.. py:function:: wifiGetConnectionInfo() - - Returns information about the currently active access point - -.. py:function:: wifiReassociate() - - Returns information about the currently active access point - - :return: True if the operation succeeded - -.. py:function:: wifiReconnect() - - Reconnects to the currently active access point - - :return: True if the operation succeeded - - -BatteryManagerFacade -========================= - -.. py:function:: readBatteryData() - - 读取电池记录数据 - -.. py:function:: batteryStartMonitoring() - - 开始监视电池状态 - -.. py:function:: batteryStopMonitoring() - - 停止监视电池状态 - -.. py:function:: batteryGetStatus() - - 返回充电状态信息 - - 1 - 未知; - 2 - 充电中; - 3 - 放电中; - 4 - 未充电; - 5 - 已充满 - -.. py:function:: batteryGetHealth() - - 查看电池健康状态 - - 1 - 未知; - 2 - 良好; - 3 - 过热; - 4 - 不可用; - 5 - 电量过饱; - 6 - 查询失败 - -.. py:function:: batteryGetPlugType() - - 返回充电状态信息 - - -1 - 未知 - 0 - 未插入电源; - 1 - 交流电源充电 - 2 - usb充电 - - -.. py:function:: batteryCheckPresent() - - 查看电池电量信息 - -.. py:function:: batteryGetLevel() - - 返回电池电量(百分比形式) - -.. py:function:: batteryGetVoltage() - - 返回电池的电压 - -.. py:function:: batteryGetTemperature() - - 返回电池温度 - -.. py:function:: batteryGetTechnology() - - 返回电池技术数据 - -:: - - # python - - import time - from androidhelper import Android - droid = Android() - droid.batteryStartMonitoring() - time.sleep(5) - bdata = droid.readBatteryData() - print(bdata.result) - - bstatus = droid.batteryGetStatus().result - bhealth = droid.batteryGetHealth().result - bplug = droid.batteryGetPlugType().result - bcheck = droid.batteryCheckPresent().result - blevel = droid.batteryGetLevel().result - bvoltage = droid.batteryGetVoltage().result - btemperature = droid.batteryGetTemperature().result - btechnology = droid.batteryGetTechnology().result - print({"status": bstatus, "health": bhealth, "plugtype": bplug, "checkpresent": bcheck, "level": blevel, "voltage": bvoltage, "temperature": btemperature, "technology": btechnology}) - - droid.batteryStopMonitoring() - -ActivityResultFacade -========================= - -.. py:function:: setResultBoolean(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - - -.. py:function:: setResultByte(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultShort(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultChar(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - - -.. py:function:: setResultInteger(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultLong(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultFloat(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultDouble(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultString(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultBooleanArray(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultByteArray(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultShortArray(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultCharArray(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultIntegerArray(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultLongArray(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultFloatArray(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultDoubleArray(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultStringArray(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultSerializable(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - - -MediaPlayerFacade -========================= - -Control ------------------ -.. py:function:: mediaPlay(url, tag, play) - - Open a media file - - :param str url: url of media resource - :param str tag(optional): string identifying resource (default=default) - :param bool play(optional): start playing immediately - - :return: true if play successful - -.. py:function:: mediaPlayPause(tag) - - pause playing media file - - :param str tag: string identifying resource (default=default) - - :return: true if successful - -.. py:function:: mediaPlayStart(tag) - - start playing media file - - :param str tag: string identifying resource (default=default) - - :return: true if successful - -.. py:function:: mediaPlayClose(tag) - - Close media file - - :param str tag: string identifying resource (default=default) - - :return: true if successful - -.. py:function:: mediaIsPlaying(tag) - - Checks if media file is playing - - :param str tag: string identifying resource (default=default) - - :return: true if successful - - -.. py:function:: mediaPlaySetLooping(enabled, tag) - - Set Looping - - :param bool enabled: default true - :param str tag: string identifying resource (default=default) - - :return: True if successful - -.. py:function:: mediaPlaySeek(msec, tag) - - Seek To Position - - :param int msec: default true - :param str tag: string identifying resource (default=default) - - :return: New Position (in ms) - -Get Information ------------------ -.. py:function:: mediaPlayInfo(tag) - - Information on current media - - :param str tag: string identifying resource (default=default) - - :return: Media Information - -.. py:function:: mediaPlayList() - - Lists currently loaded media - - :return: List of Media Tags - - -PreferencesFacade -========================= - -.. py:function:: prefGetValue(key, filename) - - Read a value from shared preferences - - :param str key: key - :param str filename(optional): Desired preferences file. If not defined, uses the default Shared Preferences. - - -.. py:function:: prefPutValue(key, value, filename) - - Write a value to shared preferences - - :param str key: key - :param str value: value - :param str filename(optional): Desired preferences file. If not defined, uses the default Shared Preferences. - -.. py:function:: prefGetAll(filename) - - Get list of Shared Preference Values - - :param str filename(optional): Desired preferences file. If not defined, uses the default Shared Preferences. - - -QPyInterfaceFacade -========================= - -.. py:function:: executeQPy(script) - - Execute a qpython script by absolute path - - :param str script: The absolute path of the qpython script - - :return: bool - - -TextToSpeechFacade -========================= - -.. py:function:: ttsSpeak(message) - - Speaks the provided message via TTS - - :param str message: message - -.. py:function:: ttsIsSpeaking() - - Returns True if speech is currently in progress - -EyesFreeFacade -========================= - -.. py:function:: ttsSpeak(message) - - Speaks the provided message via TTS - - :param str message: message - - -BluetoothFacade -========================= - -.. py:function:: bluetoothActiveConnections() - - Returns active Bluetooth connections - - -.. py:function:: bluetoothWriteBinary(base64, connID) - - Send bytes over the currently open Bluetooth connection - - :param str base64: A base64 encoded String of the bytes to be sent - :param str connID(optional): Connection id - -.. py:function:: bluetoothReadBinary(bufferSize, connID) - - Read up to bufferSize bytes and return a chunked, base64 encoded string - - :param int bufferSize: default 4096 - :param str connID(optional): Connection id - -.. py:function:: bluetoothConnect(uuid, address) - - Connect to a device over Bluetooth. Blocks until the connection is established or fails - - :param str uuid: The UUID passed here must match the UUID used by the server device - :param str address(optional): The user will be presented with a list of discovered devices to choose from if an address is not provided - - :return: True if the connection was established successfully - -.. py:function:: bluetoothAccept(uuid, timeout) - - Listens for and accepts a Bluetooth connection. Blocks until the connection is established or fails - - :param str uuid: The UUID passed here must match the UUID used by the server device - :param int timeout: How long to wait for a new connection, 0 is wait for ever (default=0) - -.. py:function:: bluetoothMakeDiscoverable(duration) - - Requests that the device be discoverable for Bluetooth connections - - :param int duration: period of time, in seconds, during which the device should be discoverable (default=300) - -.. py:function:: bluetoothWrite(ascii, connID) - - Sends ASCII characters over the currently open Bluetooth connection - - :param str ascii: text - :param str connID: Connection id - -.. py:function:: bluetoothReadReady(connID) - - Sends ASCII characters over the currently open Bluetooth connection - - :param str ascii: text - :param str connID: Connection id - -.. py:function:: bluetoothRead(bufferSize, connID) - - Read up to bufferSize ASCII characters - - :param int bufferSize: default=4096 - :param str connID(optional): Connection id - -.. py:function:: bluetoothReadLine(connID) - - Read the next line - - :param str connID(optional): Connection id - -.. py:function:: bluetoothGetRemoteDeviceName(address) - - Queries a remote device for it's name or null if it can't be resolved - - :param str address: Bluetooth Address For Target Device - -.. py:function:: bluetoothGetLocalName() - - Gets the Bluetooth Visible device name - -.. py:function:: bluetoothSetLocalName(name) - - Sets the Bluetooth Visible device name, returns True on success - - :param str name: New local name - -.. py:function:: bluetoothGetScanMode() - - Gets the scan mode for the local dongle. - Return values: - -1 when Bluetooth is disabled. - 0 if non discoverable and non connectable. - 1 connectable non discoverable. - 3 connectable and discoverable. - -.. py:function:: bluetoothGetConnectedDeviceName(connID) - - Returns the name of the connected device - - :param str connID: Connection id - -.. py:function:: checkBluetoothState() - - Checks Bluetooth state - - :return: True if Bluetooth is enabled - -.. py:function:: toggleBluetoothState(enabled, prompt) - - Toggle Bluetooth on and off - - :param bool enabled: - :param str prompt: Prompt the user to confirm changing the Bluetooth state, default=true - - :return: True if Bluetooth is enabled - -.. py:function:: bluetoothStop(connID) - - Stops Bluetooth connection - - :param str connID: Connection id - -.. py:function:: bluetoothGetLocalAddress() - - Returns the hardware address of the local Bluetooth adapter - -.. py:function:: bluetoothDiscoveryStart() - - Start the remote device discovery process - - :return: true on success, false on error - -.. py:function:: bluetoothDiscoveryCancel() - - Cancel the current device discovery process - - :return: true on success, false on error - -.. py:function:: bluetoothIsDiscovering() - - Return true if the local Bluetooth adapter is currently in the device discovery process - - -SignalStrengthFacade -========================= -.. py:function:: startTrackingSignalStrengths() - - Starts tracking signal strengths - -.. py:function:: readSignalStrengths() - - Returns the current signal strengths - - :return: A map of gsm_signal_strength - -.. py:function:: stopTrackingSignalStrengths() - - Stops tracking signal strength - - -WebCamFacade -========================= - -.. py:function:: webcamStart(resolutionLevel, jpegQuality, port) - - Starts an MJPEG stream and returns a Tuple of address and port for the stream - - :param int resolutionLevel: increasing this number provides higher resolution (default=0) - :param int jpegQuality: a number from 0-10 (default=20) - :param int port: If port is specified, the webcam service will bind to port, otherwise it will pick any available port (default=0) - -.. py:function:: webcamAdjustQuality(resolutionLevel, jpegQuality) - - Adjusts the quality of the webcam stream while it is running - - :param int resolutionLevel: increasing this number provides higher resolution (default=0) - :param int jpegQuality: a number from 0-10 (default=20) - -.. py:function:: cameraStartPreview(resolutionLevel, jpegQuality, filepath) - - Start Preview Mode. Throws 'preview' events - - :param int resolutionLevel: increasing this number provides higher resolution (default=0) - :param int jpegQuality: a number from 0-10 (default=20) - :param str filepath: Path to store jpeg files - - :return: True if successful - -.. py:function:: cameraStopPreview() - - Stop the preview mode - - -UiFacade -========================= - -Dialog --------- -.. py:function:: dialogCreateInput(title, message, defaultText, inputType) - - Create a text input dialog - - :param str title: title of the input box - :param str message: message to display above the input box - :param str defaultText(optional): text to insert into the input box - :param str inputType(optional): type of input data, ie number or text - -.. py:function:: dialogCreatePassword(title, message) - - Create a password input dialog - - :param str title: title of the input box - :param str message: message to display above the input box - -.. py:function:: dialogGetInput(title, message, defaultText) - - Create a password input dialog - - :param str title: title of the input box - :param str message: message to display above the input box - :param str defaultText(optional): text to insert into the input box - -.. py:function:: dialogGetPassword(title, message) - - Queries the user for a password - - :param str title: title of the password box - :param str message: message to display above the input box - -.. py:function:: dialogCreateSeekBar(start, maximum, title) - - Create seek bar dialog - - :param int start: default=50 - :param int maximum: default=100 - :param int title: title - -.. py:function:: dialogCreateTimePicker(hour, minute, is24hour) - - Create time picker dialog - - :param int hour: default=0 - :param int miute: default=0 - :param bool is24hour: default=false - -.. py:function:: dialogCreateDatePicker(year, month, day) - - Create date picker dialog - - :param int year: default=1970 - :param int month: default=1 - :param int day: default=1 - - -NFC -------------- -**Data structs** -*QPython NFC json result* -:: - - { - "role": , # could be self/master/slave - "stat": , # could be ok / fail / cancl - "message": - } - -**APIs** - -.. py:function:: dialogCreateNFCBeamMaster(title, message, inputType) - - Create a dialog where you could create a qpython beam master - - :param str title: title of the input box - :param str message: message to display above the input box - :param str inputType(optional): type of input data, ie number or text - -.. py:function:: NFCBeamMessage(content, title, message) - - Create a dialog where you could create a qpython beam master - - :param str content: message you want to sent - :param str title: title of the input box - :param str message: message to display above the input box - :param str inputType(optional): type of input data, ie number or text - -.. py:function:: dialogCreateNFCBeamSlave(title, message) - - Create a qpython beam slave - - :param str title: title of the input box - :param str message: message to display above the input box - -Progress --------------- -.. py:function:: dialogCreateSpinnerProgress(message, maximumProgress) - - Create a spinner progress dialog - - :param str message(optional): message - :param int maximunProgress(optional): dfault=100 - -.. py:function:: dialogSetCurrentProgress(current) - - Set progress dialog current value - - :param int current: current - -.. py:function:: dialogSetMaxProgress(max) - - Set progress dialog maximum value - - :param int max: max - - -.. py:function:: dialogCreateHorizontalProgress(title, message, maximumProgress) - - Create a horizontal progress dialog - - :param str title(optional): title - :param str message(optional): message - :param int maximunProgress(optional): dfault=100 - - -Alert ----------- -.. py:function:: dialogCreateAlert(title, message) - - Create alert dialog - - :param str title(optional): title - :param str message(optional): message - :param int maximunProgress(optional): dfault=100 - - -Dialog Control ---------------- -.. py:function:: dialogSetPositiveButtonText(text) - - Set alert dialog positive button text - - :param str text: text - -.. py:function:: dialogSetNegativeButtonText(text) - - Set alert dialog negative button text - - :param str text: text - -.. py:function:: dialogSetNeutralButtonText(text) - - Set alert dialog button text - - :param str text: text - -.. py:function:: dialogSetItems(items) - - Set alert dialog list items - - :param list items: items - -.. py:function:: dialogSetSingleChoiceItems(items, selected) - - Set alert dialog list items - - :param list items: items - :param int selected: selected item index (default=0) - -.. py:function:: dialogSetMultiChoiceItems(items, selected) - - Set dialog multiple choice items and selection - - :param list items: items - :param int selected: selected item index (default=0) - -.. py:function:: addContextMenuItem(label, event, eventData) - - Adds a new item to context menu - - :param str label: label for this menu item - :param str event: event that will be generated on menu item click - :param object eventData: event object - -.. py:function:: addOptionsMenuItem(label, event, eventData, iconName) - - Adds a new item to context menu - - :param str label: label for this menu item - :param str event: event that will be generated on menu item click - :param object eventData: event object - :param str iconName: Android system menu icon, see http://developer.android.com/reference/android/R.drawable.html - -.. py:function:: dialogGetResponse() - - Returns dialog response - -.. py:function:: dialogGetSelectedItems() - - This method provides list of items user selected - -.. py:function:: dialogDismiss() - - Dismiss dialog - -.. py:function:: dialogShow() - - Show dialog - - -Layout ---------- -.. py:function:: fullShow(layout) - - Show Full Screen - - :param string layout: String containing View layout - -.. py:function:: fullDismiss() - - Dismiss Full Screen - -.. py:function:: fullQuery() - - Get Fullscreen Properties - -.. py:function:: fullQueryDetail(id) - - Get fullscreen properties for a specific widget - - :param str id: id of layout widget - -.. py:function:: fullSetProperty(id) - - Set fullscreen widget property - - :param str id: id of layout widget - :param str property: name of property to set - :param str value: value to set property to - -.. py:function:: fullSetList(id, list) - - Attach a list to a fullscreen widget - - :param str id: id of layout widget - :param list list: List to set - -.. py:function:: fullKeyOverride(keycodes, enable) - - Override default key actions - - :param str keycodes: id of layout widget - :param bool enable: List to set (default=true) - - - -WebView ------------ -.. py:function:: webViewShow() - - Display a WebView with the given URL - - :param str url: url - :param bool wait(optional): block until the user exits the WebView - -USB Host Serial Facade -====================== - -*QPython 1.3.1+ and QPython3 1.0.3+ contains this feature* - -SL4A Facade for USB Serial devices by Android USB Host API. - - -It control the USB-Serial like devices -from Andoroid which has USB Host Controller . - -The sample -`demonstration is also available at youtube video `_ - - -Requirements -------------- -* Android device which has USB Host controller (and enabled in that firmware). -* Android 4.0 (API14) or later. -* USB Serial devices (see [Status](#Status)). -* USB Serial devices were not handled by Android kernel. - - > I heard some android phone handle USB Serial devices - > make /dev/ttyUSB0 in kernel level. - > In this case, Android does not be able to handle the device - > from OS level. - - please check Android Applications be able to grab the target USB Devices, - such as `USB Device Info `_. - -Status ---------------- -* probably work with USB CDC, like FTDI, Arduino or else. - -* 2012/09/10: work with 78K0F0730 device (new RL78) with Tragi BIOS board. - - `M78K0F0730 `_ - -* 2012/09/24: work with some pl2303 devcies. - -Author -------- -This facade developped by `Kuri65536 `_ -you can see the commit log in it. - - -APIs --------- -.. py:function:: usbserialGetDeviceList() - - Returns USB devices reported by USB Host API. - - :return: Returns "Map of id and string information Map - - -.. py:function:: usbserialDisconnect(connID) - - Disconnect all USB-device - - :param str connID: connection ID - -.. py:function:: usbserialActiveConnections() - - Returns active USB-device connections. - - :return: Returns "Active USB-device connections by Map UUID vs device-name." - - -.. py:function:: usbserialWriteBinary(base64, connID) - - Send bytes over the currently open USB Serial connection. - - :param str base64: - :param str connId: - -.. py:function:: usbserialReadBinary(bufferSize, connID) - - Read up to bufferSize bytes and return a chunked, base64 encoded string - - :param int bufferSize: - :param str connId: - -.. py:function:: usbserialConnect(hash, options) - - Connect to a device with USB-Host. request the connection and exit - - :param str hash: - :param str options: - - :return: Returns messages the request status - -.. py:function:: usbserialHostEnable() - - Requests that the host be enable for USB Serial connections. - - :return: True if the USB Device is accesible - -.. py:function:: usbserialWrite(String ascii, String connID) - - Sends ASCII characters over the currently open USB Serial connection - - :param str ascii: - :param str connID: - -.. py:function:: usbserialReadReady(connID) - - :param str connID: - - :return: True if the next read is guaranteed not to block - - -.. py:function:: usbserialRead(connID, bufferSize) - - Read up to bufferSize ASCII characters. - - :param str connID: - :param int bufferSize: - -.. py:function:: usbserialGetDeviceName(connID) - - Queries a remote device for it's name or null if it can't be resolved - - :param str connID: +QPysl4a APIs +============ +安卓的脚本层(简称为SL4A,以前叫做安卓环境或者ASE)是一个在安卓设备上用不同编程语言创建和运行的开发库。在整合了它到QPython项目中之后,QPython团队开始开发和扩展维护它来适应QPython的需要,这就是QPysl4a项目。 + +.. toctree:: + :maxdepth: 2 + + +AndroidFacade +=============== + +Clipboard APIs +---------------- +.. py:function:: setClipboard(text) + + 向剪贴板写入信息 + + :param str text: text + +.. py:function:: getClipboard() + + 读取剪贴板中的文本 + + :return: The text in the clipboard + + +:: + + # python + + from androidhelper import Android + droid = Android() + + #setClipboard + droid.setClipboard("Hello World") + + #getClipboard + clipboard = droid.getClipboard().result + + +Intent & startActivity APIs +---------------------------------- +.. py:function:: makeIntent(action, uri, type, extras, categories, packagename, classname, flags) + + Starts an activity and returns the result + + :param str action: action + :param str uri(Optional): uri + :param str type(Optional): MIME type/subtype of the URI + :param object extras(Optional): a Map of extras to add to the Intent + :param list categories(Optional): a List of categories to add to the Intent + :param str packagename(Optional): name of package. If used, requires classname to be useful + :param str classname(Optional): name of class. If used, requires packagename to be useful + :param int flags(Optional): Intent flags + + :return: An object representing an Intent + + +:: + + sample code to show makeIntent + + +.. py:function:: getIntent() + + 获取启动脚本的 intent + +:: + + sample code to show getIntent + + +.. py:function:: startActivityForResult(action, uri, type, extras, packagename, classname) + + 运行一个 activity 并返回结果 + + :param str action: action + :param str uri(Optional): uri + :param str type(Optional): MIME type/subtype of the URI + :param object extras(Optional): a Map of extras to add to the Intent + :param str packagename(Optional): name of package. If used, requires classname to be useful + :param str classname(Optional): name of class. If used, requires packagename to be useful + + :return: A Map representation of the result Intent + + +:: + + sample code to show startActivityForResult + + +.. py:function:: startActivityForResultIntent(intent) + + Starts an activity and returns the result + + :param Intent intent: Intent in the format as returned from makeIntent + + :return: A Map representation of the result Intent + + +:: + + sample code to show startActivityForResultIntent + +.. py:function:: startActivityIntent(intent, wait) + + Starts an activity + + :param Intent intent: Intent in the format as returned from makeIntent + :param bool wait(Optional): block until the user exits the started activity + +:: + + sample code to show startActivityIntent + + +.. py:function:: startActivity(action, uri, type, extras, wait, packagename, classname) + + Starts an activity + + :param str action: action + :param str uri(Optional): uri + :param str type(Optional): MIME type/subtype of the URI + :param object extras(Optional): a Map of extras to add to the Intent + :param bool wait(Optional): block until the user exits the started activity + :param str packagename(Optional): name of package. If used, requires classname to be useful + :param str classname(Optional): name of class. If used, requires packagename to be useful + +:: + + sample code to show startActivityForResultIntent + + +SendBroadcast APIs +------------------- +.. py:function:: sendBroadcast(action, uri, type, extras, packagename, classname) + + Send a broadcast + + :param str action: action + :param str uri(Optional): uri + :param str type(Optional): MIME type/subtype of the URI + :param object extras(Optional): a Map of extras to add to the Intent + :param str packagename(Optional): name of package. If used, requires classname to be useful + :param str classname(Optional): name of class. If used, requires packagename to be useful + + +:: + + sample code to show sendBroadcast + +.. py:function:: sendBroadcastIntent(intent) + + Send a broadcast + + :param Intent intent: Intent in the format as returned from makeIntent + +:: + + sample code to show sendBroadcastIntent + + +Vibrate +---------- +.. py:function:: vibrate(duration) + + 使手机震动指定的时间 + + :param int duration: duration in milliseconds + +:: + + # python + + from androidhelper import Android + droid = Android() + droid.vibrate(1000) + + +NetworkStatus +--------------- +.. py:function:: getNetworkStatus() + + Returns the status of network connection + +:: + + sample code to show getNetworkStatus + +PackageVersion APIs +------------------------------ +.. py:function:: requiredVersion(requiredVersion) + + 检测当前 sl4a 的版本是否大于制定的版本 + + :param int requiredVersion: requiredVersion + + :return: true or false + + +.. py:function:: getPackageVersionCode(packageName) + + 返回指定包的版本号 + + :param str packageName: packageName + + :return: Package version code + +.. py:function:: getPackageVersion(packageName) + + Returns package version name + + :param str packageName: packageName + + :return: Package version name + + +:: + + sample code to show getPackageVersionCode & getPackageVersion + + +System APIs +-------------------------------- +.. py:function:: getConstants(classname) + + Get list of constants (static final fields) for a class + + :param str classname: classname + + :return: list + +:: + + sample code to show getConstants + +.. py:function:: environment() + + A map of various useful environment details + + :return: environment map object includes id, display, offset, TZ, SDK, download, appcache, availblocks, blocksize, blockcount, sdcard + +:: + + sample code to show environment + +.. py:function:: log(message) + + Writes message to logcat + + :param str message: message + +:: + + sample code to show log + + +SendEmail +---------- +.. py:function:: sendEmail(to, subject, body, attachmentUri) + + Launches an activity that sends an e-mail message to a given recipient + + :param str to: A comma separated list of recipients + :param str subject: subject + :param str body: mail body + :param str attachmentUri(Optional): message + +:: + + sample code to show sendEmail + + +Toast, getInput, getPassword, notify APIs +------------------------------------------------ +.. py:function:: makeToast(message) + + Displays a short-duration Toast notification + + :param str message: message + +:: + + sample code to show makeToast + +.. py:function:: getInput(title, message) + + Queries the user for a text input + + :param str title: title of the input box + :param str message: message to display above the input box + +:: + + sample code to show getInput + +.. py:function:: getPassword(title, message) + + Queries the user for a password + + :param str title: title of the input box + :param str message: message to display above the input box + +:: + + sample code to show getPassword + +.. py:function:: notify(title, message, url) + + Displays a notification that will be canceled when the user clicks on it + + :param str title: title + :param str message: message + :param str url(optional): url + +:: + + import androidhelper + droid = androidhelper.Android() + droid.notify('Hello','QPython','http://qr.qpython.com.cn') # you could set the 3rd parameter None also + + + +ApplicationManagerFacade +========================= + +Manager APIs +------------- + +.. py:function:: getLaunchableApplications() + + Returns a list of all launchable application class names + + :return: map object + +:: + + sample code to show getLaunchableApplications + + +.. py:function:: launch(classname) + + Start activity with the given class name + + :param str classname: classname + +:: + + sample code to show launch + +.. py:function:: getRunningPackages() + + Returns a list of packages running activities or services + + :return: List of packages running activities + +:: + + sample code to show getRunningPackages + +.. py:function:: forceStopPackage(packageName) + + Force stops a package + + :param str packageName: packageName + +:: + + sample code to show forceStopPackage + + +CameraFacade +========================= + +.. py:function:: cameraCapturePicture(targetPath) + + Take a picture and save it to the specified path + + :return: A map of Booleans autoFocus and takePicture where True indicates success + +.. py:function:: cameraInteractiveCapturePicture(targetPath) + + Starts the image capture application to take a picture and saves it to the specified path + +CommonIntentsFacade +========================= + +Barcode +---------- +.. py:function:: scanBarcode() + + Starts the barcode scanner + + :return: A Map representation of the result Intent + +View APIs +---------- +.. py:function:: pick(uri) + + Display content to be picked by URI (e.g. contacts) + + :return: A map of result values + +.. py:function:: view(uri, type, extras) + + Start activity with view action by URI (i.e. browser, contacts, etc.) + +.. py:function:: viewMap(query) + + Opens a map search for query (e.g. pizza, 123 My Street) + +.. py:function:: viewContacts() + + Opens the list of contacts + +.. py:function:: viewHtml(path) + + Opens the browser to display a local HTML file + +.. py:function:: search(query) + + Starts a search for the given query + +ContactsFacade +========================= + +.. py:function:: pickContact() + + Displays a list of contacts to pick from + + :return: A map of result values + +.. py:function:: pickPhone() + + Displays a list of phone numbers to pick from + + :return: The selected phone number + +.. py:function:: contactsGetAttributes() + + Returns a List of all possible attributes for contacts + + :return: a List of contacts as Maps + +.. py:function:: contactsGetIds() + + Returns a List of all contact IDs + +.. py:function:: contactsGet(attributes) + + Returns a List of all contacts + +.. py:function:: contactsGetById(id) + + Returns contacts by ID + +.. py:function:: contactsGetCount() + + Returns the number of contacts + +.. py:function:: queryContent(uri, attributes, selection, selectionArgs, order) + + Content Resolver Query + + :return: result of query as Maps + +.. py:function:: queryAttributes(uri) + + Content Resolver Query Attributes + + :return: a list of available columns for a given content uri + +EventFacade +========================= + +.. py:function:: eventClearBuffer() + + Clears all events from the event buffer + +.. py:function:: eventRegisterForBroadcast(category, enqueue) + + Registers a listener for a new broadcast signal + +.. py:function:: eventUnregisterForBroadcast(category) + + Stop listening for a broadcast signal + +.. py:function:: eventGetBrodcastCategories() + + Lists all the broadcast signals we are listening for + +.. py:function:: eventPoll(number_of_events) + + Returns and removes the oldest n events (i.e. location or sensor update, etc.) from the event buffer + + :return: A List of Maps of event properties + +.. py:function:: eventWaitFor(eventName, timeout) + + Blocks until an event with the supplied name occurs. The returned event is not removed from the buffer + + :return: Map of event properties + +.. py:function:: eventWait(timeout) + + Blocks until an event occurs. The returned event is removed from the buffer + + :return: Map of event properties + +.. py:function:: eventPost(name, data, enqueue) + + Post an event to the event queue + +.. py:function:: rpcPostEvent(name, data) + + Post an event to the event queue + +.. py:function:: receiveEvent() + + Returns and removes the oldest event (i.e. location or sensor update, etc.) from the event buffer + + :return: Map of event properties + +.. py:function:: waitForEvent(eventName, timeout) + + Blocks until an event with the supplied name occurs. The returned event is not removed from the buffer + + :return: Map of event properties + +.. py:function:: startEventDispatcher(port) + + Opens up a socket where you can read for events posted + +.. py:function:: stopEventDispatcher() + + Stops the event server, you can't read in the port anymore + + +LocationFacade +========================= + +Providers APIs +----------------- + +.. py:function:: locationProviders() + + Returns availables providers on the phone + +.. py:function:: locationProviderEnabled(provider) + + Ask if provider is enabled + +Location APIs +----------------- +.. py:function:: startLocating(minDistance, minUpdateDistance) + + Starts collecting location data + +.. py:function:: readLocation() + + Returns the current location as indicated by all available providers + + :return: A map of location information by provider + +.. py:function:: stopLocating() + + Stops collecting location data + +.. py:function:: getLastKnownLocation() + + Returns the last known location of the device + + :return: A map of location information by provider + + +GEO +----------- +.. py:function:: geocode(latitude, longitude, maxResults) + + Returns a list of addresses for the given latitude and longitude + + :return: A list of addresses + + +*sample code* + +:: + + #qpy:console + import time + from androidhelper import Android + droid = Android() + droid.startLocating() + locproviders = droid.locationProviders().result + print("locproviders:"+repr(locproviders)) + + gpsprovider = droid.locationProviderEnabled('gps').result + print("gpsprovider:"+repr(gpsprovider)) + i = 0 + while i<100: + event = droid.eventWaitFor('location', 10000).result + print("Event:"+repr(event)) + + location = droid.readLocation().result + + if len(location) > 0: + print('Location:'+repr(location)) + + else: + location = droid.getLastKnownLocation().result + print("Last location:"+repr(lastloc)) + + loc = location['network'] # change to gps if you can get it + addr = droid.geocode(loc['latitude'], loc['longitude']).result + print("addr:"+repr(addr)) + + time.sleep(1) + i = i+1 + droid.stopLocating() + +PhoneFacade +========================= + +PhoneStat APIs +---------------- + +.. py:function:: startTrackingPhoneState() + + Starts tracking phone state + +.. py:function:: readPhoneState() + + Returns the current phone state and incoming number + + :return: A Map of "state" and "incomingNumber" + +.. py:function:: stopTrackingPhoneState() + + Stops tracking phone state + + +Call & Dia APIs +---------------- + +.. py:function:: phoneCall(uri) + + Calls a contact/phone number by URI + +.. py:function:: phoneCallNumber(number) + + Calls a phone number + +.. py:function:: phoneDial(uri) + + Dials a contact/phone number by URI + +.. py:function:: phoneDialNumber(number) + + Dials a phone number + + + +Get information APIs +------------------------ +.. py:function:: getCellLocation() + + Returns the current cell location + +.. py:function:: getNetworkOperator() + + Returns the numeric name (MCC+MNC) of current registered operator + +.. py:function:: getNetworkOperatorName() + + Returns the alphabetic name of current registered operator + +.. py:function:: getNetworkType() + + Returns a the radio technology (network type) currently in use on the device + +.. py:function:: getPhoneType() + + Returns the device phone type + +.. py:function:: getSimCountryIso() + + Returns the ISO country code equivalent for the SIM provider's country code + +.. py:function:: getSimOperator() + + Returns the MCC+MNC (mobile country code + mobile network code) of the provider of the SIM. 5 or 6 decimal digits + +.. py:function:: getSimOperatorName() + + Returns the Service Provider Name (SPN) + +.. py:function:: getSimSerialNumber() + + Returns the serial number of the SIM, if applicable. Return null if it is unavailable + +.. py:function:: getSimState() + + Returns the state of the device SIM card + +.. py:function:: getSubscriberId() + + Returns the unique subscriber ID, for example, the IMSI for a GSM phone. Return null if it is unavailable + +.. py:function:: getVoiceMailAlphaTag() + + Retrieves the alphabetic identifier associated with the voice mail number + +.. py:function:: getVoiceMailNumber() + + Returns the voice mail number. Return null if it is unavailable + +.. py:function:: checkNetworkRoaming() + + Returns true if the device is considered roaming on the current network, for GSM purposes + +.. py:function:: getDeviceId() + + Returns the unique device ID, for example, the IMEI for GSM and the MEID for CDMA phones. Return null if device ID is not available + +.. py:function:: getDeviceSoftwareVersion() + + Returns the software version number for the device, for example, the IMEI/SV for GSM phones. Return null if the software version is not available + +.. py:function:: getLine1Number() + + Returns the phone number string for line 1, for example, the MSISDN for a GSM phone. Return null if it is unavailable + +.. py:function:: getNeighboringCellInfo() + + Returns the neighboring cell information of the device + +MediaRecorderFacade +========================= + + +Audio +-------- + +.. py:function:: recorderStartMicrophone(targetPath) + + Records audio from the microphone and saves it to the given location + +Video APIs +----------- + +.. py:function:: recorderStartVideo(targetPath, duration, videoSize) + + Records video from the camera and saves it to the given location. + Duration specifies the maximum duration of the recording session. + If duration is 0 this method will return and the recording will only be stopped + when recorderStop is called or when a scripts exits. + Otherwise it will block for the time period equal to the duration argument. + videoSize: 0=160x120, 1=320x240, 2=352x288, 3=640x480, 4=800x480. + + +.. py:function:: recorderCaptureVideo(targetPath, duration, recordAudio) + + Records video (and optionally audio) from the camera and saves it to the given location. + Duration specifies the maximum duration of the recording session. + If duration is not provided this method will return immediately and the recording will only be stopped + when recorderStop is called or when a scripts exits. + Otherwise it will block for the time period equal to the duration argument. + +.. py:function:: startInteractiveVideoRecording(path) + + Starts the video capture application to record a video and saves it to the specified path + + +Stop +-------- +.. py:function:: recorderStop() + + Stops a previously started recording + + +SensorManagerFacade +========================= + +Start & Stop +------------- +.. py:function:: startSensingTimed(sensorNumber, delayTime) + + Starts recording sensor data to be available for polling + +.. py:function:: startSensingThreshold(ensorNumber, threshold, axis) + + Records to the Event Queue sensor data exceeding a chosen threshold + +.. py:function:: startSensing(sampleSize) + + Starts recording sensor data to be available for polling + +.. py:function:: stopSensing() + + Stops collecting sensor data + +Read data APIs +--------------- +.. py:function:: readSensors() + + Returns the most recently recorded sensor data + +.. py:function:: sensorsGetAccuracy() + + Returns the most recently received accuracy value + +.. py:function:: sensorsGetLight() + + Returns the most recently received light value + +.. py:function:: sensorsReadAccelerometer() + + Returns the most recently received accelerometer values + + :return: a List of Floats [(acceleration on the) X axis, Y axis, Z axis] + +.. py:function:: sensorsReadMagnetometer() + + Returns the most recently received magnetic field values + + :return: a List of Floats [(magnetic field value for) X axis, Y axis, Z axis] + +.. py:function:: sensorsReadOrientation() + + Returns the most recently received orientation values + + :return: a List of Doubles [azimuth, pitch, roll] + +*sample code* +:: + + Droid = androidhelper.Android() + Droid.startSensingTimed(1, 250) + sensor = Droid.sensorsReadOrientation().result + Droid.stopSensing() + + +SettingsFacade +========================= + +Screen +---------- + +.. py:function:: setScreenTimeout(value) + + Sets the screen timeout to this number of seconds + + :return: The original screen timeout + +.. py:function:: getScreenTimeout() + + Gets the screen timeout + + :return: the current screen timeout in seconds + +AirplanerMode +--------------------- + +.. py:function:: checkAirplaneMode() + + Checks the airplane mode setting + + :return: True if airplane mode is enabled + +.. py:function:: toggleAirplaneMode(enabled) + + Toggles airplane mode on and off + + :return: True if airplane mode is enabled + +Ringer Silent Mode +--------------------- + +.. py:function:: checkRingerSilentMode() + + Checks the ringer silent mode setting + + :return: True if ringer silent mode is enabled + +.. py:function:: toggleRingerSilentMode(enabled) + + Toggles ringer silent mode on and off + + :return: True if ringer silent mode is enabled + +Vibrate Mode +--------------------- + +.. py:function:: toggleVibrateMode(enabled) + + Toggles vibrate mode on and off. If ringer=true then set Ringer setting, else set Notification setting + + :return: True if vibrate mode is enabled + +.. py:function:: getVibrateMode(ringer) + + Checks Vibration setting. If ringer=true then query Ringer setting, else query Notification setting + + :return: True if vibrate mode is enabled + +Ringer & Media Volume +--------------------- + +.. py:function:: getMaxRingerVolume() + + Returns the maximum ringer volume + +.. py:function:: getRingerVolume() + + Returns the current ringer volume + +.. py:function:: setRingerVolume(volume) + + Sets the ringer volume + +.. py:function:: getMaxMediaVolume() + + Returns the maximum media volume + +.. py:function:: getMediaVolume() + + Returns the current media volume + +.. py:function:: setMediaVolume(volume) + + Sets the media volume + +Screen Brightness +--------------------- + +.. py:function:: getScreenBrightness() + + Returns the screen backlight brightness + + :return: the current screen brightness between 0 and 255 + +.. py:function:: setScreenBrightness(value) + + Sets the the screen backlight brightness + + :return: the original screen brightness + +.. py:function:: checkScreenOn() + + Checks if the screen is on or off (requires API level 7) + + :return: True if the screen is currently on + + +SmsFacade +========================= + +.. py:function:: smsSend(destinationAddress, text) + + 发送一条短信 + + :param str destinationAddress: typically a phone number + :param str text: + +.. py:function:: smsGetMessageCount(unreadOnly, folder) + + 返回短信息的数目 + + :param bool unreadOnly: typically a phone number + :param str folder(optional): default "inbox" + +.. py:function:: smsGetMessageIds(unreadOnly, folder) + + 返回所有信息的 id + + :param bool unreadOnly: typically a phone number + :param str folder(optional): default "inbox" + +.. py:function:: smsGetMessages(unreadOnly, folder, attributes) + + 返回所有信息的列表 + + :param bool unreadOnly: typically a phone number + :param str folder: default "inbox" + :param list attributes(optional): attributes + + :return: a List of messages as Maps + +.. py:function:: smsGetMessageById(id, attributes) + + Returns message attributes + + :param int id: message ID + :param list attributes(optional): attributes + + :return: a List of messages as Maps + +.. py:function:: smsGetAttributes() + + 返回指定信息的属性 + +.. py:function:: smsDeleteMessage(id) + + 删除指定的短信息 + + :param int id: message ID + + :return: True if the message was deleted + +.. py:function:: smsMarkMessageRead(ids, read) + + 将短信息标记为已读 + + :param list ids: List of message IDs to mark as read + :param bool read: true or false + + :return: number of messages marked read + +SpeechRecognitionFacade +========================= + +.. py:function:: recognizeSpeech(prompt, language, languageModel) + + Recognizes user's speech and returns the most likely result + + :param str prompt(optional): text prompt to show to the user when asking them to speak + :param str language(optional): language override to inform the recognizer that it should expect speech in a language different than the one set in the java.util.Locale.getDefault() + :param str languageModel(optional): informs the recognizer which speech model to prefer (see android.speech.RecognizeIntent) + + :return: An empty string in case the speech cannot be recongnized + + +ToneGeneratorFacade +========================= + +.. py:function:: generateDtmfTones(phoneNumber, toneDuration) + + Generate DTMF tones for the given phone number + + :param str phoneNumber: phone number + :param int toneDuration(optional): default 100, duration of each tone in milliseconds + + +WakeLockFacade +========================= + +.. py:function:: wakeLockAcquireFull() + + Acquires a full wake lock (CPU on, screen bright, keyboard bright) + +.. py:function:: wakeLockAcquirePartial() + + Acquires a partial wake lock (CPU on) + +.. py:function:: wakeLockAcquireBright() + + Acquires a bright wake lock (CPU on, screen bright) + +.. py:function:: wakeLockAcquireDim() + + Acquires a dim wake lock (CPU on, screen dim) + +.. py:function:: wakeLockRelease() + + Releases the wake lock + +WifiFacade +========================= + +.. py:function:: wifiGetScanResults() + + Returns the list of access points found during the most recent Wifi scan + +.. py:function:: wifiLockAcquireFull() + + Acquires a full Wifi lock + +.. py:function:: wifiLockAcquireScanOnly() + + Acquires a scan only Wifi lock + +.. py:function:: wifiLockRelease() + + Releases a previously acquired Wifi lock + +.. py:function:: wifiStartScan() + + Starts a scan for Wifi access points + + :return: True if the scan was initiated successfully + +.. py:function:: checkWifiState() + + Checks Wifi state + + :return: True if Wifi is enabled + +.. py:function:: toggleWifiState(enabled) + + Toggle Wifi on and off + + :param bool enabled(optional): enabled + + :return: True if Wifi is enabled + +.. py:function:: wifiDisconnect() + + Disconnects from the currently active access point + + :return: True if the operation succeeded + +.. py:function:: wifiGetConnectionInfo() + + Returns information about the currently active access point + +.. py:function:: wifiReassociate() + + Returns information about the currently active access point + + :return: True if the operation succeeded + +.. py:function:: wifiReconnect() + + Reconnects to the currently active access point + + :return: True if the operation succeeded + + +BatteryManagerFacade +========================= + +.. py:function:: readBatteryData() + + 读取电池记录数据 + +.. py:function:: batteryStartMonitoring() + + 开始监视电池状态 + +.. py:function:: batteryStopMonitoring() + + 停止监视电池状态 + +.. py:function:: batteryGetStatus() + + 返回充电状态信息 + + 1 - 未知; + 2 - 充电中; + 3 - 放电中; + 4 - 未充电; + 5 - 已充满 + +.. py:function:: batteryGetHealth() + + 查看电池健康状态 + + 1 - 未知; + 2 - 良好; + 3 - 过热; + 4 - 不可用; + 5 - 电量过饱; + 6 - 查询失败 + +.. py:function:: batteryGetPlugType() + + 返回充电状态信息 + + -1 - 未知 + 0 - 未插入电源; + 1 - 交流电源充电 + 2 - usb充电 + + +.. py:function:: batteryCheckPresent() + + 查看电池电量信息 + +.. py:function:: batteryGetLevel() + + 返回电池电量(百分比形式) + +.. py:function:: batteryGetVoltage() + + 返回电池的电压 + +.. py:function:: batteryGetTemperature() + + 返回电池温度 + +.. py:function:: batteryGetTechnology() + + 返回电池技术数据 + +:: + + # python + + import time + from androidhelper import Android + droid = Android() + droid.batteryStartMonitoring() + time.sleep(5) + bdata = droid.readBatteryData() + print(bdata.result) + + bstatus = droid.batteryGetStatus().result + bhealth = droid.batteryGetHealth().result + bplug = droid.batteryGetPlugType().result + bcheck = droid.batteryCheckPresent().result + blevel = droid.batteryGetLevel().result + bvoltage = droid.batteryGetVoltage().result + btemperature = droid.batteryGetTemperature().result + btechnology = droid.batteryGetTechnology().result + print({"status": bstatus, "health": bhealth, "plugtype": bplug, "checkpresent": bcheck, "level": blevel, "voltage": bvoltage, "temperature": btemperature, "technology": btechnology}) + + droid.batteryStopMonitoring() + +ActivityResultFacade +========================= + +.. py:function:: setResultBoolean(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + + +.. py:function:: setResultByte(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultShort(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultChar(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + + +.. py:function:: setResultInteger(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultLong(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultFloat(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultDouble(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultString(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultBooleanArray(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultByteArray(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultShortArray(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultCharArray(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultIntegerArray(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultLongArray(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultFloatArray(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultDoubleArray(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultStringArray(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultSerializable(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + + +MediaPlayerFacade +========================= + +Control +----------------- +.. py:function:: mediaPlay(url, tag, play) + + Open a media file + + :param str url: url of media resource + :param str tag(optional): string identifying resource (default=default) + :param bool play(optional): start playing immediately + + :return: true if play successful + +.. py:function:: mediaPlayPause(tag) + + pause playing media file + + :param str tag: string identifying resource (default=default) + + :return: true if successful + +.. py:function:: mediaPlayStart(tag) + + start playing media file + + :param str tag: string identifying resource (default=default) + + :return: true if successful + +.. py:function:: mediaPlayClose(tag) + + Close media file + + :param str tag: string identifying resource (default=default) + + :return: true if successful + +.. py:function:: mediaIsPlaying(tag) + + Checks if media file is playing + + :param str tag: string identifying resource (default=default) + + :return: true if successful + + +.. py:function:: mediaPlaySetLooping(enabled, tag) + + Set Looping + + :param bool enabled: default true + :param str tag: string identifying resource (default=default) + + :return: True if successful + +.. py:function:: mediaPlaySeek(msec, tag) + + Seek To Position + + :param int msec: default true + :param str tag: string identifying resource (default=default) + + :return: New Position (in ms) + +Get Information +----------------- +.. py:function:: mediaPlayInfo(tag) + + Information on current media + + :param str tag: string identifying resource (default=default) + + :return: Media Information + +.. py:function:: mediaPlayList() + + Lists currently loaded media + + :return: List of Media Tags + + +PreferencesFacade +========================= + +.. py:function:: prefGetValue(key, filename) + + Read a value from shared preferences + + :param str key: key + :param str filename(optional): Desired preferences file. If not defined, uses the default Shared Preferences. + + +.. py:function:: prefPutValue(key, value, filename) + + Write a value to shared preferences + + :param str key: key + :param str value: value + :param str filename(optional): Desired preferences file. If not defined, uses the default Shared Preferences. + +.. py:function:: prefGetAll(filename) + + Get list of Shared Preference Values + + :param str filename(optional): Desired preferences file. If not defined, uses the default Shared Preferences. + + +QPyInterfaceFacade +========================= + +.. py:function:: executeQPy(script) + + Execute a qpython script by absolute path + + :param str script: The absolute path of the qpython script + + :return: bool + + +TextToSpeechFacade +========================= + +.. py:function:: ttsSpeak(message) + + Speaks the provided message via TTS + + :param str message: message + +.. py:function:: ttsIsSpeaking() + + Returns True if speech is currently in progress + +EyesFreeFacade +========================= + +.. py:function:: ttsSpeak(message) + + Speaks the provided message via TTS + + :param str message: message + + +BluetoothFacade +========================= + +.. py:function:: bluetoothActiveConnections() + + Returns active Bluetooth connections + + +.. py:function:: bluetoothWriteBinary(base64, connID) + + Send bytes over the currently open Bluetooth connection + + :param str base64: A base64 encoded String of the bytes to be sent + :param str connID(optional): Connection id + +.. py:function:: bluetoothReadBinary(bufferSize, connID) + + Read up to bufferSize bytes and return a chunked, base64 encoded string + + :param int bufferSize: default 4096 + :param str connID(optional): Connection id + +.. py:function:: bluetoothConnect(uuid, address) + + Connect to a device over Bluetooth. Blocks until the connection is established or fails + + :param str uuid: The UUID passed here must match the UUID used by the server device + :param str address(optional): The user will be presented with a list of discovered devices to choose from if an address is not provided + + :return: True if the connection was established successfully + +.. py:function:: bluetoothAccept(uuid, timeout) + + Listens for and accepts a Bluetooth connection. Blocks until the connection is established or fails + + :param str uuid: The UUID passed here must match the UUID used by the server device + :param int timeout: How long to wait for a new connection, 0 is wait for ever (default=0) + +.. py:function:: bluetoothMakeDiscoverable(duration) + + Requests that the device be discoverable for Bluetooth connections + + :param int duration: period of time, in seconds, during which the device should be discoverable (default=300) + +.. py:function:: bluetoothWrite(ascii, connID) + + Sends ASCII characters over the currently open Bluetooth connection + + :param str ascii: text + :param str connID: Connection id + +.. py:function:: bluetoothReadReady(connID) + + Sends ASCII characters over the currently open Bluetooth connection + + :param str ascii: text + :param str connID: Connection id + +.. py:function:: bluetoothRead(bufferSize, connID) + + Read up to bufferSize ASCII characters + + :param int bufferSize: default=4096 + :param str connID(optional): Connection id + +.. py:function:: bluetoothReadLine(connID) + + Read the next line + + :param str connID(optional): Connection id + +.. py:function:: bluetoothGetRemoteDeviceName(address) + + Queries a remote device for it's name or null if it can't be resolved + + :param str address: Bluetooth Address For Target Device + +.. py:function:: bluetoothGetLocalName() + + Gets the Bluetooth Visible device name + +.. py:function:: bluetoothSetLocalName(name) + + Sets the Bluetooth Visible device name, returns True on success + + :param str name: New local name + +.. py:function:: bluetoothGetScanMode() + + Gets the scan mode for the local dongle. + Return values: + -1 when Bluetooth is disabled. + 0 if non discoverable and non connectable. + 1 connectable non discoverable. + 3 connectable and discoverable. + +.. py:function:: bluetoothGetConnectedDeviceName(connID) + + Returns the name of the connected device + + :param str connID: Connection id + +.. py:function:: checkBluetoothState() + + Checks Bluetooth state + + :return: True if Bluetooth is enabled + +.. py:function:: toggleBluetoothState(enabled, prompt) + + Toggle Bluetooth on and off + + :param bool enabled: + :param str prompt: Prompt the user to confirm changing the Bluetooth state, default=true + + :return: True if Bluetooth is enabled + +.. py:function:: bluetoothStop(connID) + + Stops Bluetooth connection + + :param str connID: Connection id + +.. py:function:: bluetoothGetLocalAddress() + + Returns the hardware address of the local Bluetooth adapter + +.. py:function:: bluetoothDiscoveryStart() + + Start the remote device discovery process + + :return: true on success, false on error + +.. py:function:: bluetoothDiscoveryCancel() + + Cancel the current device discovery process + + :return: true on success, false on error + +.. py:function:: bluetoothIsDiscovering() + + Return true if the local Bluetooth adapter is currently in the device discovery process + + +SignalStrengthFacade +========================= +.. py:function:: startTrackingSignalStrengths() + + Starts tracking signal strengths + +.. py:function:: readSignalStrengths() + + Returns the current signal strengths + + :return: A map of gsm_signal_strength + +.. py:function:: stopTrackingSignalStrengths() + + Stops tracking signal strength + + +WebCamFacade +========================= + +.. py:function:: webcamStart(resolutionLevel, jpegQuality, port) + + Starts an MJPEG stream and returns a Tuple of address and port for the stream + + :param int resolutionLevel: increasing this number provides higher resolution (default=0) + :param int jpegQuality: a number from 0-10 (default=20) + :param int port: If port is specified, the webcam service will bind to port, otherwise it will pick any available port (default=0) + +.. py:function:: webcamAdjustQuality(resolutionLevel, jpegQuality) + + Adjusts the quality of the webcam stream while it is running + + :param int resolutionLevel: increasing this number provides higher resolution (default=0) + :param int jpegQuality: a number from 0-10 (default=20) + +.. py:function:: cameraStartPreview(resolutionLevel, jpegQuality, filepath) + + Start Preview Mode. Throws 'preview' events + + :param int resolutionLevel: increasing this number provides higher resolution (default=0) + :param int jpegQuality: a number from 0-10 (default=20) + :param str filepath: Path to store jpeg files + + :return: True if successful + +.. py:function:: cameraStopPreview() + + Stop the preview mode + + +UiFacade +========================= + +Dialog +-------- +.. py:function:: dialogCreateInput(title, message, defaultText, inputType) + + Create a text input dialog + + :param str title: title of the input box + :param str message: message to display above the input box + :param str defaultText(optional): text to insert into the input box + :param str inputType(optional): type of input data, ie number or text + +.. py:function:: dialogCreatePassword(title, message) + + Create a password input dialog + + :param str title: title of the input box + :param str message: message to display above the input box + +.. py:function:: dialogGetInput(title, message, defaultText) + + Create a password input dialog + + :param str title: title of the input box + :param str message: message to display above the input box + :param str defaultText(optional): text to insert into the input box + +.. py:function:: dialogGetPassword(title, message) + + Queries the user for a password + + :param str title: title of the password box + :param str message: message to display above the input box + +.. py:function:: dialogCreateSeekBar(start, maximum, title) + + Create seek bar dialog + + :param int start: default=50 + :param int maximum: default=100 + :param int title: title + +.. py:function:: dialogCreateTimePicker(hour, minute, is24hour) + + Create time picker dialog + + :param int hour: default=0 + :param int miute: default=0 + :param bool is24hour: default=false + +.. py:function:: dialogCreateDatePicker(year, month, day) + + Create date picker dialog + + :param int year: default=1970 + :param int month: default=1 + :param int day: default=1 + + +NFC +------------- +**Data structs** +*QPython NFC json result* +:: + + { + "role": , # could be self/master/slave + "stat": , # could be ok / fail / cancl + "message": + } + +**APIs** + +.. py:function:: dialogCreateNFCBeamMaster(title, message, inputType) + + Create a dialog where you could create a qpython beam master + + :param str title: title of the input box + :param str message: message to display above the input box + :param str inputType(optional): type of input data, ie number or text + +.. py:function:: NFCBeamMessage(content, title, message) + + Create a dialog where you could create a qpython beam master + + :param str content: message you want to sent + :param str title: title of the input box + :param str message: message to display above the input box + :param str inputType(optional): type of input data, ie number or text + +.. py:function:: dialogCreateNFCBeamSlave(title, message) + + Create a qpython beam slave + + :param str title: title of the input box + :param str message: message to display above the input box + +Progress +-------------- +.. py:function:: dialogCreateSpinnerProgress(message, maximumProgress) + + Create a spinner progress dialog + + :param str message(optional): message + :param int maximunProgress(optional): dfault=100 + +.. py:function:: dialogSetCurrentProgress(current) + + Set progress dialog current value + + :param int current: current + +.. py:function:: dialogSetMaxProgress(max) + + Set progress dialog maximum value + + :param int max: max + + +.. py:function:: dialogCreateHorizontalProgress(title, message, maximumProgress) + + Create a horizontal progress dialog + + :param str title(optional): title + :param str message(optional): message + :param int maximunProgress(optional): dfault=100 + + +Alert +---------- +.. py:function:: dialogCreateAlert(title, message) + + Create alert dialog + + :param str title(optional): title + :param str message(optional): message + :param int maximunProgress(optional): dfault=100 + + +Dialog Control +--------------- +.. py:function:: dialogSetPositiveButtonText(text) + + Set alert dialog positive button text + + :param str text: text + +.. py:function:: dialogSetNegativeButtonText(text) + + Set alert dialog negative button text + + :param str text: text + +.. py:function:: dialogSetNeutralButtonText(text) + + Set alert dialog button text + + :param str text: text + +.. py:function:: dialogSetItems(items) + + Set alert dialog list items + + :param list items: items + +.. py:function:: dialogSetSingleChoiceItems(items, selected) + + Set alert dialog list items + + :param list items: items + :param int selected: selected item index (default=0) + +.. py:function:: dialogSetMultiChoiceItems(items, selected) + + Set dialog multiple choice items and selection + + :param list items: items + :param int selected: selected item index (default=0) + +.. py:function:: addContextMenuItem(label, event, eventData) + + Adds a new item to context menu + + :param str label: label for this menu item + :param str event: event that will be generated on menu item click + :param object eventData: event object + +.. py:function:: addOptionsMenuItem(label, event, eventData, iconName) + + Adds a new item to context menu + + :param str label: label for this menu item + :param str event: event that will be generated on menu item click + :param object eventData: event object + :param str iconName: Android system menu icon, see http://developer.android.com/reference/android/R.drawable.html + +.. py:function:: dialogGetResponse() + + Returns dialog response + +.. py:function:: dialogGetSelectedItems() + + This method provides list of items user selected + +.. py:function:: dialogDismiss() + + Dismiss dialog + +.. py:function:: dialogShow() + + Show dialog + + +Layout +--------- +.. py:function:: fullShow(layout) + + Show Full Screen + + :param string layout: String containing View layout + +.. py:function:: fullDismiss() + + Dismiss Full Screen + +.. py:function:: fullQuery() + + Get Fullscreen Properties + +.. py:function:: fullQueryDetail(id) + + Get fullscreen properties for a specific widget + + :param str id: id of layout widget + +.. py:function:: fullSetProperty(id) + + Set fullscreen widget property + + :param str id: id of layout widget + :param str property: name of property to set + :param str value: value to set property to + +.. py:function:: fullSetList(id, list) + + Attach a list to a fullscreen widget + + :param str id: id of layout widget + :param list list: List to set + +.. py:function:: fullKeyOverride(keycodes, enable) + + Override default key actions + + :param str keycodes: id of layout widget + :param bool enable: List to set (default=true) + + + +WebView +----------- +.. py:function:: webViewShow() + + Display a WebView with the given URL + + :param str url: url + :param bool wait(optional): block until the user exits the WebView + +USB Host Serial Facade +====================== + +*QPython 1.3.1+ and QPython3 1.0.3+ contains this feature* + +SL4A Facade for USB Serial devices by Android USB Host API. + + +It control the USB-Serial like devices +from Andoroid which has USB Host Controller . + +The sample +`demonstration is also available at youtube video `_ + + +Requirements +------------- +* Android device which has USB Host controller (and enabled in that firmware). +* Android 4.0 (API14) or later. +* USB Serial devices (see [Status](#Status)). +* USB Serial devices were not handled by Android kernel. + + > I heard some android phone handle USB Serial devices + > make /dev/ttyUSB0 in kernel level. + > In this case, Android does not be able to handle the device + > from OS level. + + please check Android Applications be able to grab the target USB Devices, + such as `USB Device Info `_. + +Status +--------------- +* probably work with USB CDC, like FTDI, Arduino or else. + +* 2012/09/10: work with 78K0F0730 device (new RL78) with Tragi BIOS board. + + `M78K0F0730 `_ + +* 2012/09/24: work with some pl2303 devcies. + +Author +------- +This facade developped by `Kuri65536 `_ +you can see the commit log in it. + + +APIs +-------- +.. py:function:: usbserialGetDeviceList() + + Returns USB devices reported by USB Host API. + + :return: Returns "Map of id and string information Map + + +.. py:function:: usbserialDisconnect(connID) + + Disconnect all USB-device + + :param str connID: connection ID + +.. py:function:: usbserialActiveConnections() + + Returns active USB-device connections. + + :return: Returns "Active USB-device connections by Map UUID vs device-name." + + +.. py:function:: usbserialWriteBinary(base64, connID) + + Send bytes over the currently open USB Serial connection. + + :param str base64: + :param str connId: + +.. py:function:: usbserialReadBinary(bufferSize, connID) + + Read up to bufferSize bytes and return a chunked, base64 encoded string + + :param int bufferSize: + :param str connId: + +.. py:function:: usbserialConnect(hash, options) + + Connect to a device with USB-Host. request the connection and exit + + :param str hash: + :param str options: + + :return: Returns messages the request status + +.. py:function:: usbserialHostEnable() + + Requests that the host be enable for USB Serial connections. + + :return: True if the USB Device is accesible + +.. py:function:: usbserialWrite(String ascii, String connID) + + Sends ASCII characters over the currently open USB Serial connection + + :param str ascii: + :param str connID: + +.. py:function:: usbserialReadReady(connID) + + :param str connID: + + :return: True if the next read is guaranteed not to block + + +.. py:function:: usbserialRead(connID, bufferSize) + + Read up to bufferSize ASCII characters. + + :param str connID: + :param int bufferSize: + +.. py:function:: usbserialGetDeviceName(connID) + + Queries a remote device for it's name or null if it can't be resolved + + :param str connID: diff --git a/doc/en/APIs.rst b/doc/en/APIs.rst index bb136e8..8c5cd31 100644 --- a/doc/en/APIs.rst +++ b/doc/en/APIs.rst @@ -1,2035 +1,2035 @@ -QPysl4a APIs -============ - -The Scripting Layer for Android (abridged as SL4A, and previously named Android Scripting Environment or ASE) is a library that allows the creation and running of scripts written in various scripting languages directly on Android devices. After having integrated it, QPython continue to develop and extend SL4A project to meet QPython's need, it's QPysl4a project. - - -AndroidFacade -=============== - -Clipboard APIs ----------------- -.. py:function:: setClipboard(text) - - Put text in the clipboard - - :param str text: text - -.. py:function:: getClipboard(text) - - Read text from the clipboard - - :return: The text in the clipboard - - -:: - - from androidhelper import Android - droid = Android() - - #setClipboard - droid.setClipboard("Hello World") - - #getClipboard - clipboard = droid.getClipboard().result - - -Intent & startActivity APIs ----------------------------------- -.. py:function:: makeIntent(action, uri, type, extras, categories, packagename, classname, flags) - - Starts an activity and returns the result - - :param str action: action - :param str uri(Optional): uri - :param str type(Optional): MIME type/subtype of the URI - :param object extras(Optional): a Map of extras to add to the Intent - :param list categories(Optional): a List of categories to add to the Intent - :param str packagename(Optional): name of package. If used, requires classname to be useful - :param str classname(Optional): name of class. If used, requires packagename to be useful - :param int flags(Optional): Intent flags - - :return: An object representing an Intent - - -:: - - sample code to show makeIntent - - -.. py:function:: getIntent() - - Returns the intent that launched the script - -:: - - sample code to show getIntent - - -.. py:function:: startActivityForResult(action, uri, type, extras, packagename, classname) - - Starts an activity and returns the result - - :param str action: action - :param str uri(Optional): uri - :param str type(Optional): MIME type/subtype of the URI - :param object extras(Optional): a Map of extras to add to the Intent - :param str packagename(Optional): name of package. If used, requires classname to be useful - :param str classname(Optional): name of class. If used, requires packagename to be useful - - :return: A Map representation of the result Intent - - -:: - - sample code to show startActivityForResult - - -.. py:function:: startActivityForResultIntent(intent) - - Starts an activity and returns the result - - :param Intent intent: Intent in the format as returned from makeIntent - - :return: A Map representation of the result Intent - - -:: - - sample code to show startActivityForResultIntent - -.. py:function:: startActivityIntent(intent, wait) - - Starts an activity - - :param Intent intent: Intent in the format as returned from makeIntent - :param bool wait(Optional): block until the user exits the started activity - -:: - - sample code to show startActivityIntent - - -.. py:function:: startActivity(action, uri, type, extras, wait, packagename, classname) - - Starts an activity - - :param str action: action - :param str uri(Optional): uri - :param str type(Optional): MIME type/subtype of the URI - :param object extras(Optional): a Map of extras to add to the Intent - :param bool wait(Optional): block until the user exits the started activity - :param str packagename(Optional): name of package. If used, requires classname to be useful - :param str classname(Optional): name of class. If used, requires packagename to be useful - -:: - - sample code to show startActivityForResultIntent - - -SendBroadcast APIs -------------------- -.. py:function:: sendBroadcast(action, uri, type, extras, packagename, classname) - - Send a broadcast - - :param str action: action - :param str uri(Optional): uri - :param str type(Optional): MIME type/subtype of the URI - :param object extras(Optional): a Map of extras to add to the Intent - :param str packagename(Optional): name of package. If used, requires classname to be useful - :param str classname(Optional): name of class. If used, requires packagename to be useful - - -:: - - sample code to show sendBroadcast - -.. py:function:: sendBroadcastIntent(intent) - - Send a broadcast - - :param Intent intent: Intent in the format as returned from makeIntent - -:: - - sample code to show sendBroadcastIntent - - -Vibrate ----------- -.. py:function:: vibrate(intent) - - Vibrates the phone or a specified duration in milliseconds - - :param int duration: duration in milliseconds - -:: - - sample code to show vibrate - - -NetworkStatus ---------------- -.. py:function:: getNetworkStatus() - - Returns the status of network connection - -:: - - sample code to show getNetworkStatus - -PackageVersion APIs ------------------------------- -.. py:function:: requiredVersion(requiredVersion) - - Checks if version of QPython SL4A is greater than or equal to the specified version - - :param int requiredVersion: requiredVersion - - :return: true or false - - -.. py:function:: getPackageVersionCode(packageName) - - Returns package version code - - :param str packageName: packageName - - :return: Package version code - -.. py:function:: getPackageVersion(packageName) - - Returns package version name - - :param str packageName: packageName - - :return: Package version name - - -:: - - sample code to show getPackageVersionCode & getPackageVersion - - -System APIs --------------------------------- -.. py:function:: getConstants(classname) - - Get list of constants (static final fields) for a class - - :param str classname: classname - - :return: list - -:: - - sample code to show getConstants - -.. py:function:: environment() - - A map of various useful environment details - - :return: environment map object includes id, display, offset, TZ, SDK, download, appcache, availblocks, blocksize, blockcount, sdcard - -:: - - sample code to show environment - -.. py:function:: log(message) - - Writes message to logcat - - :param str message: message - -:: - - sample code to show log - - -SendEmail ----------- -.. py:function:: sendEmail(to, subject, body, attachmentUri) - - Launches an activity that sends an e-mail message to a given recipient - - :param str to: A comma separated list of recipients - :param str subject: subject - :param str body: mail body - :param str attachmentUri(Optional): message - -:: - - sample code to show sendEmail - - -Toast, getInput, getPassword, notify APIs ------------------------------------------------- -.. py:function:: makeToast(message) - - Displays a short-duration Toast notification - - :param str message: message - -:: - - sample code to show makeToast - -.. py:function:: getInput(title, message) - - Queries the user for a text input - - :param str title: title of the input box - :param str message: message to display above the input box - -:: - - sample code to show getInput - -.. py:function:: getPassword(title, message) - - Queries the user for a password - - :param str title: title of the input box - :param str message: message to display above the input box - -:: - - sample code to show getPassword - -.. py:function:: notify(title, message, url) - - Displays a notification that will be canceled when the user clicks on it - - :param str title: title - :param str message: message - :param str url(optional): url - -:: - - import androidhelper - droid = androidhelper.Android() - droid.notify('Hello','QPython','http://qpython.org') # you could set the 3rd parameter None also - - - -ApplicationManagerFacade -========================= - -Manager APIs -------------- - -.. py:function:: getLaunchableApplications() - - 获得可运行的应用类名 - - :return: map object - - - -.. py:function:: launch(classname) - - 根据给定的类名启动一个 activity - - :param str classname: classname - - - -.. py:function:: getRunningPackages() - - 返回正在运行的 activity 或者服务的列表 - - :return: List of packages running activities - - - -.. py:function:: forceStopPackage(packageName) - - Force stops a package - - :param str packageName: packageName - -:: - - sample code to show forceStopPackage - - -CameraFacade -========================= - -.. py:function:: cameraCapturePicture(targetPath) - - 拍照并保存到指定的路径 - - :return: A map of Booleans autoFocus and takePicture where True indicates success - -.. py:function:: cameraInteractiveCapturePicture(targetPath) - - 启动拍照程序拍照并保存到指定位置 - -CommonIntentsFacade -========================= - -Barcode ----------- -.. py:function:: scanBarcode() - - Starts the barcode scanner - - :return: A Map representation of the result Intent - -View APIs ----------- -.. py:function:: pick(uri) - - Display content to be picked by URI (e.g. contacts) - - :return: A map of result values - -.. py:function:: view(uri, type, extras) - - Start activity with view action by URI (i.e. browser, contacts, etc.) - -.. py:function:: viewMap(query) - - Opens a map search for query (e.g. pizza, 123 My Street) - -.. py:function:: viewContacts() - - Opens the list of contacts - -.. py:function:: viewHtml(path) - - Opens the browser to display a local HTML file - -.. py:function:: search(query) - - Starts a search for the given query - -ContactsFacade -========================= - -.. py:function:: pickContact() - - Displays a list of contacts to pick from - - :return: A map of result values - -.. py:function:: pickPhone() - - Displays a list of phone numbers to pick from - - :return: The selected phone number - -.. py:function:: contactsGetAttributes() - - Returns a List of all possible attributes for contacts - - :return: a List of contacts as Maps - -.. py:function:: contactsGetIds() - - Returns a List of all contact IDs - -.. py:function:: contactsGet(attributes) - - Returns a List of all contacts - -.. py:function:: contactsGetById(id) - - Returns contacts by ID - -.. py:function:: contactsGetCount() - - Returns the number of contacts - -.. py:function:: queryContent(uri, attributes, selection, selectionArgs, order) - - Content Resolver Query - - :return: result of query as Maps - -.. py:function:: queryAttributes(uri) - - Content Resolver Query Attributes - - :return: a list of available columns for a given content uri - -EventFacade -========================= - -.. py:function:: eventClearBuffer() - - Clears all events from the event buffer - -.. py:function:: eventRegisterForBroadcast(category, enqueue) - - Registers a listener for a new broadcast signal - -.. py:function:: eventUnregisterForBroadcast(category) - - Stop listening for a broadcast signal - -.. py:function:: eventGetBrodcastCategories() - - Lists all the broadcast signals we are listening for - -.. py:function:: eventPoll(number_of_events) - - Returns and removes the oldest n events (i.e. location or sensor update, etc.) from the event buffer - - :return: A List of Maps of event properties - -.. py:function:: eventWaitFor(eventName, timeout) - - Blocks until an event with the supplied name occurs. The returned event is not removed from the buffer - - :return: Map of event properties - -.. py:function:: eventWait(timeout) - - Blocks until an event occurs. The returned event is removed from the buffer - - :return: Map of event properties - -.. py:function:: eventPost(name, data, enqueue) - - Post an event to the event queue - -.. py:function:: rpcPostEvent(name, data) - - Post an event to the event queue - -.. py:function:: receiveEvent() - - Returns and removes the oldest event (i.e. location or sensor update, etc.) from the event buffer - - :return: Map of event properties - -.. py:function:: waitForEvent(eventName, timeout) - - Blocks until an event with the supplied name occurs. The returned event is not removed from the buffer - - :return: Map of event properties - -.. py:function:: startEventDispatcher(port) - - Opens up a socket where you can read for events posted - -.. py:function:: stopEventDispatcher() - - Stops the event server, you can't read in the port anymore - -LocationFacade -========================= - -Providers APIs ------------------ - -.. py:function:: locationProviders() - - Returns availables providers on the phone - -.. py:function:: locationProviderEnabled(provider) - - Ask if provider is enabled - -Location APIs ------------------ -.. py:function:: startLocating(minDistance, minUpdateDistance) - - Starts collecting location data - -.. py:function:: readLocation() - - Returns the current location as indicated by all available providers - - :return: A map of location information by provider - -.. py:function:: stopLocating() - - Stops collecting location data - -.. py:function:: getLastKnownLocation() - - Returns the last known location of the device - - :return: A map of location information by provider - -*sample code* -:: - - Droid = androidhelper.Android() - location = Droid.getLastKnownLocation().result - location = location.get('network', location.get('gps')) - - -GEO ------------ -.. py:function:: geocode(latitude, longitude, maxResults) - - Returns a list of addresses for the given latitude and longitude - - :return: A list of addresses - -PhoneFacade -========================= - -PhoneStat APIs ----------------- - -.. py:function:: startTrackingPhoneState() - - Starts tracking phone state - -.. py:function:: readPhoneState() - - Returns the current phone state and incoming number - - :return: A Map of "state" and "incomingNumber" - -.. py:function:: stopTrackingPhoneState() - - Stops tracking phone state - - -Call & Dia APIs ----------------- - -.. py:function:: phoneCall(uri) - - Calls a contact/phone number by URI - -.. py:function:: phoneCallNumber(number) - - Calls a phone number - -.. py:function:: phoneDial(uri) - - Dials a contact/phone number by URI - -.. py:function:: phoneDialNumber(number) - - Dials a phone number - - - -Get information APIs ------------------------- -.. py:function:: getCellLocation() - - Returns the current cell location - -.. py:function:: getNetworkOperator() - - Returns the numeric name (MCC+MNC) of current registered operator - -.. py:function:: getNetworkOperatorName() - - Returns the alphabetic name of current registered operator - -.. py:function:: getNetworkType() - - Returns a the radio technology (network type) currently in use on the device - -.. py:function:: getPhoneType() - - Returns the device phone type - -.. py:function:: getSimCountryIso() - - Returns the ISO country code equivalent for the SIM provider's country code - -.. py:function:: getSimOperator() - - Returns the MCC+MNC (mobile country code + mobile network code) of the provider of the SIM. 5 or 6 decimal digits - -.. py:function:: getSimOperatorName() - - Returns the Service Provider Name (SPN) - -.. py:function:: getSimSerialNumber() - - Returns the serial number of the SIM, if applicable. Return null if it is unavailable - -.. py:function:: getSimState() - - Returns the state of the device SIM card - -.. py:function:: getSubscriberId() - - Returns the unique subscriber ID, for example, the IMSI for a GSM phone. Return null if it is unavailable - -.. py:function:: getVoiceMailAlphaTag() - - Retrieves the alphabetic identifier associated with the voice mail number - -.. py:function:: getVoiceMailNumber() - - Returns the voice mail number. Return null if it is unavailable - -.. py:function:: checkNetworkRoaming() - - Returns true if the device is considered roaming on the current network, for GSM purposes - -.. py:function:: getDeviceId() - - Returns the unique device ID, for example, the IMEI for GSM and the MEID for CDMA phones. Return null if device ID is not available - -.. py:function:: getDeviceSoftwareVersion() - - Returns the software version number for the device, for example, the IMEI/SV for GSM phones. Return null if the software version is not available - -.. py:function:: getLine1Number() - - Returns the phone number string for line 1, for example, the MSISDN for a GSM phone. Return null if it is unavailable - -.. py:function:: getNeighboringCellInfo() - - Returns the neighboring cell information of the device - -MediaRecorderFacade -========================= - - -Audio --------- - -.. py:function:: recorderStartMicrophone(targetPath) - - Records audio from the microphone and saves it to the given location - -Video APIs ------------ - -.. py:function:: recorderStartVideo(targetPath, duration, videoSize) - - Records video from the camera and saves it to the given location. - Duration specifies the maximum duration of the recording session. - If duration is 0 this method will return and the recording will only be stopped - when recorderStop is called or when a scripts exits. - Otherwise it will block for the time period equal to the duration argument. - videoSize: 0=160x120, 1=320x240, 2=352x288, 3=640x480, 4=800x480. - - -.. py:function:: recorderCaptureVideo(targetPath, duration, recordAudio) - - Records video (and optionally audio) from the camera and saves it to the given location. - Duration specifies the maximum duration of the recording session. - If duration is not provided this method will return immediately and the recording will only be stopped - when recorderStop is called or when a scripts exits. - Otherwise it will block for the time period equal to the duration argument. - -.. py:function:: startInteractiveVideoRecording(path) - - Starts the video capture application to record a video and saves it to the specified path - - -Stop --------- -.. py:function:: recorderStop() - - Stops a previously started recording - - -SensorManagerFacade -========================= - -Start & Stop -------------- -.. py:function:: startSensingTimed(sensorNumber, delayTime) - - Starts recording sensor data to be available for polling - -.. py:function:: startSensingThreshold(ensorNumber, threshold, axis) - - Records to the Event Queue sensor data exceeding a chosen threshold - -.. py:function:: startSensing(sampleSize) - - Starts recording sensor data to be available for polling - -.. py:function:: stopSensing() - - Stops collecting sensor data - -Read data APIs ---------------- -.. py:function:: readSensors() - - Returns the most recently recorded sensor data - -.. py:function:: sensorsGetAccuracy() - - Returns the most recently received accuracy value - -.. py:function:: sensorsGetLight() - - Returns the most recently received light value - -.. py:function:: sensorsReadAccelerometer() - - Returns the most recently received accelerometer values - - :return: a List of Floats [(acceleration on the) X axis, Y axis, Z axis] - -.. py:function:: sensorsReadMagnetometer() - - Returns the most recently received magnetic field values - - :return: a List of Floats [(magnetic field value for) X axis, Y axis, Z axis] - -.. py:function:: sensorsReadOrientation() - - Returns the most recently received orientation values - - :return: a List of Doubles [azimuth, pitch, roll] - -*sample code* -:: - - Droid = androidhelper.Android() - Droid.startSensingTimed(1, 250) - sensor = Droid.sensorsReadOrientation().result - Droid.stopSensing() - - -SettingsFacade -========================= - -Screen ----------- - -.. py:function:: setScreenTimeout(value) - - Sets the screen timeout to this number of seconds - - :return: The original screen timeout - -.. py:function:: getScreenTimeout() - - Gets the screen timeout - - :return: the current screen timeout in seconds - -AirplanerMode ---------------------- - -.. py:function:: checkAirplaneMode() - - Checks the airplane mode setting - - :return: True if airplane mode is enabled - -.. py:function:: toggleAirplaneMode(enabled) - - Toggles airplane mode on and off - - :return: True if airplane mode is enabled - -Ringer Silent Mode ---------------------- - -.. py:function:: checkRingerSilentMode() - - Checks the ringer silent mode setting - - :return: True if ringer silent mode is enabled - -.. py:function:: toggleRingerSilentMode(enabled) - - Toggles ringer silent mode on and off - - :return: True if ringer silent mode is enabled - -Vibrate Mode ---------------------- - -.. py:function:: toggleVibrateMode(enabled) - - Toggles vibrate mode on and off. If ringer=true then set Ringer setting, else set Notification setting - - :return: True if vibrate mode is enabled - -.. py:function:: getVibrateMode(ringer) - - Checks Vibration setting. If ringer=true then query Ringer setting, else query Notification setting - - :return: True if vibrate mode is enabled - -Ringer & Media Volume ---------------------- - -.. py:function:: getMaxRingerVolume() - - Returns the maximum ringer volume - -.. py:function:: getRingerVolume() - - Returns the current ringer volume - -.. py:function:: setRingerVolume(volume) - - Sets the ringer volume - -.. py:function:: getMaxMediaVolume() - - Returns the maximum media volume - -.. py:function:: getMediaVolume() - - Returns the current media volume - -.. py:function:: setMediaVolume(volume) - - Sets the media volume - -Screen Brightness ---------------------- - -.. py:function:: getScreenBrightness() - - Returns the screen backlight brightness - - :return: the current screen brightness between 0 and 255 - -.. py:function:: setScreenBrightness(value) - - Sets the the screen backlight brightness - - :return: the original screen brightness - -.. py:function:: checkScreenOn() - - Checks if the screen is on or off (requires API level 7) - - :return: True if the screen is currently on - - -SmsFacade -========================= - -.. py:function:: smsSend(destinationAddress, text) - - Sends an SMS - - :param str destinationAddress: typically a phone number - :param str text: - -.. py:function:: smsGetMessageCount(unreadOnly, folder) - - Returns the number of messages - - :param bool unreadOnly: typically a phone number - :param str folder(optional): default "inbox" - -.. py:function:: smsGetMessageIds(unreadOnly, folder) - - Returns a List of all message IDs - - :param bool unreadOnly: typically a phone number - :param str folder(optional): default "inbox" - -.. py:function:: smsGetMessages(unreadOnly, folder, attributes) - - Returns a List of all messages - - :param bool unreadOnly: typically a phone number - :param str folder: default "inbox" - :param list attributes(optional): attributes - - :return: a List of messages as Maps - -.. py:function:: smsGetMessageById(id, attributes) - - Returns message attributes - - :param int id: message ID - :param list attributes(optional): attributes - - :return: a List of messages as Maps - -.. py:function:: smsGetAttributes() - - Returns a List of all possible message attributes - -.. py:function:: smsDeleteMessage(id) - - Deletes a message - - :param int id: message ID - - :return: True if the message was deleted - -.. py:function:: smsMarkMessageRead(ids, read) - - Marks messages as read - - :param list ids: List of message IDs to mark as read - :param bool read: true or false - - :return: number of messages marked read - -SpeechRecognitionFacade -========================= - -.. py:function:: recognizeSpeech(prompt, language, languageModel) - - Recognizes user's speech and returns the most likely result - - :param str prompt(optional): text prompt to show to the user when asking them to speak - :param str language(optional): language override to inform the recognizer that it should expect speech in a language different than the one set in the java.util.Locale.getDefault() - :param str languageModel(optional): informs the recognizer which speech model to prefer (see android.speech.RecognizeIntent) - - :return: An empty string in case the speech cannot be recongnized - - -ToneGeneratorFacade -========================= - -.. py:function:: generateDtmfTones(phoneNumber, toneDuration) - - Generate DTMF tones for the given phone number - - :param str phoneNumber: phone number - :param int toneDuration(optional): default 100, duration of each tone in milliseconds - - -WakeLockFacade -========================= - -.. py:function:: wakeLockAcquireFull() - - Acquires a full wake lock (CPU on, screen bright, keyboard bright) - -.. py:function:: wakeLockAcquirePartial() - - Acquires a partial wake lock (CPU on) - -.. py:function:: wakeLockAcquireBright() - - Acquires a bright wake lock (CPU on, screen bright) - -.. py:function:: wakeLockAcquireDim() - - Acquires a dim wake lock (CPU on, screen dim) - -.. py:function:: wakeLockRelease() - - Releases the wake lock - -WifiFacade -========================= - -.. py:function:: wifiGetScanResults() - - Returns the list of access points found during the most recent Wifi scan - -.. py:function:: wifiLockAcquireFull() - - Acquires a full Wifi lock - -.. py:function:: wifiLockAcquireScanOnly() - - Acquires a scan only Wifi lock - -.. py:function:: wifiLockRelease() - - Releases a previously acquired Wifi lock - -.. py:function:: wifiStartScan() - - Starts a scan for Wifi access points - - :return: True if the scan was initiated successfully - -.. py:function:: checkWifiState() - - Checks Wifi state - - :return: True if Wifi is enabled - -.. py:function:: toggleWifiState(enabled) - - Toggle Wifi on and off - - :param bool enabled(optional): enabled - - :return: True if Wifi is enabled - -.. py:function:: wifiDisconnect() - - Disconnects from the currently active access point - - :return: True if the operation succeeded - -.. py:function:: wifiGetConnectionInfo() - - Returns information about the currently active access point - -.. py:function:: wifiReassociate() - - Returns information about the currently active access point - - :return: True if the operation succeeded - -.. py:function:: wifiReconnect() - - Reconnects to the currently active access point - - :return: True if the operation succeeded - - -BatteryManagerFacade -========================= - -.. py:function:: readBatteryData() - - Returns the most recently recorded battery data - -.. py:function:: batteryStartMonitoring() - - Starts tracking battery state - -.. py:function:: batteryStopMonitoring() - - Stops tracking battery state - -.. py:function:: batteryGetStatus() - - Returns the most recently received battery status data: - 1 - unknown; - 2 - charging; - 3 - discharging; - 4 - not charging; - 5 - full - -.. py:function:: batteryGetHealth() - - Returns the most recently received battery health data: - 1 - unknown; - 2 - good; - 3 - overheat; - 4 - dead; - 5 - over voltage; - 6 - unspecified failure - -.. py:function:: batteryGetPlugType() - - Returns the most recently received plug type data: - -1 - unknown - 0 - unplugged - 1 - power source is an AC charger - 2 - power source is a USB port - - -.. py:function:: batteryCheckPresent() - - Returns the most recently received battery presence data - -.. py:function:: batteryGetLevel() - - Returns the most recently received battery level (percentage) - -.. py:function:: batteryGetVoltage() - - Returns the most recently received battery voltage - -.. py:function:: batteryGetTemperature() - - Returns the most recently received battery temperature - -.. py:function:: batteryGetTechnology() - - Returns the most recently received battery technology data - - -ActivityResultFacade -========================= - -.. py:function:: setResultBoolean(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - - -.. py:function:: setResultByte(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultShort(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultChar(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - - -.. py:function:: setResultInteger(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultLong(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultFloat(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultDouble(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultString(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultBooleanArray(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultByteArray(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultShortArray(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultCharArray(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultIntegerArray(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultLongArray(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultFloatArray(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultDoubleArray(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultStringArray(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - -.. py:function:: setResultSerializable(resultCode, resultValue) - - Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), - the resulting intent will contain SCRIPT_RESULT extra with the given value - - :param int resultCode: - :param byte resultValue: - - -MediaPlayerFacade -========================= - -Control ------------------ -.. py:function:: mediaPlay(url, tag, play) - - Open a media file - - :param str url: url of media resource - :param str tag(optional): string identifying resource (default=default) - :param bool play(optional): start playing immediately - - :return: true if play successful - -.. py:function:: mediaPlayPause(tag) - - pause playing media file - - :param str tag: string identifying resource (default=default) - - :return: true if successful - -.. py:function:: mediaPlayStart(tag) - - start playing media file - - :param str tag: string identifying resource (default=default) - - :return: true if successful - -.. py:function:: mediaPlayClose(tag) - - Close media file - - :param str tag: string identifying resource (default=default) - - :return: true if successful - -.. py:function:: mediaIsPlaying(tag) - - Checks if media file is playing - - :param str tag: string identifying resource (default=default) - - :return: true if successful - - -.. py:function:: mediaPlaySetLooping(enabled, tag) - - Set Looping - - :param bool enabled: default true - :param str tag: string identifying resource (default=default) - - :return: True if successful - -.. py:function:: mediaPlaySeek(msec, tag) - - Seek To Position - - :param int msec: default true - :param str tag: string identifying resource (default=default) - - :return: New Position (in ms) - -Get Information ------------------ -.. py:function:: mediaPlayInfo(tag) - - Information on current media - - :param str tag: string identifying resource (default=default) - - :return: Media Information - -.. py:function:: mediaPlayList() - - Lists currently loaded media - - :return: List of Media Tags - - -PreferencesFacade -========================= - -.. py:function:: prefGetValue(key, filename) - - Read a value from shared preferences - - :param str key: key - :param str filename(optional): Desired preferences file. If not defined, uses the default Shared Preferences. - - -.. py:function:: prefPutValue(key, value, filename) - - Write a value to shared preferences - - :param str key: key - :param str value: value - :param str filename(optional): Desired preferences file. If not defined, uses the default Shared Preferences. - -.. py:function:: prefGetAll(filename) - - Get list of Shared Preference Values - - :param str filename(optional): Desired preferences file. If not defined, uses the default Shared Preferences. - - -QPyInterfaceFacade -========================= - -.. py:function:: executeQPy(script) - - Execute a qpython script by absolute path - - :param str script: The absolute path of the qpython script - - :return: bool - - -TextToSpeechFacade -========================= - -.. py:function:: ttsSpeak(message) - - Speaks the provided message via TTS - - :param str message: message - -.. py:function:: ttsIsSpeaking() - - Returns True if speech is currently in progress - -EyesFreeFacade -========================= - -.. py:function:: ttsSpeak(message) - - Speaks the provided message via TTS - - :param str message: message - - -BluetoothFacade -========================= - -.. py:function:: bluetoothActiveConnections() - - Returns active Bluetooth connections - - -.. py:function:: bluetoothWriteBinary(base64, connID) - - Send bytes over the currently open Bluetooth connection - - :param str base64: A base64 encoded String of the bytes to be sent - :param str connID(optional): Connection id - -.. py:function:: bluetoothReadBinary(bufferSize, connID) - - Read up to bufferSize bytes and return a chunked, base64 encoded string - - :param int bufferSize: default 4096 - :param str connID(optional): Connection id - -.. py:function:: bluetoothConnect(uuid, address) - - Connect to a device over Bluetooth. Blocks until the connection is established or fails - - :param str uuid: The UUID passed here must match the UUID used by the server device - :param str address(optional): The user will be presented with a list of discovered devices to choose from if an address is not provided - - :return: True if the connection was established successfully - -.. py:function:: bluetoothAccept(uuid, timeout) - - Listens for and accepts a Bluetooth connection. Blocks until the connection is established or fails - - :param str uuid: The UUID passed here must match the UUID used by the server device - :param int timeout: How long to wait for a new connection, 0 is wait for ever (default=0) - -.. py:function:: bluetoothMakeDiscoverable(duration) - - Requests that the device be discoverable for Bluetooth connections - - :param int duration: period of time, in seconds, during which the device should be discoverable (default=300) - -.. py:function:: bluetoothWrite(ascii, connID) - - Sends ASCII characters over the currently open Bluetooth connection - - :param str ascii: text - :param str connID: Connection id - -.. py:function:: bluetoothReadReady(connID) - - Sends ASCII characters over the currently open Bluetooth connection - - :param str ascii: text - :param str connID: Connection id - -.. py:function:: bluetoothRead(bufferSize, connID) - - Read up to bufferSize ASCII characters - - :param int bufferSize: default=4096 - :param str connID(optional): Connection id - -.. py:function:: bluetoothReadLine(connID) - - Read the next line - - :param str connID(optional): Connection id - -.. py:function:: bluetoothGetRemoteDeviceName(address) - - Queries a remote device for it's name or null if it can't be resolved - - :param str address: Bluetooth Address For Target Device - -.. py:function:: bluetoothGetLocalName() - - Gets the Bluetooth Visible device name - -.. py:function:: bluetoothSetLocalName(name) - - Sets the Bluetooth Visible device name, returns True on success - - :param str name: New local name - -.. py:function:: bluetoothGetScanMode() - - Gets the scan mode for the local dongle. - Return values: - -1 when Bluetooth is disabled. - 0 if non discoverable and non connectable. - 1 connectable non discoverable. - 3 connectable and discoverable. - -.. py:function:: bluetoothGetConnectedDeviceName(connID) - - Returns the name of the connected device - - :param str connID: Connection id - -.. py:function:: checkBluetoothState() - - Checks Bluetooth state - - :return: True if Bluetooth is enabled - -.. py:function:: toggleBluetoothState(enabled, prompt) - - Toggle Bluetooth on and off - - :param bool enabled: - :param str prompt: Prompt the user to confirm changing the Bluetooth state, default=true - - :return: True if Bluetooth is enabled - -.. py:function:: bluetoothStop(connID) - - Stops Bluetooth connection - - :param str connID: Connection id - -.. py:function:: bluetoothGetLocalAddress() - - Returns the hardware address of the local Bluetooth adapter - -.. py:function:: bluetoothDiscoveryStart() - - Start the remote device discovery process - - :return: true on success, false on error - -.. py:function:: bluetoothDiscoveryCancel() - - Cancel the current device discovery process - - :return: true on success, false on error - -.. py:function:: bluetoothIsDiscovering() - - Return true if the local Bluetooth adapter is currently in the device discovery process - - -SignalStrengthFacade -========================= -.. py:function:: startTrackingSignalStrengths() - - Starts tracking signal strengths - -.. py:function:: readSignalStrengths() - - Returns the current signal strengths - - :return: A map of gsm_signal_strength - -.. py:function:: stopTrackingSignalStrengths() - - Stops tracking signal strength - - -WebCamFacade -========================= - -.. py:function:: webcamStart(resolutionLevel, jpegQuality, port) - - Starts an MJPEG stream and returns a Tuple of address and port for the stream - - :param int resolutionLevel: increasing this number provides higher resolution (default=0) - :param int jpegQuality: a number from 0-10 (default=20) - :param int port: If port is specified, the webcam service will bind to port, otherwise it will pick any available port (default=0) - -.. py:function:: webcamAdjustQuality(resolutionLevel, jpegQuality) - - Adjusts the quality of the webcam stream while it is running - - :param int resolutionLevel: increasing this number provides higher resolution (default=0) - :param int jpegQuality: a number from 0-10 (default=20) - -.. py:function:: cameraStartPreview(resolutionLevel, jpegQuality, filepath) - - Start Preview Mode. Throws 'preview' events - - :param int resolutionLevel: increasing this number provides higher resolution (default=0) - :param int jpegQuality: a number from 0-10 (default=20) - :param str filepath: Path to store jpeg files - - :return: True if successful - -.. py:function:: cameraStopPreview() - - Stop the preview mode - - -UiFacade -========================= - -Dialog --------- -.. py:function:: dialogCreateInput(title, message, defaultText, inputType) - - Create a text input dialog - - :param str title: title of the input box - :param str message: message to display above the input box - :param str defaultText(optional): text to insert into the input box - :param str inputType(optional): type of input data, ie number or text - -.. py:function:: dialogCreatePassword(title, message) - - Create a password input dialog - - :param str title: title of the input box - :param str message: message to display above the input box - -.. py:function:: dialogGetInput(title, message, defaultText) - - Create a password input dialog - - :param str title: title of the input box - :param str message: message to display above the input box - :param str defaultText(optional): text to insert into the input box - -.. py:function:: dialogGetPassword(title, message) - - Queries the user for a password - - :param str title: title of the password box - :param str message: message to display above the input box - -.. py:function:: dialogCreateSeekBar(start, maximum, title) - - Create seek bar dialog - - :param int start: default=50 - :param int maximum: default=100 - :param int title: title - -.. py:function:: dialogCreateTimePicker(hour, minute, is24hour) - - Create time picker dialog - - :param int hour: default=0 - :param int miute: default=0 - :param bool is24hour: default=false - -.. py:function:: dialogCreateDatePicker(year, month, day) - - Create date picker dialog - - :param int year: default=1970 - :param int month: default=1 - :param int day: default=1 - - -NFC -------------- -**Data structs** -*QPython NFC json result* -:: - - { - "role": , # could be self/master/slave - "stat": , # could be ok / fail / cancl - "message": - } - -**APIs** - -.. py:function:: dialogCreateNFCBeamMaster(title, message, inputType) - - Create a dialog where you could create a qpython beam master - - :param str title: title of the input box - :param str message: message to display above the input box - :param str inputType(optional): type of input data, ie number or text - -.. py:function:: NFCBeamMessage(content, title, message) - - Create a dialog where you could create a qpython beam master - - :param str content: message you want to sent - :param str title: title of the input box - :param str message: message to display above the input box - :param str inputType(optional): type of input data, ie number or text - -.. py:function:: dialogCreateNFCBeamSlave(title, message) - - Create a qpython beam slave - - :param str title: title of the input box - :param str message: message to display above the input box - -Progress --------------- -.. py:function:: dialogCreateSpinnerProgress(message, maximumProgress) - - Create a spinner progress dialog - - :param str message(optional): message - :param int maximunProgress(optional): dfault=100 - -.. py:function:: dialogSetCurrentProgress(current) - - Set progress dialog current value - - :param int current: current - -.. py:function:: dialogSetMaxProgress(max) - - Set progress dialog maximum value - - :param int max: max - - -.. py:function:: dialogCreateHorizontalProgress(title, message, maximumProgress) - - Create a horizontal progress dialog - - :param str title(optional): title - :param str message(optional): message - :param int maximunProgress(optional): dfault=100 - - -Alert ----------- -.. py:function:: dialogCreateAlert(title, message) - - Create alert dialog - - :param str title(optional): title - :param str message(optional): message - :param int maximunProgress(optional): dfault=100 - - -Dialog Control ---------------- -.. py:function:: dialogSetPositiveButtonText(text) - - Set alert dialog positive button text - - :param str text: text - -.. py:function:: dialogSetNegativeButtonText(text) - - Set alert dialog negative button text - - :param str text: text - -.. py:function:: dialogSetNeutralButtonText(text) - - Set alert dialog button text - - :param str text: text - -.. py:function:: dialogSetItems(items) - - Set alert dialog list items - - :param list items: items - -.. py:function:: dialogSetSingleChoiceItems(items, selected) - - Set alert dialog list items - - :param list items: items - :param int selected: selected item index (default=0) - -.. py:function:: dialogSetMultiChoiceItems(items, selected) - - Set dialog multiple choice items and selection - - :param list items: items - :param int selected: selected item index (default=0) - -.. py:function:: addContextMenuItem(label, event, eventData) - - Adds a new item to context menu - - :param str label: label for this menu item - :param str event: event that will be generated on menu item click - :param object eventData: event object - -.. py:function:: addOptionsMenuItem(label, event, eventData, iconName) - - Adds a new item to context menu - - :param str label: label for this menu item - :param str event: event that will be generated on menu item click - :param object eventData: event object - :param str iconName: Android system menu icon, see http://developer.android.com/reference/android/R.drawable.html - -.. py:function:: dialogGetResponse() - - Returns dialog response - -.. py:function:: dialogGetSelectedItems() - - This method provides list of items user selected - -.. py:function:: dialogDismiss() - - Dismiss dialog - -.. py:function:: dialogShow() - - Show dialog - - -Layout ---------- -.. py:function:: fullShow(layout) - - Show Full Screen - - :param string layout: String containing View layout - -.. py:function:: fullDismiss() - - Dismiss Full Screen - -.. py:function:: fullQuery() - - Get Fullscreen Properties - -.. py:function:: fullQueryDetail(id) - - Get fullscreen properties for a specific widget - - :param str id: id of layout widget - -.. py:function:: fullSetProperty(id) - - Set fullscreen widget property - - :param str id: id of layout widget - :param str property: name of property to set - :param str value: value to set property to - -.. py:function:: fullSetList(id, list) - - Attach a list to a fullscreen widget - - :param str id: id of layout widget - :param list list: List to set - -.. py:function:: fullKeyOverride(keycodes, enable) - - Override default key actions - - :param str keycodes: id of layout widget - :param bool enable: List to set (default=true) - - - -WebView ------------ -.. py:function:: webViewShow() - - Display a WebView with the given URL - - :param str url: url - :param bool wait(optional): block until the user exits the WebView - -USB Host Serial Facade -====================== - -*QPython 1.3.1+ and QPython3 1.0.3+ contains this feature* - -SL4A Facade for USB Serial devices by Android USB Host API. - - -It control the USB-Serial like devices -from Andoroid which has USB Host Controller . - -The sample -`demonstration is also available at youtube video `_ - - -Requirements -------------- -* Android device which has USB Host controller (and enabled in that firmware). -* Android 4.0 (API14) or later. -* USB Serial devices (see [Status](#Status)). -* USB Serial devices were not handled by Android kernel. - - > I heard some android phone handle USB Serial devices - > make /dev/ttyUSB0 in kernel level. - > In this case, Android does not be able to handle the device - > from OS level. - - please check Android Applications be able to grab the target USB Devices, - such as `USB Device Info `_. - -Status ---------------- -* probably work with USB CDC, like FTDI, Arduino or else. - -* 2012/09/10: work with 78K0F0730 device (new RL78) with Tragi BIOS board. - - `M78K0F0730 `_ - -* 2012/09/24: work with some pl2303 devcies. - -Author -------- -This facade developped by `Kuri65536 `_ -you can see the commit log in it. - - -APIs --------- -.. py:function:: usbserialGetDeviceList() - - Returns USB devices reported by USB Host API. - - :return: Returns "Map of id and string information Map - - -.. py:function:: usbserialDisconnect(connID) - - Disconnect all USB-device - - :param str connID: connection ID - -.. py:function:: usbserialActiveConnections() - - Returns active USB-device connections. - - :return: Returns "Active USB-device connections by Map UUID vs device-name." - - -.. py:function:: usbserialWriteBinary(base64, connID) - - Send bytes over the currently open USB Serial connection. - - :param str base64: - :param str connId: - -.. py:function:: usbserialReadBinary(bufferSize, connID) - - Read up to bufferSize bytes and return a chunked, base64 encoded string - - :param int bufferSize: - :param str connId: - -.. py:function:: usbserialConnect(hash, options) - - Connect to a device with USB-Host. request the connection and exit - - :param str hash: - :param str options: - - :return: Returns messages the request status - -.. py:function:: usbserialHostEnable() - - Requests that the host be enable for USB Serial connections. - - :return: True if the USB Device is accesible - -.. py:function:: usbserialWrite(String ascii, String connID) - - Sends ASCII characters over the currently open USB Serial connection - - :param str ascii: - :param str connID: - -.. py:function:: usbserialReadReady(connID) - - :param str connID: - - :return: True if the next read is guaranteed not to block - - -.. py:function:: usbserialRead(connID, bufferSize) - - Read up to bufferSize ASCII characters. - - :param str connID: - :param int bufferSize: - -.. py:function:: usbserialGetDeviceName(connID) - - Queries a remote device for it's name or null if it can't be resolved - - :param str connID: +QPysl4a APIs +============ + +The Scripting Layer for Android (abridged as SL4A, and previously named Android Scripting Environment or ASE) is a library that allows the creation and running of scripts written in various scripting languages directly on Android devices. After having integrated it, QPython continue to develop and extend SL4A project to meet QPython's need, it's QPysl4a project. + + +AndroidFacade +=============== + +Clipboard APIs +---------------- +.. py:function:: setClipboard(text) + + Put text in the clipboard + + :param str text: text + +.. py:function:: getClipboard(text) + + Read text from the clipboard + + :return: The text in the clipboard + + +:: + + from androidhelper import Android + droid = Android() + + #setClipboard + droid.setClipboard("Hello World") + + #getClipboard + clipboard = droid.getClipboard().result + + +Intent & startActivity APIs +---------------------------------- +.. py:function:: makeIntent(action, uri, type, extras, categories, packagename, classname, flags) + + Starts an activity and returns the result + + :param str action: action + :param str uri(Optional): uri + :param str type(Optional): MIME type/subtype of the URI + :param object extras(Optional): a Map of extras to add to the Intent + :param list categories(Optional): a List of categories to add to the Intent + :param str packagename(Optional): name of package. If used, requires classname to be useful + :param str classname(Optional): name of class. If used, requires packagename to be useful + :param int flags(Optional): Intent flags + + :return: An object representing an Intent + + +:: + + sample code to show makeIntent + + +.. py:function:: getIntent() + + Returns the intent that launched the script + +:: + + sample code to show getIntent + + +.. py:function:: startActivityForResult(action, uri, type, extras, packagename, classname) + + Starts an activity and returns the result + + :param str action: action + :param str uri(Optional): uri + :param str type(Optional): MIME type/subtype of the URI + :param object extras(Optional): a Map of extras to add to the Intent + :param str packagename(Optional): name of package. If used, requires classname to be useful + :param str classname(Optional): name of class. If used, requires packagename to be useful + + :return: A Map representation of the result Intent + + +:: + + sample code to show startActivityForResult + + +.. py:function:: startActivityForResultIntent(intent) + + Starts an activity and returns the result + + :param Intent intent: Intent in the format as returned from makeIntent + + :return: A Map representation of the result Intent + + +:: + + sample code to show startActivityForResultIntent + +.. py:function:: startActivityIntent(intent, wait) + + Starts an activity + + :param Intent intent: Intent in the format as returned from makeIntent + :param bool wait(Optional): block until the user exits the started activity + +:: + + sample code to show startActivityIntent + + +.. py:function:: startActivity(action, uri, type, extras, wait, packagename, classname) + + Starts an activity + + :param str action: action + :param str uri(Optional): uri + :param str type(Optional): MIME type/subtype of the URI + :param object extras(Optional): a Map of extras to add to the Intent + :param bool wait(Optional): block until the user exits the started activity + :param str packagename(Optional): name of package. If used, requires classname to be useful + :param str classname(Optional): name of class. If used, requires packagename to be useful + +:: + + sample code to show startActivityForResultIntent + + +SendBroadcast APIs +------------------- +.. py:function:: sendBroadcast(action, uri, type, extras, packagename, classname) + + Send a broadcast + + :param str action: action + :param str uri(Optional): uri + :param str type(Optional): MIME type/subtype of the URI + :param object extras(Optional): a Map of extras to add to the Intent + :param str packagename(Optional): name of package. If used, requires classname to be useful + :param str classname(Optional): name of class. If used, requires packagename to be useful + + +:: + + sample code to show sendBroadcast + +.. py:function:: sendBroadcastIntent(intent) + + Send a broadcast + + :param Intent intent: Intent in the format as returned from makeIntent + +:: + + sample code to show sendBroadcastIntent + + +Vibrate +---------- +.. py:function:: vibrate(intent) + + Vibrates the phone or a specified duration in milliseconds + + :param int duration: duration in milliseconds + +:: + + sample code to show vibrate + + +NetworkStatus +--------------- +.. py:function:: getNetworkStatus() + + Returns the status of network connection + +:: + + sample code to show getNetworkStatus + +PackageVersion APIs +------------------------------ +.. py:function:: requiredVersion(requiredVersion) + + Checks if version of QPython SL4A is greater than or equal to the specified version + + :param int requiredVersion: requiredVersion + + :return: true or false + + +.. py:function:: getPackageVersionCode(packageName) + + Returns package version code + + :param str packageName: packageName + + :return: Package version code + +.. py:function:: getPackageVersion(packageName) + + Returns package version name + + :param str packageName: packageName + + :return: Package version name + + +:: + + sample code to show getPackageVersionCode & getPackageVersion + + +System APIs +-------------------------------- +.. py:function:: getConstants(classname) + + Get list of constants (static final fields) for a class + + :param str classname: classname + + :return: list + +:: + + sample code to show getConstants + +.. py:function:: environment() + + A map of various useful environment details + + :return: environment map object includes id, display, offset, TZ, SDK, download, appcache, availblocks, blocksize, blockcount, sdcard + +:: + + sample code to show environment + +.. py:function:: log(message) + + Writes message to logcat + + :param str message: message + +:: + + sample code to show log + + +SendEmail +---------- +.. py:function:: sendEmail(to, subject, body, attachmentUri) + + Launches an activity that sends an e-mail message to a given recipient + + :param str to: A comma separated list of recipients + :param str subject: subject + :param str body: mail body + :param str attachmentUri(Optional): message + +:: + + sample code to show sendEmail + + +Toast, getInput, getPassword, notify APIs +------------------------------------------------ +.. py:function:: makeToast(message) + + Displays a short-duration Toast notification + + :param str message: message + +:: + + sample code to show makeToast + +.. py:function:: getInput(title, message) + + Queries the user for a text input + + :param str title: title of the input box + :param str message: message to display above the input box + +:: + + sample code to show getInput + +.. py:function:: getPassword(title, message) + + Queries the user for a password + + :param str title: title of the input box + :param str message: message to display above the input box + +:: + + sample code to show getPassword + +.. py:function:: notify(title, message, url) + + Displays a notification that will be canceled when the user clicks on it + + :param str title: title + :param str message: message + :param str url(optional): url + +:: + + import androidhelper + droid = androidhelper.Android() + droid.notify('Hello','QPython','http://qpython.org') # you could set the 3rd parameter None also + + + +ApplicationManagerFacade +========================= + +Manager APIs +------------- + +.. py:function:: getLaunchableApplications() + + 获得可运行的应用类名 + + :return: map object + + + +.. py:function:: launch(classname) + + 根据给定的类名启动一个 activity + + :param str classname: classname + + + +.. py:function:: getRunningPackages() + + 返回正在运行的 activity 或者服务的列表 + + :return: List of packages running activities + + + +.. py:function:: forceStopPackage(packageName) + + Force stops a package + + :param str packageName: packageName + +:: + + sample code to show forceStopPackage + + +CameraFacade +========================= + +.. py:function:: cameraCapturePicture(targetPath) + + 拍照并保存到指定的路径 + + :return: A map of Booleans autoFocus and takePicture where True indicates success + +.. py:function:: cameraInteractiveCapturePicture(targetPath) + + 启动拍照程序拍照并保存到指定位置 + +CommonIntentsFacade +========================= + +Barcode +---------- +.. py:function:: scanBarcode() + + Starts the barcode scanner + + :return: A Map representation of the result Intent + +View APIs +---------- +.. py:function:: pick(uri) + + Display content to be picked by URI (e.g. contacts) + + :return: A map of result values + +.. py:function:: view(uri, type, extras) + + Start activity with view action by URI (i.e. browser, contacts, etc.) + +.. py:function:: viewMap(query) + + Opens a map search for query (e.g. pizza, 123 My Street) + +.. py:function:: viewContacts() + + Opens the list of contacts + +.. py:function:: viewHtml(path) + + Opens the browser to display a local HTML file + +.. py:function:: search(query) + + Starts a search for the given query + +ContactsFacade +========================= + +.. py:function:: pickContact() + + Displays a list of contacts to pick from + + :return: A map of result values + +.. py:function:: pickPhone() + + Displays a list of phone numbers to pick from + + :return: The selected phone number + +.. py:function:: contactsGetAttributes() + + Returns a List of all possible attributes for contacts + + :return: a List of contacts as Maps + +.. py:function:: contactsGetIds() + + Returns a List of all contact IDs + +.. py:function:: contactsGet(attributes) + + Returns a List of all contacts + +.. py:function:: contactsGetById(id) + + Returns contacts by ID + +.. py:function:: contactsGetCount() + + Returns the number of contacts + +.. py:function:: queryContent(uri, attributes, selection, selectionArgs, order) + + Content Resolver Query + + :return: result of query as Maps + +.. py:function:: queryAttributes(uri) + + Content Resolver Query Attributes + + :return: a list of available columns for a given content uri + +EventFacade +========================= + +.. py:function:: eventClearBuffer() + + Clears all events from the event buffer + +.. py:function:: eventRegisterForBroadcast(category, enqueue) + + Registers a listener for a new broadcast signal + +.. py:function:: eventUnregisterForBroadcast(category) + + Stop listening for a broadcast signal + +.. py:function:: eventGetBrodcastCategories() + + Lists all the broadcast signals we are listening for + +.. py:function:: eventPoll(number_of_events) + + Returns and removes the oldest n events (i.e. location or sensor update, etc.) from the event buffer + + :return: A List of Maps of event properties + +.. py:function:: eventWaitFor(eventName, timeout) + + Blocks until an event with the supplied name occurs. The returned event is not removed from the buffer + + :return: Map of event properties + +.. py:function:: eventWait(timeout) + + Blocks until an event occurs. The returned event is removed from the buffer + + :return: Map of event properties + +.. py:function:: eventPost(name, data, enqueue) + + Post an event to the event queue + +.. py:function:: rpcPostEvent(name, data) + + Post an event to the event queue + +.. py:function:: receiveEvent() + + Returns and removes the oldest event (i.e. location or sensor update, etc.) from the event buffer + + :return: Map of event properties + +.. py:function:: waitForEvent(eventName, timeout) + + Blocks until an event with the supplied name occurs. The returned event is not removed from the buffer + + :return: Map of event properties + +.. py:function:: startEventDispatcher(port) + + Opens up a socket where you can read for events posted + +.. py:function:: stopEventDispatcher() + + Stops the event server, you can't read in the port anymore + +LocationFacade +========================= + +Providers APIs +----------------- + +.. py:function:: locationProviders() + + Returns availables providers on the phone + +.. py:function:: locationProviderEnabled(provider) + + Ask if provider is enabled + +Location APIs +----------------- +.. py:function:: startLocating(minDistance, minUpdateDistance) + + Starts collecting location data + +.. py:function:: readLocation() + + Returns the current location as indicated by all available providers + + :return: A map of location information by provider + +.. py:function:: stopLocating() + + Stops collecting location data + +.. py:function:: getLastKnownLocation() + + Returns the last known location of the device + + :return: A map of location information by provider + +*sample code* +:: + + Droid = androidhelper.Android() + location = Droid.getLastKnownLocation().result + location = location.get('network', location.get('gps')) + + +GEO +----------- +.. py:function:: geocode(latitude, longitude, maxResults) + + Returns a list of addresses for the given latitude and longitude + + :return: A list of addresses + +PhoneFacade +========================= + +PhoneStat APIs +---------------- + +.. py:function:: startTrackingPhoneState() + + Starts tracking phone state + +.. py:function:: readPhoneState() + + Returns the current phone state and incoming number + + :return: A Map of "state" and "incomingNumber" + +.. py:function:: stopTrackingPhoneState() + + Stops tracking phone state + + +Call & Dia APIs +---------------- + +.. py:function:: phoneCall(uri) + + Calls a contact/phone number by URI + +.. py:function:: phoneCallNumber(number) + + Calls a phone number + +.. py:function:: phoneDial(uri) + + Dials a contact/phone number by URI + +.. py:function:: phoneDialNumber(number) + + Dials a phone number + + + +Get information APIs +------------------------ +.. py:function:: getCellLocation() + + Returns the current cell location + +.. py:function:: getNetworkOperator() + + Returns the numeric name (MCC+MNC) of current registered operator + +.. py:function:: getNetworkOperatorName() + + Returns the alphabetic name of current registered operator + +.. py:function:: getNetworkType() + + Returns a the radio technology (network type) currently in use on the device + +.. py:function:: getPhoneType() + + Returns the device phone type + +.. py:function:: getSimCountryIso() + + Returns the ISO country code equivalent for the SIM provider's country code + +.. py:function:: getSimOperator() + + Returns the MCC+MNC (mobile country code + mobile network code) of the provider of the SIM. 5 or 6 decimal digits + +.. py:function:: getSimOperatorName() + + Returns the Service Provider Name (SPN) + +.. py:function:: getSimSerialNumber() + + Returns the serial number of the SIM, if applicable. Return null if it is unavailable + +.. py:function:: getSimState() + + Returns the state of the device SIM card + +.. py:function:: getSubscriberId() + + Returns the unique subscriber ID, for example, the IMSI for a GSM phone. Return null if it is unavailable + +.. py:function:: getVoiceMailAlphaTag() + + Retrieves the alphabetic identifier associated with the voice mail number + +.. py:function:: getVoiceMailNumber() + + Returns the voice mail number. Return null if it is unavailable + +.. py:function:: checkNetworkRoaming() + + Returns true if the device is considered roaming on the current network, for GSM purposes + +.. py:function:: getDeviceId() + + Returns the unique device ID, for example, the IMEI for GSM and the MEID for CDMA phones. Return null if device ID is not available + +.. py:function:: getDeviceSoftwareVersion() + + Returns the software version number for the device, for example, the IMEI/SV for GSM phones. Return null if the software version is not available + +.. py:function:: getLine1Number() + + Returns the phone number string for line 1, for example, the MSISDN for a GSM phone. Return null if it is unavailable + +.. py:function:: getNeighboringCellInfo() + + Returns the neighboring cell information of the device + +MediaRecorderFacade +========================= + + +Audio +-------- + +.. py:function:: recorderStartMicrophone(targetPath) + + Records audio from the microphone and saves it to the given location + +Video APIs +----------- + +.. py:function:: recorderStartVideo(targetPath, duration, videoSize) + + Records video from the camera and saves it to the given location. + Duration specifies the maximum duration of the recording session. + If duration is 0 this method will return and the recording will only be stopped + when recorderStop is called or when a scripts exits. + Otherwise it will block for the time period equal to the duration argument. + videoSize: 0=160x120, 1=320x240, 2=352x288, 3=640x480, 4=800x480. + + +.. py:function:: recorderCaptureVideo(targetPath, duration, recordAudio) + + Records video (and optionally audio) from the camera and saves it to the given location. + Duration specifies the maximum duration of the recording session. + If duration is not provided this method will return immediately and the recording will only be stopped + when recorderStop is called or when a scripts exits. + Otherwise it will block for the time period equal to the duration argument. + +.. py:function:: startInteractiveVideoRecording(path) + + Starts the video capture application to record a video and saves it to the specified path + + +Stop +-------- +.. py:function:: recorderStop() + + Stops a previously started recording + + +SensorManagerFacade +========================= + +Start & Stop +------------- +.. py:function:: startSensingTimed(sensorNumber, delayTime) + + Starts recording sensor data to be available for polling + +.. py:function:: startSensingThreshold(ensorNumber, threshold, axis) + + Records to the Event Queue sensor data exceeding a chosen threshold + +.. py:function:: startSensing(sampleSize) + + Starts recording sensor data to be available for polling + +.. py:function:: stopSensing() + + Stops collecting sensor data + +Read data APIs +--------------- +.. py:function:: readSensors() + + Returns the most recently recorded sensor data + +.. py:function:: sensorsGetAccuracy() + + Returns the most recently received accuracy value + +.. py:function:: sensorsGetLight() + + Returns the most recently received light value + +.. py:function:: sensorsReadAccelerometer() + + Returns the most recently received accelerometer values + + :return: a List of Floats [(acceleration on the) X axis, Y axis, Z axis] + +.. py:function:: sensorsReadMagnetometer() + + Returns the most recently received magnetic field values + + :return: a List of Floats [(magnetic field value for) X axis, Y axis, Z axis] + +.. py:function:: sensorsReadOrientation() + + Returns the most recently received orientation values + + :return: a List of Doubles [azimuth, pitch, roll] + +*sample code* +:: + + Droid = androidhelper.Android() + Droid.startSensingTimed(1, 250) + sensor = Droid.sensorsReadOrientation().result + Droid.stopSensing() + + +SettingsFacade +========================= + +Screen +---------- + +.. py:function:: setScreenTimeout(value) + + Sets the screen timeout to this number of seconds + + :return: The original screen timeout + +.. py:function:: getScreenTimeout() + + Gets the screen timeout + + :return: the current screen timeout in seconds + +AirplanerMode +--------------------- + +.. py:function:: checkAirplaneMode() + + Checks the airplane mode setting + + :return: True if airplane mode is enabled + +.. py:function:: toggleAirplaneMode(enabled) + + Toggles airplane mode on and off + + :return: True if airplane mode is enabled + +Ringer Silent Mode +--------------------- + +.. py:function:: checkRingerSilentMode() + + Checks the ringer silent mode setting + + :return: True if ringer silent mode is enabled + +.. py:function:: toggleRingerSilentMode(enabled) + + Toggles ringer silent mode on and off + + :return: True if ringer silent mode is enabled + +Vibrate Mode +--------------------- + +.. py:function:: toggleVibrateMode(enabled) + + Toggles vibrate mode on and off. If ringer=true then set Ringer setting, else set Notification setting + + :return: True if vibrate mode is enabled + +.. py:function:: getVibrateMode(ringer) + + Checks Vibration setting. If ringer=true then query Ringer setting, else query Notification setting + + :return: True if vibrate mode is enabled + +Ringer & Media Volume +--------------------- + +.. py:function:: getMaxRingerVolume() + + Returns the maximum ringer volume + +.. py:function:: getRingerVolume() + + Returns the current ringer volume + +.. py:function:: setRingerVolume(volume) + + Sets the ringer volume + +.. py:function:: getMaxMediaVolume() + + Returns the maximum media volume + +.. py:function:: getMediaVolume() + + Returns the current media volume + +.. py:function:: setMediaVolume(volume) + + Sets the media volume + +Screen Brightness +--------------------- + +.. py:function:: getScreenBrightness() + + Returns the screen backlight brightness + + :return: the current screen brightness between 0 and 255 + +.. py:function:: setScreenBrightness(value) + + Sets the the screen backlight brightness + + :return: the original screen brightness + +.. py:function:: checkScreenOn() + + Checks if the screen is on or off (requires API level 7) + + :return: True if the screen is currently on + + +SmsFacade +========================= + +.. py:function:: smsSend(destinationAddress, text) + + Sends an SMS + + :param str destinationAddress: typically a phone number + :param str text: + +.. py:function:: smsGetMessageCount(unreadOnly, folder) + + Returns the number of messages + + :param bool unreadOnly: typically a phone number + :param str folder(optional): default "inbox" + +.. py:function:: smsGetMessageIds(unreadOnly, folder) + + Returns a List of all message IDs + + :param bool unreadOnly: typically a phone number + :param str folder(optional): default "inbox" + +.. py:function:: smsGetMessages(unreadOnly, folder, attributes) + + Returns a List of all messages + + :param bool unreadOnly: typically a phone number + :param str folder: default "inbox" + :param list attributes(optional): attributes + + :return: a List of messages as Maps + +.. py:function:: smsGetMessageById(id, attributes) + + Returns message attributes + + :param int id: message ID + :param list attributes(optional): attributes + + :return: a List of messages as Maps + +.. py:function:: smsGetAttributes() + + Returns a List of all possible message attributes + +.. py:function:: smsDeleteMessage(id) + + Deletes a message + + :param int id: message ID + + :return: True if the message was deleted + +.. py:function:: smsMarkMessageRead(ids, read) + + Marks messages as read + + :param list ids: List of message IDs to mark as read + :param bool read: true or false + + :return: number of messages marked read + +SpeechRecognitionFacade +========================= + +.. py:function:: recognizeSpeech(prompt, language, languageModel) + + Recognizes user's speech and returns the most likely result + + :param str prompt(optional): text prompt to show to the user when asking them to speak + :param str language(optional): language override to inform the recognizer that it should expect speech in a language different than the one set in the java.util.Locale.getDefault() + :param str languageModel(optional): informs the recognizer which speech model to prefer (see android.speech.RecognizeIntent) + + :return: An empty string in case the speech cannot be recongnized + + +ToneGeneratorFacade +========================= + +.. py:function:: generateDtmfTones(phoneNumber, toneDuration) + + Generate DTMF tones for the given phone number + + :param str phoneNumber: phone number + :param int toneDuration(optional): default 100, duration of each tone in milliseconds + + +WakeLockFacade +========================= + +.. py:function:: wakeLockAcquireFull() + + Acquires a full wake lock (CPU on, screen bright, keyboard bright) + +.. py:function:: wakeLockAcquirePartial() + + Acquires a partial wake lock (CPU on) + +.. py:function:: wakeLockAcquireBright() + + Acquires a bright wake lock (CPU on, screen bright) + +.. py:function:: wakeLockAcquireDim() + + Acquires a dim wake lock (CPU on, screen dim) + +.. py:function:: wakeLockRelease() + + Releases the wake lock + +WifiFacade +========================= + +.. py:function:: wifiGetScanResults() + + Returns the list of access points found during the most recent Wifi scan + +.. py:function:: wifiLockAcquireFull() + + Acquires a full Wifi lock + +.. py:function:: wifiLockAcquireScanOnly() + + Acquires a scan only Wifi lock + +.. py:function:: wifiLockRelease() + + Releases a previously acquired Wifi lock + +.. py:function:: wifiStartScan() + + Starts a scan for Wifi access points + + :return: True if the scan was initiated successfully + +.. py:function:: checkWifiState() + + Checks Wifi state + + :return: True if Wifi is enabled + +.. py:function:: toggleWifiState(enabled) + + Toggle Wifi on and off + + :param bool enabled(optional): enabled + + :return: True if Wifi is enabled + +.. py:function:: wifiDisconnect() + + Disconnects from the currently active access point + + :return: True if the operation succeeded + +.. py:function:: wifiGetConnectionInfo() + + Returns information about the currently active access point + +.. py:function:: wifiReassociate() + + Returns information about the currently active access point + + :return: True if the operation succeeded + +.. py:function:: wifiReconnect() + + Reconnects to the currently active access point + + :return: True if the operation succeeded + + +BatteryManagerFacade +========================= + +.. py:function:: readBatteryData() + + Returns the most recently recorded battery data + +.. py:function:: batteryStartMonitoring() + + Starts tracking battery state + +.. py:function:: batteryStopMonitoring() + + Stops tracking battery state + +.. py:function:: batteryGetStatus() + + Returns the most recently received battery status data: + 1 - unknown; + 2 - charging; + 3 - discharging; + 4 - not charging; + 5 - full + +.. py:function:: batteryGetHealth() + + Returns the most recently received battery health data: + 1 - unknown; + 2 - good; + 3 - overheat; + 4 - dead; + 5 - over voltage; + 6 - unspecified failure + +.. py:function:: batteryGetPlugType() + + Returns the most recently received plug type data: + -1 - unknown + 0 - unplugged + 1 - power source is an AC charger + 2 - power source is a USB port + + +.. py:function:: batteryCheckPresent() + + Returns the most recently received battery presence data + +.. py:function:: batteryGetLevel() + + Returns the most recently received battery level (percentage) + +.. py:function:: batteryGetVoltage() + + Returns the most recently received battery voltage + +.. py:function:: batteryGetTemperature() + + Returns the most recently received battery temperature + +.. py:function:: batteryGetTechnology() + + Returns the most recently received battery technology data + + +ActivityResultFacade +========================= + +.. py:function:: setResultBoolean(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + + +.. py:function:: setResultByte(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultShort(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultChar(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + + +.. py:function:: setResultInteger(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultLong(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultFloat(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultDouble(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultString(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultBooleanArray(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultByteArray(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultShortArray(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultCharArray(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultIntegerArray(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultLongArray(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultFloatArray(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultDoubleArray(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultStringArray(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + +.. py:function:: setResultSerializable(resultCode, resultValue) + + Sets the result of a script execution. Whenever the script APK is called via startActivityForResult(), + the resulting intent will contain SCRIPT_RESULT extra with the given value + + :param int resultCode: + :param byte resultValue: + + +MediaPlayerFacade +========================= + +Control +----------------- +.. py:function:: mediaPlay(url, tag, play) + + Open a media file + + :param str url: url of media resource + :param str tag(optional): string identifying resource (default=default) + :param bool play(optional): start playing immediately + + :return: true if play successful + +.. py:function:: mediaPlayPause(tag) + + pause playing media file + + :param str tag: string identifying resource (default=default) + + :return: true if successful + +.. py:function:: mediaPlayStart(tag) + + start playing media file + + :param str tag: string identifying resource (default=default) + + :return: true if successful + +.. py:function:: mediaPlayClose(tag) + + Close media file + + :param str tag: string identifying resource (default=default) + + :return: true if successful + +.. py:function:: mediaIsPlaying(tag) + + Checks if media file is playing + + :param str tag: string identifying resource (default=default) + + :return: true if successful + + +.. py:function:: mediaPlaySetLooping(enabled, tag) + + Set Looping + + :param bool enabled: default true + :param str tag: string identifying resource (default=default) + + :return: True if successful + +.. py:function:: mediaPlaySeek(msec, tag) + + Seek To Position + + :param int msec: default true + :param str tag: string identifying resource (default=default) + + :return: New Position (in ms) + +Get Information +----------------- +.. py:function:: mediaPlayInfo(tag) + + Information on current media + + :param str tag: string identifying resource (default=default) + + :return: Media Information + +.. py:function:: mediaPlayList() + + Lists currently loaded media + + :return: List of Media Tags + + +PreferencesFacade +========================= + +.. py:function:: prefGetValue(key, filename) + + Read a value from shared preferences + + :param str key: key + :param str filename(optional): Desired preferences file. If not defined, uses the default Shared Preferences. + + +.. py:function:: prefPutValue(key, value, filename) + + Write a value to shared preferences + + :param str key: key + :param str value: value + :param str filename(optional): Desired preferences file. If not defined, uses the default Shared Preferences. + +.. py:function:: prefGetAll(filename) + + Get list of Shared Preference Values + + :param str filename(optional): Desired preferences file. If not defined, uses the default Shared Preferences. + + +QPyInterfaceFacade +========================= + +.. py:function:: executeQPy(script) + + Execute a qpython script by absolute path + + :param str script: The absolute path of the qpython script + + :return: bool + + +TextToSpeechFacade +========================= + +.. py:function:: ttsSpeak(message) + + Speaks the provided message via TTS + + :param str message: message + +.. py:function:: ttsIsSpeaking() + + Returns True if speech is currently in progress + +EyesFreeFacade +========================= + +.. py:function:: ttsSpeak(message) + + Speaks the provided message via TTS + + :param str message: message + + +BluetoothFacade +========================= + +.. py:function:: bluetoothActiveConnections() + + Returns active Bluetooth connections + + +.. py:function:: bluetoothWriteBinary(base64, connID) + + Send bytes over the currently open Bluetooth connection + + :param str base64: A base64 encoded String of the bytes to be sent + :param str connID(optional): Connection id + +.. py:function:: bluetoothReadBinary(bufferSize, connID) + + Read up to bufferSize bytes and return a chunked, base64 encoded string + + :param int bufferSize: default 4096 + :param str connID(optional): Connection id + +.. py:function:: bluetoothConnect(uuid, address) + + Connect to a device over Bluetooth. Blocks until the connection is established or fails + + :param str uuid: The UUID passed here must match the UUID used by the server device + :param str address(optional): The user will be presented with a list of discovered devices to choose from if an address is not provided + + :return: True if the connection was established successfully + +.. py:function:: bluetoothAccept(uuid, timeout) + + Listens for and accepts a Bluetooth connection. Blocks until the connection is established or fails + + :param str uuid: The UUID passed here must match the UUID used by the server device + :param int timeout: How long to wait for a new connection, 0 is wait for ever (default=0) + +.. py:function:: bluetoothMakeDiscoverable(duration) + + Requests that the device be discoverable for Bluetooth connections + + :param int duration: period of time, in seconds, during which the device should be discoverable (default=300) + +.. py:function:: bluetoothWrite(ascii, connID) + + Sends ASCII characters over the currently open Bluetooth connection + + :param str ascii: text + :param str connID: Connection id + +.. py:function:: bluetoothReadReady(connID) + + Sends ASCII characters over the currently open Bluetooth connection + + :param str ascii: text + :param str connID: Connection id + +.. py:function:: bluetoothRead(bufferSize, connID) + + Read up to bufferSize ASCII characters + + :param int bufferSize: default=4096 + :param str connID(optional): Connection id + +.. py:function:: bluetoothReadLine(connID) + + Read the next line + + :param str connID(optional): Connection id + +.. py:function:: bluetoothGetRemoteDeviceName(address) + + Queries a remote device for it's name or null if it can't be resolved + + :param str address: Bluetooth Address For Target Device + +.. py:function:: bluetoothGetLocalName() + + Gets the Bluetooth Visible device name + +.. py:function:: bluetoothSetLocalName(name) + + Sets the Bluetooth Visible device name, returns True on success + + :param str name: New local name + +.. py:function:: bluetoothGetScanMode() + + Gets the scan mode for the local dongle. + Return values: + -1 when Bluetooth is disabled. + 0 if non discoverable and non connectable. + 1 connectable non discoverable. + 3 connectable and discoverable. + +.. py:function:: bluetoothGetConnectedDeviceName(connID) + + Returns the name of the connected device + + :param str connID: Connection id + +.. py:function:: checkBluetoothState() + + Checks Bluetooth state + + :return: True if Bluetooth is enabled + +.. py:function:: toggleBluetoothState(enabled, prompt) + + Toggle Bluetooth on and off + + :param bool enabled: + :param str prompt: Prompt the user to confirm changing the Bluetooth state, default=true + + :return: True if Bluetooth is enabled + +.. py:function:: bluetoothStop(connID) + + Stops Bluetooth connection + + :param str connID: Connection id + +.. py:function:: bluetoothGetLocalAddress() + + Returns the hardware address of the local Bluetooth adapter + +.. py:function:: bluetoothDiscoveryStart() + + Start the remote device discovery process + + :return: true on success, false on error + +.. py:function:: bluetoothDiscoveryCancel() + + Cancel the current device discovery process + + :return: true on success, false on error + +.. py:function:: bluetoothIsDiscovering() + + Return true if the local Bluetooth adapter is currently in the device discovery process + + +SignalStrengthFacade +========================= +.. py:function:: startTrackingSignalStrengths() + + Starts tracking signal strengths + +.. py:function:: readSignalStrengths() + + Returns the current signal strengths + + :return: A map of gsm_signal_strength + +.. py:function:: stopTrackingSignalStrengths() + + Stops tracking signal strength + + +WebCamFacade +========================= + +.. py:function:: webcamStart(resolutionLevel, jpegQuality, port) + + Starts an MJPEG stream and returns a Tuple of address and port for the stream + + :param int resolutionLevel: increasing this number provides higher resolution (default=0) + :param int jpegQuality: a number from 0-10 (default=20) + :param int port: If port is specified, the webcam service will bind to port, otherwise it will pick any available port (default=0) + +.. py:function:: webcamAdjustQuality(resolutionLevel, jpegQuality) + + Adjusts the quality of the webcam stream while it is running + + :param int resolutionLevel: increasing this number provides higher resolution (default=0) + :param int jpegQuality: a number from 0-10 (default=20) + +.. py:function:: cameraStartPreview(resolutionLevel, jpegQuality, filepath) + + Start Preview Mode. Throws 'preview' events + + :param int resolutionLevel: increasing this number provides higher resolution (default=0) + :param int jpegQuality: a number from 0-10 (default=20) + :param str filepath: Path to store jpeg files + + :return: True if successful + +.. py:function:: cameraStopPreview() + + Stop the preview mode + + +UiFacade +========================= + +Dialog +-------- +.. py:function:: dialogCreateInput(title, message, defaultText, inputType) + + Create a text input dialog + + :param str title: title of the input box + :param str message: message to display above the input box + :param str defaultText(optional): text to insert into the input box + :param str inputType(optional): type of input data, ie number or text + +.. py:function:: dialogCreatePassword(title, message) + + Create a password input dialog + + :param str title: title of the input box + :param str message: message to display above the input box + +.. py:function:: dialogGetInput(title, message, defaultText) + + Create a password input dialog + + :param str title: title of the input box + :param str message: message to display above the input box + :param str defaultText(optional): text to insert into the input box + +.. py:function:: dialogGetPassword(title, message) + + Queries the user for a password + + :param str title: title of the password box + :param str message: message to display above the input box + +.. py:function:: dialogCreateSeekBar(start, maximum, title) + + Create seek bar dialog + + :param int start: default=50 + :param int maximum: default=100 + :param int title: title + +.. py:function:: dialogCreateTimePicker(hour, minute, is24hour) + + Create time picker dialog + + :param int hour: default=0 + :param int miute: default=0 + :param bool is24hour: default=false + +.. py:function:: dialogCreateDatePicker(year, month, day) + + Create date picker dialog + + :param int year: default=1970 + :param int month: default=1 + :param int day: default=1 + + +NFC +------------- +**Data structs** +*QPython NFC json result* +:: + + { + "role": , # could be self/master/slave + "stat": , # could be ok / fail / cancl + "message": + } + +**APIs** + +.. py:function:: dialogCreateNFCBeamMaster(title, message, inputType) + + Create a dialog where you could create a qpython beam master + + :param str title: title of the input box + :param str message: message to display above the input box + :param str inputType(optional): type of input data, ie number or text + +.. py:function:: NFCBeamMessage(content, title, message) + + Create a dialog where you could create a qpython beam master + + :param str content: message you want to sent + :param str title: title of the input box + :param str message: message to display above the input box + :param str inputType(optional): type of input data, ie number or text + +.. py:function:: dialogCreateNFCBeamSlave(title, message) + + Create a qpython beam slave + + :param str title: title of the input box + :param str message: message to display above the input box + +Progress +-------------- +.. py:function:: dialogCreateSpinnerProgress(message, maximumProgress) + + Create a spinner progress dialog + + :param str message(optional): message + :param int maximunProgress(optional): dfault=100 + +.. py:function:: dialogSetCurrentProgress(current) + + Set progress dialog current value + + :param int current: current + +.. py:function:: dialogSetMaxProgress(max) + + Set progress dialog maximum value + + :param int max: max + + +.. py:function:: dialogCreateHorizontalProgress(title, message, maximumProgress) + + Create a horizontal progress dialog + + :param str title(optional): title + :param str message(optional): message + :param int maximunProgress(optional): dfault=100 + + +Alert +---------- +.. py:function:: dialogCreateAlert(title, message) + + Create alert dialog + + :param str title(optional): title + :param str message(optional): message + :param int maximunProgress(optional): dfault=100 + + +Dialog Control +--------------- +.. py:function:: dialogSetPositiveButtonText(text) + + Set alert dialog positive button text + + :param str text: text + +.. py:function:: dialogSetNegativeButtonText(text) + + Set alert dialog negative button text + + :param str text: text + +.. py:function:: dialogSetNeutralButtonText(text) + + Set alert dialog button text + + :param str text: text + +.. py:function:: dialogSetItems(items) + + Set alert dialog list items + + :param list items: items + +.. py:function:: dialogSetSingleChoiceItems(items, selected) + + Set alert dialog list items + + :param list items: items + :param int selected: selected item index (default=0) + +.. py:function:: dialogSetMultiChoiceItems(items, selected) + + Set dialog multiple choice items and selection + + :param list items: items + :param int selected: selected item index (default=0) + +.. py:function:: addContextMenuItem(label, event, eventData) + + Adds a new item to context menu + + :param str label: label for this menu item + :param str event: event that will be generated on menu item click + :param object eventData: event object + +.. py:function:: addOptionsMenuItem(label, event, eventData, iconName) + + Adds a new item to context menu + + :param str label: label for this menu item + :param str event: event that will be generated on menu item click + :param object eventData: event object + :param str iconName: Android system menu icon, see http://developer.android.com/reference/android/R.drawable.html + +.. py:function:: dialogGetResponse() + + Returns dialog response + +.. py:function:: dialogGetSelectedItems() + + This method provides list of items user selected + +.. py:function:: dialogDismiss() + + Dismiss dialog + +.. py:function:: dialogShow() + + Show dialog + + +Layout +--------- +.. py:function:: fullShow(layout) + + Show Full Screen + + :param string layout: String containing View layout + +.. py:function:: fullDismiss() + + Dismiss Full Screen + +.. py:function:: fullQuery() + + Get Fullscreen Properties + +.. py:function:: fullQueryDetail(id) + + Get fullscreen properties for a specific widget + + :param str id: id of layout widget + +.. py:function:: fullSetProperty(id) + + Set fullscreen widget property + + :param str id: id of layout widget + :param str property: name of property to set + :param str value: value to set property to + +.. py:function:: fullSetList(id, list) + + Attach a list to a fullscreen widget + + :param str id: id of layout widget + :param list list: List to set + +.. py:function:: fullKeyOverride(keycodes, enable) + + Override default key actions + + :param str keycodes: id of layout widget + :param bool enable: List to set (default=true) + + + +WebView +----------- +.. py:function:: webViewShow() + + Display a WebView with the given URL + + :param str url: url + :param bool wait(optional): block until the user exits the WebView + +USB Host Serial Facade +====================== + +*QPython 1.3.1+ and QPython3 1.0.3+ contains this feature* + +SL4A Facade for USB Serial devices by Android USB Host API. + + +It control the USB-Serial like devices +from Andoroid which has USB Host Controller . + +The sample +`demonstration is also available at youtube video `_ + + +Requirements +------------- +* Android device which has USB Host controller (and enabled in that firmware). +* Android 4.0 (API14) or later. +* USB Serial devices (see [Status](#Status)). +* USB Serial devices were not handled by Android kernel. + + > I heard some android phone handle USB Serial devices + > make /dev/ttyUSB0 in kernel level. + > In this case, Android does not be able to handle the device + > from OS level. + + please check Android Applications be able to grab the target USB Devices, + such as `USB Device Info `_. + +Status +--------------- +* probably work with USB CDC, like FTDI, Arduino or else. + +* 2012/09/10: work with 78K0F0730 device (new RL78) with Tragi BIOS board. + + `M78K0F0730 `_ + +* 2012/09/24: work with some pl2303 devcies. + +Author +------- +This facade developped by `Kuri65536 `_ +you can see the commit log in it. + + +APIs +-------- +.. py:function:: usbserialGetDeviceList() + + Returns USB devices reported by USB Host API. + + :return: Returns "Map of id and string information Map + + +.. py:function:: usbserialDisconnect(connID) + + Disconnect all USB-device + + :param str connID: connection ID + +.. py:function:: usbserialActiveConnections() + + Returns active USB-device connections. + + :return: Returns "Active USB-device connections by Map UUID vs device-name." + + +.. py:function:: usbserialWriteBinary(base64, connID) + + Send bytes over the currently open USB Serial connection. + + :param str base64: + :param str connId: + +.. py:function:: usbserialReadBinary(bufferSize, connID) + + Read up to bufferSize bytes and return a chunked, base64 encoded string + + :param int bufferSize: + :param str connId: + +.. py:function:: usbserialConnect(hash, options) + + Connect to a device with USB-Host. request the connection and exit + + :param str hash: + :param str options: + + :return: Returns messages the request status + +.. py:function:: usbserialHostEnable() + + Requests that the host be enable for USB Serial connections. + + :return: True if the USB Device is accesible + +.. py:function:: usbserialWrite(String ascii, String connID) + + Sends ASCII characters over the currently open USB Serial connection + + :param str ascii: + :param str connID: + +.. py:function:: usbserialReadReady(connID) + + :param str connID: + + :return: True if the next read is guaranteed not to block + + +.. py:function:: usbserialRead(connID, bufferSize) + + Read up to bufferSize ASCII characters. + + :param str connID: + :param int bufferSize: + +.. py:function:: usbserialGetDeviceName(connID) + + Queries a remote device for it's name or null if it can't be resolved + + :param str connID: diff --git a/proguard-rules.pro b/proguard-rules.pro index 36693fa..ce61216 100644 --- a/proguard-rules.pro +++ b/proguard-rules.pro @@ -1,17 +1,17 @@ -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in C:\Users\Jay\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt -# You can edit the include mPath and order by changing the proguardFiles -# directive in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:\Users\Jay\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt +# You can edit the include mPath and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index d59ae2c..78271fb 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -1,15 +1,20 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/org/qpython/qsl4a/QPyScriptService.java b/src/main/java/org/qpython/qsl4a/QPyScriptService.java index ffc95b5..ef30e61 100644 --- a/src/main/java/org/qpython/qsl4a/QPyScriptService.java +++ b/src/main/java/org/qpython/qsl4a/QPyScriptService.java @@ -1,165 +1,160 @@ -package org.qpython.qsl4a; - -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.AsyncTask; -import android.os.Binder; -import android.os.IBinder; -import android.util.Log; - -import org.qpython.qsl4a.qsl4a.AndroidProxy; -import org.qpython.qsl4a.qsl4a.LogUtil; -import org.qpython.qsl4a.qsl4a.interpreter.InterpreterConfiguration; -import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiverManager; -import org.qpython.qsl4a.qsl4a.util.SPFUtils; - -import java.util.concurrent.CountDownLatch; - -public class QPyScriptService extends Service { - private static final String TAG = "QPyScriptService"; - public static String scriptName; - - //private final static int NOTIFICATION_ID = NotificationIdFactory.create(); - private final CountDownLatch mLatch = new CountDownLatch(1); - private final IBinder mBinder; - @SuppressWarnings("unused") - private boolean killMe; - private InterpreterConfiguration mInterpreterConfiguration = null; - private RpcReceiverManager mFacadeManager; - private AndroidProxy mProxy; - @SuppressWarnings("unused") - private int mStartId; - - // ------------------------------------------------------------------------------------------------------ - - public QPyScriptService() { -// super(NOTIFICATION_ID); - super(); - mBinder = new LocalBinder(); - } - // ------------------------------------------------------------------------------------------------------ - - public static String getSP(Context context, String key) { - String val; - SharedPreferences obj = context.getSharedPreferences("qsl4a_db", 0); - val = obj.getString(key, ""); - return val; - } - - // ------------------------------------------------------------------------------------------------------ - - public static void setSP(Context context, String key, String val) { - SharedPreferences obj = context.getSharedPreferences("qsl4a_db", 0); - SharedPreferences.Editor wobj; - wobj = obj.edit(); - wobj.putString(key, val); - wobj.commit(); - } - - // ------------------------------------------------------------------------------------------------------ - - @Override - public void onDestroy() { - Log.d(TAG, "onDestroy"); - LogUtil.e("doWork444444"); - - if (mProxy != null) { - mProxy.shutdown(); - } - -// Intent intent = new Intent("org.qpython.qpysl4a.KeepAlive"); -// sendBroadcast(intent); - - super.onDestroy(); - } - - @Override - public IBinder onBind(Intent intent) { - return mBinder; - } - - // ------------------------------------------------------------------------------------------------------ - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - super.onStartCommand(intent, flags, startId); - return START_STICKY; - } - - // ------------------------------------------------------------------------------------------------------ - - @Override - public void onCreate() { - Log.d(TAG, "onCreate"); - LogUtil.e("doWork3333333"); - super.onCreate(); - // clear before run - /*File logFile = new File( Environment.getExternalStorageDirectory()+"/"+CONF.BASE_PATH+"/"+scriptName.substring(scriptName.lastIndexOf("/")+1)+".log" ); - if (logFile.exists()) { - logFile.delete(); - } - killProcess(); */ - // River Modify - this.killMe = false; - - new startMyAsyncTask().execute(101); - } - - // ------------------------------------------------------------------------------------------------------ - - private void startMyMain(final int startId) { - try { - mProxy = new AndroidProxy(this, null, true); - - mProxy.startLocal(); - SPFUtils.setSP(getApplicationContext(), "sl4a.hostname", mProxy.getAddress().getHostName()); - SPFUtils.setSP(getApplicationContext(), "sl4a.port", "" + mProxy.getAddress().getPort()); - SPFUtils.setSP(getApplicationContext(), "sl4a.secue", mProxy.getSecret()); - - Log.d(TAG, "startMyMain:" + mProxy.getAddress().getHostName() + ":" + mProxy.getAddress().getPort() + ":" + mProxy.getSecret()); - mLatch.countDown(); - } catch (Exception e) { - - } - } - - RpcReceiverManager getRpcReceiverManager() throws InterruptedException { - mLatch.await(); - - if (mFacadeManager == null) { // Facade manage may not be available on startup. - mFacadeManager = mProxy.getRpcReceiverManagerFactory() - .getRpcReceiverManagers().get(0); - } - return mFacadeManager; - } - - public class LocalBinder extends Binder { - public QPyScriptService getService() { - return QPyScriptService.this; - } - } - - public class startMyAsyncTask extends AsyncTask { - @Override - protected void onPreExecute() { - } - - @Override - protected Boolean doInBackground(Integer... params) { - Log.d(TAG, "doInBackground"); - startMyMain(params[0]); - return true; - } - - @Override - protected void onProgressUpdate(Integer... values) { - } - - @Override - protected void onPostExecute(Boolean installStatus) { - Log.d(TAG, "startMyAsyncTask:onPostExecute"); - } - } +package org.qpython.qsl4a; + +import android.annotation.SuppressLint; +import android.app.Service; +import android.content.Intent; +import android.os.AsyncTask; +import android.os.Binder; +import android.os.IBinder; +import android.util.Log; + +import org.qpython.qsl4a.qsl4a.AndroidProxy; +import org.qpython.qsl4a.qsl4a.util.SPFUtils; + +import java.util.concurrent.CountDownLatch; + +public class QPyScriptService extends Service { + private static final String TAG = "QPyScriptService"; + //public static String scriptName; + + //private final static int NOTIFICATION_ID = NotificationIdFactory.create(); + private final CountDownLatch mLatch = new CountDownLatch(1); + private final IBinder mBinder; + @SuppressWarnings("unused") + private boolean killMe; + //private InterpreterConfiguration mInterpreterConfiguration = null; + //private RpcReceiverManager mFacadeManager; + private AndroidProxy mProxy; + @SuppressWarnings("unused") + private int mStartId; + + // ------------------------------------------------------------------------------------------------------ + + public QPyScriptService() { +// super(NOTIFICATION_ID); + super(); + mBinder = new LocalBinder(); + } + // ------------------------------------------------------------------------------------------------------ + + /*public static String getSP(Context context, String key) { + String val; + SharedPreferences obj = context.getSharedPreferences("qsl4a_db", 0); + val = obj.getString(key, ""); + return val; + } + + // ------------------------------------------------------------------------------------------------------ + + public static void setSP(Context context, String key, String val) { + SharedPreferences obj = context.getSharedPreferences("qsl4a_db", 0); + SharedPreferences.Editor wobj; + wobj = obj.edit(); + wobj.putString(key, val); + wobj.commit(); + }*/ + + // ------------------------------------------------------------------------------------------------------ + + @Override + public void onDestroy() { + Log.d(TAG, "onDestroy"); + + if (mProxy != null) { + mProxy.shutdown(); + } + +// Intent intent = new Intent("org.qpython.qpysl4a.KeepAlive"); +// sendBroadcast(intent); + + super.onDestroy(); + } + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + // ------------------------------------------------------------------------------------------------------ + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + super.onStartCommand(intent, flags, startId); + return START_STICKY; + } + + // ------------------------------------------------------------------------------------------------------ + + @Override + public void onCreate() { + Log.d(TAG, "onCreate"); + super.onCreate(); + // clear before run + /*File logFile = new File( Environment.getExternalStorageDirectory()+"/"+CONF.BASE_PATH+"/"+scriptName.substring(scriptName.lastIndexOf("/")+1)+".log" ); + if (logFile.exists()) { + logFile.delete(); + } + killProcess(); */ + // River Modify + this.killMe = false; + + new startMyAsyncTask().execute(101); + } + + // ------------------------------------------------------------------------------------------------------ + + private void startMyMain() { + try { + mProxy = new AndroidProxy(this, null); + + mProxy.startLocal(); + SPFUtils.setSP(getApplicationContext(), "sl4a.hostname", mProxy.getAddress().getHostName()); + SPFUtils.setSP(getApplicationContext(), "sl4a.port", "" + mProxy.getAddress().getPort()); + SPFUtils.setSP(getApplicationContext(), "sl4a.secue", mProxy.getSecret()); + + //Log.d(TAG, "startMyMain:" + mProxy.getAddress().getHostName() + ":" + mProxy.getAddress().getPort() + ":" + mProxy.getSecret()); + mLatch.countDown(); + } catch (Exception ignored) { + + } + } + + /*RpcReceiverManager getRpcReceiverManager() throws InterruptedException { + mLatch.await(); + + if (mFacadeManager == null) { // Facade manage may not be available on startup. + mFacadeManager = mProxy.getRpcReceiverManagerFactory() + .getRpcReceiverManagers().get(0); + } + return mFacadeManager; + }*/ + + public class LocalBinder extends Binder { + public QPyScriptService getService() { + return QPyScriptService.this; + } + } + + @SuppressLint("StaticFieldLeak") + public class startMyAsyncTask extends AsyncTask { + @Override + protected void onPreExecute() { + } + + @Override + protected Boolean doInBackground(Integer... params) { + Log.d(TAG, "doInBackground"); + startMyMain(); + return true; + } + + @Override + protected void onProgressUpdate(Integer... values) { + } + + @Override + protected void onPostExecute(Boolean installStatus) { + Log.d(TAG, "startMyAsyncTask:onPostExecute"); + } + } } \ No newline at end of file diff --git a/src/main/java/org/qpython/qsl4a/QSL4APP.java b/src/main/java/org/qpython/qsl4a/QSL4APP.java index 2c6d8a7..5fbdbe1 100644 --- a/src/main/java/org/qpython/qsl4a/QSL4APP.java +++ b/src/main/java/org/qpython/qsl4a/QSL4APP.java @@ -1,81 +1,81 @@ -package org.qpython.qsl4a; - -import com.quseit.base.MyApp; - -import org.qpython.qsl4a.qsl4a.future.FutureActivityTaskExecutor; -import org.qpython.qsl4a.qsl4a.LogUtil; -import org.qpython.qsl4a.qsl4a.interpreter.InterpreterConfiguration; -import org.qpython.qsl4a.qsl4a.interpreter.InterpreterConstants; -import org.qpython.qsl4a.qsl4a.trigger.TriggerRepository; - -import java.util.Map; -import java.util.concurrent.CountDownLatch; - - -public class QSL4APP extends MyApp implements InterpreterConfiguration.ConfigurationObserver { - - private final CountDownLatch mLatch = new CountDownLatch(1); - private final FutureActivityTaskExecutor mTaskExecutor = new FutureActivityTaskExecutor(this); - - - /*@Override - public Class getHomeActivityClass() { - }*/ -// -// @Override -// public Intent getMainApplicationIntent() { -// return null; -// //return new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.app_url))); -// } - protected InterpreterConfiguration mConfiguration; - Map movieDirs = null; - private volatile boolean receivedConfigUpdate = false; - private TriggerRepository mTriggerRepository; - - protected QSL4APP() { - super(); - } - - - @Override - public void onCreate() { - super.onCreate(); - } - - public void initConfiguration() { - mConfiguration = new InterpreterConfiguration(this); - mConfiguration.registerObserver(this); - mConfiguration.startDiscovering(InterpreterConstants.MIME + QSL4AScript.getFileExtension(this)); - } - - public FutureActivityTaskExecutor getTaskExecutor() { - return mTaskExecutor; - } - - - public InterpreterConfiguration getInterpreterConfiguration() { - return mConfiguration; - } - - public TriggerRepository getTriggerRepository() { - return mTriggerRepository; - } - - - @Override - public void onConfigurationChanged() { - receivedConfigUpdate = true; - mLatch.countDown(); - } - - public boolean readyToStart() { - try { - mLatch.await(); - } catch (InterruptedException e) { - LogUtil.e(e); - } - return receivedConfigUpdate; - } - - -} +package org.qpython.qsl4a; + +import com.quseit.base.MyApp; + +import org.qpython.qsl4a.qsl4a.future.FutureActivityTaskExecutor; +import org.qpython.qsl4a.qsl4a.LogUtil; +import org.qpython.qsl4a.qsl4a.interpreter.InterpreterConfiguration; +import org.qpython.qsl4a.qsl4a.interpreter.InterpreterConstants; +import org.qpython.qsl4a.qsl4a.trigger.TriggerRepository; + +import java.util.Map; +import java.util.concurrent.CountDownLatch; + + +public class QSL4APP extends MyApp implements InterpreterConfiguration.ConfigurationObserver { + + private final CountDownLatch mLatch = new CountDownLatch(1); + private final FutureActivityTaskExecutor mTaskExecutor = new FutureActivityTaskExecutor(this); + + /*@Override + public Class getHomeActivityClass() { + }*/ +// +// @Override +// public Intent getMainApplicationIntent() { +// return null; +// //return new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.app_url))); +// } + protected InterpreterConfiguration mConfiguration; + Map movieDirs = null; + private volatile boolean receivedConfigUpdate = false; + private TriggerRepository mTriggerRepository; + + protected QSL4APP() { + super(); + } + + + @Override + public void onCreate() { + super.onCreate(); + + mConfiguration = new InterpreterConfiguration(this); + mConfiguration.registerObserver(this); + mConfiguration.startDiscovering(InterpreterConstants.MIME + QSL4AScript.getFileExtension(this)); + + //注册crashHandler类 + //int xq = 30; + } + + public FutureActivityTaskExecutor getTaskExecutor() { + return mTaskExecutor; + } + + + public InterpreterConfiguration getInterpreterConfiguration() { + return mConfiguration; + } + + public TriggerRepository getTriggerRepository() { + return mTriggerRepository; + } + + + @Override + public void onConfigurationChanged() { + receivedConfigUpdate = true; + mLatch.countDown(); + } + + public boolean readyToStart() { + try { + mLatch.await(); + } catch (InterruptedException e) { + LogUtil.e(e); + } + return receivedConfigUpdate; + } + + +} diff --git a/src/main/java/org/qpython/qsl4a/QSL4AScript.java b/src/main/java/org/qpython/qsl4a/QSL4AScript.java index 7b9a96c..72ce37c 100644 --- a/src/main/java/org/qpython/qsl4a/QSL4AScript.java +++ b/src/main/java/org/qpython/qsl4a/QSL4AScript.java @@ -1,32 +1,32 @@ -package org.qpython.qsl4a; - -import android.content.Context; - -public class QSL4AScript { - -// public final static int ID = R.raw.pymain; - - public static String sFileName; - - public static String getFileName(Context context) { -// if (sFileName == null) { -// Resources resources = context.getResources(); -// String name = resources.getText(ID).toString(); -// sFileName = name.substring(name.lastIndexOf('/') + 1, name.length()); -// } - sFileName = "main.py"; - return sFileName; - } - - public static String getFileExtension(Context context) { - if (sFileName == null) { - getFileName(context); - } - int dotIndex = sFileName.lastIndexOf('.'); - if (dotIndex == -1) { - return null; - } - return sFileName.substring(dotIndex); - } - -} +package org.qpython.qsl4a; + +import android.content.Context; + +public class QSL4AScript { + +// public final static int ID = R.raw.pymain; + + public static String sFileName; + + public static String getFileName(Context context) { +// if (sFileName == null) { +// Resources resources = context.getResources(); +// String name = resources.getText(ID).toString(); +// sFileName = name.substring(name.lastIndexOf('/') + 1, name.length()); +// } + sFileName = "main.py"; + return sFileName; + } + + public static String getFileExtension(Context context) { + if (sFileName == null) { + getFileName(context); + } + int dotIndex = sFileName.lastIndexOf('.'); + if (dotIndex == -1) { + return null; + } + return sFileName.substring(dotIndex); + } + +} diff --git a/src/main/java/org/qpython/qsl4a/codec/Base64Codec.java b/src/main/java/org/qpython/qsl4a/codec/Base64Codec.java index 8e0ce20..badf8ba 100644 --- a/src/main/java/org/qpython/qsl4a/codec/Base64Codec.java +++ b/src/main/java/org/qpython/qsl4a/codec/Base64Codec.java @@ -1,1050 +1,1050 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.qpython.qsl4a.codec; - -import java.math.BigInteger; - -/** - * Provides Base64 encoding and decoding as defined by RFC 2045. - * - *

- * This class implements section 6.8. Base64 Content-Transfer-Encoding from RFC 2045 Multipurpose - * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies by Freed and Borenstein. - *

- *

- * The class can be parameterized in the following manner with various constructors: - *

    - *
  • URL-safe mode: Default off.
  • - *
  • Line length: Default 76. Line length that aren't multiples of 4 will still essentially end up being multiples of - * 4 in the encoded data. - *
  • Line separator: Default is CRLF ("\r\n")
  • - *
- *

- *

- * Since this class operates directly on byte streams, and not character streams, it is hard-coded to only encode/decode - * character encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, UTF-8, etc). - *

- * - * @see RFC 2045 - * @author Apache Software Foundation - * @since 1.0 - * @version $Id: Base64.java 801706 2009-08-06 16:27:06Z niallp $ - */ -public class Base64Codec implements BinaryEncoder, BinaryDecoder { - private static final int DEFAULT_BUFFER_RESIZE_FACTOR = 2; - - private static final int DEFAULT_BUFFER_SIZE = 8192; - - /** - * Chunk size per RFC 2045 section 6.8. - * - *

- * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any - * equal signs. - *

- * - * @see RFC 2045 section 6.8 - */ - static final int CHUNK_SIZE = 76; - - /** - * Chunk separator per RFC 2045 section 2.1. - * - *

- * N.B. The next major release may break compatibility and make this field private. - *

- * - * @see RFC 2045 section 2.1 - */ - static final byte[] CHUNK_SEPARATOR = {'\r', '\n'}; - - /** - * This array is a lookup table that translates 6-bit positive integer index values into their "Base64 Alphabet" - * equivalents as specified in Table 1 of RFC 2045. - * - * Thanks to "commons" project in ws.apache.org for this code. - * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ - */ - private static final byte[] STANDARD_ENCODE_TABLE = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' - }; - - /** - * This is a copy of the STANDARD_ENCODE_TABLE above, but with + and / - * changed to - and _ to make the encoded Base64 results more URL-SAFE. - * This table is only used when the Base64's mode is set to URL-SAFE. - */ - private static final byte[] URL_SAFE_ENCODE_TABLE = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' - }; - - /** - * Byte used to pad output. - */ - private static final byte PAD = '='; - - /** - * This array is a lookup table that translates Unicode characters drawn from the "Base64 Alphabet" (as specified in - * Table 1 of RFC 2045) into their 6-bit positive integer equivalents. Characters that are not in the Base64 - * alphabet but fall within the bounds of the array are translated to -1. - * - * Note: '+' and '-' both decode to 62. '/' and '_' both decode to 63. This means decoder seamlessly handles both - * URL_SAFE and STANDARD base64. (The encoder, on the other hand, needs to know ahead of time what to emit). - * - * Thanks to "commons" project in ws.apache.org for this code. - * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ - */ - private static final byte[] DECODE_TABLE = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, 52, 53, 54, - 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, -1, -1, -1, -1, 63, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, - 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 - }; - - /** Mask used to extract 6 bits, used when encoding */ - private static final int MASK_6BITS = 0x3f; - - /** Mask used to extract 8 bits, used in decoding base64 bytes */ - private static final int MASK_8BITS = 0xff; - - // The static final fields above are used for the original static byte[] methods on Base64. - // The private member fields below are used with the new streaming approach, which requires - // some state be preserved between calls of encode() and decode(). - - /** - * Encode table to use: either STANDARD or URL_SAFE. Note: the DECODE_TABLE above remains static because it is able - * to decode both STANDARD and URL_SAFE streams, but the encodeTable must be a member variable so we can switch - * between the two modes. - */ - private final byte[] encodeTable; - - /** - * Line length for encoding. Not used when decoding. A value of zero or less implies no chunking of the base64 - * encoded data. - */ - private final int lineLength; - - /** - * Line separator for encoding. Not used when decoding. Only used if lineLength > 0. - */ - private final byte[] lineSeparator; - - /** - * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing. - * decodeSize = 3 + lineSeparator.length; - */ - private final int decodeSize; - - /** - * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing. - * encodeSize = 4 + lineSeparator.length; - */ - private final int encodeSize; - - /** - * Buffer for streaming. - */ - private byte[] buffer; - - /** - * Position where next character should be written in the buffer. - */ - private int pos; - - /** - * Position where next character should be read from the buffer. - */ - private int readPos; - - /** - * Variable tracks how many characters have been written to the current line. Only used when encoding. We use it to - * make sure each encoded line never goes beyond lineLength (if lineLength > 0). - */ - private int currentLinePos; - - /** - * Writes to the buffer only occur after every 3 reads when encoding, an every 4 reads when decoding. This variable - * helps track that. - */ - private int modulus; - - /** - * Boolean flag to indicate the EOF has been reached. Once EOF has been reached, this Base64 object becomes useless, - * and must be thrown away. - */ - private boolean eof; - - /** - * Place holder for the 3 bytes we're dealing with for our base64 logic. Bitwise operations store and extract the - * base64 encoding or decoding from this variable. - */ - private int x; - - /** - * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. - *

- * When encoding the line length is 76, the line separator is CRLF, and the encoding table is STANDARD_ENCODE_TABLE. - *

- * - *

- * When decoding all variants are supported. - *

- */ - public Base64Codec() { - this(false); - } - - /** - * Creates a Base64 codec used for decoding (all modes) and encoding in the given URL-safe mode. - *

- * When encoding the line length is 76, the line separator is CRLF, and the encoding table is STANDARD_ENCODE_TABLE. - *

- * - *

- * When decoding all variants are supported. - *

- * - * @param urlSafe - * if true, URL-safe encoding is used. In most cases this should be set to - * false. - * @since 1.4 - */ - public Base64Codec(boolean urlSafe) { - this(CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe); - } - - /** - * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. - *

- * When encoding the line length is given in the constructor, the line separator is CRLF, and the encoding table is - * STANDARD_ENCODE_TABLE. - *

- *

- * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. - *

- *

- * When decoding all variants are supported. - *

- * - * @param lineLength - * Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4). - * If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when decoding. - * @since 1.4 - */ - public Base64Codec(int lineLength) { - this(lineLength, CHUNK_SEPARATOR); - } - - /** - * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. - *

- * When encoding the line length and line separator are given in the constructor, and the encoding table is - * STANDARD_ENCODE_TABLE. - *

- *

- * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. - *

- *

- * When decoding all variants are supported. - *

- * - * @param lineLength - * Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4). - * If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when decoding. - * @param lineSeparator - * Each line of encoded data will end with this sequence of bytes. - * @throws IllegalArgumentException - * Thrown when the provided lineSeparator included some base64 characters. - * @since 1.4 - */ - public Base64Codec(int lineLength, byte[] lineSeparator) { - this(lineLength, lineSeparator, false); - } - - /** - * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. - *

- * When encoding the line length and line separator are given in the constructor, and the encoding table is - * STANDARD_ENCODE_TABLE. - *

- *

- * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. - *

- *

- * When decoding all variants are supported. - *

- * - * @param lineLength - * Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4). - * If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when decoding. - * @param lineSeparator - * Each line of encoded data will end with this sequence of bytes. - * @param urlSafe - * Instead of emitting '+' and '/' we emit '-' and '_' respectively. urlSafe is only applied to encode - * operations. Decoding seamlessly handles both modes. - * @throws IllegalArgumentException - * The provided lineSeparator included some base64 characters. That's not going to work! - * @since 1.4 - */ - public Base64Codec(int lineLength, byte[] lineSeparator, boolean urlSafe) { - if (lineSeparator == null) { - lineLength = 0; // disable chunk-separating - lineSeparator = CHUNK_SEPARATOR; // this just gets ignored - } - this.lineLength = lineLength > 0 ? (lineLength / 4) * 4 : 0; - this.lineSeparator = new byte[lineSeparator.length]; - System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length); - if (lineLength > 0) { - this.encodeSize = 4 + lineSeparator.length; - } else { - this.encodeSize = 4; - } - this.decodeSize = this.encodeSize - 1; - if (containsBase64Byte(lineSeparator)) { - String sep = StringUtils.newStringUtf8(lineSeparator); - throw new IllegalArgumentException("lineSeperator must not contain base64 characters: [" + sep + "]"); - } - this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE; - } - - /** - * Returns our current encode mode. True if we're URL-SAFE, false otherwise. - * - * @return true if we're in URL-SAFE mode, false otherwise. - * @since 1.4 - */ - public boolean isUrlSafe() { - return this.encodeTable == URL_SAFE_ENCODE_TABLE; - } - - /** - * Returns true if this Base64 object has buffered data for reading. - * - * @return true if there is Base64 object still available for reading. - */ - boolean hasData() { - return this.buffer != null; - } - - /** - * Returns the amount of buffered data available for reading. - * - * @return The amount of buffered data available for reading. - */ - int avail() { - return buffer != null ? pos - readPos : 0; - } - - /** Doubles our buffer. */ - private void resizeBuffer() { - if (buffer == null) { - buffer = new byte[DEFAULT_BUFFER_SIZE]; - pos = 0; - readPos = 0; - } else { - byte[] b = new byte[buffer.length * DEFAULT_BUFFER_RESIZE_FACTOR]; - System.arraycopy(buffer, 0, b, 0, buffer.length); - buffer = b; - } - } - - /** - * Extracts buffered data into the provided byte[] array, starting at position bPos, up to a maximum of bAvail - * bytes. Returns how many bytes were actually extracted. - * - * @param b - * byte[] array to extract the buffered data into. - * @param bPos - * position in byte[] array to start extraction at. - * @param bAvail - * amount of bytes we're allowed to extract. We may extract fewer (if fewer are available). - * @return The number of bytes successfully extracted into the provided byte[] array. - */ - int readResults(byte[] b, int bPos, int bAvail) { - if (buffer != null) { - int len = Math.min(avail(), bAvail); - if (buffer != b) { - System.arraycopy(buffer, readPos, b, bPos, len); - readPos += len; - if (readPos >= pos) { - buffer = null; - } - } else { - // Re-using the original consumer's output array is only - // allowed for one round. - buffer = null; - } - return len; - } - return eof ? -1 : 0; - } - - /** - * Sets the streaming buffer. This is a small optimization where we try to buffer directly to the consumer's output - * array for one round (if the consumer calls this method first) instead of starting our own buffer. - * - * @param out - * byte[] array to buffer directly to. - * @param outPos - * Position to start buffering into. - * @param outAvail - * Amount of bytes available for direct buffering. - */ - void setInitialBuffer(byte[] out, int outPos, int outAvail) { - // We can re-use consumer's original output array under - // special circumstances, saving on some System.arraycopy(). - if (out != null && out.length == outAvail) { - buffer = out; - pos = outPos; - readPos = outPos; - } - } - - /** - *

- * Encodes all of the provided data, starting at inPos, for inAvail bytes. Must be called at least twice: once with - * the data to encode, and once with inAvail set to "-1" to alert encoder that EOF has been reached, so flush last - * remaining bytes (if not multiple of 3). - *

- *

- * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. - * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ - *

- * - * @param in - * byte[] array of binary data to base64 encode. - * @param inPos - * Position to start reading data from. - * @param inAvail - * Amount of bytes available from input for encoding. - */ - void encode(byte[] in, int inPos, int inAvail) { - if (eof) { - return; - } - // inAvail < 0 is how we're informed of EOF in the underlying data we're - // encoding. - if (inAvail < 0) { - eof = true; - if (buffer == null || buffer.length - pos < encodeSize) { - resizeBuffer(); - } - switch (modulus) { - case 1 : - buffer[pos++] = encodeTable[(x >> 2) & MASK_6BITS]; - buffer[pos++] = encodeTable[(x << 4) & MASK_6BITS]; - // URL-SAFE skips the padding to further reduce size. - if (encodeTable == STANDARD_ENCODE_TABLE) { - buffer[pos++] = PAD; - buffer[pos++] = PAD; - } - break; - - case 2 : - buffer[pos++] = encodeTable[(x >> 10) & MASK_6BITS]; - buffer[pos++] = encodeTable[(x >> 4) & MASK_6BITS]; - buffer[pos++] = encodeTable[(x << 2) & MASK_6BITS]; - // URL-SAFE skips the padding to further reduce size. - if (encodeTable == STANDARD_ENCODE_TABLE) { - buffer[pos++] = PAD; - } - break; - } - if (lineLength > 0 && pos > 0) { - System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length); - pos += lineSeparator.length; - } - } else { - for (int i = 0; i < inAvail; i++) { - if (buffer == null || buffer.length - pos < encodeSize) { - resizeBuffer(); - } - modulus = (++modulus) % 3; - int b = in[inPos++]; - if (b < 0) { - b += 256; - } - x = (x << 8) + b; - if (0 == modulus) { - buffer[pos++] = encodeTable[(x >> 18) & MASK_6BITS]; - buffer[pos++] = encodeTable[(x >> 12) & MASK_6BITS]; - buffer[pos++] = encodeTable[(x >> 6) & MASK_6BITS]; - buffer[pos++] = encodeTable[x & MASK_6BITS]; - currentLinePos += 4; - if (lineLength > 0 && lineLength <= currentLinePos) { - System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length); - pos += lineSeparator.length; - currentLinePos = 0; - } - } - } - } - } - - /** - *

- * Decodes all of the provided data, starting at inPos, for inAvail bytes. Should be called at least twice: once - * with the data to decode, and once with inAvail set to "-1" to alert decoder that EOF has been reached. The "-1" - * call is not necessary when decoding, but it doesn't hurt, either. - *

- *

- * Ignores all non-base64 characters. This is how chunked (e.g. 76 character) data is handled, since CR and LF are - * silently ignored, but has implications for other bytes, too. This method subscribes to the garbage-in, - * garbage-out philosophy: it will not check the provided data for validity. - *

- *

- * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. - * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ - *

- * - * @param in - * byte[] array of ascii data to base64 decode. - * @param inPos - * Position to start reading data from. - * @param inAvail - * Amount of bytes available from input for encoding. - */ - void decode(byte[] in, int inPos, int inAvail) { - if (eof) { - return; - } - if (inAvail < 0) { - eof = true; - } - for (int i = 0; i < inAvail; i++) { - if (buffer == null || buffer.length - pos < decodeSize) { - resizeBuffer(); - } - byte b = in[inPos++]; - if (b == PAD) { - // We're done. - eof = true; - break; - } else { - if (b >= 0 && b < DECODE_TABLE.length) { - int result = DECODE_TABLE[b]; - if (result >= 0) { - modulus = (++modulus) % 4; - x = (x << 6) + result; - if (modulus == 0) { - buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS); - buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS); - buffer[pos++] = (byte) (x & MASK_8BITS); - } - } - } - } - } - - // Two forms of EOF as far as base64 decoder is concerned: actual - // EOF (-1) and first time '=' character is encountered in stream. - // This approach makes the '=' padding characters completely optional. - if (eof && modulus != 0) { - x = x << 6; - switch (modulus) { - case 2 : - x = x << 6; - buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS); - break; - case 3 : - buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS); - buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS); - break; - } - } - } - - /** - * Returns whether or not the octet is in the base 64 alphabet. - * - * @param octet - * The value to test - * @return true if the value is defined in the the base 64 alphabet, false otherwise. - * @since 1.4 - */ - public static boolean isBase64(byte octet) { - return octet == PAD || (octet >= 0 && octet < DECODE_TABLE.length && DECODE_TABLE[octet] != -1); - } - - /** - * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet. Currently the - * method treats whitespace as valid. - * - * @param arrayOctet - * byte array to test - * @return true if all bytes are valid characters in the Base64 alphabet or if the byte array is empty; - * false, otherwise - */ - public static boolean isArrayByteBase64(byte[] arrayOctet) { - for (int i = 0; i < arrayOctet.length; i++) { - if (!isBase64(arrayOctet[i]) && !isWhiteSpace(arrayOctet[i])) { - return false; - } - } - return true; - } - - /** - * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet. - * - * @param arrayOctet - * byte array to test - * @return true if any byte is a valid character in the Base64 alphabet; false herwise - */ - private static boolean containsBase64Byte(byte[] arrayOctet) { - for (int i = 0; i < arrayOctet.length; i++) { - if (isBase64(arrayOctet[i])) { - return true; - } - } - return false; - } - - /** - * Encodes binary data using the base64 algorithm but does not chunk the output. - * - * @param binaryData - * binary data to encode - * @return byte[] containing Base64 characters in their UTF-8 representation. - */ - public static byte[] encodeBase64(byte[] binaryData) { - return encodeBase64(binaryData, false); - } - - /** - * Encodes binary data using the base64 algorithm into 76 character blocks separated by CRLF. - * - * @param binaryData - * binary data to encode - * @return String containing Base64 characters. - * @since 1.4 - */ - public static String encodeBase64String(byte[] binaryData) { - return StringUtils.newStringUtf8(encodeBase64(binaryData, true)); - } - - /** - * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The - * url-safe variation emits - and _ instead of + and / characters. - * - * @param binaryData - * binary data to encode - * @return byte[] containing Base64 characters in their UTF-8 representation. - * @since 1.4 - */ - public static byte[] encodeBase64URLSafe(byte[] binaryData) { - return encodeBase64(binaryData, false, true); - } - - /** - * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The - * url-safe variation emits - and _ instead of + and / characters. - * - * @param binaryData - * binary data to encode - * @return String containing Base64 characters - * @since 1.4 - */ - public static String encodeBase64URLSafeString(byte[] binaryData) { - return StringUtils.newStringUtf8(encodeBase64(binaryData, false, true)); - } - - /** - * Encodes binary data using the base64 algorithm and chunks the encoded output into 76 character blocks - * - * @param binaryData - * binary data to encode - * @return Base64 characters chunked in 76 character blocks - */ - public static byte[] encodeBase64Chunked(byte[] binaryData) { - return encodeBase64(binaryData, true); - } - - /** - * Decodes an Object using the base64 algorithm. This method is provided in order to satisfy the requirements of the - * Decoder interface, and will throw a DecoderException if the supplied object is not of type byte[] or String. - * - * @param pObject - * Object to decode - * @return An object (of type byte[]) containing the binary data which corresponds to the byte[] or String supplied. - * @throws DecoderException - * if the parameter supplied is not of type byte[] - */ - public Object decode(Object pObject) throws DecoderException { - if (pObject instanceof byte[]) { - return decode((byte[]) pObject); - } else if (pObject instanceof String) { - return decode((String) pObject); - } else { - throw new DecoderException("Parameter supplied to Base64 decode is not a byte[] or a String"); - } - } - - /** - * Decodes a String containing containing characters in the Base64 alphabet. - * - * @param pArray - * A String containing Base64 character data - * @return a byte array containing binary data - * @since 1.4 - */ - public byte[] decode(String pArray) { - return decode(StringUtils.getBytesUtf8(pArray)); - } - - /** - * Decodes a byte[] containing containing characters in the Base64 alphabet. - * - * @param pArray - * A byte array containing Base64 character data - * @return a byte array containing binary data - */ - public byte[] decode(byte[] pArray) { - reset(); - if (pArray == null || pArray.length == 0) { - return pArray; - } - long len = (pArray.length * 3) / 4; - byte[] buf = new byte[(int) len]; - setInitialBuffer(buf, 0, buf.length); - decode(pArray, 0, pArray.length); - decode(pArray, 0, -1); // Notify decoder of EOF. - - // Would be nice to just return buf (like we sometimes do in the encode - // logic), but we have no idea what the line-length was (could even be - // variable). So we cannot determine ahead of time exactly how big an - // array is necessary. Hence the need to construct a 2nd byte array to - // hold the final result: - - byte[] result = new byte[pos]; - readResults(result, 0, result.length); - return result; - } - - /** - * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. - * - * @param binaryData - * Array containing binary data to encode. - * @param isChunked - * if true this encoder will chunk the base64 output into 76 character blocks - * @return Base64-encoded data. - * @throws IllegalArgumentException - * Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE} - */ - public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) { - return encodeBase64(binaryData, isChunked, false); - } - - /** - * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. - * - * @param binaryData - * Array containing binary data to encode. - * @param isChunked - * if true this encoder will chunk the base64 output into 76 character blocks - * @param urlSafe - * if true this encoder will emit - and _ instead of the usual + and / characters. - * @return Base64-encoded data. - * @throws IllegalArgumentException - * Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE} - * @since 1.4 - */ - public static byte[] encodeBase64(byte[] binaryData, boolean isChunked, boolean urlSafe) { - return encodeBase64(binaryData, isChunked, urlSafe, Integer.MAX_VALUE); - } - - /** - * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. - * - * @param binaryData - * Array containing binary data to encode. - * @param isChunked - * if true this encoder will chunk the base64 output into 76 character blocks - * @param urlSafe - * if true this encoder will emit - and _ instead of the usual + and / characters. - * @param maxResultSize - * The maximum result size to accept. - * @return Base64-encoded data. - * @throws IllegalArgumentException - * Thrown when the input array needs an output array bigger than maxResultSize - * @since 1.4 - */ - public static byte[] encodeBase64(byte[] binaryData, boolean isChunked, boolean urlSafe, int maxResultSize) { - if (binaryData == null || binaryData.length == 0) { - return binaryData; - } - - long len = getEncodeLength(binaryData, CHUNK_SIZE, CHUNK_SEPARATOR); - if (len > maxResultSize) { - throw new IllegalArgumentException("Input array too big, the output array would be bigger (" + - len + - ") than the specified maxium size of " + - maxResultSize); - } - - Base64Codec b64 = isChunked ? new Base64Codec(urlSafe) : new Base64Codec(0, CHUNK_SEPARATOR, urlSafe); - return b64.encode(binaryData); - } - - /** - * Decodes a Base64 String into octets - * - * @param base64String - * String containing Base64 data - * @return Array containing decoded data. - * @since 1.4 - */ - public static byte[] decodeBase64(String base64String) { - return new Base64Codec().decode(base64String); - } - - /** - * Decodes Base64 data into octets - * - * @param base64Data - * Byte array containing Base64 data - * @return Array containing decoded data. - */ - public static byte[] decodeBase64(byte[] base64Data) { - return new Base64Codec().decode(base64Data); - } - - /** - * Discards any whitespace from a base-64 encoded block. - * - * @param data - * The base-64 encoded data to discard the whitespace from. - * @return The data, less whitespace (see RFC 2045). - * @deprecated This method is no longer needed - */ - static byte[] discardWhitespace(byte[] data) { - byte groomedData[] = new byte[data.length]; - int bytesCopied = 0; - for (int i = 0; i < data.length; i++) { - switch (data[i]) { - case ' ' : - case '\n' : - case '\r' : - case '\t' : - break; - default : - groomedData[bytesCopied++] = data[i]; - } - } - byte packedData[] = new byte[bytesCopied]; - System.arraycopy(groomedData, 0, packedData, 0, bytesCopied); - return packedData; - } - - /** - * Checks if a byte value is whitespace or not. - * - * @param byteToCheck - * the byte to check - * @return true if byte is whitespace, false otherwise - */ - private static boolean isWhiteSpace(byte byteToCheck) { - switch (byteToCheck) { - case ' ' : - case '\n' : - case '\r' : - case '\t' : - return true; - default : - return false; - } - } - - // Implementation of the Encoder Interface - - /** - * Encodes an Object using the base64 algorithm. This method is provided in order to satisfy the requirements of the - * Encoder interface, and will throw an EncoderException if the supplied object is not of type byte[]. - * - * @param pObject - * Object to encode - * @return An object (of type byte[]) containing the base64 encoded data which corresponds to the byte[] supplied. - * @throws EncoderException - * if the parameter supplied is not of type byte[] - */ - public Object encode(Object pObject) throws EncoderException { - if (!(pObject instanceof byte[])) { - throw new EncoderException("Parameter supplied to Base64 encode is not a byte[]"); - } - return encode((byte[]) pObject); - } - - /** - * Encodes a byte[] containing binary data, into a String containing characters in the Base64 alphabet. - * - * @param pArray - * a byte array containing binary data - * @return A String containing only Base64 character data - * @since 1.4 - */ - public String encodeToString(byte[] pArray) { - return StringUtils.newStringUtf8(encode(pArray)); - } - - /** - * Encodes a byte[] containing binary data, into a byte[] containing characters in the Base64 alphabet. - * - * @param pArray - * a byte array containing binary data - * @return A byte array containing only Base64 character data - */ - public byte[] encode(byte[] pArray) { - reset(); - if (pArray == null || pArray.length == 0) { - return pArray; - } - long len = getEncodeLength(pArray, lineLength, lineSeparator); - byte[] buf = new byte[(int) len]; - setInitialBuffer(buf, 0, buf.length); - encode(pArray, 0, pArray.length); - encode(pArray, 0, -1); // Notify encoder of EOF. - // Encoder might have resized, even though it was unnecessary. - if (buffer != buf) { - readResults(buf, 0, buf.length); - } - // In URL-SAFE mode we skip the padding characters, so sometimes our - // final length is a bit smaller. - if (isUrlSafe() && pos < buf.length) { - byte[] smallerBuf = new byte[pos]; - System.arraycopy(buf, 0, smallerBuf, 0, pos); - buf = smallerBuf; - } - return buf; - } - - /** - * Pre-calculates the amount of space needed to base64-encode the supplied array. - * - * @param pArray byte[] array which will later be encoded - * @param chunkSize line-length of the output (<= 0 means no chunking) between each - * chunkSeparator (e.g. CRLF). - * @param chunkSeparator the sequence of bytes used to separate chunks of output (e.g. CRLF). - * - * @return amount of space needed to encoded the supplied array. Returns - * a long since a max-len array will require Integer.MAX_VALUE + 33%. - */ - private static long getEncodeLength(byte[] pArray, int chunkSize, byte[] chunkSeparator) { - // base64 always encodes to multiples of 4. - chunkSize = (chunkSize / 4) * 4; - - long len = (pArray.length * 4) / 3; - long mod = len % 4; - if (mod != 0) { - len += 4 - mod; - } - if (chunkSize > 0) { - boolean lenChunksPerfectly = len % chunkSize == 0; - len += (len / chunkSize) * chunkSeparator.length; - if (!lenChunksPerfectly) { - len += chunkSeparator.length; - } - } - return len; - } - - // Implementation of integer encoding used for crypto - /** - * Decodes a byte64-encoded integer according to crypto standards such as W3C's XML-Signature - * - * @param pArray - * a byte array containing base64 character data - * @return A BigInteger - * @since 1.4 - */ - public static BigInteger decodeInteger(byte[] pArray) { - return new BigInteger(1, decodeBase64(pArray)); - } - - /** - * Encodes to a byte64-encoded integer according to crypto standards such as W3C's XML-Signature - * - * @param bigInt - * a BigInteger - * @return A byte array containing base64 character data - * @throws NullPointerException - * if null is passed in - * @since 1.4 - */ - public static byte[] encodeInteger(BigInteger bigInt) { - if (bigInt == null) { - throw new NullPointerException("encodeInteger called with null parameter"); - } - return encodeBase64(toIntegerBytes(bigInt), false); - } - - /** - * Returns a byte-array representation of a BigInteger without sign bit. - * - * @param bigInt - * BigInteger to be converted - * @return a byte array representation of the BigInteger parameter - */ - static byte[] toIntegerBytes(BigInteger bigInt) { - int bitlen = bigInt.bitLength(); - // round bitlen - bitlen = ((bitlen + 7) >> 3) << 3; - byte[] bigBytes = bigInt.toByteArray(); - - if (((bigInt.bitLength() % 8) != 0) && (((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) { - return bigBytes; - } - // set up params for copying everything but sign bit - int startSrc = 0; - int len = bigBytes.length; - - // if bigInt is exactly byte-aligned, just skip signbit in copy - if ((bigInt.bitLength() % 8) == 0) { - startSrc = 1; - len--; - } - int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec - byte[] resizedBytes = new byte[bitlen / 8]; - System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len); - return resizedBytes; - } - - /** - * Resets this Base64 object to its initial newly constructed state. - */ - private void reset() { - buffer = null; - pos = 0; - readPos = 0; - currentLinePos = 0; - modulus = 0; - eof = false; - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.qpython.qsl4a.codec; + +import java.math.BigInteger; + +/** + * Provides Base64 encoding and decoding as defined by RFC 2045. + * + *

+ * This class implements section 6.8. Base64 Content-Transfer-Encoding from RFC 2045 Multipurpose + * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies by Freed and Borenstein. + *

+ *

+ * The class can be parameterized in the following manner with various constructors: + *

    + *
  • URL-safe mode: Default off.
  • + *
  • Line length: Default 76. Line length that aren't multiples of 4 will still essentially end up being multiples of + * 4 in the encoded data. + *
  • Line separator: Default is CRLF ("\r\n")
  • + *
+ *

+ *

+ * Since this class operates directly on byte streams, and not character streams, it is hard-coded to only encode/decode + * character encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, UTF-8, etc). + *

+ * + * @see RFC 2045 + * @author Apache Software Foundation + * @since 1.0 + * @version $Id: Base64.java 801706 2009-08-06 16:27:06Z niallp $ + */ +public class Base64Codec implements BinaryEncoder, BinaryDecoder { + private static final int DEFAULT_BUFFER_RESIZE_FACTOR = 2; + + private static final int DEFAULT_BUFFER_SIZE = 8192; + + /** + * Chunk size per RFC 2045 section 6.8. + * + *

+ * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any + * equal signs. + *

+ * + * @see RFC 2045 section 6.8 + */ + static final int CHUNK_SIZE = 76; + + /** + * Chunk separator per RFC 2045 section 2.1. + * + *

+ * N.B. The next major release may break compatibility and make this field private. + *

+ * + * @see RFC 2045 section 2.1 + */ + static final byte[] CHUNK_SEPARATOR = {'\r', '\n'}; + + /** + * This array is a lookup table that translates 6-bit positive integer index values into their "Base64 Alphabet" + * equivalents as specified in Table 1 of RFC 2045. + * + * Thanks to "commons" project in ws.apache.org for this code. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + */ + private static final byte[] STANDARD_ENCODE_TABLE = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' + }; + + /** + * This is a copy of the STANDARD_ENCODE_TABLE above, but with + and / + * changed to - and _ to make the encoded Base64 results more URL-SAFE. + * This table is only used when the Base64's mode is set to URL-SAFE. + */ + private static final byte[] URL_SAFE_ENCODE_TABLE = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' + }; + + /** + * Byte used to pad output. + */ + private static final byte PAD = '='; + + /** + * This array is a lookup table that translates Unicode characters drawn from the "Base64 Alphabet" (as specified in + * Table 1 of RFC 2045) into their 6-bit positive integer equivalents. Characters that are not in the Base64 + * alphabet but fall within the bounds of the array are translated to -1. + * + * Note: '+' and '-' both decode to 62. '/' and '_' both decode to 63. This means decoder seamlessly handles both + * URL_SAFE and STANDARD base64. (The encoder, on the other hand, needs to know ahead of time what to emit). + * + * Thanks to "commons" project in ws.apache.org for this code. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + */ + private static final byte[] DECODE_TABLE = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, -1, -1, -1, -1, 63, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 + }; + + /** Mask used to extract 6 bits, used when encoding */ + private static final int MASK_6BITS = 0x3f; + + /** Mask used to extract 8 bits, used in decoding base64 bytes */ + private static final int MASK_8BITS = 0xff; + + // The static final fields above are used for the original static byte[] methods on Base64. + // The private member fields below are used with the new streaming approach, which requires + // some state be preserved between calls of encode() and decode(). + + /** + * Encode table to use: either STANDARD or URL_SAFE. Note: the DECODE_TABLE above remains static because it is able + * to decode both STANDARD and URL_SAFE streams, but the encodeTable must be a member variable so we can switch + * between the two modes. + */ + private final byte[] encodeTable; + + /** + * Line length for encoding. Not used when decoding. A value of zero or less implies no chunking of the base64 + * encoded data. + */ + private final int lineLength; + + /** + * Line separator for encoding. Not used when decoding. Only used if lineLength > 0. + */ + private final byte[] lineSeparator; + + /** + * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing. + * decodeSize = 3 + lineSeparator.length; + */ + private final int decodeSize; + + /** + * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing. + * encodeSize = 4 + lineSeparator.length; + */ + private final int encodeSize; + + /** + * Buffer for streaming. + */ + private byte[] buffer; + + /** + * Position where next character should be written in the buffer. + */ + private int pos; + + /** + * Position where next character should be read from the buffer. + */ + private int readPos; + + /** + * Variable tracks how many characters have been written to the current line. Only used when encoding. We use it to + * make sure each encoded line never goes beyond lineLength (if lineLength > 0). + */ + private int currentLinePos; + + /** + * Writes to the buffer only occur after every 3 reads when encoding, an every 4 reads when decoding. This variable + * helps track that. + */ + private int modulus; + + /** + * Boolean flag to indicate the EOF has been reached. Once EOF has been reached, this Base64 object becomes useless, + * and must be thrown away. + */ + private boolean eof; + + /** + * Place holder for the 3 bytes we're dealing with for our base64 logic. Bitwise operations store and extract the + * base64 encoding or decoding from this variable. + */ + private int x; + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. + *

+ * When encoding the line length is 76, the line separator is CRLF, and the encoding table is STANDARD_ENCODE_TABLE. + *

+ * + *

+ * When decoding all variants are supported. + *

+ */ + public Base64Codec() { + this(false); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in the given URL-safe mode. + *

+ * When encoding the line length is 76, the line separator is CRLF, and the encoding table is STANDARD_ENCODE_TABLE. + *

+ * + *

+ * When decoding all variants are supported. + *

+ * + * @param urlSafe + * if true, URL-safe encoding is used. In most cases this should be set to + * false. + * @since 1.4 + */ + public Base64Codec(boolean urlSafe) { + this(CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. + *

+ * When encoding the line length is given in the constructor, the line separator is CRLF, and the encoding table is + * STANDARD_ENCODE_TABLE. + *

+ *

+ * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. + *

+ *

+ * When decoding all variants are supported. + *

+ * + * @param lineLength + * Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4). + * If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when decoding. + * @since 1.4 + */ + public Base64Codec(int lineLength) { + this(lineLength, CHUNK_SEPARATOR); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. + *

+ * When encoding the line length and line separator are given in the constructor, and the encoding table is + * STANDARD_ENCODE_TABLE. + *

+ *

+ * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. + *

+ *

+ * When decoding all variants are supported. + *

+ * + * @param lineLength + * Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4). + * If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when decoding. + * @param lineSeparator + * Each line of encoded data will end with this sequence of bytes. + * @throws IllegalArgumentException + * Thrown when the provided lineSeparator included some base64 characters. + * @since 1.4 + */ + public Base64Codec(int lineLength, byte[] lineSeparator) { + this(lineLength, lineSeparator, false); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. + *

+ * When encoding the line length and line separator are given in the constructor, and the encoding table is + * STANDARD_ENCODE_TABLE. + *

+ *

+ * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. + *

+ *

+ * When decoding all variants are supported. + *

+ * + * @param lineLength + * Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4). + * If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when decoding. + * @param lineSeparator + * Each line of encoded data will end with this sequence of bytes. + * @param urlSafe + * Instead of emitting '+' and '/' we emit '-' and '_' respectively. urlSafe is only applied to encode + * operations. Decoding seamlessly handles both modes. + * @throws IllegalArgumentException + * The provided lineSeparator included some base64 characters. That's not going to work! + * @since 1.4 + */ + public Base64Codec(int lineLength, byte[] lineSeparator, boolean urlSafe) { + if (lineSeparator == null) { + lineLength = 0; // disable chunk-separating + lineSeparator = CHUNK_SEPARATOR; // this just gets ignored + } + this.lineLength = lineLength > 0 ? (lineLength / 4) * 4 : 0; + this.lineSeparator = new byte[lineSeparator.length]; + System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length); + if (lineLength > 0) { + this.encodeSize = 4 + lineSeparator.length; + } else { + this.encodeSize = 4; + } + this.decodeSize = this.encodeSize - 1; + if (containsBase64Byte(lineSeparator)) { + String sep = StringUtils.newStringUtf8(lineSeparator); + throw new IllegalArgumentException("lineSeperator must not contain base64 characters: [" + sep + "]"); + } + this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE; + } + + /** + * Returns our current encode mode. True if we're URL-SAFE, false otherwise. + * + * @return true if we're in URL-SAFE mode, false otherwise. + * @since 1.4 + */ + public boolean isUrlSafe() { + return this.encodeTable == URL_SAFE_ENCODE_TABLE; + } + + /** + * Returns true if this Base64 object has buffered data for reading. + * + * @return true if there is Base64 object still available for reading. + */ + boolean hasData() { + return this.buffer != null; + } + + /** + * Returns the amount of buffered data available for reading. + * + * @return The amount of buffered data available for reading. + */ + int avail() { + return buffer != null ? pos - readPos : 0; + } + + /** Doubles our buffer. */ + private void resizeBuffer() { + if (buffer == null) { + buffer = new byte[DEFAULT_BUFFER_SIZE]; + pos = 0; + readPos = 0; + } else { + byte[] b = new byte[buffer.length * DEFAULT_BUFFER_RESIZE_FACTOR]; + System.arraycopy(buffer, 0, b, 0, buffer.length); + buffer = b; + } + } + + /** + * Extracts buffered data into the provided byte[] array, starting at position bPos, up to a maximum of bAvail + * bytes. Returns how many bytes were actually extracted. + * + * @param b + * byte[] array to extract the buffered data into. + * @param bPos + * position in byte[] array to start extraction at. + * @param bAvail + * amount of bytes we're allowed to extract. We may extract fewer (if fewer are available). + * @return The number of bytes successfully extracted into the provided byte[] array. + */ + int readResults(byte[] b, int bPos, int bAvail) { + if (buffer != null) { + int len = Math.min(avail(), bAvail); + if (buffer != b) { + System.arraycopy(buffer, readPos, b, bPos, len); + readPos += len; + if (readPos >= pos) { + buffer = null; + } + } else { + // Re-using the original consumer's output array is only + // allowed for one round. + buffer = null; + } + return len; + } + return eof ? -1 : 0; + } + + /** + * Sets the streaming buffer. This is a small optimization where we try to buffer directly to the consumer's output + * array for one round (if the consumer calls this method first) instead of starting our own buffer. + * + * @param out + * byte[] array to buffer directly to. + * @param outPos + * Position to start buffering into. + * @param outAvail + * Amount of bytes available for direct buffering. + */ + void setInitialBuffer(byte[] out, int outPos, int outAvail) { + // We can re-use consumer's original output array under + // special circumstances, saving on some System.arraycopy(). + if (out != null && out.length == outAvail) { + buffer = out; + pos = outPos; + readPos = outPos; + } + } + + /** + *

+ * Encodes all of the provided data, starting at inPos, for inAvail bytes. Must be called at least twice: once with + * the data to encode, and once with inAvail set to "-1" to alert encoder that EOF has been reached, so flush last + * remaining bytes (if not multiple of 3). + *

+ *

+ * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + *

+ * + * @param in + * byte[] array of binary data to base64 encode. + * @param inPos + * Position to start reading data from. + * @param inAvail + * Amount of bytes available from input for encoding. + */ + void encode(byte[] in, int inPos, int inAvail) { + if (eof) { + return; + } + // inAvail < 0 is how we're informed of EOF in the underlying data we're + // encoding. + if (inAvail < 0) { + eof = true; + if (buffer == null || buffer.length - pos < encodeSize) { + resizeBuffer(); + } + switch (modulus) { + case 1 : + buffer[pos++] = encodeTable[(x >> 2) & MASK_6BITS]; + buffer[pos++] = encodeTable[(x << 4) & MASK_6BITS]; + // URL-SAFE skips the padding to further reduce size. + if (encodeTable == STANDARD_ENCODE_TABLE) { + buffer[pos++] = PAD; + buffer[pos++] = PAD; + } + break; + + case 2 : + buffer[pos++] = encodeTable[(x >> 10) & MASK_6BITS]; + buffer[pos++] = encodeTable[(x >> 4) & MASK_6BITS]; + buffer[pos++] = encodeTable[(x << 2) & MASK_6BITS]; + // URL-SAFE skips the padding to further reduce size. + if (encodeTable == STANDARD_ENCODE_TABLE) { + buffer[pos++] = PAD; + } + break; + } + if (lineLength > 0 && pos > 0) { + System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length); + pos += lineSeparator.length; + } + } else { + for (int i = 0; i < inAvail; i++) { + if (buffer == null || buffer.length - pos < encodeSize) { + resizeBuffer(); + } + modulus = (++modulus) % 3; + int b = in[inPos++]; + if (b < 0) { + b += 256; + } + x = (x << 8) + b; + if (0 == modulus) { + buffer[pos++] = encodeTable[(x >> 18) & MASK_6BITS]; + buffer[pos++] = encodeTable[(x >> 12) & MASK_6BITS]; + buffer[pos++] = encodeTable[(x >> 6) & MASK_6BITS]; + buffer[pos++] = encodeTable[x & MASK_6BITS]; + currentLinePos += 4; + if (lineLength > 0 && lineLength <= currentLinePos) { + System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length); + pos += lineSeparator.length; + currentLinePos = 0; + } + } + } + } + } + + /** + *

+ * Decodes all of the provided data, starting at inPos, for inAvail bytes. Should be called at least twice: once + * with the data to decode, and once with inAvail set to "-1" to alert decoder that EOF has been reached. The "-1" + * call is not necessary when decoding, but it doesn't hurt, either. + *

+ *

+ * Ignores all non-base64 characters. This is how chunked (e.g. 76 character) data is handled, since CR and LF are + * silently ignored, but has implications for other bytes, too. This method subscribes to the garbage-in, + * garbage-out philosophy: it will not check the provided data for validity. + *

+ *

+ * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + *

+ * + * @param in + * byte[] array of ascii data to base64 decode. + * @param inPos + * Position to start reading data from. + * @param inAvail + * Amount of bytes available from input for encoding. + */ + void decode(byte[] in, int inPos, int inAvail) { + if (eof) { + return; + } + if (inAvail < 0) { + eof = true; + } + for (int i = 0; i < inAvail; i++) { + if (buffer == null || buffer.length - pos < decodeSize) { + resizeBuffer(); + } + byte b = in[inPos++]; + if (b == PAD) { + // We're done. + eof = true; + break; + } else { + if (b >= 0 && b < DECODE_TABLE.length) { + int result = DECODE_TABLE[b]; + if (result >= 0) { + modulus = (++modulus) % 4; + x = (x << 6) + result; + if (modulus == 0) { + buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS); + buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS); + buffer[pos++] = (byte) (x & MASK_8BITS); + } + } + } + } + } + + // Two forms of EOF as far as base64 decoder is concerned: actual + // EOF (-1) and first time '=' character is encountered in stream. + // This approach makes the '=' padding characters completely optional. + if (eof && modulus != 0) { + x = x << 6; + switch (modulus) { + case 2 : + x = x << 6; + buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS); + break; + case 3 : + buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS); + buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS); + break; + } + } + } + + /** + * Returns whether or not the octet is in the base 64 alphabet. + * + * @param octet + * The value to test + * @return true if the value is defined in the the base 64 alphabet, false otherwise. + * @since 1.4 + */ + public static boolean isBase64(byte octet) { + return octet == PAD || (octet >= 0 && octet < DECODE_TABLE.length && DECODE_TABLE[octet] != -1); + } + + /** + * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet. Currently the + * method treats whitespace as valid. + * + * @param arrayOctet + * byte array to test + * @return true if all bytes are valid characters in the Base64 alphabet or if the byte array is empty; + * false, otherwise + */ + public static boolean isArrayByteBase64(byte[] arrayOctet) { + for (int i = 0; i < arrayOctet.length; i++) { + if (!isBase64(arrayOctet[i]) && !isWhiteSpace(arrayOctet[i])) { + return false; + } + } + return true; + } + + /** + * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet. + * + * @param arrayOctet + * byte array to test + * @return true if any byte is a valid character in the Base64 alphabet; false herwise + */ + private static boolean containsBase64Byte(byte[] arrayOctet) { + for (int i = 0; i < arrayOctet.length; i++) { + if (isBase64(arrayOctet[i])) { + return true; + } + } + return false; + } + + /** + * Encodes binary data using the base64 algorithm but does not chunk the output. + * + * @param binaryData + * binary data to encode + * @return byte[] containing Base64 characters in their UTF-8 representation. + */ + public static byte[] encodeBase64(byte[] binaryData) { + return encodeBase64(binaryData, false); + } + + /** + * Encodes binary data using the base64 algorithm into 76 character blocks separated by CRLF. + * + * @param binaryData + * binary data to encode + * @return String containing Base64 characters. + * @since 1.4 + */ + public static String encodeBase64String(byte[] binaryData) { + return StringUtils.newStringUtf8(encodeBase64(binaryData, true)); + } + + /** + * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The + * url-safe variation emits - and _ instead of + and / characters. + * + * @param binaryData + * binary data to encode + * @return byte[] containing Base64 characters in their UTF-8 representation. + * @since 1.4 + */ + public static byte[] encodeBase64URLSafe(byte[] binaryData) { + return encodeBase64(binaryData, false, true); + } + + /** + * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The + * url-safe variation emits - and _ instead of + and / characters. + * + * @param binaryData + * binary data to encode + * @return String containing Base64 characters + * @since 1.4 + */ + public static String encodeBase64URLSafeString(byte[] binaryData) { + return StringUtils.newStringUtf8(encodeBase64(binaryData, false, true)); + } + + /** + * Encodes binary data using the base64 algorithm and chunks the encoded output into 76 character blocks + * + * @param binaryData + * binary data to encode + * @return Base64 characters chunked in 76 character blocks + */ + public static byte[] encodeBase64Chunked(byte[] binaryData) { + return encodeBase64(binaryData, true); + } + + /** + * Decodes an Object using the base64 algorithm. This method is provided in order to satisfy the requirements of the + * Decoder interface, and will throw a DecoderException if the supplied object is not of type byte[] or String. + * + * @param pObject + * Object to decode + * @return An object (of type byte[]) containing the binary data which corresponds to the byte[] or String supplied. + * @throws DecoderException + * if the parameter supplied is not of type byte[] + */ + public Object decode(Object pObject) throws DecoderException { + if (pObject instanceof byte[]) { + return decode((byte[]) pObject); + } else if (pObject instanceof String) { + return decode((String) pObject); + } else { + throw new DecoderException("Parameter supplied to Base64 decode is not a byte[] or a String"); + } + } + + /** + * Decodes a String containing containing characters in the Base64 alphabet. + * + * @param pArray + * A String containing Base64 character data + * @return a byte array containing binary data + * @since 1.4 + */ + public byte[] decode(String pArray) { + return decode(StringUtils.getBytesUtf8(pArray)); + } + + /** + * Decodes a byte[] containing containing characters in the Base64 alphabet. + * + * @param pArray + * A byte array containing Base64 character data + * @return a byte array containing binary data + */ + public byte[] decode(byte[] pArray) { + reset(); + if (pArray == null || pArray.length == 0) { + return pArray; + } + long len = (pArray.length * 3) / 4; + byte[] buf = new byte[(int) len]; + setInitialBuffer(buf, 0, buf.length); + decode(pArray, 0, pArray.length); + decode(pArray, 0, -1); // Notify decoder of EOF. + + // Would be nice to just return buf (like we sometimes do in the encode + // logic), but we have no idea what the line-length was (could even be + // variable). So we cannot determine ahead of time exactly how big an + // array is necessary. Hence the need to construct a 2nd byte array to + // hold the final result: + + byte[] result = new byte[pos]; + readResults(result, 0, result.length); + return result; + } + + /** + * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. + * + * @param binaryData + * Array containing binary data to encode. + * @param isChunked + * if true this encoder will chunk the base64 output into 76 character blocks + * @return Base64-encoded data. + * @throws IllegalArgumentException + * Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE} + */ + public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) { + return encodeBase64(binaryData, isChunked, false); + } + + /** + * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. + * + * @param binaryData + * Array containing binary data to encode. + * @param isChunked + * if true this encoder will chunk the base64 output into 76 character blocks + * @param urlSafe + * if true this encoder will emit - and _ instead of the usual + and / characters. + * @return Base64-encoded data. + * @throws IllegalArgumentException + * Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE} + * @since 1.4 + */ + public static byte[] encodeBase64(byte[] binaryData, boolean isChunked, boolean urlSafe) { + return encodeBase64(binaryData, isChunked, urlSafe, Integer.MAX_VALUE); + } + + /** + * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. + * + * @param binaryData + * Array containing binary data to encode. + * @param isChunked + * if true this encoder will chunk the base64 output into 76 character blocks + * @param urlSafe + * if true this encoder will emit - and _ instead of the usual + and / characters. + * @param maxResultSize + * The maximum result size to accept. + * @return Base64-encoded data. + * @throws IllegalArgumentException + * Thrown when the input array needs an output array bigger than maxResultSize + * @since 1.4 + */ + public static byte[] encodeBase64(byte[] binaryData, boolean isChunked, boolean urlSafe, int maxResultSize) { + if (binaryData == null || binaryData.length == 0) { + return binaryData; + } + + long len = getEncodeLength(binaryData, CHUNK_SIZE, CHUNK_SEPARATOR); + if (len > maxResultSize) { + throw new IllegalArgumentException("Input array too big, the output array would be bigger (" + + len + + ") than the specified maxium size of " + + maxResultSize); + } + + Base64Codec b64 = isChunked ? new Base64Codec(urlSafe) : new Base64Codec(0, CHUNK_SEPARATOR, urlSafe); + return b64.encode(binaryData); + } + + /** + * Decodes a Base64 String into octets + * + * @param base64String + * String containing Base64 data + * @return Array containing decoded data. + * @since 1.4 + */ + public static byte[] decodeBase64(String base64String) { + return new Base64Codec().decode(base64String); + } + + /** + * Decodes Base64 data into octets + * + * @param base64Data + * Byte array containing Base64 data + * @return Array containing decoded data. + */ + public static byte[] decodeBase64(byte[] base64Data) { + return new Base64Codec().decode(base64Data); + } + + /** + * Discards any whitespace from a base-64 encoded block. + * + * @param data + * The base-64 encoded data to discard the whitespace from. + * @return The data, less whitespace (see RFC 2045). + * @deprecated This method is no longer needed + */ + static byte[] discardWhitespace(byte[] data) { + byte groomedData[] = new byte[data.length]; + int bytesCopied = 0; + for (int i = 0; i < data.length; i++) { + switch (data[i]) { + case ' ' : + case '\n' : + case '\r' : + case '\t' : + break; + default : + groomedData[bytesCopied++] = data[i]; + } + } + byte packedData[] = new byte[bytesCopied]; + System.arraycopy(groomedData, 0, packedData, 0, bytesCopied); + return packedData; + } + + /** + * Checks if a byte value is whitespace or not. + * + * @param byteToCheck + * the byte to check + * @return true if byte is whitespace, false otherwise + */ + private static boolean isWhiteSpace(byte byteToCheck) { + switch (byteToCheck) { + case ' ' : + case '\n' : + case '\r' : + case '\t' : + return true; + default : + return false; + } + } + + // Implementation of the Encoder Interface + + /** + * Encodes an Object using the base64 algorithm. This method is provided in order to satisfy the requirements of the + * Encoder interface, and will throw an EncoderException if the supplied object is not of type byte[]. + * + * @param pObject + * Object to encode + * @return An object (of type byte[]) containing the base64 encoded data which corresponds to the byte[] supplied. + * @throws EncoderException + * if the parameter supplied is not of type byte[] + */ + public Object encode(Object pObject) throws EncoderException { + if (!(pObject instanceof byte[])) { + throw new EncoderException("Parameter supplied to Base64 encode is not a byte[]"); + } + return encode((byte[]) pObject); + } + + /** + * Encodes a byte[] containing binary data, into a String containing characters in the Base64 alphabet. + * + * @param pArray + * a byte array containing binary data + * @return A String containing only Base64 character data + * @since 1.4 + */ + public String encodeToString(byte[] pArray) { + return StringUtils.newStringUtf8(encode(pArray)); + } + + /** + * Encodes a byte[] containing binary data, into a byte[] containing characters in the Base64 alphabet. + * + * @param pArray + * a byte array containing binary data + * @return A byte array containing only Base64 character data + */ + public byte[] encode(byte[] pArray) { + reset(); + if (pArray == null || pArray.length == 0) { + return pArray; + } + long len = getEncodeLength(pArray, lineLength, lineSeparator); + byte[] buf = new byte[(int) len]; + setInitialBuffer(buf, 0, buf.length); + encode(pArray, 0, pArray.length); + encode(pArray, 0, -1); // Notify encoder of EOF. + // Encoder might have resized, even though it was unnecessary. + if (buffer != buf) { + readResults(buf, 0, buf.length); + } + // In URL-SAFE mode we skip the padding characters, so sometimes our + // final length is a bit smaller. + if (isUrlSafe() && pos < buf.length) { + byte[] smallerBuf = new byte[pos]; + System.arraycopy(buf, 0, smallerBuf, 0, pos); + buf = smallerBuf; + } + return buf; + } + + /** + * Pre-calculates the amount of space needed to base64-encode the supplied array. + * + * @param pArray byte[] array which will later be encoded + * @param chunkSize line-length of the output (<= 0 means no chunking) between each + * chunkSeparator (e.g. CRLF). + * @param chunkSeparator the sequence of bytes used to separate chunks of output (e.g. CRLF). + * + * @return amount of space needed to encoded the supplied array. Returns + * a long since a max-len array will require Integer.MAX_VALUE + 33%. + */ + private static long getEncodeLength(byte[] pArray, int chunkSize, byte[] chunkSeparator) { + // base64 always encodes to multiples of 4. + chunkSize = (chunkSize / 4) * 4; + + long len = (pArray.length * 4) / 3; + long mod = len % 4; + if (mod != 0) { + len += 4 - mod; + } + if (chunkSize > 0) { + boolean lenChunksPerfectly = len % chunkSize == 0; + len += (len / chunkSize) * chunkSeparator.length; + if (!lenChunksPerfectly) { + len += chunkSeparator.length; + } + } + return len; + } + + // Implementation of integer encoding used for crypto + /** + * Decodes a byte64-encoded integer according to crypto standards such as W3C's XML-Signature + * + * @param pArray + * a byte array containing base64 character data + * @return A BigInteger + * @since 1.4 + */ + public static BigInteger decodeInteger(byte[] pArray) { + return new BigInteger(1, decodeBase64(pArray)); + } + + /** + * Encodes to a byte64-encoded integer according to crypto standards such as W3C's XML-Signature + * + * @param bigInt + * a BigInteger + * @return A byte array containing base64 character data + * @throws NullPointerException + * if null is passed in + * @since 1.4 + */ + public static byte[] encodeInteger(BigInteger bigInt) { + if (bigInt == null) { + throw new NullPointerException("encodeInteger called with null parameter"); + } + return encodeBase64(toIntegerBytes(bigInt), false); + } + + /** + * Returns a byte-array representation of a BigInteger without sign bit. + * + * @param bigInt + * BigInteger to be converted + * @return a byte array representation of the BigInteger parameter + */ + static byte[] toIntegerBytes(BigInteger bigInt) { + int bitlen = bigInt.bitLength(); + // round bitlen + bitlen = ((bitlen + 7) >> 3) << 3; + byte[] bigBytes = bigInt.toByteArray(); + + if (((bigInt.bitLength() % 8) != 0) && (((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) { + return bigBytes; + } + // set up params for copying everything but sign bit + int startSrc = 0; + int len = bigBytes.length; + + // if bigInt is exactly byte-aligned, just skip signbit in copy + if ((bigInt.bitLength() % 8) == 0) { + startSrc = 1; + len--; + } + int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec + byte[] resizedBytes = new byte[bitlen / 8]; + System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len); + return resizedBytes; + } + + /** + * Resets this Base64 object to its initial newly constructed state. + */ + private void reset() { + buffer = null; + pos = 0; + readPos = 0; + currentLinePos = 0; + modulus = 0; + eof = false; + } + +} diff --git a/src/main/java/org/qpython/qsl4a/codec/BinaryDecoder.java b/src/main/java/org/qpython/qsl4a/codec/BinaryDecoder.java index 67243c6..2c3190a 100644 --- a/src/main/java/org/qpython/qsl4a/codec/BinaryDecoder.java +++ b/src/main/java/org/qpython/qsl4a/codec/BinaryDecoder.java @@ -1,42 +1,42 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.qpython.qsl4a.codec; - -/** - * Defines common decoding methods for byte array decoders. - * - * @author Apache Software Foundation - * @version $Id: BinaryDecoder.java 651573 2008-04-25 11:11:21Z niallp $ - */ -public interface BinaryDecoder extends Decoder { - - /** - * Decodes a byte array and returns the results as a byte array. - * - * @param pArray A byte array which has been encoded with the - * appropriate encoder - * - * @return a byte array that contains decoded content - * - * @throws DecoderException A decoder exception is thrown - * if a Decoder encounters a failure condition during - * the decode process. - */ - byte[] decode(byte[] pArray) throws DecoderException; -} - +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.qpython.qsl4a.codec; + +/** + * Defines common decoding methods for byte array decoders. + * + * @author Apache Software Foundation + * @version $Id: BinaryDecoder.java 651573 2008-04-25 11:11:21Z niallp $ + */ +public interface BinaryDecoder extends Decoder { + + /** + * Decodes a byte array and returns the results as a byte array. + * + * @param pArray A byte array which has been encoded with the + * appropriate encoder + * + * @return a byte array that contains decoded content + * + * @throws DecoderException A decoder exception is thrown + * if a Decoder encounters a failure condition during + * the decode process. + */ + byte[] decode(byte[] pArray) throws DecoderException; +} + diff --git a/src/main/java/org/qpython/qsl4a/codec/BinaryEncoder.java b/src/main/java/org/qpython/qsl4a/codec/BinaryEncoder.java index d1cf5bf..66b691a 100644 --- a/src/main/java/org/qpython/qsl4a/codec/BinaryEncoder.java +++ b/src/main/java/org/qpython/qsl4a/codec/BinaryEncoder.java @@ -1,42 +1,42 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.qpython.qsl4a.codec; - -/** - * Defines common encoding methods for byte array encoders. - * - * @author Apache Software Foundation - * @version $Id: BinaryEncoder.java 651573 2008-04-25 11:11:21Z niallp $ - */ -public interface BinaryEncoder extends Encoder { - - /** - * Encodes a byte array and return the encoded data - * as a byte array. - * - * @param pArray Data to be encoded - * - * @return A byte array containing the encoded data - * - * @throws EncoderException thrown if the Encoder - * encounters a failure condition during the - * encoding process. - */ - byte[] encode(byte[] pArray) throws EncoderException; -} - +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.qpython.qsl4a.codec; + +/** + * Defines common encoding methods for byte array encoders. + * + * @author Apache Software Foundation + * @version $Id: BinaryEncoder.java 651573 2008-04-25 11:11:21Z niallp $ + */ +public interface BinaryEncoder extends Encoder { + + /** + * Encodes a byte array and return the encoded data + * as a byte array. + * + * @param pArray Data to be encoded + * + * @return A byte array containing the encoded data + * + * @throws EncoderException thrown if the Encoder + * encounters a failure condition during the + * encoding process. + */ + byte[] encode(byte[] pArray) throws EncoderException; +} + diff --git a/src/main/java/org/qpython/qsl4a/codec/CharEncoding.java b/src/main/java/org/qpython/qsl4a/codec/CharEncoding.java index f9d9135..86b206e 100644 --- a/src/main/java/org/qpython/qsl4a/codec/CharEncoding.java +++ b/src/main/java/org/qpython/qsl4a/codec/CharEncoding.java @@ -1,126 +1,126 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.qpython.qsl4a.codec; - -/** - * Character encoding names required of every implementation of the Java platform. - * - * From the Java documentation Standard - * charsets: - *

- * Every implementation of the Java platform is required to support the following character encodings. Consult the - * release documentation for your implementation to see if any other encodings are supported. Consult the release - * documentation for your implementation to see if any other encodings are supported. - *

- * - *
    - *
  • US-ASCII
    - * Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the Unicode character set.
  • - *
  • ISO-8859-1
    - * ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1.
  • - *
  • UTF-8
    - * Eight-bit Unicode Transformation Format.
  • - *
  • UTF-16BE
    - * Sixteen-bit Unicode Transformation Format, big-endian byte order.
  • - *
  • UTF-16LE
    - * Sixteen-bit Unicode Transformation Format, little-endian byte order.
  • - *
  • UTF-16
    - * Sixteen-bit Unicode Transformation Format, byte order specified by a mandatory initial byte-order mark (either order - * accepted on input, big-endian used on output.)
  • - *
- * - * This perhaps would best belong in the [lang] project. Even if a similar interface is defined in [lang], it is not - * forseen that [codec] would be made to depend on [lang]. - * - * @see Standard charsets - * @author Apache Software Foundation - * @since 1.4 - * @version $Id: CharEncoding.java 797857 2009-07-25 23:43:33Z ggregory $ - */ -public class CharEncoding { - /** - * CharEncodingISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1.

- *

- * Every implementation of the Java platform is required to support this character encoding. - *

- * - * @see Standard charsets - */ - public static final String ISO_8859_1 = "ISO-8859-1"; - - /** - *

- * Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block of the Unicode character set. - *

- *

- * Every implementation of the Java platform is required to support this character encoding. - *

- * - * @see Standard charsets - */ - public static final String US_ASCII = "US-ASCII"; - - /** - *

- * Sixteen-bit Unicode Transformation Format, The byte order specified by a mandatory initial byte-order mark - * (either order accepted on input, big-endian used on output) - *

- *

- * Every implementation of the Java platform is required to support this character encoding. - *

- * - * @see Standard charsets - */ - public static final String UTF_16 = "UTF-16"; - - /** - *

- * Sixteen-bit Unicode Transformation Format, big-endian byte order. - *

- *

- * Every implementation of the Java platform is required to support this character encoding. - *

- * - * @see Standard charsets - */ - public static final String UTF_16BE = "UTF-16BE"; - - /** - *

- * Sixteen-bit Unicode Transformation Format, little-endian byte order. - *

- *

- * Every implementation of the Java platform is required to support this character encoding. - *

- * - * @see Standard charsets - */ - public static final String UTF_16LE = "UTF-16LE"; - - /** - *

- * Eight-bit Unicode Transformation Format. - *

- *

- * Every implementation of the Java platform is required to support this character encoding. - *

- * - * @see Standard charsets - */ - public static final String UTF_8 = "UTF-8"; +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.qpython.qsl4a.codec; + +/** + * Character encoding names required of every implementation of the Java platform. + * + * From the Java documentation Standard + * charsets: + *

+ * Every implementation of the Java platform is required to support the following character encodings. Consult the + * release documentation for your implementation to see if any other encodings are supported. Consult the release + * documentation for your implementation to see if any other encodings are supported. + *

+ * + *
    + *
  • US-ASCII
    + * Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the Unicode character set.
  • + *
  • ISO-8859-1
    + * ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1.
  • + *
  • UTF-8
    + * Eight-bit Unicode Transformation Format.
  • + *
  • UTF-16BE
    + * Sixteen-bit Unicode Transformation Format, big-endian byte order.
  • + *
  • UTF-16LE
    + * Sixteen-bit Unicode Transformation Format, little-endian byte order.
  • + *
  • UTF-16
    + * Sixteen-bit Unicode Transformation Format, byte order specified by a mandatory initial byte-order mark (either order + * accepted on input, big-endian used on output.)
  • + *
+ * + * This perhaps would best belong in the [lang] project. Even if a similar interface is defined in [lang], it is not + * forseen that [codec] would be made to depend on [lang]. + * + * @see Standard charsets + * @author Apache Software Foundation + * @since 1.4 + * @version $Id: CharEncoding.java 797857 2009-07-25 23:43:33Z ggregory $ + */ +public class CharEncoding { + /** + * CharEncodingISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1.

+ *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see Standard charsets + */ + public static final String ISO_8859_1 = "ISO-8859-1"; + + /** + *

+ * Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block of the Unicode character set. + *

+ *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see Standard charsets + */ + public static final String US_ASCII = "US-ASCII"; + + /** + *

+ * Sixteen-bit Unicode Transformation Format, The byte order specified by a mandatory initial byte-order mark + * (either order accepted on input, big-endian used on output) + *

+ *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see Standard charsets + */ + public static final String UTF_16 = "UTF-16"; + + /** + *

+ * Sixteen-bit Unicode Transformation Format, big-endian byte order. + *

+ *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see Standard charsets + */ + public static final String UTF_16BE = "UTF-16BE"; + + /** + *

+ * Sixteen-bit Unicode Transformation Format, little-endian byte order. + *

+ *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see Standard charsets + */ + public static final String UTF_16LE = "UTF-16LE"; + + /** + *

+ * Eight-bit Unicode Transformation Format. + *

+ *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see Standard charsets + */ + public static final String UTF_8 = "UTF-8"; } \ No newline at end of file diff --git a/src/main/java/org/qpython/qsl4a/codec/Decoder.java b/src/main/java/org/qpython/qsl4a/codec/Decoder.java index 7c1be53..7c4a29d 100644 --- a/src/main/java/org/qpython/qsl4a/codec/Decoder.java +++ b/src/main/java/org/qpython/qsl4a/codec/Decoder.java @@ -1,55 +1,55 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.qpython.qsl4a.codec; - -/** - *

Provides the highest level of abstraction for Decoders. - * This is the sister interface of {@link Encoder}. All - * Decoders implement this common generic interface.

- * - *

Allows a user to pass a generic Object to any Decoder - * implementation in the codec package.

- * - *

One of the two interfaces at the center of the codec package.

- * - * @author Apache Software Foundation - * @version $Id: Decoder.java 797690 2009-07-24 23:28:35Z ggregory $ - */ -public interface Decoder { - - /** - * Decodes an "encoded" Object and returns a "decoded" - * Object. Note that the implementation of this - * interface will try to cast the Object parameter - * to the specific type expected by a particular Decoder - * implementation. If a {@link ClassCastException} occurs - * this decode method will throw a DecoderException. - * - * @param pObject an object to "decode" - * - * @return a 'decoded" object - * - * @throws DecoderException a decoder exception can - * be thrown for any number of reasons. Some good - * candidates are that the parameter passed to this - * method is null, a param cannot be cast to the - * appropriate type for a specific encoder. - */ - Object decode(Object pObject) throws DecoderException; -} - +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.qpython.qsl4a.codec; + +/** + *

Provides the highest level of abstraction for Decoders. + * This is the sister interface of {@link Encoder}. All + * Decoders implement this common generic interface.

+ * + *

Allows a user to pass a generic Object to any Decoder + * implementation in the codec package.

+ * + *

One of the two interfaces at the center of the codec package.

+ * + * @author Apache Software Foundation + * @version $Id: Decoder.java 797690 2009-07-24 23:28:35Z ggregory $ + */ +public interface Decoder { + + /** + * Decodes an "encoded" Object and returns a "decoded" + * Object. Note that the implementation of this + * interface will try to cast the Object parameter + * to the specific type expected by a particular Decoder + * implementation. If a {@link ClassCastException} occurs + * this decode method will throw a DecoderException. + * + * @param pObject an object to "decode" + * + * @return a 'decoded" object + * + * @throws DecoderException a decoder exception can + * be thrown for any number of reasons. Some good + * candidates are that the parameter passed to this + * method is null, a param cannot be cast to the + * appropriate type for a specific encoder. + */ + Object decode(Object pObject) throws DecoderException; +} + diff --git a/src/main/java/org/qpython/qsl4a/codec/DecoderException.java b/src/main/java/org/qpython/qsl4a/codec/DecoderException.java index 509d8ac..4243c77 100644 --- a/src/main/java/org/qpython/qsl4a/codec/DecoderException.java +++ b/src/main/java/org/qpython/qsl4a/codec/DecoderException.java @@ -1,88 +1,88 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.qpython.qsl4a.codec; - -/** - * Thrown when a Decoder has encountered a failure condition during a decode. - * - * @author Apache Software Foundation - * @version $Id: DecoderException.java 797804 2009-07-25 17:27:04Z ggregory $ - */ -public class DecoderException extends Exception { - - /** - * Declares the Serial Version Uid. - * - * @see Always Declare Serial Version Uid - */ - private static final long serialVersionUID = 1L; - - /** - * Constructs a new exception with null as its detail message. The cause is not initialized, and may - * subsequently be initialized by a call to {@link #initCause}. - * - * @since 1.4 - */ - public DecoderException() { - super(); - } - - /** - * Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently - * be initialized by a call to {@link #initCause}. - * - * @param message - * The detail message which is saved for later retrieval by the {@link #getMessage()} method. - */ - public DecoderException(String message) { - super(message); - } - - /** - * Constructsa new exception with the specified detail message and cause. - * - *

- * Note that the detail message associated with cause is not automatically incorporated into this - * exception's detail message. - *

- * - * @param message - * The detail message which is saved for later retrieval by the {@link #getMessage()} method. - * @param cause - * The cause which is saved for later retrieval by the {@link #getCause()} method. A null - * value is permitted, and indicates that the cause is nonexistent or unknown. - * @since 1.4 - */ - public DecoderException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Constructs a new exception with the specified cause and a detail message of (cause==null ? - * null : cause.toString()) (which typically contains the class and detail message of cause). - * This constructor is useful for exceptions that are little more than wrappers for other throwables. - * - * @param cause - * The cause which is saved for later retrieval by the {@link #getCause()} method. A null - * value is permitted, and indicates that the cause is nonexistent or unknown. - * @since 1.4 - */ - public DecoderException(Throwable cause) { - super(cause); - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.qpython.qsl4a.codec; + +/** + * Thrown when a Decoder has encountered a failure condition during a decode. + * + * @author Apache Software Foundation + * @version $Id: DecoderException.java 797804 2009-07-25 17:27:04Z ggregory $ + */ +public class DecoderException extends Exception { + + /** + * Declares the Serial Version Uid. + * + * @see Always Declare Serial Version Uid + */ + private static final long serialVersionUID = 1L; + + /** + * Constructs a new exception with null as its detail message. The cause is not initialized, and may + * subsequently be initialized by a call to {@link #initCause}. + * + * @since 1.4 + */ + public DecoderException() { + super(); + } + + /** + * Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently + * be initialized by a call to {@link #initCause}. + * + * @param message + * The detail message which is saved for later retrieval by the {@link #getMessage()} method. + */ + public DecoderException(String message) { + super(message); + } + + /** + * Constructsa new exception with the specified detail message and cause. + * + *

+ * Note that the detail message associated with cause is not automatically incorporated into this + * exception's detail message. + *

+ * + * @param message + * The detail message which is saved for later retrieval by the {@link #getMessage()} method. + * @param cause + * The cause which is saved for later retrieval by the {@link #getCause()} method. A null + * value is permitted, and indicates that the cause is nonexistent or unknown. + * @since 1.4 + */ + public DecoderException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new exception with the specified cause and a detail message of (cause==null ? + * null : cause.toString()) (which typically contains the class and detail message of cause). + * This constructor is useful for exceptions that are little more than wrappers for other throwables. + * + * @param cause + * The cause which is saved for later retrieval by the {@link #getCause()} method. A null + * value is permitted, and indicates that the cause is nonexistent or unknown. + * @since 1.4 + */ + public DecoderException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/org/qpython/qsl4a/codec/Encoder.java b/src/main/java/org/qpython/qsl4a/codec/Encoder.java index c053c4b..b5c1c13 100644 --- a/src/main/java/org/qpython/qsl4a/codec/Encoder.java +++ b/src/main/java/org/qpython/qsl4a/codec/Encoder.java @@ -1,46 +1,46 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.qpython.qsl4a.codec; - -/** - *

Provides the highest level of abstraction for Encoders. - * This is the sister interface of {@link Decoder}. Every implementation of - * Encoder provides this common generic interface whic allows a user to pass a - * generic Object to any Encoder implementation in the codec package.

- * - * @author Apache Software Foundation - * @version $Id: Encoder.java 634915 2008-03-08 09:30:25Z bayard $ - */ -public interface Encoder { - - /** - * Encodes an "Object" and returns the encoded content - * as an Object. The Objects here may just be byte[] - * or Strings depending on the implementation used. - * - * @param pObject An object ot encode - * - * @return An "encoded" Object - * - * @throws EncoderException an encoder exception is - * thrown if the encoder experiences a failure - * condition during the encoding process. - */ - Object encode(Object pObject) throws EncoderException; -} - +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.qpython.qsl4a.codec; + +/** + *

Provides the highest level of abstraction for Encoders. + * This is the sister interface of {@link Decoder}. Every implementation of + * Encoder provides this common generic interface whic allows a user to pass a + * generic Object to any Encoder implementation in the codec package.

+ * + * @author Apache Software Foundation + * @version $Id: Encoder.java 634915 2008-03-08 09:30:25Z bayard $ + */ +public interface Encoder { + + /** + * Encodes an "Object" and returns the encoded content + * as an Object. The Objects here may just be byte[] + * or Strings depending on the implementation used. + * + * @param pObject An object ot encode + * + * @return An "encoded" Object + * + * @throws EncoderException an encoder exception is + * thrown if the encoder experiences a failure + * condition during the encoding process. + */ + Object encode(Object pObject) throws EncoderException; +} + diff --git a/src/main/java/org/qpython/qsl4a/codec/EncoderException.java b/src/main/java/org/qpython/qsl4a/codec/EncoderException.java index b809e14..ad4cb3b 100644 --- a/src/main/java/org/qpython/qsl4a/codec/EncoderException.java +++ b/src/main/java/org/qpython/qsl4a/codec/EncoderException.java @@ -1,90 +1,90 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.qpython.qsl4a.codec; - -/** - * Thrown when there is a failure condition during the encoding process. This exception is thrown when an Encoder - * encounters a encoding specific exception such as invalid data, inability to calculate a checksum, characters outside - * of the expected range. - * - * @author Apache Software Foundation - * @version $Id: EncoderException.java 797804 2009-07-25 17:27:04Z ggregory $ - */ -public class EncoderException extends Exception { - - /** - * Declares the Serial Version Uid. - * - * @see Always Declare Serial Version Uid - */ - private static final long serialVersionUID = 1L; - - /** - * Constructs a new exception with null as its detail message. The cause is not initialized, and may - * subsequently be initialized by a call to {@link #initCause}. - * - * @since 1.4 - */ - public EncoderException() { - super(); - } - - /** - * Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently - * be initialized by a call to {@link #initCause}. - * - * @param message - * a useful message relating to the encoder specific error. - */ - public EncoderException(String message) { - super(message); - } - - /** - * Constructs a new exception with the specified detail message and cause. - * - *

- * Note that the detail message associated with cause is not automatically incorporated into this - * exception's detail message. - *

- * - * @param message - * The detail message which is saved for later retrieval by the {@link #getMessage()} method. - * @param cause - * The cause which is saved for later retrieval by the {@link #getCause()} method. A null - * value is permitted, and indicates that the cause is nonexistent or unknown. - * @since 1.4 - */ - public EncoderException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Constructs a new exception with the specified cause and a detail message of (cause==null ? - * null : cause.toString()) (which typically contains the class and detail message of cause). - * This constructor is useful for exceptions that are little more than wrappers for other throwables. - * - * @param cause - * The cause which is saved for later retrieval by the {@link #getCause()} method. A null - * value is permitted, and indicates that the cause is nonexistent or unknown. - * @since 1.4 - */ - public EncoderException(Throwable cause) { - super(cause); - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.qpython.qsl4a.codec; + +/** + * Thrown when there is a failure condition during the encoding process. This exception is thrown when an Encoder + * encounters a encoding specific exception such as invalid data, inability to calculate a checksum, characters outside + * of the expected range. + * + * @author Apache Software Foundation + * @version $Id: EncoderException.java 797804 2009-07-25 17:27:04Z ggregory $ + */ +public class EncoderException extends Exception { + + /** + * Declares the Serial Version Uid. + * + * @see Always Declare Serial Version Uid + */ + private static final long serialVersionUID = 1L; + + /** + * Constructs a new exception with null as its detail message. The cause is not initialized, and may + * subsequently be initialized by a call to {@link #initCause}. + * + * @since 1.4 + */ + public EncoderException() { + super(); + } + + /** + * Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently + * be initialized by a call to {@link #initCause}. + * + * @param message + * a useful message relating to the encoder specific error. + */ + public EncoderException(String message) { + super(message); + } + + /** + * Constructs a new exception with the specified detail message and cause. + * + *

+ * Note that the detail message associated with cause is not automatically incorporated into this + * exception's detail message. + *

+ * + * @param message + * The detail message which is saved for later retrieval by the {@link #getMessage()} method. + * @param cause + * The cause which is saved for later retrieval by the {@link #getCause()} method. A null + * value is permitted, and indicates that the cause is nonexistent or unknown. + * @since 1.4 + */ + public EncoderException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new exception with the specified cause and a detail message of (cause==null ? + * null : cause.toString()) (which typically contains the class and detail message of cause). + * This constructor is useful for exceptions that are little more than wrappers for other throwables. + * + * @param cause + * The cause which is saved for later retrieval by the {@link #getCause()} method. A null + * value is permitted, and indicates that the cause is nonexistent or unknown. + * @since 1.4 + */ + public EncoderException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/org/qpython/qsl4a/codec/StreamGobbler.java b/src/main/java/org/qpython/qsl4a/codec/StreamGobbler.java index 340fe8d..63b5933 100644 --- a/src/main/java/org/qpython/qsl4a/codec/StreamGobbler.java +++ b/src/main/java/org/qpython/qsl4a/codec/StreamGobbler.java @@ -1,310 +1,310 @@ -package org.qpython.qsl4a.codec; - - - -import org.qpython.qsl4a.qsl4a.LogUtil; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; - -/** - * A StreamGobbler is an InputStream that uses an internal worker thread to constantly consume input from - * another InputStream. It uses a buffer to store the consumed data. The buffer size is automatically adjusted, if - * needed. - *

- * This class is sometimes very convenient - if you wrap a session's STDOUT and STDERR InputStreams with instances of - * this class, then you don't have to bother about the shared window of STDOUT and STDERR in the low level SSH-2 - * protocol, since all arriving data will be immediatelly consumed by the worker threads. Also, as a side effect, the - * streams will be buffered (e.g., single byte read() operations are faster). - *

- * Other SSH for Java libraries include this functionality by default in their STDOUT and STDERR InputStream - * implementations, however, please be aware that this approach has also a downside: - *

- * If you do not call the StreamGobbler's read() method often enough and the peer is constantly sending - * huge amounts of data, then you will sooner or later encounter a low memory situation due to the aggregated data - * (well, it also depends on the Java heap size). Joe Average will like this class anyway - a paranoid programmer would - * never use such an approach. - *

- * The term "StreamGobbler" was taken from an article called "When Runtime.exec() won't", see - * http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html. - * - * @author Christian Plattner, plattner@trilead.com - * @version $Id: StreamGobbler.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ - */ - -public class StreamGobbler extends InputStream { - class GobblerThread extends Thread { - /* - @Override - public void run() { - - while (true) { - try { - byte[] saveBuffer = null; - - int avail = is.read(buffer, write_pos, buffer.length - write_pos); - synchronized (synchronizer) { - if (avail <= 0) { - isEOF = true; - synchronizer.notifyAll(); - break; - } - write_pos += avail; - - int space_available = buffer.length - write_pos; - LogUtil.e("space_available:" + buffer.length + "-" + write_pos); - - if (space_available == 0) { - if (read_pos > 0) { - saveBuffer = new byte[read_pos]; - System.arraycopy(buffer, 0, saveBuffer, 0, read_pos); - System.arraycopy(buffer, read_pos, buffer, 0, buffer.length - read_pos); - write_pos -= read_pos; - read_pos = 0; - - LogUtil.e("read_pos > 0:" + buffer); - - } - else { - write_pos = 0; - saveBuffer = buffer; - - LogUtil.e("read_pos <=0 :" + buffer); - - } - } - - synchronizer.notifyAll(); - } - writeToFile(saveBuffer); - - } - catch (IOException e) { - synchronized (synchronizer) { - exception = e; - synchronizer.notifyAll(); - break; - } - } - } - }*/ - - public void run() { - byte[] buff = new byte[8192]; - - while (true) { - try { - int avail = is.read(buff); - - synchronized (synchronizer) { - if (avail <= 0) { - isEOF = true; - synchronizer.notifyAll(); - break; - } - - int space_available = buffer.length - write_pos; - - if (space_available < avail) { - - int unread_size = write_pos - read_pos; - int need_space = unread_size + avail; - - byte[] new_buffer = buffer; - - if (need_space > buffer.length) { - int inc = need_space / 3; - inc = (inc < 256) ? 256 : inc; - inc = (inc > 8192) ? 8192 : inc; - new_buffer = new byte[need_space + inc]; - } - - if (unread_size > 0) - System.arraycopy(buffer, read_pos, new_buffer, 0, unread_size); - - buffer = new_buffer; - - read_pos = 0; - write_pos = unread_size; - } - - byte[] s_buffer = new byte[avail]; - System.arraycopy(buff, 0, s_buffer, 0, avail); - System.arraycopy(buff, 0, buffer, write_pos, avail); - write_pos += avail; - - synchronizer.notifyAll(); - writeToFile(s_buffer); - // LogUtil.e("OUTPUT:"+String.valueOf(s_buffer)); - } - - } - catch (IOException e) { - synchronized (synchronizer) { - exception = e; - synchronizer.notifyAll(); - break; - } - } - } - } - } - - private InputStream is; - - private GobblerThread t; - - private Object synchronizer = new Object(); - - private boolean isEOF = false; - - private boolean isClosed = false; - - private IOException exception = null; - - private byte[] buffer; - - private int read_pos = 0; - - private int write_pos = 0; - - private final FileOutputStream mLogStream; - - private final int mBufferSize; - - public StreamGobbler(InputStream is, File log, int buffer_size) { - this.is = is; - mBufferSize = buffer_size; - FileOutputStream out = null; - try { - out = new FileOutputStream(log, false); - } - catch (IOException e) { - LogUtil.e(e); - } - mLogStream = out; - buffer = new byte[mBufferSize]; - t = new GobblerThread(); - t.setDaemon(true); - t.start(); - } - - public void writeToFile(byte[] buffer) { - - if (mLogStream != null && buffer != null) { - try { - mLogStream.write(buffer); - } - catch (IOException e) { - LogUtil.e(e); - } - } - } - - @Override - public int read() throws IOException { - synchronized (synchronizer) { - if (isClosed) { - throw new IOException("This StreamGobbler is closed."); - } - - while (read_pos == write_pos) { - if (exception != null) { - throw exception; - } - - if (isEOF) { - return -1; - } - - try { - synchronizer.wait(); - } - catch (InterruptedException e) { - } - } - - int b = buffer[read_pos++] & 0xff; - - return b; - } - } - - @Override - public int available() throws IOException { - synchronized (synchronizer) { - if (isClosed) { - throw new IOException("This StreamGobbler is closed."); - } - - return write_pos - read_pos; - } - } - - @Override - public int read(byte[] b) throws IOException { - return read(b, 0, b.length); - } - - @Override - public void close() throws IOException { - synchronized (synchronizer) { - if (isClosed) { - return; - } - isClosed = true; - isEOF = true; - synchronizer.notifyAll(); - is.close(); - } - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - if (b == null) { - throw new NullPointerException(); - } - - if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length)) { - throw new IndexOutOfBoundsException(); - } - - if (len == 0) { - return 0; - } - - synchronized (synchronizer) { - if (isClosed) { - throw new IOException("This StreamGobbler is closed."); - } - - while (read_pos == write_pos) { - if (exception != null) { - throw exception; - } - - if (isEOF) { - return -1; - } - - try { - synchronizer.wait(); - } - catch (InterruptedException e) { - } - } - - int avail = write_pos - read_pos; - - avail = (avail > len) ? len : avail; - - System.arraycopy(buffer, read_pos, b, off, avail); - - read_pos += avail; - - return avail; - } - } -} +package org.qpython.qsl4a.codec; + + + +import org.qpython.qsl4a.qsl4a.LogUtil; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * A StreamGobbler is an InputStream that uses an internal worker thread to constantly consume input from + * another InputStream. It uses a buffer to store the consumed data. The buffer size is automatically adjusted, if + * needed. + *

+ * This class is sometimes very convenient - if you wrap a session's STDOUT and STDERR InputStreams with instances of + * this class, then you don't have to bother about the shared window of STDOUT and STDERR in the low level SSH-2 + * protocol, since all arriving data will be immediatelly consumed by the worker threads. Also, as a side effect, the + * streams will be buffered (e.g., single byte read() operations are faster). + *

+ * Other SSH for Java libraries include this functionality by default in their STDOUT and STDERR InputStream + * implementations, however, please be aware that this approach has also a downside: + *

+ * If you do not call the StreamGobbler's read() method often enough and the peer is constantly sending + * huge amounts of data, then you will sooner or later encounter a low memory situation due to the aggregated data + * (well, it also depends on the Java heap size). Joe Average will like this class anyway - a paranoid programmer would + * never use such an approach. + *

+ * The term "StreamGobbler" was taken from an article called "When Runtime.exec() won't", see + * http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: StreamGobbler.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ + */ + +public class StreamGobbler extends InputStream { + class GobblerThread extends Thread { + /* + @Override + public void run() { + + while (true) { + try { + byte[] saveBuffer = null; + + int avail = is.read(buffer, write_pos, buffer.length - write_pos); + synchronized (synchronizer) { + if (avail <= 0) { + isEOF = true; + synchronizer.notifyAll(); + break; + } + write_pos += avail; + + int space_available = buffer.length - write_pos; + LogUtil.e("space_available:" + buffer.length + "-" + write_pos); + + if (space_available == 0) { + if (read_pos > 0) { + saveBuffer = new byte[read_pos]; + System.arraycopy(buffer, 0, saveBuffer, 0, read_pos); + System.arraycopy(buffer, read_pos, buffer, 0, buffer.length - read_pos); + write_pos -= read_pos; + read_pos = 0; + + LogUtil.e("read_pos > 0:" + buffer); + + } + else { + write_pos = 0; + saveBuffer = buffer; + + LogUtil.e("read_pos <=0 :" + buffer); + + } + } + + synchronizer.notifyAll(); + } + writeToFile(saveBuffer); + + } + catch (IOException e) { + synchronized (synchronizer) { + exception = e; + synchronizer.notifyAll(); + break; + } + } + } + }*/ + + public void run() { + byte[] buff = new byte[8192]; + + while (true) { + try { + int avail = is.read(buff); + + synchronized (synchronizer) { + if (avail <= 0) { + isEOF = true; + synchronizer.notifyAll(); + break; + } + + int space_available = buffer.length - write_pos; + + if (space_available < avail) { + + int unread_size = write_pos - read_pos; + int need_space = unread_size + avail; + + byte[] new_buffer = buffer; + + if (need_space > buffer.length) { + int inc = need_space / 3; + inc = (inc < 256) ? 256 : inc; + inc = (inc > 8192) ? 8192 : inc; + new_buffer = new byte[need_space + inc]; + } + + if (unread_size > 0) + System.arraycopy(buffer, read_pos, new_buffer, 0, unread_size); + + buffer = new_buffer; + + read_pos = 0; + write_pos = unread_size; + } + + byte[] s_buffer = new byte[avail]; + System.arraycopy(buff, 0, s_buffer, 0, avail); + System.arraycopy(buff, 0, buffer, write_pos, avail); + write_pos += avail; + + synchronizer.notifyAll(); + writeToFile(s_buffer); + // LogUtil.e("OUTPUT:"+String.valueOf(s_buffer)); + } + + } + catch (IOException e) { + synchronized (synchronizer) { + exception = e; + synchronizer.notifyAll(); + break; + } + } + } + } + } + + private InputStream is; + + private GobblerThread t; + + private Object synchronizer = new Object(); + + private boolean isEOF = false; + + private boolean isClosed = false; + + private IOException exception = null; + + private byte[] buffer; + + private int read_pos = 0; + + private int write_pos = 0; + + private final FileOutputStream mLogStream; + + private final int mBufferSize; + + public StreamGobbler(InputStream is, File log, int buffer_size) { + this.is = is; + mBufferSize = buffer_size; + FileOutputStream out = null; + try { + out = new FileOutputStream(log, false); + } + catch (IOException e) { + LogUtil.e(e); + } + mLogStream = out; + buffer = new byte[mBufferSize]; + t = new GobblerThread(); + t.setDaemon(true); + t.start(); + } + + public void writeToFile(byte[] buffer) { + + if (mLogStream != null && buffer != null) { + try { + mLogStream.write(buffer); + } + catch (IOException e) { + LogUtil.e(e); + } + } + } + + @Override + public int read() throws IOException { + synchronized (synchronizer) { + if (isClosed) { + throw new IOException("This StreamGobbler is closed."); + } + + while (read_pos == write_pos) { + if (exception != null) { + throw exception; + } + + if (isEOF) { + return -1; + } + + try { + synchronizer.wait(); + } + catch (InterruptedException e) { + } + } + + int b = buffer[read_pos++] & 0xff; + + return b; + } + } + + @Override + public int available() throws IOException { + synchronized (synchronizer) { + if (isClosed) { + throw new IOException("This StreamGobbler is closed."); + } + + return write_pos - read_pos; + } + } + + @Override + public int read(byte[] b) throws IOException { + return read(b, 0, b.length); + } + + @Override + public void close() throws IOException { + synchronized (synchronizer) { + if (isClosed) { + return; + } + isClosed = true; + isEOF = true; + synchronizer.notifyAll(); + is.close(); + } + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (b == null) { + throw new NullPointerException(); + } + + if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length)) { + throw new IndexOutOfBoundsException(); + } + + if (len == 0) { + return 0; + } + + synchronized (synchronizer) { + if (isClosed) { + throw new IOException("This StreamGobbler is closed."); + } + + while (read_pos == write_pos) { + if (exception != null) { + throw exception; + } + + if (isEOF) { + return -1; + } + + try { + synchronizer.wait(); + } + catch (InterruptedException e) { + } + } + + int avail = write_pos - read_pos; + + avail = (avail > len) ? len : avail; + + System.arraycopy(buffer, read_pos, b, off, avail); + + read_pos += avail; + + return avail; + } + } +} diff --git a/src/main/java/org/qpython/qsl4a/codec/StringUtils.java b/src/main/java/org/qpython/qsl4a/codec/StringUtils.java index 487c69d..64096fc 100644 --- a/src/main/java/org/qpython/qsl4a/codec/StringUtils.java +++ b/src/main/java/org/qpython/qsl4a/codec/StringUtils.java @@ -1,277 +1,277 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.qpython.qsl4a.codec; - -import java.io.UnsupportedEncodingException; - -/** - * Converts String to and from bytes using the encodings required by the Java specification. These encodings are specified in Standard charsets - * - * @see CharEncoding - * @see Standard charsets - * @author Gary Gregory - * @version $Id: StringUtils.java 801391 2009-08-05 19:55:54Z ggregory $ - * @since 1.4 - */ -public class StringUtils { - - /** - * Encodes the given string into a sequence of bytes using the ISO-8859-1 charset, storing the result into a new - * byte array. - * - * @param string - * the String to encode - * @return encoded bytes - * @throws IllegalStateException - * Thrown when the charset is missing, which should be never according the the Java specification. - * @see Standard charsets - * @see #getBytesUnchecked(String, String) - */ - public static byte[] getBytesIso8859_1(String string) { - return StringUtils.getBytesUnchecked(string, CharEncoding.ISO_8859_1); - } - - /** - * Encodes the given string into a sequence of bytes using the US-ASCII charset, storing the result into a new byte - * array. - * - * @param string - * the String to encode - * @return encoded bytes - * @throws IllegalStateException - * Thrown when the charset is missing, which should be never according the the Java specification. - * @see Standard charsets - * @see #getBytesUnchecked(String, String) - */ - public static byte[] getBytesUsAscii(String string) { - return StringUtils.getBytesUnchecked(string, CharEncoding.US_ASCII); - } - - /** - * Encodes the given string into a sequence of bytes using the UTF-16 charset, storing the result into a new byte - * array. - * - * @param string - * the String to encode - * @return encoded bytes - * @throws IllegalStateException - * Thrown when the charset is missing, which should be never according the the Java specification. - * @see Standard charsets - * @see #getBytesUnchecked(String, String) - */ - public static byte[] getBytesUtf16(String string) { - return StringUtils.getBytesUnchecked(string, CharEncoding.UTF_16); - } - - /** - * Encodes the given string into a sequence of bytes using the UTF-16BE charset, storing the result into a new byte - * array. - * - * @param string - * the String to encode - * @return encoded bytes - * @throws IllegalStateException - * Thrown when the charset is missing, which should be never according the the Java specification. - * @see Standard charsets - * @see #getBytesUnchecked(String, String) - */ - public static byte[] getBytesUtf16Be(String string) { - return StringUtils.getBytesUnchecked(string, CharEncoding.UTF_16BE); - } - - /** - * Encodes the given string into a sequence of bytes using the UTF-16LE charset, storing the result into a new byte - * array. - * - * @param string - * the String to encode - * @return encoded bytes - * @throws IllegalStateException - * Thrown when the charset is missing, which should be never according the the Java specification. - * @see Standard charsets - * @see #getBytesUnchecked(String, String) - */ - public static byte[] getBytesUtf16Le(String string) { - return StringUtils.getBytesUnchecked(string, CharEncoding.UTF_16LE); - } - - /** - * Encodes the given string into a sequence of bytes using the UTF-8 charset, storing the result into a new byte - * array. - * - * @param string - * the String to encode - * @return encoded bytes - * @throws IllegalStateException - * Thrown when the charset is missing, which should be never according the the Java specification. - * @see Standard charsets - * @see #getBytesUnchecked(String, String) - */ - public static byte[] getBytesUtf8(String string) { - return StringUtils.getBytesUnchecked(string, CharEncoding.UTF_8); - } - - /** - * Encodes the given string into a sequence of bytes using the named charset, storing the result into a new byte - * array. - *

- * This method catches {@link UnsupportedEncodingException} and rethrows it as {@link IllegalStateException}, which - * should never happen for a required charset name. Use this method when the encoding is required to be in the JRE. - *

- * - * @param string - * the String to encode - * @param charsetName - * The name of a required {@link java.nio.charset.Charset} - * @return encoded bytes - * @throws IllegalStateException - * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen for a - * required charset name. - * @see CharEncoding - * @see String#getBytes(String) - */ - public static byte[] getBytesUnchecked(String string, String charsetName) { - if (string == null) { - return null; - } - try { - return string.getBytes(charsetName); - } catch (UnsupportedEncodingException e) { - throw StringUtils.newIllegalStateException(charsetName, e); - } - } - - private static IllegalStateException newIllegalStateException(String charsetName, UnsupportedEncodingException e) { - return new IllegalStateException(charsetName + ": " + e); - } - - /** - * Constructs a new String by decoding the specified array of bytes using the given charset. - *

- * This method catches {@link UnsupportedEncodingException} and re-throws it as {@link IllegalStateException}, which - * should never happen for a required charset name. Use this method when the encoding is required to be in the JRE. - *

- * - * @param bytes - * The bytes to be decoded into characters - * @param charsetName - * The name of a required {@link java.nio.charset.Charset} - * @return A new String decoded from the specified array of bytes using the given charset. - * @throws IllegalStateException - * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen for a - * required charset name. - * @see CharEncoding - * @see String#String(byte[], String) - */ - public static String newString(byte[] bytes, String charsetName) { - if (bytes == null) { - return null; - } - try { - return new String(bytes, charsetName); - } catch (UnsupportedEncodingException e) { - throw StringUtils.newIllegalStateException(charsetName, e); - } - } - - /** - * Constructs a new String by decoding the specified array of bytes using the ISO-8859-1 charset. - * - * @param bytes - * The bytes to be decoded into characters - * @return A new String decoded from the specified array of bytes using the given charset. - * @throws IllegalStateException - * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the - * charset is required. - */ - public static String newStringIso8859_1(byte[] bytes) { - return StringUtils.newString(bytes, CharEncoding.ISO_8859_1); - } - - /** - * Constructs a new String by decoding the specified array of bytes using the US-ASCII charset. - * - * @param bytes - * The bytes to be decoded into characters - * @return A new String decoded from the specified array of bytes using the given charset. - * @throws IllegalStateException - * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the - * charset is required. - */ - public static String newStringUsAscii(byte[] bytes) { - return StringUtils.newString(bytes, CharEncoding.US_ASCII); - } - - /** - * Constructs a new String by decoding the specified array of bytes using the UTF-16 charset. - * - * @param bytes - * The bytes to be decoded into characters - * @return A new String decoded from the specified array of bytes using the given charset. - * @throws IllegalStateException - * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the - * charset is required. - */ - public static String newStringUtf16(byte[] bytes) { - return StringUtils.newString(bytes, CharEncoding.UTF_16); - } - - /** - * Constructs a new String by decoding the specified array of bytes using the UTF-16BE charset. - * - * @param bytes - * The bytes to be decoded into characters - * @return A new String decoded from the specified array of bytes using the given charset. - * @throws IllegalStateException - * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the - * charset is required. - */ - public static String newStringUtf16Be(byte[] bytes) { - return StringUtils.newString(bytes, CharEncoding.UTF_16BE); - } - - /** - * Constructs a new String by decoding the specified array of bytes using the UTF-16LE charset. - * - * @param bytes - * The bytes to be decoded into characters - * @return A new String decoded from the specified array of bytes using the given charset. - * @throws IllegalStateException - * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the - * charset is required. - */ - public static String newStringUtf16Le(byte[] bytes) { - return StringUtils.newString(bytes, CharEncoding.UTF_16LE); - } - - /** - * Constructs a new String by decoding the specified array of bytes using the UTF-8 charset. - * - * @param bytes - * The bytes to be decoded into characters - * @return A new String decoded from the specified array of bytes using the given charset. - * @throws IllegalStateException - * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the - * charset is required. - */ - public static String newStringUtf8(byte[] bytes) { - return StringUtils.newString(bytes, CharEncoding.UTF_8); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.qpython.qsl4a.codec; + +import java.io.UnsupportedEncodingException; + +/** + * Converts String to and from bytes using the encodings required by the Java specification. These encodings are specified in Standard charsets + * + * @see CharEncoding + * @see Standard charsets + * @author Gary Gregory + * @version $Id: StringUtils.java 801391 2009-08-05 19:55:54Z ggregory $ + * @since 1.4 + */ +public class StringUtils { + + /** + * Encodes the given string into a sequence of bytes using the ISO-8859-1 charset, storing the result into a new + * byte array. + * + * @param string + * the String to encode + * @return encoded bytes + * @throws IllegalStateException + * Thrown when the charset is missing, which should be never according the the Java specification. + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesIso8859_1(String string) { + return StringUtils.getBytesUnchecked(string, CharEncoding.ISO_8859_1); + } + + /** + * Encodes the given string into a sequence of bytes using the US-ASCII charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode + * @return encoded bytes + * @throws IllegalStateException + * Thrown when the charset is missing, which should be never according the the Java specification. + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUsAscii(String string) { + return StringUtils.getBytesUnchecked(string, CharEncoding.US_ASCII); + } + + /** + * Encodes the given string into a sequence of bytes using the UTF-16 charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode + * @return encoded bytes + * @throws IllegalStateException + * Thrown when the charset is missing, which should be never according the the Java specification. + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUtf16(String string) { + return StringUtils.getBytesUnchecked(string, CharEncoding.UTF_16); + } + + /** + * Encodes the given string into a sequence of bytes using the UTF-16BE charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode + * @return encoded bytes + * @throws IllegalStateException + * Thrown when the charset is missing, which should be never according the the Java specification. + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUtf16Be(String string) { + return StringUtils.getBytesUnchecked(string, CharEncoding.UTF_16BE); + } + + /** + * Encodes the given string into a sequence of bytes using the UTF-16LE charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode + * @return encoded bytes + * @throws IllegalStateException + * Thrown when the charset is missing, which should be never according the the Java specification. + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUtf16Le(String string) { + return StringUtils.getBytesUnchecked(string, CharEncoding.UTF_16LE); + } + + /** + * Encodes the given string into a sequence of bytes using the UTF-8 charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode + * @return encoded bytes + * @throws IllegalStateException + * Thrown when the charset is missing, which should be never according the the Java specification. + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUtf8(String string) { + return StringUtils.getBytesUnchecked(string, CharEncoding.UTF_8); + } + + /** + * Encodes the given string into a sequence of bytes using the named charset, storing the result into a new byte + * array. + *

+ * This method catches {@link UnsupportedEncodingException} and rethrows it as {@link IllegalStateException}, which + * should never happen for a required charset name. Use this method when the encoding is required to be in the JRE. + *

+ * + * @param string + * the String to encode + * @param charsetName + * The name of a required {@link java.nio.charset.Charset} + * @return encoded bytes + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen for a + * required charset name. + * @see CharEncoding + * @see String#getBytes(String) + */ + public static byte[] getBytesUnchecked(String string, String charsetName) { + if (string == null) { + return null; + } + try { + return string.getBytes(charsetName); + } catch (UnsupportedEncodingException e) { + throw StringUtils.newIllegalStateException(charsetName, e); + } + } + + private static IllegalStateException newIllegalStateException(String charsetName, UnsupportedEncodingException e) { + return new IllegalStateException(charsetName + ": " + e); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the given charset. + *

+ * This method catches {@link UnsupportedEncodingException} and re-throws it as {@link IllegalStateException}, which + * should never happen for a required charset name. Use this method when the encoding is required to be in the JRE. + *

+ * + * @param bytes + * The bytes to be decoded into characters + * @param charsetName + * The name of a required {@link java.nio.charset.Charset} + * @return A new String decoded from the specified array of bytes using the given charset. + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen for a + * required charset name. + * @see CharEncoding + * @see String#String(byte[], String) + */ + public static String newString(byte[] bytes, String charsetName) { + if (bytes == null) { + return null; + } + try { + return new String(bytes, charsetName); + } catch (UnsupportedEncodingException e) { + throw StringUtils.newIllegalStateException(charsetName, e); + } + } + + /** + * Constructs a new String by decoding the specified array of bytes using the ISO-8859-1 charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the given charset. + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the + * charset is required. + */ + public static String newStringIso8859_1(byte[] bytes) { + return StringUtils.newString(bytes, CharEncoding.ISO_8859_1); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the US-ASCII charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the given charset. + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the + * charset is required. + */ + public static String newStringUsAscii(byte[] bytes) { + return StringUtils.newString(bytes, CharEncoding.US_ASCII); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the UTF-16 charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the given charset. + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the + * charset is required. + */ + public static String newStringUtf16(byte[] bytes) { + return StringUtils.newString(bytes, CharEncoding.UTF_16); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the UTF-16BE charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the given charset. + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the + * charset is required. + */ + public static String newStringUtf16Be(byte[] bytes) { + return StringUtils.newString(bytes, CharEncoding.UTF_16BE); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the UTF-16LE charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the given charset. + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the + * charset is required. + */ + public static String newStringUtf16Le(byte[] bytes) { + return StringUtils.newString(bytes, CharEncoding.UTF_16LE); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the UTF-8 charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the given charset. + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the + * charset is required. + */ + public static String newStringUtf8(byte[] bytes) { + return StringUtils.newString(bytes, CharEncoding.UTF_8); + } + +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/Analytics.java b/src/main/java/org/qpython/qsl4a/qsl4a/Analytics.java index 3af0dd7..ac63121 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/Analytics.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/Analytics.java @@ -1,90 +1,90 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a; - -import android.app.Activity; -import android.content.Context; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; - - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -public class Analytics { - private static SharedPreferences mPrefs; - private static String mSl4aVersion; - private static ExecutorService mWorkPool; - private static volatile boolean mStarted = false; - - private Analytics() { - // Utility class. - } - - // TODO(Alexey): Add Javadoc. "Also, it would be cool to wrap up the Analytics API into a facade." - @SuppressWarnings("deprecation") -public static synchronized void start(Context context, String analyticsAccountId) { - if (context == null || analyticsAccountId == null) { - return; - } - mSl4aVersion = Version.getVersion(context); - mPrefs = PreferenceManager.getDefaultSharedPreferences(context); - mWorkPool = Executors.newSingleThreadExecutor(); - mStarted = true; - } - - private static class PageNameBuilder { - private final StringBuilder mmName = new StringBuilder(); - - void add(String pathPart) { - mmName.append("/"); - mmName.append(pathPart); - } - - String build() { - return mmName.toString(); - } - } - - public static void track(final String... nameParts) { - if (mStarted && mPrefs.getBoolean("usagetracking", false)) { - mWorkPool.submit(new Runnable() { - public void run() { - PageNameBuilder builder = new PageNameBuilder(); - builder.add(mSl4aVersion); - for (String part : nameParts) { - builder.add(part); - } - String name = builder.build(); - } - }); - } - } - - public static void trackActivity(Activity activity) { - String name = activity.getClass().getSimpleName(); - track(name); - } - - @SuppressWarnings("deprecation") -public static synchronized void stop() { - if (mStarted) { - mStarted = false; - mWorkPool.shutdownNow(); - } - } -} +/* + * Copyright (C) 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.qpython.qsl4a.qsl4a; + +import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; + + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class Analytics { + private static SharedPreferences mPrefs; + private static String mSl4aVersion; + private static ExecutorService mWorkPool; + private static volatile boolean mStarted = false; + + private Analytics() { + // Utility class. + } + + // TODO(Alexey): Add Javadoc. "Also, it would be cool to wrap up the Analytics API into a facade." + @SuppressWarnings("deprecation") +public static synchronized void start(Context context, String analyticsAccountId) { + if (context == null || analyticsAccountId == null) { + return; + } + mSl4aVersion = Version.getVersion(context); + mPrefs = PreferenceManager.getDefaultSharedPreferences(context); + mWorkPool = Executors.newSingleThreadExecutor(); + mStarted = true; + } + + private static class PageNameBuilder { + private final StringBuilder mmName = new StringBuilder(); + + void add(String pathPart) { + mmName.append("/"); + mmName.append(pathPart); + } + + String build() { + return mmName.toString(); + } + } + + public static void track(final String... nameParts) { + if (mStarted && mPrefs.getBoolean("usagetracking", false)) { + mWorkPool.submit(new Runnable() { + public void run() { + PageNameBuilder builder = new PageNameBuilder(); + builder.add(mSl4aVersion); + for (String part : nameParts) { + builder.add(part); + } + String name = builder.build(); + } + }); + } + } + + public static void trackActivity(Activity activity) { + String name = activity.getClass().getSimpleName(); + track(name); + } + + @SuppressWarnings("deprecation") +public static synchronized void stop() { + if (mStarted) { + mStarted = false; + mWorkPool.shutdownNow(); + } + } +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/AndroidProxy.java b/src/main/java/org/qpython/qsl4a/qsl4a/AndroidProxy.java index e106f75..024b1cb 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/AndroidProxy.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/AndroidProxy.java @@ -1,96 +1,93 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a; - -import android.app.Service; -import android.content.Intent; - -import org.qpython.qsl4a.qsl4a.facade.FacadeConfiguration; -import org.qpython.qsl4a.qsl4a.facade.FacadeManagerFactory; -import org.qpython.qsl4a.qsl4a.jsonrpc.JsonRpcServer; -import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiverManagerFactory; - -import java.net.InetSocketAddress; -import java.util.UUID; - -public class AndroidProxy { - - private InetSocketAddress mAddress; - private final JsonRpcServer mJsonRpcServer; - private final UUID mSecret; - private final RpcReceiverManagerFactory mFacadeManagerFactory; - - /** - * - * @param service - * Android service (required to build facades). - * @param intent - * the intent that launched the proxy/script. - * @param requiresHandshake - * indicates whether RPC security protocol should be enabled. - */ - public AndroidProxy(Service service, Intent intent, boolean requiresHandshake) { - if (requiresHandshake) { - mSecret = UUID.randomUUID(); - } else { - mSecret = null; - } - mFacadeManagerFactory = - new FacadeManagerFactory(FacadeConfiguration.getSdkLevel(), service, intent, - FacadeConfiguration.getFacadeClasses()); - mJsonRpcServer = new JsonRpcServer(mFacadeManagerFactory, getSecret()); - } - - public InetSocketAddress getAddress() { - return mAddress; - } - - public InetSocketAddress startLocal() { - return startLocal(0); - } - - public InetSocketAddress startLocal(int port) { - mAddress = mJsonRpcServer.startLocal(port); - return mAddress; - } - - public InetSocketAddress startPublic() { - return startPublic(0); - } - - public InetSocketAddress startPublic(int port) { - mAddress = mJsonRpcServer.startPublic(port); - return mAddress; - } - - public void shutdown() { - if (mJsonRpcServer!=null) { - mJsonRpcServer.shutdown(); - } - } - - public String getSecret() { - if (mSecret == null) { - return null; - } - return mSecret.toString(); - } - - public RpcReceiverManagerFactory getRpcReceiverManagerFactory() { - return mFacadeManagerFactory; - } -} +/* + * Copyright (C) 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.qpython.qsl4a.qsl4a; + +import android.app.Service; +import android.content.Intent; + +import org.qpython.qsl4a.qsl4a.facade.FacadeConfiguration; +import org.qpython.qsl4a.qsl4a.facade.FacadeManagerFactory; +import org.qpython.qsl4a.qsl4a.jsonrpc.JsonRpcServer; +import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiverManagerFactory; + +import java.net.InetSocketAddress; +import java.util.UUID; + +public class AndroidProxy { + + private InetSocketAddress mAddress; + private final JsonRpcServer mJsonRpcServer; + private final UUID mSecret; + private final RpcReceiverManagerFactory mFacadeManagerFactory; + + /** + * + * @param service + * Android service (required to build facades). + * @param intent + * the intent that launched the proxy/script. + */ + public AndroidProxy(Service service, Intent intent) { + //if (requiresHandshake) { + mSecret = UUID.randomUUID(); + // } else { + //mSecret = null; + //} + mFacadeManagerFactory = + new FacadeManagerFactory(FacadeConfiguration.getSdkLevel(), service, intent, + FacadeConfiguration.getFacadeClasses()); + mJsonRpcServer = new JsonRpcServer(mFacadeManagerFactory, getSecret()); + } + + public InetSocketAddress getAddress() { + return mAddress; + } + + public void startLocal() { + startLocal(0); + } + + public void startLocal(int port) { + mAddress = mJsonRpcServer.startLocal(port); + } + + /*public InetSocketAddress startPublic() { + return startPublic(0); + } + + public InetSocketAddress startPublic(int port) { + mAddress = mJsonRpcServer.startPublic(port); + return mAddress; + }*/ + + public void shutdown() { + if (mJsonRpcServer!=null) { + mJsonRpcServer.shutdown(); + } + } + + public String getSecret() { + /*if (mSecret == null) { + return null; + }*/ + return mSecret.toString(); + } + + public RpcReceiverManagerFactory getRpcReceiverManagerFactory() { + return mFacadeManagerFactory; + } +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/BaseApplication.java b/src/main/java/org/qpython/qsl4a/qsl4a/BaseApplication.java index 174951b..b3a7c0d 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/BaseApplication.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/BaseApplication.java @@ -1,51 +1,51 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a; - -import android.app.Application; - -import org.qpython.qsl4a.qsl4a.interpreter.InterpreterConfiguration; -import org.qpython.qsl4a.qsl4a.trigger.TriggerRepository; -import org.qpython.qsl4a.qsl4a.future.FutureActivityTaskExecutor; - -public class BaseApplication extends Application { - - private final FutureActivityTaskExecutor mTaskExecutor = new FutureActivityTaskExecutor(this); - private TriggerRepository mTriggerRepository; - - protected InterpreterConfiguration mConfiguration; - - public FutureActivityTaskExecutor getTaskExecutor() { - return mTaskExecutor; - } - - @Override - public void onCreate() { - super.onCreate(); - mConfiguration = new InterpreterConfiguration(this); - mConfiguration.startDiscovering(); - mTriggerRepository = new TriggerRepository(this); - } - - public InterpreterConfiguration getInterpreterConfiguration() { - return mConfiguration; - } - - public TriggerRepository getTriggerRepository() { - return mTriggerRepository; - } -} +/* + * Copyright (C) 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.qpython.qsl4a.qsl4a; + +import android.app.Application; + +import org.qpython.qsl4a.qsl4a.future.FutureActivityTaskExecutor; +import org.qpython.qsl4a.qsl4a.interpreter.InterpreterConfiguration; +import org.qpython.qsl4a.qsl4a.trigger.TriggerRepository; + +public class BaseApplication extends Application { + + private final FutureActivityTaskExecutor mTaskExecutor = new FutureActivityTaskExecutor(this); + private TriggerRepository mTriggerRepository; + + protected InterpreterConfiguration mConfiguration; + + public FutureActivityTaskExecutor getTaskExecutor() { + return mTaskExecutor; + } + + @Override + public void onCreate() { + super.onCreate(); + mConfiguration = new InterpreterConfiguration(this); + mConfiguration.startDiscovering(); + mTriggerRepository = new TriggerRepository(this); + } + + public InterpreterConfiguration getInterpreterConfiguration() { + return mConfiguration; + } + + public TriggerRepository getTriggerRepository() { + return mTriggerRepository; + } +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/Constants.java b/src/main/java/org/qpython/qsl4a/qsl4a/Constants.java index 4a2c32c..4f76102 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/Constants.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/Constants.java @@ -1,101 +1,103 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a; - -import android.content.ComponentName; - -public interface Constants { - - public static final String ACTION_LAUNCH_FOREGROUND_SCRIPT = - "com.googlecode.android_scripting.action.LAUNCH_FOREGROUND_SCRIPT"; - public static final String ACTION_LAUNCH_BACKGROUND_SCRIPT = - "com.googlecode.android_scripting.action.LAUNCH_BACKGROUND_SCRIPT"; - public static final String ACTION_LAUNCH_SCRIPT_FOR_RESULT = - "com.googlecode.android_scripting.action.ACTION_LAUNCH_SCRIPT_FOR_RESULT"; - public static final String ACTION_LAUNCH_INTERPRETER = - "com.googlecode.android_scripting.action.LAUNCH_INTERPRETER"; - public static final String ACTION_EDIT_SCRIPT = - "com.googlecode.android_scripting.action.EDIT_SCRIPT"; - public static final String ACTION_SAVE_SCRIPT = - "com.googlecode.android_scripting.action.SAVE_SCRIPT"; - public static final String ACTION_SAVE_AND_RUN_SCRIPT = - "com.googlecode.android_scripting.action.SAVE_AND_RUN_SCRIPT"; - public static final String ACTION_KILL_PROCESS = - "com.googlecode.android_scripting.action.KILL_PROCESS"; - public static final String ACTION_KILL_ALL = "com.googlecode.android_scripting.action.KILL_ALL"; - public static final String ACTION_SHOW_RUNNING_SCRIPTS = - "com.googlecode.android_scripting.action.SHOW_RUNNING_SCRIPTS"; - public static final String ACTION_CANCEL_NOTIFICATION = - "com.googlecode.android_scripting.action.CANCEL_NOTIFICAITON"; - public static final String ACTION_ACTIVITY_RESULT = - "com.googlecode.android_scripting.action.ACTIVITY_RESULT"; - public static final String ACTION_LAUNCH_SERVER = - "com.googlecode.android_scripting.action.LAUNCH_SERVER"; - - public static final String EXTRA_RESULT = "SCRIPT_RESULT"; - public static final String EXTRA_SCRIPT_PATH = - "com.googlecode.android_scripting.extra.SCRIPT_PATH"; - public static final String EXTRA_SCRIPT_CONTENT = - "com.googlecode.android_scripting.extra.SCRIPT_CONTENT"; - public static final String EXTRA_INTERPRETER_NAME = - "com.googlecode.android_scripting.extra.INTERPRETER_NAME"; - - public static final String EXTRA_USE_EXTERNAL_IP = - "com.googlecode.android_scripting.extra.USE_PUBLIC_IP"; - public static final String EXTRA_USE_SERVICE_PORT = - "com.googlecode.android_scripting.extra.USE_SERVICE_PORT"; - public static final String EXTRA_SCRIPT_TEXT = - "com.googlecode.android_scripting.extra.SCRIPT_TEXT"; - public static final String EXTRA_RPC_HELP_TEXT = - "com.googlecode.android_scripting.extra.RPC_HELP_TEXT"; - public static final String EXTRA_API_PROMPT_RPC_NAME = - "com.googlecode.android_scripting.extra.API_PROMPT_RPC_NAME"; - public static final String EXTRA_API_PROMPT_VALUES = - "com.googlecode.android_scripting.extra.API_PROMPT_VALUES"; - public static final String EXTRA_PROXY_PORT = "com.googlecode.android_scripting.extra.PROXY_PORT"; - public static final String EXTRA_PROCESS_ID = - "com.googlecode.android_scripting.extra.SCRIPT_PROCESS_ID"; - public static final String EXTRA_IS_NEW_SCRIPT = - "com.googlecode.android_scripting.extra.IS_NEW_SCRIPT"; - public static final String EXTRA_TRIGGER_ID = - "com.googlecode.android_scripting.extra.EXTRA_TRIGGER_ID"; - public static final String EXTRA_LAUNCH_IN_BACKGROUND = - "com.googlecode.android_scripting.extra.EXTRA_LAUNCH_IN_BACKGROUND"; - public static final String EXTRA_TASK_ID = "com.googlecode.android_scripting.extra.EXTRA_TASK_ID"; - - // BluetoothDeviceManager - public static final String EXTRA_DEVICE_ADDRESS = - "com.googlecode.android_scripting.extra.device_address"; - - public static final ComponentName SL4A_SERVICE_COMPONENT_NAME = new ComponentName( - "com.googlecode.android_scripting", - "com.googlecode.android_scripting.activity.ScriptingLayerService"); - public static final ComponentName SL4A_SERVICE_LAUNCHER_COMPONENT_NAME = new ComponentName( - "com.googlecode.android_scripting", - "com.googlecode.android_scripting.activity.ScriptingLayerServiceLauncher"); - public static final ComponentName BLUETOOTH_DEVICE_LIST_COMPONENT_NAME = new ComponentName( - "com.googlecode.android_scripting", - "com.googlecode.android_scripting.activity.BluetoothDeviceList"); - public static final ComponentName TRIGGER_SERVICE_COMPONENT_NAME = new ComponentName( - "com.googlecode.android_scripting", - "com.googlecode.android_scripting.activity.TriggerService"); - - // Preference Keys - - public static final String FORCE_BROWSER = "helpForceBrowser"; - public final static String HIDE_NOTIFY = "hideServiceNotifications"; +/* + * Copyright (C) 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.qpython.qsl4a.qsl4a; + +import android.content.ComponentName; + +public interface Constants { + + public static final String ACTION_LAUNCH_FOREGROUND_SCRIPT = + "com.googlecode.android_scripting.action.LAUNCH_FOREGROUND_SCRIPT"; + public static final String ACTION_LAUNCH_BACKGROUND_SCRIPT = + "com.googlecode.android_scripting.action.LAUNCH_BACKGROUND_SCRIPT"; + public static final String ACTION_LAUNCH_SCRIPT_FOR_RESULT = + "com.googlecode.android_scripting.action.ACTION_LAUNCH_SCRIPT_FOR_RESULT"; + public static final String ACTION_LAUNCH_INTERPRETER = + "com.googlecode.android_scripting.action.LAUNCH_INTERPRETER"; + /*public static final String ACTION_EDIT_SCRIPT = + "com.googlecode.android_scripting.action.EDIT_SCRIPT"; + public static final String ACTION_SAVE_SCRIPT = + "com.googlecode.android_scripting.action.SAVE_SCRIPT"; + public static final String ACTION_SAVE_AND_RUN_SCRIPT = + "com.googlecode.android_scripting.action.SAVE_AND_RUN_SCRIPT"; + public static final String ACTION_KILL_PROCESS = + "com.googlecode.android_scripting.action.KILL_PROCESS"; + public static final String ACTION_KILL_ALL = "com.googlecode.android_scripting.action.KILL_ALL"; + public static final String ACTION_SHOW_RUNNING_SCRIPTS = + "com.googlecode.android_scripting.action.SHOW_RUNNING_SCRIPTS"; + public static final String ACTION_CANCEL_NOTIFICATION = + "com.googlecode.android_scripting.action.CANCEL_NOTIFICAITON"; + public static final String ACTION_ACTIVITY_RESULT = + "com.googlecode.android_scripting.action.ACTIVITY_RESULT"; + public static final String ACTION_LAUNCH_SERVER = + "com.googlecode.android_scripting.action.LAUNCH_SERVER";*/ + + public static final String EXTRA_RESULT = "SCRIPT_RESULT"; + public static final String EXTRA_SCRIPT_PATH = + "com.googlecode.android_scripting.extra.SCRIPT_PATH"; + public static final String EXTRA_SCRIPT_CONTENT = + "com.googlecode.android_scripting.extra.SCRIPT_CONTENT"; + public static final String EXTRA_INTERPRETER_NAME = + "com.googlecode.android_scripting.extra.INTERPRETER_NAME"; + + /*public static final String EXTRA_USE_EXTERNAL_IP = + "com.googlecode.android_scripting.extra.USE_PUBLIC_IP"; + public static final String EXTRA_USE_SERVICE_PORT = + "com.googlecode.android_scripting.extra.USE_SERVICE_PORT"; + public static final String EXTRA_SCRIPT_TEXT = + "com.googlecode.android_scripting.extra.SCRIPT_TEXT"; + public static final String EXTRA_RPC_HELP_TEXT = + "com.googlecode.android_scripting.extra.RPC_HELP_TEXT"; + public static final String EXTRA_API_PROMPT_RPC_NAME = + "com.googlecode.android_scripting.extra.API_PROMPT_RPC_NAME"; + public static final String EXTRA_API_PROMPT_VALUES = + "com.googlecode.android_scripting.extra.API_PROMPT_VALUES"; + public static final String EXTRA_PROXY_PORT = "com.googlecode.android_scripting.extra.PROXY_PORT"; + public static final String EXTRA_PROCESS_ID = + "com.googlecode.android_scripting.extra.SCRIPT_PROCESS_ID"; + public static final String EXTRA_IS_NEW_SCRIPT = + "com.googlecode.android_scripting.extra.IS_NEW_SCRIPT"; + public static final String EXTRA_TRIGGER_ID = + "com.googlecode.android_scripting.extra.EXTRA_TRIGGER_ID"; + public static final String EXTRA_LAUNCH_IN_BACKGROUND = + "com.googlecode.android_scripting.extra.EXTRA_LAUNCH_IN_BACKGROUND"; + public static final String EXTRA_TASK_ID = "com.googlecode.android_scripting.extra.EXTRA_TASK_ID";*/ + String EXTRA_TASK_ID = "QSL4A_TASK_ID"; + String EXTRA_TASK_TITLE = "QSL4A_TASK_TITLE"; + + // BluetoothDeviceManager + public static final String EXTRA_DEVICE_ADDRESS = + "com.googlecode.android_scripting.extra.device_address"; + + public static final ComponentName SL4A_SERVICE_COMPONENT_NAME = new ComponentName( + "com.googlecode.android_scripting", + "com.googlecode.android_scripting.activity.ScriptingLayerService"); + public static final ComponentName SL4A_SERVICE_LAUNCHER_COMPONENT_NAME = new ComponentName( + "com.googlecode.android_scripting", + "com.googlecode.android_scripting.activity.ScriptingLayerServiceLauncher"); + public static final ComponentName BLUETOOTH_DEVICE_LIST_COMPONENT_NAME = new ComponentName( + "com.googlecode.android_scripting", + "com.googlecode.android_scripting.activity.BluetoothDeviceList"); + public static final ComponentName TRIGGER_SERVICE_COMPONENT_NAME = new ComponentName( + "com.googlecode.android_scripting", + "com.googlecode.android_scripting.activity.TriggerService"); + + // Preference Keys + + /*public static final String FORCE_BROWSER = "helpForceBrowser"; + public final static String HIDE_NOTIFY = "hideServiceNotifications";*/ } \ No newline at end of file diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/Exec.java b/src/main/java/org/qpython/qsl4a/qsl4a/Exec.java index 1acb34e..c105160 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/Exec.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/Exec.java @@ -1,70 +1,70 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.qpython.qsl4a.qsl4a; - -import java.io.FileDescriptor; - -/** - * Tools for executing commands. - */ -public class Exec { - /** - * @param cmd - * The command to execute - * @param arg0 - * The first argument to the command, may be null - * @param arg1 - * the second argument to the command, may be null - * @return the file descriptor of the started process. - * - */ - public static FileDescriptor createSubprocess(String command, String[] arguments, - String[] environmentVariables, String workingDirectory) { - return createSubprocess(command, arguments, environmentVariables, workingDirectory, null); - } - - /** - * @param cmd - * The command to execute - * @param arguments - * Array of arguments, may be null - * @param environmentVariables - * Array of environment variables, may be null - * @param processId - * A one-element array to which the process ID of the started process will be written. - * @return the file descriptor of the opened process's psuedo-terminal. - * - */ - public static native FileDescriptor createSubprocess(String command, String[] arguments, - String[] environmentVariables, String workingDirectory, int[] processId); - - public static native void setPtyWindowSize(FileDescriptor fd, int row, int col, int xpixel, - int ypixel); - - /** - * Causes the calling thread to wait for the process associated with the receiver to finish - * executing. - * - * @return The exit value of the Process being waited on - * - */ - public static native int waitFor(int processId); - - static { - System.loadLibrary("com_googlecode_android_scripting_Exec"); - } -} +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.qpython.qsl4a.qsl4a; + +import java.io.FileDescriptor; + +/** + * Tools for executing commands. + */ +public class Exec { + /** + * @param cmd + * The command to execute + * @param arg0 + * The first argument to the command, may be null + * @param arg1 + * the second argument to the command, may be null + * @return the file descriptor of the started process. + * + */ + public static FileDescriptor createSubprocess(String command, String[] arguments, + String[] environmentVariables, String workingDirectory) { + return createSubprocess(command, arguments, environmentVariables, workingDirectory, null); + } + + /** + * @param cmd + * The command to execute + * @param arguments + * Array of arguments, may be null + * @param environmentVariables + * Array of environment variables, may be null + * @param processId + * A one-element array to which the process ID of the started process will be written. + * @return the file descriptor of the opened process's psuedo-terminal. + * + */ + public static native FileDescriptor createSubprocess(String command, String[] arguments, + String[] environmentVariables, String workingDirectory, int[] processId); + + public static native void setPtyWindowSize(FileDescriptor fd, int row, int col, int xpixel, + int ypixel); + + /** + * Causes the calling thread to wait for the process associated with the receiver to finish + * executing. + * + * @return The exit value of the Process being waited on + * + */ + public static native int waitFor(int processId); + + static { + System.loadLibrary("com_googlecode_android_scripting_Exec"); + } +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/FeaturedInterpreters.java b/src/main/java/org/qpython/qsl4a/qsl4a/FeaturedInterpreters.java index a2791af..085afdc 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/FeaturedInterpreters.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/FeaturedInterpreters.java @@ -1,106 +1,106 @@ -// Copyright 2010 Google Inc. All Rights Reserved. - -package org.qpython.qsl4a.qsl4a; - -import android.content.Context; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class FeaturedInterpreters { - private static final Map mNameMap = - new HashMap(); - private static final Map mExtensionMap = - new HashMap(); - - static { - try { - FeaturedInterpreter interpreters[] = - { - new FeaturedInterpreter("BeanShell 2.0b4", ".bsh", - "http://android-scripting.googlecode.com/files/beanshell_for_android_r2.apk"), - new FeaturedInterpreter("JRuby", ".rb", - "https://github.com/downloads/ruboto/sl4a_jruby_interpreter/JRubyForAndroid_r2dev.apk"), - new FeaturedInterpreter("Lua 5.1.4", ".lua", - "http://android-scripting.googlecode.com/files/lua_for_android_r1.apk"), - new FeaturedInterpreter("Perl 5.10.1", ".pl", - "http://android-scripting.googlecode.com/files/perl_for_android_r1.apk"), - new FeaturedInterpreter("Python 2.6.2", ".py", - "http://python-for-android.googlecode.com/files/PythonForAndroid_r5.apk"), - new FeaturedInterpreter("Rhino 1.7R2", ".js", - "http://android-scripting.googlecode.com/files/rhino_for_android_r2.apk"), - new FeaturedInterpreter("PHP 5.3.3", ".php", - "http://php-for-android.googlecode.com/files/phpforandroid_r1.apk") }; - for (FeaturedInterpreter interpreter : interpreters) { - mNameMap.put(interpreter.mmName, interpreter); - mExtensionMap.put(interpreter.mmExtension, interpreter); - } - } catch (MalformedURLException e) { - LogUtil.e(e); - } - } - - public static List getList() { - ArrayList list = new ArrayList(mNameMap.keySet()); - Collections.sort(list); - return list; - } - - public static URL getUrlForName(String name) { - if (!mNameMap.containsKey(name)) { - return null; - } - return mNameMap.get(name).mmUrl; - } - - public static String getInterpreterNameForScript(String fileName) { - String extension = getExtension(fileName); - if (extension == null || !mExtensionMap.containsKey(extension)) { - return null; - } - return mExtensionMap.get(extension).mmName; - } - - public static boolean isSupported(String fileName) { - String extension = getExtension(fileName); - return (extension != null) && (mExtensionMap.containsKey(extension)); - } - - public static int getInterpreterIcon(Context context, String key) { - String packageName = context.getPackageName(); - String name = "_icon"; - if (key.contains(".")) { - name = key.substring(key.lastIndexOf('.') + 1) + name; - } else { - name = key + name; - } - return context.getResources().getIdentifier(name, "drawable", packageName); - } - - private static String getExtension(String fileName) { - int dotIndex = fileName.lastIndexOf('.'); - if (dotIndex == -1) { - return null; - } - return fileName.substring(dotIndex); - } - - private static class FeaturedInterpreter { - private final String mmName; - private final String mmExtension; - private final URL mmUrl; - - private FeaturedInterpreter(String name, String extension, String url) - throws MalformedURLException { - mmName = name; - mmExtension = extension; - mmUrl = new URL(url); - } - } - -} +// Copyright 2010 Google Inc. All Rights Reserved. + +package org.qpython.qsl4a.qsl4a; + +import android.content.Context; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class FeaturedInterpreters { + private static final Map mNameMap = + new HashMap(); + private static final Map mExtensionMap = + new HashMap(); + + static { + try { + FeaturedInterpreter interpreters[] = + { + new FeaturedInterpreter("BeanShell 2.0b4", ".bsh", + "http://android-scripting.googlecode.com/files/beanshell_for_android_r2.apk"), + new FeaturedInterpreter("JRuby", ".rb", + "https://github.com/downloads/ruboto/sl4a_jruby_interpreter/JRubyForAndroid_r2dev.apk"), + new FeaturedInterpreter("Lua 5.1.4", ".lua", + "http://android-scripting.googlecode.com/files/lua_for_android_r1.apk"), + new FeaturedInterpreter("Perl 5.10.1", ".pl", + "http://android-scripting.googlecode.com/files/perl_for_android_r1.apk"), + new FeaturedInterpreter("Python 2.6.2", ".py", + "http://python-for-android.googlecode.com/files/PythonForAndroid_r5.apk"), + new FeaturedInterpreter("Rhino 1.7R2", ".js", + "http://android-scripting.googlecode.com/files/rhino_for_android_r2.apk"), + new FeaturedInterpreter("PHP 5.3.3", ".php", + "http://php-for-android.googlecode.com/files/phpforandroid_r1.apk") }; + for (FeaturedInterpreter interpreter : interpreters) { + mNameMap.put(interpreter.mmName, interpreter); + mExtensionMap.put(interpreter.mmExtension, interpreter); + } + } catch (MalformedURLException e) { + LogUtil.e(e); + } + } + + public static List getList() { + ArrayList list = new ArrayList(mNameMap.keySet()); + Collections.sort(list); + return list; + } + + public static URL getUrlForName(String name) { + if (!mNameMap.containsKey(name)) { + return null; + } + return mNameMap.get(name).mmUrl; + } + + public static String getInterpreterNameForScript(String fileName) { + String extension = getExtension(fileName); + if (extension == null || !mExtensionMap.containsKey(extension)) { + return null; + } + return mExtensionMap.get(extension).mmName; + } + + public static boolean isSupported(String fileName) { + String extension = getExtension(fileName); + return (extension != null) && (mExtensionMap.containsKey(extension)); + } + + public static int getInterpreterIcon(Context context, String key) { + String packageName = context.getPackageName(); + String name = "_icon"; + if (key.contains(".")) { + name = key.substring(key.lastIndexOf('.') + 1) + name; + } else { + name = key + name; + } + return context.getResources().getIdentifier(name, "drawable", packageName); + } + + private static String getExtension(String fileName) { + int dotIndex = fileName.lastIndexOf('.'); + if (dotIndex == -1) { + return null; + } + return fileName.substring(dotIndex); + } + + private static class FeaturedInterpreter { + private final String mmName; + private final String mmExtension; + private final URL mmUrl; + + private FeaturedInterpreter(String name, String extension, String url) + throws MalformedURLException { + mmName = name; + mmExtension = extension; + mmUrl = new URL(url); + } + } + +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/ForegroundService.java b/src/main/java/org/qpython/qsl4a/qsl4a/ForegroundService.java index f773637..9471857 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/ForegroundService.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/ForegroundService.java @@ -1,95 +1,95 @@ -package org.qpython.qsl4a.qsl4a; - -import android.app.Notification; -import android.app.NotificationManager; -import android.app.Service; - -import java.lang.reflect.Method; - -public abstract class ForegroundService extends Service { - private static final Class[] mStartForegroundSignature = - new Class[] { int.class, Notification.class }; - private static final Class[] mStopForegroundSignature = new Class[] { boolean.class }; - - private final int mNotificationId; - - private NotificationManager mNotificationManager; - private Method mStartForeground; - private Method mStopForeground; - private Object[] mStartForegroundArgs = new Object[2]; - private Object[] mStopForegroundArgs = new Object[1]; - - public ForegroundService(int id) { - mNotificationId = id; - } - - protected abstract Notification createNotification(); - - /** - * This is a wrapper around the new startForeground method, using the older APIs if it is not - * available. - */ - private void startForegroundCompat(Notification notification) { - // If we have the new startForeground API, then use it. - if (mStartForeground != null) { - mStartForegroundArgs[0] = Integer.valueOf(mNotificationId); - mStartForegroundArgs[1] = notification; - try { - mStartForeground.invoke(this, mStartForegroundArgs); - } catch (Exception e) { - LogUtil.e(e); - } - return; - } - - // Fall back on the old API. - startForegroundCompat(notification); - if (notification != null) { - mNotificationManager.notify(mNotificationId, notification); - } - } - - - - /** - * This is a wrapper around the new stopForeground method, using the older APIs if it is not - * available. - */ - private void stopForegroundCompat() { - // If we have the new stopForeground API, then use it. - if (mStopForeground != null) { - mStopForegroundArgs[0] = Boolean.TRUE; - try { - mStopForeground.invoke(this, mStopForegroundArgs); - } catch (Exception e) { - LogUtil.e(e); - } - return; - } - - // Fall back on the old API. Note to cancel BEFORE changing the - // foreground state, since we could be killed at that point. - mNotificationManager.cancel(mNotificationId); - stopForeground(true); - - } - - @Override - public void onCreate() { - mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); - try { - mStartForeground = getClass().getMethod("startForeground", mStartForegroundSignature); - mStopForeground = getClass().getMethod("stopForeground", mStopForegroundSignature); - } catch (NoSuchMethodException e) { - // Running on an older platform. - mStartForeground = mStopForeground = null; - } - startForegroundCompat(createNotification()); - } - - @Override - public void onDestroy() { - // Make sure our notification is gone. - stopForegroundCompat(); - } -} +package org.qpython.qsl4a.qsl4a; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.Service; + +import java.lang.reflect.Method; + +public abstract class ForegroundService extends Service { + private static final Class[] mStartForegroundSignature = + new Class[] { int.class, Notification.class }; + private static final Class[] mStopForegroundSignature = new Class[] { boolean.class }; + + private final int mNotificationId; + + private NotificationManager mNotificationManager; + private Method mStartForeground; + private Method mStopForeground; + private Object[] mStartForegroundArgs = new Object[2]; + private Object[] mStopForegroundArgs = new Object[1]; + + public ForegroundService(int id) { + mNotificationId = id; + } + + protected abstract Notification createNotification(); + + /** + * This is a wrapper around the new startForeground method, using the older APIs if it is not + * available. + */ + private void startForegroundCompat(Notification notification) { + // If we have the new startForeground API, then use it. + if (mStartForeground != null) { + mStartForegroundArgs[0] = Integer.valueOf(mNotificationId); + mStartForegroundArgs[1] = notification; + try { + mStartForeground.invoke(this, mStartForegroundArgs); + } catch (Exception e) { + LogUtil.e(e); + } + return; + } + + // Fall back on the old API. + startForegroundCompat(notification); + if (notification != null) { + mNotificationManager.notify(mNotificationId, notification); + } + } + + + + /** + * This is a wrapper around the new stopForeground method, using the older APIs if it is not + * available. + */ + private void stopForegroundCompat() { + // If we have the new stopForeground API, then use it. + if (mStopForeground != null) { + mStopForegroundArgs[0] = Boolean.TRUE; + try { + mStopForeground.invoke(this, mStopForegroundArgs); + } catch (Exception e) { + LogUtil.e(e); + } + return; + } + + // Fall back on the old API. Note to cancel BEFORE changing the + // foreground state, since we could be killed at that point. + mNotificationManager.cancel(mNotificationId); + stopForeground(true); + + } + + @Override + public void onCreate() { + mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + try { + mStartForeground = getClass().getMethod("startForeground", mStartForegroundSignature); + mStopForeground = getClass().getMethod("stopForeground", mStopForegroundSignature); + } catch (NoSuchMethodException e) { + // Running on an older platform. + mStartForeground = mStopForeground = null; + } + startForegroundCompat(createNotification()); + } + + @Override + public void onDestroy() { + // Make sure our notification is gone. + stopForegroundCompat(); + } +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/IntentBuilders.java b/src/main/java/org/qpython/qsl4a/qsl4a/IntentBuilders.java index 2640964..9b414f4 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/IntentBuilders.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/IntentBuilders.java @@ -1,157 +1,157 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a; - -import android.app.PendingIntent; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.os.Parcelable; - -import org.qpython.qsl4a.qsl4a.interpreter.Interpreter; - -import java.io.File; - -public class IntentBuilders { - /** An arbitrary value that is used to identify pending intents for executing scripts. */ - private static final int EXECUTE_SCRIPT_REQUEST_CODE = 0x12f412a; - - private IntentBuilders() { - // Utility class. - } - - public static Intent buildTriggerServiceIntent() { - Intent intent = new Intent(); - intent.setComponent(Constants.TRIGGER_SERVICE_COMPONENT_NAME); - return intent; - } - - /** - * Builds an intent that will launch a script in the background. - * - * @param script - * the script to launch - * @return the intent that will launch the script - */ - public static Intent buildStartInBackgroundIntent(File script) { - final ComponentName componentName = Constants.SL4A_SERVICE_LAUNCHER_COMPONENT_NAME; - Intent intent = new Intent(); - intent.setComponent(componentName); - intent.setAction(Constants.ACTION_LAUNCH_BACKGROUND_SCRIPT); - intent.putExtra(Constants.EXTRA_SCRIPT_PATH, script.getAbsolutePath()); - return intent; - } - - /** - * Builds an intent that launches a script in a terminal. - * - * @param script - * the script to launch - * @return the intent that will launch the script - */ - public static Intent buildStartInTerminalIntent(File script) { - final ComponentName componentName = Constants.SL4A_SERVICE_LAUNCHER_COMPONENT_NAME; - Intent intent = new Intent(); - intent.setComponent(componentName); - intent.setAction(Constants.ACTION_LAUNCH_FOREGROUND_SCRIPT); - intent.putExtra(Constants.EXTRA_SCRIPT_PATH, script.getAbsolutePath()); - return intent; - } - - /** - * Builds an intent that launches an interpreter. - * - * @param interpreterName - * the interpreter to launch - * @return the intent that will launch the interpreter - */ - public static Intent buildStartInterpreterIntent(String interpreterName) { - final ComponentName componentName = Constants.SL4A_SERVICE_LAUNCHER_COMPONENT_NAME; - Intent intent = new Intent(); - intent.setComponent(componentName); - intent.setAction(Constants.ACTION_LAUNCH_INTERPRETER); - intent.putExtra(Constants.EXTRA_INTERPRETER_NAME, interpreterName); - return intent; - } - - /** - * Builds an intent that creates a shortcut to launch the provided interpreter. - * - * @param interpreter - * the interpreter to link to - * @param iconResource - * the icon resource to associate with the shortcut - * @return the intent that will create the shortcut - */ - public static Intent buildInterpreterShortcutIntent(Interpreter interpreter, - Parcelable iconResource) { - Intent intent = new Intent(); - intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, - buildStartInterpreterIntent(interpreter.getName())); - intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, interpreter.getNiceName()); - intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource); - return intent; - } - - /** - * Builds an intent that creates a shortcut to launch the provided script in the background. - * - * @param script - * the script to link to - * @param iconResource - * the icon resource to associate with the shortcut - * @return the intent that will create the shortcut - */ - public static Intent buildBackgroundShortcutIntent(File script, Parcelable iconResource) { - Intent intent = new Intent(); - intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, buildStartInBackgroundIntent(script)); - intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, script.getName()); - intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource); - return intent; - } - - /** - * Builds an intent that creates a shortcut to launch the provided script in a terminal. - * - * @param script - * the script to link to - * @param iconResource - * the icon resource to associate with the shortcut - * @return the intent that will create the shortcut - */ - public static Intent buildTerminalShortcutIntent(File script, Parcelable iconResource) { - Intent intent = new Intent(); - intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, buildStartInTerminalIntent(script)); - intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, script.getName()); - intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource); - return intent; - } - - /** - * Creates a pending intent that can be used to start the trigger service. - * - * @param context - * the context under whose authority to launch the intent - * - * @return {@link PendingIntent} object for running the trigger service - */ - public static PendingIntent buildTriggerServicePendingIntent(Context context) { - final Intent intent = buildTriggerServiceIntent(); - return PendingIntent.getService(context, EXECUTE_SCRIPT_REQUEST_CODE, intent, - PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); - } -} +/* + * Copyright (C) 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.qpython.qsl4a.qsl4a; + +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.Parcelable; + +import org.qpython.qsl4a.qsl4a.interpreter.Interpreter; + +import java.io.File; + +public class IntentBuilders { + /** An arbitrary value that is used to identify pending intents for executing scripts. */ + private static final int EXECUTE_SCRIPT_REQUEST_CODE = 0x12f412a; + + private IntentBuilders() { + // Utility class. + } + + public static Intent buildTriggerServiceIntent() { + Intent intent = new Intent(); + intent.setComponent(Constants.TRIGGER_SERVICE_COMPONENT_NAME); + return intent; + } + + /** + * Builds an intent that will launch a script in the background. + * + * @param script + * the script to launch + * @return the intent that will launch the script + */ + public static Intent buildStartInBackgroundIntent(File script) { + final ComponentName componentName = Constants.SL4A_SERVICE_LAUNCHER_COMPONENT_NAME; + Intent intent = new Intent(); + intent.setComponent(componentName); + intent.setAction(Constants.ACTION_LAUNCH_BACKGROUND_SCRIPT); + intent.putExtra(Constants.EXTRA_SCRIPT_PATH, script.getAbsolutePath()); + return intent; + } + + /** + * Builds an intent that launches a script in a terminal. + * + * @param script + * the script to launch + * @return the intent that will launch the script + */ + public static Intent buildStartInTerminalIntent(File script) { + final ComponentName componentName = Constants.SL4A_SERVICE_LAUNCHER_COMPONENT_NAME; + Intent intent = new Intent(); + intent.setComponent(componentName); + intent.setAction(Constants.ACTION_LAUNCH_FOREGROUND_SCRIPT); + intent.putExtra(Constants.EXTRA_SCRIPT_PATH, script.getAbsolutePath()); + return intent; + } + + /** + * Builds an intent that launches an interpreter. + * + * @param interpreterName + * the interpreter to launch + * @return the intent that will launch the interpreter + */ + public static Intent buildStartInterpreterIntent(String interpreterName) { + final ComponentName componentName = Constants.SL4A_SERVICE_LAUNCHER_COMPONENT_NAME; + Intent intent = new Intent(); + intent.setComponent(componentName); + intent.setAction(Constants.ACTION_LAUNCH_INTERPRETER); + intent.putExtra(Constants.EXTRA_INTERPRETER_NAME, interpreterName); + return intent; + } + + /** + * Builds an intent that creates a shortcut to launch the provided interpreter. + * + * @param interpreter + * the interpreter to link to + * @param iconResource + * the icon resource to associate with the shortcut + * @return the intent that will create the shortcut + */ + public static Intent buildInterpreterShortcutIntent(Interpreter interpreter, + Parcelable iconResource) { + Intent intent = new Intent(); + intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, + buildStartInterpreterIntent(interpreter.getName())); + intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, interpreter.getNiceName()); + intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource); + return intent; + } + + /** + * Builds an intent that creates a shortcut to launch the provided script in the background. + * + * @param script + * the script to link to + * @param iconResource + * the icon resource to associate with the shortcut + * @return the intent that will create the shortcut + */ + public static Intent buildBackgroundShortcutIntent(File script, Parcelable iconResource) { + Intent intent = new Intent(); + intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, buildStartInBackgroundIntent(script)); + intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, script.getName()); + intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource); + return intent; + } + + /** + * Builds an intent that creates a shortcut to launch the provided script in a terminal. + * + * @param script + * the script to link to + * @param iconResource + * the icon resource to associate with the shortcut + * @return the intent that will create the shortcut + */ + public static Intent buildTerminalShortcutIntent(File script, Parcelable iconResource) { + Intent intent = new Intent(); + intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, buildStartInTerminalIntent(script)); + intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, script.getName()); + intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource); + return intent; + } + + /** + * Creates a pending intent that can be used to start the trigger service. + * + * @param context + * the context under whose authority to launch the intent + * + * @return {@link PendingIntent} object for running the trigger service + */ + public static PendingIntent buildTriggerServicePendingIntent(Context context) { + final Intent intent = buildTriggerServiceIntent(); + return PendingIntent.getService(context, EXECUTE_SCRIPT_REQUEST_CODE, intent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); + } +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/LogUtil.java b/src/main/java/org/qpython/qsl4a/qsl4a/LogUtil.java index bc3f88e..7987933 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/LogUtil.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/LogUtil.java @@ -1,179 +1,179 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a; - -//import android.support.v7.app.AlertDialog; -import android.app.AlertDialog; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.Context; -import android.content.DialogInterface; -import android.widget.Toast; - -public class LogUtil { - private LogUtil() { - // Utility class. - } - - private static String getTag() { - StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); - String fullClassName = stackTraceElements[4].getClassName(); - String className = fullClassName.substring(fullClassName.lastIndexOf(".") + 1); - int lineNumber = stackTraceElements[4].getLineNumber(); - return "QPY:" + className + ":" + lineNumber; - } - - private static void toast(Context context, String message) { - Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); - } - - @SuppressWarnings("deprecation") -public static void notify(Context context, String title, String contentTitle, String message) { - android.util.Log.v(getTag(), String.format("%s %s", contentTitle, message)); - - String packageName = context.getPackageName(); - int iconId = context.getResources().getIdentifier("stat_sys_warning", "drawable", packageName); - NotificationManager notificationManager = - (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - Notification note = new Notification(iconId > 0 ? iconId : -1, title, 0); - //note.setLatestEventInfo(context, contentTitle, message, PendingIntent.getService(context, 0, - // null, 0)); - note.contentView.getLayoutId(); - notificationManager.notify(NotificationIdFactory.create(), note); - } - - public static void showDialog(final Context context, final String title, final String message) { - android.util.Log.v(getTag(), String.format("%s %s", title, message)); - - MainThread.run(context, new Runnable() { - @Override - public void run() { - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(title); - builder.setMessage(message); - - DialogInterface.OnClickListener buttonListener = new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }; - builder.setPositiveButton("Ok", buttonListener); - builder.show(); - } - }); - } - - public static void v(String message) { - android.util.Log.v(getTag(), message); - } - - public static void v(String message, Throwable e) { - android.util.Log.v(getTag(), message, e); - } - - public static void v(Context context, String message) { - toast(context, message); - android.util.Log.v(getTag(), message); - } - - public static void v(Context context, String message, Throwable e) { - toast(context, message); - android.util.Log.v(getTag(), message, e); - } - - public static void e(Throwable e) { - android.util.Log.e(getTag(), "Error", e); - } - - public static void e(String message) { - android.util.Log.e(getTag(), message); - } - - public static void e(String message, Throwable e) { - android.util.Log.e(getTag(), message, e); - } - - public static void e(Context context, String message) { - toast(context, message); - android.util.Log.e(getTag(), message); - } - - public static void e(Context context, String message, Throwable e) { - toast(context, message); - android.util.Log.e(getTag(), message, e); - } - - public static void w(Throwable e) { - android.util.Log.w(getTag(), "Warning", e); - } - - public static void w(String message) { - android.util.Log.w(getTag(), message); - } - - public static void w(String message, Throwable e) { - android.util.Log.w(getTag(), message, e); - } - - public static void w(Context context, String message) { - toast(context, message); - android.util.Log.w(getTag(), message); - } - - public static void w(Context context, String message, Throwable e) { - toast(context, message); - android.util.Log.w(getTag(), message, e); - } - - public static void d(String message) { - android.util.Log.d(getTag(), message); - } - - public static void d(String message, Throwable e) { - android.util.Log.d(getTag(), message, e); - } - - public static void d(Context context, String message) { - toast(context, message); - android.util.Log.d(getTag(), message); - } - - public static void d(Context context, String message, Throwable e) { - toast(context, message); - android.util.Log.d(getTag(), message, e); - } - - public static void i(String message) { - android.util.Log.i(getTag(), message); - } - - public static void i(String message, Throwable e) { - android.util.Log.i(getTag(), message, e); - } - - public static void i(Context context, String message) { - toast(context, message); - android.util.Log.i(getTag(), message); - } - - public static void i(Context context, String message, Throwable e) { - toast(context, message); - android.util.Log.i(getTag(), message, e); - } -} +/* + * Copyright (C) 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.qpython.qsl4a.qsl4a; + +//import android.support.v7.app.AlertDialog; +import android.app.AlertDialog; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.DialogInterface; +import android.widget.Toast; + +public class LogUtil { + private LogUtil() { + // Utility class. + } + + private static String getTag() { + StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); + String fullClassName = stackTraceElements[4].getClassName(); + String className = fullClassName.substring(fullClassName.lastIndexOf(".") + 1); + int lineNumber = stackTraceElements[4].getLineNumber(); + return "QPY:" + className + ":" + lineNumber; + } + + private static void toast(Context context, String message) { + Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); + } + + @SuppressWarnings("deprecation") +public static void notify(Context context, String title, String contentTitle, String message) { + android.util.Log.v(getTag(), String.format("%s %s", contentTitle, message)); + + String packageName = context.getPackageName(); + int iconId = context.getResources().getIdentifier("stat_sys_warning", "drawable", packageName); + NotificationManager notificationManager = + (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + Notification note = new Notification(iconId > 0 ? iconId : -1, title, 0); + //note.setLatestEventInfo(context, contentTitle, message, PendingIntent.getService(context, 0, + // null, 0)); + note.contentView.getLayoutId(); + notificationManager.notify(NotificationIdFactory.create(), note); + } + + public static void showDialog(final Context context, final String title, final String message) { + android.util.Log.v(getTag(), String.format("%s %s", title, message)); + + MainThread.run(context, new Runnable() { + @Override + public void run() { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(title); + builder.setMessage(message); + + DialogInterface.OnClickListener buttonListener = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }; + builder.setPositiveButton("Ok", buttonListener); + builder.show(); + } + }); + } + + public static void v(String message) { + android.util.Log.v(getTag(), message); + } + + public static void v(String message, Throwable e) { + android.util.Log.v(getTag(), message, e); + } + + public static void v(Context context, String message) { + toast(context, message); + android.util.Log.v(getTag(), message); + } + + public static void v(Context context, String message, Throwable e) { + toast(context, message); + android.util.Log.v(getTag(), message, e); + } + + public static void e(Throwable e) { + android.util.Log.e(getTag(), "Error", e); + } + + public static void e(String message) { + android.util.Log.e(getTag(), message); + } + + public static void e(String message, Throwable e) { + android.util.Log.e(getTag(), message, e); + } + + public static void e(Context context, String message) { + toast(context, message); + android.util.Log.e(getTag(), message); + } + + public static void e(Context context, String message, Throwable e) { + toast(context, message); + android.util.Log.e(getTag(), message, e); + } + + public static void w(Throwable e) { + android.util.Log.w(getTag(), "Warning", e); + } + + public static void w(String message) { + android.util.Log.w(getTag(), message); + } + + public static void w(String message, Throwable e) { + android.util.Log.w(getTag(), message, e); + } + + public static void w(Context context, String message) { + toast(context, message); + android.util.Log.w(getTag(), message); + } + + public static void w(Context context, String message, Throwable e) { + toast(context, message); + android.util.Log.w(getTag(), message, e); + } + + public static void d(String message) { + android.util.Log.d(getTag(), message); + } + + public static void d(String message, Throwable e) { + android.util.Log.d(getTag(), message, e); + } + + public static void d(Context context, String message) { + toast(context, message); + android.util.Log.d(getTag(), message); + } + + public static void d(Context context, String message, Throwable e) { + toast(context, message); + android.util.Log.d(getTag(), message, e); + } + + public static void i(String message) { + android.util.Log.i(getTag(), message); + } + + public static void i(String message, Throwable e) { + android.util.Log.i(getTag(), message, e); + } + + public static void i(Context context, String message) { + toast(context, message); + android.util.Log.i(getTag(), message); + } + + public static void i(Context context, String message, Throwable e) { + toast(context, message); + android.util.Log.i(getTag(), message, e); + } +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/MainThread.java b/src/main/java/org/qpython/qsl4a/qsl4a/MainThread.java index 95fc2e7..49736fe 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/MainThread.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/MainThread.java @@ -1,54 +1,54 @@ -// Copyright 2010 Google Inc. All Rights Reserved. - -package org.qpython.qsl4a.qsl4a; - -import android.content.Context; -import android.os.Handler; - - -import org.qpython.qsl4a.qsl4a.future.FutureResult; - -import java.util.concurrent.Callable; - -public class MainThread { - - private MainThread() { - // Utility class. - } - - /** - * Executed in the main thread, returns the result of an execution. Anything that runs here should - * finish quickly to avoid hanging the UI thread. - */ - public static T run(Context context, final Callable task) { - final FutureResult result = new FutureResult(); - Handler handler = new Handler(context.getMainLooper()); - handler.post(new Runnable() { - @Override - public void run() { - try { - result.set(task.call()); - } catch (Exception e) { - LogUtil.e(e); - result.set(null); - } - } - }); - try { - return result.get(); - } catch (InterruptedException e) { - LogUtil.e(e); - } - return null; - } - - public static void run(Context context, final Runnable task) { - Handler handler = new Handler(context.getMainLooper()); - handler.post(new Runnable() { - @Override - public void run() { - task.run(); - } - }); - } -} +// Copyright 2010 Google Inc. All Rights Reserved. + +package org.qpython.qsl4a.qsl4a; + +import android.content.Context; +import android.os.Handler; + + +import org.qpython.qsl4a.qsl4a.future.FutureResult; + +import java.util.concurrent.Callable; + +public class MainThread { + + private MainThread() { + // Utility class. + } + + /** + * Executed in the main thread, returns the result of an execution. Anything that runs here should + * finish quickly to avoid hanging the UI thread. + */ + public static T run(Context context, final Callable task) { + final FutureResult result = new FutureResult(); + Handler handler = new Handler(context.getMainLooper()); + handler.post(new Runnable() { + @Override + public void run() { + try { + result.set(task.call()); + } catch (Exception e) { + LogUtil.e(e); + result.set(null); + } + } + }); + try { + return result.get(); + } catch (InterruptedException e) { + LogUtil.e(e); + } + return null; + } + + public static void run(Context context, final Runnable task) { + Handler handler = new Handler(context.getMainLooper()); + handler.post(new Runnable() { + @Override + public void run() { + task.run(); + } + }); + } +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/NotificationIdFactory.java b/src/main/java/org/qpython/qsl4a/qsl4a/NotificationIdFactory.java index db0b349..27bf37f 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/NotificationIdFactory.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/NotificationIdFactory.java @@ -1,37 +1,37 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a; - -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Creates unique ids to identify the notifications created by the android scripting service and the - * trigger service. - * - * @author Felix Arends (felix.arends@gmail.com) - * - */ -public final class NotificationIdFactory { - private static final AtomicInteger mNextId = new AtomicInteger(0); - - public static int create() { - return mNextId.incrementAndGet(); - } - - private NotificationIdFactory() { - } -} +/* + * Copyright (C) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.qpython.qsl4a.qsl4a; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Creates unique ids to identify the notifications created by the android scripting service and the + * trigger service. + * + * @author Felix Arends (felix.arends@gmail.com) + * + */ +public final class NotificationIdFactory { + private static final AtomicInteger mNextId = new AtomicInteger(0); + + public static int create() { + return mNextId.incrementAndGet(); + } + + private NotificationIdFactory() { + } +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/Process.java b/src/main/java/org/qpython/qsl4a/qsl4a/Process.java index 67becf3..2932ea6 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/Process.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/Process.java @@ -1,205 +1,205 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a; - -import org.qpython.qsl4a.codec.StreamGobbler; -import org.qpython.qsl4a.qsl4a.interpreter.InterpreterConstants; - -import java.io.File; -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.atomic.AtomicInteger; - -public class Process { - - private static final int DEFAULT_BUFFER_SIZE = 8192; - - private final List mArguments; - private final Map mEnvironment; - - private static final int PID_INIT_VALUE = -1; - - private File mBinary; - private String mName; - private long mStartTime; - private long mEndTime; - - protected final AtomicInteger mPid; - protected FileDescriptor mFd; - protected OutputStream mOut; - protected InputStream mIn; - protected File mLog; - - public Process() { - mArguments = new ArrayList(); - mEnvironment = new HashMap(); - mPid = new AtomicInteger(PID_INIT_VALUE); - } - - public void addArgument(String argument) { - mArguments.add(argument); - } - - public void addAllArguments(List arguments) { - mArguments.addAll(arguments); - } - - public void putAllEnvironmentVariables(Map environment) { - mEnvironment.putAll(environment); - } - - public void putEnvironmentVariable(String key, String value) { - mEnvironment.put(key, value); - } - - public void setBinary(File binary) { - if (!binary.exists()) { - throw new RuntimeException("Binary " + binary + " does not exist!"); - } - mBinary = binary; - } - - public Integer getPid() { - return mPid.get(); - } - - public FileDescriptor getFd() { - return mFd; - } - - public OutputStream getOut() { - return mOut; - } - - public OutputStream getErr() { - return getOut(); - } - - public File getLogFile() { - return mLog; - } - - public InputStream getIn() { - return mIn; - } - - public void start(final Runnable shutdownHook) { - if (isAlive()) { - throw new RuntimeException("Attempted to start process that is already running."); - } - - String binaryPath = mBinary.getAbsolutePath(); - LogUtil.v("Executing " + binaryPath + " with arguments " + mArguments + " and with environment " - + mEnvironment.toString()); - - int[] pid = new int[1]; - String[] argumentsArray = mArguments.toArray(new String[mArguments.size()]); - mLog = new File(String.format("%s/%s.log", InterpreterConstants.SDCARD_SL4A_ROOT, getName())); - - mFd = - Exec.createSubprocess(binaryPath, argumentsArray, getEnvironmentArray(), - getWorkingDirectory(), pid); - mPid.set(pid[0]); - mOut = new FileOutputStream(mFd); - mIn = new StreamGobbler(new FileInputStream(mFd), mLog, DEFAULT_BUFFER_SIZE); - mStartTime = System.currentTimeMillis(); - - new Thread(new Runnable() { - public void run() { - int result = Exec.waitFor(mPid.get()); - mEndTime = System.currentTimeMillis(); - int pid = mPid.getAndSet(PID_INIT_VALUE); - LogUtil.v("Process " + pid + " exited with result code " + result + "."); - try { - mIn.close(); - } catch (IOException e) { - LogUtil.e(e); - } - try { - mOut.close(); - } catch (IOException e) { - LogUtil.e(e); - } - if (shutdownHook != null) { - shutdownHook.run(); - } - } - }).start(); - } - - private String[] getEnvironmentArray() { - List environmentVariables = new ArrayList(); - for (Entry entry : mEnvironment.entrySet()) { - environmentVariables.add(entry.getKey() + "=" + entry.getValue()); - } - String[] environment = environmentVariables.toArray(new String[environmentVariables.size()]); - return environment; - } - - public void kill() { - if (isAlive()) { - android.os.Process.killProcess(mPid.get()); - LogUtil.v("Killed process " + mPid); - } - } - - public boolean isAlive() { - return (mFd != null && mFd.valid()) && mPid.get() != PID_INIT_VALUE; - } - - public String getUptime() { - long ms; - if (!isAlive()) { - ms = mEndTime - mStartTime; - } else { - ms = System.currentTimeMillis() - mStartTime; - } - StringBuilder buffer = new StringBuilder(); - int days = (int) (ms / (1000 * 60 * 60 * 24)); - int hours = (int) (ms % (1000 * 60 * 60 * 24)) / 3600000; - int minutes = (int) (ms % 3600000) / 60000; - int seconds = (int) (ms % 60000) / 1000; - if (days != 0) { - buffer.append(String.format("%02d:%02d:", days, hours)); - } else if (hours != 0) { - buffer.append(String.format("%02d:", hours)); - } - buffer.append(String.format("%02d:%02d", minutes, seconds)); - return buffer.toString(); - } - - public String getName() { - return mName; - } - - public void setName(String name) { - mName = name; - } - - public String getWorkingDirectory() { - return null; - } -} +/* + * Copyright (C) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.qpython.qsl4a.qsl4a; + +import org.qpython.qsl4a.codec.StreamGobbler; +import org.qpython.qsl4a.qsl4a.interpreter.InterpreterConstants; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.atomic.AtomicInteger; + +public class Process { + + private static final int DEFAULT_BUFFER_SIZE = 8192; + + private final List mArguments; + private final Map mEnvironment; + + private static final int PID_INIT_VALUE = -1; + + private File mBinary; + private String mName; + private long mStartTime; + private long mEndTime; + + protected final AtomicInteger mPid; + protected FileDescriptor mFd; + protected OutputStream mOut; + protected InputStream mIn; + protected File mLog; + + public Process() { + mArguments = new ArrayList(); + mEnvironment = new HashMap(); + mPid = new AtomicInteger(PID_INIT_VALUE); + } + + public void addArgument(String argument) { + mArguments.add(argument); + } + + public void addAllArguments(List arguments) { + mArguments.addAll(arguments); + } + + public void putAllEnvironmentVariables(Map environment) { + mEnvironment.putAll(environment); + } + + public void putEnvironmentVariable(String key, String value) { + mEnvironment.put(key, value); + } + + public void setBinary(File binary) { + if (!binary.exists()) { + throw new RuntimeException("Binary " + binary + " does not exist!"); + } + mBinary = binary; + } + + public Integer getPid() { + return mPid.get(); + } + + public FileDescriptor getFd() { + return mFd; + } + + public OutputStream getOut() { + return mOut; + } + + public OutputStream getErr() { + return getOut(); + } + + public File getLogFile() { + return mLog; + } + + public InputStream getIn() { + return mIn; + } + + public void start(final Runnable shutdownHook) { + if (isAlive()) { + throw new RuntimeException("Attempted to start process that is already running."); + } + + String binaryPath = mBinary.getAbsolutePath(); + LogUtil.v("Executing " + binaryPath + " with arguments " + mArguments + " and with environment " + + mEnvironment.toString()); + + int[] pid = new int[1]; + String[] argumentsArray = mArguments.toArray(new String[mArguments.size()]); + mLog = new File(String.format("%s/%s.log", InterpreterConstants.SDCARD_SL4A_ROOT, getName())); + + mFd = + Exec.createSubprocess(binaryPath, argumentsArray, getEnvironmentArray(), + getWorkingDirectory(), pid); + mPid.set(pid[0]); + mOut = new FileOutputStream(mFd); + mIn = new StreamGobbler(new FileInputStream(mFd), mLog, DEFAULT_BUFFER_SIZE); + mStartTime = System.currentTimeMillis(); + + new Thread(new Runnable() { + public void run() { + int result = Exec.waitFor(mPid.get()); + mEndTime = System.currentTimeMillis(); + int pid = mPid.getAndSet(PID_INIT_VALUE); + LogUtil.v("Process " + pid + " exited with result code " + result + "."); + try { + mIn.close(); + } catch (IOException e) { + LogUtil.e(e); + } + try { + mOut.close(); + } catch (IOException e) { + LogUtil.e(e); + } + if (shutdownHook != null) { + shutdownHook.run(); + } + } + }).start(); + } + + private String[] getEnvironmentArray() { + List environmentVariables = new ArrayList(); + for (Entry entry : mEnvironment.entrySet()) { + environmentVariables.add(entry.getKey() + "=" + entry.getValue()); + } + String[] environment = environmentVariables.toArray(new String[environmentVariables.size()]); + return environment; + } + + public void kill() { + if (isAlive()) { + android.os.Process.killProcess(mPid.get()); + LogUtil.v("Killed process " + mPid); + } + } + + public boolean isAlive() { + return (mFd != null && mFd.valid()) && mPid.get() != PID_INIT_VALUE; + } + + public String getUptime() { + long ms; + if (!isAlive()) { + ms = mEndTime - mStartTime; + } else { + ms = System.currentTimeMillis() - mStartTime; + } + StringBuilder buffer = new StringBuilder(); + int days = (int) (ms / (1000 * 60 * 60 * 24)); + int hours = (int) (ms % (1000 * 60 * 60 * 24)) / 3600000; + int minutes = (int) (ms % 3600000) / 60000; + int seconds = (int) (ms % 60000) / 1000; + if (days != 0) { + buffer.append(String.format("%02d:%02d:", days, hours)); + } else if (hours != 0) { + buffer.append(String.format("%02d:", hours)); + } + buffer.append(String.format("%02d:%02d", minutes, seconds)); + return buffer.toString(); + } + + public String getName() { + return mName; + } + + public void setName(String name) { + mName = name; + } + + public String getWorkingDirectory() { + return null; + } +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/ScriptLauncher.java b/src/main/java/org/qpython/qsl4a/qsl4a/ScriptLauncher.java index 016acd9..1922171 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/ScriptLauncher.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/ScriptLauncher.java @@ -1,101 +1,101 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a; - -import android.app.Service; -import android.content.Intent; - - -import java.io.File; - -import org.qpython.qsl4a.QSL4APP; -import org.qpython.qsl4a.qsl4a.facade.FacadeConfiguration; -import org.qpython.qsl4a.qsl4a.facade.FacadeManager; -import org.qpython.qsl4a.qsl4a.future.FutureActivityTaskExecutor; -import org.qpython.qsl4a.qsl4a.interpreter.Interpreter; -import org.qpython.qsl4a.qsl4a.interpreter.InterpreterConfiguration; -import org.qpython.qsl4a.qsl4a.interpreter.InterpreterProcess; -import org.qpython.qsl4a.qsl4a.interpreter.html.HtmlActivityTask; -import org.qpython.qsl4a.qsl4a.interpreter.html.HtmlInterpreter; - -public class ScriptLauncher { - - private ScriptLauncher() { - // Utility class. - } - - public static HtmlActivityTask launchHtmlScript(File script, Service service, Intent intent, - InterpreterConfiguration config) { - if (!script.exists()) { - throw new RuntimeException("No such script to launch."); - } - HtmlInterpreter interpreter = - (HtmlInterpreter) config.getInterpreterByName(HtmlInterpreter.HTML); - if (interpreter == null) { - throw new RuntimeException("HtmlInterpreter is not available."); - } - final FacadeManager manager = - new FacadeManager(FacadeConfiguration.getSdkLevel(), service, intent, - FacadeConfiguration.getFacadeClasses()); - FutureActivityTaskExecutor executor = - ((QSL4APP) service.getApplication()).getTaskExecutor(); - final HtmlActivityTask task = - new HtmlActivityTask(manager, interpreter.getAndroidJsSource(), - interpreter.getJsonSource(),interpreter.getTemplateSource(), script.getAbsolutePath(), true); - executor.execute(task); - return task; - } - - public static InterpreterProcess launchInterpreter(final AndroidProxy proxy, Intent intent, - InterpreterConfiguration config, Runnable shutdownHook) { - Interpreter interpreter; - String interpreterName; - interpreterName = intent.getStringExtra(Constants.EXTRA_INTERPRETER_NAME); - interpreter = config.getInterpreterByName(interpreterName); - InterpreterProcess process = new InterpreterProcess(interpreter, proxy); - if (shutdownHook == null) { - process.start(new Runnable() { - @Override - public void run() { - proxy.shutdown(); - } - }); - } else { - process.start(shutdownHook); - } - return process; - } - - public static ScriptProcess launchScript(File script, InterpreterConfiguration configuration, - final AndroidProxy proxy, Runnable shutdownHook) { - if (!script.exists()) { - throw new RuntimeException("No such script to launch."); - } - ScriptProcess process = new ScriptProcess(script, configuration, proxy); - if (shutdownHook == null) { - process.start(new Runnable() { - @Override - public void run() { - proxy.shutdown(); - } - }); - } else { - process.start(shutdownHook); - } - return process; - } +/* + * Copyright (C) 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.qpython.qsl4a.qsl4a; + +import android.app.Service; +import android.content.Intent; + + +import java.io.File; + +import org.qpython.qsl4a.QSL4APP; +import org.qpython.qsl4a.qsl4a.facade.FacadeConfiguration; +import org.qpython.qsl4a.qsl4a.facade.FacadeManager; +import org.qpython.qsl4a.qsl4a.future.FutureActivityTaskExecutor; +import org.qpython.qsl4a.qsl4a.interpreter.Interpreter; +import org.qpython.qsl4a.qsl4a.interpreter.InterpreterConfiguration; +import org.qpython.qsl4a.qsl4a.interpreter.InterpreterProcess; +import org.qpython.qsl4a.qsl4a.interpreter.html.HtmlActivityTask; +import org.qpython.qsl4a.qsl4a.interpreter.html.HtmlInterpreter; + +public class ScriptLauncher { + + private ScriptLauncher() { + // Utility class. + } + + public static HtmlActivityTask launchHtmlScript(File script, Service service, Intent intent, + InterpreterConfiguration config) { + if (!script.exists()) { + throw new RuntimeException("No such script to launch."); + } + HtmlInterpreter interpreter = + (HtmlInterpreter) config.getInterpreterByName(HtmlInterpreter.HTML); + if (interpreter == null) { + throw new RuntimeException("HtmlInterpreter is not available."); + } + final FacadeManager manager = + new FacadeManager(FacadeConfiguration.getSdkLevel(), service, intent, + FacadeConfiguration.getFacadeClasses()); + FutureActivityTaskExecutor executor = + ((QSL4APP) service.getApplication()).getTaskExecutor(); + final HtmlActivityTask task = + new HtmlActivityTask(manager, interpreter.getAndroidJsSource(), + interpreter.getJsonSource(),interpreter.getTemplateSource(), script.getAbsolutePath(), true); + executor.execute(task); + return task; + } + + public static InterpreterProcess launchInterpreter(final AndroidProxy proxy, Intent intent, + InterpreterConfiguration config, Runnable shutdownHook) { + Interpreter interpreter; + String interpreterName; + interpreterName = intent.getStringExtra(Constants.EXTRA_INTERPRETER_NAME); + interpreter = config.getInterpreterByName(interpreterName); + InterpreterProcess process = new InterpreterProcess(interpreter, proxy); + if (shutdownHook == null) { + process.start(new Runnable() { + @Override + public void run() { + proxy.shutdown(); + } + }); + } else { + process.start(shutdownHook); + } + return process; + } + + public static ScriptProcess launchScript(File script, InterpreterConfiguration configuration, + final AndroidProxy proxy, Runnable shutdownHook) { + if (!script.exists()) { + throw new RuntimeException("No such script to launch."); + } + ScriptProcess process = new ScriptProcess(script, configuration, proxy); + if (shutdownHook == null) { + process.start(new Runnable() { + @Override + public void run() { + proxy.shutdown(); + } + }); + } else { + process.start(shutdownHook); + } + return process; + } } \ No newline at end of file diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/ScriptProcess.java b/src/main/java/org/qpython/qsl4a/qsl4a/ScriptProcess.java index f5b7433..97d4436 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/ScriptProcess.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/ScriptProcess.java @@ -1,42 +1,42 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a; - -import org.qpython.qsl4a.qsl4a.interpreter.Interpreter; -import org.qpython.qsl4a.qsl4a.interpreter.InterpreterConfiguration; -import org.qpython.qsl4a.qsl4a.interpreter.InterpreterProcess; - -import java.io.File; - -public class ScriptProcess extends InterpreterProcess { - - private final File mScript; - - public ScriptProcess(File script, InterpreterConfiguration configuration, AndroidProxy proxy) { - super(configuration.getInterpreterForScript(script.getName()), proxy); - mScript = script; - String scriptName = script.getName(); - setName(scriptName); - Interpreter interpreter = configuration.getInterpreterForScript(scriptName); - setCommand(String.format(interpreter.getScriptCommand(), script.getAbsolutePath())); - } - - public String getPath() { - return mScript.getPath(); - } - -} +/* + * Copyright (C) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.qpython.qsl4a.qsl4a; + +import org.qpython.qsl4a.qsl4a.interpreter.Interpreter; +import org.qpython.qsl4a.qsl4a.interpreter.InterpreterConfiguration; +import org.qpython.qsl4a.qsl4a.interpreter.InterpreterProcess; + +import java.io.File; + +public class ScriptProcess extends InterpreterProcess { + + private final File mScript; + + public ScriptProcess(File script, InterpreterConfiguration configuration, AndroidProxy proxy) { + super(configuration.getInterpreterForScript(script.getName()), proxy); + mScript = script; + String scriptName = script.getName(); + setName(scriptName); + Interpreter interpreter = configuration.getInterpreterForScript(scriptName); + setCommand(String.format(interpreter.getScriptCommand(), script.getAbsolutePath())); + } + + public String getPath() { + return mScript.getPath(); + } + +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/ScriptStorageAdapter.java b/src/main/java/org/qpython/qsl4a/qsl4a/ScriptStorageAdapter.java index 527a20f..9125271 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/ScriptStorageAdapter.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/ScriptStorageAdapter.java @@ -1,139 +1,135 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a; - -import android.content.Context; - -import com.quseit.util.FileUtils; - -import org.qpython.qsl4a.qsl4a.interpreter.Interpreter; -import org.qpython.qsl4a.qsl4a.interpreter.InterpreterConfiguration; -import org.qpython.qsl4a.qsl4a.interpreter.InterpreterConstants; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - -/** - * Manages storage and retrieval of scripts on the file system. - * - * @author Damon Kohler (damonkohler@gmail.com) - */ -public class ScriptStorageAdapter { - - private ScriptStorageAdapter() { - // Utility class. - } - - /** - * Writes data to the script by name and overwrites any existing data. - */ - public static void writeScript(Context context, File script, String data) { - if (script.getParent() == null) { - script = new File(FileUtils.getScriptsRootPath(context), script.getPath()); - } - try { - FileWriter stream = new FileWriter(script, false /* overwrite */); - BufferedWriter out = new BufferedWriter(stream); - out.write(data); - out.close(); - } catch (IOException e) { - LogUtil.e("Failed to write script.", e); - } - } - - /** - * Returns a list of all available script {@link File}s. - */ - public static List listAllScripts(Context context,File dir) { - if (dir == null) { - dir = new File(FileUtils.getScriptsRootPath(context)); - } - if (dir.exists()) { - List scripts = Arrays.asList(dir.listFiles()); - Collections.sort(scripts, new Comparator() { - @Override - public int compare(File file1, File file2) { - if (file1.isDirectory() && !file2.isDirectory()) { - return -1; - } else if (!file1.isDirectory() && file2.isDirectory()) { - return 1; - } - return file1.compareTo(file2); - } - }); - return scripts; - } - return new ArrayList(); - } - - /** - * Returns a list of script {@link File}s from the given folder for which there is an interpreter - * installed. - */ - public static List listExecutableScripts(Context context,File directory, InterpreterConfiguration config) { - // NOTE(damonkohler): Creating a LinkedList here is necessary in order to be able to filter it - // later. - List scripts = new LinkedList<>(listAllScripts(context,directory)); - // Filter out any files that don't have interpreters installed. - for (Iterator it = scripts.iterator(); it.hasNext();) { - File script = it.next(); - if (script.isDirectory()) { - continue; - } - Interpreter interpreter = config.getInterpreterForScript(script.getName()); - if (interpreter == null || !interpreter.isInstalled()) { - it.remove(); - } - } - return scripts; - } - - /** - * Returns a list of all (including subfolders) script {@link File}s for which there is an - * interpreter installed. - */ - public static List listExecutableScriptsRecursively(Context context,File directory, - InterpreterConfiguration config) { - // NOTE(damonkohler): Creating a LinkedList here is necessary in order to be able to filter it - // later. - List scripts = new LinkedList(); - List files = listAllScripts(context,directory); - - // Filter out any files that don't have interpreters installed. - for (Iterator it = files.iterator(); it.hasNext();) { - File file = it.next(); - if (file.isDirectory()) { - scripts.addAll(listExecutableScriptsRecursively(context,file, config)); - } - Interpreter interpreter = config.getInterpreterForScript(file.getName()); - if (interpreter != null && interpreter.isInstalled()) { - scripts.add(file); - } - } - Collections.sort(scripts); - return scripts; - } +/* + * Copyright (C) 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.qpython.qsl4a.qsl4a; + +import org.qpython.qsl4a.qsl4a.interpreter.Interpreter; +import org.qpython.qsl4a.qsl4a.interpreter.InterpreterConfiguration; +import org.qpython.qsl4a.qsl4a.interpreter.InterpreterConstants; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +/** + * Manages storage and retrieval of scripts on the file system. + * + * @author Damon Kohler (damonkohler@gmail.com) + */ +public class ScriptStorageAdapter { + + private ScriptStorageAdapter() { + // Utility class. + } + + /** + * Writes data to the script by name and overwrites any existing data. + */ + public static void writeScript(File script, String data) { + if (script.getParent() == null) { + script = new File(InterpreterConstants.SCRIPTS_ROOT, script.getPath()); + } + try { + FileWriter stream = new FileWriter(script, false /* overwrite */); + BufferedWriter out = new BufferedWriter(stream); + out.write(data); + out.close(); + } catch (IOException e) { + LogUtil.e("Failed to write script.", e); + } + } + + /** + * Returns a list of all available script {@link File}s. + */ + public static List listAllScripts(File dir) { + if (dir == null) { + dir = new File(InterpreterConstants.SCRIPTS_ROOT); + } + if (dir.exists()) { + List scripts = Arrays.asList(dir.listFiles()); + Collections.sort(scripts, new Comparator() { + @Override + public int compare(File file1, File file2) { + if (file1.isDirectory() && !file2.isDirectory()) { + return -1; + } else if (!file1.isDirectory() && file2.isDirectory()) { + return 1; + } + return file1.compareTo(file2); + } + }); + return scripts; + } + return new ArrayList(); + } + + /** + * Returns a list of script {@link File}s from the given folder for which there is an interpreter + * installed. + */ + public static List listExecutableScripts(File directory, InterpreterConfiguration config) { + // NOTE(damonkohler): Creating a LinkedList here is necessary in order to be able to filter it + // later. + List scripts = new LinkedList(listAllScripts(directory)); + // Filter out any files that don't have interpreters installed. + for (Iterator it = scripts.iterator(); it.hasNext();) { + File script = it.next(); + if (script.isDirectory()) { + continue; + } + Interpreter interpreter = config.getInterpreterForScript(script.getName()); + if (interpreter == null || !interpreter.isInstalled()) { + it.remove(); + } + } + return scripts; + } + + /** + * Returns a list of all (including subfolders) script {@link File}s for which there is an + * interpreter installed. + */ + public static List listExecutableScriptsRecursively(File directory, + InterpreterConfiguration config) { + // NOTE(damonkohler): Creating a LinkedList here is necessary in order to be able to filter it + // later. + List scripts = new LinkedList(); + List files = listAllScripts(directory); + + // Filter out any files that don't have interpreters installed. + for (Iterator it = files.iterator(); it.hasNext();) { + File file = it.next(); + if (file.isDirectory()) { + scripts.addAll(listExecutableScriptsRecursively(file, config)); + } + Interpreter interpreter = config.getInterpreterForScript(file.getName()); + if (interpreter != null && interpreter.isInstalled()) { + scripts.add(file); + } + } + Collections.sort(scripts); + return scripts; + } } \ No newline at end of file diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/SimpleServer.java b/src/main/java/org/qpython/qsl4a/qsl4a/SimpleServer.java index da9e58f..6aa1915 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/SimpleServer.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/SimpleServer.java @@ -1,259 +1,259 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a; - -import com.google.common.collect.Lists; - -import java.io.IOException; -import java.net.Inet4Address; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.NetworkInterface; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.SocketException; -import java.net.UnknownHostException; -import java.util.Collections; -import java.util.Enumeration; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * A simple server. - * - * @author Damon Kohler (damonkohler@gmail.com) - */ -public abstract class SimpleServer { - - private final CopyOnWriteArrayList mConnectionThreads = - new CopyOnWriteArrayList(); - private final List mObservers = Lists.newArrayList(); - private volatile boolean mStopServer = false; - private ServerSocket mServer; - private Thread mServerThread; - - public interface SimpleServerObserver { - public void onConnect(); - - public void onDisconnect(); - } - - protected abstract void handleConnection(Socket socket) throws Exception; - - /** Adds an observer. */ - public void addObserver(SimpleServerObserver observer) { - mObservers.add(observer); - } - - /** Removes an observer. */ - public void removeObserver(SimpleServerObserver observer) { - mObservers.remove(observer); - } - - private void notifyOnConnect() { - for (SimpleServerObserver observer : mObservers) { - observer.onConnect(); - } - } - - private void notifyOnDisconnect() { - for (SimpleServerObserver observer : mObservers) { - observer.onDisconnect(); - } - } - - private final class ConnectionThread extends Thread { - private final Socket mmSocket; - - private ConnectionThread(Socket socket) { - setName("SimpleServer ConnectionThread " + getId()); - mmSocket = socket; - } - - @Override - public void run() { - LogUtil.v("Server thread " + getId() + " started."); - try { - handleConnection(mmSocket); - } catch (Exception e) { - if (!mStopServer) { - LogUtil.e("Server error.", e); - } - } finally { - close(); - mConnectionThreads.remove(this); - notifyOnDisconnect(); - LogUtil.v("Server thread " + getId() + " died."); - } - } - - private void close() { - if (mmSocket != null) { - try { - mmSocket.close(); - } catch (IOException e) { - LogUtil.e(e.getMessage(), e); - } - } - } - } - - /** Returns the number of active connections to this server. */ - public int getNumberOfConnections() { - return mConnectionThreads.size(); - } - - public static InetAddress getPublicInetAddress() throws UnknownHostException, SocketException { - - InetAddress candidate = null; - Enumeration nets = NetworkInterface.getNetworkInterfaces(); - for (NetworkInterface netint : Collections.list(nets)) { - /*if (netint.isLoopback() || !netint.isUp()) { // Ignore if localhost or not active - continue; - }*/ - Enumeration addresses = netint.getInetAddresses(); - for (InetAddress address : Collections.list(addresses)) { - if (address instanceof Inet4Address) { - return address; // Prefer ipv4 - } - candidate = address; // Probably an ipv6 - } - } - if (candidate != null) { - return candidate; // return ipv6 address if no suitable ipv6 - } - return InetAddress.getLocalHost(); // No damn matches. Give up, return local host. - } - - /** - * Starts the RPC server bound to the localhost address. - * - * @param port - * the port to bind to or 0 to pick any unused port - * - * @return the port that the server is bound to - */ - public InetSocketAddress startLocal(int port) { - InetAddress address; - try { - address = InetAddress.getLocalHost(); - mServer = new ServerSocket(port, 5 /* backlog */, address); - } catch (Exception e) { - LogUtil.e("Failed to start server.", e); - return null; - } - int boundPort = start(); - return InetSocketAddress.createUnresolved(mServer.getInetAddress().getHostAddress(), boundPort); - } - - /** - * data Starts the RPC server bound to the public facing address. - * - * @param port - * the port to bind to or 0 to pick any unused port - * - * @return the port that the server is bound to - */ - public InetSocketAddress startPublic(int port) { - InetAddress address; - try { - // address = getPublicInetAddress(); - address = null; - mServer = new ServerSocket(port, 5 /* backlog */, address); - } catch (Exception e) { - LogUtil.e("Failed to start server.", e); - return null; - } - int boundPort = start(); - return InetSocketAddress.createUnresolved(mServer.getInetAddress().getHostAddress(), boundPort); - } - - /** - * data Starts the RPC server bound to all interfaces - * - * @param port - * the port to bind to or 0 to pick any unused port - * - * @return the port that the server is bound to - */ - public InetSocketAddress startAllInterfaces(int port) { - try { - mServer = new ServerSocket(port, 5 /* backlog */); - } catch (Exception e) { - LogUtil.e("Failed to start server.", e); - return null; - } - int boundPort = start(); - return InetSocketAddress.createUnresolved(mServer.getInetAddress().getHostAddress(), boundPort); - } - - private int start() { - mServerThread = new Thread() { - @Override - public void run() { - while (!mStopServer) { - try { - Socket sock = mServer.accept(); - if (!mStopServer) { - startConnectionThread(sock); - } else { - sock.close(); - } - } catch (IOException e) { - if (!mStopServer) { - LogUtil.e("Failed to accept connection.", e); - } - } - } - } - }; - mServerThread.start(); - LogUtil.v("Bound to " + mServer.getInetAddress()); - return mServer.getLocalPort(); - } - - private void startConnectionThread(final Socket sock) { - ConnectionThread networkThread = new ConnectionThread(sock); - mConnectionThreads.add(networkThread); - networkThread.start(); - notifyOnConnect(); - } - - public void shutdown() { - // Stop listening on the server socket to ensure that - // beyond this point there are no incoming requests. - mStopServer = true; - try { - if (mServer!=null) { - mServer.close(); - } - } catch (IOException e) { - LogUtil.e("Failed to close server socket.", e); - } - // Since the server is not running, the mNetworkThreads set can only - // shrink from this point onward. We can just stop all of the running helper - // threads. In the worst case, one of the running threads will already have - // shut down. Since this is a CopyOnWriteList, we don't have to worry about - // concurrency issues while iterating over the set of threads. - for (ConnectionThread connectionThread : mConnectionThreads) { - connectionThread.close(); - } - for (SimpleServerObserver observer : mObservers) { - removeObserver(observer); - } - } -} +/* + * Copyright (C) 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.qpython.qsl4a.qsl4a; + +import com.google.common.collect.Lists; + +import java.io.IOException; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.NetworkInterface; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * A simple server. + * + * @author Damon Kohler (damonkohler@gmail.com) + */ +public abstract class SimpleServer { + + private final CopyOnWriteArrayList mConnectionThreads = + new CopyOnWriteArrayList(); + private final List mObservers = Lists.newArrayList(); + private volatile boolean mStopServer = false; + private ServerSocket mServer; + private Thread mServerThread; + + public interface SimpleServerObserver { + public void onConnect(); + + public void onDisconnect(); + } + + protected abstract void handleConnection(Socket socket) throws Exception; + + /** Adds an observer. */ + public void addObserver(SimpleServerObserver observer) { + mObservers.add(observer); + } + + /** Removes an observer. */ + public void removeObserver(SimpleServerObserver observer) { + mObservers.remove(observer); + } + + private void notifyOnConnect() { + for (SimpleServerObserver observer : mObservers) { + observer.onConnect(); + } + } + + private void notifyOnDisconnect() { + for (SimpleServerObserver observer : mObservers) { + observer.onDisconnect(); + } + } + + private final class ConnectionThread extends Thread { + private final Socket mmSocket; + + private ConnectionThread(Socket socket) { + setName("SimpleServer ConnectionThread " + getId()); + mmSocket = socket; + } + + @Override + public void run() { + LogUtil.v("Server thread " + getId() + " started."); + try { + handleConnection(mmSocket); + } catch (Exception e) { + if (!mStopServer) { + LogUtil.e("Server error.", e); + } + } finally { + close(); + mConnectionThreads.remove(this); + notifyOnDisconnect(); + LogUtil.v("Server thread " + getId() + " died."); + } + } + + private void close() { + if (mmSocket != null) { + try { + mmSocket.close(); + } catch (IOException e) { + LogUtil.e(e.getMessage(), e); + } + } + } + } + + /** Returns the number of active connections to this server. */ + public int getNumberOfConnections() { + return mConnectionThreads.size(); + } + + public static InetAddress getPublicInetAddress() throws UnknownHostException, SocketException { + + InetAddress candidate = null; + Enumeration nets = NetworkInterface.getNetworkInterfaces(); + for (NetworkInterface netint : Collections.list(nets)) { + /*if (netint.isLoopback() || !netint.isUp()) { // Ignore if localhost or not active + continue; + }*/ + Enumeration addresses = netint.getInetAddresses(); + for (InetAddress address : Collections.list(addresses)) { + if (address instanceof Inet4Address) { + return address; // Prefer ipv4 + } + candidate = address; // Probably an ipv6 + } + } + if (candidate != null) { + return candidate; // return ipv6 address if no suitable ipv6 + } + return InetAddress.getLocalHost(); // No damn matches. Give up, return local host. + } + + /** + * Starts the RPC server bound to the localhost address. + * + * @param port + * the port to bind to or 0 to pick any unused port + * + * @return the port that the server is bound to + */ + public InetSocketAddress startLocal(int port) { + InetAddress address; + try { + address = InetAddress.getLocalHost(); + mServer = new ServerSocket(port, 5 /* backlog */, address); + } catch (Exception e) { + LogUtil.e("Failed to start server.", e); + return null; + } + int boundPort = start(); + return InetSocketAddress.createUnresolved(mServer.getInetAddress().getHostAddress(), boundPort); + } + + /** + * data Starts the RPC server bound to the public facing address. + * + * @param port + * the port to bind to or 0 to pick any unused port + * + * @return the port that the server is bound to + */ + public InetSocketAddress startPublic(int port) { + InetAddress address; + try { + // address = getPublicInetAddress(); + address = null; + mServer = new ServerSocket(port, 5 /* backlog */, address); + } catch (Exception e) { + LogUtil.e("Failed to start server.", e); + return null; + } + int boundPort = start(); + return InetSocketAddress.createUnresolved(mServer.getInetAddress().getHostAddress(), boundPort); + } + + /** + * data Starts the RPC server bound to all interfaces + * + * @param port + * the port to bind to or 0 to pick any unused port + * + * @return the port that the server is bound to + */ + public InetSocketAddress startAllInterfaces(int port) { + try { + mServer = new ServerSocket(port, 5 /* backlog */); + } catch (Exception e) { + LogUtil.e("Failed to start server.", e); + return null; + } + int boundPort = start(); + return InetSocketAddress.createUnresolved(mServer.getInetAddress().getHostAddress(), boundPort); + } + + private int start() { + mServerThread = new Thread() { + @Override + public void run() { + while (!mStopServer) { + try { + Socket sock = mServer.accept(); + if (!mStopServer) { + startConnectionThread(sock); + } else { + sock.close(); + } + } catch (IOException e) { + if (!mStopServer) { + LogUtil.e("Failed to accept connection.", e); + } + } + } + } + }; + mServerThread.start(); + LogUtil.v("Bound to " + mServer.getInetAddress()); + return mServer.getLocalPort(); + } + + private void startConnectionThread(final Socket sock) { + ConnectionThread networkThread = new ConnectionThread(sock); + mConnectionThreads.add(networkThread); + networkThread.start(); + notifyOnConnect(); + } + + public void shutdown() { + // Stop listening on the server socket to ensure that + // beyond this point there are no incoming requests. + mStopServer = true; + try { + if (mServer!=null) { + mServer.close(); + } + } catch (IOException e) { + LogUtil.e("Failed to close server socket.", e); + } + // Since the server is not running, the mNetworkThreads set can only + // shrink from this point onward. We can just stop all of the running helper + // threads. In the worst case, one of the running threads will already have + // shut down. Since this is a CopyOnWriteList, we don't have to worry about + // concurrency issues while iterating over the set of threads. + for (ConnectionThread connectionThread : mConnectionThreads) { + connectionThread.close(); + } + for (SimpleServerObserver observer : mObservers) { + removeObserver(observer); + } + } +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/SingleThreadExecutor.java b/src/main/java/org/qpython/qsl4a/qsl4a/SingleThreadExecutor.java index 5a72c0b..13582c4 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/SingleThreadExecutor.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/SingleThreadExecutor.java @@ -1,35 +1,34 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a; - -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -public class SingleThreadExecutor extends ThreadPoolExecutor { - - public SingleThreadExecutor() { - super(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); - } - - @Override - protected void afterExecute(Runnable r, Throwable t) { - if (t != null) { - throw new RuntimeException(t); - } - } +/* + * Copyright (C) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.qpython.qsl4a.qsl4a; + +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +public class SingleThreadExecutor extends ThreadPoolExecutor { + + public SingleThreadExecutor() { + super(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); + } + + @Override + protected void afterExecute(Runnable r, Throwable t) { + if (t != null) { + throw new RuntimeException(t); } + } } \ No newline at end of file diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/StringUtils.java b/src/main/java/org/qpython/qsl4a/qsl4a/StringUtils.java index da1fa35..d074154 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/StringUtils.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/StringUtils.java @@ -1,35 +1,35 @@ -package org.qpython.qsl4a.qsl4a; - -import java.util.Collection; -import java.util.Iterator; - -public class StringUtils { - - private StringUtils() { - // Utility class. - } - public static String addSlashes(String txt) - { - if (null != txt) - { - txt = txt.replace("\\", "\\\\") ; - txt = txt.replace("\'", "\\\'") ; - //txt = txt.replace(" ", "\\ ") ; - - } - - return txt ; - } - - public static String join(Collection collection, String delimiter) { - StringBuffer buffer = new StringBuffer(); - Iterator iter = collection.iterator(); - while (iter.hasNext()) { - buffer.append(iter.next()); - if (iter.hasNext()) { - buffer.append(delimiter); - } - } - return buffer.toString(); - } -} +package org.qpython.qsl4a.qsl4a; + +import java.util.Collection; +import java.util.Iterator; + +public class StringUtils { + + private StringUtils() { + // Utility class. + } + public static String addSlashes(String txt) + { + if (null != txt) + { + txt = txt.replace("\\", "\\\\") ; + txt = txt.replace("\'", "\\\'") ; + //txt = txt.replace(" ", "\\ ") ; + + } + + return txt ; + } + + public static String join(Collection collection, String delimiter) { + StringBuffer buffer = new StringBuffer(); + Iterator iter = collection.iterator(); + while (iter.hasNext()) { + buffer.append(iter.next()); + if (iter.hasNext()) { + buffer.append(delimiter); + } + } + return buffer.toString(); + } +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/Version.java b/src/main/java/org/qpython/qsl4a/qsl4a/Version.java index 614313e..460d73e 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/Version.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/Version.java @@ -1,39 +1,39 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a; - -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; - -public class Version { - - private Version() { - // Utility class. - } - - public static String getVersion(Context context) { - try { - PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); - return info.versionName; - } catch (PackageManager.NameNotFoundException e) { - LogUtil.e("Package name not found", e); - } - return "?"; - } - -} +/* + * Copyright (C) 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.qpython.qsl4a.qsl4a; + +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; + +public class Version { + + private Version() { + // Utility class. + } + + public static String getVersion(Context context) { + try { + PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); + return info.versionName; + } catch (PackageManager.NameNotFoundException e) { + LogUtil.e("Package name not found", e); + } + return "?"; + } + +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/bluetooth/BluetoothDiscoveryHelper.java b/src/main/java/org/qpython/qsl4a/qsl4a/bluetooth/BluetoothDiscoveryHelper.java deleted file mode 100644 index c85b73b..0000000 --- a/src/main/java/org/qpython/qsl4a/qsl4a/bluetooth/BluetoothDiscoveryHelper.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a.bluetooth; - -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; - -import java.util.Set; - -public class BluetoothDiscoveryHelper { - - public static interface BluetoothDiscoveryListener { - public void addBondedDevice(String name, String address); - - public void addDevice(String name, String address); - - public void scanDone(); - } - - private final Context mContext; - private final BluetoothDiscoveryListener mListener; - private final BroadcastReceiver mReceiver; - - public BluetoothDiscoveryHelper(Context context, BluetoothDiscoveryListener listener) { - mContext = context; - mListener = listener; - mReceiver = new BluetoothReceiver(); - } - - private class BluetoothReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - - if (BluetoothDevice.ACTION_FOUND.equals(action)) { - // Get the BluetoothDevice object from the Intent. - BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); - // If it's already paired, skip it, because it's been listed already. - if (device.getBondState() != BluetoothDevice.BOND_BONDED) { - mListener.addDevice(device.getName(), device.getAddress()); - } - } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { - mListener.scanDone(); - } - } - } - - public void startDiscovery() { - BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); - - if (bluetoothAdapter.isDiscovering()) { - bluetoothAdapter.cancelDiscovery(); - } - - Set pairedDevices = bluetoothAdapter.getBondedDevices(); - for (BluetoothDevice device : pairedDevices) { - mListener.addBondedDevice(device.getName(), device.getAddress()); - } - - final IntentFilter deviceFoundFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND); - mContext.registerReceiver(mReceiver, deviceFoundFilter); - - final IntentFilter discoveryFinishedFilter = - new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); - mContext.registerReceiver(mReceiver, discoveryFinishedFilter); - - if (!bluetoothAdapter.isEnabled()) { - bluetoothAdapter.enable(); - } - - bluetoothAdapter.startDiscovery(); - } - - public void cancel() { - mContext.unregisterReceiver(mReceiver); - mListener.scanDone(); - } -} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/event/Event.java b/src/main/java/org/qpython/qsl4a/qsl4a/event/Event.java index e614401..21ed749 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/event/Event.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/event/Event.java @@ -1,57 +1,57 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a.event; - -import com.google.common.base.Preconditions; - -public class Event { - - private String mName; - private Object mData; - private double mCreationTime; - - public Event(String name, Object data) { - Preconditions.checkNotNull(name); - setName(name); - setData(data); - mCreationTime = System.currentTimeMillis() * 1000; - } - - public void setName(String name) { - mName = name; - } - - public String getName() { - return mName; - } - - public void setData(Object data) { - mData = data; - } - - public Object getData() { - return mData; - } - - public double getCreationTime() { - return mCreationTime; - } - - public boolean nameEquals(String name) { - return mName.equals(name); - } -} +/* + * Copyright (C) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.qpython.qsl4a.qsl4a.event; + +import com.google.common.base.Preconditions; + +public class Event { + + private String mName; + private Object mData; + private double mCreationTime; + + public Event(String name, Object data) { + Preconditions.checkNotNull(name); + setName(name); + setData(data); + mCreationTime = System.currentTimeMillis() * 1000; + } + + public void setName(String name) { + mName = name; + } + + public String getName() { + return mName; + } + + public void setData(Object data) { + mData = data; + } + + public Object getData() { + return mData; + } + + public double getCreationTime() { + return mCreationTime; + } + + public boolean nameEquals(String name) { + return mName.equals(name); + } +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/exception/Sl4aException.java b/src/main/java/org/qpython/qsl4a/qsl4a/exception/Sl4aException.java index 2cbb4b9..6fdbed1 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/exception/Sl4aException.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/exception/Sl4aException.java @@ -1,34 +1,34 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a.exception; - -@SuppressWarnings("serial") -public class Sl4aException extends Exception { - - public Sl4aException(Exception e) { - super(e); - } - - public Sl4aException(String message) { - super(message); - } - - public Sl4aException(String message, Exception e) { - super(message, e); - } - -} +/* + * Copyright (C) 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.qpython.qsl4a.qsl4a.exception; + +@SuppressWarnings("serial") +public class Sl4aException extends Exception { + + public Sl4aException(Exception e) { + super(e); + } + + public Sl4aException(String message) { + super(message); + } + + public Sl4aException(String message, Exception e) { + super(message, e); + } + +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/ActivityResultFacade.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/ActivityResultFacade.java index 37db1f5..9f6575b 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/ActivityResultFacade.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/ActivityResultFacade.java @@ -1,327 +1,327 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a.facade; - -import android.app.Activity; -import android.content.Intent; - -import org.qpython.qsl4a.qsl4a.Constants; -import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; -import org.qpython.qsl4a.qsl4a.rpc.Rpc; -import org.qpython.qsl4a.qsl4a.rpc.RpcParameter; - -import java.io.Serializable; - -/** - * Allows you to return results to a startActivityForResult call. - * - * @author Alexey Reznichenko (alexey.reznichenko@gmail.com) - */ -public class ActivityResultFacade extends RpcReceiver { - - private static final String sRpcDescription = - "Sets the result of a script execution. Whenever the script APK is called via " - + "startActivityForResult(), the resulting intent will contain " + Constants.EXTRA_RESULT - + " extra with the given value."; - private static final String sCodeDescription = - "The result code to propagate back to the originating activity, often RESULT_CANCELED (0) " - + "or RESULT_OK (-1)"; - - private Activity mActivity = null; - private Intent mResult = null; - private int mResultCode; - - public ActivityResultFacade(FacadeManager manager) { - super(manager); - } - - @Rpc(description = sRpcDescription) - public synchronized void setResultBoolean( - @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, - @RpcParameter(name = "resultValue") Boolean resultValue) { - mResult = new Intent(); - mResult.putExtra(Constants.EXTRA_RESULT, resultValue.booleanValue()); - mResultCode = resultCode; - if (mActivity != null) { - setResult(); - } - } - - @Rpc(description = sRpcDescription) - public synchronized void setResultByte( - @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, - @RpcParameter(name = "resultValue") Byte resultValue) { - mResult = new Intent(); - mResult.putExtra(Constants.EXTRA_RESULT, resultValue.byteValue()); - mResultCode = resultCode; - if (mActivity != null) { - setResult(); - } - } - - @Rpc(description = sRpcDescription) - public synchronized void setResultShort( - @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, - @RpcParameter(name = "resultValue") Short resultValue) { - mResult = new Intent(); - mResult.putExtra(Constants.EXTRA_RESULT, resultValue.shortValue()); - mResultCode = resultCode; - if (mActivity != null) { - setResult(); - } - } - - @Rpc(description = sRpcDescription) - public synchronized void setResultChar( - @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, - @RpcParameter(name = "resultValue") Character resultValue) { - mResult = new Intent(); - mResult.putExtra(Constants.EXTRA_RESULT, resultValue.charValue()); - mResultCode = resultCode; - if (mActivity != null) { - setResult(); - } - } - - @Rpc(description = sRpcDescription) - public synchronized void setResultInteger( - @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, - @RpcParameter(name = "resultValue") Integer resultValue) { - mResult = new Intent(); - mResult.putExtra(Constants.EXTRA_RESULT, resultValue.intValue()); - mResultCode = resultCode; - if (mActivity != null) { - setResult(); - } - } - - @Rpc(description = sRpcDescription) - public synchronized void setResultLong( - @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, - @RpcParameter(name = "resultValue") Long resultValue) { - mResult = new Intent(); - mResult.putExtra(Constants.EXTRA_RESULT, resultValue.longValue()); - mResultCode = resultCode; - if (mActivity != null) { - setResult(); - } - } - - @Rpc(description = sRpcDescription) - public synchronized void setResultFloat( - @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, - @RpcParameter(name = "resultValue") Float resultValue) { - mResult = new Intent(); - mResult.putExtra(Constants.EXTRA_RESULT, resultValue.floatValue()); - mResultCode = resultCode; - if (mActivity != null) { - setResult(); - } - } - - @Rpc(description = sRpcDescription) - public synchronized void setResultDouble( - @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, - @RpcParameter(name = "resultValue") Double resultValue) { - mResult = new Intent(); - mResult.putExtra(Constants.EXTRA_RESULT, resultValue.doubleValue()); - mResultCode = resultCode; - if (mActivity != null) { - setResult(); - } - } - - @Rpc(description = sRpcDescription) - public synchronized void setResultString( - @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, - @RpcParameter(name = "resultValue") String resultValue) { - mResult = new Intent(); - mResult.putExtra(Constants.EXTRA_RESULT, resultValue); - mResultCode = resultCode; - if (mActivity != null) { - setResult(); - } - } - - @Rpc(description = sRpcDescription) - public synchronized void setResultBooleanArray( - @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, - @RpcParameter(name = "resultValue") Boolean[] resultValue) { - mResult = new Intent(); - boolean[] array = new boolean[resultValue.length]; - for (int i = 0; i < resultValue.length; i++) { - array[i] = resultValue[i]; - } - mResult.putExtra(Constants.EXTRA_RESULT, array); - mResultCode = resultCode; - if (mActivity != null) { - setResult(); - } - } - - @Rpc(description = sRpcDescription) - public synchronized void setResultByteArray( - @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, - @RpcParameter(name = "resultValue") Byte[] resultValue) { - mResult = new Intent(); - byte[] array = new byte[resultValue.length]; - for (int i = 0; i < resultValue.length; i++) { - array[i] = resultValue[i]; - } - mResult.putExtra(Constants.EXTRA_RESULT, array); - mResultCode = resultCode; - if (mActivity != null) { - setResult(); - } - } - - @Rpc(description = sRpcDescription) - public synchronized void setResultShortArray( - @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, - @RpcParameter(name = "resultValue") Short[] resultValue) { - mResult = new Intent(); - short[] array = new short[resultValue.length]; - for (int i = 0; i < resultValue.length; i++) { - array[i] = resultValue[i]; - } - mResult.putExtra(Constants.EXTRA_RESULT, array); - mResultCode = resultCode; - if (mActivity != null) { - setResult(); - } - } - - @Rpc(description = sRpcDescription) - public synchronized void setResultCharArray( - @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, - @RpcParameter(name = "resultValue") Character[] resultValue) { - mResult = new Intent(); - char[] array = new char[resultValue.length]; - for (int i = 0; i < resultValue.length; i++) { - array[i] = resultValue[i]; - } - mResult.putExtra(Constants.EXTRA_RESULT, array); - mResultCode = resultCode; - if (mActivity != null) { - setResult(); - } - } - - @Rpc(description = sRpcDescription) - public synchronized void setResultIntegerArray( - @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, - @RpcParameter(name = "resultValue") Integer[] resultValue) { - mResult = new Intent(); - int[] array = new int[resultValue.length]; - for (int i = 0; i < resultValue.length; i++) { - array[i] = resultValue[i]; - } - mResult.putExtra(Constants.EXTRA_RESULT, array); - mResultCode = resultCode; - if (mActivity != null) { - setResult(); - } - } - - @Rpc(description = sRpcDescription) - public synchronized void setResultLongArray( - @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, - @RpcParameter(name = "resultValue") Long[] resultValue) { - mResult = new Intent(); - long[] array = new long[resultValue.length]; - for (int i = 0; i < resultValue.length; i++) { - array[i] = resultValue[i]; - } - mResult.putExtra(Constants.EXTRA_RESULT, array); - mResultCode = resultCode; - if (mActivity != null) { - setResult(); - } - } - - @Rpc(description = sRpcDescription) - public synchronized void setResultFloatArray( - @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, - @RpcParameter(name = "resultValue") Float[] resultValue) { - mResult = new Intent(); - float[] array = new float[resultValue.length]; - for (int i = 0; i < resultValue.length; i++) { - array[i] = resultValue[i]; - } - mResult.putExtra(Constants.EXTRA_RESULT, array); - mResultCode = resultCode; - if (mActivity != null) { - setResult(); - } - } - - @Rpc(description = sRpcDescription) - public synchronized void setResultDoubleArray( - @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, - @RpcParameter(name = "resultValue") Double[] resultValue) { - mResult = new Intent(); - double[] array = new double[resultValue.length]; - for (int i = 0; i < resultValue.length; i++) { - array[i] = resultValue[i]; - } - mResult.putExtra(Constants.EXTRA_RESULT, array); - mResultCode = resultCode; - if (mActivity != null) { - setResult(); - } - } - - @Rpc(description = sRpcDescription) - public synchronized void setResultStringArray( - @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, - @RpcParameter(name = "resultValue") String[] resultValue) { - mResult = new Intent(); - mResult.putExtra(Constants.EXTRA_RESULT, resultValue); - mResultCode = resultCode; - if (mActivity != null) { - setResult(); - } - } - - @Rpc(description = sRpcDescription) - public synchronized void setResultSerializable( - @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, - @RpcParameter(name = "resultValue") Serializable resultValue) { - mResult = new Intent(); - mResult.putExtra(Constants.EXTRA_RESULT, resultValue); - mResultCode = resultCode; - if (mActivity != null) { - setResult(); - } - } - - public synchronized void setActivity(Activity activity) { - mActivity = activity; - if (mResult != null) { - setResult(); - } - } - - private void setResult() { - mActivity.setResult(mResultCode, mResult); - mActivity.finish(); - } - - @Override - public void shutdown() { - } -} +/* + * Copyright (C) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.qpython.qsl4a.qsl4a.facade; + +import android.app.Activity; +import android.content.Intent; + +import org.qpython.qsl4a.qsl4a.Constants; +import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; +import org.qpython.qsl4a.qsl4a.rpc.Rpc; +import org.qpython.qsl4a.qsl4a.rpc.RpcParameter; + +import java.io.Serializable; + +/** + * Allows you to return results to a startActivityForResult call. + * + * @author Alexey Reznichenko (alexey.reznichenko@gmail.com) + */ +public class ActivityResultFacade extends RpcReceiver { + + private static final String sRpcDescription = + "Sets the result of a script execution. Whenever the script APK is called via " + + "startActivityForResult(), the resulting intent will contain " + Constants.EXTRA_RESULT + + " extra with the given value."; + private static final String sCodeDescription = + "The result code to propagate back to the originating activity, often RESULT_CANCELED (0) " + + "or RESULT_OK (-1)"; + + private Activity mActivity = null; + private Intent mResult = null; + private int mResultCode; + + public ActivityResultFacade(FacadeManager manager) { + super(manager); + } + + @Rpc(description = sRpcDescription) + public synchronized void setResultBoolean( + @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, + @RpcParameter(name = "resultValue") Boolean resultValue) { + mResult = new Intent(); + mResult.putExtra(Constants.EXTRA_RESULT, resultValue.booleanValue()); + mResultCode = resultCode; + if (mActivity != null) { + setResult(); + } + } + + @Rpc(description = sRpcDescription) + public synchronized void setResultByte( + @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, + @RpcParameter(name = "resultValue") Integer resultValue) { + mResult = new Intent(); + mResult.putExtra(Constants.EXTRA_RESULT, resultValue.byteValue()); + mResultCode = resultCode; + if (mActivity != null) { + setResult(); + } + } + + @Rpc(description = sRpcDescription) + public synchronized void setResultShort( + @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, + @RpcParameter(name = "resultValue") Integer resultValue) { + mResult = new Intent(); + mResult.putExtra(Constants.EXTRA_RESULT, resultValue.shortValue()); + mResultCode = resultCode; + if (mActivity != null) { + setResult(); + } + } + + @Rpc(description = sRpcDescription) + public synchronized void setResultChar( + @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, + @RpcParameter(name = "resultValue") String resultValue) { + mResult = new Intent(); + mResult.putExtra(Constants.EXTRA_RESULT, resultValue.charAt(0)); + mResultCode = resultCode; + if (mActivity != null) { + setResult(); + } + } + + @Rpc(description = sRpcDescription) + public synchronized void setResultInteger( + @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, + @RpcParameter(name = "resultValue") Integer resultValue) { + mResult = new Intent(); + mResult.putExtra(Constants.EXTRA_RESULT, resultValue.intValue()); + mResultCode = resultCode; + if (mActivity != null) { + setResult(); + } + } + + @Rpc(description = sRpcDescription) + public synchronized void setResultLong( + @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, + @RpcParameter(name = "resultValue") Long resultValue) { + mResult = new Intent(); + mResult.putExtra(Constants.EXTRA_RESULT, resultValue.longValue()); + mResultCode = resultCode; + if (mActivity != null) { + setResult(); + } + } + + @Rpc(description = sRpcDescription) + public synchronized void setResultFloat( + @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, + @RpcParameter(name = "resultValue") Double resultValue) { + mResult = new Intent(); + mResult.putExtra(Constants.EXTRA_RESULT, resultValue.floatValue()); + mResultCode = resultCode; + if (mActivity != null) { + setResult(); + } + } + + @Rpc(description = sRpcDescription) + public synchronized void setResultDouble( + @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, + @RpcParameter(name = "resultValue") Double resultValue) { + mResult = new Intent(); + mResult.putExtra(Constants.EXTRA_RESULT, resultValue.doubleValue()); + mResultCode = resultCode; + if (mActivity != null) { + setResult(); + } + } + + @Rpc(description = sRpcDescription) + public synchronized void setResultString( + @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, + @RpcParameter(name = "resultValue") String resultValue) { + mResult = new Intent(); + mResult.putExtra(Constants.EXTRA_RESULT, resultValue); + mResultCode = resultCode; + if (mActivity != null) { + setResult(); + } + } + + @Rpc(description = sRpcDescription) + public synchronized void setResultBooleanArray( + @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, + @RpcParameter(name = "resultValue") Boolean[] resultValue) { + mResult = new Intent(); + boolean[] array = new boolean[resultValue.length]; + for (int i = 0; i < resultValue.length; i++) { + array[i] = resultValue[i]; + } + mResult.putExtra(Constants.EXTRA_RESULT, array); + mResultCode = resultCode; + if (mActivity != null) { + setResult(); + } + } + + @Rpc(description = sRpcDescription) + public synchronized void setResultByteArray( + @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, + @RpcParameter(name = "resultValue") Integer[] resultValue) { + mResult = new Intent(); + byte[] array = new byte[resultValue.length]; + for (int i = 0; i < resultValue.length; i++) { + array[i] = resultValue[i].byteValue(); + } + mResult.putExtra(Constants.EXTRA_RESULT, array); + mResultCode = resultCode; + if (mActivity != null) { + setResult(); + } + } + + @Rpc(description = sRpcDescription) + public synchronized void setResultShortArray( + @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, + @RpcParameter(name = "resultValue") Integer[] resultValue) { + mResult = new Intent(); + short[] array = new short[resultValue.length]; + for (int i = 0; i < resultValue.length; i++) { + array[i] = resultValue[i].shortValue(); + } + mResult.putExtra(Constants.EXTRA_RESULT, array); + mResultCode = resultCode; + if (mActivity != null) { + setResult(); + } + } + + @Rpc(description = sRpcDescription) + public synchronized void setResultCharArray( + @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, + @RpcParameter(name = "resultValue") String[] resultValue) { + mResult = new Intent(); + char[] array = new char[resultValue.length]; + for (int i = 0; i < resultValue.length; i++) { + array[i] = resultValue[i].charAt(0); + } + mResult.putExtra(Constants.EXTRA_RESULT, array); + mResultCode = resultCode; + if (mActivity != null) { + setResult(); + } + } + + @Rpc(description = sRpcDescription) + public synchronized void setResultIntegerArray( + @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, + @RpcParameter(name = "resultValue") Integer[] resultValue) { + mResult = new Intent(); + int[] array = new int[resultValue.length]; + for (int i = 0; i < resultValue.length; i++) { + array[i] = resultValue[i]; + } + mResult.putExtra(Constants.EXTRA_RESULT, array); + mResultCode = resultCode; + if (mActivity != null) { + setResult(); + } + } + + @Rpc(description = sRpcDescription) + public synchronized void setResultLongArray( + @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, + @RpcParameter(name = "resultValue") Long[] resultValue) { + mResult = new Intent(); + long[] array = new long[resultValue.length]; + for (int i = 0; i < resultValue.length; i++) { + array[i] = resultValue[i]; + } + mResult.putExtra(Constants.EXTRA_RESULT, array); + mResultCode = resultCode; + if (mActivity != null) { + setResult(); + } + } + + @Rpc(description = sRpcDescription) + public synchronized void setResultFloatArray( + @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, + @RpcParameter(name = "resultValue") Double[] resultValue) { + mResult = new Intent(); + float[] array = new float[resultValue.length]; + for (int i = 0; i < resultValue.length; i++) { + array[i] = resultValue[i].floatValue(); + } + mResult.putExtra(Constants.EXTRA_RESULT, array); + mResultCode = resultCode; + if (mActivity != null) { + setResult(); + } + } + + @Rpc(description = sRpcDescription) + public synchronized void setResultDoubleArray( + @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, + @RpcParameter(name = "resultValue") Double[] resultValue) { + mResult = new Intent(); + double[] array = new double[resultValue.length]; + for (int i = 0; i < resultValue.length; i++) { + array[i] = resultValue[i]; + } + mResult.putExtra(Constants.EXTRA_RESULT, array); + mResultCode = resultCode; + if (mActivity != null) { + setResult(); + } + } + + @Rpc(description = sRpcDescription) + public synchronized void setResultStringArray( + @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, + @RpcParameter(name = "resultValue") String[] resultValue) { + mResult = new Intent(); + mResult.putExtra(Constants.EXTRA_RESULT, resultValue); + mResultCode = resultCode; + if (mActivity != null) { + setResult(); + } + } + + @Rpc(description = sRpcDescription) + public synchronized void setResultSerializable( + @RpcParameter(name = "resultCode", description = sCodeDescription) Integer resultCode, + @RpcParameter(name = "resultValue") Serializable resultValue) { + mResult = new Intent(); + mResult.putExtra(Constants.EXTRA_RESULT, resultValue); + mResultCode = resultCode; + if (mActivity != null) { + setResult(); + } + } + + public synchronized void setActivity(Activity activity) { + mActivity = activity; + if (mResult != null) { + setResult(); + } + } + + private void setResult() { + mActivity.setResult(mResultCode, mResult); + mActivity.finish(); + } + + @Override + public void shutdown() { + } +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/AndroidFacade.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/AndroidFacade.java index 77871b5..3a216a8 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/AndroidFacade.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/AndroidFacade.java @@ -1,916 +1,916 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a.facade; - -import android.annotation.SuppressLint; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.app.Service; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.StatFs; -import android.os.Vibrator; -import android.text.ClipboardManager; -import android.widget.Toast; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.qpython.qsl4a.QSL4APP; -import org.qpython.qsl4a.qsl4a.util.FileUtils; -import org.qpython.qsl4a.qsl4a.future.FutureActivityTaskExecutor; -import org.qpython.qsl4a.qsl4a.LogUtil; -import org.qpython.qsl4a.qsl4a.NotificationIdFactory; -import org.qpython.qsl4a.qsl4a.future.FutureActivityTask; -import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; -import org.qpython.qsl4a.qsl4a.rpc.Rpc; -import org.qpython.qsl4a.qsl4a.rpc.RpcDefault; -import org.qpython.qsl4a.qsl4a.rpc.RpcOptional; -import org.qpython.qsl4a.qsl4a.rpc.RpcParameter; -import org.qpython.qsl4a.qsl4a.util.HtmlUtil; -import org.qpython.qsl4a.qsl4a.util.SPFUtils; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.TimeZone; - -/** - * Some general purpose Android routines.
- *

Intents

Intents are returned as a map, in the following form:
- *
    - *
  • action - action. - *
  • data - url - *
  • type - mime type - *
  • packagename - name of package. If used, requires classname to be useful (optional) - *
  • classname - name of class. If used, requires packagename to be useful (optional) - *
  • categories - list of categories - *
  • extras - map of extras - *
  • flags - integer flags. - *
- *
- * An intent can be built using the {@see #makeIntent} call, but can also be constructed exterally. - * - */ -@SuppressWarnings("deprecation") -public class AndroidFacade extends RpcReceiver { - /** - * An instance of this interface is passed to the facade. From this object, the resource IDs can - * be obtained. - */ - public interface Resources { - int getLogo48(); - } - - public final Service mService; - public final Handler mHandler; - private final Intent mIntent; - private final FutureActivityTaskExecutor mTaskQueue; - - private final Vibrator mVibrator; - private final NotificationManager mNotificationManager; - - private final Resources mResources; - private ClipboardManager mClipboard = null; - public final Context context; - public final String qpyProvider; - - public static Handler handler; - - private final int intentFlags = Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION ; - - @Override - public void shutdown() { - } - - public Service getmService() { - return this.mService; - } - - public AndroidFacade(FacadeManager manager) { - super(manager); - mService = manager.getService(); - mIntent = manager.getIntent(); - // River Modify - QSL4APP application = ((QSL4APP) mService.getApplication()); - mTaskQueue = application.getTaskExecutor(); - mHandler = new Handler(mService.getMainLooper()); - mVibrator = (Vibrator) mService.getSystemService(Context.VIBRATOR_SERVICE); - mNotificationManager = - (NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE); - mResources = manager.getAndroidFacadeResources(); - //乘着船 修改 - context = mService.getApplicationContext(); - qpyProvider = context.getPackageName() + ".provider"; - } - - ClipboardManager getClipboardManager() { - Object clipboard; - if (mClipboard == null) { - try { - clipboard = mService.getSystemService(Context.CLIPBOARD_SERVICE); - } catch (Exception e) { - Looper.prepare(); // Clipboard manager won't work without this on higher SDK levels... - clipboard = mService.getSystemService(Context.CLIPBOARD_SERVICE); - } - mClipboard = (ClipboardManager) clipboard; - if (mClipboard == null) { - LogUtil.w("Clipboard managed not accessible."); - } - } - return mClipboard; - } - - /** - * Creates a new AndroidFacade that simplifies the interface to various Android APIs. - */ - - @Rpc(description = "Put text in the clipboard.") - public void setClipboard(@RpcParameter(name = "text") String text) { - getClipboardManager().setText(text); - } - - @Rpc(description = "Read text from the clipboard.", - returns = "The text in the clipboard.") - public String getClipboard() { - CharSequence text = getClipboardManager().getText(); - return text == null ? null : text.toString(); - } - - Intent startActivityForResult(final Intent intent) { - FutureActivityTask task = new FutureActivityTask() { - @Override - public void onCreate() { - super.onCreate(); - try { - startActivityForResult(intent, 1024); - } catch (Exception e) { - intent.putExtra("EXCEPTION", e.getMessage()); - setResult(intent); - } - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - setResult(data); - } - }; - mTaskQueue.execute(task); - - try { - return task.getResult(); - } catch (Exception e) { - throw new RuntimeException(e); - } finally { - task.finish(); - } - } - - Intent startActivityForResultCode(final Intent intent) { - FutureActivityTask task = new FutureActivityTask() { - @Override - public void onCreate() { - super.onCreate(); - try { - int requestCode; - if (intent!=null){ - requestCode=intent.getIntExtra("REQUEST_CODE",1024); - } else { - requestCode=2048; - } - startActivityForResult(intent, requestCode); - } catch (Exception e) { - if(intent!=null) { - intent.putExtra("EXCEPTION", e.getMessage()); - setResult(intent); - } - } - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (data==null) - data=new Intent(); - data.putExtra("RESULT_CODE",resultCode); - setResult(data); - } - }; - mTaskQueue.execute(task); - - try { - return task.getResult(); - } catch (Exception e) { - throw new RuntimeException(e); - } finally { - task.finish(); - } - } - - // TODO(damonkohler): Pull this out into proper argument deserialization and support - // complex/nested types being passed in. - public static void putExtrasFromJsonObject(JSONObject extras, Intent intent) throws JSONException { - JSONArray names = extras.names(); - for (int i = 0; i < names.length(); i++) { - String name = names.getString(i); - Object data = extras.get(name); - //if (data == null) { - // continue; - //} - if (data instanceof Integer) { - intent.putExtra(name, (Integer) data); - } - if (data instanceof Float) { - intent.putExtra(name, (Float) data); - } - if (data instanceof Double) { - intent.putExtra(name, (Double) data); - } - if (data instanceof Long) { - intent.putExtra(name, (Long) data); - } - if (data instanceof String) { - intent.putExtra(name, (String) data); - } - if (data instanceof Boolean) { - intent.putExtra(name, (Boolean) data); - } - // Nested JSONObject - if (data instanceof JSONObject) { - Bundle nestedBundle = new Bundle(); - intent.putExtra(name, nestedBundle); - putNestedJSONObject((JSONObject) data, nestedBundle); - } - // Nested JSONArray. Doesn't support mixed types in single array - if (data instanceof JSONArray) { - // Empty array. No way to tell what type of data to pass on, so skipping - if (((JSONArray) data).length() == 0) { - LogUtil.e("Empty array not supported in JSONObject, skipping"); - continue; - } - // Integer - if (((JSONArray) data).get(0) instanceof Integer) { - Integer[] integerArrayData = new Integer[((JSONArray) data).length()]; - for (int j = 0; j < ((JSONArray) data).length(); ++j) { - integerArrayData[j] = ((JSONArray) data).getInt(j); - } - intent.putExtra(name, integerArrayData); - } - // Double - if (((JSONArray) data).get(0) instanceof Double) { - Double[] doubleArrayData = new Double[((JSONArray) data).length()]; - for (int j = 0; j < ((JSONArray) data).length(); ++j) { - doubleArrayData[j] = ((JSONArray) data).getDouble(j); - } - intent.putExtra(name, doubleArrayData); - } - // Long - if (((JSONArray) data).get(0) instanceof Long) { - Long[] longArrayData = new Long[((JSONArray) data).length()]; - for (int j = 0; j < ((JSONArray) data).length(); ++j) { - longArrayData[j] = ((JSONArray) data).getLong(j); - } - intent.putExtra(name, longArrayData); - } - // String - if (((JSONArray) data).get(0) instanceof String) { - String[] stringArrayData = new String[((JSONArray) data).length()]; - for (int j = 0; j < ((JSONArray) data).length(); ++j) { - stringArrayData[j] = ((JSONArray) data).getString(j); - } - intent.putExtra(name, stringArrayData); - } - // Boolean - if (((JSONArray) data).get(0) instanceof Boolean) { - Boolean[] booleanArrayData = new Boolean[((JSONArray) data).length()]; - for (int j = 0; j < ((JSONArray) data).length(); ++j) { - booleanArrayData[j] = ((JSONArray) data).getBoolean(j); - } - intent.putExtra(name, booleanArrayData); - } - } - } - } - - // Contributed by Emmanuel T - // Nested Array handling contributed by Sergey Zelenev - private static void putNestedJSONObject(JSONObject jsonObject, Bundle bundle) - throws JSONException { - JSONArray names = jsonObject.names(); - for (int i = 0; i < names.length(); i++) { - String name = names.getString(i); - Object data = jsonObject.get(name); - if (data == null) { - continue; - } - if (data instanceof Integer) { - bundle.putInt(name, ((Integer) data).intValue()); - } - if (data instanceof Float) { - bundle.putFloat(name, ((Float) data).floatValue()); - } - if (data instanceof Double) { - bundle.putDouble(name, ((Double) data).doubleValue()); - } - if (data instanceof Long) { - bundle.putLong(name, ((Long) data).longValue()); - } - if (data instanceof String) { - bundle.putString(name, (String) data); - } - if (data instanceof Boolean) { - bundle.putBoolean(name, ((Boolean) data).booleanValue()); - } - // Nested JSONObject - if (data instanceof JSONObject) { - Bundle nestedBundle = new Bundle(); - bundle.putBundle(name, nestedBundle); - putNestedJSONObject((JSONObject) data, nestedBundle); - } - // Nested JSONArray. Doesn't support mixed types in single array - if (data instanceof JSONArray) { - // Empty array. No way to tell what type of data to pass on, so skipping - if (((JSONArray) data).length() == 0) { - LogUtil.e("Empty array not supported in nested JSONObject, skipping"); - continue; - } - // Integer - if (((JSONArray) data).get(0) instanceof Integer) { - int[] integerArrayData = new int[((JSONArray) data).length()]; - for (int j = 0; j < ((JSONArray) data).length(); ++j) { - integerArrayData[j] = ((JSONArray) data).getInt(j); - } - bundle.putIntArray(name, integerArrayData); - } - // Double - if (((JSONArray) data).get(0) instanceof Double) { - double[] doubleArrayData = new double[((JSONArray) data).length()]; - for (int j = 0; j < ((JSONArray) data).length(); ++j) { - doubleArrayData[j] = ((JSONArray) data).getDouble(j); - } - bundle.putDoubleArray(name, doubleArrayData); - } - // Long - if (((JSONArray) data).get(0) instanceof Long) { - long[] longArrayData = new long[((JSONArray) data).length()]; - for (int j = 0; j < ((JSONArray) data).length(); ++j) { - longArrayData[j] = ((JSONArray) data).getLong(j); - } - bundle.putLongArray(name, longArrayData); - } - // String - if (((JSONArray) data).get(0) instanceof String) { - String[] stringArrayData = new String[((JSONArray) data).length()]; - for (int j = 0; j < ((JSONArray) data).length(); ++j) { - stringArrayData[j] = ((JSONArray) data).getString(j); - } - bundle.putStringArray(name, stringArrayData); - } - // Boolean - if (((JSONArray) data).get(0) instanceof Boolean) { - boolean[] booleanArrayData = new boolean[((JSONArray) data).length()]; - for (int j = 0; j < ((JSONArray) data).length(); ++j) { - booleanArrayData[j] = ((JSONArray) data).getBoolean(j); - } - bundle.putBooleanArray(name, booleanArrayData); - } - } - } - } - - void startActivity(final Intent intent) throws Exception { - try { - intent.setFlags(intent.getFlags() | intentFlags); - context.startActivity(intent); - } catch (Exception e) { - throw new Exception("Failed to launch intent : "+ e); - } - } - - private Intent buildIntent(String action, String uri, String type, JSONObject extras, - String packagename, String classname, JSONArray categories) throws JSONException { - Intent intent = new Intent(action); - intent.setDataAndType(uri != null ? Uri.parse(uri) : null, type); - if (packagename != null && classname != null) { - intent.setComponent(new ComponentName(packagename, classname)); - } - if (extras != null) { - putExtrasFromJsonObject(extras, intent); - } - if (categories != null) { - for (int i = 0; i < categories.length(); i++) { - intent.addCategory(categories.getString(i)); - } - } - return intent; - } - - // TODO(damonkohler): It's unnecessary to add the complication of choosing between startActivity - // and startActivityForResult. It's probably better to just always use the ForResult version. - // However, this makes the call always blocking. We'd need to add an extra boolean parameter to - // indicate if we should wait for a result. - @Rpc(description = "Starts an activity and returns the result.", - returns = "A Map representation of the result Intent.") - public Intent startActivityForResult( - @RpcParameter(name = "action") String action, - @RpcParameter(name = "uri") @RpcOptional String uri, - @RpcParameter(name = "type", description = "MIME type/subtype of the URI") @RpcOptional String type, - @RpcParameter(name = "extras", description = "a Map of extras to add to the Intent") @RpcOptional JSONObject extras, - @RpcParameter(name = "packagename", description = "name of package. If used, requires classname to be useful") @RpcOptional String packagename, - @RpcParameter(name = "classname", description = "name of class. If used, requires packagename to be useful") @RpcOptional String classname) - throws JSONException { - final Intent intent = buildIntent(action, uri, type, extras, packagename, classname, null); - return startActivityForResult(intent); - } - - @Rpc(description = "Starts an activity and returns the result.", returns = "A Map representation of the result Intent.") - public Intent startActivityForResultIntent( - @RpcParameter(name = "intent", description = "Intent in the format as returned from makeIntent") Intent intent) { - return startActivityForResult(intent); - } - - public void doStartActivity(final Intent intent, Boolean wait) throws Exception { - Exception[] except = new Exception[1]; - if (wait == null || !wait) { - startActivity(intent); - } else { - FutureActivityTask task = new FutureActivityTask() { - private boolean mSecondResume = false; - - @Override - public void onCreate() { - super.onCreate(); - intent.setFlags(intent.getFlags() | intentFlags); - try { - startActivity(intent); - } catch (Exception exception) { - except[0] = exception; - } - } - - @Override - public void onResume() { - if (mSecondResume) { - finish(); - } - mSecondResume = true; - } - - @Override - public void onDestroy() { - setResult(null); - } - - }; - mTaskQueue.execute(task); - - try { - task.getResult(); - } catch (Exception e) { - throw new RuntimeException(e); - } - if(except[0]!=null) { - throw except[0]; - } - } - } - - public void doStartActivity(final Intent intent, Boolean wait,int flags) throws Exception { - Exception[] except = new Exception[1]; - if (wait == null || !wait) { - startActivity(intent); - } else { - FutureActivityTask task = new FutureActivityTask() { - private boolean mSecondResume = false; - - @Override - public void onCreate() { - super.onCreate(); - intent.setFlags(intent.getFlags() | intentFlags); - try { - startActivity(intent); - } catch (Exception exception) { - except[0] = exception; - } - } - - @Override - public void onResume() { - if (mSecondResume) { - finish(); - } - mSecondResume = true; - } - - @Override - public void onDestroy() { - setResult(null); - } - - }; - mTaskQueue.execute(task,flags); - - try { - task.getResult(); - } catch (Exception e) { - throw new RuntimeException(e); - } - if(except[0]!=null) { - throw except[0]; - } - } - } - - /** - * packagename and classname, if provided, are used in a 'setComponent' call. - */ - @Rpc(description = "Starts an activity.") - public void startActivity( - @RpcParameter(name = "action") String action, - @RpcParameter(name = "uri") @RpcOptional String uri, - @RpcParameter(name = "type", description = "MIME type/subtype of the URI") @RpcOptional String type, - @RpcParameter(name = "extras", description = "a Map of extras to add to the Intent") @RpcOptional JSONObject extras, - @RpcParameter(name = "wait", description = "block until the user exits the started activity") @RpcOptional Boolean wait, - @RpcParameter(name = "packagename", description = "name of package. If used, requires classname to be useful") @RpcOptional String packagename, - @RpcParameter(name = "classname", description = "name of class. If used, requires packagename to be useful") @RpcOptional String classname) - throws Exception { - final Intent intent = buildIntent(action, uri, type, extras, packagename, classname, null); - doStartActivity(intent, wait); - } - - @Rpc(description = "Send a broadcast.") - public void sendBroadcast( - @RpcParameter(name = "action") String action, - @RpcParameter(name = "uri") @RpcOptional String uri, - @RpcParameter(name = "type", description = "MIME type/subtype of the URI") @RpcOptional String type, - @RpcParameter(name = "extras", description = "a Map of extras to add to the Intent") @RpcOptional JSONObject extras, - @RpcParameter(name = "packagename", description = "name of package. If used, requires classname to be useful") @RpcOptional String packagename, - @RpcParameter(name = "classname", description = "name of class. If used, requires packagename to be useful") @RpcOptional String classname) - throws JSONException { - final Intent intent = buildIntent(action, uri, type, extras, packagename, classname, null); - try { - mService.sendBroadcast(intent); - } catch (Exception e) { - LogUtil.e("Failed to broadcast intent.", e); - } - } - - @Rpc(description = "Create an Intent.", returns = "An object representing an Intent") - public Intent makeIntent( - @RpcParameter(name = "action") String action, - @RpcParameter(name = "uri") @RpcOptional String uri, - @RpcParameter(name = "type", description = "MIME type/subtype of the URI") @RpcOptional String type, - @RpcParameter(name = "extras", description = "a Map of extras to add to the Intent") @RpcOptional JSONObject extras, - @RpcParameter(name = "categories", description = "a List of categories to add to the Intent") @RpcOptional JSONArray categories, - @RpcParameter(name = "packagename", description = "name of package. If used, requires classname to be useful") @RpcOptional String packagename, - @RpcParameter(name = "classname", description = "name of class. If used, requires packagename to be useful") @RpcOptional String classname, - @RpcParameter(name = "flags", description = "Intent flags") @RpcOptional Integer flags) - throws JSONException { - Intent intent = buildIntent(action, uri, type, extras, packagename, classname, categories); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - if (flags != null) { - intent.setFlags(flags); - } - return intent; - } - - @Rpc(description = "Start Activity using Intent") - public void startActivityIntent( - @RpcParameter(name = "intent", description = "Intent in the format as returned from makeIntent") Intent intent, - @RpcParameter(name = "wait", description = "block until the user exits the started activity") @RpcOptional Boolean wait) - throws Exception { - doStartActivity(intent, wait); - } - - @Rpc(description = "Send Broadcast Intent") - public void sendBroadcastIntent( - @RpcParameter(name = "intent", description = "Intent in the format as returned from makeIntent") Intent intent) - throws Exception { - mService.sendBroadcast(intent); - } - - @Rpc(description = "Vibrates the phone or a specified duration in milliseconds.") - public void vibrate( - @RpcParameter(name = "duration", description = "duration in milliseconds") @RpcDefault("300") Integer duration) { - mVibrator.vibrate(duration); - } - - @Rpc(description = "Displays a Toast notification.") - public void makeToast(@RpcParameter(name = "message") final String message, - @RpcParameter(name = "length") @RpcDefault("0") final Integer length, - @RpcParameter(name = "isHtml") @RpcDefault("false") final Boolean isHtml) { - mHandler.post(() -> { - if(isHtml) - Toast.makeText(mService, HtmlUtil.textToHtml(message), length).show(); - else - Toast.makeText(mService, message, length).show(); - }); - } - - public void makeToast(final String message,final Integer length) { - makeToast(message,length,false); - } - - public void makeToast(final String message) { - makeToast(message,0,false); - } - - /* 乘着船:过时删除 - private String getInputFromAlertDialog(final String title, final String message, - final boolean password) { - final FutureActivityTask task = new FutureActivityTask() { - @Override - public void onCreate() { - super.onCreate(); - final EditText input = new EditText(getActivity()); - if (password) { - input.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD); - input.setTransformationMethod(new PasswordTransformationMethod()); - } - AlertDialog.Builder alert = new AlertDialog.Builder(getActivity()); - alert.setTitle(title); - alert.setMessage(message); - alert.setView(input); - alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int whichButton) { - dialog.dismiss(); - setResult(input.getText().toString()); - finish(); - } - }); - alert.setOnCancelListener(new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - dialog.dismiss(); - setResult(null); - finish(); - } - }); - alert.show(); - } - }; - mTaskQueue.execute(task); - - try { - return task.getResult(); - } catch (Exception e) { - LogUtil.e("Failed to display dialog.", e); - throw new RuntimeException(e); - } - } - - @Rpc(description = "Queries the user for a text input.") - @RpcDeprecated(value = "dialogGetInput", release = "r3") - public String getInput( - @RpcParameter(name = "title", description = "title of the input box") @RpcDefault("SL4A Input") final String title, - @RpcParameter(name = "message", description = "message to display above the input box") @RpcDefault("Please enter value:") final String message) { - return getInputFromAlertDialog(title, message, false); - } - - @Rpc(description = "Queries the user for a password.") - @RpcDeprecated(value = "dialogGetPassword", release = "r3") - public String getPassword( - @RpcParameter(name = "title", description = "title of the input box") @RpcDefault("SL4A Password Input") final String title, - @RpcParameter(name = "message", description = "message to display above the input box") @RpcDefault("Please enter password:") final String message) { - return getInputFromAlertDialog(title, message, true); - }*/ - - private static int NOTIFICATION_ID = 0x20002;//通知栏消息id - - @SuppressLint("UnspecifiedImmutableFlag") - @Rpc(description = "Displays a notification that will be canceled when the user clicks on it.") - public void notify( - @RpcParameter(name = "title", description = "title") final String title, - @RpcParameter(name = "message") final String message, - @RpcParameter(name = "uri") @RpcOptional final String uri, - @RpcParameter(name = "arg") @RpcOptional final String arg) { - // This contentIntent is a noop. - Intent intent; - PendingIntent contentIntent = null; - if (uri!=null) { - //android.util.Log.d("AndroidFacade", "attachmentUri:"+attachmentUri); - if (uri.startsWith("http:") || uri.startsWith("https:")) { - intent = SPFUtils.getLinkAsIntent(context, uri); - } else { - if (uri.endsWith(".py")) { - try{ - intent = new Intent (context,NotificationClickReceiver.class); - intent.putExtra("path",uri); - intent.putExtra("arg",arg); - contentIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); - } catch (Exception e) { - intent = new Intent(); - intent.setClassName(mService.getPackageName(), "org.qpython.qpylib.MPyApi"); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.setAction("org.qpython.qpylib.action.MPyApi"); - Bundle mBundle = new Bundle(); - mBundle.putString("app", SPFUtils.getCode(mService.getApplicationContext())); - mBundle.putString("act", "onPyApi"); - mBundle.putString("flag", "api"); - mBundle.putString("param", "fileapi"); - mBundle.putString("pyfile", uri); - mBundle.putString("pyarg", arg); - intent.putExtras(mBundle); - } - } else { - intent = new Intent(); - intent.setClassName(context.getPackageName(), uri); - }} - } else { - intent = new Intent(); - } - if(contentIntent == null) - contentIntent = PendingIntent.getActivity(mService, NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); - Notification notification = SPFUtils.getNotification(context, title, message, contentIntent, - SPFUtils.getDrawableId(mService, "img_home_logo"), null); - - // Get a unique notification id from the application. - mNotificationManager.notify(NotificationIdFactory.create(), notification); - } - - @Rpc(description = "Returns the status of network connection.") - public boolean getNetworkStatus() { - return SPFUtils.netCheckin(context); - } - - - @Rpc(description = "Returns the intent that launched the script.") - public Object getIntent() { - return mIntent; - } - - @Rpc(description = "Launches an activity that sends an e-mail message to a given recipient.") - public void sendEmail( - @RpcParameter(name = "to", description = "A comma separated list of recipients.") final String to, - @RpcParameter(name = "subject") final String subject, - @RpcParameter(name = "body") final String body, - @RpcParameter(name = "attachmentUri") @RpcOptional final String attachmentUri) throws Exception { - final Intent intent = new Intent(android.content.Intent.ACTION_SEND); - intent.setType("plain/text"); - intent.putExtra(android.content.Intent.EXTRA_EMAIL, to.split(",")); - intent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject); - intent.putExtra(android.content.Intent.EXTRA_TEXT, body); - if (attachmentUri != null) { - intent.putExtra(android.content.Intent.EXTRA_STREAM, Uri.parse(attachmentUri)); - } - startActivity(intent); - } - - @Rpc(description = "Returns package version code.") - public int getPackageVersionCode(@RpcParameter(name = "packageName") final String packageName) { - int result = -1; - PackageInfo pInfo = null; - try { - pInfo = - mService.getPackageManager().getPackageInfo(packageName, PackageManager.GET_META_DATA); - } catch (NameNotFoundException e) { - pInfo = null; - } - if (pInfo != null) { - result = pInfo.versionCode; - } - return result; - } - - @Rpc(description = "Returns package version name.") - public String getPackageVersion(@RpcParameter(name = "packageName") final String packageName) { - PackageInfo packageInfo = null; - try { - packageInfo = - mService.getPackageManager().getPackageInfo(packageName, PackageManager.GET_META_DATA); - } catch (NameNotFoundException e) { - return null; - } - if (packageInfo != null) { - return packageInfo.versionName; - } - return null; - } - - /*@Rpc(description = "Checks if version of QPython SL4A is greater than or equal to the specified version.") - public boolean requiredVersion(@RpcParameter(name = "requiredVersion") final Integer version) { - boolean result = false; - int packageVersion = getPackageVersionCode("com.googlecode.android_scripting"); - if (version > -1) { - result = (packageVersion >= version); - } - return result; - }*/ - - @Rpc(description = "Writes message to logcat.") - public void log(@RpcParameter(name = "message") String message) { - android.util.Log.v("QSL4A", message); - } - - /** - * - * Map returned: - * - *
-   *   TZ = Timezone
-   *     id = Timezone ID
-   *     display = Timezone display name
-   *     offset = Offset from UTC (in ms)
-   *   SDK = SDK Version
-   *   download = default download path
-   *   appcache = Location of application cache
-   *   sdcard = Space on sdcard
-   *     availblocks = Available blocks
-   *     blockcount = Total Blocks
-   *     blocksize = size of block.
-   * 
- */ - @SuppressLint("SdCardPath") - @Rpc(description = "A map of various useful environment details") - public Map environment() { - Map result = new HashMap(); - Map zone = new HashMap(); - Map space = new HashMap(); - TimeZone tz = TimeZone.getDefault(); - zone.put("id", tz.getID()); - zone.put("display", tz.getDisplayName()); - zone.put("offset", tz.getOffset((new Date()).getTime())); - result.put("TZ", zone); - result.put("SDK", android.os.Build.VERSION.SDK); - result.put("download", FileUtils.getExternalDownload().getAbsolutePath()); - result.put("appcache", mService.getCacheDir().getAbsolutePath()); - try { - StatFs fs = new StatFs("/sdcard"); - space.put("availblocks", fs.getAvailableBlocks()); - space.put("blocksize", fs.getBlockSize()); - space.put("blockcount", fs.getBlockCount()); - } catch (Exception e) { - space.put("exception", e.toString()); - } - result.put("sdcard", space); - return result; - } - - @Rpc(description = "Get list of constants (static final fields) for a class") - public Bundle getConstants( - @RpcParameter(name = "classname", description = "Class to get constants from") String classname) - throws Exception { - Bundle result = new Bundle(); - int flags = Modifier.FINAL | Modifier.PUBLIC | Modifier.STATIC; - Class clazz = Class.forName(classname); - for (Field field : clazz.getFields()) { - if ((field.getModifiers() & flags) == flags) { - Class type = field.getType(); - String name = field.getName(); - if (type == int.class) { - result.putInt(name, field.getInt(null)); - } else if (type == long.class) { - result.putLong(name, field.getLong(null)); - } else if (type == double.class) { - result.putDouble(name, field.getDouble(null)); - } else if (type == char.class) { - result.putChar(name, field.getChar(null)); - } else if (type instanceof Object) { - result.putString(name, field.get(null).toString()); - } - } - } - return result; - } - - public static class NotificationClickReceiver extends BroadcastReceiver { - public NotificationClickReceiver(){} - - @Override - public void onReceive(Context context, Intent intent) { - Message msg = new Message(); - msg.obj = new String[]{ - intent.getStringExtra("path"), - intent.getStringExtra("arg") - }; - handler.sendMessage(msg); - } - } -} +/* + * Copyright (C) 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.qpython.qsl4a.qsl4a.facade; + +import android.annotation.SuppressLint; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.StatFs; +import android.os.Vibrator; +import android.text.ClipboardManager; +import android.util.Base64; +import android.widget.Toast; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.qpython.qsl4a.QSL4APP; +import org.qpython.qsl4a.R; +import org.qpython.qsl4a.qsl4a.LogUtil; +import org.qpython.qsl4a.qsl4a.NotificationIdFactory; +import org.qpython.qsl4a.qsl4a.future.FutureActivityTask; +import org.qpython.qsl4a.qsl4a.future.FutureActivityTaskExecutor; +import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; +import org.qpython.qsl4a.qsl4a.rpc.Rpc; +import org.qpython.qsl4a.qsl4a.rpc.RpcDefault; +import org.qpython.qsl4a.qsl4a.rpc.RpcOptional; +import org.qpython.qsl4a.qsl4a.rpc.RpcParameter; +import org.qpython.qsl4a.qsl4a.util.FileUtils; +import org.qpython.qsl4a.qsl4a.util.HtmlUtil; +import org.qpython.qsl4a.qsl4a.util.SPFUtils; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.net.URI; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.TimeZone; + +/** + * Some general purpose Android routines.
+ *

Intents

Intents are returned as a map, in the following form:
+ *
    + *
  • action - action. + *
  • data - url + *
  • type - mime type + *
  • packagename - name of package. If used, requires classname to be useful (optional) + *
  • classname - name of class. If used, requires packagename to be useful (optional) + *
  • categories - list of categories + *
  • extras - map of extras + *
  • flags - integer flags. + *
+ *
+ * An intent can be built using the {@see #makeIntent} call, but can also be constructed exterally. + * + */ +@SuppressWarnings("deprecation") +public class AndroidFacade extends RpcReceiver { + /** + * An instance of this interface is passed to the facade. From this object, the resource IDs can + * be obtained. + */ + public interface Resources { + //int getLogo48(); + } + + public final Service mService; + public final Handler mHandler; + private final Intent mIntent; + public final FutureActivityTaskExecutor mTaskQueue; + + private final Vibrator mVibrator; + private final NotificationManager mNotificationManager; + + private final Resources mResources; + private ClipboardManager mClipboard = null; + private static int deamonCount = 0; + public final Context context; + public final String qpyProvider; + public static Handler handler; + public final String SCOPE_STORAGE_PATH; + + public final int intentFlags = Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION ; + + @Override + public void shutdown() { + } + + public Service getmService() { + return this.mService; + } + + public AndroidFacade(FacadeManager manager) { + super(manager); + mService = manager.getService(); + mIntent = manager.getIntent(); + // River Modify + QSL4APP application = ((QSL4APP) mService.getApplication()); + mTaskQueue = application.getTaskExecutor(); + mHandler = new Handler(mService.getMainLooper()); + mVibrator = (Vibrator) mService.getSystemService(Context.VIBRATOR_SERVICE); + mNotificationManager = + (NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE); + mResources = manager.getAndroidFacadeResources(); + //乘着船 修改 + context = mService.getApplicationContext(); + qpyProvider = context.getPackageName() + ".provider"; + SCOPE_STORAGE_PATH = context.getExternalFilesDir("").getParent(); + } + + ClipboardManager getClipboardManager() { + Object clipboard; + if (mClipboard == null) { + try { + clipboard = mService.getSystemService(Context.CLIPBOARD_SERVICE); + } catch (Exception e) { + Looper.prepare(); // Clipboard manager won't work without this on higher SDK levels... + clipboard = mService.getSystemService(Context.CLIPBOARD_SERVICE); + } + mClipboard = (ClipboardManager) clipboard; + if (mClipboard == null) { + LogUtil.w("Clipboard managed not accessible."); + } + } + return mClipboard; + } + + /** + * Creates a new AndroidFacade that simplifies the interface to various Android APIs. + */ + + @Rpc(description = "Put text in the clipboard.") + public void setClipboard(@RpcParameter(name = "text") String text) { + getClipboardManager().setText(text); + } + + @Rpc(description = "Read text from the clipboard.", + returns = "The text in the clipboard.") + public String getClipboard() { + CharSequence text = getClipboardManager().getText(); + return text == null ? null : text.toString(); + } + + Intent startActivityForResult(final Intent intent) { + FutureActivityTask task = new FutureActivityTask() { + @Override + public void onCreate() { + super.onCreate(); + try { + startActivityForResult(intent, 1024); + } catch (Exception e) { + intent.putExtra("EXCEPTION", e.toString()); + setResult(intent); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + setResult(data); + } + }; + mTaskQueue.execute(task); + + try { + return task.getResult(); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + task.finish(); + } + } + + Intent startActivityForResultCode(final Intent intent) { + FutureActivityTask task = new FutureActivityTask() { + @Override + public void onCreate() { + super.onCreate(); + try { + int requestCode; + if (intent!=null){ + requestCode=intent.getIntExtra("REQUEST_CODE",1024); + } else { + requestCode=2048; + } + startActivityForResult(intent, requestCode); + } catch (Exception e) { + if(intent!=null) { + intent.putExtra("EXCEPTION", e.toString()); + setResult(intent); + } + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (data==null) + data=new Intent(); + data.putExtra("RESULT_CODE",resultCode); + setResult(data); + } + }; + mTaskQueue.execute(task); + + try { + return task.getResult(); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + task.finish(); + } + } + + // TODO(damonkohler): Pull this out into proper argument deserialization and support + // complex/nested types being passed in. + public static void putExtrasFromJsonObject(JSONObject extras, Intent intent) throws Exception { + JSONArray names = extras.names(); + if(names==null) + return; + for (int i = 0; i < names.length(); i++) { + String name = names.getString(i); + Object data = extras.get(name); + //if (data == null) { + // continue; + //} + if (data instanceof Integer) { + intent.putExtra(name, (Integer) data); + } + /* if (data instanceof Float) { + intent.putExtra(name, (Float) data); + }*/ + if (data instanceof Double) { + intent.putExtra(name, (Double) data); + } + if (data instanceof Long) { + intent.putExtra(name, (Long) data); + } + if (data instanceof String) { + putExtrasFromString(intent, name, data); + } + if (data instanceof Boolean) { + intent.putExtra(name, (Boolean) data); + } + // Nested JSONObject + if (data instanceof JSONObject) { + Bundle nestedBundle = new Bundle(); + intent.putExtra(name, nestedBundle); + putNestedJSONObject((JSONObject) data, nestedBundle); + } + // Nested JSONArray. Doesn't support mixed types in single array + if (data instanceof JSONArray) { + JSONArray jdata = (JSONArray) data; + // Empty array. No way to tell what type of data to pass on, so skipping + if (jdata.length() == 0) { + LogUtil.e("Empty array not supported in JSONObject, skipping"); + continue; + } + Object fdata = jdata.get(0); + // Integer + if (fdata instanceof Integer) { + Integer[] integerArrayData = new Integer[jdata.length()]; + for (int j = 0; j < jdata.length(); ++j) { + integerArrayData[j] = jdata.getInt(j); + } + intent.putExtra(name, integerArrayData); + } + // Double + if (fdata instanceof Double) { + Double[] doubleArrayData = new Double[jdata.length()]; + for (int j = 0; j < jdata.length(); ++j) { + doubleArrayData[j] = jdata.getDouble(j); + } + intent.putExtra(name, doubleArrayData); + } + // Long + if (fdata instanceof Long) { + Long[] longArrayData = new Long[jdata.length()]; + for (int j = 0; j < jdata.length(); ++j) { + longArrayData[j] = jdata.getLong(j); + } + intent.putExtra(name, longArrayData); + } + // String + if (fdata instanceof String) { + String[] stringArrayData = new String[jdata.length()]; + for (int j = 0; j < jdata.length(); ++j) { + stringArrayData[j] = jdata.getString(j); + } + intent.putExtra(name, stringArrayData); + } + // Boolean + if (fdata instanceof Boolean) { + Boolean[] booleanArrayData = new Boolean[jdata.length()]; + for (int j = 0; j < jdata.length(); ++j) { + booleanArrayData[j] = jdata.getBoolean(j); + } + intent.putExtra(name, booleanArrayData); + } + } + } + } + + private static void putExtrasFromString(Intent intent, String name, Object data) throws Exception { + String str = (String) data; + if(str.length()>5 && str.charAt(0) == 0 ){ + int p = str.indexOf(0,1); + if(p<0) + throw new Exception("No type of special String : " + str); + String type = str.substring(1,p); + str = str.substring(p+1); + if(type.equals("uri")) + intent.putExtra(name, Uri.parse(str)); + else if(type.equals("byte")) + intent.putExtra(name, Base64.decode(str, Base64.DEFAULT)); + else intent.putExtra(name, str); + } else intent.putExtra(name, str); + } + + private static void putStringToBundle(Bundle bundle, String name, Object data) throws Exception { + String str = (String) data; + if(str.length()>5 && str.charAt(0) == 0 ){ + int p = str.indexOf(0,1); + if(p<0) + throw new Exception("No type of special String : " + str); + String type = str.substring(1,p); + str = str.substring(p+1); + if(type.equals("byte")) + bundle.putByteArray(name, Base64.decode(str, Base64.DEFAULT)); + else bundle.putString(name, str); + } else bundle.putString(name, str); + } + + // Contributed by Emmanuel T + // Nested Array handling contributed by Sergey Zelenev + private static void putNestedJSONObject(JSONObject jsonObject, Bundle bundle) + throws Exception { + JSONArray names = jsonObject.names(); + if(names == null) + return; + for (int i = 0; i < names.length(); i++) { + String name = names.getString(i); + Object data = jsonObject.get(name); + /*if (data == null) { + continue; + }*/ + if (data instanceof Integer) { + bundle.putInt(name, (Integer) data); + } + /*if (data instanceof Float) { + bundle.putFloat(name, ((Float) data).floatValue()); + }*/ + if (data instanceof Double) { + bundle.putDouble(name, (Double) data); + } + if (data instanceof Long) { + bundle.putLong(name, (Long) data); + } + if (data instanceof String) { + putStringToBundle(bundle,name,data); + } + if (data instanceof Boolean) { + bundle.putBoolean(name, (Boolean) data); + } + // Nested JSONObject + if (data instanceof JSONObject) { + Bundle nestedBundle = new Bundle(); + bundle.putBundle(name, nestedBundle); + putNestedJSONObject((JSONObject) data, nestedBundle); + } + // Nested JSONArray. Doesn't support mixed types in single array + if (data instanceof JSONArray) { + // Empty array. No way to tell what type of data to pass on, so skipping + JSONArray jdata = (JSONArray) data; + if (jdata.length() == 0) { + LogUtil.e("Empty array not supported in nested JSONObject, skipping"); + continue; + } + Object fdata = jdata.get(0); + // Integer + if (fdata instanceof Integer) { + int[] integerArrayData = new int[jdata.length()]; + for (int j = 0; j < jdata.length(); ++j) { + integerArrayData[j] = jdata.getInt(j); + } + bundle.putIntArray(name, integerArrayData); + } + // Double + if (fdata instanceof Double) { + double[] doubleArrayData = new double[jdata.length()]; + for (int j = 0; j < jdata.length(); ++j) { + doubleArrayData[j] = jdata.getDouble(j); + } + bundle.putDoubleArray(name, doubleArrayData); + } + // Long + if (fdata instanceof Long) { + long[] longArrayData = new long[jdata.length()]; + for (int j = 0; j < jdata.length(); ++j) { + longArrayData[j] = jdata.getLong(j); + } + bundle.putLongArray(name, longArrayData); + } + // String + if (fdata instanceof String) { + String[] stringArrayData = new String[jdata.length()]; + for (int j = 0; j < jdata.length(); ++j) { + stringArrayData[j] = jdata.getString(j); + } + bundle.putStringArray(name, stringArrayData); + } + // Boolean + if (fdata instanceof Boolean) { + boolean[] booleanArrayData = new boolean[jdata.length()]; + for (int j = 0; j < jdata.length(); ++j) { + booleanArrayData[j] = jdata.getBoolean(j); + } + bundle.putBooleanArray(name, booleanArrayData); + } + } + } + } + + void startActivity(final Intent intent) throws Exception { + try { + intent.setFlags(intent.getFlags() | intentFlags); + context.startActivity(intent); + } catch (Exception e) { + throw new Exception("Failed to launch intent : "+ e); + } + } + + private Intent buildIntent(String action, String uri, String type, JSONObject extras, + String packagename, String classname, JSONArray categories) throws Exception { + Intent intent = new Intent(action); + intent.setDataAndType(uri != null ? Uri.parse(uri) : null, type); + if (packagename != null && classname != null) { + intent.setComponent(new ComponentName(packagename, classname)); + } + if (extras != null) { + putExtrasFromJsonObject(extras, intent); + } + if (categories != null) { + for (int i = 0; i < categories.length(); i++) { + intent.addCategory(categories.getString(i)); + } + } + return intent; + } + + // TODO(damonkohler): It's unnecessary to add the complication of choosing between startActivity + // and startActivityForResult. It's probably better to just always use the ForResult version. + // However, this makes the call always blocking. We'd need to add an extra boolean parameter to + // indicate if we should wait for a result. + @Rpc(description = "Starts an activity and returns the result.", + returns = "A Map representation of the result Intent.") + public Intent startActivityForResult( + @RpcParameter(name = "action") String action, + @RpcParameter(name = "uri") @RpcOptional String uri, + @RpcParameter(name = "type", description = "MIME type/subtype of the URI") @RpcOptional String type, + @RpcParameter(name = "extras", description = "a Map of extras to add to the Intent") @RpcOptional JSONObject extras, + @RpcParameter(name = "packagename", description = "name of package. If used, requires classname to be useful") @RpcOptional String packagename, + @RpcParameter(name = "classname", description = "name of class. If used, requires packagename to be useful") @RpcOptional String classname) + throws Exception { + final Intent intent = buildIntent(action, uri, type, extras, packagename, classname, null); + return startActivityForResult(intent); + } + + @Rpc(description = "Starts an activity and returns the result.", returns = "A Map representation of the result Intent.") + public Intent startActivityForResultIntent( + @RpcParameter(name = "intent", description = "Intent in the format as returned from makeIntent") Intent intent) { + return startActivityForResult(intent); + } + + public void doStartActivity(final Intent intent, Boolean wait,int description) throws Exception { + Exception[] except = new Exception[1]; + if (wait == null || !wait) { + startActivity(intent); + } else { + FutureActivityTask task = new FutureActivityTask() { + private boolean mSecondResume = false; + + @Override + public void onCreate() { + super.onCreate(); + if(deamonCount==0) + Toast.makeText(context,R.string.click_daemon,Toast.LENGTH_SHORT).show(); + setTaskDescription(context.getString(description)+context.getString(R.string.daemon)+(++deamonCount)); + intent.setFlags(intent.getFlags() | intentFlags); + try { + startActivity(intent); + } catch (Exception exception) { + except[0] = exception; + } + } + + @Override + public void onResume() { + if (mSecondResume) { + finish(); + } + mSecondResume = true; + } + + @Override + public void onDestroy() { + setResult(null); + } + + }; + mTaskQueue.execute(task); + + try { + task.getResult(); + } catch (Exception e) { + throw new RuntimeException(e); + } + if(except[0]!=null) { + throw except[0]; + } + } + } + + /** + * packagename and classname, if provided, are used in a 'setComponent' call. + */ + @Rpc(description = "Starts an activity.") + public void startActivity( + @RpcParameter(name = "action") String action, + @RpcParameter(name = "uri") @RpcOptional String uri, + @RpcParameter(name = "type", description = "MIME type/subtype of the URI") @RpcOptional String type, + @RpcParameter(name = "extras", description = "a Map of extras to add to the Intent") @RpcOptional JSONObject extras, + @RpcParameter(name = "wait", description = "block until the user exits the started activity") @RpcOptional Boolean wait, + @RpcParameter(name = "packagename", description = "name of package. If used, requires classname to be useful") @RpcOptional String packagename, + @RpcParameter(name = "classname", description = "name of class. If used, requires packagename to be useful") @RpcOptional String classname) + throws Exception { + final Intent intent = buildIntent(action, uri, type, extras, packagename, classname, null); + doStartActivity(intent, wait, R.string.normal); + } + + @Rpc(description = "Send a broadcast.") + public void sendBroadcast( + @RpcParameter(name = "action") String action, + @RpcParameter(name = "uri") @RpcOptional String uri, + @RpcParameter(name = "type", description = "MIME type/subtype of the URI") @RpcOptional String type, + @RpcParameter(name = "extras", description = "a Map of extras to add to the Intent") @RpcOptional JSONObject extras, + @RpcParameter(name = "packagename", description = "name of package. If used, requires classname to be useful") @RpcOptional String packagename, + @RpcParameter(name = "classname", description = "name of class. If used, requires packagename to be useful") @RpcOptional String classname) + throws Exception { + final Intent intent = buildIntent(action, uri, type, extras, packagename, classname, null); + try { + mService.sendBroadcast(intent); + } catch (Exception e) { + LogUtil.e("Failed to broadcast intent.", e); + } + } + + @Rpc(description = "Create an Intent.", returns = "An object representing an Intent") + public Intent makeIntent( + @RpcParameter(name = "action") String action, + @RpcParameter(name = "uri") @RpcOptional String uri, + @RpcParameter(name = "type", description = "MIME type/subtype of the URI") @RpcOptional String type, + @RpcParameter(name = "extras", description = "a Map of extras to add to the Intent") @RpcOptional JSONObject extras, + @RpcParameter(name = "categories", description = "a List of categories to add to the Intent") @RpcOptional JSONArray categories, + @RpcParameter(name = "packagename", description = "name of package. If used, requires classname to be useful") @RpcOptional String packagename, + @RpcParameter(name = "classname", description = "name of class. If used, requires packagename to be useful") @RpcOptional String classname, + @RpcParameter(name = "flags", description = "Intent flags") @RpcOptional Integer flags) + throws Exception { + Intent intent = buildIntent(action, uri, type, extras, packagename, classname, categories); + if (flags == null) { + flags = intentFlags; + } + intent.setFlags(flags); + return intent; + } + + @Rpc(description = "Start Activity using Intent") + public void startActivityIntent( + @RpcParameter(name = "intent", description = "Intent in the format as returned from makeIntent") Intent intent, + @RpcParameter(name = "wait", description = "block until the user exits the started activity") @RpcOptional Boolean wait) + throws Exception { + doStartActivity(intent, wait, R.string.normal); + } + + @Rpc(description = "Send Broadcast Intent") + public void sendBroadcastIntent( + @RpcParameter(name = "intent", description = "Intent in the format as returned from makeIntent") Intent intent) + throws Exception { + mService.sendBroadcast(intent); + } + + @Rpc(description = "Vibrates the phone or a specified duration in milliseconds.") + public void vibrate( + @RpcParameter(name = "duration", description = "duration in milliseconds") @RpcDefault("300") Integer duration) { + mVibrator.vibrate(duration); + } + + @Rpc(description = "Displays a Toast notification.") + public void makeToast(@RpcParameter(name = "message") final String message, + @RpcParameter(name = "length") @RpcDefault("0") final Integer length, + @RpcParameter(name = "isHtml") @RpcDefault("false") final Boolean isHtml) { + mHandler.post(() -> { + if(isHtml) + Toast.makeText(mService, HtmlUtil.textToHtml(message), length).show(); + else + Toast.makeText(mService, message, length).show(); + }); + } + + public void makeToast(final String message,final Integer length) { + makeToast(message,length,false); + } + + public void makeToast(final String message) { + makeToast(message,0,false); + } + + /* 乘着船:过时删除 + private String getInputFromAlertDialog(final String title, final String message, + final boolean password) { + final FutureActivityTask task = new FutureActivityTask() { + @Override + public void onCreate() { + super.onCreate(); + final EditText input = new EditText(getActivity()); + if (password) { + input.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD); + input.setTransformationMethod(new PasswordTransformationMethod()); + } + AlertDialog.Builder alert = new AlertDialog.Builder(getActivity()); + alert.setTitle(title); + alert.setMessage(message); + alert.setView(input); + alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int whichButton) { + dialog.dismiss(); + setResult(input.getText().toString()); + finish(); + } + }); + alert.setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + dialog.dismiss(); + setResult(null); + finish(); + } + }); + alert.show(); + } + }; + mTaskQueue.execute(task); + + try { + return task.getResult(); + } catch (Exception e) { + LogUtil.e("Failed to display dialog.", e); + throw new RuntimeException(e); + } + } + + @Rpc(description = "Queries the user for a text input.") + @RpcDeprecated(value = "dialogGetInput", release = "r3") + public String getInput( + @RpcParameter(name = "title", description = "title of the input box") @RpcDefault("SL4A Input") final String title, + @RpcParameter(name = "message", description = "message to display above the input box") @RpcDefault("Please enter value:") final String message) { + return getInputFromAlertDialog(title, message, false); + } + + @Rpc(description = "Queries the user for a password.") + @RpcDeprecated(value = "dialogGetPassword", release = "r3") + public String getPassword( + @RpcParameter(name = "title", description = "title of the input box") @RpcDefault("SL4A Password Input") final String title, + @RpcParameter(name = "message", description = "message to display above the input box") @RpcDefault("Please enter password:") final String message) { + return getInputFromAlertDialog(title, message, true); + }*/ + + private static int NOTIFICATION_ID = 0x20002;//通知栏消息id + + @SuppressLint("UnspecifiedImmutableFlag") + @Rpc(description = "Displays a notification that will be canceled when the user clicks on it.") + public void notify( + @RpcParameter(name = "title", description = "title") final String title, + @RpcParameter(name = "message") final String message, + @RpcParameter(name = "uri") @RpcOptional final String uri, + @RpcParameter(name = "arg") @RpcOptional final String arg) { + // This contentIntent is a noop. + Intent intent; + PendingIntent contentIntent = null; + if (uri!=null) { + //android.util.Log.d("AndroidFacade", "attachmentUri:"+attachmentUri); + if (uri.startsWith("http:") || uri.startsWith("https:")) { + intent = SPFUtils.getLinkAsIntent(context, uri); + } else { + if (uri.endsWith(".py")) { + try{ + intent = new Intent (context,NotificationClickReceiver.class); + intent.putExtra("path",uri); + intent.putExtra("arg",arg); + contentIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); + } catch (Exception e) { + intent = new Intent(); + intent.setClassName(mService.getPackageName(), "org.qpython.qpylib.MPyApi"); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setAction("org.qpython.qpylib.action.MPyApi"); + Bundle mBundle = new Bundle(); + mBundle.putString("app", SPFUtils.getCode(mService.getApplicationContext())); + mBundle.putString("act", "onPyApi"); + mBundle.putString("flag", "api"); + mBundle.putString("param", "fileapi"); + mBundle.putString("pyfile", uri); + mBundle.putString("pyarg", arg); + intent.putExtras(mBundle); + } + } else { + intent = new Intent(); + intent.setClassName(context.getPackageName(), uri); + }} + } else { + intent = new Intent(); + } + if(contentIntent == null) + contentIntent = PendingIntent.getActivity(mService, NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); + Notification notification = SPFUtils.getNotification(context, title, message, contentIntent, + SPFUtils.getDrawableId(mService, "img_home_logo"), null); + + // Get a unique notification id from the application. + mNotificationManager.notify(NotificationIdFactory.create(), notification); + } + + @Rpc(description = "Returns the status of network connection.") + public boolean getNetworkStatus() { + return SPFUtils.netCheckin(context); + } + + + @Rpc(description = "Returns the intent that launched the script.") + public Object getIntent() { + return mIntent; + } + + @Rpc(description = "Launches an activity that sends an e-mail message to a given recipient.") + public void sendEmail( + @RpcParameter(name = "to", description = "A comma separated list of recipients.") final String to, + @RpcParameter(name = "subject") final String subject, + @RpcParameter(name = "body") final String body, + @RpcParameter(name = "attachmentUri") @RpcOptional final String attachmentUri) throws Exception { + final Intent intent = new Intent(android.content.Intent.ACTION_SEND); + intent.setType("plain/text"); + intent.putExtra(android.content.Intent.EXTRA_EMAIL, to.split(",")); + intent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject); + intent.putExtra(android.content.Intent.EXTRA_TEXT, body); + if (attachmentUri != null) { + intent.putExtra(android.content.Intent.EXTRA_STREAM, Uri.parse(attachmentUri)); + } + startActivity(intent); + } + + @Rpc(description = "Returns package version code.") + public int getPackageVersionCode(@RpcParameter(name = "packageName") final String packageName) { + int result = -1; + PackageInfo pInfo = null; + try { + pInfo = + mService.getPackageManager().getPackageInfo(packageName, PackageManager.GET_META_DATA); + } catch (NameNotFoundException e) { + pInfo = null; + } + if (pInfo != null) { + result = pInfo.versionCode; + } + return result; + } + + @Rpc(description = "Returns package version name.") + public String getPackageVersion(@RpcParameter(name = "packageName") final String packageName) { + PackageInfo packageInfo = null; + try { + packageInfo = + mService.getPackageManager().getPackageInfo(packageName, PackageManager.GET_META_DATA); + } catch (NameNotFoundException e) { + return null; + } + if (packageInfo != null) { + return packageInfo.versionName; + } + return null; + } + + /*@Rpc(description = "Checks if version of QPython SL4A is greater than or equal to the specified version.") + public boolean requiredVersion(@RpcParameter(name = "requiredVersion") final Integer version) { + boolean result = false; + int packageVersion = getPackageVersionCode("com.googlecode.android_scripting"); + if (version > -1) { + result = (packageVersion >= version); + } + return result; + }*/ + + @Rpc(description = "Writes message to logcat.") + public void log(@RpcParameter(name = "message") String message) { + android.util.Log.v("QSL4A", message); + } + + /** + * + * Map returned: + * + *
+   *   TZ = Timezone
+   *     id = Timezone ID
+   *     display = Timezone display name
+   *     offset = Offset from UTC (in ms)
+   *   SDK = SDK Version
+   *   download = default download path
+   *   appcache = Location of application cache
+   *   sdcard = Space on sdcard
+   *     availblocks = Available blocks
+   *     blockcount = Total Blocks
+   *     blocksize = size of block.
+   * 
+ */ + @SuppressLint("SdCardPath") + @Rpc(description = "A map of various useful environment details") + public Map environment() { + Map result = new HashMap(); + Map zone = new HashMap(); + Map space = new HashMap(); + TimeZone tz = TimeZone.getDefault(); + zone.put("id", tz.getID()); + zone.put("display", tz.getDisplayName()); + zone.put("offset", tz.getOffset((new Date()).getTime())); + result.put("TZ", zone); + result.put("SDK", android.os.Build.VERSION.SDK); + result.put("download", FileUtils.getExternalDownload().getAbsolutePath()); + result.put("appcache", mService.getCacheDir().getAbsolutePath()); + try { + StatFs fs = new StatFs("/sdcard"); + space.put("availblocks", fs.getAvailableBlocks()); + space.put("blocksize", fs.getBlockSize()); + space.put("blockcount", fs.getBlockCount()); + } catch (Exception e) { + space.put("exception", e.toString()); + } + result.put("sdcard", space); + return result; + } + + @Rpc(description = "Get list of constants (static final fields) for a class") + public Bundle getConstants( + @RpcParameter(name = "classname", description = "Class to get constants from") String classname) + throws Exception { + Bundle result = new Bundle(); + int flags = Modifier.FINAL | Modifier.PUBLIC | Modifier.STATIC; + Class clazz = Class.forName(classname); + for (Field field : clazz.getFields()) { + if ((field.getModifiers() & flags) == flags) { + Class type = field.getType(); + String name = field.getName(); + if (type == int.class) { + result.putInt(name, field.getInt(null)); + } else if (type == long.class) { + result.putLong(name, field.getLong(null)); + } else if (type == double.class) { + result.putDouble(name, field.getDouble(null)); + } else if (type == char.class) { + result.putChar(name, field.getChar(null)); + } else if (type instanceof Object) { + result.putString(name, field.get(null).toString()); + } + } + } + return result; + } + + public static class NotificationClickReceiver extends BroadcastReceiver { + public NotificationClickReceiver(){} + + @Override + public void onReceive(Context context, Intent intent) { + Message msg = new Message(); + msg.obj = new String[]{ + intent.getStringExtra("path"), + intent.getStringExtra("arg") + }; + handler.sendMessage(msg); + } + } +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/ApplicationManagerFacade.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/ApplicationManagerFacade.java index eb17fcc..c829d91 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/ApplicationManagerFacade.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/ApplicationManagerFacade.java @@ -1,166 +1,217 @@ -package org.qpython.qsl4a.qsl4a.facade; - -import android.app.ActivityManager; -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.os.Build; - - -import org.json.JSONObject; -import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; -import org.qpython.qsl4a.qsl4a.rpc.Rpc; -import org.qpython.qsl4a.qsl4a.rpc.RpcDefault; -import org.qpython.qsl4a.qsl4a.rpc.RpcOptional; -import org.qpython.qsl4a.qsl4a.rpc.RpcParameter; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Facade for managing Applications. - * - */ -public class ApplicationManagerFacade extends RpcReceiver { - - private final AndroidFacade mAndroidFacade; - private final ActivityManager mActivityManager; - private final PackageManager mPackageManager; - private final Context context; - - public ApplicationManagerFacade(FacadeManager manager) { - super(manager); - Service service = manager.getService(); - mAndroidFacade = manager.getReceiver(AndroidFacade.class); - mActivityManager = (ActivityManager) service.getSystemService(Context.ACTIVITY_SERVICE); - mPackageManager = service.getPackageManager(); - context = mAndroidFacade.context; - } - - @Rpc(description = "Returns a list of all launchable packages with class name and application name .") - public Map getLaunchablePackages( - @RpcParameter(name = "need class name") @RpcDefault("false") Boolean needClassName) { - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.addCategory(Intent.CATEGORY_LAUNCHER); - List resolveInfos = mPackageManager.queryIntentActivities(intent, 0); - Map applications = new HashMap(); - if (needClassName){ - for (ResolveInfo info : resolveInfos) { - applications.put(info.activityInfo.packageName, info.activityInfo.name+"|"+info.loadLabel(mPackageManager).toString()); - }} - else { - for (ResolveInfo info : resolveInfos) { - applications.put(info.activityInfo.packageName, info.loadLabel(mPackageManager).toString()); - }} - return applications; - } - - @Rpc(description = "get Application Info") - public JSONObject getApplicationInfo( - @RpcParameter(name = "package name") @RpcOptional String packageName) throws Exception { - if(packageName == null) - packageName = context.getPackageName(); - ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES); - JSONObject result = new JSONObject(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) - result.put("compileSdkVersion",appInfo.compileSdkVersion); - result.put("targetSdkVersion",appInfo.targetSdkVersion); - result.put("minSdkVersion",appInfo.minSdkVersion); - result.put("className",appInfo.className); - result.put("uid",appInfo.uid); - result.put("dataDir",appInfo.dataDir); - result.put("nativeLibraryDir",appInfo.nativeLibraryDir); - result.put("sourceDir",appInfo.sourceDir); - result.put("publicSourceDir",appInfo.publicSourceDir); - result.put("deviceProtectedDataDir",appInfo.deviceProtectedDataDir); - result.put("label",appInfo.loadLabel(mPackageManager)); - return result; - } - - @Rpc(description = "Start activity with the given classname and/or packagename .") - public void launch(@RpcParameter(name = "classname") @RpcOptional final String classname, - @RpcParameter(name = "packagename") @RpcOptional String packagename, - @RpcParameter(name = "wait") @RpcDefault("true") final Boolean wait) - throws Exception { - Intent intent; - if (classname == null) { - intent=context.getPackageManager().getLaunchIntentForPackage(packagename); - } else { - intent = new Intent(Intent.ACTION_MAIN); - if (packagename == null) { - packagename = classname.substring(0, classname.lastIndexOf(".")); - } - intent.setClassName(packagename, classname); - } - mAndroidFacade.doStartActivity(intent,wait); - } - - @Rpc(description = "Returns a list of packages running activities or services.", returns = "List of packages running activities.") - public Set getRunningPackages() { - Set runningPackages = new HashSet<>(); - List appProcesses = - mActivityManager.getRunningAppProcesses(); - for (ActivityManager.RunningAppProcessInfo info : appProcesses) { - runningPackages.addAll(Arrays.asList(info.pkgList)); - } - List serviceProcesses = - mActivityManager.getRunningServices(Integer.MAX_VALUE); - for (ActivityManager.RunningServiceInfo info : serviceProcesses) { - runningPackages.add(info.service.getPackageName()); - } - return runningPackages; - } - - @Rpc(description = "get installed packages") - public Map getInstalledPackages( - @RpcParameter(name = "flag") @RpcDefault("4") final Integer flag - ) { - Map packages = new HashMap<>(); - List packageInfos = context.getPackageManager().getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES); - boolean allow; - for (PackageInfo info : packageInfos) { - switch (flag) { - case 5://所有应用 - allow = true; - break; - case 4://用户应用 - allow = ( info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM ) == 0; - break; - case 3://系统应用 - allow = ( info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM ) != 0; - break; - case 2://系统已更新应用 - allow = ( info.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP ) != 0; - break; - case 1://系统未更新应用 - allow = ( info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM ) != 0 && ( info.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP ) == 0; - break; - default: - allow = false; - } - if(allow) - packages.put(info.packageName, info.applicationInfo.loadLabel(mPackageManager).toString()); - } - return packages; - } - - @SuppressWarnings("deprecation") -@Rpc(description = "Force stops a package.") - public void forceStopPackage( - @RpcParameter(name = "packageName", description = "name of package") final String packageName) { - mActivityManager.restartPackage(packageName); - } - - @Override - public void shutdown() { - } -} +package org.qpython.qsl4a.qsl4a.facade; + +import android.app.ActivityManager; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.provider.Settings; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; + + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.qpython.qsl4a.R; +import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; +import org.qpython.qsl4a.qsl4a.rpc.Rpc; +import org.qpython.qsl4a.qsl4a.rpc.RpcDefault; +import org.qpython.qsl4a.qsl4a.rpc.RpcOptional; +import org.qpython.qsl4a.qsl4a.rpc.RpcParameter; +import org.qpython.qsl4a.qsl4a.util.PermissionUtil; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * Facade for managing Applications. + * + */ +public class ApplicationManagerFacade extends RpcReceiver { + + private final AndroidFacade mAndroidFacade; + private final ActivityManager mActivityManager; + private final PackageManager mPackageManager; + private final Context context; + + public ApplicationManagerFacade(FacadeManager manager) { + super(manager); + Service service = manager.getService(); + mAndroidFacade = manager.getReceiver(AndroidFacade.class); + mActivityManager = (ActivityManager) service.getSystemService(Context.ACTIVITY_SERVICE); + mPackageManager = service.getPackageManager(); + context = mAndroidFacade.context; + } + + @Rpc(description = "Returns a list of all launchable packages with class name and application name .") + public Map getLaunchablePackages( + @RpcParameter(name = "need class name") @RpcDefault("false") Boolean needClassName) { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_LAUNCHER); + List resolveInfos = mPackageManager.queryIntentActivities(intent, 0); + Map applications = new HashMap(); + if (needClassName){ + for (ResolveInfo info : resolveInfos) { + applications.put(info.activityInfo.packageName, info.activityInfo.name+"|"+info.loadLabel(mPackageManager).toString()); + }} + else { + for (ResolveInfo info : resolveInfos) { + applications.put(info.activityInfo.packageName, info.loadLabel(mPackageManager).toString()); + }} + return applications; + } + + @Rpc(description = "get Application Info") + public JSONObject getApplicationInfo( + @RpcParameter(name = "package name") @RpcOptional String packageName) throws Exception { + if(packageName == null) + packageName = context.getPackageName(); + ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES); + JSONObject result = new JSONObject(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + result.put("compileSdkVersion",appInfo.compileSdkVersion); + result.put("targetSdkVersion",appInfo.targetSdkVersion); + result.put("minSdkVersion",appInfo.minSdkVersion); + result.put("className",appInfo.className); + result.put("uid",appInfo.uid); + result.put("dataDir",appInfo.dataDir); + result.put("nativeLibraryDir",appInfo.nativeLibraryDir); + result.put("sourceDir",appInfo.sourceDir); + result.put("publicSourceDir",appInfo.publicSourceDir); + result.put("deviceProtectedDataDir",appInfo.deviceProtectedDataDir); + result.put("label",appInfo.loadLabel(mPackageManager)); + return result; + } + + @Rpc(description = "Start activity with the given classname and/or packagename .") + public void launch(@RpcParameter(name = "classname") @RpcOptional final String classname, + @RpcParameter(name = "packagename") @RpcOptional String packagename, + @RpcParameter(name = "wait") @RpcDefault("true") final Boolean wait) + throws Exception { + Intent intent; + if (classname == null) { + intent=context.getPackageManager().getLaunchIntentForPackage(packagename); + } else { + intent = new Intent(Intent.ACTION_MAIN); + if (packagename == null) { + packagename = classname.substring(0, classname.lastIndexOf(".")); + } + intent.setClassName(packagename, classname); + } + mAndroidFacade.doStartActivity(intent,wait, R.string.launch); + } + + @Rpc(description = "Returns a list of packages running activities or services.", returns = "List of packages running activities.") + public Set getRunningPackages() { + Set runningPackages = new HashSet<>(); + List appProcesses = + mActivityManager.getRunningAppProcesses(); + for (ActivityManager.RunningAppProcessInfo info : appProcesses) { + runningPackages.addAll(Arrays.asList(info.pkgList)); + } + List serviceProcesses = + mActivityManager.getRunningServices(Integer.MAX_VALUE); + for (ActivityManager.RunningServiceInfo info : serviceProcesses) { + runningPackages.add(info.service.getPackageName()); + } + return runningPackages; + } + + @Rpc(description = "get installed packages") + public Map getInstalledPackages( + @RpcParameter(name = "flag") @RpcDefault("4") final Integer flag + ) { + Map packages = new HashMap<>(); + List packageInfos = context.getPackageManager().getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES); + boolean allow; + for (PackageInfo info : packageInfos) { + switch (flag) { + case 5://所有应用 + allow = true; + break; + case 4://用户应用 + allow = ( info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM ) == 0; + break; + case 3://系统应用 + allow = ( info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM ) != 0; + break; + case 2://系统已更新应用 + allow = ( info.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP ) != 0; + break; + case 1://系统未更新应用 + allow = ( info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM ) != 0 && ( info.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP ) == 0; + break; + default: + allow = false; + } + if(allow) + packages.put(info.packageName, info.applicationInfo.loadLabel(mPackageManager).toString()); + } + return packages; + } + + @SuppressWarnings("deprecation") +@Rpc(description = "Force stops a package.") + public void forceStopPackage( + @RpcParameter(name = "packageName", description = "name of package") final String packageName) { + mActivityManager.restartPackage(packageName); + } + + @Rpc(description = "check all Permissions are granted or denied .") + public static JSONObject checkPermissions() throws Exception { + JSONArray granted,denied; + granted = new JSONArray(); + denied = new JSONArray(); + JSONObject json = new JSONObject(); + PackageManager pm = PermissionUtil.activity.getPackageManager(); + PackageInfo info; + info = pm.getPackageInfo(PermissionUtil.activity.getPackageName(), PackageManager.GET_PERMISSIONS); + for (String permission:info.requestedPermissions){ + if(ContextCompat.checkSelfPermission(PermissionUtil.activity, permission) == PackageManager.PERMISSION_GRANTED) + granted.put(permission); + else + denied.put(permission); + } + json.put("granted",granted); + json.put("denied",denied); + return json; + } + + @Rpc(description = "request Permissions") + public static void requestPermissions( + @RpcParameter(name = "permissions") @RpcOptional JSONArray permissions) throws Exception { + if(permissions == null) + permissions = checkPermissions().getJSONArray("denied"); + String[] permission = new String[permissions.length()]; + for(int i = 0; i < permission.length; i++) { + try { + permission[i] = (String) permissions.get(i); + if(permission[i].equals("android.permission.MANAGE_EXTERNAL_STORAGE")) { + PermissionUtil.requestAllFilesPermission(); + permission[i] = ""; + } + } catch (JSONException e) { + permission[i] = ""; + } + } + + ActivityCompat.requestPermissions(PermissionUtil.activity, permission, 100); + } + + @Override + public void shutdown() { + } +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/BatteryManagerFacade.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/BatteryManagerFacade.java index 0798787..ed20f6d 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/BatteryManagerFacade.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/BatteryManagerFacade.java @@ -1,219 +1,229 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a.facade; - -import android.app.Service; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.BatteryManager; -import android.os.Bundle; - -import org.qpython.qsl4a.qsl4a.LogUtil; -import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; -import org.qpython.qsl4a.qsl4a.rpc.Rpc; -import org.qpython.qsl4a.qsl4a.rpc.RpcMinSdk; -import org.qpython.qsl4a.qsl4a.rpc.RpcStartEvent; -import org.qpython.qsl4a.qsl4a.rpc.RpcStopEvent; - -import java.lang.reflect.Field; - -/** - * Exposes Batterymanager API. Note that in order to use any of the batteryGet* functions, you need - * to batteryStartMonitoring, and then wait for a "battery" event. Sleeping for a second will - * usually work just as well. - * - * @author Alexey Reznichenko (alexey.reznichenko@gmail.com) - * @author Robbie Matthews (rjmatthews62@gmail.com) - */ -public class BatteryManagerFacade extends RpcReceiver { - - private final Service mService; - private final EventFacade mEventFacade; - private final int mSdkVersion; - - private BatteryStateListener mReceiver; - - private volatile Bundle mBatteryData = null; - private volatile Integer mBatteryStatus = null; - private volatile Integer mBatteryHealth = null; - private volatile Integer mPlugType = null; - - private volatile Boolean mBatteryPresent = null; - private volatile Integer mBatteryLevel = null; - private volatile Integer mBatteryMaxLevel = null; - private volatile Integer mBatteryVoltage = null; - private volatile Integer mBatteryTemperature = null; - private volatile String mBatteryTechnology = null; - - public BatteryManagerFacade(FacadeManager manager) { - super(manager); - mService = manager.getService(); - mSdkVersion = manager.getSdkLevel(); - mEventFacade = manager.getReceiver(EventFacade.class); - mReceiver = null; - mBatteryData = null; - } - - private class BatteryStateListener extends BroadcastReceiver { - - private final EventFacade mmEventFacade; - - private BatteryStateListener(EventFacade facade) { - mmEventFacade = facade; - } - - @Override - public void onReceive(Context context, Intent intent) { - mBatteryStatus = intent.getIntExtra("status", 1); - mBatteryHealth = intent.getIntExtra("health", 1); - mPlugType = intent.getIntExtra("plugged", -1); - if (mSdkVersion >= 5) { - mBatteryPresent = - intent.getBooleanExtra(getBatteryManagerFieldValue("EXTRA_PRESENT"), false); - mBatteryLevel = intent.getIntExtra(getBatteryManagerFieldValue("EXTRA_LEVEL"), -1); - mBatteryMaxLevel = intent.getIntExtra(getBatteryManagerFieldValue("EXTRA_SCALE"), 0); - mBatteryVoltage = intent.getIntExtra(getBatteryManagerFieldValue("EXTRA_VOLTAGE"), -1); - mBatteryTemperature = - intent.getIntExtra(getBatteryManagerFieldValue("EXTRA_TEMPERATURE"), -1); - mBatteryTechnology = intent.getStringExtra(getBatteryManagerFieldValue("EXTRA_TECHNOLOGY")); - } - Bundle data = new Bundle(); - data.putInt("status", mBatteryStatus); - data.putInt("health", mBatteryHealth); - data.putInt("plugged", mPlugType); - if (mSdkVersion >= 5) { - data.putBoolean("battery_present", mBatteryPresent); - if (mBatteryMaxLevel == null || mBatteryMaxLevel == 100 || mBatteryMaxLevel == 0) { - data.putInt("level", mBatteryLevel); - } else { - data.putInt("level", (int) (mBatteryLevel * 100.0 / mBatteryMaxLevel)); - } - data.putInt("voltage", mBatteryVoltage); - data.putInt("temperature", mBatteryTemperature); - data.putString("technology", mBatteryTechnology); - } - mBatteryData = data; - mmEventFacade.postEvent("battery", mBatteryData.clone()); - } - } - - private String getBatteryManagerFieldValue(String name) { - try { - Field f = BatteryManager.class.getField(name); - return f.get(null).toString(); - } catch (Exception e) { - LogUtil.e(e); - } - return null; - } - - @Rpc(description = "Returns the most recently recorded battery data.") - public Bundle readBatteryData() { - return mBatteryData; - } - - /** - * throws "battery" events - */ - @Rpc(description = "Starts tracking battery state.") - @RpcStartEvent("battery") - public void batteryStartMonitoring() { - if (mReceiver == null) { - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_BATTERY_CHANGED); - mReceiver = new BatteryStateListener(mEventFacade); - mService.registerReceiver(mReceiver, filter); - } - } - - @Rpc(description = "Stops tracking battery state.") - @RpcStopEvent("battery") - public void batteryStopMonitoring() { - if (mReceiver != null) { - mService.unregisterReceiver(mReceiver); - mReceiver = null; - } - mBatteryData = null; - } - - @Override - public void shutdown() { - batteryStopMonitoring(); - } - - @Rpc(description = "Returns the most recently received battery status data:" + "\n1 - unknown;" - + "\n2 - charging;" + "\n3 - discharging;" + "\n4 - not charging;" + "\n5 - full;") - public Integer batteryGetStatus() { - return mBatteryStatus; - } - - @Rpc(description = "Returns the most recently received battery health data:" + "\n1 - unknown;" - + "\n2 - good;" + "\n3 - overheat;" + "\n4 - dead;" + "\n5 - over voltage;" - + "\n6 - unspecified failure;") - public Integer batteryGetHealth() { - return mBatteryHealth; - } - - /** Power source is an AC charger. */ - public static final int BATTERY_PLUGGED_AC = 1; - /** Power source is a USB port. */ - public static final int BATTERY_PLUGGED_USB = 2; - - @Rpc(description = "Returns the most recently received plug type data:" + "\n-1 - unknown" - + "\n0 - unplugged;" + "\n1 - power source is an AC charger" - + "\n2 - power source is a USB port") - public Integer batteryGetPlugType() { - return mPlugType; - } - - @Rpc(description = "Returns the most recently received battery presence data.") - @RpcMinSdk(5) - public Boolean batteryCheckPresent() { - return mBatteryPresent; - } - - @Rpc(description = "Returns the most recently received battery level (percentage).") - @RpcMinSdk(5) - public Integer batteryGetLevel() { - if (mBatteryMaxLevel == null || mBatteryMaxLevel == 100 || mBatteryMaxLevel == 0) { - return mBatteryLevel; - } else { - return (int) (mBatteryLevel * 100.0 / mBatteryMaxLevel); - } - } - - @Rpc(description = "Returns the most recently received battery voltage.") - @RpcMinSdk(5) - public Integer batteryGetVoltage() { - return mBatteryVoltage; - } - - @Rpc(description = "Returns the most recently received battery temperature.") - @RpcMinSdk(5) - public Integer batteryGetTemperature() { - return mBatteryTemperature; - } - - @Rpc(description = "Returns the most recently received battery technology data.") - @RpcMinSdk(5) - public String batteryGetTechnology() { - return mBatteryTechnology; - } - -} +/* + * Copyright (C) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.qpython.qsl4a.qsl4a.facade; + +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.BatteryManager; +import android.os.Bundle; + +import org.qpython.qsl4a.qsl4a.LogUtil; +import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; +import org.qpython.qsl4a.qsl4a.rpc.Rpc; +import org.qpython.qsl4a.qsl4a.rpc.RpcMinSdk; +import org.qpython.qsl4a.qsl4a.rpc.RpcStartEvent; +import org.qpython.qsl4a.qsl4a.rpc.RpcStopEvent; + +import java.lang.reflect.Field; + +/** + * Exposes Batterymanager API. Note that in order to use any of the batteryGet* functions, you need + * to batteryStartMonitoring, and then wait for a "battery" event. Sleeping for a second will + * usually work just as well. + * + * @author Alexey Reznichenko (alexey.reznichenko@gmail.com) + * @author Robbie Matthews (rjmatthews62@gmail.com) + */ +public class BatteryManagerFacade extends RpcReceiver { + + private final Service mService; + private final EventFacade mEventFacade; + private final AndroidFacade mAndroidFacade; + private final Context context; + + private BatteryStateListener mReceiver; + private BatteryManager mBatteryManager; + + private volatile Bundle mBatteryData; + private volatile Integer mBatteryStatus = null; + private volatile Integer mBatteryHealth = null; + private volatile Integer mPlugType = null; + + private volatile Boolean mBatteryPresent = null; + private volatile Integer mBatteryLevel = null; + private volatile Integer mBatteryMaxLevel = null; + private volatile Integer mBatteryVoltage = null; + private volatile Integer mBatteryTemperature = null; + private volatile String mBatteryTechnology = null; + + public BatteryManagerFacade(FacadeManager manager) { + super(manager); + mService = manager.getService(); + mEventFacade = manager.getReceiver(EventFacade.class); + mAndroidFacade = manager.getReceiver(AndroidFacade.class); + context = mAndroidFacade.context; + mBatteryManager = (BatteryManager) context.getSystemService(Context.BATTERY_SERVICE); + mReceiver = null; + mBatteryData = null; + } + + private class BatteryStateListener extends BroadcastReceiver { + + private final EventFacade mmEventFacade; + + private BatteryStateListener(EventFacade facade) { + mmEventFacade = facade; + } + + @Override + public void onReceive(Context context, Intent intent) { + mBatteryStatus = intent.getIntExtra("status", 1); + mBatteryHealth = intent.getIntExtra("health", 1); + mPlugType = intent.getIntExtra("plugged", -1); + mBatteryPresent = intent.getBooleanExtra(getBatteryManagerFieldValue("EXTRA_PRESENT"), false); + mBatteryLevel = intent.getIntExtra(getBatteryManagerFieldValue("EXTRA_LEVEL"), -1); + mBatteryMaxLevel = intent.getIntExtra(getBatteryManagerFieldValue("EXTRA_SCALE"), 0); + mBatteryVoltage = intent.getIntExtra(getBatteryManagerFieldValue("EXTRA_VOLTAGE"), -1); + mBatteryTemperature = intent.getIntExtra(getBatteryManagerFieldValue("EXTRA_TEMPERATURE"), -1); + mBatteryTechnology = intent.getStringExtra(getBatteryManagerFieldValue("EXTRA_TECHNOLOGY")); + Bundle data = new Bundle(); + data.putInt("status", mBatteryStatus); + data.putInt("health", mBatteryHealth); + data.putInt("plugged", mPlugType); + data.putBoolean("battery_present", mBatteryPresent); + if (mBatteryMaxLevel == null || mBatteryMaxLevel == 100 || mBatteryMaxLevel == 0) { + data.putInt("level", mBatteryLevel); + } else { + data.putFloat("level", getBatteryLevel()); + } + data.putInt("voltage", mBatteryVoltage); + data.putInt("temperature", mBatteryTemperature); + data.putString("technology", mBatteryTechnology); + mBatteryData = data; + mmEventFacade.postEvent("battery", mBatteryData.clone()); + } + } + + private String getBatteryManagerFieldValue(String name) { + try { + Field f = BatteryManager.class.getField(name); + return f.get(null).toString(); + } catch (Exception e) { + LogUtil.e(e); + } + return null; + } + + private int getBatteryCurrent(){ + return mBatteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CURRENT_NOW); + } + + private float getBatteryLevel(){ + return ((int) (mBatteryLevel * 10000 / mBatteryMaxLevel))/100; + } + + @Rpc(description = "Returns the most recently recorded battery data.") + public Bundle readBatteryData() { + mBatteryData.putInt("current", getBatteryCurrent()); + return mBatteryData; + } + + /** + * throws "battery" events + */ + @Rpc(description = "Starts tracking battery state.") + @RpcStartEvent("battery") + public void batteryStartMonitoring() { + if (mReceiver == null) { + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_BATTERY_CHANGED); + mReceiver = new BatteryStateListener(mEventFacade); + mService.registerReceiver(mReceiver, filter); + } + } + + @Rpc(description = "Stops tracking battery state.") + @RpcStopEvent("battery") + public void batteryStopMonitoring() { + if (mReceiver != null) { + mService.unregisterReceiver(mReceiver); + mReceiver = null; + } + mBatteryData = null; + } + + @Override + public void shutdown() { + batteryStopMonitoring(); + } + + @Rpc(description = "Returns the most recently received battery status data:" + "\n1 - unknown;" + + "\n2 - charging;" + "\n3 - discharging;" + "\n4 - not charging;" + "\n5 - full;") + public Integer batteryGetStatus() { + return mBatteryStatus; + } + + @Rpc(description = "Returns the most recently received battery health data:" + "\n1 - unknown;" + + "\n2 - good;" + "\n3 - overheat;" + "\n4 - dead;" + "\n5 - over voltage;" + + "\n6 - unspecified failure;") + public Integer batteryGetHealth() { + return mBatteryHealth; + } + + /** Power source is an AC charger. */ + public static final int BATTERY_PLUGGED_AC = 1; + /** Power source is a USB port. */ + public static final int BATTERY_PLUGGED_USB = 2; + + @Rpc(description = "Returns the most recently received plug type data:" + "\n-1 - unknown" + + "\n0 - unplugged;" + "\n1 - power source is an AC charger" + + "\n2 - power source is a USB port") + public Integer batteryGetPlugType() { + return mPlugType; + } + + @Rpc(description = "Returns the most recently received battery presence data.") + + public Boolean batteryCheckPresent() { + return mBatteryPresent; + } + + @Rpc(description = "Returns the most recently received battery level (percentage).") + public Float batteryGetLevel() { + if (mBatteryMaxLevel == null || mBatteryMaxLevel == 100 || mBatteryMaxLevel == 0) { + return (float) mBatteryLevel; + } else { + return getBatteryLevel(); + } + } + + @Rpc(description = "Returns the most recently received battery voltage.") + public Integer batteryGetVoltage() { + return mBatteryVoltage; + } + + @Rpc(description = "Returns the most recently received battery Now Current.") + public Integer batteryGetCurrent() { + return getBatteryCurrent(); + } + + @Rpc(description = "Returns the most recently received battery temperature.") + + public Integer batteryGetTemperature() { + return mBatteryTemperature; + } + + @Rpc(description = "Returns the most recently received battery technology data.") + + public String batteryGetTechnology() { + return mBatteryTechnology; + } + +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/BluetoothFacade.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/BluetoothFacade.java index 7657329..000d2ab 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/BluetoothFacade.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/BluetoothFacade.java @@ -1,532 +1,757 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a.facade; - -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothServerSocket; -import android.bluetooth.BluetoothSocket; -import android.content.Intent; - - -import org.qpython.qsl4a.codec.Base64Codec; -import org.qpython.qsl4a.qsl4a.Constants; -import org.qpython.qsl4a.qsl4a.LogUtil; -import org.qpython.qsl4a.qsl4a.MainThread; -import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; -import org.qpython.qsl4a.qsl4a.rpc.Rpc; -import org.qpython.qsl4a.qsl4a.rpc.RpcDefault; -import org.qpython.qsl4a.qsl4a.rpc.RpcMinSdk; -import org.qpython.qsl4a.qsl4a.rpc.RpcOptional; -import org.qpython.qsl4a.qsl4a.rpc.RpcParameter; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.Callable; - - -/** - * Bluetooth functions. - * - */ -// Discovery functions added by Eden Sayag - -@RpcMinSdk(5) -public class BluetoothFacade extends RpcReceiver { - - // UUID for SL4A. - private static final String DEFAULT_UUID = "457807c0-4897-11df-9879-0800200c9a66"; - private static final String SDP_NAME = "SL4A"; - - private Map connections = new HashMap(); - private AndroidFacade mAndroidFacade; - private BluetoothAdapter mBluetoothAdapter; - - public BluetoothFacade(FacadeManager manager) { - super(manager); - mAndroidFacade = manager.getReceiver(AndroidFacade.class); - mBluetoothAdapter = MainThread.run(manager.getService(), new Callable() { - @Override - public BluetoothAdapter call() throws Exception { - return BluetoothAdapter.getDefaultAdapter(); - } - }); - } - - @Rpc(description = "Returns active Bluetooth connections.") - public Map bluetoothActiveConnections() { - Map out = new HashMap(); - for (Map.Entry entry : connections.entrySet()) { - if (entry.getValue().isConnected()) { - out.put(entry.getKey(), entry.getValue().getRemoteBluetoothAddress()); - } - } - - return out; - } - - private BluetoothConnection getConnection(String connID) throws IOException { - BluetoothConnection conn = null; - if (connID.trim().length() > 0) { - conn = connections.get(connID); - } else if (connections.size() == 1) { - conn = (BluetoothConnection) connections.values().toArray()[0]; - } - if (conn == null) { - throw new IOException("Bluetooth not ready for this connID."); - } - return conn; - } - - @Rpc(description = "Send bytes over the currently open Bluetooth connection.") - public void bluetoothWriteBinary( - @RpcParameter(name = "base64", description = "A base64 encoded String of the bytes to be sent.") String base64, - @RpcParameter(name = "connID", description = "Connection id") @RpcDefault("") @RpcOptional String connID) - throws IOException { - BluetoothConnection conn = getConnection(connID); - try { - conn.write(Base64Codec.decodeBase64(base64)); - } catch (IOException e) { - connections.remove(conn.getUUID()); - throw e; - } - } - - @Rpc(description = "Read up to bufferSize bytes and return a chunked, base64 encoded string.") - public String bluetoothReadBinary( - @RpcParameter(name = "bufferSize") @RpcDefault("4096") Integer bufferSize, - @RpcParameter(name = "connID", description = "Connection id") @RpcDefault("") @RpcOptional String connID) - throws IOException { - - BluetoothConnection conn = getConnection(connID); - try { - return Base64Codec.encodeBase64String(conn.readBinary(bufferSize)); - } catch (IOException e) { - connections.remove(conn.getUUID()); - throw e; - } - } - - private String addConnection(BluetoothConnection conn) { - String uuid = UUID.randomUUID().toString(); - connections.put(uuid, conn); - conn.setUUID(uuid); - return uuid; - } - - @Rpc(description = "Connect to a device over Bluetooth. Blocks until the connection is established or fails.", returns = "True if the connection was established successfully.") - public String bluetoothConnect( - @RpcParameter(name = "uuid", description = "The UUID passed here must match the UUID used by the server device.") @RpcDefault(DEFAULT_UUID) String uuid, - @RpcParameter(name = "address", description = "The user will be presented with a list of discovered devices to choose from if an address is not provided.") @RpcOptional String address) - throws IOException { - if (address == null) { - Intent deviceChooserIntent = new Intent(); - deviceChooserIntent.setComponent(Constants.BLUETOOTH_DEVICE_LIST_COMPONENT_NAME); - Intent result = mAndroidFacade.startActivityForResult(deviceChooserIntent); - if (result != null && result.hasExtra(Constants.EXTRA_DEVICE_ADDRESS)) { - address = result.getStringExtra(Constants.EXTRA_DEVICE_ADDRESS); - } else { - return null; - } - } - BluetoothDevice mDevice; - BluetoothSocket mSocket; - BluetoothConnection conn; - mDevice = mBluetoothAdapter.getRemoteDevice(address); - mSocket = mDevice.createRfcommSocketToServiceRecord(UUID.fromString(uuid)); - // Always cancel discovery because it will slow down a connection. - mBluetoothAdapter.cancelDiscovery(); - mSocket.connect(); - conn = new BluetoothConnection(mSocket); - return addConnection(conn); - } - - @Rpc(description = "Listens for and accepts a Bluetooth connection. Blocks until the connection is established or fails.") - public String bluetoothAccept( - @RpcParameter(name = "uuid") @RpcDefault(DEFAULT_UUID) String uuid, - @RpcParameter(name = "timeout", description = "How long to wait for a new connection, 0 is wait for ever") @RpcDefault("0") Integer timeout) - throws IOException { - BluetoothServerSocket mServerSocket; - mServerSocket = - mBluetoothAdapter.listenUsingRfcommWithServiceRecord(SDP_NAME, UUID.fromString(uuid)); - BluetoothSocket mSocket = mServerSocket.accept(timeout.intValue()); - BluetoothConnection conn = new BluetoothConnection(mSocket, mServerSocket); - return addConnection(conn); - } - - @Rpc(description = "Requests that the device be discoverable for Bluetooth connections.") - public void bluetoothMakeDiscoverable( - @RpcParameter(name = "duration", description = "period of time, in seconds, during which the device should be discoverable") @RpcDefault("300") Integer duration) { - if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { - Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); - discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, duration); - // Use startActivityForResult to make this a synchronous call. - mAndroidFacade.startActivityForResult(discoverableIntent); - } - } - - @Rpc(description = "Sends ASCII characters over the currently open Bluetooth connection.") - public void bluetoothWrite(@RpcParameter(name = "ascii") String ascii, - @RpcParameter(name = "connID", description = "Connection id") @RpcDefault("") String connID) - throws IOException { - BluetoothConnection conn = getConnection(connID); - try { - conn.write(ascii); - } catch (IOException e) { - connections.remove(conn.getUUID()); - throw e; - } - } - - @Rpc(description = "Returns True if the next read is guaranteed not to block.") - public Boolean bluetoothReadReady( - @RpcParameter(name = "connID", description = "Connection id") @RpcDefault("") @RpcOptional String connID) - throws IOException { - BluetoothConnection conn = getConnection(connID); - try { - return conn.readReady(); - } catch (IOException e) { - connections.remove(conn.getUUID()); - throw e; - } - } - - @Rpc(description = "Read up to bufferSize ASCII characters.") - public String bluetoothRead( - @RpcParameter(name = "bufferSize") @RpcDefault("4096") Integer bufferSize, - @RpcParameter(name = "connID", description = "Connection id") @RpcOptional @RpcDefault("") String connID) - throws IOException { - BluetoothConnection conn = getConnection(connID); - try { - return conn.read(bufferSize); - } catch (IOException e) { - connections.remove(conn.getUUID()); - throw e; - } - } - - @Rpc(description = "Read the next line.") - public String bluetoothReadLine( - @RpcParameter(name = "connID", description = "Connection id") @RpcOptional @RpcDefault("") String connID) - throws IOException { - BluetoothConnection conn = getConnection(connID); - try { - return conn.readLine(); - } catch (IOException e) { - connections.remove(conn.getUUID()); - throw e; - } - } - - @Rpc(description = "Queries a remote device for it's name or null if it can't be resolved") - public String bluetoothGetRemoteDeviceName( - @RpcParameter(name = "address", description = "Bluetooth Address For Target Device") String address) { - try { - BluetoothDevice mDevice; - mDevice = mBluetoothAdapter.getRemoteDevice(address); - return mDevice.getName(); - } catch (Exception e) { - return null; - } - } - - @Rpc(description = "Gets the Bluetooth Visible device name") - public String bluetoothGetLocalName() { - return mBluetoothAdapter.getName(); - } - - @Rpc(description = "Sets the Bluetooth Visible device name, returns True on success") - public boolean bluetoothSetLocalName( - @RpcParameter(name = "name", description = "New local name") String name) { - return mBluetoothAdapter.setName(name); - } - - @Rpc(description = "Gets the scan mode for the local dongle.\r\n" + "Return values:\r\n" - + "\t-1 when Bluetooth is disabled.\r\n" + "\t0 if non discoverable and non connectable.\r\n" - + "\r1 connectable non discoverable." + "\r3 connectable and discoverable.") - public int bluetoothGetScanMode() { - if (mBluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF - || mBluetoothAdapter.getState() == BluetoothAdapter.STATE_TURNING_OFF) { - return -1; - } - - switch (mBluetoothAdapter.getScanMode()) { - case BluetoothAdapter.SCAN_MODE_NONE: - return 0; - case BluetoothAdapter.SCAN_MODE_CONNECTABLE: - return 1; - case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE: - return 3; - default: - return mBluetoothAdapter.getScanMode() - 20; - } - } - - @Rpc(description = "Returns the name of the connected device.") - public String bluetoothGetConnectedDeviceName( - @RpcParameter(name = "connID", description = "Connection id") @RpcOptional @RpcDefault("") String connID) - throws IOException { - BluetoothConnection conn = getConnection(connID); - return conn.getConnectedDeviceName(); - } - - @Rpc(description = "Checks Bluetooth state.", returns = "True if Bluetooth is enabled.") - public Boolean checkBluetoothState() { - return mBluetoothAdapter.isEnabled(); - } - - @Rpc(description = "Toggle Bluetooth on and off.", returns = "True if Bluetooth is enabled.") - public Boolean toggleBluetoothState( - @RpcParameter(name = "enabled") @RpcOptional Boolean enabled, - @RpcParameter(name = "prompt", description = "Prompt the user to confirm changing the Bluetooth state.") @RpcDefault("true") Boolean prompt) { - if (enabled == null) { - enabled = !checkBluetoothState(); - } - if (enabled) { - if (prompt) { - Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); - // TODO(damonkohler): Use the result to determine if this was successful. At any rate, keep - // using startActivityForResult in order to synchronize this call. - mAndroidFacade.startActivityForResult(intent); - } else { - // TODO(damonkohler): Make this synchronous as well. - mBluetoothAdapter.enable(); - } - } else { - // TODO(damonkohler): Add support for prompting on disable. - // TODO(damonkohler): Make this synchronous as well. - shutdown(); - mBluetoothAdapter.disable(); - } - return enabled; - } - - @Rpc(description = "Stops Bluetooth connection.") - public void bluetoothStop( - @RpcParameter(name = "connID", description = "Connection id") @RpcOptional @RpcDefault("") String connID) { - BluetoothConnection conn; - try { - conn = getConnection(connID); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - return; - } - if (conn == null) { - return; - } - - conn.stop(); - connections.remove(conn.getUUID()); - } - - @Rpc(description = "Returns the hardware address of the local Bluetooth adapter. ") - public String bluetoothGetLocalAddress() { - return mBluetoothAdapter.getAddress(); - } - - @Rpc(description = "Start the remote device discovery process. ", returns = "true on success, false on error") - public Boolean bluetoothDiscoveryStart() { - return mBluetoothAdapter.startDiscovery(); - } - - @Rpc(description = "Cancel the current device discovery process.", returns = "true on success, false on error") - public Boolean bluetoothDiscoveryCancel() { - return mBluetoothAdapter.cancelDiscovery(); - } - - @Rpc(description = "Return true if the local Bluetooth adapter is currently in the device discovery process. ") - public Boolean bluetoothIsDiscovering() { - return mBluetoothAdapter.isDiscovering(); - } - - @Override - public void shutdown() { - for (Map.Entry entry : connections.entrySet()) { - entry.getValue().stop(); - } - connections.clear(); - } -} - -class BluetoothConnection { - private BluetoothSocket mSocket; - private BluetoothDevice mDevice; - private OutputStream mOutputStream; - private InputStream mInputStream; - private BufferedReader mReader; - private BluetoothServerSocket mServerSocket; - private String UUID; - - public BluetoothConnection(BluetoothSocket mSocket) throws IOException { - this(mSocket, null); - } - - public BluetoothConnection(BluetoothSocket mSocket, BluetoothServerSocket mServerSocket) - throws IOException { - this.mSocket = mSocket; - mOutputStream = mSocket.getOutputStream(); - mInputStream = mSocket.getInputStream(); - mDevice = mSocket.getRemoteDevice(); - mReader = new BufferedReader(new InputStreamReader(mInputStream, "ASCII")); - this.mServerSocket = mServerSocket; - } - - public void setUUID(String UUID) { - this.UUID = UUID; - } - - public String getUUID() { - return UUID; - } - - public String getRemoteBluetoothAddress() { - return mDevice.getAddress(); - } - - public boolean isConnected() { - if (mSocket == null) { - return false; - } - try { - mSocket.getRemoteDevice(); - mInputStream.available(); - mReader.ready(); - return true; - } catch (Exception e) { - return false; - } - } - - public void write(byte[] out) throws IOException { - if (mOutputStream != null) { - mOutputStream.write(out); - } else { - throw new IOException("Bluetooth not ready."); - } - } - - public void write(String out) throws IOException { - this.write(out.getBytes()); - } - - public Boolean readReady() throws IOException { - if (mReader != null) { - return mReader.ready(); - } - throw new IOException("Bluetooth not ready."); - } - - public byte[] readBinary() throws IOException { - return this.readBinary(4096); - } - - public byte[] readBinary(int bufferSize) throws IOException { - if (mReader != null) { - byte[] buffer = new byte[bufferSize]; - int bytesRead = mInputStream.read(buffer); - if (bytesRead == -1) { - LogUtil.e("Read failed."); - throw new IOException("Read failed."); - } - byte[] truncatedBuffer = new byte[bytesRead]; - System.arraycopy(buffer, 0, truncatedBuffer, 0, bytesRead); - return truncatedBuffer; - } - - throw new IOException("Bluetooth not ready."); - - } - - public String read() throws IOException { - return this.read(4096); - } - - public String read(int bufferSize) throws IOException { - if (mReader != null) { - char[] buffer = new char[bufferSize]; - int bytesRead = mReader.read(buffer); - if (bytesRead == -1) { - LogUtil.e("Read failed."); - throw new IOException("Read failed."); - } - return new String(buffer, 0, bytesRead); - } - throw new IOException("Bluetooth not ready."); - } - - public String readLine() throws IOException { - if (mReader != null) { - return mReader.readLine(); - } - throw new IOException("Bluetooth not ready."); - } - - public String getConnectedDeviceName() { - return mDevice.getName(); - } - - public void stop() { - if (mSocket != null) { - try { - mSocket.close(); - } catch (IOException e) { - LogUtil.e(e); - } - } - mSocket = null; - if (mServerSocket != null) { - try { - mServerSocket.close(); - } catch (IOException e) { - LogUtil.e(e); - } - } - mServerSocket = null; - - if (mInputStream != null) { - try { - mInputStream.close(); - } catch (IOException e) { - LogUtil.e(e); - } - } - mInputStream = null; - if (mOutputStream != null) { - try { - mOutputStream.close(); - } catch (IOException e) { - LogUtil.e(e); - } - } - mOutputStream = null; - if (mReader != null) { - try { - mReader.close(); - } catch (IOException e) { - LogUtil.e(e); - } - } - mReader = null; - } -} +/* + * Copyright (C) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.qpython.qsl4a.qsl4a.facade; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCallback; +import android.bluetooth.BluetoothServerSocket; +import android.bluetooth.BluetoothSocket; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.ParcelUuid; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.qpython.qsl4a.codec.Base64Codec; +import org.qpython.qsl4a.qsl4a.Constants; +import org.qpython.qsl4a.qsl4a.LogUtil; +import org.qpython.qsl4a.qsl4a.MainThread; +import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; +import org.qpython.qsl4a.qsl4a.rpc.Rpc; +import org.qpython.qsl4a.qsl4a.rpc.RpcDefault; +import org.qpython.qsl4a.qsl4a.rpc.RpcOptional; +import org.qpython.qsl4a.qsl4a.rpc.RpcParameter; +import org.qpython.qsl4a.qsl4a.util.PermissionUtil; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.UUID; + + +/** + * Bluetooth functions. + * + */ +// Discovery functions added by Eden Sayag + +public class BluetoothFacade extends RpcReceiver { + + // UUID for SL4A. + private static final String DEFAULT_UUID = "457807c0-4897-11df-9879-0800200c9a66"; + private static final String SDP_NAME = "SL4A"; + + private final Map connections = new HashMap<>(); + private final AndroidFacade mAndroidFacade; + private final BluetoothAdapter mBluetoothAdapter; + @SuppressLint("StaticFieldLeak") + private static Context context; + private static JSONObject mDeviceList; + private static BroadcastReceiver mReceiver; + private static HashMap> mRssi; + private static int mRssiInterval; + + public BluetoothFacade(FacadeManager manager) { + super(manager); + mAndroidFacade = manager.getReceiver(AndroidFacade.class); + context = mAndroidFacade.context; + mReceiver = new BluetoothReceiver(); + mBluetoothAdapter = MainThread.run(manager.getService(), BluetoothAdapter::getDefaultAdapter); + mRssiInterval = 1000; + } + + @Rpc(description = "Returns active Bluetooth connections.") + public Map bluetoothActiveConnections() { + Map out = new HashMap<>(); + for (Map.Entry entry : connections.entrySet()) { + if (entry.getValue().isConnected()) { + out.put(entry.getKey(), entry.getValue().getRemoteBluetoothAddress()); + } + } + return out; + } + + private BluetoothConnection getConnection(String connID) throws IOException { + BluetoothConnection conn = null; + if (connID.trim().length() > 0) { + conn = connections.get(connID); + } else if (connections.size() == 1) { + conn = (BluetoothConnection) connections.values().toArray()[0]; + } + if (conn == null) { + throw new IOException("Bluetooth not ready for this connID."); + } + return conn; + } + + @Rpc(description = "Send bytes over the currently open Bluetooth connection.") + public void bluetoothWriteBinary( + @RpcParameter(name = "base64", description = "A base64 encoded String of the bytes to be sent.") String base64, + @RpcParameter(name = "connID", description = "Connection id") @RpcDefault("") @RpcOptional String connID) + throws IOException { + BluetoothConnection conn = getConnection(connID); + try { + conn.write(Base64Codec.decodeBase64(base64)); + } catch (IOException e) { + connections.remove(conn.getUUID()); + throw e; + } + } + + @Rpc(description = "Read up to bufferSize bytes and return a chunked, base64 encoded string.") + public String bluetoothReadBinary( + @RpcParameter(name = "bufferSize") @RpcDefault("4096") Integer bufferSize, + @RpcParameter(name = "connID", description = "Connection id") @RpcDefault("") @RpcOptional String connID) + throws IOException { + + BluetoothConnection conn = getConnection(connID); + try { + return Base64Codec.encodeBase64String(conn.readBinary(bufferSize)); + } catch (IOException e) { + connections.remove(conn.getUUID()); + throw e; + } + } + + private String addConnection(BluetoothConnection conn) { + String uuid = UUID.randomUUID().toString(); + connections.put(uuid, conn); + conn.setUUID(uuid); + return uuid; + } + + public static void checkBluetoothPermission() throws Exception { + if(mDeviceList != null) + return; + String[] permissions; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { + permissions = new String[]{Manifest.permission.BLUETOOTH_CONNECT,Manifest.permission.BLUETOOTH_SCAN,Manifest.permission.BLUETOOTH}; + } else { + permissions = new String[]{Manifest.permission.BLUETOOTH}; + } + PermissionUtil.checkPermission(permissions); + mDeviceList = new JSONObject(); + } + + @Rpc(description = "Connect to a device over Bluetooth. Blocks until the connection is established or fails.", returns = "True if the connection was established successfully.") + public String bluetoothConnect( + @RpcParameter(name = "uuid", description = "The UUID passed here must match the UUID used by the server device.") @RpcDefault(DEFAULT_UUID) String uuid, + @RpcParameter(name = "address", description = "The user will be presented with a list of discovered devices to choose from if an address is not provided.") @RpcOptional String address) + throws Exception { + checkBluetoothPermission(); + if (address == null) { + Intent deviceChooserIntent = new Intent(); + deviceChooserIntent.setComponent(Constants.BLUETOOTH_DEVICE_LIST_COMPONENT_NAME); + Intent result = mAndroidFacade.startActivityForResult(deviceChooserIntent); + if (result != null && result.hasExtra(Constants.EXTRA_DEVICE_ADDRESS)) { + address = result.getStringExtra(Constants.EXTRA_DEVICE_ADDRESS); + } else { + return null; + } + } + BluetoothDevice mDevice; + BluetoothSocket mSocket; + BluetoothConnection conn; + mDevice = mBluetoothAdapter.getRemoteDevice(address); + mSocket = mDevice.createRfcommSocketToServiceRecord(UUID.fromString(uuid)); + // Always cancel discovery because it will slow down a connection. + mBluetoothAdapter.cancelDiscovery(); + mSocket.connect(); + conn = new BluetoothConnection(mSocket); + return addConnection(conn); + } + + @Rpc(description = "Listens for and accepts a Bluetooth connection. Blocks until the connection is established or fails.") + public String bluetoothAccept( + @RpcParameter(name = "uuid") @RpcDefault(DEFAULT_UUID) String uuid, + @RpcParameter(name = "timeout", description = "How long to wait for a new connection, 0 is wait for ever") @RpcDefault("0") Integer timeout) + throws Exception { + checkBluetoothPermission(); + BluetoothServerSocket mServerSocket; + mServerSocket = + mBluetoothAdapter.listenUsingRfcommWithServiceRecord(SDP_NAME, UUID.fromString(uuid)); + BluetoothSocket mSocket = mServerSocket.accept(timeout); + BluetoothConnection conn = new BluetoothConnection(mSocket, mServerSocket); + return addConnection(conn); + } + + @Rpc(description = "Requests that the device be discoverable for Bluetooth connections.") + public void bluetoothMakeDiscoverable( + @RpcParameter(name = "duration", description = "period of time, in seconds, during which the device should be discoverable") @RpcDefault("300") Integer duration) throws Exception { + checkBluetoothPermission(); + if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { + Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); + discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, duration); + // Use startActivityForResult to make this a synchronous call. + mAndroidFacade.startActivityForResult(discoverableIntent); + } + } + + @Rpc(description = "Sends ASCII characters over the currently open Bluetooth connection.") + public void bluetoothWrite(@RpcParameter(name = "ascii") String ascii, + @RpcParameter(name = "connID", description = "Connection id") @RpcDefault("") String connID) + throws IOException { + BluetoothConnection conn = getConnection(connID); + try { + conn.write(ascii); + } catch (IOException e) { + connections.remove(conn.getUUID()); + throw e; + } + } + + @Rpc(description = "Returns True if the next read is guaranteed not to block.") + public Boolean bluetoothReadReady( + @RpcParameter(name = "connID", description = "Connection id") @RpcDefault("") @RpcOptional String connID) + throws IOException { + BluetoothConnection conn = getConnection(connID); + try { + return conn.readReady(); + } catch (IOException e) { + connections.remove(conn.getUUID()); + throw e; + } + } + + @Rpc(description = "Read up to bufferSize ASCII characters.") + public String bluetoothRead( + @RpcParameter(name = "bufferSize") @RpcDefault("4096") Integer bufferSize, + @RpcParameter(name = "connID", description = "Connection id") @RpcOptional @RpcDefault("") String connID) + throws IOException { + BluetoothConnection conn = getConnection(connID); + try { + return conn.read(bufferSize); + } catch (IOException e) { + connections.remove(conn.getUUID()); + throw e; + } + } + + @Rpc(description = "Read the next line.") + public String bluetoothReadLine( + @RpcParameter(name = "connID", description = "Connection id") @RpcOptional @RpcDefault("") String connID) + throws IOException { + BluetoothConnection conn = getConnection(connID); + try { + return conn.readLine(); + } catch (IOException e) { + connections.remove(conn.getUUID()); + throw e; + } + } + + @Rpc(description = "Queries a remote device for it's name or null if it can't be resolved") + public String bluetoothGetRemoteDeviceName( + @RpcParameter(name = "address", description = "Bluetooth Address For Target Device") String address) throws Exception { + checkBluetoothPermission(); + try { + BluetoothDevice mDevice; + mDevice = mBluetoothAdapter.getRemoteDevice(address); + return mDevice.getName(); + } catch (Exception e) { + return null; + } + } + + @Rpc(description = "bluetooth Get Bonded Devices") + public JSONObject bluetoothGetBondedDevices() throws Exception { + checkBluetoothPermission(); + return getBondedDevices(true); + } + + @Rpc(description = "bluetooth Get Bonded Devices Rssi") + public JSONObject bluetoothGetBondedDevicesRssi( + @RpcParameter(name = "interval") @RpcOptional Integer interval + ) throws Exception { + checkBluetoothPermission(); + if(interval == null || interval == 0){ + if(mRssi == null) + return null; + else return getBondedDevices(false); + } else { + if (interval < 0) { + if(mRssi!=null){ + for (ArrayList device:mRssi.values()){ + if(device.size()==4) { + ((BluetoothGatt) device.get(0)).close(); + ((Timer) device.get(1)).cancel(); + } + device.clear(); + } + mRssi.clear(); + } + mRssi = null; + } else { + mRssi = new HashMap<>(); + mRssiInterval = interval; + } + } + return null; + } + + @SuppressLint("MissingPermission") + private JSONObject getBondedDevices(boolean deviceData) throws JSONException { + Set mDevice = mBluetoothAdapter.getBondedDevices(); + JSONObject result = new JSONObject(); + for(BluetoothDevice bluetoothDevice : mDevice) { + JSONObject device; + if(deviceData) + device = putDeviceData(bluetoothDevice, result); + else { + device = new JSONObject(); + result.put(bluetoothDevice.getAddress(),device); + } + if(mRssi != null) + getDeviceRssi(bluetoothDevice,device); + } + return result; + } + + @SuppressLint("MissingPermission") + private void getDeviceRssi(BluetoothDevice bluetoothDevice,JSONObject device){ + String address = bluetoothDevice.getAddress(); + ArrayList lRssi; + + if (mRssi.containsKey(address)) + lRssi = mRssi.get(address); + else { + lRssi = new ArrayList<>(); + mRssi.put(address,lRssi); + } + + if (lRssi.size()==4) { + try { + device.put("time", (long) lRssi.get(2)); + device.put("rssi", (int) lRssi.get(3)); + } catch (Exception ignored) {} + } else if (lRssi.size()==0){ + + final BluetoothGatt bluetoothGatt = bluetoothDevice.connectGatt( + context, true, new BluetoothGattCallback() { + + final ArrayList lRssi = mRssi.get(address); + + /*@Override + public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { + super.onConnectionStateChange(gatt, status, newState); + }*/ + + @Override + public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { + if (BluetoothGatt.GATT_SUCCESS == status) { + try { + lRssi.set(2, System.currentTimeMillis()); + lRssi.set(3, rssi); + } catch (Exception ignored) { + } + } + } + }); + + lRssi.add(bluetoothGatt); + Timer timer = new Timer(); + TimerTask task = new TimerTask() { + @Override + public void run() { + bluetoothGatt.readRemoteRssi(); + } + }; + timer.schedule(task, mRssiInterval, mRssiInterval); + lRssi.add(timer); + lRssi.add(null); + lRssi.add(null); + }} + + @Rpc(description = "bluetooth Get Received Devices") + public JSONObject bluetoothGetReceivedDevices(){ + return mDeviceList; + } + + @Rpc(description = "Gets the Bluetooth Visible device name") + public String bluetoothGetLocalName() throws Exception { + checkBluetoothPermission(); + return mBluetoothAdapter.getName(); + } + + @Rpc(description = "Sets the Bluetooth Visible device name, returns True on success") + public boolean bluetoothSetLocalName( + @RpcParameter(name = "name", description = "New local name") String name) throws Exception { + checkBluetoothPermission(); + return mBluetoothAdapter.setName(name); + } + + @Rpc(description = "Gets the scan mode for the local dongle.\r\n" + "Return values:\r\n" + + "\t-1 when Bluetooth is disabled.\r\n" + "\t0 if non discoverable and non connectable.\r\n" + + "\r1 connectable non discoverable." + "\r3 connectable and discoverable.") + public int bluetoothGetScanMode() throws Exception { + checkBluetoothPermission(); + if (mBluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF + || mBluetoothAdapter.getState() == BluetoothAdapter.STATE_TURNING_OFF) { + return -1; + } + switch (mBluetoothAdapter.getScanMode()) { + case BluetoothAdapter.SCAN_MODE_NONE: + return 0; + case BluetoothAdapter.SCAN_MODE_CONNECTABLE: + return 1; + case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE: + return 3; + default: + return mBluetoothAdapter.getScanMode() - 20; + } + } + + @Rpc(description = "Returns the name of the connected device.") + public String bluetoothGetConnectedDeviceName( + @RpcParameter(name = "connID", description = "Connection id") @RpcOptional @RpcDefault("") String connID) + throws Exception { + checkBluetoothPermission(); + BluetoothConnection conn = getConnection(connID); + return conn.getConnectedDeviceName(); + } + + @Rpc(description = "Checks Bluetooth state.", returns = "True if Bluetooth is enabled.") + public Boolean checkBluetoothState() { + return mBluetoothAdapter.isEnabled(); + } + + @Rpc(description = "Toggle Bluetooth on and off.", returns = "True if Bluetooth is enabled.") + public Boolean toggleBluetoothState( + @RpcParameter(name = "enabled") @RpcOptional Boolean enabled, + @RpcParameter(name = "prompt", description = "Prompt the user to confirm changing the Bluetooth state.") @RpcDefault("true") Boolean prompt) throws Exception { + checkBluetoothPermission(); + if (enabled == null) { + enabled = !checkBluetoothState(); + } + if (enabled) { + if (prompt) { + Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); + // TODO(damonkohler): Use the result to determine if this was successful. At any rate, keep + // using startActivityForResult in order to synchronize this call. + mAndroidFacade.startActivityForResult(intent); + } else { + // TODO(damonkohler): Make this synchronous as well. + mBluetoothAdapter.enable(); + } + } else { + // TODO(damonkohler): Add support for prompting on disable. + // TODO(damonkohler): Make this synchronous as well. + shutdown(); + mBluetoothAdapter.disable(); + } + return enabled; + } + + @Rpc(description = "Stops Bluetooth connection.") + public void bluetoothStop( + @RpcParameter(name = "connID", description = "Connection id") @RpcOptional @RpcDefault("") String connID) { + BluetoothConnection conn; + try { + conn = getConnection(connID); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return; + } + /*if (conn == null) { + return; + }*/ + + conn.stop(); + connections.remove(conn.getUUID()); + } + + @Rpc(description = "Returns the hardware address of the local Bluetooth adapter. ") + public String bluetoothGetLocalAddress() { + return mBluetoothAdapter.getAddress(); + } + + @Rpc(description = "Start the remote device discovery process. ", returns = "true on success, false on error") + public Boolean bluetoothDiscoveryStart() throws Exception { + checkBluetoothPermission(); + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(BluetoothDevice.ACTION_FOUND);//发现设备 + intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);//配对 + intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//搜索结束 + //注意,有些设备连接状态是这个广播,我就是栽在这里,一台设备是这里的回调,有些设备又是下面的广播回调,所以要做兼容... + intentFilter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED); + mDeviceList = new JSONObject(); + context.registerReceiver(mReceiver, intentFilter); + return mBluetoothAdapter.startDiscovery(); + } + + @Rpc(description = "Cancel the current device discovery process.", returns = "true on success, false on error") + public Boolean bluetoothDiscoveryCancel() throws Exception { + checkBluetoothPermission(); + context.unregisterReceiver(mReceiver); + mDeviceList = null; + return mBluetoothAdapter.cancelDiscovery(); + } + + @Rpc(description = "Return true if the local Bluetooth adapter is currently in the device discovery process. ") + public Boolean bluetoothIsDiscovering() throws Exception { + checkBluetoothPermission(); + return mBluetoothAdapter.isDiscovering(); + } + + @Override + public void shutdown() { + for (Map.Entry entry : connections.entrySet()) { + entry.getValue().stop(); + } + connections.clear(); + } + + @SuppressLint("MissingPermission") + private static JSONObject putDeviceData(BluetoothDevice bluetoothDevice,JSONObject targetObject) throws JSONException { + JSONObject device = new JSONObject(); + device.put("name",bluetoothDevice.getName()); + device.put("bondState",bluetoothDevice.getBondState()); + device.put("type",bluetoothDevice.getType()); + JSONArray uuid = new JSONArray(); + ParcelUuid[] Uuids = bluetoothDevice.getUuids(); + if (Uuids != null) { + for (ParcelUuid Uuid : bluetoothDevice.getUuids()) + uuid.put(Uuid.toString()); + device.put("uuid", uuid); + } + targetObject.put(bluetoothDevice.getAddress(),device); + return device; + } + + private static void putDeviceData(BluetoothDevice bluetoothDevice,short rssi) throws JSONException { + JSONObject device = putDeviceData(bluetoothDevice,mDeviceList); + if(rssi!=Short.MIN_VALUE) { + device.put("rssi", rssi); + device.put("time",System.currentTimeMillis()); + } + } + + private static void putDeviceExcept(Exception exception){ + try { + mDeviceList.put("EXCEPTION"+mDeviceList.length(),exception.toString()); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + public static class BluetoothReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + try { + checkBluetoothPermission(); + if(mDeviceList==null) + mDeviceList=new JSONObject(); + } catch (Exception exception) { + putDeviceExcept(exception); + return; + } + + final String action = intent.getAction(); + + if (BluetoothDevice.ACTION_FOUND.equals(action)) { + // Get the BluetoothDevice object from the Intent. + BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI,Short.MIN_VALUE); + try { + putDeviceData(device,rssi); + } catch (Exception exception) { + putDeviceExcept(exception); + } + } + } + } + +} + +class BluetoothConnection { + private BluetoothSocket mSocket; + private BluetoothDevice mDevice; + private OutputStream mOutputStream; + private InputStream mInputStream; + private BufferedReader mReader; + private BluetoothServerSocket mServerSocket; + private String UUID; + + public BluetoothConnection(BluetoothSocket mSocket) throws IOException { + this(mSocket, null); + } + + public BluetoothConnection(BluetoothSocket mSocket, BluetoothServerSocket mServerSocket) + throws IOException { + this.mSocket = mSocket; + mOutputStream = mSocket.getOutputStream(); + mInputStream = mSocket.getInputStream(); + mDevice = mSocket.getRemoteDevice(); + mReader = new BufferedReader(new InputStreamReader(mInputStream, "ASCII")); + this.mServerSocket = mServerSocket; + } + + public void setUUID(String UUID) { + this.UUID = UUID; + } + + public String getUUID() { + return UUID; + } + + public String getRemoteBluetoothAddress() { + return mDevice.getAddress(); + } + + public boolean isConnected() { + if (mSocket == null) { + return false; + } + try { + mSocket.getRemoteDevice(); + mInputStream.available(); + mReader.ready(); + return true; + } catch (Exception e) { + return false; + } + } + + public void write(byte[] out) throws IOException { + if (mOutputStream != null) { + mOutputStream.write(out); + } else { + throw new IOException("Bluetooth not ready."); + } + } + + public void write(String out) throws IOException { + this.write(out.getBytes()); + } + + public Boolean readReady() throws IOException { + if (mReader != null) { + return mReader.ready(); + } + throw new IOException("Bluetooth not ready."); + } + + public byte[] readBinary() throws IOException { + return this.readBinary(4096); + } + + public byte[] readBinary(int bufferSize) throws IOException { + if (mReader != null) { + byte[] buffer = new byte[bufferSize]; + int bytesRead = mInputStream.read(buffer); + if (bytesRead == -1) { + LogUtil.e("Read failed."); + throw new IOException("Read failed."); + } + byte[] truncatedBuffer = new byte[bytesRead]; + System.arraycopy(buffer, 0, truncatedBuffer, 0, bytesRead); + return truncatedBuffer; + } + + throw new IOException("Bluetooth not ready."); + + } + + public String read() throws IOException { + return this.read(4096); + } + + public String read(int bufferSize) throws IOException { + if (mReader != null) { + char[] buffer = new char[bufferSize]; + int bytesRead = mReader.read(buffer); + if (bytesRead == -1) { + LogUtil.e("Read failed."); + throw new IOException("Read failed."); + } + return new String(buffer, 0, bytesRead); + } + throw new IOException("Bluetooth not ready."); + } + + public String readLine() throws IOException { + if (mReader != null) { + return mReader.readLine(); + } + throw new IOException("Bluetooth not ready."); + } + + public String getConnectedDeviceName() throws Exception { + BluetoothFacade.checkBluetoothPermission(); + return mDevice.getName(); + } + + public void stop() { + if (mSocket != null) { + try { + mSocket.close(); + } catch (IOException e) { + LogUtil.e(e); + } + } + mSocket = null; + if (mServerSocket != null) { + try { + mServerSocket.close(); + } catch (IOException e) { + LogUtil.e(e); + } + } + mServerSocket = null; + + if (mInputStream != null) { + try { + mInputStream.close(); + } catch (IOException e) { + LogUtil.e(e); + } + } + mInputStream = null; + if (mOutputStream != null) { + try { + mOutputStream.close(); + } catch (IOException e) { + LogUtil.e(e); + } + } + mOutputStream = null; + if (mReader != null) { + try { + mReader.close(); + } catch (IOException e) { + LogUtil.e(e); + } + } + mReader = null; + } +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/CameraFacade.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/CameraFacade.java index 5afb66c..921830c 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/CameraFacade.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/CameraFacade.java @@ -1,252 +1,328 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a.facade; - -import android.app.Service; - -import android.content.Context; -import android.content.Intent; -import android.hardware.Camera; -import android.hardware.Camera.AutoFocusCallback; -import android.hardware.Camera.Parameters; -import android.hardware.Camera.PictureCallback; -import android.hardware.camera2.CameraCharacteristics; -import android.hardware.camera2.CameraManager; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.provider.MediaStore; -import android.support.annotation.RequiresApi; -import android.view.SurfaceHolder; -import android.view.SurfaceView; -import android.view.WindowManager; -import android.view.SurfaceHolder.Callback; - -import org.qpython.qsl4a.QSL4APP; -import org.qpython.qsl4a.qsl4a.util.FileUtils; -import org.qpython.qsl4a.qsl4a.future.FutureActivityTaskExecutor; -import org.qpython.qsl4a.qsl4a.LogUtil; - - -import org.qpython.qsl4a.qsl4a.future.FutureActivityTask; -import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; -import org.qpython.qsl4a.qsl4a.rpc.Rpc; -import org.qpython.qsl4a.qsl4a.rpc.RpcDefault; -import org.qpython.qsl4a.qsl4a.rpc.RpcParameter; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.lang.reflect.Method; -import java.util.concurrent.CountDownLatch; - -/** - * Access Camera functions. - * - */ -public class CameraFacade extends RpcReceiver { - - private final Service mService; - private Parameters mParameters; - private final Context context; - - private class BooleanResult { - boolean mmResult = false; - } - - public CameraFacade(FacadeManager manager) { - super(manager); - mService = manager.getService(); - - //乘着船 修改 - context = mService.getApplicationContext(); - // ↑ // - - /*Camera camera = Camera.open(); - try { - mParameters = camera.getParameters(); - } finally { - camera.release(); - }*/ - } - - @Rpc(description = "Take a picture and save it to the specified path.", returns = "A map of Booleans autoFocus and takePicture where True indicates success.") - public Bundle cameraCapturePicture(@RpcParameter(name = "targetPath") final String targetPath, - @RpcParameter(name = "useAutoFocus") @RpcDefault("true") Boolean useAutoFocus) - throws InterruptedException { - final BooleanResult autoFocusResult = new BooleanResult(); - final BooleanResult takePictureResult = new BooleanResult(); - - Camera camera = Camera.open(); - /*try { - mParameters = camera.getParameters(); - } finally { - camera.release(); - } - camera.setParameters(mParameters);*/ - - try { - Method method = camera.getClass().getMethod("setDisplayOrientation", int.class); - method.invoke(camera, 90); - } catch (Exception e) { - LogUtil.e(e); - } - - try { - FutureActivityTask previewTask = setPreviewDisplay(camera); - camera.startPreview(); - if (useAutoFocus) { - autoFocus(autoFocusResult, camera); - } - takePicture(new File(targetPath), takePictureResult, camera); - previewTask.finish(); - } catch (Exception e) { - LogUtil.e(e); - } finally { - camera.release(); - } - - Bundle result = new Bundle(); - result.putBoolean("autoFocus", autoFocusResult.mmResult); - result.putBoolean("takePicture", takePictureResult.mmResult); - return result; - } - - private FutureActivityTask setPreviewDisplay(Camera camera) throws IOException, - InterruptedException { - FutureActivityTask task = new FutureActivityTask() { - @SuppressWarnings("deprecation") - @Override - public void onCreate() { - super.onCreate(); - final SurfaceView view = new SurfaceView(getActivity()); - getActivity().setContentView(view); - getActivity().getWindow().setSoftInputMode( - WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED); - view.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); - view.getHolder().addCallback(new Callback() { - @Override - public void surfaceDestroyed(SurfaceHolder holder) { - } - - @Override - public void surfaceCreated(SurfaceHolder holder) { - setResult(view.getHolder()); - } - - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - } - }); - } - }; - FutureActivityTaskExecutor taskQueue = - ((QSL4APP) mService.getApplication()).getTaskExecutor(); - taskQueue.execute(task); - camera.setPreviewDisplay(task.getResult()); - return task; - } - - private void takePicture(final File file, final BooleanResult takePictureResult, - final Camera camera) throws InterruptedException { - final CountDownLatch latch = new CountDownLatch(1); - camera.takePicture(null, null, new PictureCallback() { - @Override - public void onPictureTaken(byte[] data, Camera camera) { - if (!FileUtils.makeDirectories(file.getParentFile(), 0755)) { - takePictureResult.mmResult = false; - return; - } - try { - FileOutputStream output = new FileOutputStream(file); - output.write(data); - output.close(); - takePictureResult.mmResult = true; - } catch (FileNotFoundException e) { - LogUtil.e("Failed to save picture.", e); - takePictureResult.mmResult = false; - return; - } catch (IOException e) { - LogUtil.e("Failed to save picture.", e); - takePictureResult.mmResult = false; - return; - } finally { - latch.countDown(); - } - } - }); - latch.await(); - } - - private void autoFocus(final BooleanResult result, final Camera camera) - throws InterruptedException { - final CountDownLatch latch = new CountDownLatch(1); - { - camera.autoFocus(new AutoFocusCallback() { - @Override - public void onAutoFocus(boolean success, Camera camera) { - result.mmResult = success; - latch.countDown(); - } - }); - latch.await(); - } - } - - @Override - public void shutdown() { - // Nothing to clean up. - } - - @Rpc(description = "Starts the image capture application to take a picture and saves it to the specified path.") - public void cameraInteractiveCapturePicture( - @RpcParameter(name = "targetPath") final String targetPath) { - Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - File file = new File(targetPath); - intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file)); - - AndroidFacade facade = mManager.getReceiver(AndroidFacade.class); - if (intent.resolveActivity(mService.getPackageManager())!=null) { - facade.startActivityForResult(intent); - } else { - LogUtil.e("No camera found"); - } - } - - // 打开或关闭闪光灯 - //@SuppressLint("NewApi") - @RequiresApi(api = Build.VERSION_CODES.M) - @Rpc(description = "open or close flash light torch of camera.") - public void cameraSetTorchMode(@RpcParameter(name = "enabled") Boolean enabled) throws Exception { - //获取CameraManager - CameraManager mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); - //获取当前手机所有摄像头设备ID - String[] ids = mCameraManager.getCameraIdList(); - for (String id : ids) { - CameraCharacteristics c = mCameraManager.getCameraCharacteristics(id); - //查询该摄像头组件是否包含闪光灯 - Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); - Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING); - if (flashAvailable != null && flashAvailable - && lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK) { - //打开或关闭手电筒 - mCameraManager.setTorchMode(id, enabled); - } - } - } -} +/* + * Copyright (C) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.qpython.qsl4a.qsl4a.facade; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.hardware.Camera; +import android.hardware.Camera.PictureCallback; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraManager; +import android.net.Uri; +import android.provider.MediaStore; +import android.support.v4.content.FileProvider; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.WindowManager; + +import util.DocumentUtil; + +import org.qpython.qsl4a.QSL4APP; +import org.qpython.qsl4a.qsl4a.util.FileUtils; +import org.qpython.qsl4a.qsl4a.future.FutureActivityTaskExecutor; +import org.qpython.qsl4a.qsl4a.LogUtil; +import org.qpython.qsl4a.qsl4a.future.FutureActivityTask; +import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; +import org.qpython.qsl4a.qsl4a.rpc.Rpc; +import org.qpython.qsl4a.qsl4a.rpc.RpcDefault; +import org.qpython.qsl4a.qsl4a.rpc.RpcOptional; +import org.qpython.qsl4a.qsl4a.rpc.RpcParameter; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.reflect.Method; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.concurrent.CountDownLatch; + + +/** + * Access Camera functions. + * + */ +public class CameraFacade extends RpcReceiver { + + private final Service mService; + + private final String sdcard; + private final AndroidFacade mAndroidFacade; + private final Context context; + private final String qpyProvider; + + private static class BooleanResult { + boolean mmResult = false; + } + + public CameraFacade(FacadeManager manager) { + super(manager); + mService = manager.getService(); + + mAndroidFacade = manager.getReceiver(AndroidFacade.class); + sdcard = DocumentUtil.SDCARD; + context = mAndroidFacade.context; + qpyProvider = mAndroidFacade.qpyProvider; + } + + @Rpc(description = "Take a picture and save it to the specified path.", returns = "A map of Booleans autoFocus and takePicture where True indicates success.") + public String cameraCapturePicture(@RpcParameter(name = "targetPath") final String targetPath, + //cameraId: back==0, front==1 + @RpcParameter(name = "cameraId") @RpcDefault("0") Integer cameraId, + @RpcParameter(name = "useAutoFocus") @RpcDefault("true") Boolean useAutoFocus) + throws Exception { + final BooleanResult autoFocusResult = new BooleanResult(); + final BooleanResult takePictureResult = new BooleanResult(); + + Camera camera = Camera.open(cameraId); + /*try{ + mParameters = camera.getParameters(); + camera.setParameters(mParameters); + } catch (Exception e){ + //throw new Exception(Arrays.toString(e.getStackTrace())); + }*/ + + try { + Method method = camera.getClass().getMethod("setDisplayOrientation", int.class); + method.invoke(camera, 90); + } catch (Exception e) { + LogUtil.e(e); + } + + try { + FutureActivityTask previewTask = setPreviewDisplay(camera); + camera.startPreview(); + if (useAutoFocus) { + autoFocus(autoFocusResult, camera); + } + takePicture(new File(targetPath), takePictureResult, camera); + previewTask.finish(); + } catch (Exception e) { + LogUtil.e(e); + } finally { + camera.release(); + } + + /*Bundle result = new Bundle(); + result.putBoolean("autoFocus", autoFocusResult.mmResult); + result.putBoolean("takePicture", takePictureResult.mmResult); + return result;*/ + return targetPath; + } + + private FutureActivityTask setPreviewDisplay(Camera camera) throws IOException, + InterruptedException { + FutureActivityTask task = new FutureActivityTask() { + @SuppressWarnings("deprecation") + @Override + public void onCreate() { + super.onCreate(); + final SurfaceView view = new SurfaceView(getActivity()); + getActivity().setContentView(view); + getActivity().getWindow().setSoftInputMode( + WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED); + view.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); + view.getHolder().addCallback(new SurfaceHolder.Callback() { + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + setResult(view.getHolder()); + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + } + }); + } + }; + FutureActivityTaskExecutor taskQueue = + ((QSL4APP) mService.getApplication()).getTaskExecutor(); + taskQueue.execute(task); + camera.setPreviewDisplay(task.getResult()); + return task; + } + + private void takePicture(final File file, final BooleanResult takePictureResult, + final Camera camera) throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + camera.takePicture(null, null, new PictureCallback() { + @Override + public void onPictureTaken(byte[] data, Camera camera) { + if (!FileUtils.makeDirectories(file.getParentFile(), 0755)) { + takePictureResult.mmResult = false; + return; + } + try { + FileOutputStream output = new FileOutputStream(file); + output.write(data); + output.close(); + takePictureResult.mmResult = true; + } catch (FileNotFoundException e) { + LogUtil.e("Failed to save picture.", e); + takePictureResult.mmResult = false; + return; + } catch (IOException e) { + LogUtil.e("Failed to save picture.", e); + takePictureResult.mmResult = false; + return; + } finally { + latch.countDown(); + } + } + }); + latch.await(); + } + + private void autoFocus(final BooleanResult result, final Camera camera) + throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + { + camera.autoFocus((success, camera1) -> { + result.mmResult = success; + latch.countDown(); + }); + latch.await(); + } + } + + /*@Rpc(description = "Starts the image capture application to take a picture and saves it to the specified path.") + public void cameraInteractiveCapturePicture( + @RpcParameter(name = "targetPath") final String targetPath) { + Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + File file = new File(targetPath); + intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file)); + + AndroidFacade facade = mManager.getReceiver(AndroidFacade.class); + if (intent.resolveActivity(mService.getPackageManager())!=null) { + facade.startActivityForResult(intent); + } else { + LogUtil.e("No camera found"); + } + }*/ + + + @Override + public void shutdown() { + // Nothing to clean up. + } + + //乘着船 添加 + @Rpc(description = "Take Picture with system camera .") + public String takePicture( + @RpcParameter(name = "path") @RpcOptional String path + ) throws Exception { + Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + String fn; + File out; + if (path == null) { + String imgPath = sdcard + "/DCIM/";//存放照片的文件夹 + fn = new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date()) + ".jpg";//照片命名 + out = new File(imgPath); + if (!out.exists()) { + out.mkdirs(); + } + out = new File(imgPath, fn); + fn = imgPath + fn;//该照片的绝对路径 + } else { + fn = path; + out = new File(fn); + } + Uri uri; + // if (Build.VERSION.SDK_INT>=24){ + uri = FileProvider.getUriForFile(context,qpyProvider,out); + //} else { + // uri = Uri.fromFile(out); + //} + intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); + Intent intentR = mAndroidFacade.startActivityForResultCode(intent); + switch (intentR.getIntExtra("RESULT_CODE",-1025)){ + case -1025: + throw new Exception(intentR.getStringExtra("EXCEPTION")); + case Activity.RESULT_OK: + return fn; + default: + return null; + } + } + + @Rpc(description = "Take Video with system camera .") + public String takeVideo( + @RpcParameter(name = "path") @RpcOptional String path, + @RpcParameter(name = "quality") @RpcDefault("1") Integer quality + ) throws Exception { + Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); + String fn; + File out; + if (path == null) { + String vidPath = sdcard + "/DCIM/";//存放照片的文件夹 + fn = new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date()) + ".mp4";//视频命名 + out = new File(vidPath); + if (!out.exists()) { + out.mkdirs(); + } + out = new File(vidPath, fn); + fn = vidPath + fn;//该照片的绝对路径 + } else { + fn = path; + out = new File(fn); + } + Uri uri; + //if (Build.VERSION.SDK_INT>=24){ + uri = FileProvider.getUriForFile(context,qpyProvider,out); + //} else { + // uri = Uri.fromFile(out); + //} + intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); + intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY,quality); + Intent intentR = mAndroidFacade.startActivityForResultCode(intent); + switch (intentR.getIntExtra("RESULT_CODE",-1025)){ + case -1025: + throw new Exception(intentR.getStringExtra("EXCEPTION")); + case Activity.RESULT_OK: + return fn; + default: + return null; + } + } + + // 打开或关闭闪光灯 + @SuppressLint("NewApi") + @Rpc(description = "open or close flash light torch of camera.") + public void cameraSetTorchMode(@RpcParameter(name = "enabled") Boolean enabled) throws Exception { + //获取CameraManager + CameraManager mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); + //获取当前手机所有摄像头设备ID + String[] ids = mCameraManager.getCameraIdList(); + for (String id : ids) { + CameraCharacteristics c = mCameraManager.getCameraCharacteristics(id); + //查询该摄像头组件是否包含闪光灯 + Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); + Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING); + if (flashAvailable != null && flashAvailable + && lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK) { + //打开或关闭手电筒 + mCameraManager.setTorchMode(id, enabled); + } + } + } + } diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/CipherFacade.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/CipherFacade.java index 9446abe..1170267 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/CipherFacade.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/CipherFacade.java @@ -1,213 +1,254 @@ -package org.qpython.qsl4a.qsl4a.facade; - -import android.app.Service; -import android.content.pm.PackageManager; -import android.util.Base64; - -import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; -import org.qpython.qsl4a.qsl4a.rpc.Rpc; -import org.qpython.qsl4a.qsl4a.rpc.RpcDefault; -import org.qpython.qsl4a.qsl4a.rpc.RpcParameter; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.nio.ByteBuffer; - -import javax.crypto.Cipher; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -public class CipherFacade extends RpcReceiver { - - private final Service mService; - private final PackageManager mPackageManager; - //加密引擎 - private Cipher cipherEncrypt; - //解密引擎 - private Cipher cipherDecrypt; - //字符串编码 - private String EncodingFormat; - public static final int MAX_BUFFER_SIZE = 5242848;//max buffer size (5MB-32B) - - public CipherFacade(FacadeManager manager) { - super(manager); - mService = manager.getService(); - mPackageManager = mService.getPackageManager(); - } - - private static byte[] readFromFile(String filePath) throws Exception { - File file = new File(filePath); - FileInputStream fis = new FileInputStream(file); - int length = fis.available(); - byte[] data = new byte[length]; - fis.read(data); - fis.close(); - return data; - } - - private static void writeToFile(String filePath, byte[] data) throws Exception { - FileOutputStream fos = new FileOutputStream(filePath); - fos.write(data); - fos.flush(); - fos.close(); - } - - /** - * @function cipherInit 加密解密引擎设置 - * @params key 密钥 - * @params algorithm 加密算法(默认为AES/CBC/PKCS5Padding) - * @params encodingFormat 字符串编码格式(默认为空白(Base64),也可为UTF-8,GBK) - * @params initialVector 初始向量(CBC模式时使用) - */ - @Rpc(description = "Initialize Encrypt Engine / Decrypt Engine .") - public void cipherInit( - @RpcParameter(name = "key") final String key, - @RpcParameter(name = "algorithm") @RpcDefault("AES/CBC/PKCS5Padding") final String algorithm, - @RpcParameter(name = "encodingFormat") @RpcDefault("") final String encodingFormat, - @RpcParameter(name = "initialVector") @RpcDefault("") final String initialVector ) throws Exception { - cipherEncrypt = Cipher.getInstance(algorithm); - cipherDecrypt = Cipher.getInstance(algorithm); - byte[] Key; - byte[] InitialVector; - EncodingFormat = encodingFormat.toUpperCase(); - if (EncodingFormat.equals("")) { - //Base64解码 - Key = Base64.decode( key, Base64.DEFAULT ); - if (initialVector.equals("")) { - InitialVector = Key; - } else { - InitialVector = Base64.decode( initialVector, Base64.DEFAULT ); - } - } else { - //其他字符串编码解码 - Key = key.getBytes( encodingFormat ); - if (initialVector.equals("")) { - InitialVector = Key; - } else { - InitialVector = initialVector.getBytes(encodingFormat); - } - } - if (InitialVector.length > 16){ - //InitialVector长度大于16时自动截取 - byte[] IV = new byte[16]; - System.arraycopy(InitialVector,0,IV,0,16); - InitialVector = IV; - } - //Algorithm 加密算法如AES - String Algorithm; - int pos = algorithm.indexOf("/"); - if ( pos == -1 ) { - Algorithm = algorithm; - } - else { - Algorithm = algorithm.substring( 0, pos ); - } - SecretKeySpec keySpec = new SecretKeySpec(Key, Algorithm); - //使用CBC模式,需要一个向量iv,可增加加密算法的强度 - IvParameterSpec iv = new IvParameterSpec(InitialVector); - if (algorithm.contains("CBC")) { - cipherEncrypt.init(Cipher.ENCRYPT_MODE, keySpec, iv); - cipherDecrypt.init(Cipher.DECRYPT_MODE, keySpec, iv); - } else {//ECB模式 - cipherEncrypt.init(Cipher.ENCRYPT_MODE, keySpec); - cipherDecrypt.init(Cipher.DECRYPT_MODE, keySpec); - } - } - - @Override - public void shutdown() { - } - - /** 全字符串传输模式 */ - - @Rpc(description = "Encrypt a Normal / Base64 String to another Base64 String .") - public String encryptString( - @RpcParameter(name = "srcString") final String srcString) - throws Exception { - byte[] rst; - if (EncodingFormat.equals("")) { - rst = Base64.decode( srcString, Base64.DEFAULT ); - } else { - rst = srcString.getBytes( EncodingFormat ); - } - return Base64.encodeToString( - cipherEncrypt.doFinal( rst ) - , Base64.DEFAULT); - } - - @Rpc(description = "Decrypt a Base64 String to another Normal / Base64 String .") - public String decryptString( - @RpcParameter(name = "srcString") final String srcString) - throws Exception { - byte[] rst = cipherDecrypt.doFinal( Base64.decode( srcString, Base64.DEFAULT )); - if (EncodingFormat.equals("")) { - return Base64.encodeToString( rst, Base64.DEFAULT ); - } else { - return new String( rst, EncodingFormat ); - } - } - - /** 半字符串半文件模式 */ - - @Rpc(description = "Encrypt a Normal / Base64 String to another File .") - public void encryptStringToFile( - @RpcParameter(name = "srcString") final String srcString, - @RpcParameter(name = "dstFile") final String dstFile) - throws Exception { - byte[] rst; - if (EncodingFormat.equals("")) { - rst = Base64.decode( srcString, Base64.DEFAULT ); - } else { - rst = srcString.getBytes( EncodingFormat ); - } - writeToFile( dstFile , - cipherEncrypt.doFinal( rst )); - } - - @Rpc(description = "Decrypt a File to another Normal / Base64 String .") - public String decryptFileToString( - @RpcParameter(name = "srcFile") final String srcFile) - throws Exception { - byte[] rst = cipherDecrypt.doFinal( readFromFile( srcFile )); - if (EncodingFormat.equals("")) { - return Base64.encodeToString( rst, Base64.DEFAULT ); - } else { - return new String( rst, EncodingFormat ); - } - } - - /** 全二进制文件模式 */ - - private void cipherFile(final String srcFile,final String dstFile,final Cipher cipher) - throws Exception { - FileInputStream fis = new FileInputStream(srcFile); - FileOutputStream fos = new FileOutputStream(dstFile); - int len = fis.available(); - if (len>MAX_BUFFER_SIZE) len=MAX_BUFFER_SIZE; //max buffer size 5MB - byte[] data = new byte[len]; - while((len=fis.read(data))==MAX_BUFFER_SIZE) - fos.write(cipher.update(data)); - fos.write(cipher.doFinal(data,0,len)); - fos.flush(); - fis.close(); - fos.close(); - } - - @Rpc(description = "Encrypt a File to another File .") - public void encryptFile( - @RpcParameter(name = "srcFile") final String srcFile, - @RpcParameter(name = "dstFile") final String dstFile) - throws Exception { - cipherFile(srcFile,dstFile,cipherEncrypt); - } - - @Rpc(description = "Decrypt a File to another File .") - public void decryptFile( - @RpcParameter(name = "srcFile") final String srcFile, - @RpcParameter(name = "dstFile") final String dstFile) - throws Exception { - cipherFile(srcFile,dstFile,cipherDecrypt); - } +package org.qpython.qsl4a.qsl4a.facade; + +import android.app.Service; +import android.content.Context; +import android.content.pm.PackageManager; +import android.support.v4.provider.DocumentFile; +import android.util.Base64; + +import util.DocumentUtil; + +import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; +import org.qpython.qsl4a.qsl4a.rpc.Rpc; +import org.qpython.qsl4a.qsl4a.rpc.RpcDefault; +import org.qpython.qsl4a.qsl4a.rpc.RpcParameter; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class CipherFacade extends RpcReceiver { + + private final Service mService; + private final PackageManager mPackageManager; + private final AndroidFacade mAndroidFacade; + private static Context context; + //加密引擎 + private Cipher cipherEncrypt; + //解密引擎 + private Cipher cipherDecrypt; + //字符串编码 + private String EncodingFormat; + public static final int MAX_BUFFER_SIZE = 5242848;//max buffer size (5MB-32B) + + public CipherFacade(FacadeManager manager) { + super(manager); + mService = manager.getService(); + mPackageManager = mService.getPackageManager(); + mAndroidFacade = manager.getReceiver(AndroidFacade.class); + context = mAndroidFacade.context; + } + + private static byte[] readFromFile(String filePath) throws Exception { + File file = new File(filePath); + FileInputStream fis; + byte[] data; + int length; + try { + fis = new FileInputStream(file); + length = fis.available(); + } catch (IOException e) { + DocumentFile destFile = DocumentUtil.getDocumentFile(file, false, context); + fis = (FileInputStream) context.getContentResolver().openInputStream(destFile.getUri()); + length = fis.available(); + } + data = new byte[length]; + fis.read(data); + fis.close(); + return data; + } + + private static void writeToFile(String filePath, byte[] data) throws Exception { + FileOutputStream fos; + try { + fos = new FileOutputStream(filePath); + fos.write(data); + } catch (IOException e) { + DocumentFile file = DocumentUtil.getDocumentFile(new File(filePath), false, context); + fos = (FileOutputStream) context.getContentResolver().openOutputStream(file.getUri(),"wt"); + fos.write(data); + } + fos.flush(); + fos.close(); + } + + /** + * @function cipherInit 加密解密引擎设置 + * @params key 密钥 + * @params algorithm 加密算法(默认为AES/CBC/PKCS5Padding) + * @params encodingFormat 字符串编码格式(默认为空白(Base64),也可为UTF-8,GBK) + * @params initialVector 初始向量(CBC模式时使用) + */ + @Rpc(description = "Initialize Encrypt Engine / Decrypt Engine .") + public void cipherInit( + @RpcParameter(name = "key") final String key, + @RpcParameter(name = "algorithm") @RpcDefault("AES/CBC/PKCS5Padding") final String algorithm, + @RpcParameter(name = "encodingFormat") @RpcDefault("") final String encodingFormat, + @RpcParameter(name = "initialVector") @RpcDefault("") final String initialVector ) throws Exception { + cipherEncrypt = Cipher.getInstance(algorithm); + cipherDecrypt = Cipher.getInstance(algorithm); + byte[] Key; + byte[] InitialVector; + EncodingFormat = encodingFormat.toUpperCase(); + if (EncodingFormat.equals("")) { + //Base64解码 + Key = Base64.decode( key, Base64.DEFAULT ); + if (initialVector.equals("")) { + InitialVector = Key; + } else { + InitialVector = Base64.decode( initialVector, Base64.DEFAULT ); + } + } else { + //其他字符串编码解码 + Key = key.getBytes( encodingFormat ); + if (initialVector.equals("")) { + InitialVector = Key; + } else { + InitialVector = initialVector.getBytes(encodingFormat); + } + } + if (InitialVector.length > 16){ + //InitialVector长度大于16时自动截取 + byte[] IV = new byte[16]; + System.arraycopy(InitialVector,0,IV,0,16); + InitialVector = IV; + } + //Algorithm 加密算法如AES + String Algorithm; + int pos = algorithm.indexOf("/"); + if ( pos == -1 ) { + Algorithm = algorithm; + } + else { + Algorithm = algorithm.substring( 0, pos ); + } + SecretKeySpec keySpec = new SecretKeySpec(Key, Algorithm); + //使用CBC模式,需要一个向量iv,可增加加密算法的强度 + IvParameterSpec iv = new IvParameterSpec(InitialVector); + if (algorithm.contains("CBC")) { + cipherEncrypt.init(Cipher.ENCRYPT_MODE, keySpec, iv); + cipherDecrypt.init(Cipher.DECRYPT_MODE, keySpec, iv); + } else {//ECB模式 + cipherEncrypt.init(Cipher.ENCRYPT_MODE, keySpec); + cipherDecrypt.init(Cipher.DECRYPT_MODE, keySpec); + } + } + + @Override + public void shutdown() { + } + + /** 全字符串传输模式 */ + + @Rpc(description = "Encrypt a Normal / Base64 String to another Base64 String .") + public String encryptString( + @RpcParameter(name = "srcString") final String srcString) + throws Exception { + byte[] rst; + if (EncodingFormat.equals("")) { + rst = Base64.decode( srcString, Base64.DEFAULT ); + } else { + rst = srcString.getBytes( EncodingFormat ); + } + return Base64.encodeToString( + cipherEncrypt.doFinal( rst ) + , Base64.DEFAULT); + } + + @Rpc(description = "Decrypt a Base64 String to another Normal / Base64 String .") + public String decryptString( + @RpcParameter(name = "srcString") final String srcString) + throws Exception { + byte[] rst = cipherDecrypt.doFinal( Base64.decode( srcString, Base64.DEFAULT )); + if (EncodingFormat.equals("")) { + return Base64.encodeToString( rst, Base64.DEFAULT ); + } else { + return new String( rst, EncodingFormat ); + } + } + + /** 半字符串半文件模式 */ + + @Rpc(description = "Encrypt a Normal / Base64 String to another File .") + public void encryptStringToFile( + @RpcParameter(name = "srcString") final String srcString, + @RpcParameter(name = "dstFile") final String dstFile) + throws Exception { + byte[] rst; + if (EncodingFormat.equals("")) { + rst = Base64.decode( srcString, Base64.DEFAULT ); + } else { + rst = srcString.getBytes( EncodingFormat ); + } + writeToFile( dstFile , + cipherEncrypt.doFinal( rst )); + } + + @Rpc(description = "Decrypt a File to another Normal / Base64 String .") + public String decryptFileToString( + @RpcParameter(name = "srcFile") final String srcFile) + throws Exception { + byte[] rst = cipherDecrypt.doFinal( readFromFile( srcFile )); + if (EncodingFormat.equals("")) { + return Base64.encodeToString( rst, Base64.DEFAULT ); + } else { + return new String( rst, EncodingFormat ); + } + } + + /** 全二进制文件模式 */ + + private void cipherFile(final String srcFile,final String dstFile,final Cipher cipher) throws Exception { + FileInputStream fis; + FileOutputStream fos; + int len; + try { + fis = new FileInputStream(srcFile); + len = fis.available(); + } catch (IOException e) { + DocumentFile sorcFile = DocumentUtil.getDocumentFile(new File(srcFile), false, context); + fis = (FileInputStream) context.getContentResolver().openInputStream(sorcFile.getUri()); + len = fis.available(); + } + if (len > MAX_BUFFER_SIZE) //max buffer size 5MB + len = MAX_BUFFER_SIZE; + try { + fos = new FileOutputStream(dstFile); + byte[] data = new byte[len]; + while ((len = fis.read(data)) == MAX_BUFFER_SIZE) + fos.write(cipher.update(data)); + fos.write(cipher.doFinal(data, 0, len)); + } catch (IOException e){ + DocumentFile file = DocumentUtil.getDocumentFile(new File(dstFile), false, context); + fos = (FileOutputStream) context.getContentResolver().openOutputStream(file.getUri(),"wt"); + byte[] data = new byte[len]; + while ((len = fis.read(data)) == MAX_BUFFER_SIZE) + fos.write(cipher.update(data)); + fos.write(cipher.doFinal(data, 0, len)); + } + fos.flush(); + fis.close(); + fos.close(); + } + + @Rpc(description = "Encrypt a File to another File .") + public void encryptFile( + @RpcParameter(name = "srcFile") final String srcFile, + @RpcParameter(name = "dstFile") final String dstFile) + throws Exception { + cipherFile(srcFile,dstFile,cipherEncrypt); + } + + @Rpc(description = "Decrypt a File to another File .") + public void decryptFile( + @RpcParameter(name = "srcFile") final String srcFile, + @RpcParameter(name = "dstFile") final String dstFile) + throws Exception { + cipherFile(srcFile,dstFile,cipherDecrypt); + } } \ No newline at end of file diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/CommonIntentsFacade.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/CommonIntentsFacade.java index 730819c..5b76931 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/CommonIntentsFacade.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/CommonIntentsFacade.java @@ -1,179 +1,418 @@ -package org.qpython.qsl4a.qsl4a.facade; - -import android.app.SearchManager; -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Build; -import android.provider.Contacts.People; -import android.support.v4.content.FileProvider; -import android.webkit.MimeTypeMap; - - -import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; -import org.qpython.qsl4a.qsl4a.rpc.Rpc; -import org.qpython.qsl4a.qsl4a.rpc.RpcDefault; -import org.qpython.qsl4a.qsl4a.rpc.RpcOptional; -import org.qpython.qsl4a.qsl4a.rpc.RpcParameter; - -import java.io.File; - -import org.json.JSONException; -import org.json.JSONObject; - -/** - * A selection of commonly used intents.
- *
- * These can be used to trigger some common tasks. - * - */ -@SuppressWarnings("deprecation") -public class CommonIntentsFacade extends RpcReceiver { - - private final AndroidFacade mAndroidFacade; - private final Context context; - private final String qpyProvider; - private final Service mService; - - public CommonIntentsFacade(FacadeManager manager) { - super(manager); - mAndroidFacade = manager.getReceiver(AndroidFacade.class); - context = mAndroidFacade.context; - qpyProvider = mAndroidFacade.qpyProvider; - mService = manager.getService(); - } - - @Override - public void shutdown() { - } - - @Rpc(description = "Display content to be picked by URI (e.g. contacts)", returns = "A map of result values.") - public Intent pick(@RpcParameter(name = "uri") String uri) throws JSONException { - return mAndroidFacade.startActivityForResult(Intent.ACTION_PICK, uri, null, null, null, null); - } - - @Rpc(description = "Starts the barcode scanner.", returns = "Scan Result String .") - public String scanBarcode( - @RpcParameter(name = "title") @RpcOptional String title - ) throws Exception { - Intent intent = new Intent(); - intent.setClassName(mService.getPackageName(),"org.qpython.qpy.main.activity.QrCodeActivityRstOnly"); - intent.setAction(Intent.ACTION_VIEW); - intent.putExtra("title",title); - intent = mAndroidFacade.startActivityForResult(intent); - try { - return intent.getStringExtra("result"); } - catch (NullPointerException e) { - return null; - } - } - - private void view(Uri uri, String type) throws Exception { - - Intent intent = new Intent(); - intent.setClassName(this.mAndroidFacade.getmService().getApplicationContext(),"org.qpython.qpy.main.QWebViewActivity"); - intent.putExtra("com.quseit.common.extra.CONTENT_URL1", "main"); - intent.putExtra("com.quseit.common.extra.CONTENT_URL2", "QPyWebApp"); - //intent.putExtra("com.quseit.common.extra.CONTENT_URL6", "drawer"); - intent.setDataAndType(uri, type); - mAndroidFacade.startActivity(intent); - } - - @Rpc(description = "Start activity with view action by URI (i.e. browser, contacts, etc.).") - public void view( - @RpcParameter(name = "uri") String uri, - @RpcParameter(name = "type", description = "MIME type/subtype of the URI") @RpcOptional String type, - @RpcParameter(name = "extras", description = "a Map of extras to add to the Intent") @RpcOptional JSONObject extras) - throws Exception { - mAndroidFacade.startActivity(Intent.ACTION_VIEW, uri, type, extras, true, null, null); - } - - @Rpc(description = "Opens a map search for query (e.g. pizza, 123 My Street).") - public void viewMap(@RpcParameter(name = "query, e.g. pizza, 123 My Street") String query) - throws Exception { - view("geo:0,0?q=" + query, null, null); - } - - @Rpc(description = "Opens the list of contacts.") - public void viewContacts() throws Exception { - view(People.CONTENT_URI, null); - } - - @Rpc(description = "Opens the browser to display a local HTML/text/audio/video File or http(s) Website .") - public void viewHtml( - @RpcParameter(name = "path", description = "the path to the local HTML/text/audio/video File or http(s) Website") String path, - @RpcParameter(name = "title") @RpcOptional String title, - @RpcParameter(name = "wait") @RpcDefault("true") Boolean wait) - throws Exception { - Uri uri; - Intent intent = new Intent(); - if (path.contains("://")) { - uri=Uri.parse(path); - intent.putExtra("src",path); - } else { - uri=Uri.fromFile(new File(path)); - intent.putExtra("LOG_PATH",path); - } - intent.setClassName(context,"org.qpython.qpy.main.activity.QWebViewActivity"); - intent.setDataAndType(uri, "text/html"); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT|Intent.FLAG_ACTIVITY_MULTIPLE_TASK|Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra("title",title); - mAndroidFacade.doStartActivity(intent,wait); - } - - @Rpc(description = "Starts a search for the given query.") - public void search(@RpcParameter(name = "query") String query) throws Exception { - Intent intent = new Intent(Intent.ACTION_SEARCH); - intent.putExtra(SearchManager.QUERY, query); - mAndroidFacade.startActivity(intent); - } - - @Rpc(description = "Convert normal path to content:// or file:// .") - public String pathToUri( - @RpcParameter(name = "path") String path) { - File file = new File(path); - Uri uri; - if (Build.VERSION.SDK_INT>=24) { - uri = FileProvider.getUriForFile(context,qpyProvider,file); - } else { - uri = Uri.fromFile(file); - } - return uri.toString(); - } - - @Rpc(description = "Open a file with path") - public void openFile( - @RpcParameter(name = "path") String path, - @RpcParameter(name = "type", description = "a MIME type of a file") @RpcOptional String type, - @RpcParameter(name = "wait") @RpcDefault("true") Boolean wait) - throws Exception { - MimeTypeMap mime = MimeTypeMap.getSingleton(); - if (type == null) { - /* 获取文件的后缀名 */ - int dotIndex = path.lastIndexOf("."); - if (dotIndex < 0) { - type = "*/*"; //找不到扩展名 - } else { - try { - type = mime.getMimeTypeFromExtension( path.substring( dotIndex + 1 ).toLowerCase() ); - if (type == null) { - type = "*/*"; //找不到打开方式 - } - } catch (Exception e) { - type="*/*"; //出现错误 - } - }} - Intent intent = new Intent(); - intent.setAction(android.content.Intent.ACTION_VIEW); - File file = new File(path); - Uri uri; - uri = FileProvider.getUriForFile(context,qpyProvider,file); - intent.setDataAndType(uri, type); - try { - mAndroidFacade.doStartActivity(intent,wait); - } catch (Exception e) { - e.printStackTrace(); - } - } -} +package org.qpython.qsl4a.qsl4a.facade; + +import android.annotation.SuppressLint; +import android.app.PendingIntent; +import android.app.SearchManager; +import android.app.Service; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutManager; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.Icon; +import android.net.Uri; +import android.os.Build; +import android.provider.ContactsContract; +import android.support.annotation.RequiresApi; +import android.support.v4.content.FileProvider; +import android.text.TextUtils; +import android.webkit.MimeTypeMap; + +import com.google.zxing.BinaryBitmap; +import com.google.zxing.DecodeHintType; +import com.google.zxing.RGBLuminanceSource; +import com.google.zxing.common.HybridBinarizer; +import com.google.zxing.qrcode.QRCodeReader; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.qpython.qsl4a.R; +import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; +import org.qpython.qsl4a.qsl4a.rpc.Rpc; +import org.qpython.qsl4a.qsl4a.rpc.RpcDefault; +import org.qpython.qsl4a.qsl4a.rpc.RpcOptional; +import org.qpython.qsl4a.qsl4a.rpc.RpcParameter; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Hashtable; + +/** + * A selection of commonly used intents.
+ *
+ * These can be used to trigger some common tasks. + * + */ +@SuppressWarnings("deprecation") +public class CommonIntentsFacade extends RpcReceiver { + + private final AndroidFacade mAndroidFacade; + private final Context context; + private final String qpyProvider; + private final Service mService; + + + public CommonIntentsFacade(FacadeManager manager) { + super(manager); + mAndroidFacade = manager.getReceiver(AndroidFacade.class); + context = mAndroidFacade.context; + qpyProvider = mAndroidFacade.qpyProvider; + mService = mAndroidFacade.mService; + } + + @Override + public void shutdown() { + } + + @Rpc(description = "Display content to be picked by URI (e.g. contacts)", returns = "A map of result values.") + public Intent pick(@RpcParameter(name = "uri") String uri) throws Exception { + return mAndroidFacade.startActivityForResult(Intent.ACTION_PICK, uri, null, null, null, null); + } + + @Rpc(description = "Starts the barcode scanner.", returns = "Scan Result String .") + public String scanBarcode( + @RpcParameter(name = "title") @RpcOptional String title + ) throws Exception { + Intent intent = new Intent(); + intent.setClassName(mService.getPackageName(),"org.qpython.qpy.main.auxActivity.QrCodeActivityRstOnly"); + intent.setAction(Intent.ACTION_VIEW); + intent.putExtra("title",title); + intent = mAndroidFacade.startActivityForResult(intent); + try { + return intent.getStringExtra("result"); } + catch (NullPointerException e) { + return null; + } + } + + @Rpc(description = "scan Barcode From Image", returns = "Scan Result String .") + public String scanBarcodeFromImage( + @RpcParameter(name = "path") String path, + @RpcParameter(name = "sampleSize") @RpcDefault("0") Integer sampleSize, + @RpcParameter(name = "x") @RpcDefault("0") Integer x, + @RpcParameter(name = "y") @RpcDefault("0") Integer y, + @RpcParameter(name = "width") @RpcDefault("0") Integer width, + @RpcParameter(name = "height") @RpcDefault("0") Integer height + ) throws Exception { + if (TextUtils.isEmpty(path)) { + return null; + } + // DecodeHintType和EncodeHintType + Hashtable hints = new Hashtable<>(); + hints.put(DecodeHintType.CHARACTER_SET, "utf-8"); // 设置二维码内容的编码 + //hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);//优化精度 + //hints.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE);//复杂模式,开启PURE_BARCODE模式 + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; // 先获取原大小 + Bitmap scanBitmap = BitmapFactory.decodeFile(path, options); + options.inJustDecodeBounds = false; // 获取新的大小 + if (sampleSize == 0) + sampleSize = 1;//(int) (options.outHeight / (float) 200); + //if (sampleSize <= 0) + // sampleSize = 1; + options.inSampleSize = sampleSize; + scanBitmap = BitmapFactory.decodeFile(path, options); + if(width == 0) + width = scanBitmap.getWidth(); + else width = (int) width / sampleSize; + if(height == 0) + height = scanBitmap.getHeight(); + else height = (int) height / sampleSize; + if (x != 0) + x = (int) x / sampleSize; + if (y != 0) + y = (int) y / sampleSize; + int[] data = new int[width * height]; + scanBitmap.getPixels(data, 0, width, x, y, width, height); + RGBLuminanceSource source = new RGBLuminanceSource(width,height,data); + BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source)); + QRCodeReader reader = new QRCodeReader(); + return reader.decode(bitmap1, hints).toString(); + } + + /*private void view(Uri uri, String type, String title, boolean wait) throws Exception { + Intent intent = new Intent(); + intent.setClassName(this.mAndroidFacade.getmService().getApplicationContext(),"org.qpython.qpy.main.activity.QWebViewActivity"); + //intent.putExtra("com.quseit.common.extra.CONTENT_URL1", "main"); + //intent.putExtra("com.quseit.common.extra.CONTENT_URL2", "QPyWebApp"); + //intent.putExtra("com.quseit.common.extra.CONTENT_URL6", "drawer"); + intent.setDataAndType(uri, type); + mAndroidFacade.doStartActivity(intent,wait); + }*/ + + @Rpc(description = "Start activity with view action by URI (i.e. browser, contacts, etc.).") + public void view( + @RpcParameter(name = "uri") String uri, + @RpcParameter(name = "type", description = "MIME type/subtype of the URI") @RpcOptional String type, + @RpcParameter(name = "extras", description = "a Map of extras to add to the Intent") @RpcOptional JSONObject extras, + @RpcParameter(name = "wait") @RpcDefault ("true") @RpcOptional Boolean wait) + throws Exception { + mAndroidFacade.startActivity(Intent.ACTION_VIEW, uri, type, extras, wait, null, null); + } + + @Rpc(description = "Start activity with send action by URI (i.e. browser, contacts, etc.).") + public void send( + @RpcParameter(name = "uri") String uri, + @RpcParameter(name = "type", description = "MIME type/subtype of the URI") @RpcOptional String type, + @RpcParameter(name = "extras", description = "a Map of extras to add to the Intent") @RpcOptional JSONObject extras, + @RpcParameter(name = "wait") @RpcDefault ("true") @RpcOptional Boolean wait) + throws Exception { + mAndroidFacade.startActivity(Intent.ACTION_SEND, uri, type, extras, wait, null, null); + } + + @Rpc(description = "Start activity with send action by text .") + public void sendText( + @RpcParameter(name = "text") String text, + @RpcParameter(name = "extras", description = "put extras") @RpcOptional JSONObject extras, + @RpcParameter(name = "wait") @RpcDefault ("true") @RpcOptional Boolean wait) + throws Exception { + if(extras==null) + extras = new JSONObject(); + if(!extras.has(Intent.EXTRA_TEXT)) + extras.put(Intent.EXTRA_TEXT, text); + mAndroidFacade.startActivity(Intent.ACTION_SEND, null, "text/plain", extras, wait, null, null); + } + + @Rpc(description = "Convert normal path to content:// .") + public String pathToUri( + @RpcParameter(name = "path") String path, + @RpcParameter(name = "File Provider") @RpcDefault("true") Boolean fileProvider) throws Exception { + if(fileProvider) + return getPathUri(path).toString(); + else return getMediaUri(path,getPathType(path,null)).toString(); + } + + @Rpc(description = "Open a file with path") + public void openFile( + @RpcParameter(name = "path") String path, + @RpcParameter(name = "type", description = "a MIME type of a file") @RpcOptional String type, + @RpcParameter(name = "wait") @RpcDefault("true") Boolean wait) + throws Exception { + Intent intent = new Intent(android.content.Intent.ACTION_VIEW); + intent.setDataAndType(getPathUri(path), getPathType(path,type)); + mAndroidFacade.doStartActivity(intent,wait, R.string.open); + } + + @Rpc(description = "Send file(s) with path") + public void sendFile( + @RpcParameter(name = "path(s)") Object path, + @RpcParameter(name = "type", description = "a MIME type of a file") @RpcOptional String type, + @RpcParameter(name = "extras", description = "put extras") @RpcOptional JSONObject extras, + @RpcParameter(name = "wait") @RpcDefault("true") Boolean wait) + throws Exception { + if (extras == null) + extras = new JSONObject(); + Intent intent; + if(path instanceof String) + intent = sendFile1((String) path,type,extras); + else if(path instanceof JSONArray){ + JSONArray paths = (JSONArray) path; + if (paths.length() > 0) + intent = sendFiles(paths,type,extras); + else return; + } else return; + mAndroidFacade.doStartActivity(intent,wait,R.string.share); + } + + private Intent sendFile1(String path, String type, JSONObject extras) throws Exception { + // One File + Intent intent = new Intent(android.content.Intent.ACTION_SEND); + type = getPathType(path,type); + intent.setType(type); + AndroidFacade.putExtrasFromJsonObject(extras,intent); + if (!intent.hasExtra(Intent.EXTRA_STREAM)) { + intent.putExtra(Intent.EXTRA_STREAM, getMediaUri(path,type)); + } + return intent; + } + + private Intent sendFiles(JSONArray paths, String type, JSONObject extras) throws Exception { + // Files List + Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE); + type = getPathType((String) paths.get(0),type); + intent.setType(type); + ArrayList uris = new ArrayList<>(); + for(int i=0;i0) label = label.substring(0,dot); + } catch (Exception ignored){} + } + Icon icon; + if (iconPath!=null) { + Bitmap bitmap = BitmapFactory.decodeFile(iconPath); + bitmap = Bitmap.createScaledBitmap(bitmap, 192, 192, true); + icon = Icon.createWithBitmap(bitmap); + } else { + icon = Icon.createWithResource(context, android.R.drawable.sym_def_app_icon); + } + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + ShortcutManager mShortcutManager = context.getSystemService(ShortcutManager.class); + if (mShortcutManager.isRequestPinShortcutSupported()) { + ShortcutInfo pinShortcutInfo = + new ShortcutInfo.Builder(context, label) + .setShortLabel(label) + .setLongLabel(label) + .setIcon(icon) + .setIntent(intent) + .build(); + Intent pinnedShortcutCallbackIntent = + mShortcutManager.createShortcutResultIntent(pinShortcutInfo); + PendingIntent successCallback = PendingIntent.getBroadcast(context, 0, + pinnedShortcutCallbackIntent, PendingIntent.FLAG_IMMUTABLE); + mShortcutManager.requestPinShortcut(pinShortcutInfo, + successCallback.getIntentSender()); + } + } else { + mAndroidFacade.makeToast("createScriptShortCut need Android >= 8.0 .",1); + } + } +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/ContactsFacade.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/ContactsFacade.java index 3174c47..ed3973f 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/ContactsFacade.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/ContactsFacade.java @@ -1,306 +1,306 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a.facade; - -import android.app.Service; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; -import android.provider.Contacts.People; -import android.provider.Contacts.PhonesColumns; - - -import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; -import org.qpython.qsl4a.qsl4a.rpc.Rpc; -import org.qpython.qsl4a.qsl4a.rpc.RpcOptional; -import org.qpython.qsl4a.qsl4a.rpc.RpcParameter; - -import java.util.ArrayList; -import java.util.List; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -/** - * Provides access to contacts related functionality. - * - * @author MeanEYE.rcf (meaneye.rcf@gmail.com - */ -@SuppressWarnings("deprecation") -public class ContactsFacade extends RpcReceiver { - private static final Uri CONTACTS_URI = Uri.parse("content://contacts/people"); - private final ContentResolver mContentResolver; - private final Service mService; - private final CommonIntentsFacade mCommonIntentsFacade; - public Uri mPhoneContent = null; - public String mContactId; - public String mPrimary; - public String mPhoneNumber; - public String mHasPhoneNumber; - - public ContactsFacade(FacadeManager manager) { - super(manager); - mService = manager.getService(); - mContentResolver = mService.getContentResolver(); - mCommonIntentsFacade = manager.getReceiver(CommonIntentsFacade.class); - try { - // Backward compatibility... get contract stuff using reflection - Class phone = Class.forName("android.provider.ContactsContract$CommonDataKinds$Phone"); - mPhoneContent = (Uri) phone.getField("CONTENT_URI").get(null); - mContactId = (String) phone.getField("CONTACT_ID").get(null); - mPrimary = (String) phone.getField("IS_PRIMARY").get(null); - mPhoneNumber = (String) phone.getField("NUMBER").get(null); - mHasPhoneNumber = (String) phone.getField("HAS_PHONE_NUMBER").get(null); - } catch (Exception e) { - } - } - - private Uri buildUri(Integer id) { - Uri uri = ContentUris.withAppendedId(People.CONTENT_URI, id); - return uri; - } - - @Rpc(description = "Displays a list of contacts to pick from.", returns = "A map of result values.") - public Intent pickContact() throws JSONException { - return mCommonIntentsFacade.pick("content://contacts/people"); - } - - @Rpc(description = "Displays a list of phone numbers to pick from.", returns = "The selected phone number.") - public String pickPhone() throws JSONException { - String result = null; - Intent data = mCommonIntentsFacade.pick("content://contacts/phones"); - if (data != null) { - Uri phoneData = data.getData(); - Cursor cursor = mService.getContentResolver().query(phoneData, null, null, null, null); - if (cursor != null) { - if (cursor.moveToFirst()) { - result = cursor.getString(cursor.getColumnIndexOrThrow(PhonesColumns.NUMBER)); - } - cursor.close(); - } - } - return result; - } - - @Rpc(description = "Returns a List of all possible attributes for contacts.") - public List contactsGetAttributes() { - List result = new ArrayList(); - Cursor cursor = mContentResolver.query(CONTACTS_URI, null, null, null, null); - if (cursor != null) { - String[] columns = cursor.getColumnNames(); - for (int i = 0; i < columns.length; i++) { - result.add(columns[i]); - } - cursor.close(); - } - return result; - } - - // TODO(MeanEYE.rcf): Add ability to narrow selection by providing named pairs of attributes. - @Rpc(description = "Returns a List of all contact IDs.") - public List contactsGetIds() { - List result = new ArrayList(); - String[] columns = { "_id" }; - Cursor cursor = mContentResolver.query(CONTACTS_URI, columns, null, null, null); - if (cursor != null) { - while (cursor.moveToNext()) { - result.add(cursor.getInt(0)); - } - cursor.close(); - } - return result; - } - - @Rpc(description = "Returns a List of all contacts.", returns = "a List of contacts as Maps") - public List contactsGet( - @RpcParameter(name = "attributes") @RpcOptional JSONArray attributes) throws JSONException { - List result = new ArrayList(); - String[] columns; - if (attributes == null || attributes.length() == 0) { - // In case no attributes are specified we set the default ones. - columns = new String[] { "_id", "name", "primary_phone", "primary_email", "type" }; - } else { - // Convert selected attributes list into usable string list. - columns = new String[attributes.length()]; - for (int i = 0; i < attributes.length(); i++) { - columns[i] = attributes.getString(i); - } - } - List queryList = new ArrayList(); - for (String s : columns) { - queryList.add(s); - } - if (!queryList.contains("_id")) { - queryList.add("_id"); - } - - String[] query = queryList.toArray(new String[queryList.size()]); - Cursor cursor = mContentResolver.query(CONTACTS_URI, query, null, null, null); - if (cursor != null) { - int idIndex = cursor.getColumnIndex("_id"); - while (cursor.moveToNext()) { - String id = cursor.getString(idIndex); - JSONObject message = new JSONObject(); - for (int i = 0; i < columns.length; i++) { - String key = columns[i]; - String value = cursor.getString(cursor.getColumnIndex(key)); - if (mPhoneNumber != null) { - if (key.equals("primary_phone")) { - value = findPhone(id); - } - } - message.put(key, value); - } - result.add(message); - } - cursor.close(); - } - return result; - } - - private String findPhone(String id) { - String result = null; - if (id == null || id.equals("")) { - return result; - } - try { - if (Integer.parseInt(id) > 0) { - Cursor pCur = - mContentResolver.query(mPhoneContent, new String[] { mPhoneNumber }, mContactId - + " = ? and " + mPrimary + "=1", new String[] { id }, null); - if (pCur != null) { - pCur.getColumnNames(); - while (pCur.moveToNext()) { - result = pCur.getString(0); - break; - } - } - pCur.close(); - } - } catch (Exception e) { - return null; - } - return result; - } - - @Rpc(description = "Returns contacts by ID.") - public JSONObject contactsGetById(@RpcParameter(name = "id") Integer id, - @RpcParameter(name = "attributes") @RpcOptional JSONArray attributes) throws JSONException { - JSONObject result = null; - Uri uri = buildUri(id); - String[] columns; - if (attributes == null || attributes.length() == 0) { - // In case no attributes are specified we set the default ones. - columns = new String[] { "_id", "name", "primary_phone", "primary_email", "type" }; - } else { - // Convert selected attributes list into usable string list. - columns = new String[attributes.length()]; - for (int i = 0; i < attributes.length(); i++) { - columns[i] = attributes.getString(i); - } - } - Cursor cursor = mContentResolver.query(uri, columns, null, null, null); - if (cursor != null) { - result = new JSONObject(); - cursor.moveToFirst(); - for (int i = 0; i < columns.length; i++) { - result.put(columns[i], cursor.getString(i)); - } - cursor.close(); - } - return result; - } - - // TODO(MeanEYE.rcf): Add ability to narrow selection by providing named pairs of attributes. - @Rpc(description = "Returns the number of contacts.") - public Integer contactsGetCount() { - Integer result = 0; - Cursor cursor = mContentResolver.query(CONTACTS_URI, null, null, null, null); - if (cursor != null) { - result = cursor.getCount(); - cursor.close(); - } - return result; - } - - private String[] jsonToArray(JSONArray array) throws JSONException { - String[] result = null; - if (array != null && array.length() > 0) { - result = new String[array.length()]; - for (int i = 0; i < array.length(); i++) { - result[i] = array.getString(i); - } - } - return result; - } - - /** - * Exactly as per ContentResolver.query - */ - @Rpc(description = "Content Resolver Query", returns = "result of query as Maps") - public List queryContent( - @RpcParameter(name = "uri", description = "The URI, using the content:// scheme, for the content to retrieve.") String uri, - @RpcParameter(name = "attributes", description = "A list of which columns to return. Passing null will return all columns") @RpcOptional JSONArray attributes, - @RpcParameter(name = "selection", description = "A filter declaring which rows to return") @RpcOptional String selection, - @RpcParameter(name = "selectionArgs", description = "You may include ?s in selection, which will be replaced by the values from selectionArgs") @RpcOptional JSONArray selectionArgs, - @RpcParameter(name = "order", description = "How to order the rows") @RpcOptional String order) - throws JSONException { - List result = new ArrayList(); - String[] columns = jsonToArray(attributes); - String[] args = jsonToArray(selectionArgs); - Cursor cursor = mContentResolver.query(Uri.parse(uri), columns, selection, args, order); - if (cursor != null) { - String[] names = cursor.getColumnNames(); - while (cursor.moveToNext()) { - JSONObject message = new JSONObject(); - for (int i = 0; i < cursor.getColumnCount(); i++) { - String key = names[i]; - String value = cursor.getString(i); - message.put(key, value); - } - result.add(message); - } - cursor.close(); - } - return result; - } - - @Rpc(description = "Content Resolver Query Attributes", returns = "a list of available columns for a given content uri") - public JSONArray queryAttributes( - @RpcParameter(name = "uri", description = "The URI, using the content:// scheme, for the content to retrieve.") String uri) - throws JSONException { - JSONArray result = new JSONArray(); - Cursor cursor = mContentResolver.query(Uri.parse(uri), null, "1=0", null, null); - if (cursor != null) { - String[] names = cursor.getColumnNames(); - for (String name : names) { - result.put(name); - } - cursor.close(); - } - return result; - } - - @Override - public void shutdown() { - } -} +/* + * Copyright (C) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.qpython.qsl4a.qsl4a.facade; + +import android.app.Service; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.provider.Contacts.People; +import android.provider.Contacts.PhonesColumns; + + +import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; +import org.qpython.qsl4a.qsl4a.rpc.Rpc; +import org.qpython.qsl4a.qsl4a.rpc.RpcOptional; +import org.qpython.qsl4a.qsl4a.rpc.RpcParameter; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Provides access to contacts related functionality. + * + * @author MeanEYE.rcf (meaneye.rcf@gmail.com + */ +@SuppressWarnings("deprecation") +public class ContactsFacade extends RpcReceiver { + private static final Uri CONTACTS_URI = Uri.parse("content://contacts/people"); + private final ContentResolver mContentResolver; + private final Service mService; + private final CommonIntentsFacade mCommonIntentsFacade; + public Uri mPhoneContent = null; + public String mContactId; + public String mPrimary; + public String mPhoneNumber; + public String mHasPhoneNumber; + + public ContactsFacade(FacadeManager manager) { + super(manager); + mService = manager.getService(); + mContentResolver = mService.getContentResolver(); + mCommonIntentsFacade = manager.getReceiver(CommonIntentsFacade.class); + try { + // Backward compatibility... get contract stuff using reflection + Class phone = Class.forName("android.provider.ContactsContract$CommonDataKinds$Phone"); + mPhoneContent = (Uri) phone.getField("CONTENT_URI").get(null); + mContactId = (String) phone.getField("CONTACT_ID").get(null); + mPrimary = (String) phone.getField("IS_PRIMARY").get(null); + mPhoneNumber = (String) phone.getField("NUMBER").get(null); + mHasPhoneNumber = (String) phone.getField("HAS_PHONE_NUMBER").get(null); + } catch (Exception e) { + } + } + + private Uri buildUri(Integer id) { + Uri uri = ContentUris.withAppendedId(People.CONTENT_URI, id); + return uri; + } + + @Rpc(description = "Displays a list of contacts to pick from.", returns = "A map of result values.") + public Intent pickContact() throws Exception { + return mCommonIntentsFacade.pick("content://contacts/people"); + } + + @Rpc(description = "Displays a list of phone numbers to pick from.", returns = "The selected phone number.") + public String pickPhone() throws Exception { + String result = null; + Intent data = mCommonIntentsFacade.pick("content://contacts/phones"); + if (data != null) { + Uri phoneData = data.getData(); + Cursor cursor = mService.getContentResolver().query(phoneData, null, null, null, null); + if (cursor != null) { + if (cursor.moveToFirst()) { + result = cursor.getString(cursor.getColumnIndexOrThrow(PhonesColumns.NUMBER)); + } + cursor.close(); + } + } + return result; + } + + @Rpc(description = "Returns a List of all possible attributes for contacts.") + public List contactsGetAttributes() { + List result = new ArrayList(); + Cursor cursor = mContentResolver.query(CONTACTS_URI, null, null, null, null); + if (cursor != null) { + String[] columns = cursor.getColumnNames(); + for (int i = 0; i < columns.length; i++) { + result.add(columns[i]); + } + cursor.close(); + } + return result; + } + + // TODO(MeanEYE.rcf): Add ability to narrow selection by providing named pairs of attributes. + @Rpc(description = "Returns a List of all contact IDs.") + public List contactsGetIds() { + List result = new ArrayList(); + String[] columns = { "_id" }; + Cursor cursor = mContentResolver.query(CONTACTS_URI, columns, null, null, null); + if (cursor != null) { + while (cursor.moveToNext()) { + result.add(cursor.getInt(0)); + } + cursor.close(); + } + return result; + } + + @Rpc(description = "Returns a List of all contacts.", returns = "a List of contacts as Maps") + public List contactsGet( + @RpcParameter(name = "attributes") @RpcOptional JSONArray attributes) throws JSONException { + List result = new ArrayList(); + String[] columns; + if (attributes == null || attributes.length() == 0) { + // In case no attributes are specified we set the default ones. + columns = new String[] { "_id", "name", "primary_phone", "primary_email", "type" }; + } else { + // Convert selected attributes list into usable string list. + columns = new String[attributes.length()]; + for (int i = 0; i < attributes.length(); i++) { + columns[i] = attributes.getString(i); + } + } + List queryList = new ArrayList(); + for (String s : columns) { + queryList.add(s); + } + if (!queryList.contains("_id")) { + queryList.add("_id"); + } + + String[] query = queryList.toArray(new String[queryList.size()]); + Cursor cursor = mContentResolver.query(CONTACTS_URI, query, null, null, null); + if (cursor != null) { + int idIndex = cursor.getColumnIndex("_id"); + while (cursor.moveToNext()) { + String id = cursor.getString(idIndex); + JSONObject message = new JSONObject(); + for (int i = 0; i < columns.length; i++) { + String key = columns[i]; + String value = cursor.getString(cursor.getColumnIndex(key)); + if (mPhoneNumber != null) { + if (key.equals("primary_phone")) { + value = findPhone(id); + } + } + message.put(key, value); + } + result.add(message); + } + cursor.close(); + } + return result; + } + + private String findPhone(String id) { + String result = null; + if (id == null || id.equals("")) { + return result; + } + try { + if (Integer.parseInt(id) > 0) { + Cursor pCur = + mContentResolver.query(mPhoneContent, new String[] { mPhoneNumber }, mContactId + + " = ? and " + mPrimary + "=1", new String[] { id }, null); + if (pCur != null) { + pCur.getColumnNames(); + while (pCur.moveToNext()) { + result = pCur.getString(0); + break; + } + } + pCur.close(); + } + } catch (Exception e) { + return null; + } + return result; + } + + @Rpc(description = "Returns contacts by ID.") + public JSONObject contactsGetById(@RpcParameter(name = "id") Integer id, + @RpcParameter(name = "attributes") @RpcOptional JSONArray attributes) throws JSONException { + JSONObject result = null; + Uri uri = buildUri(id); + String[] columns; + if (attributes == null || attributes.length() == 0) { + // In case no attributes are specified we set the default ones. + columns = new String[] { "_id", "name", "primary_phone", "primary_email", "type" }; + } else { + // Convert selected attributes list into usable string list. + columns = new String[attributes.length()]; + for (int i = 0; i < attributes.length(); i++) { + columns[i] = attributes.getString(i); + } + } + Cursor cursor = mContentResolver.query(uri, columns, null, null, null); + if (cursor != null) { + result = new JSONObject(); + cursor.moveToFirst(); + for (int i = 0; i < columns.length; i++) { + result.put(columns[i], cursor.getString(i)); + } + cursor.close(); + } + return result; + } + + // TODO(MeanEYE.rcf): Add ability to narrow selection by providing named pairs of attributes. + @Rpc(description = "Returns the number of contacts.") + public Integer contactsGetCount() { + Integer result = 0; + Cursor cursor = mContentResolver.query(CONTACTS_URI, null, null, null, null); + if (cursor != null) { + result = cursor.getCount(); + cursor.close(); + } + return result; + } + + private String[] jsonToArray(JSONArray array) throws JSONException { + String[] result = null; + if (array != null && array.length() > 0) { + result = new String[array.length()]; + for (int i = 0; i < array.length(); i++) { + result[i] = array.getString(i); + } + } + return result; + } + + /** + * Exactly as per ContentResolver.query + */ + @Rpc(description = "Content Resolver Query", returns = "result of query as Maps") + public List queryContent( + @RpcParameter(name = "uri", description = "The URI, using the content:// scheme, for the content to retrieve.") String uri, + @RpcParameter(name = "attributes", description = "A list of which columns to return. Passing null will return all columns") @RpcOptional JSONArray attributes, + @RpcParameter(name = "selection", description = "A filter declaring which rows to return") @RpcOptional String selection, + @RpcParameter(name = "selectionArgs", description = "You may include ?s in selection, which will be replaced by the values from selectionArgs") @RpcOptional JSONArray selectionArgs, + @RpcParameter(name = "order", description = "How to order the rows") @RpcOptional String order) + throws JSONException { + List result = new ArrayList(); + String[] columns = jsonToArray(attributes); + String[] args = jsonToArray(selectionArgs); + Cursor cursor = mContentResolver.query(Uri.parse(uri), columns, selection, args, order); + if (cursor != null) { + String[] names = cursor.getColumnNames(); + while (cursor.moveToNext()) { + JSONObject message = new JSONObject(); + for (int i = 0; i < cursor.getColumnCount(); i++) { + String key = names[i]; + String value = cursor.getString(i); + message.put(key, value); + } + result.add(message); + } + cursor.close(); + } + return result; + } + + @Rpc(description = "Content Resolver Query Attributes", returns = "a list of available columns for a given content uri") + public JSONArray queryAttributes( + @RpcParameter(name = "uri", description = "The URI, using the content:// scheme, for the content to retrieve.") String uri) + throws JSONException { + JSONArray result = new JSONArray(); + Cursor cursor = mContentResolver.query(Uri.parse(uri), null, "1=0", null, null); + if (cursor != null) { + String[] names = cursor.getColumnNames(); + for (String name : names) { + result.put(name); + } + cursor.close(); + } + return result; + } + + @Override + public void shutdown() { + } +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/DocumentFileFacade.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/DocumentFileFacade.java index 184df02..577f540 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/DocumentFileFacade.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/DocumentFileFacade.java @@ -1,292 +1,324 @@ -package org.qpython.qsl4a.qsl4a.facade; -//by 乘着船 at 2021-2022 - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.net.Uri; -import android.os.Build; -import android.os.storage.StorageManager; -import android.os.storage.StorageVolume; -import android.preference.PreferenceManager; -import android.provider.DocumentsContract; -import android.support.annotation.RequiresApi; -import android.support.v4.provider.DocumentFile; -import android.util.Base64; - -import com.quseit.util.DocumentsUtils; - -import org.json.JSONArray; -import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; -import org.qpython.qsl4a.qsl4a.rpc.Rpc; -import org.qpython.qsl4a.qsl4a.rpc.RpcDefault; -import org.qpython.qsl4a.qsl4a.rpc.RpcOptional; -import org.qpython.qsl4a.qsl4a.rpc.RpcParameter; - -import java.io.File; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.HashMap; -import java.util.Map; - -public class DocumentFileFacade extends RpcReceiver { - - private final AndroidFacade mAndroidFacade; - private final Context context; - - public DocumentFileFacade(FacadeManager manager) { - super(manager); - mAndroidFacade = manager.getReceiver(AndroidFacade.class); - context = mAndroidFacade.context; - } - - @RequiresApi(api = Build.VERSION_CODES.N) - @Rpc(description = "Show Open Document Tree with RootPath .") - public Uri documentTreeShowOpen( - @RpcParameter(name = "rootPath") String rootPath - ) throws Exception { - File file = new File(rootPath); - if(file.canWrite()) - return Uri.fromFile(file); - String p = file.getAbsolutePath(); - if(p.startsWith(DocumentsUtils.ANDROID_PATH)) - return documentTreeAndroid(file); - // else next grade 1 - DocumentFile documentFile; - if (DocumentsUtils.isOnExtSdCard(file, context)) { - documentFile = DocumentsUtils.getDocumentFile(file, true, context); - if(documentFile != null && documentFile.canWrite()) - return documentFile.getUri(); - // else next grade 2 - } else { - p = PreferenceManager.getDefaultSharedPreferences(context).getString(rootPath, null); - if (p != null) { - documentFile = DocumentFile.fromTreeUri(context, Uri.parse(p)); - if(documentFile != null && documentFile.canWrite()) - return documentFile.getUri(); - // else next grade 3 - } // else next grade 2 - } - Intent intent = null; - StorageManager sm = context.getSystemService(StorageManager.class); - StorageVolume volume = sm.getStorageVolume(file); - if (volume != null) { - intent = volume.createAccessIntent(null); - } - if (intent == null) { - intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); - } - Intent intentR = mAndroidFacade.startActivityForResultCode(intent); - switch (intentR.getIntExtra("RESULT_CODE", -1025)) { - case -1025: - throw new Exception(intentR.getStringExtra("EXCEPTION")); - case Activity.RESULT_OK: - Uri uri = intentR.getData(); - DocumentsUtils.saveTreeUri(context,rootPath,uri); - return uri; - default: - return null; - } - } - - @Rpc(description = "Document File Rename .") - public boolean documentFileRenameTo ( - @RpcParameter(name = "src") String src, - @RpcParameter(name = "dest") String dest) throws Exception { - return DocumentsUtils.renameTo(context,new File(src),new File(dest)); - } - - @Rpc(description = "Document File ( or Tree ) Delete .") - public boolean documentFileDelete ( - @RpcParameter(name = "file or tree") String file) { - return DocumentsUtils.delete(context,new File(file)); - } - - @Rpc(description = "Document File Make Directorys .") - public boolean documentFileMkdir ( - @RpcParameter(name = "dir") String dir) { - return DocumentsUtils.mkdirs(context,new File(dir)); - } - - @Rpc(description = "Document File Input Stream .") - public String documentFileInputStream ( - @RpcParameter(name = "srcFile") String srcFile, - @RpcParameter(name = "encodingFormat") @RpcDefault("") String encodingFormat) - throws Exception{ - byte[] data; - InputStream fis=DocumentsUtils.getInputStream(context,new File(srcFile)); - int length = fis.available(); - data = new byte[length]; - fis.read(data); - fis.close(); - if (encodingFormat.equals("")) { - return Base64.encodeToString( data, Base64.DEFAULT ); - } else { - return new String(data, encodingFormat); - } - } - - @Rpc(description = "Document File Output Stream .") - public void documentFileOutputStream ( - @RpcParameter(name = "destFile") String destFile, - @RpcParameter(name = "srcString") @RpcDefault("") String srcString, - @RpcParameter(name = "encodingFormat") @RpcDefault("") String encodingFormat) - throws Exception{ - byte[] data; - if (encodingFormat.equals("")) { - data = Base64.decode( srcString, Base64.DEFAULT ); - } else { - data = srcString.getBytes( encodingFormat ); - } - OutputStream fos=DocumentsUtils.getOutputStream(context,new File(destFile)); - fos.write(data); - fos.flush(); - fos.close(); - } - - @Rpc(description = "Document File Copy .") - public void documentFileCopy ( - @RpcParameter(name = "src") String src, - @RpcParameter(name = "dest") String dest) - throws Exception{ - DocumentsUtils.copy(context,new File(src),new File(dest)); - } - - @Rpc(description = "Document File List Files .") - public JSONArray documentFileListFiles ( - @RpcParameter(name = "folder") String folder - ) throws Exception { - JSONArray jsonArray = new JSONArray(); - String[] S = DocumentsUtils.listFiles(context,new File(folder)); - if(S==null) return null; - for (String s : S) - jsonArray.put(s); - return jsonArray; - } - - @RequiresApi(api = Build.VERSION_CODES.N) - @Rpc(description = "The same as documentTreeShowOpen .") - public Uri documentFileShowOpen( - @RpcParameter(name = "rootPath") String rootPath - ) throws Exception { - return documentTreeShowOpen(rootPath); - } - - @Rpc(description = "The same as documentFileRenameTo .") - public boolean documentFileMoveTo ( - @RpcParameter(name = "src") String src, - @RpcParameter(name = "dest") String dest) throws Exception { - return documentFileRenameTo(src,dest); - } - - @Rpc(description = "The same as documentFileMkdir .") - public boolean documentFileMkdirs ( - @RpcParameter(name = "dir") String dir) { - return documentFileMkdir(dir); - } - - @Rpc(description = "Document File Get Uri .") - public Uri documentFileGetUri ( - @RpcParameter(name = "path") String path, - @RpcParameter(name = "isDirectory") @RpcOptional Boolean isDirectory) { - return DocumentsUtils.getUri(context,new File(path)); - } - - @Rpc(description = "Document File Is Directory .") - public Boolean documentFileIsDirectory ( - @RpcParameter(name = "path") String path - ) throws Exception { - return DocumentsUtils.isDirectory(context,new File(path)); - } - - @Rpc(description = "Document File Get Stat .") - public Map documentFileGetStat( - @RpcParameter(name = "path") String path - ){ - DocumentFile documentFile = DocumentsUtils.getDocumentFile(new File(path),null,context); - if(documentFile == null) return getFileStat(path); - Map map = new HashMap<>(); - map.put("length",documentFile.length()); - map.put("lastModified",documentFile.lastModified()); - map.put("isDirectory",documentFile.isDirectory()); - map.put("canRead",documentFile.canRead()); - map.put("canWrite",documentFile.canWrite());//外置卡此处为true - return map; - } - - @Rpc(description = "get file stat .") - public Map getFileStat( - @RpcParameter(name = "path") String path - ){ - File file = new File(path); - Map map = new HashMap<>(); - map.put("length",file.length()); - map.put("lastModified",file.lastModified()); - map.put("canRead",file.canRead()); - map.put("canWrite",file.canWrite());//外置卡此处为false - map.put("canExecute",file.canExecute()); - map.put("FreeSpace",file.getFreeSpace()); - map.put("TotalSpace",file.getTotalSpace()); - return map; - } - - public Uri documentTreeAndroid(File file) throws Exception { - String path = file.getAbsolutePath(); - String subPath = path.substring(DocumentsUtils.ANDROID_PATH.length()); - SharedPreferences perf = PreferenceManager.getDefaultSharedPreferences(context); - String uriStr = perf.getString(path,null); - Uri uri = null; - DocumentFile documentFile = null; - if(uriStr != null) - uri = Uri.parse(uriStr); - if(uri != null) - documentFile = DocumentFile.fromTreeUri(context, uri); - if(documentFile != null && documentFile.canWrite()) { - addOnSdCardList(path); - return uri; - } - String content = subPath.substring(0,subPath.indexOf("/")); - content = DocumentsUtils.ANDROID_CONTENT[0] + subPathToContent(content) + - DocumentsUtils.ANDROID_CONTENT[1] + subPathToContent(subPath); - uri = Uri.parse(content); - documentFile = DocumentFile.fromTreeUri(context, uri); - if(documentFile == null) - return null; - Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); - intent.setFlags(DocumentsUtils.ANDROID_OPEN_INTENT); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, uri); - } - Intent intentR = mAndroidFacade.startActivityForResultCode(intent); - switch (intentR.getIntExtra("RESULT_CODE", -1025)) { - case -1025: - throw new Exception(intentR.getStringExtra("EXCEPTION")); - case Activity.RESULT_OK: - uri = intentR.getData(); - context.getContentResolver().takePersistableUriPermission(uri,DocumentsUtils.ANDROID_SAVE_INTENT); - addOnSdCardList(path); - perf.edit().putString(path, uri.toString()).apply(); - return uri; - default: - return null; - } - } - - public String subPathToContent(String subPath) { - if (subPath.endsWith("/")) { - subPath = subPath.substring(0, subPath.length() - 1); - } - return subPath.replace("%","%25").replace("/", "%2F"). - replace(" ","%20"); - } - - private void addOnSdCardList(String path){ - if(!DocumentsUtils.sExtSdCardPaths.contains(path)) - DocumentsUtils.sExtSdCardPaths.add(path); - } - - @Override - public void shutdown() { - } +package org.qpython.qsl4a.qsl4a.facade; +//by 乘着船 at 2021-2023 + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.os.storage.StorageManager; +import android.os.storage.StorageVolume; +import android.preference.PreferenceManager; +import android.provider.DocumentsContract; +import android.support.annotation.RequiresApi; +import android.support.v4.provider.DocumentFile; +import android.util.Base64; + +import util.DocumentUtil; + +import org.json.JSONArray; +import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; +import org.qpython.qsl4a.qsl4a.rpc.Rpc; +import org.qpython.qsl4a.qsl4a.rpc.RpcDefault; +import org.qpython.qsl4a.qsl4a.rpc.RpcOptional; +import org.qpython.qsl4a.qsl4a.rpc.RpcParameter; + +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; + +public class DocumentFileFacade extends RpcReceiver { + + private final AndroidFacade mAndroidFacade; + private final Context context; + + public DocumentFileFacade(FacadeManager manager) { + super(manager); + mAndroidFacade = manager.getReceiver(AndroidFacade.class); + context = mAndroidFacade.context; + } + + @RequiresApi(api = Build.VERSION_CODES.N) + @Rpc(description = "Show Open Document Tree with RootPath .") + public Uri documentTreeShowOpen( + @RpcParameter(name = "rootPath") String rootPath + ) throws Exception { + File file = (new File(rootPath)).getCanonicalFile(); + if(file.canWrite() || file.getAbsolutePath().equals(Environment.getExternalStorageDirectory().getAbsolutePath())) + return Uri.fromFile(file); + rootPath = file.getAbsolutePath(); + if(rootPath.startsWith(DocumentUtil.ANDROID_PATH)) + return documentTreeAndroid(file); + // else next grade 1 + DocumentFile documentFile; + if (DocumentUtil.isOnExtSdCard(file, context)) { + documentFile = DocumentUtil.getDocumentFile(file, true, context); + if(documentFile != null && documentFile.canWrite()) + return documentFile.getUri(); + // else next grade 2 + } else { + String p = PreferenceManager.getDefaultSharedPreferences(context).getString(rootPath, null); + if (p != null) { + documentFile = DocumentFile.fromTreeUri(context, Uri.parse(p)); + if(documentFile != null && documentFile.canWrite()) + return documentFile.getUri(); + // else next grade 3 + } // else next grade 2 + } + Intent intent = null; + StorageManager sm = context.getSystemService(StorageManager.class); + StorageVolume volume = sm.getStorageVolume(file); + if (volume != null) { + intent = volume.createAccessIntent(null); + } + if (intent == null) { + intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); + } + Intent intentR = mAndroidFacade.startActivityForResultCode(intent); + switch (intentR.getIntExtra("RESULT_CODE", -1025)) { + case -1025: + throw new Exception(intentR.getStringExtra("EXCEPTION")); + case Activity.RESULT_OK: + Uri uri = intentR.getData(); + DocumentUtil.saveTreeUri(context,rootPath,uri); + return uri; + default: + return null; + } + } + + @Rpc(description = "Document File Rename .") + public boolean documentFileRenameTo ( + @RpcParameter(name = "src") String src, + @RpcParameter(name = "dest") String dest) throws Exception { + return DocumentUtil.renameTo(context,new File(src),new File(dest)); + } + + @Rpc(description = "Document File ( or Tree ) Delete .") + public boolean documentFileDelete ( + @RpcParameter(name = "file or tree") String file) { + return DocumentUtil.delete(context,new File(file)); + } + + @Rpc(description = "Document File Make Directorys .") + public boolean documentFileMkdir ( + @RpcParameter(name = "dir") String dir) { + return DocumentUtil.mkdirs(context,new File(dir)); + } + + @Rpc(description = "Document File Input Stream .") + public String documentFileInputStream ( + @RpcParameter(name = "srcFile") String srcFile, + @RpcParameter(name = "encodingFormat") @RpcDefault("") String encodingFormat, + @RpcParameter(name = "skip") @RpcOptional Integer skip, + @RpcParameter(name = "length") @RpcOptional Integer length) + throws Exception{ + byte[] data; + InputStream fis= DocumentUtil.getInputStream(context,new File(srcFile)); + if(skip!=null) + fis.skip(skip); + int len = fis.available(); + if(length!=null && length documentFileGetStat( + @RpcParameter(name = "path") String path + ){ + DocumentFile documentFile = DocumentUtil.getDocumentFile(new File(path),null,context); + if(documentFile == null) return getFileStat(path); + Map map = new HashMap<>(); + map.put("length",documentFile.length()); + map.put("lastModified",documentFile.lastModified()); + map.put("isDirectory",documentFile.isDirectory()); + map.put("canRead",documentFile.canRead()); + map.put("canWrite",documentFile.canWrite());//外置卡此处为true + return map; + } + + @Rpc(description = "get file stat .") + public Map getFileStat( + @RpcParameter(name = "path") String path + ){ + File file = new File(path); + Map map = new HashMap<>(); + map.put("length",file.length()); + map.put("lastModified",file.lastModified()); + map.put("canRead",file.canRead()); + map.put("canWrite",file.canWrite());//外置卡此处为false + map.put("canExecute",file.canExecute()); + map.put("FreeSpace",file.getFreeSpace()); + map.put("TotalSpace",file.getTotalSpace()); + return map; + } + + public Uri documentTreeAndroid(File file) throws Exception { + String path = file.getAbsolutePath(); + String subPath = path.substring(DocumentUtil.ANDROID_PATH.length()); + SharedPreferences perf = PreferenceManager.getDefaultSharedPreferences(context); + String uriStr = perf.getString(path,null); + Uri uri = null; + DocumentFile documentFile = null; + if(uriStr != null) + uri = Uri.parse(uriStr); + if(uri != null) + documentFile = DocumentFile.fromTreeUri(context, uri); + if(documentFile != null && documentFile.canWrite()) { + addOnSdCardList(path); + return uri; + } + String content = subPath.substring(0,subPath.indexOf("/")); + content = DocumentUtil.ANDROID_CONTENT[0] + subPathToContent(content) + + DocumentUtil.ANDROID_CONTENT[1] + subPathToContent(subPath); + uri = Uri.parse(content); + documentFile = DocumentFile.fromTreeUri(context, uri); + if(documentFile == null) + return null; + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); + intent.setFlags(DocumentUtil.ANDROID_OPEN_INTENT); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, uri); + } + Intent intentR = mAndroidFacade.startActivityForResultCode(intent); + switch (intentR.getIntExtra("RESULT_CODE", -1025)) { + case -1025: + throw new Exception(intentR.getStringExtra("EXCEPTION")); + case Activity.RESULT_OK: + uri = intentR.getData(); + context.getContentResolver().takePersistableUriPermission(uri, DocumentUtil.ANDROID_SAVE_INTENT); + addOnSdCardList(path); + perf.edit().putString(path, uri.toString()).apply(); + return uri; + default: + return null; + } + } + + public String subPathToContent(String subPath) { + if (subPath.endsWith("/")) { + subPath = subPath.substring(0, subPath.length() - 1); + } + return subPath.replace("%","%25").replace("/", "%2F"). + replace(" ","%20"); + } + + private void addOnSdCardList(String path){ + if(!DocumentUtil.sExtSdCardPaths.contains(path)) + DocumentUtil.sExtSdCardPaths.add(path); + } + + @Override + public void shutdown() { + } } \ No newline at end of file diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/EventFacade.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/EventFacade.java index 5405c35..5ec713a 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/EventFacade.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/EventFacade.java @@ -1,375 +1,375 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a.facade; - -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Bundle; - -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.Multimap; -import com.google.common.collect.Multimaps; -import org.qpython.qsl4a.qsl4a.event.Event; -import org.qpython.qsl4a.qsl4a.future.FutureResult; -import org.qpython.qsl4a.qsl4a.jsonrpc.JsonBuilder; -import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; -import org.qpython.qsl4a.qsl4a.rpc.Rpc; -import org.qpython.qsl4a.qsl4a.rpc.RpcDefault; -import org.qpython.qsl4a.qsl4a.rpc.RpcDeprecated; -import org.qpython.qsl4a.qsl4a.rpc.RpcName; -import org.qpython.qsl4a.qsl4a.rpc.RpcOptional; -import org.qpython.qsl4a.qsl4a.rpc.RpcParameter; - - -import java.util.HashMap; -import java.util.List; -import java.util.Queue; -import java.util.Set; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.TimeUnit; - -import org.json.JSONException; - -/** - * Manage the event queue.
- *
- * Usage Notes:
- * EventFacade APIs interact with the Event Queue (a data buffer containing up to 1024 event - * entries).
- * Events are automatically entered into the Event Queue following API calls such as startSensing() - * and startLocating().
- * The Event Facade provides control over how events are entered into (and removed from) the Event - * Queue.
- * The Event Queue provides a useful means of recording background events (such as sensor data) when - * the phone is busy with foreground activities. - * - * @author Felix Arends (felix.arends@gmail.com) - * - */ -public class EventFacade extends RpcReceiver { - /** - * The maximum length of the event queue. Old events will be discarded when this limit is - * exceeded. - */ - private static final int MAX_QUEUE_SIZE = 1024; - private final Queue mEventQueue = new ConcurrentLinkedQueue(); - private final CopyOnWriteArrayList mGlobalEventObservers = - new CopyOnWriteArrayList(); - private final Multimap mNamedEventObservers = Multimaps - .synchronizedListMultimap(ArrayListMultimap. create()); - private EventServer mEventServer = null; - private final HashMap mBroadcastListeners = - new HashMap(); - private final Context mContext; - - public EventFacade(FacadeManager manager) { - super(manager); - mContext = manager.getService().getApplicationContext(); - } - - /** - * Example (python): droid.eventClearBuffer() - */ - @Rpc(description = "Clears all events from the event buffer.") - public void eventClearBuffer() { - mEventQueue.clear(); - } - - /** - * Registers a listener for a new broadcast signal - */ - @Rpc(description = "Registers a listener for a new broadcast signal") - public boolean eventRegisterForBroadcast( - @RpcParameter(name = "category") String category, - @RpcParameter(name = "enqueue", description = "Should this events be added to the event queue or only dispatched") @RpcDefault(value = "true") Boolean enqueue) { - if (mBroadcastListeners.containsKey(category)) { - return false; - } - - BroadcastListener b = new BroadcastListener(this, enqueue.booleanValue()); - IntentFilter c = new IntentFilter(category); - mContext.registerReceiver(b, c); - mBroadcastListeners.put(category, b); - - return true; - } - - @Rpc(description = "Stop listening for a broadcast signal") - public void eventUnregisterForBroadcast(@RpcParameter(name = "category") String category) { - if (!mBroadcastListeners.containsKey(category)) { - return; - } - - mContext.unregisterReceiver(mBroadcastListeners.get(category)); - mBroadcastListeners.remove(category); - } - - @Rpc(description = "Lists all the broadcast signals we are listening for") - public Set eventGetBrodcastCategories() { - return mBroadcastListeners.keySet(); - } - - /** - * Actual data returned in the map will depend on the type of event. - * - *
-   * Example (python):
-   *     import android, time
-   *     droid = android.Android()
-   *     droid.startSensing()
-   *     time.sleep(1)
-   *     droid.eventClearBuffer()
-   *     time.sleep(1)
-   *     e = eventPoll(1).result
-   *     event_entry_number = 0
-   *     x = e[event_entry_ number]['data']['xforce']
-   * 
- * - * e has the format:
- * [{u'data': {u'accuracy': 0, u'pitch': -0.48766891956329345, u'xmag': -5.6875, u'azimuth': - * 0.3312483489513397, u'zforce': 8.3492730000000002, u'yforce': 4.5628165999999997, u'time': - * 1297072704.813, u'ymag': -11.125, u'zmag': -42.375, u'roll': -0.059393649548292161, u'xforce': - * 0.42223078000000003}, u'name': u'sensors', u'time': 1297072704813000L}]
- * x has the string value of the x force data (0.42223078000000003) at the time of the event - * entry. - */ - - @Rpc(description = "Returns and removes the oldest n events (i.e. location or sensor update, etc.) from the event buffer.", returns = "A List of Maps of event properties.") - public List eventPoll( - @RpcParameter(name = "number_of_events") @RpcDefault("1") Integer number_of_events) { - List events = Lists.newArrayList(); - for (int i = 0; i < number_of_events; i++) { - Event event = mEventQueue.poll(); - if (event == null) { - break; - } - events.add(event); - } - return events; - } - - @Rpc(description = "Blocks until an event with the supplied name occurs. The returned event is not removed from the buffer.", returns = "Map of event properties.") - public Event eventWaitFor( - @RpcParameter(name = "eventName") final String eventName, - @RpcParameter(name = "timeout", description = "the maximum time to wait (in ms)") @RpcOptional Integer timeout) - throws InterruptedException { - synchronized (mEventQueue) { // First check to make sure it isn't already there - for (Event event : mEventQueue) { - if (event.getName().equals(eventName)) { - return event; - } - } - } - final FutureResult futureEvent = new FutureResult(); - addNamedEventObserver(eventName, new EventObserver() { - @Override - public void onEventReceived(Event event) { - if (event.getName().equals(eventName)) { - synchronized (futureEvent) { - if (!futureEvent.isDone()) { - futureEvent.set(event); - removeEventObserver(this); - } - } - } - } - }); - if (timeout != null) { - return futureEvent.get(timeout, TimeUnit.MILLISECONDS); - } else { - return futureEvent.get(); - } - } - - @Rpc(description = "Blocks until an event occurs. The returned event is removed from the buffer.", returns = "Map of event properties.") - public Event eventWait( - @RpcParameter(name = "timeout", description = "the maximum time to wait") @RpcOptional Integer timeout) - throws InterruptedException { - Event result = null; - final FutureResult futureEvent = new FutureResult(); - synchronized (mEventQueue) { // Anything in queue? - if (mEventQueue.size() > 0) { - return mEventQueue.poll(); // return it. - } - } - EventObserver observer = new EventObserver() { - @Override - public void onEventReceived(Event event) { // set up observer for any events. - synchronized (futureEvent) { - if (!futureEvent.isDone()) { - futureEvent.set(event); - removeEventObserver(this); - } - mEventQueue.remove(event); - } - } - }; - addGlobalEventObserver(observer); - if (timeout != null) { - result = futureEvent.get(timeout, TimeUnit.MILLISECONDS); - } else { - result = futureEvent.get(); - } - removeEventObserver(observer); // Make quite sure this goes away. - return result; - } - - /** - *
-   * Example:
-   *   import android
-   *   from datetime import datetime
-   *   droid = android.Android()
-   *   t = datetime.now()
-   *   droid.eventPost('Some Event', t)
-   * 
- */ - @Rpc(description = "Post an event to the event queue.") - public void eventPost( - @RpcParameter(name = "name", description = "Name of event") String name, - @RpcParameter(name = "data", description = "Data contained in event.") String data, - @RpcParameter(name = "enqueue", description = "Set to False if you don't want your events to be added to the event queue, just dispatched.") @RpcOptional @RpcDefault("false") Boolean enqueue) { - postEvent(name, data, enqueue.booleanValue()); - } - - /** - * Post an event and queue it - */ - public void postEvent(String name, Object data) { - postEvent(name, data, true); - } - - /** - * Posts an event with to the event queue. - */ - public void postEvent(String name, Object data, boolean enqueue) { - Event event = new Event(name, data); - if (enqueue != false) { - mEventQueue.add(event); - if (mEventQueue.size() > MAX_QUEUE_SIZE) { - mEventQueue.remove(); - } - } - synchronized (mNamedEventObservers) { - for (EventObserver observer : mNamedEventObservers.get(name)) { - observer.onEventReceived(event); - } - } - synchronized (mGlobalEventObservers) { - for (EventObserver observer : mGlobalEventObservers) { - observer.onEventReceived(event); - } - } - } - - @RpcDeprecated(value = "eventPost", release = "r4") - @Rpc(description = "Post an event to the event queue.") - @RpcName(name = "postEvent") - public void rpcPostEvent(@RpcParameter(name = "name") String name, - @RpcParameter(name = "data") String data) { - postEvent(name, data); - } - - @RpcDeprecated(value = "eventPoll", release = "r4") - @Rpc(description = "Returns and removes the oldest event (i.e. location or sensor update, etc.) from the event buffer.", returns = "Map of event properties.") - public Event receiveEvent() { - return mEventQueue.poll(); - } - - @RpcDeprecated(value = "eventWaitFor", release = "r4") - @Rpc(description = "Blocks until an event with the supplied name occurs. The returned event is not removed from the buffer.", returns = "Map of event properties.") - public Event waitForEvent( - @RpcParameter(name = "eventName") final String eventName, - @RpcParameter(name = "timeout", description = "the maximum time to wait") @RpcOptional Integer timeout) - throws InterruptedException { - return eventWaitFor(eventName, timeout); - } - - @Rpc(description = "Opens up a socket where you can read for events posted") - public int startEventDispatcher( - @RpcParameter(name = "port", description = "Port to use") @RpcDefault("0") @RpcOptional() Integer port) { - if (mEventServer == null) { - mEventServer = new EventServer(port); - addGlobalEventObserver(mEventServer); - } - return mEventServer.getAddress().getPort(); - } - - @Rpc(description = "Stops the event server, you can't read in the port anymore") - public void stopEventDispatcher() throws RuntimeException { - if (mEventServer == null) { - throw new RuntimeException("Not running"); - } - mEventServer.shutdown(); - removeEventObserver(mEventServer); - mEventServer = null; - return; - } - - @Override - public void shutdown() { - try { - stopEventDispatcher(); - } catch (Exception err) { - } - // let others (like webviews) know we're going down - postEvent("sl4a", "{\"shutdown\": \"event-facade\"}"); - } - - public void addNamedEventObserver(String eventName, EventObserver observer) { - mNamedEventObservers.put(eventName, observer); - } - - public void addGlobalEventObserver(EventObserver observer) { - mGlobalEventObservers.add(observer); - } - - public void removeEventObserver(EventObserver observer) { - mNamedEventObservers.removeAll(observer); - mGlobalEventObservers.remove(observer); - } - - public interface EventObserver { - public void onEventReceived(Event event); - } - - public class BroadcastListener extends android.content.BroadcastReceiver { - private EventFacade mParent; - private boolean mEnQueue; - - public BroadcastListener(EventFacade parent, boolean enqueue) { - mParent = parent; - mEnQueue = enqueue; - } - - @Override - public void onReceive(Context context, Intent intent) { - Bundle data = (Bundle) intent.getExtras().clone(); - data.putString("action", intent.getAction()); - try { - mParent.eventPost("sl4a", JsonBuilder.build(data).toString(), mEnQueue); - } catch (JSONException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - } -} +/* + * Copyright (C) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.qpython.qsl4a.qsl4a.facade; + +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; +import org.qpython.qsl4a.qsl4a.event.Event; +import org.qpython.qsl4a.qsl4a.future.FutureResult; +import org.qpython.qsl4a.qsl4a.jsonrpc.JsonBuilder; +import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; +import org.qpython.qsl4a.qsl4a.rpc.Rpc; +import org.qpython.qsl4a.qsl4a.rpc.RpcDefault; +import org.qpython.qsl4a.qsl4a.rpc.RpcDeprecated; +import org.qpython.qsl4a.qsl4a.rpc.RpcName; +import org.qpython.qsl4a.qsl4a.rpc.RpcOptional; +import org.qpython.qsl4a.qsl4a.rpc.RpcParameter; + + +import java.util.HashMap; +import java.util.List; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.TimeUnit; + +import org.json.JSONException; + +/** + * Manage the event queue.
+ *
+ * Usage Notes:
+ * EventFacade APIs interact with the Event Queue (a data buffer containing up to 1024 event + * entries).
+ * Events are automatically entered into the Event Queue following API calls such as startSensing() + * and startLocating().
+ * The Event Facade provides control over how events are entered into (and removed from) the Event + * Queue.
+ * The Event Queue provides a useful means of recording background events (such as sensor data) when + * the phone is busy with foreground activities. + * + * @author Felix Arends (felix.arends@gmail.com) + * + */ +public class EventFacade extends RpcReceiver { + /** + * The maximum length of the event queue. Old events will be discarded when this limit is + * exceeded. + */ + private static final int MAX_QUEUE_SIZE = 1024; + private final Queue mEventQueue = new ConcurrentLinkedQueue(); + private final CopyOnWriteArrayList mGlobalEventObservers = + new CopyOnWriteArrayList(); + private final Multimap mNamedEventObservers = Multimaps + .synchronizedListMultimap(ArrayListMultimap. create()); + private EventServer mEventServer = null; + private final HashMap mBroadcastListeners = + new HashMap(); + private final Context mContext; + + public EventFacade(FacadeManager manager) { + super(manager); + mContext = manager.getService().getApplicationContext(); + } + + /** + * Example (python): droid.eventClearBuffer() + */ + @Rpc(description = "Clears all events from the event buffer.") + public void eventClearBuffer() { + mEventQueue.clear(); + } + + /** + * Registers a listener for a new broadcast signal + */ + @Rpc(description = "Registers a listener for a new broadcast signal") + public boolean eventRegisterForBroadcast( + @RpcParameter(name = "category") String category, + @RpcParameter(name = "enqueue", description = "Should this events be added to the event queue or only dispatched") @RpcDefault(value = "true") Boolean enqueue) { + if (mBroadcastListeners.containsKey(category)) { + return false; + } + + BroadcastListener b = new BroadcastListener(this, enqueue.booleanValue()); + IntentFilter c = new IntentFilter(category); + mContext.registerReceiver(b, c); + mBroadcastListeners.put(category, b); + + return true; + } + + @Rpc(description = "Stop listening for a broadcast signal") + public void eventUnregisterForBroadcast(@RpcParameter(name = "category") String category) { + if (!mBroadcastListeners.containsKey(category)) { + return; + } + + mContext.unregisterReceiver(mBroadcastListeners.get(category)); + mBroadcastListeners.remove(category); + } + + @Rpc(description = "Lists all the broadcast signals we are listening for") + public Set eventGetBrodcastCategories() { + return mBroadcastListeners.keySet(); + } + + /** + * Actual data returned in the map will depend on the type of event. + * + *
+   * Example (python):
+   *     import android, time
+   *     droid = android.Android()
+   *     droid.startSensing()
+   *     time.sleep(1)
+   *     droid.eventClearBuffer()
+   *     time.sleep(1)
+   *     e = eventPoll(1).result
+   *     event_entry_number = 0
+   *     x = e[event_entry_ number]['data']['xforce']
+   * 
+ * + * e has the format:
+ * [{u'data': {u'accuracy': 0, u'pitch': -0.48766891956329345, u'xmag': -5.6875, u'azimuth': + * 0.3312483489513397, u'zforce': 8.3492730000000002, u'yforce': 4.5628165999999997, u'time': + * 1297072704.813, u'ymag': -11.125, u'zmag': -42.375, u'roll': -0.059393649548292161, u'xforce': + * 0.42223078000000003}, u'name': u'sensors', u'time': 1297072704813000L}]
+ * x has the string value of the x force data (0.42223078000000003) at the time of the event + * entry. + */ + + @Rpc(description = "Returns and removes the oldest n events (i.e. location or sensor update, etc.) from the event buffer.", returns = "A List of Maps of event properties.") + public List eventPoll( + @RpcParameter(name = "number_of_events") @RpcDefault("1") Integer number_of_events) { + List events = Lists.newArrayList(); + for (int i = 0; i < number_of_events; i++) { + Event event = mEventQueue.poll(); + if (event == null) { + break; + } + events.add(event); + } + return events; + } + + @Rpc(description = "Blocks until an event with the supplied name occurs. The returned event is not removed from the buffer.", returns = "Map of event properties.") + public Event eventWaitFor( + @RpcParameter(name = "eventName") final String eventName, + @RpcParameter(name = "timeout", description = "the maximum time to wait (in ms)") @RpcOptional Integer timeout) + throws InterruptedException { + synchronized (mEventQueue) { // First check to make sure it isn't already there + for (Event event : mEventQueue) { + if (event.getName().equals(eventName)) { + return event; + } + } + } + final FutureResult futureEvent = new FutureResult(); + addNamedEventObserver(eventName, new EventObserver() { + @Override + public void onEventReceived(Event event) { + if (event.getName().equals(eventName)) { + synchronized (futureEvent) { + if (!futureEvent.isDone()) { + futureEvent.set(event); + removeEventObserver(this); + } + } + } + } + }); + if (timeout != null) { + return futureEvent.get(timeout, TimeUnit.MILLISECONDS); + } else { + return futureEvent.get(); + } + } + + @Rpc(description = "Blocks until an event occurs. The returned event is removed from the buffer.", returns = "Map of event properties.") + public Event eventWait( + @RpcParameter(name = "timeout", description = "the maximum time to wait") @RpcOptional Integer timeout) + throws InterruptedException { + Event result = null; + final FutureResult futureEvent = new FutureResult(); + synchronized (mEventQueue) { // Anything in queue? + if (mEventQueue.size() > 0) { + return mEventQueue.poll(); // return it. + } + } + EventObserver observer = new EventObserver() { + @Override + public void onEventReceived(Event event) { // set up observer for any events. + synchronized (futureEvent) { + if (!futureEvent.isDone()) { + futureEvent.set(event); + removeEventObserver(this); + } + mEventQueue.remove(event); + } + } + }; + addGlobalEventObserver(observer); + if (timeout != null) { + result = futureEvent.get(timeout, TimeUnit.MILLISECONDS); + } else { + result = futureEvent.get(); + } + removeEventObserver(observer); // Make quite sure this goes away. + return result; + } + + /** + *
+   * Example:
+   *   import android
+   *   from datetime import datetime
+   *   droid = android.Android()
+   *   t = datetime.now()
+   *   droid.eventPost('Some Event', t)
+   * 
+ */ + @Rpc(description = "Post an event to the event queue.") + public void eventPost( + @RpcParameter(name = "name", description = "Name of event") String name, + @RpcParameter(name = "data", description = "Data contained in event.") String data, + @RpcParameter(name = "enqueue", description = "Set to False if you don't want your events to be added to the event queue, just dispatched.") @RpcOptional @RpcDefault("false") Boolean enqueue) { + postEvent(name, data, enqueue.booleanValue()); + } + + /** + * Post an event and queue it + */ + public void postEvent(String name, Object data) { + postEvent(name, data, true); + } + + /** + * Posts an event with to the event queue. + */ + public void postEvent(String name, Object data, boolean enqueue) { + Event event = new Event(name, data); + if (enqueue) { + mEventQueue.add(event); + if (mEventQueue.size() > MAX_QUEUE_SIZE) { + mEventQueue.remove(); + } + } + synchronized (mNamedEventObservers) { + for (EventObserver observer : mNamedEventObservers.get(name)) { + observer.onEventReceived(event); + } + } + synchronized (mGlobalEventObservers) { + for (EventObserver observer : mGlobalEventObservers) { + observer.onEventReceived(event); + } + } + } + + @RpcDeprecated(value = "eventPost", release = "r4") + @Rpc(description = "Post an event to the event queue.") + @RpcName(name = "postEvent") + public void rpcPostEvent(@RpcParameter(name = "name") String name, + @RpcParameter(name = "data") String data) { + postEvent(name, data); + } + + @RpcDeprecated(value = "eventPoll", release = "r4") + @Rpc(description = "Returns and removes the oldest event (i.e. location or sensor update, etc.) from the event buffer.", returns = "Map of event properties.") + public Event receiveEvent() { + return mEventQueue.poll(); + } + + @RpcDeprecated(value = "eventWaitFor", release = "r4") + @Rpc(description = "Blocks until an event with the supplied name occurs. The returned event is not removed from the buffer.", returns = "Map of event properties.") + public Event waitForEvent( + @RpcParameter(name = "eventName") final String eventName, + @RpcParameter(name = "timeout", description = "the maximum time to wait") @RpcOptional Integer timeout) + throws InterruptedException { + return eventWaitFor(eventName, timeout); + } + + @Rpc(description = "Opens up a socket where you can read for events posted") + public int startEventDispatcher( + @RpcParameter(name = "port", description = "Port to use") @RpcDefault("0") @RpcOptional() Integer port) { + if (mEventServer == null) { + mEventServer = new EventServer(port); + addGlobalEventObserver(mEventServer); + } + return mEventServer.getAddress().getPort(); + } + + @Rpc(description = "Stops the event server, you can't read in the port anymore") + public void stopEventDispatcher() throws RuntimeException { + if (mEventServer == null) { + throw new RuntimeException("Not running"); + } + mEventServer.shutdown(); + removeEventObserver(mEventServer); + mEventServer = null; + return; + } + + @Override + public void shutdown() { + try { + stopEventDispatcher(); + } catch (Exception err) { + } + // let others (like webviews) know we're going down + postEvent("sl4a", "{\"shutdown\": \"event-facade\"}"); + } + + public void addNamedEventObserver(String eventName, EventObserver observer) { + mNamedEventObservers.put(eventName, observer); + } + + public void addGlobalEventObserver(EventObserver observer) { + mGlobalEventObservers.add(observer); + } + + public void removeEventObserver(EventObserver observer) { + mNamedEventObservers.removeAll(observer); + mGlobalEventObservers.remove(observer); + } + + public interface EventObserver { + public void onEventReceived(Event event); + } + + public class BroadcastListener extends android.content.BroadcastReceiver { + private EventFacade mParent; + private boolean mEnQueue; + + public BroadcastListener(EventFacade parent, boolean enqueue) { + mParent = parent; + mEnQueue = enqueue; + } + + @Override + public void onReceive(Context context, Intent intent) { + Bundle data = (Bundle) intent.getExtras().clone(); + data.putString("action", intent.getAction()); + try { + mParent.eventPost("sl4a", JsonBuilder.build(data).toString(), mEnQueue); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + } +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/EventServer.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/EventServer.java index 9f7030c..d201f71 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/EventServer.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/EventServer.java @@ -1,119 +1,119 @@ -/* - * Copyright (C) 2001 Naranjo Manuel Francisco - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package org.qpython.qsl4a.qsl4a.facade; - -import org.qpython.qsl4a.qsl4a.LogUtil; -import org.qpython.qsl4a.qsl4a.SimpleServer; -import org.qpython.qsl4a.qsl4a.event.Event; -import org.qpython.qsl4a.qsl4a.jsonrpc.JsonBuilder; - -import java.io.IOException; -import java.io.PrintWriter; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.util.Vector; -import java.util.concurrent.CountDownLatch; - -import org.json.JSONException; - -/** - * An Event Forwarding server that forwards events from the rpc queue in realtime to listener - * clients. - * - * @author Manuel Naranjo (manuel@aircable.net) - * @see com.googlecode.android_scripting.SimpleServer - */ -public class EventServer extends SimpleServer implements EventFacade.EventObserver { - private static final Vector mListeners = new Vector(); - private InetSocketAddress address = null; - - public EventServer() { - this(0); - } - - public EventServer(int port) { - address = startAllInterfaces(port); - } - - public InetSocketAddress getAddress() { - return address; - } - - @Override - public void shutdown() { - onEventReceived(new Event("sl4a", "{\"shutdown\": \"event-server\"}")); - for (Listener listener : mListeners) { - mListeners.remove(listener); - listener.lock.countDown(); - } - super.shutdown(); - } - - @Override - protected void handleConnection(Socket socket) throws IOException { - Listener l = new Listener(socket); - LogUtil.v("Adding EventServer listener " + socket.getPort()); - mListeners.add(l); - // we are running in the socket accept thread - // wait until the event dispatcher gets us the events - // or we die, what ever happens first - try { - l.lock.await(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - try { - l.sock.close(); - } catch (IOException e) { - e.printStackTrace(); - } - LogUtil.v("Ending EventServer listener " + socket.getPort()); - } - - @Override - public void onEventReceived(Event event) { - Object result = null; - try { - result = JsonBuilder.build(event); - } catch (JSONException e) { - return; - } - - LogUtil.v("EventServer dispatching " + result); - - for (Listener listener : mListeners) { - if (!listener.out.checkError()) { - listener.out.write(result + "\n"); - listener.out.flush(); - } else { - // let the socket accept thread we're done - mListeners.remove(listener); - listener.lock.countDown(); - } - } - } - - private class Listener { - private Socket sock; - private PrintWriter out; - private CountDownLatch lock = new CountDownLatch(1); - - public Listener(Socket l) throws IOException { - sock = l; - out = new PrintWriter(l.getOutputStream(), true); - } - } -} +/* + * Copyright (C) 2001 Naranjo Manuel Francisco + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.qpython.qsl4a.qsl4a.facade; + +import org.qpython.qsl4a.qsl4a.LogUtil; +import org.qpython.qsl4a.qsl4a.SimpleServer; +import org.qpython.qsl4a.qsl4a.event.Event; +import org.qpython.qsl4a.qsl4a.jsonrpc.JsonBuilder; + +import java.io.IOException; +import java.io.PrintWriter; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.Vector; +import java.util.concurrent.CountDownLatch; + +import org.json.JSONException; + +/** + * An Event Forwarding server that forwards events from the rpc queue in realtime to listener + * clients. + * + * @author Manuel Naranjo (manuel@aircable.net) + * @see com.googlecode.android_scripting.SimpleServer + */ +public class EventServer extends SimpleServer implements EventFacade.EventObserver { + private static final Vector mListeners = new Vector(); + private InetSocketAddress address = null; + + public EventServer() { + this(0); + } + + public EventServer(int port) { + address = startAllInterfaces(port); + } + + public InetSocketAddress getAddress() { + return address; + } + + @Override + public void shutdown() { + onEventReceived(new Event("sl4a", "{\"shutdown\": \"event-server\"}")); + for (Listener listener : mListeners) { + mListeners.remove(listener); + listener.lock.countDown(); + } + super.shutdown(); + } + + @Override + protected void handleConnection(Socket socket) throws IOException { + Listener l = new Listener(socket); + LogUtil.v("Adding EventServer listener " + socket.getPort()); + mListeners.add(l); + // we are running in the socket accept thread + // wait until the event dispatcher gets us the events + // or we die, what ever happens first + try { + l.lock.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + try { + l.sock.close(); + } catch (IOException e) { + e.printStackTrace(); + } + LogUtil.v("Ending EventServer listener " + socket.getPort()); + } + + @Override + public void onEventReceived(Event event) { + Object result; + try { + result = JsonBuilder.build(event); + } catch (JSONException e) { + return; + } + + LogUtil.v("EventServer dispatching " + result); + + for (Listener listener : mListeners) { + if (!listener.out.checkError()) { + listener.out.write(result + "\n"); + listener.out.flush(); + } else { + // let the socket accept thread we're done + mListeners.remove(listener); + listener.lock.countDown(); + } + } + } + + private class Listener { + private Socket sock; + private PrintWriter out; + private CountDownLatch lock = new CountDownLatch(1); + + public Listener(Socket l) throws IOException { + sock = l; + out = new PrintWriter(l.getOutputStream(), true); + } + } +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/FacadeConfiguration.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/FacadeConfiguration.java index 2e5a4af..325b4e9 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/FacadeConfiguration.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/FacadeConfiguration.java @@ -1,193 +1,177 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.qpython.qsl4a.qsl4a.facade; - -import com.google.common.collect.Maps; -import org.qpython.qsl4a.qsl4a.LogUtil; -import org.qpython.qsl4a.qsl4a.facade.ui.UiFacade; -import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; -import org.qpython.qsl4a.qsl4a.rpc.MethodDescriptor; -import org.qpython.qsl4a.qsl4a.rpc.RpcDeprecated; -import org.qpython.qsl4a.qsl4a.rpc.RpcMinSdk; -import org.qpython.qsl4a.qsl4a.rpc.RpcStartEvent; -import org.qpython.qsl4a.qsl4a.rpc.RpcStopEvent; - -import org.qpython.qsl4a.qsl4a.facade.usb.USBHostSerialFacade; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.SortedMap; -import java.util.TreeMap; - -/** - * Encapsulates the list of supported facades and their construction. - * - * @author Damon Kohler (damonkohler@gmail.com) - * @author Igor Karp (igor.v.karp@gmail.com) - */ -@SuppressWarnings("deprecation") -public class FacadeConfiguration { - private final static Set> sFacadeClassList; - private final static SortedMap sRpcs = - new TreeMap(); - - private static int sSdkLevel; - - static { - - if (android.os.Build.VERSION.SDK == null) { - sSdkLevel = 8; // For documentation purposes. - } else { - try { - sSdkLevel = Integer.parseInt(android.os.Build.VERSION.SDK); - } catch (NumberFormatException e) { - LogUtil.e(e); - } - } - - sFacadeClassList = new HashSet>(); - sFacadeClassList.add(AndroidFacade.class); - sFacadeClassList.add(ApplicationManagerFacade.class); - sFacadeClassList.add(CameraFacade.class); - sFacadeClassList.add(CommonIntentsFacade.class); - sFacadeClassList.add(ContactsFacade.class); - sFacadeClassList.add(EventFacade.class); - sFacadeClassList.add(LocationFacade.class); - sFacadeClassList.add(PhoneFacade.class); - sFacadeClassList.add(MediaRecorderFacade.class); - sFacadeClassList.add(SensorManagerFacade.class); - sFacadeClassList.add(SettingsFacade.class); - sFacadeClassList.add(SmsFacade.class); - sFacadeClassList.add(SpeechRecognitionFacade.class); - sFacadeClassList.add(ToneGeneratorFacade.class); - sFacadeClassList.add(WakeLockFacade.class); - sFacadeClassList.add(WifiFacade.class); - sFacadeClassList.add(UiFacade.class); - sFacadeClassList.add(BatteryManagerFacade.class); - sFacadeClassList.add(ActivityResultFacade.class); - sFacadeClassList.add(MediaPlayerFacade.class); - sFacadeClassList.add(PreferencesFacade.class); - sFacadeClassList.add(QPyInterfaceFacade.class); - sFacadeClassList.add(USBHostSerialFacade.class); - sFacadeClassList.add(VideoFacade.class); - sFacadeClassList.add(FloatViewFacade.class); - sFacadeClassList.add(CipherFacade.class); - sFacadeClassList.add(DocumentFileFacade.class); - - //if (sSdkLevel >= 4) { - sFacadeClassList.add(TextToSpeechFacade.class); - //} else { - //sFacadeClassList.add(EyesFreeFacade.class); - //} - - //if (sSdkLevel >= 5) { - sFacadeClassList.add(BluetoothFacade.class); - //} - - //if (sSdkLevel >= 7) { - sFacadeClassList.add(SignalStrengthFacade.class); - //} - - //if (sSdkLevel >= 8) { - sFacadeClassList.add(WebCamFacade.class); - //} - - for (Class recieverClass : sFacadeClassList) { - for (MethodDescriptor rpcMethod : MethodDescriptor.collectFrom(recieverClass)) { - sRpcs.put(rpcMethod.getName(), rpcMethod); - } - } - } - - private FacadeConfiguration() { - // Utility class. - } - - public static int getSdkLevel() { - return sSdkLevel; - } - - /** Returns a list of {@link MethodDescriptor} objects for all facades. */ - public static List collectMethodDescriptors() { - return new ArrayList(sRpcs.values()); - } - - /** - * Returns a list of not deprecated {@link MethodDescriptor} objects for facades supported by the - * current SDK version. - */ - public static List collectSupportedMethodDescriptors() { - List list = new ArrayList(); - for (MethodDescriptor descriptor : sRpcs.values()) { - Method method = descriptor.getMethod(); - if (method.isAnnotationPresent(RpcDeprecated.class)) { - continue; - } else if (method.isAnnotationPresent(RpcMinSdk.class)) { - int requiredSdkLevel = method.getAnnotation(RpcMinSdk.class).value(); - if (sSdkLevel < requiredSdkLevel) { - continue; - } - } - list.add(descriptor); - } - return list; - } - - public static Map collectStartEventMethodDescriptors() { - Map map = Maps.newHashMap(); - for (MethodDescriptor descriptor : sRpcs.values()) { - Method method = descriptor.getMethod(); - if (method.isAnnotationPresent(RpcStartEvent.class)) { - String eventName = method.getAnnotation(RpcStartEvent.class).value(); - if (map.containsKey(eventName)) { - throw new RuntimeException("Duplicate start event method descriptor found."); - } - map.put(eventName, descriptor); - } - } - return map; - } - - public static Map collectStopEventMethodDescriptors() { - Map map = Maps.newHashMap(); - for (MethodDescriptor descriptor : sRpcs.values()) { - Method method = descriptor.getMethod(); - if (method.isAnnotationPresent(RpcStopEvent.class)) { - String eventName = method.getAnnotation(RpcStopEvent.class).value(); - if (map.containsKey(eventName)) { - throw new RuntimeException("Duplicate stop event method descriptor found."); - } - map.put(eventName, descriptor); - } - } - return map; - } - - /** Returns a method by name. */ - public static MethodDescriptor getMethodDescriptor(String name) { - return sRpcs.get(name); - } - - public static Collection> getFacadeClasses() { - return sFacadeClassList; - } -} +/* + * Copyright (C) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.qpython.qsl4a.qsl4a.facade; + +import android.os.Build; + +import com.google.common.collect.Maps; + +import org.qpython.qsl4a.qsl4a.facade.ui.UiConfig; +import org.qpython.qsl4a.qsl4a.facade.ui.UiFacade; +import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; +import org.qpython.qsl4a.qsl4a.rpc.MethodDescriptor; +import org.qpython.qsl4a.qsl4a.rpc.RpcDeprecated; +import org.qpython.qsl4a.qsl4a.rpc.RpcMinSdk; +import org.qpython.qsl4a.qsl4a.rpc.RpcStartEvent; +import org.qpython.qsl4a.qsl4a.rpc.RpcStopEvent; + +import org.qpython.qsl4a.qsl4a.facade.usb.USBHostSerialFacade; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; + +/** + * Encapsulates the list of supported facades and their construction. + * + * @author Damon Kohler (damonkohler@gmail.com) + * @author Igor Karp (igor.v.karp@gmail.com) + */ +@SuppressWarnings("deprecation") +public class FacadeConfiguration { + private final static Set> sFacadeClassList; + private final static SortedMap sRpcs = + new TreeMap(); + + private static int sSdkLevel; + + static { + + sSdkLevel = Build.VERSION.SDK_INT; + + sFacadeClassList = new HashSet>(); + sFacadeClassList.add(AndroidFacade.class); + sFacadeClassList.add(ApplicationManagerFacade.class); + sFacadeClassList.add(CameraFacade.class); + sFacadeClassList.add(CommonIntentsFacade.class); + sFacadeClassList.add(ContactsFacade.class); + sFacadeClassList.add(EventFacade.class); + sFacadeClassList.add(LocationFacade.class); + sFacadeClassList.add(PhoneFacade.class); + sFacadeClassList.add(MediaRecorderFacade.class); + sFacadeClassList.add(SensorManagerFacade.class); + sFacadeClassList.add(SettingsFacade.class); + sFacadeClassList.add(SmsFacade.class); + sFacadeClassList.add(SpeechRecognitionFacade.class); + sFacadeClassList.add(ToneGeneratorFacade.class); + sFacadeClassList.add(WakeLockFacade.class); + sFacadeClassList.add(WifiFacade.class); + sFacadeClassList.add(UiFacade.class); + sFacadeClassList.add(BatteryManagerFacade.class); + //sFacadeClassList.add(ActivityResultFacade.class); + sFacadeClassList.add(MediaPlayerFacade.class); + sFacadeClassList.add(PreferencesFacade.class); + sFacadeClassList.add(QPyInterfaceFacade.class); + sFacadeClassList.add(USBHostSerialFacade.class); + sFacadeClassList.add(VideoFacade.class); + sFacadeClassList.add(CipherFacade.class); + sFacadeClassList.add(TextToSpeechFacade.class); + sFacadeClassList.add(BluetoothFacade.class); + sFacadeClassList.add(SignalStrengthFacade.class); + sFacadeClassList.add(WebCamFacade.class); + sFacadeClassList.add(FloatViewFacade.class); + sFacadeClassList.add(DocumentFileFacade.class); + sFacadeClassList.add(HarmonyOsFacade.class); + sFacadeClassList.add(UiConfig.class); + sFacadeClassList.add(FtpFacade.class); + + for (Class recieverClass : sFacadeClassList) { + for (MethodDescriptor rpcMethod : MethodDescriptor.collectFrom(recieverClass)) { + sRpcs.put(rpcMethod.getName(), rpcMethod); + } + } + } + + private FacadeConfiguration() { + // Utility class. + } + + public static int getSdkLevel() { + return sSdkLevel; + } + + /** Returns a list of {@link MethodDescriptor} objects for all facades. */ + public static List collectMethodDescriptors() { + return new ArrayList(sRpcs.values()); + } + + /** + * Returns a list of not deprecated {@link MethodDescriptor} objects for facades supported by the + * current SDK version. + */ + public static List collectSupportedMethodDescriptors() { + List list = new ArrayList(); + for (MethodDescriptor descriptor : sRpcs.values()) { + Method method = descriptor.getMethod(); + if (method.isAnnotationPresent(RpcDeprecated.class)) { + continue; + } else if (method.isAnnotationPresent(RpcMinSdk.class)) { + int requiredSdkLevel = method.getAnnotation(RpcMinSdk.class).value(); + if (sSdkLevel < requiredSdkLevel) { + continue; + } + } + list.add(descriptor); + } + return list; + } + + public static Map collectStartEventMethodDescriptors() { + Map map = Maps.newHashMap(); + for (MethodDescriptor descriptor : sRpcs.values()) { + Method method = descriptor.getMethod(); + if (method.isAnnotationPresent(RpcStartEvent.class)) { + String eventName = method.getAnnotation(RpcStartEvent.class).value(); + if (map.containsKey(eventName)) { + throw new RuntimeException("Duplicate start event method descriptor found."); + } + map.put(eventName, descriptor); + } + } + return map; + } + + public static Map collectStopEventMethodDescriptors() { + Map map = Maps.newHashMap(); + for (MethodDescriptor descriptor : sRpcs.values()) { + Method method = descriptor.getMethod(); + if (method.isAnnotationPresent(RpcStopEvent.class)) { + String eventName = method.getAnnotation(RpcStopEvent.class).value(); + if (map.containsKey(eventName)) { + throw new RuntimeException("Duplicate stop event method descriptor found."); + } + map.put(eventName, descriptor); + } + } + return map; + } + + /** Returns a method by name. */ + public static MethodDescriptor getMethodDescriptor(String name) { + return sRpcs.get(name); + } + + public static Collection> getFacadeClasses() { + return sFacadeClassList; + } +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/FacadeManager.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/FacadeManager.java index 76c91db..2db038e 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/FacadeManager.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/FacadeManager.java @@ -1,79 +1,79 @@ -package org.qpython.qsl4a.qsl4a.facade; - -import android.app.Service; -import android.content.Intent; - -import org.qpython.qsl4a.qsl4a.LogUtil; -import org.qpython.qsl4a.qsl4a.exception.Sl4aException; -import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; -import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiverManager; -import org.qpython.qsl4a.qsl4a.rpc.RpcDeprecated; -import org.qpython.qsl4a.qsl4a.rpc.RpcMinSdk; - - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Collection; - -public class FacadeManager extends RpcReceiverManager { - - private final Service mService; - private final Intent mIntent; - private int mSdkLevel; - - public FacadeManager(int sdkLevel, Service service, Intent intent, - Collection> classList) { - super(classList); - mSdkLevel = sdkLevel; - mService = service; - mIntent = intent; - } - - public int getSdkLevel() { - return mSdkLevel; - } - - public Service getService() { - return mService; - } - - public Intent getIntent() { - return mIntent; - } - - @Override - public Object invoke(Class clazz, Method method, Object[] args) - throws Exception { - try { - if (method.isAnnotationPresent(RpcDeprecated.class)) { - String replacedBy = method.getAnnotation(RpcDeprecated.class).value(); - String title = method.getName() + " is deprecated"; - LogUtil.notify(mService, title, title, String.format("Please use %s instead.", replacedBy)); - } else if (method.isAnnotationPresent(RpcMinSdk.class)) { - int requiredSdkLevel = method.getAnnotation(RpcMinSdk.class).value(); - if (mSdkLevel < requiredSdkLevel) { - throw new Sl4aException(String.format("%s requires API level %d, current level is %d", - method.getName(), requiredSdkLevel, mSdkLevel)); - } - } - return super.invoke(clazz, method, args); - } catch (InvocationTargetException e) { - if (e.getCause() instanceof SecurityException) { - LogUtil.notify(mService, "RPC invoke failed...", mService.getPackageName(), e.getCause() - .getMessage()); - } - throw e; - } - } - - public AndroidFacade.Resources getAndroidFacadeResources() { - return new AndroidFacade.Resources() { - @Override - public int getLogo48() { - // TODO(Alexey): As an alternative, ask application for resource ids. - String packageName = mService.getApplication().getPackageName(); - return mService.getResources().getIdentifier("script_logo_48", "drawable", packageName); - } - }; - } -} +package org.qpython.qsl4a.qsl4a.facade; + +import android.app.Service; +import android.content.Intent; + +import org.qpython.qsl4a.qsl4a.LogUtil; +import org.qpython.qsl4a.qsl4a.exception.Sl4aException; +import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; +import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiverManager; +import org.qpython.qsl4a.qsl4a.rpc.RpcDeprecated; +import org.qpython.qsl4a.qsl4a.rpc.RpcMinSdk; + + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collection; + +public class FacadeManager extends RpcReceiverManager { + + private final Service mService; + private final Intent mIntent; + private int mSdkLevel; + + public FacadeManager(int sdkLevel, Service service, Intent intent, + Collection> classList) { + super(classList); + mSdkLevel = sdkLevel; + mService = service; + mIntent = intent; + } + + public int getSdkLevel() { + return mSdkLevel; + } + + public Service getService() { + return mService; + } + + public Intent getIntent() { + return mIntent; + } + + @Override + public Object invoke(Class clazz, Method method, Object[] args) + throws Exception { + try { + if (method.isAnnotationPresent(RpcDeprecated.class)) { + String replacedBy = method.getAnnotation(RpcDeprecated.class).value(); + String title = method.getName() + " is deprecated"; + LogUtil.notify(mService, title, title, String.format("Please use %s instead.", replacedBy)); + } else if (method.isAnnotationPresent(RpcMinSdk.class)) { + int requiredSdkLevel = method.getAnnotation(RpcMinSdk.class).value(); + if (mSdkLevel < requiredSdkLevel) { + throw new Sl4aException(String.format("%s requires API level %d, current level is %d", + method.getName(), requiredSdkLevel, mSdkLevel)); + } + } + return super.invoke(clazz, method, args); + } catch (InvocationTargetException e) { + if (e.getCause() instanceof SecurityException) { + LogUtil.notify(mService, "RPC invoke failed...", mService.getPackageName(), e.getCause() + .getMessage()); + } + throw e; + } + } + + public AndroidFacade.Resources getAndroidFacadeResources() { + return new AndroidFacade.Resources() { + /*@Override + public int getLogo48() { + // TODO(Alexey): As an alternative, ask application for resource ids. + String packageName = mService.getApplication().getPackageName(); + return mService.getResources().getIdentifier("script_logo_48", "drawable", packageName); + }*/ + }; + } +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/FacadeManagerFactory.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/FacadeManagerFactory.java index 1b37872..3297526 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/FacadeManagerFactory.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/FacadeManagerFactory.java @@ -1,42 +1,42 @@ -package org.qpython.qsl4a.qsl4a.facade; - -import android.app.Service; -import android.content.Intent; - - -import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; -import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiverManager; -import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiverManagerFactory; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -public class FacadeManagerFactory implements RpcReceiverManagerFactory { - - private final int mSdkLevel; - private final Service mService; - private final Intent mIntent; - private final Collection> mClassList; - private final List mFacadeManagers; - - public FacadeManagerFactory(int sdkLevel, Service service, Intent intent, - Collection> classList) { - mSdkLevel = sdkLevel; - mService = service; - mIntent = intent; - mClassList = classList; - mFacadeManagers = new ArrayList(); - } - - public FacadeManager create() { - FacadeManager facadeManager = new FacadeManager(mSdkLevel, mService, mIntent, mClassList); - mFacadeManagers.add(facadeManager); - return facadeManager; - } - - @Override - public List getRpcReceiverManagers() { - return mFacadeManagers; - } -} +package org.qpython.qsl4a.qsl4a.facade; + +import android.app.Service; +import android.content.Intent; + + +import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; +import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiverManager; +import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiverManagerFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class FacadeManagerFactory implements RpcReceiverManagerFactory { + + private final int mSdkLevel; + private final Service mService; + private final Intent mIntent; + private final Collection> mClassList; + private final List mFacadeManagers; + + public FacadeManagerFactory(int sdkLevel, Service service, Intent intent, + Collection> classList) { + mSdkLevel = sdkLevel; + mService = service; + mIntent = intent; + mClassList = classList; + mFacadeManagers = new ArrayList(); + } + + public FacadeManager create() { + FacadeManager facadeManager = new FacadeManager(mSdkLevel, mService, mIntent, mClassList); + mFacadeManagers.add(facadeManager); + return facadeManager; + } + + @Override + public List getRpcReceiverManagers() { + return mFacadeManagers; + } +} diff --git a/src/main/java/org/qpython/qsl4a/qsl4a/facade/FloatViewFacade.java b/src/main/java/org/qpython/qsl4a/qsl4a/facade/FloatViewFacade.java index 925606f..c11c78f 100644 --- a/src/main/java/org/qpython/qsl4a/qsl4a/facade/FloatViewFacade.java +++ b/src/main/java/org/qpython/qsl4a/qsl4a/facade/FloatViewFacade.java @@ -1,230 +1,185 @@ -package org.qpython.qsl4a.qsl4a.facade; - -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.os.Handler; -import android.os.Message; -import android.view.WindowManager; -import android.widget.Button; - -import org.json.JSONObject; -import org.qpython.qsl4a.R; -import org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver; -import org.qpython.qsl4a.qsl4a.rpc.Rpc; -import org.qpython.qsl4a.qsl4a.rpc.RpcDefault; -import org.qpython.qsl4a.qsl4a.rpc.RpcOptional; -import org.qpython.qsl4a.qsl4a.rpc.RpcParameter; - -import java.util.ArrayList; - -public class FloatViewFacade extends RpcReceiver { - - private final Service mService; - private final PackageManager mPackageManager; - private final AndroidFacade mAndroidFacade; - private final String floatViewActivity = "org.qpython.qpy.main.activity.FloatViewActivity"; - private final String protectActivity = "org.qpython.qpy.main.auxActivity.ProtectActivity"; - private final Context context; - - private final String[] argName = new String[] { - "x","y","width","height","textSize", //Integer型(可以设为上次) - "index", //Integer型(索引,不可设为上次) - "text","html", //字符型(二选一) - "backColor","textColor", //字符型(二选一) - "clickRemove", //布尔型 - "flag", //Integer型(索引,不可设为上次) - "script","arg" //字符型(无前则无后) - }; - - //按钮数组 - public static final ArrayList