Repository: hanks-zyh/hydrogenApp Branch: master Commit: f3ba707a3055 Files: 308 Total size: 1.2 MB Directory structure: gitextract_ntyztauy/ ├── .gitignore ├── LICENSE ├── PluginDev.md ├── README.md ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── hydrogen-library/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── pub/ │ │ └── hanks/ │ │ └── luajandroid/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ ├── androlua/ │ │ │ │ ├── LuaActivity.java │ │ │ │ ├── LuaAdapter.java │ │ │ │ ├── LuaBitmap.java │ │ │ │ ├── LuaBroadcastReceiver.java │ │ │ │ ├── LuaContext.java │ │ │ │ ├── LuaDexClassLoader.java │ │ │ │ ├── LuaDexLoader.java │ │ │ │ ├── LuaDrawable.java │ │ │ │ ├── LuaFragment.java │ │ │ │ ├── LuaGcable.java │ │ │ │ ├── LuaHttp.java │ │ │ │ ├── LuaImageLoader.java │ │ │ │ ├── LuaManager.java │ │ │ │ ├── LuaPrint.java │ │ │ │ ├── LuaTimer.java │ │ │ │ ├── LuaUtil.java │ │ │ │ ├── LuaView.java │ │ │ │ ├── LuaWebView.java │ │ │ │ ├── NineBitmapDrawable.java │ │ │ │ ├── adapter/ │ │ │ │ │ ├── LuaFragmentPageAdapter.java │ │ │ │ │ ├── LuaPagerAdapter.java │ │ │ │ │ ├── LuaRecyclerAdapter.java │ │ │ │ │ └── LuaRecyclerHolder.java │ │ │ │ ├── base/ │ │ │ │ │ ├── BaseActivity.java │ │ │ │ │ └── BaseFragment.java │ │ │ │ ├── common/ │ │ │ │ │ ├── LuaConstants.java │ │ │ │ │ ├── LuaFileUtils.java │ │ │ │ │ ├── LuaLog.java │ │ │ │ │ ├── LuaSp.java │ │ │ │ │ ├── LuaStringUtils.java │ │ │ │ │ └── LuaToast.java │ │ │ │ ├── fragment/ │ │ │ │ │ └── MenuFragment.java │ │ │ │ ├── plugin/ │ │ │ │ │ └── Plugin.java │ │ │ │ ├── utils/ │ │ │ │ │ ├── ColorStateListFactory.java │ │ │ │ │ ├── DialogUtils.java │ │ │ │ │ ├── LauncherUtil.java │ │ │ │ │ └── ShortcutUtils.java │ │ │ │ └── widget/ │ │ │ │ ├── glide/ │ │ │ │ │ └── LuaGlideModule.java │ │ │ │ ├── htmltext/ │ │ │ │ │ ├── URLImageParser.java │ │ │ │ │ └── UrlDrawable.java │ │ │ │ ├── marqueetext/ │ │ │ │ │ └── MarqueeTextView.java │ │ │ │ ├── ninegride/ │ │ │ │ │ ├── LuaNineGridView.java │ │ │ │ │ ├── LuaNineGridViewAdapter.java │ │ │ │ │ ├── NineGridImageView.java │ │ │ │ │ └── NineGridImageViewAdapter.java │ │ │ │ ├── picture/ │ │ │ │ │ ├── ElasticDragDismissFrameLayout.java │ │ │ │ │ └── PicturePreviewActivity.java │ │ │ │ ├── statusbar/ │ │ │ │ │ ├── FixInsetsFrameLayout.java │ │ │ │ │ └── StatusBarView.java │ │ │ │ ├── swipebacklayout/ │ │ │ │ │ ├── SwipeBackLayout.java │ │ │ │ │ ├── Utils.java │ │ │ │ │ ├── ViewDragHelper.java │ │ │ │ │ └── app/ │ │ │ │ │ ├── SwipeBackActivity.java │ │ │ │ │ ├── SwipeBackActivityBase.java │ │ │ │ │ ├── SwipeBackActivityHelper.java │ │ │ │ │ └── SwipeBackPreferenceActivity.java │ │ │ │ ├── video/ │ │ │ │ │ └── VideoPlayerActivity.java │ │ │ │ ├── viewpager/ │ │ │ │ │ └── NoScrollViewPager.java │ │ │ │ └── webview/ │ │ │ │ └── WebViewActivity.java │ │ │ └── com/ │ │ │ └── luajava/ │ │ │ ├── Console.java │ │ │ ├── JavaFunction.java │ │ │ ├── LuaException.java │ │ │ ├── LuaFunction.java │ │ │ ├── LuaInvocationHandler.java │ │ │ ├── LuaJavaAPI.java │ │ │ ├── LuaList.java │ │ │ ├── LuaMetaTable.java │ │ │ ├── LuaObject.java │ │ │ ├── LuaStack.java │ │ │ ├── LuaState.java │ │ │ ├── LuaStateFactory.java │ │ │ └── LuaTable.java │ │ └── res/ │ │ ├── drawable/ │ │ │ ├── bg_circle.xml │ │ │ ├── bg_rect_radius.xml │ │ │ ├── ic_loading.xml │ │ │ ├── layout_selector_tran.xml │ │ │ ├── loading_shap.xml │ │ │ ├── shadow_line_bottom.xml │ │ │ └── shadow_line_top.xml │ │ ├── layout/ │ │ │ ├── activity_lua.xml │ │ │ ├── activity_picture.xml │ │ │ ├── activity_video.xml │ │ │ ├── activity_webview.xml │ │ │ ├── dialog_add_shortcut.xml │ │ │ ├── dialog_input.xml │ │ │ ├── fragment_menu.xml │ │ │ └── item_pager_image.xml │ │ ├── raw/ │ │ │ └── keep.xml │ │ └── values/ │ │ ├── attrs_elastic_drag_dismiss_frame_layout.xml │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test/ │ ├── java/ │ │ └── pub/ │ │ └── hanks/ │ │ └── luajandroid/ │ │ └── ExampleUnitTest.java │ └── lua/ │ ├── a/ │ │ └── aa.lua │ ├── a.lua │ ├── b/ │ │ └── ba.lua │ └── c/ │ └── json.lua ├── lua/ │ ├── 163news/ │ │ ├── activity_news_detail.lua │ │ ├── fragment_news.lua │ │ ├── info.json │ │ └── main.lua │ ├── 500px/ │ │ ├── fragment_news.lua │ │ ├── info.json │ │ └── main.lua │ ├── 91pic/ │ │ ├── info.json │ │ └── main.lua │ ├── appinn/ │ │ ├── activity_news_detail.lua │ │ ├── fragment_news.lua │ │ ├── info.json │ │ └── main.lua │ ├── bilibili/ │ │ ├── fragment_news.lua │ │ ├── info.json │ │ └── main.lua │ ├── btmayi/ │ │ ├── info.json │ │ └── main.lua │ ├── buka/ │ │ ├── detail.lua │ │ ├── fragment_category.lua │ │ ├── info.json │ │ ├── main.lua │ │ ├── search.lua │ │ └── viewer.lua │ ├── digit/ │ │ ├── activity_news_detail.lua │ │ ├── fragment_news.lua │ │ ├── info.json │ │ └── main.lua │ ├── dm5/ │ │ ├── detail.lua │ │ ├── info.json │ │ ├── main.lua │ │ ├── search.lua │ │ └── viewer.lua │ ├── douban-daily/ │ │ ├── activity_news_detail.lua │ │ ├── fragment_news.lua │ │ ├── info.json │ │ └── main.lua │ ├── doubanmovie/ │ │ ├── detail.lua │ │ ├── info.json │ │ └── main.lua │ ├── douyu/ │ │ ├── detail.lua │ │ ├── info.json │ │ └── main.lua │ ├── eyepetizer/ │ │ ├── info.json │ │ └── main.lua │ ├── gacha/ │ │ ├── info.json │ │ └── main.lua │ ├── gamesky/ │ │ ├── info.json │ │ └── main.lua │ ├── graphmovies/ │ │ ├── detail.lua │ │ ├── info.json │ │ └── main.lua │ ├── huaban-popular/ │ │ ├── info.json │ │ └── main.lua │ ├── iciba/ │ │ ├── info.json │ │ └── main.lua │ ├── it168/ │ │ ├── activity_news_detail.lua │ │ ├── fragment_news.lua │ │ ├── info.json │ │ └── main.lua │ ├── ithome/ │ │ ├── activity_news_detail.lua │ │ ├── fragment_news.lua │ │ ├── info.json │ │ └── main.lua │ ├── jdly/ │ │ ├── info.json │ │ └── main.lua │ ├── jike/ │ │ ├── fragment_feed.lua │ │ ├── fragment_hot.lua │ │ ├── fragment_recomend.lua │ │ ├── info.json │ │ ├── item_msg.lua │ │ └── main.lua │ ├── magmoe-cos/ │ │ ├── info.json │ │ └── main.lua │ ├── magmoe-image/ │ │ ├── info.json │ │ └── main.lua │ ├── notead/ │ │ ├── info.json │ │ └── main.lua │ ├── packwap/ │ │ ├── info.json │ │ ├── main.lua │ │ └── testwap.lua │ ├── papapatimer/ │ │ ├── info.json │ │ └── main.lua │ ├── pengpai/ │ │ ├── activity_news_detail.lua │ │ ├── fragment_news.lua │ │ ├── info.json │ │ └── main.lua │ ├── pixiv/ │ │ ├── info.json │ │ └── main.lua │ ├── proxy_fetch/ │ │ ├── info.json │ │ └── main.lua │ ├── proxy_fetch_mogu/ │ │ ├── info.json │ │ └── main.lua │ ├── qiqu/ │ │ ├── info.json │ │ └── main.lua │ ├── qqtools/ │ │ ├── info.json │ │ └── main.lua │ ├── quanmm/ │ │ ├── fragment_news.lua │ │ ├── info.json │ │ └── main.lua │ ├── readhub/ │ │ ├── fragment_news.lua │ │ ├── info.json │ │ └── main.lua │ ├── splash/ │ │ ├── info.json │ │ └── main.lua │ ├── sspai/ │ │ ├── activity_news_detail.lua │ │ ├── fragment_news.lua │ │ ├── info.json │ │ └── main.lua │ ├── taobao/ │ │ ├── info.json │ │ └── main.lua │ ├── tieba/ │ │ ├── activity_news_detail.lua │ │ ├── fragment_news.lua │ │ ├── info.json │ │ └── main.lua │ ├── tv005/ │ │ ├── activity_agc_detail.lua │ │ ├── fragment_agc_news.lua │ │ ├── info.json │ │ └── main.lua │ ├── weather/ │ │ ├── city.lua │ │ ├── info.json │ │ ├── list_city.lua │ │ ├── main.lua │ │ └── weather.lua │ ├── weibo-hot/ │ │ ├── info.json │ │ ├── item_msg.lua │ │ └── main.lua │ ├── wikibaidu/ │ │ ├── info.json │ │ └── main.lua │ ├── yilin/ │ │ ├── activity_detail.lua │ │ ├── fragment_yilin.lua │ │ ├── info.json │ │ └── main.lua │ ├── zhihu-recommend/ │ │ ├── info.json │ │ └── main.lua │ └── zhihudaliy/ │ ├── activity_zhihu_daliy_detail.lua │ ├── fragment_zhihu_daliy.lua │ ├── info.json │ └── main.lua ├── lua_main/ │ ├── activity_plugins.lua │ ├── activity_setting.lua │ ├── filehelper.lua │ ├── fragment_home.lua │ ├── fragment_list.lua │ ├── import.lua │ ├── json.lua │ ├── loadbitmap.lua │ ├── loadlayout.lua │ ├── loadmenu.lua │ ├── log.lua │ ├── main.lua │ ├── md5.lua │ └── uihelper.lua ├── sample/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── pub/ │ │ └── hanks/ │ │ └── sample/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── assets/ │ │ │ └── lua/ │ │ │ ├── activity_plugins.lua │ │ │ ├── activity_setting.lua │ │ │ ├── filehelper.lua │ │ │ ├── fragment_home.lua │ │ │ ├── fragment_list.lua │ │ │ ├── import.lua │ │ │ ├── json.lua │ │ │ ├── loadbitmap.lua │ │ │ ├── loadlayout.lua │ │ │ ├── loadmenu.lua │ │ │ ├── log.lua │ │ │ ├── main.lua │ │ │ ├── md5.lua │ │ │ └── uihelper.lua │ │ ├── java/ │ │ │ └── pub/ │ │ │ └── hanks/ │ │ │ └── sample/ │ │ │ ├── App.java │ │ │ ├── ITHomeUtils.java │ │ │ ├── SplashActivity.java │ │ │ └── adapter/ │ │ │ └── DragTouchHelper.java │ │ └── res/ │ │ ├── anim/ │ │ │ ├── slide_in_from_bottom.xml │ │ │ ├── slide_in_right.xml │ │ │ ├── slide_out_from_bottom.xml │ │ │ ├── slide_out_left.xml │ │ │ └── slide_out_right.xml │ │ ├── drawable/ │ │ │ └── shadow_splash.xml │ │ ├── layout/ │ │ │ ├── activity_splash.xml │ │ │ └── upgrade_dialog.xml │ │ ├── raw/ │ │ │ └── keep.xml │ │ └── values/ │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test/ │ └── java/ │ └── pub/ │ └── hanks/ │ └── sample/ │ └── ExampleUnitTest.java ├── script/ │ ├── lua/ │ │ └── buildfile.lua │ └── node/ │ └── watch/ │ ├── build_lua_main_dir.js │ ├── exec_luac.js │ ├── package.json │ ├── update_plugin_info.js │ └── watch.js └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.jks *.iml .gradle /local.properties /.idea/workspace.xml /.idea/libraries .DS_Store /build /captures .externalNativeBuild .idea/ /.tags /.tags1 /script/node/watch/node_modules/ /keystore.jks /.vscode/ /.settings/ /sample/.settings/ /app/.settings/ /app/build/ .project app/.classpath app/.project ================================================ FILE: 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 androlua.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. ================================================ FILE: PluginDev.md ================================================ # 氢应用插件开发 ## 插件介绍 插件用 lua 语言开发,所以需要能看懂 lua,插件基于 androlua 框架下, 氢应用插件结构很简单,每个插件放在手机的 sdcard/Android/data/pub.hydrogen.android/files/LLLLLua 目录下 1. 首先在上面的目录下创建插件文件夹 2. 编写插件主程序,插件**至少**包含2文件: `info.json` 插件描述文件,`main.lua` 插件启动文件,接下来分别介绍: ## 插件结构 首先是 `info.json` ``` { "id": "pub.hanks.gacha", "name": "网易插画", "icon": "http://ww1.sinaimg.cn/large/8c9b876fly1fhaaa8qcofj2046046we9.jpg", "main": "main.lua", "versionName": "1.0", "versionCode": 1, "desc": "网易每日插画排行" } ``` id: 插件唯一标识符号 name: 插件名称 icon: 插件图标 main: 插件启动文件, 可以自定义名称 versionName: 插件版本名称 versionVersion: 插件版本号 desc: 插件描述 然后是启动文件(main.lua),也就是主程序,以`网易插画`的代码为例: ```lua require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaAdapter" import "androlua.LuaImageLoader" import "android.support.v7.widget.RecyclerView" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.StaggeredGridLayoutManager" import "androlua.widget.picture.PicturePreviewActivity" import "java.util.Calendar" local JSON = require("cjson") local uihelper = require('uihelper') -- 上面是需要导入用到的 class 类以及引用的 so 或 lua local calender = Calendar.getInstance() local max local data = {} local adapter local imageWidth = uihelper.getScreenWidth() / 2 -- 布局文件 local layout = { RecyclerView, id = "recyclerView", layout_width = "fill", layout_height = "fill", } local item_view = { FrameLayout, layout_width = "fill", { ImageView, id = "iv_image", layout_width = "fill", layout_height = "200dp", scaleType = "fitXY", }, { TextView, id = "tv_title", layout_gravity = "right", background = "#88000000", paddingLeft = "6dp", paddingRight = "6dp", paddingTop = "2dp", paddingBottom = "2dp", textSize = "10sp", visibility = 'gone', textColor = "#aaffffff", }, { View, id = "layer", layout_width = "fill", layout_height = "fill", background = "@drawable/layout_selector_tran", clickable = true, }, } local function fetchData() -- 获取数据 local year = calender.get(Calendar.YEAR) local month = calender.get(Calendar.MONTH) + 1 local day = calender.get(Calendar.DAY_OF_MONTH) local markFrom = string.format('%04d-%02d-%02d', year, month, day) calender.add(Calendar.DAY_OF_MONTH, -1) year = calender.get(Calendar.YEAR) month = calender.get(Calendar.MONTH) + 1 day = calender.get(Calendar.DAY_OF_MONTH) local mark = string.format('%04d-%02d-%02d', year, month, day) local url = string.format("http://gacha.163.com/api/v1/ranking/pic?type=0&mark=%s&fromMark=%s", mark, markFrom) -- 发起请求 LuaHttp.request({ url = url }, function(error, code, body) local json = JSON.decode(body) local html = json.result.rankingHtml -- 异步回调 uihelper.runOnUiThread(activity, function() -- UI 线程执行 local s = #data for w, h, url in string.gmatch(html, 'data[-]width="([0-9]+)" data[-]height="([0-9]+)".-data[-]src="(.-)"') do local item = { url = url, w = w, h = h } local id, type = string.match(item.url, 'http://gacha[.]nosdn[.]127[.]net/([0-9a-z]+)[.]([a-z]+)') item.id = id item.type = type item.fullUrl = string.format('http://gacha.nosdn.127.net/%s.%s', id, type) item.calcHeight = math.floor(imageWidth * tonumber(item.h) / tonumber(item.w)) data[#data + 1] = item end adapter.notifyItemRangeChanged(s, #data) end) end) end local function launchDetail(item) local args = { uris = { item.fullUrl } } PicturePreviewActivity.start(activity, JSON.encode(args)) end function onCreate(savedInstanceState) activity.setStatusBarColor(0x33000000) -- 设置状态栏颜色 activity.setContentView(loadlayout(layout)) -- 设置布局 -- 创建Adapter adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() return #data end, getItemViewType = function(position) return 0 end, onCreateViewHolder = function(parent, viewType) local views = {} local holder = LuaRecyclerHolder(loadlayout(item_view, views, RecyclerView)) holder.itemView.getLayoutParams().width = imageWidth holder.itemView.setTag(views) views.layer.onClick = function(view) local position = holder.getAdapterPosition() + 1 launchDetail(data[position]) end return holder end, onBindViewHolder = function(holder, position) position = position + 1 local item = data[position] local views = holder.itemView.getTag() views.iv_image.getLayoutParams().height = item.calcHeight LuaImageLoader.load(views.iv_image, item.url) if position == #data then fetchData() end end, })) recyclerView.setLayoutManager(StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)) recyclerView.setAdapter(adapter) fetchData() end ``` 插件开发完毕 ### 参考链接 [AndroLua_pro](https://github.com/nirenr/AndroLua_pro) [lua语法](http://www.runoob.com/lua/lua-basic-syntax.html) [lua的手册](https://cloudwu.github.io/lua53doc/manual.html) [Android文档](https://developer.android.com/develop/index.html?hl=zh-cn) ================================================ FILE: README.md ================================================ # hydrogenApp hydrogen is a **pluggable** android app, use `Lua` develop android, minSdkVersion="15", lua 5.3 plugin wrote by `lua` program language [APK Download](https://www.coolapk.com/apk/pub.hydrogen.android) ## App Plugin ![](http://image.coolapk.com/apk_image/2017/0706/1-for-148937-o_1bkb0ue7m16mp165il5srd41ei815-uid-518407.jpg.t.jpg) ![](http://image.coolapk.com/apk_image/2017/0706/2-for-148937-o_1bkb0ue7n1h1p1ke3ssuj4q1dab16-uid-518407.jpg.t.jpg) ![](http://image.coolapk.com/apk_image/2017/0706/3-for-148937-o_1bkb0ue7n1sn1nc01k8b17bk3h017-uid-518407.jpg.t.jpg) ![](http://image.coolapk.com/apk_image/2017/0706/4-for-148937-o_1bkb0ue7natj1uk010qm1kbgdq218-uid-518407.jpg.t.jpg) ![](http://image.coolapk.com/apk_image/2017/0901/S70901-173605-for-148937-o_1boucs1494kp1qo81qul1656ei9q-uid-518407.jpg.t.jpg) ![](http://image.coolapk.com/apk_image/2017/0901/S70901-173626-for-148937-o_1boucsbvrkcu1omqrte5d8amc10-uid-518407.jpg.t.jpg) ![](http://image.coolapk.com/apk_image/2017/0901/S70901-173652-for-148937-o_1boucsgnk6rlsvn1j9f1q8177n16-uid-518407.jpg.t.jpg) ![](http://image.coolapk.com/apk_image/2017/0901/S70901-173716-for-148937-o_1boucsl56mdt10am1vgsqs099m1c-uid-518407.jpg.t.jpg) ## 项目结构 宿主:`sample` 宿主用到的 lua 文件: `lua_main` 插件目录:`lua` 脚本:script ### 插件开发步骤 插件目录:lua 插件包含文件 `info.json` `main.lua` ``` { "id": "pub.hanks.gacha", "name": "网易插画", "icon": "http://ww1.sinaimg.cn/large/8c9b876fly1fhaaa8qcofj2046046we9.jpg", "main": "main.lua", "versionName": "1.0", "versionCode": 1, "desc": "网易每日插画排行" } id: 插件唯一标识符号 name: 插件名称 icon: 插件图标 main: 插件启动文件 versionName: 插件版本名称 versionVersion: 插件版本号 desc: 插件描述 ``` ### 插件发布 插件生成目录 api_luanroid, 执行 java 单元测试 `zipPlugin`, 该方法会打包好插件并更新获取插件的 api, 成功后,然后 push 到线上 [lua语法](http://www.runoob.com/lua/lua-basic-syntax.html) [lua的手册](https://cloudwu.github.io/lua53doc/manual.html) ``` require "import" import "android.widget.*" import "android.view.*" import "android.app.*" import "android.net.*" import "android.content.*" ``` ### 更新每日壁纸 https://coding.net/u/zhangyuhan/p/api_luanroid/git/blob/master/api/splash ### 版本更新 https://coding.net/u/zhangyuhan/p/api_luanroid/git/blob/master/api/update ## 插件开发 请看[插件开发指南](https://github.com/hanks-zyh/hydrogenApp/blob/master/PluginDev.md),更多的功能使用可以参考已有的[插件列表](https://github.com/hanks-zyh/hydrogenApp/tree/master/lua),目录为 lua 目录 ================================================ FILE: build.gradle ================================================ // Top-level build file where you can add configuration options androlua.common to all sub-projects/modules. buildscript { repositories { jcenter() google() } dependencies { classpath 'com.android.tools.build:gradle:3.2.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { jcenter() google() } } task clean(type: Delete) { delete rootProject.buildDir } ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ #Fri Nov 16 10:45:13 CST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip ================================================ FILE: gradle.properties ================================================ # Project-wide Gradle settings. # IDE (e.g. Android Studio) users: # Gradle settings configured through the IDE *will override* # any settings specified in this file. # For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. org.gradle.jvmargs=-Xmx1536m # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true ================================================ FILE: gradlew ================================================ #!/usr/bin/env bash ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn ( ) { echo "$*" } die ( ) { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; esac # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules function splitJvmOpts() { JVM_OPTS=("$@") } eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" ================================================ FILE: gradlew.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windowz variants if not "%OS%" == "Windows_NT" goto win9xME_args if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* goto execute :4NT_args @rem Get arguments from the 4NT Shell from JP Software set CMD_LINE_ARGS=%$ :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: hydrogen-library/.gitignore ================================================ /build ================================================ FILE: hydrogen-library/build.gradle ================================================ apply plugin: 'com.android.library' android { compileSdkVersion 28 defaultConfig { minSdkVersion 16 targetSdkVersion 28 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true } signingConfigs { release { keyAlias 'luajandroid' keyPassword 'CeRRV5eG' storeFile file('../keystore/luajandroid_keystore.jks') storePassword 'CeRRV5eG' } } buildTypes { release { minifyEnabled true zipAlignEnabled true signingConfig signingConfigs.release proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } preRelease { debuggable true signingConfig signingConfigs.release proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } sourceSets { main { jniLibs.srcDirs = ['libs'] } } productFlavors { } } repositories { flatDir { dirs 'libs' } } def support_version = '28.0.0' dependencies { // test androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) // local implementation fileTree(include: ['*.jar'], dir: 'libs') testImplementation 'junit:junit:4.12' // remote implementation "com.android.support:appcompat-v7:$support_version" implementation "com.android.support:design:$support_version" implementation 'com.github.bumptech.glide:glide:3.7.0' implementation 'cn.jzvd:jiaozivideoplayer:6.2.3' implementation 'com.squareup.okhttp3:okhttp:3.10.0' implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1' implementation 'com.davemorrissey.labs:subsampling-scale-image-view:3.7.2' implementation 'jp.wasabeef:glide-transformations:2.0.2' } ================================================ FILE: hydrogen-library/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in D:\android-sdk/tools/proguard/proguard-android.txt # You can edit the include path 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 *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile -keep class android.**{*;} -keep class androlua.**{*;} -keep class com.**{*;} -keep class androlua.common.**{*;} -keep class pub.**{*;} # Glide -keep public class * implements com.bumptech.glide.module.GlideModule -keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** { **[] $VALUES; public *; } # Okhttp -dontwarn okio.** -dontwarn javax.annotation.Nullable -dontwarn javax.annotation.ParametersAreNonnullByDefault ================================================ FILE: hydrogen-library/src/androidTest/java/pub/hanks/luajandroid/ExampleInstrumentedTest.java ================================================ package pub.hanks.luajandroid; import android.animation.LayoutTransition; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.res.ColorStateList; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Paint; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.GradientDrawable; import android.os.Build; import android.support.annotation.NonNull; import android.support.design.widget.AppBarLayout; import android.support.design.widget.BottomNavigationView; import android.support.design.widget.BottomSheetBehavior; import android.support.design.widget.CollapsingToolbarLayout; import android.support.design.widget.CoordinatorLayout; import android.support.design.widget.TabLayout; import android.support.graphics.drawable.VectorDrawableCompat; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import android.support.v7.app.AlertDialog; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.webkit.WebChromeClient; import android.webkit.WebView; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.GridView; import android.widget.HorizontalScrollView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.RadioButton; import android.widget.RadioGroup; import android.widget.TextView; import org.junit.Test; import org.junit.runner.RunWith; import java.io.File; import java.net.URI; import static junit.framework.Assert.assertEquals; /** * Instrumentation test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() throws Exception { // Context of the app under test. Context appContext = InstrumentationRegistry.getTargetContext(); new ColorDrawable(0xff); LinearLayout linearLayout = new LinearLayout(appContext); linearLayout.setOrientation(LinearLayout.VERTICAL); assertEquals("pub.hanks.luajandroid", appContext.getPackageName()); linearLayout.animate().scaleX(2).scaleY(2).translationX(100).setDuration(3000).start(); VectorDrawableCompat.createFromPath(""); linearLayout.setClickable(true); linearLayout.setFocusable(true); linearLayout.setFocusableInTouchMode(true); linearLayout.setBackgroundResource(R.drawable.layout_selector_tran); Intent intent = new Intent(); new File(new URI(intent.getData().getPath())).getAbsolutePath(); RecyclerView recyclerView = new RecyclerView(appContext); recyclerView.setVerticalFadingEdgeEnabled(false); GridView gridLayout = new GridView(appContext); gridLayout.setNumColumns(5); gridLayout.setStretchMode(GridView.STRETCH_SPACING); gridLayout.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { } }); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { gridLayout.setElevation(20); } gridLayout.postDelayed(new Runnable() { @Override public void run() { } }, 1000); BottomSheetBehavior sheetBehavior = BottomSheetBehavior.from(recyclerView); if (sheetBehavior.getState() != BottomSheetBehavior.STATE_COLLAPSED) { sheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); } new GridLayoutManager(appContext, 3); HorizontalScrollView horizontalScrollView = new HorizontalScrollView(appContext); horizontalScrollView.setHorizontalScrollBarEnabled(false); ListView listView = new ListView(appContext); listView.setDividerHeight(0); TextView textView = new TextView(appContext); textView.setMaxLines(1); textView.setSingleLine(true); TabLayout tabLayout = new TabLayout(appContext); tabLayout.setVisibility(View.VISIBLE); WebView webView = new WebView(appContext); webView.setWebChromeClient(new WebChromeClient()); //webView.addJavascriptInterface(this, ""); Bitmap bitmap = BitmapFactory.decodeResource(appContext.getResources(), R.id.et_url); tabLayout.setLayoutTransition(new LayoutTransition()); final BottomNavigationView bottomView = new BottomNavigationView(appContext); ColorStateList textColor = ColorStateList.valueOf(0xFFFF0000); bottomView.setItemTextColor(textColor); bottomView.getMenu().add(""); bottomView.getMenu().add(""); bottomView.getMenu().add(""); bottomView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { item.getTitle(); item.setChecked(true); return true; } }); Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); new AlertDialog.Builder(appContext) .setItems(new String[]{}, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }) .setSingleChoiceItems(new String[]{}, 0, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }) .show(); RadioGroup radioGroup = new RadioGroup(appContext); RadioButton radioButton = new RadioButton(appContext); radioGroup.addView(radioButton); new Toolbar(appContext).setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { } }); GradientDrawable drawable = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, new int[]{}); CollapsingToolbarLayout collapsingToolbarLayout = new CollapsingToolbarLayout(appContext); CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) collapsingToolbarLayout.getLayoutParams(); params.setBehavior(new AppBarLayout.ScrollingViewBehavior()); new AlertDialog.Builder(appContext).setTitle("").setMessage("") .setPositiveButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }) .show(); GradientDrawable gd = new GradientDrawable(); gd.setShape(GradientDrawable.OVAL); gd.setColor(0xFFFFFFFF); // 动态代理 } class MyAdapter extends BaseAdapter { @Override public int getCount() { return 0; } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { return null; } } } ================================================ FILE: hydrogen-library/src/main/AndroidManifest.xml ================================================ ================================================ FILE: hydrogen-library/src/main/java/androlua/LuaActivity.java ================================================ package androlua; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v4.app.FragmentManager; import android.support.v4.widget.DrawerLayout; import android.view.ContextMenu; import android.view.Gravity; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.widget.FrameLayout; import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; import com.luajava.JavaFunction; import com.luajava.LuaException; import com.luajava.LuaObject; import com.luajava.LuaState; import com.luajava.LuaStateFactory; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; import androlua.base.BaseActivity; import androlua.common.LuaLog; import androlua.fragment.MenuFragment; import pub.hanks.luajandroid.R; public class LuaActivity extends BaseActivity implements LuaContext { public Handler handler; public TextView status; private LuaState L; private ScrollView errorLayout; private LuaObject mOnKeyDown; private LuaObject mOnKeyUp; private LuaObject mOnKeyLongPress; private LuaObject mOnTouchEvent; private LuaDexLoader luaDexLoader; private LuaManager luaManager; private FrameLayout main; private MenuFragment menuFragment; private DrawerLayout layout_drawer; public static void start(Context context, String luaPath) { Intent starter = new Intent(context, LuaActivity.class); starter.putExtra("luaPath", luaPath); context.startActivity(starter); } public void setContentView(View view) { main.removeAllViews(); main.addView(view, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); FragmentManager fragmentManager = getSupportFragmentManager(); if (fragmentManager.findFragmentByTag("menu") == null) { fragmentManager.beginTransaction() .replace(R.id.menu, menuFragment, "menu").commitAllowingStateLoss(); } } public void closeDrawer() { if (layout_drawer == null) { return; } layout_drawer.closeDrawer(Gravity.RIGHT); } @Override public void setContentView(View view, LayoutParams params) { super.setContentView(view, params); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_lua); main = (FrameLayout) findViewById(R.id.main); layout_drawer = (DrawerLayout) findViewById(R.id.layout_drawer); menuFragment = MenuFragment.newInstance(); handler = new MainHandler(this); // 用于出错时显示 initErrorLayout(); initLua(savedInstanceState); } public void disableDrawer(){ if (layout_drawer == null) { return; } layout_drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); } private void initLua(Bundle savedInstanceState) { try { Object[] arg = LuaUtil.IntentHelper.getArgs(getIntent()); luaManager = LuaManager.getInstance(); String luaFile = LuaUtil.IntentHelper.getLuaPath(getIntent()); L = LuaStateFactory.newLuaState(); L.openLibs(); L.pushJavaObject(this); L.setGlobal("activity"); L.getGlobal("activity"); L.setGlobal("this"); L.pushContext(this); L.getGlobal("luajava"); L.pushString(luaManager.getLuaExtDir()); L.setField(-2, "luaextdir"); L.pushString(luaManager.getLuaDir()); L.setField(-2, "luadir"); L.pushString(luaManager.getLuaLpath()); L.setField(-2, "luapath"); L.pop(1); JavaFunction print = new LuaPrint(L); print.register("print"); L.getGlobal("package"); L.pushString(luaManager.getLuaLpath()); L.setField(-2, "path"); L.pushString(luaManager.getLuaCpath()); L.setField(-2, "cpath"); L.pop(1); mOnKeyDown = L.getLuaObject("onKeyDown"); if (mOnKeyDown.isNil()) mOnKeyDown = null; mOnKeyUp = L.getLuaObject("onKeyUp"); if (mOnKeyUp.isNil()) mOnKeyUp = null; mOnKeyLongPress = L.getLuaObject("onKeyLongPress"); if (mOnKeyLongPress.isNil()) mOnKeyLongPress = null; mOnTouchEvent = L.getLuaObject("onTouchEvent"); if (mOnTouchEvent.isNil()) mOnTouchEvent = null; luaDexLoader = new LuaDexLoader(); luaDexLoader.loadLibs(); if (!luaFile.startsWith("/")) { luaFile = luaManager.getLuaExtDir() + "/" + luaFile; } luaManager.doFile(L, luaFile, arg); luaManager.runFunc(L, "onCreate", savedInstanceState); } catch (Exception e) { sendMsg(e.getMessage()); setContentView(this.errorLayout); } } private void initErrorLayout() { errorLayout = new ScrollView(this); errorLayout.setFillViewport(true); errorLayout.setBackgroundColor(Color.WHITE); status = new TextView(this); status.setPadding(10, 100, 10, 0); status.setTextColor(Color.BLACK); status.setText(""); status.setTextIsSelectable(true); errorLayout.addView(status, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); } public ArrayList getClassLoaders() { return luaDexLoader.getClassLoaders(); } public HashMap getLibrarys() { return luaDexLoader.getLibrarys(); } public void loadResources(String path) { luaDexLoader.loadResources(path); } public LuaState getLuaState() { return L; } @Override protected void onStart() { super.onStart(); luaManager.runFunc(L, "onStart"); } @Override protected void onResume() { super.onResume(); luaManager.runFunc(L, "onResume"); } @Override protected void onPause() { super.onPause(); luaManager.runFunc(L, "onPause"); } @Override protected void onStop() { super.onStop(); luaManager.runFunc(L, "onStop"); } @Override protected void onNewIntent(Intent intent) { luaManager.runFunc(L, "onNewIntent", intent); super.onNewIntent(intent); } @Override protected void onDestroy() { try { luaManager.runFunc(L, "onDestroy"); super.onDestroy(); // L.close(); L.gc(LuaState.LUA_GCCOLLECT, 1); System.gc(); } catch (Exception e) { LuaLog.e(e); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { luaManager.runFunc(L, "onActivityResult", requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data); } @Override public void onBackPressed() { Object ret = luaManager.runFunc(L, "onBackPressed"); if (ret != null && ret.getClass() == Boolean.class && (Boolean) ret) { return; } super.onBackPressed(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (mOnKeyDown != null) { try { Object ret = mOnKeyDown.call(keyCode, event); if (ret != null && ret.getClass() == Boolean.class && (Boolean) ret) return true; } catch (LuaException e) { sendMsg("onKeyDown " + e.getMessage()); } } return super.onKeyDown(keyCode, event); } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if (mOnKeyUp != null) { try { Object ret = mOnKeyUp.call(keyCode, event); if (ret != null && ret.getClass() == Boolean.class && (Boolean) ret) return true; } catch (LuaException e) { sendMsg("onKeyUp " + e.getMessage()); } } return super.onKeyUp(keyCode, event); } @Override public boolean onKeyLongPress(int keyCode, KeyEvent event) { if (mOnKeyLongPress != null) { try { Object ret = mOnKeyLongPress.call(keyCode, event); if (ret != null && ret.getClass() == Boolean.class && (Boolean) ret) return true; } catch (LuaException e) { sendMsg("onKeyLongPress " + e.getMessage()); } } return super.onKeyLongPress(keyCode, event); } @Override public boolean onTouchEvent(MotionEvent event) { if (mOnTouchEvent != null) { try { Object ret = mOnTouchEvent.call(event); if (ret != null && ret.getClass() == Boolean.class && (Boolean) ret) return true; } catch (LuaException e) { sendMsg("onTouchEvent " + e.getMessage()); } } return super.onTouchEvent(event); } @Override public boolean onCreateOptionsMenu(Menu menu) { luaManager.runFunc(L, "onCreateOptionsMenu", menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { Object ret = null; if (!item.hasSubMenu()) ret = luaManager.runFunc(L, "onOptionsItemSelected", item); if (ret != null && ret.getClass() == Boolean.class && (Boolean) ret) return true; return super.onOptionsItemSelected(item); } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { luaManager.runFunc(L, "onCreateContextMenu", menu, v, menuInfo); super.onCreateContextMenu(menu, v, menuInfo); } @Override public boolean onContextItemSelected(MenuItem item) { luaManager.runFunc(L, "onContextItemSelected", item); return super.onContextItemSelected(item); } public int getWidth() { return getResources().getDisplayMetrics().widthPixels; } public int getHeight() { return getResources().getDisplayMetrics().heightPixels; } public void toast(String msg) { Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); } //显示信息 public void sendMsg(String msg) { Message message = new Message(); Bundle bundle = new Bundle(); bundle.putString("data", msg); message.setData(bundle); message.what = 0; handler.sendMessage(message); LuaLog.e(msg); } // avoid handler leak memory private static class MainHandler extends Handler { WeakReference activityWeakReference; private MainHandler(Activity activity) { activityWeakReference = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); Activity activity = activityWeakReference.get(); if (activity == null || !(activity instanceof LuaActivity)) { return; } LuaActivity luaActivity = (LuaActivity) activity; switch (msg.what) { case 0: { String data = msg.getData().getString("data"); luaActivity.toast(data); luaActivity.status.append(data + "\n"); luaActivity.setContentView(luaActivity.errorLayout); } break; // case 1: { // Bundle data = msg.getData(); // luaActivity.setField(data.getString("data"), ((Object[]) data.getSerializable("args"))[0]); // } // break; // case 2: { // String src = msg.getData().getString("data"); // luaActivity.luaManager.runFunc(L, src); // } // break; // case 3: { // String src = msg.getData().getString("data"); // Serializable args = msg.getData().getSerializable("args"); // luaActivity.luaManager.runFunc(L, src, (Object[]) args); // } } } } } ================================================ FILE: hydrogen-library/src/main/java/androlua/LuaAdapter.java ================================================ package androlua; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; /** * LuaAdapter * Created by hanks on 2017/5/13. */ public class LuaAdapter extends BaseAdapter { AdapterCreator adapterCreator; public LuaAdapter(AdapterCreator adapterCreator) { this.adapterCreator = adapterCreator; } @Override public int getCount() { return (int) adapterCreator.getCount(); } @Override public Object getItem(int position) { return adapterCreator.getItem(position); } @Override public long getItemId(int position) { return adapterCreator.getItemId(position); } @Override public View getView(int position, View convertView, ViewGroup parent) { return adapterCreator.getView(position, convertView, parent); } public interface AdapterCreator { long getCount(); Object getItem(int position); long getItemId(int position); View getView(int position, View convertView, ViewGroup parent); } } ================================================ FILE: hydrogen-library/src/main/java/androlua/LuaBitmap.java ================================================ package androlua; import android.content.Context; import android.content.res.AssetManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; import java.net.HttpURLConnection; import java.net.URL; import java.util.WeakHashMap; public class LuaBitmap { static WeakHashMap> cache = new WeakHashMap>(); private static int l; public static boolean checkCache(LuaContext context, String url) { // TODO: Implement this method String path = LuaManager.getInstance().getLuaExtDir() + "/" + url.hashCode(); File f = new File(path); return f.exists(); } public static Bitmap getLoacalBitmap(String url) throws FileNotFoundException, IOException { FileInputStream fis = new FileInputStream(url); Bitmap bitmap = BitmapFactory.decodeStream(fis); fis.close(); return bitmap; } public static Bitmap getLoacalBitmap(LuaContext context, String url) { return decodeScale(1080, new File(url)); } public static Bitmap getHttpBitmap(String url) throws IOException { //Log.d(TAG, url); URL myFileUrl = new URL(url); HttpURLConnection conn = (HttpURLConnection) myFileUrl.openConnection(); //conn.setConnectTimeout(0); conn.setDoInput(true); conn.connect(); InputStream is = conn.getInputStream(); Bitmap bitmap = BitmapFactory.decodeStream(is); is.close(); return bitmap; } public static Bitmap getHttpBitmap(LuaContext context, String url) throws IOException { //Log.d(TAG, url); String path = LuaManager.getInstance().getLuaExtDir() + "/" + url.hashCode(); File f = new File(path); if (f.exists()) { try { /*HttpURLConnection con=(HttpURLConnection) new URL(url).openConnection(); con.setRequestMethod("HEAD"); con.setConnectTimeout(1000); con.connect(); l = con.getContentLength(); android.util.Log.d("lua",l+","+f.length()); if(l==f.length())*/ return decodeScale(1080, new File(path)); } catch (Exception e) { return decodeScale(1080, new File(path)); } } URL myFileUrl = new URL(url); HttpURLConnection conn = (HttpURLConnection) myFileUrl.openConnection(); //conn.setConnectTimeout(0); conn.setDoInput(true); conn.connect(); InputStream is = conn.getInputStream(); FileOutputStream out = new FileOutputStream(path); //LuaUtil.copyFile(is, out); //Bitmap bitmap = BitmapFactory.decodeStream(is); Bitmap bitmap = decodeScale(1080, new File(path)); is.close(); return bitmap; } public static Bitmap getAssetBitmap(Context context, String name) throws IOException { AssetManager am = context.getAssets(); InputStream is = am.open(name); Bitmap bitmap = BitmapFactory.decodeStream(is); is.close(); return bitmap; } public static Bitmap getBitmap(LuaContext context, String path) throws IOException { WeakReference wRef = cache.get(path); if (wRef != null) { Bitmap bt = wRef.get(); if (bt != null) return bt; } Bitmap bitmap; if (path.indexOf("http://") == 0) { bitmap = getHttpBitmap(context, path); } else if (path.charAt(0) != '/') { bitmap = getLoacalBitmap(context, LuaManager.getInstance().getLuaDir() + "/" + path); } else { bitmap = getLoacalBitmap(context, path); } cache.put(path, new WeakReference(bitmap)); return bitmap; } private static Bitmap decodeScale(int IMAGE_MAX_SIZE, File fis) { Bitmap b = null; BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; BitmapFactory.decodeFile(fis.getAbsolutePath(), o); int scale = 1; if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) { scale = (int) Math.pow(2, (int) Math.round(Math.log(IMAGE_MAX_SIZE / (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5))); } BitmapFactory.Options o2 = new BitmapFactory.Options(); o2.inSampleSize = scale; b = BitmapFactory.decodeFile(fis.getAbsolutePath(), o2); return b; } public static Bitmap getImageFromPath(String filePath) { Bitmap bitmap = null; BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inJustDecodeBounds = true; BitmapFactory.decodeFile(filePath, opts); //缩放图片,避免内存不足 opts.inSampleSize = computeSampleSize(opts, -1, 250 * 250); opts.inJustDecodeBounds = false; try { bitmap = BitmapFactory.decodeFile(filePath, opts); } catch (Exception e) { // TODO: handle exception } return bitmap; } //缩放图片算法 private static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) { int initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels); int roundedSize; if (initialSize <= 8) { roundedSize = 1; while (roundedSize < initialSize) { roundedSize <<= 1; } } else { roundedSize = (initialSize + 7) / 8 * 8; } return roundedSize; } private static int computeInitialSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) { double w = options.outWidth; double h = options.outHeight; int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels)); int upperBound = (minSideLength == -1) ? 128 : (int) Math.min(Math.floor(w / minSideLength), Math.floor(h / minSideLength)); if (upperBound < lowerBound) { // return the larger one when there is no overlapping zone. return lowerBound; } if ((maxNumOfPixels == -1) && (minSideLength == -1)) { return 1; } else if (minSideLength == -1) { return lowerBound; } else { return upperBound; } } public static Bitmap getBitmapFromFile(File file, int width, int height) { BitmapFactory.Options opts = null; if (null != file && file.exists()) { if (width > 0 && height > 0) { opts = new BitmapFactory.Options(); // 只是返回的是图片的宽和高,并不是返回一个Bitmap对象 opts.inJustDecodeBounds = true; // 信息没有保存在bitmap里面,而是保存在options里面 BitmapFactory.decodeFile(file.getPath(), opts); // 计算图片缩放比例 final int minSideLength = Math.min(width, height); // 缩略图大小为原始图片大小的几分之一。根据业务需求来做。 opts.inSampleSize = computeSampleSize(opts, minSideLength, width * height); // 重新读入图片,注意此时已经把options.inJustDecodeBounds设回false opts.inJustDecodeBounds = false; // 设置是否深拷贝,与inPurgeable结合使用 opts.inInputShareable = true; // 设置为True时,表示系统内存不足时可以被回 收,设置为False时,表示不能被回收。 opts.inPurgeable = true; } try { return BitmapFactory.decodeFile(file.getPath(), opts); } catch (OutOfMemoryError e) { e.printStackTrace(); } } return null; } } ================================================ FILE: hydrogen-library/src/main/java/androlua/LuaBroadcastReceiver.java ================================================ package androlua; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; public class LuaBroadcastReceiver extends BroadcastReceiver { private OnReceiveListener mRlt; public LuaBroadcastReceiver(OnReceiveListener rlt) { mRlt = rlt; } @Override public void onReceive(Context context, Intent intent) { if (mRlt == null) { return; } mRlt.onReceive(context, intent); } public interface OnReceiveListener { void onReceive(Context context, Intent intent); } } ================================================ FILE: hydrogen-library/src/main/java/androlua/LuaContext.java ================================================ package androlua; public interface LuaContext { } ================================================ FILE: hydrogen-library/src/main/java/androlua/LuaDexClassLoader.java ================================================ package androlua; import java.util.HashMap; import dalvik.system.DexClassLoader; public class LuaDexClassLoader extends DexClassLoader { private HashMap> classCache = new HashMap(); private String mDexPath; public LuaDexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent) { super(dexPath, optimizedDirectory, libraryPath, parent); this.mDexPath = dexPath; } public String getDexPath() { return this.mDexPath; } protected Class findClass(String name) throws ClassNotFoundException { Class cls = (Class) this.classCache.get(name); if (cls != null) { return cls; } cls = super.findClass(name); this.classCache.put(name, cls); return cls; } } ================================================ FILE: hydrogen-library/src/main/java/androlua/LuaDexLoader.java ================================================ package androlua; import android.content.res.AssetManager; import android.content.res.Resources; import android.content.res.Resources.Theme; import com.luajava.LuaException; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import dalvik.system.DexClassLoader; public class LuaDexLoader { private static HashMap dexCache = new HashMap(); private final LuaManager luaManager; private ArrayList dexList = new ArrayList(); private HashMap libCache = new HashMap(); private String luaDir; private AssetManager mAssetManager; private LuaContext mContext; private Resources mResources; private Theme mTheme; private String odexDir; public LuaDexLoader() { luaManager = LuaManager.getInstance(); this.luaDir = luaManager.getLuaDir(); this.odexDir = luaManager.getOdexDir(); } public Theme getTheme() { return this.mTheme; } public ArrayList getClassLoaders() { return this.dexList; } public void loadLibs() throws LuaException { File[] libs = new File(LuaManager.getInstance().getLuaExtDir() + "/libs").listFiles(); if (libs != null) { for (File f : libs) { if (f.getAbsolutePath().endsWith(".so")) { loadLib(f.getName()); } else { loadDex(f.getAbsolutePath()); } } } } public void loadLib(String name) throws LuaException { // String fn = name; // int i = name.indexOf("."); // if (i > 0) { // fn = name.substring(0, i); // } // if (fn.startsWith("lib")) { // fn = fn.substring(3); // } // String libPath = this.mContext.getContext().getDir(fn, 0).getAbsolutePath() + "/lib" + fn + ".so"; // if (!new File(libPath).exists()) { // if (new File(this.luaDir + "/libs/lib" + fn + ".so").exists()) { // LuaUtil.copyFile(this.luaDir + "/libs/lib" + fn + ".so", libPath); // } else { // throw new LuaException("can not find lib " + name); // } // } // this.libCache.put(fn, libPath); } public HashMap getLibrarys() { return this.libCache; } public DexClassLoader loadDex(String path) throws LuaException { String name = path; LuaDexClassLoader dex = (LuaDexClassLoader) dexCache.get(name); // if (dex == null) { // if (path.charAt(0) != '/') { // path = this.luaDir + "/" + path; // } // if (!new File(path).exists()) { // if (new File(path + ".dex").exists()) { // path = path + ".dex"; // } else if (new File(path + ".jar").exists()) { // path = path + ".jar"; // } else { // throw new LuaException(path + " not found"); // } // } // dex = new LuaDexClassLoader(path, this.odexDir,luaManager.getContext().getApplicationInfo().nativeLibraryDir, this.mContext.getContext().getClassLoader()); // dexCache.put(name, dex); // } // if (!this.dexList.contains(dex)) { // this.dexList.add(dex); // path = dex.getDexPath(); // if (path.endsWith(".jar")) { // loadResources(path); // } // } return dex; } public void loadResources(String path) { try { // AssetManager assetManager = (AssetManager) AssetManager.class.newInstance(); // if (((Integer) assetManager.getClass().getMethod("addAssetPath", new Class[]{String.class}).invoke(assetManager, new Object[]{path})).intValue() != 0) { // this.mAssetManager = assetManager; // Resources superRes = this.mContext.getContext().getResources(); // this.mResources = new Resources(this.mAssetManager, superRes.getDisplayMetrics(), superRes.getConfiguration()); // this.mTheme = this.mResources.newTheme(); // this.mTheme.setTo(this.mContext.getContext().getTheme()); // } } catch (Exception e) { e.printStackTrace(); } } public AssetManager getAssets() { return this.mAssetManager; } public Resources getResources() { return this.mResources; } } ================================================ FILE: hydrogen-library/src/main/java/androlua/LuaDrawable.java ================================================ package androlua; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import com.luajava.LuaException; import com.luajava.LuaFunction; import com.luajava.LuaObject; import androlua.common.LuaLog; public class LuaDrawable extends Drawable { private LuaObject mDraw; private final LuaContext mContext = this.mDraw.getLuaState().getContext(); private LuaFunction mOnDraw; private Paint mPaint = new Paint(); public LuaDrawable(LuaFunction func) { this.mDraw = func; } public static Drawable create(String filePath) { if (!filePath.startsWith("/")) { filePath = LuaManager.getInstance().getLuaExtDir() + "/" + filePath; } return new BitmapDrawable(LuaManager.getInstance().getContext().getResources(), BitmapFactory.decodeFile(filePath)); } public void draw(Canvas p1) { try { if (this.mOnDraw == null) { Object r = this.mDraw.call(p1, this.mPaint, this); if (r != null && (r instanceof LuaFunction)) { this.mOnDraw = (LuaFunction) r; } } if (this.mOnDraw != null) { this.mOnDraw.call(p1); } } catch (LuaException e) { LuaLog.e(e); } } public void setAlpha(int p1) { this.mPaint.setAlpha(p1); } public void setColorFilter(ColorFilter p1) { this.mPaint.setColorFilter(p1); } public int getOpacity() { return PixelFormat.UNKNOWN; } public Paint getPaint() { return this.mPaint; } } ================================================ FILE: hydrogen-library/src/main/java/androlua/LuaFragment.java ================================================ package androlua; import android.content.Context; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; /** * Created by hanks on 2017/5/26. Copyright (C) 2017 Hanks */ public class LuaFragment extends Fragment { private FragmentCreator creator; public static LuaFragment newInstance() { Bundle args = new Bundle(); LuaFragment fragment = new LuaFragment(); fragment.setArguments(args); return fragment; } @Override public void onPause() { super.onPause(); if (creator != null) { creator.onPause(); } } @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (creator != null) creator.onUserVisible(isVisibleToUser); } public void setCreator(FragmentCreator creator) { this.creator = creator; } @Override public void onAttach(Context context) { super.onAttach(context); if (creator != null)creator.onAttach(context); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { if (creator != null)creator.onCreate(savedInstanceState); super.onCreate(savedInstanceState); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { if (creator != null) { return creator.onCreateView(inflater, container, savedInstanceState); } return super.onCreateView(inflater, container, savedInstanceState); } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); if (creator != null)creator.onActivityCreated(savedInstanceState); } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); if (creator != null)creator.onViewCreated(view, savedInstanceState); } @Override public void onStart() { if (creator != null) creator.onStart(); super.onStart(); } @Override public void onResume() { if (creator != null)creator.onResume(); super.onResume(); } @Override public void onStop() { if (creator != null)creator.onStop(); super.onStop(); } @Override public void onDestroyView() { super.onDestroyView(); if (creator != null)creator.onDestroyView(); } @Override public void onDestroy() { super.onDestroy(); if (creator != null)creator.onDestroy(); } @Override public void onDetach() { super.onDetach(); if (creator != null)creator.onDetach(); } public interface FragmentCreator { void onCreate(@Nullable Bundle savedInstanceState); void onAttach(Context context); View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState); void onActivityCreated(Bundle savedInstanceState); void onViewCreated(View view, @Nullable Bundle savedInstanceState); void onStart(); void onResume(); void onStop(); void onPause(); void onDestroyView(); void onDestroy(); void onDetach(); void onUserVisible(boolean isVisibleToUser); } } ================================================ FILE: hydrogen-library/src/main/java/androlua/LuaGcable.java ================================================ package androlua; public interface LuaGcable { void gc(); } ================================================ FILE: hydrogen-library/src/main/java/androlua/LuaHttp.java ================================================ package androlua; import android.support.annotation.NonNull; import com.luajava.LuaException; import com.luajava.LuaObject; import com.luajava.LuaTable; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Map; import java.util.concurrent.TimeUnit; import androlua.common.LuaFileUtils; import androlua.common.LuaLog; import okhttp3.Call; import okhttp3.Callback; import okhttp3.FormBody; import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; import okhttp3.logging.HttpLoggingInterceptor; import pub.hanks.luajandroid.BuildConfig; /** * context client for lua * Created by hanks on 2017/5/15. Copyright (C) 2017 Hanks */ public class LuaHttp { private static LuaHttp instance; private final OkHttpClient httpClient; private LuaHttp() { HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); OkHttpClient.Builder builder = new OkHttpClient.Builder(); if (!"release".equals(BuildConfig.BUILD_TYPE)) { builder.addInterceptor(interceptor); } httpClient = builder.build(); } public static LuaHttp getInstance() { if (instance == null) { synchronized (LuaHttp.class) { if (instance == null) { instance = new LuaHttp(); } } } return instance; } public static void cancelAll() { getInstance().httpClient.dispatcher().cancelAll(); } public static void request(final LuaTable options, final LuaObject callback) { getInstance().httpClient.newCall(buildRequest(options)) .enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { try { callback.call(e); } catch (LuaException e1) { LuaLog.e(e1); } } @Override public void onResponse(Call call, Response response) throws IOException { try { Object o = options.get("outputFile"); if (o != null) { InputStream inputStream = response.body().byteStream(); String filePath; if (o instanceof String) { filePath = (String) o; } else { filePath = o.toString(); } String outputFile = LuaFileUtils.saveToFile(inputStream, filePath); callback.call(null, response.code(), outputFile); return; } callback.call(null, response.code(), response.body().string()); } catch (LuaException e) { LuaLog.e(e); } } }); } public static void requestSync(final LuaTable options, final LuaObject callback) { try { Response response = getInstance().httpClient.newCall(buildRequest(options)).execute(); Object o = options.get("outputFile"); if (o != null) { InputStream inputStream = response.body().byteStream(); String filePath; if (o instanceof String) { filePath = (String) o; } else { filePath = o.toString(); } String outputFile = LuaFileUtils.saveToFile(inputStream, filePath); callback.call(null, response.code(), outputFile); return; } callback.call(null, response.code(), response.body().string()); } catch (Exception e) { e.printStackTrace(); } } @NonNull private static Request buildRequest(LuaTable options) { Request.Builder builder = new Request.Builder(); String url = (String) options.get("url"); builder.url(url); String method = (String) options.get("method"); if ("GET".equals(method)) { builder.get(); } else if ("POST".equals(method)) { RequestBody requestBody = getRequestBody(options); if (requestBody != null) { builder.post(requestBody); } } else if ("PUT".equals(method)) { RequestBody requestBody = getRequestBody(options); if (requestBody != null) { builder.put(requestBody); } } else if ("DELETE".equals(method)) { RequestBody requestBody = getRequestBody(options); if (requestBody != null) { builder.delete(requestBody); } else { builder.delete(); } } else { builder.get(); } LuaTable headers = (LuaTable) options.get("headers"); if (headers != null) { for (Object key : headers.keySet()) { String value = (String) headers.get(key); int i = value.indexOf(":"); if (i == -1) { continue; } String[] header = new String[]{ value.substring(0, i), value.substring(i + 1) }; builder.header(header[0].trim(), header[1].trim()); } } return builder.build(); } private static RequestBody getRequestBody(LuaTable options) { String body = (String) options.get("body"); if (body != null) { return RequestBody.create(MediaType.parse("application/json; charset=utf-8"), body); } Map formData = (Map) options.get("formData"); if (formData != null) { FormBody.Builder bodyBuilder = new FormBody.Builder(); for (Object key : formData.keySet()) { String value = (String) formData.get(key); int i = value.indexOf(":"); if (i == -1) { continue; } String[] params = new String[]{ value.substring(0, i), value.substring(i + 1) }; bodyBuilder.add(params[0].trim(), params[1].trim()); } return bodyBuilder.build(); } Map multipart = (Map) options.get("multipart"); if (multipart != null) { MultipartBody.Builder bodyBuilder = new MultipartBody.Builder() .setType(MultipartBody.FORM); for (Object key : multipart.keySet()) { String value = (String) multipart.get(key); int i = value.indexOf(":"); if (i == -1) { continue; } String[] params = new String[]{ value.substring(0, i), value.substring(i + 1) }; String itemKey = params[0].trim(); String itemValue = params[1].trim(); if (itemValue.startsWith("/")) { File file = new File(itemValue); if (file.exists()) { bodyBuilder.addFormDataPart(itemKey, file.getName(), RequestBody.create(MediaType.parse("image/png"), file)); } } else { bodyBuilder.addFormDataPart(itemKey, itemValue); } } return bodyBuilder.build(); } return new FormBody.Builder().build(); } public static boolean downloadFile(String url, String savePath) { return downloadFile(url, savePath, null); } public static boolean downloadFile(String url, String savePath, ArrayList headers) { try { final File file = new File(savePath); if (file.exists()) { return true; } OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .build(); Request.Builder builder = new Request.Builder().url(url); if (headers != null) { for (String header : headers) { int i = header.indexOf(":"); if (i <= 0) { continue; } String key = header.substring(0, i); String v = header.substring(i + 1); builder.addHeader(key, v); } } final Request request = builder.build(); Response response = client.newCall(request).execute(); InputStream is = null; byte[] buf = new byte[2048]; FileOutputStream fos = null; try { is = response.body().byteStream(); fos = new FileOutputStream(file); int len = 0; while ((len = is.read(buf)) != -1) { fos.write(buf, 0, len); } fos.flush(); } catch (IOException e) { LuaLog.e(e); } finally { try { if (is != null) { is.close(); } if (fos != null) { fos.close(); } } catch (IOException e) { LuaLog.e(e); } } return true; } catch (IOException e) { LuaLog.e(e); return false; } } } ================================================ FILE: hydrogen-library/src/main/java/androlua/LuaImageLoader.java ================================================ package androlua; import android.content.Context; import android.graphics.BitmapFactory; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.support.v4.content.ContextCompat; import android.util.Log; import android.widget.ImageView; import com.bumptech.glide.DrawableTypeRequest; import com.bumptech.glide.Glide; import com.bumptech.glide.load.model.GlideUrl; import com.bumptech.glide.load.model.LazyHeaders; import com.bumptech.glide.load.resource.bitmap.CenterCrop; import jp.wasabeef.glide.transformations.BlurTransformation; import jp.wasabeef.glide.transformations.RoundedCornersTransformation; import pub.hanks.luajandroid.R; /** * user image loader * Created by hanks on 2017/5/12. Copyright (C) 2017 Hanks */ public class LuaImageLoader { public static void load(ImageView imageView, String uri) { load(imageView.getContext(), imageView, uri); } public static void loadWithRadius(ImageView imageView, float radius, String uri) { Context context = imageView.getContext(); GradientDrawable gd = new GradientDrawable(); gd.setCornerRadius(LuaUtil.dp2px(radius)); gd.setColor(0xffebf0f2); load(context, imageView, uri, radius, 0, gd, gd); } public static void load(Context context, ImageView imageView, String uri) { load(context, imageView, uri, 0, 0, ContextCompat.getDrawable(context, R.drawable.ic_loading), ContextCompat.getDrawable(context, R.drawable.ic_loading)); } public static void load(Context context, ImageView imageView, String uri, float radius, float blueRadius, Drawable placeholderDrawable, Drawable errorDrawable) { if (imageView == null || uri == null) { return; } boolean loadLocal = false; if (uri.startsWith("#")) { // load local file uri = uri.substring(1); loadLocal = true; } if (!uri.startsWith("http://") && !uri.startsWith("https://") && !uri.startsWith("content://") && !uri.startsWith("file://")) { String path = uri; if (!uri.startsWith("/")) { path = LuaManager.getInstance().getLuaExtDir() + "/" + uri; } if (loadLocal) { imageView.setImageBitmap(BitmapFactory.decodeFile(path)); return; } uri = "file://" + path; } DrawableTypeRequest manager = Glide.with(context).load(uri); BlurTransformation blurTransformation = null; RoundedCornersTransformation roundedCornersTransformation = null; if (radius > 0){ roundedCornersTransformation = new RoundedCornersTransformation(context, LuaUtil.dp2px(radius), 0); } if (blueRadius > 0) { blurTransformation = new BlurTransformation(context, (int) blueRadius); } if (radius > 0 && blueRadius > 0) { manager.bitmapTransform(new CenterCrop(context),roundedCornersTransformation, blurTransformation); } else if (radius > 0) { manager.bitmapTransform(new CenterCrop(context),roundedCornersTransformation); } else if (blueRadius > 0) { manager.bitmapTransform(new CenterCrop(context),blurTransformation); } manager .placeholder(placeholderDrawable) .error(errorDrawable) .crossFade() .into(imageView); } public static void load(ImageView imageView, String uri, String referer) { if (imageView == null || uri == null) { return; } LazyHeaders headers = new LazyHeaders.Builder() .addHeader("Referer", referer) .build(); GlideUrl glideUrl = new GlideUrl(uri, headers); Glide.with(imageView.getContext()) .load(glideUrl) .placeholder(R.drawable.ic_loading) .error(R.drawable.ic_loading) .crossFade() .into(imageView); } } ================================================ FILE: hydrogen-library/src/main/java/androlua/LuaManager.java ================================================ package androlua; import android.content.Context; import com.luajava.JavaFunction; import com.luajava.LuaException; import com.luajava.LuaObject; import com.luajava.LuaState; import com.luajava.LuaStateFactory; import java.io.File; import androlua.common.LuaFileUtils; import androlua.common.LuaLog; import dalvik.system.DexClassLoader; public class LuaManager { private static LuaManager instance; private Context context; private String odexDir; private String libDir; // 外部 so 文件路径 private String luaDir; // 内部 lua 文件路径 private String luaExtDir; // 外部 lua 文件路径 private String luaCpath; // 相当于 LUA_CPATH private String luaLpath; // 相当于 LUA_PATH private boolean debugable = true; private LuaManager() { } public static LuaManager getInstance() { if (instance == null) { synchronized (LuaManager.class) { if (instance == null) { instance = new LuaManager(); } } } return instance; } public void init(Context context) { this.context = context; // 注册crashHandler // CrashHandler crashHandler = CrashHandler.getInstance(); // crashHandler.init(context.getApplicationContext()); //初始化AndroLua工作目录 luaExtDir = LuaFileUtils.getAndroLuaDir(); //定义文件夹 odexDir = context.getDir("odex", Context.MODE_PRIVATE).getAbsolutePath(); libDir = context.getDir("lib", Context.MODE_PRIVATE).getAbsolutePath(); luaDir = context.getDir("lua", Context.MODE_PRIVATE).getAbsolutePath(); luaCpath = context.getApplicationInfo().nativeLibraryDir + "/lib?.so" + ";" + libDir + "/lib?.so"; luaLpath = luaDir + "/?.lua;" + luaDir + "/lua/?.lua;" + luaDir + "/?/initEnv.lua;" + luaExtDir + "/?.lua;"; } public boolean isDebugable() { return debugable; } public LuaManager setDebugable(boolean debugable) { this.debugable = debugable; return this; } //运行lua脚本 public Object doFile(LuaState L, String filePath) throws LuaException { return doFile(L, filePath, new Object[0]); } public Object doFile(LuaState L, String filePath, Object[] args) throws LuaException { appendLuaDir(L, filePath); int ok = 0; L.setTop(0); ok = L.LloadFile(filePath); if (ok == 0) { L.getGlobal("debug"); L.getField(-1, "traceback"); L.remove(-2); L.insert(-2); int l = args.length; for (Object arg : args) { L.pushObjectValue(arg); } ok = L.pcall(l, 1, -2 - l); if (ok == 0) { return L.toJavaObject(-1); } } throw new LuaException(errorReason(ok) + ": " + L.toString(-1)); } //运行lua函数 public Object runFunc(LuaState L, String funcName, Object... args) { try { L.setTop(0); L.getGlobal(funcName); if (L.isFunction(-1)) { L.getGlobal("debug"); L.getField(-1, "traceback"); L.remove(-2); L.insert(-2); int argsLength = args.length; for (Object arg : args) { L.pushObjectValue(arg); } int ok = L.pcall(argsLength, 1, -2 - argsLength); if (ok == 0) { return L.toJavaObject(-1); } throw new LuaException(errorReason(ok) + ": " + L.toString(-1)); } } catch (LuaException e) { LuaLog.e(e); } return null; } //运行lua代码 public Object doString(LuaState L, String funcSrc, Object... args) throws LuaException { L.setTop(0); int ok = L.LloadString(funcSrc); if (ok == 0) { L.getGlobal("debug"); L.getField(-1, "traceback"); L.remove(-2); L.insert(-2); int l = args.length; for (Object arg : args) { L.pushObjectValue(arg); } ok = L.pcall(l, 1, -2 - l); if (ok == 0) { return L.toJavaObject(-1); } } throw new LuaException(errorReason(ok) + ": " + L.toString(-1)); } public DexClassLoader loadDex(ClassLoader parent, String path) throws LuaException { if (path.charAt(0) != '/') path = getLuaDir() + "/" + path; if (!new File(path).exists()) if (new File(path + ".dex").exists()) path += ".dex"; else if (new File(path + ".jar").exists()) path += ".jar"; else throw new LuaException(path + " not found"); return new DexClassLoader(path, odexDir, getContext().getApplicationInfo().nativeLibraryDir, parent); } public Object loadLib(LuaState L, String soPath) throws LuaException { if (!soPath.startsWith("/")) { soPath = libDir + "/" + soPath; } File soFile = new File(soPath); if (!soFile.exists()) { throw new LuaException("can not find lib " + soFile.getAbsolutePath()); } if (!libDir.equals(soFile.getParent())) { LuaUtil.copyFile(soFile.getAbsolutePath(), libDir + "/lib" + soFile.getName() + ".so"); } LuaObject require = L.getLuaObject("require"); return require.call(soFile.getName()); } //生成错误信息 private String errorReason(int error) { switch (error) { case 6: return "error error"; case 5: return "GC error"; case 4: return "Out of memory"; case 3: return "Syntax error"; case 2: return "Runtime error"; case 1: return "Yield error"; } return "Unknown error " + error; } public void appendSoDir(String dir) { if (!dir.startsWith("/")) { dir = getLibDir() + "/" + dir; } if (dir.endsWith(".so")) { dir = dir.substring(0, dir.lastIndexOf('/')); } String newPath = String.format(";%s/?.so", dir); if (luaCpath.contains(newPath)) { return; } luaCpath += newPath; } public void appendLuaDir(LuaState L, String dir) { if (!dir.startsWith("/")) { dir = getLuaExtDir() + "/" + dir; } if (dir.endsWith(".lua")) { dir = dir.substring(0, dir.lastIndexOf('/')); } String newPath = String.format(";%s/?.lua", dir); if (luaLpath.contains(newPath)) { return; } luaLpath += newPath; initLuaPath(L); } public Context getContext() { return context; } public String getOdexDir() { return odexDir; } public String getLibDir() { return libDir; } public String getLuaDir() { return luaDir; } public String getLuaExtDir() { return luaExtDir; } public String getLuaCpath() { return luaCpath; } public String getLuaLpath() { return luaLpath; } public LuaState initLua() { try { LuaState L = LuaStateFactory.newLuaState(); L.openLibs(); // // push 一个 context // L.pushJavaObject(getContext()); // // pop 并赋值给 activity // L.setGlobal("activity"); // // // 把全局变量 activity 的值 push 进栈 // L.getGlobal("activity"); // // pop 并赋值给 this // L.setGlobal("this"); // //// L.pushJavaObject(this); //// L.getGlobal("luajava"); // // L.pushString(getLuaExtDir()); // L.setField(-2, "luaextdir"); // // L.pushString(getLuaDir()); // L.setField(-2, "luadir"); // // // L.pushString(getLuaLpath()); // L.setField(-2, "luapath"); // // // 彈出一个元素 // L.pop(1); // 注册全局函数 print JavaFunction print = new LuaPrint(L); print.register("print"); initLuaPath(L); L.pop(1); return L; } catch (LuaException e) { e.printStackTrace(); return null; } } private void initLuaPath(LuaState L) { L.getGlobal("package"); L.pushString(getLuaLpath()); L.setField(-2, "path"); L.pushString(getLuaCpath()); L.setField(-2, "cpath"); } } ================================================ FILE: hydrogen-library/src/main/java/androlua/LuaPrint.java ================================================ package androlua; import android.util.Log; import com.luajava.JavaFunction; import com.luajava.LuaException; import com.luajava.LuaState; import androlua.common.LuaLog; public class LuaPrint extends JavaFunction { private LuaState L; private StringBuilder output = new StringBuilder(); public LuaPrint(LuaState L) { super(L); this.L = L; } @Override public int execute() throws LuaException { if (L.getTop() < 2) { LuaLog.e("error print"); return 0; } for (int i = 2; i <= L.getTop(); i++) { int type = L.type(i); String val = null; String stype = L.typeName(type); if (stype.equals("userdata")) { Object obj = L.toJavaObject(i); if (obj != null) val = obj.toString(); } else if (stype.equals("boolean")) { val = L.toBoolean(i) ? "true" : "false"; } else { val = L.toString(i); } if (val == null) val = stype; output.append("\t"); output.append(val); output.append("\t"); } Log.e("Luandroid", output.toString().substring(1, output.length() - 1)); output.setLength(0); return 0; } } ================================================ FILE: hydrogen-library/src/main/java/androlua/LuaTimer.java ================================================ package androlua; public class LuaTimer { // private LuaTimerTask task; // // public LuaTimer(LuaContext main, String src) throws LuaException { // this(main, src, null); // } // // public LuaTimer(LuaContext main, String src, Object[] arg) throws LuaException { // super("LuaTimer"); // this.task = new LuaTimerTask(main, src, arg); // } // // public LuaTimer(LuaContext main, LuaObject func) throws LuaException { // this(main, func, null); // } // // public LuaTimer(LuaContext main, LuaObject func, Object[] arg) throws LuaException { // super("LuaTimer"); // this.task = new LuaTimerTask(main, func, arg); // } // // public void gc() { // stop(); // } // // public void start(long delay, long period) { // schedule(this.task, delay, period); // } // // public void start(long delay) { // schedule(this.task, delay); // } // // public void stop() { // this.task.cancel(); // } // // public boolean isEnabled() { // return this.task.isEnabled(); // } // // public boolean getEnabled() { // return this.task.isEnabled(); // } // // public void setEnabled(boolean enabled) { // this.task.setEnabled(enabled); // } // // public long getPeriod() { // return this.task.getPeriod(); // } // // public void setPeriod(long period) { // this.task.setPeriod(period); // } } ================================================ FILE: hydrogen-library/src/main/java/androlua/LuaUtil.java ================================================ package androlua; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.res.AssetManager; import android.graphics.Bitmap; import android.graphics.PixelFormat; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.util.DisplayMetrics; import android.view.Display; import android.view.WindowManager; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import androlua.common.LuaStringUtils; public class LuaUtil { /** * 截屏 * * @param activity * @return */ public static Bitmap captureScreen(Activity activity) { // 获取屏幕大小: DisplayMetrics metrics = new DisplayMetrics(); WindowManager WM = (WindowManager) activity .getSystemService(Context.WINDOW_SERVICE); Display display = WM.getDefaultDisplay(); display.getMetrics(metrics); int height = metrics.heightPixels; // 屏幕高 int width = metrics.widthPixels; // 屏幕的宽 // 获取显示方式 int pixelformat = display.getPixelFormat(); PixelFormat localPixelFormat1 = new PixelFormat(); PixelFormat.getPixelFormatInfo(pixelformat, localPixelFormat1); int deepth = localPixelFormat1.bytesPerPixel;// 位深 byte[] piex = new byte[height * width * deepth]; try { Runtime.getRuntime().exec( new String[]{"/system/bin/su", "-c", "chmod 777 /dev/graphics/fb0"}); } catch (IOException e) { e.printStackTrace(); } try { // 获取fb0数据输入流 InputStream stream = new FileInputStream(new File( "/dev/graphics/fb0")); DataInputStream dStream = new DataInputStream(stream); dStream.readFully(piex); } catch (Exception e) { e.printStackTrace(); } // 保存图片 int[] colors = new int[height * width]; for (int m = 0; m < colors.length; m++) { int r = (piex[m * 4] & 0xFF); int g = (piex[m * 4 + 1] & 0xFF); int b = (piex[m * 4 + 2] & 0xFF); int a = (piex[m * 4 + 3] & 0xFF); colors[m] = (a << 24) + (r << 16) + (g << 8) + b; } // piex生成Bitmap Bitmap bitmap = Bitmap.createBitmap(colors, width, height, Bitmap.Config.ARGB_8888); return bitmap; } public static byte[] readAsset(Context context, String name) throws IOException { AssetManager am = context.getAssets(); InputStream is = am.open(name); byte[] ret = readAll(is); is.close(); //am.close(); return ret; } //读取asset文件 private static byte[] readAll(InputStream input) throws IOException { ByteArrayOutputStream output = new ByteArrayOutputStream(4096); byte[] buffer = new byte[4096]; int n = 0; while (-1 != (n = input.read(buffer))) { output.write(buffer, 0, n); } byte[] ret = output.toByteArray(); output.close(); return ret; } //复制asset文件到sd卡 public static void assetsToSD(Context context, String InFileName, String OutFileName) throws IOException { InputStream myInput; OutputStream myOutput = new FileOutputStream(OutFileName); myInput = context.getAssets().open(InFileName); byte[] buffer = new byte[8192]; int length = myInput.read(buffer); while (length > 0) { myOutput.write(buffer, 0, length); length = myInput.read(buffer); } myOutput.flush(); myInput.close(); myOutput.close(); } public static void copyFile(String oldPath, String newPath) { try { int bytesum = 0; int byteread = 0; File oldfile = new File(oldPath); if (oldfile.exists()) { //文件存在时 InputStream inStream = new FileInputStream(oldPath); //读入原文件 FileOutputStream fs = new FileOutputStream(newPath); byte[] buffer = new byte[4096]; int length; while ((byteread = inStream.read(buffer)) != -1) { bytesum += byteread; //字节数 文件大小 System.out.println(bytesum); fs.write(buffer, 0, byteread); } inStream.close(); } } catch (Exception e) { System.out.println("复制文件操作出错"); e.printStackTrace(); } } public static void rmDir(File dir) { File[] fs = dir.listFiles(); for (File f : fs) { if (f.isDirectory()) rmDir(f); else f.delete(); } dir.delete(); } public static void rmDir(File dir, String ext) { File[] fs = dir.listFiles(); for (File f : fs) { if (f.isDirectory()) rmDir(f); else if (f.getName().endsWith(ext)) f.delete(); } //dir.delete(); } public static Context getContext() { return LuaManager.getInstance().getContext(); } public static float getDensity() { return getContext().getResources().getDisplayMetrics().density; } public static int dp2px(float dp) { float density = getContext().getResources().getDisplayMetrics().density; return (int) (0.5F + dp * density); } public static int getScreenWidth() { return getContext().getResources().getDisplayMetrics().widthPixels; } protected int getStatusBarHeight() { int identifier = getContext().getResources().getIdentifier("status_bar_height", "dimen", "android"); if (identifier > 0) { return getContext().getResources().getDimensionPixelSize(identifier); } return 0; } public static class IntentHelper { public static String getLuaPath(Intent intent) { String luaPath = intent.getStringExtra("luaPath"); return LuaStringUtils.isEmpty(luaPath) ? "main.lua" : luaPath; } public static Object[] getArgs(Intent intent) { Object[] arg = (Object[]) intent.getSerializableExtra("arg"); if (arg == null) arg = new Object[0]; return arg; } } public static boolean isWifi(){ ConnectivityManager cm = (ConnectivityManager)getContext().getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); return activeNetwork != null && activeNetwork.isConnectedOrConnecting() && activeNetwork.getType() == ConnectivityManager.TYPE_WIFI; } } ================================================ FILE: hydrogen-library/src/main/java/androlua/LuaView.java ================================================ package androlua; import android.content.Context; import android.graphics.Canvas; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.view.View; /** * custom view * Created by hanks on 2017/6/6. */ public class LuaView extends View { private Creator creator; public LuaView(Context context) { this(context, null); } public LuaView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public LuaView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); if (creator != null) { creator.init(context, attrs, defStyleAttr); } } public void setCreator(Creator creator) { this.creator = creator; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (creator != null) { creator.onMeasure(widthMeasureSpec, heightMeasureSpec); } } @Override protected void onFinishInflate() { super.onFinishInflate(); if (creator != null) { creator.onFinishInflate(); } } @Override protected void onDraw(Canvas canvas) { if (creator != null) { creator.onDraw(canvas); } } public interface Creator { void init(Context context, @Nullable AttributeSet attrs, int defStyleAttr); void onDraw(Canvas canvas); void onFinishInflate(); void onMeasure(int widthMeasureSpec, int heightMeasureSpec); } } ================================================ FILE: hydrogen-library/src/main/java/androlua/LuaWebView.java ================================================ package androlua; import android.app.Activity; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Bitmap; import android.net.Uri; import android.net.http.SslError; import android.os.Build; import android.support.v7.app.AlertDialog; import android.util.AttributeSet; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; import android.webkit.HttpAuthHandler; import android.webkit.SslErrorHandler; import android.webkit.WebChromeClient; import android.webkit.WebResourceError; import android.webkit.WebResourceRequest; import android.webkit.WebResourceResponse; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import com.luajava.LuaException; import com.luajava.LuaObject; /** * LuaWebView * Created by hanks on 2017/5/27. */ public class LuaWebView extends WebView { private WebChromeClientListener webChromeClientListener; private WebViewClientListener webViewClientListener; public LuaWebView(Context context) { this(context, null); } public LuaWebView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public LuaWebView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); if (Build.VERSION.SDK_INT >= 19) { setLayerType(View.LAYER_TYPE_HARDWARE, null); } else { setLayerType(View.LAYER_TYPE_SOFTWARE, null); } WebSettings setting = getSettings(); setting.setSupportZoom(false); setting.setBuiltInZoomControls(false); setting.setDefaultFontSize(14); setting.setDefaultFixedFontSize(14); setting.setUseWideViewPort(true); setting.setLoadWithOverviewMode(true); setting.setDomStorageEnabled(true); setting.setAllowContentAccess(true); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { setting.setAllowFileAccessFromFileURLs(true); } setting.setAppCacheEnabled(true); setting.setDatabaseEnabled(true); setting.setSaveFormData(true); setting.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); setting.setAllowFileAccess(true); setting.setJavaScriptEnabled(true); setWebChromeClient(new LuaWebChromeClient()); setWebViewClient(new LuaWebViewClient()); if (Build.VERSION.SDK_INT >= 19) { setWebContentsDebuggingEnabled(true); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { setting.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); } setFocusable(true); setFocusableInTouchMode(true); } public void release() { if (getParent() == null || !(getParent() instanceof ViewGroup)) { return; } ((ViewGroup) getParent()).removeView(this); destroy(); } public void injectObjectToJavascript(LuaObject luaObject, String objectName) { addJavascriptInterface(new JavascriptInterface(luaObject), objectName); } public void setWebChromeClientListener(WebChromeClientListener webChromeClientListener) { this.webChromeClientListener = webChromeClientListener; } public void setWebViewClientListener(WebViewClientListener webViewClientListener) { this.webViewClientListener = webViewClientListener; } public interface WebViewClientListener { boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request); boolean shouldOverrideKeyEvent(WebView view, KeyEvent event); WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request); void onPageFinished(WebView view, String url); void onPageStarted(WebView view, String url, Bitmap favicon); void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error); } public interface WebChromeClientListener { void onProgressChanged(WebView view, int newProgress); void onReceivedTitle(WebView view, String title); void onReceivedIcon(WebView view, Bitmap icon); void onReceivedTouchIconUrl(WebView view, String url, boolean precomposed); } public static class JavascriptInterface { private final LuaObject luaObject; public JavascriptInterface(LuaObject luaObject) { this.luaObject = luaObject; } @android.webkit.JavascriptInterface public void call(String json) { try { luaObject.call(json); } catch (LuaException e) { e.printStackTrace(); } } } public class LuaWebViewClient extends WebViewClient { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (url.startsWith("hydrogen://")) { Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); intent.addCategory(Intent.CATEGORY_BROWSABLE); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setData(Uri.parse(url)); if (intent.resolveActivity(view.getContext().getPackageManager()) != null) { view.getContext().startActivity(intent); } return true; } return super.shouldOverrideUrlLoading(view, url); } @Override public void onReceivedSslError(WebView view, final SslErrorHandler handler, SslError error) { try { Context context = getContext(); if (context == null || !(context instanceof Activity)) { super.onReceivedSslError(view, handler, error); return; } new AlertDialog.Builder(context) .setMessage("error ssl cert invalid") .setPositiveButton("continue", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { handler.proceed(); } }).setNegativeButton("cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { handler.cancel(); } }).show(); } catch (Exception e) { e.printStackTrace(); } } @Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { if (webViewClientListener != null) { return webViewClientListener.shouldOverrideUrlLoading(view, request); } return super.shouldOverrideUrlLoading(view, request); } @Override public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) { return super.shouldOverrideKeyEvent(view, event); } @Override public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) { handler.proceed(host.trim(), realm.trim()); } @Override public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) { super.onReceivedHttpError(view, request, errorResponse); } @Override public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { if (webViewClientListener != null) { return webViewClientListener.shouldInterceptRequest(view, request); } return super.shouldInterceptRequest(view, request); } @Override public void onPageFinished(WebView view, String url) { if (webViewClientListener != null) { webViewClientListener.onPageFinished(view, url); } super.onPageFinished(view, url); } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { if (webViewClientListener != null) { webViewClientListener.onPageStarted(view, url, favicon); } super.onPageStarted(view, url, favicon); } @Override public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { if (webViewClientListener != null) { webViewClientListener.onReceivedError(view, request, error); } super.onReceivedError(view, request, error); } } public class LuaWebChromeClient extends WebChromeClient { @Override public void onProgressChanged(WebView view, int newProgress) { if (webChromeClientListener != null) { webChromeClientListener.onProgressChanged(view, newProgress); } super.onProgressChanged(view, newProgress); } @Override public void onReceivedTitle(WebView view, String title) { if (webChromeClientListener != null) { webChromeClientListener.onReceivedTitle(view, title); } super.onReceivedTitle(view, title); } @Override public void onReceivedIcon(WebView view, Bitmap icon) { if (webChromeClientListener != null) { webChromeClientListener.onReceivedIcon(view, icon); } super.onReceivedIcon(view, icon); } @Override public void onReceivedTouchIconUrl(WebView view, String url, boolean precomposed) { if (webChromeClientListener != null) { webChromeClientListener.onReceivedTouchIconUrl(view, url, precomposed); } super.onReceivedTouchIconUrl(view, url, precomposed); } } } ================================================ FILE: hydrogen-library/src/main/java/androlua/NineBitmapDrawable.java ================================================ package androlua; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; import java.io.IOException; public class NineBitmapDrawable extends Drawable { private Paint mPaint = new Paint(); private Bitmap mBitmap; private int mX1; private int mY1; private int mX2; private int mY2; private Rect mRect1; private Rect mRect2; private Rect mRect3; private Rect mRect4; private Rect mRect5; private Rect mRect6; private Rect mRect7; private Rect mRect8; private Rect mRect9; public NineBitmapDrawable(String path) throws IOException { this(LuaBitmap.getLoacalBitmap(path)); } public NineBitmapDrawable(Bitmap bitmap) { int w = bitmap.getWidth(); int h = bitmap.getHeight(); int c = Color.BLACK; int x1 = 0; int x2 = 0; for (int i = 0; i < w; i++) { if (bitmap.getPixel(i, 0) == c) { x1 = i; break; } } if (x1 == 0 || x1 == w - 1) throw new IllegalArgumentException("not found x1"); for (int i = x1; i < w; i++) { if (bitmap.getPixel(i, 0) != c) { x2 = w - i; break; } } if (x2 == 0 || x2 == 1) throw new IllegalArgumentException("not found x2"); int y1 = 0; int y2 = 0; for (int i = 0; i < h; i++) { if (bitmap.getPixel(0, i) == c) { y1 = i; break; } } if (y1 == 0 || y1 == h - 1) throw new IllegalArgumentException("not found y1"); for (int i = y1; i < h; i++) { if (bitmap.getPixel(0, i) != c) { y2 = h - i; break; } } if (y2 == 0 || y2 == 1) throw new IllegalArgumentException("not found y2"); init(bitmap, x1, y1, x2, y2); } public NineBitmapDrawable(Bitmap bitmap, int x1, int y1, int x2, int y2) { init(bitmap, x1, y1, x2, y2); } private void init(Bitmap bitmap, int x1, int y1, int x2, int y2) { mBitmap = bitmap; int w = bitmap.getWidth(); int h = bitmap.getHeight(); mX1 = x1; mY1 = y1; mX2 = x2; mY2 = y2; x2 = w - x2; y2 = h - y2; mRect1 = new Rect(1, 1, x1, y1); mRect2 = new Rect(x1, 1, x2, y1); mRect3 = new Rect(x2, 1, w - 1, y1); mRect4 = new Rect(1, y1, x1, y2); mRect5 = new Rect(x1, y1, x2, y2); mRect6 = new Rect(x2, y1, w - 1, y2); mRect7 = new Rect(1, y2, x1, h - 1); mRect8 = new Rect(x1, y2, x2, h - 1); mRect9 = new Rect(x2, y2, w - 1, h - 1); } @Override public void draw(Canvas canvas) { Rect rect = getBounds(); int w = rect.right; int h = rect.bottom; Rect rect1 = new Rect(0, 0, mX1, mY1); Rect rect2 = new Rect(mX1, 0, w - mX2, mY1); Rect rect3 = new Rect(w - mX2, 0, w, mY1); Rect rect4 = new Rect(0, mY1, mX1, h - mY2); Rect rect5 = new Rect(mX1, mY1, w - mX2, h - mY2); Rect rect6 = new Rect(w - mX2, mY1, w, h - mY2); Rect rect7 = new Rect(0, h - mY2, mX1, h); Rect rect8 = new Rect(mX1, h - mY2, w - mX2, h); Rect rect9 = new Rect(w - mX2, h - mY2, w, h); canvas.drawBitmap(mBitmap, mRect1, rect1, mPaint); canvas.drawBitmap(mBitmap, mRect2, rect2, mPaint); canvas.drawBitmap(mBitmap, mRect3, rect3, mPaint); canvas.drawBitmap(mBitmap, mRect4, rect4, mPaint); canvas.drawBitmap(mBitmap, mRect5, rect5, mPaint); canvas.drawBitmap(mBitmap, mRect6, rect6, mPaint); canvas.drawBitmap(mBitmap, mRect7, rect7, mPaint); canvas.drawBitmap(mBitmap, mRect8, rect8, mPaint); canvas.drawBitmap(mBitmap, mRect9, rect9, mPaint); } @Override public void setAlpha(int p1) { mPaint.setAlpha(p1); } @Override public void setColorFilter(ColorFilter p1) { mPaint.setColorFilter(p1); } @Override public int getOpacity() { return PixelFormat.UNKNOWN; } } ================================================ FILE: hydrogen-library/src/main/java/androlua/adapter/LuaFragmentPageAdapter.java ================================================ package androlua.adapter; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; /** * Created by hanks on 2017/5/26. Copyright (C) 2017 Hanks */ public class LuaFragmentPageAdapter extends FragmentPagerAdapter { private AdapterCreator creator; public LuaFragmentPageAdapter(FragmentManager fm, AdapterCreator creator) { super(fm); this.creator = creator; } @Override public Fragment getItem(int position) { return creator.getItem(position); } @Override public int getCount() { return (int) creator.getCount(); } @Override public CharSequence getPageTitle(int position) { return creator.getPageTitle(position); } public interface AdapterCreator { long getCount(); Fragment getItem(int position); String getPageTitle(int position); } } ================================================ FILE: hydrogen-library/src/main/java/androlua/adapter/LuaPagerAdapter.java ================================================ package androlua.adapter; import android.support.v4.view.PagerAdapter; import android.view.View; import android.view.ViewGroup; import com.luajava.LuaTable; import java.util.ArrayList; import java.util.List; /** * adapter for viewpager * Created by hanks on 2017/5/13. */ public class LuaPagerAdapter extends PagerAdapter { public List mListViews = new ArrayList<>(); public LuaPagerAdapter(LuaTable luaTable) { addViews(luaTable); } public void addViews(LuaTable luaTable) { if (luaTable == null) { return; } int size = luaTable.keySet().size(); for (int i = 1; i <= size; i++) { Object v = luaTable.get(i); if (v != null && v instanceof View) { mListViews.add((View) v); } } } @Override public int getCount() { return mListViews != null ? mListViews.size() : 0; } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } @Override public Object instantiateItem(ViewGroup container, int position) { View view = mListViews.get(position); container.addView(view); return view; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); } } ================================================ FILE: hydrogen-library/src/main/java/androlua/adapter/LuaRecyclerAdapter.java ================================================ package androlua.adapter; import android.support.v7.widget.RecyclerView; import android.view.ViewGroup; /** * Created by hanks on 2017/5/31. Copyright (C) 2017 Hanks */ public class LuaRecyclerAdapter extends RecyclerView.Adapter { AdapterCreator adapterCreator; public LuaRecyclerAdapter(AdapterCreator adapterCreator) { this.adapterCreator = adapterCreator; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return adapterCreator.onCreateViewHolder(parent, viewType); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { adapterCreator.onBindViewHolder(holder, position); } @Override public int getItemViewType(int position) { return (int) adapterCreator.getItemViewType(position); } @Override public int getItemCount() { return (int) adapterCreator.getItemCount(); } public interface AdapterCreator { RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType); void onBindViewHolder(RecyclerView.ViewHolder holder, int position); long getItemViewType(int position); long getItemCount(); } } ================================================ FILE: hydrogen-library/src/main/java/androlua/adapter/LuaRecyclerHolder.java ================================================ package androlua.adapter; import android.support.v7.widget.RecyclerView; import android.view.View; /** * Created by hanks on 2017/5/31. Copyright (C) 2017 Hanks */ public class LuaRecyclerHolder extends RecyclerView.ViewHolder { public LuaRecyclerHolder(View itemView) { super(itemView); } } ================================================ FILE: hydrogen-library/src/main/java/androlua/base/BaseActivity.java ================================================ package androlua.base; import android.graphics.Color; import android.os.Build; import android.os.Bundle; import android.support.annotation.Nullable; import android.view.View; import androlua.widget.statusbar.StatusBarView; import androlua.widget.swipebacklayout.app.SwipeBackActivity; import pub.hanks.luajandroid.R; /** * Created by hanks on 2017/6/2. Copyright (C) 2017 Hanks */ public class BaseActivity extends SwipeBackActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (Build.VERSION.SDK_INT >= 21) { View decorView = getWindow().getDecorView(); int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; decorView.setSystemUiVisibility(option); getWindow().setStatusBarColor(Color.TRANSPARENT); } } public void setStatusBarColor(int color) { View statusbar = findViewById(R.id.view_statusbar); if (statusbar != null && statusbar instanceof StatusBarView) { ((StatusBarView) statusbar).setStatusBarColor(color); return; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { getWindow().setStatusBarColor(color); } } public void setLightStatusBar() { if (Build.VERSION.SDK_INT >= 23) { View decorView = getWindow().getDecorView(); int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; decorView.setSystemUiVisibility(option); setStatusBarColor(0xFFFFFFFF); } else if (Build.VERSION.SDK_INT >= 21) { setStatusBarColor(0x33000000); } } } ================================================ FILE: hydrogen-library/src/main/java/androlua/base/BaseFragment.java ================================================ package androlua.base; import android.support.v4.app.Fragment; /** * Created by hanks on 2017/6/2. Copyright (C) 2017 Hanks */ public class BaseFragment extends Fragment { } ================================================ FILE: hydrogen-library/src/main/java/androlua/common/LuaConstants.java ================================================ package androlua.common; /** * Created by hanks on 2017/6/19. */ public class LuaConstants { public static final String KEY_VERSION = "key_version"; } ================================================ FILE: hydrogen-library/src/main/java/androlua/common/LuaFileUtils.java ================================================ package androlua.common; import android.content.Context; import android.content.res.AssetManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.os.Environment; import android.support.annotation.NonNull; import android.view.View; import com.luajava.LuaObject; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import androlua.LuaHttp; import androlua.LuaManager; import androlua.plugin.Plugin; import static android.graphics.Bitmap.CompressFormat.JPEG; import static android.graphics.Bitmap.CompressFormat.PNG; /** * LuaFileUtils * Created by hanks on 16/6/28. */ public class LuaFileUtils { // 缓存文件头信息-文件头信息 public static final HashMap mFileTypes = new HashMap<>(); private static final String APP_DIR = "LLLLLua"; // 32k 缩略图的限制,所以采用 RGB_565 比较小 private static final Bitmap.Config CONFIG = Bitmap.Config.RGB_565; static { // images mFileTypes.put("FFD8FFE1", "jpg"); mFileTypes.put("FFD8FFE0", "jpg"); mFileTypes.put("FFD8", "jpg"); mFileTypes.put("89504E47", "png"); mFileTypes.put("47494638", "gif"); mFileTypes.put("49492A00", "tif"); mFileTypes.put("424D", "bmp"); // mFileTypes.put("41433130", "dwg"); // CAD mFileTypes.put("38425053", "psd"); mFileTypes.put("7B5C727466", "rtf"); // 日记本 mFileTypes.put("3C3F786D6C", "xml"); mFileTypes.put("68746D6C3E", "html"); mFileTypes.put("44656C69766572792D646174653A", "eml"); // 邮件 mFileTypes.put("D0CF11E0", "doc"); mFileTypes.put("5374616E64617264204A", "mdb"); mFileTypes.put("252150532D41646F6265", "ps"); mFileTypes.put("255044462D312E", "pdf"); mFileTypes.put("504B0304", "docx"); mFileTypes.put("52617221", "rar"); mFileTypes.put("57415645", "wav"); mFileTypes.put("41564920", "avi"); mFileTypes.put("2E524D46", "rm"); mFileTypes.put("000001BA", "mpg"); mFileTypes.put("000001B3", "mpg"); mFileTypes.put("6D6F6F76", "mov"); mFileTypes.put("3026B2758E66CF11", "asf"); mFileTypes.put("4D546864", "mid"); mFileTypes.put("1F8B08", "gz"); mFileTypes.put("4D5A9000", "exe/dll"); mFileTypes.put("75736167", "txt"); } private static Context getContext() { return LuaManager.getInstance().getContext(); } /** * 根据文件路径获取文件头信息 * * @param filePath 文件路径 * @return 文件头信息 */ public static String getFileType(String filePath) { String fileHeader = getFileHeader(filePath); if (LuaStringUtils.isEmpty(fileHeader) || fileHeader.startsWith("FFD8")) { return "jpg"; } return mFileTypes.get(fileHeader); } /** * 根据文件路径获取文件头信息 * * @param filePath 文件路径 * @return 文件头信息 */ public static String getFileHeader(String filePath) { FileInputStream is = null; String value = null; try { is = new FileInputStream(filePath); byte[] b = new byte[4]; /* * int read() 从此输入流中读取一个数据字节。 int read(byte[] b) 从此输入流中将最多 b.length * 个字节的数据读入一个 byte 数组中。 int read(byte[] b, int off, int len) * 从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。 */ is.read(b, 0, b.length); value = bytesToHexString(b); } catch (Exception e) { } finally { if (null != is) { try { is.close(); } catch (IOException e) { } } } return value; } /** * 将要读取文件头信息的文件的byte数组转换成string类型表示 * * @param src 要读取文件头信息的文件的byte数组 * @return 文件头信息 */ private static String bytesToHexString(byte[] src) { StringBuilder builder = new StringBuilder(); if (src == null || src.length <= 0) { return null; } String hv; for (int i = 0; i < src.length; i++) { // 以十六进制(基数 16)无符号整数形式返回一个整数参数的字符串表示形式,并转换为大写 hv = Integer.toHexString(src[i] & 0xFF).toUpperCase(); if (hv.length() < 2) { builder.append(0); } builder.append(hv); } return builder.toString(); } public static void downloadPlugin(final String url, final String pluginName, final LuaObject callback) { new Thread() { @Override public void run() { super.run(); try { String destDirectory = getPluginsDir() + "/" + pluginName; String savePath = destDirectory + ".zip"; LuaHttp.downloadFile(url, savePath); LuaFileUtils.unzip(savePath, getPluginsDir()); deleteFileOrDir(new File(savePath)); callback.call(destDirectory); } catch (Exception e) { e.printStackTrace(); } } }.start(); } public static void downloadLuaFile(final String url, final LuaObject callback) { new Thread() { @Override public void run() { super.run(); try { String destDirectory = LuaManager.getInstance().getLuaExtDir() + "/lua"; String savePath = destDirectory + ".zip"; LuaHttp.downloadFile(url, savePath); LuaFileUtils.unzip(savePath, LuaManager.getInstance().getLuaExtDir()); deleteFileOrDir(new File(savePath)); if (callback != null) callback.call(destDirectory); } catch (Exception e) { e.printStackTrace(); } } }.start(); } public static void deleteFileOrDir(File file) { if (file == null || !file.exists()) { return; } if (!file.isDirectory()) { file.delete(); return; } File[] files = file.listFiles(); if (files == null) { return; } for (File f : files) { deleteFileOrDir(f); } file.delete(); } public static void removePlugin(String pluginId) { for (Plugin plugin : getPluginList()) { if (pluginId.equals(plugin.getId())) { File file = new File(plugin.getPath()); deleteFileOrDir(file); } } } public static String getPluginsDir() { return getAndroLuaDir(); } public static List getPluginList() { // 读取总目录 File pluginDir = new File(getPluginsDir()); if (!pluginDir.exists()) { return Collections.emptyList(); } List pluginList = new ArrayList<>(); for (File file : pluginDir.listFiles()) { // 读取单个插件文件 Plugin plugin = parsePluginInfo(file); if (plugin == null) { continue; } pluginList.add(plugin); } Collections.sort(pluginList, new Comparator() { @Override public int compare(Plugin o1, Plugin o2) { return (int) (o1.getUpdateAt() - o2.getUpdateAt()); } }); return pluginList; } // 解析插件 private static Plugin parsePluginInfo(File pluginDir) { if (pluginDir == null || !pluginDir.isDirectory()) { return null; } File info = null; for (File pFile : pluginDir.listFiles()) { if ("info.json".equals(pFile.getName())) { info = pFile; } } if (info == null) { return null; } String str = file2String(info); try { Plugin plugin = new Plugin(); plugin.setUpdateAt(info.lastModified()); JSONObject jsonObject = new JSONObject(str); plugin.setPath(pluginDir.getAbsolutePath()); plugin.setPlugin(true); plugin.setId(jsonObject.getString("id")); plugin.setName(jsonObject.getString("name")); plugin.setIconPath(jsonObject.getString("icon")); plugin.setMainPath(jsonObject.getString("main")); plugin.setVersionName(jsonObject.getString("versionName")); plugin.setVersionCode(jsonObject.getInt("versionCode")); return plugin; } catch (JSONException e) { e.printStackTrace(); } return null; } public static void copyAssetsFlies(String assetDir, String outputDir) { try { String[] files = getContext().getAssets().list(assetDir); if (files == null) { return; } for (String file : files) { copyFile(getContext().getAssets().open(assetDir + "/" + file), outputDir + "/" + file); } } catch (IOException e) { e.printStackTrace(); } } public static void copyFile(InputStream inStream, String newPath) throws IOException { int len; FileOutputStream fs = new FileOutputStream(newPath); byte[] buffer = new byte[4096]; while ((len = inStream.read(buffer)) != -1) { fs.write(buffer, 0, len); } inStream.close(); } private static String file2String(File file) { BufferedReader br = null; try { br = new BufferedReader(new FileReader(file)); StringBuilder sb = new StringBuilder(); String line = br.readLine(); while (line != null) { sb.append(line); sb.append("\n"); line = br.readLine(); } return sb.toString(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (br != null) br.close(); } catch (IOException e) { e.printStackTrace(); } } return ""; } public static File convertViewToImage(View view, @NonNull String filePath) throws Exception { if (view.getWidth() == 0 || view.getHeight() == 0) { throw new Exception("width or height must not be 0"); } if (view.getHeight() > 100000) { throw new Exception("must small"); } final File saveFile = new File(filePath); Bitmap createBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(createBitmap); canvas.save(); view.draw(canvas); canvas.restore(); String imageType = createBitmap.getHeight() > 20000 ? "jpg" : "png"; Bitmap.CompressFormat compressFormat = "jpg".equals(imageType) ? PNG : JPEG; bitmapToFile(createBitmap, saveFile, compressFormat, "jpg".equals(imageType) ? 90 : 100); if (createBitmap != null) { createBitmap.recycle(); } canvas.setBitmap(null); System.gc(); return saveFile; } public static File bitmapToFile(Bitmap bitmap, File file, Bitmap.CompressFormat compressFormat, int quality) { if (file == null) { return null; } FileOutputStream fileOutputStream = null; try { fileOutputStream = new FileOutputStream(file); bitmap.compress(compressFormat, quality, fileOutputStream); fileOutputStream.flush(); fileOutputStream.close(); return file; } catch (Exception e) { e.printStackTrace(); return null; } finally { try { if (fileOutputStream != null) fileOutputStream.close(); } catch (IOException e22) { e22.printStackTrace(); } } } public static String saveImage(String imagePath) { File file = new File(imagePath); if (!file.exists()) { return null; } try { String fileName = System.currentTimeMillis() + ".png"; String outputPath = getProjectImagePath(); //create output directory if it doesn't exist File dir = new File(outputPath); if (!dir.exists()) { dir.mkdirs(); } InputStream in = new FileInputStream(imagePath); OutputStream out = new FileOutputStream(outputPath + "/" + fileName); byte[] buffer = new byte[1024]; int read; while ((read = in.read(buffer)) != -1) { out.write(buffer, 0, read); } in.close(); // write the output file (You have now copied the file) out.flush(); out.close(); return fileName; } catch (Exception e) { e.printStackTrace(); return null; } } public static String getImagePath(String name) { return getProjectImagePath() + "/" + name; } public static String getPublicPicturePath(String fileName) { // Get the directory for the user's public pictures directory. File file = new File(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES), APP_DIR); if (!file.mkdirs()) { LuaLog.e("Directory not created"); } return file.getAbsolutePath() + "/" + fileName; } public static Bitmap getBitmapFromFile(String name) { String filePath = getProjectImagePath() + "/" + name; return BitmapFactory.decodeFile(filePath); } public static void makeDefaultCSSFile() { String path = getProjectCSSPath() + "/marked.css"; File file = new File(path); if (!file.exists()) { makeDefaultCSSFile(path); } } private static void makeDefaultCSSFile(String path) { try { InputStream inputStream = getContext().getResources().getAssets().open("marked.css"); saveToFile(inputStream, path); } catch (IOException e) { e.printStackTrace(); } } public static String convertStreamToString(InputStream is) { BufferedReader br = null; StringBuilder sb = new StringBuilder(); String line; try { br = new BufferedReader(new InputStreamReader(is)); while ((line = br.readLine()) != null) { sb.append(line); } } catch (IOException e) { e.printStackTrace(); } finally { if (br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } return sb.toString(); } public static String getProjectCSSPath() { return insureDirExists(getProjectPath() + "/css"); } public static String getBackupPath() { return insureDirExists(getProjectPath() + "/backup"); } public static String getBackupNotePath() { return insureDirExists(getProjectPath() + "/notejson"); } private static String insureDirExists(String dir) { File file = new File(dir); if (!file.exists()) { file.mkdirs(); } return file.getAbsolutePath(); } public static String getProjectImagePath() { String path = getProjectPath() + "/images"; insureDirExists(path); File noMediaFile = new File(path, ".nomedia"); if (!noMediaFile.exists()) { try { noMediaFile.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } return path; } public static String getProjectPath() { String downloadDir = getContext().getExternalFilesDir(APP_DIR).getAbsolutePath(); String filePath = downloadDir; File file = new File(filePath); if (!file.exists()) { file.mkdirs(); } return filePath; } public static boolean sdCardAvaible() { return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); } public static String saveToFile(InputStream in, String filePath) { try { FileOutputStream out = new FileOutputStream(filePath); byte[] buffer = new byte[1024]; int read; while ((read = in.read(buffer)) != -1) { out.write(buffer, 0, read); } in.close(); out.flush(); out.close(); return filePath; } catch (Exception e) { e.printStackTrace(); return filePath; } } public static String saveToFile(String txt, String filePath) { try { File file = new File(filePath); if (!file.exists()) { file.getParentFile().mkdirs(); file.createNewFile(); } FileWriter fooWriter = new FileWriter(file, false); // true to append // false to overwrite. fooWriter.write(txt); fooWriter.close(); return file.getAbsolutePath(); } catch (IOException e) { e.printStackTrace(); } return txt; } public static String getFontPath(String fontAlias) { String path = getProjectPath() + File.separator + "font"; File dir = new File(path); if (!dir.exists()) { dir.mkdirs(); } return (dir + File.separator + fontAlias).toLowerCase(); } /** * Extracts a zip file specified by the zipFilePath to a directory specified by * destDirectory (will be created if does not exists) * * @param zipFilePath * @param destDirectory * @throws IOException */ public static void unzip(String zipFilePath, String destDirectory) throws IOException { File destDir = new File(destDirectory); if (!destDir.exists()) { destDir.mkdir(); } ZipInputStream zipIn = new ZipInputStream(new FileInputStream(zipFilePath)); ZipEntry entry = zipIn.getNextEntry(); // iterates over entries in the zip file while (entry != null) { String filePath = (destDirectory + File.separator + entry.getName()).toLowerCase(); if (!entry.isDirectory()) { // if the entry is a file, extracts it extractFile(zipIn, filePath); } else { // if the entry is a directory, make the directory File dir = new File(filePath); dir.mkdir(); } zipIn.closeEntry(); entry = zipIn.getNextEntry(); } zipIn.close(); } /** * Extracts a zip entry (file entry) * * @param zipIn * @param filePath * @throws IOException */ private static void extractFile(ZipInputStream zipIn, String filePath) throws IOException { BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath)); byte[] bytesIn = new byte[2048]; int read = 0; while ((read = zipIn.read(bytesIn)) != -1) { bos.write(bytesIn, 0, read); } bos.close(); } public static void rewriteFile(File file, String content) { BufferedWriter bw = null; FileWriter fw = null; try { fw = new FileWriter(file); bw = new BufferedWriter(fw); bw.write(content); } catch (IOException e) { e.printStackTrace(); } finally { try { if (bw != null) bw.close(); if (fw != null) fw.close(); } catch (IOException ex) { ex.printStackTrace(); } } } public static String getFileContent(File file) { BufferedReader br = null; try { br = new BufferedReader(new FileReader(file)); StringBuilder sb = new StringBuilder(); String line = br.readLine(); while (line != null) { sb.append(line); sb.append("\n"); line = br.readLine(); } return sb.toString(); } catch (Exception e) { e.printStackTrace(); } finally { try { if (br != null) br.close(); } catch (IOException e) { e.printStackTrace(); } } return null; } public static void deleteFile(String path) { File file = new File(path); if (file.exists()) { file.delete(); } } public static void deleteDir(String dirPath) { File dir = new File(dirPath); if (!dir.exists()) { return; } if (dir.isDirectory()) { if (dir.listFiles() == null) { dir.delete(); return; } for (File file : dir.listFiles()) { deleteDir(file.getAbsolutePath()); } } else { dir.delete(); } } public static void writeZip(File file, String parentPath, ZipOutputStream zos) { if (file.exists()) { if (file.isDirectory()) {//处理文件夹 parentPath += file.getName() + File.separator; File[] files = file.listFiles(); if (files == null) { return; } for (File f : files) { writeZip(f, parentPath, zos); } } else { FileInputStream fis = null; try { fis = new FileInputStream(file); ZipEntry ze = new ZipEntry(parentPath + file.getName()); zos.putNextEntry(ze); byte[] content = new byte[1024]; int len; while ((len = fis.read(content)) != -1) { zos.write(content, 0, len); zos.flush(); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (fis != null) { fis.close(); } } catch (IOException e) { e.printStackTrace(); } } } } } public static String getAndroLuaDir() { File appDir; if (sdCardAvaible()) { appDir = getContext().getExternalFilesDir(APP_DIR); } else { appDir = new File(getContext().getFilesDir(), APP_DIR); } appDir.mkdirs(); // dont need judge dir exits return appDir.getAbsolutePath(); } public static byte[] readAsset(String name) throws IOException { AssetManager am = getContext().getAssets(); InputStream is = am.open(name); byte[] ret = readAll(is); is.close(); //am.close(); return ret; } private static byte[] readAll(InputStream input) throws IOException { ByteArrayOutputStream output = new ByteArrayOutputStream(4096); byte[] buffer = new byte[4096]; int n = 0; while (-1 != (n = input.read(buffer))) { output.write(buffer, 0, n); } byte[] ret = output.toByteArray(); output.close(); return ret; } //复制asset文件到sd卡 public static void assetsToSD(String InFileName, String OutFileName) throws IOException { InputStream myInput; OutputStream myOutput = new FileOutputStream(OutFileName); myInput = getContext().getAssets().open(InFileName); byte[] buffer = new byte[8192]; int length = myInput.read(buffer); while (length > 0) { myOutput.write(buffer, 0, length); length = myInput.read(buffer); } myOutput.flush(); myInput.close(); myOutput.close(); } /** * 解压Assets中的文件 */ public static void unZipAssets(String assetName, String outputDirectory) throws IOException { //创建解压目标目录 File file = new File(outputDirectory); //如果目标目录不存在,则创建 if (!file.exists()) { file.mkdirs(); } InputStream inputStream = null; //打开压缩文件 try { inputStream = getContext().getAssets().open(assetName); } catch (IOException e) { return; } ZipInputStream zipInputStream = new ZipInputStream(inputStream); //读取一个进入点 ZipEntry zipEntry = zipInputStream.getNextEntry(); //使用1Mbuffer byte[] buffer = new byte[1024 * 32]; //解压时字节计数 int count = 0; //如果进入点为空说明已经遍历完所有压缩包中文件和目录 while (zipEntry != null) { //如果是一个目录 if (zipEntry.isDirectory()) { //String name = zipEntry.getName(); //name = name.substring(0, name.length() - 1); file = new File(outputDirectory + File.separator + zipEntry.getName()); file.mkdir(); } else { //如果是文件 file = new File(outputDirectory + File.separator + zipEntry.getName()); //创建该文件 file.createNewFile(); FileOutputStream fileOutputStream = new FileOutputStream(file); while ((count = zipInputStream.read(buffer)) > 0) { fileOutputStream.write(buffer, 0, count); } fileOutputStream.close(); } //定位到下一个文件入口 zipEntry = zipInputStream.getNextEntry(); } zipInputStream.close(); } } ================================================ FILE: hydrogen-library/src/main/java/androlua/common/LuaLog.java ================================================ package androlua.common; import android.util.Log; import androlua.LuaManager; /** * LuaLog * Created by hanks on 2016/11/19. */ public class LuaLog { private static final String TAG = "LLogs"; public static boolean showLog() { return LuaManager.getInstance().isDebugable(); } public static void i(String s) { if (showLog()) { Log.i(TAG, s == null ? "null" : s); } } public static void w(String s) { if (showLog()) { Log.w(TAG, s == null ? "null" : s); } } public static void d(String s) { if (showLog()) { Log.d(TAG, s == null ? "null" : s); } } public static void e(String s) { if (showLog()) { Log.e(TAG, s == null ? "null" : s); } } public static void e(Throwable e) { if (showLog() && e != null) { e.printStackTrace(); } } } ================================================ FILE: hydrogen-library/src/main/java/androlua/common/LuaSp.java ================================================ package androlua.common; import android.content.Context; import android.content.SharedPreferences; import androlua.LuaManager; /** * SharedPreferences * Created by hanks on 2017/6/19. */ public class LuaSp { private static LuaSp instance; private final SharedPreferences sp; public static LuaSp getInstance(String fileName) { if (instance == null) { synchronized (LuaSp.class) { if (instance == null) { instance = new LuaSp(fileName); } } } return instance; } private LuaSp(String fileName) { Context context = LuaManager.getInstance().getContext(); sp = context.getSharedPreferences(fileName, Context.MODE_PRIVATE); } public void save(String key, Object value) { SharedPreferences.Editor editor = sp.edit(); if (value instanceof Boolean) { editor.putBoolean(key, (Boolean) value); } else if (value instanceof String) { editor.putString(key, (String) value); } else if (value instanceof Integer) { editor.putInt(key, (Integer) value); } else if (value instanceof Float) { editor.putFloat(key, (Float) value); } else if (value instanceof Long) { editor.putLong(key, (Long) value); } editor.apply(); } public T get(String key, T defaultValue) { Object value = null; if (defaultValue instanceof Boolean) { value = sp.getBoolean(key, (Boolean) defaultValue); } else if (defaultValue instanceof String) { value = sp.getString(key, (String) defaultValue); } else if (defaultValue instanceof Float) { value = sp.getFloat(key, (Float) defaultValue); } else if (defaultValue instanceof Long) { value = sp.getLong(key, (Long) defaultValue); } else if (defaultValue instanceof Integer) { value = sp.getInt(key, (Integer) defaultValue); } return (T) value; } /** * 移除某个key值已经对应的值 */ public void remove(String key) { SharedPreferences.Editor editor = sp.edit(); editor.remove(key); editor.apply(); } /** * 是否已经存在该 key */ public boolean contains(String key) { return sp.contains(key); } /** * 清除所有数据 */ public void clear() { SharedPreferences.Editor editor = sp.edit(); editor.clear(); editor.apply(); } } ================================================ FILE: hydrogen-library/src/main/java/androlua/common/LuaStringUtils.java ================================================ package androlua.common; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * 字符串处理 * Created by hanks on 2016/11/29. */ public class LuaStringUtils { public static final String EMPTY_CHAR = "\u200B"; public static boolean isEmpty(String s) { return s == null || s.length() == 0; } // 去除最后一行换行 public static String trimEnd(String s) { if (!isEmpty(s) && (s.endsWith("\n") || s.endsWith(EMPTY_CHAR))) { return s.substring(0, s.length() - 1); } else { return s; } } public static boolean isEmptyTrim(String s) { return s == null || s.trim().length() == 0 || EMPTY_CHAR.equals(s); } public static String md5(String source) { String target = ""; if (source == null) source = ""; try { MessageDigest md = MessageDigest.getInstance("MD5"); md.update(source.getBytes()); byte b[] = md.digest(); int i; StringBuffer buf = new StringBuffer(""); for (int offset = 0; offset < b.length; offset++) { i = b[offset]; if (i < 0) i += 256; if (i < 16) buf.append("0"); buf.append(Integer.toHexString(i)); } target = buf.toString(); } catch (NoSuchAlgorithmException e) { } return target; } public static boolean isUrl(String str) { return str != null && (str.startsWith("http://") || str.startsWith("https://")); } } ================================================ FILE: hydrogen-library/src/main/java/androlua/common/LuaToast.java ================================================ package androlua.common; import android.widget.Toast; import androlua.LuaManager; /** * Created by hanks on 2017/6/19. */ public class LuaToast { public static void show(String s) { Toast.makeText(LuaManager.getInstance().getContext(), s, Toast.LENGTH_SHORT).show(); } } ================================================ FILE: hydrogen-library/src/main/java/androlua/fragment/MenuFragment.java ================================================ package androlua.fragment; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.GradientDrawable; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AlertDialog; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.EditText; import android.widget.ImageView; import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.target.Target; import java.io.File; import java.util.List; import androlua.LuaActivity; import androlua.LuaManager; import androlua.LuaUtil; import androlua.base.BaseFragment; import androlua.common.LuaFileUtils; import androlua.common.LuaStringUtils; import androlua.plugin.Plugin; import androlua.utils.ShortcutUtils; import jp.wasabeef.glide.transformations.RoundedCornersTransformation; import pub.hanks.luajandroid.R; /** * MenuFragment * Created by hanks on 2017/8/22. */ public class MenuFragment extends BaseFragment { public static MenuFragment newInstance() { Bundle args = new Bundle(); MenuFragment fragment = new MenuFragment(); fragment.setArguments(args); return fragment; } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_menu, container, false); return view; } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); view.findViewById(R.id.add_shortcut).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (getActivity() == null || !(getActivity() instanceof LuaActivity)) { return; } Intent intent = getActivity().getIntent(); String luaFile = LuaUtil.IntentHelper.getLuaPath(intent); String luaExtDir = LuaManager.getInstance().getLuaExtDir(); if (!luaFile.startsWith("/")) { luaFile = luaExtDir + "/" + luaFile; } File file = new File(luaFile); if (!file.exists()) { return; } String pluginRoot; do { pluginRoot = file.getAbsolutePath(); file = file.getParentFile(); } while (!luaExtDir.equals(file.getAbsolutePath())); String name = "氢-" + file.getName(); Plugin p = null; List pluginList = LuaFileUtils.getPluginList(); for (Plugin plugin : pluginList) { if (plugin.getPath().equals(pluginRoot)) { name = plugin.getName(); p = plugin; break; } } if (p != null) { showAddShortcutDialog(intent, name, p.getIconPath()); } else { showAddShortcutDialog(intent, name, null); } } }); } private void showAddShortcutDialog(final Intent intent, String name, String iconPath) { if (getActivity() == null || !(getActivity() instanceof LuaActivity)) { return; } ((LuaActivity) getActivity()).closeDrawer(); View view = View.inflate(getActivity(), R.layout.dialog_add_shortcut, null); final EditText et_name = (EditText) view.findViewById(R.id.name); final ImageView iv_icon = (ImageView) view.findViewById(R.id.icon); et_name.setText("氢 · " + name); final Bitmap[] bm = new Bitmap[1]; if (!LuaStringUtils.isEmpty(iconPath)) { GradientDrawable gd = new GradientDrawable(); gd.setCornerRadius(LuaUtil.dp2px(100)); gd.setColor(0xffebf0f2); Glide.with(this) .load(iconPath) .asBitmap() .placeholder(gd) .transform(new RoundedCornersTransformation(getContext(), LuaUtil.dp2px(100), 0)) .listener(new RequestListener() { @Override public boolean onException(Exception e, String model, Target target, boolean isFirstResource) { return false; } @Override public boolean onResourceReady(Bitmap resource, String model, Target target, boolean isFromMemoryCache, boolean isFirstResource) { bm[0] = resource; return false; } }).into(iv_icon); } new AlertDialog.Builder(getActivity()) .setTitle("放到桌面") .setView(view) .setNegativeButton("取消", null) .setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { String name = et_name.getText().toString(); if (LuaStringUtils.isEmpty(name)) { name = " "; } if (bm[0] == null) { bm[0] = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher); } ShortcutUtils.installShortcut(getActivity(), name, bm[0], intent); } }) .show(); } } ================================================ FILE: hydrogen-library/src/main/java/androlua/plugin/Plugin.java ================================================ package androlua.plugin; import com.luajava.LuaState; import androlua.LuaManager; /** * Created by hanks on 2017/5/5. Copyright (C) 2017 Hanks */ public class Plugin { private final LuaManager luaManager; private String path; private String id; private String name; private String iconPath; private String mainPath; private String versionName; private int versionCode; private boolean isPlugin; private long updateAt; private LuaState L; public Plugin() { luaManager = LuaManager.getInstance(); } public boolean isPlugin() { return isPlugin; } public void setPlugin(boolean plugin) { isPlugin = plugin; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void setUpdateAt(long updateAt) { this.updateAt = updateAt; } public long getUpdateAt() { return updateAt; } public String getIconPath() { if (iconPath == null) { return "http://image.coolapk.com/apk_logo/2016/0108/12202_1452248424_4592.png"; } if (iconPath.startsWith("http://") || iconPath.startsWith("https://")) { return iconPath; } if (!iconPath.startsWith("/")) { iconPath = getPath() + "/" + iconPath; } if (iconPath.startsWith("/")) { iconPath = "file://" + iconPath; } return iconPath; } public void setIconPath(String iconPath) { this.iconPath = iconPath; } public String getMainPath() { if (!mainPath.startsWith("/")) { setMainPath(getPath() + "/" + mainPath); } return mainPath; } public void setMainPath(String mainPath) { this.mainPath = mainPath; } public String getVersionName() { return versionName; } public void setVersionName(String versionName) { this.versionName = versionName; } public int getVersionCode() { return versionCode; } public void setVersionCode(int versionCode) { this.versionCode = versionCode; } } ================================================ FILE: hydrogen-library/src/main/java/androlua/utils/ColorStateListFactory.java ================================================ package androlua.utils; import android.content.res.ColorStateList; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.RippleDrawable; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.StateListDrawable; import android.graphics.drawable.shapes.RoundRectShape; import android.os.Build; import java.util.Arrays; /** * Created by hanks on 2017/5/31. Copyright (C) 2017 Hanks */ public class ColorStateListFactory { public static ColorStateList newInstance(int normalColor) { return ColorStateList.valueOf(normalColor); } public static ColorStateList newInstance(int normalColor, int selectedColor) { int[][] states = new int[][]{ {-android.R.attr.state_checked}, {android.R.attr.state_checked}, {android.R.attr.state_pressed}, {android.R.attr.state_enabled}, {android.R.attr.state_selected}, }; int[] colorList = new int[9]; colorList[0] = normalColor; colorList[1] = selectedColor; colorList[2] = selectedColor; colorList[3] = selectedColor; colorList[4] = selectedColor; return new ColorStateList(states, colorList); } public static Drawable getRippleDrawable( int normalColor, int pressedColor) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { return new RippleDrawable(ColorStateList.valueOf(pressedColor), null, getRippleMask(normalColor)); } else { return getStateListDrawable(normalColor, pressedColor); } } private static Drawable getRippleMask(int color) { float[] outerRadii = new float[8]; // 3 is radius of final ripple, // instead of 3 you can give required final radius Arrays.fill(outerRadii, 3); RoundRectShape r = new RoundRectShape(outerRadii, null, null); ShapeDrawable shapeDrawable = new ShapeDrawable(r); shapeDrawable.getPaint().setColor(color); return shapeDrawable; } public static StateListDrawable getStateListDrawable( int normalColor, int pressedColor) { StateListDrawable states = new StateListDrawable(); states.addState(new int[]{android.R.attr.state_pressed}, new ColorDrawable(pressedColor)); states.addState(new int[]{android.R.attr.state_focused}, new ColorDrawable(pressedColor)); states.addState(new int[]{android.R.attr.state_activated}, new ColorDrawable(pressedColor)); states.addState(new int[]{}, new ColorDrawable(normalColor)); return states; } } ================================================ FILE: hydrogen-library/src/main/java/androlua/utils/DialogUtils.java ================================================ package androlua.utils; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.view.View; import android.widget.EditText; import com.luajava.LuaException; import com.luajava.LuaObject; import com.luajava.LuaTable; import pub.hanks.luajandroid.R; /** * DialogUtils * Created by hanks on 2017/6/30. */ public class DialogUtils { public static AlertDialog showWithInput(Context context, LuaTable config, final LuaObject callback) { if (!(context instanceof Activity)) { return null; } View view = View.inflate(context, R.layout.dialog_input, null); final EditText et = (EditText) view.findViewById(R.id.et); AlertDialog.Builder builder = new AlertDialog.Builder(context); if (config.containsKey("title")) { builder.setTitle((String) config.get("title")); } if (config.containsKey("msg")) { builder.setMessage((String) config.get("msg")); } if (config.containsKey("cancelable")) { builder.setCancelable((Boolean) config.get("cancelable")); } if (config.containsKey("content")) { et.setText((String) config.get("content")); } if (config.containsKey("hit")) { et.setText((String) config.get("content")); } if (config.containsKey("title")) { builder.setTitle((String) config.get("title")); } if (config.containsKey("ok")) { builder.setPositiveButton((String) config.get("ok"), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { try { callback.call(et.getText().toString()); } catch (LuaException e) { e.printStackTrace(); } } }); } else { builder.setPositiveButton("确定", null); } if (config.containsKey("cancel")) { builder.setNegativeButton((String) config.get("cancel"), null); } else { builder.setNegativeButton("取消", null); } return builder.create(); } } ================================================ FILE: hydrogen-library/src/main/java/androlua/utils/LauncherUtil.java ================================================ package androlua.utils; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.text.TextUtils; public class LauncherUtil { public static final String READ_SETTINGS = "READ_SETTINGS"; public static final String WRITE_SETTINGS = "WRITE_SETTINGS"; private LauncherUtil() { } public static String getDefaultLauncher(Context context) { try { Intent intent = new Intent("android.intent.action.MAIN"); intent.addCategory("android.intent.category.HOME"); ResolveInfo resolveActivity = context.getPackageManager().resolveActivity(intent, 0); if (resolveActivity.activityInfo.packageName.equals("android")) { return null; } return resolveActivity.activityInfo.packageName; } catch (Exception e) { e.printStackTrace(); return null; } } public static String getAuthorityFromPermission(Context context, String str) { String str2 = null; if (!(TextUtils.isEmpty(str) || context == null)) { String defaultLauncher = getDefaultLauncher(context); if (!TextUtils.isEmpty(defaultLauncher)) { try { PackageInfo packageInfo = context.getPackageManager().getPackageInfo(defaultLauncher, PackageManager.GET_PROVIDERS); if (packageInfo != null) { ProviderInfo[] providerInfoArr = packageInfo.providers; if (providerInfoArr != null) { for (ProviderInfo providerInfo : providerInfoArr) { if ((!TextUtils.isEmpty(providerInfo.readPermission) && providerInfo.readPermission.contains(str)) || (!TextUtils.isEmpty(providerInfo.writePermission) && providerInfo.writePermission.contains(str))) { str2 = providerInfo.authority; break; } } } } } catch (Exception e) { e.printStackTrace(); } } } return str2; } } ================================================ FILE: hydrogen-library/src/main/java/androlua/utils/ShortcutUtils.java ================================================ package androlua.utils; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.text.TextUtils; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor; public class ShortcutUtils { private static final String ACTION_INSTALL_SHORTCUT = "com.android.launcher.action.INSTALL_SHORTCUT"; private static final String ACTION_UNINSTALL_SHORTCUT = "com.android.launcher.action.UNINSTALL_SHORTCUT"; private static final String KEY_DUPLICATE = "duplicate"; private static final int POOL_SIZE = 3; private static final String TAG = "ShortcutUtil"; private static ThreadPoolExecutor EXECUTOR = null; static { EXECUTOR = new ScheduledThreadPoolExecutor(POOL_SIZE); } private ShortcutUtils() { } public static void installShortcut(Context context, String str, int i, Intent intent) { installShortcut(context, str, i, intent, null); } public static void installShortcut(final Context context, final String str, final int i, final Intent intent, final ActionListener actionListener) { EXECUTOR.execute(new Runnable() { @Override public void run() { addShortcut(context, str, i, intent); if (actionListener != null) { actionListener.onSuccess(); } } }); } public static void installShortcut(Context context, String str, Bitmap bm, Intent intent) { installShortcut(context, str, bm, intent, null); } public static void installShortcut(final Context context, final String str, final Bitmap bm, final Intent intent, final ActionListener actionListener) { EXECUTOR.execute(new Runnable() { @Override public void run() { addShortcut(context, str, bm, intent); if (actionListener != null) { actionListener.onSuccess(); } } }); } public static void uninstallShortcut(Context context, String str, Intent intent) { uninstallShortcut(context, str, intent, null); } public static void uninstallShortcut(final Context context, final String str, final Intent intent, final ActionListener actionListener) { EXECUTOR.execute(new Runnable() { @Override public void run() { removeShortcut(context, str, intent); if (actionListener != null) { actionListener.onSuccess(); } } }); } private static boolean addShortcut(Context context, String str, int i, Intent intent) { intent.setAction("android.intent.action.MAIN"); intent.addFlags(65536); Intent intent2 = new Intent(ACTION_INSTALL_SHORTCUT); intent2.putExtra("android.intent.extra.shortcut.INTENT", intent); intent2.putExtra("android.intent.extra.shortcut.NAME", str); intent2.putExtra("android.intent.extra.shortcut.ICON", BitmapFactory.decodeResource(context.getResources(), i)); intent2.putExtra("android.intent.extra.shortcut.ICON_RESOURCE", Intent.ShortcutIconResource.fromContext(context, i)); intent2.putExtra(KEY_DUPLICATE, false); context.sendBroadcast(intent2); return true; } private static boolean addShortcut(Context context, String str, Bitmap bm, Intent intent) { intent.setAction("android.intent.action.MAIN"); intent.addFlags(65536); Intent intent2 = new Intent(ACTION_INSTALL_SHORTCUT); intent2.putExtra("android.intent.extra.shortcut.INTENT", intent); intent2.putExtra("android.intent.extra.shortcut.NAME", str); intent2.putExtra("android.intent.extra.shortcut.ICON", bm); intent2.putExtra(KEY_DUPLICATE, false); context.sendBroadcast(intent2); return true; } private static void removeShortcut(Context context, String str, Intent intent) { intent.setAction("android.intent.action.MAIN"); Intent intent2 = new Intent(ACTION_UNINSTALL_SHORTCUT); intent2.putExtra("android.intent.extra.shortcut.INTENT", intent); intent2.putExtra("android.intent.extra.shortcut.NAME", str); context.sendBroadcast(intent2); } public static boolean hasShortcut(Context context, String str) { boolean z = false; Exception e; Throwable th; Cursor cursor = null; if (TextUtils.isEmpty(str)) { return false; } String authorityFromPermission = LauncherUtil.getAuthorityFromPermission(context, LauncherUtil.READ_SETTINGS); if (TextUtils.isEmpty(authorityFromPermission)) { return false; } Cursor query; try { query = context.getContentResolver().query(Uri.parse("content://" + authorityFromPermission + "/favorites?notify=true"), null, "title=?", new String[]{str}, null); if (query != null) { try { if (query.getCount() > 0) { z = true; if (query != null) { query.close(); } return z; } } catch (Exception e2) { e = e2; try { e.printStackTrace(); if (query == null) { query.close(); z = false; } else { z = false; } return z; } catch (Throwable th2) { th = th2; cursor = query; if (cursor != null) { cursor.close(); } throw th; } } } z = false; if (query != null) { query.close(); } } catch (Exception e3) { e = e3; query = null; e.printStackTrace(); if (query == null) { z = false; } else { query.close(); z = false; } return z; } catch (Throwable th3) { th = th3; if (cursor != null) { cursor.close(); } } return z; } public interface ActionListener { void onFailure(int i); void onSuccess(); } } ================================================ FILE: hydrogen-library/src/main/java/androlua/widget/glide/LuaGlideModule.java ================================================ package androlua.widget.glide; import android.content.Context; import com.bumptech.glide.Glide; import com.bumptech.glide.GlideBuilder; import com.bumptech.glide.load.engine.bitmap_recycle.LruBitmapPool; import com.bumptech.glide.load.engine.cache.LruResourceCache; import com.bumptech.glide.module.GlideModule; /** * custom glide * Created by hanks on 2017/6/1. */ public class LuaGlideModule implements GlideModule { @Override public void applyOptions(Context context, GlideBuilder builder) { int memoryCacheSize = 1024 * 1024 * 10;//设置图片内存缓存占用八分之一 //设置内存缓存大小 builder.setMemoryCache(new LruResourceCache(memoryCacheSize)); //设置BitmapPool缓存内存大小 builder.setBitmapPool(new LruBitmapPool(memoryCacheSize)); } @Override public void registerComponents(Context context, Glide glide) { } } ================================================ FILE: hydrogen-library/src/main/java/androlua/widget/htmltext/URLImageParser.java ================================================ package androlua.widget.htmltext; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.drawable.Drawable; import android.text.Html; import android.util.DisplayMetrics; import android.util.Log; import android.view.WindowManager; import android.widget.TextView; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.resource.drawable.GlideDrawable; import com.bumptech.glide.load.resource.gif.GifDrawable; import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.animation.GlideAnimation; import com.bumptech.glide.request.target.Target; import com.bumptech.glide.request.target.ViewTarget; public class URLImageParser implements Html.ImageGetter { private TextView container; public URLImageParser(TextView v) { this.container = v; } @Override public Drawable getDrawable(String url) { final UrlDrawable urlDrawable = new UrlDrawable(); final String source = url; debug("Url is " + url); DisplayMetrics metrics = new DisplayMetrics(); ((WindowManager) container.getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics(metrics); final float dpi = (int) metrics.density; Glide.with(container.getContext()).load(source).diskCacheStrategy(DiskCacheStrategy.ALL). listener(new RequestListener() { @Override public boolean onException(Exception e, String s, Target glideDrawableTarget, boolean b) { debug("Error in Glide listener"); if (e != null) { e.printStackTrace(); } return false; } @Override public boolean onResourceReady(GlideDrawable glideDrawable, String s, Target glideDrawableTarget, boolean b, boolean b2) { return false; } }). into(new ViewTarget(container) { @Override public void onResourceReady(GlideDrawable d, GlideAnimation glideAnimation) { int width = (int) (d.getIntrinsicWidth() * dpi); int height = (int) (d.getIntrinsicHeight() * dpi); d.setBounds(0, 0, width, height); d.setVisible(true, true); d.setCallback(new Drawable.Callback() { @Override public void invalidateDrawable(Drawable who) { } @Override public void scheduleDrawable(Drawable who, Runnable what, long when) { } @Override public void unscheduleDrawable(Drawable who, Runnable what) { } }); urlDrawable.setBounds(0, 0, width, height); urlDrawable.drawable = d; debug("Lisnt1er ended " + width + ", " + height + ", source: " + source + ", animated? " + d.isAnimated() + ", " + d.getClass().getSimpleName()); if (d instanceof GifDrawable) { debug("Gif drawable ! animated? " + d.isAnimated() + ", " + (d.getCallback() == null)); GifDrawable a = (GifDrawable) d; d.setLoopCount(GlideDrawable.LOOP_FOREVER); d.start(); } } }); return urlDrawable; } private void debug(String msg) { Log.d("AAA", msg); } } ================================================ FILE: hydrogen-library/src/main/java/androlua/widget/htmltext/UrlDrawable.java ================================================ package androlua.widget.htmltext; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.drawable.Drawable; import com.bumptech.glide.load.resource.drawable.GlideDrawable; public class UrlDrawable extends Drawable { public GlideDrawable drawable; public UrlDrawable() { super(); } @Override public void setAlpha(int alpha) { if (drawable != null) { drawable.setAlpha(alpha); } } @Override public void setColorFilter(ColorFilter cf) { if (drawable != null) { drawable.setColorFilter(cf); } } @Override public int getOpacity() { if (drawable != null) { return drawable.getOpacity(); } return PixelFormat.UNKNOWN; } @Override public void draw(Canvas canvas) { // override the draw to facilitate refresh function later if (drawable != null) { Paint p = new Paint(); p.setColor(Color.GREEN); canvas.drawRect(drawable.getBounds(), p); drawable.draw(canvas); if (!drawable.isRunning()) { drawable.start(); } } } } ================================================ FILE: hydrogen-library/src/main/java/androlua/widget/marqueetext/MarqueeTextView.java ================================================ package androlua.widget.marqueetext; import android.content.Context; import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.AttributeSet; import android.widget.TextView; /** * 跑马灯 * Created by hanks on 2017/6/21. */ public class MarqueeTextView extends android.support.v7.widget.AppCompatTextView { public MarqueeTextView(Context context) { this(context,null); } public MarqueeTextView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public MarqueeTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); setSingleLine(true); setMaxLines(1); setEllipsize(TextUtils.TruncateAt.MARQUEE); } @Override public boolean isFocused() { return true; } } ================================================ FILE: hydrogen-library/src/main/java/androlua/widget/ninegride/LuaNineGridView.java ================================================ package androlua.widget.ninegride; import android.content.Context; import android.util.AttributeSet; import com.luajava.LuaTable; import java.util.ArrayList; import java.util.List; /** * Created by hanks on 2017/5/31. Copyright (C) 2017 Hanks */ public class LuaNineGridView extends NineGridImageView { public LuaNineGridView(Context context) { super(context); } public LuaNineGridView(Context context, AttributeSet attrs) { super(context, attrs); } public void setImagesData(LuaTable lists) { List data = new ArrayList<>(); int size = lists.size(); for (int i = 1; i <= size; i++) { data.add((String) lists.get(i)); } setImagesData(data); } } ================================================ FILE: hydrogen-library/src/main/java/androlua/widget/ninegride/LuaNineGridViewAdapter.java ================================================ package androlua.widget.ninegride; import android.content.Context; import android.widget.ImageView; import java.util.List; /** * Created by hanks on 2017/5/31. Copyright (C) 2017 Hanks */ public class LuaNineGridViewAdapter extends NineGridImageViewAdapter { AdapterCreator adapterCreator; public LuaNineGridViewAdapter(AdapterCreator adapterCreator) { this.adapterCreator = adapterCreator; } @Override protected void onDisplayImage(Context context, ImageView imageView, String url) { adapterCreator.onDisplayImage(context, imageView, url); } @Override protected void onItemImageClick(Context context, ImageView imageView, int index, List list) { super.onItemImageClick(context, imageView, index, list); adapterCreator.onItemImageClick(context, imageView, index, list); } public interface AdapterCreator { void onDisplayImage(Context context, ImageView imageView, String url); void onItemImageClick(Context context, ImageView imageView, int index, List list); } } ================================================ FILE: hydrogen-library/src/main/java/androlua/widget/ninegride/NineGridImageView.java ================================================ package androlua.widget.ninegride; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import java.util.ArrayList; import java.util.List; import androlua.LuaImageLoader; /** * Created by Jaeger on 16/2/24. *

* Email: chjie.jaeger@gamil.com * GitHub: https://github.com/laobie */ public class NineGridImageView extends ViewGroup { public final static int STYLE_GRID = 0; // 宫格布局 public final static int STYLE_FILL = 1; // 全填充布局 private int mRowCount; // 行数 private int mColumnCount; // 列数 private int mMaxSize; // 最大图片数 private int mShowStyle; // 显示风格 private int mGap; // 宫格间距 private int mSingleImgWidth; private int mSingleImgHeight; // 单张图片时的尺寸 private int mGridSize; // 宫格大小,即图片大小 private List mImageViewList = new ArrayList<>(); private List mImgDataList = new ArrayList<>(); private NineGridImageViewAdapter mAdapter; private int totalWidth; public NineGridImageView(Context context) { this(context, null); } public NineGridImageView(Context context, AttributeSet attrs) { super(context, attrs); this.mGap = 0; this.mSingleImgHeight = dp2px(180); this.mSingleImgWidth = dp2px(180); this.mShowStyle = STYLE_GRID; this.mMaxSize = 9; } /** * 设置 宫格参数 * * @param imagesSize 图片数量 * @param showStyle 显示风格 * @return 宫格参数 gridParam[0] 宫格行数 gridParam[1] 宫格列数 */ protected static int[] calculateGridParam(int imagesSize, int showStyle) { int[] gridParam = new int[2]; switch (showStyle) { case STYLE_FILL: if (imagesSize < 3) { gridParam[0] = 1; gridParam[1] = imagesSize; } else if (imagesSize <= 4) { gridParam[0] = 2; gridParam[1] = 2; } else { gridParam[0] = imagesSize / 3 + (imagesSize % 3 == 0 ? 0 : 1); gridParam[1] = 3; } break; default: case STYLE_GRID: gridParam[0] = imagesSize / 3 + (imagesSize % 3 == 0 ? 0 : 1); gridParam[1] = 3; } return gridParam; } public void setSingleImgSize(int width, int height) { int targetH = dp2px(180); int targetW = getScreenWidth() - dp2px(32); if (height > 2000) { height = 2000; } if (width > 3000) { width = 3000; } float scale = 1f; float scaleY = targetH * 1f / height; float scaleX = targetW * 1f / width; if (height < targetH) { if (width * scaleY > targetW) { scale = scaleX; } else { scale = scaleY; } } else { scale = Math.min(scaleX, scaleY); } this.mSingleImgWidth = (int) (width * scale); this.mSingleImgHeight = (int) (height * scale); } public NineGridImageViewAdapter getAdapter() { return mAdapter; } /** * 设置适配器 * * @param adapter 适配器 */ public void setAdapter(NineGridImageViewAdapter adapter) { mAdapter = adapter; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); totalWidth = width - getPaddingLeft() - getPaddingRight(); if (mImgDataList == null || mImgDataList.isEmpty()) { setMeasuredDimension(width, height); return; } switch (mImgDataList.size()) { case 1: mColumnCount = 1; mRowCount = 1; mGridSize = height = mSingleImgHeight; break; case 2: case 4: mGridSize = (int) ((totalWidth - mGap) / 2f); mRowCount = mImgDataList.size() / 2; mColumnCount = 2; height = mGridSize * mRowCount + mGap * (mRowCount - 1); break; default: mColumnCount = 3; mRowCount = mImgDataList.size() % 3 == 0 ? mImgDataList.size() / 3 : mImgDataList.size() / 3 + 1; mGridSize = (int) ((totalWidth - 2 * mGap) / 3f); height = mGridSize * mRowCount + (mGap * mRowCount - 1); break; } height = height + getPaddingTop() + getPaddingBottom(); setMeasuredDimension(width, height); } private int dp2px(float dp) { float density = getContext().getResources().getDisplayMetrics().density; return (int) (0.5F + dp * density); } private int getScreenWidth() { return getContext().getResources().getDisplayMetrics().widthPixels; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int childCount = getChildCount(); if (childCount == 0) { return; } if (childCount == 1) { View child = getChildAt(0); child.layout(getPaddingLeft(), getPaddingTop(), getPaddingLeft() + mSingleImgWidth, getPaddingTop() + mSingleImgHeight); return; } for (int i = 0; i < childCount; i++) { View child = getChildAt(i); int rowNum = i / mColumnCount; int columnNum = i % mColumnCount; int left = (mGridSize + mGap) * columnNum + getPaddingLeft(); int top = (mGridSize + mGap) * rowNum + getPaddingTop(); int right = left + mGridSize; int bottom = top + mGridSize; child.layout(left, top, right, bottom); } } /** * 设置图片数据 * * @param lists 图片数据集合 */ public void setImagesData(List lists) { if (lists == null || lists.isEmpty()) { this.setVisibility(GONE); return; } else { this.setVisibility(VISIBLE); } removeAllViews(); int newShowCount = getNeedShowCount(lists.size()); int[] gridParam = calculateGridParam(newShowCount, mShowStyle); mRowCount = gridParam[0]; mColumnCount = gridParam[1]; mImgDataList.clear(); mImgDataList.addAll(lists); for (int i = 0; i < newShowCount; i++) { ImageView iv = getImageView(); if (iv == null) { continue; } int w,h; if (newShowCount == 1) { iv.setScaleType(ImageView.ScaleType.FIT_XY); w = mSingleImgWidth; h = mSingleImgHeight; } else { w = h = mGridSize; iv.setScaleType(ImageView.ScaleType.CENTER_CROP); } addView(iv, new ViewGroup.LayoutParams(w,h)); LuaImageLoader.load(iv, mImgDataList.get(i)); final int finalI = i; iv.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mAdapter.onItemImageClick(getContext(), (ImageView) v, finalI, mImgDataList); } }); mAdapter.onDisplayImage(getContext(), iv, mImgDataList.get(i)); } requestLayout(); } private int getNeedShowCount(int size) { if (mMaxSize > 0 && size > mMaxSize) { return mMaxSize; } else { return size; } } /** * 获得 ImageView * 保证了 ImageView 的重用 */ private ImageView getImageView() { if (mAdapter != null) { ImageView imageView = mAdapter.generateImageView(getContext()); return imageView; } else { Log.e("NineGirdImageView", "Your must set a NineGridImageViewAdapter for NineGirdImageView"); return null; } } /** * 设置宫格间距 * * @param gap 宫格间距 px */ public void setGap(int gap) { mGap = gap; } /** * 设置显示风格 * * @param showStyle 显示风格 */ public void setShowStyle(int showStyle) { mShowStyle = showStyle; } /** * 设置最大图片数 * * @param maxSize 最大图片数 */ public void setMaxSize(int maxSize) { mMaxSize = maxSize; } } ================================================ FILE: hydrogen-library/src/main/java/androlua/widget/ninegride/NineGridImageViewAdapter.java ================================================ package androlua.widget.ninegride; import android.content.Context; import android.widget.ImageView; import java.util.List; /** * Created by Jaeger on 16/2/24. *

* Email: chjie.jaeger@gmail.com * GitHub: https://github.com/laobie */ public abstract class NineGridImageViewAdapter { protected abstract void onDisplayImage(Context context, ImageView imageView, String t); protected void onItemImageClick(Context context, ImageView imageView, int index, List list) { } protected ImageView generateImageView(Context context) { ImageView imageView = new ImageView(context); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); return imageView; } } ================================================ FILE: hydrogen-library/src/main/java/androlua/widget/picture/ElasticDragDismissFrameLayout.java ================================================ /* * Copyright 2015 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 androlua.widget.picture; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Color; import android.graphics.RectF; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.View; import android.view.WindowManager; import android.view.animation.AccelerateInterpolator; import android.view.animation.Interpolator; import android.widget.FrameLayout; import java.util.ArrayList; import java.util.List; public class ElasticDragDismissFrameLayout extends FrameLayout { public static final float DRAG_ELASTICITY_NORMAL = .5f; public static final float DRAG_ELASTICITY_LARGE = .9f; public static final float DRAG_ELASTICITY_XLARGE = 1.25f; public static final float DRAG_ELASTICITY_XXLARGE = 2f; // configurable attribs private float dragDismissDistance = Float.MAX_VALUE; private float alplaDistance = Float.MAX_VALUE; private float dragDismissFraction = -1f; private float dragDismissScale = 0.7f; // 0..1 private boolean shouldScale = false; private float dragElasticity = DRAG_ELASTICITY_NORMAL; // state private float totalDrag; private boolean draggingDown = false; private boolean draggingUp = false; private boolean enabled = true; private static Interpolator fastOutSlowInInterpolator; private List callbacks; private RectF draggingBackground; private int bgAlpha; public ElasticDragDismissFrameLayout(Context context) { this(context, null, 0); } public ElasticDragDismissFrameLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ElasticDragDismissFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { DisplayMetrics metrics = new DisplayMetrics(); WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); windowManager.getDefaultDisplay().getMetrics(metrics); dragDismissDistance = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 180, metrics); alplaDistance = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 600, metrics); shouldScale = dragDismissScale != 1f; setBackgroundColor(0xff000000); } public static abstract class ElasticDragDismissCallback { /** * Called for each drag event. * * @param elasticOffset Indicating the drag offset with elasticity applied i.e. may * exceed 1. * @param elasticOffsetPixels The elastically scaled drag distance in pixels. * @param rawOffset Value from [0, 1] indicating the raw drag offset i.e. * without elasticity applied. A value of 1 indicates that the * dismiss distance has been reached. * @param rawOffsetPixels The raw distance the user has dragged */ public void onDrag(float elasticOffset, float elasticOffsetPixels, float rawOffset, float rawOffsetPixels) { } /** * Called when dragging is released and has exceeded the threshold dismiss distance. */ public void onDragDismissed() { } } @Override public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { return enabled && (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0; } @Override public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { if (!enabled) { return; } if (draggingDown && dy > 0 || draggingUp && dy < 0) { dragScale(dy); consumed[1] = dy; } } @Override public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { if (enabled) { dragScale(dyUnconsumed); } } @Override public void onStopNestedScroll(View child) { if (enabled) { if (Math.abs(totalDrag) >= dragDismissDistance) { dispatchDismissCallback(); } else { // settle back to natural position if (fastOutSlowInInterpolator == null) { fastOutSlowInInterpolator = new AccelerateInterpolator(); } getChildAt(0).animate() .translationY(0f) .scaleX(1f) .scaleY(1f) .setDuration(200L) .setInterpolator(fastOutSlowInInterpolator) .start(); ValueAnimator animator = ValueAnimator.ofInt(bgAlpha, 255); animator.setDuration(200); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int v = (int) animation.getAnimatedValue(); setBackgroundColor(Color.argb(v, 0, 0, 0)); } }); animator.start(); totalDrag = 0; draggingDown = draggingUp = false; dispatchDragCallback(0f, 0f, 0f, 0f); } } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if (dragDismissFraction > 0f) { dragDismissDistance = h * dragDismissFraction; } } public void addListener(ElasticDragDismissCallback listener) { if (callbacks == null) { callbacks = new ArrayList<>(); } callbacks.add(listener); } public void setEnabled(boolean enabled) { this.enabled = enabled; } public boolean isEnabled() { return enabled; } public void removeListener(ElasticDragDismissCallback listener) { if (callbacks != null && callbacks.size() > 0) { callbacks.remove(listener); } } private void dragScale(int scroll) { if (scroll == 0) return; totalDrag += scroll; View child = getChildAt(0); // track the direction & set the pivot point for scaling // don't double track i.e. if play dragging down and then reverse, keep tracking as // dragging down until they reach the 'natural' position if (scroll < 0 && !draggingUp && !draggingDown) { draggingDown = true; if (shouldScale) child.setPivotY(getHeight()); } else if (scroll > 0 && !draggingDown && !draggingUp) { draggingUp = true; if (shouldScale) child.setPivotY(0f); } // how far have we dragged relative to the distance to perform a dismiss // (0–1 where 1 = dismiss distance). Decreasing logarithmically as we approach the limit float dragFraction = (float) Math.log10(1 + (Math.abs(totalDrag) / dragDismissDistance)); // calculate the desired translation given the drag fraction float dragTo = dragFraction * dragDismissDistance * dragElasticity; if (draggingUp) { // as we use the absolute magnitude when calculating the drag fraction, need to // re-apply the drag direction dragTo *= -1; } child.setTranslationY(dragTo); if (draggingBackground == null) { draggingBackground = new RectF(); draggingBackground.left = 0; draggingBackground.right = getWidth(); draggingBackground.top = 0; draggingBackground.bottom = getHeight(); } float dx = Math.abs(totalDrag); dx = dx > alplaDistance ? alplaDistance : dx; bgAlpha = (int) (255 * (1f - (dx / alplaDistance))); bgAlpha = bgAlpha > 255 ? 255 : bgAlpha; bgAlpha = bgAlpha < 0 ? 0 : bgAlpha; setBackgroundColor(Color.argb(bgAlpha, 0, 0, 0)); if (shouldScale) { final float scale = 1 - ((1 - dragDismissScale) * dragFraction); child.setScaleX(scale); child.setScaleY(scale); } // if we've reversed direction and gone past the settle point then clear the flags to // allow the list to get the scroll events & reset any transforms if ((draggingDown && totalDrag >= 0) || (draggingUp && totalDrag <= 0)) { totalDrag = dragTo = dragFraction = 0; draggingDown = draggingUp = false; child.setTranslationY(0f); child.setScaleX(1f); child.setScaleY(1f); } invalidate(); dispatchDragCallback(dragFraction, dragTo, Math.min(1f, Math.abs(totalDrag) / dragDismissDistance), totalDrag); } private void dispatchDragCallback(float elasticOffset, float elasticOffsetPixels, float rawOffset, float rawOffsetPixels) { if (callbacks != null && !callbacks.isEmpty()) { for (ElasticDragDismissCallback callback : callbacks) { callback.onDrag(elasticOffset, elasticOffsetPixels, rawOffset, rawOffsetPixels); } } } private void dispatchDismissCallback() { if (callbacks != null && !callbacks.isEmpty()) { for (ElasticDragDismissCallback callback : callbacks) { callback.onDragDismissed(); } } } public boolean isDragging() { return draggingDown || draggingUp; } public void setDragElasticity(float elasticity) { this.dragElasticity = elasticity; } public void halfDistanceRequired() { this.dragDismissDistance = dragDismissDistance / 2; } } ================================================ FILE: hydrogen-library/src/main/java/androlua/widget/picture/PicturePreviewActivity.java ================================================ package androlua.widget.picture; import android.Manifest; import android.app.DownloadManager; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.BitmapFactory; import android.graphics.Color; import android.graphics.PointF; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.support.annotation.Nullable; import android.support.v4.app.ActivityCompat; import android.support.v4.app.Fragment; import android.support.v4.content.ContextCompat; import android.support.v4.view.ViewPager; import android.support.v7.app.AppCompatActivity; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.model.GlideUrl; import com.bumptech.glide.load.model.LazyHeaders; import com.bumptech.glide.request.animation.GlideAnimation; import com.bumptech.glide.request.target.SimpleTarget; import com.davemorrissey.labs.subscaleview.ImageSource; import com.davemorrissey.labs.subscaleview.ImageViewState; import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.File; import java.util.ArrayList; import androlua.LuaHttp; import androlua.adapter.LuaFragmentPageAdapter; import androlua.base.BaseFragment; import androlua.common.LuaFileUtils; import androlua.common.LuaStringUtils; import androlua.common.LuaToast; import pub.hanks.luajandroid.R; /** * Created by hanks on 2017/6/2. Copyright (C) 2017 Hanks */ public class PicturePreviewActivity extends AppCompatActivity { private LuaFragmentPageAdapter adapter; private ArrayList uris = new ArrayList<>(); private ArrayList fragments = new ArrayList<>(); private int currentIndex; private ViewPager viewPager; private TextView tv_count; private ArrayList headerList = new ArrayList<>(); private boolean isVisible; private ImageView iv_share, iv_download; private Context context; public static void start(Context context, String json) { Intent starter = new Intent(context, PicturePreviewActivity.class); starter.putExtra("json", json); context.startActivity(starter); } public void setStatusBarColor(int color) { if (Build.VERSION.SDK_INT >= 21) { View decorView = getWindow().getDecorView(); int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; decorView.setSystemUiVisibility(option); getWindow().setStatusBarColor(color); } } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { overridePendingTransition( android.R.anim.fade_in, 0);//结束的动画 super.onCreate(savedInstanceState); setContentView(R.layout.activity_picture); context = this; setStatusBarColor(Color.TRANSPARENT); getWindow().setBackgroundDrawableResource(android.R.color.transparent); getWindow().getDecorView().setBackgroundColor(Color.TRANSPARENT); ElasticDragDismissFrameLayout dragDismissLayout = (ElasticDragDismissFrameLayout) findViewById(R.id.dragdismiss_drag_dismiss_layout); dragDismissLayout.addListener(new ElasticDragDismissFrameLayout.ElasticDragDismissCallback() { @Override public void onDragDismissed() { super.onDragDismissed(); PicturePreviewActivity.this.finish(); } }); dragDismissLayout.setDragElasticity(ElasticDragDismissFrameLayout.DRAG_ELASTICITY_XXLARGE); dragDismissLayout.halfDistanceRequired(); tv_count = (TextView) findViewById(R.id.tv_count); iv_download = (ImageView) findViewById(R.id.iv_download); iv_share = (ImageView) findViewById(R.id.iv_share); viewPager = (ViewPager) findViewById(R.id.viewpager); adapter = new LuaFragmentPageAdapter(getSupportFragmentManager(), new LuaFragmentPageAdapter.AdapterCreator() { @Override public long getCount() { return fragments.size(); } @Override public Fragment getItem(int position) { return fragments.get(position); } @Override public String getPageTitle(int position) { return (position + 1) + "/" + fragments.size(); } }); viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { tv_count.setText(adapter.getPageTitle(position)); } @Override public void onPageScrollStateChanged(int state) { } }); viewPager.setAdapter(adapter); getData(); iv_download.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { int hasWriteContactsPermission = ContextCompat.checkSelfPermission(PicturePreviewActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE); if (hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) { requestPermission(); } else { int currentItem = viewPager.getCurrentItem(); downloadPicture(context, uris.get(currentItem), headerList); } } catch (Exception e) { e.printStackTrace(); } } }); iv_share.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { Intent sendIntent = new Intent(Intent.ACTION_SEND); int i = viewPager.getCurrentItem(); sendIntent.putExtra(Intent.EXTRA_TEXT, uris.get(i) + "\n来自【氢应用】https://www.coolapk.com/apk/pub.hydrogen.android " ); sendIntent.setType("text/plain"); if (sendIntent.resolveActivity(getPackageManager()) != null) { startActivity(sendIntent); } } catch (Exception e) { e.printStackTrace(); } } }); } private void getData() { // { "uris":[], "currentIndex":0, "headers":["UA:android",""] } String str = getIntent().getStringExtra("json"); if (TextUtils.isEmpty(str) && Intent.ACTION_VIEW.equals(getIntent().getAction())) { Uri uri = getIntent().getData(); str = uri.getQueryParameter("data"); } if (TextUtils.isEmpty(str)) { Toast.makeText(this, "数据出错", Toast.LENGTH_SHORT).show(); return; } try { JSONObject json = new JSONObject(str); if (json.has("headers")) { JSONArray headers = json.getJSONArray("headers"); for (int i = 0; i < headers.length(); i++) { headerList.add(headers.getString(i)); } } if (json.has("uris")) { JSONArray list = json.getJSONArray("uris"); for (int i = 0; i < list.length(); i++) { uris.add(list.getString(i)); } } if (json.has("currentIndex")) { currentIndex = json.getInt("currentIndex"); } tv_count.setText(String.format("%s/%s", 1, uris.size())); for (final String uri : uris) { PicturePreviewFragment fragment = PicturePreviewFragment.newInstance(uri, headerList); fragments.add(fragment); } adapter.notifyDataSetChanged(); viewPager.setCurrentItem(currentIndex, false); } catch (JSONException e) { e.printStackTrace(); } } private void downloadPicture(Context context, final String uri, final ArrayList headers) { try { DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); //得到系统的下载管理 DownloadManager.Request request = new DownloadManager.Request(Uri.parse(uri)); //得到连接请求对象 //request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI); if (headers != null) { for (String header : headers) { int i = header.indexOf(":"); if (i <= 0) { continue; } String key = header.substring(0, i); String v = header.substring(i + 1); request.addRequestHeader(key, v); } } request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); request.setDescription("下载中..."); request.setTitle("下载"); request.setDestinationInExternalPublicDir(Environment.DIRECTORY_PICTURES, System.currentTimeMillis() + ".jpg"); manager.enqueue(request); LuaToast.show("正在保存..."); } catch (Exception e) { final String savePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath() + System.currentTimeMillis() + ".jpg"; new Thread() { @Override public void run() { LuaHttp.downloadFile(uri, savePath, headers); } }.start(); LuaToast.show("正在保存..."); } } @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case 0x200: { // If request is cancelled, the result arrays are empty. if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { Toast.makeText(this, "授权成功", Toast.LENGTH_SHORT).show(); } } } } private void requestPermission() { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0x200); } public static class PicturePreviewFragment extends BaseFragment { private View.OnClickListener listener; private SubsamplingScaleImageView iv_big; private ImageView iv_small; private View loading; public static PicturePreviewFragment newInstance(String uri, ArrayList headerList) { Bundle args = new Bundle(); args.putString("uri", uri); args.putStringArrayList("headers", headerList); PicturePreviewFragment fragment = new PicturePreviewFragment(); fragment.setArguments(args); return fragment; } public static PicturePreviewFragment newInstance(String uri) { Bundle args = new Bundle(); args.putString("uri", uri); PicturePreviewFragment fragment = new PicturePreviewFragment(); fragment.setArguments(args); return fragment; } public void setOnClickImageListener(View.OnClickListener listener) { this.listener = listener; } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.item_pager_image, container, false); } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); final ArrayList headerList = getArguments().getStringArrayList("headers"); final String uri = getArguments().getString("uri", ""); if (LuaStringUtils.isEmpty(uri)) { return; } iv_big = (SubsamplingScaleImageView) view.findViewById(R.id.iv_big); iv_small = (ImageView) view.findViewById(R.id.iv_small); loading = view.findViewById(R.id.loading); loading.setVisibility(View.VISIBLE); iv_small.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (listener != null) { listener.onClick(v); } } }); iv_big.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (listener != null) { listener.onClick(v); } } }); LazyHeaders.Builder builder = new LazyHeaders.Builder(); if (headerList != null) { for (String header : headerList) { int i = header.indexOf(":"); if (i <= 0) { continue; } String key = header.substring(0, i); String v = header.substring(i + 1); builder.addHeader(key, v); } } Glide.with(iv_big.getContext()) .load(new GlideUrl(uri, builder.build())) .downloadOnly(new SimpleTarget() { @Override public void onResourceReady(File resource, GlideAnimation glideAnimation) { loadFile(resource); } @Override public void onLoadFailed(Exception e, Drawable errorDrawable) { super.onLoadFailed(e, errorDrawable); hideLoading(); } }); // .diskCacheStrategy(DiskCacheStrategy.SOURCE) // .placeholder(R.drawable.bg_circle) // .error(R.drawable.bg_circle) // .crossFade() // .into(iv_big); } private void hideLoading() { if (loading != null) { loading.setVisibility(View.GONE); } } public void getImageSize(File file, int[] size) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(file.getAbsolutePath(), options); size[0] = options.outWidth; size[1] = options.outHeight; } public void loadFile(File file) { if (iv_big == null || loading == null) { return; } if (file == null || !file.exists()) { LuaToast.show("加载失败.."); hideLoading(); return; } if ("gif".equals(LuaFileUtils.getFileType(file.getAbsolutePath()))) { iv_small.setVisibility(View.VISIBLE); iv_big.setVisibility(View.GONE); Glide.with(this).load(file).diskCacheStrategy(DiskCacheStrategy.SOURCE).into(iv_small); hideLoading(); return; } iv_small.setVisibility(View.GONE); iv_big.setVisibility(View.VISIBLE); int[] size = new int[2]; getImageSize(file, size); float scale = iv_big.getWidth() * 1.0f / size[0]; ImageViewState state = new ImageViewState(scale, new PointF(0, 0), 0); iv_big.setImage(ImageSource.uri(Uri.fromFile(file)), state); hideLoading(); } } @Override public void finish() { super.finish(); overridePendingTransition(0, android.R.anim.fade_out);//结束的动画 } } ================================================ FILE: hydrogen-library/src/main/java/androlua/widget/statusbar/FixInsetsFrameLayout.java ================================================ package androlua.widget.statusbar; import android.content.Context; import android.graphics.Rect; import android.os.Build; import android.util.AttributeSet; import android.view.WindowInsets; import android.widget.FrameLayout; /** * @author Kevin * Date Created: 3/7/14 *

* https://code.google.com/p/android/issues/detail?id=63777 *

* When using a translucent status bar on API 19+, the window will not * resize to make room for input methods (i.e. * {@link android.view.WindowManager.LayoutParams#SOFT_INPUT_ADJUST_RESIZE} and * {@link android.view.WindowManager.LayoutParams#SOFT_INPUT_ADJUST_PAN} are * ignored). *

* To work around this; override {@link #fitSystemWindows(Rect)}, * capture and override the system insets, and then call through to FrameLayout's * implementation. *

* For reasons yet unknown, modifying the bottom inset causes this workaround to * fail. Modifying the top, left, and right insets works as expected. */ public final class FixInsetsFrameLayout extends FrameLayout { private int[] mInsets = new int[4]; public FixInsetsFrameLayout(Context context) { super(context); } public FixInsetsFrameLayout(Context context, AttributeSet attrs) { super(context, attrs); } public FixInsetsFrameLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } private boolean insetEnable = false; public void setInsetEnable(boolean insetEnable) { this.insetEnable = insetEnable; } @Override protected final boolean fitSystemWindows(Rect insets) { if (!insetEnable && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Intentionally do not modify the bottom inset. For some reason, // if the bottom inset is modified, window resizing stops working. mInsets[0] = insets.left; mInsets[1] = insets.top; mInsets[2] = insets.right; insets.left = 0; insets.top = 0; insets.right = 0; } return super.fitSystemWindows(insets); } @Override public final WindowInsets onApplyWindowInsets(WindowInsets insets) { if (!insetEnable && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mInsets[0] = insets.getSystemWindowInsetLeft(); mInsets[1] = insets.getSystemWindowInsetTop(); mInsets[2] = insets.getSystemWindowInsetRight(); return super.onApplyWindowInsets(insets.replaceSystemWindowInsets(0, 0, 0, insets.getSystemWindowInsetBottom())); } else { return insets; } } } ================================================ FILE: hydrogen-library/src/main/java/androlua/widget/statusbar/StatusBarView.java ================================================ package androlua.widget.statusbar; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.os.Build; import android.util.AttributeSet; import android.view.View; /** * StatusBarView * Created by hanks on 18-4-17. */ public class StatusBarView extends View { private int statusBarColor; private int statusBarHeight; public StatusBarView(Context context) { this(context, null); } public StatusBarView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public StatusBarView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); statusBarColor = Color.BLACK; statusBarHeight = getStatusBarHeight(context); } public void setStatusBarColor(int statusBarColor) { this.statusBarColor = statusBarColor; invalidate(); } public void setStatusBarHeight(int statusBarHeight) { this.statusBarHeight = statusBarHeight; getLayoutParams().height = statusBarHeight; requestLayout(); } @Override protected void onDraw(Canvas canvas) { canvas.drawColor(statusBarColor); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(widthMeasureSpec, statusBarHeight); } private int getStatusBarHeight(Context context) { if (Build.VERSION.SDK_INT < 21) { return 0; } int result = 0; int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); if (resourceId > 0) { result = context.getResources().getDimensionPixelSize(resourceId); } return result; } private int getNavigationBarHeight(Context context) { int result = 0; int resourceId = context.getResources().getIdentifier("navigation_bar_height", "dimen", "android"); if (resourceId > 0) { result = context.getResources().getDimensionPixelSize(resourceId); } return result; } } ================================================ FILE: hydrogen-library/src/main/java/androlua/widget/swipebacklayout/SwipeBackLayout.java ================================================ package androlua.widget.swipebacklayout; import android.app.Activity; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.FrameLayout; import java.util.ArrayList; import java.util.List; import pub.hanks.luajandroid.R; public class SwipeBackLayout extends FrameLayout { /** * Edge flag indicating that the left edge should be affected. */ public static final int EDGE_LEFT = ViewDragHelper.EDGE_LEFT; /** * Edge flag indicating that the right edge should be affected. */ public static final int EDGE_RIGHT = ViewDragHelper.EDGE_RIGHT; /** * Edge flag indicating that the bottom edge should be affected. */ public static final int EDGE_BOTTOM = ViewDragHelper.EDGE_BOTTOM; /** * Edge flag set indicating all edges should be affected. */ public static final int EDGE_ALL = EDGE_LEFT | EDGE_RIGHT | EDGE_BOTTOM; /** * A view is not currently being dragged or animating as a result of a * fling/snap. */ public static final int STATE_IDLE = ViewDragHelper.STATE_IDLE; /** * A view is currently being dragged. The position is currently changing as * a result of user input or simulated user input. */ public static final int STATE_DRAGGING = ViewDragHelper.STATE_DRAGGING; /** * A view is currently settling into place as a result of a fling or * predefined non-interactive motion. */ public static final int STATE_SETTLING = ViewDragHelper.STATE_SETTLING; /** * Minimum velocity that will be detected as a fling */ private static final int MIN_FLING_VELOCITY = 400; // dips per second private static final int DEFAULT_SCRIM_COLOR = 0x99000000; private static final int FULL_ALPHA = 255; /** * Default threshold of scroll */ private static final float DEFAULT_SCROLL_THRESHOLD = 0.3f; private static final int OVERSCROLL_DISTANCE = 10; private static final int[] EDGE_FLAGS = { EDGE_LEFT, EDGE_RIGHT, EDGE_BOTTOM, EDGE_ALL }; private int mEdgeFlag; /** * Threshold of scroll, we will close the activity, when scrollPercent over * this value; */ private float mScrollThreshold = DEFAULT_SCROLL_THRESHOLD; private Activity mActivity; private boolean mEnable = true; private View mContentView; private ViewDragHelper mDragHelper; private float mScrollPercent; private int mContentLeft; private int mContentTop; /** * The set of listeners to be sent events through. */ private List mListeners; private Drawable mShadowLeft; private Drawable mShadowRight; private Drawable mShadowBottom; private float mScrimOpacity; private int mScrimColor = DEFAULT_SCRIM_COLOR; private boolean mInLayout; private Rect mTmpRect = new Rect(); /** * Edge being dragged */ private int mTrackingEdge; public SwipeBackLayout(Context context) { this(context, null); } public SwipeBackLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SwipeBackLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs); mDragHelper = ViewDragHelper.create(this, new ViewDragCallback()); setEdgeSize(dp2px(50)); int mode = EDGE_FLAGS[0]; setEdgeTrackingEnabled(mode); int shadowLeft = R.drawable.shadow_left; int shadowRight = R.drawable.shadow_right; int shadowBottom = R.drawable.shadow_bottom; setShadow(shadowLeft, EDGE_LEFT); setShadow(shadowRight, EDGE_RIGHT); setShadow(shadowBottom, EDGE_BOTTOM); final float density = getResources().getDisplayMetrics().density; final float minVel = MIN_FLING_VELOCITY * density; mDragHelper.setMinVelocity(minVel); mDragHelper.setMaxVelocity(minVel * 2f); } private int dp2px(float dp) { DisplayMetrics metrics = new DisplayMetrics(); WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); wm.getDefaultDisplay().getMetrics(metrics); return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, metrics); } /** * Sets the sensitivity of the NavigationLayout. * * @param context The application context. * @param sensitivity value between 0 and 1, the final value for touchSlop = * ViewConfiguration.getScaledTouchSlop * (1 / s); */ public void setSensitivity(Context context, float sensitivity) { mDragHelper.setSensitivity(context, sensitivity); } /** * Set up contentView which will be moved by user gesture * * @param view */ private void setContentView(View view) { mContentView = view; } public void setEnableGesture(boolean enable) { mEnable = enable; } /** * Enable edge tracking for the selected edges of the parent view. The * callback's * {@link ViewDragHelper.Callback#onEdgeTouched(int, int)} * and * {@link ViewDragHelper.Callback#onEdgeDragStarted(int, int)} * methods will only be invoked for edges for which edge tracking has been * enabled. * * @param edgeFlags Combination of edge flags describing the edges to watch * @see #EDGE_LEFT * @see #EDGE_RIGHT * @see #EDGE_BOTTOM */ public void setEdgeTrackingEnabled(int edgeFlags) { mEdgeFlag = edgeFlags; mDragHelper.setEdgeTrackingEnabled(mEdgeFlag); } /** * Set a color to use for the scrim that obscures primary content while a * drawer is open. * * @param color Color to use in 0xAARRGGBB format. */ public void setScrimColor(int color) { mScrimColor = color; invalidate(); } /** * Set the size of an edge. This is the range in pixels along the edges of * this view that will actively detect edge touches or drags if edge * tracking is enabled. * * @param size The size of an edge in pixels */ public void setEdgeSize(int size) { mDragHelper.setEdgeSize(size); } /** * Register a callback to be invoked when a swipe event is sent to this * view. * * @param listener the swipe listener to attach to this view * @deprecated use {@link #addSwipeListener} instead */ @Deprecated public void setSwipeListener(SwipeListener listener) { addSwipeListener(listener); } /** * Add a callback to be invoked when a swipe event is sent to this view. * * @param listener the swipe listener to attach to this view */ public void addSwipeListener(SwipeListener listener) { if (mListeners == null) { mListeners = new ArrayList(); } mListeners.add(listener); } /** * Removes a listener from the set of listeners * * @param listener */ public void removeSwipeListener(SwipeListener listener) { if (mListeners == null) { return; } mListeners.remove(listener); } /** * Set scroll threshold, we will close the activity, when scrollPercent over * this value * * @param threshold */ public void setScrollThresHold(float threshold) { if (threshold >= 1.0f || threshold <= 0) { throw new IllegalArgumentException("Threshold value should be between 0 and 1.0"); } mScrollThreshold = threshold; } /** * Set a drawable used for edge shadow. * * @param shadow Drawable to use * @see #EDGE_LEFT * @see #EDGE_RIGHT * @see #EDGE_BOTTOM */ public void setShadow(Drawable shadow, int edgeFlag) { if ((edgeFlag & EDGE_LEFT) != 0) { mShadowLeft = shadow; } else if ((edgeFlag & EDGE_RIGHT) != 0) { mShadowRight = shadow; } else if ((edgeFlag & EDGE_BOTTOM) != 0) { mShadowBottom = shadow; } invalidate(); } /** * Set a drawable used for edge shadow. * * @param resId Resource of drawable to use * @see #EDGE_LEFT * @see #EDGE_RIGHT * @see #EDGE_BOTTOM */ public void setShadow(int resId, int edgeFlag) { setShadow(getResources().getDrawable(resId), edgeFlag); } /** * Scroll out contentView and finish the activity */ public void scrollToFinishActivity() { final int childWidth = mContentView.getWidth(); final int childHeight = mContentView.getHeight(); int left = 0, top = 0; if ((mEdgeFlag & EDGE_LEFT) != 0) { left = childWidth + mShadowLeft.getIntrinsicWidth() + OVERSCROLL_DISTANCE; mTrackingEdge = EDGE_LEFT; } else if ((mEdgeFlag & EDGE_RIGHT) != 0) { left = -childWidth - mShadowRight.getIntrinsicWidth() - OVERSCROLL_DISTANCE; mTrackingEdge = EDGE_RIGHT; } else if ((mEdgeFlag & EDGE_BOTTOM) != 0) { top = -childHeight - mShadowBottom.getIntrinsicHeight() - OVERSCROLL_DISTANCE; mTrackingEdge = EDGE_BOTTOM; } mDragHelper.smoothSlideViewTo(mContentView, left, top); invalidate(); } @Override public boolean onInterceptTouchEvent(MotionEvent event) { if (!mEnable) { return false; } try { return mDragHelper.shouldInterceptTouchEvent(event); } catch (ArrayIndexOutOfBoundsException e) { // FIXME: handle exception // issues #9 return false; } } @Override public boolean onTouchEvent(MotionEvent event) { if (!mEnable) { return false; } mDragHelper.processTouchEvent(event); return true; } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { mInLayout = true; if (mContentView != null) mContentView.layout(mContentLeft, mContentTop, mContentLeft + mContentView.getMeasuredWidth(), mContentTop + mContentView.getMeasuredHeight()); mInLayout = false; } @Override public void requestLayout() { if (!mInLayout) { super.requestLayout(); } } @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { final boolean drawContent = child == mContentView; boolean ret = super.drawChild(canvas, child, drawingTime); if (mScrimOpacity > 0 && drawContent && mDragHelper.getViewDragState() != ViewDragHelper.STATE_IDLE) { drawShadow(canvas, child); drawScrim(canvas, child); } return ret; } private void drawScrim(Canvas canvas, View child) { final int baseAlpha = (mScrimColor & 0xff000000) >>> 24; final int alpha = (int) (baseAlpha * mScrimOpacity); final int color = alpha << 24 | (mScrimColor & 0xffffff); if ((mTrackingEdge & EDGE_LEFT) != 0) { canvas.clipRect(0, 0, child.getLeft(), getHeight()); } else if ((mTrackingEdge & EDGE_RIGHT) != 0) { canvas.clipRect(child.getRight(), 0, getRight(), getHeight()); } else if ((mTrackingEdge & EDGE_BOTTOM) != 0) { canvas.clipRect(child.getLeft(), child.getBottom(), getRight(), getHeight()); } canvas.drawColor(color); } private void drawShadow(Canvas canvas, View child) { final Rect childRect = mTmpRect; child.getHitRect(childRect); if ((mEdgeFlag & EDGE_LEFT) != 0) { mShadowLeft.setBounds(childRect.left - mShadowLeft.getIntrinsicWidth(), childRect.top, childRect.left, childRect.bottom); mShadowLeft.setAlpha((int) (mScrimOpacity * FULL_ALPHA)); mShadowLeft.draw(canvas); } if ((mEdgeFlag & EDGE_RIGHT) != 0) { mShadowRight.setBounds(childRect.right, childRect.top, childRect.right + mShadowRight.getIntrinsicWidth(), childRect.bottom); mShadowRight.setAlpha((int) (mScrimOpacity * FULL_ALPHA)); mShadowRight.draw(canvas); } if ((mEdgeFlag & EDGE_BOTTOM) != 0) { mShadowBottom.setBounds(childRect.left, childRect.bottom, childRect.right, childRect.bottom + mShadowBottom.getIntrinsicHeight()); mShadowBottom.setAlpha((int) (mScrimOpacity * FULL_ALPHA)); mShadowBottom.draw(canvas); } } public void attachToActivity(Activity activity) { mActivity = activity; TypedArray a = activity.getTheme().obtainStyledAttributes(new int[]{ android.R.attr.windowBackground }); int background = a.getResourceId(0, 0); a.recycle(); ViewGroup decor = (ViewGroup) activity.getWindow().getDecorView(); ViewGroup decorChild = (ViewGroup) decor.getChildAt(0); decorChild.setBackgroundResource(background); decor.removeView(decorChild); addView(decorChild); setContentView(decorChild); decor.addView(this); } @Override public void computeScroll() { mScrimOpacity = 1 - mScrollPercent; if (mDragHelper.continueSettling(true)) { ViewCompat.postInvalidateOnAnimation(this); } } public static interface SwipeListener { /** * Invoke when state change * * @param state flag to describe scroll state * @param scrollPercent scroll percent of this view * @see #STATE_IDLE * @see #STATE_DRAGGING * @see #STATE_SETTLING */ public void onScrollStateChange(int state, float scrollPercent); /** * Invoke when edge touched * * @param edgeFlag edge flag describing the edge being touched * @see #EDGE_LEFT * @see #EDGE_RIGHT * @see #EDGE_BOTTOM */ public void onEdgeTouch(int edgeFlag); /** * Invoke when scroll percent over the threshold for the first time */ public void onScrollOverThreshold(); } private class ViewDragCallback extends ViewDragHelper.Callback { private boolean mIsScrollOverValid; @Override public boolean tryCaptureView(View view, int i) { boolean ret = mDragHelper.isEdgeTouched(mEdgeFlag, i); if (ret) { if (mDragHelper.isEdgeTouched(EDGE_LEFT, i)) { mTrackingEdge = EDGE_LEFT; } else if (mDragHelper.isEdgeTouched(EDGE_RIGHT, i)) { mTrackingEdge = EDGE_RIGHT; } else if (mDragHelper.isEdgeTouched(EDGE_BOTTOM, i)) { mTrackingEdge = EDGE_BOTTOM; } if (mListeners != null && !mListeners.isEmpty()) { for (SwipeListener listener : mListeners) { listener.onEdgeTouch(mTrackingEdge); } } mIsScrollOverValid = true; } return ret; } @Override public int getViewHorizontalDragRange(View child) { return mEdgeFlag & (EDGE_LEFT | EDGE_RIGHT); } @Override public int getViewVerticalDragRange(View child) { return mEdgeFlag & EDGE_BOTTOM; } @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { super.onViewPositionChanged(changedView, left, top, dx, dy); if ((mTrackingEdge & EDGE_LEFT) != 0) { mScrollPercent = Math.abs((float) left / (mContentView.getWidth() + mShadowLeft.getIntrinsicWidth())); } else if ((mTrackingEdge & EDGE_RIGHT) != 0) { mScrollPercent = Math.abs((float) left / (mContentView.getWidth() + mShadowRight.getIntrinsicWidth())); } else if ((mTrackingEdge & EDGE_BOTTOM) != 0) { mScrollPercent = Math.abs((float) top / (mContentView.getHeight() + mShadowBottom.getIntrinsicHeight())); } mContentLeft = left; mContentTop = top; invalidate(); if (mScrollPercent < mScrollThreshold && !mIsScrollOverValid) { mIsScrollOverValid = true; } if (mListeners != null && !mListeners.isEmpty() && mDragHelper.getViewDragState() == STATE_DRAGGING && mScrollPercent >= mScrollThreshold && mIsScrollOverValid) { mIsScrollOverValid = false; for (SwipeListener listener : mListeners) { listener.onScrollOverThreshold(); } } if (mScrollPercent >= 1) { if (!mActivity.isFinishing()) { mActivity.finish(); mActivity.overridePendingTransition(0, 0); } } } @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { final int childWidth = releasedChild.getWidth(); final int childHeight = releasedChild.getHeight(); int left = 0, top = 0; if ((mTrackingEdge & EDGE_LEFT) != 0) { left = xvel > 0 || xvel == 0 && mScrollPercent > mScrollThreshold ? childWidth + mShadowLeft.getIntrinsicWidth() + OVERSCROLL_DISTANCE : 0; } else if ((mTrackingEdge & EDGE_RIGHT) != 0) { left = xvel < 0 || xvel == 0 && mScrollPercent > mScrollThreshold ? -(childWidth + mShadowLeft.getIntrinsicWidth() + OVERSCROLL_DISTANCE) : 0; } else if ((mTrackingEdge & EDGE_BOTTOM) != 0) { top = yvel < 0 || yvel == 0 && mScrollPercent > mScrollThreshold ? -(childHeight + mShadowBottom.getIntrinsicHeight() + OVERSCROLL_DISTANCE) : 0; } mDragHelper.settleCapturedViewAt(left, top); invalidate(); } @Override public int clampViewPositionHorizontal(View child, int left, int dx) { int ret = 0; if ((mTrackingEdge & EDGE_LEFT) != 0) { ret = Math.min(child.getWidth(), Math.max(left, 0)); } else if ((mTrackingEdge & EDGE_RIGHT) != 0) { ret = Math.min(0, Math.max(left, -child.getWidth())); } return ret; } @Override public int clampViewPositionVertical(View child, int top, int dy) { int ret = 0; if ((mTrackingEdge & EDGE_BOTTOM) != 0) { ret = Math.min(0, Math.max(top, -child.getHeight())); } return ret; } @Override public void onViewDragStateChanged(int state) { super.onViewDragStateChanged(state); if (mListeners != null && !mListeners.isEmpty()) { for (SwipeListener listener : mListeners) { listener.onScrollStateChange(state, mScrollPercent); } } } } } ================================================ FILE: hydrogen-library/src/main/java/androlua/widget/swipebacklayout/Utils.java ================================================ package androlua.widget.swipebacklayout; import android.app.Activity; import java.lang.reflect.Method; /** * Created by Chaojun Wang on 6/9/14. */ public class Utils { private Utils() { } /** * Convert a translucent themed Activity * {@link android.R.attr#windowIsTranslucent} to a fullscreen opaque * Activity. *

* Call this whenever the background of a translucent Activity has changed * to become opaque. Doing so will allow the {@link android.view.Surface} of * the Activity behind to be released. *

* This call has no effect on non-translucent activities or on activities * with the {@link android.R.attr#windowIsFloating} attribute. */ public static void convertActivityFromTranslucent(Activity activity) { try { Method method = Activity.class.getDeclaredMethod("convertFromTranslucent"); method.setAccessible(true); method.invoke(activity); } catch (Throwable t) { } } /** * Convert a translucent themed Activity * {@link android.R.attr#windowIsTranslucent} back from opaque to * translucent following a call to * {@link #convertActivityFromTranslucent(Activity)} . *

* Calling this allows the Activity behind this one to be seen again. Once * all such Activities have been redrawn *

* This call has no effect on non-translucent activities or on activities * with the {@link android.R.attr#windowIsFloating} attribute. */ public static void convertActivityToTranslucent(Activity activity) { try { Class[] classes = Activity.class.getDeclaredClasses(); Class translucentConversionListenerClazz = null; for (Class clazz : classes) { if (clazz.getSimpleName().contains("TranslucentConversionListener")) { translucentConversionListenerClazz = clazz; } } Method method = Activity.class.getDeclaredMethod("convertToTranslucent", translucentConversionListenerClazz); method.setAccessible(true); method.invoke(activity, new Object[]{ null }); } catch (Throwable t) { } } } ================================================ FILE: hydrogen-library/src/main/java/androlua/widget/swipebacklayout/ViewDragHelper.java ================================================ /* * Copyright (C) 2013 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 androlua.widget.swipebacklayout; import android.content.Context; import android.support.v4.view.MotionEventCompat; import android.support.v4.view.VelocityTrackerCompat; import android.support.v4.view.ViewCompat; import android.support.v4.widget.ScrollerCompat; import android.util.Log; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.animation.Interpolator; import java.util.Arrays; /** * ViewDragHelper is a utility class for writing custom ViewGroups. It offers a * number of useful operations and state tracking for allowing a user to drag * and reposition views within their parent ViewGroup. */ public class ViewDragHelper { /** * A null/invalid pointer ID. */ public static final int INVALID_POINTER = -1; /** * A view is not currently being dragged or animating as a result of a * fling/snap. */ public static final int STATE_IDLE = 0; /** * A view is currently being dragged. The position is currently changing as * a result of user input or simulated user input. */ public static final int STATE_DRAGGING = 1; /** * A view is currently settling into place as a result of a fling or * predefined non-interactive motion. */ public static final int STATE_SETTLING = 2; /** * Edge flag indicating that the left edge should be affected. */ public static final int EDGE_LEFT = 1 << 0; /** * Edge flag indicating that the right edge should be affected. */ public static final int EDGE_RIGHT = 1 << 1; /** * Edge flag indicating that the top edge should be affected. */ public static final int EDGE_TOP = 1 << 2; /** * Edge flag indicating that the bottom edge should be affected. */ public static final int EDGE_BOTTOM = 1 << 3; /** * Edge flag set indicating all edges should be affected. */ public static final int EDGE_ALL = EDGE_LEFT | EDGE_TOP | EDGE_RIGHT | EDGE_BOTTOM; /** * Indicates that a check should occur along the horizontal axis */ public static final int DIRECTION_HORIZONTAL = 1 << 0; /** * Indicates that a check should occur along the vertical axis */ public static final int DIRECTION_VERTICAL = 1 << 1; /** * Indicates that a check should occur along all axes */ public static final int DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL; public static final int EDGE_SIZE = 20; // dp private static final String TAG = "ViewDragHelper"; private static final int BASE_SETTLE_DURATION = 256; // ms private static final int MAX_SETTLE_DURATION = 600; // ms /** * Interpolator defining the animation curve for mScroller */ private static final Interpolator sInterpolator = new Interpolator() { public float getInterpolation(float t) { t -= 1.0f; return t * t * t * t * t + 1.0f; } }; private final Callback mCallback; private final ViewGroup mParentView; // Current drag state; idle, dragging or settling private int mDragState; // Distance to travel before a drag may begin private int mTouchSlop; // Last known position/pointer tracking private int mActivePointerId = INVALID_POINTER; private float[] mInitialMotionX; private float[] mInitialMotionY; private float[] mLastMotionX; private float[] mLastMotionY; private int[] mInitialEdgeTouched; private int[] mEdgeDragsInProgress; private int[] mEdgeDragsLocked; private int mPointersDown; private VelocityTracker mVelocityTracker; private float mMaxVelocity; private float mMinVelocity; private int mEdgeSize; private int mTrackingEdges; private ScrollerCompat mScroller; private View mCapturedView; private final Runnable mSetIdleRunnable = new Runnable() { public void run() { setDragState(STATE_IDLE); } }; private boolean mReleaseInProgress; /** * Apps should use ViewDragHelper.create() to get a new instance. This will * allow VDH to use internal compatibility implementations for different * platform versions. * * @param context Context to initialize config-dependent params from * @param forParent Parent view to monitor */ private ViewDragHelper(Context context, ViewGroup forParent, Callback cb) { if (forParent == null) { throw new IllegalArgumentException("Parent view may not be null"); } if (cb == null) { throw new IllegalArgumentException("Callback may not be null"); } mParentView = forParent; mCallback = cb; final ViewConfiguration vc = ViewConfiguration.get(context); final float density = context.getResources().getDisplayMetrics().density; mEdgeSize = (int) (EDGE_SIZE * density + 0.5f); mTouchSlop = vc.getScaledTouchSlop(); mMaxVelocity = vc.getScaledMaximumFlingVelocity(); mMinVelocity = vc.getScaledMinimumFlingVelocity(); mScroller = ScrollerCompat.create(context, sInterpolator); } /** * Factory method to create a new ViewDragHelper. * * @param forParent Parent view to monitor * @param cb Callback to provide information and receive events * @return a new ViewDragHelper instance */ public static ViewDragHelper create(ViewGroup forParent, Callback cb) { return new ViewDragHelper(forParent.getContext(), forParent, cb); } /** * Factory method to create a new ViewDragHelper. * * @param forParent Parent view to monitor * @param sensitivity Multiplier for how sensitive the helper should be * about detecting the start of a drag. Larger values are more * sensitive. 1.0f is normal. * @param cb Callback to provide information and receive events * @return a new ViewDragHelper instance */ public static ViewDragHelper create(ViewGroup forParent, float sensitivity, Callback cb) { final ViewDragHelper helper = create(forParent, cb); helper.mTouchSlop = (int) (helper.mTouchSlop * (1 / sensitivity)); return helper; } /** * Sets the sensitivity of the dragger. * * @param context The application context. * @param sensitivity value between 0 and 1, the final value for touchSlop = * ViewConfiguration.getScaledTouchSlop * (1 / s); */ public void setSensitivity(Context context, float sensitivity) { float s = Math.max(0f, Math.min(1.0f, sensitivity)); ViewConfiguration viewConfiguration = ViewConfiguration.get(context); mTouchSlop = (int) (viewConfiguration.getScaledTouchSlop() * (1 / s)); } /** * Set the max velocity that will be detected as having a magnitude * greater than zero in pixels per second. Callback methods accepting a * velocity will be clamped appropriately. * * @param maxVel max velocity to detect */ public void setMaxVelocity(float maxVel) { mMaxVelocity = maxVel; } /** * Return the currently configured minimum velocity. Any flings with a * magnitude less than this value in pixels per second. Callback methods * accepting a velocity will receive zero as a velocity value if the real * detected velocity was below this threshold. * * @return the minimum velocity that will be detected */ public float getMinVelocity() { return mMinVelocity; } /** * Set the minimum velocity that will be detected as having a magnitude * greater than zero in pixels per second. Callback methods accepting a * velocity will be clamped appropriately. * * @param minVel minimum velocity to detect */ public void setMinVelocity(float minVel) { mMinVelocity = minVel; } /** * Retrieve the current drag state of this helper. This will return one of * {@link #STATE_IDLE}, {@link #STATE_DRAGGING} or {@link #STATE_SETTLING}. * * @return The current drag state */ public int getViewDragState() { return mDragState; } /** * Enable edge tracking for the selected edges of the parent view. The * callback's * {@link ViewDragHelper.Callback#onEdgeTouched(int, int)} * and * {@link ViewDragHelper.Callback#onEdgeDragStarted(int, int)} * methods will only be invoked for edges for which edge tracking has been * enabled. * * @param edgeFlags Combination of edge flags describing the edges to watch * @see #EDGE_LEFT * @see #EDGE_TOP * @see #EDGE_RIGHT * @see #EDGE_BOTTOM */ public void setEdgeTrackingEnabled(int edgeFlags) { mTrackingEdges = edgeFlags; } /** * Return the size of an edge. This is the range in pixels along the edges * of this view that will actively detect edge touches or drags if edge * tracking is enabled. * * @return The size of an edge in pixels * @see #setEdgeTrackingEnabled(int) */ public int getEdgeSize() { return mEdgeSize; } /** * Set the size of an edge. This is the range in pixels along the edges of * this view that will actively detect edge touches or drags if edge * tracking is enabled. * * @param size The size of an edge in pixels */ public void setEdgeSize(int size) { mEdgeSize = size; } /** * Capture a specific child view for dragging within the parent. The * callback will be notified but * {@link ViewDragHelper.Callback#tryCaptureView(View, int)} * will not be asked permission to capture this view. * * @param childView Child view to capture * @param activePointerId ID of the pointer that is dragging the captured * child view */ public void captureChildView(View childView, int activePointerId) { if (childView.getParent() != mParentView) { throw new IllegalArgumentException("captureChildView: parameter must be a descendant " + "of the ViewDragHelper's tracked parent view (" + mParentView + ")"); } mCapturedView = childView; mActivePointerId = activePointerId; mCallback.onViewCaptured(childView, activePointerId); setDragState(STATE_DRAGGING); } /** * @return The currently captured view, or null if no view has been * captured. */ public View getCapturedView() { return mCapturedView; } /** * @return The ID of the pointer currently dragging the captured view, or * {@link #INVALID_POINTER}. */ public int getActivePointerId() { return mActivePointerId; } /** * @return The minimum distance in pixels that the user must travel to * initiate a drag */ public int getTouchSlop() { return mTouchSlop; } /** * The result of a call to this method is equivalent to * {@link #processTouchEvent(MotionEvent)} receiving an * ACTION_CANCEL event. */ public void cancel() { mActivePointerId = INVALID_POINTER; clearMotionHistory(); if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } } /** * {@link #cancel()}, but also abort all motion in progress and snap to the * end of any animation. */ public void abort() { cancel(); if (mDragState == STATE_SETTLING) { final int oldX = mScroller.getCurrX(); final int oldY = mScroller.getCurrY(); mScroller.abortAnimation(); final int newX = mScroller.getCurrX(); final int newY = mScroller.getCurrY(); mCallback.onViewPositionChanged(mCapturedView, newX, newY, newX - oldX, newY - oldY); } setDragState(STATE_IDLE); } /** * Animate the view child to the given (left, top) position. If * this method returns true, the caller should invoke * {@link #continueSettling(boolean)} on each subsequent frame to continue * the motion until it returns false. If this method returns false there is * no further work to do to complete the movement. *

* This operation does not count as a capture event, though * {@link #getCapturedView()} will still report the sliding view while the * slide is in progress. *

* * @param child Child view to capture and animate * @param finalLeft Final left position of child * @param finalTop Final top position of child * @return true if animation should continue through * {@link #continueSettling(boolean)} calls */ public boolean smoothSlideViewTo(View child, int finalLeft, int finalTop) { mCapturedView = child; mActivePointerId = INVALID_POINTER; return forceSettleCapturedViewAt(finalLeft, finalTop, 0, 0); } /** * Settle the captured view at the given (left, top) position. The * appropriate velocity from prior motion will be taken into account. If * this method returns true, the caller should invoke * {@link #continueSettling(boolean)} on each subsequent frame to continue * the motion until it returns false. If this method returns false there is * no further work to do to complete the movement. * * @param finalLeft Settled left edge position for the captured view * @param finalTop Settled top edge position for the captured view * @return true if animation should continue through * {@link #continueSettling(boolean)} calls */ public boolean settleCapturedViewAt(int finalLeft, int finalTop) { if (!mReleaseInProgress) { throw new IllegalStateException("Cannot settleCapturedViewAt outside of a call to " + "Callback#onViewReleased"); } return forceSettleCapturedViewAt(finalLeft, finalTop, (int) VelocityTrackerCompat.getXVelocity(mVelocityTracker, mActivePointerId), (int) VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId)); } /** * Settle the captured view at the given (left, top) position. * * @param finalLeft Target left position for the captured view * @param finalTop Target top position for the captured view * @param xvel Horizontal velocity * @param yvel Vertical velocity * @return true if animation should continue through * {@link #continueSettling(boolean)} calls */ private boolean forceSettleCapturedViewAt(int finalLeft, int finalTop, int xvel, int yvel) { final int startLeft = mCapturedView.getLeft(); final int startTop = mCapturedView.getTop(); final int dx = finalLeft - startLeft; final int dy = finalTop - startTop; if (dx == 0 && dy == 0) { // Nothing to do. Send callbacks, be done. mScroller.abortAnimation(); setDragState(STATE_IDLE); return false; } final int duration = computeSettleDuration(mCapturedView, dx, dy, xvel, yvel); mScroller.startScroll(startLeft, startTop, dx, dy, duration); setDragState(STATE_SETTLING); return true; } private int computeSettleDuration(View child, int dx, int dy, int xvel, int yvel) { xvel = clampMag(xvel, (int) mMinVelocity, (int) mMaxVelocity); yvel = clampMag(yvel, (int) mMinVelocity, (int) mMaxVelocity); final int absDx = Math.abs(dx); final int absDy = Math.abs(dy); final int absXVel = Math.abs(xvel); final int absYVel = Math.abs(yvel); final int addedVel = absXVel + absYVel; final int addedDistance = absDx + absDy; final float xweight = xvel != 0 ? (float) absXVel / addedVel : (float) absDx / addedDistance; final float yweight = yvel != 0 ? (float) absYVel / addedVel : (float) absDy / addedDistance; int xduration = computeAxisDuration(dx, xvel, mCallback.getViewHorizontalDragRange(child)); int yduration = computeAxisDuration(dy, yvel, mCallback.getViewVerticalDragRange(child)); return (int) (xduration * xweight + yduration * yweight); } private int computeAxisDuration(int delta, int velocity, int motionRange) { if (delta == 0) { return 0; } final int width = mParentView.getWidth(); final int halfWidth = width / 2; final float distanceRatio = Math.min(1f, (float) Math.abs(delta) / width); final float distance = halfWidth + halfWidth * distanceInfluenceForSnapDuration(distanceRatio); int duration; velocity = Math.abs(velocity); if (velocity > 0) { duration = 4 * Math.round(1000 * Math.abs(distance / velocity)); } else { final float range = (float) Math.abs(delta) / motionRange; duration = (int) ((range + 1) * BASE_SETTLE_DURATION); } return Math.min(duration, MAX_SETTLE_DURATION); } /** * Clamp the magnitude of value for absMin and absMax. If the value is below * the minimum, it will be clamped to zero. If the value is above the * maximum, it will be clamped to the maximum. * * @param value Value to clamp * @param absMin Absolute value of the minimum significant value to return * @param absMax Absolute value of the maximum value to return * @return The clamped value with the same sign as value */ private int clampMag(int value, int absMin, int absMax) { final int absValue = Math.abs(value); if (absValue < absMin) return 0; if (absValue > absMax) return value > 0 ? absMax : -absMax; return value; } /** * Clamp the magnitude of value for absMin and absMax. If the value is below * the minimum, it will be clamped to zero. If the value is above the * maximum, it will be clamped to the maximum. * * @param value Value to clamp * @param absMin Absolute value of the minimum significant value to return * @param absMax Absolute value of the maximum value to return * @return The clamped value with the same sign as value */ private float clampMag(float value, float absMin, float absMax) { final float absValue = Math.abs(value); if (absValue < absMin) return 0; if (absValue > absMax) return value > 0 ? absMax : -absMax; return value; } private float distanceInfluenceForSnapDuration(float f) { f -= 0.5f; // center the values about 0. f *= 0.3f * Math.PI / 2.0f; return (float) Math.sin(f); } /** * Settle the captured view based on standard free-moving fling behavior. * The caller should invoke {@link #continueSettling(boolean)} on each * subsequent frame to continue the motion until it returns false. * * @param minLeft Minimum X position for the view's left edge * @param minTop Minimum Y position for the view's top edge * @param maxLeft Maximum X position for the view's left edge * @param maxTop Maximum Y position for the view's top edge */ public void flingCapturedView(int minLeft, int minTop, int maxLeft, int maxTop) { if (!mReleaseInProgress) { throw new IllegalStateException("Cannot flingCapturedView outside of a call to " + "Callback#onViewReleased"); } mScroller.fling(mCapturedView.getLeft(), mCapturedView.getTop(), (int) VelocityTrackerCompat.getXVelocity(mVelocityTracker, mActivePointerId), (int) VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId), minLeft, maxLeft, minTop, maxTop); setDragState(STATE_SETTLING); } /** * Move the captured settling view by the appropriate amount for the current * time. If continueSettling returns true, the caller should * call it again on the next frame to continue. * * @param deferCallbacks true if state callbacks should be deferred via * posted message. Set this to true if you are calling this * method from {@link View#computeScroll()} or * similar methods invoked as part of layout or drawing. * @return true if settle is still in progress */ public boolean continueSettling(boolean deferCallbacks) { if (mDragState == STATE_SETTLING) { boolean keepGoing = mScroller.computeScrollOffset(); final int x = mScroller.getCurrX(); final int y = mScroller.getCurrY(); final int dx = x - mCapturedView.getLeft(); final int dy = y - mCapturedView.getTop(); if (dx != 0) { mCapturedView.offsetLeftAndRight(dx); } if (dy != 0) { mCapturedView.offsetTopAndBottom(dy); } if (dx != 0 || dy != 0) { mCallback.onViewPositionChanged(mCapturedView, x, y, dx, dy); } if (keepGoing && x == mScroller.getFinalX() && y == mScroller.getFinalY()) { // Close enough. The interpolator/scroller might think we're // still moving // but the user sure doesn't. mScroller.abortAnimation(); keepGoing = mScroller.isFinished(); } if (!keepGoing) { if (deferCallbacks) { mParentView.post(mSetIdleRunnable); } else { setDragState(STATE_IDLE); } } } return mDragState == STATE_SETTLING; } /** * Like all callback events this must happen on the UI thread, but release * involves some extra semantics. During a release (mReleaseInProgress) is * the only time it is valid to call {@link #settleCapturedViewAt(int, int)} * or {@link #flingCapturedView(int, int, int, int)}. */ private void dispatchViewReleased(float xvel, float yvel) { mReleaseInProgress = true; mCallback.onViewReleased(mCapturedView, xvel, yvel); mReleaseInProgress = false; if (mDragState == STATE_DRAGGING) { // onViewReleased didn't call a method that would have changed this. // Go idle. setDragState(STATE_IDLE); } } private void clearMotionHistory() { if (mInitialMotionX == null) { return; } Arrays.fill(mInitialMotionX, 0); Arrays.fill(mInitialMotionY, 0); Arrays.fill(mLastMotionX, 0); Arrays.fill(mLastMotionY, 0); Arrays.fill(mInitialEdgeTouched, 0); Arrays.fill(mEdgeDragsInProgress, 0); Arrays.fill(mEdgeDragsLocked, 0); mPointersDown = 0; } private void clearMotionHistory(int pointerId) { if (mInitialMotionX == null) { return; } mInitialMotionX[pointerId] = 0; mInitialMotionY[pointerId] = 0; mLastMotionX[pointerId] = 0; mLastMotionY[pointerId] = 0; mInitialEdgeTouched[pointerId] = 0; mEdgeDragsInProgress[pointerId] = 0; mEdgeDragsLocked[pointerId] = 0; mPointersDown &= ~(1 << pointerId); } private void ensureMotionHistorySizeForId(int pointerId) { if (mInitialMotionX == null || mInitialMotionX.length <= pointerId) { float[] imx = new float[pointerId + 1]; float[] imy = new float[pointerId + 1]; float[] lmx = new float[pointerId + 1]; float[] lmy = new float[pointerId + 1]; int[] iit = new int[pointerId + 1]; int[] edip = new int[pointerId + 1]; int[] edl = new int[pointerId + 1]; if (mInitialMotionX != null) { System.arraycopy(mInitialMotionX, 0, imx, 0, mInitialMotionX.length); System.arraycopy(mInitialMotionY, 0, imy, 0, mInitialMotionY.length); System.arraycopy(mLastMotionX, 0, lmx, 0, mLastMotionX.length); System.arraycopy(mLastMotionY, 0, lmy, 0, mLastMotionY.length); System.arraycopy(mInitialEdgeTouched, 0, iit, 0, mInitialEdgeTouched.length); System.arraycopy(mEdgeDragsInProgress, 0, edip, 0, mEdgeDragsInProgress.length); System.arraycopy(mEdgeDragsLocked, 0, edl, 0, mEdgeDragsLocked.length); } mInitialMotionX = imx; mInitialMotionY = imy; mLastMotionX = lmx; mLastMotionY = lmy; mInitialEdgeTouched = iit; mEdgeDragsInProgress = edip; mEdgeDragsLocked = edl; } } private void saveInitialMotion(float x, float y, int pointerId) { ensureMotionHistorySizeForId(pointerId); mInitialMotionX[pointerId] = mLastMotionX[pointerId] = x; mInitialMotionY[pointerId] = mLastMotionY[pointerId] = y; mInitialEdgeTouched[pointerId] = getEdgeTouched((int) x, (int) y); mPointersDown |= 1 << pointerId; } private void saveLastMotion(MotionEvent ev) { final int pointerCount = MotionEventCompat.getPointerCount(ev); for (int i = 0; i < pointerCount; i++) { final int pointerId = MotionEventCompat.getPointerId(ev, i); final float x = MotionEventCompat.getX(ev, i); final float y = MotionEventCompat.getY(ev, i); mLastMotionX[pointerId] = x; mLastMotionY[pointerId] = y; } } /** * Check if the given pointer ID represents a pointer that is currently down * (to the best of the ViewDragHelper's knowledge). *

* The state used to report this information is populated by the methods * {@link #shouldInterceptTouchEvent(MotionEvent)} or * {@link #processTouchEvent(MotionEvent)}. If one of these * methods has not been called for all relevant MotionEvents to track, the * information reported by this method may be stale or incorrect. *

* * @param pointerId pointer ID to check; corresponds to IDs provided by * MotionEvent * @return true if the pointer with the given ID is still down */ public boolean isPointerDown(int pointerId) { return (mPointersDown & 1 << pointerId) != 0; } void setDragState(int state) { if (mDragState != state) { mDragState = state; mCallback.onViewDragStateChanged(state); if (state == STATE_IDLE) { mCapturedView = null; } } } /** * Attempt to capture the view with the given pointer ID. The callback will * be involved. This will put us into the "dragging" state. If we've already * captured this view with this pointer this method will immediately return * true without consulting the callback. * * @param toCapture View to capture * @param pointerId Pointer to capture with * @return true if capture was successful */ boolean tryCaptureViewForDrag(View toCapture, int pointerId) { if (toCapture == mCapturedView && mActivePointerId == pointerId) { // Already done! return true; } if (toCapture != null && mCallback.tryCaptureView(toCapture, pointerId)) { mActivePointerId = pointerId; captureChildView(toCapture, pointerId); return true; } return false; } /** * Tests scrollability within child views of v given a delta of dx. * * @param v View to test for horizontal scrollability * @param checkV Whether the view v passed should itself be checked for * scrollability (true), or just its children (false). * @param dx Delta scrolled in pixels along the X axis * @param dy Delta scrolled in pixels along the Y axis * @param x X coordinate of the active touch point * @param y Y coordinate of the active touch point * @return true if child views of v can be scrolled by delta of dx. */ protected boolean canScroll(View v, boolean checkV, int dx, int dy, int x, int y) { if (v instanceof ViewGroup) { final ViewGroup group = (ViewGroup) v; final int scrollX = v.getScrollX(); final int scrollY = v.getScrollY(); final int count = group.getChildCount(); // Count backwards - let topmost views consume scroll distance // first. for (int i = count - 1; i >= 0; i--) { // TODO: Add versioned support here for transformed views. // This will not work for transformed views in Honeycomb+ final View child = group.getChildAt(i); if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() && y + scrollY >= child.getTop() && y + scrollY < child.getBottom() && canScroll(child, true, dx, dy, x + scrollX - child.getLeft(), y + scrollY - child.getTop())) { return true; } } } return checkV && (ViewCompat.canScrollHorizontally(v, -dx) || ViewCompat.canScrollVertically(v, -dy)); } /** * Check if this event as provided to the parent view's * onInterceptTouchEvent should cause the parent to intercept the touch * event stream. * * @param ev MotionEvent provided to onInterceptTouchEvent * @return true if the parent view should return true from * onInterceptTouchEvent */ public boolean shouldInterceptTouchEvent(MotionEvent ev) { final int action = MotionEventCompat.getActionMasked(ev); final int actionIndex = MotionEventCompat.getActionIndex(ev); if (action == MotionEvent.ACTION_DOWN) { // Reset things for a new event stream, just in case we didn't get // the whole previous stream. cancel(); } if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(ev); switch (action) { case MotionEvent.ACTION_DOWN: { final float x = ev.getX(); final float y = ev.getY(); final int pointerId = MotionEventCompat.getPointerId(ev, 0); saveInitialMotion(x, y, pointerId); final View toCapture = findTopChildUnder((int) x, (int) y); // Catch a settling view if possible. if (toCapture == mCapturedView && mDragState == STATE_SETTLING) { tryCaptureViewForDrag(toCapture, pointerId); } final int edgesTouched = mInitialEdgeTouched[pointerId]; if ((edgesTouched & mTrackingEdges) != 0) { mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId); } break; } case MotionEventCompat.ACTION_POINTER_DOWN: { final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex); final float x = MotionEventCompat.getX(ev, actionIndex); final float y = MotionEventCompat.getY(ev, actionIndex); saveInitialMotion(x, y, pointerId); // A ViewDragHelper can only manipulate one view at a time. if (mDragState == STATE_IDLE) { final int edgesTouched = mInitialEdgeTouched[pointerId]; if ((edgesTouched & mTrackingEdges) != 0) { mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId); } } else if (mDragState == STATE_SETTLING) { // Catch a settling view if possible. final View toCapture = findTopChildUnder((int) x, (int) y); if (toCapture == mCapturedView) { tryCaptureViewForDrag(toCapture, pointerId); } } break; } case MotionEvent.ACTION_MOVE: { // First to cross a touch slop over a draggable view wins. Also // report edge drags. final int pointerCount = MotionEventCompat.getPointerCount(ev); for (int i = 0; i < pointerCount; i++) { final int pointerId = MotionEventCompat.getPointerId(ev, i); final float x = MotionEventCompat.getX(ev, i); final float y = MotionEventCompat.getY(ev, i); final float dx = x - mInitialMotionX[pointerId]; final float dy = y - mInitialMotionY[pointerId]; reportNewEdgeDrags(dx, dy, pointerId); if (mDragState == STATE_DRAGGING) { // Callback might have started an edge drag break; } final View toCapture = findTopChildUnder((int) x, (int) y); if (toCapture != null && checkTouchSlop(toCapture, dx, dy) && tryCaptureViewForDrag(toCapture, pointerId)) { break; } } saveLastMotion(ev); break; } case MotionEventCompat.ACTION_POINTER_UP: { final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex); clearMotionHistory(pointerId); break; } case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { cancel(); break; } } return mDragState == STATE_DRAGGING; } /** * Process a touch event received by the parent view. This method will * dispatch callback events as needed before returning. The parent view's * onTouchEvent implementation should call this. * * @param ev The touch event received by the parent view */ public void processTouchEvent(MotionEvent ev) { final int action = MotionEventCompat.getActionMasked(ev); final int actionIndex = MotionEventCompat.getActionIndex(ev); if (action == MotionEvent.ACTION_DOWN) { // Reset things for a new event stream, just in case we didn't get // the whole previous stream. cancel(); } if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(ev); switch (action) { case MotionEvent.ACTION_DOWN: { final float x = ev.getX(); final float y = ev.getY(); final int pointerId = MotionEventCompat.getPointerId(ev, 0); final View toCapture = findTopChildUnder((int) x, (int) y); saveInitialMotion(x, y, pointerId); // Since the parent is already directly processing this touch // event, // there is no reason to delay for a slop before dragging. // Start immediately if possible. tryCaptureViewForDrag(toCapture, pointerId); final int edgesTouched = mInitialEdgeTouched[pointerId]; if ((edgesTouched & mTrackingEdges) != 0) { mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId); } break; } case MotionEventCompat.ACTION_POINTER_DOWN: { final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex); final float x = MotionEventCompat.getX(ev, actionIndex); final float y = MotionEventCompat.getY(ev, actionIndex); saveInitialMotion(x, y, pointerId); // A ViewDragHelper can only manipulate one view at a time. if (mDragState == STATE_IDLE) { // If we're idle we can do anything! Treat it like a normal // down event. final View toCapture = findTopChildUnder((int) x, (int) y); tryCaptureViewForDrag(toCapture, pointerId); final int edgesTouched = mInitialEdgeTouched[pointerId]; if ((edgesTouched & mTrackingEdges) != 0) { mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId); } } else if (isCapturedViewUnder((int) x, (int) y)) { // We're still tracking a captured view. If the same view is // under this // point, we'll swap to controlling it with this pointer // instead. // (This will still work if we're "catching" a settling // view.) tryCaptureViewForDrag(mCapturedView, pointerId); } break; } case MotionEvent.ACTION_MOVE: { if (mDragState == STATE_DRAGGING) { final int index = MotionEventCompat.findPointerIndex(ev, mActivePointerId); if (index == -1) { Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent"); break; } final float x = MotionEventCompat.getX(ev, index); final float y = MotionEventCompat.getY(ev, index); final int idx = (int) (x - mLastMotionX[mActivePointerId]); final int idy = (int) (y - mLastMotionY[mActivePointerId]); dragTo(mCapturedView.getLeft() + idx, mCapturedView.getTop() + idy, idx, idy); saveLastMotion(ev); } else { // Check to see if any pointer is now over a draggable view. final int pointerCount = MotionEventCompat.getPointerCount(ev); for (int i = 0; i < pointerCount; i++) { final int pointerId = MotionEventCompat.getPointerId(ev, i); final float x = MotionEventCompat.getX(ev, i); final float y = MotionEventCompat.getY(ev, i); final float dx = x - mInitialMotionX[pointerId]; final float dy = y - mInitialMotionY[pointerId]; reportNewEdgeDrags(dx, dy, pointerId); if (mDragState == STATE_DRAGGING) { // Callback might have started an edge drag. break; } final View toCapture = findTopChildUnder((int) x, (int) y); if (checkTouchSlop(toCapture, dx, dy) && tryCaptureViewForDrag(toCapture, pointerId)) { break; } } saveLastMotion(ev); } break; } case MotionEventCompat.ACTION_POINTER_UP: { final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex); if (mDragState == STATE_DRAGGING && pointerId == mActivePointerId) { // Try to find another pointer that's still holding on to // the captured view. int newActivePointer = INVALID_POINTER; final int pointerCount = MotionEventCompat.getPointerCount(ev); for (int i = 0; i < pointerCount; i++) { final int id = MotionEventCompat.getPointerId(ev, i); if (id == mActivePointerId) { // This one's going away, skipActivity. continue; } final float x = MotionEventCompat.getX(ev, i); final float y = MotionEventCompat.getY(ev, i); if (findTopChildUnder((int) x, (int) y) == mCapturedView && tryCaptureViewForDrag(mCapturedView, id)) { newActivePointer = mActivePointerId; break; } } if (newActivePointer == INVALID_POINTER) { // We didn't find another pointer still touching the // view, release it. releaseViewForPointerUp(); } } clearMotionHistory(pointerId); break; } case MotionEvent.ACTION_UP: { if (mDragState == STATE_DRAGGING) { releaseViewForPointerUp(); } cancel(); break; } case MotionEvent.ACTION_CANCEL: { if (mDragState == STATE_DRAGGING) { dispatchViewReleased(0, 0); } cancel(); break; } } } private void reportNewEdgeDrags(float dx, float dy, int pointerId) { int dragsStarted = 0; if (checkNewEdgeDrag(dx, dy, pointerId, EDGE_LEFT)) { dragsStarted |= EDGE_LEFT; } if (checkNewEdgeDrag(dy, dx, pointerId, EDGE_TOP)) { dragsStarted |= EDGE_TOP; } if (checkNewEdgeDrag(dx, dy, pointerId, EDGE_RIGHT)) { dragsStarted |= EDGE_RIGHT; } if (checkNewEdgeDrag(dy, dx, pointerId, EDGE_BOTTOM)) { dragsStarted |= EDGE_BOTTOM; } if (dragsStarted != 0) { mEdgeDragsInProgress[pointerId] |= dragsStarted; mCallback.onEdgeDragStarted(dragsStarted, pointerId); } } private boolean checkNewEdgeDrag(float delta, float odelta, int pointerId, int edge) { final float absDelta = Math.abs(delta); final float absODelta = Math.abs(odelta); if ((mInitialEdgeTouched[pointerId] & edge) != edge || (mTrackingEdges & edge) == 0 || (mEdgeDragsLocked[pointerId] & edge) == edge || (mEdgeDragsInProgress[pointerId] & edge) == edge || (absDelta <= mTouchSlop && absODelta <= mTouchSlop)) { return false; } if (absDelta < absODelta * 0.5f && mCallback.onEdgeLock(edge)) { mEdgeDragsLocked[pointerId] |= edge; return false; } return (mEdgeDragsInProgress[pointerId] & edge) == 0 && absDelta > mTouchSlop; } /** * Check if we've crossed a reasonable touch slop for the given child view. * If the child cannot be dragged along the horizontal or vertical axis, * motion along that axis will not count toward the slop check. * * @param child Child to check * @param dx Motion since initial position along X axis * @param dy Motion since initial position along Y axis * @return true if the touch slop has been crossed */ private boolean checkTouchSlop(View child, float dx, float dy) { if (child == null) { return false; } final boolean checkHorizontal = mCallback.getViewHorizontalDragRange(child) > 0; final boolean checkVertical = mCallback.getViewVerticalDragRange(child) > 0; if (checkHorizontal && checkVertical) { return dx * dx + dy * dy > mTouchSlop * mTouchSlop; } else if (checkHorizontal) { return Math.abs(dx) > mTouchSlop; } else if (checkVertical) { return Math.abs(dy) > mTouchSlop; } return false; } /** * Check if any pointer tracked in the current gesture has crossed the * required slop threshold. *

* This depends on internal state populated by * {@link #shouldInterceptTouchEvent(MotionEvent)} or * {@link #processTouchEvent(MotionEvent)}. You should only * rely on the results of this method after all currently available touch * data has been provided to one of these two methods. *

* * @param directions Combination of direction flags, see * {@link #DIRECTION_HORIZONTAL}, {@link #DIRECTION_VERTICAL}, * {@link #DIRECTION_ALL} * @return true if the slop threshold has been crossed, false otherwise */ public boolean checkTouchSlop(int directions) { final int count = mInitialMotionX.length; for (int i = 0; i < count; i++) { if (checkTouchSlop(directions, i)) { return true; } } return false; } /** * Check if the specified pointer tracked in the current gesture has crossed * the required slop threshold. *

* This depends on internal state populated by * {@link #shouldInterceptTouchEvent(MotionEvent)} or * {@link #processTouchEvent(MotionEvent)}. You should only * rely on the results of this method after all currently available touch * data has been provided to one of these two methods. *

* * @param directions Combination of direction flags, see * {@link #DIRECTION_HORIZONTAL}, {@link #DIRECTION_VERTICAL}, * {@link #DIRECTION_ALL} * @param pointerId ID of the pointer to slop check as specified by * MotionEvent * @return true if the slop threshold has been crossed, false otherwise */ public boolean checkTouchSlop(int directions, int pointerId) { if (!isPointerDown(pointerId)) { return false; } final boolean checkHorizontal = (directions & DIRECTION_HORIZONTAL) == DIRECTION_HORIZONTAL; final boolean checkVertical = (directions & DIRECTION_VERTICAL) == DIRECTION_VERTICAL; final float dx = mLastMotionX[pointerId] - mInitialMotionX[pointerId]; final float dy = mLastMotionY[pointerId] - mInitialMotionY[pointerId]; if (checkHorizontal && checkVertical) { return dx * dx + dy * dy > mTouchSlop * mTouchSlop; } else if (checkHorizontal) { return Math.abs(dx) > mTouchSlop; } else if (checkVertical) { return Math.abs(dy) > mTouchSlop; } return false; } /** * Check if any of the edges specified were initially touched in the * currently active gesture. If there is no currently active gesture this * method will return false. * * @param edges Edges to check for an initial edge touch. See * {@link #EDGE_LEFT}, {@link #EDGE_TOP}, {@link #EDGE_RIGHT}, * {@link #EDGE_BOTTOM} and {@link #EDGE_ALL} * @return true if any of the edges specified were initially touched in the * current gesture */ public boolean isEdgeTouched(int edges) { final int count = mInitialEdgeTouched.length; for (int i = 0; i < count; i++) { if (isEdgeTouched(edges, i)) { return true; } } return false; } /** * Check if any of the edges specified were initially touched by the pointer * with the specified ID. If there is no currently active gesture or if * there is no pointer with the given ID currently down this method will * return false. * * @param edges Edges to check for an initial edge touch. See * {@link #EDGE_LEFT}, {@link #EDGE_TOP}, {@link #EDGE_RIGHT}, * {@link #EDGE_BOTTOM} and {@link #EDGE_ALL} * @return true if any of the edges specified were initially touched in the * current gesture */ public boolean isEdgeTouched(int edges, int pointerId) { return isPointerDown(pointerId) && (mInitialEdgeTouched[pointerId] & edges) != 0; } private void releaseViewForPointerUp() { mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity); final float xvel = clampMag( VelocityTrackerCompat.getXVelocity(mVelocityTracker, mActivePointerId), mMinVelocity, mMaxVelocity); final float yvel = clampMag( VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId), mMinVelocity, mMaxVelocity); dispatchViewReleased(xvel, yvel); } private void dragTo(int left, int top, int dx, int dy) { int clampedX = left; int clampedY = top; final int oldLeft = mCapturedView.getLeft(); final int oldTop = mCapturedView.getTop(); if (dx != 0) { clampedX = mCallback.clampViewPositionHorizontal(mCapturedView, left, dx); mCapturedView.offsetLeftAndRight(clampedX - oldLeft); } if (dy != 0) { clampedY = mCallback.clampViewPositionVertical(mCapturedView, top, dy); mCapturedView.offsetTopAndBottom(clampedY - oldTop); } if (dx != 0 || dy != 0) { final int clampedDx = clampedX - oldLeft; final int clampedDy = clampedY - oldTop; mCallback .onViewPositionChanged(mCapturedView, clampedX, clampedY, clampedDx, clampedDy); } } /** * Determine if the currently captured view is under the given point in the * parent view's coordinate system. If there is no captured view this method * will return false. * * @param x X position to test in the parent's coordinate system * @param y Y position to test in the parent's coordinate system * @return true if the captured view is under the given point, false * otherwise */ public boolean isCapturedViewUnder(int x, int y) { return isViewUnder(mCapturedView, x, y); } /** * Determine if the supplied view is under the given point in the parent * view's coordinate system. * * @param view Child view of the parent to hit test * @param x X position to test in the parent's coordinate system * @param y Y position to test in the parent's coordinate system * @return true if the supplied view is under the given point, false * otherwise */ public boolean isViewUnder(View view, int x, int y) { if (view == null) { return false; } return x >= view.getLeft() && x < view.getRight() && y >= view.getTop() && y < view.getBottom(); } /** * Find the topmost child under the given point within the parent view's * coordinate system. The child order is determined using * {@link ViewDragHelper.Callback#getOrderedChildIndex(int)} * . * * @param x X position to test in the parent's coordinate system * @param y Y position to test in the parent's coordinate system * @return The topmost child view under (x, y) or null if none found. */ public View findTopChildUnder(int x, int y) { final int childCount = mParentView.getChildCount(); for (int i = childCount - 1; i >= 0; i--) { final View child = mParentView.getChildAt(mCallback.getOrderedChildIndex(i)); if (x >= child.getLeft() && x < child.getRight() && y >= child.getTop() && y < child.getBottom()) { return child; } } return null; } private int getEdgeTouched(int x, int y) { int result = 0; if (x < mParentView.getLeft() + mEdgeSize) result = EDGE_LEFT; if (y < mParentView.getTop() + mEdgeSize) result = EDGE_TOP; if (x > mParentView.getRight() - mEdgeSize) result = EDGE_RIGHT; if (y > mParentView.getBottom() - mEdgeSize) result = EDGE_BOTTOM; return result; } /** * A Callback is used as a communication channel with the ViewDragHelper * back to the parent view using it. on*methods are invoked on * siginficant events and several accessor methods are expected to provide * the ViewDragHelper with more information about the state of the parent * view upon request. The callback also makes decisions governing the range * and draggability of child views. */ public static abstract class Callback { /** * Called when the drag state changes. See the STATE_* * constants for more information. * * @param state The new drag state * @see #STATE_IDLE * @see #STATE_DRAGGING * @see #STATE_SETTLING */ public void onViewDragStateChanged(int state) { } /** * Called when the captured view's position changes as the result of a * drag or settle. * * @param changedView View whose position changed * @param left New X coordinate of the left edge of the view * @param top New Y coordinate of the top edge of the view * @param dx Change in X position from the last call * @param dy Change in Y position from the last call */ public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { } /** * Called when a child view is captured for dragging or settling. The ID * of the pointer currently dragging the captured view is supplied. If * activePointerId is identified as {@link #INVALID_POINTER} the capture * is programmatic instead of pointer-initiated. * * @param capturedChild Child view that was captured * @param activePointerId Pointer id tracking the child capture */ public void onViewCaptured(View capturedChild, int activePointerId) { } /** * Called when the child view is no longer being actively dragged. The * fling velocity is also supplied, if relevant. The velocity values may * be clamped to system minimums or maximums. *

* Calling code may decide to fling or otherwise release the view to let * it settle into place. It should do so using * {@link #settleCapturedViewAt(int, int)} or * {@link #flingCapturedView(int, int, int, int)}. If the Callback * invokes one of these methods, the ViewDragHelper will enter * {@link #STATE_SETTLING} and the view capture will not fully end until * it comes to a complete stop. If neither of these methods is invoked * before onViewReleased returns, the view will stop in * place and the ViewDragHelper will return to {@link #STATE_IDLE}. *

* * @param releasedChild The captured child view now being released * @param xvel X velocity of the pointer as it left the screen in pixels * per second. * @param yvel Y velocity of the pointer as it left the screen in pixels * per second. */ public void onViewReleased(View releasedChild, float xvel, float yvel) { } /** * Called when one of the subscribed edges in the parent view has been * touched by the user while no child view is currently captured. * * @param edgeFlags A combination of edge flags describing the edge(s) * currently touched * @param pointerId ID of the pointer touching the described edge(s) * @see #EDGE_LEFT * @see #EDGE_TOP * @see #EDGE_RIGHT * @see #EDGE_BOTTOM */ public void onEdgeTouched(int edgeFlags, int pointerId) { } /** * Called when the given edge may become locked. This can happen if an * edge drag was preliminarily rejected before beginning, but after * {@link #onEdgeTouched(int, int)} was called. This method should * return true to lock this edge or false to leave it unlocked. The * default behavior is to leave edges unlocked. * * @param edgeFlags A combination of edge flags describing the edge(s) * locked * @return true to lock the edge, false to leave it unlocked */ public boolean onEdgeLock(int edgeFlags) { return false; } /** * Called when the user has started a deliberate drag away from one of * the subscribed edges in the parent view while no child view is * currently captured. * * @param edgeFlags A combination of edge flags describing the edge(s) * dragged * @param pointerId ID of the pointer touching the described edge(s) * @see #EDGE_LEFT * @see #EDGE_TOP * @see #EDGE_RIGHT * @see #EDGE_BOTTOM */ public void onEdgeDragStarted(int edgeFlags, int pointerId) { } /** * Called to determine the Z-order of child views. * * @param index the ordered position to query for * @return index of the view that should be ordered at position * index */ public int getOrderedChildIndex(int index) { return index; } /** * Return the magnitude of a draggable child view's horizontal range of * motion in pixels. This method should return 0 for views that cannot * move horizontally. * * @param child Child view to check * @return range of horizontal motion in pixels */ public int getViewHorizontalDragRange(View child) { return 0; } /** * Return the magnitude of a draggable child view's vertical range of * motion in pixels. This method should return 0 for views that cannot * move vertically. * * @param child Child view to check * @return range of vertical motion in pixels */ public int getViewVerticalDragRange(View child) { return 0; } /** * Called when the user's input indicates that they want to capture the * given child view with the pointer indicated by pointerId. The * callback should return true if the user is permitted to drag the * given view with the indicated pointer. *

* ViewDragHelper may call this method multiple times for the same view * even if the view is already captured; this indicates that a new * pointer is trying to take control of the view. *

*

* If this method returns true, a call to * {@link #onViewCaptured(View, int)} will follow if the * capture is successful. *

* * @param child Child the user is attempting to capture * @param pointerId ID of the pointer attempting the capture * @return true if capture should be allowed, false otherwise */ public abstract boolean tryCaptureView(View child, int pointerId); /** * Restrict the motion of the dragged child view along the horizontal * axis. The default implementation does not allow horizontal motion; * the extending class must override this method and provide the desired * clamping. * * @param child Child view being dragged * @param left Attempted motion along the X axis * @param dx Proposed change in position for left * @return The new clamped position for left */ public int clampViewPositionHorizontal(View child, int left, int dx) { return 0; } /** * Restrict the motion of the dragged child view along the vertical * axis. The default implementation does not allow vertical motion; the * extending class must override this method and provide the desired * clamping. * * @param child Child view being dragged * @param top Attempted motion along the Y axis * @param dy Proposed change in position for top * @return The new clamped position for top */ public int clampViewPositionVertical(View child, int top, int dy) { return 0; } } } ================================================ FILE: hydrogen-library/src/main/java/androlua/widget/swipebacklayout/app/SwipeBackActivity.java ================================================ package androlua.widget.swipebacklayout.app; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import androlua.widget.swipebacklayout.SwipeBackLayout; import androlua.widget.swipebacklayout.Utils; public class SwipeBackActivity extends AppCompatActivity implements SwipeBackActivityBase { private SwipeBackActivityHelper mHelper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mHelper = new SwipeBackActivityHelper(this); mHelper.onActivityCreate(); } @Override protected void onResume() { super.onResume(); } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); if (needPostCreate()) { mHelper.onPostCreate(); } } @Override public View findViewById(int id) { View v = super.findViewById(id); if (v == null && mHelper != null) return mHelper.findViewById(id); return v; } @Override public SwipeBackLayout getSwipeBackLayout() { return mHelper.getSwipeBackLayout(); } @Override public void setSwipeBackEnable(boolean enable) { getSwipeBackLayout().setEnableGesture(enable); } @Override public void scrollToFinishActivity() { Utils.convertActivityToTranslucent(this); getSwipeBackLayout().scrollToFinishActivity(); } protected boolean needPostCreate() { return true; } } ================================================ FILE: hydrogen-library/src/main/java/androlua/widget/swipebacklayout/app/SwipeBackActivityBase.java ================================================ package androlua.widget.swipebacklayout.app; import androlua.widget.swipebacklayout.SwipeBackLayout; /** * @author Yrom */ public interface SwipeBackActivityBase { /** * @return the SwipeBackLayout associated with this activity. */ public abstract SwipeBackLayout getSwipeBackLayout(); public abstract void setSwipeBackEnable(boolean enable); /** * Scroll out contentView and finish the activity */ public abstract void scrollToFinishActivity(); } ================================================ FILE: hydrogen-library/src/main/java/androlua/widget/swipebacklayout/app/SwipeBackActivityHelper.java ================================================ package androlua.widget.swipebacklayout.app; import android.app.Activity; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.view.View; import android.view.ViewGroup; import androlua.widget.swipebacklayout.SwipeBackLayout; import androlua.widget.swipebacklayout.Utils; /** * @author Yrom */ public class SwipeBackActivityHelper { private Activity mActivity; private SwipeBackLayout mSwipeBackLayout; public SwipeBackActivityHelper(Activity activity) { mActivity = activity; } @SuppressWarnings("deprecation") public void onActivityCreate() { mActivity.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); mActivity.getWindow().getDecorView().setBackgroundDrawable(null); mSwipeBackLayout = new SwipeBackLayout(mActivity); mSwipeBackLayout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); mSwipeBackLayout.addSwipeListener(new SwipeBackLayout.SwipeListener() { @Override public void onScrollStateChange(int state, float scrollPercent) { } @Override public void onEdgeTouch(int edgeFlag) { Utils.convertActivityToTranslucent(mActivity); } @Override public void onScrollOverThreshold() { } }); } public void onPostCreate() { mSwipeBackLayout.attachToActivity(mActivity); } public View findViewById(int id) { if (mSwipeBackLayout != null) { return mSwipeBackLayout.findViewById(id); } return null; } public SwipeBackLayout getSwipeBackLayout() { return mSwipeBackLayout; } } ================================================ FILE: hydrogen-library/src/main/java/androlua/widget/swipebacklayout/app/SwipeBackPreferenceActivity.java ================================================ package androlua.widget.swipebacklayout.app; import android.os.Bundle; import android.preference.PreferenceActivity; import android.view.View; import androlua.widget.swipebacklayout.SwipeBackLayout; public class SwipeBackPreferenceActivity extends PreferenceActivity implements SwipeBackActivityBase { private SwipeBackActivityHelper mHelper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mHelper = new SwipeBackActivityHelper(this); mHelper.onActivityCreate(); } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); mHelper.onPostCreate(); } @Override public View findViewById(int id) { View v = super.findViewById(id); if (v == null && mHelper != null) return mHelper.findViewById(id); return v; } @Override public SwipeBackLayout getSwipeBackLayout() { return mHelper.getSwipeBackLayout(); } @Override public void setSwipeBackEnable(boolean enable) { getSwipeBackLayout().setEnableGesture(enable); } @Override public void scrollToFinishActivity() { getSwipeBackLayout().scrollToFinishActivity(); } } ================================================ FILE: hydrogen-library/src/main/java/androlua/widget/video/VideoPlayerActivity.java ================================================ package androlua.widget.video; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.os.Bundle; import android.support.annotation.Nullable; import android.view.View; import org.json.JSONException; import org.json.JSONObject; import androlua.LuaImageLoader; import androlua.base.BaseActivity; import cn.jzvd.JZVideoPlayer; import cn.jzvd.JZVideoPlayerStandard; import pub.hanks.luajandroid.R; /** * Created by hanks on 2017/6/2. Copyright (C) 2017 Hanks */ public class VideoPlayerActivity extends BaseActivity { public static void start(Context context, String json) { Intent starter = new Intent(context, VideoPlayerActivity.class); starter.putExtra("json", json); context.startActivity(starter); } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_video); this.getWindow().getDecorView().setBackgroundColor(Color.TRANSPARENT); JZVideoPlayerStandard videoplayer = (JZVideoPlayerStandard) findViewById(R.id.videoplayer); try { String extra = getIntent().getStringExtra("json"); JSONObject json = new JSONObject(extra); String url = json.getString("url"); String poster = ""; if (json.has("poster")) { poster = json.getString("poster"); } LuaImageLoader.load(videoplayer.thumbImageView, poster); videoplayer.setAllControlsVisiblity(0, 0, 0, 0, 0, View.INVISIBLE, View.INVISIBLE); videoplayer.setUp(url, JZVideoPlayer.SCREEN_WINDOW_LIST, ""); // JZVideoPlayerStandard.startFullscreen(this, JZVideoPlayerStandard.class, url, ""); } catch (JSONException e) { e.printStackTrace(); } } @Override public void onBackPressed() { if (JZVideoPlayer.backPress()) { return; } super.onBackPressed(); } @Override protected void onPause() { super.onPause(); JZVideoPlayer.releaseAllVideos(); } } ================================================ FILE: hydrogen-library/src/main/java/androlua/widget/viewpager/NoScrollViewPager.java ================================================ package androlua.widget.viewpager; import android.content.Context; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; /** * 可以禁止左右滑动 * Created by hanks on 16/7/18. */ public class NoScrollViewPager extends ViewPager { private boolean isPagingEnabled = false; public NoScrollViewPager(Context context) { super(context); } public NoScrollViewPager(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onTouchEvent(MotionEvent event) { return this.isPagingEnabled && super.onTouchEvent(event); } @Override public boolean onInterceptTouchEvent(MotionEvent event) { return this.isPagingEnabled && super.onInterceptTouchEvent(event); } @Override protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) { if (v != this && v instanceof ViewPager) { int currentItem = ((ViewPager) v).getCurrentItem(); int countItem = ((ViewPager) v).getAdapter().getCount(); return !((currentItem == (countItem - 1) && dx < 0) || (currentItem == 0 && dx > 0)); } return super.canScroll(v, checkV, dx, x, y); } public void setPagingEnabled(boolean b) { this.isPagingEnabled = b; } } ================================================ FILE: hydrogen-library/src/main/java/androlua/widget/webview/WebViewActivity.java ================================================ package androlua.widget.webview; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.support.annotation.ColorInt; import android.support.annotation.Nullable; import android.support.v4.view.GravityCompat; import android.support.v7.widget.PopupMenu; import android.text.TextUtils; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.webkit.WebChromeClient; import android.webkit.WebResourceRequest; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.EditText; import android.widget.TextView; import androlua.LuaUtil; import androlua.base.BaseActivity; import androlua.widget.statusbar.StatusBarView; import pub.hanks.luajandroid.R; /** * Created by hanks on 2017/6/2. Copyright (C) 2017 Hanks */ public class WebViewActivity extends BaseActivity { private EditText etUrl; private String url, webTitle; private int color; private WebView mWebView; private View loading; private View layout_toolbar; private View ivRefresh, iv_more; private Bitmap colorBitmap; private Canvas canvas; private StatusBarView view_statusbar; public static void start(Context context, String url) { start(context, url, Color.TRANSPARENT); } public static void start(Context context, String url, int color) { Intent starter = new Intent(context, WebViewActivity.class); starter.putExtra("url", url); starter.putExtra("color", color); context.startActivity(starter); } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_webview); etUrl = (EditText) findViewById(R.id.et_url); loading = findViewById(R.id.loading); layout_toolbar = findViewById(R.id.layout_toolbar); mWebView = (WebView) findViewById(R.id.webview); ivRefresh = findViewById(R.id.iv_refresh); iv_more = findViewById(R.id.iv_more); view_statusbar = (StatusBarView) findViewById(R.id.view_statusbar); colorBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565); canvas = new Canvas(colorBitmap); WebSettings settings = mWebView.getSettings(); settings.setUseWideViewPort(true); settings.setAppCacheEnabled(true); settings.setJavaScriptCanOpenWindowsAutomatically(true); settings.setDisplayZoomControls(false); settings.setSupportMultipleWindows(true); settings.setJavaScriptEnabled(true); settings.setDomStorageEnabled(true); settings.setAllowContentAccess(true); settings.setDatabaseEnabled(true); if (Build.VERSION.SDK_INT >= 19) { mWebView.setWebContentsDebuggingEnabled(true); } url = getIntent().getStringExtra("url"); color = getIntent().getIntExtra("color", 0); if (color == Color.TRANSPARENT) { setLightStatusBar(); } else { setStatusBarColor(color); layout_toolbar.setBackgroundColor(color); } etUrl.setText(url); etUrl.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (TextUtils.isEmpty(url) || TextUtils.isEmpty(webTitle)) { return; } if (hasFocus) { etUrl.setText(url); } else { etUrl.setText(webTitle); } } }); mWebView.setWebChromeClient(new WebChromeClient() { @Override public void onProgressChanged(WebView view, int newProgress) { super.onProgressChanged(view, newProgress); } @Override public void onReceivedTitle(WebView view, String title) { super.onReceivedTitle(view, title); webTitle = title; etUrl.setText(title); } }); mWebView.setWebViewClient(new WebViewClient() { @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); loading.setVisibility(View.VISIBLE); ivRefresh.setVisibility(View.GONE); } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); loading.setVisibility(View.GONE); ivRefresh.setVisibility(View.VISIBLE); fetchColor(); } @Deprecated public boolean shouldOverrideUrlLoading(WebView view, String url) { return !url.startsWith("http://") && !url.startsWith("https://") && !url.startsWith("hydrogen://"); } @Override public boolean shouldOverrideUrlLoading(WebView webView, WebResourceRequest webResourceRequest) { String url = ""; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { url = webResourceRequest.getUrl().toString(); } return !url.startsWith("http://") && !url.startsWith("https://") && !url.startsWith("hydrogen://"); } }); ivRefresh.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mWebView.loadUrl(url); } }); mWebView.loadUrl(url); iv_more.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { PopupMenu popupMenu = new PopupMenu(iv_more.getContext(), v, GravityCompat.START); popupMenu.getMenu().add(Menu.NONE, 1, Menu.NONE, "复制链接"); popupMenu.getMenu().add(Menu.NONE, 2, Menu.NONE, "在浏览器打开"); popupMenu.getMenu().add(Menu.NONE, 3, Menu.NONE, "分享"); popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { if (TextUtils.isEmpty(url)) { return false; } switch (item.getItemId()) { case 1: android.content.ClipboardManager clipboard = (android.content.ClipboardManager) WebViewActivity.this.getSystemService(Context.CLIPBOARD_SERVICE); android.content.ClipData clip = android.content.ClipData.newPlainText("lua", url); clipboard.setPrimaryClip(clip); break; case 2: Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(url)); WebViewActivity.this.startActivity(intent); break; case 3: Intent sendIntent = new Intent(Intent.ACTION_SEND); sendIntent.putExtra(Intent.EXTRA_TEXT, url); sendIntent.setType("text/plain"); WebViewActivity.this.startActivity(sendIntent); break; } return false; } }); popupMenu.show(); } }); etUrl.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { String url = etUrl.getText().toString(); if (!TextUtils.isEmpty(url) && url.startsWith("http")) { mWebView.loadUrl(url); } return false; } }); } private boolean canAsBgColor(int i) { return Color.red(i) < 220 || Color.green(i) < 220 || Color.blue(i) < 220; } private void fetchColor() { if (mWebView != null && mWebView.getVisibility() == View.VISIBLE && mWebView.getScrollX() < LuaUtil.dp2px(20)) { mWebView.draw(canvas); if (colorBitmap != null) { int pixel = colorBitmap.getPixel(0, 0); if (canAsBgColor(pixel)) { layout_toolbar.setBackgroundColor(pixel); if (pixel == Color.WHITE) { setLightStatusBar(); } else { setStatusBarColor(pixel); } } } } } @Override public void onBackPressed() { if (mWebView.canGoBack()) { mWebView.goBack(); return; } super.onBackPressed(); } @Override protected void onDestroy() { super.onDestroy(); try { if (mWebView != null) { if (mWebView.getParent() instanceof ViewGroup) { ((ViewGroup) mWebView.getParent()).removeAllViews(); } mWebView.stopLoading(); mWebView.setWebChromeClient(null); mWebView.setWebViewClient(null); mWebView.removeAllViews(); mWebView.destroy(); mWebView = null; } } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: hydrogen-library/src/main/java/com/luajava/Console.java ================================================ package com.luajava; import java.io.BufferedReader; import java.io.InputStreamReader; public class Console { public static void main(String[] args) { try { LuaState L = LuaStateFactory.newLuaState(); L.openLibs(); if (args.length > 0) { for (int i = 0; i < args.length; i++) { int res = L.LloadFile(args[i]); if (res == 0) { res = L.pcall(0, 0, 0); } if (res != 0) { throw new LuaException("Error on file: " + args[i] + ". " + L.toString(-1)); } } return; } System.out.println("API Lua Java - console mode."); BufferedReader inp = new BufferedReader(new InputStreamReader(System.in)); System.out.print("> "); while (true) { String line = inp.readLine(); if (line == null || line.equals("exit")) { L.close(); } else { int ret = L.LloadBuffer(line.getBytes(), "from console"); if (ret == 0) { ret = L.pcall(0, 0, 0); } if (ret != 0) { System.err.println("Error on line: " + line); System.err.println(L.toString(-1)); } System.out.print("> "); } } } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: hydrogen-library/src/main/java/com/luajava/JavaFunction.java ================================================ package com.luajava; public abstract class JavaFunction { protected LuaState L; public JavaFunction(LuaState L) { this.L = L; } public abstract int execute() throws LuaException; public LuaObject getParam(int idx) { return this.L.getLuaObject(idx); } public void register(String name) throws LuaException { synchronized (this.L) { this.L.pushJavaFunction(this); this.L.setGlobal(name); } } } ================================================ FILE: hydrogen-library/src/main/java/com/luajava/LuaException.java ================================================ /* * $Id: LuaException.java,v 1.6 2006/12/22 14:06:40 thiago Exp $ * Copyright (C) 2003-2007 Kepler Project. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package com.luajava; /** * LuaJava exception * * @author Thiago Ponte */ public class LuaException extends Exception { /** * */ private static final long serialVersionUID = 1L; public LuaException(String str) { super(str); } /** * Will work only on Java 1.4 or later. * To work with Java 1.3, comment the first line and uncomment the second one. */ public LuaException(Exception e) { super((e.getCause() != null) ? e.getCause() : e); //super(e.getMessage()); } } ================================================ FILE: hydrogen-library/src/main/java/com/luajava/LuaFunction.java ================================================ package com.luajava; public class LuaFunction extends LuaObject implements LuaMetaTable { protected LuaFunction(LuaState L, String globalName) { super(L, globalName); } protected LuaFunction(LuaState L, int index) { super(L, index); } @Override public T __call(Object[] arg) throws LuaException { // TODO: Implement this method return (T) super.call(arg); } @Override public Object __index(String key) { // TODO: Implement this method return null; } @Override public void __newIndex(String key, Object value) { // TODO: Implement this method } @Override public T call(Object... args) throws LuaException { // TODO: Implement this method return (T) super.call(args); } } ================================================ FILE: hydrogen-library/src/main/java/com/luajava/LuaInvocationHandler.java ================================================ /* * $Id: LuaInvocationHandler.java,v 1.4 2006/12/22 14:06:40 thiago Exp $ * Copyright (C) 2003-2007 Kepler Project. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package com.luajava; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * Class that implements the InvocationHandler interface. * This class is used in the LuaJava's proxy system. * When a proxy object is accessed, the method invoked is * called from Lua * * @author Rizzato * @author Thiago Ponte */ public class LuaInvocationHandler implements InvocationHandler { private LuaObject obj; private LuaState L; private LuaObject print; public LuaInvocationHandler(LuaObject obj) { this.obj = obj; L = obj.L; print = L.getLuaObject("print"); } /** * Function called when a proxy object function is invoked. */ public Object invoke(Object proxy, Method method, Object[] args) throws LuaException { synchronized (obj.L) { String methodName = method.getName(); LuaObject func = obj.getField(methodName); Class retType = method.getReturnType(); if (func.isNil()) { if (retType.equals(boolean.class) || retType.equals(Boolean.class)) return false; else if (retType.isPrimitive() || Number.class.isAssignableFrom(retType)) return 0; else return null; } Object ret = null; try { // Checks if returned type is void. if it is returns null. if (retType.equals(Void.class) || retType.equals(void.class)) { func.call(args); ret = null; } else { ret = func.call(args); if (ret != null && ret instanceof Double) { ret = LuaState.convertLuaNumber((Double) ret, retType); } } } catch (LuaException e) { print.call(methodName + " " + e.getMessage()); } if (ret == null) if (retType.equals(boolean.class) || retType.equals(Boolean.class)) return false; else if (retType.isPrimitive() || Number.class.isAssignableFrom(retType)) return 0; return ret; } } } ================================================ FILE: hydrogen-library/src/main/java/com/luajava/LuaJavaAPI.java ================================================ /* * $Id: LuaJavaAPI.java,v 1.4 2006/12/22 14:06:40 thiago Exp $ * Copyright (C) 2003-2007 Kepler Project. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Softwarea. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package com.luajava; import android.util.Log; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Class that contains functions accessed by lua. * * @author Thiago Ponte */ public final class LuaJavaAPI { public static HashMap methodsMap = new HashMap(); public static HashMap methodCache = new HashMap(); private static Class LuaState_class = LuaState.class; private static Class String_class = String.class; private static Class List_class = List.class; private static Class ArrayList_class = ArrayList.class; private static Class HashMap_class = HashMap.class; private static Class Map_class = Map.class; private static Class LuaFunction_class = LuaFunction.class; private static Class LuaObject_class = LuaObject.class; private static Class LuaTable_class = LuaTable.class; private static Class Number_class = Number.class; private static Class Character_class = Character.class; private LuaJavaAPI() { } /** * Java implementation of the metamethod __index * * @param luaState int that indicates the state used * @param obj Object to be indexed * @param methodName the name of the method * @return number of returned objects */ public static int objectIndex(long luaState, Object obj, String searchName, int type) throws LuaException { LuaState L = LuaStateFactory.getExistingState(luaState); synchronized (L) { int ret = 0; if (type == 0) if (checkMethod(L, obj, searchName) != 0) return 2; if (type == 0 || type == 1 || type == 5) if ((ret = checkField(L, obj, searchName)) != 0) return ret; if (type == 0 || type == 4) if (javaGetter(L, obj, searchName) != 0) return 4; if (type == 0 || type == 3) if (checkClass(L, obj, searchName) != 0) return 3; if ((type == 0 || type == 6) && obj instanceof LuaMetaTable) { Object res = ((LuaMetaTable) obj).__index(searchName); L.pushObjectValue(res); return 6; } return 0; } } public static int callMethod(long luaState, Object obj, String cacheName) throws LuaException { LuaState L = LuaStateFactory.getExistingState(luaState); synchronized (L) { Method[] methods = methodCache.get(cacheName); int top = L.getTop(); Object[] objs = new Object[top]; Method method = null; // gets method and arguments for (int i = 0; i < methods.length; i++) { Class[] parameters = methods[i].getParameterTypes(); if (parameters.length != top) continue; boolean okMethod = true; for (int j = 0; j < parameters.length; j++) { try { objs[j] = compareTypes(L, parameters[j], j + 1); } catch (Exception e) { okMethod = false; break; } } if (okMethod) { method = methods[i]; break; } } // If method is null means there isn't one receiving the given arguments if (method == null) { StringBuilder msgbuilder = new StringBuilder(); for (int i = 0; i < methods.length; i++) { msgbuilder.append(methods[i].toString()); msgbuilder.append("\n"); } throw new LuaException("Invalid method call. Invalid Parameters.\n" + msgbuilder.toString()); } Object ret; try { if (!Modifier.isPublic(method.getModifiers())) method.setAccessible(true); ret = method.invoke(obj, objs); } catch (Exception e) { throw new LuaException(e); } // Void function returns null if (ret == null && method.getReturnType().equals(Void.TYPE)) return 0; // push result L.pushObjectValue(ret); return 1; } } /** * Java implementation of the metamethod __newindex * * @param luaState int that indicates the state used * @param obj Object to be indexed * @param searchName the name of the method * @return number of returned objects */ public static int objectNewIndex(long luaState, Object obj, String searchName) throws LuaException { LuaState L = LuaStateFactory.getExistingState(luaState); synchronized (L) { int res; res = setFieldValue(L, obj, searchName); if (res != 0) return 1; res = javaSetter(L, obj, searchName); if (res != 0) return 1; return 0; } } public static int setFieldValue(LuaState L, Object obj, String fieldName) throws LuaException { synchronized (L) { Field field = null; Class objClass; boolean isClass = false; if (obj == null) return 0; if (obj instanceof Class) { objClass = (Class) obj; isClass = true; } else { objClass = obj.getClass(); } try { field = objClass.getField(fieldName); } catch (NoSuchFieldException e) { /*try { field = objClass.getDeclaredField(fieldName); } catch (Exception e2) */ { return 0; } } if (field == null) return 0; if (isClass && !Modifier.isStatic(field.getModifiers())) return 0; Class type = field.getType(); try { if (!Modifier.isPublic(field.getModifiers())) field.setAccessible(true); field.set(obj, compareTypes(L, type, 3)); } catch (LuaException e) { argError(L, fieldName, 3, type); } catch (Exception e) { throw new LuaException(e); } return 1; } } private static String argError(LuaState L, String name, int idx, Class type) throws LuaException { throw new LuaException("bad argument to '" + name + "' (" + type.getName() + " expected, got " + typeName(L, 3) + " value)"); } private static String typeName(LuaState L, int idx) throws LuaException { if (L.isObject(idx)) { return L.getObjectFromUserdata(idx).getClass().getName(); } switch (L.type(idx)) { case LuaState.LUA_TSTRING: return "string"; case LuaState.LUA_TNUMBER: return "number"; case LuaState.LUA_TBOOLEAN: return "boolean"; case LuaState.LUA_TFUNCTION: return "function"; case LuaState.LUA_TTABLE: return "table"; case LuaState.LUA_TTHREAD: return "thread"; case LuaState.LUA_TLIGHTUSERDATA: case LuaState.LUA_TUSERDATA: return "userdata"; } return "unkown"; } /** * Java implementation of the metamethod __index * * @param luaState int that indicates the state used * @param obj Object to be indexed * @param methodName the name of the method * @return number of returned objects */ public static int setArrayValue(long luaState, Object obj, int index) throws LuaException { LuaState L = LuaStateFactory.getExistingState(luaState); synchronized (L) { if (obj.getClass().isArray()) { Class type = obj.getClass().getComponentType(); try { Object value = compareTypes(L, type, 3); Array.set(obj, index, value); } catch (LuaException e) { argError(L, obj.getClass().getName() + " [" + index + "]", 3, type); } } else if (obj instanceof List) { ((List) obj).set(index, L.toJavaObject(3)); } else if (obj instanceof Map) { ((Map) obj).put(index, L.toJavaObject(3)); } else { throw new LuaException("can not set " + obj.getClass().getName() + " value: " + L.toJavaObject(3) + " in " + index); } return 0; } } public static int getArrayValue(long luaState, Object obj, int index) throws LuaException { LuaState L = LuaStateFactory.getExistingState(luaState); synchronized (L) { Object ret = null; if (obj.getClass().isArray()) { ret = Array.get(obj, index); } else if (obj instanceof List) { ret = ((List) obj).get(index); } else if (obj instanceof Map) { ret = ((Map) obj).get(index); } else { throw new LuaException("can not get " + obj.getClass().getName() + " value in " + index); } L.pushObjectValue(ret); return 1; } } public static int asTable(long luaState, Object obj) throws LuaException { LuaState L = LuaStateFactory.getExistingState(luaState); synchronized (L) { try { L.newTable(); if (obj.getClass().isArray()) { int n = Array.getLength(obj); for (int i = 0; i <= n - 1; i++) { L.pushObjectValue(Array.get(obj, i)); L.rawSetI(-2, i + 1); } } else if (obj instanceof Collection) { Collection list = (Collection) obj; int i = 1; for (Object v : list) { L.pushObjectValue(v); L.rawSetI(-2, i++); } } else if (obj instanceof Map) { Map map = (Map) obj; Iterator itor = map.entrySet().iterator(); while (itor.hasNext()) { Map.Entry entry = (Map.Entry) itor.next(); L.pushObjectValue(entry.getKey()); L.pushObjectValue(entry.getValue()); L.setTable(-3); } /* for (Map.Entry entry : map.entrySet()) { L.pushObjectValue(entry.getKey()); L.pushObjectValue(entry.getValue()); L.setTable(-3); }*/ } L.pushValue(-1); return 1; } catch (Exception e) { throw new LuaException("can not astable: " + e.getMessage()); } } } public static int newArray(long luaState, Class clazz, int size) throws LuaException { LuaState L = LuaStateFactory.getExistingState(luaState); synchronized (L) { try { Object obj = Array.newInstance(clazz, size); L.pushJavaObject(obj); } catch (Exception e) { throw new LuaException("can not create a array: " + e.getMessage()); } return 1; } } public static int newArray(long luaState, Class clazz) throws LuaException { LuaState L = LuaStateFactory.getExistingState(luaState); synchronized (L) { try { int top = L.getTop(); int[] dimensions = new int[top - 1]; for (int i = 0; i < top - 1; i++) { dimensions[i] = (int) L.toInteger(i + 2); } Object obj = Array.newInstance(clazz, dimensions); L.pushJavaObject(obj); } catch (Exception e) { throw new LuaException("can not create a array: " + e.getMessage()); } return 1; } } public static Class javaBindClass(String className) throws LuaException { Class clazz; try { clazz = Class.forName(className); } catch (Exception e) { if (className.equals("boolean")) clazz = Boolean.TYPE; else if (className.equals("byte")) clazz = Byte.TYPE; else if (className.equals("char")) clazz = Character.TYPE; else if (className.equals("short")) clazz = Short.TYPE; else if (className.equals("int")) clazz = Integer.TYPE; else if (className.equals("long")) clazz = Long.TYPE; else if (className.equals("float")) clazz = Float.TYPE; else if (className.equals("double")) clazz = Double.TYPE; else throw new LuaException("Class not found: " + className); } return clazz; } /** * Pushes a new instance of a java Object of the type className * * @param luaState int that represents the state to be used * @param className name of the class * @return number of returned objects * @throws LuaException */ public static int javaNewInstance(long luaState, String className) throws LuaException { LuaState L = LuaStateFactory.getExistingState(luaState); synchronized (L) { Class clazz; clazz = javaBindClass(className); if (clazz.isPrimitive()) return toPrimitive(L, clazz, -1); else return getObjInstance(L, clazz); } } /** * javaNew returns a new instance of a given clazz * * @param luaState int that represents the state to be used * @param clazz class to be instanciated * @return number of returned objects * @throws LuaException */ public static int javaNew(long luaState, Class clazz) throws LuaException { LuaState L = LuaStateFactory.getExistingState(luaState); synchronized (L) { if (clazz.isPrimitive()) { return toPrimitive(L, clazz, -1); } else { return getObjInstance(L, clazz); } } } public static int javaCreate(long luaState, Class clazz) throws LuaException { LuaState L = LuaStateFactory.getExistingState(luaState); synchronized (L) { if (clazz.isInterface()) { return createProxyObject(L, clazz); } else if (clazz.isPrimitive()) { return createArray(L, clazz); } else if (List_class.isAssignableFrom(clazz)) { return createList(L, clazz); } else if (Map_class.isAssignableFrom(clazz)) { return createMap(L, clazz); } else { if (L.objLen(-1) == 0) return createArray(L, clazz); else if (clazz.isAssignableFrom(new LuaTable(L, -1).get(1).getClass())) return createArray(L, clazz); else return getObjInstance(L, clazz); /* LuaTable tab=new LuaTable(L, -1); tab.push(); if(tab.isList()){ if(tab.isEmpty()) return createArray(L, clazz); else if(clazz.isAssignableFrom(tab.get(1).getClass())) return createArray(L, clazz); else return getObjInstance(L, clazz); } else{ L.setTop(1); getObjInstance(L, clazz); LuaObject obj=L.getLuaObject(-1); Set sets=(Set)tab.entrySet(); for (LuaTable.LuaEntry entry:sets){ try{ obj.setField((String)entry.getKey(),entry.getValue()); } catch(Exception e) {} } return 1; }*/ } } } public static int objectCall(long luaState, Object obj) throws LuaException { LuaState L = LuaStateFactory.getExistingState(luaState); synchronized (L) { if (obj instanceof LuaMetaTable) { int n = L.getTop(); Object[] args = new Object[n - 1]; for (int i = 2; i <= n; i++) { args[i - 2] = L.toJavaObject(i); } Object ret = ((LuaMetaTable) obj).__call(args); L.pushObjectValue(ret); return 1; } else { return 0; } } } /** * Function that creates an object proxy and pushes it into the stack * * @param luaState int that represents the state to be used * @param implem interfaces implemented separated by comma (,) * @return number of returned objects * @throws LuaException */ public static int createProxy(long luaState, String implem) throws LuaException { LuaState L = LuaStateFactory.getExistingState(luaState); synchronized (L) { return createProxyObject(L, implem); } } public static int createArray(long luaState, String className) throws LuaException { LuaState L = LuaStateFactory.getExistingState(luaState); synchronized (L) { Class type = javaBindClass(className); return createArray(L, type); } } /** * Calls the static method methodName in class className * that receives a LuaState as first parameter. * * @param luaState int that represents the state to be used * @param className name of the class that has the open library method * @param methodName method to open library * @return number of returned objects * @throws LuaException */ public static int javaLoadLib(long luaState, String className, String methodName) throws LuaException { LuaState L = LuaStateFactory.getExistingState(luaState); synchronized (L) { Class clazz; try { clazz = Class.forName(className); } catch (ClassNotFoundException e) { throw new LuaException(e); } try { Method mt = clazz.getMethod(methodName, new Class[]{LuaState_class}); Object obj = mt.invoke(null, new Object[]{L}); if (obj != null && obj instanceof Integer) { return ((Integer) obj).intValue(); } else return 0; } catch (Exception e) { throw new LuaException("Error on calling method. Library could not be loaded. " + e.getMessage()); } } } public static int javaToString(long luaState, Object obj) throws LuaException { LuaState L = LuaStateFactory.getExistingState(luaState); synchronized (L) { if (obj == null) L.pushString("null"); else L.pushString(obj.toString()); return 1; } } public static int javaEquals(long luaState, Object obj, Object obj2) throws LuaException { LuaState L = LuaStateFactory.getExistingState(luaState); synchronized (L) { boolean eq = obj.equals(obj2); L.pushBoolean(eq); return 1; } } public static int javaObjectLength(long luaState, Object obj) throws LuaException { LuaState L = LuaStateFactory.getExistingState(luaState); synchronized (L) { int ret; try { if (obj instanceof CharSequence) ret = ((CharSequence) obj).length(); else if (obj instanceof Collection) ret = ((Collection) obj).size(); else if (obj instanceof Map) ret = ((Map) obj).size(); else ret = Array.getLength(obj); } catch (Exception e) { throw new LuaException(e); } L.pushInteger(ret); return 1; } } private static int getObjInstance(LuaState L, Class clazz) throws LuaException { synchronized (L) { int top = L.getTop(); if (top == 1) { try { Object ret = clazz.newInstance(); L.pushJavaObject(ret); return 1; } catch (Exception e) { if (android.view.View.class.isAssignableFrom(clazz)) { try { Constructor ctr = clazz.getConstructor(new Class[]{android.content.Context.class}); Object ret = ctr.newInstance(new Object[]{L.getContext()}); L.pushJavaObject(ret); return 1; } catch (Exception e2) { } } } } Object[] objs = new Object[top - 1]; Constructor[] constructors = clazz.getConstructors(); Constructor constructor = null; // gets method and arguments for (int i = 0; i < constructors.length; i++) { Class[] parameters = constructors[i].getParameterTypes(); if (parameters.length != top - 1) continue; boolean okConstruc = true; for (int j = 0; j < parameters.length; j++) { try { objs[j] = compareTypes(L, parameters[j], j + 2); } catch (Exception e) { okConstruc = false; break; } } if (okConstruc) { constructor = constructors[i]; break; } } // If method is null means there isn't one receiving the given arguments if (constructor == null) { StringBuilder msgbuilder = new StringBuilder(); for (int i = 0; i < constructors.length; i++) { msgbuilder.append(constructors[i].toString()); msgbuilder.append("\n"); } throw new LuaException("Invalid constructor method call. Invalid Parameters.\n" + msgbuilder.toString()); } Object ret; try { ret = constructor.newInstance(objs); } catch (Exception e) { throw new LuaException(e); } if (ret == null) { throw new LuaException("Couldn't instantiate java Object"); } L.pushJavaObject(ret); return 1; } } /** * Checks if there is a field on the obj with the given name * * @param luaState int that represents the state to be used * @param obj object to be inspected * @param fieldName name of the field to be inpected * @return number of returned objects */ public static int checkField(LuaState L, Object obj, String fieldName) throws LuaException { synchronized (L) { Field field = null; Class objClass; boolean isClass = false; if (obj instanceof Class) { objClass = (Class) obj; isClass = true; } else { objClass = obj.getClass(); } try { field = objClass.getField(fieldName); } catch (NoSuchFieldException e) { return 0; } if (field == null) return 0; if (isClass && !Modifier.isStatic(field.getModifiers())) return 0; Object ret = null; try { if (!Modifier.isPublic(field.getModifiers())) field.setAccessible(true); ret = field.get(obj); } catch (Exception e) { throw new LuaException(e); } L.pushObjectValue(ret); if (Modifier.isFinal(field.getModifiers())) return 5; else return 1; } } /** * Checks to see if there is a method with the given name. * * @param luaState int that represents the state to be used * @param obj object to be inspected * @param methodName name of the field to be inpected * @return number of returned objects */ public static int checkMethod(LuaState L, Object obj, String methodName) throws LuaException { synchronized (L) { Class clazz; boolean isClass = false; if (obj instanceof Class) { clazz = (Class) obj; isClass = true; } else { clazz = obj.getClass(); } String className = clazz.getName(); String cacheName = L.toString(-1);//String.format("%c %s %s",c,className,methodName); Method[] mlist = methodCache.get(cacheName); if (mlist == null) { Method[] methods = methodsMap.get(className); if (methods == null) { methods = clazz.getMethods(); methodsMap.put(className, methods); } ArrayList list = new ArrayList(); for (int i = 0; i < methods.length; i++) { if (methods[i].getName().equals(methodName)) { if (isClass && !Modifier.isStatic(methods[i].getModifiers())) continue; list.add(methods[i]); } } if (list.isEmpty() && isClass) { methods = clazz.getClass().getMethods(); for (int i = 0; i < methods.length; i++) { if (methods[i].getName().equals(methodName)) list.add(methods[i]); } } mlist = new Method[list.size()]; list.toArray(mlist); methodCache.put(cacheName, mlist); } if (mlist.length == 0) return 0; return 2; } } /** * Checks to see if there is a class with the given name. * * @param L int that represents the state to be used * @param obj object to be inspected * @param className name of the field to be inpected * @return number of returned objects */ public static int checkClass(LuaState L, Object obj, String className) throws LuaException { synchronized (L) { Class clazz; if (obj instanceof Class) { clazz = (Class) obj; } else { return 0; } Class[] clazzes = clazz.getClasses(); for (int i = 0; i < clazzes.length; i++) { if (clazzes[i].getSimpleName().equals(className)) { L.pushJavaObject(clazzes[i]); return 3; } } return 0; } } public static int javaGetter(LuaState L, Object obj, String methodName) throws LuaException { synchronized (L) { Class clazz; Method method = null; boolean isClass = false; if (obj instanceof Map) { Map map = (Map) obj; L.pushObjectValue(map.get(methodName)); return 1; } else if (obj instanceof Class) { clazz = (Class) obj; isClass = true; } else { clazz = obj.getClass(); } try { method = clazz.getMethod("get" + methodName); } catch (NoSuchMethodException e) { return 0; } if (isClass && !Modifier.isStatic(method.getModifiers())) return 0; Object ret; try { ret = method.invoke(obj); } catch (Exception e) { throw new LuaException(e); } if (ret instanceof CharSequence) L.pushString(ret.toString()); else L.pushObjectValue(ret); return 1; } } public static int javaSetter(LuaState L, Object obj, String methodName) throws LuaException { synchronized (L) { Class clazz; boolean isClass = false; if (obj instanceof Map) { Map map = (Map) obj; map.put(methodName, L.toJavaObject(3)); return 1; } else if (obj instanceof Class) { clazz = (Class) obj; isClass = true; } else { clazz = obj.getClass(); } String className = clazz.getName(); Method[] methods = methodsMap.get(className); if (methods == null) { methods = clazz.getMethods(); methodsMap.put(className, methods); } if (methodName.length() > 2 && methodName.substring(0, 2).equals("on") && L.type(-1) == LuaState.LUA_TFUNCTION) return javaSetListener(L, obj, methodName, methods, isClass); return javaSetMethod(L, obj, methodName, methods, isClass); } } private static int javaSetListener(LuaState L, Object obj, String methodName, Method[] methods, boolean isClass) throws LuaException { synchronized (L) { String name = "setOn" + methodName.substring(2) + "Listener"; for (Method m : methods) { if (!m.getName().equals(name)) continue; if (isClass && !Modifier.isStatic(m.getModifiers())) continue; Class[] tp = m.getParameterTypes(); if (tp.length == 1 && tp[0].isInterface()) { L.newTable(); L.pushValue(-2); L.setField(-2, methodName); try { Object listener = L.getLuaObject(-1).createProxy(tp[0]); m.invoke(obj, new Object[]{listener}); return 1; } catch (Exception e) { throw new LuaException(e); } } } } return 0; } private static int javaSetMethod(LuaState L, Object obj, String methodName, Method[] methods, boolean isClass) throws LuaException { synchronized (L) { String name = "set" + methodName; Object arg = null; StringBuilder buf = new StringBuilder(); for (Method m : methods) { if (!m.getName().equals(name)) continue; if (isClass && !Modifier.isStatic(m.getModifiers())) continue; Class[] tp = m.getParameterTypes(); if (tp.length != 1) continue; try { arg = compareTypes(L, tp[0], -1); } catch (LuaException e) { buf.append(tp[0]); buf.append("\n"); continue; } try { m.invoke(obj, new Object[]{arg}); return 1; } catch (Exception e) { throw new LuaException(e); } } if (buf.length() > 0) throw new LuaException("Invalid setter " + methodName + ". Invalid Parameters.\n" + buf.toString() + L.typeName(-1)); } return 0; } private static int createProxyObject(LuaState L, String implem) throws LuaException { synchronized (L) { try { LuaObject luaObj = L.getLuaObject(2); Object proxy = luaObj.createProxy(implem); L.pushJavaObject(proxy); } catch (Exception e) { throw new LuaException(e); } return 1; } } private static int createProxyObject(LuaState L, Class implem) throws LuaException { synchronized (L) { L.pushJavaObject(createProxyObject(L, implem, 2)); return 1; } } private static Object createProxyObject(LuaState L, Class implem, int idx) throws LuaException { synchronized (L) { try { LuaObject luaObj = L.getLuaObject(idx); Object proxy = luaObj.createProxy(implem); return proxy; } catch (Exception e) { throw new LuaException(e); } } } private static int createArray(LuaState L, Class type) throws LuaException { synchronized (L) { L.pushJavaObject(createArray(L, type, 2)); return 1; } } private static Object createArray(LuaState L, Class type, int idx) throws LuaException { synchronized (L) { try { int n = L.objLen(idx); Object array = Array.newInstance(type, n); if (type == String_class) { for (int i = 1; i <= n; i++) { L.pushNumber(i); L.getTable(idx); Array.set(array, i - 1, L.toString(-1)); L.pop(1); } } else if (type == Double.TYPE) { for (int i = 1; i <= n; i++) { L.pushNumber(i); L.getTable(idx); Array.set(array, i - 1, L.toNumber(-1)); L.pop(1); } } else if (type == Float.TYPE) { for (int i = 1; i <= n; i++) { L.pushNumber(i); L.getTable(idx); Array.set(array, i - 1, (float) L.toNumber(-1)); L.pop(1); } } else if (type == Long.TYPE) { for (int i = 1; i <= n; i++) { L.pushNumber(i); L.getTable(idx); Array.set(array, i - 1, L.toInteger(-1)); L.pop(1); } } else if (type == Integer.TYPE) { for (int i = 1; i <= n; i++) { L.pushNumber(i); L.getTable(idx); Array.set(array, i - 1, (int) L.toInteger(-1)); L.pop(1); } } else if (type == Short.TYPE) { for (int i = 1; i <= n; i++) { L.pushNumber(i); L.getTable(idx); Array.set(array, i - 1, (short) L.toInteger(-1)); L.pop(1); } } else if (type == Character.TYPE) { for (int i = 1; i <= n; i++) { L.pushNumber(i); L.getTable(idx); Array.set(array, i - 1, (char) L.toInteger(-1)); L.pop(1); } } else if (type == Byte.TYPE) { for (int i = 1; i <= n; i++) { L.pushNumber(i); L.getTable(idx); Array.set(array, i - 1, (byte) L.toInteger(-1)); L.pop(1); } } else { for (int i = 1; i <= n; i++) { L.pushNumber(i); L.getTable(idx); Array.set(array, i - 1, compareTypes(L, type, -1)); L.pop(1); } } return array; } catch (Exception e) { throw new LuaException(e); } } } private static int createList(LuaState L, Class type) throws LuaException { synchronized (L) { L.pushJavaObject(createList(L, type, 2)); return 1; } } private static Object createList(LuaState L, Class type, int idx) throws LuaException { synchronized (L) { int n = L.objLen(idx); try { if (type.equals(List_class)) type = ArrayList_class; List list = (List) type.newInstance(); for (int i = 1; i <= n; i++) { L.pushNumber(i); L.getTable(idx); list.add(L.toJavaObject(-1)); L.pop(1); } return list; } catch (Exception e) { throw new LuaException(e); } } } private static int createMap(LuaState L, Class clazz) throws LuaException { // TODO: Implement this method synchronized (L) { L.pushJavaObject(createMap(L, clazz, 2)); return 1; } } private static Object createMap(LuaState L, Class clazz, int idx) throws LuaException { // TODO: Implement this method synchronized (L) { try { if (clazz.equals(Map_class)) clazz = HashMap_class; Map map = (Map) clazz.newInstance(); L.pushNil(); while (L.next(idx) != 0) { map.put(L.toJavaObject(-2), L.toJavaObject(-1)); L.pop(1); } return map; } catch (Exception e) { throw new LuaException(e); } } } private static Object compareTypes(LuaState L, Class parameter, int idx) throws LuaException { boolean okType = true; Object obj = null; int type = L.type(idx); switch (type) { case 1: //boolean { if (parameter.isPrimitive()) { if (parameter != Boolean.TYPE) { okType = false; } } else if (!parameter.isAssignableFrom(Boolean.TYPE)) { okType = false; } obj = L.toBoolean(idx); } break; case 4: //string { if (!parameter.isAssignableFrom(String_class)) { okType = false; } else { obj = L.toString(idx); } } break; case 6: //function { if (!parameter.isAssignableFrom(LuaFunction_class)) { okType = false; } else { obj = L.getLuaObject(idx); } } break; case 5: //table { if (parameter.isAssignableFrom(LuaTable_class)) { obj = L.getLuaObject(idx); } else if (parameter.isArray()) { obj = createArray(L, parameter.getComponentType(), idx); } else if (parameter.isInterface()) { obj = createProxyObject(L, parameter, idx); } else if (Map_class.isAssignableFrom(parameter)) { obj = createMap(L, parameter, idx); } else { okType = false; } } break; case 3: //number { if (L.isInteger(idx)) { Long lg = new Long(L.toInteger(idx)); obj = LuaState.convertLuaNumber(lg, parameter); } else { Double db = new Double(L.toNumber(idx)); obj = LuaState.convertLuaNumber(db, parameter); } if (obj == null) { okType = false; } } break; case 7: //userdata { if (L.isObject(idx)) { Object userObj = L.getObjectFromUserdata(idx); if (userObj == null) { obj = null; } else if (parameter.isPrimitive() && (Number_class.isAssignableFrom(userObj.getClass()) || Character_class.isAssignableFrom(userObj.getClass()))) { obj = userObj; } else if (parameter.isAssignableFrom(userObj.getClass())) { obj = userObj; } else { okType = false; } } else { if (!parameter.isAssignableFrom(LuaObject_class)) { okType = false; } else { obj = L.getLuaObject(idx); } } } break; case 0: //nil { obj = null; } break; default: //other { throw new LuaException("Invalid Parameters."); } } if (!okType) { throw new LuaException("Invalid Parameter."); } return obj; } private static int toPrimitive(LuaState L, Class type, int idx) throws LuaException { Object obj = null; if (type == Character.TYPE && L.type(idx) == LuaState.LUA_TSTRING) { String s = L.toString(idx); if (s == null) { obj = ""; } else if (s.length() == 1) { obj = s.charAt(0); } else { obj = s.toCharArray(); } } else if (!L.isNumber(idx)) { throw new LuaException(L.toString(idx) + " is not number"); } else if (type == Double.TYPE) { obj = L.toNumber(idx); } else if (type == Float.TYPE) { obj = (float) L.toNumber(idx); } else if (type == Long.TYPE) { obj = L.toInteger(idx); } else if (type == Integer.TYPE) { obj = (int) L.toInteger(idx); } else if (type == Short.TYPE) { obj = (short) L.toInteger(idx); } else if (type == Character.TYPE) { obj = (char) L.toInteger(idx); } else if (type == Byte.TYPE) { obj = (byte) L.toInteger(idx); } else if (type == Boolean.TYPE) { obj = L.toBoolean(idx); } L.pushJavaObject(obj); return 1; } } ================================================ FILE: hydrogen-library/src/main/java/com/luajava/LuaList.java ================================================ package com.luajava; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.ListIterator; public class LuaList extends LuaObject implements List { protected LuaList(LuaState L, String globalName) { super(L, globalName); } protected LuaList(LuaState L, int index) { super(L, index); } public LuaList(LuaState L) { super(L); L.newTable(); registerValue(-1); } @Override public void clear() { // TODO: Implement this method } @Override public boolean isEmpty() { // TODO: Implement this method push(); int len = L.rawLen(-1); L.pop(1); return len == 0; } @Override public boolean remove(Object p1) { // TODO: Implement this method return false; } @Override public int size() { // TODO: Implement this method push(); int len = L.rawLen(-1); L.pop(1); return len; } @Override public void add(int p1, Object p2) { // TODO: Implement this method } @Override public boolean add(Object p1) { // TODO: Implement this method push(); int len = L.rawLen(-1); try { L.pushObjectValue(p1); L.setI(-2, len + 1); pop(); return true; } catch (LuaException e) { pop(); return false; } } @Override public boolean addAll(int p1, Collection p2) { // TODO: Implement this method return false; } @Override public boolean addAll(Collection p1) { // TODO: Implement this method return false; } @Override public boolean contains(Object p1) { // TODO: Implement this method return false; } @Override public boolean containsAll(Collection p1) { // TODO: Implement this method return false; } @Override public Object get(int p1) { // TODO: Implement this method return null; } @Override public int indexOf(Object p1) { // TODO: Implement this method return 0; } @Override public Iterator iterator() { // TODO: Implement this method return null; } @Override public int lastIndexOf(Object p1) { // TODO: Implement this method return 0; } @Override public ListIterator listIterator() { // TODO: Implement this method return null; } @Override public ListIterator listIterator(int p1) { // TODO: Implement this method return null; } @Override public Object remove(int p1) { // TODO: Implement this method return null; } @Override public boolean removeAll(Collection p1) { // TODO: Implement this method return false; } @Override public boolean retainAll(Collection p1) { // TODO: Implement this method return false; } @Override public Object set(int p1, Object p2) { // TODO: Implement this method return null; } @Override public List subList(int p1, int p2) { // TODO: Implement this method return null; } @Override public Object[] toArray() { // TODO: Implement this method return null; } @Override public Object[] toArray(Object[] p1) { // TODO: Implement this method return null; } } ================================================ FILE: hydrogen-library/src/main/java/com/luajava/LuaMetaTable.java ================================================ package com.luajava; public interface LuaMetaTable { public Object __call(Object... arg) throws LuaException; public Object __index(String key); public void __newIndex(String key, Object value); } ================================================ FILE: hydrogen-library/src/main/java/com/luajava/LuaObject.java ================================================ /* * $Id: LuaObject.java,v 1.6 2006/12/22 14:06:40 thiago Exp $ * Copyright (C) 2003-2007 Kepler Project. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package com.luajava; import java.lang.reflect.Array; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; /** * This class represents a Lua object of any type. A LuaObject is constructed by a {@link LuaState} object using one of * the four methods: *
    *
  • {@link LuaState#getLuaObject(String globalName)}
  • *
  • {@link LuaState#getLuaObject(LuaObject parent, String name)}
  • *
  • {@link LuaState#getLuaObject(LuaObject parent, Number name)}
  • *
  • {@link LuaState#getLuaObject(LuaObject parent, LuaObject name)}
  • *
  • {@link LuaState#getLuaObject(int index)}
  • *
* The LuaObject will represent only the object itself, not a variable or a stack index, so when you change a string, * remember that strings are immutable objects in Lua, and the LuaObject you have will represent the old one. *

*

Proxies

*

* LuaJava allows you to implement a class in Lua, like said before. If you want to create this proxy from Java, you * should have a LuaObject representing the table that has the functions that implement the interface. From this * LuaObject you can call the createProxy(String implements). This method receives the string with the * name of the interfaces implemented by the object separated by comma. * * @author Rizzato * @author Thiago Ponte */ public class LuaObject { protected int ref; protected LuaState L; protected LuaObject(LuaState L) { this.L = L; } /** * Creates a reference to an object in the variable globalName * * @param L * @param globalName */ protected LuaObject(LuaState L, String globalName) { synchronized (L) { this.L = L; L.getGlobal(globalName); registerValue(-1); L.pop(1); } } /** * Creates a reference to an object inside another object * * @param parent The Lua Table or Userdata that contains the Field. * @param name The name that index the field */ protected LuaObject(LuaObject parent, String name) throws LuaException { synchronized (parent.getLuaState()) { this.L = parent.getLuaState(); if (!parent.isTable() && !parent.isUserdata()) { throw new LuaException("Object parent should be a table or userdata ."); } parent.push(); L.pushString(name); L.getTable(-2); L.remove(-2); registerValue(-1); L.pop(1); } } /** * This constructor creates a LuaObject from a table that is indexed by a number. * * @param parent The Lua Table or Userdata that contains the Field. * @param name The name (number) that index the field * @throws LuaException When the parent object isn't a Table or Userdata */ protected LuaObject(LuaObject parent, Number name) throws LuaException { synchronized (parent.getLuaState()) { this.L = parent.getLuaState(); if (!parent.isTable() && !parent.isUserdata()) throw new LuaException("Object parent should be a table or userdata ."); parent.push(); L.pushNumber(name.doubleValue()); L.getTable(-2); L.remove(-2); registerValue(-1); L.pop(1); } } /** * This constructor creates a LuaObject from a table that is indexed by a LuaObject. * * @param parent The Lua Table or Userdata that contains the Field. * @param name The name (LuaObject) that index the field * @throws LuaException When the parent object isn't a Table or Userdata */ protected LuaObject(LuaObject parent, LuaObject name) throws LuaException { if (parent.getLuaState() != name.getLuaState()) throw new LuaException("LuaStates must be the same!"); synchronized (parent.getLuaState()) { if (!parent.isTable() && !parent.isUserdata()) throw new LuaException("Object parent should be a table or userdata ."); this.L = parent.getLuaState(); parent.push(); name.push(); L.getTable(-2); L.remove(-2); registerValue(-1); L.pop(1); } } /** * Creates a reference to an object in the given index of the stack * * @param L * @param index of the object on the lua stack */ protected LuaObject(LuaState L, int index) { synchronized (L) { this.L = L; registerValue(index); } } /** * Gets the Object's State */ public LuaState getLuaState() { return L; } /** * Creates the reference to the object in the registry table * * @param index of the object on the lua stack */ protected void registerValue(int index) { synchronized (L) { L.pushValue(index); int key; ref = L.Lref(LuaState.LUA_REGISTRYINDEX); // ref = new Integer(key); } } protected void finalize() { try { synchronized (L) { if (L.getPointer() != 0) L.LunRef(LuaState.LUA_REGISTRYINDEX, ref); } } catch (Exception e) { System.err.println("Unable to release object " + ref); } } /** * Pushes the object represented by this into L's stack */ public void push() { L.rawGetI(LuaState.LUA_REGISTRYINDEX, ref); } public void pop() { L.pop(1); } public boolean isNil() { synchronized (L) { push(); boolean bool = L.isNil(-1); L.pop(1); return bool; } } public boolean isBoolean() { synchronized (L) { push(); boolean bool = L.isBoolean(-1); L.pop(1); return bool; } } public boolean isNumber() { synchronized (L) { push(); boolean bool = L.isNumber(-1); L.pop(1); return bool; } } public boolean isInteger() { synchronized (L) { push(); boolean bool = L.isInteger(-1); L.pop(1); return bool; } } public boolean isString() { synchronized (L) { push(); boolean bool = L.isString(-1); L.pop(1); return bool; } } public boolean isFunction() { synchronized (L) { push(); boolean bool = L.isFunction(-1); L.pop(1); return bool; } } public boolean isJavaObject() { synchronized (L) { push(); boolean bool = L.isObject(-1); L.pop(1); return bool; } } public boolean isJavaFunction() { synchronized (L) { push(); boolean bool = L.isJavaFunction(-1); L.pop(1); return bool; } } public boolean isTable() { synchronized (L) { push(); boolean bool = L.isTable(-1); L.pop(1); return bool; } } public boolean isUserdata() { synchronized (L) { push(); boolean bool = L.isUserdata(-1); L.pop(1); return bool; } } public int type() { synchronized (L) { push(); int type = L.type(-1); L.pop(1); return type; } } public boolean getBoolean() { synchronized (L) { push(); boolean bool = L.toBoolean(-1); L.pop(1); return bool; } } public double getNumber() { synchronized (L) { push(); double db = L.toNumber(-1); L.pop(1); return db; } } public long getInteger() { synchronized (L) { push(); long lg = L.toInteger(-1); L.pop(1); return lg; } } public String getString() { synchronized (L) { push(); String str = L.toString(-1); L.pop(1); return str; } } public LuaTable getTable() { synchronized (L) { push(); LuaTable td = new LuaTable(L, -1); L.pop(1); return td; } } public LuaFunction getFunction() { synchronized (L) { push(); LuaFunction ft = new LuaFunction(L, -1); L.pop(1); return ft; } } public Object getObject() throws LuaException { synchronized (L) { push(); Object obj = L.getObjectFromUserdata(-1); L.pop(1); return obj; } } /** * If this is a table or userdata tries to set * a field value. */ public LuaObject getField(String field) throws LuaException { return L.getLuaObject(this, field); } public void setField(String field, Object obj) { push(); try { L.pushObjectValue(obj); } catch (LuaException e) { L.pushNil(); } L.setField(-2, field); L.pop(1); } public LuaObject getI(long idx) throws LuaException { return L.getLuaObject(this, idx); } public void setI(long idx, Object obj) { push(); try { L.pushObjectValue(obj); } catch (LuaException e) { L.pushNil(); } L.setI(-2, idx); L.pop(1); } /** * Calls the object represented by this using Lua function pcall. * * @param args - * Call arguments * @param nres - * Number of objects returned * @return Object[] - Returned Objects * @throws LuaException */ public Object[] callx(Object[] args, int nres) throws LuaException { synchronized (L) { if (!isFunction() && !isTable() && !isUserdata()) throw new LuaException("Invalid object. Not a function, table or userdata ."); int top = L.getTop(); push(); int nargs; if (args != null) { nargs = args.length; for (int i = 0; i < nargs; i++) { Object obj = args[i]; L.pushObjectValue(obj); } } else nargs = 0; int err = L.pcall(nargs, nres, 0); if (err != 0) { String str; if (L.isString(-1)) { str = L.toString(-1); L.pop(1); } else str = ""; if (err == LuaState.LUA_ERRRUN) { str = "Runtime error. " + str; } else if (err == LuaState.LUA_ERRMEM) { str = "Memory allocation error. " + str; } else if (err == LuaState.LUA_ERRERR) { str = "Error while running the error handler function. " + str; } else { str = "Lua Error code " + err + ". " + str; } throw new LuaException(str); } if (nres == LuaState.LUA_MULTRET) nres = L.getTop() - top; if (L.getTop() - top < nres) { throw new LuaException("Invalid Number of Results ."); } Object[] res = new Object[nres]; for (int i = nres; i > 0; i--) { res[i - 1] = L.toJavaObject(-1); L.pop(1); } return res; } } /** * Calls the object represented by this using Lua function pcall. Returns 1 object * * @param args - * Call arguments * @return Object - Returned Object * @throws LuaException */ public Object call(Object... args) throws LuaException { return callx(args, 1)[0]; } public byte[] dump() throws LuaException { synchronized (L) { if (!isFunction()) throw new LuaException("Invalid object. Not a function ."); push(); byte[] buf = L.dump(-1); L.pop(1); return buf; } } public Object[] asArray() throws IllegalArgumentException, ArrayIndexOutOfBoundsException, LuaException { synchronized (L) { if (!isTable()) throw new LuaException("Invalid object. Not a table ."); push(); int n = L.objLen(-1); Object array = Array.newInstance(Object.class, n); for (int i = 1; i <= n; i++) { L.pushInteger(i); L.getTable(-2); try { Array.set(array, i - 1, L.toJavaObject(-1)); } catch (LuaException e) { } L.pop(1); } L.pop(1); return (Object[]) array; } } public Map asMap(LuaState L, Class clazz, int idx) throws LuaException { // TODO: Implement this method synchronized (L) { if (!isTable()) throw new LuaException("Invalid object. Not a table ."); HashMap map = new HashMap(); push(); L.pushNil(); while (L.next(idx) != 0) { try { map.put(L.toJavaObject(-2), L.toJavaObject(-1)); } catch (LuaException e) { } L.pop(1); } L.pop(1); return map; } } public String toString() { synchronized (L) { try { if (isNil()) return "nil"; else if (isBoolean()) return String.valueOf(getBoolean()); else if (isNumber()) return String.valueOf(getNumber()); else if (isString()) return getString(); else if (isFunction()) return "Lua Function"; else if (isJavaObject()) return getObject().toString(); else if (isUserdata()) return "Userdata"; else if (isTable()) return "Lua Table"; else if (isJavaFunction()) return "Java Function"; else return null; } catch (LuaException e) { return null; } } } /** * Function that creates a java proxy to the object represented by this * * @param implem Interfaces that are implemented, separated by , */ public Object createProxy(String implem) throws ClassNotFoundException, LuaException { synchronized (L) { if (!isTable()) throw new LuaException("Invalid Object. Must be Table."); StringTokenizer st = new StringTokenizer(implem, ","); Class[] interfaces = new Class[st.countTokens()]; for (int i = 0; st.hasMoreTokens(); i++) interfaces[i] = Class.forName(st.nextToken()); InvocationHandler handler = new LuaInvocationHandler(this); return Proxy.newProxyInstance(this.getClass().getClassLoader(), interfaces, handler); } } public Object createProxy(Class implem) throws LuaException { synchronized (L) { if (!isTable()) throw new LuaException("Invalid Object. Must be Table."); Class[] interfaces = new Class[]{implem}; InvocationHandler handler = new LuaInvocationHandler(this); return Proxy.newProxyInstance(implem.getClassLoader(), interfaces, handler); } } } ================================================ FILE: hydrogen-library/src/main/java/com/luajava/LuaStack.java ================================================ package com.luajava; import java.util.HashMap; public class LuaStack { private final static HashMap luaStack = new HashMap(); public static void put(String name, LuaState L) { luaStack.put(name, L); } public static LuaState get(String name) { return luaStack.get(name); } public static Object call(String name, String func, Object[] arg) throws LuaException { return new LuaFunction(get(name), func).call(arg); } } ================================================ FILE: hydrogen-library/src/main/java/com/luajava/LuaState.java ================================================ package com.luajava; import androlua.LuaContext; public class LuaState { public static final int LUAI_MAXSTACK = 1000000; public static final int LUA_ERRERR = 6; public static final int LUA_ERRGCMM = 5; public static final int LUA_ERRMEM = 4; public static final int LUA_ERRRUN = 2; public static final int LUA_ERRSYNTAX = 3; public static final int LUA_GCCOLLECT = 2; public static final int LUA_GCCOUNT = 3; public static final int LUA_GCCOUNTB = 4; public static final int LUA_GCRESTART = 1; public static final int LUA_GCSETPAUSE = 6; public static final int LUA_GCSETSTEPMUL = 7; public static final int LUA_GCSTEP = 5; public static final int LUA_GCSTOP = 0; public static final int LUA_MULTRET = -1; public static final int LUA_OPEQ = 0; public static final int LUA_OPLE = 2; public static final int LUA_OPLT = 1; public static final int LUA_REGISTRYINDEX = -1001000; public static final int LUA_RIDX_GLOBALS = 2; public static final int LUA_RIDX_LAST = 2; public static final int LUA_RIDX_MAINTHREAD = 1; public static final int LUA_TBOOLEAN = 1; public static final int LUA_TFUNCTION = 6; public static final int LUA_TLIGHTUSERDATA = 2; public static final int LUA_TNIL = 0; public static final int LUA_TNONE = -1; public static final int LUA_TNUMBER = 3; public static final int LUA_TSTRING = 4; public static final int LUA_TTABLE = 5; public static final int LUA_TTHREAD = 8; public static final int LUA_TUSERDATA = 7; public static final int LUA_YIELD = 1; private static final String LUAJAVA_LIB = "luajava"; private static Class Byte_class = Byte.class; private static Class Double_class = Double.class; private static Class Float_class = Float.class; private static Class Integer_class = Integer.class; private static Class Long_class = Long.class; private static Class Number_class = Number.class; private static Class Short_class = Short.class; static { System.loadLibrary(LUAJAVA_LIB); } private long luaState; private LuaContext mContext; protected LuaState() { this.luaState = _newstate(); } protected LuaState(long luaState) { this.luaState = luaState; LuaStateFactory.insertLuaState(this); } public static Number convertLuaNumber(Double db, Class retType) { if (retType.isPrimitive()) { if (retType == Integer.TYPE) { return Integer.valueOf(db.intValue()); } if (retType == Long.TYPE) { return Long.valueOf(db.longValue()); } if (retType == Float.TYPE) { return Float.valueOf(db.floatValue()); } if (retType == Double.TYPE) { return Double.valueOf(db.doubleValue()); } if (retType == Byte.TYPE) { return Byte.valueOf(db.byteValue()); } if (retType == Short.TYPE) { return Short.valueOf(db.shortValue()); } } else if (retType.isAssignableFrom(Number_class)) { if (retType.isAssignableFrom(Integer_class)) { return new Integer(db.intValue()); } if (retType.isAssignableFrom(Long_class)) { return new Long(db.longValue()); } if (retType.isAssignableFrom(Float_class)) { return new Float(db.floatValue()); } if (retType.isAssignableFrom(Double_class)) { return db; } if (retType.isAssignableFrom(Byte_class)) { return new Byte(db.byteValue()); } if (retType.isAssignableFrom(Short_class)) { return new Short(db.shortValue()); } } return null; } public static Number convertLuaNumber(Long lg, Class retType) { if (retType.isPrimitive()) { if (retType == Integer.TYPE) { return Integer.valueOf(lg.intValue()); } if (retType == Long.TYPE) { return Long.valueOf(lg.longValue()); } if (retType == Float.TYPE) { return Float.valueOf(lg.floatValue()); } if (retType == Double.TYPE) { return Double.valueOf(lg.doubleValue()); } if (retType == Byte.TYPE) { return Byte.valueOf(lg.byteValue()); } if (retType == Short.TYPE) { return Short.valueOf(lg.shortValue()); } } else if (retType.isAssignableFrom(Number_class)) { if (retType.isAssignableFrom(Integer_class)) { return new Integer(lg.intValue()); } if (retType.isAssignableFrom(Long_class)) { return new Long(lg.longValue()); } if (retType.isAssignableFrom(Float_class)) { return new Float(lg.floatValue()); } if (retType.isAssignableFrom(Double_class)) { return lg; } if (retType.isAssignableFrom(Byte_class)) { return new Byte(lg.byteValue()); } if (retType.isAssignableFrom(Short_class)) { return new Short(lg.shortValue()); } } return null; } private native synchronized int _LargError(long j, int i, String str); private native synchronized int _LcallMeta(long j, int i, String str); private native synchronized void _LcheckAny(long j, int i); private native synchronized int _LcheckInteger(long j, int i); private native synchronized double _LcheckNumber(long j, int i); private native synchronized void _LcheckStack(long j, int i, String str); private native synchronized String _LcheckString(long j, int i); private native synchronized void _LcheckType(long j, int i, int i2); private native synchronized int _LdoFile(long j, String str); private native synchronized int _LdoString(long j, String str); private native synchronized int _LgetMetaField(long j, int i, String str); private native synchronized void _LgetMetatable(long j, String str); private native synchronized String _Lgsub(long j, String str, String str2, String str3); private native synchronized int _LloadBuffer(long j, byte[] bArr, long j2, String str); private native synchronized int _LloadFile(long j, String str); private native synchronized int _LloadString(long j, String str); private native synchronized int _LnewMetatable(long j, String str); private native synchronized int _LoptInteger(long j, int i, int i2); private native synchronized double _LoptNumber(long j, int i, double d); private native synchronized String _LoptString(long j, int i, String str); private native synchronized int _Lref(long j, int i); private native synchronized void _LunRef(long j, int i, int i2); private native synchronized void _Lwhere(long j, int i); private native synchronized void _call(long j, int i, int i2); private native synchronized int _checkStack(long j, int i); private native synchronized void _close(long j); private native synchronized int _compare(long j, int i, int i2, int i3); private native synchronized void _concat(long j, int i); private native synchronized void _copy(long j, int i, int i2); private native synchronized void _createTable(long j, int i, int i2); private native synchronized byte[] _dump(long j, int i); private native synchronized int _equal(long j, int i, int i2); private native synchronized int _error(long j); private native synchronized int _gc(long j, int i, int i2); private native synchronized int _getField(long j, int i, String str); private native synchronized int _getGlobal(long j, String str); private native synchronized int _getI(long j, int i, long j2); private native synchronized int _getMetaTable(long j, int i); private native synchronized Object _getObjectFromUserdata(long j, int i) throws LuaException; private native synchronized int _getTable(long j, int i); private native synchronized int _getTop(long j); private native synchronized String _getUpValue(long j, int i, int i2); private native synchronized int _getUserValue(long j, int i); private native synchronized void _insert(long j, int i); private native synchronized int _isBoolean(long j, int i); private native synchronized int _isCFunction(long j, int i); private native synchronized int _isFunction(long j, int i); private native synchronized int _isInteger(long j, int i); private native synchronized boolean _isJavaFunction(long j, int i); private native synchronized int _isNil(long j, int i); private native synchronized int _isNone(long j, int i); private native synchronized int _isNoneOrNil(long j, int i); private native synchronized int _isNumber(long j, int i); private native synchronized boolean _isObject(long j, int i); private native synchronized int _isString(long j, int i); private native synchronized int _isTable(long j, int i); private native synchronized int _isThread(long j, int i); private native synchronized int _isUserdata(long j, int i); private native synchronized int _isYieldable(long j); private native synchronized int _lessThan(long j, int i, int i2); private native synchronized void _newTable(long j); private native synchronized long _newstate(); private native synchronized long _newthread(long j); private native synchronized int _next(long j, int i); private native synchronized int _objlen(long j, int i); private native synchronized void _openBase(long j); private native synchronized void _openDebug(long j); private native synchronized void _openIo(long j); private native synchronized void _openLibs(long j); private native synchronized void _openLuajava(long j); private native synchronized void _openMath(long j); private native synchronized void _openOs(long j); private native synchronized void _openPackage(long j); private native synchronized void _openString(long j); private native synchronized void _openTable(long j); private native synchronized int _pcall(long j, int i, int i2, int i3); private native synchronized void _pop(long j, int i); private native synchronized void _pushBoolean(long j, int i); private native synchronized void _pushGlobalTable(long j); private native synchronized void _pushInteger(long j, long j2); private native synchronized void _pushJavaFunction(long j, JavaFunction javaFunction) throws LuaException; private native synchronized void _pushJavaObject(long j, Object obj); private native synchronized void _pushNil(long j); private native synchronized void _pushNumber(long j, double d); private native synchronized void _pushString(long j, String str); private native synchronized void _pushString(long j, byte[] bArr, int i); private native synchronized void _pushValue(long j, int i); private native synchronized int _rawGet(long j, int i); private native synchronized int _rawGetI(long j, int i, long j2); private native synchronized void _rawSet(long j, int i); private native synchronized void _rawSetI(long j, int i, long j2); private native synchronized int _rawequal(long j, int i, int i2); private native synchronized int _rawlen(long j, int i); private native synchronized void _remove(long j, int i); private native synchronized void _replace(long j, int i); private native synchronized int _resume(long j, long j2, int i); private native synchronized void _rotate(long j, int i, int i2); private native synchronized void _setField(long j, int i, String str); private native synchronized void _setGlobal(long j, String str); private native synchronized void _setI(long j, int i, long j2); private native synchronized int _setMetaTable(long j, int i); private native synchronized void _setTable(long j, int i); private native synchronized void _setTop(long j, int i); private native synchronized String _setUpValue(long j, int i, int i2); private native synchronized void _setUserValue(long j, int i); private native synchronized int _status(long j); private native synchronized int _strlen(long j, int i); private native synchronized int _toBoolean(long j, int i); private native synchronized long _toInteger(long j, int i); private native synchronized double _toNumber(long j, int i); private native synchronized String _toString(long j, int i); private native synchronized long _toThread(long j, int i); private native synchronized int _type(long j, int i); private native synchronized String _typeName(long j, int i); private native synchronized void _xmove(long j, long j2, int i); private native synchronized int _yield(long j, int i); public synchronized void close() { LuaStateFactory.removeLuaState(this.luaState); _close(this.luaState); this.luaState = 0; } public synchronized boolean isClosed() { return this.luaState == 0; } public long getPointer() { return this.luaState; } public void pushContext(LuaContext context) { this.mContext = context; } public LuaContext getContext() { return this.mContext; } public LuaState newThread() { LuaState l = new LuaState(_newthread(this.luaState)); LuaStateFactory.insertLuaState(l); return l; } public int getTop() { return _getTop(this.luaState); } public void setTop(int idx) { _setTop(this.luaState, idx); } public void pushValue(int idx) { _pushValue(this.luaState, idx); } public void rotate(int idx, int n) { _rotate(this.luaState, idx, n); } public void copy(int fromidx, int toidx) { _copy(this.luaState, fromidx, toidx); } public void remove(int idx) { _remove(this.luaState, idx); } public void insert(int idx) { _insert(this.luaState, idx); } public void replace(int idx) { _replace(this.luaState, idx); } public int checkStack(int sz) { return _checkStack(this.luaState, sz); } public void xmove(LuaState to, int n) { _xmove(this.luaState, to.luaState, n); } public boolean isNumber(int idx) { return _isNumber(this.luaState, idx) != 0; } public boolean isInteger(int idx) { return _isInteger(this.luaState, idx) != 0; } public boolean isString(int idx) { return _isString(this.luaState, idx) != 0; } public boolean isFunction(int idx) { return _isFunction(this.luaState, idx) != 0; } public boolean isCFunction(int idx) { return _isCFunction(this.luaState, idx) != 0; } public boolean isUserdata(int idx) { return _isUserdata(this.luaState, idx) != 0; } public boolean isTable(int idx) { return _isTable(this.luaState, idx) != 0; } public boolean isBoolean(int idx) { return _isBoolean(this.luaState, idx) != 0; } public boolean isNil(int idx) { return _isNil(this.luaState, idx) != 0; } public boolean isThread(int idx) { return _isThread(this.luaState, idx) != 0; } public boolean isNone(int idx) { return _isNone(this.luaState, idx) != 0; } public boolean isNoneOrNil(int idx) { return _isNoneOrNil(this.luaState, idx) != 0; } public int type(int idx) { return _type(this.luaState, idx); } public String typeName(int tp) { return _typeName(this.luaState, tp); } public int equal(int idx1, int idx2) { return _equal(this.luaState, idx1, idx2); } public int compare(int idx1, int idx2, int op) { return _compare(this.luaState, idx1, idx2, op); } public int rawequal(int idx1, int idx2) { return _rawequal(this.luaState, idx1, idx2); } public int lessThan(int idx1, int idx2) { return _lessThan(this.luaState, idx1, idx2); } public double toNumber(int idx) { return _toNumber(this.luaState, idx); } public long toInteger(int idx) { return _toInteger(this.luaState, idx); } public boolean toBoolean(int idx) { return _toBoolean(this.luaState, idx) != 0; } public String toString(int idx) { return _toString(this.luaState, idx); } public int strLen(int idx) { return _strlen(this.luaState, idx); } public int objLen(int idx) { return _objlen(this.luaState, idx); } public int rawLen(int idx) { return _rawlen(this.luaState, idx); } public LuaState toThread(int idx) { return new LuaState(_toThread(this.luaState, idx)); } public void pushNil() { _pushNil(this.luaState); } public void pushNumber(double db) { _pushNumber(this.luaState, db); } public void pushInteger(long integer) { _pushInteger(this.luaState, integer); } public void pushString(String str) { if (str == null) { _pushNil(this.luaState); } else { _pushString(this.luaState, str); } } public void pushString(byte[] bytes) { if (bytes == null) { _pushNil(this.luaState); } else { _pushString(this.luaState, bytes, bytes.length); } } public void pushBoolean(boolean bool) { _pushBoolean(this.luaState, bool ? 1 : 0); } public int getTable(int idx) { return _getTable(this.luaState, idx); } public int getField(int idx, String k) { return _getField(this.luaState, idx, k); } public int getI(int idx, long n) { return _getI(this.luaState, idx, n); } public int rawGet(int idx) { return _rawGet(this.luaState, idx); } public int rawGetI(int idx, long n) { return _rawGetI(this.luaState, idx, n); } public void createTable(int narr, int nrec) { _createTable(this.luaState, narr, nrec); } public void newTable() { _newTable(this.luaState); } public int getMetaTable(int idx) { return _getMetaTable(this.luaState, idx); } public int getUserValue(int idx) { return _getUserValue(this.luaState, idx); } public void setTable(int idx) { _setTable(this.luaState, idx); } public void setField(int idx, String k) { _setField(this.luaState, idx, k); } public void setI(int idx, long n) { _setI(this.luaState, idx, n); } public void rawSet(int idx) { _rawSet(this.luaState, idx); } public void rawSetI(int idx, long n) { _rawSetI(this.luaState, idx, n); } public int setMetaTable(int idx) { return _setMetaTable(this.luaState, idx); } public void setUserValue(int idx) { _setUserValue(this.luaState, idx); } public void call(int nArgs, int nResults) { _call(this.luaState, nArgs, nResults); } public int pcall(int nArgs, int nResults, int errFunc) { return _pcall(this.luaState, nArgs, nResults, errFunc); } public int yield(int nResults) { return _yield(this.luaState, nResults); } public int resume(LuaState from, int nArgs) { return _resume(this.luaState, from.getPointer(), nArgs); } public int status() { return _status(this.luaState); } public int isYieldable() { return _isYieldable(this.luaState); } public int gc(int what, int data) { return _gc(this.luaState, what, data); } public int next(int idx) { return _next(this.luaState, idx); } public int error() { return _error(this.luaState); } public void concat(int n) { _concat(this.luaState, n); } public int LdoFile(String fileName) { return _LdoFile(this.luaState, fileName); } public int LdoString(String str) { return _LdoString(this.luaState, str); } public int LgetMetaField(int obj, String e) { return _LgetMetaField(this.luaState, obj, e); } public int LcallMeta(int obj, String e) { return _LcallMeta(this.luaState, obj, e); } public int LargError(int numArg, String extraMsg) { return _LargError(this.luaState, numArg, extraMsg); } public String LcheckString(int numArg) { return _LcheckString(this.luaState, numArg); } public String LoptString(int numArg, String def) { return _LoptString(this.luaState, numArg, def); } public double LcheckNumber(int numArg) { return _LcheckNumber(this.luaState, numArg); } public double LoptNumber(int numArg, double def) { return _LoptNumber(this.luaState, numArg, def); } public int LcheckInteger(int numArg) { return _LcheckInteger(this.luaState, numArg); } public int LoptInteger(int numArg, int def) { return _LoptInteger(this.luaState, numArg, def); } public void LcheckStack(int sz, String msg) { _LcheckStack(this.luaState, sz, msg); } public void LcheckType(int nArg, int t) { _LcheckType(this.luaState, nArg, t); } public void LcheckAny(int nArg) { _LcheckAny(this.luaState, nArg); } public int LnewMetatable(String tName) { return _LnewMetatable(this.luaState, tName); } public void LgetMetatable(String tName) { _LgetMetatable(this.luaState, tName); } public void Lwhere(int lvl) { _Lwhere(this.luaState, lvl); } public int Lref(int t) { return _Lref(this.luaState, t); } public void LunRef(int t, int ref) { _LunRef(this.luaState, t, ref); } public int LloadFile(String fileName) { return _LloadFile(this.luaState, fileName); } public int LloadString(String s) { return _LloadString(this.luaState, s); } public int LloadBuffer(byte[] buff, String name) { return _LloadBuffer(this.luaState, buff, (long) buff.length, name); } public String Lgsub(String s, String p, String r) { return _Lgsub(this.luaState, s, p, r); } public String getUpValue(int funcindex, int n) { return _getUpValue(this.luaState, funcindex, n); } public String setUpValue(int funcindex, int n) { return _setUpValue(this.luaState, funcindex, n); } public byte[] dump(int funcindex) { return _dump(this.luaState, funcindex); } public void pop(int n) { _pop(this.luaState, n); } public synchronized void pushGlobalTable() { _pushGlobalTable(this.luaState); } public synchronized int getGlobal(String global) { return _getGlobal(this.luaState, global); } public synchronized void setGlobal(String name) { _setGlobal(this.luaState, name); } public void openBase() { _openBase(this.luaState); } public void openTable() { _openTable(this.luaState); } public void openIo() { _openIo(this.luaState); } public void openOs() { _openOs(this.luaState); } public void openString() { _openString(this.luaState); } public void openMath() { _openMath(this.luaState); } public void openDebug() { _openDebug(this.luaState); } public void openPackage() { _openPackage(this.luaState); } public void openLibs() { _openLibs(this.luaState); _openLuajava(this.luaState); pushPrimitive(); } public void openLuajava() { _openLuajava(this.luaState); pushPrimitive(); } public Object getObjectFromUserdata(int idx) throws LuaException { return _getObjectFromUserdata(this.luaState, idx); } public boolean isObject(int idx) { return _isObject(this.luaState, idx); } public void pushJavaObject(Object obj) { _pushJavaObject(this.luaState, obj); } public void pushJavaFunction(JavaFunction func) throws LuaException { _pushJavaFunction(this.luaState, func); } public boolean isJavaFunction(int idx) { return _isJavaFunction(this.luaState, idx); } public void pushObjectValue(Object obj) throws LuaException { if (obj == null) { pushNil(); } else if (obj instanceof Boolean) { pushBoolean(((Boolean) obj).booleanValue()); } else if (obj instanceof Long) { pushInteger(((Long) obj).longValue()); } else if (obj instanceof Integer) { pushInteger((long) ((Integer) obj).intValue()); } else if (obj instanceof Short) { pushInteger((long) ((Short) obj).shortValue()); } else if (obj instanceof Character) { pushInteger((long) ((Character) obj).charValue()); } else if (obj instanceof Byte) { pushInteger((long) ((Byte) obj).byteValue()); } else if (obj instanceof Float) { pushNumber((double) ((Float) obj).floatValue()); } else if (obj instanceof Double) { pushNumber(((Double) obj).doubleValue()); } else if (obj instanceof String) { pushString((String) obj); } else if (obj instanceof JavaFunction) { pushJavaFunction((JavaFunction) obj); } else if (obj instanceof LuaObject) { LuaObject ref = (LuaObject) obj; if (ref.getLuaState() == this) { ref.push(); } else { pushJavaObject(ref); } } else { pushJavaObject(obj); } } public synchronized Object toJavaObject(int idx) throws LuaException { Object obj; obj = null; if (isBoolean(idx)) { obj = Boolean.valueOf(toBoolean(idx)); } else if (type(idx) == 4) { obj = toString(idx); } else if (isFunction(idx)) { obj = getLuaObject(idx); } else if (isTable(idx)) { obj = getLuaObject(idx); } else if (type(idx) == 3) { if (isInteger(idx)) { obj = Long.valueOf(toInteger(idx)); } else { obj = Double.valueOf(toNumber(idx)); } } else if (isUserdata(idx)) { if (isObject(idx)) { obj = getObjectFromUserdata(idx); } else { obj = getLuaObject(idx); } } else if (isNil(idx)) { obj = null; } return obj; } public LuaObject getLuaObject(String globalName) { pushGlobalTable(); pushString(globalName); rawGet(-2); LuaObject obj = getLuaObject(-1); pop(2); return obj; } public LuaObject getLuaObject(LuaObject parent, String name) throws LuaException { return new LuaObject(parent, name); } public LuaObject getLuaObject(LuaObject parent, Number name) throws LuaException { return new LuaObject(parent, name); } public LuaObject getLuaObject(LuaObject parent, LuaObject name) throws LuaException { if (parent.getLuaState().getPointer() == this.luaState && parent.getLuaState().getPointer() == name.getLuaState().getPointer()) { return new LuaObject(parent, name); } throw new LuaException("Object must have the same LuaState as the parent!"); } public LuaObject getLuaObject(int index) { if (isFunction(index)) { return new LuaFunction(this, index); } if (isTable(index)) { return new LuaTable(this, index); } return new LuaObject(this, index); } public void pushPrimitive() { pushJavaObject(Boolean.TYPE); setGlobal("boolean"); pushJavaObject(Byte.TYPE); setGlobal("byte"); pushJavaObject(Character.TYPE); setGlobal("char"); pushJavaObject(Short.TYPE); setGlobal("short"); pushJavaObject(Integer.TYPE); setGlobal("int"); pushJavaObject(Long.TYPE); setGlobal("long"); pushJavaObject(Float.TYPE); setGlobal("float"); pushJavaObject(Double.TYPE); setGlobal("double"); } } ================================================ FILE: hydrogen-library/src/main/java/com/luajava/LuaStateFactory.java ================================================ /* * $Id: LuaStateFactory.java,v 1.4 2006/12/22 14:06:40 thiago Exp $ * Copyright (C) 2003-2007 Kepler Project. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package com.luajava; import java.util.HashMap; import java.util.Map; /** * This class is responsible for instantiating new LuaStates. * When a new LuaState is instantiated it is put into a List * and an index is returned. This index is registred in Lua * and it is used to find the right LuaState when lua calls * a Java Function. * * @author Thiago Ponte */ public final class LuaStateFactory { /** * Array with all luaState's instances */ private static final Map states = new HashMap(); /** * Non-public constructor. */ private LuaStateFactory() { } /** * Method that creates a new instance of LuaState * * @return LuaState */ public synchronized static LuaState newLuaState() { LuaState L = new LuaState(); states.put(L.getPointer(), L); return L; } /** * Returns a existing instance of LuaState * * @param index * @return LuaState */ public synchronized static LuaState getExistingState(long index) { LuaState l = states.get(index); if (l == null) { l = new LuaState(index); states.put(index, l); } return l; } /** * Receives a existing LuaState and checks if it exists in the states list. * If it doesn't exist adds it to the list. * * @param L * @return int */ public synchronized static long insertLuaState(LuaState L) { states.put(L.getPointer(), L); return L.getPointer(); } /** * removes the luaState from the states list * * @param idx */ public synchronized static void removeLuaState(long idx) { states.put(idx, null); } } ================================================ FILE: hydrogen-library/src/main/java/com/luajava/LuaTable.java ================================================ package com.luajava; import java.util.Collection; import java.util.HashSet; import java.util.Map; import java.util.Set; public class LuaTable extends LuaObject implements Map { protected LuaTable(LuaState L, String globalName) { super(L, globalName); } protected LuaTable(LuaState L, int index) { super(L, index); } public LuaTable(LuaState L) { super(L); L.newTable(); registerValue(-1); } @Override public void clear() { push(); L.pushNil(); while (L.next(-2) != 0) { L.pop(1); L.pushValue(-1); L.pushNil(); L.setTable(-4); } L.pop(1); } @Override public boolean containsKey(Object key) { boolean b = false; push(); try { L.pushObjectValue(key); b = L.getTable(-2) == LuaState.LUA_TNIL; L.pop(1); } catch (LuaException e) { return false; } L.pop(1); return b; } @Override public boolean containsValue(Object value) { return false; } @Override public Set> entrySet() { HashSet> sets = new HashSet>(); push(); L.pushNil(); while (L.next(-2) != 0) { try { sets.add(new LuaEntry((K) L.toJavaObject(-2), (V) L.toJavaObject(-1))); } catch (LuaException e) { } L.pop(1); } L.pop(1); return sets; } @Override public V get(Object key) { push(); V obj = null; try { L.pushObjectValue(key); L.getTable(-2); obj = (V) L.toJavaObject(-1); L.pop(1); } catch (LuaException e) { } L.pop(1); return obj; } @Override public boolean isEmpty() { push(); L.pushNil(); boolean b = L.next(-2) == 0; if (b) L.pop(1); else L.pop(3); return b; } @Override public Set keySet() { HashSet sets = new HashSet(); push(); L.pushNil(); while (L.next(-2) != 0) { try { sets.add((K) L.toJavaObject(-2)); } catch (LuaException e) { } L.pop(1); } L.pop(1); return sets; } @Override public V put(K key, V value) { push(); try { L.pushObjectValue(key); L.pushObjectValue(value); L.setTable(-3); } catch (LuaException e) { } L.pop(1); return null; } @Override public void putAll(Map p1) { } @Override public V remove(Object key) { push(); try { L.pushObjectValue(key); L.setTable(-2); } catch (LuaException e) { } L.pop(1); return null; } public boolean isList() { push(); int len = L.rawLen(-1); if (len != 0) { pop(); return true; } L.pushNil(); boolean b = L.next(-2) == 0; if (b) L.pop(1); else L.pop(3); return b; } public int length() { push(); int len = L.rawLen(-1); pop(); return len; } @Override public int size() { int n = 0; push(); L.pushNil(); while (L.next(-2) != 0) { n++; L.pop(1); } L.pop(1); return n; } @Override public Collection values() { return null; } public static class LuaEntry implements Entry { private K mKey; private V mValue; public LuaEntry(K k, V v) { mKey = k; mValue = v; } @Override public K getKey() { return mKey; } @Override public V getValue() { return mValue; } public V setValue(V value) { V old = mValue; mValue = value; return old; } } } ================================================ FILE: hydrogen-library/src/main/res/drawable/bg_circle.xml ================================================ ================================================ FILE: hydrogen-library/src/main/res/drawable/bg_rect_radius.xml ================================================ ================================================ FILE: hydrogen-library/src/main/res/drawable/ic_loading.xml ================================================ ================================================ FILE: hydrogen-library/src/main/res/drawable/layout_selector_tran.xml ================================================ ================================================ FILE: hydrogen-library/src/main/res/drawable/loading_shap.xml ================================================ ================================================ FILE: hydrogen-library/src/main/res/drawable/shadow_line_bottom.xml ================================================ ================================================ FILE: hydrogen-library/src/main/res/drawable/shadow_line_top.xml ================================================ ================================================ FILE: hydrogen-library/src/main/res/layout/activity_lua.xml ================================================ ================================================ FILE: hydrogen-library/src/main/res/layout/activity_picture.xml ================================================ ================================================ FILE: hydrogen-library/src/main/res/layout/activity_video.xml ================================================ ================================================ FILE: hydrogen-library/src/main/res/layout/activity_webview.xml ================================================ ================================================ FILE: hydrogen-library/src/main/res/layout/dialog_add_shortcut.xml ================================================ ================================================ FILE: hydrogen-library/src/main/res/layout/dialog_input.xml ================================================ ================================================ FILE: hydrogen-library/src/main/res/layout/fragment_menu.xml ================================================ ================================================ FILE: hydrogen-library/src/main/res/layout/item_pager_image.xml ================================================ ================================================ FILE: hydrogen-library/src/main/res/raw/keep.xml ================================================ ================================================ FILE: hydrogen-library/src/main/res/values/attrs_elastic_drag_dismiss_frame_layout.xml ================================================ ================================================ FILE: hydrogen-library/src/main/res/values/colors.xml ================================================ #3F51B5 #303F9F #FF4081 ================================================ FILE: hydrogen-library/src/main/res/values/strings.xml ================================================ LuaJAndroid ================================================ FILE: hydrogen-library/src/main/res/values/styles.xml ================================================ ================================================ FILE: hydrogen-library/src/test/java/pub/hanks/luajandroid/ExampleUnitTest.java ================================================ package pub.hanks.luajandroid; import org.junit.Test; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; import java.util.zip.Adler32; import java.util.zip.CheckedOutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import static org.junit.Assert.assertEquals; /** * Example local unit test, which will execute on the development machine (host). * * @see Testing documentation */ public class ExampleUnitTest { @Test public void testAdler32() throws Exception { String target = "ajavachecksum.zip"; FileOutputStream fos = new FileOutputStream(target); //使用Adler32算法创建CheckedOutputStream校验输出流 CheckedOutputStream checksum = new CheckedOutputStream(fos, new Adler32()); ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(checksum)); int size = 0; byte[] buffer = new byte[1024]; // // Get all text files on the working folder. //通过FilenameFilter取得所有txt文件 File dir = new File("."); String[] files = dir.list(new FilenameFilter() { public boolean accept(File dir, String name) { if (name.endsWith(".txt")) { return true; } else { return false; } } }); //压缩成ajavachecksum.zip for (int i = 0; i < files.length; i++) { System.out.println("压缩中...: " + files[i]); FileInputStream fis = new FileInputStream(files[i]); ZipEntry zipEntry = new ZipEntry(files[i]); zos.putNextEntry(zipEntry); while ((size = fis.read(buffer, 0, buffer.length)) > 0) { zos.write(buffer, 0, size); } zos.flush(); zos.closeEntry(); fis.close(); } zos.flush(); zos.close(); System.out.println(" 校验码 : " + checksum.getChecksum().getValue()); } @Test public void testStartWith(){ System.out.println("asdas".startsWith("[a-z]")); } } ================================================ FILE: hydrogen-library/src/test/lua/a/aa.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/12 -- Time: 15:07 -- local me = require "b.ba" print(me.name) ================================================ FILE: hydrogen-library/src/test/lua/a.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/12 -- Time: 15:06 -- local me = { name = "hanks" } require("c.json") print(json.decode('')) return me ================================================ FILE: hydrogen-library/src/test/lua/b/ba.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/12 -- Time: 15:07 -- return { name = "from be" } ================================================ FILE: hydrogen-library/src/test/lua/c/json.lua ================================================ module('json') function decode(text) return { name = 'hhhhha' } end ================================================ FILE: lua/163news/activity_news_detail.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaWebView" import "androlua.LuaHttp" local uihelper = require "uihelper" -- create view table local layout = { LinearLayout, layout_width = "fill", layout_height = "fill", orientation = "vertical", statusBarColor = "#ff3333", { LinearLayout, orientation = "horizontal", layout_width = "fill", layout_height = "56dp", background = "#ff3333", gravity = "center_vertical", { ImageView, id = "back", layout_width = "40dp", layout_height = "40dp", layout_marginLeft = "8dp", scaleType = "centerInside", src = "@drawable/ic_menu_back", }, { TextView, layout_height = "56dp", layout_width = "fill", paddingRight = "16dp", singleLine = true, textIsSelectable = true, ellipsize = "end", id = "tv_title", gravity = "center_vertical", paddingLeft = "8dp", textColor = "#ffffff", textSize = "16sp", }, }, { FrameLayout, layout_width = "fill", layout_height = "fill", { LuaWebView, id = "webview", layout_width = "fill", layout_height = "fill", }, { ProgressBar, layout_gravity = "center", id = "progressBar", layout_width = "40dp", layout_height = "40dp", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", }, { FrameLayout, layout_gravity = 85, layout_width = "100dp", layout_height = "100dp", id = "layout_content", } } } local css = [[ video{width:100%} article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}html{font-family:sans-serif;-webkit-text-size-adjust:100%}body{font-family:'Helvetica Neue',Helvetica,Arial,Sans-serif;background:#fff;padding-top:0;margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{margin:.67em 0}h1,h2,h3,h4,h5,h6{font-size:16px}abbr[title]{border-bottom:1px dotted}hr{box-sizing:content-box;height:0}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap}q{quotes:\201C\201D\2018\2019}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0;vertical-align:middle;color:transparent;font-size:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}table{border-collapse:collapse;border-spacing:0;overflow:hidden}a{text-decoration:none}blockquote{border-left:3px solid #d0e5f2;font-style:normal;display:block;vertical-align:baseline;font-size:100%;margin:.5em 0;padding:0 0 0 1em}ul,ol{padding-left:20px}.content{color:#444;line-height:1.6em;font-size:16px;margin:16px}.content img{max-width:100%;display:block;margin:30px auto}.content img+img{margin-top:15px}.content img[src*="zhihu.com/equation"]{display:inline-block;margin:0 3px}.content a{color:#259}.content a:hover{text-decoration:underline} ]] local htmlTemplate = [[

%s
]] local function getData(url) url = url:gsub('.html', '_0.html') LuaHttp.request({ url = url }, function(error, code, body) print(url) local content = string.match(body, '(.-)') local data = string.format(htmlTemplate, css, content) data = data:gsub('src="//', 'src="http://'):gsub('data[-]src="', 'src="') uihelper.runOnUiThread(activity, function() webview.loadData(data, "text/html; charset=UTF-8", nil) end) end) end function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) back.onClick = function() activity.finish() end local url = activity.getIntent().getStringExtra('url') tv_title.setText(url) webview.setVisibility(0) progressBar.setVisibility(8) getData(url) end function onDestroy() if webview then webview.getParent().removeView(webview) webview.destroy() webview = nil end end ================================================ FILE: lua/163news/fragment_news.lua ================================================ -- -- Created by IntelliJ IDEA. -- User: hanks -- Date: 2017/5/13 -- Time: 00:01 -- To change this template use File | Settings | File Templates. -- require "import" import "android.widget.*" import "android.content.*" import "androlua.LuaAdapter" import "androlua.LuaImageLoader" import "androlua.LuaFragment" import "androlua.LuaHttp" import "androlua.widget.webview.WebViewActivity" import "android.support.v4.widget.SwipeRefreshLayout" local uihelper = require "uihelper" local JSON = require "cjson" local log = require "log" local function getData(params, data, adapter, fragment, swipe_layout, reload) -- http://3g.163.com/touch/jsonp/sy/recommend/30-10.html?hasad=1&miss=25&refresh=A&offset=0&size=10&callback=syrec3 -- http://3g.163.com/touch/jsonp/sy/recommend/40-10.html?hasad=1&miss=25&refresh=A&offset=0&size=10&callback=syrec4 -- http://3g.163.com/touch/reconstruct/article/list/BBM54PGAwangning/10-10.html -- http://3g.163.com/touch/reconstruct/article/list/BBM54PGAwangning/20-10.html -- http://3g.163.com/touch/reconstruct/article/list/BCR1UC1Qwangning/0-10.html local url = string.format('http://3g.163.com/touch/reconstruct/article/list/%s/%d-10.html', params.rid, params.page * 10) print(url) LuaHttp.request({ url = url }, function(error, code, body) if error or code ~= 200 then return end body = body:sub(10, #body - 1) local arr = JSON.decode(body)[params.rid] uihelper.runOnUiThread(fragment.getActivity(), function() if reload then for k, _ in pairs(date) do data[k] = nil end end for i = 1, #arr do data[#data + 1] = arr[i] end params.page = params.page + 1 adapter.notifyDataSetChanged() swipe_layout.setRefreshing(false) end) end) end local function launchDetail(fragment, item) local activity = fragment.getActivity() if item == nil or item.url == nil then activity.toast('没有 url 可以打开') return end if not item.url:find('^http://') then WebViewActivity.start(activity, item.skipURL, 0xFFff3333) return else local activity = fragment.getActivity() local intent = Intent(activity, LuaActivity) intent.putExtra("luaPath", '163news/activity_news_detail.lua') intent.putExtra("url", item.url) activity.startActivity(intent) end end local function dateStr(d) if type(d) == 'string' then local Y, M, D, h, m, s = string.match(d, '(%d+)-(%d+)-(%d+) (%d+):(%d+):(%d+)') return dateStr(os.time({ day = D, month = M, year = Y, hour = h, min = m, sec = s })) end local now = os.time() local dx = now - d if dx < 600 then return '刚刚' elseif dx < 3600 then return math.floor(dx / 60) .. '分钟前' elseif dx < 3600 * 24 then return math.floor(dx / 3600) .. '小时前' else return os.date('%y-%m-%d', d) end end local function newInstance(rid) -- create view table local layout = { SwipeRefreshLayout, layout_width = "fill", layout_height = "fill", id = "swipe_layout", { ListView, id = "listview", layout_width = "fill", layout_height = "fill", } } local item_view = { RelativeLayout, layout_width = "fill", layout_height = "wrap", paddingLeft = "16dp", paddingRight = "12dp", paddingTop = "16dp", paddingBottom = "16dp", { ImageView, id = "iv_image", layout_width = "110dp", layout_height = "83dp", layout_marginRight = "12dp", scaleType = "centerCrop", }, { TextView, id = "tv_title", layout_toRightOf = "iv_image", layout_width = "fill", maxLines = "2", lineSpacingMultiplier = 1.3, textSize = "16sp", textColor = "#222222", }, { LinearLayout, id = "layout_imgs", layout_below = "tv_title", layout_marginTop = "8dp", layout_marginBottom = "8dp", layout_width = "match", }, { TextView, id = "tv_date", layout_below = "layout_imgs", layout_toRightOf = "iv_image", layout_alignParentBottom = true, layout_width = "fill", textSize = "12sp", textColor = "#aaaaaa", } } local singleImg = { ImageView, layout_width = (uihelper.getScreenWidth() - uihelper.dp2px(44)) / 3, layout_height = "83dp", layout_marginRight = "8dp", scaleType = "centerCrop", } local hadLoadData local isVisible local lastId local params = { rid = rid, page = 0 } local data = {} local ids = {} local adapter local fragment = LuaFragment.newInstance() local function lazyLoad() if not isVisible then return end if hadLoadData then return end if adapter == nil then return end hadLoadData = true getData(params, data, adapter, fragment, ids.swipe_layout) end fragment.setCreator(luajava.createProxy('androlua.LuaFragment$FragmentCreator', { onCreateView = function(inflater, container, savedInstanceState) return loadlayout(layout, ids) end, onViewCreated = function(view, savedInstanceState) adapter = LuaAdapter(luajava.createProxy("androlua.LuaAdapter$AdapterCreator", { getCount = function() return #data end, getView = function(position, convertView, parent) position = position + 1 -- lua 索引从 1开始 if convertView == nil then local views = {} -- store views convertView = loadlayout(item_view, views, ListView) convertView.getLayoutParams().width = parent.getWidth() convertView.setTag(views) end local views = convertView.getTag() local item = data[position] if item then if item.imgextra and #item.imgextra > 0 then views.iv_image.setVisibility(8) views.layout_imgs.setVisibility(0) views.layout_imgs.removeAllViews() item.imgextra[#item.imgextra + 1] = { imgsrc = item.imgsrc } local len = #item.imgextra if len > 3 then len = 3 end for i = 1, len do local img = loadlayout(singleImg, {}, LinearLayout) LuaImageLoader.load(img, item.imgextra[i].imgsrc) views.layout_imgs.addView(img) end else views.iv_image.setVisibility(0) views.layout_imgs.setVisibility(8) LuaImageLoader.load(views.iv_image, item.imgsrc) end views.tv_date.setText(string.format('%s %s', dateStr(item.ptime), item.source)) views.tv_title.setText(item.title) end if position == #data then getData(params, data, adapter, fragment, ids.swipe_layout) end return convertView end })) ids.listview.setAdapter(adapter) ids.listview.setOnItemClickListener(luajava.createProxy("android.widget.AdapterView$OnItemClickListener", { onItemClick = function(adapter, view, position, id) launchDetail(fragment, data[position + 1]) end, })) ids.swipe_layout.setRefreshing(true) ids.swipe_layout.setOnRefreshListener(luajava.createProxy('android.support.v4.widget.SwipeRefreshLayout$OnRefreshListener', { onRefresh = function() getData(params, data, adapter, fragment, ids.swipe_layout, true) end })) lazyLoad() end, onUserVisible = function(visible) isVisible = visible lazyLoad() end, })) return fragment end return { newInstance = newInstance } ================================================ FILE: lua/163news/info.json ================================================ { "id": "pub.hanks.163news", "name": "网易新闻", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b08438866d950f?w=150&h=150&f=png&s=3114", "main": "main.lua", "versionName": "1.0.9", "versionCode": 10, "desc": "网易新闻客户端" } ================================================ FILE: lua/163news/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "android.support.v4.view.ViewPager" import "android.support.design.widget.TabLayout" import "androlua.adapter.LuaFragmentPageAdapter" local uihelper = require "uihelper" local JSON = require "cjson" local log = require "log" local fragmentNews = require "163news/fragment_news" -- create view table local layout = { LinearLayout, layout_width = "fill", layout_height = "fill", orientation = "vertical", statusBarColor = "#FF3333", { TabLayout, id = "tab", layout_width = "fill", layout_height = "48dp", background = "#FF3333", }, { FrameLayout, layout_width = "fill", layout_height = "fill", { ViewPager, id = "viewPager", layout_width = "fill", layout_height = "fill", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", } } } local data = { titles = {}, fragments = {}, } table.insert(data.fragments, fragmentNews.newInstance('BA8J7DG9wangning')) table.insert(data.titles, '推荐') table.insert(data.fragments, fragmentNews.newInstance('BBM54PGAwangning')) table.insert(data.titles, '新闻') table.insert(data.fragments, fragmentNews.newInstance('BD29LPUBwangning')) table.insert(data.titles, '国内') table.insert(data.fragments, fragmentNews.newInstance('BD29MJTVwangning')) table.insert(data.titles, '国际') table.insert(data.fragments, fragmentNews.newInstance('BA8D4A3Rwangning')) table.insert(data.titles, '科技') table.insert(data.fragments, fragmentNews.newInstance('BAI6I0O5wangning')) table.insert(data.titles, '手机') table.insert(data.fragments, fragmentNews.newInstance('BAI67OGGwangning')) table.insert(data.titles, '军事') table.insert(data.fragments, fragmentNews.newInstance('BA8E6OEOwangning')) table.insert(data.titles, '体育') table.insert(data.fragments, fragmentNews.newInstance('BCR1UC1Qwangning')) table.insert(data.titles, '社会') table.insert(data.fragments, fragmentNews.newInstance('BA10TA81wangning')) table.insert(data.titles, '娱乐') table.insert(data.fragments, fragmentNews.newInstance('BA8FF5PRwangning')) table.insert(data.titles, '教育') table.insert(data.fragments, fragmentNews.newInstance('BAI6RHDKwangning')) table.insert(data.titles, '图片') local adapter = LuaFragmentPageAdapter(activity.getSupportFragmentManager(), luajava.createProxy("androlua.adapter.LuaFragmentPageAdapter$AdapterCreator", { getCount = function() return #data.fragments end, getItem = function(position) position = position + 1 return data.fragments[position] end, getPageTitle = function(position) position = position + 1 return data.titles[position] end })) local function unicode_to_utf8(convertStr) local t = {} for a in string.gmatch(convertStr, '\\u([0-9a-z][0-9a-z][0-9a-z][0-9a-z])') do if #a == 4 then local n = tonumber(a, 16) assert(n, "String decoding failed: bad Unicode escape " .. a) local x if n < 0x80 then x = string.char(n % 0x80) elseif n < 0x800 then -- [110x xxxx] [10xx xxxx] x = string.char(0xC0 + (math.floor(n / 64) % 0x20), 0x80 + (n % 0x40)) else -- [1110 xxxx] [10xx xxxx] [10xx xxxx] x = string.char(0xE0 + (math.floor(n / 4096) % 0x10), 0x80 + (math.floor(n / 64) % 0x40), 0x80 + (n % 0x40)) end convertStr = string.gsub(convertStr, '\\u' .. a, x) end end return convertStr end function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) viewPager.setAdapter(adapter) viewPager.setOffscreenPageLimit(#data.fragments) viewPager.setCurrentItem(0) tab.setSelectedTabIndicatorColor(0xffffffff) tab.setTabTextColors(0x88ffffff, 0xffffffff) tab.setTabMode(TabLayout.MODE_SCROLLABLE) tab.setTabGravity(TabLayout.GRAVITY_CENTER) tab.setupWithViewPager(viewPager) end ================================================ FILE: lua/500px/fragment_news.lua ================================================ -- -- Created by IntelliJ IDEA. -- User: hanks -- Date: 2017/5/13 -- Time: 00:01 -- To change this template use File | Settings | File Templates. -- require "import" import "android.widget.*" import "android.content.*" import "androlua.LuaAdapter" import "androlua.LuaImageLoader" import "androlua.LuaFragment" import "androlua.LuaHttp" import "androlua.widget.webview.WebViewActivity" import "android.support.v4.widget.SwipeRefreshLayout" import "android.graphics.drawable.GradientDrawable" import "android.os.Build" import "android.support.v7.widget.RecyclerView" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.LinearLayoutManager" import "androlua.widget.picture.PicturePreviewActivity" local uihelper = require "uihelper" local JSON = require "cjson" local log = require "log" local floor = math.floor local tonum = tonumber local imageWidth = uihelper.getScreenWidth() local function getData(params, data, adapter, fragment, swipe_layout, reload) local url = string.format('https://api.qingmang.me/v2/article.list?token=c400a7e21688496ca3e7f17c6b0d1846&category_id=%s', params.rid) if params.nextUrl then url = params.nextUrl end LuaHttp.request({ url = url }, function(error, code, body) if error or code ~= 200 then return end local json = JSON.decode(body) if json.hasMore and json.nextUrl then params.nextUrl = json.nextUrl end local arr = json.articles uihelper.runOnUiThread(fragment.getActivity(), function() if reload then for k, _ in ipairs(data) do data[k] = nil end end local s = #data for i = 1, #arr do local item = arr[i] if #item.images > 0 then data[#data + 1] = { imgUrl = item.images[1].url, calcHeight = floor(imageWidth * tonum(item.images[1].height) / tonum(item.images[1].width)) } end end adapter.notifyItemRangeChanged(s, #arr) swipe_layout.setRefreshing(false) end) end) end local function launchDetail(fragment, data, index) local args = { uris = {}, currentIndex = index } for i = 1, #data do args.uris[i] = data[i].imgUrl end PicturePreviewActivity.start(fragment.getActivity(), JSON.encode(args)) end local function newInstance(rid) -- create view table local layout = { SwipeRefreshLayout, layout_width = "fill", layout_height = "fill", id = "swipe_layout", { RecyclerView, id = "recyclerView", background = '#EEEEEE', layout_width = "fill", layout_height = "fill", } } local item_view = { FrameLayout, layout_width = "fill", layout_height = "200dp", { ImageView, id = "iv_image", layout_width = "fill", layout_height = "fill", scaleType = "fitXY", }, { View, id = "layer", layout_width = "fill", layout_height = "fill", background = '@drawable/layout_selector_tran', }, } local hadLoadData local isVisible local lastId local params = { rid = rid } local data = {} local ids = {} local adapter local fragment = LuaFragment.newInstance() local function lazyLoad() if not isVisible then return end if hadLoadData then return end if adapter == nil then return end hadLoadData = true getData(params, data, adapter, fragment, ids.swipe_layout) end fragment.setCreator(luajava.createProxy('androlua.LuaFragment$FragmentCreator', { onCreateView = function(inflater, container, savedInstanceState) return loadlayout(layout, ids) end, onViewCreated = function(view, savedInstanceState) adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() return #data end, onCreateViewHolder = function(parent, viewType) local views = {} local holder = LuaRecyclerHolder(loadlayout(item_view, views, RecyclerView)) holder.itemView.getLayoutParams().width = imageWidth holder.itemView.setTag(views) views.layer.onClick = function(view) local position = holder.getAdapterPosition() launchDetail(fragment, data, position) end return holder end, onBindViewHolder = function(holder, position) position = position + 1 local item = data[position] local views = holder.itemView.getTag() holder.itemView.getLayoutParams().height = item.calcHeight LuaImageLoader.load(views.iv_image, item.imgUrl) if position == #data then getData(params, data, adapter, fragment, ids.swipe_layout) end end, })) ids.recyclerView.setLayoutManager(LinearLayoutManager(activity)) ids.recyclerView.setAdapter(adapter) ids.swipe_layout.setRefreshing(true) ids.swipe_layout.setOnRefreshListener(luajava.createProxy('android.support.v4.widget.SwipeRefreshLayout$OnRefreshListener', { onRefresh = function() getData(params, data, adapter, fragment, ids.swipe_layout, true) end })) lazyLoad() end, onUserVisible = function(visible) isVisible = visible lazyLoad() end, })) return fragment end return { newInstance = newInstance } ================================================ FILE: lua/500px/info.json ================================================ { "id": "pub.hanks.500px", "name": "500px", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b0842c3273e0c5?w=150&h=150&f=png&s=3578", "main": "main.lua", "versionName": "1.0.2", "versionCode": 3, "desc": "500px 图片" } ================================================ FILE: lua/500px/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "android.support.v4.view.ViewPager" import "android.support.design.widget.TabLayout" import "androlua.adapter.LuaFragmentPageAdapter" import "android.support.v7.widget.Toolbar" import "android.support.design.widget.CoordinatorLayout" import "pub.hydrogen.android.R" import "android.net.Uri" import "android.support.design.widget.AppBarLayout" import "android.support.design.widget.CollapsingToolbarLayout" import "android.os.Build" local uihelper = require "uihelper" local JSON = require "cjson" local log = require "log" local fragmentNews = require "500px/fragment_news" local AppBarLayoutScrollingViewBehavior = import "android.support.design.widget.AppBarLayout$ScrollingViewBehavior" -- create view table local layout = { CoordinatorLayout, layout_width = "fill", layout_height = "fill", background = "#eeeeee", { AppBarLayout, id = "appbar", layout_width = "match", { LinearLayout, layout_width = "fill", orientation = "vertical", applayout_scrollFlags = 0x15, { View, id = "statusBar", background = '#FF111111', layout_width = "fill", }, { Toolbar, background = '#FF111111', id = 'toolbar', layout_width = "match", layout_height = "48dp", titleTextColor = "#88ffffff", { TabLayout, id = "tab", layout_width = "match", layout_height = "match", }, }, }, }, { FrameLayout, applayout_behavior = AppBarLayoutScrollingViewBehavior(), layout_width = "fill", layout_height = "fill", { ViewPager, id = "viewPager", layout_width = "fill", layout_height = "fill", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", } } } local data = { titles = {}, fragments = {}, } table.insert(data.fragments, fragmentNews.newInstance('p280')) table.insert(data.titles, '编辑精选') table.insert(data.fragments, fragmentNews.newInstance('p3473')) table.insert(data.titles, '热门') table.insert(data.fragments, fragmentNews.newInstance('p3475')) table.insert(data.titles, '抽象') table.insert(data.fragments, fragmentNews.newInstance('p3477')) table.insert(data.titles, '动物') table.insert(data.fragments, fragmentNews.newInstance('p3479')) table.insert(data.titles, '黑白') table.insert(data.fragments, fragmentNews.newInstance('p3481')) table.insert(data.titles, '名人') table.insert(data.fragments, fragmentNews.newInstance('p3483')) table.insert(data.titles, '城市与建筑') table.insert(data.fragments, fragmentNews.newInstance('p3487')) table.insert(data.titles, '音乐会') table.insert(data.fragments, fragmentNews.newInstance('p3489')) table.insert(data.titles, '家庭') table.insert(data.fragments, fragmentNews.newInstance('p3493')) table.insert(data.titles, '电影') table.insert(data.fragments, fragmentNews.newInstance('p3495')) table.insert(data.titles, '艺术') table.insert(data.fragments, fragmentNews.newInstance('p3497')) table.insert(data.titles, '美食') table.insert(data.fragments, fragmentNews.newInstance('p3499')) table.insert(data.titles, '新闻') table.insert(data.fragments, fragmentNews.newInstance('p3501')) table.insert(data.titles, '风景') table.insert(data.fragments, fragmentNews.newInstance('p3503')) table.insert(data.titles, '微距') table.insert(data.fragments, fragmentNews.newInstance('p3505')) table.insert(data.titles, '自然') table.insert(data.fragments, fragmentNews.newInstance('p3507')) table.insert(data.titles, '人物') table.insert(data.fragments, fragmentNews.newInstance('p3509')) table.insert(data.titles, '表演艺术') table.insert(data.fragments, fragmentNews.newInstance('p3511')) table.insert(data.titles, '运动') table.insert(data.fragments, fragmentNews.newInstance('p3513')) table.insert(data.titles, '静物') table.insert(data.fragments, fragmentNews.newInstance('p3515')) table.insert(data.titles, '街拍') table.insert(data.fragments, fragmentNews.newInstance('p3517')) table.insert(data.titles, '交通') table.insert(data.fragments, fragmentNews.newInstance('p3519')) table.insert(data.titles, '旅行') table.insert(data.fragments, fragmentNews.newInstance('p3521')) table.insert(data.titles, '水下摄影') table.insert(data.fragments, fragmentNews.newInstance('p3523')) table.insert(data.titles, '城市探索') table.insert(data.fragments, fragmentNews.newInstance('p3525')) table.insert(data.titles, '婚礼') local adapter = LuaFragmentPageAdapter(activity.getSupportFragmentManager(), luajava.createProxy("androlua.adapter.LuaFragmentPageAdapter$AdapterCreator", { getCount = function() return #data.fragments end, getItem = function(position) position = position + 1 return data.fragments[position] end, getPageTitle = function(position) position = position + 1 return data.titles[position] end })) function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) activity.setSupportActionBar(toolbar) activity.setStatusBarColor(0x33000000) activity.setTitle('') toolbar.setNavigationIcon(LuaDrawable.create('500px/500px.png')) local h = 0 if Build.VERSION.SDK_INT >= 21 then h = uihelper.dp2px(25) end statusBar.getLayoutParams().height = h viewPager.setAdapter(adapter) viewPager.setOffscreenPageLimit(#data.fragments) viewPager.setCurrentItem(0) tab.setSelectedTabIndicatorColor(0xeeffffff) tab.setTabTextColors(0x88ffffff, 0xeeffffff) tab.setTabMode(TabLayout.MODE_SCROLLABLE) tab.setTabGravity(TabLayout.GRAVITY_CENTER) tab.setupWithViewPager(viewPager) end ================================================ FILE: lua/91pic/info.json ================================================ { "id": "pub.hanks.91pic", "name": "91 美女", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b084ebc4a73a6f?w=150&h=150&f=png&s=2897", "main": "main.lua", "versionName": "1.0.1", "versionCode": 2, "desc": "大量高清图" } ================================================ FILE: lua/91pic/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaAdapter" import "androlua.widget.video.VideoPlayerActivity" import "androlua.LuaImageLoader" import "android.support.v7.widget.RecyclerView" import "android.support.v4.widget.SwipeRefreshLayout" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.LinearLayoutManager" import "androlua.widget.picture.PicturePreviewActivity" import "android.graphics.BitmapFactory" import "java.io.File" import "java.lang.Thread" local BitmapFactory_Options = import "android.graphics.BitmapFactory$Options" local JSON = require("cjson") local uihelper = require('uihelper') local md5 = require "md5" local adapter local imageWidth = uihelper.getScreenWidth() local data = {} local list = { page = 1, index = 1, subList = {} } -- create view table local layout = { RelativeLayout, layout_width = "fill", layout_height = "fill", { RecyclerView, id = "recyclerView", layout_width = "fill", layout_height = "fill", }, { TextView, id = "tv_loading", text = "加载中....", textSize = "12sp", textColor = "#888888", layout_margin = "16dp", layout_alignParentBottom = true, layout_alignParentRight = true, } } local item_view = { FrameLayout, layout_width = "fill", { ImageView, id = "iv_image", layout_width = "fill", layout_height = "225dp", }, { View, id = "layer", layout_width = "fill", layout_height = "fill", background = "@drawable/layout_selector_tran", clickable = true, }, } local function calcImgInfo(filePath, info) if not File(filePath).exists() then info.h = uihelper.dp2px(240) info.w = imageWidth info.localUrl = info.url return end local options = BitmapFactory_Options() options.inJustDecodeBounds = true local bitmap = BitmapFactory.decodeFile(filePath, options) info.h = options.outHeight info.w = options.outWidth info.localUrl = filePath end local function notifyUI(arr) uihelper.runOnUiThread(activity, function() local s = #data for i = 1, #arr do local item = arr[i] item.calcHeight = math.floor(imageWidth * item.h / item.w) data[#data + 1] = item end tv_loading.setVisibility(8) adapter.notifyItemRangeChanged(s, #data) end) end local function downloadFile(urls, i, arr) local item = {} item.url = urls[i] local filePath = activity.getExternalCacheDir().getAbsolutePath() .. "/" .. md5.sumhexa(item.url) if File(filePath).exists() then calcImgInfo(filePath, item) arr[#arr + 1] = item if #arr == #urls then notifyUI(arr) end else LuaHttp.request({ url = item.url, outputFile = filePath }, function(e, code, path) calcImgInfo(path, item) arr[#arr + 1] = item if #arr == #urls then notifyUI(arr) end end) end end local function fetchData() tv_loading.setVisibility(0) local url =string.format('http://m.hao123.com/hao123_api/a/tupian/more?pn=%d&tag=meinv', list.page) print(url) LuaHttp.request({ url = url }, function(error, code, body) if error or code ~= 200 then print('error', code, url) return end local arr = {} local urls = {} local json = JSON.decode(body).data for i = 1, #json.data do local item = json.data[i] for j = 1, #item.img_list do local img = item.img_list[j].img.l urls[#urls + 1] = img end end for i = 1, #urls do downloadFile(urls, i, arr) end list.page = list.page + 1 end) end local function launchDetail(item) if item == nil or item.url == nil then return end local args = { uris = { item.url } } PicturePreviewActivity.start(activity, JSON.encode(args)) end function onCreate(savedInstanceState) activity.setStatusBarColor(0x33000000) activity.setContentView(loadlayout(layout)) adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() return #data end, getItemViewType = function(position) return 0 end, onCreateViewHolder = function(parent, viewType) local views = {} local holder = LuaRecyclerHolder(loadlayout(item_view, views, RecyclerView)) holder.itemView.getLayoutParams().width = imageWidth holder.itemView.setTag(views) views.layer.onClick = function(view) local position = holder.getAdapterPosition() + 1 launchDetail(data[position]) end return holder end, onBindViewHolder = function(holder, position) position = position + 1 local item = data[position] local views = holder.itemView.getTag() views.iv_image.getLayoutParams().height = item.calcHeight LuaImageLoader.load(views.iv_image, item.localUrl) if position == #data then fetchData() end end, })) recyclerView.setLayoutManager(LinearLayoutManager(activity)) recyclerView.setAdapter(adapter) fetchData() end ================================================ FILE: lua/appinn/activity_news_detail.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaWebView" import "androlua.LuaHttp" local uihelper = require "uihelper" -- create view table local layout = { LinearLayout, layout_width = "fill", layout_height = "fill", orientation = "vertical", statusBarColor = "#B64926", { LinearLayout, orientation = "horizontal", layout_width = "fill", layout_height = "56dp", background = "#B64926", gravity = "center_vertical", { ImageView, id = "back", layout_width = "40dp", layout_height = "40dp", layout_marginLeft = "8dp", scaleType = "centerInside", src = "@drawable/ic_menu_back", }, { TextView, layout_height = "56dp", layout_width = "fill", paddingRight = "16dp", singleLine = true, textIsSelectable = true, ellipsize = "end", id = "tv_title", gravity = "center_vertical", paddingLeft = "8dp", textColor = "#ffffff", textSize = "16sp", }, }, { FrameLayout, layout_width = "fill", layout_height = "fill", { LuaWebView, id = "webview", layout_width = "fill", layout_height = "fill", }, { ProgressBar, layout_gravity = "center", id = "progressBar", layout_width = "40dp", layout_height = "40dp", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", }, { FrameLayout, layout_gravity = 85, layout_width = "100dp", layout_height = "100dp", id = "layout_content", } } } local css = [[ video{width:100%}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}html{font-family:sans-serif;-webkit-text-size-adjust:100%}body{font-family:'Helvetica Neue',Helvetica,Arial,Sans-serif;background:#fff;padding-top:0;margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{margin:.67em 0}h1,h2,h3,h4,h5,h6{font-size:16px}abbr[title]{border-bottom:1px dotted}hr{box-sizing:content-box;height:0}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap}q{quotes:\201C\201D\2018\2019}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0;vertical-align:middle;color:transparent;font-size:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}table{border-collapse:collapse;border-spacing:0;overflow:hidden}a{text-decoration:none}blockquote{border-left:3px solid #d0e5f2;font-style:normal;display:block;vertical-align:baseline;font-size:100%;margin:.5em 0;padding:0 0 0 1em}ul,ol{padding-left:20px}.content{color:#444;line-height:1.6em;font-size:16px;margin:16px}.content img{max-width:100%;display:block;margin:30px auto}.content img+img{margin-top:15px}.content img[src*="zhihu.com/equation"]{display:inline-block;margin:0 3px}.content a{color:#259}.content a:hover{text-decoration:underline} ]] local htmlTemplate = [[
%s
]] local function html_unescape(s) return s:gsub("<", "<"):gsub(">", ">"):gsub("&", "&"):gsub(""", '"'):gsub("'", "'"):gsub("/", "/") end function onCreate(savedInstanceState) activity.setStatusBarColor(0xffB64926) activity.setContentView(loadlayout(layout)) back.onClick = function() activity.finish() end local url = activity.getIntent().getStringExtra('url') tv_title.setText(url or "error url") webview.setVisibility(0) progressBar.setVisibility(8) LuaHttp.request({ url = url }, function(error, code, body) local content = string.match(body, 'class="entry[-]content".->(.-)(.-)') do local title, url, content, img = string.match(div,'(.-)%s+(.-).-.-.-(.-)') do info = info:gsub("^%s+", ""):gsub("%s+$", "") url = 'http://www.cilicili8.com' .. url data[#data + 1] = { title = title, info = info, url = url } end adapter.notifyDataSetChanged() end) end) end local function launchDetail(position) local item = data[position + 1] item.loading = true adapter.notifyItemChanged(position) LuaHttp.request({ url = item.url }, function(e, c, body) local magnet = string.match(body, "magnetQRCode[(]'(.-)'") item.magnet = magnet item.loading = false, uihelper.runOnUiThread(activity, function() adapter.notifyItemChanged(position) end) end) end function onDestroy() LuaHttp.cancelAll() end local function highlight(text, key) local spannable = SpannableStringBuilder(text) local p = Pattern.compile(key) local m = p.matcher(text) while m.find() do local span = ForegroundColorSpan(0xFFDB3C2E) spannable.setSpan(span, m.start(), m['end'](), 0x21) end return spannable end function onCreate(savedInstanceState) activity.setLightStatusBar() activity.setContentView(loadlayout(layout)) local screenWidth = uihelper.getScreenWidth() adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() return #data end, getItemViewType = function(position) return 0 end, onCreateViewHolder = function(parent, viewType) local views = {} local holder = LuaRecyclerHolder(loadlayout(item_view, views, RecyclerView)) holder.itemView.setTag(views) holder.itemView.getLayoutParams().width = screenWidth holder.itemView.onClick = function() local p = holder.getAdapterPosition() launchDetail(p) end views.tv_magnet.onClick = function(v) local p = holder.getAdapterPosition() + 1 openOrCopy(data[p]) end return holder end, onBindViewHolder = function(holder, position) position = position + 1 local views = holder.itemView.getTag() if views == nil then return end local item = data[position] if item then views.tv_title.setText(highlight(item.title, params.key)) views.tv_desc.setText(item.info) if item.magnet then views.tv_magnet.setVisibility(0) views.tv_magnet.setText(item.magnet) else views.tv_magnet.setVisibility(8) end if item.loading then views.pb_loading.setVisibility(0) else views.pb_loading.setVisibility(8) end end if position == #data then getData() end end, })) recyclerView.setLayoutManager(LinearLayoutManager(activity)) recyclerView.setAdapter(adapter) iv_search.onClick = function(v) local key = et_key.getText().toString() params.key = key params.page = 1 getData() end et_key.setImeOptions(0x00000003) et_key.setOnEditorActionListener(luajava.createProxy('android.widget.TextView$OnEditorActionListener', { onEditorAction = function(v, actionId, event) local key = et_key.getText().toString() params.key = key params.page = 1 getData() return false end })) end ================================================ FILE: lua/buka/detail.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- 布卡漫画 -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaAdapter" import "androlua.widget.video.VideoPlayerActivity" import "androlua.LuaImageLoader" import "androlua.LuaImageLoader" import "androlua.LuaFragment" import "android.support.v7.widget.RecyclerView" import "android.support.v4.widget.SwipeRefreshLayout" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.LinearLayoutManager" import "android.support.v7.widget.GridLayoutManager" import "android.view.View" import "android.support.v4.widget.Space" import "androlua.widget.ninegride.LuaNineGridView" import "androlua.widget.ninegride.LuaNineGridViewAdapter" import "androlua.widget.picture.PicturePreviewActivity" import "androlua.widget.webview.WebViewActivity" local uihelper = require("uihelper") local JSON = require("cjson") local log = require("log") local screenWidth = uihelper.getScreenWidth() import "android.support.design.widget.CoordinatorLayout" import "android.support.design.widget.AppBarLayout" import "android.support.design.widget.CollapsingToolbarLayout" import "android.support.v7.widget.Toolbar" import "android.support.design.widget.FloatingActionButton" local AppBarLayoutScrollingViewBehavior = import "android.support.design.widget.AppBarLayout$ScrollingViewBehavior" -- create view table local layout = { CoordinatorLayout, layout_width = "match", layout_height = "match", { AppBarLayout, id = "appbar", layout_width = "match", { CollapsingToolbarLayout, id = "collapsing_toolbar", applayout_scrollFlags = 0x3, background = "#ffffff", layout_width = "match", { ImageView, layout_width = "match", layout_height = "240dp", applayout_collapseMode = 2, id = "iv_cover", scaleType = "centerCrop", }, { View, layout_width = "match", layout_height = "240dp", applayout_collapseMode = 2, background = "#22000000", }, { LinearLayout, layout_width = "match", applayout_collapseMode = 1, layout_marginTop = "180dp", background = "#ffffff", orientation = "vertical", { FrameLayout, padding = "16dp", layout_width = "match", background = "#2D2118", { TextView, id = "tv_title", textSize = "22sp", textColor = "#FFFFFF", }, { TextView, id = "tv_updateinfo", layout_marginTop = "34dp", textSize = "14sp", textColor = "#FFFFFF", }, { TextView, id = "tv_score", layout_gravity = "right", layout_marginTop = "8dp", textSize = "16sp", textColor = "#FFFFFF", }, { TextView, id = "tv_type", textSize = "12sp", layout_marginTop = "60dp", textColor = "#EEFFFFFF", }, }, { TextView, id = "tv_desc", padding = "16dp", textSize = "14sp", lineSpacingMultiplier = 1.3, textColor = "#444444", background = "#ffffff", }, } }, }, { RecyclerView, id = "recyclerView", layout_width = "fill", layout_height = "fill", background = "#ffffff", applayout_behavior = AppBarLayoutScrollingViewBehavior(), }, } local item_capter = { LinearLayout, orientation = "vertical", layout_height = "60dp", gravity = "center", { TextView, layout_height = "match", layout_width = "match", layout_margin = "8dp", gravity = "center", id = "tv_chapter", textSize = "13sp", textColor = "#444444", background = "#C5C8C0", }, } local baseInfo = {} local data = {} local adapter local function trim(s) if s == nil then return "" end return s:gsub("^%s+", ""):gsub("%s+$", "") end local function updateHeader() -- header LuaImageLoader.load(iv_cover, baseInfo.coverImg or '') tv_title.setText(baseInfo.title or '') tv_type.setText(string.format('%s %s', trim(baseInfo.author), trim(baseInfo.type))) tv_score.setText(baseInfo.score) tv_desc.setText(baseInfo.desc or '') tv_updateinfo.setText(baseInfo.updateInfo or '') end local function getData(mid) local url = string.format('http://www.buka.cn/detail/%s', mid) print(url) LuaHttp.request({ url = url, headers={ "User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36" } }, function(error, code, body) if error or code ~= 200 then print('fetch dm5 data error') return end uihelper.runOnUiThread(activity, function() local coverImg, title = string.match(body, 'class="manga.img.-(.-)') local author = string.match(body, '(.-)') local desc = string.match(body, '
(.-)
') print(coverImg) baseInfo.coverImg = coverImg baseInfo.title = trim(title) baseInfo.type = '' baseInfo.author = author:gsub('%s+', ' ') baseInfo.score = '' baseInfo.updateInfo = updateInfo:gsub('%s+', ' ') baseInfo.desc = trim(desc) updateHeader() for url, name in string.gmatch(body, 'class="epsbox.eplink%s+".-href="(.-)".->(.-)') do data[#data + 1] = { url = url, name = trim(name) } end adapter.notifyDataSetChanged() end) end) end function launchDetail(item) local intent = Intent(activity, LuaActivity) intent.putExtra("luaPath", "buka/viewer.lua") intent.putExtra("url", item.url) activity.startActivity(intent) end function onCreate(savedInstanceState) activity.setStatusBarColor(0x00000000) activity.setContentView(loadlayout(layout)) adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() return #data end, getItemViewType = function(position) return 0 end, onCreateViewHolder = function(parent, viewType) viewType = viewType + 1 local views = {} local holder = LuaRecyclerHolder(loadlayout(item_capter, views, RecyclerView)) holder.itemView.setTag(views) holder.itemView.getLayoutParams().width = screenWidth / 4 holder.itemView.onClick = function(view) local p = holder.getAdapterPosition() + 1 launchDetail(data[p]) end return holder end, onBindViewHolder = function(holder, position) position = position + 1 local views = holder.itemView.getTag() if views == nil then return end local item = data[position] views.tv_chapter.setText(item.name or '') end, })) recyclerView.setLayoutManager(GridLayoutManager(activity, 4)) recyclerView.setAdapter(adapter) local mid = activity.getIntent().getStringExtra('mid') getData(mid) end ================================================ FILE: lua/buka/fragment_category.lua ================================================ -- -- Created by IntelliJ IDEA. -- User: hanks -- Date: 2017/5/13 -- Time: 00:01 -- To change this template use File | Settings | File Templates. -- require "import" import "android.widget.*" import "android.content.*" import "androlua.LuaAdapter" import "androlua.LuaImageLoader" import "androlua.LuaFragment" import "androlua.LuaHttp" import "androlua.widget.webview.WebViewActivity" import "android.support.v4.widget.SwipeRefreshLayout" import "android.view.View" import "androlua.LuaAdapter" import "androlua.widget.video.VideoPlayerActivity" import "androlua.LuaImageLoader" import "androlua.LuaFragment" import "android.support.v7.widget.RecyclerView" import "android.support.v4.widget.SwipeRefreshLayout" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.GridLayoutManager" import "android.view.View" import "android.support.v4.widget.Space" import "androlua.widget.ninegride.LuaNineGridView" import "androlua.widget.ninegride.LuaNineGridViewAdapter" import "androlua.widget.picture.PicturePreviewActivity" import "androlua.widget.webview.WebViewActivity" local uihelper = require "uihelper" local JSON = require "cjson" local log = require("log") local screenWidth = uihelper.getScreenWidth() local function getData(rid, data, adapter, fragment, swipe_layout) if rid == nil then rid = '/news/getnews' end local url = string.format('http://www.buka.cn%s', rid) local options = { url = url, method = 'POST', headers = { 'X-Requested-With:XMLHttpRequest' }, formData = { 'start=' .. data.page } } LuaHttp.request(options, function(error, code, body) if error or code ~= 200 then print('fetch buka data error') return end local arr = JSON.decode(body).items uihelper.runOnUiThread(activity, function() for i = 1, #arr do data.items[#data.items + 1] = arr[i] end data.page = data.page + 1 adapter.notifyDataSetChanged() swipe_layout.setRefreshing(false) swipe_layout.setEnabled(false) end) end) end local function launchDetail(fragment, item) local activity = fragment.getActivity() if item and item.mid then local intent = Intent(activity, LuaActivity) intent.putExtra("luaPath", 'buka/detail.lua') intent.putExtra("mid", item.mid) activity.startActivity(intent) end end function newInstance(rid) -- create view table local layout = { SwipeRefreshLayout, layout_width = "fill", layout_height = "fill", id = "swipe_layout", { RecyclerView, id = "recyclerView", layout_width = "fill", layout_height = "fill", paddingTop = "8dp", paddingLeft = "4dp", paddingRight = "4dp", clipToPadding = false, }, } local item_category = { LinearLayout, layout_width = (screenWidth - uihelper.dp2px(8)) / 3, layout_height = "210dp", paddingLeft = "4dp", paddingRight = "4dp", paddingTop = "8dp", orientation = "vertical", gravity = "center", { ImageView, id = "iv_cover", layout_width = "fill", layout_height = "160dp", scaleType = "centerCrop", }, { TextView, id = "tv_title", layout_height = "26dp", layout_width = "match", padding = "4dp", textSize = "14sp", textColor = "#444444", singleLine = true, ellipsize = "end", gravity = "center", }, { TextView, id = "tv_info", layout_height = "15dp", layout_width = "match", textSize = "12sp", textColor = "#888888", singleLine = true, ellipsize = "end", gravity = "center", }, } local data = { page = 0, items = {} } local hadLoadData local isVisible local ids = {} local adapter local fragment = LuaFragment.newInstance() local function lazyLoad() if not isVisible then return end if hadLoadData then return end if adapter == nil then return end hadLoadData = true getData(rid, data, adapter, fragment, ids.swipe_layout) end fragment.setCreator(luajava.createProxy('androlua.LuaFragment$FragmentCreator', { onCreateView = function(inflater, container, savedInstanceState) return loadlayout(layout, ids) end, onViewCreated = function(view, savedInstanceState) adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() return #data.items end, getItemViewType = function(position) return 0 end, onCreateViewHolder = function(parent, viewType) local views = {} local holder = LuaRecyclerHolder(loadlayout(item_category, views, RecyclerView)) holder.itemView.setTag(views) holder.itemView.getLayoutParams().width = screenWidth / 3 holder.itemView.onClick = function(v) local position = holder.getAdapterPosition() + 1 launchDetail(fragment, data.items[position]) end return holder end, onBindViewHolder = function(holder, position) position = position + 1 local views = holder.itemView.getTag() local item = data.items[position] LuaImageLoader.load(views.iv_cover, item.logo) views.tv_title.setText(item.name) views.tv_info.setText(item.lastup) if position == #data.items then getData(rid, data, adapter, fragment, ids.swipe_layout) end end, })) ids.recyclerView.setLayoutManager(GridLayoutManager(activity, 3)) ids.recyclerView.setAdapter(adapter) ids.swipe_layout.setRefreshing(true) lazyLoad() end, onUserVisible = function(visible) isVisible = visible lazyLoad() end, })) return fragment end return { newInstance = newInstance } ================================================ FILE: lua/buka/info.json ================================================ { "id": "pub.hanks.buka", "name": "布卡漫画", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b0852649adc474?w=150&h=150&f=png&s=3557", "main": "main.lua", "versionName": "1.0.1", "versionCode": 3, "desc": "布卡漫画" } ================================================ FILE: lua/buka/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "android.support.v4.view.ViewPager" import "android.support.design.widget.TabLayout" import "androlua.adapter.LuaFragmentPageAdapter" local fragmentNews = require "buka/fragment_category" -- create view table local layout = { LinearLayout, layout_width = "fill", layout_height = "fill", orientation = "vertical", statusBarColor = "#F4B440", { TabLayout, id = "tab", layout_width = "fill", layout_height = "48dp", background = "#F4B440", }, { FrameLayout, layout_width = "fill", layout_height = "fill", { ViewPager, id = "viewPager", layout_width = "fill", layout_height = "fill", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", } } } local data = { titles = {}, fragments = {}, } table.insert(data.fragments, fragmentNews.newInstance('/news/getnews')) table.insert(data.titles, '最近更新') table.insert(data.fragments, fragmentNews.newInstance('/ranking/getranking')) table.insert(data.titles, '今日热榜') table.insert(data.fragments, fragmentNews.newInstance('/category/12022/已完结')) table.insert(data.titles, '已完结') table.insert(data.fragments, fragmentNews.newInstance('/category/12084/最近上新')) table.insert(data.titles, '最近上新') table.insert(data.fragments, fragmentNews.newInstance('/category/12053/日韩')) table.insert(data.titles, '日韩') table.insert(data.fragments, fragmentNews.newInstance('/category/12036/条漫')) table.insert(data.titles, '条漫') local adapter = LuaFragmentPageAdapter(activity.getSupportFragmentManager(), luajava.createProxy("androlua.adapter.LuaFragmentPageAdapter$AdapterCreator", { getCount = function() return #data.fragments end, getItem = function(position) position = position + 1 return data.fragments[position] end, getPageTitle = function(position) position = position + 1 return data.titles[position] end })) function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) viewPager.setAdapter(adapter) viewPager.setOffscreenPageLimit(#data.fragments) viewPager.setCurrentItem(0) tab.setSelectedTabIndicatorColor(0xffffffff) tab.setTabTextColors(0x88ffffff, 0xffffffff) tab.setTabMode(TabLayout.MODE_SCROLLABLE) tab.setTabGravity(TabLayout.GRAVITY_CENTER) tab.setupWithViewPager(viewPager) end ================================================ FILE: lua/buka/search.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- 漫本联盟 dm5.com -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaAdapter" import "androlua.widget.video.VideoPlayerActivity" import "androlua.LuaImageLoader" import "androlua.LuaImageLoader" import "androlua.LuaFragment" import "android.support.v7.widget.RecyclerView" import "android.support.v4.widget.SwipeRefreshLayout" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.LinearLayoutManager" import "android.view.View" import "android.support.v4.widget.Space" import "androlua.widget.ninegride.LuaNineGridView" import "androlua.widget.ninegride.LuaNineGridViewAdapter" import "androlua.widget.picture.PicturePreviewActivity" import "androlua.widget.webview.WebViewActivity" local uihelper = require("uihelper") local JSON = require("cjson") local log = require("log") local screenWidth = uihelper.getScreenWidth() -- create view table local layout = { LinearLayout, orientation = "vertical", layout_width = "fill", layout_height = "fill", statusBarColor = "#FDE04C", { RelativeLayout, layout_width = "fill", layout_height = "56dp", background = "#FDE04C", { EditText, id = "et_keyword", layout_width = "fill", layout_height = "fill", layout_marginLeft = "16dp", layout_marginRight = "64dp", maxLines = 1, background = "#00FDE04C", layout_centerInParent = true, hint = "请输入关键字", textColor = "#43250C", textSize = "16sp", }, { ImageView, id = "iv_search", layout_width = "56dp", layout_height = "56dp", padding = "16dp", layout_alignParentRight = true, src = "#dm5/ic_search.png" } }, { RecyclerView, id = "recyclerView", layout_width = "fill", layout_height = "fill", }, } local item_topList = { FrameLayout, id = "layout_top", layout_height = "96dp", padding = "8dp", { ImageView, id = "iv_cover", layout_width = "120dp", layout_height = "80dp", scaleType = "centerCrop", }, { TextView, id = "tv_title", layout_marginLeft = "128dp", textColor = "#444444", textSize = "14sp", }, { TextView, id = "tv_subtitle", layout_marginLeft = "128dp", maxLines = 1, textSize = "12sp", textColor = "#767676", layout_gravity = "center_vertical", }, { TextView, id = "tv_info", layout_marginLeft = "128dp", textSize = "12sp", textColor = "#ec4646", layout_gravity = "bottom", }, { TextView, id = "tv_score", layout_gravity = "right", textSize = "10sp", }, } local page = 1 local data = {} local adapter local lastKey = '' local function search() -- search local key = et_keyword.getText().toString() local reset = false if key ~= lastKey then reset = true lastKey = key page = 1 end local options = { url = 'http://m.dm5.com/pagerdata.ashx', method = "POST", formData = { "t:7", "f:0", "pageindex:" .. page, "title:" .. key, } } log.print_r(options) LuaHttp.request(options, function(e, code, body) if e or code ~= 200 then return end local json = JSON.decode(body) page = page + 1 uihelper.runOnUiThread(activity, function() if reset then for k, _ in pairs(data) do data[k] = nil end end for i = 1, #json do data[#data + 1] = json[i] end adapter.notifyDataSetChanged() end) end) end function onDestroy() LuaHttp.cancelAll() end local function launchDetail(url) if url:find('^http://') == nil then url = 'http://m.dm5.com' .. url end local intent = Intent(activity, LuaActivity) intent.putExtra("luaPath", 'dm5/detail.lua') intent.putExtra("url", url) activity.startActivity(intent) end function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) iv_search.onClick = search adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() return #data end, getItemViewType = function(position) return 0 end, onCreateViewHolder = function(parent, viewType) local views = {} local holder = LuaRecyclerHolder(loadlayout(item_topList, views, RecyclerView)) holder.itemView.setTag(views) holder.itemView.getLayoutParams().width = screenWidth return holder end, onBindViewHolder = function(holder, position) position = position + 1 local item = data[position] local views = holder.itemView.getTag() if item == nil or views == nil then return end log.print_r(item) LuaImageLoader.load(views.iv_cover, item.Pic) views.tv_title.setText(item.Title or 'xxxx') views.tv_subtitle.setText(item.Categorys or '--') views.tv_info.setText(item.LastUpdateInfo or '--') views.tv_score.setText(item.Status or '--') views.layout_top.onClick = function() launchDetail(item.Url) end if position == #data then search(true) end end, })) recyclerView.setLayoutManager(LinearLayoutManager(activity)) recyclerView.setAdapter(adapter) end ================================================ FILE: lua/buka/viewer.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaAdapter" import "androlua.widget.video.VideoPlayerActivity" import "androlua.LuaImageLoader" import "androlua.LuaWebView" local uihelper = require("uihelper") -- create view table local layout = { LinearLayout, orientation = "vertical", layout_width = "fill", layout_height = "fill", { ListView, id = "listview", dividerHeight = "4dp", layout_width = "fill", layout_height = "fill", }, { LuaWebView, id = "webview", layout_height = 1, layout_width = 1, background = '#e1e1e1', } } local item_view = { FrameLayout, layout_width = "fill", layout_height = "560dp", { ImageView, id = "iv_image", layout_width = "fill", layout_height = "fill", }, } local data = {} local adapter local function getData(url) LuaHttp.request({ url = url }, function(error, code, body) uihelper.runOnUiThread(activity, function() for url in string.gmatch(body, ' data[-]original="(.-)"') do data[#data + 1] = url end local u = data[1] or 'http://c-r6.sosobook.cn/pics/103915/65603/t4029739_0001.jpg' table.insert(data, 1, u:sub(1, #u - 5) .. '1.jpg') adapter.notifyDataSetChanged() end) end) end function launchDetail(item) end function onCreate(savedInstanceState) activity.setStatusBarColor(0x00000000) activity.setContentView(loadlayout(layout)) local url = activity.getIntent().getStringExtra('url') if not url:find('^http://') then url = 'http://www.buka.cn' .. url end adapter = LuaAdapter(luajava.createProxy("androlua.LuaAdapter$AdapterCreator", { getCount = function() return #data end, getView = function(position, convertView, parent) position = position + 1 -- lua 索引从 1开始 if convertView == nil then local views = {} -- store views convertView = loadlayout(item_view, views, ListView) convertView.getLayoutParams().width = parent.getWidth() convertView.setTag(views) end local views = convertView.getTag() local item = data[position] print(position, item) if item then LuaImageLoader.load(views.iv_image, item, url) end return convertView end })) listview.setAdapter(adapter) listview.setOnItemClickListener(luajava.createProxy("android.widget.AdapterView$OnItemClickListener", { onItemClick = function(adapter, view, position, id) end, })) getData(url) end ================================================ FILE: lua/digit/activity_news_detail.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaWebView" import "androlua.LuaHttp" local uihelper = require "uihelper" -- create view table local layout = { LinearLayout, layout_width = "fill", layout_height = "fill", orientation = "vertical", statusBarColor = "#434343", { LinearLayout, orientation = "horizontal", layout_width = "fill", layout_height = "56dp", background = "#434343", gravity = "center_vertical", { ImageView, id = "back", layout_width = "40dp", layout_height = "40dp", layout_marginLeft = "8dp", scaleType = "centerInside", src = "@drawable/ic_menu_back", }, { TextView, layout_height = "56dp", layout_width = "fill", paddingRight = "16dp", singleLine = true, textIsSelectable = true, ellipsize = "end", id = "tv_title", gravity = "center_vertical", paddingLeft = "8dp", textColor = "#ffffff", textSize = "16sp", }, }, { FrameLayout, layout_width = "fill", layout_height = "fill", { LuaWebView, id = "webview", layout_width = "fill", layout_height = "fill", }, { ProgressBar, layout_gravity = "center", id = "progressBar", layout_width = "40dp", layout_height = "40dp", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", } } } local css = [[ article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}html{font-family:sans-serif;-webkit-text-size-adjust:100%}body{font-family:'Helvetica Neue',Helvetica,Arial,Sans-serif;background:#fff;padding-top:0;margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{margin:.67em 0}h1,h2,h3,h4,h5,h6{font-size:16px}abbr[title]{border-bottom:1px dotted}hr{box-sizing:content-box;height:0}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap}q{quotes:\201C\201D\2018\2019}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0;vertical-align:middle;color:transparent;font-size:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}table{border-collapse:collapse;border-spacing:0;overflow:hidden}a{text-decoration:none}blockquote{border-left:3px solid #d0e5f2;font-style:normal;display:block;vertical-align:baseline;font-size:100%;margin:.5em 0;padding:0 0 0 1em}ul,ol{padding-left:20px}.content{color:#444;line-height:1.6em;font-size:16px;margin:16px}.content img{max-width:100%;display:block;margin:30px auto}.content img+img{margin-top:15px}.content img[src*="zhihu.com/equation"]{display:inline-block;margin:0 3px}.content a{color:#259}.content a:hover{text-decoration:underline} ]] local htmlTemplate = [[
%s
]] local function getData(url) print(url) LuaHttp.request({ url = url }, function(error, code, body) local content = string.match(body, '(.-) 0 then views.iv_image.setVisibility(0) LuaImageLoader.load(views.iv_image, item.covers[1].url .. '?imageMogr2/quality/95/thumbnail/!1440x480r/gravity/Center/crop/1440x480') else views.iv_image.setVisibility(8) end views.tv_title.setText(item.title or 'ERROR TITLE') views.tv_desc.setText(item.snippet or '') end if position == #data then getData(params, data, adapter, fragment, ids.swipe_layout) end return convertView end })) ids.listview.setAdapter(adapter) ids.listview.setOnItemClickListener(luajava.createProxy("android.widget.AdapterView$OnItemClickListener", { onItemClick = function(adapter, view, position, id) launchDetail(fragment, data[position + 1]) end, })) ids.swipe_layout.setRefreshing(true) ids.swipe_layout.setOnRefreshListener(luajava.createProxy('android.support.v4.widget.SwipeRefreshLayout$OnRefreshListener', { onRefresh = function() getData(params, data, adapter, fragment, ids.swipe_layout, true) end })) lazyLoad() end, onUserVisible = function(visible) isVisible = visible lazyLoad() end, })) return fragment end return { newInstance = newInstance } ================================================ FILE: lua/digit/info.json ================================================ { "id": "pub.hanks.digit", "name": "数字尾巴", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b0852c2ccda17c?w=150&h=150&f=png&s=3953", "main": "main.lua", "versionName": "1.0.1", "versionCode": 3, "desc": "数字尾巴" } ================================================ FILE: lua/digit/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "android.support.v4.view.ViewPager" import "android.support.design.widget.TabLayout" import "androlua.adapter.LuaFragmentPageAdapter" import "android.support.v7.widget.Toolbar" import "pub.hydrogen.android.R" import "android.net.Uri" local uihelper = require "uihelper" local JSON = require "cjson" local log = require "log" local fragmentNews = require "digit/fragment_news" -- create view table local layout = { LinearLayout, layout_width = "fill", layout_height = "fill", orientation = "vertical", statusBarColor = "#FFFFFF", { Toolbar, background = '#FFFFFF', id = 'toolbar', layout_width = "match", layout_height = "56dp", titleTextColor = "#434343", }, { TabLayout, id = "tab", layout_width = "fill", layout_height = "48dp", background = "#FFFFFF", }, { FrameLayout, layout_width = "fill", layout_height = "fill", { ViewPager, id = "viewPager", layout_width = "fill", layout_height = "fill", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", } } } local data = { titles = {}, fragments = {}, } table.insert(data.fragments, fragmentNews.newInstance('p90')) table.insert(data.titles, '首页') table.insert(data.fragments, fragmentNews.newInstance('p3333')) table.insert(data.titles, '资讯') table.insert(data.fragments, fragmentNews.newInstance('p3335')) table.insert(data.titles, '手机') table.insert(data.fragments, fragmentNews.newInstance('p3339')) table.insert(data.titles, '周边') table.insert(data.fragments, fragmentNews.newInstance('p3341')) table.insert(data.titles, '影音') table.insert(data.fragments, fragmentNews.newInstance('p3343')) table.insert(data.titles, '电脑') table.insert(data.fragments, fragmentNews.newInstance('p3345')) table.insert(data.titles, '数码') table.insert(data.fragments, fragmentNews.newInstance('p3347')) table.insert(data.titles, '摄影') table.insert(data.fragments, fragmentNews.newInstance('p3349')) table.insert(data.titles, '旅行') table.insert(data.fragments, fragmentNews.newInstance('p3351')) table.insert(data.titles, '生活') table.insert(data.fragments, fragmentNews.newInstance('p3355')) table.insert(data.titles, '玩物') table.insert(data.fragments, fragmentNews.newInstance('p3357')) table.insert(data.titles, '应用') local adapter = LuaFragmentPageAdapter(activity.getSupportFragmentManager(), luajava.createProxy("androlua.adapter.LuaFragmentPageAdapter$AdapterCreator", { getCount = function() return #data.fragments end, getItem = function(position) position = position + 1 return data.fragments[position] end, getPageTitle = function(position) position = position + 1 return data.titles[position] end })) function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) activity.setSupportActionBar(toolbar) activity.setStatusBarColor(0x33000000) activity.setTitle('数字尾巴') toolbar.setNavigationIcon(LuaDrawable.create('digit/logo.png')) viewPager.setAdapter(adapter) viewPager.setOffscreenPageLimit(#data.fragments) viewPager.setCurrentItem(0) tab.setSelectedTabIndicatorColor(0xff434343) tab.setTabTextColors(0x88434343, 0xff434343) tab.setTabMode(TabLayout.MODE_SCROLLABLE) tab.setTabGravity(TabLayout.GRAVITY_CENTER) tab.setupWithViewPager(viewPager) end function onCreateOptionsMenu(menu) menu.add("网页版") return true end function onOptionsItemSelected(item) local title = item.getTitle() if title == "网页版" then activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse('http://www.dgtle.com/'))) end end ================================================ FILE: lua/dm5/detail.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- 漫本联盟 dm5.com -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaAdapter" import "androlua.widget.video.VideoPlayerActivity" import "androlua.LuaImageLoader" import "androlua.LuaImageLoader" import "androlua.LuaFragment" import "android.support.v7.widget.RecyclerView" import "android.support.v4.widget.SwipeRefreshLayout" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.LinearLayoutManager" import "android.support.v7.widget.GridLayoutManager" import "android.view.View" import "android.support.v4.widget.Space" import "androlua.widget.ninegride.LuaNineGridView" import "androlua.widget.ninegride.LuaNineGridViewAdapter" import "androlua.widget.picture.PicturePreviewActivity" import "androlua.widget.webview.WebViewActivity" local uihelper = require("uihelper") local JSON = require("cjson") local log = require("log") local screenWidth = uihelper.getScreenWidth() import "android.support.design.widget.CoordinatorLayout" import "android.support.design.widget.AppBarLayout" import "android.support.design.widget.CollapsingToolbarLayout" import "android.support.v7.widget.Toolbar" import "android.support.design.widget.FloatingActionButton" local AppBarLayoutScrollingViewBehavior = import "android.support.design.widget.AppBarLayout$ScrollingViewBehavior" -- create view table local layout = { CoordinatorLayout, layout_width = "match", layout_height = "match", { AppBarLayout, id = "appbar", layout_width = "match", { CollapsingToolbarLayout, id = "collapsing_toolbar", applayout_scrollFlags = 0x3, background = "#ffffff", layout_width = "match", { ImageView, layout_width = "match", layout_height = "240dp", applayout_collapseMode = 2, id = "iv_cover", scaleType = "centerCrop", }, { View, layout_width = "match", layout_height = "240dp", applayout_collapseMode = 2, background = "#22000000", }, { LinearLayout, layout_width = "match", applayout_collapseMode = 1, layout_marginTop = "180dp", background = "#ffffff", orientation = "vertical", { FrameLayout, padding = "16dp", layout_width = "match", background = "#2D2118", { TextView, id = "tv_title", textSize = "22sp", textColor = "#FFFFFF", }, { TextView, id = "tv_updateinfo", layout_marginTop = "34dp", textSize = "14sp", textColor = "#FFFFFF", }, { TextView, id = "tv_score", layout_gravity = "right", layout_marginTop = "8dp", textSize = "16sp", textColor = "#FFFFFF", }, { TextView, id = "tv_type", textSize = "12sp", layout_marginTop = "60dp", textColor = "#EEFFFFFF", }, }, { TextView, id = "tv_desc", padding = "16dp", textSize = "14sp", lineSpacingMultiplier = 1.3, textColor = "#444444", background = "#ffffff", }, } }, }, { RecyclerView, id = "recyclerView", layout_width = "fill", layout_height = "fill", background = "#ffffff", applayout_behavior = AppBarLayoutScrollingViewBehavior(), }, } local item_capter = { LinearLayout, orientation = "vertical", layout_height = "60dp", gravity = "center", { TextView, layout_height = "match", layout_width = "match", layout_margin = "8dp", gravity = "center", id = "tv_chapter", textSize = "13sp", textColor = "#444444", background = "#C5C8C0", }, } local baseInfo = {} local data = {} local adapter local function trim(s) return s:gsub("^%s+", ""):gsub("%s+$", "") end local function updateHeader() -- header LuaImageLoader.load(iv_cover, baseInfo.coverImg or '') tv_title.setText(baseInfo.title or '') tv_type.setText(string.format('%s %s', trim(baseInfo.author), trim(baseInfo.type))) tv_score.setText('评分:' .. baseInfo.score) tv_desc.setText(baseInfo.desc or '') tv_updateinfo.setText(baseInfo.updateInfo or '') end local function getData(url) LuaHttp.request({ url = url }, function(error, code, body) if error or code ~= 200 then print('fetch dm5 data error') return end uihelper.runOnUiThread(activity, function() -- TOP10 local coverImg = string.match(body, '
(.-)
') local title = string.match(info, '

(.-)

') local updateInfo = string.match(info, '

(.-)

') local author, type for sub in string.gmatch(info, '

(.-)

') do if type == nil then type = sub else author = sub end end local score = string.match(body, '
(.-)
'):gsub('<.->', '') local desc = string.match(body, '
(.-)
'):gsub('<.->', '') baseInfo.coverImg = coverImg baseInfo.title = trim(title) baseInfo.type = type:gsub('%s+', ' ') baseInfo.author = author:gsub('%s+', ' ') baseInfo.score = score:gsub('%s+', ' ') baseInfo.updateInfo = updateInfo:gsub('%s+', ' ') baseInfo.desc = trim(desc) updateHeader() local capters = string.match(body, '
(.-)
') for li in string.gmatch(capters, '
  • (.-)
  • ') do local url = string.match(li, '', ''):gsub('%s+', '') data[#data + 1] = { url = url, name = name } end adapter.notifyDataSetChanged() end) end) end function launchDetail(item) local intent = Intent(activity, LuaActivity) intent.putExtra("luaPath", "dm5/viewer.lua") intent.putExtra("id", item.url) activity.startActivity(intent) end function onCreate(savedInstanceState) activity.setStatusBarColor(0x00000000) activity.setContentView(loadlayout(layout)) adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() return #data end, getItemViewType = function(position) return 0 end, onCreateViewHolder = function(parent, viewType) viewType = viewType + 1 local views = {} local holder = LuaRecyclerHolder(loadlayout(item_capter, views, RecyclerView)) holder.itemView.setTag(views) holder.itemView.getLayoutParams().width = screenWidth / 4 holder.itemView.onClick = function(view) local p = holder.getAdapterPosition() + 1 launchDetail(data[p]) end return holder end, onBindViewHolder = function(holder, position) position = position + 1 local views = holder.itemView.getTag() if views == nil then return end local item = data[position] views.tv_chapter.setText(item.name or '') end, })) recyclerView.setLayoutManager(GridLayoutManager(activity, 4)) recyclerView.setAdapter(adapter) local url = activity.getIntent().getStringExtra('url') getData(url or 'http://m.dm5.com/manhua-yongzheheluku/') end ================================================ FILE: lua/dm5/info.json ================================================ { "id": "pub.hanks.dm5", "name": "动漫屋", "icon": "http://ww1.sinaimg.cn/large/8c9b876fly1fh10o351k7j20460460sh.jpg", "main": "main.lua", "versionName": "1.0.1", "versionCode": 2, "private": true, "desc": "选自动漫屋(dm5)的漫画,可搜索" } ================================================ FILE: lua/dm5/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- 漫本联盟 dm5.com -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaAdapter" import "androlua.widget.video.VideoPlayerActivity" import "androlua.LuaImageLoader" import "androlua.LuaImageLoader" import "androlua.LuaFragment" import "android.support.v7.widget.RecyclerView" import "android.support.v4.widget.SwipeRefreshLayout" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.LinearLayoutManager" import "android.view.View" import "android.support.v4.widget.Space" import "androlua.widget.ninegride.LuaNineGridView" import "androlua.widget.ninegride.LuaNineGridViewAdapter" import "androlua.widget.picture.PicturePreviewActivity" import "androlua.widget.webview.WebViewActivity" local uihelper = require("uihelper") local JSON = require("cjson") local log = require("log") local screenWidth = uihelper.getScreenWidth() local category = { "原创精品", "最新更新", "热门连载", "少年热血", "少女爱情", "最新上架", "TOP10", } -- create view table local layout = { LinearLayout, orientation = "vertical", layout_width = "fill", layout_height = "fill", statusBarColor = "#FDE04C", { RelativeLayout, layout_width = "fill", layout_height = "56dp", background = "#FDE04C", { TextView, layout_centerInParent = true, text = "动漫屋", textColor = "#43250C", textSize = "18sp", }, { ImageView, id = "iv_search", layout_width = "56dp", layout_height = "56dp", padding = "16dp", layout_alignParentRight = true, src = "#dm5/ic_search.png" } }, { RecyclerView, id = "recyclerView", layout_width = "fill", layout_height = "fill", }, } local function launchSearch() local intent = Intent(activity, LuaActivity) intent.putExtra("luaPath", "dm5/search.lua") activity.startActivity(intent) end local item_banner = { FrameLayout, layout_height = "192dp", layout_width = "match", { ImageView, layout_height = "match", layout_width = "match", id = "iv_banner", scaleType = "centerCrop", } } local item_title = { RelativeLayout, layout_height = "48dp", paddingLeft = "8dp", paddingRight = "8dp", { TextView, id = "tv_category", textSize = "16sp", text = "原创精品", textColor = "#222222", layout_centerVertical = true, }, { TextView, text = '更多﹥', visibility = "gone", layout_alignParentRight = true, layout_centerVertical = true, }, } local ceil_category = { LinearLayout, layout_width = (screenWidth - uihelper.dp2px(8)) / 3, layout_height = "200dp", paddingLeft = "4dp", paddingRight = "4dp", orientation = "vertical", { ImageView, layout_width = "fill", layout_height = "160dp", scaleType = "centerCrop", }, { TextView, layout_height = "match", layout_width = "match", gravity = "center", }, } local item_topList = { FrameLayout, id = "layout_top", layout_height = "96dp", padding = "8dp", { ImageView, id = "iv_cover", layout_width = "120dp", layout_height = "80dp", scaleType = "centerCrop", }, { TextView, id = "tv_title", layout_marginLeft = "128dp", textColor = "#444444", textSize = "14sp", }, { TextView, id = "tv_subtitle", layout_marginLeft = "128dp", maxLines = 1, textSize = "12sp", textColor = "#767676", layout_gravity = "center_vertical", }, { TextView, id = "tv_info", layout_marginLeft = "128dp", textSize = "12sp", textColor = "#ec4646", layout_gravity = "bottom", }, { TextView, id = "tv_score", layout_gravity = "right", textSize = "10sp", }, } local item_category = { LinearLayout, id = "row", orientation = "horizontal", paddingLeft = "4dp", paddingRight = "4dp", ceil_category, ceil_category, ceil_category, } local data_type = { banner = 1, title = 2, category = 3, top = 4, } local data = {} local adapter local function getData() LuaHttp.request({ url = 'http://m.dm5.com/' }, function(error, code, body) if error or code ~= 200 then print('fetch dm5 data error') return end uihelper.runOnUiThread(activity, function() -- banner local arr = {} local ul = string.match(body, '
      (.-)
    ') for url, img in string.gmatch(ul, '
    (.-)') do local url, title = string.match(li, '') local img = string.match(li, '.-
      (.-)
    ') for li in string.gmatch(rankList, '
    ') do local url, title = string.match(li, '') local img = string.match(li, '(.-)

    ') local updateInfo = string.match(li, '(.-)') local score = string.match(li, '(.-)') if title and url and img then local item = { title = title, score = score, updateInfo = updateInfo, url = url, img = img, subtitle = subtitle } data[#data + 1] = { type = data_type.top, data = item } end end adapter.notifyDataSetChanged() end) end) end function onDestroy() LuaHttp.cancelAll() end local function launchDetail(url) if url:find('^http://') == nil then url = 'http://m.dm5.com' .. url end local intent = Intent(activity, LuaActivity) intent.putExtra("luaPath", 'dm5/detail.lua') intent.putExtra("url", url) activity.startActivity(intent) end function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) iv_search.onClick = launchSearch adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() return #data end, getItemViewType = function(position) position = position + 1 return data[position].type end, onCreateViewHolder = function(parent, viewType) local views = {} local holder if viewType == data_type.banner then holder = LuaRecyclerHolder(loadlayout(item_banner, views, RecyclerView)) elseif viewType == data_type.title then holder = LuaRecyclerHolder(loadlayout(item_title, views, RecyclerView)) elseif viewType == data_type.category then holder = LuaRecyclerHolder(loadlayout(item_category, views, RecyclerView)) else holder = LuaRecyclerHolder(loadlayout(item_topList, views, RecyclerView)) end holder.itemView.setTag(views) holder.itemView.getLayoutParams().width = screenWidth return holder end, onBindViewHolder = function(holder, position) position = position + 1 local item = data[position] local views = holder.itemView.getTag() if item == nil or views == nil then return end -- fill data if item.type == data_type.banner then LuaImageLoader.load(views.iv_banner, item.data[1].img) views.iv_banner.onClick = function() launchDetail(item.data[1].url) end elseif item.type == data_type.title then views.tv_category.setText(item.data) elseif item.type == data_type.category then for i = 1, #item.data do local child = views.row.getChildAt(i - 1) LuaImageLoader.load(child.getChildAt(0), item.data[i].img) child.getChildAt(1).setText(item.data[i].title) child.onClick = function() launchDetail(item.data[i].url) end end elseif item.type == data_type.top then LuaImageLoader.load(views.iv_cover, item.data.img) views.tv_title.setText(item.data.title) views.tv_subtitle.setText(item.data.subtitle) views.tv_info.setText(item.data.updateInfo) views.tv_score.setText(item.data.score) views.layout_top.onClick = function() launchDetail(item.data.url) end end end, })) recyclerView.setLayoutManager(LinearLayoutManager(activity)) recyclerView.setAdapter(adapter) getData() end ================================================ FILE: lua/dm5/search.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- 漫本联盟 dm5.com -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaAdapter" import "androlua.widget.video.VideoPlayerActivity" import "androlua.LuaImageLoader" import "androlua.LuaImageLoader" import "androlua.LuaFragment" import "android.support.v7.widget.RecyclerView" import "android.support.v4.widget.SwipeRefreshLayout" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.LinearLayoutManager" import "android.view.View" import "android.support.v4.widget.Space" import "androlua.widget.ninegride.LuaNineGridView" import "androlua.widget.ninegride.LuaNineGridViewAdapter" import "androlua.widget.picture.PicturePreviewActivity" import "androlua.widget.webview.WebViewActivity" local uihelper = require("uihelper") local JSON = require("cjson") local log = require("log") local screenWidth = uihelper.getScreenWidth() -- create view table local layout = { LinearLayout, orientation = "vertical", layout_width = "fill", layout_height = "fill", statusBarColor = "#FDE04C", { RelativeLayout, layout_width = "fill", layout_height = "56dp", background = "#FDE04C", { EditText, id = "et_keyword", layout_width = "fill", layout_height = "fill", layout_marginLeft = "16dp", layout_marginRight = "64dp", maxLines = 1, background = "#00FDE04C", layout_centerInParent = true, hint = "请输入关键字", textColor = "#43250C", textSize = "16sp", }, { ImageView, id = "iv_search", layout_width = "56dp", layout_height = "56dp", padding = "16dp", layout_alignParentRight = true, src = "#dm5/ic_search.png" } }, { RecyclerView, id = "recyclerView", layout_width = "fill", layout_height = "fill", }, } local item_topList = { FrameLayout, id = "layout_top", layout_height = "96dp", padding = "8dp", { ImageView, id = "iv_cover", layout_width = "120dp", layout_height = "80dp", scaleType = "centerCrop", }, { TextView, id = "tv_title", layout_marginLeft = "128dp", textColor = "#444444", textSize = "14sp", }, { TextView, id = "tv_subtitle", layout_marginLeft = "128dp", maxLines = 1, textSize = "12sp", textColor = "#767676", layout_gravity = "center_vertical", }, { TextView, id = "tv_info", layout_marginLeft = "128dp", textSize = "12sp", textColor = "#ec4646", layout_gravity = "bottom", }, { TextView, id = "tv_score", layout_gravity = "right", textSize = "10sp", }, } local page = 1 local data = {} local adapter local lastKey = '' local function search() -- search local key = et_keyword.getText().toString() local reset = false if key ~= lastKey then reset = true lastKey = key page = 1 end local options = { url = 'http://m.dm5.com/pagerdata.ashx', method = "POST", formData = { "t:7", "f:0", "pageindex:" .. page, "title:" .. key, } } log.print_r(options) LuaHttp.request(options, function(e, code, body) if e or code ~= 200 then return end local json = JSON.decode(body) page = page + 1 uihelper.runOnUiThread(activity, function() if reset then for k, _ in pairs(data) do data[k] = nil end end for i = 1, #json do data[#data + 1] = json[i] end adapter.notifyDataSetChanged() end) end) end function onDestroy() LuaHttp.cancelAll() end local function launchDetail(url) if url:find('^http://') == nil then url = 'http://m.dm5.com' .. url end local intent = Intent(activity, LuaActivity) intent.putExtra("luaPath", 'dm5/detail.lua') intent.putExtra("url", url) activity.startActivity(intent) end function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) iv_search.onClick = search adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() return #data end, getItemViewType = function(position) return 0 end, onCreateViewHolder = function(parent, viewType) local views = {} local holder = LuaRecyclerHolder(loadlayout(item_topList, views, RecyclerView)) holder.itemView.setTag(views) holder.itemView.getLayoutParams().width = screenWidth return holder end, onBindViewHolder = function(holder, position) position = position + 1 local item = data[position] local views = holder.itemView.getTag() if item == nil or views == nil then return end log.print_r(item) LuaImageLoader.load(views.iv_cover, item.Pic) views.tv_title.setText(item.Title or 'xxxx') views.tv_subtitle.setText(item.Categorys or '--') views.tv_info.setText(item.LastUpdateInfo or '--') views.tv_score.setText(item.Status or '--') views.layout_top.onClick = function() launchDetail(item.Url) end if position == #data then search(true) end end, })) recyclerView.setLayoutManager(LinearLayoutManager(activity)) recyclerView.setAdapter(adapter) end ================================================ FILE: lua/dm5/viewer.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaAdapter" import "androlua.widget.video.VideoPlayerActivity" import "androlua.LuaImageLoader" import "androlua.LuaWebView" local uihelper = require("uihelper") local JSON = require("cjson") -- create view table local layout = { LinearLayout, orientation = "vertical", layout_width = "fill", layout_height = "fill", { ListView, id = "listview", dividerHeight = "4dp", layouti_width = "fill", layout_height = "fill", }, { LuaWebView, id = "webview", layout_height = 1, layout_width = 1, background = '#e1e1e1', } } local item_view = { FrameLayout, layout_width = "fill", layout_height = "560dp", { ImageView, id = "iv_image", layout_width = "fill", layout_height = "fill", }, } local data = {} local adapter local htmlTemplate = [[ ]] local function toast(s) uihelper.runOnUiThread(activity, function() activity.toast(s) end) end local function getData(url) LuaHttp.request({ url = url }, function(error, code, body) local script = string.match(body, '') local data = string.format(htmlTemplate, script) uihelper.runOnUiThread(activity, function() webview.loadData(data, "text/html; charset=UTF-8", nil) end) end) end local log = require('log') function launchDetail(item) end local function callback(jsonStr) local json = JSON.decode(jsonStr) if json.method ~= 'setImg' then return end uihelper.runOnUiThread(activity, function() for i = 1, #json.data do data[#data + 1] = json.data[i] end adapter.notifyDataSetChanged() end) end function onCreate(savedInstanceState) activity.setStatusBarColor(0x00000000) activity.setContentView(loadlayout(layout)) local id = activity.getIntent().getStringExtra('id') local url = 'http://m.dm5.com' .. id webview.injectObjectToJavascript(callback, "luaApp") adapter = LuaAdapter(luajava.createProxy("androlua.LuaAdapter$AdapterCreator", { getCount = function() return #data end, getView = function(position, convertView, parent) position = position + 1 -- lua 索引从 1开始 if convertView == nil then local views = {} -- store views convertView = loadlayout(item_view, views, ListView) convertView.getLayoutParams().width = parent.getWidth() convertView.setTag(views) end local views = convertView.getTag() local item = data[position] print(position, item) if item then LuaImageLoader.load(views.iv_image, item, url) end return convertView end })) listview.setAdapter(adapter) listview.setOnItemClickListener(luajava.createProxy("android.widget.AdapterView$OnItemClickListener", { onItemClick = function(adapter, view, position, id) end, })) getData(url) end ================================================ FILE: lua/douban-daily/activity_news_detail.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaWebView" import "androlua.LuaHttp" local uihelper = require "uihelper" -- create view table local layout = { LinearLayout, layout_width = "fill", layout_height = "fill", orientation = "vertical", statusBarColor = "#1CC4AD", { LinearLayout, orientation = "horizontal", layout_width = "fill", layout_height = "56dp", background = "#1CC4AD", gravity = "center_vertical", { ImageView, id = "back", layout_width = "40dp", layout_height = "40dp", layout_marginLeft = "8dp", scaleType = "centerInside", src = "@drawable/ic_menu_back", }, { TextView, layout_height = "56dp", layout_width = "fill", paddingRight = "16dp", singleLine = true, textIsSelectable = true, ellipsize = "end", id = "tv_title", gravity = "center_vertical", paddingLeft = "8dp", textColor = "#ffffff", textSize = "16sp", }, }, { FrameLayout, layout_width = "fill", layout_height = "fill", { LuaWebView, id = "webview", layout_width = "fill", layout_height = "fill", }, { ProgressBar, layout_gravity = "center", id = "progressBar", layout_width = "40dp", layout_height = "40dp", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", } } } local css = [[ article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}html{font-family:sans-serif;-webkit-text-size-adjust:100%}body{font-family:'Helvetica Neue',Helvetica,Arial,Sans-serif;background:#fff;padding-top:0;margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{margin:.67em 0}h1,h2,h3,h4,h5,h6{font-size:16px}abbr[title]{border-bottom:1px dotted}hr{box-sizing:content-box;height:0}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap}q{quotes:\201C\201D\2018\2019}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0;vertical-align:middle;color:transparent;font-size:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}table{border-collapse:collapse;border-spacing:0;overflow:hidden}a{text-decoration:none}blockquote{border-left:3px solid #d0e5f2;font-style:normal;display:block;vertical-align:baseline;font-size:100%;margin:.5em 0;padding:0 0 0 1em}ul,ol{padding-left:20px}.content{color:#444;line-height:1.6em;font-size:16px;margin:16px}.content img{max-width:100%;display:block;margin:30px auto}.content img+img{margin-top:15px}.content img[src*="zhihu.com/equation"]{display:inline-block;margin:0 3px}.content a{color:#259}.content a:hover{text-decoration:underline} ]] local htmlTemplate = [[
    %s
    ]] local function getData(url) LuaHttp.request({ url = url }, function(error, code, body) local content = string.match(body, '
    0 then item.img = item.images[1].url .. '?imageMogr2/quality/95' item.calcHeight = math.floor(imageWidth * tonumber(item.images[1].height) / tonumber(item.images[1].width)) end data[#data + 1] = item end adapter.notifyDataSetChanged() swipe_layout.setRefreshing(false) end) end) end local function launchDetail(fragment, item) local activity = fragment.getActivity() if item == nil or item.contentUrl == nil then activity.toast('没有 url 可以打开') return end local activity = fragment.getActivity() local intent = Intent(activity, LuaActivity) intent.putExtra("luaPath", 'douban-daily/activity_news_detail.lua') intent.putExtra("url", item.contentUrl) activity.startActivity(intent) end local function newInstance(rid) -- create view table local layout = { SwipeRefreshLayout, layout_width = "fill", layout_height = "fill", id = "swipe_layout", { ListView, id = "listview", background = '#FFFFFF', dividerHeight = 0, paddingTop = "16dp", clipToPadding = false, layout_width = "fill", layout_height = "fill", } } local item_view = { LinearLayout, background = '#ffffff', orientation = 'vertical', layout_width = "fill", { ImageView, id = "iv_image", layout_marginLeft = '16dp', layout_marginRight = '16dp', layout_width = "fill", layout_height = "200dp", scaleType = "centerCrop", }, { TextView, id = "tv_title", layout_width = "fill", layout_marginLeft = '16dp', layout_marginRight = '16dp', layout_marginTop = '8dp', maxLines = 2, lineSpacingMultiplier = 1.3, textSize = "16sp", textColor = "#222222", }, { TextView, id = "tv_desc", layout_width = "fill", layout_marginTop = '8dp', layout_marginLeft = '16dp', layout_marginRight = '16dp', layout_marginBottom = '16dp', maxLines = 4, lineSpacingMultiplier = 1.3, textSize = "12sp", textColor = "#666666", }, { View, layout_width = "fill", layout_height = "16dp", } } local hadLoadData local isVisible local lastId local params = { rid = rid } local data = {} local ids = {} local adapter local fragment = LuaFragment.newInstance() local function lazyLoad() if not isVisible then return end if hadLoadData then return end if adapter == nil then return end hadLoadData = true getData(params, data, adapter, fragment, ids.swipe_layout) end fragment.setCreator(luajava.createProxy('androlua.LuaFragment$FragmentCreator', { onCreateView = function(inflater, container, savedInstanceState) return loadlayout(layout, ids) end, onViewCreated = function(view, savedInstanceState) adapter = LuaAdapter(luajava.createProxy("androlua.LuaAdapter$AdapterCreator", { getCount = function() return #data end, getView = function(position, convertView, parent) position = position + 1 -- lua 索引从 1开始 if convertView == nil then local views = {} -- store views convertView = loadlayout(item_view, views, ListView) convertView.getLayoutParams().width = parent.getWidth() convertView.setTag(views) end local views = convertView.getTag() local item = data[position] if item then if item.img then views.iv_image.setVisibility(0) LuaImageLoader.loadWithRadius(views.iv_image, 3, item.img) views.iv_image.getLayoutParams().height = item.calcHeight else views.iv_image.setVisibility(8) end views.tv_title.setText(item.title or 'ERROR TITLE') views.tv_desc.setText(item.snippet or '') end if position == #data then getData(params, data, adapter, fragment, ids.swipe_layout) end return convertView end })) ids.listview.setAdapter(adapter) ids.listview.setOnItemClickListener(luajava.createProxy("android.widget.AdapterView$OnItemClickListener", { onItemClick = function(adapter, view, position, id) launchDetail(fragment, data[position + 1]) end, })) ids.swipe_layout.setRefreshing(true) ids.swipe_layout.setOnRefreshListener(luajava.createProxy('android.support.v4.widget.SwipeRefreshLayout$OnRefreshListener', { onRefresh = function() getData(params, data, adapter, fragment, ids.swipe_layout, true) end })) lazyLoad() end, onUserVisible = function(visible) isVisible = visible lazyLoad() end, })) return fragment end return { newInstance = newInstance } ================================================ FILE: lua/douban-daily/info.json ================================================ { "id": "pub.hanks.douban-daily", "name": "豆瓣一刻", "icon": "http://ww1.sinaimg.cn/large/8c9b876fly1fhlllrrhr0j2046046jr8.jpg", "main": "main.lua", "versionName": "1.0", "versionCode": 1, "private": true, "desc": "豆瓣一刻" } ================================================ FILE: lua/douban-daily/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "android.support.v4.view.ViewPager" import "android.support.design.widget.TabLayout" import "androlua.adapter.LuaFragmentPageAdapter" local uihelper = require "uihelper" local JSON = require "cjson" local log = require "log" local fragmentNews = require "douban-daily/fragment_news" -- create view table local layout = { LinearLayout, layout_width = "fill", layout_height = "fill", orientation = "vertical", statusBarColor = "#1CC4AD", { TabLayout, id = "tab", layout_width = "fill", layout_height = "48dp", background = "#1CC4AD", }, { FrameLayout, layout_width = "fill", layout_height = "fill", { ViewPager, id = "viewPager", layout_width = "fill", layout_height = "fill", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", } } } local data = { titles = {}, fragments = {}, } table.insert(data.fragments, fragmentNews.newInstance('p38')) table.insert(data.titles, '今日一刻') table.insert(data.fragments, fragmentNews.newInstance('p3401')) table.insert(data.titles, '热门精选') table.insert(data.fragments, fragmentNews.newInstance('p3363')) table.insert(data.titles, '打鸡血') table.insert(data.fragments, fragmentNews.newInstance('p3367')) table.insert(data.titles, '洗洗睡') table.insert(data.fragments, fragmentNews.newInstance('p15913')) table.insert(data.titles, '爱美丽') table.insert(data.fragments, fragmentNews.newInstance('p3369')) table.insert(data.titles, '闲翻书') table.insert(data.fragments, fragmentNews.newInstance('p3371')) table.insert(data.titles, '看电影') table.insert(data.fragments, fragmentNews.newInstance('p3373')) table.insert(data.titles, '听音乐') table.insert(data.fragments, fragmentNews.newInstance('p3375')) table.insert(data.titles, '聊艺术') table.insert(data.fragments, fragmentNews.newInstance('p3379')) table.insert(data.titles, '哈哈哈') table.insert(data.fragments, fragmentNews.newInstance('p3381')) table.insert(data.titles, '假日厨房') table.insert(data.fragments, fragmentNews.newInstance('p3383')) table.insert(data.titles, '食记') table.insert(data.fragments, fragmentNews.newInstance('p3387')) table.insert(data.titles, '生活家') table.insert(data.fragments, fragmentNews.newInstance('p3389')) table.insert(data.titles, '去远方') table.insert(data.fragments, fragmentNews.newInstance('p3391')) table.insert(data.titles, '海外志') table.insert(data.fragments, fragmentNews.newInstance('p3393')) table.insert(data.titles, '冷知识') table.insert(data.fragments, fragmentNews.newInstance('p3395')) table.insert(data.titles, '萌') table.insert(data.fragments, fragmentNews.newInstance('p3397')) table.insert(data.titles, '连载') table.insert(data.fragments, fragmentNews.newInstance('p3399')) table.insert(data.titles, '鬼敲门') local adapter = LuaFragmentPageAdapter(activity.getSupportFragmentManager(), luajava.createProxy("androlua.adapter.LuaFragmentPageAdapter$AdapterCreator", { getCount = function() return #data.fragments end, getItem = function(position) position = position + 1 return data.fragments[position] end, getPageTitle = function(position) position = position + 1 return data.titles[position] end })) function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) viewPager.setAdapter(adapter) viewPager.setOffscreenPageLimit(#data.fragments) viewPager.setCurrentItem(0) tab.setSelectedTabIndicatorColor(0xffffffff) tab.setTabTextColors(0x88ffffff, 0xffffffff) tab.setTabMode(TabLayout.MODE_SCROLLABLE) tab.setTabGravity(TabLayout.GRAVITY_CENTER) tab.setupWithViewPager(viewPager) end ================================================ FILE: lua/doubanmovie/detail.lua ================================================ require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.widget.video.VideoPlayerActivity" local JSON = require "cjson" local uihelper = require "uihelper" local layout = { ScrollView, layout_width = "fill", layout_height = "wrap", { LinearLayout, layout_width = "fill", layout_height = "wrap", orientation = "vertical", background = "#FFFFFF", { FrameLayout, layout_width = "fill", layout_height = "300dp", { ImageView, id = "iv_bg", layout_width = "fill", scaleType = "centerCrop", layout_height = "220dp", }, { View, layout_width = "fill", layout_height = "220dp", background = "#88000000", }, { ImageView, id = "iv_cover", layout_gravity = "bottom", layout_marginLeft = "16dp", layout_marginBottom = "8dp", layout_width = "104dp", layout_height = "160dp", scaleType = "centerCrop", background = "@drawable/ic_loading", elevation = "2dp", }, { View, layout_gravity = "bottom", layout_marginLeft = "16dp", layout_marginBottom = "8dp", layout_width = "104dp", layout_height = "160dp", elevation = "2dp", background = "#44000000", }, { ImageView, layout_gravity = "bottom", layout_marginLeft = "16dp", layout_marginBottom = "8dp", layout_width = "104dp", layout_height = "160dp", padding = "35dp", elevation = "2dp", scaleType = "centerInside", src = "#doubanmovie/ic_video_play.png", }, { RelativeLayout, layout_width = "fill", layout_height = "164dp", layout_gravity = "bottom", layout_marginRight = "16dp", layout_marginLeft = "136dp", layout_marginBottom = "4dp", { TextView, id = "tv_title", layout_width = "fill", textColor = "#faffffff", textSize = "20sp", }, { TextView, layout_below = "tv_title", id = "tv_rate", layout_width = "fill", textColor = "#faffffff", textSize = "16sp", layout_marginTop = "4dp", }, { TextView, id = "tv_year", layout_marginTop = "4dp", layout_alignParentBottom = true, layout_width = "fill", textSize = "14sp", textColor = "#8a8a8a" }, { TextView, id = "tv_directors", layout_marginTop = "4dp", layout_width = "fill", textSize = "14sp", layout_above = "tv_year", textColor = "#8a8a8a" }, { TextView, id = "tv_genres", layout_width = "fill", layout_marginTop = "4dp", textSize = "14sp", textColor = "#8a8a8a", layout_above = "tv_directors", }, { TextView, id = "tv_duration", layout_width = "fill", textColor = "#00ffffff", textSize = "14sp", layout_marginTop = "4dp", layout_above = "tv_genres", }, }, }, { TextView, textSize = "20sp", text = "剧情简介", textColor = "#444444", layout_margin = "16dp", }, { TextView, id = "tv_summary", textSize = "12sp", layout_marginLeft = "16dp", layout_marginRight = "16dp", lineSpacingMultiplier = 1.5, textColor = "#777777", }, { View, layout_margin = "16dp", layout_height = 2, background = "#f1f1f1", }, { TextView, layout_margin = "16dp", textSize = "20sp", text = "剧照", textColor = "#444444", }, { HorizontalScrollView, layout_marginBottom = "16dp", { LinearLayout, id = "layout_casts", layout_width = "fill", layout_height = "120dp", paddingLeft = "16dp", clipToPadding = false, } }, { TextView, layout_margin = "16dp", textSize = "20sp", text = "热门评论", textColor = "#444444", }, { LinearLayout, id = "layout_comment", layout_width = "fill", orientation = "vertical", } }, } local item_comment = { RelativeLayout, layout_width = "fill", paddingLeft = "16dp", paddingRight = "16dp", paddingTop = "16dp", { ImageView, id = "iv_avatar", layout_width = "36dp", layout_height = "36dp", scaleType = "centerCrop", }, { TextView, id = "tv_nick", layout_width = "fill", textColor = "#111111", textSize = "15sp", layout_marginLeft = "48dp", }, { TextView, id = "tv_content", layout_width = "fill", textColor = "#222222", textSize = "13sp", layout_below = "tv_nick", layout_marginLeft = "48dp", layout_marginTop = "8dp", lineSpacingMultiplier = 1.2, }, { View, layout_below = "tv_content", layout_marginTop = "16dp", layout_marginLeft = "48dp", layout_width = "fill", layout_height = "1dp", background = "#f1f1f1", }, } local item_cast = { LinearLayout, layout_width = "168dp", layout_height = "fill", orientation = "vertical", gravity = "center", paddingRight = "8dp", { ImageView, layout_width = "fill", layout_height = "fill", scaleType = "centerCrop", }, } local function updateHeader(movie) uihelper.runOnUiThread(activity, function() local imgUrl = movie.img:gsub("w.h","148.208") LuaImageLoader.load(iv_bg, imgUrl) LuaImageLoader.load(iv_cover, imgUrl) local rate = movie.sc if rate == '0' or rate == 0 then rate = '暂无' end tv_title.setText(movie.nm) tv_rate.setText('评分: ' .. rate) tv_summary.setText(movie.dra or '暂无简介') tv_year.setText('上映时间: ' .. movie.rt or '') tv_genres.setText('分类: ' .. movie.cat) tv_duration.setText(string.format('%s/%s分钟', movie.src or '未知', movie.pn)) tv_directors.setText('导演: ' .. movie.dir) if movie.vd then iv_cover.onClick = function(view) local json = { url = movie.vd, poster = movie.img } VideoPlayerActivity.start(activity, JSON.encode(json)) end end if movie.photos then layout_casts.removeAllViews() for i = 1, #movie.photos do local img = movie.photos[i]:gsub('net/.-/movie', 'net/800.1600/movie') local child = loadlayout(item_cast) LuaImageLoader.load(child.getChildAt(0), img) layout_casts.addView(child) end end end) end local function updateComment(comments) uihelper.runOnUiThread(activity, function() layout_comment.removeAllViews() for i = 1, #comments do local views = {} local child = loadlayout(item_comment, views) LuaImageLoader.load(views.iv_avatar, comments[i].avatarUrl) views.tv_nick.setText(comments[i].nick or '') views.tv_content.setText(comments[i].content or '') layout_comment.addView(child) end end) end local function getData(id) local url = string.format('http://m.maoyan.com/ajax/detailmovie?movieId=%d', id) LuaHttp.request({ url = url }, function(error, code, body) if error or code ~= 200 then print('get data error ' .. code) end local json = JSON.decode(body) local movie = json.detailMovie if movie then updateHeader(movie) end end) local commentUrl = string.format('http://m.maoyan.com/review/v2/comments.json?movieId=%d&userId=-1&offset=0&limit=15&ts=0&type=3',id) LuaHttp.request({ url = commentUrl }, function(error, code, body) if error or code ~= 200 then print('get data error ' .. code) end local json = JSON.decode(body) local comments = json.data.hotComments -- 热门 if comments then updateComment(comments) end end) end function onCreate(savedInstanceState) activity.setStatusBarColor(0x00000000) activity.setContentView(loadlayout(layout)) local id = activity.getIntent().getStringExtra('id') getData(id) end ================================================ FILE: lua/doubanmovie/info.json ================================================ { "id": "pub.hanks.doubanmovie", "name": "热映电影", "icon": "http://ww1.sinaimg.cn/large/8c9b876fly1fh34zfgjcvj2046046weh.jpg", "main": "main.lua", "versionName": "1.0.2", "versionCode": 5, "desc": "展示最近热映电影,可看预告片" } ================================================ FILE: lua/doubanmovie/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- douban - hot movie -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaAdapter" import "androlua.widget.video.VideoPlayerActivity" import "androlua.LuaImageLoader" import "android.support.v7.widget.RecyclerView" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.GridLayoutManager" local uihelper = require("uihelper") local JSON = require("cjson") local log = require("log") -- create view table local layout = { LinearLayout, orientation = "vertical", layout_width = "fill", layout_height = "fill", statusBarColor = "#DB3C2E", { TextView, layout_width = "fill", layout_height = "48dp", background = "#DB3C2E", gravity = "center", text = "热映电影", textColor = "#FFFFFF", textSize = "18sp", }, { FrameLayout, layout_width = "fill", layout_height = "fill", { RecyclerView, id = "recyclerView", layout_width = "fill", layout_height = "fill", paddingTop = "8dp", paddingLeft = "4dp", paddingRight = "4dp", clipToPadding = false, }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", } } } local item_view = { FrameLayout, layout_width = "match", padding = "4dp", { ImageView, id = "iv_image", layout_width = "fill", layout_height = "168dp", scaleType = "centerCrop", }, { View, layout_width = "fill", layout_height = "36dp", background = "#AA000000", layout_gravity = "bottom", }, { TextView, id = "tv_title", layout_height = "36dp", layout_width = "fill", gravity = "center_vertical", layout_gravity = "bottom", paddingLeft = "28dp", textSize = "12sp", maxLines = 1, ellipsize = "end", textColor = "#FFFFFF", }, { TextView, id = "tv_score", layout_height = "36dp", paddingLeft = "4dp", gravity = "center_vertical", layout_gravity = "bottom", textSize = "13sp", textColor = "#F9B600", }, } local data = {} local adapter function getData() local url = string.format('http://m.maoyan.com/ajax/movieOnInfoList') LuaHttp.request({ url = url }, function(error, code, body) if error or code ~= 200 then print('fetch data error') return end print(body) local arr = JSON.decode(body).movieList uihelper.runOnUiThread(activity, function() for i = 1, #arr do data[#data + 1] = arr[i] end adapter.notifyDataSetChanged() end) end) end local function launchDetail(item) local intent = Intent(activity, LuaActivity) intent.putExtra("luaPath", 'doubanmovie/detail.lua') intent.putExtra("id", item.id .. '') activity.startActivity(intent) end function onDestroy() LuaHttp.cancelAll() end function onCreate(savedInstanceState) activity.setLightStatusBar() activity.setContentView(loadlayout(layout)) local screenWidth = uihelper.getScreenWidth() adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() return #data end, getItemViewType = function(position) return 0 end, onCreateViewHolder = function(parent, viewType) local views = {} local holder holder = LuaRecyclerHolder(loadlayout(item_view, views, RecyclerView)) holder.itemView.setTag(views) holder.itemView.onClick = function(view) local position = holder.getAdapterPosition() + 1 end holder.itemView.getLayoutParams().width = screenWidth / 3 holder.itemView.onClick = function() local p = holder.getAdapterPosition() + 1 launchDetail(data[p]) end return holder end, onBindViewHolder = function(holder, position) position = position + 1 local views = holder.itemView.getTag() if views == nil then return end local item = data[position] if item then local imgUrl = item.img:gsub("w.h","148.208") print(imgUrl) LuaImageLoader.load(views.iv_image, imgUrl) views.tv_title.setText(item.nm) local sc = item.sc if sc == nil or sc == 0 then sc = '' end views.tv_score.setText('' .. sc) end end, })) recyclerView.setLayoutManager(GridLayoutManager(activity, 3)) recyclerView.setAdapter(adapter) getData() end ================================================ FILE: lua/douyu/detail.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaWebView" import "androlua.LuaHttp" local uihelper = require("uihelper") -- create view table local layout = { FrameLayout, layout_width = "fill", layout_height = "fill", { LuaWebView, id = "webview", layout_width = "fill", layout_height = "fill", } } function onCreate(savedInstanceState) activity.setStatusBarColor(0x00000000) activity.setContentView(loadlayout(layout)) local url = activity.getIntent().getStringExtra('url') webview.loadUrl(url) end function onDestroy() if webview then webview.getParent().removeView(webview) webview.destroy() webview = nil end end ================================================ FILE: lua/douyu/info.json ================================================ { "id": "pub.hanks.douyu", "name": "斗鱼直播", "icon": "http://ww1.sinaimg.cn/large/8c9b876fly1fj45bhugy4j2046046q2s.jpg", "main": "main.lua", "versionName": "1.0.0", "versionCode": 1, "desc": "斗鱼直播" } ================================================ FILE: lua/douyu/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- 漫本联盟 dm5.com -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaAdapter" import "androlua.widget.video.VideoPlayerActivity" import "androlua.LuaImageLoader" import "androlua.LuaImageLoader" import "androlua.LuaFragment" import "android.support.v7.widget.RecyclerView" import "android.support.v4.widget.SwipeRefreshLayout" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.LinearLayoutManager" import "android.view.View" import "android.support.v4.widget.Space" import "androlua.widget.ninegride.LuaNineGridView" import "androlua.widget.ninegride.LuaNineGridViewAdapter" import "androlua.widget.picture.PicturePreviewActivity" import "androlua.widget.webview.WebViewActivity" import "android.support.v7.widget.Toolbar" import "android.support.design.widget.CoordinatorLayout" import "pub.hydrogen.android.R" import "android.net.Uri" local uihelper = require("uihelper") local JSON = require("cjson") local log = require("log") local screenWidth = uihelper.getScreenWidth() activity.setTheme(R.style.Theme_AppCompat_NoActionBar) -- create view table local layout = { LinearLayout, orientation = "vertical", layout_width = "fill", layout_height = "fill", statusBarColor = "#fe7b09", { Toolbar, background = '#fe7b09', id = 'toolbar', layout_width = "match", layout_height = "56dp", titleTextColor = "#ffffff", }, { RecyclerView, background = '#ffffff', id = "recyclerView", layout_width = "fill", layout_height = "fill", }, } local item_banner = { FrameLayout, layout_height = "142dp", layout_width = "match", { ImageView, layout_height = "match", layout_width = "match", id = "iv_banner", scaleType = "centerCrop", } } local item_title = { RelativeLayout, layout_height = "48dp", { View, layout_width = "4dp", layout_height = "24dp", background = '#fe7b09', layout_centerVertical = true }, { TextView, paddingLeft = "8dp", id = "tv_category", textSize = "18sp", text = "原创", textColor = "#222222", layout_centerVertical = true, }, { TextView, text = '更多﹥', layout_alignParentRight = true, layout_centerVertical = true, textColor = '#888888', paddingRight = '8dp' }, } local ceil_category = { LinearLayout, layout_width = (screenWidth - uihelper.dp2px(4)) / 2, orientation = "vertical", { ImageView, layout_width = "fill", layout_height = "120dp", scaleType = "centerCrop", }, { TextView, layout_height = "36dp", layout_width = "match", singleLine = true, maxLines = 1, paddingLeft = '4dp', ellipsize = 'end', textColor = '#000000', gravity = "center_vertical", }, } local item_video = { LinearLayout, id = "row", layout_width = "fill", orientation = "horizontal", ceil_category, { View, layout_width = "4dp", layout_height = 1 }, ceil_category, } local data_type = { banner = 1, title = 2, video = 3, } local data = {} local adapter local function getData() LuaHttp.request({ url = 'https://m.douyu.com/api/home/mix' }, function(error, code, body) if error or code ~= 200 then print('fetch douyu data error') return end local json = JSON.decode(body) uihelper.runOnUiThread(activity, function() if json == nil then return end -- banner if json.banner and #json.banner > 0 then data[#data + 1] = { type = data_type.banner, banner = json.banner } end if json.hotList and #json.hotList > 0 then data[#data + 1] = { type = data_type.title, title = '最热', room_id = 'https://m.douyu.com/list/index' } local item = json.hotList[1] for j = 1, #item.data, 2 do if j < #item.data then data[#data + 1] = { type = data_type.video, data = { item.data[j], item.data[j + 1] } } end end end if json.liveList and #json.liveList > 0 then data[#data + 1] = { type = data_type.title, title = '正在直播', room_id = 'https://m.douyu.com/list/index' } for i = 1, #json.liveList, 2 do if i < #json.liveList then data[#data + 1] = { type = data_type.video, data = { json.liveList[i], json.liveList[i + 1] } } end end end if json.yzList and #json.yzList > 0 then data[#data + 1] = { type = data_type.title, title = '颜值', room_id = 'https://m.douyu.com/roomlists/yz' } for i = 1, #json.yzList, 2 do if i < #json.yzList then data[#data + 1] = { type = data_type.video, data = { json.yzList[i], json.yzList[i + 1] } } end end end if json.mixList and #json.mixList > 0 then for i = 1, #json.mixList do local item = json.mixList[i] data[#data + 1] = { type = data_type.title, title = item.tabName, room_id = 'https://m.douyu.com/list/custom/' .. item.shortName } for j = 1, #item.data, 2 do if j < #item.data then data[#data + 1] = { type = data_type.video, data = { item.data[j], item.data[j + 1] } } end end end end adapter.notifyDataSetChanged() end) end) end function onDestroy() LuaHttp.cancelAll() end local function launchDetail(item) local url = item.room_id if type(url) == 'number' or not url:find("^http") then url = string.format('https://m.douyu.com/%d', url) end local intent = Intent(activity, LuaActivity) intent.putExtra("luaPath", 'douyu/detail.lua') print(url) intent.putExtra("url", url) activity.startActivity(intent) end function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) activity.setSupportActionBar(toolbar) activity.setTitle('斗鱼直播') toolbar.setNavigationIcon(LuaDrawable.create('douyu/douyu.png')) adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() return #data end, getItemViewType = function(position) position = position + 1 return data[position].type end, onCreateViewHolder = function(parent, viewType) local views = {} local holder if viewType == data_type.banner then holder = LuaRecyclerHolder(loadlayout(item_banner, views, RecyclerView)) elseif viewType == data_type.title then holder = LuaRecyclerHolder(loadlayout(item_title, views, RecyclerView)) elseif viewType == data_type.video then holder = LuaRecyclerHolder(loadlayout(item_video, views, RecyclerView)) end holder.itemView.setTag(views) holder.itemView.getLayoutParams().width = screenWidth return holder end, onBindViewHolder = function(holder, position) position = position + 1 local item = data[position] local views = holder.itemView.getTag() if item == nil or views == nil then return end -- fill data if item.type == data_type.banner then LuaImageLoader.load(views.iv_banner, item.banner[1].pic_url) views.iv_banner.onClick = function() launchDetail(item.banner[1].room) end elseif item.type == data_type.title then views.tv_category.setText(item.title) holder.itemView.onClick = function() launchDetail(item) end elseif item.type == data_type.video then local child1 = views.row.getChildAt(0) local child2 = views.row.getChildAt(2) LuaImageLoader.load(child1.getChildAt(0), item.data[1].room_src) LuaImageLoader.load(child2.getChildAt(0), item.data[2].room_src) child1.getChildAt(1).setText(item.data[1].room_name) child2.getChildAt(1).setText(item.data[2].room_name) child1.onClick = function() launchDetail(item.data[1]) end child2.onClick = function() launchDetail(item.data[2]) end end end, })) recyclerView.setLayoutManager(LinearLayoutManager(activity)) recyclerView.setAdapter(adapter) getData() end function onCreateOptionsMenu(menu) menu.add("网页版") return true end function onOptionsItemSelected(item) local title = item.getTitle() if title == "网页版" then activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse('http://m.douyu.com'))) end end ================================================ FILE: lua/eyepetizer/info.json ================================================ { "id": "pub.hanks.eyepetizer", "name": "开眼", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b088bc73fd882a?w=150&h=150&f=png&s=2157", "main": "main.lua", "versionName": "1.0.1", "versionCode": 3, "desc": "每日开眼视频精选" } ================================================ FILE: lua/eyepetizer/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaAdapter" import "androlua.widget.video.VideoPlayerActivity" import "androlua.LuaImageLoader" local JSON = require("cjson") local uihelper = require('uihelper') -- create view table local layout = { LinearLayout, orientation = "vertical", layout_width = "fill", layout_height = "fill", statusBarColor = "#F0000000", { TextView, layout_width = "fill", layout_height = "48dp", background = "#F0000000", gravity = "center", text = "Eyepetizer", textColor = "#AAffffff", textSize = "18sp", }, { ListView, id = "listview", dividerHeight = 0, layout_width = "fill", layout_height = "fill", }, } local item_view = { FrameLayout, layout_width = "fill", layout_height = "240dp", { ImageView, id = "iv_image", layout_width = "fill", layout_height = "fill", scaleType = "centerCrop", }, { TextView, id = "tv_title", background = "#66000000", layout_width = "fill", layout_height = "fill", padding = "32dp", gravity = "center", maxLines = "5", lineSpacingMultiplier = '1.2', textSize = "14sp", textColor = "#CCFFFFFF", }, } local data = { dailyList = {} } local adapter local function getData() -- http://baobab.kaiyanapp.com/api/v1/feed local url = data.nextPageUrl if url == nil then url = 'http://baobab.kaiyanapp.com/api/v1/feed?udid=3e7ee30c6fc0004a773dc33b0597b5732b145c04' end if url:find('udid=') == nil then url = url .. '&udid=3e7ee30c6fc0004a773dc33b0597b5732b145c04' end print(url) LuaHttp.request({ url = url }, function(error, code, body) if error or code ~= 200 then print('fetch data error') return end local str = JSON.decode(body) uihelper.runOnUiThread(activity, function() data.nextPageUrl = str.nextPageUrl local list = str.dailyList[1].videoList for i = 1, #list do data.dailyList[#data.dailyList + 1] = list[i] end adapter.notifyDataSetChanged() end) end) end local function launchDetail(item) local json = { url = item.playInfo[#item.playInfo].url, poster = item.coverForDetail } VideoPlayerActivity.start(activity, JSON.encode(json)) end function onCreate(savedInstanceState) activity.setStatusBarColor(0xF0000000) activity.setContentView(loadlayout(layout)) adapter = LuaAdapter(luajava.createProxy("androlua.LuaAdapter$AdapterCreator", { getCount = function() return #data.dailyList end, getItem = function(position) return nil end, getItemId = function(position) return position end, getView = function(position, convertView, parent) position = position + 1 -- lua 索引从 1开始 if position == #data.dailyList then getData() end if convertView == nil then local views = {} -- store views convertView = loadlayout(item_view, views, ListView) if parent then local params = convertView.getLayoutParams() params.width = parent.getWidth() end convertView.setTag(views) end local views = convertView.getTag() local item = data.dailyList[position] if item then LuaImageLoader.load(views.iv_image, item.coverForFeed) views.tv_title.setText(item.title) end return convertView end })) listview.setAdapter(adapter) listview.setOnItemClickListener(luajava.createProxy("android.widget.AdapterView$OnItemClickListener", { onItemClick = function(adapter, view, position, id) launchDetail(data.dailyList[position + 1]) end, })) getData() end ================================================ FILE: lua/gacha/info.json ================================================ { "id": "pub.hanks.gacha", "name": "网易插画", "icon": "http://ww1.sinaimg.cn/large/8c9b876fly1fhaaa8qcofj2046046we9.jpg", "main": "main.lua", "versionName": "1.0", "versionCode": 1, "private": true, "desc": "网易每日插画排行" } ================================================ FILE: lua/gacha/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaAdapter" import "androlua.LuaImageLoader" import "android.support.v7.widget.RecyclerView" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.StaggeredGridLayoutManager" import "androlua.widget.picture.PicturePreviewActivity" import "java.util.Calendar" local calender = Calendar.getInstance() local JSON = require("cjson") local uihelper = require('uihelper') local max local data = {} local adapter local imageWidth = uihelper.getScreenWidth() / 2 -- create view table local layout = { RecyclerView, id = "recyclerView", layout_width = "fill", layout_height = "fill", } local item_view = { FrameLayout, layout_width = "fill", { ImageView, id = "iv_image", layout_width = "fill", layout_height = "200dp", scaleType = "fitXY", }, { TextView, id = "tv_title", layout_gravity = "right", background = "#88000000", paddingLeft = "6dp", paddingRight = "6dp", paddingTop = "2dp", paddingBottom = "2dp", textSize = "10sp", visibility = 8, textColor = "#aaffffff", }, { View, id = "layer", layout_width = "fill", layout_height = "fill", background = "@drawable/layout_selector_tran", clickable = true, }, } local function fetchData() local year = calender.get(Calendar.YEAR) local month = calender.get(Calendar.MONTH) + 1 local day = calender.get(Calendar.DAY_OF_MONTH) local markFrom = string.format('%04d-%02d-%02d', year, month, day) calender.add(Calendar.DAY_OF_MONTH, -1) year = calender.get(Calendar.YEAR) month = calender.get(Calendar.MONTH) + 1 day = calender.get(Calendar.DAY_OF_MONTH) local mark = string.format('%04d-%02d-%02d', year, month, day) local url = string.format("http://gacha.163.com/api/v1/ranking/pic?type=0&mark=%s&fromMark=%s", mark, markFrom) LuaHttp.request({ url = url }, function(error, code, body) local json = JSON.decode(body) local html = json.result.rankingHtml uihelper.runOnUiThread(activity, function() local s = #data for w, h, url in string.gmatch(html, 'data[-]width="([0-9]+)" data[-]height="([0-9]+)".-data[-]src="(.-)"') do local item = { url = url, w = w, h = h } local id, type = string.match(item.url, 'http://gacha[.]nosdn[.]127[.]net/([0-9a-z]+)[.]([a-z]+)') item.id = id item.type = type item.fullUrl = string.format('http://gacha.nosdn.127.net/%s.%s', id, type) item.calcHeight = math.floor(imageWidth * tonumber(item.h) / tonumber(item.w)) data[#data + 1] = item end adapter.notifyItemRangeChanged(s, #data) end) end) end local function launchDetail(item) local args = { uris = { item.fullUrl } } PicturePreviewActivity.start(activity, JSON.encode(args)) end function onCreate(savedInstanceState) activity.setStatusBarColor(0x33000000) activity.setContentView(loadlayout(layout)) adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() return #data end, getItemViewType = function(position) return 0 end, onCreateViewHolder = function(parent, viewType) local views = {} local holder = LuaRecyclerHolder(loadlayout(item_view, views, RecyclerView)) holder.itemView.getLayoutParams().width = imageWidth holder.itemView.setTag(views) views.layer.onClick = function(view) local position = holder.getAdapterPosition() + 1 launchDetail(data[position]) end return holder end, onBindViewHolder = function(holder, position) position = position + 1 local item = data[position] local views = holder.itemView.getTag() views.iv_image.getLayoutParams().height = item.calcHeight LuaImageLoader.load(views.iv_image, item.url) if position == #data then fetchData() end end, })) recyclerView.setLayoutManager(StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)) recyclerView.setAdapter(adapter) fetchData() end ================================================ FILE: lua/gamesky/info.json ================================================ { "id": "pub.hanks.gamesky", "name": "游民星空", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b088ce8e81426a?w=150&h=150&f=png&s=3228", "main": "main.lua", "versionName": "1.0.0", "versionCode": 2, "desc": "大量游戏截图,高清" } ================================================ FILE: lua/gamesky/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaAdapter" import "androlua.widget.video.VideoPlayerActivity" import "androlua.LuaImageLoader" import "android.support.v7.widget.RecyclerView" import "android.support.v4.widget.SwipeRefreshLayout" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.StaggeredGridLayoutManager" import "androlua.widget.picture.PicturePreviewActivity" local JSON = require("cjson") local uihelper = require('uihelper') local data = {} local adapter local imageWidth = uihelper.getScreenWidth() local list = { index = 1, page = 1, urls = {} } local maxHeight = uihelper.dp2px(640) math.randomseed(os.time()) --- -然后不断产生随机数 list.page = math.floor(math.random() * 160) -- create view table local layout = { RelativeLayout, layout_width = "fill", layout_height = "fill", { RecyclerView, id = "recyclerView", layout_width = "fill", layout_height = "fill", }, { TextView, id = "tv_loading", text = "加载中....", textSize = "12sp", textColor = "#888888", layout_margin = "16dp", layout_alignParentBottom = true, layout_alignParentRight = true, } } local item_view = { FrameLayout, layout_width = "fill", { ImageView, id = "iv_image", layout_width = "fill", layout_height = "200dp", scaleType = "fitXY", }, { TextView, id = "tv_title", layout_gravity = "right", background = "#88000000", paddingLeft = "6dp", paddingRight = "6dp", paddingTop = "2dp", paddingBottom = "2dp", textSize = "10sp", visibility = 8, textColor = "#aaffffff", }, { View, id = "layer", layout_width = "fill", layout_height = "fill", background = "@drawable/layout_selector_tran", clickable = true, }, } local function fetchData() tv_loading.setVisibility(0) local url = string.format('http://pic.gamersky.com/home/getimagesindex?sort=hot_desc&pageIndex=%d&pageSize=50&nodeId=21086', list.page) LuaHttp.request({ url = url }, function(error, code, body) list.page = list.page + 1 body = body:gsub('\\"', '"') body = body:sub(2, #body - 1) local arr = JSON.decode(body).body uihelper.runOnUiThread(activity, function() local s = #data for i = 1, #arr do local fullUrl = arr[i].originImg local w, h = arr[i].width, arr[i].height local item = { url = fullUrl, w = w, h = h } item.calcHeight = math.floor(imageWidth * tonumber(item.h) / tonumber(item.w)) if item.calcHeight > maxHeight then item.calcHeight = maxHeight end data[#data + 1] = item end tv_loading.setVisibility(8) adapter.notifyItemRangeChanged(s, #data) end) end) end local function launchDetail(item) local args = { uris = { item.url }, headers = { 'Referer:http://gamersky.com', 'User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.109 Safari/537.36' } } PicturePreviewActivity.start(activity, JSON.encode(args)) end function onCreate(savedInstanceState) activity.setStatusBarColor(0x33000000) activity.setContentView(loadlayout(layout)) adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() return #data end, getItemViewType = function(position) return 0 end, onCreateViewHolder = function(parent, viewType) local views = {} local holder = LuaRecyclerHolder(loadlayout(item_view, views, RecyclerView)) holder.itemView.getLayoutParams().width = imageWidth holder.itemView.setTag(views) views.layer.onClick = function(view) local position = holder.getAdapterPosition() + 1 launchDetail(data[position]) end return holder end, onBindViewHolder = function(holder, position) position = position + 1 local item = data[position] local views = holder.itemView.getTag() views.iv_image.getLayoutParams().height = item.calcHeight LuaImageLoader.load(views.iv_image, item.url) if position == #data then fetchData() end end, })) recyclerView.setLayoutManager(StaggeredGridLayoutManager(1, StaggeredGridLayoutManager.VERTICAL)) recyclerView.setAdapter(adapter) fetchData() end ================================================ FILE: lua/graphmovies/detail.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- 图解电影 -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaWebView" import "androlua.LuaHttp" import "android.os.Build" import "android.view.View" local uihelper = require "uihelper" local JSON = require "cjson" local log = require "log" -- create view table local layout = { LinearLayout, layout_width = "fill", layout_height = "fill", orientation = "vertical", statusBarColor = "#ff000000", { LinearLayout, orientation = "horizontal", layout_width = "fill", layout_height = "56dp", background = "#ff000000", gravity = "center_vertical", { ImageView, id = "back", layout_width = "40dp", layout_height = "40dp", layout_marginLeft = "8dp", scaleType = "centerInside", src = "@drawable/ic_menu_back", }, { TextView, layout_height = "56dp", layout_width = "fill", paddingRight = "16dp", singleLine = true, textIsSelectable = true, ellipsize = "end", id = "tv_title", gravity = "center_vertical", paddingLeft = "8dp", textColor = "#ffffff", textSize = "16sp", }, }, { FrameLayout, layout_width = "fill", layout_height = "fill", { ListView, id = "listview", dividerHeight = 0, layout_width = "fill", layout_height = "fill", }, { ProgressBar, layout_gravity = "center", id = "progressBar", layout_width = "40dp", layout_height = "40dp", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", } } } local item_view = { LinearLayout, layout_width = "fill", orientation = "vertical", paddingLeft = "16dp", paddingRight = "16dp", { ImageView, id = "iv_image", layout_width = "fill", layout_height = "220dp", }, { TextView, id = "tv_content", layout_width = "fill", lineSpacingMultiplier = '1.3', paddingTop = "8dp", paddingBottom = "8dp", textSize = "14sp", textColor = "#444444", }, } local data = {} local baseUrl = "" local adapter local function unicode_to_utf8(convertStr) local t = {} for a in string.gmatch(convertStr, '\\u([0-9a-z][0-9a-z][0-9a-z][0-9a-z])') do if #a == 4 then local n = tonumber(a, 16) assert(n, "String decoding failed: bad Unicode escape " .. a) local x if n < 0x80 then x = string.char(n % 0x80) elseif n < 0x800 then -- [110x xxxx] [10xx xxxx] x = string.char(0xC0 + (math.floor(n / 64) % 0x20), 0x80 + (n % 0x40)) else -- [1110 xxxx] [10xx xxxx] [10xx xxxx] x = string.char(0xE0 + (math.floor(n / 4096) % 0x10), 0x80 + (math.floor(n / 64) % 0x40), 0x80 + (n % 0x40)) end convertStr = string.gsub(convertStr, '\\u' .. a, x) end end return convertStr end local function getData() local jsonStr = activity.getIntent().getStringExtra('json') local json = JSON.decode(jsonStr) listview.setVisibility(0) progressBar.setVisibility(8) local url1 = string.format('http://h5.graphmovie.com/gmspanel/olr/rw.shu.php?m=%s&p=web&c=me&v=0&n=%s', json.orkey, json.name) tv_title.setText(url1) LuaHttp.request({ url = url1 }, function(error, code, body) -- get orkey local orkey = string.match(body, "script.php[?]orkey=(.-)'") local options = { url = string.format('http://h5.graphmovie.com/gmspanel/olr/script.php?orkey=%s', orkey), method = 'POST', headers = { "X-Requested-With: XMLHttpRequest", "Content-Type: application/x-www-form-urlencoded", }, formData = { "a:1", }, } LuaHttp.request(options, function(e, c, b) baseUrl = string.match(b, 'data":{"p":"(.-)"') print(baseUrl) for m, r in string.gmatch(b, '"m":"(.-)","r":"(.-)"') do data[#data + 1] = { m = m, r = r } end uihelper.runOnUiThread(activity, function() adapter.notifyDataSetChanged() end) end) end) end function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) back.onClick = function() activity.finish() end adapter = LuaAdapter(luajava.createProxy("androlua.LuaAdapter$AdapterCreator", { getCount = function() return #data end, getView = function(position, convertView, parent) position = position + 1 -- lua 索引从 1开始 if position == #data then getData() end if convertView == nil then local views = {} -- store views convertView = loadlayout(item_view, views, ListView) convertView.getLayoutParams().width = parent.getWidth() convertView.setTag(views) end local views = convertView.getTag() local item = data[position] if item then local img = item.m if not img:find('^http') then img = baseUrl .. img end img = img:gsub('\\/', '/') LuaImageLoader.load(views.iv_image, img) views.tv_content.setText(unicode_to_utf8(item.r or '')) end return convertView end })) listview.setAdapter(adapter) getData() end ================================================ FILE: lua/graphmovies/info.json ================================================ { "id": "pub.hanks.graphmovies", "name": "图解电影", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b088d46dc93a57?w=150&h=150&f=png&s=6212", "main": "main.lua", "versionName": "1.0.1", "versionCode": 3, "desc": "图片+文字看电影,graphmovie.com" } ================================================ FILE: lua/graphmovies/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaAdapter" import "androlua.widget.video.VideoPlayerActivity" import("androlua.LuaImageLoader") local uihelper = require("uihelper") local JSON = require("cjson") -- create view table local layout = { LinearLayout, orientation = "vertical", layout_width = "fill", layout_height = "fill", background = "#f1f1f1", statusBarColor = "#F0000000", { TextView, layout_width = "fill", layout_height = "48dp", background = "#F0000000", gravity = "center", text = "图解电影", textColor = "#FFFFFF", textSize = "18sp", }, { ListView, id = "listview", paddingLeft = "16dp", paddingRight = "16dp", scrollBarStyle = "outsideOverlay", dividerHeight = 0, layout_width = "fill", layout_height = "fill", }, } local item_view = { LinearLayout, layout_width = "fill", orientation = "vertical", background = "#FFFFFF", { View, layout_width = "fill", layout_height = "12dp", background = "#f1f1f1", }, { ImageView, id = "iv_image", layout_width = "fill", layout_height = "200dp", scaleType = "centerCrop", }, { TextView, id = "tv_title", layout_width = "fill", paddingTop = "16dp", paddingBottom = "16dp", paddingLeft = "12dp", paddingRight = "12dp", maxLines = "2", lineSpacingMultiplier = '1.2', textSize = "16sp", textColor = "#222222", }, { TextView, id = "tv_subtitle", layout_width = "fill", paddingLeft = "12dp", paddingRight = "12dp", paddingBottom = "16dp", lineSpacingMultiplier = '1.2', textSize = "14sp", textColor = "#888888", }, -- { -- View, -- layout_width = "fill", -- layout_height = 2, -- background = "#eeeeee", -- }, -- { -- LinearLayout, -- orientation = "horizontal", -- layout_width = "fill", -- layout_height = "48dp", -- gravity = "center_vertical", -- { -- TextView, -- layout_weight = 1, -- id = "tv_username", -- textSize = "12sp", -- textColor = "#888888", -- }, -- { -- TextView, -- id = "tv_views", -- textSize = "12sp", -- textColor = "#888888", -- }, -- { -- TextView, -- id = "tv_rate", -- textSize = "12sp", -- textColor = "#888888", -- }, -- }, } local data = { dailyList = {}, } local adapter local orkey local function getJsonData() local url = string.format('http://www.graphmovies.com/home/2/get.php?orkey=%s', orkey) local options = { url = url, method = 'POST', headers = { "X-Requested-With: XMLHttpRequest", "Content-Type: application/x-www-form-urlencoded", }, formData = { "p:15", "type:movie", "zone:0", "tag:0", "showtime:0", "level:0", }, } if #data.dailyList > 0 then options.formData[#options.formData + 1] = 't:' .. (data.dailyList[#data.dailyList].onlinetime or '') end LuaHttp.request(options, function(e, code, body) local json = JSON.decode(body) local arr = json.data uihelper.runOnUiThread(activity, function() for i = 1, #arr do data.dailyList[#data.dailyList + 1] = arr[i] end adapter.notifyDataSetChanged() end) end) end local function getData() if orkey == nil then LuaHttp.request({ url = 'http://www.graphmovies.com/home/2/' }, function(error, code, body) orkey = string.match(body, "get.php[?]orkey=(.-)'") getJsonData() end) else getJsonData() end end local log = require('log') function launchDetail(item) local json = { orkey = item.orkey, name = item.name } local intent = Intent(activity, LuaActivity) intent.putExtra("luaPath", 'graphmovies/detail.lua') intent.putExtra("json", JSON.encode(json)) activity.startActivity(intent) end function onDestroy() LuaHttp.cancelAll() end function onCreate(savedInstanceState) activity.setStatusBarColor(0x00000000) activity.setContentView(loadlayout(layout)) adapter = LuaAdapter(luajava.createProxy("androlua.LuaAdapter$AdapterCreator", { getCount = function() return #data.dailyList end, getView = function(position, convertView, parent) position = position + 1 -- lua 索引从 1开始 if position == #data.dailyList then getData() end if convertView == nil then local views = {} -- store views convertView = loadlayout(item_view, views, ListView) convertView.getLayoutParams().width = parent.getWidth() convertView.setTag(views) end local views = convertView.getTag() local item = data.dailyList[position] if item then LuaImageLoader.load(views.iv_image, item.bpic or '') views.tv_title.setText(item.name or 'error title') views.tv_subtitle.setText(item.subtitle or '') -- views.tv_username.setText(item.users[1].name) -- views.tv_views.setText(item.readdata.played) -- views.tv_rate.setText(item.score .. '分') end return convertView end })) listview.setAdapter(adapter) listview.setOnItemClickListener(luajava.createProxy("android.widget.AdapterView$OnItemClickListener", { onItemClick = function(adapter, view, position, id) launchDetail(data.dailyList[position + 1]) end, })) getData() end ================================================ FILE: lua/huaban-popular/info.json ================================================ { "id": "pub.hanks.huaban-popular", "name": "花瓣热门", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b088d9ccb657bf?w=150&h=150&f=png&s=2265", "main": "main.lua", "versionName": "1.0.4", "versionCode": 5, "desc": "花瓣网热门图片采集" } ================================================ FILE: lua/huaban-popular/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaAdapter" import "androlua.widget.video.VideoPlayerActivity" import "androlua.LuaImageLoader" import "android.support.v7.widget.RecyclerView" import "android.support.v4.widget.SwipeRefreshLayout" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.StaggeredGridLayoutManager" import "androlua.widget.picture.PicturePreviewActivity" import "android.graphics.drawable.ColorDrawable" local JSON = require("cjson") local uihelper = require('uihelper') local date = os.date("%Y%m%d", os.time() - 60 * 60 * 24) -- 20170518 local max local data = {} local adapter local imageWidth = uihelper.getScreenWidth() / 2 local colors = {0xffF9F8EB, 0xffABCDCB, 0xffECECEC, 0xffF5F5F5, 0xffF5FEFF, 0xffE8F1F5, 0xffCDE3EB, 0xffFFEBEB} -- create view table local layout = { RecyclerView, id = "recyclerView", layout_width = "fill", layout_height = "fill", } local item_view = { FrameLayout, layout_width = "fill", { ImageView, id = "iv_image", layout_width = "fill", layout_height = "200dp", scaleType = "fitXY", }, { TextView, id = "tv_title", layout_gravity = "right", background = "#88000000", paddingLeft = "6dp", paddingRight = "6dp", paddingTop = "2dp", paddingBottom = "2dp", textSize = "10sp", visibility = 8, textColor = "#aaffffff", }, { View, id = "layer", layout_width = "fill", layout_height = "fill", background = "@drawable/layout_selector_tran", clickable = true, }, } local function fetchData() local url = 'http://huaban.com/popular/?limit=20&wfl=1' if max then url = string.format('http://huaban.com/popular/?max=%d&limit=20&wfl=1', max) end local options = { url = url, headers = { "X-Requested-With:XMLHttpRequest", } } LuaHttp.request(options, function(error, code, body) local json = JSON.decode(body) local arr = json.pins max = arr[#arr].pin_id uihelper.runOnUiThread(activity, function() local s = #data for i = 1, #arr do local item = arr[i] item.url = string.format('http://img.hb.aicdn.com/%s', item.file.key) item.calcHeight = math.floor(imageWidth * tonumber(item.file.height) / tonumber(item.file.width)) data[#data + 1] = item end adapter.notifyItemRangeChanged(s, #arr) end) end) end local function launchDetail(item,index) local args = { uris = {}, currentIndex=index } for i=1,#data do args.uris[i] = data[i].url end PicturePreviewActivity.start(activity, JSON.encode(args)) end function onCreate(savedInstanceState) activity.setStatusBarColor(0x33000000) activity.setContentView(loadlayout(layout)) adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() return #data end, getItemViewType = function(position) return 0 end, onCreateViewHolder = function(parent, viewType) local views = {} local holder = LuaRecyclerHolder(loadlayout(item_view, views, RecyclerView)) holder.itemView.getLayoutParams().width = imageWidth holder.itemView.setTag(views) views.layer.onClick = function(view) local position = holder.getAdapterPosition() + 1 launchDetail(data[position],position-1) end return holder end, onBindViewHolder = function(holder, position) local colorIndex = position % 8 position = position + 1 local item = data[position] local views = holder.itemView.getTag() views.iv_image.getLayoutParams().height = item.calcHeight -- LuaImageLoader.load(views.iv_image, item.url) LuaImageLoader.load(views.iv_image.getContext(), views.iv_image, item.url, 0, 0, ColorDrawable(colors[colorIndex + 1]), ColorDrawable(colors[colorIndex + 1])) if position == #data then fetchData() end end, })) recyclerView.setLayoutManager(StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)) recyclerView.setAdapter(adapter) fetchData() end ================================================ FILE: lua/iciba/info.json ================================================ { "id": "pub.hanks.iciba", "name": "金山词霸", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b088df179ab4d7?w=150&h=150&f=png&s=2753", "main": "main.lua", "versionName": "1.0.2", "versionCode": 4, "desc": "金山词霸,用来查单词" } ================================================ FILE: lua/iciba/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "android.support.v7.widget.RecyclerView" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.LinearLayoutManager" import "android.view.inputmethod.InputMethodManager" import "android.support.v4.widget.Space" local JSON = require("cjson") local uihelper = require('uihelper') local log = require('log') -- create view table local layout = { LinearLayout, layout_width = "fill", layout_height = "fill", orientation = "vertical", { LinearLayout, layout_width = "fill", layout_height = "48dp", orientation = "horizontal", layout_marginTop = "48dp", layout_marginLeft = "16dp", layout_marginRight = "16dp", background = "#FFFFFF", elevation = "2dp", { EditText, layout_height = "fill", layout_weight = 1, paddingLeft = "12sp", gravity = "center_vertical", id = "et_keyword", singleLine = true, textSize = "14sp", textColor = "#888888", background = "#00FFFFFF", }, { Button, background = '#0090ff', id = "tv_search", textColor = '#ffffff', layout_width = "48dp", layout_height = "48dp", text = "G", }, }, { ScrollView, layout_width = "fill", padding = "16dp", clipToPadding = false, { LinearLayout, id = "layout_content", layout_width = "fill", layout_height = "fill", orientation = "vertical", } } } local adapter local function unicode_to_utf8(convertStr) for a in string.gmatch(convertStr, '\\u([0-9a-z][0-9a-z][0-9a-z][0-9a-z])') do if #a == 4 then local n = tonumber(a, 16) assert(n, "String decoding failed: bad Unicode escape " .. a) local x if n < 0x80 then x = string.char(n % 0x80) elseif n < 0x800 then x = string.char(0xC0 + (math.floor(n / 64) % 0x20), 0x80 + (n % 0x40)) else x = string.char(0xE0 + (math.floor(n / 4096) % 0x10), 0x80 + (math.floor(n / 64) % 0x40), 0x80 + (n % 0x40)) end convertStr = string.gsub(convertStr, '\\u' .. a, x) end end return convertStr end local function text(txt, size, color) return { TextView, background = "@drawable/layout_selector_tran", layout_width = "fill", paddingTop = "24dp", textIsSelectable = true, gravity = "center_vertical", text = txt, textSize = size, textColor = color, } end local function text_lr(textLeft, textRight) return { LinearLayout, layout_width = "fill", paddingTop = "8dp", orientation = "horizontal", { TextView, layout_width = "30dp", text = textLeft, textIsSelectable = true, textSize = '15sp', textColor = '#888888', }, { TextView, layout_width = "fill", textIsSelectable = true, lineSpacingMultiplier = 1.3, text = textRight, textSize = '14sp', textColor = '#222222', }, } end local function text_tb(textTop, textBottom) return { LinearLayout, layout_width = "fill", paddingTop = "8dp", orientation = "vertical", { TextView, layout_width = "fill", text = textTop, textSize = '13sp', lineSpacingMultiplier = 1.3, textIsSelectable = true, textColor = '#222222', }, { TextView, layout_width = "fill", layout_marginTop = "8dp", text = textBottom, textSize = '12sp', textColor = '#888888', textIsSelectable = true, lineSpacingMultiplier = 1.3, }, } end local function fillData(json) layout_content.removeAllViews() local symbol = json.basic if symbol then layout_content.addView(loadlayout(text('基本释义', '16sp', '#0090ff'))) local ph = {} if symbol['uk-phonetic'] then ph[#ph + 1] = '英[' .. symbol['uk-phonetic'] .. ']' end if symbol['us-phonetic'] then ph[#ph + 1] = '美[' .. symbol['us-phonetic'] .. ']' end layout_content.addView(loadlayout(text(table.concat(ph, ' '), '14sp', '#222222'))) if symbol.explains and #symbol.explains > 0 then for i = 1, #symbol.explains do print(symbol.explains[i]) layout_content.addView(loadlayout(text(symbol.explains[i], '16sp', '#333333'), {}, LinearLayout)) end end end end local function search(keyword) local url = string.format('http://fanyi.youdao.com/openapi.do?keyfrom=LinYaoTian&key=141887672&type=data&doctype=json&version=1.2&q=%s', keyword) print(url) local options = { url = url } LuaHttp.request(options, function(e, code, body) uihelper.runOnUiThread(activity, function() local json = JSON.decode(body) fillData(json) end) end) end function onCreate(savedInstanceState) activity.setStatusBarColor(0x33000000) activity.setContentView(loadlayout(layout)) tv_search.onClick = function(view) local imm = activity.getSystemService(Context.INPUT_METHOD_SERVICE) imm.hideSoftInputFromWindow(tv_search.getWindowToken(), 0) local keyword = et_keyword.getText().toString() search(keyword) end end ================================================ FILE: lua/it168/activity_news_detail.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaWebView" import "androlua.LuaHttp" local uihelper = require "uihelper" -- create view table local layout = { LinearLayout, layout_width = "fill", layout_height = "fill", orientation = "vertical", statusBarColor = "#3164ba", { LinearLayout, orientation = "horizontal", layout_width = "fill", layout_height = "56dp", background = "#3164ba", gravity = "center_vertical", { ImageView, id = "back", layout_width = "40dp", layout_height = "40dp", layout_marginLeft = "8dp", scaleType = "centerInside", src = "@drawable/ic_menu_back", }, { TextView, layout_height = "56dp", layout_width = "fill", paddingRight = "16dp", singleLine = true, textIsSelectable = true, ellipsize = "end", id = "tv_title", gravity = "center_vertical", paddingLeft = "8dp", textColor = "#ffffff", textSize = "16sp", }, }, { FrameLayout, layout_width = "fill", layout_height = "fill", { LuaWebView, id = "webview", layout_width = "fill", layout_height = "fill", }, { ProgressBar, layout_gravity = "center", id = "progressBar", layout_width = "40dp", layout_height = "40dp", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", }, { FrameLayout, layout_gravity = 85, layout_width = "100dp", layout_height = "100dp", id = "layout_content", } } } local css = [[ video{width:100%}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}html{font-family:sans-serif;-webkit-text-size-adjust:100%}body{font-family:'Helvetica Neue',Helvetica,Arial,Sans-serif;background:#fff;padding-top:0;margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{margin:.67em 0}h1,h2,h3,h4,h5,h6{font-size:16px}abbr[title]{border-bottom:1px dotted}hr{box-sizing:content-box;height:0}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap}q{quotes:\201C\201D\2018\2019}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0;vertical-align:middle;color:transparent;font-size:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}table{border-collapse:collapse;border-spacing:0;overflow:hidden}a{text-decoration:none}blockquote{border-left:3px solid #d0e5f2;font-style:normal;display:block;vertical-align:baseline;font-size:100%;margin:.5em 0;padding:0 0 0 1em}ul,ol{padding-left:20px}.content{color:#444;line-height:1.6em;font-size:16px;margin:16px}.content img{max-width:100%;display:block;margin:30px auto}.content img+img{margin-top:15px}.content img[src*="zhihu.com/equation"]{display:inline-block;margin:0 3px}.content a{color:#259}.content a:hover{text-decoration:underline} ]] local htmlTemplate = [[
    %s
    ]] local function html_unescape(s) return s:gsub("<", "<"):gsub(">", ">"):gsub("&", "&"):gsub(""", '"'):gsub("'", "'"):gsub("/", "/") end function onCreate(savedInstanceState) activity.setStatusBarColor(0xffd22222) activity.setContentView(loadlayout(layout)) back.onClick = function() activity.finish() end local url = activity.getIntent().getStringExtra('url') tv_title.setText(url or "error url") webview.setVisibility(0) progressBar.setVisibility(8) print(url) LuaHttp.request({ url = url }, function(error, code, body) local content = string.match(body, '
    (.-)
    %s+
    %s
    ]] local function html_unescape(s) return s:gsub("<", "<"):gsub(">", ">"):gsub("&", "&"):gsub(""", '"'):gsub("'", "'"):gsub("/", "/") end function onCreate(savedInstanceState) activity.setStatusBarColor(0xffd22222) activity.setContentView(loadlayout(layout)) back.onClick = function() activity.finish() end local newsid = activity.getIntent().getStringExtra('newsid') local originalUrl = string.format('http://wap.ithome.com/html/%s.htm', newsid) tv_title.setText(originalUrl) webview.setVisibility(0) progressBar.setVisibility(8) local url = string.format("http://api.ithome.com/xml/newscontent/%s/%s.xml", newsid:sub(1, 3), newsid:sub(4, 6)) LuaHttp.request({ url = url }, function(error, code, body) local content = string.match(body, '(.-)') local data = string.format(htmlTemplate, css, html_unescape(content)) uihelper.runOnUiThread(activity, function() print(data) webview.loadData(data, "text/html; charset=UTF-8", nil) end) end) end function onDestroy() if webview then webview.getParent().removeView(webview) webview.destroy() webview = nil end end ================================================ FILE: lua/ithome/fragment_news.lua ================================================ -- -- Created by IntelliJ IDEA. -- User: hanks -- Date: 2017/5/13 -- Time: 00:01 -- To change this template use File | Settings | File Templates. -- require "import" import "android.widget.*" import "android.content.*" local Adapter = luajava.bindClass("androlua.LuaAdapter") local ImageLoader = luajava.bindClass("androlua.LuaImageLoader") local LuaFragment = luajava.bindClass("androlua.LuaFragment") local Http = luajava.bindClass("androlua.LuaHttp") local ITHomeUtils = luajava.bindClass("pub.hanks.sample.ITHomeUtils") function readContent(str, pattern, defalut) for content in string.gmatch(str, pattern) do return content end return defalut end function runOnUiThread(activity, f) activity.runOnUiThread(luajava.createProxy('java.lang.Runnable', { run = f })) end function getData(path, data, adapter, fragment) -- http://api.ithome.com/xml/newslist/news.xml news_3213df6f23a21dfa.xml -- http://api.ithome.com/xml/slide/news.xml -- 166 的是广告 含有 live 的是直播 local id = '' if #data > 0 then id = '_' .. ITHomeUtils.desEncode(data[#data].newsid) end local url = string.format('http://api.ithome.com/xml/newslist/' .. path, id) Http.request({ url = url }, function(error, code, body) for item in string.gmatch(body, '(.-)') do local news = {} news.cid = readContent(item, '(.-)') if news.cid ~= '166' then news.newsid = readContent(item, '(.-)', 0) news.title = readContent(item, '%s*<!%[CDATA%[(.-)]]>%s*', 'errorTitle') news.url = readContent(item, '(.-)', 'http://hanks.pub') news.postdate = readContent(item, '(.-)') news.image = readContent(item, '(.-)') news.description = readContent(item, '%s*%s*') news.hitcount = readContent(item, '(.-)') news.commentcount = readContent(item, '(.-)') news.forbidcomment = readContent(item, '(.-)') data[1 + #data] = news end end runOnUiThread(fragment.getActivity(), function() adapter.notifyDataSetChanged() end) end) end function launchDetail(fragment, newsid) local activity = fragment.getActivity() local intent = Intent(activity, LuaActivity) intent.putExtra("luaPath", 'ithome/activity_news_detail.lua') intent.putExtra("newsid", newsid) activity.startActivity(intent) end function newInstance(path) -- create view table local layout = { ListView, id = "listview", layout_width = "fill", layout_height = "fill", } local item_view = { FrameLayout, layout_width = "fill", layout_height = "wrap", paddingLeft = "16dp", paddingRight = "12dp", paddingTop = "12dp", paddingBottom = "12dp", { ImageView, id = "iv_image", layout_gravity = "center_vertical", layout_width = "72dp", layout_height = "72dp", }, { TextView, id = "tv_title", layout_marginLeft = "84dp", layout_width = "fill", maxLines = "2", lineSpacingMultiplier = '1.2', layout_gravity = "top", textSize = "14sp", textColor = "#222222", }, { TextView, id = "tv_date", layout_gravity = "bottom", layout_marginLeft = "84dp", layout_width = "fill", textSize = "12sp", textColor = "#aaaaaa", } } local lastId local data = {} local ids = {} local contentView = loadlayout(layout, ids) local fragment = LuaFragment.newInstance() local adapter fragment.setCreator(luajava.createProxy('androlua.LuaFragment$FragmentCreator', { onDestroyView = function() end, onDestroy = function() end, onCreateView = function(inflater, container, savedInstanceState) return contentView end, onViewCreated = function(view, savedInstanceState) adapter = Adapter(luajava.createProxy("androlua.LuaAdapter$AdapterCreator", { getCount = function() return #data end, getItem = function(position) return nil end, getItemId = function(position) return position end, getView = function(position, convertView, parent) position = position + 1 -- lua 索引从 1开始 if position == #data then print('load more') getData(path, data, adapter, fragment) end if convertView == nil then local views = {} -- store views convertView = loadlayout(item_view, views, ListView) if parent then local params = convertView.getLayoutParams() params.width = parent.getWidth() end convertView.setTag(views) end local views = convertView.getTag() local item = data[position] if item then ImageLoader.load(views.iv_image, item.image) views.tv_date.setText(item.postdate) views.tv_title.setText(item.title) end return convertView end })) ids.listview.setAdapter(adapter) ids.listview.setOnItemClickListener(luajava.createProxy("android.widget.AdapterView$OnItemClickListener", { onItemClick = function(adapter, view, position, id) local newsid = data[position + 1].newsid launchDetail(fragment, newsid) end, })) getData(path, data, adapter, fragment) end, })) return fragment end return { newInstance = newInstance } ================================================ FILE: lua/ithome/info.json ================================================ { "id": "pub.hanks.ithome", "name": "IT之家", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b088ea4e40a077?w=150&h=150&f=png&s=1589", "main": "main.lua", "versionName": "1.0.3", "versionCode": 5, "desc": "快速精选IT新闻" } ================================================ FILE: lua/ithome/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "android.support.v4.view.ViewPager" import "android.support.design.widget.TabLayout" import "androlua.adapter.LuaFragmentPageAdapter" local fragmentNews = require "ithome/fragment_news" -- create view table local layout = { LinearLayout, layout_width = "fill", layout_height = "fill", orientation = "vertical", statusBarColor = "#D22222", { TabLayout, id = "tab", layout_width = "fill", layout_height = "48dp", background = "#D22222", }, { FrameLayout, layout_width = "fill", layout_height = "fill", { ViewPager, id = "viewPager", layout_width = "fill", layout_height = "fill", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", } } } local data = { titles = {}, fragments = {}, } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- table.insert(data.fragments, fragmentNews.newInstance("news%s.xml")) table.insert(data.titles, '最新') table.insert(data.fragments, fragmentNews.newInstance("android%s.xml")) table.insert(data.titles, '安卓') table.insert(data.fragments, fragmentNews.newInstance("ios%s.xml")) table.insert(data.titles, '苹果') table.insert(data.fragments, fragmentNews.newInstance("windows%s.xml")) table.insert(data.titles, 'Windows') table.insert(data.fragments, fragmentNews.newInstance("game%s.xml")) table.insert(data.titles, '游戏') table.insert(data.fragments, fragmentNews.newInstance("it%s.xml")) table.insert(data.titles, '行业前沿') local adapter = LuaFragmentPageAdapter(activity.getSupportFragmentManager(), luajava.createProxy("androlua.adapter.LuaFragmentPageAdapter$AdapterCreator", { getCount = function() return #data.fragments end, getItem = function(position) position = position + 1 return data.fragments[position] end, getPageTitle = function(position) position = position + 1 return data.titles[position] end })) function onCreate(savedInstanceState) activity.setStatusBarColor(0xffd22222) activity.setContentView(loadlayout(layout)) viewPager.setAdapter(adapter) tab.setSelectedTabIndicatorColor(0xffffffff) tab.setTabTextColors(0x88ffffff, 0xffffffff) tab.setTabMode(TabLayout.MODE_SCROLLABLE) tab.setTabGravity(TabLayout.GRAVITY_CENTER) tab.setupWithViewPager(viewPager) end ================================================ FILE: lua/jdly/info.json ================================================ { "id": "pub.hanks.jdly", "name": "绝对领域", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b088f6c984a623?w=150&h=150&f=png&s=3498", "main": "main.lua", "versionName": "1.0.1", "versionCode": 2, "private": true, "desc": "绝对领域图库" } ================================================ FILE: lua/jdly/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaAdapter" import "androlua.widget.video.VideoPlayerActivity" import "androlua.LuaImageLoader" import "android.support.v7.widget.RecyclerView" import "android.support.v4.widget.SwipeRefreshLayout" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.StaggeredGridLayoutManager" import "androlua.widget.picture.PicturePreviewActivity" local JSON = require("cjson") local uihelper = require('uihelper') local data = {} local adapter local imageWidth = uihelper.getScreenWidth() local list = { index = 1, page = 1, urls = {} } local maxHeight = uihelper.dp2px(640) math.randomseed(os.time()) --- -然后不断产生随机数 list.page = math.floor(math.random() * 100) -- create view table local layout = { RelativeLayout, layout_width = "fill", layout_height = "fill", { RecyclerView, id = "recyclerView", layout_width = "fill", layout_height = "fill", }, { TextView, id = "tv_loading", text = "加载中....", textSize = "12sp", textColor = "#888888", layout_margin = "16dp", layout_alignParentBottom = true, layout_alignParentRight = true, } } local item_view = { FrameLayout, layout_width = "fill", { ImageView, id = "iv_image", layout_width = "fill", layout_height = "200dp", scaleType = "fitXY", }, { TextView, id = "tv_title", layout_gravity = "right", background = "#88000000", paddingLeft = "6dp", paddingRight = "6dp", paddingTop = "2dp", paddingBottom = "2dp", textSize = "10sp", visibility = 8, textColor = "#aaffffff", }, { View, id = "layer", layout_width = "fill", layout_height = "fill", background = "@drawable/layout_selector_tran", clickable = true, }, } local function fetchData() tv_loading.setVisibility(0) if list.index > #list.urls then local u = string.format('http://www.jdlingyu.fun/page/%d/', list.page) for k, _ in pairs(list.urls) do list.urls[k] = nil end LuaHttp.request({ url = u }, function(error, code, body) for url in string.gmatch(body, 'href="(http://www.jdlingyu.fun/%d+/)"') do list.urls[#list.urls + 1] = url end list.index = 1 list.page = list.page + 1 if list.page > 335 then list.page = 1 end fetchData() end) return end local url = list.urls[list.index] LuaHttp.request({ url = url }, function(error, code, body) list.index = list.index + 1 uihelper.runOnUiThread(activity, function() local s = #data for img, w, h in string.gmatch(body, 'data.original="(http://wx%d.sinaimg.cn/large/[a-zA-Z0-9_-]+.jpg)"%s+width="(%d+)"%s+height="(%d+)"') do local item = { url = img, w = w, h = h } item.calcHeight = math.floor(imageWidth * tonumber(item.h) / tonumber(item.w)) if item.calcHeight > maxHeight then item.calcHeight = maxHeight end data[#data + 1] = item end tv_loading.setVisibility(8) adapter.notifyItemRangeChanged(s, #data) end) end) end local function launchDetail(item) local args = { uris = { item.url }, headers = { 'Referer:http://www.jdlingyu.fun', 'User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.109 Safari/537.36' } } PicturePreviewActivity.start(activity, JSON.encode(args)) end function onCreate(savedInstanceState) activity.setStatusBarColor(0x33000000) activity.setContentView(loadlayout(layout)) adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() return #data end, getItemViewType = function(position) return 0 end, onCreateViewHolder = function(parent, viewType) local views = {} local holder = LuaRecyclerHolder(loadlayout(item_view, views, RecyclerView)) holder.itemView.getLayoutParams().width = imageWidth holder.itemView.setTag(views) views.layer.onClick = function(view) local position = holder.getAdapterPosition() + 1 launchDetail(data[position]) end return holder end, onBindViewHolder = function(holder, position) position = position + 1 local item = data[position] local views = holder.itemView.getTag() views.iv_image.getLayoutParams().height = item.calcHeight LuaImageLoader.load(views.iv_image, item.url) if position == #data then fetchData() end end, })) recyclerView.setLayoutManager(StaggeredGridLayoutManager(1, StaggeredGridLayoutManager.VERTICAL)) recyclerView.setAdapter(adapter) fetchData() end ================================================ FILE: lua/jike/fragment_feed.lua ================================================ -- -- Created by IntelliJ IDEA. -- User: hanks -- Date: 2017/5/13 -- Time: 00:01 -- To change this template use File | Settings | File Templates. -- local JSON = require("cjson") local ImageLoader = import "androlua.LuaImageLoader" local LuaFragment = import("androlua.LuaFragment") local Http = import "androlua.LuaHttp" local uihelper = require("uihelper") import "android.support.v7.widget.RecyclerView" import "android.support.v4.widget.SwipeRefreshLayout" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.LinearLayoutManager" import "android.view.View" import "android.support.v4.widget.Space" import "androlua.widget.ninegride.LuaNineGridView" import "androlua.widget.ninegride.LuaNineGridViewAdapter" local function clearTable(t) for k in pairs(t) do t[k] = nil end end local function fetchData(refreshLayout, data, adapter, fragment, reload) local url = string.format('http://app.jike.ruguoapp.com/1.0/newsFeed/list') print(url) local postBody = { trigger = 'user' } if data.loadMoreKey and not reload then postBody.loadMoreKey = data.loadMoreKey postBody.trigger = 'auto' end local options = { url = url, method = 'POST', body = JSON.encode(postBody), headers = { "Cookie:io=0_Djvr_i0yLPqdsuFnzY; jike:sess.sig=d-IvFa3n5DhxWNim_0gVasNfTP0; jike:feed:latestNormalMessageId=592e495c7a27e200117d35b3; jike:recommendfeed:latestRecCreatedAt=2017-05-31T06:16:06.797Z; jike:sess=eyJfdWlkIjoiNTdmYjc2YTJhNzViY2ExMzAwZjYyMzkyIiwiX3Nlc3Npb25Ub2tlbiI6IkdRTUU0RmNkTHZhNTZlcExXR1BaYURDaDQifQ==; jikeSocketSticky=33b938e0c7b12816f8f2e027067ee82d69975eb2; jike:feed:latestFeedItemId=592e495c7a27e200117d35b3; jike:feed:noContentPullCount=0" } } Http.request(options, function(error, code, body) if error or code ~= 200 then print(' ================== get data error') return end local json = JSON.decode(body) data.loadMoreKey = json.loadMoreKey if reload then clearTable(data.msg) end for i = 1, #json.data do local type = json.data[i].type if type == 'MESSAGE' then data.msg[#data.msg + 1] = json.data[i] end end uihelper.runOnUiThread(fragment.getActivity(), function() refreshLayout.setRefreshing(false) adapter.notifyDataSetChanged() end) end) end -- local log = require("androlua.common.log") local function launchDetail(fragment, msg) local activity = fragment.getActivity() local intent = Intent(activity, LuaActivity) intent.putExtra("luaPath", 'ithome/activity_news_detail.lua') intent.putExtra("url", msg.item.linkUrl) activity.startActivity(intent) end -- create view table local layout = { LinearLayout, layout_width = "match", layout_height = "match", orientation = "vertical", { SwipeRefreshLayout, id = "refreshLayout", { RecyclerView, id = "recyclerView", paddingTop = "25dp", clipToPadding = false, layout_width = "fill", layout_height = "fill", }, }, } local item_view = require('jike.item_msg') local item_loading = { LinearLayout, layout_width = "match", layout_height = "72dp", gravity = "center", { ProgressBar, layout_width = "32dp", layout_height = "32dp", }, } function newInstance() local data = { msg = {} } local ids = {} local fragment = LuaFragment.newInstance() local adapter fragment.setCreator(luajava.createProxy('androlua.LuaFragment$FragmentCreator', { onDestroyView = function() end, onDestroy = function() end, onCreateView = function(inflater, container, savedInstanceState) return loadlayout(layout, ids) end, onViewCreated = function(view, savedInstanceState) adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() if #data.msg > 0 then return #data.msg + 1 else return 0 end end, getItemViewType = function(position) if position > 0 and position == #data.msg then return 1 end return 0 end, onCreateViewHolder = function(parent, viewType) local views = {} local holder if viewType == 1 then holder = LuaRecyclerHolder(loadlayout(item_loading, views, RecyclerView)) else holder = LuaRecyclerHolder(loadlayout(item_view, views, RecyclerView)) holder.itemView.setTag(views) holder.itemView.onClick = function(view) local position = holder.getAdapterPosition() + 1 if position <= #data.msg then launchDetail(fragment, data.msg[position]) end end end holder.itemView.getLayoutParams().width = parent.getWidth() return holder end, onBindViewHolder = function(holder, position) position = position + 1 if (position == #data.msg) then fetchData(ids.refreshLayout, data, adapter, fragment) -- getdata may call ther lua files return end local msg = data.msg[position] local views = holder.itemView.getTag() views.tv_title.setText(msg.item.title or 'error title') views.tv_content.setText(msg.item.content or '') views.tv_date.setText(msg.item.updatedAt:sub(1, 10) or '') views.tv_collect.setText(string.format('%s', msg.item.collectCount)) views.tv_comment.setText(string.format('%s', msg.item.commentCount)) ImageLoader.load(views.iv_image, msg.item.topic.thumbnailUrl) if msg.item.video then ImageLoader.load(views.iv_video, msg.item.video.thumbnailUrl) views.layout_video.setVisibility(0) else views.layout_video.setVisibility(8) end if msg.item.pictureUrls and #msg.item.pictureUrls > 0 then local pictures = msg.item.pictureUrls local urls = {} local len = #pictures for i = 1, len do if len == 1 then urls[i] = pictures[i].picUrl views.iv_nine_grid.setSingleImgSize(pictures[i].width, pictures[i].height) else urls[i] = pictures[i].thumbnailUrl end end views.iv_nine_grid.setVisibility(0) if views.iv_nine_grid.getAdapter() == nil then views.iv_nine_grid.setAdapter(LuaNineGridViewAdapter(luajava.createProxy('androlua.widget.ninegride.LuaNineGridViewAdapter$AdapterCreator', { onDisplayImage = function(context, imageView, url) ImageLoader.load(imageView, url) end, onItemImageClick = function(context, imageView, index, list) print(list.get(index)) end }))) end views.iv_nine_grid.setImagesData(urls) else views.iv_nine_grid.setVisibility(8) end end, })) ids.recyclerView.setLayoutManager(LinearLayoutManager(fragment.getActivity())) ids.recyclerView.setAdapter(adapter) ids.refreshLayout.setOnRefreshListener(luajava.createProxy('android.support.v4.widget.SwipeRefreshLayout$OnRefreshListener', { onRefresh = function() fetchData(ids.refreshLayout, data, adapter, fragment, true) end })) ids.refreshLayout.setRefreshing(true) fetchData(ids.refreshLayout, data, adapter, fragment) -- getdata may call ther lua files end, })) return fragment end return { newInstance = newInstance } ================================================ FILE: lua/jike/fragment_hot.lua ================================================ -- -- Created by IntelliJ IDEA. -- User: hanks -- Date: 2017/5/13 -- Time: 00:01 -- To change this template use File | Settings | File Templates. -- import "androlua.LuaImageLoader" import "androlua.LuaFragment" import "androlua.LuaHttp" import "android.support.v7.widget.RecyclerView" import "android.support.v4.widget.SwipeRefreshLayout" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.LinearLayoutManager" import "android.view.View" import "android.support.v4.widget.Space" import "androlua.widget.ninegride.LuaNineGridView" import "androlua.widget.ninegride.LuaNineGridViewAdapter" import "androlua.widget.picture.PicturePreviewActivity" import "androlua.widget.webview.WebViewActivity" local JSON = require("cjson") local uihelper = require("uihelper") local function clearTable(t) for k in pairs(t) do t[k] = nil end end local function fetchData(refreshLayout, data, adapter, fragment, reload) local url = string.format('http://app.jike.ruguoapp.com/1.0/users/messages/listPopularByTag?tag=ALL') local options = { url = url, headers = { "Cookie:io=0_Djvr_i0yLPqdsuFnzY; jike:sess.sig=d-IvFa3n5DhxWNim_0gVasNfTP0; jike:feed:latestNormalMessageId=592e495c7a27e200117d35b3; jike:recommendfeed:latestRecCreatedAt=2017-05-31T06:16:06.797Z; jike:sess=eyJfdWlkIjoiNTdmYjc2YTJhNzViY2ExMzAwZjYyMzkyIiwiX3Nlc3Npb25Ub2tlbiI6IkdRTUU0RmNkTHZhNTZlcExXR1BaYURDaDQifQ==; jikeSocketSticky=33b938e0c7b12816f8f2e027067ee82d69975eb2; jike:feed:latestFeedItemId=592e495c7a27e200117d35b3; jike:feed:noContentPullCount=0" } } LuaHttp.request(options, function(error, code, body) if error or code ~= 200 then print(' ================== get data error') return end local arr = JSON.decode(body).data uihelper.runOnUiThread(fragment.getActivity(), function() if reload then clearTable(data.msg) end for i = 1, #arr do data.msg[#data.msg + 1] = arr[i] end refreshLayout.setRefreshing(false) adapter.notifyDataSetChanged() end) end) end local log = require("log") local function launchDetail(fragment, msg) local activity = fragment.getActivity() --log.print_r(msg) local url = 'https://m.okjike.com/originalPosts/' .. msg.id WebViewActivity.start(activity, url, 0xFFffe411) end local function launchPicturePreview(fragment, msg, index) local urls = {} for i = 1, #msg.pictures do urls[i] = msg.pictures[i].picUrl end local data = { uris = urls, currentIndex = index } PicturePreviewActivity.start(fragment.getActivity(), JSON.encode(data)) end function newInstance() -- create view table local layout = { LinearLayout, layout_width = "match", layout_height = "match", orientation = "vertical", { SwipeRefreshLayout, id = "refreshLayout", layout_width = "match", { RecyclerView, id = "recyclerView", paddingTop = "25dp", clipToPadding = false, layout_width = "fill", layout_height = "fill", }, }, } local item_view = require('jike.item_msg') local data = { msg = {} } local ids = {} local fragment = LuaFragment.newInstance() local adapter fragment.setCreator(luajava.createProxy('androlua.LuaFragment$FragmentCreator', { onCreateView = function(inflater, container, savedInstanceState) return loadlayout(layout, ids) end, onViewCreated = function(view, savedInstanceState) adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() return #data.msg end, getItemViewType = function(position) return 0 end, onCreateViewHolder = function(parent, viewType) local views = {} local holder = LuaRecyclerHolder(loadlayout(item_view, views, RecyclerView)) holder.itemView.getLayoutParams().width = parent.getWidth() holder.itemView.setTag(views) holder.itemView.onClick = function(view) local position = holder.getAdapterPosition() + 1 launchDetail(fragment, data.msg[position]) end return holder end, onBindViewHolder = function(holder, position) position = position + 1 local msg = data.msg[position] local views = holder.itemView.getTag() views.tv_title.setText(msg.topic.content or 'error title') views.tv_content.setText(msg.content or '') views.tv_date.setText(msg.createdAt:sub(1, 10) or '') LuaImageLoader.loadWithRadius(views.iv_image, 3, msg.topic.squarePicture.thumbnailUrl) if msg.video then views.layout_video.setVisibility(0) LuaImageLoader.load(views.iv_video, msg.video.thumbnailUrl) else views.layout_video.setVisibility(8) end views.tv_collect.setText(string.format('%d', msg.likeCount)) views.tv_comment.setText(string.format('%d', msg.commentCount)) if msg.pictures and #msg.pictures > 0 then local pictures = msg.pictures local urls = {} local len = #pictures for i = 1, len do if len == 1 then urls[i] = pictures[i].picUrl views.iv_nine_grid.setSingleImgSize(pictures[i].width, pictures[i].height) else urls[i] = pictures[i].thumbnailUrl end end views.iv_nine_grid.setVisibility(0) views.iv_nine_grid.setAdapter(LuaNineGridViewAdapter(luajava.createProxy('androlua.widget.ninegride.LuaNineGridViewAdapter$AdapterCreator', { onDisplayImage = function(context, imageView, url) LuaImageLoader.load(imageView, url) end, onItemImageClick = function(context, imageView, index, list) launchPicturePreview(fragment, msg, index) end }))) views.iv_nine_grid.setImagesData(urls) else views.iv_nine_grid.setVisibility(8) end end, })) ids.recyclerView.setLayoutManager(LinearLayoutManager(fragment.getActivity())) ids.recyclerView.setAdapter(adapter) ids.refreshLayout.setOnRefreshListener(luajava.createProxy('android.support.v4.widget.SwipeRefreshLayout$OnRefreshListener', { onRefresh = function() fetchData(ids.refreshLayout, data, adapter, fragment, true) end })) ids.refreshLayout.setRefreshing(true) fetchData(ids.refreshLayout, data, adapter, fragment) -- getdata may call ther lua files end, })) return fragment end return { newInstance = newInstance } ================================================ FILE: lua/jike/fragment_recomend.lua ================================================ -- -- Created by IntelliJ IDEA. -- User: hanks -- Date: 2017/5/13 -- Time: 00:01 -- To change this template use File | Settings | File Templates. -- import "androlua.LuaImageLoader" import "androlua.LuaFragment" import "androlua.LuaHttp" import "android.support.v7.widget.RecyclerView" import "android.support.v4.widget.SwipeRefreshLayout" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.LinearLayoutManager" import "android.view.View" import "android.support.v4.widget.Space" import "androlua.widget.ninegride.LuaNineGridView" import "androlua.widget.ninegride.LuaNineGridViewAdapter" import "androlua.widget.picture.PicturePreviewActivity" import "androlua.widget.webview.WebViewActivity" local JSON = require("cjson") local uihelper = require("uihelper") local function clearTable(t) for k in pairs(t) do t[k] = nil end end local function fetchData(refreshLayout, data, adapter, fragment, reload) local url = string.format('http://app.jike.ruguoapp.com/1.0/recommendFeed/list') local postBody = { trigger = 'user' } if data.loadMoreKey and not reload then postBody.loadMoreKey = data.loadMoreKey postBody.trigger = 'auto' end local options = { url = url, method = 'POST', body = JSON.encode(postBody), headers = { "Cookie:io=0_Djvr_i0yLPqdsuFnzY; jike:sess.sig=d-IvFa3n5DhxWNim_0gVasNfTP0; jike:feed:latestNormalMessageId=592e495c7a27e200117d35b3; jike:recommendfeed:latestRecCreatedAt=2017-05-31T06:16:06.797Z; jike:sess=eyJfdWlkIjoiNTdmYjc2YTJhNzViY2ExMzAwZjYyMzkyIiwiX3Nlc3Npb25Ub2tlbiI6IkdRTUU0RmNkTHZhNTZlcExXR1BaYURDaDQifQ==; jikeSocketSticky=33b938e0c7b12816f8f2e027067ee82d69975eb2; jike:feed:latestFeedItemId=592e495c7a27e200117d35b3; jike:feed:noContentPullCount=0" } } LuaHttp.request(options, function(error, code, body) if error or code ~= 200 then print(' ================== get data error') return end local json = JSON.decode(body) data.loadMoreKey = json.loadMoreKey uihelper.runOnUiThread(fragment.getActivity(), function() if reload then clearTable(data.msg) end for i = 1, #json.data do local type = json.data[i].type if type == 'MESSAGE_RECOMMENDATION' then data.msg[#data.msg + 1] = json.data[i] end end refreshLayout.setRefreshing(false) adapter.notifyDataSetChanged() end) end) end -- local log = require("log") local function launchDetail(fragment, msg) local activity = fragment.getActivity() -- log.print_r(msg) if msg and msg.item and msg.item.linkUrl then WebViewActivity.start(activity, msg.item.linkUrl, 0xF12979FB) return end activity.toast('没有 url 可以打开') end local function launchPicturePreview(fragment, msg, index) local urls = {} for i = 1, #msg.item.pictureUrls do urls[i] = msg.item.pictureUrls[i].picUrl end local data = { uris = urls, currentIndex = index } PicturePreviewActivity.start(fragment.getActivity(), JSON.encode(data)) end function newInstance() -- create view table local layout = { LinearLayout, layout_width = "match", layout_height = "match", orientation = "vertical", { SwipeRefreshLayout, layout_width = "match", id = "refreshLayout", { RecyclerView, id = "recyclerView", paddingTop = "25dp", clipToPadding = false, layout_width = "fill", layout_height = "fill", }, }, } local item_view = require('jike.item_msg') local item_loading = { LinearLayout, layout_width = "match", layout_height = "72dp", gravity = "center", { ProgressBar, layout_width = "32dp", layout_height = "32dp", }, } local data = { msg = {} } local ids = {} local fragment = LuaFragment.newInstance() local adapter fragment.setCreator(luajava.createProxy('androlua.LuaFragment$FragmentCreator', { onCreateView = function(inflater, container, savedInstanceState) return loadlayout(layout, ids) end, onViewCreated = function(view, savedInstanceState) adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() if #data.msg > 0 then return #data.msg + 1 else return 0 end end, getItemViewType = function(position) if position > 0 and position == #data.msg then return 1 end return 0 end, onCreateViewHolder = function(parent, viewType) local views = {} local holder if viewType == 1 then holder = LuaRecyclerHolder(loadlayout(item_loading, views, RecyclerView)) else holder = LuaRecyclerHolder(loadlayout(item_view, views, RecyclerView)) holder.itemView.setTag(views) holder.itemView.onClick = function(view) local position = holder.getAdapterPosition() + 1 if position <= #data.msg then launchDetail(fragment, data.msg[position]) end end end holder.itemView.getLayoutParams().width = parent.getWidth() return holder end, onBindViewHolder = function(holder, position) position = position + 1 if (position == #data.msg + 1) then fetchData(ids.refreshLayout, data, adapter, fragment) -- getdata may call ther lua files return end local msg = data.msg[position] local views = holder.itemView.getTag() if views == nil then return end views.tv_title.setText(msg.item.title or 'error title') views.tv_content.setText(msg.item.content or '') views.tv_date.setText(msg.item.updatedAt:sub(1, 10) or '') views.tv_collect.setText(string.format('%s', msg.item.collectCount)) views.tv_comment.setText(string.format('%s', msg.item.commentCount)) LuaImageLoader.load(views.iv_image, msg.item.topic.thumbnailUrl) if msg.item.video then LuaImageLoader.load(views.iv_video, msg.item.video.thumbnailUrl) views.layout_video.setVisibility(0) else views.layout_video.setVisibility(8) end if msg.item.pictureUrls and #msg.item.pictureUrls > 0 then local pictures = msg.item.pictureUrls local urls = {} local len = #pictures for i = 1, len do if len == 1 then urls[i] = pictures[i].middlePicUrl views.iv_nine_grid.setSingleImgSize(pictures[i].width, pictures[i].height) else urls[i] = pictures[i].thumbnailUrl end end views.iv_nine_grid.setVisibility(0) views.iv_nine_grid.setAdapter(LuaNineGridViewAdapter(luajava.createProxy('androlua.widget.ninegride.LuaNineGridViewAdapter$AdapterCreator', { onDisplayImage = function(context, imageView, url) LuaImageLoader.load(imageView, url) end, onItemImageClick = function(context, imageView, index, list) launchPicturePreview(fragment, msg, index) end }))) views.iv_nine_grid.setImagesData(urls) else views.iv_nine_grid.setVisibility(8) end end, })) ids.recyclerView.setLayoutManager(LinearLayoutManager(fragment.getActivity())) ids.recyclerView.setAdapter(adapter) ids.refreshLayout.setOnRefreshListener(luajava.createProxy('android.support.v4.widget.SwipeRefreshLayout$OnRefreshListener', { onRefresh = function() fetchData(ids.refreshLayout, data, adapter, fragment, true) end })) ids.refreshLayout.setRefreshing(true) fetchData(ids.refreshLayout, data, adapter, fragment) -- getdata may call ther lua files end, })) return fragment end return { newInstance = newInstance } ================================================ FILE: lua/jike/info.json ================================================ { "id": "pub.hanks.jike", "name": "即刻", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b088fd40e849d7?w=150&h=150&f=png&s=1924", "main": "main.lua", "versionName": "1.0.5", "versionCode": 6, "desc": "即刻热门的信息流" } ================================================ FILE: lua/jike/item_msg.lua ================================================ return { LinearLayout, layout_width = "fill", paddingTop = "16dp", orientation = "vertical", background = "@drawable/layout_se", -- head { FrameLayout, layout_width = "match", layout_height = "36dp", paddingLeft = "16dp", paddingRight = "16dp", { ImageView, id = "iv_image", layout_width = "36dp", layout_height = "36dp", scaleType = "centerCrop" }, { TextView, id = "tv_title", layout_marginLeft = "44dp", layout_width = "fill", paddingRight = "16dp", maxLines = "1", ellipsize = "end", textSize = "13sp", textColor = "#4888B0", }, { TextView, id = "tv_date", layout_gravity = "bottom", layout_marginLeft = "44dp", layout_width = "fill", maxLines = "1", textSize = "11sp", textColor = "#BFBFBF", }, }, -- content { TextView, id = "tv_content", layout_width = "fill", layout_marginLeft = "16dp", layout_marginRight = "16dp", layout_marginTop = "12dp", lineSpacingMultiplier = '1.3', textSize = "14sp", textColor = "#404040", }, -- pictures { LuaNineGridView, id = "iv_nine_grid", layout_width = "match", layout_height = "200dp", gap = "4dp", maxSize = 9, visibility = "gone", layout_marginTop = "12dp", layout_marginLeft = "16dp", layout_marginRight = "16dp", }, -- video { FrameLayout, id = "layout_video", layout_width = "match", layout_height = "200dp", layout_marginTop = "12dp", layout_marginLeft = "16dp", layout_marginRight = "16dp", { ImageView, id = "iv_video", layout_width = "match", layout_height = "200dp", scaleType = "centerCrop" }, { View, layout_height = "match", layout_width = "match", background = "#66000000", }, { ImageView, layout_gravity = "center", layout_width = "40dp", layout_height = "40dp", src = "#jike/img/ic_video_play.png", }, }, -- foot { LinearLayout, layout_width = "match", layout_height = "56dp", paddingLeft = "16dp", paddingRight = "16dp", gravity = "center_vertical", orientation = "horizontal", { ImageView, layout_width = "20dp", layout_height = "20dp", src = "#jike/img/ic_like_border.png", }, { TextView, id = "tv_collect", layout_width = "70dp", paddingLeft = "4dp", textSize = "12sp", textColor = "#BFBFBF", }, { ImageView, layout_width = "20dp", layout_height = "20dp", src = "#jike/img/ic_comment.png" }, { TextView, id = "tv_comment", paddingLeft = "4dp", layout_width = "70dp", textSize = "12sp", textColor = "#BFBFBF", }, { ImageView, layout_width = "20dp", layout_height = "20dp", src = "#jike/img/ic_share.png" }, }, { View, layout_height = "8dp", layout_width = "fill", background = "#F0F3F5", } } ================================================ FILE: lua/jike/main.lua ================================================ require "import" import "android.widget.*" import "android.content.*" import "android.support.design.widget.BottomNavigationView" import "androlua.widget.viewpager.NoScrollViewPager" import "androlua.utils.ColorStateListFactory" import "androlua.LuaDrawable" import "androlua.adapter.LuaFragmentPageAdapter" -- local feedFragment = require("jike.fragment_feed") local hotFragment = require("jike.fragment_hot") local layout = { LinearLayout, orientation = "vertical", { NoScrollViewPager, id = "viewPager", layout_width = "fill", layout_weight = 1, background = "#ffffff", }, } local data = { titles = { "热门", "热门", "订阅" }, -- fragments = {recommendFragment.newInstance(), hotFragment.newInstance(), feedFragment.newInstance()}, fragments = { hotFragment.newInstance() }, } local adapter = LuaFragmentPageAdapter(activity.getSupportFragmentManager(), luajava.createProxy("androlua.adapter.LuaFragmentPageAdapter$AdapterCreator", { getCount = function() return #data.fragments end, getItem = function(position) position = position + 1 return data.fragments[position] end, getPageTitle = function(position) position = position + 1 return data.titles[position] end })) function onCreate(savedInstanceState) activity.setStatusBarColor(0x33000000) activity.setContentView(loadlayout(layout)) -- bottomView -- bottomView.setItemTextColor(ColorStateListFactory.newInstance(0xFFC7C7C7, 0xFF1E1E1E)) -- bottomView.setItemIconTintList(ColorStateListFactory.newInstance(0xFFC7C7C7, 0xFF1E1E1E)) -- local recommentDrawable = LuaDrawable.create('jike/img/recoment.png') -- local hotDrawable = LuaDrawable.create('jike/img/hot.png') -- local feedDrawable = LuaDrawable.create('jike/img/feed.png') -- bottomView.getMenu().add("推荐").setIcon(recommentDrawable) -- bottomView.getMenu().add("热门").setIcon(hotDrawable) -- bottomView.getMenu().add("订阅").setIcon(feedDrawable) -- bottomView.setOnNavigationItemSelectedListener(luajava.createProxy('android.support.design.widget.BottomNavigationView$OnNavigationItemSelectedListener', { -- onNavigationItemSelected = function(item) -- local title = item.getTitle() -- if title == "推荐" then viewPager.setCurrentItem(0, false) end -- if title == "热门" then viewPager.setCurrentItem(1, false) end -- if title == "订阅" then viewPager.setCurrentItem(2, false) end -- return true -- end -- })) -- viewpager viewPager.setAdapter(adapter) end ================================================ FILE: lua/magmoe-cos/info.json ================================================ { "id": "pub.hanks.magmoe-cos", "name": "moe cos", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b088b4ae47d08e?w=150&h=150&f=png&s=3459", "main": "main.lua", "versionName": "1.0.2", "versionCode": 4, "desc": "cosplay ,来源:mag.moe" } ================================================ FILE: lua/magmoe-cos/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaAdapter" import "androlua.widget.video.VideoPlayerActivity" import "androlua.LuaImageLoader" import "android.support.v7.widget.RecyclerView" import "android.support.v4.widget.SwipeRefreshLayout" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.StaggeredGridLayoutManager" import "androlua.widget.picture.PicturePreviewActivity" local JSON = require("cjson") local uihelper = require('uihelper') local data = {} local adapter local imageWidth = uihelper.getScreenWidth() / 2 local list = { index = 1, page = 1, urls = {} } local maxHeight = uihelper.dp2px(400) math.randomseed(os.time()) --- -然后不断产生随机数 list.page = math.floor(math.random() * 240) -- create view table local layout = { RelativeLayout, layout_width = "fill", layout_height = "fill", { RecyclerView, id = "recyclerView", layout_width = "fill", layout_height = "fill", }, { TextView, id = "tv_loading", text = "加载中....", textSize = "12sp", textColor = "#888888", layout_margin = "16dp", layout_alignParentBottom = true, layout_alignParentRight = true, } } local item_view = { FrameLayout, layout_width = "fill", { ImageView, id = "iv_image", layout_width = "fill", layout_height = "200dp", scaleType = "fitXY", }, { TextView, id = "tv_title", layout_gravity = "right", background = "#88000000", paddingLeft = "6dp", paddingRight = "6dp", paddingTop = "2dp", paddingBottom = "2dp", textSize = "10sp", visibility = 8, textColor = "#aaffffff", }, { View, id = "layer", layout_width = "fill", layout_height = "fill", background = "@drawable/layout_selector_tran", clickable = true, }, } local function fetchData() tv_loading.setVisibility(0) if list.index > #list.urls then local u = string.format('https://mag.moe/category/cosplay/page/%d', list.page) if list.page == 1 then u = 'https://mag.moe/category/cosplay' end for k, _ in pairs(list.urls) do list.urls[k] = nil end LuaHttp.request({ url = u,headers={ 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.79 Safari/537.36' } }, function(error, code, body) for url in string.gmatch(body, '
    <') do list.urls[#list.urls + 1] = url end list.index = 1 list.page = list.page + 1 if list.page > 244 then list.page = 1 end fetchData() end) return end local url = list.urls[list.index] LuaHttp.request({ url = url ,headers={ 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.79 Safari/537.36' }}, function(error, code, body) list.index = list.index + 1 uihelper.runOnUiThread(activity, function() local s = #data for img, w, h in string.gmatch(body, '(https://mmxv.imgmoe.com/.-)".-width="(%d+)" height="(%d+)"') do local item = { url = img, w = w, h = h } item.calcHeight = math.floor(imageWidth * tonumber(item.h) / tonumber(item.w)) if item.calcHeight > maxHeight then item.calcHeight = maxHeight end data[#data + 1] = item end tv_loading.setVisibility(8) adapter.notifyItemRangeChanged(s, #data) end) end) end local function launchDetail(item) local args = { uris = { item.url }, headers = { 'Referer:https://mag.moe', 'User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.109 Safari/537.36' } } PicturePreviewActivity.start(activity, JSON.encode(args)) end function onCreate(savedInstanceState) activity.setStatusBarColor(0x33000000) activity.setContentView(loadlayout(layout)) adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() return #data end, getItemViewType = function(position) return 0 end, onCreateViewHolder = function(parent, viewType) local views = {} local holder = LuaRecyclerHolder(loadlayout(item_view, views, RecyclerView)) holder.itemView.getLayoutParams().width = imageWidth holder.itemView.setTag(views) views.layer.onClick = function(view) local position = holder.getAdapterPosition() + 1 launchDetail(data[position]) end return holder end, onBindViewHolder = function(holder, position) position = position + 1 local item = data[position] local views = holder.itemView.getTag() views.iv_image.getLayoutParams().height = item.calcHeight LuaImageLoader.load(views.iv_image, item.url) if position == #data then fetchData() end end, })) recyclerView.setLayoutManager(StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)) recyclerView.setAdapter(adapter) fetchData() end ================================================ FILE: lua/magmoe-image/info.json ================================================ { "id": "pub.hanks.magmoe-image", "name": "MOE二次元", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b088b4ae47d08e?w=150&h=150&f=png&s=3459", "main": "main.lua", "versionName": "1.0.2", "versionCode": 4, "desc": "质量很好的二次元图, mag.moe" } ================================================ FILE: lua/magmoe-image/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaAdapter" import "androlua.widget.video.VideoPlayerActivity" import "androlua.LuaImageLoader" import "android.support.v7.widget.RecyclerView" import "android.support.v4.widget.SwipeRefreshLayout" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.StaggeredGridLayoutManager" import "androlua.widget.picture.PicturePreviewActivity" local JSON = require("cjson") local uihelper = require('uihelper') local data = {} local adapter local imageWidth = uihelper.getScreenWidth() / 2 local list = { index = 1, page = 1, urls = {} } local maxHeight = uihelper.dp2px(400) math.randomseed(os.time()) --- -然后不断产生随机数 list.page = math.floor(math.random() * 256) -- create view table local layout = { RelativeLayout, layout_width = "fill", layout_height = "fill", { RecyclerView, id = "recyclerView", layout_width = "fill", layout_height = "fill", }, { TextView, id = "tv_loading", text = "加载中....", textSize = "12sp", textColor = "#888888", layout_margin = "16dp", layout_alignParentBottom = true, layout_alignParentRight = true, } } local item_view = { FrameLayout, layout_width = "fill", { ImageView, id = "iv_image", layout_width = "fill", layout_height = "200dp", scaleType = "fitXY", }, { TextView, id = "tv_title", layout_gravity = "right", background = "#88000000", paddingLeft = "6dp", paddingRight = "6dp", paddingTop = "2dp", paddingBottom = "2dp", textSize = "10sp", visibility = 8, textColor = "#aaffffff", }, { View, id = "layer", layout_width = "fill", layout_height = "fill", background = "@drawable/layout_selector_tran", clickable = true, }, } local function fetchData() tv_loading.setVisibility(0) if list.index > #list.urls then local u = string.format('https://mag.moe/category/images/page/%d', list.page) if list.page == 1 then u = 'https://mag.moe/category/images' end for k, _ in pairs(list.urls) do list.urls[k] = nil end print(u) LuaHttp.request({ url = u, headers={ 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.79 Safari/537.36' } }, function(error, code, body) print(error, code,body) for url in string.gmatch(body, '') do print(url) list.urls[#list.urls + 1] = url end list.index = 1 list.page = list.page + 1 if list.page > 256 then list.page = 1 end fetchData() end) return end local url = list.urls[list.index] LuaHttp.request({ url = url, headers={ 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.79 Safari/537.36' } }, function(error, code, body) list.index = list.index + 1 uihelper.runOnUiThread(activity, function() local s = #data for img, w, h in string.gmatch(body, '(https://mmxv.imgmoe.com/.-)".-width="(%d+)" height="(%d+)"') do local item = { url = img, w = w, h = h } item.calcHeight = math.floor(imageWidth * tonumber(item.h) / tonumber(item.w)) if item.calcHeight > maxHeight then item.calcHeight = maxHeight end data[#data + 1] = item end tv_loading.setVisibility(8) adapter.notifyItemRangeChanged(s, #data) end) end) end local function launchDetail(item) local args = { uris = { item.url }, headers = { 'Referer:https://mag.moe', 'User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.109 Safari/537.36' } } PicturePreviewActivity.start(activity, JSON.encode(args)) end function onCreate(savedInstanceState) activity.setStatusBarColor(0x33000000) activity.setContentView(loadlayout(layout)) adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() return #data end, getItemViewType = function(position) return 0 end, onCreateViewHolder = function(parent, viewType) local views = {} local holder = LuaRecyclerHolder(loadlayout(item_view, views, RecyclerView)) holder.itemView.getLayoutParams().width = imageWidth holder.itemView.setTag(views) views.layer.onClick = function(view) local position = holder.getAdapterPosition() + 1 launchDetail(data[position]) end return holder end, onBindViewHolder = function(holder, position) position = position + 1 local item = data[position] local views = holder.itemView.getTag() views.iv_image.getLayoutParams().height = item.calcHeight LuaImageLoader.load(views.iv_image, item.url) if position == #data then fetchData() end end, })) recyclerView.setLayoutManager(StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)) recyclerView.setAdapter(adapter) fetchData() end ================================================ FILE: lua/notead/info.json ================================================ { "id": "pub.hanks.note_pro", "name": "便签Pro", "icon": "http://ww1.sinaimg.cn/large/8c9b876fly1ftf62owi8wj2040040wec.jpg", "main": "main.lua", "versionName": "1.0.0", "versionCode": 1, "private": true, "desc": "便签 Pro 版本获取" } ================================================ FILE: lua/notead/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.net.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaAdapter" import "androlua.widget.video.VideoPlayerActivity" import "androlua.LuaImageLoader" import "android.support.v7.widget.RecyclerView" import "android.support.v4.widget.SwipeRefreshLayout" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.LinearLayoutManager" import "androlua.widget.picture.PicturePreviewActivity" import "android.graphics.BitmapFactory" import "java.io.File" local layout = { LinearLayout, layout_width = "fill", layout_height = "fill", gravity = "center_horizontal", background = "#ffffff", orientation = "vertical", { ImageView, id = "logo", layout_width = "72dp", layout_height = "72dp", layout_marginTop = "70dp", }, { TextView, layout_width = "200dp", layout_height = "40dp", gravity = "center", text = "Note Pro", textColor = "#333333", textSize = "12sp" }, { TextView, layout_width = "wrap_content", layout_height = "wrap_content", layout_marginTop = "48dp", lineSpacingMultiplier = "2.5", text = "☑ 纯净无广告\n☑ 更多的布局样式\n☑ 自定义主背景\n☑ 便签置顶功能\n☑ 优先体验新功能\n☑ 支持便签项目长期开发", textColor = "#333333", textSize = "14sp", }, { TextView, id = "btn_get_pro", layout_width = "104dp", layout_height = "40dp", layout_gravity = "center", layout_marginTop = "56dp", background = "#C13D34", elevation = "2dp", gravity = "center", text = "Get", textColor = "#ffffff", textSize = "16sp", }, } function onCreate(savedInstanceState) activity.setStatusBarColor(0x33000000) activity.setContentView(loadlayout(layout)) LuaImageLoader.load(logo, "http://ww1.sinaimg.cn/large/8c9b876fly1ftf62owi8wj2040040wec.jpg") btn_get_pro.onClick = function() -- Toast.makeText(activity,"222",0).show() pcall(function() local intent = Intent(Intent.ACTION_VIEW) intent.setData(Uri.parse("market://details?id=xyz.hanks.note.pro")) activity.startActivity(intent) end) end end ================================================ FILE: lua/packwap/info.json ================================================ { "id": "pub.hanks.packwap", "name": "网页转插件", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b088aa62abd550?w=150&h=150&f=png&s=2811", "main": "main.lua", "versionName": "1.0", "versionCode": 2, "desc": "将网页应用打包成插件" } ================================================ FILE: lua/packwap/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- qiqu -- require "import" import "android.widget.*" import "android.content.*" import "androlua.LuaWebView" import "android.os.Build" import "android.view.View" import "android.support.v7.widget.AppCompatSeekBar" import "android.support.design.widget.FloatingActionButton" import "android.graphics.drawable.GradientDrawable" import "android.animation.ValueAnimator" import "java.lang.String" import "java.io.File" import "java.io.FileOutputStream" import "android.graphics.Canvas" import "android.graphics.Bitmap" import "android.graphics.Paint" import "android.graphics.Rect" local CompressFormat = import "android.graphics.Bitmap$CompressFormat" local Config = import "android.graphics.Bitmap$Config" local JSON = require "cjson" local Orientation = import "android.graphics.drawable.GradientDrawable$Orientation" local colors = luajava.createArray("int", { 0xFF72A3FF, 0xFF607dff }) local gd = GradientDrawable(Orientation.TOP_BOTTOM, colors) math.randomseed(os.time()) local colors = { 0xFF131313, 0xFFD90C17, 0xFF33BB68, 0xFF3EA6FC, 0xFFE96138, 0xFFFDA236, 0xFF1E89E9, 0xFFF13525, 0xFF3EC0D5, 0xFF1EBBA5, 0xFF3273EC, 0xFF22D926, 0xFFA630BF } local luaTemp = [[ require "import" import "android.widget.*" import "android.content.*" import "androlua.LuaWebView" local layout = { LinearLayout, orientation = "vertical", layout_width = "fill", layout_height = "fill", statusBarColor = "{ssColor}", { LuaWebView, id = "webview", layout_width = "fill",layout_height = "fill", } } function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) webview.loadUrl("{url}") end function onBackPressed() if webview.canGoBack() then webview.goBack() return true end return false end function onDestroy() pcall(function() webview.release() end) end ]] local function write(filePath, txt) pcall(function() local file = File(filePath) file.getParentFile().mkdirs() local f = io.open(filePath, 'wb') f:write(txt) f:close() end) end local function drawLogo(txt, filePath, bgColor) local c = '氢' if txt then c = String(txt).substring(0, 1) end if bgColor == 0x33000000 then bgColor = colors[math.random(#colors)] end local file = File(filePath) file.getParentFile().mkdirs() local bm = Bitmap.createBitmap(100, 100, Config.RGB_565) local canvas = Canvas(bm) canvas.drawColor(bgColor) local mPaint = Paint(1) mPaint.setTextSize(40) mPaint.setColor(0xFFFFFFFF) local bounds = Rect() mPaint.getTextBounds(c, 0, 1, bounds) canvas.drawText(c, 47 - bounds.width() / 2, 47 + bounds.height() / 2, mPaint) local stream = FileOutputStream(file) bm.compress(CompressFormat.PNG, 100, stream) stream.close() end local function pack(params) if params == nil or params.url == nil or params.name == nil then return end local dirName = tostring(os.time()) local pluginId = 'pub.hydrogen' .. dirName local infoPath = string.format('%s/%s/info.json', luajava.luaextdir, dirName) local luaPath = string.format('%s/%s/main.lua', luajava.luaextdir, dirName) local logoPath = string.format('%s/%s/logo.png', luajava.luaextdir, dirName) local info = { id = pluginId, name = params.name, icon = "logo.png", main = "main.lua", versionCode = 1, versionName = "1.0.0", desc = params.name .. " - 氢页面", } local code = luaTemp:gsub('{url}', params.url):gsub('{ssColor}', string.format("#%x", params.themeColor)) write(infoPath, JSON.encode(info)) write(luaPath, code) drawLogo(params.name, logoPath, params.themeColor) activity.toast('打包成功,到主界面看一下吧!') end -- create view table local function editText(id, hint, inputType) local t = { EditText, id = id, layout_width = "fill", layout_height = "48dp", textColor = "#444444", hintTextColor = "#AAAAAA", textSize = "13sp", hint = hint, singleLine = true, layout_marginBottom = "12dp", } if inputType then t.inputType = inputType end return t end local layout = { FrameLayout, layout_width = "match", layout_height = "match", background = "#f1f1f1", { ImageView, id = 'iv_bg', layout_width = "match", layout_height = "360dp", }, { LinearLayout, layout_width = "match", gravity = "center_horizontal", orientation = "vertical", paddingLeft = "16dp", paddingRight = "16dp", { TextView, layout_marginTop = "40dp", text = "打包网页", textColor = "#ffffff", textSize = "20sp", }, { TextView, id = 'tv_left', layout_marginTop = "40dp", textColor = "#fafafa", text = ' 该插件可以将网页应用转化成氢应用的插件,这样就可把一些做的比较好的网站或在线H5小游戏直接加入氢应用了,给你轻而纯粹的应用体验。', lineSpacingMultiplier = 1.4, }, { FrameLayout, layout_width = "match", layout_marginTop = "56dp", layout_marginLeft = "16dp", layout_marginRight = "16dp", { LinearLayout, layout_width = "match", layout_marginBottom = "32dp", background = "#ffffff", orientation = "vertical", paddingBottom = "24dp", paddingLeft = "16dp", paddingRight = "16dp", paddingTop = "16dp", editText('et_url', '网址 http(s)://', 11), editText('et_name', '插件名称'), editText('et_themeColor', '主题色(如 #3273EC 选填)'), { Button, id = "bt_test", layout_width = "fill", layout_marginTop = "24dp", layout_marginBottom = "48dp", textSize = "13sp", text = "戳我预览", background = "#eeeeee", textColor = "#444444", } }, { FloatingActionButton, id = "fab", layout_width = "48dp", layout_height = "48dp", layout_gravity = 81, layout_marginBottom = "12dp", src = '#packwap/check.png', elevation = "2dp", }, }, }, } local function getParams() local url = et_url.getText().toString() local name = et_name.getText().toString() local c = et_themeColor.getText().toString() if c ~= '' and (not c:find("^#")) then c = '#' .. c et_themeColor.setText(c) end local color = string.match(c, '#([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])') if color == nil then color = 0x33000000 else color = tonumber('0xff' .. color) end return { url = url, name = name, themeColor = color } end function onCreate(savedInstanceState) activity.setStatusBarColor(0x00000000) activity.setContentView(loadlayout(layout)) if Build.VERSION.SDK_INT < 16 then iv_bg.setBackgroundDrawable(gd) else iv_bg.setBackground(gd) end bt_test.onClick = function() local params = getParams() if params.url == '' then activity.toast('URL和名字不能为空') return end if not params.url:find("^http") then params.url = 'http://' .. params.url et_url.setText(params.url) end local intent = Intent(activity, LuaActivity) intent.putExtra("luaPath", 'packwap/testwap.lua') intent.putExtra("url", params.url) intent.putExtra("params", JSON.encode(params)) activity.startActivity(intent) end fab.onClick = function() local params = getParams() if params.url == '' or params.name == '' then activity.toast('URL和名字不能为空') return end pack(params) end end ================================================ FILE: lua/packwap/testwap.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- qiqu -- require "import" import "android.widget.*" import "android.content.*" import "androlua.LuaWebView" local JSON = require "cjson" local log = require "log" -- create view table local layout = { LinearLayout, orientation = "vertical", layout_width = "fill", layout_height = "fill", statusBarColor = "#33000000", { LuaWebView, id = "webview", layout_width = "fill", layout_height = "fill", } } function onCreate(savedInstanceState) local s = activity.getIntent().getStringExtra("params") local params = JSON.decode(s) if params.url == nil then params.url = "https://www.coolapk.com/apk/pub.hydrogen.android" end layout.statusBarColor = string.format("#%x",params.themeColor) activity.setContentView(loadlayout(layout)) webview.loadUrl(params.url ) end function onBackPressed() if webview.canGoBack() then webview.goBack() return true end return false end function onDestroy() pcall(function( ) webview.release() end) end ================================================ FILE: lua/papapatimer/info.json ================================================ { "id": "pub.hanks.papapatimer", "name": "啪啪倒计时", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b088a4ee4ca73c?w=150&h=150&f=png&s=2582", "main": "main.lua", "versionName": "1.0", "versionCode": 2, "desc": "小工具,计算相处多少天后啪啪啪" } ================================================ FILE: lua/papapatimer/main.lua ================================================ require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "android.support.v7.widget.AppCompatSeekBar" import "android.support.design.widget.FloatingActionButton" import "android.graphics.drawable.GradientDrawable" import "android.os.Build" import "android.animation.ValueAnimator" local Orientation = import "android.graphics.drawable.GradientDrawable$Orientation" local colors = luajava.createArray("int", { 0xFF72A3FF, 0xFF607dff }) local gd = GradientDrawable(Orientation.TOP_BOTTOM, colors) local function text(text) return { TextView, layout_marginLeft = '16dp', layout_marginTop = '12dp', layout_marginBottom = '4dp', text = text, textColor = '#AAAAAA', textSize = "10sp", } end local function seekBar(id_sb, max, progress, id_tv, text) return { LinearLayout, gravity = 'center_vertical', layout_width = 'match', paddingLeft = '2dp', { AppCompatSeekBar, layout_weight = 1, id = id_sb, max = max, progress = progress, }, { TextView, layout_width = '20dp', id = id_tv, gravity = 'right', textSize = '10sp', text = text, }, } end local layout = { FrameLayout, layout_width = "match", layout_height = "match", background = "#f1f1f1", { ImageView, id = 'iv_bg', layout_width = "match", layout_height = "360dp", }, { FrameLayout, layout_width = "match", layout_gravity = "bottom", layout_marginBottom = "24dp", layout_marginLeft = "16dp", layout_marginRight = "16dp", { LinearLayout, layout_width = "match", layout_marginBottom = "32dp", background = "#ffffff", orientation = "vertical", paddingBottom = "24dp", paddingLeft = "16dp", paddingRight = "16dp", paddingTop = "16dp", text('女方年龄 (40岁以上女性本公式不适用)'), seekBar('seek_af', 40, 20, 'tv_af', '20'), text('女方外貌 (10为满分)'), seekBar('seek_lf', 10, 5, 'tv_lf', '5'), text('男方外貌 (10为满分)'), seekBar('seek_lm', 10, 5, 'tv_lm', '5'), text('男方资产(价值),每10万港元为1个单位,无上限'), { LinearLayout, gravity = 'center_vertical', layout_width = 'match', paddingLeft = '2dp', { AppCompatSeekBar, layout_weight = 1, id = 'seek_wm', max = 100, progress = 0, }, { EditText, inputType = 'number', background = '#00FFFFFF', layout_width = '20dp', id = 'tv_wm', gravity = 'right', textSize = '10sp', text = '0', }, }, text('女方曾有性行为的男性数目(性伴侣)'), seekBar('seek_sf', 15, 0, 'tv_sf', '0'), { View, layout_height = '16dp', }, }, { FloatingActionButton, id = "fab", layout_width = "48dp", layout_height = "48dp", layout_gravity = 81, layout_marginBottom = "12dp", src = '#papapatimer/check.png', elevation = "2dp", }, }, { LinearLayout, layout_width = "match", layout_height = "match", gravity = "center_horizontal", orientation = "vertical", { ImageView, layout_width = '120dp', layout_height = '16dp', layout_marginTop = "34dp", src = '#papapatimer/title.png', }, { LinearLayout, layout_marginTop = '24dp', gravity = "bottom", { TextView, id = 'tv_left', textColor = "#fafafa", text = '交往', textSize = "10sp", }, { TextView, id = "tv_result", layout_marginLeft = "8dp", layout_marginRight = "8dp", textColor = "#ffffff", textSize = "36sp", text = '999', }, { TextView, id = 'tv_right', text = '天后', textColor = "#fafafa", textSize = "10sp", }, }, { LinearLayout, layout_marginTop = '12dp', gravity = "center_vertical", { ImageView, layout_width = '12dp', layout_height = '12dp', src = '#papapatimer/me.png', }, { ImageView, id = 'iv_line', layout_width = '100dp', layout_height = '36dp', layout_margin = '12dp', src = '#papapatimer/line.png', }, { ImageView, layout_width = '12dp', layout_height = '12dp', src = '#papapatimer/fe.png', }, }, }, } local function bindSeekText(sb_id, tv_id) sb_id.setOnSeekBarChangeListener(luajava.createProxy('android.widget.SeekBar$OnSeekBarChangeListener', { onProgressChanged = function(sb, progress, fromUser) tv_id.setText(string.format('%d', progress)) end })) end function onCreate(savedInstanceState) activity.setStatusBarColor(0x00000000) activity.setContentView(loadlayout(layout)) bindSeekText(seek_af, tv_af) bindSeekText(seek_lf, tv_lf) bindSeekText(seek_lm, tv_lm) bindSeekText(seek_wm, tv_wm) bindSeekText(seek_sf, tv_sf) if Build.VERSION.SDK_INT < 16 then iv_bg.setBackgroundDrawable(gd) else iv_bg.setBackground(gd) end fab.onClick = function(view) local v_af = seek_af.getProgress() local v_lm = seek_lm.getProgress() local v_lf = seek_lf.getProgress() local v_sf = seek_sf.getProgress() local v_wm = tonumber(tv_wm.getText().toString()) local res = ((40 - v_af) * (40 - v_af) + v_lf * v_lf * v_lf) * 10 / ((v_lm * v_lm + v_wm) * (v_sf + 1) * (v_sf + 1)); local text = string.format('%.2f', res) if text:find('.') then text = text:gsub('0+$', '') if text:find('[.]$') then text = text:sub(1, #text - 1) end end if text == 'inf' then text = '洗洗睡吧' tv_left.setVisibility(8) tv_right.setVisibility(8) else tv_left.setVisibility(0) tv_right.setVisibility(0) end tv_result.setText(text) local arr = luajava.createArray('float', { 1, 1.1, 0.9, 1.1, 0.9, 1 }) local animator = ValueAnimator.ofFloat(arr).setDuration(500) animator.addUpdateListener(luajava.createProxy('android.animation.ValueAnimator$AnimatorUpdateListener', { onAnimationUpdate = function(animation) local value = animation.getAnimatedValue(); if iv_line then iv_line.setScaleX(value) iv_line.setScaleY(value) end end })) animator.start(); end local info = [[ 一条在线计算拍拖几天可以啪啪啪公式,纯理论,只作参考,实战一定有误差,过可理论日子都没啪到请先自我检讨。 可以作为自己交往妹子,什么时候提出啪啪啪作为依据,以免被拒绝好尴尬。。。 ]] iv_line.onClick = function() local DialogBuilder = import "android.app.AlertDialog$Builder" DialogBuilder(activity).setMessage(info).show() end end ================================================ FILE: lua/pengpai/activity_news_detail.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaWebView" import "androlua.LuaHttp" import "android.support.v7.widget.Toolbar" import "android.net.Uri" import "pub.hydrogen.android.R" import "androlua.widget.webview.WebViewActivity" local android_R = import "android.R" local uihelper = require "uihelper" -- create view table local layout = { LinearLayout, layout_width = "fill", layout_height = "fill", orientation = "vertical", statusBarColor = "#33000000", { Toolbar, background = '#ffffff', id = 'toolbar', layout_width = "match", layout_height = "56dp", }, { FrameLayout, layout_width = "fill", layout_height = "fill", { LuaWebView, id = "webview", layout_width = "fill", layout_height = "fill", }, { ProgressBar, layout_gravity = "center", id = "progressBar", layout_width = "40dp", layout_height = "40dp", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", } } } local css = [[ video{width:100%}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}html{font-family:sans-serif;-webkit-text-size-adjust:100%}body{font-family:'Helvetica Neue',Helvetica,Arial,Sans-serif;background:#fff;padding-top:0;margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{margin:.67em 0}h1,h2,h3,h4,h5,h6{font-size:16px}abbr[title]{border-bottom:1px dotted}hr{box-sizing:content-box;height:0}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap}q{quotes:\201C\201D\2018\2019}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0;vertical-align:middle;color:transparent;font-size:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}table{border-collapse:collapse;border-spacing:0;overflow:hidden}a{text-decoration:none}blockquote{border-left:3px solid #d0e5f2;font-style:normal;display:block;vertical-align:baseline;font-size:100%;margin:.5em 0;padding:0 0 0 1em}ul,ol{padding-left:20px}.content{color:#444;line-height:1.6em;font-size:16px;margin:16px}.content img{max-width:100%;display:block;margin:30px auto}.content img+img{margin-top:15px}.content img[src*="zhihu.com/equation"]{display:inline-block;margin:0 3px}.content a{color:#259}.content a:hover{text-decoration:underline} ]] local htmlTemplate = [[
    %s
    ]] local function getData(url) print(url) LuaHttp.request({ url = url }, function(error, code, body) local content = string.match(body, 'class="news_part%s+.-">%s+(.-)
    (.-)') uihelper.runOnUiThread(activity, function() webview.loadData(data, "text/html; charset=UTF-8", nil) activity.setTitle(title) end) end) end local url function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) activity.setSupportActionBar(toolbar) activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true) activity.getSupportActionBar().setDisplayShowHomeEnabled(true) activity.setTitle('加载中...') url = activity.getIntent().getStringExtra('url') webview.setVisibility(0) progressBar.setVisibility(8) getData(url) end function onDestroy() if webview then webview.getParent().removeView(webview) webview.destroy() webview = nil end end function onCreateOptionsMenu(menu) menu.add("浏览器打开") return true end function onOptionsItemSelected(item) if item.getItemId() == android_R.id.home then activity.onBackPressed() return end local title = item.getTitle() if title == "浏览器打开" then activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url))) end end ================================================ FILE: lua/pengpai/fragment_news.lua ================================================ -- -- Created by IntelliJ IDEA. -- User: hanks -- Date: 2017/5/13 -- Time: 00:01 -- To change this template use File | Settings | File Templates. -- require "import" import "android.widget.*" import "android.content.*" import "androlua.LuaAdapter" import "androlua.LuaImageLoader" import "androlua.LuaFragment" import "androlua.LuaHttp" import "androlua.widget.webview.WebViewActivity" import "android.support.v4.widget.SwipeRefreshLayout" local uihelper = require "uihelper" local JSON = require "cjson" local log = require "log" local function getData(params, data, adapter, fragment, swipe_layout, reload) local url = string.format('http://www.thepaper.cn/load_chosen.jsp?nodeids=%s&topCids=1772933,1773313,1773404,1773624,1773571,&pageidx=%d&lastTime=%d', params.rid, params.page, os.time() * 1000) print(url) LuaHttp.request({ url = url }, function(error, code, body) if error or code ~= 200 then return end local arr = {} for img, url, title, p, source, ptime in string.gmatch(body, 'class="tiptitleImg".-%s+(.-)%s+.-

    (.-)

    %s+
    .-(.-).-(.-)') do local item = { img = 'http:'.. img, title = title, p = p, ptime = ptime, source = source, url = url } print(item) arr[#arr + 1] = item end uihelper.runOnUiThread(fragment.getActivity(), function() if reload then for k, _ in pairs(date) do data[k] = nil end end for i = 1, #arr do data[#data + 1] = arr[i] end params.page = params.page + 1 adapter.notifyDataSetChanged() swipe_layout.setRefreshing(false) end) end) end local function launchDetail(fragment, item) local activity = fragment.getActivity() if item == nil or item.url == nil then activity.toast('没有 url 可以打开') return end local url = item.url if not item.url:find('^http://') then url = 'http://m.thepaper.cn/' .. item.url end local activity = fragment.getActivity() local intent = Intent(activity, LuaActivity) intent.putExtra("luaPath", 'pengpai/activity_news_detail.lua') intent.putExtra("url", url) activity.startActivity(intent) end local function newInstance(rid) -- create view table local layout = { SwipeRefreshLayout, layout_width = "fill", layout_height = "fill", id = "swipe_layout", { ListView, id = "listview", layout_width = "fill", layout_height = "fill", } } local item_view = { RelativeLayout, layout_width = "fill", layout_height = "wrap", paddingLeft = "16dp", paddingRight = "12dp", paddingTop = "16dp", paddingBottom = "16dp", { ImageView, id = "iv_image", layout_width = "110dp", layout_height = "83dp", layout_marginRight = "12dp", scaleType = "centerCrop", }, { TextView, id = "tv_title", layout_toRightOf = "iv_image", layout_width = "fill", maxLines = "2", lineSpacingMultiplier = 1.3, textSize = "16sp", textColor = "#222222", }, { TextView, id = "tv_date", layout_toRightOf = "iv_image", layout_alignParentBottom = true, layout_width = "fill", textSize = "12sp", textColor = "#aaaaaa", } } local singleImg = { ImageView, layout_width = (uihelper.getScreenWidth() - uihelper.dp2px(44)) / 3, layout_height = "83dp", layout_marginRight = "8dp", scaleType = "centerCrop", } local hadLoadData local isVisible local lastId local params = { rid = rid, page = 0 } local data = {} local ids = {} local adapter local fragment = LuaFragment.newInstance() local function lazyLoad() if not isVisible then return end if hadLoadData then return end if adapter == nil then return end hadLoadData = true getData(params, data, adapter, fragment, ids.swipe_layout) end fragment.setCreator(luajava.createProxy('androlua.LuaFragment$FragmentCreator', { onCreateView = function(inflater, container, savedInstanceState) return loadlayout(layout, ids) end, onViewCreated = function(view, savedInstanceState) adapter = LuaAdapter(luajava.createProxy("androlua.LuaAdapter$AdapterCreator", { getCount = function() return #data end, getView = function(position, convertView, parent) position = position + 1 -- lua 索引从 1开始 if convertView == nil then local views = {} -- store views convertView = loadlayout(item_view, views, ListView) convertView.getLayoutParams().width = parent.getWidth() convertView.setTag(views) end local views = convertView.getTag() local item = data[position] if item then views.iv_image.setVisibility(0) LuaImageLoader.load(views.iv_image, item.img) views.tv_date.setText(string.format('%s %s', item.ptime, item.source)) views.tv_title.setText(item.title) end if position == #data then getData(params, data, adapter, fragment, ids.swipe_layout) end return convertView end })) ids.listview.setAdapter(adapter) ids.listview.setOnItemClickListener(luajava.createProxy("android.widget.AdapterView$OnItemClickListener", { onItemClick = function(adapter, view, position, id) launchDetail(fragment, data[position + 1]) end, })) ids.swipe_layout.setRefreshing(true) ids.swipe_layout.setOnRefreshListener(luajava.createProxy('android.support.v4.widget.SwipeRefreshLayout$OnRefreshListener', { onRefresh = function() getData(params, data, adapter, fragment, ids.swipe_layout, true) end })) lazyLoad() end, onUserVisible = function(visible) isVisible = visible lazyLoad() end, })) return fragment end return { newInstance = newInstance } ================================================ FILE: lua/pengpai/info.json ================================================ { "id": "pub.hanks.pengpainews", "name": "澎湃新闻", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b0889e8328d952?w=150&h=150&f=png&s=6544", "main": "main.lua", "versionName": "1.0.6", "versionCode": 7, "desc": "澎湃新闻客户端" } ================================================ FILE: lua/pengpai/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "android.support.v4.view.ViewPager" import "android.support.design.widget.TabLayout" import "androlua.adapter.LuaFragmentPageAdapter" import "android.support.v7.widget.Toolbar" import "android.net.Uri" local uihelper = require "uihelper" local JSON = require "cjson" local log = require "log" local fragmentNews = require "pengpai/fragment_news" -- create view table local layout = { LinearLayout, layout_width = "fill", layout_height = "fill", orientation = "vertical", statusBarColor = "#33000000", { Toolbar, background = '#ffffff', id = 'toolbar', layout_width = "match", layout_height = "56dp", { ImageView, layout_width = "54dp", layout_height = "32dp", scaleType = "fitXY", src = "#pengpai/logo.png" }, }, { FrameLayout, layout_width = "fill", layout_height = "fill", { ViewPager, id = "viewPager", layout_width = "fill", layout_height = "fill", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", } } } local data = { titles = {}, fragments = {}, } table.insert(data.fragments, fragmentNews.newInstance('25949')) table.insert(data.titles, '精选') local adapter = LuaFragmentPageAdapter(activity.getSupportFragmentManager(), luajava.createProxy("androlua.adapter.LuaFragmentPageAdapter$AdapterCreator", { getCount = function() return #data.fragments end, getItem = function(position) position = position + 1 return data.fragments[position] end, getPageTitle = function(position) position = position + 1 return data.titles[position] end })) function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) activity.setSupportActionBar(toolbar) activity.setTitle('') viewPager.setAdapter(adapter) viewPager.setOffscreenPageLimit(#data.fragments) viewPager.setCurrentItem(0) end function onCreateOptionsMenu(menu) menu.add("网页版") return true end function onOptionsItemSelected(item) local title = item.getTitle() if title == "网页版" then activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse('http://m.thepaper.cn'))) end end ================================================ FILE: lua/pixiv/info.json ================================================ { "id": "pub.hanks.pixiv", "name": "P站排行", "icon": "http://ww1.sinaimg.cn/large/8c9b876fly1fh803sp03xj2046046a9x.jpg", "main": "main.lua", "versionName": "1.0.1", "versionCode": 2, "private": true, "desc": "P站每日插画排行" } ================================================ FILE: lua/pixiv/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaAdapter" import "androlua.widget.video.VideoPlayerActivity" import "androlua.LuaImageLoader" import "android.support.v7.widget.RecyclerView" import "android.support.v4.widget.SwipeRefreshLayout" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.StaggeredGridLayoutManager" import "androlua.widget.picture.PicturePreviewActivity" local JSON = require("cjson") local uihelper = require('uihelper') local date = os.date("%Y%m%d", os.time() - 60 * 60 * 24) -- 20170518 local page = 1 local data = {} local adapter local imageWidth = uihelper.getScreenWidth() / 2 -- create view table local layout = { RecyclerView, id = "recyclerView", layout_width = "fill", layout_height = "fill", } local item_view = { FrameLayout, layout_width = "fill", { ImageView, id = "iv_image", layout_width = "fill", layout_height = "200dp", scaleType = "fitXY", }, { TextView, id = "tv_title", layout_gravity = "right", background = "#88000000", paddingLeft = "6dp", paddingRight = "6dp", paddingTop = "2dp", paddingBottom = "2dp", textSize = "10sp", textColor = "#aaffffff", }, { View, id = "layer", layout_width = "fill", layout_height = "fill", background = "@drawable/layout_selector_tran", clickable = true, }, } local function offsetDate() local y = date:sub(1, 4) local m = date:sub(5, 6) local d = date:sub(7, 8) date = os.date("%Y%m%d", os.time({ year = y, month = m, day = d, hour = 1, min = 1, sec = 1 }) - 60 * 60 * 24) page = 1 end local function fetchData() local url = string.format('http://www.pixiv.net/ranking.php?mode=daily&content=illust&p=%s&format=json&date=%s', page, date) LuaHttp.request({ url = url }, function(error, code, body) if error or code ~= 200 then offsetDate() fetchData() return end local json = JSON.decode(body) if json.error then offsetDate() fetchData() return end if json.next == nil or json.next == false then page = 1 date = json.prev_date else page = json.next end local arr = json.contents uihelper.runOnUiThread(activity, function() local s = #data for i = 1, #arr do local item = arr[i] item.calcHeight = math.floor(imageWidth * item.height / item.width) data[#data + 1] = item end adapter.notifyItemRangeChanged(s, #data) end) end) end local function launchDetail(item) local args = { uris = {}, headers = { "referer:https://pximg.net" } } local original = item.url:gsub('/c/240x480', '') local count = tonumber(item.illust_page_count) if count then for i = 1, count do local l, r = original:find('_p(%d+).-jpg$') if l and r then local rr = original:sub(l, r):gsub('_p(%d+)', '_p' .. i - 1) args.uris[#args.uris + 1] = original:sub(1, l - 1) .. rr end end end PicturePreviewActivity.start(activity, JSON.encode(args)) end function onCreate(savedInstanceState) activity.setStatusBarColor(0x33000000) activity.setContentView(loadlayout(layout)) adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() return #data end, getItemViewType = function(position) return 0 end, onCreateViewHolder = function(parent, viewType) local views = {} local holder = LuaRecyclerHolder(loadlayout(item_view, views, RecyclerView)) holder.itemView.getLayoutParams().width = imageWidth holder.itemView.setTag(views) views.layer.onClick = function(view) local position = holder.getAdapterPosition() + 1 launchDetail(data[position]) end return holder end, onBindViewHolder = function(holder, position) position = position + 1 local item = data[position] local views = holder.itemView.getTag() views.iv_image.getLayoutParams().height = item.calcHeight if tonumber(item.illust_page_count) == 1 then views.tv_title.setVisibility(8) else views.tv_title.setVisibility(0) views.tv_title.setText(item.illust_page_count .. 'P') end LuaImageLoader.load(views.iv_image, item.url) if position == #data then fetchData() end end, })) recyclerView.setLayoutManager(StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)) recyclerView.setAdapter(adapter) fetchData() end ================================================ FILE: lua/proxy_fetch/info.json ================================================ { "id": "pub.hanks.proxy_fetch", "private": true, "name": "抓取代理", "icon": "http://ww1.sinaimg.cn/large/8c9b876fly1fhmrw8qtpej2046046t8h.jpg", "main": "main.lua", "versionName": "1.0.0", "versionCode": 1, "desc": "抓取代理" } ================================================ FILE: lua/proxy_fetch/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaAdapter" import "androlua.widget.video.VideoPlayerActivity" import "androlua.LuaImageLoader" import "android.support.v7.widget.RecyclerView" import "android.support.v4.widget.SwipeRefreshLayout" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.StaggeredGridLayoutManager" import "androlua.widget.picture.PicturePreviewActivity" import "android.support.v4.app.ActivityCompat" import "android.Manifest" local filehelper = require("filehelper") local JSON = require("cjson") local uihelper = require('uihelper') local page = 1 local data = {} local adapter local imageWidth = uihelper.getScreenWidth() / 2 local filePath = '/sdcard/ip_pool.json' -- create view table local layout = { LinearLayout, orientation = "vertical", layout_width = "fill", layout_height = "fill", { TextView, id = "tv_go", layout_width = "fill", layout_height = "56dp", layout_margin = "40dp", background = "#88000000", gravity = "center", textSize = "14sp", text = "开始", textColor = "#aaffffff", }, { ScrollView, layout_width = "fill", layout_height = "fill", { TextView, id = "tv_result", layout_width = "fill", layout_height = "fill", textSize = "10sp", textColor = "#414141", }, } } local function fetchData() local date = os.time() * 1000 local url = string.format('http://47.97.7.119:8080/proxypool/proxyController/queryProxy?page=1&rows=150&sort=proxyAddress&order=desc&_=%d', date) print(url) LuaHttp.request({ url = url }, function(error, code, body) if error or code ~= 200 then uihelper.runOnUiThread(activity, function() tv_result.setText('失败:' .. code) end) return end local json = JSON.decode(body) local arr = json.rows uihelper.runOnUiThread(activity, function() for i = 1, #arr do local item = arr[i] data[#data + 1] = { ip = item.proxyAddress, port = item.proxyPort, type = item.proxyType } end local result = JSON.encode(data) tv_result.setText(result) filehelper.writefile(filePath, result) end) end) end function onCreate(savedInstanceState) activity.setStatusBarColor(0x33000000) activity.setContentView(loadlayout(layout)) tv_go.onClick = function() fetchData() end ActivityCompat.requestPermissions(activity, {Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0x23); end ================================================ FILE: lua/proxy_fetch_mogu/info.json ================================================ { "id": "pub.hanks.proxy_fetch_mogu", "private": true, "name": "蘑菇代理", "icon": "http://ww1.sinaimg.cn/large/8c9b876fly1fhmrw8qtpej2046046t8h.jpg", "main": "main.lua", "versionName": "1.0.0", "versionCode": 1, "desc": "抓取代理" } ================================================ FILE: lua/proxy_fetch_mogu/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaAdapter" import "androlua.widget.video.VideoPlayerActivity" import "androlua.LuaImageLoader" import "android.support.v7.widget.RecyclerView" import "android.support.v4.widget.SwipeRefreshLayout" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.StaggeredGridLayoutManager" import "androlua.widget.picture.PicturePreviewActivity" import "android.support.v4.app.ActivityCompat" import "android.Manifest" local filehelper = require("filehelper") local JSON = require("cjson") local uihelper = require('uihelper') local page = 1 local data = {} local adapter local imageWidth = uihelper.getScreenWidth() / 2 local filePath = '/sdcard/ip_pool.json' -- create view table local layout = { LinearLayout, orientation = "vertical", layout_width = "fill", layout_height = "fill", { TextView, id = "tv_go", layout_width = "fill", layout_height = "56dp", layout_margin = "40dp", background = "#88000000", gravity = "center", textSize = "14sp", text = "开始", textColor = "#aaffffff", }, { ScrollView, layout_width = "fill", layout_height = "fill", { TextView, id = "tv_result", layout_width = "fill", layout_height = "fill", textSize = "10sp", textColor = "#414141", }, } } local function fetchData() local date = os.time() * 1000 local url = string.format('http://www.mogumiao.com/proxy/free/listFreeIp') print(url) LuaHttp.request({ url = url }, function(error, code, body) if error or code ~= 200 then uihelper.runOnUiThread(activity, function() tv_result.setText('失败:' .. code) end) return end local json = JSON.decode(body) local arr = json.msg uihelper.runOnUiThread(activity, function() for i = 1, #arr do local item = arr[i] data[#data + 1] = { ip = item.ip, port = item.port, type = "mogu", } end local result = JSON.encode(data) tv_result.setText(result) filehelper.writefile(filePath, result) end) end) end function onCreate(savedInstanceState) activity.setStatusBarColor(0x33000000) activity.setContentView(loadlayout(layout)) tv_go.onClick = function() fetchData() end ActivityCompat.requestPermissions(activity, {Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0x23); end ================================================ FILE: lua/qiqu/info.json ================================================ { "id": "pub.hanks.qiqu", "name": "奇趣百科", "icon": "http://ww1.sinaimg.cn/large/8c9b876fly1fh10pf0gqkj2046046748.jpg", "main": "main.lua", "versionName": "1.0", "versionCode": 1, "private": true, "desc": "提取自奇趣百科,优化浏览体验" } ================================================ FILE: lua/qiqu/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- qiqu -- require "import" import "android.widget.*" import "android.content.*" import "androlua.LuaWebView" -- create view table local layout = { LinearLayout, orientation = "vertical", layout_width = "fill", layout_height = "fill", statusBarColor = "#F79100", { TextView, layout_width = "fill", layout_height = "48dp", background = "#F79100", gravity = "center", text = "奇趣百科", textColor = "#FFFFFF", textSize = "18sp", }, { FrameLayout, layout_width = "fill", layout_height = "fill", { LuaWebView, id = "webview", layout_width = "fill", layout_height = "fill", }, { ProgressBar, layout_gravity = "center", id = "progressBar", layout_width = "40dp", layout_height = "40dp", }, } } function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) webview.setVisibility(0) progressBar.setVisibility(8) webview.loadUrl('http://hanks.pub/joke/') end function onDestroy() if webview then webview.getParent().removeView(webview) webview.destroy() webview = nil end end ================================================ FILE: lua/qqtools/info.json ================================================ { "id": "pub.hanks.qqtools", "name": "QQ 工具箱", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b088883254f7d4?w=150&h=150&f=png&s=3988", "main": "main.lua", "versionName": "1.0.2", "versionCode": 4, "desc": "QQ 工具箱" } ================================================ FILE: lua/qqtools/main.lua ================================================ require "import" import "android.widget.*" import "android.content.*" import "android.net.Uri" import "android.support.v7.widget.Toolbar" import "android.support.v7.widget.AppCompatSeekBar" import "android.view.inputmethod.InputMethodManager" local DialogBuilder = import "android.app.AlertDialog$Builder" local uihelper = require "uihelper" -- create view table local itemHeight = uihelper.getScreenWidth() * 0.16 local itemWidth = uihelper.getScreenWidth() * 0.5 local function doubleButton(leftId, leftText, rightId, rightText) return { LinearLayout, layout_width = "fill", { FrameLayout, layout_width = itemWidth, layout_height = itemHeight, padding = '8dp', { ImageView, layout_width = "match", layout_height = "match", scaleType = "centerCrop", src = 'http://ww1.sinaimg.cn/large/8c9b876fly1fic6hr2qe9j20ch05mq3q.jpg', }, { TextView, layout_width = "match", layout_height = "match", background = '@drawable/layout_selector_tran', id = leftId, gravity = 'center', textColor = '#ffffff', text = leftText, } }, { FrameLayout, layout_width = itemWidth, layout_height = itemHeight, padding = '8dp', { ImageView, scaleType = "centerCrop", layout_width = "match", layout_height = "match", src = 'http://ww1.sinaimg.cn/large/8c9b876fly1fic6i5up5yj20ct0580w6.jpg', }, { TextView, background = '@drawable/layout_selector_tran', layout_width = "match", layout_height = "match", id = rightId, gravity = 'center', textColor = '#ffffff', text = rightText, } }, } end local layout = { LinearLayout, orientation = "vertical", layout_width = "fill", statusBarColor = '#12B7F5', { Toolbar, background = '#12B7F5', id = 'toolbar', titleTextColor = '#FFFFFF', layout_width = "fill", layout_height = "56dp", }, { LinearLayout, orientation = "vertical", padding = "8dp", layout_width = "fill", doubleButton('qq_chat', 'QQ 强制会话', 'qq_hide_card', 'QQ 隐藏名片'), doubleButton('qq_dashang', 'QQ 说说打赏', 'qq_blue_ss', 'QQ 蓝字说说'), doubleButton('qq_shuaping', 'QQ 无限刷屏', 'qq_kasi', 'QQ 聊天卡死'), }, } local layout_intput = { RelativeLayout, layout_width = 'wrap', paddingBottom = '8dp', paddingRight = '8dp', { EditText, id = 'et', layout_margin = '16dp', layout_width = 'match', }, { Button, id = 'tv_ok', layout_below = 'et', background = '@drawable/layout_selector_tran', layout_alignParentRight = true, text = '确定', }, { Button, id = 'tv_cancel', layout_below = 'et', background = '@drawable/layout_selector_tran', layout_toLeftOf = 'tv_ok', text = '取消', }, } local function showDialog(title, hint, leftText, rightText, inputType, callback) local ids = {} local view = loadlayout(layout_intput, ids, ViewGroup) local dialog = DialogBuilder(activity).setTitle(title).setView(view).create() dialog.show() ids.et.setHint(hint or '') ids.et.setInputType(inputType or 0x00000001) ids.tv_cancel.setText(leftText or '取消') ids.tv_ok.setText(rightText or '确定') ids.tv_ok.onClick = function() dialog.dismiss() local text = ids.et.getText().toString() callback(text) end ids.tv_cancel.onClick = function() dialog.dismiss() end ids.et.postDelayed(luajava.createProxy('java.lang.Runnable', { run = function() activity.getSystemService(Context.INPUT_METHOD_SERVICE).toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS) end }), 300) end local function copyText(text) local clipboard = activity.getSystemService(Context.CLIPBOARD_SERVICE) local clip = ClipData.newPlainText("氢应用", text) clipboard.setPrimaryClip(clip) activity.toast('已复制到剪切板') end local function installed(package) return pcall(function() activity.getPackageManager().getPackageInfo(package,0) end) end function onCreate(savedInstanceState) activity.setStatusBarColor(0xff12B7F5) activity.setContentView(loadlayout(layout)) activity.setSupportActionBar(toolbar) activity.setTitle('QQ 工具箱') toolbar.setNavigationIcon(LuaDrawable.create('qqtools/qq.png')) qq_chat.onClick = function() showDialog('QQ 强制会话', '对方 qq 号', '放弃', '会话', 0x00000002, function(text) local url = "mqqwpa://im/chat?chat_type=wpa&uin=" .. text local intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) if (intent.resolveActivity(activity.getPackageManager())) then activity.startActivity(intent) else activity.toast('找不到 QQ 客户端') end end) end qq_dashang.onClick = function() showDialog('QQ 说说打赏', '打赏金额', '放弃', '生成并复制', 0x00000002, function(text) copyText("[em]e10033[/em]{uin:2742,nick: 打赏了" .. text .. "元红包}") end) end qq_blue_ss.onClick = function() showDialog('QQ 蓝字说说', '输入说说内容', nil, '生成并复制', nil, function(text) copyText("{uin:0,nick:" .. text .. ",who:1}") end) end qq_hide_card.onClick = function() showDialog('QQ 隐藏名片', '输入你的名片', nil, '生成并复制', nil, function(text) local t = {} for i = 1, #text - 1 do t[i] = text:sub(i, i + 1) end copyText("\020" .. table.concat(t, "\020") .. "\020") end) end qq_shuaping.onClick = function() local ids = {} local layout = { LinearLayout, layout_width = 'match', padding = '12dp', { AppCompatSeekBar, max = 100, id = 'progress', layout_weight = 1, }, { TextView, id = 'tv_progress', layout_width = '20dp', textSize = '12sp', } } local view = loadlayout(layout, ids, ViewGroup) ids.progress.setOnSeekBarChangeListener(luajava.createProxy('android.widget.SeekBar$OnSeekBarChangeListener', { onProgressChanged = function(seekBar, p, fromUser) ids.tv_progress.setText(p .. '') end })) ids.progress.setProgress(50) DialogBuilder(activity).setTitle('设置刷屏强度').setView(view).setNegativeButton('取消', nil).setPositiveButton('确定', luajava.createProxy('android.content.DialogInterface$OnClickListener', { onClick = function(dialog, which) local p = ids.progress.getProgress() local text = '' for i = 1, p do text = text .. '\n\n\n\n\n\n\n\n\n\n' end local qqIntent = Intent(Intent.ACTION_SEND) local package = "com.tencent.mobileqq" if installed("com.tencent.tim") then package = "com.tencent.tim" end qqIntent.setClassName(package, "com.tencent.mobileqq.activity.JumpActivity") qqIntent.setType("text/plain") qqIntent.putExtra(Intent.EXTRA_TEXT, text) if qqIntent.resolveActivity(activity.getPackageManager()) then activity.startActivity(qqIntent) else activity.toast('找不到 QQ 客户端') end end })).show() end qq_kasi.onClick = function() local ids = {} local layout = { LinearLayout, layout_width = 'match', padding = '12dp', { AppCompatSeekBar, max = 15, id = 'progress', layout_weight = 1, }, { TextView, id = 'tv_progress', layout_width = '20dp', textSize = '12sp', } } local view = loadlayout(layout, ids, ViewGroup) ids.progress.setOnSeekBarChangeListener(luajava.createProxy('android.widget.SeekBar$OnSeekBarChangeListener', { onProgressChanged = function(seekBar, p, fromUser) ids.tv_progress.setText(p .. '') end })) ids.progress.setProgress(8) DialogBuilder(activity).setTitle('设置卡死强度').setView(view).setNegativeButton('取消', nil).setPositiveButton('确定', luajava.createProxy('android.content.DialogInterface$OnClickListener', { onClick = function(dialog, which) local p = ids.progress.getProgress() local text = '' for i = 1, p do text = text .. "\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\195\186\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\n\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\195\186\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\n\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\195\186\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\n\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\195\186\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\n\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\n\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170\020\194\170" end local qqIntent = Intent(Intent.ACTION_SEND) local package = "com.tencent.mobileqq" if installed("com.tencent.tim") then package = "com.tencent.tim" end qqIntent.setClassName(package, "com.tencent.mobileqq.activity.JumpActivity") qqIntent.setType("text/plain") qqIntent.putExtra(Intent.EXTRA_TEXT, text) if qqIntent.resolveActivity(activity.getPackageManager()) then activity.startActivity(qqIntent) else activity.toast('找不到 QQ 客户端') end end })).show() end end ================================================ FILE: lua/quanmm/fragment_news.lua ================================================ -- -- Created by IntelliJ IDEA. -- User: hanks -- Date: 2017/5/13 -- Time: 00:01 -- To change this template use File | Settings | File Templates. -- require "import" import "android.widget.*" import "android.content.*" import "androlua.LuaAdapter" import "androlua.LuaImageLoader" import "androlua.LuaFragment" import "androlua.LuaHttp" import "androlua.widget.webview.WebViewActivity" import "android.support.v4.widget.SwipeRefreshLayout" import "android.graphics.drawable.GradientDrawable" import "android.os.Build" import "android.support.v7.widget.RecyclerView" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.LinearLayoutManager" import "androlua.widget.picture.PicturePreviewActivity" local uihelper = require "uihelper" local JSON = require "cjson" local log = require "log" local floor = math.floor local tonum = tonumber local imageWidth = uihelper.getScreenWidth() local function getData(params, data, adapter, fragment, swipe_layout, reload) local url = string.format(params.url, params.page) LuaHttp.request({ url = url }, function(error, code, body) if error or code ~= 200 then return end params.page = params.page + 1 local json = JSON.decode(body) local arr = json.data.rows uihelper.runOnUiThread(fragment.getActivity(), function() if reload then for k, _ in ipairs(data) do data[k] = nil end end local s = #data for i = 1, #arr do local item = arr[i] data[#data + 1] = item end adapter.notifyItemRangeChanged(s, #arr) swipe_layout.setRefreshing(false) end) end) end local function launchDetail(fragment, data, index) local item = data[index+1] WebViewActivity.start(activity, item.article_link, 0xFFFF6666) end local function newInstance(rid) -- create view table local layout = { SwipeRefreshLayout, layout_width = "fill", layout_height = "fill", id = "swipe_layout", { RecyclerView, id = "recyclerView", background = '#ffffff', layout_width = "fill", layout_height = "fill", } } local item_view = { FrameLayout, layout_width = "fill", layout_height = "104dp", paddingLeft = "16dp", paddingRight = "16dp", background = '@drawable/layout_selector_tran', { View, layout_width="fill", layout_height="0.5dp", background = "#f0f0f0"}, { ImageView, id = "iv_image", layout_marginTop = "16dp", layout_width = "72dp", layout_height = "72dp", }, { TextView, id = "tv_title", layout_width = "fill", layout_height = "20dp", layout_marginTop = "14dp", layout_marginLeft = "92dp", textSize = "15sp", textColor = "#111111", maxLines = 1, }, { TextView, id = "tv_desc", layout_width = "fill", layout_height = "20dp", layout_marginLeft = "92dp", layout_gravity = "center_vertical", textColor = "#FF4D4D", textSize = "13sp", maxLines = 1, }, { TextView, id = "tv_from", layout_height = "16dp", layout_gravity = "bottom", textColor = "#AEAEAE", layout_marginLeft = "92dp", layout_marginBottom = "16dp", textSize = "12sp", maxLines = 1, }, { TextView, layout_marginBottom = "16dp", id = "tv_viewcount", layout_gravity = 85, textColor = "#AEAEAE", layout_height = "16dp", textSize = "12sp", maxLines = 1, }, } local hadLoadData local isVisible local lastId local params = { url = rid, page = 1 } local data = {} local ids = {} local adapter local fragment = LuaFragment.newInstance() local function lazyLoad() if not isVisible then return end if hadLoadData then return end if adapter == nil then return end hadLoadData = true getData(params, data, adapter, fragment, ids.swipe_layout) end fragment.setCreator(luajava.createProxy('androlua.LuaFragment$FragmentCreator', { onCreateView = function(inflater, container, savedInstanceState) return loadlayout(layout, ids) end, onViewCreated = function(view, savedInstanceState) adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() return #data end, onCreateViewHolder = function(parent, viewType) local views = {} local holder = LuaRecyclerHolder(loadlayout(item_view, views, RecyclerView)) holder.itemView.getLayoutParams().width = imageWidth holder.itemView.setTag(views) holder.itemView.onClick = function(view) local position = holder.getAdapterPosition() launchDetail(fragment, data, position) end return holder end, onBindViewHolder = function(holder, position) position = position + 1 local item = data[position] local views = holder.itemView.getTag() LuaImageLoader.load(views.iv_image, item.article_thumbnail) views.tv_title.setText(item.article_title) views.tv_desc.setText(item.article_vicetitle) views.tv_from.setText(item.article_mall) views.tv_viewcount.setText(item.article_read_count_str) if position == #data then getData(params, data, adapter, fragment, ids.swipe_layout) end end, })) ids.recyclerView.setLayoutManager(LinearLayoutManager(activity)) ids.recyclerView.setAdapter(adapter) ids.swipe_layout.setRefreshing(true) ids.swipe_layout.setOnRefreshListener(luajava.createProxy('android.support.v4.widget.SwipeRefreshLayout$OnRefreshListener', { onRefresh = function() getData(params, data, adapter, fragment, ids.swipe_layout, true) end })) lazyLoad() end, onUserVisible = function(visible) isVisible = visible lazyLoad() end, })) return fragment end return { newInstance = newInstance } ================================================ FILE: lua/quanmm/info.json ================================================ { "id": "pub.hanks.quanmm", "name": "券妈妈", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b08882ec273df8?w=150&h=150&f=png&s=2540", "main": "main.lua", "versionName": "1.0.1", "versionCode": 2, "desc": "领取各种优惠券" } ================================================ FILE: lua/quanmm/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "android.support.v4.view.ViewPager" import "android.support.design.widget.TabLayout" import "androlua.adapter.LuaFragmentPageAdapter" import "android.support.v7.widget.Toolbar" import "pub.hydrogen.android.R" import "android.net.Uri" import "android.os.Build" local uihelper = require "uihelper" local JSON = require "cjson" local log = require "log" local fragmentNews = require "quanmm/fragment_news" -- create view table local layout = { LinearLayout, layout_width = "fill", orientation = "vertical", { View, id = "statusBar", background = '#FFFF6666', layout_width = "fill", }, { Toolbar, background = '#FFFF6666', id = 'toolbar', layout_width = "match", layout_height = "48dp", titleTextColor = "#88ffffff", { TabLayout, id = "tab", layout_width = "match", layout_height = "match", }, }, { FrameLayout, layout_width = "fill", layout_height = "fill", { ViewPager, id = "viewPager", layout_width = "fill", layout_height = "fill", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", } } } local data = { titles = {}, fragments = {}, } table.insert(data.fragments, fragmentNews.newInstance('http://app.quanmama.com/apios/v5/appZdmList.ashx?f=android&test=0&code=503&platform=Tencent&v=5.0.3&phoneversion=22&pageindex=%d&adsysno=1006&trackSysNo=1006&pagetype=3&dplus_fenlei_name=最新&apphomerankindex=1&sort=1&pagesize=20')) table.insert(data.titles, '最新') table.insert(data.fragments, fragmentNews.newInstance('http://app.quanmama.com/apios/v5/appZdmList.ashx?f=android&test=0&code=503&platform=Tencent&v=5.0.3&phoneversion=22&pageindex=%d&adsysno=1004&trackSysNo=1004&pagetype=3&dplus_fenlei_name=最热&apphomerankindex=1&sort=1&pagesize=20')) table.insert(data.titles, '最热') table.insert(data.fragments, fragmentNews.newInstance('http://app.quanmama.com/apios/v5/appZdmList.ashx?f=android&test=0&code=503&platform=Tencent&v=5.0.3&phoneversion=22&pageindex=%d&category=5391&sort=1&trackSysNo=4030&pagesize=20')) table.insert(data.titles, '外卖') table.insert(data.fragments, fragmentNews.newInstance('http://app.quanmama.com/apios/v5/appZdmList.ashx?f=android&test=0&code=503&platform=Tencent&v=5.0.3&phoneversion=22&pageindex=%d&category=1257&sort=1&trackSysNo=4028&pagesize=20')) table.insert(data.titles, '出行') table.insert(data.fragments, fragmentNews.newInstance('http://app.quanmama.com/apios/v5/appZdmList.ashx?f=android&test=0&code=503&platform=Tencent&v=5.0.3&phoneversion=22&pageindex=%d&category=1187&sort=1&trackSysNo=4031&pagesize=20')) table.insert(data.titles, '观影') table.insert(data.fragments, fragmentNews.newInstance('http://app.quanmama.com/apios/v5/appZdmList.ashx?f=android&test=0&usertoken=&code=503&platform=Tencent&v=5.0.3&phoneversion=22&imei=867247020524723&channelrankindex=1&sort=1&trackSysNo=4032&storetype=-1&youhuitype=100101&pagesize=20&pageindex=%d')) table.insert(data.titles, '网购') table.insert(data.fragments, fragmentNews.newInstance('http://app.quanmama.com/apios/v5/appZdmList.ashx?f=android&test=0&usertoken=&code=503&platform=Tencent&v=5.0.3&phoneversion=22&imei=867247020524723&channelrankindex=1&sort=1&trackSysNo=4140&storetype=-1&youhuitype=100102&tagSysNo=26615&pagesize=20&pageindex=%d')) table.insert(data.titles, '团购') table.insert(data.fragments, fragmentNews.newInstance('http://app.quanmama.com/apios/v5/appZdmList.ashx?f=android&test=0&usertoken=&code=503&platform=Tencent&v=5.0.3&phoneversion=22&imei=867247020524723&channelrankindex=1&sort=1&trackSysNo=4051&storetype=-1&youhuitype=10010204&pagesize=20&pageindex=%d')) table.insert(data.titles, '旅游酒店') table.insert(data.fragments, fragmentNews.newInstance('http://app.quanmama.com/apios/v5/appZdmList.ashx?f=android&test=0&usertoken=&code=503&platform=Tencent&v=5.0.3&phoneversion=22&imei=867247020524723&channelrankindex=1&sort=1&trackSysNo=4053&storetype=-1&tagSysNo=90584&pagesize=20&pageindex=%d')) table.insert(data.titles, '专享券') local adapter = LuaFragmentPageAdapter(activity.getSupportFragmentManager(), luajava.createProxy("androlua.adapter.LuaFragmentPageAdapter$AdapterCreator", { getCount = function() return #data.fragments end, getItem = function(position) position = position + 1 return data.fragments[position] end, getPageTitle = function(position) position = position + 1 return data.titles[position] end })) function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) activity.setSupportActionBar(toolbar) activity.setStatusBarColor(0x33000000) activity.setTitle('') toolbar.setNavigationIcon(LuaDrawable.create('quanmm/quan.png')) toolbar.setNavigationOnClickListener(luajava.createProxy("android.view.View$OnClickListener",{ onClick = function(v) activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse('http://m.quanmama.com/mobile/home'))) end })); local h = 0 if Build.VERSION.SDK_INT >= 21 then h = uihelper.dp2px(25) end statusBar.getLayoutParams().height = h viewPager.setAdapter(adapter) viewPager.setOffscreenPageLimit(#data.fragments) viewPager.setCurrentItem(0) tab.setSelectedTabIndicatorColor(0xeeffffff) tab.setTabTextColors(0x88ffffff, 0xeeffffff) tab.setTabMode(TabLayout.MODE_SCROLLABLE) tab.setTabGravity(TabLayout.GRAVITY_CENTER) tab.setupWithViewPager(viewPager) end ================================================ FILE: lua/readhub/fragment_news.lua ================================================ -- -- Created by IntelliJ IDEA. -- User: hanks -- Date: 2017/5/13 -- Time: 00:01 -- To change this template use File | Settings | File Templates. -- require "import" import "android.widget.*" import "android.content.*" import "androlua.LuaAdapter" import "androlua.LuaImageLoader" import "androlua.LuaFragment" import "androlua.LuaHttp" import "androlua.widget.webview.WebViewActivity" import "android.support.v4.widget.SwipeRefreshLayout" import "android.graphics.drawable.GradientDrawable" import "android.os.Build" local uihelper = require "uihelper" local JSON = require "cjson" local log = require "log" local function dateStr(d) if type(d) == 'string' then local y,m,d,h,M,s = string.match(d,'(%d%d%d%d)[-](%d%d)[-](%d%d)T(%d%d):(%d%d):(%d%d).') return dateStr(os.time({ year = y, month = m, day = d, hour = h+8, min = M, sec=s})) end local now = os.time() local dx = now - d if dx < 600 then return '刚刚' elseif dx < 3600 then return math.floor(dx / 60) .. '分钟前' elseif dx < 3600 * 24 then return math.floor(dx / 3600) .. '小时前' else return os.date('%y-%m-%d', d) end end local function getData(params, data, adapter, fragment, swipe_layout, reload) local url = string.format('https://api.readhub.me/%s?pageSize=10', params.rid) if params.lastId then url = string.format('https://api.readhub.me/%s?lastCursor=%d&pageSize=10', params.rid, tonumber(params.lastId)) end LuaHttp.request({ url = url }, function(error, code, body) if error or code ~= 200 then return end local json = JSON.decode(body) local arr = json.data local lastItem = arr[#arr] log.print_r(lastItem) if lastItem.order then params.lastId = lastItem.order else local y,m,d,h,M,s = string.match(lastItem.publishDate,'(%d%d%d%d)[-](%d%d)[-](%d%d)T(%d%d):(%d%d):(%d%d).') a = { year = y, month = m, day = d, hour = h+8, min = M, sec=s} params.lastId = os.time(a) * 1000 end uihelper.runOnUiThread(fragment.getActivity(), function() if reload then for k, _ in ipairs(data) do data[k] = nil end end local s = #data for i = 1, #arr do data[#data + 1] = arr[i] end adapter.notifyDataSetChanged() swipe_layout.setRefreshing(false) end) end) end local function launchDetail(fragment, item) local url if item.newsArray and #item.newsArray > 0 then url = item.newsArray[1].mobileUrl end if item.url then url = item.url end if url == nil then return end local activity = fragment.getActivity() WebViewActivity.start(activity,url, 0xff406d91) end local function newInstance(rid) -- create view table local layout = { SwipeRefreshLayout, layout_width = "fill", layout_height = "fill", id = "swipe_layout", { ListView, id = "listview", background = '#EEEEEE', dividerHeight = 0, paddingTop = "8dp", clipToPadding = false, layout_width = "fill", layout_height = "fill", } } local item_view = { FrameLayout, layout_width = "fill", { LinearLayout, background = '#FFFFFF', layout_marginLeft = "16dp", layout_marginRight = "16dp", layout_marginTop = "8dp", layout_marginBottom = "8dp", elevation = "2dp", orientation = 'vertical', layout_width = "fill", { TextView, id = "tv_title", layout_width = "fill", layout_margin = '16dp', maxLines = 2, lineSpacingMultiplier = 1.3, textSize = "16sp", textColor = "#434343", }, { TextView, id = "tv_time", layout_width = "fill", layout_marginLeft = '16dp', layout_marginRight = '16dp', layout_marginBottom = '16dp', maxLines = 4, lineSpacingMultiplier = 1.3, textSize = "12sp", textColor = "#666666", }, }, } local hadLoadData local isVisible local lastId local params = { rid = rid } local data = {} local ids = {} local adapter local fragment = LuaFragment.newInstance() local function lazyLoad() if not isVisible then return end if hadLoadData then return end if adapter == nil then return end hadLoadData = true getData(params, data, adapter, fragment, ids.swipe_layout) end fragment.setCreator(luajava.createProxy('androlua.LuaFragment$FragmentCreator', { onCreateView = function(inflater, container, savedInstanceState) return loadlayout(layout, ids) end, onViewCreated = function(view, savedInstanceState) adapter = LuaAdapter(luajava.createProxy("androlua.LuaAdapter$AdapterCreator", { getCount = function() return #data end, getView = function(position, convertView, parent) position = position + 1 -- lua 索引从 1开始 if convertView == nil then local views = {} -- store views convertView = loadlayout(item_view, views, ListView) convertView.getLayoutParams().width = parent.getWidth() convertView.setTag(views) views.tv_title.setTypeface(nil, 1); end local views = convertView.getTag() local item = data[position] if item then views.tv_title.setText(item.title or 'ERROR TITLE') views.tv_time.setText( dateStr(item.updatedAt or item.publishDate or os.time() )) end if position == #data then getData(params, data, adapter, fragment, ids.swipe_layout) end return convertView end })) ids.listview.setAdapter(adapter) ids.listview.setOnItemClickListener(luajava.createProxy("android.widget.AdapterView$OnItemClickListener", { onItemClick = function(adapter, view, position, id) launchDetail(fragment, data[position + 1]) end, })) ids.swipe_layout.setRefreshing(true) ids.swipe_layout.setOnRefreshListener(luajava.createProxy('android.support.v4.widget.SwipeRefreshLayout$OnRefreshListener', { onRefresh = function() getData(params, data, adapter, fragment, ids.swipe_layout, true) end })) lazyLoad() end, onUserVisible = function(visible) isVisible = visible lazyLoad() end, })) return fragment end return { newInstance = newInstance } ================================================ FILE: lua/readhub/info.json ================================================ { "id": "pub.hanks.readhub", "name": "ReadHub", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b0887dffcca79a?w=150&h=150&f=png&s=2619", "main": "main.lua", "versionName": "1.0", "versionCode": 2, "desc": "ReadHub" } ================================================ FILE: lua/readhub/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "android.support.v4.view.ViewPager" import "android.support.design.widget.TabLayout" import "androlua.adapter.LuaFragmentPageAdapter" import "android.support.v7.widget.Toolbar" import "pub.hydrogen.android.R" import "android.net.Uri" local uihelper = require "uihelper" local JSON = require "cjson" local log = require "log" local fragmentNews = require "readhub/fragment_news" -- create view table local layout = { LinearLayout, layout_width = "fill", layout_height = "fill", orientation = "vertical", statusBarColor = "#FFFFFF", { Toolbar, background = '#FFFFFF', id = 'toolbar', layout_width = "match", layout_height = "56dp", titleTextColor = "#434343", }, { TabLayout, id = "tab", layout_width = "fill", layout_height = "48dp", background = "#FFFFFF", }, { FrameLayout, layout_width = "fill", layout_height = "fill", { ViewPager, id = "viewPager", layout_width = "fill", layout_height = "fill", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", } } } local data = { titles = {}, fragments = {}, } table.insert(data.fragments, fragmentNews.newInstance('topic')) table.insert(data.titles, '热门话题') table.insert(data.fragments, fragmentNews.newInstance('news')) table.insert(data.titles, '科技动态') table.insert(data.fragments, fragmentNews.newInstance('technews')) table.insert(data.titles, '开发者资讯') local adapter = LuaFragmentPageAdapter(activity.getSupportFragmentManager(), luajava.createProxy("androlua.adapter.LuaFragmentPageAdapter$AdapterCreator", { getCount = function() return #data.fragments end, getItem = function(position) position = position + 1 return data.fragments[position] end, getPageTitle = function(position) position = position + 1 return data.titles[position] end })) function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) activity.setSupportActionBar(toolbar) activity.setStatusBarColor(0x33000000) activity.setTitle('ReadHub') toolbar.setNavigationIcon(LuaDrawable.create('readhub/readhub.png')) viewPager.setAdapter(adapter) viewPager.setOffscreenPageLimit(#data.fragments) viewPager.setCurrentItem(0) tab.setSelectedTabIndicatorColor(0xff434343) tab.setTabTextColors(0x88434343, 0xff434343) tab.setTabMode(TabLayout.MODE_FIXED) tab.setTabGravity(TabLayout.GRAVITY_CENTER) tab.setupWithViewPager(viewPager) end function onCreateOptionsMenu(menu) menu.add("网页版") return true end function onOptionsItemSelected(item) local title = item.getTitle() if title == "网页版" then activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse('https://readhub.me/'))) end end ================================================ FILE: lua/splash/info.json ================================================ { "id": "pub.hanks.splash", "name": "启动图", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b088776a2871cb?w=150&h=150&f=png&s=1915", "main": "main.lua", "versionName": "1.0", "versionCode": 3, "desc": "每日启动图回顾" } ================================================ FILE: lua/splash/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- qiqu -- require "import" import "android.widget.*" import "android.content.*" import "android.support.v4.view.ViewPager" import "androlua.adapter.LuaPagerAdapter" import "androlua.LuaImageLoader" import "android.app.DownloadManager" import "android.os.Environment" import "android.net.Uri" local DialogBuilder = import "android.app.AlertDialog$Builder" local DownloadManagerRequest = import "android.app.DownloadManager$Request" local uihelper = require('uihelper') local JSON = require("cjson") -- create view table local layout = { FrameLayout, layout_width = "fill", layout_height = "fill", { ViewPager, id = "viewPager", layout_width = "fill", layout_height = "fill", }, { LinearLayout, layout_width = "fill", layout_gravity = "bottom", orientation = "vertical", paddingBottom = "24dp", paddingLeft = "20dp", paddingRight = "20dp", paddingTop = "120dp", background = "@drawable/shadow_splash", { RelativeLayout, layout_width = "fill", layout_height = "40dp", layout_marginBottom = "20dp", { TextView, id = "tv_date", layout_alignParentRight = true, layout_alignParentBottom = true, textSize = "14sp", textColor = "#FFFFFF", }, { TextView, id = "tv_day", layout_toLeftOf = "tv_date", layout_alignBaseline = "tv_date", textSize = "30sp", gravity = "right", textColor = "#FFFFFF", }, }, { TextView, id = "tv_text", layout_width = "match", gravity = "right", lineSpacingMultiplier = 1.5, textSize = "14sp", textColor = "#FFFFFF", }, } } local item_view = { ImageView, id = "iv_bg", layout_width = "match", layout_height = "match", scaleType = "centerCrop", } local adapter local data = {} local function downloadPicture(url) local manager = activity.getSystemService(Context.DOWNLOAD_SERVICE) local request = DownloadManagerRequest(Uri.parse(url)) request.setNotificationVisibility(DownloadManagerRequest.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); request.setDescription("下载中...") request.setTitle("下载") request.setDestinationInExternalPublicDir(Environment.DIRECTORY_PICTURES, os.time() .. ".jpg") manager.enqueue(request) end local function getData() LuaHttp.request({ url = 'https://coding.net/u/zhangyuhan/p/api_luanroid/git/raw/master/api/splash' }, function(e, code, body) print(body) local json = JSON.decode(body) local arr = json.data uihelper.runOnUiThread(activity, function() local views = {} for i = 1, #arr do data[#data + 1] = arr[i] local ids = {} local view = loadlayout(item_view, ids, ViewGroup) LuaImageLoader.load(ids.iv_bg, arr[i].img) ids.iv_bg.onClick = function() DialogBuilder(activity).setTitle('保存图片').setMessage('保存到相册?').setNegativeButton('取消', nil).setPositiveButton('确定', luajava.createProxy('android.content.DialogInterface$OnClickListener', { onClick = function(dialog, which) downloadPicture(arr[i].img) activity.toast('保存到相册...') end })).show() end views[#views + 1] = view end adapter.addViews(views) adapter.notifyDataSetChanged() if #data > 0 then tv_date.setText(string.format('%d月', tonumber(data[1].date:sub(5, 6)))) tv_day.setText(string.format('%d/', tonumber(data[1].date:sub(7, 8)))) tv_text.setText(data[1].text) end end) end) end function onCreate(savedInstanceState) activity.setStatusBarColor(0x00000000) activity.setContentView(loadlayout(layout)) adapter = LuaPagerAdapter(nil) viewPager.setAdapter(adapter) viewPager.addOnPageChangeListener(luajava.createProxy('android.support.v4.view.ViewPager$OnPageChangeListener', { onPageSelected = function(position) position = position + 1 local item = data[position] tv_date.setText(string.format('/%d月', tonumber(item.date:sub(5, 6)))) tv_day.setText(string.format('%d', tonumber(item.date:sub(7, 8)))) tv_text.setText(item.text) end })) getData() end ================================================ FILE: lua/sspai/activity_news_detail.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaWebView" import "androlua.LuaHttp" local uihelper = require "uihelper" -- create view table local layout = { LinearLayout, layout_width = "fill", layout_height = "fill", orientation = "vertical", statusBarColor = "#D8171C", { LinearLayout, orientation = "horizontal", layout_width = "fill", layout_height = "56dp", background = "#D8171C", gravity = "center_vertical", { ImageView, id = "back", layout_width = "40dp", layout_height = "40dp", layout_marginLeft = "8dp", scaleType = "centerInside", src = "@drawable/ic_menu_back", }, { TextView, layout_height = "56dp", layout_width = "fill", paddingRight = "16dp", singleLine = true, textIsSelectable = true, ellipsize = "end", id = "tv_title", gravity = "center_vertical", paddingLeft = "8dp", textColor = "#ffffff", textSize = "16sp", }, }, { FrameLayout, layout_width = "fill", layout_height = "fill", { LuaWebView, id = "webview", layout_width = "fill", layout_height = "fill", }, { ProgressBar, layout_gravity = "center", id = "progressBar", layout_width = "40dp", layout_height = "40dp", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", } } } local css = [[ video{width:100%}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}html{font-family:sans-serif;-webkit-text-size-adjust:100%}body{font-family:'Helvetica Neue',Helvetica,Arial,Sans-serif;background:#fff;padding-top:0;margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{margin:.67em 0}h1,h2,h3,h4,h5,h6{font-size:16px}abbr[title]{border-bottom:1px dotted}hr{box-sizing:content-box;height:0}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap}q{quotes:\201C\201D\2018\2019}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0;vertical-align:middle;color:transparent;font-size:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}table{border-collapse:collapse;border-spacing:0;overflow:hidden}a{text-decoration:none}blockquote{border-left:3px solid #d0e5f2;font-style:normal;display:block;vertical-align:baseline;font-size:100%;margin:.5em 0;padding:0 0 0 1em}ul,ol{padding-left:20px}.content{color:#444;line-height:1.6em;font-size:16px;margin:16px}.content img{max-width:100%;display:block;margin:30px auto}.content img+img{margin-top:15px}.content img[src*="zhihu.com/equation"]{display:inline-block;margin:0 3px}.content a{color:#259}.content a:hover{text-decoration:underline} ]] local htmlTemplate = [[
    %s
    ]] local function getData(url) LuaHttp.request({ url = url }, function(error, code, body) local content = string.match(body, 'class="content%s+wangEditor[-]txt.->(.-)
    '):gsub('style=".-"', ''):gsub('width=%d+', ''):gsub('height=%d+', '') local data = string.format(htmlTemplate, css, content) uihelper.runOnUiThread(activity, function() webview.loadData(data, "text/html; charset=UTF-8", nil) end) end) end function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) back.onClick = function() activity.finish() end local url = activity.getIntent().getStringExtra('url') tv_title.setText(url) webview.setVisibility(0) progressBar.setVisibility(8) getData(url) end function onDestroy() if webview then webview.getParent().removeView(webview) webview.destroy() webview = nil end end ================================================ FILE: lua/sspai/fragment_news.lua ================================================ -- -- Created by IntelliJ IDEA. -- User: hanks -- Date: 2017/5/13 -- Time: 00:01 -- To change this template use File | Settings | File Templates. -- require "import" import "android.widget.*" import "android.content.*" import "androlua.LuaAdapter" import "androlua.LuaImageLoader" import "androlua.LuaFragment" import "androlua.LuaHttp" import "androlua.widget.webview.WebViewActivity" import "android.support.v4.widget.SwipeRefreshLayout" import "android.graphics.drawable.GradientDrawable" import "android.os.Build" local Orientation = import "android.graphics.drawable.GradientDrawable$Orientation" local uihelper = require "uihelper" local JSON = require "cjson" local log = require "log" local colors = luajava.createArray("int", { 0x77000000, 0x00000000 }) local gd = GradientDrawable(Orientation.TOP_BOTTOM, colors) gd.setCornerRadius(uihelper.dp2px(3)) local function getData(params, data, adapter, fragment, swipe_layout, reload) local url = string.format('https://api.qingmang.me/v2/article.list?token=c400a7e21688496ca3e7f17c6b0d1846&category_id=%s', params.rid) if params.nextUrl then url = params.nextUrl end LuaHttp.request({ url = url }, function(error, code, body) if error or code ~= 200 then return end local json = JSON.decode(body) if json.hasMore and json.nextUrl then params.nextUrl = json.nextUrl end local arr = json.articles uihelper.runOnUiThread(fragment.getActivity(), function() if reload then for k, _ in pairs(data) do data[k] = nil end end local s = #data for i = 1, #arr do data[#data + 1] = arr[i] end adapter.notifyDataSetChanged() swipe_layout.setRefreshing(false) end) end) end local function launchDetail(fragment, item) local activity = fragment.getActivity() if item == nil or item.webUrl == nil then activity.toast('没有 url 可以打开') return end local activity = fragment.getActivity() local intent = Intent(activity, LuaActivity) intent.putExtra("luaPath", 'sspai/activity_news_detail.lua') intent.putExtra("url", item.webUrl) activity.startActivity(intent) end local function newInstance(rid) -- create view table local layout = { SwipeRefreshLayout, layout_width = "fill", layout_height = "fill", id = "swipe_layout", { ListView, id = "listview", background = '#FFFFFF', dividerHeight = 0, paddingTop = "16dp", clipToPadding = false, layout_width = "fill", layout_height = "fill", } } local item_view = { LinearLayout, background = '#ffffff', orientation = 'vertical', layout_width = "fill", { FrameLayout, layout_marginLeft = '16dp', layout_marginRight = '16dp', layout_width = "fill", layout_height = "140dp", { ImageView, id = "iv_image", layout_width = "fill", layout_height = "fill", scaleType = "centerCrop", }, { View, background = gd, layout_width = "fill", layout_height = "90dp", }, { TextView, id = "tv_title", layout_width = "fill", layout_marginLeft = '16dp', layout_marginRight = '16dp', layout_marginTop = '8dp', maxLines = 2, lineSpacingMultiplier = 1.3, textSize = "16sp", textColor = "#EEFFFFFF", }, }, { TextView, id = "tv_desc", layout_width = "fill", layout_marginTop = '8dp', layout_marginLeft = '16dp', layout_marginRight = '16dp', layout_marginBottom = '16dp', maxLines = 4, lineSpacingMultiplier = 1.3, textSize = "12sp", textColor = "#666666", }, { View, layout_width = "fill", layout_height = "16dp", } } local hadLoadData local isVisible local lastId local params = { rid = rid } local data = {} local ids = {} local adapter local fragment = LuaFragment.newInstance() local function lazyLoad() if not isVisible then return end if hadLoadData then return end if adapter == nil then return end hadLoadData = true getData(params, data, adapter, fragment, ids.swipe_layout) end fragment.setCreator(luajava.createProxy('androlua.LuaFragment$FragmentCreator', { onCreateView = function(inflater, container, savedInstanceState) return loadlayout(layout, ids) end, onViewCreated = function(view, savedInstanceState) adapter = LuaAdapter(luajava.createProxy("androlua.LuaAdapter$AdapterCreator", { getCount = function() return #data end, getView = function(position, convertView, parent) position = position + 1 -- lua 索引从 1开始 if convertView == nil then local views = {} -- store views convertView = loadlayout(item_view, views, ListView) convertView.getLayoutParams().width = parent.getWidth() convertView.setTag(views) end local views = convertView.getTag() local item = data[position] if item then if item.covers and #item.covers > 0 then views.iv_image.setVisibility(0) LuaImageLoader.loadWithRadius(views.iv_image, 3, item.covers[1].url .. '?imageMogr2/quality/95/thumbnail/!1440x480r/gravity/Center/crop/1440x480') else views.iv_image.setVisibility(8) end views.tv_title.setText(item.title or 'ERROR TITLE') views.tv_desc.setText(item.snippet or '') end if position == #data then getData(params, data, adapter, fragment, ids.swipe_layout) end return convertView end })) ids.listview.setAdapter(adapter) ids.listview.setOnItemClickListener(luajava.createProxy("android.widget.AdapterView$OnItemClickListener", { onItemClick = function(adapter, view, position, id) launchDetail(fragment, data[position + 1]) end, })) ids.swipe_layout.setRefreshing(true) ids.swipe_layout.setOnRefreshListener(luajava.createProxy('android.support.v4.widget.SwipeRefreshLayout$OnRefreshListener', { onRefresh = function() getData(params, data, adapter, fragment, ids.swipe_layout, true) end })) lazyLoad() end, onUserVisible = function(visible) isVisible = visible lazyLoad() end, })) return fragment end return { newInstance = newInstance } ================================================ FILE: lua/sspai/info.json ================================================ { "id": "pub.hanks.sspai", "name": "少数派", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b088719a5dfd47?w=150&h=150&f=png&s=2762", "main": "main.lua", "versionName": "1.0.2", "versionCode": 4, "desc": "少数派" } ================================================ FILE: lua/sspai/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "android.support.v4.view.ViewPager" import "android.support.design.widget.TabLayout" import "androlua.adapter.LuaFragmentPageAdapter" local uihelper = require "uihelper" local JSON = require "cjson" local log = require "log" local fragmentNews = require "sspai/fragment_news" -- create view table local layout = { LinearLayout, layout_width = "fill", layout_height = "fill", orientation = "vertical", statusBarColor = "#D8171C", { TabLayout, id = "tab", layout_width = "fill", layout_height = "48dp", background = "#D8171C", }, { FrameLayout, layout_width = "fill", layout_height = "fill", { ViewPager, id = "viewPager", layout_width = "fill", layout_height = "fill", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", } } } local data = { titles = {}, fragments = {}, } table.insert(data.fragments, fragmentNews.newInstance('p14')) table.insert(data.titles, '每日更新') table.insert(data.fragments, fragmentNews.newInstance('p15756')) table.insert(data.titles, 'Matrix') table.insert(data.fragments, fragmentNews.newInstance('p15757')) table.insert(data.titles, '效率工具') table.insert(data.fragments, fragmentNews.newInstance('p15912')) table.insert(data.titles, '手机摄影') table.insert(data.fragments, fragmentNews.newInstance('p15913')) table.insert(data.titles, '生活方式') table.insert(data.fragments, fragmentNews.newInstance('p15914')) table.insert(data.titles, '游戏') table.insert(data.fragments, fragmentNews.newInstance('p15104')) table.insert(data.titles, '硬件') table.insert(data.fragments, fragmentNews.newInstance('p15915')) table.insert(data.titles, '人物') local adapter = LuaFragmentPageAdapter(activity.getSupportFragmentManager(), luajava.createProxy("androlua.adapter.LuaFragmentPageAdapter$AdapterCreator", { getCount = function() return #data.fragments end, getItem = function(position) position = position + 1 return data.fragments[position] end, getPageTitle = function(position) position = position + 1 return data.titles[position] end })) function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) viewPager.setAdapter(adapter) viewPager.setOffscreenPageLimit(#data.fragments) viewPager.setCurrentItem(0) tab.setSelectedTabIndicatorColor(0xffffffff) tab.setTabTextColors(0x88ffffff, 0xffffffff) tab.setTabMode(TabLayout.MODE_SCROLLABLE) tab.setTabGravity(TabLayout.GRAVITY_CENTER) tab.setupWithViewPager(viewPager) end ================================================ FILE: lua/taobao/info.json ================================================ { "id": "pub.hanks.taobao", "name": "淘宝", "icon": "http://ww1.sinaimg.cn/large/8c9b876fly1fikliu17ghj2046046jr6.jpg", "main": "main.lua", "versionName": "1.0.0", "versionCode": 1, "private": true, "desc": "轻版淘宝" } ================================================ FILE: lua/taobao/main.lua ================================================ require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "android.support.v4.view.ViewPager" import "android.support.design.widget.TabLayout" import "androlua.adapter.LuaFragmentPageAdapter" import "android.support.v7.widget.Toolbar" import "android.support.v7.widget.RecyclerView" import "android.support.v4.widget.SwipeRefreshLayout" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.LinearLayoutManager" import "android.view.View" import "java.net.URL" import "android.net.Uri" import "androlua.widget.picture.PicturePreviewActivity" import "androlua.widget.webview.WebViewActivity" import "pub.hydrogen.android.R" import "android.support.design.widget.FloatingActionButton" import "androlua.utils.ColorStateListFactory" import "java.lang.String" import "android.graphics.drawable.GradientDrawable" local DialogBuilder = import "android.app.AlertDialog$Builder" local uihelper = require "uihelper" local JSON = require "cjson" local log = require "log" local tabTypes = {'all','mall'} local adapter local data = {} local page = 1 local sort = "" local tab = tabTypes[1] local filter = { start_price=-1, end_price=-1, filter={} } local bg_selector = GradientDrawable() bg_selector.setColor(ColorStateListFactory.newInstance(0x11ffffff,0xffff0000)) activity.setTheme(R.style.Theme_AppCompat_NoActionBar) -- create view table local layout = { LinearLayout, layout_width = "match", layout_height = "match", orientation = "vertical", statusBarColor = "#ff5500", { Toolbar, background = '#ff5500', id = 'toolbar', layout_width = "match", layout_height = "56dp", titleTextColor = "#ffffff", popupTheme = R.style.ThemeOverlay_AppCompat_Light, { RelativeLayout, layout_width = "match", layout_height = "match", { EditText, id = 'et_key', layout_width = "match", layout_height = "match", maxLines = 1, singleLine = true, textColor = '#ffffff', hintTextColor = '#88ffffff', background = '#00ffffff', hint = "搜索商品", textColor = "#FFFFFF", }, { ImageView, layout_centerVertical = true, layout_alignParentRight = true, layout_marginRight = '16dp', src = '#taobao/ic_search.png', id = 'bt_search', }, }, }, { FrameLayout, layout_width = "match", layout_height = "match", { SwipeRefreshLayout, id = "refreshLayout", layout_width = "match", { RecyclerView, background = '#ffffff', id = "recyclerView", layout_width = "fill", layout_height = "fill", }, }, { FloatingActionButton, id = 'fab', src = '#taobao/ic_sort.png', backgroundTintList = ColorStateListFactory.newInstance(0xFFFF5500), layout_gravity = 85, layout_margin = '16dp', }, } } local item_view = { FrameLayout, layout_width = "fill", layout_height = "132dp", paddingLeft = "12dp", paddingRight = "12dp", paddingTop = "16dp", { ImageView, id = 'iv_image', layout_width = "100dp", layout_height = "100dp", }, { TextView, id = "tv_title", layout_marginLeft = "108dp", textColor = "#4b566a", lineSpacingMultiplier = 1.3, textSize = "14sp", maxLines = 2, }, { TextView, id = "tv_money", layout_marginLeft = "108dp", layout_marginBottom = "40dp", maxLines = 1, textSize = "17sp", textColor = "#ff5500", layout_gravity = "bottom", }, { TextView, id = "tv_info", layout_marginLeft = "108dp", layout_marginBottom = "16dp", maxLines = 1, textSize = "10sp", textColor = "#999999", layout_gravity = "bottom", }, { View, layout_width = 'match', layout_height = '0.5dp', background = '#eeeeee', layout_gravity = "bottom", }, } local sortTypes = { names = {"综合排序","销量优先","价格从高到低","价格从低到高","信用排序"}, keys = {"","_sale","_bid","bid","_ratesum"} } local function resetFilter() filter.start_price = -1 filter.end_price = -1 for i=1,#filter.filter do filter.filter[i] = nil end end local function launchDetail(msg) if msg and msg.url then local url = msg.url if url:find('^//') then url = 'https:' .. url end WebViewActivity.start(activity, url, 0xFFFF5500) return end activity.toast('没有 url 可以打开') end local function launchPicturePreview(msg, index) local urls = {} for i = 1, #msg.mblog.pics do urls[i] = msg.mblog.pics[i].large.url end local data = { uris = urls, currentIndex = index } PicturePreviewActivity.start(activity, JSON.encode(data)) end local function fetchData(loadMore) -- &start_price=2&end_price=3333333&filter=service_myf -- https://s.m.taobao.com/search?q=多肉&sst=1&n=20&buying=buyitnow&m=api4h5&abtest=7&wlsort=7&style=list&closeModues=nav%2Cselecthot%2Conesearch&sort=_sale&page=1 local key = et_key.getText().toString() if key==nil or key == '' then refreshLayout.setRefreshing(false) return end local url = string.format("https://s.m.taobao.com/search?q=%s&tab=%s&sst=1&n=20&buying=buyitnow&m=api4h5&abtest=6&wlsort=6&sort=%s&page=%d",key,tab,sort,page) local t = {} if filter.start_price > 0 then t[#t+1] = string.format('&start_price=%d',filter.start_price) end if filter.end_price > 0 then t[#t+1] = string.format('&end_price=%d',filter.end_price) end if #filter.filter > 0 then t[#t+1] = '&filter=' .. table.concat( filter.filter, ";") end if #t>0 then url = url .. table.concat( t, "") end print(url) local options = { url = url, } LuaHttp.request(options, function(error, code, body) if error or code ~= 200 then activity.toast('网络错误') refreshLayout.setRefreshing(false) return end local listItem = JSON.decode(body).listItem if listItem == nil then listItem = {} end uihelper.runOnUiThread(activity, function() if page == 1 then for k, _ in pairs(data) do data[k] = nil end end local s = #data for i = 1, #listItem do local item = listItem[i] data[#data + 1] = item end page = page + 1 if loadMore then adapter.notifyItemRangeChanged(s, #data) else adapter.notifyDataSetChanged() end refreshLayout.setRefreshing(false) end) end) end local function reload( ) refreshLayout.setRefreshing(true) page = 1 fetchData() end function onCreate(savedInstanceState) activity.setStatusBarColor(0xFFFF5500) activity.setContentView(loadlayout(layout)) activity.setSupportActionBar(toolbar) activity.setTitle('淘宝') toolbar.setTitle('淘宝') toolbar.setNavigationIcon(LuaDrawable.create('taobao/taobao.png')) toolbar.setNavigationOnClickListener(luajava.createProxy('android.view.View$OnClickListener',{ onClick = function() if tab == tabTypes[1] then tab = tabTypes[2] toolbar.setNavigationIcon(LuaDrawable.create('taobao/tmall.png')) else tab = tabTypes[1] toolbar.setNavigationIcon(LuaDrawable.create('taobao/taobao.png')) end reload() end })) adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() return #data end, getItemViewType = function(position) return 0 end, onCreateViewHolder = function(parent, viewType) local views = {} local holder = LuaRecyclerHolder(loadlayout(item_view, views, RecyclerView)) holder.itemView.getLayoutParams().width = parent.getWidth() holder.itemView.setTag(views) holder.itemView.onClick = function(view) local position = holder.getAdapterPosition() + 1, print(position) launchDetail(data[position]) end return holder end, onBindViewHolder = function(holder, position) position = position + 1 local msg = data[position] local views = holder.itemView.getTag() views.tv_title.setText(msg.title) views.tv_money.setText( '¥' .. msg.price) local fee = '免运费' local f = tonumber(msg.fastPostFee); if f and f > 0 then fee = '运费' .. f end views.tv_info.setText(string.format('%s %s 人付款 %s',fee, msg.act,msg.area)) LuaImageLoader.load(views.iv_image, 'https:'..msg.img2) if position == #data then fetchData(true) end end, })) recyclerView.setLayoutManager(LinearLayoutManager(activity)) recyclerView.setAdapter(adapter) refreshLayout.setOnRefreshListener(luajava.createProxy('android.support.v4.widget.SwipeRefreshLayout$OnRefreshListener', { onRefresh = function() reload() end })) bt_search.onClick = function() resetFilter() reload() end et_key.setImeOptions(0x00000003) et_key.setOnEditorActionListener(luajava.createProxy('android.widget.TextView$OnEditorActionListener', { onEditorAction = function(v, actionId, event) resetFilter() reload() return false end })) fab.onClick = function() local choiceItem = 1 for i=1,#sortTypes.keys do if sort == sortTypes.keys[i] then choiceItem = i end end DialogBuilder(activity) .setSingleChoiceItems(sortTypes.names,choiceItem-1,luajava.createProxy('android.content.DialogInterface$OnClickListener',{ onClick = function(dialog, which ) dialog.dismiss() sort = sortTypes.keys[which+1] reload() end })) .show() end end function onCreateOptionsMenu(menu) menu.add("过滤").setIcon(LuaDrawable.create('taobao/ic_filter.png')).setShowAsAction(2) menu.add("网页版") return true end local function showFilterView() local function findCheck( parent ) local c = parent.getChildCount() for i=1,c do local view = parent.getChildAt(i-1) if view.isSelected() then filter.filter[#filter.filter + 1] = view.getTag() end end end local function toggleCheck(v ) v.setSelected(not v.isSelected()) local bgColor = 0x11ffffff if v.isSelected() then bgColor = 0x55ffffff end v.setBackgroundColor(bgColor) end local function title( text ) return { TextView, layout_marginTop = "16dp", layout_marginBottom = "8dp", text = text, textSize = '16sp', } end local function checkText(text,key) local selected = false for i=1,#filter.filter do if filter.filter[i] == key then selected = true end end local bgColor = 0x11ffffff if selected then bgColor = 0x55ffffff end return { TextView, backgroundColor = bgColor, gravity = "center", layout_weight = 1, layout_height = '30dp', layout_marginRight = "8dp", layout_marginTop= "8dp", text = text, textSize = '12sp', tag = key, clickable = true, selected = selected, onClick = toggleCheck, } end local filter_view = { LinearLayout, orientation="vertical", layout_width = "fill", padding = '16dp', title("价格区间"), { LinearLayout, layout_width = "fill", gravity= "center_vertical", {EditText, id = "et_price_start", textSize='13sp', layout_width = '100dp', hint = '最低价', inputType = "number", }, {View, layout_height = "1dp", layout_width = "8dp", background = "#eeeeee", layout_margin = "4dp", }, {EditText, id = "et_price_end", textSize='13sp', layout_width = '100dp', hint = '最高价', inputType = "number", }, }, title("折扣和服务"), { LinearLayout, id = "layout_zk_1", layout_width = "fill", checkText("免运费","service_myf"), checkText("天猫","tab_mall"), checkText("全球购","service_hwsp"), }, { LinearLayout, id = "layout_zk_2", layout_width = "fill", checkText("消费者保障","service_xfzbz"), checkText("手机专享价","service_sjzx"), checkText("淘金币","service_tjb"), }, { LinearLayout, id = "layout_zk_3", layout_width = "fill", checkText("促销","tab_discount"), checkText("7天退换","service_qtth"), checkText("货到付款","service_hdfk"), }, } local views = {} local view = loadlayout(filter_view,views,ViewGroup) if filter.start_price > 0 then views.et_price_start.setText(''..filter.start_price) end if filter.end_price >0 then views.et_price_end.setText(''..filter.end_price) end DialogBuilder(activity).setView(view).setNegativeButton('取消', nil).setPositiveButton('确定', luajava.createProxy('android.content.DialogInterface$OnClickListener', { onClick = function(dialog, which) resetFilter() local p_s = views.et_price_start.getText().toString() if p_s and p_s ~= '' then filter.start_price = tonumber(p_s) end local p_e = views.et_price_end.getText().toString() if p_e and p_e ~= '' then filter.end_price = tonumber(p_e) end findCheck(views.layout_zk_1) findCheck(views.layout_zk_2) findCheck(views.layout_zk_3) if filter.start_price > filter.end_price then filter.start_price,filter.end_price = filter.end_price,filter.start_price end log.print_r(filter) reload() end })).show() end function onOptionsItemSelected(item) local title = item.getTitle() if title == "网页版" then activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse('https://s.m.taobao.com/h5'))) elseif title == '过滤' then showFilterView() end end ================================================ FILE: lua/tieba/activity_news_detail.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaWebView" import "androlua.LuaHttp" local uihelper = require("uihelper") local JSON = require("cjson") local log = require("log") -- create view table local layout = { LinearLayout, layout_width = "fill", layout_height = "fill", orientation = "vertical", statusBarColor = "#ff198ef1", { LinearLayout, orientation = "horizontal", layout_width = "fill", layout_height = "56dp", background = "#ff198ef1", gravity = "center_vertical", { ImageView, id = "back", layout_width = "40dp", layout_height = "40dp", layout_marginLeft = "8dp", scaleType = "centerInside", src = "@drawable/ic_menu_back", }, { TextView, layout_height = "56dp", layout_width = "fill", paddingRight = "16dp", singleLine = true, textIsSelectable = true, ellipsize = "end", id = "tv_title", gravity = "center_vertical", paddingLeft = "8dp", textColor = "#ffffff", textSize = "18sp", }, }, { FrameLayout, layout_width = "fill", layout_height = "fill", { LuaWebView, id = "webview", layout_width = "fill", layout_height = "fill", }, { ProgressBar, layout_gravity = "center", id = "progressBar", layout_width = "40dp", layout_height = "40dp", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", } } } local css = [[ article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}html{font-family:sans-serif;-webkit-text-size-adjust:100%}body{font-family:'Helvetica Neue',Helvetica,Arial,Sans-serif;background:#fff;padding-top:0;margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{margin:.67em 0}h1,h2,h3,h4,h5,h6{font-size:16px}abbr[title]{border-bottom:1px dotted}hr{box-sizing:content-box;height:0}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap}q{quotes:\201C\201D\2018\2019}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0;vertical-align:middle;color:transparent;font-size:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}table{border-collapse:collapse;border-spacing:0;overflow:hidden}a{text-decoration:none}blockquote{border-left:3px solid #d0e5f2;font-style:normal;display:block;vertical-align:baseline;font-size:100%;margin:.5em 0;padding:0 0 0 1em}ul,ol{padding-left:20px}.content{color:#444;line-height:1.6em;font-size:16px;margin:16px 16px 0 16px;}.content img{max-width:100%;margin:4px auto}.content img+img{margin-top:15px}.content img[src*="zhihu.com/equation"]{display:inline-block;margin:0 3px}.content a{color:#259}.content a:hover{text-decoration:underline} .i {border-bottom: 1px solid #EFEFEF; padding-top: 16px; padding-bottom: 16px; } td.l,td.r{ font-size: 12px;} span.g{color: #5199E2;} ]] local htmlTemplate = [[ 氢应用-贴吧
    %s
    下一页
    ]] local params = { rid, page = 0 } function html_unescape(s) return s:gsub("<", "<"):gsub(">", ">"):gsub("&", "&"):gsub(""", '"'):gsub("'", "'"):gsub("/", "/") end local function getData(fromJs) local url = string.format('http://c.tieba.baidu.com/mo/q----,sz@320_240-1-3---2/m?kz=%s&new_word=&pn=%d&lp=6021', params.rid, params.page * 30) if params.pid and params.kid then url = string.format('http://c.tieba.baidu.com/mo/q----,sz@320_240-1-3---2/flr?pid=%s&kz=%s&pn=%d&pinf=1_2_20', params.pid, params.kid, params.page * 30) end LuaHttp.request({ url = url }, function(error, code, body) local tag = 'class="d">(.-)笑话大放送' if params.pid and params.kid then tag = 'class="m t">(.-)' end local content = string.match(body, tag) content = html_unescape(content) content = content:gsub('&quality=45&size=b96_2000', '&quality=80&size=b400_2000') local nextUrl = string.find(content, '>下一页') if nextUrl then params.page = params.page + 1 end content = content:gsub('', ''):gsub('.-', '') local data = string.format(htmlTemplate, css, content) uihelper.runOnUiThread(activity, function() if nextUrl == nil then webview.loadUrl("javascript:hideLoadMore()") end if fromJs then webview.loadUrl(string.format("javascript:appendList('%s')", content)) return end webview.loadDataWithBaseURL("http://c.tieba.baidu.com/mo/q----,sz@320_240-1-3---2/", data, "text/html", "utf-8", nil) end) end) end local function callback(jsonStr) local json = JSON.decode(jsonStr) if json.method == 'loadMore' then getData(true) return end if json.method == 'launchReply' then local lnk = json.data local item = { reply = true, title = '回复', url = lnk } local intent = Intent(activity, LuaActivity) intent.putExtra("luaPath", 'tieba/activity_news_detail.lua') intent.putExtra("item", JSON.encode(item)) activity.startActivity(intent) return end end function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) back.onClick = function() activity.finish() end local str = activity.getIntent().getStringExtra("item"):gsub('\\/', '/') local item = JSON.decode(str) tv_title.setText(item.title) if item.reply then local pid, kid = string.match(item.url, 'pid=(%d+)&kz=(%d+)') params.pid = pid params.kid = kid else params.rid = string.match(item.url, '/p/(%d+)') end webview.setVisibility(0) progressBar.setVisibility(8) getData() webview.injectObjectToJavascript(callback, "luaApp") end function onBackPressed() if webview.canGoBack() then webview.goBack() return true end return false end function onDestroy() if webview then webview.getParent().removeView(webview) webview.destroy() webview = nil end end ================================================ FILE: lua/tieba/fragment_news.lua ================================================ -- -- Created by IntelliJ IDEA. -- User: hanks -- Date: 2017/5/13 -- Time: 00:01 -- To change this template use File | Settings | File Templates. -- require "import" import "android.widget.*" import "android.content.*" import "androlua.LuaAdapter" import "androlua.LuaImageLoader" import "androlua.LuaFragment" import "androlua.LuaHttp" import "androlua.widget.webview.WebViewActivity" import "android.support.v4.widget.SwipeRefreshLayout" import "android.graphics.drawable.GradientDrawable" import "android.os.Build" import "androlua.common.LuaToast" import "androlua.widget.picture.PicturePreviewActivity" local uihelper = require "uihelper" local JSON = require "cjson" local function cleanTag(text) return text:gsub("<.->", ""):gsub("^%s+", ""):gsub("%s$", "") end local function getData(params, data, adapter, fragment, swipe_layout, reload) -- http://c.tieba.baidu.com/mo/q/m?kw=cxczxzxzxx&pn=0&lp=5024&forum_recommend=1&lm=0&cid=0&has_url_param=0&pn=50&is_ajax=1 -- http://c.tieba.baidu.com/mo/q/m?kw=河南理工大学&pn=0&lp=5024&forum_recommend=1&lm=0&cid=0&has_url_param=0&pn=50&is_ajax=1 local url = string.format('http://c.tieba.baidu.com/mo/q/m?kw=%s&pn=0&lp=5024&forum_recommend=1&lm=0&cid=0&has_url_param=0&pn=%d&is_ajax=1', params.rid, params.page * 50) print(url) LuaHttp.request({ url = url }, function(error, code, body) if error or code ~= 200 then return end local json = JSON.decode(body) local isEnd = false if params.page + 1 >= json.data.page.total_page then isEnd = true end params.page = params.page + 1 local body = json.data.content:gsub('\\"', '"') -- print(body) local arr = {} local match_p = '(.-).-class="ti_time">(.-).-(.-)
    .-
    (.-)
    ' local i = 0; for tl_shadow in string.gmatch(body, '
  • (.-)
  • ') do avatar, author, time, url, title, commentCount = string.match(tl_shadow, match_p) -- print(tl_shadow) if title ~= nil and author ~= nil and time ~= nil and commentCount ~= nil then local imgArr = {} for img in string.gmatch(tl_shadow, 'medias_thumb_holder".-data.url="(.-)"') do imgArr[#imgArr + 1] = img end local item = { url = url, title = cleanTag(title), author = cleanTag(author), avatar = avatar, time = cleanTag(time), commentCount = cleanTag(commentCount), imgs = imgArr, } arr[#arr + 1] = item end end uihelper.runOnUiThread(fragment.getActivity(), function() if isEnd then LuaToast.show("finish!!!") end if reload then for k, _ in ipairs(data) do data[k] = nil end end local s = #data for i = 1, #arr do local item = arr[i] if item ~= nil and item.title ~= nil and item.title ~= "" then data[#data + 1] = item end end adapter.notifyDataSetChanged() swipe_layout.setRefreshing(false) end) end) end local function launchDetail(fragment, item) local activity = fragment.getActivity() local intent = Intent(activity, LuaActivity) intent.putExtra("luaPath", 'tieba/activity_news_detail.lua') intent.putExtra("item", JSON.encode(item)) activity.startActivity(intent) end local dividerHeight = uihelper.dp2px(8) local function preview(url) local pid = string.match(url,'/([0-9a-z]-).jpg') url = string.format('http://imgsrc.baidu.com/forum/pic/item/%s.jpg', pid) local args = { uris = { url }, currentIndex = 0 } PicturePreviewActivity.start(activity, JSON.encode(args)) end local function newInstance(rid) -- create view table local layout = { SwipeRefreshLayout, layout_width = "fill", layout_height = "fill", id = "swipe_layout", { ListView, id = "listview", background = '#ebedf0', dividerHeight = dividerHeight, paddingTop = "8dp", clipToPadding = false, layout_width = "fill", layout_height = "fill", } } local item_view = { LinearLayout, background = '#FFFFFF', padding = "12dp", orientation = 'vertical', layout_width = "fill", { FrameLayout, layout_width = "fill", { ImageView, id = "iv_avatar", layout_width = "32dp", layout_height = "32dp", }, { TextView, id = "tv_author", textSize = "13sp", textColor = "#626466", layout_marginLeft = "40dp", }, { TextView, id = "tv_time", layout_width = "100dp", textSize = "11sp", layout_marginLeft = "40dp", layout_marginTop = "18dp", textColor = "#abaeb2", }, }, { TextView, id = "tv_title", layout_width = "fill", maxLines = 2, layout_marginTop = "8dp", lineSpacingMultiplier = 1.3, textSize = "16sp", textColor = "#262626", }, { LinearLayout, id = "layout_image", { ImageView, id = "iv_image1", layout_marginTop = '8dp', layout_width = "100dp", layout_height = "100dp", scaleType = "centerCrop", }, { ImageView, id = "iv_image2", layout_marginTop = '8dp', layout_marginLeft = '8dp', layout_width = "100dp", layout_height = "100dp", scaleType = "centerCrop", }, { ImageView, id = "iv_image3", layout_marginTop = '8dp', layout_marginLeft = '8dp', layout_width = "100dp", layout_height = "100dp", scaleType = "centerCrop", }, }, { TextView, layout_marginTop = '8dp', layout_width = "fill", gravity = "right", id = "tv_comment_count", textSize = "12sp", textColor = "#7798ca", }, } local hadLoadData local isVisible local lastId local params = { rid = rid, page = 0 } local data = {} local ids = {} local adapter local fragment = LuaFragment.newInstance() local function lazyLoad() if not isVisible then return end if hadLoadData then return end if adapter == nil then return end hadLoadData = true getData(params, data, adapter, fragment, ids.swipe_layout) end fragment.setCreator(luajava.createProxy('androlua.LuaFragment$FragmentCreator', { onCreateView = function(inflater, container, savedInstanceState) return loadlayout(layout, ids) end, onViewCreated = function(view, savedInstanceState) adapter = LuaAdapter(luajava.createProxy("androlua.LuaAdapter$AdapterCreator", { getCount = function() return #data end, getView = function(position, convertView, parent) position = position + 1 -- lua 索引从 1开始 if convertView == nil then local views = {} -- store views convertView = loadlayout(item_view, views, ListView) convertView.getLayoutParams().width = parent.getWidth() convertView.setTag(views) views.tv_title.setTypeface(nil, 1); end local views = convertView.getTag() local item = data[position] if item then if item.imgs and #item.imgs > 0 then views.layout_image.setVisibility(0) views.iv_image1.setVisibility(0) LuaImageLoader.load(views.iv_image1, item.imgs[1]) views.iv_image1.onClick = function() preview(item.imgs[1]) end if #item.imgs > 1 then views.iv_image2.setVisibility(0) LuaImageLoader.load(views.iv_image2, item.imgs[2]) views.iv_image2.onClick = function() preview(item.imgs[2]) end else views.iv_image2.setVisibility(8) end if #item.imgs > 2 then views.iv_image3.setVisibility(0) LuaImageLoader.load(views.iv_image3, item.imgs[3]) views.iv_image3.onClick = function() preview(item.imgs[3]) end else views.iv_image3.setVisibility(8) end else views.layout_image.setVisibility(8) end LuaImageLoader.loadWithRadius(views.iv_avatar,16,item.avatar) views.tv_title.setText(item.title or 'ERROR TITLE') views.tv_author.setText(item.author or '') views.tv_time.setText(item.time or '') views.tv_comment_count.setText(item.commentCount .. ' 回复') end if position == #data then getData(params, data, adapter, fragment, ids.swipe_layout) end return convertView end })) ids.listview.setAdapter(adapter) ids.listview.setOnItemClickListener(luajava.createProxy("android.widget.AdapterView$OnItemClickListener", { onItemClick = function(adapter, view, position, id) launchDetail(fragment, data[position + 1]) end, })) ids.swipe_layout.setRefreshing(true) ids.swipe_layout.setOnRefreshListener(luajava.createProxy('android.support.v4.widget.SwipeRefreshLayout$OnRefreshListener', { onRefresh = function() getData(params, data, adapter, fragment, ids.swipe_layout, true) end })) lazyLoad() end, onUserVisible = function(visible) isVisible = visible lazyLoad() end, })) return fragment end return { newInstance = newInstance } ================================================ FILE: lua/tieba/info.json ================================================ { "id": "pub.hanks.tieba", "name": "贴吧", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b0884c77a94448?w=150&h=150&f=png&s=1838", "main": "main.lua", "versionName": "1.4", "versionCode": 6, "desc": "贴吧" } ================================================ FILE: lua/tieba/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "android.support.v4.view.ViewPager" import "android.support.design.widget.TabLayout" import "androlua.adapter.LuaFragmentPageAdapter" import "android.support.v7.widget.Toolbar" import "pub.hydrogen.android.R" import "android.net.Uri" local DialogBuilder = import "android.app.AlertDialog$Builder" local uihelper = require "uihelper" local JSON = require "cjson" local log = require "log" local fragmentNews = require "tieba/fragment_news" local sp = activity.getSharedPreferences('tieba', Context.MODE_PRIVATE) local CONFIG_SITE = "sites" activity.setTheme(R.style.Theme_AppCompat_NoActionBar) -- create view table local layout = { LinearLayout, layout_width = "fill", layout_height = "fill", orientation = "vertical", statusBarColor = "#ff198ef1", { Toolbar, background = '#ff198ef1', id = 'toolbar', layout_width = "match", layout_height = "56dp", titleTextColor = "#FFFFFF", }, { TabLayout, id = "tab", layout_width = "fill", layout_height = "48dp", background = "#ff198ef1", }, { FrameLayout, layout_width = "fill", layout_height = "fill", { ViewPager, id = "viewPager", layout_width = "fill", layout_height = "fill", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", } } } local data = { titles = {}, fragments = {}, } local sites = sp.getString(CONFIG_SITE, "二次元") if not sites:find('||$') then sites = sites .. '||' end for site in string.gmatch(sites, '(.-)||') do print(site) data.fragments[#data.fragments + 1] = fragmentNews.newInstance(site) data.titles[#data.titles + 1] = site .. '吧' end local adapter = LuaFragmentPageAdapter(activity.getSupportFragmentManager(), luajava.createProxy("androlua.adapter.LuaFragmentPageAdapter$AdapterCreator", { getCount = function() return #data.fragments end, getItem = function(position) position = position + 1 return data.fragments[position] end, getPageTitle = function(position) position = position + 1 return data.titles[position] end })) function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) activity.setSupportActionBar(toolbar) activity.setStatusBarColor(0xff198ef1) activity.setTitle('贴吧') toolbar.setNavigationIcon(LuaDrawable.create('tieba/tieba.png')) viewPager.setAdapter(adapter) viewPager.setOffscreenPageLimit(#data.fragments) viewPager.setCurrentItem(0) tab.setSelectedTabIndicatorColor(0xffffffff) tab.setTabTextColors(0x88ffffff, 0xffffffff) tab.setTabMode(TabLayout.MODE_SCROLLABLE) tab.setTabGravity(TabLayout.GRAVITY_CENTER) tab.setupWithViewPager(viewPager) end function onCreateOptionsMenu(menu) menu.add("网页版") menu.add("管理站点") return true end local layout_intput = { LinearLayout, orientation = "vertical", layout_width = 'match', paddingBottom = '8dp', paddingRight = '8dp', { EditText, id = 'et', layout_margin = '16dp', layout_width = 'match', }, { TextView, id = "insert_", text = "点我插入 || ", textSize = "13sp", textColor = "#888888", paddingLeft = "16dp", paddingRight = "16dp", layout_height = "48dp", background = "#11888888", layout_marginLeft = "20dp", gravity = "center", }, } local function manageSites() local ids = {} local view = loadlayout(layout_intput, ids, ViewGroup) ids.et.setText(sp.getString(CONFIG_SITE, '二次元')) ids.insert_.onClick = function() ids.et.append("||") end DialogBuilder(activity).setTitle('多个贴吧之间用 || 分隔').setView(view).setNegativeButton('取消', nil).setPositiveButton('确定', luajava.createProxy('android.content.DialogInterface$OnClickListener', { onClick = function(dialog, which) local txt = ids.et.getText().toString() sp.edit().putString(CONFIG_SITE, txt).apply() activity.toast('保存成功,下次进入生效') end })).show() end function onOptionsItemSelected(item) local title = item.getTitle() if title == "网页版" then activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse('http://c.tieba.baidu.com'))) end if title == "管理站点" then manageSites() end end ================================================ FILE: lua/tv005/activity_agc_detail.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaWebView" import "androlua.LuaHttp" local uihelper = require "uihelper" -- create view table local layout = { LinearLayout, layout_width = "fill", layout_height = "fill", orientation = "vertical", statusBarColor = "#fffb7299", { LinearLayout, orientation = "horizontal", layout_width = "fill", layout_height = "56dp", background = "#fffb7299", gravity = "center_vertical", { ImageView, id = "back", layout_width = "40dp", layout_height = "40dp", layout_marginLeft = "8dp", scaleType = "centerInside", src = "@drawable/ic_menu_back", }, { TextView, layout_height = "56dp", layout_width = "fill", paddingRight = "16dp", singleLine = true, textIsSelectable = true, ellipsize = "end", id = "tv_title", gravity = "center_vertical", paddingLeft = "8dp", textColor = "#ffffff", textSize = "16sp", }, }, { FrameLayout, layout_width = "fill", layout_height = "fill", { LuaWebView, id = "webview", layout_width = "fill", layout_height = "fill", }, { ProgressBar, layout_gravity = "center", id = "progressBar", layout_width = "40dp", layout_height = "40dp", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", } } } local css = [[ video{width:100%}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}html{font-family:sans-serif;-webkit-text-size-adjust:100%}body{font-family:'Helvetica Neue',Helvetica,Arial,Sans-serif;background:#fff;padding-top:0;margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{margin:.67em 0}h1,h2,h3,h4,h5,h6{font-size:16px}abbr[title]{border-bottom:1px dotted}hr{box-sizing:content-box;height:0}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap}q{quotes:\201C\201D\2018\2019}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0;vertical-align:middle;color:transparent;font-size:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}table{border-collapse:collapse;border-spacing:0;overflow:hidden}a{text-decoration:none}blockquote{border-left:3px solid #d0e5f2;font-style:normal;display:block;vertical-align:baseline;font-size:100%;margin:.5em 0;padding:0 0 0 1em}ul,ol{padding-left:20px}.content{color:#444;line-height:1.6em;font-size:16px;margin:16px}.content img{max-width:100%;display:block;margin:30px auto}.content img+img{margin-top:15px}.content img[src*="zhihu.com/equation"]{display:inline-block;margin:0 3px}.content a{color:#259}.content a:hover{text-decoration:underline} ]] local htmlTemplate = [[
    %s
    ]] function html_unescape(s) return s:gsub("<", "<"):gsub(">", ">"):gsub("&", "&"):gsub(""", '"'):gsub("'", "'"):gsub("/", "/") end function onCreate(savedInstanceState) activity.setStatusBarColor(0xfffb7299) activity.setContentView(loadlayout(layout)) back.onClick = function() activity.finish() end local url = activity.getIntent().getStringExtra('url') tv_title.setText(url) webview.setVisibility(0) progressBar.setVisibility(8) LuaHttp.request({ url = url }, function(error, code, body) local content = string.match(body, '(
    '):gsub('style=".-"', ''):gsub('width=".-"', ''):gsub('height=".-"', '') local data = string.format(htmlTemplate, css, html_unescape(content)) uihelper.runOnUiThread(activity, function() --print(data) webview.loadData(data, "text/html; charset=UTF-8", nil) end) end) end function onDestroy() if webview then webview.getParent().removeView(webview) webview.destroy() webview = nil end end ================================================ FILE: lua/tv005/fragment_agc_news.lua ================================================ import "androlua.LuaAdapter" import "androlua.LuaImageLoader" import "androlua.LuaHttp" import "androlua.LuaFragment" import "androlua.widget.webview.WebViewActivity" local uihelper = require("uihelper") local JSON = require("cjson") local function fetchData(baseUrl, data, adapter, fragment) local url = string.format(baseUrl, data.page) LuaHttp.request({ url = url }, function(error, code, body) if error or code ~= 200 then print("fetchData error : " .. url) return end local news = {} for v in string.gmatch(body, '
  • (.-)
  • ') do local url = string.match(v, '
    ') local imgUrl = string.match(v, '') local title = string.match(v, '

    .-target="_blank">(.-)') local time = string.match(v, '(.-)') -- local desc = string.match(v,'
    (.-)
    ') if url and imgUrl and time then title = title:gsub("%s+", "") time = time:gsub("%s+", "") news[#news + 1] = { title = title, time = time, url = url, imgUrl = imgUrl } end end uihelper.runOnUiThread(fragment.getActivity(), function() for i = 1, #news do data.news[#data.news + 1] = news[i] end data.page = data.page + 1 adapter.notifyDataSetChanged() end) end) end function launchDetail(fragment, item) local activity = fragment.getActivity() local intent = Intent(activity, LuaActivity) intent.putExtra("luaPath", 'tv005/activity_agc_detail.lua') intent.putExtra("url", '' .. item.url) activity.startActivity(intent) end function newInstance(baseUrl) local layout = { ListView, id = "listview", layout_width = "fill", layout_height = "fill", } local item_view = { RelativeLayout, layout_width = "fill", layout_height = "wrap", paddingLeft = "16dp", paddingRight = "12dp", paddingTop = "16dp", paddingBottom = "16dp", { ImageView, id = "iv_image", layout_alignParentRight = true, layout_width = "100dp", layout_height = "75dp", scaleType = "centerCrop", }, { TextView, id = "tv_title", layout_width = "fill", paddingRight = "16dp", maxLines = "2", layout_alignParentLeft = true, lineSpacingMultiplier = '1.3', textSize = "14sp", textColor = "#222222", layout_toLeftOf = "iv_image", }, { TextView, id = "tv_time", layout_width = "120dp", paddingRight = "16dp", layout_alignParentLeft = true, layout_alignParentBottom = true, textSize = "12sp", textColor = "#aaaaaa", layout_toLeftOf = "iv_image", }, } local data = { page = 1, news = {} } local ids = {} local fragment = LuaFragment.newInstance() local adapter fragment.setCreator(luajava.createProxy('androlua.LuaFragment$FragmentCreator', { onCreateView = function(inflater, container, savedInstanceState) return loadlayout(layout, ids) end, onViewCreated = function(view, savedInstanceState) adapter = LuaAdapter(luajava.createProxy("androlua.LuaAdapter$AdapterCreator", { getCount = function() return #data.news end, getView = function(position, convertView, parent) position = position + 1 -- lua 索引从 1开始 if position == #data.news then fetchData(baseUrl, data, adapter, fragment) end if convertView == nil then local views = {} -- store views convertView = loadlayout(item_view, views, ListView) convertView.getLayoutParams().width = parent.getWidth() convertView.setTag(views) end local views = convertView.getTag() local item = data.news[position] if item.imgUrl then views.iv_image.setVisibility(0) LuaImageLoader.load(views.iv_image, item.imgUrl) else views.iv_image.setVisibility(8) end views.tv_title.setText(item.title) views.tv_time.setText(item.time) return convertView end })) ids.listview.setAdapter(adapter) ids.listview.setOnItemClickListener(luajava.createProxy("android.widget.AdapterView$OnItemClickListener", { onItemClick = function(adapter, view, position, id) launchDetail(fragment, data.news[position + 1]) end, })) fetchData(baseUrl, data, adapter, fragment) -- getdata may call ther lua files end, })) return fragment end return { newInstance = newInstance } ================================================ FILE: lua/tv005/info.json ================================================ { "id": "pub.hanks.tv005", "name": "动漫资讯", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b0885388e77ef8?w=150&h=150&f=png&s=2567", "main": "main.lua", "versionName": "1.0.2", "versionCode": 4, "desc": "动漫新闻,cosplay,005.tv" } ================================================ FILE: lua/tv005/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A agc news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "android.support.v4.view.ViewPager" import "android.support.design.widget.TabLayout" import "androlua.adapter.LuaFragmentPageAdapter" local fragmentNews = require "tv005/fragment_agc_news" local layout = { LinearLayout, layout_width = "fill", layout_height = "fill", orientation = "vertical", statusBarColor = "#fb7299", { TabLayout, id = "tab", layout_width = "fill", layout_height = "48dp", background = "#fffb7299", }, { FrameLayout, { ViewPager, id = "viewPager", layout_width = "fill", layout_height = "fill", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", } }, } local data = { titles = {}, fragments = {}, } table.insert(data.fragments, fragmentNews.newInstance("http://www.005.tv/zx/list_526_%s.html")) table.insert(data.titles, '二次元资讯') table.insert(data.fragments, fragmentNews.newInstance("http://www.005.tv/zwh/list_519_%s.html")) table.insert(data.titles, '慢慢说') table.insert(data.fragments, fragmentNews.newInstance("http://www.005.tv/Cosplay/list_522_%s.html")) table.insert(data.titles, 'cos本子图') table.insert(data.fragments, fragmentNews.newInstance("http://www.005.tv/zh/list_524_%s.html")) table.insert(data.titles, '展会资讯') local adapter = LuaFragmentPageAdapter(activity.getSupportFragmentManager(), luajava.createProxy("androlua.adapter.LuaFragmentPageAdapter$AdapterCreator", { getCount = function() return #data.fragments end, getItem = function(position) position = position + 1 return data.fragments[position] end, getPageTitle = function(position) position = position + 1 return data.titles[position] end })) function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) viewPager.setAdapter(adapter) tab.setSelectedTabIndicatorColor(0xffffffff) tab.setTabTextColors(0x88ffffff, 0xffffffff) tab.setTabMode(TabLayout.MODE_SCROLLABLE) tab.setTabGravity(TabLayout.GRAVITY_CENTER) tab.setupWithViewPager(viewPager) end ================================================ FILE: lua/weather/city.lua ================================================ local China = { ['北京'] = { ['北京'] = { _101010700 = '昌平', _101011400 = '门头沟', _101011000 = '石景山', _101010900 = '丰台', _101010300 = '朝阳', _101010100 = '北京', _101010800 = '延庆', _101011300 = '密云', _101010600 = '通州', _101011100 = '大兴', _101011200 = '房山', _101010400 = '顺义', _101010200 = '海淀', _101010500 = '怀柔', _101011500 = '平谷', }, }, ['上海'] = { ['上海'] = { _101021000 = '奉贤', _101020100 = '上海', _101020800 = '青浦', _101020700 = '金山', _101021200 = '徐汇', _101020500 = '嘉定', _101020600 = '浦东新区', _101020900 = '松江', _101021100 = '崇明', _101020300 = '宝山', _101020200 = '闵行', }, }, ['天津'] = { ['天津'] = { _101031400 = '蓟县', _101030300 = '宝坻', _101030500 = '西青', _101031000 = '津南', _101030100 = '天津', _101030700 = '宁河', _101030600 = '北辰', _101030200 = '武清', _101031100 = '滨海新区', _101030900 = '静海', _101030400 = '东丽', }, }, ['湖南'] = { ['益阳'] = { _101250701 = '赫山区', _101250702 = '南县', _101250705 = '沅江', _101250700 = '益阳', _101250704 = '安化', _101250703 = '桃江', }, ['邵阳'] = { _101250901 = '邵阳', _101250902 = '隆回', _101250908 = '武冈', _101250909 = '城步', _101250905 = '邵东', _101250906 = '绥宁', _101250907 = '新宁', _101250910 = '邵阳县', _101250903 = '洞口', _101250904 = '新邵', }, ['郴州'] = { _101250509 = '安仁', _101250510 = '永兴', _101250501 = '郴州', _101250507 = '资兴', _101250502 = '桂阳', _101250505 = '临武', _101250512 = '苏仙', _101250504 = '宜章', _101250511 = '桂东', _101250508 = '汝城', _101250503 = '嘉禾', }, ['常德'] = { _101250607 = '石门', _101250608 = '津市', _101250605 = '澧县', _101250603 = '桃源', _101250606 = '临澧', _101250601 = '常德', _101250604 = '汉寿', _101250602 = '安乡', }, ['湘西'] = { _101251508 = '花垣', _101251506 = '泸溪', _101251504 = '古丈', _101251502 = '保靖', _101251507 = '龙山', _101251503 = '永顺', _101251505 = '凤凰', _101251501 = '吉首', }, ['长沙'] = { _101250102 = '宁乡', _101250104 = '湘江新区', _101250105 = '望城', _101250103 = '浏阳', _101250106 = '长沙县', _101250101 = '长沙', }, ['怀化'] = { _101251204 = '辰溪', _101251203 = '沅陵', _101251208 = '麻阳', _101251211 = '溆浦', _101251207 = '通道', _101251210 = '芷江', _101251209 = '新晃', _101251212 = '中方', _101251206 = '会同', _101251213 = '洪江', _101251205 = '靖州', _101251201 = '怀化', }, ['株洲'] = { _101250301 = '株洲', _101250303 = '醴陵', _101250305 = '茶陵', _101250302 = '攸县', _101250306 = '炎陵', }, ['湘潭'] = { _101250203 = '湘乡', _101250201 = '湘潭', _101250202 = '韶山', }, ['衡阳'] = { _101250409 = '南岳', _101250404 = '祁东', _101250402 = '衡山', _101250405 = '衡阳县', _101250407 = '衡南', _101250401 = '衡阳', _101250406 = '常宁', _101250403 = '衡东', _101250408 = '耒阳', }, ['永州'] = { _101251409 = '新田', _101251411 = '冷水滩', _101251406 = '宁远', _101251410 = '江华', _101251408 = '蓝山', _101251407 = '江永', _101251404 = '双牌', _101251401 = '永州', _101251403 = '东安', _101251402 = '祁阳', _101251405 = '道县', }, ['娄底'] = { _101250801 = '娄底', _101250802 = '双峰', _101250806 = '涟源', _101250805 = '新化', _101250803 = '冷水江', }, ['岳阳'] = { _101251002 = '华容', _101251003 = '湘阴', _101251004 = '汨罗', _101251006 = '临湘', _101251001 = '岳阳', _101251005 = '平江', }, ['张家界'] = { _101251103 = '慈利', _101251102 = '桑植', _101251104 = '武陵源', _101251101 = '张家界', }, }, ['浙江'] = { ['嘉兴'] = { _101210301 = '嘉兴', _101210306 = '海盐', _101210305 = '平湖', _101210302 = '嘉善', _101210303 = '海宁', _101210304 = '桐乡', }, ['宁波'] = { _101210404 = '余姚', _101210401 = '宁波', _101210410 = '北仑', _101210408 = '宁海', _101210405 = '奉化', _101210403 = '慈溪', _101210412 = '镇海', _101210411 = '鄞州', _101210406 = '象山', }, ['杭州'] = { _101210104 = '淳安', _101210102 = '萧山', _101210105 = '建德', _101210103 = '桐庐', _101210107 = '临安', _101210106 = '余杭', _101210108 = '富阳', _101210101 = '杭州', }, ['舟山'] = { _101211104 = '岱山', _101211105 = '普陀', _101211106 = '定海', _101211102 = '嵊泗', _101211101 = '舟山', }, ['台州'] = { _101210609 = '洪家', _101210603 = '玉环', _101210607 = '温岭', _101210605 = '天台', _101210601 = '台州', _101210604 = '三门', _101210610 = '临海', _101210606 = '仙居', _101210612 = '黄岩', _101210611 = '椒江', _101210613 = '路桥', }, ['温州'] = { _101210709 = '苍南', _101210705 = '瑞安', _101210708 = '永嘉', _101210704 = '平阳', _101210701 = '温州', _101210707 = '乐清', _101210702 = '泰顺', _101210703 = '文成', _101210706 = '洞头', }, ['绍兴'] = { _101210501 = '越城', _101210504 = '新昌', _101210502 = '诸暨', _101210506 = '柯桥', _101210503 = '上虞', _101210505 = '嵊州', }, ['丽水'] = { _101210806 = '云和', _101210808 = '松阳', _101210804 = '缙云', _101210803 = '龙泉', _101210807 = '庆元', _101210802 = '遂昌', _101210801 = '丽水', _101210809 = '景宁', _101210805 = '青田', }, ['湖州'] = { _101210204 = '德清', _101210201 = '湖州', _101210203 = '安吉', _101210202 = '长兴', }, ['金华'] = { _101210908 = '磐安', _101210903 = '兰溪', _101210907 = '永康', _101210906 = '武义', _101210902 = '浦江', _101210904 = '义乌', _101210901 = '金华', _101210905 = '东阳', }, ['衢州'] = { _101211006 = '衢江', _101211003 = '开化', _101211002 = '常山', _101211005 = '江山', _101211004 = '龙游', _101211001 = '衢州', }, }, ['海南'] = { ['三亚'] = { _101310201 = '三亚', }, ['东方'] = { _101310202 = '东方', }, ['西沙'] = { _101310217 = '西沙', }, ['海口'] = { _101310101 = '海口', }, ['定安'] = { _101310209 = '定安', }, ['屯昌'] = { _101310210 = '屯昌', }, ['南沙'] = { _101310220 = '南沙', }, ['陵水'] = { _101310216 = '陵水', }, ['五指山'] = { _101310222 = '五指山', }, ['临高'] = { _101310203 = '临高', }, ['中沙'] = { _101310224 = '中沙', }, ['儋州'] = { _101310205 = '儋州', }, ['乐东'] = { _101310221 = '乐东', }, ['文昌'] = { _101310212 = '文昌', }, ['万宁'] = { _101310215 = '万宁', }, ['琼海'] = { _101310211 = '琼海', }, ['琼中'] = { _101310208 = '琼中', }, ['澄迈'] = { _101310204 = '澄迈', }, ['保亭'] = { _101310214 = '保亭', }, ['昌江'] = { _101310206 = '昌江', }, ['白沙'] = { _101310207 = '白沙', }, }, ['青海'] = { ['西宁'] = { _101150102 = '大通', _101150103 = '湟源', _101150104 = '湟中', _101150101 = '西宁', }, ['海北'] = { _101150802 = '门源', _101150801 = '海北', _101150806 = '刚察', _101150803 = '祁连', }, ['海西'] = { _101150714 = '格尔木', _101150708 = '天峻', _101150713 = '大柴旦', _101150701 = '德令哈', _101150716 = '冷湖', _101150709 = '乌兰', _101150712 = '茫崖', _101150715 = '都兰', }, ['海东'] = { _101150206 = '循化', _101150201 = '平安', _101150205 = '化隆', _101150204 = '互助', _101150202 = '乐都', _101150203 = '民和', }, ['玉树'] = { _101150605 = '囊谦', _101150604 = '杂多', _101150601 = '玉树', _101150606 = '曲麻莱', _101150603 = '治多', _101150602 = '称多', }, ['海南'] = { _101150406 = '兴海', _101150401 = '共和', _101150408 = '同德', _101150407 = '贵南', _101150404 = '贵德', }, ['果洛'] = { _101150501 = '玛沁', _101150503 = '甘德', _101150506 = '玛多', _101150504 = '达日', _101150502 = '班玛', _101150505 = '久治', }, ['黄南'] = { _101150304 = '河南', _101150301 = '同仁', _101150303 = '泽库', _101150302 = '尖扎', }, }, ['湖北'] = { ['仙桃'] = { _101201601 = '仙桃', }, ['恩施'] = { _101201007 = '来凤', _101201001 = '恩施', _101201003 = '建始', _101201005 = '宣恩', _101201008 = '巴东', _101201004 = '咸丰', _101201002 = '利川', _101201006 = '鹤峰', }, ['武汉'] = { _101200104 = '新洲', _101200102 = '蔡甸', _101200101 = '武汉', _101200106 = '东西湖', _101200105 = '江夏', _101200103 = '黄陂', }, ['鄂州'] = { _101200301 = '鄂州', _101200302 = '梁子湖', }, ['宜昌'] = { _101200902 = '远安', _101200903 = '秭归', _101200908 = '长阳', _101200909 = '宜都', _101200912 = '夷陵', _101200910 = '枝江', _101200904 = '兴山', _101200911 = '三峡', _101200906 = '五峰', _101200907 = '当阳', _101200901 = '宜昌', }, ['随州'] = { _101201301 = '随州', _101201302 = '广水', }, ['黄石'] = { _101200604 = '铁山', _101200601 = '黄石', _101200602 = '大冶', _101200603 = '阳新', _101200606 = '西塞山', _101200605 = '下陆', }, ['孝感'] = { _101200404 = '大悟', _101200402 = '安陆', _101200405 = '应城', _101200401 = '孝感', _101200406 = '汉川', _101200407 = '孝昌', _101200403 = '云梦', }, ['襄阳'] = { _101200204 = '南漳', _101200202 = '襄州', _101200203 = '保康', _101200208 = '枣阳', _101200205 = '宜城', _101200206 = '老河口', _101200207 = '谷城', _101200201 = '襄阳', }, ['天门'] = { _101201501 = '天门', }, ['潜江'] = { _101201701 = '潜江', }, ['咸宁'] = { _101200703 = '嘉鱼', _101200701 = '咸宁', _101200702 = '赤壁', _101200705 = '通城', _101200704 = '崇阳', _101200706 = '通山', }, ['神农架'] = { _101201201 = '神农架', }, ['十堰'] = { _101201104 = '郧阳', _101201109 = '张湾', _101201106 = '房县', _101201107 = '丹江口', _101201105 = '竹山', _101201102 = '竹溪', _101201108 = '茅箭', _101201103 = '郧西', _101201101 = '十堰', }, ['黄冈'] = { _101200508 = '黄梅', _101200502 = '红安', _101200506 = '浠水', _101200505 = '英山', _101200510 = '团风', _101200509 = '武穴', _101200507 = '蕲春', _101200503 = '麻城', _101200501 = '黄冈', _101200504 = '罗田', }, ['荆州'] = { _101200807 = '松滋', _101200801 = '荆州', _101200808 = '沙市', _101200804 = '石首', _101200806 = '洪湖', _101200805 = '监利', _101200803 = '公安', _101200802 = '江陵', }, ['荆门'] = { _101201403 = '京山', _101201404 = '掇刀', _101201405 = '沙洋', _101201402 = '钟祥', _101201401 = '荆门', }, }, ['新疆'] = { ['伊犁'] = { _101131002 = '察布查尔', _101131004 = '伊宁县', _101131010 = '霍尔果斯', _101131001 = '伊宁', _101131003 = '尼勒克', _101131008 = '特克斯', _101131011 = '奎屯', _101131007 = '昭苏', _101131006 = '新源', _101131005 = '巩留', _101131009 = '霍城', }, ['石河子'] = { _101130303 = '莫索湾', _101130301 = '石河子', _101130302 = '炮台', }, ['和田'] = { _101131306 = '民丰', _101131301 = '和田', _101131302 = '皮山', _101131307 = '于田', _101131304 = '墨玉', _101131305 = '洛浦', _101131303 = '策勒', }, ['克州'] = { _101131503 = '阿克陶', _101131502 = '乌恰', _101131501 = '阿图什', _101131504 = '阿合奇', }, ['克拉玛依'] = { _101130202 = '乌尔禾', _101130203 = '白碱滩', _101130201 = '克拉玛依', }, ['阿克苏'] = { _101130804 = '拜城', _101130809 = '阿瓦提', _101130802 = '乌什', _101130806 = '沙雅', _101130807 = '库车', _101130801 = '阿克苏', _101130805 = '新和', _101130803 = '温宿', _101130808 = '柯坪', }, ['博尔塔拉'] = { _101131606 = '阿拉山口', _101131601 = '博乐', _101131603 = '精河', _101131602 = '温泉', }, ['阿拉尔'] = { _101130701 = '阿拉尔', }, ['吐鲁番'] = { _101130502 = '托克逊', _101130504 = '鄯善', _101130501 = '吐鲁番', }, ['哈密'] = { _101131203 = '巴里坤', _101131201 = '哈密', _101131204 = '伊吾', }, ['昌吉'] = { _101130405 = '吉木萨尔', _101130403 = '米泉', _101130404 = '阜康', _101130401 = '昌吉', _101130406 = '奇台', _101130409 = '蔡家湖', _101130408 = '木垒', _101130402 = '呼图壁', _101130407 = '玛纳斯', }, ['阿勒泰'] = { _101131407 = '福海', _101131409 = '青河', _101131401 = '阿勒泰', _101131408 = '富蕴', _101131402 = '哈巴河', _101131406 = '布尔津', _101131405 = '吉木乃', }, ['塔城'] = { _101131103 = '额敏', _101131105 = '托里', _101131102 = '裕民', _101131101 = '塔城', _101131107 = '沙湾', _101131106 = '乌苏', _101131104 = '和布克赛尔', }, ['巴音郭楞'] = { _101130603 = '尉犁', _101130606 = '和静', _101130611 = '铁干里克', _101130613 = '塔中', _101130601 = '库尔勒', _101130608 = '和硕', _101130610 = '巴音布鲁克', _101130612 = '博湖', _101130604 = '若羌', _101130607 = '焉耆', _101130605 = '且末', _101130602 = '轮台', _101130614 = '巴仑台', }, ['乌鲁木齐'] = { _101130103 = '小渠子', _101130109 = '天池', _101130108 = '乌鲁木齐牧试站', _101130110 = '白杨沟', _101130101 = '乌鲁木齐', _101130105 = '达坂城', }, ['喀什'] = { _101130905 = '莎车', _101130907 = '泽普', _101130912 = '疏勒', _101130911 = '疏附', _101130910 = '伽师', _101130906 = '叶城', _101130909 = '岳普湖', _101130903 = '塔什库尔干', _101130908 = '巴楚', _101130902 = '英吉沙', _101130904 = '麦盖提', _101130901 = '喀什', }, }, ['宁夏'] = { ['吴忠'] = { _101170306 = '青铜峡', _101170302 = '同心', _101170301 = '吴忠', _101170303 = '盐池', }, ['固原'] = { _101170403 = '隆德', _101170404 = '泾源', _101170401 = '固原', _101170402 = '西吉', _101170406 = '彭阳', }, ['银川'] = { _101170101 = '银川', _101170104 = '贺兰', _101170103 = '灵武', _101170102 = '永宁', }, ['中卫'] = { _101170501 = '中卫', _101170504 = '海原', _101170502 = '中宁', }, ['石嘴山'] = { _101170204 = '陶乐', _101170203 = '平罗', _101170201 = '石嘴山', _101170202 = '惠农', }, }, ['西藏'] = { ['林芝'] = { _101140401 = '林芝', _101140403 = '米林', _101140406 = '朗县', _101140402 = '波密', _101140405 = '工布江达', _101140407 = '墨脱', _101140404 = '察隅', }, ['拉萨'] = { _101140108 = '墨竹工卡', _101140101 = '拉萨', _101140107 = '达孜', _101140103 = '尼木', _101140106 = '曲水', _101140102 = '当雄', _101140104 = '林周', _101140105 = '堆龙德庆', }, ['山南'] = { _101140314 = '曲松', _101140313 = '琼结', _101140309 = '乃东', _101140306 = '错那', _101140304 = '加查', _101140305 = '浪卡子', _101140310 = '桑日', _101140312 = '措美', _101140301 = '山南', _101140303 = '扎囊', _101140307 = '隆子', _101140311 = '洛扎', _101140308 = '泽当', _101140302 = '贡嘎', }, ['日喀则'] = { _101140209 = '萨嘎', _101140202 = '拉孜', _101140212 = '定结', _101140217 = '白朗', _101140203 = '南木林', _101140207 = '帕里', _101140204 = '聂拉木', _101140201 = '日喀则', _101140210 = '吉隆', _101140205 = '定日', _101140220 = '仁布', _101140211 = '昂仁', _101140219 = '康马', _101140218 = '亚东', _101140216 = '岗巴', _101140214 = '谢通门', _101140208 = '仲巴', _101140206 = '江孜', _101140213 = '萨迦', }, ['昌都'] = { _101140510 = '察雅', _101140502 = '丁青', _101140511 = '贡觉', _101140509 = '江达', _101140508 = '八宿', _101140505 = '左贡', _101140507 = '类乌齐', _101140503 = '边坝', _101140501 = '昌都', _101140504 = '洛隆', _101140506 = '芒康', }, ['阿里'] = { _101140706 = '札达', _101140702 = '改则', _101140709 = '革吉', _101140705 = '普兰', _101140707 = '噶尔', _101140708 = '日土', _101140701 = '阿里', _101140704 = '狮泉河', _101140710 = '措勤', _101140703 = '申扎', }, ['那曲'] = { _101140605 = '安多', _101140606 = '索县', _101140602 = '尼玛', _101140601 = '那曲', _101140603 = '嘉黎', _101140607 = '聂荣', _101140610 = '双湖', _101140609 = '比如', _101140608 = '巴青', _101140604 = '班戈', }, }, ['吉林'] = { ['延边'] = { _101060307 = '龙井', _101060304 = '汪清', _101060309 = '图们', _101060308 = '珲春', _101060305 = '和龙', _101060302 = '敦化', _101060301 = '延吉', _101060303 = '安图', }, ['吉林'] = { _101060204 = '蛟河', _101060201 = '吉林', _101060202 = '舒兰', _101060203 = '永吉', _101060205 = '磐石', _101060206 = '桦甸', }, ['白山'] = { _101060902 = '靖宇', _101060904 = '东岗', _101060905 = '长白', _101060901 = '白山', _101060903 = '临江', _101060906 = '抚松', _101060907 = '江源', }, ['白城'] = { _101060604 = '镇赉', _101060602 = '洮南', _101060605 = '通榆', _101060603 = '大安', _101060601 = '白城', }, ['四平'] = { _101060401 = '四平', _101060405 = '伊通', _101060403 = '梨树', _101060404 = '公主岭', _101060402 = '双辽', }, ['长春'] = { _101060104 = '九台', _101060107 = '二道', _101060101 = '长春', _101060102 = '农安', _101060103 = '德惠', _101060106 = '双阳', _101060105 = '榆树', }, ['通化'] = { _101060506 = '通化县', _101060501 = '通化', _101060502 = '梅河口', _101060504 = '辉南', _101060505 = '集安', _101060503 = '柳河', }, ['松原'] = { _101060805 = '扶余', _101060804 = '长岭', _101060803 = '前郭', _101060801 = '松原', _101060802 = '乾安', }, ['辽源'] = { _101060702 = '东丰', _101060703 = '东辽', _101060701 = '辽源', }, }, ['江西'] = { ['赣州'] = { _101240716 = '寻乌', _101240712 = '安远', _101240710 = '于都', _101240705 = '大余', _101240713 = '全南', _101240701 = '赣州', _101240704 = '南康', _101240707 = '宁都', _101240709 = '瑞金', _101240706 = '信丰', _101240715 = '定南', _101240711 = '会昌', _101240708 = '石城', _101240717 = '兴国', _101240703 = '上犹', _101240714 = '龙南', _101240718 = '赣县', _101240702 = '崇义', }, ['新余'] = { _101241002 = '分宜', _101241001 = '新余', }, ['吉安'] = { _101240609 = '万安', _101240601 = '吉安', _101240612 = '安福', _101240602 = '吉安县', _101240611 = '泰和', _101240603 = '吉水', _101240607 = '永新', _101240605 = '峡江', _101240608 = '井冈山', _101240613 = '宁冈', _101240606 = '永丰', _101240610 = '遂川', _101240604 = '新干', }, ['九江'] = { _101240204 = '武宁', _101240208 = '彭泽', _101240202 = '瑞昌', _101240201 = '九江', _101240205 = '德安', _101240206 = '永修', _101240212 = '修水', _101240210 = '都昌', _101240209 = '星子', _101240203 = '庐山', _101240207 = '湖口', }, ['萍乡'] = { _101240903 = '上栗', _101240904 = '安源', _101240902 = '莲花', _101240905 = '芦溪', _101240906 = '湘东', _101240901 = '萍乡', }, ['南昌'] = { _101240101 = '南昌', _101240102 = '新建', _101240103 = '南昌县', _101240104 = '安义', _101240105 = '进贤', }, ['宜春'] = { _101240509 = '樟树', _101240510 = '丰城', _101240506 = '靖安', _101240507 = '奉新', _101240503 = '宜丰', _101240504 = '万载', _101240502 = '铜鼓', _101240508 = '高安', _101240501 = '宜春', _101240505 = '上高', }, ['抚州'] = { _101240407 = '宜黄', _101240405 = '金溪', _101240411 = '东乡', _101240408 = '南城', _101240406 = '资溪', _101240410 = '黎川', _101240409 = '南丰', _101240401 = '抚州', _101240404 = '崇仁', _101240402 = '广昌', _101240403 = '乐安', }, ['鹰潭'] = { _101241102 = '余江', _101241101 = '鹰潭', _101241103 = '贵溪', }, ['景德镇'] = { _101240801 = '景德镇', _101240802 = '乐平', _101240803 = '浮梁', }, ['上饶'] = { _101240310 = '横峰', _101240307 = '德兴', _101240309 = '弋阳', _101240302 = '鄱阳', _101240306 = '万年', _101240308 = '上饶县', _101240313 = '广丰', _101240301 = '上饶', _101240312 = '玉山', _101240303 = '婺源', _101240311 = '铅山', _101240305 = '余干', }, }, ['广东'] = { ['汕尾'] = { _101282101 = '汕尾', _101282102 = '海丰', _101282104 = '陆河', _101282103 = '陆丰', }, ['佛山'] = { _101280800 = '佛山', _101280801 = '顺德', _101280803 = '南海', _101280802 = '三水', _101280804 = '高明', }, ['惠州'] = { _101280305 = '龙门', _101280304 = '惠东', _101280302 = '博罗', _101280301 = '惠州', _101280303 = '惠阳', }, ['揭阳'] = { _101281904 = '惠来', _101281902 = '揭西', _101281905 = '揭东', _101281903 = '普宁', _101281901 = '揭阳', }, ['肇庆'] = { _101280901 = '肇庆', _101280908 = '高要', _101280906 = '怀集', _101280907 = '封开', _101280905 = '德庆', _101280902 = '广宁', _101280903 = '四会', }, ['阳江'] = { _101281804 = '阳西', _101281801 = '阳江', _101281802 = '阳春', _101281803 = '阳东', }, ['茂名'] = { _101282003 = '化州', _101282002 = '高州', _101282001 = '茂名', _101282004 = '电白', _101282005 = '信宜', _101282006 = '茂港', }, ['清远'] = { _101281308 = '清新', _101281307 = '英德', _101281301 = '清远', _101281303 = '连州', _101281302 = '连南', _101281304 = '连山', _101281306 = '佛冈', _101281305 = '阳山', }, ['汕头'] = { _101280502 = '潮阳', _101280504 = '南澳', _101280503 = '澄海', _101280501 = '汕头', }, ['梅州'] = { _101280401 = '梅州', _101280404 = '大埔', _101280407 = '平远', _101280409 = '梅县', _101280408 = '五华', _101280406 = '丰顺', _101280403 = '蕉岭', _101280402 = '兴宁', }, ['云浮'] = { _101281401 = '云浮', _101281403 = '新兴', _101281406 = '云安', _101281402 = '罗定', _101281404 = '郁南', }, ['湛江'] = { _101281004 = '徐闻', _101281001 = '湛江', _101281002 = '吴川', _101281005 = '廉江', _101281007 = '遂溪', _101281008 = '坡头', _101281006 = '赤坎', _101281003 = '雷州', _101281009 = '霞山', _101281010 = '麻章', }, ['潮州'] = { _101281503 = '潮安', _101281501 = '潮州', _101281502 = '饶平', }, ['广州'] = { _101280103 = '从化', _101280104 = '增城', _101280102 = '番禺', _101280105 = '花都', _101280101 = '广州', }, ['中山'] = { _101281701 = '中山', }, ['东莞'] = { _101281601 = '东莞', }, ['河源'] = { _101281203 = '连平', _101281206 = '东源', _101281201 = '河源', _101281202 = '紫金', _101281205 = '龙川', _101281204 = '和平', }, ['韶关'] = { _101280207 = '南雄', _101280206 = '仁化', _101280209 = '曲江', _101280203 = '始兴', _101280205 = '乐昌', _101280208 = '新丰', _101280204 = '翁源', _101280211 = '武江', _101280201 = '韶关', _101280210 = '浈江', _101280202 = '乳源', }, ['江门'] = { _101281109 = '江海', _101281106 = '台山', _101281103 = '开平', _101281108 = '鹤山', _101281104 = '新会', _101281105 = '恩平', _101281107 = '蓬江', _101281101 = '江门', }, ['深圳'] = { _101280601 = '深圳', }, ['珠海'] = { _101280702 = '斗门', _101280703 = '金湾', _101280701 = '珠海', }, }, ['安徽'] = { ['池州'] = { _101221703 = '青阳', _101221705 = '石台', _101221704 = '九华山', _101221702 = '东至', _101221701 = '池州', }, ['芜湖'] = { _101220301 = '芜湖', _101220304 = '南陵', _101220303 = '芜湖县', _101220302 = '繁昌', _101220305 = '无为', }, ['蚌埠'] = { _101220202 = '怀远', _101220204 = '五河', _101220201 = '蚌埠', _101220203 = '固镇', }, ['淮北'] = { _101221202 = '濉溪', _101221201 = '淮北', }, ['宣城'] = { _101221403 = '旌德', _101221407 = '郎溪', _101221406 = '广德', _101221401 = '宣城', _101221404 = '宁国', _101221402 = '泾县', _101221405 = '绩溪', }, ['六安'] = { _101221502 = '霍邱', _101221507 = '舒城', _101221506 = '霍山', _101221501 = '六安', _101221505 = '金寨', _101221503 = '寿县', }, ['黄山'] = { _101221007 = '休宁', _101221005 = '黟县', _101221008 = '黄山风景区(光明顶)', _101221002 = '黄山区', _101221006 = '歙县', _101221003 = '屯溪', _101221001 = '黄山', _101221004 = '祁门', }, ['淮南'] = { _101220403 = '潘集', _101220402 = '凤台', _101220401 = '淮南', }, ['滁州'] = { _101221106 = '来安', _101221102 = '凤阳', _101221105 = '全椒', _101221107 = '天长', _101221101 = '滁州', _101221103 = '明光', _101221104 = '定远', }, ['阜阳'] = { _101220806 = '太和', _101220804 = '临泉', _101220802 = '阜南', _101220805 = '界首', _101220801 = '阜阳', _101220803 = '颍上', }, ['宿州'] = { _101220701 = '宿州', _101220704 = '泗县', _101220705 = '萧县', _101220703 = '灵璧', _101220702 = '砀山', }, ['亳州'] = { _101220903 = '利辛', _101220902 = '涡阳', _101220904 = '蒙城', _101220901 = '亳州', }, ['铜陵'] = { _101221301 = '铜陵', }, ['合肥'] = { _101220104 = '肥西', _101220102 = '长丰', _101220103 = '肥东', _101220101 = '合肥', _101220105 = '巢湖', _101220106 = '庐江', }, ['安庆'] = { _101220608 = '岳西', _101220602 = '枞阳', _101220607 = '望江', _101220609 = '桐城', _101220606 = '宿松', _101220604 = '潜山', _101220603 = '太湖', _101220601 = '安庆', _101220605 = '怀宁', }, ['马鞍山'] = { _101220504 = '和县', _101220503 = '含山', _101220502 = '当涂', _101220501 = '马鞍山', }, }, ['甘肃'] = { ['平凉'] = { _101160308 = '崆峒', _101160301 = '平凉', _101160306 = '庄浪', _101160307 = '静宁', _101160302 = '泾川', _101160304 = '崇信', _101160303 = '灵台', _101160305 = '华亭', }, ['庆阳'] = { _101160409 = '庆城', _101160404 = '华池', _101160401 = '庆阳', _101160407 = '宁县', _101160406 = '正宁', _101160408 = '镇原', _101160403 = '环县', _101160405 = '合水', }, ['嘉峪关'] = { _101161401 = '嘉峪关', }, ['张掖'] = { _101160706 = '山丹', _101160705 = '高台', _101160704 = '临泽', _101160703 = '民乐', _101160702 = '肃南', _101160701 = '张掖', }, ['兰州'] = { _101160104 = '榆中', _101160103 = '永登', _101160101 = '兰州', _101160102 = '皋兰', }, ['金昌'] = { _101160602 = '永昌', _101160601 = '金昌', }, ['陇南'] = { _101161004 = '宕昌', _101161002 = '成县', _101161006 = '西和', _101161008 = '徽县', _101161003 = '文县', _101161005 = '康县', _101161007 = '礼县', _101161009 = '两当', _101161001 = '武都', }, ['定西'] = { _101160202 = '通渭', _101160204 = '渭源', _101160208 = '安定', _101160207 = '岷县', _101160203 = '陇西', _101160205 = '临洮', _101160206 = '漳县', _101160201 = '定西', }, ['临夏'] = { _101161103 = '永靖', _101161104 = '广河', _101161101 = '临夏', _101161102 = '康乐', _101161106 = '东乡', _101161105 = '和政', _101161107 = '积石山', }, ['白银'] = { _101161303 = '会宁', _101161305 = '景泰', _101161301 = '白银', _101161302 = '靖远', _101161304 = '平川', }, ['甘南'] = { _101161205 = '迭部', _101161208 = '夏河', _101161207 = '碌曲', _101161203 = '卓尼', _101161201 = '合作', _101161202 = '临潭', _101161204 = '舟曲', _101161206 = '玛曲', }, ['天水'] = { _101160905 = '甘谷', _101160907 = '张家川', _101160903 = '清水', _101160908 = '麦积', _101160901 = '天水', _101160906 = '武山', _101160904 = '秦安', }, ['酒泉'] = { _101160803 = '金塔', _101160808 = '敦煌', _101160801 = '酒泉', _101160807 = '玉门', _101160806 = '肃北', _101160804 = '阿克塞', _101160805 = '瓜州', }, ['武威'] = { _101160503 = '古浪', _101160502 = '民勤', _101160501 = '武威', _101160505 = '天祝', }, }, ['陕西'] = { ['西安'] = { _101110101 = '西安', _101110107 = '高陵', _101110106 = '户县', _101110103 = '临潼', _101110102 = '长安', _101110104 = '蓝田', _101110105 = '周至', }, ['延安'] = { _101110308 = '甘泉', _101110300 = '延安', _101110304 = '宜川', _101110309 = '洛川', _101110302 = '延川', _101110312 = '吴起', _101110311 = '黄龙', _101110307 = '安塞', _101110303 = '子长', _101110306 = '志丹', _101110310 = '黄陵', _101110305 = '富县', _101110301 = '延长', }, ['杨凌'] = { _101111101 = '杨凌', }, ['榆林'] = { _101110410 = '绥德', _101110412 = '清涧', _101110403 = '神木', _101110413 = '榆阳', _101110404 = '佳县', _101110401 = '榆林', _101110409 = '子洲', _101110411 = '吴堡', _101110408 = '米脂', _101110402 = '府谷', _101110406 = '靖边', _101110405 = '定边', _101110407 = '横山', }, ['渭南'] = { _101110508 = '澄城', _101110502 = '华县', _101110504 = '大荔', _101110503 = '潼关', _101110501 = '渭南', _101110511 = '华阴', _101110505 = '白水', _101110510 = '韩城', _101110509 = '合阳', _101110507 = '蒲城', _101110506 = '富平', }, ['铜川'] = { _101111002 = '耀县', _101111001 = '铜川', _101111004 = '耀州', _101111003 = '宜君', }, ['宝鸡'] = { _101110909 = '太白', _101110911 = '陇县', _101110904 = '麟游', _101110903 = '千阳', _101110906 = '凤翔', _101110901 = '宝鸡', _101110907 = '扶风', _101110910 = '凤县', _101110908 = '眉县', _101110912 = '陈仓', _101110905 = '岐山', }, ['安康'] = { _101110707 = '平利', _101110704 = '汉阴', _101110702 = '紫阳', _101110703 = '石泉', _101110708 = '白河', _101110709 = '镇坪', _101110701 = '安康', _101110706 = '岚皋', _101110710 = '宁陕', _101110705 = '旬阳', }, ['咸阳'] = { _101110202 = '礼泉', _101110205 = '泾阳', _101110203 = '永寿', _101110208 = '彬县', _101110210 = '旬邑', _101110200 = '咸阳', _101110209 = '长武', _101110211 = '兴平', _101110204 = '淳化', _101110207 = '乾县', _101110206 = '武功', _101110201 = '三原', }, ['商洛'] = { _101110603 = '柞水', _101110608 = '山阳', _101110604 = '商州', _101110602 = '洛南', _101110607 = '商南', _101110606 = '丹凤', _101110601 = '商洛', _101110605 = '镇安', }, ['汉中'] = { _101110801 = '汉中', _101110807 = '西乡', _101110805 = '洋县', _101110810 = '南郑', _101110808 = '佛坪', _101110804 = '留坝', _101110809 = '宁强', _101110811 = '镇巴', _101110803 = '勉县', _101110806 = '城固', _101110802 = '略阳', }, }, ['四川'] = { ['资阳'] = { _101271303 = '乐至', _101271304 = '简阳', _101271302 = '安岳', _101271301 = '资阳', }, ['遂宁'] = { _101270703 = '射洪', _101270701 = '遂宁', _101270702 = '蓬溪', }, ['阿坝'] = { _101271904 = '茂县', _101271905 = '松潘', _101271910 = '马尔康', _101271902 = '汶川', _101271908 = '小金', _101271911 = '壤塘', _101271912 = '若尔盖', _101271901 = '阿坝', _101271906 = '九寨沟', _101271907 = '金川', _101271913 = '红原', _101271903 = '理县', _101271909 = '黑水', }, ['绵阳'] = { _101270402 = '三台', _101270408 = '江油', _101270404 = '安县', _101270401 = '绵阳', _101270406 = '北川', _101270405 = '梓潼', _101270403 = '盐亭', _101270407 = '平武', }, ['成都'] = { _101270115 = '青白江', _101270113 = '邛崃', _101270114 = '崇州', _101270109 = '蒲江', _101270108 = '大邑', _101270102 = '龙泉驿', _101270107 = '郫县', _101270105 = '金堂', _101270103 = '新都', _101270110 = '新津', _101270104 = '温江', _101270111 = '都江堰', _101270106 = '双流', _101270112 = '彭州', _101270101 = '成都', }, ['内江'] = { _101271201 = '内江', _101271204 = '资中', _101271203 = '威远', _101271205 = '隆昌', _101271202 = '东兴', }, ['广元'] = { _101272102 = '旺苍', _101272103 = '青川', _101272105 = '苍溪', _101272101 = '广元', _101272104 = '剑阁', }, ['达州'] = { _101270602 = '宣汉', _101270608 = '达川', _101270601 = '达州', _101270605 = '渠县', _101270607 = '通川', _101270606 = '万源', _101270604 = '大竹', _101270603 = '开江', }, ['德阳'] = { _101272005 = '绵竹', _101272001 = '德阳', _101272006 = '罗江', _101272002 = '中江', _101272003 = '广汉', _101272004 = '什邡', }, ['泸州'] = { _101271005 = '叙永', _101271006 = '古蔺', _101271007 = '纳溪', _101271004 = '合江', _101271003 = '泸县', _101271001 = '泸州', }, ['雅安'] = { _101271704 = '汉源', _101271703 = '荥经', _101271708 = '宝兴', _101271706 = '天全', _101271707 = '芦山', _101271702 = '名山', _101271701 = '雅安', _101271705 = '石棉', }, ['南充'] = { _101270504 = '蓬安', _101270506 = '西充', _101270507 = '阆中', _101270505 = '仪陇', _101270501 = '南充', _101270502 = '南部', _101270503 = '营山', }, ['宜宾'] = { _101271103 = '宜宾县', _101271110 = '兴文', _101271106 = '长宁', _101271101 = '宜宾', _101271111 = '屏山', _101271105 = '江安', _101271107 = '高县', _101271109 = '筠连', _101271108 = '珙县', _101271104 = '南溪', }, ['巴中'] = { _101270901 = '巴中', _101270903 = '南江', _101270902 = '通江', _101270904 = '平昌', }, ['自贡'] = { _101270303 = '荣县', _101270301 = '自贡', _101270302 = '富顺', }, ['广安'] = { _101270802 = '岳池', _101270801 = '广安', _101270805 = '华蓥', _101270803 = '武胜', _101270804 = '邻水', }, ['凉山'] = { _101271614 = '冕宁', _101271610 = '西昌', _101271615 = '越西', _101271611 = '金阳', _101271616 = '甘洛', _101271604 = '盐源', _101271605 = '德昌', _101271617 = '雷波', _101271609 = '普格', _101271612 = '昭觉', _101271603 = '木里', _101271619 = '布拖', _101271618 = '美姑', _101271613 = '喜德', _101271607 = '会东', _101271601 = '凉山', _101271606 = '会理', _101271608 = '宁南', }, ['乐山'] = { _101271406 = '峨边', _101271405 = '沐川', _101271402 = '犍为', _101271404 = '夹江', _101271401 = '乐山', _101271407 = '马边', _101271409 = '峨眉山', _101271403 = '井研', _101271408 = '峨眉', }, ['眉山'] = { _101271502 = '仁寿', _101271504 = '洪雅', _101271506 = '青神', _101271501 = '眉山', _101271505 = '丹棱', _101271503 = '彭山', }, ['甘孜'] = { _101271813 = '色达', _101271815 = '巴塘', _101271811 = '白玉', _101271814 = '理塘', _101271810 = '德格', _101271803 = '泸定', _101271806 = '雅江', _101271805 = '九龙', _101271816 = '乡城', _101271804 = '丹巴', _101271812 = '石渠', _101271808 = '炉霍', _101271817 = '稻城', _101271802 = '康定', _101271818 = '得荣', _101271809 = '新龙', _101271801 = '甘孜', _101271807 = '道孚', }, ['攀枝花'] = { _101270202 = '仁和', _101270203 = '米易', _101270204 = '盐边', _101270201 = '攀枝花', }, }, ['台湾'] = { ['高雄'] = { _101340202 = '嘉义', _101340203 = '台南', _101340205 = '屏东', _101340201 = '高雄', _101340204 = '台东', }, ['台中'] = { _101340402 = '苗栗', _101340404 = '南投', _101340405 = '花莲', _101340406 = '云林', _101340403 = '彰化', _101340401 = '台中', }, ['台北'] = { _101340101 = '台北', _101340102 = '桃园', _101340103 = '新竹', _101340104 = '宜兰', }, }, ['澳门'] = { ['澳门'] = { _101330101 = '澳门', _101330102 = '氹仔岛', _101330103 = '路环岛', }, }, ['香港'] = { ['香港'] = { _101320103 = '新界', _101320101 = '香港', _101320102 = '九龙', }, }, ['云南'] = { ['西双版纳'] = { _101291603 = '勐海', _101291601 = '景洪', _101291605 = '勐腊', }, ['玉溪'] = { _101290704 = '通海', _101290705 = '华宁', _101290702 = '澄江', _101290709 = '元江', _101290708 = '峨山', _101290706 = '新平', _101290707 = '易门', _101290701 = '玉溪', _101290703 = '江川', }, ['丽江'] = { _101291404 = '宁蒗', _101291402 = '永胜', _101291401 = '丽江', _101291403 = '华坪', }, ['临沧'] = { _101291104 = '双江', _101291108 = '镇康', _101291103 = '耿马', _101291101 = '临沧', _101291106 = '永德', _101291105 = '凤庆', _101291107 = '云县', _101291102 = '沧源', }, ['曲靖'] = { _101290409 = '宣威', _101290402 = '沾益', _101290401 = '曲靖', _101290404 = '富源', _101290406 = '师宗', _101290408 = '会泽', _101290403 = '陆良', _101290405 = '马龙', _101290407 = '罗平', }, ['德宏'] = { _101291503 = '陇川', _101291501 = '德宏', _101291506 = '瑞丽', _101291507 = '梁河', _101291508 = '芒市', _101291504 = '盈江', }, ['迪庆'] = { _101291302 = '德钦', _101291303 = '维西', _101291301 = '香格里拉', _101291304 = '中甸', }, ['怒江'] = { _101291201 = '怒江', _101291203 = '福贡', _101291204 = '兰坪', _101291207 = '贡山', _101291206 = '六库', _101291205 = '泸水', }, ['文山'] = { _101290601 = '文山', _101290604 = '麻栗坡', _101290603 = '马关', _101290605 = '砚山', _101290608 = '富宁', _101290606 = '丘北', _101290602 = '西畴', _101290607 = '广南', }, ['昭通'] = { _101291005 = '威信', _101291009 = '盐津', _101291004 = '镇雄', _101291010 = '大关', _101291002 = '鲁甸', _101291011 = '水富', _101291006 = '巧家', _101291001 = '昭通', _101291003 = '彝良', _101291007 = '绥江', _101291008 = '永善', }, ['保山'] = { _101290504 = '施甸', _101290505 = '昌宁', _101290503 = '龙陵', _101290501 = '保山', _101290506 = '腾冲', }, ['楚雄'] = { _101290806 = '南华', _101290801 = '楚雄', _101290804 = '姚安', _101290810 = '永仁', _101290809 = '双柏', _101290808 = '禄丰', _101290807 = '武定', _101290805 = '牟定', _101290803 = '元谋', _101290802 = '大姚', }, ['昆明'] = { _101290104 = '寻甸', _101290111 = '禄劝', _101290106 = '宜良', _101290112 = '安宁', _101290105 = '晋宁', _101290113 = '太华山', _101290110 = '嵩明', _101290107 = '石林', _101290101 = '昆明', _101290109 = '富民', _101290108 = '呈贡', _101290103 = '东川', }, ['普洱'] = { _101290912 = '宁洱', _101290907 = '江城', _101290903 = '景东', _101290906 = '墨江', _101290911 = '镇沅', _101290909 = '西盟', _101290908 = '孟连', _101290901 = '普洱', _101290902 = '景谷', _101290904 = '澜沧', }, ['大理'] = { _101290208 = '巍山', _101290209 = '剑川', _101290206 = '弥渡', _101290212 = '南涧', _101290210 = '洱源', _101290211 = '鹤庆', _101290204 = '永平', _101290205 = '宾川', _101290207 = '祥云', _101290201 = '大理', _101290202 = '云龙', _101290203 = '漾濞', }, ['红河'] = { _101290306 = '绿春', _101290305 = '元阳', _101290301 = '红河', _101290313 = '河口', _101290307 = '开远', _101290310 = '屏边', _101290302 = '石屏', _101290311 = '泸西', _101290309 = '蒙自', _101290308 = '个旧', _101290312 = '金平', _101290303 = '建水', _101290304 = '弥勒', }, }, ['广西'] = { ['南宁'] = { _101300104 = '横县', _101300101 = '南宁', _101300103 = '邕宁', _101300105 = '隆安', _101300108 = '武鸣', _101300109 = '宾阳', _101300107 = '上林', _101300106 = '马山', }, ['玉林'] = { _101300905 = '陆川', _101300901 = '玉林', _101300906 = '兴业', _101300904 = '容县', _101300903 = '北流', _101300902 = '博白', }, ['崇左'] = { _101300203 = '龙州', _101300202 = '天等', _101300201 = '崇左', _101300206 = '扶绥', _101300205 = '大新', _101300204 = '凭祥', _101300207 = '宁明', }, ['柳州'] = { _101300308 = '三江', _101300307 = '融水', _101300302 = '柳城', _101300301 = '柳州', _101300305 = '柳江', _101300306 = '融安', _101300304 = '鹿寨', }, ['桂林'] = { _101300506 = '兴安', _101300512 = '平乐', _101300513 = '荔浦', _101300501 = '桂林', _101300514 = '资源', _101300507 = '灵川', _101300511 = '恭城', _101300508 = '全州', _101300510 = '阳朔', _101300504 = '永福', _101300509 = '灌阳', _101300503 = '龙胜', _101300505 = '临桂', }, ['来宾'] = { _101300403 = '金秀', _101300404 = '象州', _101300405 = '武宣', _101300402 = '忻城', _101300406 = '合山', _101300401 = '来宾', }, ['防城港'] = { _101301403 = '东兴', _101301405 = '防城', _101301401 = '防城港', _101301402 = '上思', }, ['北海'] = { _101301301 = '北海', _101301302 = '合浦', _101301303 = '涠洲岛', }, ['河池'] = { _101301202 = '天峨', _101301205 = '环江', _101301207 = '宜州', _101301204 = '巴马', _101301210 = '都安', _101301201 = '河池', _101301208 = '凤山', _101301211 = '大化', _101301203 = '东兰', _101301206 = '罗城', _101301209 = '南丹', }, ['钦州'] = { _101301103 = '灵山', _101301102 = '浦北', _101301101 = '钦州', }, ['梧州'] = { _101300604 = '苍梧', _101300602 = '藤县', _101300601 = '梧州', _101300605 = '蒙山', _101300606 = '岑溪', }, ['百色'] = { _101301011 = '凌云', _101301010 = '乐业', _101301007 = '平果', _101301009 = '西林', _101301008 = '隆林', _101301005 = '靖西', _101301012 = '田林', _101301006 = '田东', _101301002 = '那坡', _101301001 = '百色', _101301004 = '德保', _101301003 = '田阳', }, ['贵港'] = { _101300802 = '桂平', _101300801 = '贵港', _101300803 = '平南', }, ['贺州'] = { _101300704 = '钟山', _101300703 = '富川', _101300702 = '昭平', _101300701 = '贺州', }, }, ['内蒙古'] = { ['通辽'] = { _101080501 = '通辽', _101080509 = '扎鲁特', _101080507 = '库伦', _101080508 = '奈曼', _101080511 = '巴雅尔吐胡硕', _101080512 = '霍林郭勒', _101080506 = '开鲁', _101080505 = '青龙山', _101080504 = '科左后旗', _101080502 = '舍伯吐', _101080503 = '科左中旗', }, ['呼伦贝尔'] = { _101081006 = '鄂温克旗', _101081005 = '鄂伦春旗', _101081008 = '新左旗', _101081015 = '根河', _101081003 = '阿荣旗', _101081009 = '新右旗', _101081002 = '小二沟', _101081007 = '陈旗', _101081012 = '扎兰屯', _101081014 = '额尔古纳', _101081004 = '莫力达瓦', _101081011 = '牙克石', _101081010 = '满洲里', _101081016 = '图里河', _101081001 = '海拉尔', }, ['乌海'] = { _101080301 = '乌海', }, ['乌兰察布'] = { _101080412 = '丰镇', _101080406 = '兴和', _101080401 = '集宁', _101080404 = '商都', _101080409 = '察右中旗', _101080408 = '察右前旗', _101080407 = '凉城', _101080403 = '化德', _101080410 = '察右后旗', _101080411 = '四子王旗', _101080402 = '卓资', }, ['巴彦淖尔'] = { _101080803 = '磴口', _101080806 = '乌中旗', _101080801 = '临河', _101080809 = '那仁宝力格', _101080808 = '海力素', _101080805 = '大佘太', _101080802 = '五原', _101080807 = '乌后旗', _101080810 = '杭锦后旗', _101080804 = '乌前旗', }, ['阿拉善盟'] = { _101081208 = '中泉子', _101081211 = '乌斯太', _101081206 = '锡林高勒', _101081205 = '吉兰太', _101081209 = '巴彦诺日公', _101081202 = '阿右旗', _101081201 = '阿左旗', _101081210 = '雅布赖', _101081203 = '额济纳', _101081207 = '头道湖', _101081212 = '孪井滩', _101081204 = '拐子湖', }, ['兴安盟'] = { _101081104 = '胡尔勒', _101081102 = '阿尔山', _101081105 = '扎赉特', _101081107 = '突泉', _101081109 = '科右前旗', _101081110 = '高力板', _101081103 = '科右中旗', _101081101 = '乌兰浩特', _101081106 = '索伦', }, ['锡林郭勒'] = { _101080909 = '东乌旗', _101080901 = '锡林浩特', _101080907 = '苏右旗', _101080910 = '西乌旗', _101080911 = '太仆寺', _101080906 = '苏左旗', _101080913 = '正镶白旗', _101080903 = '二连浩特', _101080916 = '博克图', _101080915 = '多伦', _101080904 = '阿巴嘎', _101080908 = '朱日和', _101080914 = '正蓝旗', _101080912 = '镶黄旗', _101080917 = '乌拉盖', }, ['鄂尔多斯'] = { _101080708 = '鄂托克', _101080703 = '达拉特', _101080707 = '伊和乌素', _101080706 = '河南', _101080701 = '鄂尔多斯', _101080712 = '乌审召', _101080705 = '鄂前旗', _101080713 = '东胜', _101080711 = '伊金霍洛', _101080710 = '乌审旗', _101080709 = '杭锦旗', _101080704 = '准格尔', }, ['赤峰'] = { _101080606 = '巴林右旗', _101080611 = '喀喇沁', _101080605 = '巴林左旗', _101080601 = '赤峰', _101080608 = '克什克腾', _101080609 = '翁牛特', _101080613 = '宁城', _101080610 = '岗子', _101080612 = '八里罕', _101080604 = '浩尔吐', _101080614 = '敖汉', _101080615 = '宝国吐', _101080607 = '林西', _101080603 = '阿鲁旗', }, ['包头'] = { _101080202 = '白云鄂博', _101080207 = '希拉穆仁', _101080206 = '达茂旗', _101080205 = '固阳', _101080203 = '满都拉', _101080204 = '土右旗', _101080201 = '包头', }, ['呼和浩特'] = { _101080103 = '托县', _101080102 = '土左旗', _101080107 = '武川', _101080105 = '清水河', _101080106 = '赛罕', _101080104 = '和林', _101080101 = '呼和浩特', }, }, ['重庆'] = { ['重庆'] = { _101042000 = '巫山', _101042400 = '忠县', _101040800 = '北碚', _101040300 = '合川', _101042900 = '璧山', _101042200 = '垫江', _101042800 = '铜梁', _101043200 = '彭水', _101040900 = '巴南', _101041000 = '长寿', _101041900 = '奉节', _101043300 = '綦江', _101041400 = '涪陵', _101040500 = '江津', _101042100 = '潼南', _101043400 = '酉阳', _101042600 = '大足', _101041700 = '云阳', _101041100 = '黔江', _101041600 = '城口', _101043100 = '武隆', _101043000 = '丰都', _101040400 = '南川', _101042500 = '石柱', _101041300 = '万州', _101042300 = '梁平', _101041800 = '巫溪', _101040100 = '重庆', _101040700 = '渝北', _101041500 = '开县', _101043600 = '秀山', _101042700 = '荣昌', _101040600 = '万盛', _101040200 = '永川', }, }, ['山西'] = { ['运城'] = { _101100801 = '运城', _101100802 = '临猗', _101100808 = '闻喜', _101100813 = '平陆', _101100809 = '垣曲', _101100811 = '芮城', _101100806 = '新绛', _101100812 = '夏县', _101100810 = '永济', _101100804 = '万荣', _101100805 = '河津', _101100807 = '绛县', _101100803 = '稷山', }, ['大同'] = { _101100202 = '阳高', _101100201 = '大同', _101100208 = '左云', _101100207 = '浑源', _101100206 = '灵丘', _101100203 = '大同县', _101100204 = '天镇', _101100205 = '广灵', }, ['临汾'] = { _101100707 = '襄汾', _101100713 = '翼城', _101100717 = '古县', _101100711 = '霍州', _101100716 = '安泽', _101100702 = '曲沃', _101100715 = '浮山', _101100708 = '蒲县', _101100701 = '临汾', _101100709 = '汾西', _101100705 = '大宁', _101100706 = '吉县', _101100710 = '洪洞', _101100704 = '隰县', _101100712 = '乡宁', _101100714 = '侯马', _101100703 = '永和', }, ['朔州'] = { _101100903 = '山阴', _101100902 = '平鲁', _101100904 = '右玉', _101100906 = '怀仁', _101100905 = '应县', _101100901 = '朔州', }, ['太原'] = { _101100101 = '太原', _101100105 = '古交', _101100104 = '娄烦', _101100107 = '小店区', _101100103 = '阳曲', _101100102 = '清徐', _101100106 = '尖草坪区', }, ['长治'] = { _101100505 = '襄垣', _101100510 = '沁源', _101100503 = '屯留', _101100507 = '武乡', _101100501 = '长治', _101100506 = '平顺', _101100508 = '沁县', _101100504 = '潞城', _101100509 = '长子', _101100502 = '黎城', _101100511 = '壶关', }, ['吕梁'] = { _101101104 = '岚县', _101101112 = '文水', _101101110 = '孝义', _101101103 = '兴县', _101101105 = '柳林', _101101101 = '离石', _101101113 = '交城', _101101109 = '中阳', _101101111 = '汾阳', _101101107 = '方山', _101101102 = '临县', _101101106 = '石楼', _101101108 = '交口', _101101100 = '吕梁', }, ['忻州'] = { _101101004 = '河曲', _101101005 = '偏关', _101101012 = '静乐', _101101014 = '五寨', _101101011 = '保德', _101101008 = '代县', _101101001 = '忻州', _101101007 = '宁武', _101101015 = '原平', _101101006 = '神池', _101101010 = '五台山', _101101002 = '定襄', _101101009 = '繁峙', _101101003 = '五台县', _101101013 = '岢岚', }, ['晋中'] = { _101100410 = '平遥', _101100404 = '左权', _101100408 = '太谷', _101100409 = '祁县', _101100402 = '榆次', _101100403 = '榆社', _101100407 = '寿阳', _101100405 = '和顺', _101100412 = '介休', _101100401 = '晋中', _101100411 = '灵石', _101100406 = '昔阳', }, ['阳泉'] = { _101100301 = '阳泉', _101100302 = '盂县', _101100303 = '平定', }, ['晋城'] = { _101100604 = '陵川', _101100606 = '泽州', _101100605 = '高平', _101100603 = '阳城', _101100601 = '晋城', _101100602 = '沁水', }, }, ['河南'] = { ['安阳'] = { _101180204 = '内黄', _101180203 = '滑县', _101180205 = '林州', _101180202 = '汤阴', _101180201 = '安阳', }, ['濮阳'] = { _101181303 = '南乐', _101181301 = '濮阳', _101181304 = '清丰', _101181302 = '台前', _101181305 = '范县', }, ['焦作'] = { _101181108 = '孟州', _101181104 = '沁阳', _101181101 = '焦作', _101181102 = '修武', _101181107 = '温县', _101181106 = '博爱', _101181103 = '武陟', }, ['开封'] = { _101180803 = '尉氏', _101180804 = '通许', _101180805 = '兰考', _101180802 = '杞县', _101180801 = '开封', }, ['周口'] = { _101181408 = '郸城', _101181406 = '商水', _101181404 = '淮阳', _101181401 = '周口', _101181403 = '太康', _101181410 = '沈丘', _101181409 = '鹿邑', _101181407 = '项城', _101181402 = '扶沟', _101181405 = '西华', }, ['三门峡'] = { _101181702 = '灵宝', _101181704 = '卢氏', _101181706 = '陕县', _101181703 = '渑池', _101181705 = '义马', _101181701 = '三门峡', }, ['许昌'] = { _101180401 = '许昌', _101180403 = '襄城', _101180405 = '禹州', _101180404 = '长葛', _101180402 = '鄢陵', }, ['洛阳'] = { _101180906 = '伊川', _101180905 = '洛宁', _101180911 = '吉利', _101180903 = '孟津', _101180901 = '洛阳', _101180907 = '嵩县', _101180910 = '汝阳', _101180908 = '偃师', _101180909 = '栾川', _101180902 = '新安', _101180904 = '宜阳', }, ['南阳'] = { _101180702 = '南召', _101180704 = '社旗', _101180707 = '镇平', _101180703 = '方城', _101180709 = '新野', _101180705 = '西峡', _101180708 = '淅川', _101180701 = '南阳', _101180711 = '邓州', _101180710 = '唐河', _101180712 = '桐柏', _101180706 = '内乡', }, ['济源'] = { _101181801 = '济源', }, ['漯河'] = { _101181501 = '漯河', _101181502 = '临颍', _101181503 = '舞阳', }, ['信阳'] = { _101180607 = '潢川', _101180609 = '商城', _101180604 = '光山', _101180602 = '息县', _101180608 = '固始', _101180605 = '新县', _101180603 = '罗山', _101180601 = '信阳', _101180606 = '淮滨', }, ['商丘'] = { _101181009 = '永城', _101181005 = '虞城', _101181006 = '柘城', _101181003 = '睢县', _101181007 = '宁陵', _101181008 = '夏邑', _101181004 = '民权', _101181001 = '商丘', }, ['驻马店'] = { _101181609 = '确山', _101181601 = '驻马店', _101181608 = '新蔡', _101181603 = '遂平', _101181610 = '正阳', _101181602 = '西平', _101181605 = '汝南', _101181604 = '上蔡', _101181606 = '泌阳', _101181607 = '平舆', }, ['郑州'] = { _101180104 = '登封', _101180101 = '郑州', _101180106 = '新郑', _101180102 = '巩义', _101180105 = '新密', _101180108 = '上街', _101180107 = '中牟', _101180103 = '荥阳', }, ['平顶山'] = { _101180505 = '叶县', _101180503 = '宝丰', _101180507 = '鲁山', _101180501 = '平顶山', _101180502 = '郏县', _101180504 = '汝州', _101180506 = '舞钢', _101180508 = '石龙', }, ['鹤壁'] = { _101181203 = '淇县', _101181202 = '浚县', _101181201 = '鹤壁', }, ['新乡'] = { _101180302 = '获嘉', _101180305 = '卫辉', _101180307 = '封丘', _101180306 = '延津', _101180303 = '原阳', _101180308 = '长垣', _101180304 = '辉县', _101180301 = '新乡', }, }, ['河北'] = { ['沧州'] = { _101090706 = '肃宁', _101090711 = '泊头', _101090709 = '献县', _101090716 = '沧县', _101090705 = '盐山', _101090710 = '孟村', _101090703 = '东光', _101090702 = '青县', _101090707 = '南皮', _101090713 = '黄骅', _101090714 = '河间', _101090708 = '吴桥', _101090704 = '海兴', _101090701 = '沧州', _101090712 = '任丘', }, ['邢台'] = { _101090916 = '南宫', _101090911 = '广宗', _101090909 = '巨鹿', _101090912 = '平乡', _101090906 = '隆尧', _101090905 = '柏乡', _101090917 = '沙河', _101090901 = '邢台', _101090914 = '清河', _101090908 = '宁晋', _101090913 = '威县', _101090910 = '新河', _101090918 = '任县', _101090907 = '南和', _101090915 = '临西', _101090902 = '临城', _101090904 = '内丘', }, ['廊坊'] = { _101090601 = '廊坊', _101090604 = '香河', _101090607 = '大厂', _101090602 = '固安', _101090609 = '三河', _101090608 = '霸州', _101090605 = '大城', _101090606 = '文安', _101090603 = '永清', }, ['保定'] = { _101090215 = '蠡县', _101090201 = '保定', _101090211 = '安新', _101090209 = '涞源', _101090204 = '徐水', _101090207 = '容城', _101090220 = '安国', _101090217 = '雄县', _101090214 = '曲阳', _101090210 = '望都', _101090203 = '阜平', _101090225 = '博野', _101090218 = '涿州', _101090224 = '清苑', _101090216 = '顺平', _101090219 = '定州', _101090223 = '定兴', _101090222 = '涞水', _101090221 = '高碑店', _101090206 = '高阳', _101090212 = '易县', _101090205 = '唐县', _101090202 = '满城', }, ['唐山'] = { _101090502 = '丰南', _101090504 = '滦县', _101090505 = '滦南', _101090507 = '迁西', _101090501 = '唐山', _101090512 = '曹妃甸工业区', _101090510 = '遵化', _101090506 = '乐亭', _101090503 = '丰润', _101090508 = '玉田', _101090509 = '曹妃甸', _101090511 = '迁安', }, ['邯郸'] = { _101091006 = '涉县', _101091008 = '肥乡', _101091004 = '成安', _101091016 = '武安', _101091015 = '曲周', _101091001 = '邯郸', _101091002 = '峰峰', _101091010 = '邱县', _101091013 = '馆陶', _101091012 = '广平', _101091007 = '磁县', _101091005 = '大名', _101091009 = '永年', _101091011 = '鸡泽', _101091003 = '临漳', _101091014 = '魏县', }, ['秦皇岛'] = { _101091101 = '秦皇岛', _101091106 = '北戴河', _101091103 = '昌黎', _101091105 = '卢龙', _101091102 = '青龙', _101091104 = '抚宁', }, ['张家口'] = { _101090301 = '张家口', _101090306 = '尚义', _101090313 = '赤城', _101090310 = '万全', _101090308 = '阳原', _101090312 = '涿鹿', _101090304 = '康保', _101090309 = '怀安', _101090314 = '崇礼', _101090302 = '宣化', _101090311 = '怀来', _101090303 = '张北', _101090305 = '沽源', _101090307 = '蔚县', }, ['石家庄'] = { _101090107 = '高邑', _101090103 = '正定', _101090110 = '无极', _101090112 = '元氏', _101090111 = '平山', _101090108 = '深泽', _101090102 = '井陉', _101090106 = '灵寿', _101090109 = '赞皇', _101090113 = '赵县', _101090115 = '藁城', _101090114 = '辛集', _101090118 = '鹿泉', _101090117 = '新乐', _101090116 = '晋州', _101090101 = '石家庄', _101090104 = '栾城', _101090105 = '行唐', }, ['承德'] = { _101090407 = '隆化', _101090404 = '兴隆', _101090406 = '滦平', _101090403 = '承德县', _101090405 = '平泉', _101090409 = '宽城', _101090410 = '围场', _101090402 = '承德', _101090408 = '丰宁', }, ['衡水'] = { _101090805 = '饶阳', _101090809 = '阜城', _101090803 = '武邑', _101090804 = '武强', _101090806 = '安平', _101090811 = '深州', _101090808 = '景县', _101090802 = '枣强', _101090810 = '冀州', _101090801 = '衡水', _101090807 = '故城', }, }, ['贵州'] = { ['铜仁'] = { _101260605 = '思南', _101260602 = '江口', _101260603 = '玉屏', _101260604 = '万山', _101260609 = '沿河', _101260601 = '铜仁', _101260610 = '德江', _101260608 = '石阡', _101260611 = '松桃', _101260607 = '印江', }, ['遵义'] = { _101260214 = '汇川', _101260203 = '仁怀', _101260205 = '湄潭', _101260215 = '红花岗', _101260207 = '桐梓', _101260212 = '务川', _101260211 = '正安', _101260208 = '赤水', _101260209 = '习水', _101260210 = '道真', _101260213 = '余庆', _101260201 = '遵义', _101260202 = '遵义县', _101260204 = '绥阳', _101260206 = '凤冈', }, ['黔东南'] = { _101260515 = '锦屏', _101260505 = '黄平', _101260501 = '凯里', _101260514 = '天柱', _101260516 = '榕江', _101260503 = '施秉', _101260511 = '剑河', _101260508 = '丹寨', _101260504 = '镇远', _101260513 = '黎平', _101260512 = '雷山', _101260507 = '麻江', _101260502 = '岑巩', _101260517 = '从江', _101260509 = '三穗', _101260510 = '台江', }, ['贵阳'] = { _101260110 = '云岩', _101260103 = '花溪', _101260102 = '白云', _101260101 = '贵阳', _101260106 = '开阳', _101260111 = '南明', _101260104 = '乌当', _101260109 = '小河', _101260108 = '清镇', _101260107 = '修文', _101260105 = '息烽', }, ['六盘水'] = { _101260804 = '盘县', _101260802 = '六枝', _101260801 = '水城', }, ['黔西南'] = { _101260908 = '册亨', _101260901 = '兴义', _101260904 = '贞丰', _101260902 = '晴隆', _101260909 = '普安', _101260903 = '兴仁', _101260907 = '安龙', _101260905 = '望谟', }, ['毕节'] = { _101260703 = '金沙', _101260707 = '织金', _101260708 = '黔西', _101260706 = '纳雍', _101260701 = '毕节', _101260704 = '威宁', _101260702 = '赫章', _101260705 = '大方', }, ['黔南'] = { _101260404 = '长顺', _101260405 = '福泉', _101260402 = '贵定', _101260411 = '三都', _101260407 = '龙里', _101260410 = '独山', _101260403 = '瓮安', _101260406 = '惠水', _101260412 = '荔波', _101260409 = '平塘', _101260401 = '都匀', _101260408 = '罗甸', }, ['安顺'] = { _101260303 = '镇宁', _101260302 = '普定', _101260306 = '关岭', _101260301 = '安顺', _101260305 = '紫云', _101260304 = '平坝', }, }, ['福建'] = { ['钓鱼岛'] = { _101231001 = '钓鱼岛', }, ['南平'] = { _101230901 = '南平', _101230905 = '武夷山', _101230907 = '建阳', _101230910 = '建瓯', _101230904 = '邵武', _101230903 = '光泽', _101230909 = '政和', _101230908 = '松溪', _101230902 = '顺昌', _101230906 = '浦城', }, ['福州'] = { _101230111 = '福清', _101230110 = '长乐', _101230104 = '罗源', _101230101 = '福州', _101230102 = '闽清', _101230103 = '闽侯', _101230108 = '平潭', _101230105 = '连江', _101230107 = '永泰', }, ['三明'] = { _101230802 = '宁化', _101230807 = '明溪', _101230805 = '将乐', _101230804 = '泰宁', _101230801 = '三明', _101230808 = '沙县', _101230803 = '清流', _101230809 = '尤溪', _101230810 = '永安', _101230811 = '大田', _101230806 = '建宁', }, ['龙岩'] = { _101230701 = '龙岩', _101230706 = '永定', _101230705 = '上杭', _101230707 = '漳平', _101230704 = '武平', _101230702 = '长汀', _101230703 = '连城', }, ['泉州'] = { _101230504 = '永春', _101230509 = '晋江', _101230508 = '惠安', _101230510 = '石狮', _101230506 = '南安', _101230507 = '崇武', _101230502 = '安溪', _101230505 = '德化', _101230501 = '泉州', }, ['厦门'] = { _101230202 = '同安', _101230201 = '厦门', }, ['莆田'] = { _101230406 = '荔城', _101230402 = '仙游', _101230403 = '秀屿港', _101230407 = '城厢', _101230401 = '莆田', _101230405 = '秀屿', _101230404 = '涵江', }, ['漳州'] = { _101230608 = '东山', _101230604 = '平和', _101230605 = '龙海', _101230607 = '诏安', _101230610 = '华安', _101230609 = '云霄', _101230601 = '漳州', _101230602 = '长泰', _101230603 = '南靖', _101230606 = '漳浦', }, ['宁德'] = { _101230306 = '福安', _101230303 = '霞浦', _101230304 = '寿宁', _101230302 = '古田', _101230308 = '福鼎', _101230309 = '屏南', _101230301 = '宁德', _101230305 = '周宁', _101230307 = '柘荣', }, }, ['江苏'] = { ['镇江'] = { _101190301 = '镇江', _101190303 = '扬中', _101190304 = '句容', _101190305 = '丹徒', _101190302 = '丹阳', }, ['扬州'] = { _101190606 = '邗江', _101190603 = '仪征', _101190601 = '扬州', _101190605 = '江都', _101190602 = '宝应', _101190604 = '高邮', }, ['宿迁'] = { _101191303 = '泗阳', _101191302 = '沭阳', _101191305 = '宿豫', _101191301 = '宿迁', _101191304 = '泗洪', }, ['苏州'] = { _101190405 = '吴中', _101190407 = '吴江', _101190402 = '常熟', _101190408 = '太仓', _101190403 = '张家港', _101190401 = '苏州', _101190404 = '昆山', }, ['南京'] = { _101190105 = '六合', _101190106 = '江浦', _101190103 = '高淳', _101190102 = '溧水', _101190107 = '浦口', _101190104 = '江宁', _101190101 = '南京', }, ['无锡'] = { _101190202 = '江阴', _101190203 = '宜兴', _101190201 = '无锡', _101190204 = '锡山', }, ['泰州'] = { _101191201 = '泰州', _101191204 = '姜堰', _101191203 = '泰兴', _101191202 = '兴化', _101191205 = '靖江', }, ['盐城'] = { _101190707 = '东台', _101190702 = '响水', _101190706 = '建湖', _101190705 = '射阳', _101190703 = '滨海', _101190701 = '盐城', _101190708 = '大丰', _101190709 = '盐都', _101190704 = '阜宁', }, ['常州'] = { _101191102 = '溧阳', _101191104 = '武进', _101191103 = '金坛', _101191101 = '常州', }, ['徐州'] = { _101190803 = '丰县', _101190807 = '新沂', _101190802 = '铜山', _101190801 = '徐州', _101190806 = '睢宁', _101190804 = '沛县', _101190805 = '邳州', }, ['连云港'] = { _101191004 = '灌云', _101191003 = '赣榆', _101191002 = '东海', _101191001 = '连云港', _101191005 = '灌南', }, ['淮安'] = { _101190908 = '淮安区', _101190903 = '盱眙', _101190904 = '洪泽', _101190901 = '淮安', _101190906 = '淮阴区', _101190905 = '涟水', _101190902 = '金湖', }, ['南通'] = { _101190501 = '南通', _101190509 = '通州', _101190502 = '海安', _101190504 = '如东', _101190503 = '如皋', _101190508 = '海门', _101190507 = '启东', }, }, ['山东'] = { ['滨州'] = { _101121106 = '沾化', _101121101 = '滨州', _101121105 = '惠民', _101121103 = '无棣', _101121104 = '阳信', _101121107 = '邹平', _101121102 = '博兴', }, ['泰安'] = { _101120804 = '肥城', _101120801 = '泰安', _101120806 = '宁阳', _101120802 = '新泰', _101120805 = '东平', }, ['威海'] = { _101121306 = '石岛', _101121304 = '乳山', _101121302 = '文登', _101121301 = '威海', _101121303 = '荣成', _101121305 = '成山头', }, ['济南'] = { _101120106 = '济阳', _101120102 = '长清', _101120104 = '章丘', _101120103 = '商河', _101120105 = '平阴', _101120101 = '济南', }, ['东营'] = { _101121204 = '利津', _101121203 = '垦利', _101121202 = '河口', _101121205 = '广饶', _101121201 = '东营', }, ['淄博'] = { _101120307 = '桓台', _101120308 = '临淄', _101120306 = '沂源', _101120301 = '淄博', _101120304 = '高青', _101120303 = '博山', _101120305 = '周村', _101120302 = '淄川', }, ['潍坊'] = { _101120603 = '寿光', _101120607 = '安丘', _101120604 = '临朐', _101120609 = '诸城', _101120602 = '青州', _101120608 = '高密', _101120606 = '昌邑', _101120605 = '昌乐', _101120601 = '潍坊', }, ['青岛'] = { _101120201 = '青岛', _101120208 = '平度', _101120207 = '莱西', _101120204 = '即墨', _101120206 = '黄岛', _101120205 = '胶州', _101120202 = '崂山', }, ['莱芜'] = { _101121601 = '莱芜', }, ['日照'] = { _101121503 = '莒县', _101121501 = '日照', _101121502 = '五莲', }, ['菏泽'] = { _101121009 = '单县', _101121005 = '定陶', _101121007 = '曹县', _101121006 = '巨野', _101121002 = '鄄城', _101121003 = '郓城', _101121004 = '东明', _101121001 = '菏泽', _101121008 = '成武', }, ['烟台'] = { _101120502 = '莱州', _101120509 = '牟平', _101120503 = '长岛', _101120508 = '福山', _101120507 = '栖霞', _101120505 = '龙口', _101120511 = '海阳', _101120510 = '莱阳', _101120506 = '招远', _101120504 = '蓬莱', _101120501 = '烟台', }, ['聊城'] = { _101121701 = '聊城', _101121705 = '茌平', _101121703 = '阳谷', _101121707 = '临清', _101121702 = '冠县', _101121706 = '东阿', _101121709 = '莘县', _101121704 = '高唐', }, ['济宁'] = { _101120701 = '济宁', _101120708 = '泗水', _101120706 = '金乡', _101120711 = '邹城', _101120710 = '曲阜', _101120709 = '梁山', _101120704 = '鱼台', _101120703 = '微山', _101120705 = '兖州', _101120702 = '嘉祥', _101120707 = '汶上', }, ['德州'] = { _101120409 = '宁津', _101120406 = '乐陵', _101120408 = '平原', _101120407 = '庆云', _101120405 = '齐河', _101120403 = '临邑', _101120404 = '陵县', _101120410 = '夏津', _101120401 = '德州', _101120411 = '禹城', _101120402 = '武城', }, ['枣庄'] = { _101121402 = '薛城', _101121403 = '峄城', _101121405 = '滕州', _101121401 = '枣庄', _101121404 = '台儿庄', }, ['临沂'] = { _101120902 = '莒南', _101120908 = '平邑', _101120909 = '费县', _101120905 = '临沭', _101120910 = '沂水', _101120907 = '蒙阴', _101120901 = '临沂', _101120904 = '兰陵', _101120903 = '沂南', _101120906 = '郯城', }, }, ['辽宁'] = { ['营口'] = { _101070801 = '营口', _101070802 = '大石桥', _101070803 = '盖州', }, ['锦州'] = { _101070706 = '北镇', _101070701 = '锦州', _101070705 = '黑山', _101070704 = '义县', _101070702 = '凌海', }, ['葫芦岛'] = { _101071401 = '葫芦岛', _101071403 = '绥中', _101071404 = '兴城', _101071402 = '建昌', }, ['抚顺'] = { _101070402 = '新宾', _101070401 = '抚顺', _101070403 = '清原', _101070404 = '章党', }, ['辽阳'] = { _101071004 = '弓长岭', _101071002 = '辽阳县', _101071003 = '灯塔', _101071001 = '辽阳', }, ['鞍山'] = { _101070303 = '岫岩', _101070304 = '海城', _101070302 = '台安', _101070301 = '鞍山', }, ['朝阳'] = { _101071201 = '朝阳', _101071203 = '凌源', _101071205 = '北票', _101071204 = '喀左', _101071207 = '建平县', }, ['丹东'] = { _101070602 = '凤城', _101070601 = '丹东', _101070603 = '宽甸', _101070604 = '东港', }, ['铁岭'] = { _101071102 = '开原', _101071105 = '调兵山', _101071101 = '铁岭', _101071104 = '西丰', _101071103 = '昌图', }, ['盘锦'] = { _101071303 = '盘山', _101071302 = '大洼', _101071301 = '盘锦', }, ['大连'] = { _101070203 = '金州', _101070204 = '普兰店', _101070206 = '长海', _101070201 = '大连', _101070205 = '旅顺', _101070207 = '庄河', _101070202 = '瓦房店', }, ['沈阳'] = { _101070103 = '辽中', _101070106 = '新民', _101070101 = '沈阳', _101070105 = '法库', _101070104 = '康平', }, ['本溪'] = { _101070504 = '桓仁', _101070502 = '本溪县', _101070501 = '本溪', }, ['阜新'] = { _101070901 = '阜新', _101070902 = '彰武', }, }, ['黑龙江'] = { ['佳木斯'] = { _101050405 = '桦南', _101050401 = '佳木斯', _101050407 = '富锦', _101050406 = '同江', _101050402 = '汤原', _101050404 = '桦川', _101050403 = '抚远', }, ['伊春'] = { _101050805 = '嘉荫', _101050801 = '伊春', _101050804 = '铁力', _101050802 = '乌伊岭', _101050803 = '五营', }, ['齐齐哈尔'] = { _101050209 = '克东', _101050206 = '依安', _101050201 = '齐齐哈尔', _101050203 = '龙江', _101050202 = '讷河', _101050208 = '克山', _101050204 = '甘南', _101050205 = '富裕', _101050207 = '拜泉', _101050210 = '泰来', }, ['双鸭山'] = { _101051302 = '集贤', _101051303 = '宝清', _101051301 = '双鸭山', _101051304 = '饶河', _101051305 = '友谊', }, ['鹤岗'] = { _101051202 = '绥滨', _101051203 = '萝北', _101051201 = '鹤岗', }, ['哈尔滨'] = { _101050107 = '巴彦', _101050106 = '依兰', _101050108 = '通河', _101050105 = '宾县', _101050110 = '延寿', _101050101 = '哈尔滨', _101050113 = '木兰', _101050111 = '尚志', _101050112 = '五常', _101050109 = '方正', _101050102 = '双城', _101050104 = '阿城', _101050103 = '呼兰', }, ['鸡西'] = { _101051104 = '鸡东', _101051102 = '虎林', _101051103 = '密山', _101051101 = '鸡西', }, ['大庆'] = { _101050903 = '肇州', _101050901 = '大庆', _101050905 = '杜尔伯特', _101050904 = '肇源', _101050902 = '林甸', }, ['大兴安岭'] = { _101050706 = '新林', _101050702 = '塔河', _101050701 = '大兴安岭', _101050704 = '呼玛', _101050708 = '加格达奇', _101050705 = '呼中', _101050703 = '漠河', }, ['七台河'] = { _101051002 = '七台河', _101051003 = '勃利', }, ['黑河'] = { _101050604 = '逊克', _101050605 = '五大连池', _101050603 = '孙吴', _101050606 = '北安', _101050602 = '嫩江', _101050601 = '黑河', }, ['牡丹江'] = { _101050302 = '海林', _101050307 = '东宁', _101050304 = '林口', _101050305 = '绥芬河', _101050306 = '宁安', _101050301 = '牡丹江', _101050303 = '穆棱', }, ['绥化'] = { _101050502 = '肇东', _101050510 = '绥棱', _101050504 = '海伦', _101050503 = '安达', _101050509 = '庆安', _101050507 = '兰西', _101050506 = '望奎', _101050508 = '青冈', _101050505 = '明水', _101050501 = '绥化', }, }, } return China ================================================ FILE: lua/weather/info.json ================================================ { "id": "pub.hanks.weather", "name": "天气", "icon": "http://ww1.sinaimg.cn/large/8c9b876fly1fh10p6tgqtj2046046jr5.jpg", "main": "main.lua", "versionName": "1.0.2", "versionCode": 4, "private": false, "desc": "简洁的看天气的插件" } ================================================ FILE: lua/weather/list_city.lua ================================================ require("import") import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaAdapter" local China = require("weather.city") local filehelper = require("filehelper") local layout = { LinearLayout, layout_width = "match", layout_height = "match", orientation = 'vertical', statusBarColor = "#8CCACE", { LinearLayout, layout_height = "56dp", layout_width = "match", orientation = 'vertical', gravity = "center", background = "#8CCACE", { TextView, paddingLeft = "16dp", textSize = "18sp", text = "选择城市", textColor = "#eeffffff", }, }, { ListView, id = 'listview2', layout_width = "match", layout_height = "match", visibility = "gone", }, { ListView, id = 'listview', layout_width = "match", layout_height = "match", }, } local showCityData = false local item_view = { TextView, id = "tv_name", gravity = "center_vertical", layout_width = "fill", layout_height = "56dp", paddingLeft = "16dp", textColor = "#666666", } local filePath = luajava.luaextdir .. '/weather/id' local data = {} local cityData = {} local function clearTable(t) for k in pairs(t) do t[k] = nil end end function onCreate(savedInstanceState) activity.setStatusBarColor(0xFF8CCACE) activity.setContentView(loadlayout(layout)) for k, _ in pairs(China) do data[#data + 1] = k end local adapter2 = LuaAdapter(luajava.createProxy("androlua.LuaAdapter$AdapterCreator", { getCount = function() return #cityData end, getView = function(position, convertView, parent) position = position + 1 -- lua 索引从 1开始 if convertView == nil then local views = {} -- store views convertView = loadlayout(item_view, views, ListView) convertView.getLayoutParams().width = parent.getWidth() convertView.setTag(views) end local views = convertView.getTag() local item = cityData[position] views.tv_name.setText(item[2]) return convertView end })) listview2.setAdapter(adapter2) listview2.setOnItemClickListener(luajava.createProxy("android.widget.AdapterView$OnItemClickListener", { onItemClick = function(adapter, view, position, id) activity.toast('请稍候...') position = position + 1 local id = cityData[position][1]:sub(2) filehelper.writefile(filePath, id) view.postDelayed(luajava.createProxy('java.lang.Runnable', { run = function() activity.finish() end }), 500) end, })) local adapter = LuaAdapter(luajava.createProxy("androlua.LuaAdapter$AdapterCreator", { getCount = function() return #data end, getView = function(position, convertView, parent) position = position + 1 -- lua 索引从 1开始 if convertView == nil then local views = {} -- store views convertView = loadlayout(item_view, views, ListView) if parent then convertView.getLayoutParams().width = parent.getWidth() end convertView.setTag(views) end local views = convertView.getTag() local item = data[position] print(position, item) print(views.tv_name) views.tv_name.setText(item) return convertView end })) listview.setAdapter(adapter) listview.setOnItemClickListener(luajava.createProxy("android.widget.AdapterView$OnItemClickListener", { onItemClick = function(adapter, view, position, id) position = position + 1 local sTable = China[data[position]] clearTable(cityData) for k, v in pairs(sTable) do for k2, v2 in pairs(v) do local name = k .. ' · ' .. v2 if k == v2 then name = k end cityData[#cityData + 1] = { k2, name } end end listview2.setVisibility(0) listview.setVisibility(8) adapter2.notifyDataSetChanged() showCityData = true end, })) end function onBackPressed() if showCityData then listview2.setVisibility(8) listview.setVisibility(0) showCityData = false return true end return false end ================================================ FILE: lua/weather/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaImageLoader" import "androlua.LuaDrawable" import "androlua.LuaView" import "android.graphics.Paint" import "androlua.LuaUtil" local uihelper = require("uihelper") local JSON = require("cjson") local filehelper = require("filehelper") local weather = require("weather.weather") local log = require("log") local item_hour = { LinearLayout, layout_weight = 1, orientation = "vertical", gravity = "center", paddingTop = "16dp", paddingBottom = "16dp", { TextView, textSize = "13sp", textColor = "#666666", text = "00:00", }, { ImageView, layout_width = "match", layout_height = "24dp", layout_marginBottom = "4dp", layout_marginTop = "12dp", }, { TextView, textSize = "12sp", textColor = "#666666", text = "0°", }, } local item_week = { LinearLayout, layout_weight = 1, orientation = "vertical", gravity = "center", paddingTop = "16dp", paddingBottom = "16dp", { TextView, textSize = "13sp", textColor = "#666666", text = "今天", }, { ImageView, layout_width = "match", layout_height = "24dp", layout_marginBottom = "4dp", layout_marginTop = "16dp", }, { TextView, textSize = "10sp", textColor = "#666666", text = "多云", }, } -- create view table local layout = { ScrollView, { LinearLayout, orientation = "vertical", { LinearLayout, background = "#8CCACE", layout_width = "fill", layout_height = "wrap", orientation = "vertical", gravity = "center_horizontal", { RelativeLayout, layout_width = "fill", layout_height = "56dp", layout_marginTop = "25dp", { TextView, id = "tv_city", layout_height = "match", gravity = "center", layout_centerVertical = true, textSize = "16sp", textColor = "#ffffff", paddingLeft = "16dp", paddingRight = "16dp", }, { TextView, layout_marginRight = "16dp", id = "tv_update", layout_alignParentRight = true, layout_centerVertical = true, textSize = "12sp", textColor = "#aaffffff", }, }, { LinearLayout, layout_width = "fill", orientation = "vertical", gravity = "center_horizontal", paddingBottom = "72dp", paddingTop = "24dp", { TextView, id = "tv_weather", textSize = "18sp", textColor = "#eeffffff", }, { TextView, id = "tv_temp", layout_marginTop = "8dp", layout_marginBottom = "12dp", textSize = "82sp", textColor = "#f1ffffff", }, { TextView, id = "tv_wind", textSize = "13sp", textColor = "#aaffffff", }, }, }, { LinearLayout, id = "layout_week", layout_width = "fill", orientation = "horizontal", item_week, item_week, item_week, item_week, item_week, item_week, }, { LuaView, id = "line_view", layout_width = "fill", layout_height = "80dp", }, { LinearLayout, id = "layout_24h", layout_width = "fill", orientation = "horizontal", item_hour, item_hour, item_hour, item_hour, item_hour, item_hour, }, }, } -- bg http://i.tq121.com.cn/i/wap2016/news/d11.jpg local weekTemp = {} local function safeRun(f) pcall(function() f() end) end local function fillBaseInfo(body) local json = JSON.decode(string.match(body, '{.*}')) tv_city.setText(json.cityname) tv_temp.setText(json.temp .. '°') tv_update.setText(json.time .. ' 更新') tv_weather.setText(json.weather) tv_wind.setText(string.format('空气指数 %s • %s • 湿度 %s', json.aqi_pm25, json.WD .. ' ' .. json.WS, json.SD)) end local function fillWeekInfo(body) local json = JSON.decode(string.match(body, '{.*}')) for i = 1, #json.f do local child = layout_week.getChildAt(i - 1) child.getChildAt(0).setText(json.f[i].fj) local xmlPath = string.format('%s/weather/img/line_%s.png', luajava.luaextdir, json.f[i].fa) child.getChildAt(1).setImageDrawable(LuaDrawable.create(xmlPath)) child.getChildAt(2).setText(weather['_' .. json.f[i].fa]) weekTemp[i] = { json.f[i].fc, json.f[i].fd } end line_view.invalidate() end local function fill24HInfo(body) print(body) local json = JSON.decode(string.match(body, 'fc1h_24%s+=(.*);')) local j = 0 for i = 1, #json.jh, 3 do local child = layout_24h.getChildAt(j) if child then child.getChildAt(0).setText(json.jh[i].jf:sub(9, 10) .. ':00') local xmlPath = string.format('%s/weather/img/line_%s.png', luajava.luaextdir, json.jh[i].ja) child.getChildAt(1).setImageDrawable(LuaDrawable.create(xmlPath)) child.getChildAt(2).setText(json.jh[i].jb .. '°') end j = j + 1 end end local function getData(url, successFunc) print(url) local options = { url = url, headers = { "Referer:http://m.weather.com.cn" } } LuaHttp.request(options, function(error, code, body) if error or code ~= 200 then print('fetch data error') return end uihelper.runOnUiThread(activity, function() successFunc(body) end) end) end local filePath = luajava.luaextdir .. '/weather/id' local function fetchData(id) if id == nil then return end getData(string.format('http://d1.weather.com.cn/sk_2d/%s.html', id), fillBaseInfo) getData(string.format('http://d1.weather.com.cn/weixinfc/%s.html', id), fillWeekInfo) getData(string.format('http://d1.weather.com.cn/wap_40d/%s.html', id), fill24HInfo) end function onCreate(savedInstanceState) activity.setStatusBarColor(0x00000000) activity.setContentView(loadlayout(layout)) local paint = Paint(1) paint.setColor(0xFF666666) paint.setTextSize(uihelper.dp2px(10)) paint.setStrokeWidth(uihelper.dp2px(1.5)) local linePaint1 = Paint(paint) linePaint1.setColor(0xFFFFD139) local linePaint2 = Paint(paint) linePaint2.setColor(0xFF7FDCEF) local radius = uihelper.dp2px(2.5) local texWidth = uihelper.dp2px(6) line_view.setCreator(luajava.createProxy('androlua.LuaView$Creator', { onDraw = function(canvas) local count = #weekTemp local max = -999 local min = 999 for i = 1, count do if tonumber(weekTemp[i][1]) > max then max = tonumber(weekTemp[i][1]) end if tonumber(weekTemp[i][2]) < min then min = tonumber(weekTemp[i][2]) end end local dx = line_view.getWidth() / count local startY = uihelper.dp2px(18) local dy = (line_view.getHeight() - line_view.getPaddingTop() - line_view.getPaddingBottom() - startY - startY) / (max - min) local lastPoint1 = {} local lastPoint2 = {} for i = 1, count do local maxT = weekTemp[i][1] local minT = weekTemp[i][2] local x = dx * (i - 0.5) local y1 = startY + (max - maxT) * dy local y2 = startY + (max - minT) * dy canvas.drawText(maxT .. '°', x - texWidth, y1 - texWidth, paint) canvas.drawText(minT .. '°', x - texWidth, y2 + texWidth + texWidth, paint) canvas.drawCircle(x, y1, radius, linePaint1) canvas.drawCircle(x, y2, radius, linePaint2) if lastPoint1[1] and lastPoint1[2] then canvas.drawLine(lastPoint1[1], lastPoint1[2], x, y1, linePaint1) end if lastPoint2[1] and lastPoint2[2] then canvas.drawLine(lastPoint2[1], lastPoint2[2], x, y2, linePaint2) end lastPoint1[1] = x lastPoint1[2] = y1 lastPoint2[1] = x lastPoint2[2] = y2 end end, })) tv_city.onClick = function(v) local intent = Intent(activity, LuaActivity) intent.putExtra("luaPath", 'weather/list_city.lua') activity.startActivity(intent) end end function onDestroy() LuaHttp.cancelAll() end local function findCityCode(province, city) local China = require("weather.city") for k, v in pairs(China) do if province == k then for k2, v2 in pairs(v) do if k2 == city then print(city) log.print_r(v2) for k3,v3 in pairs(v2) do if(v3 == city) then return k3:sub(2) end end end end end end return '101010100' end local function locateMe() local id = '101010100' local options = { url = 'http://ip.taobao.com/service/getIpInfo2.php', method = "POST", formData = { "ip:myip" } } LuaHttp.request(options, function(error, code, body) print(body) if error or code ~= 200 then print('locate failure') return end local json = JSON.decode(body) local province = json.data.region local city = json.data.city local county = json.data.county local p = string.match(province, '(.*)省') if p then province = p else p = string.match(province, '(.*)市') if p then province = p end end local c = string.match(province, '(.*)市') if c then city = c end local id = findCityCode(province, city) filehelper.writefile(filePath, id) fetchData(id) end) end function onResume() safeRun(function() local id = filehelper.readfile(filePath) if id == nil then locateMe() else fetchData(id) end end) end ================================================ FILE: lua/weather/weather.lua ================================================ return { _00 = "晴", _01 = "多云", _02 = "阴", _03 = "阵雨", _04 = "雷阵雨", _05 = "雷阵雨伴有冰雹", _06 = "雨夹雪", _07 = "小雨", _08 = "中雨", _09 = "大雨", _10 = "暴雨", _11 = "大暴雨", _12 = "特大暴雨", _13 = "阵雪", _14 = "小雪", _15 = "中雪", _16 = "大雪", _17 = "暴雪", _18 = "雾", _19 = "冻雨", _20 = "沙尘暴", _21 = "小到中雨", _22 = "中到大雨", _23 = "大到暴雨", _24 = "暴雨到大暴雨", _25 = "大暴雨到特大暴雨", _26 = "小到中雪", _27 = "中到大雪", _28 = "大到暴雪", _29 = "浮尘", _30 = "扬沙", _31 = "强沙尘暴", _32 = "浓雾", _49 = "强浓雾", _53 = "霾", _54 = "中度霾", _55 = "重度霾", _56 = "严重霾", _57 = "大雾", _58 = "特强浓雾", _99 = "无", _301 = "雨", _302 = "雪" } ================================================ FILE: lua/weibo-hot/info.json ================================================ { "id": "pub.hanks.weibo-hot", "name": "微博", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b08590d2920037?w=150&h=150&f=png&s=3556", "main": "main.lua", "versionName": "1.0.3", "versionCode": 4, "desc": "热门微博" } ================================================ FILE: lua/weibo-hot/item_msg.lua ================================================ return { LinearLayout, layout_width = "fill", paddingTop = "16dp", orientation = "vertical", background = "@drawable/layout_selector_tran", -- head { FrameLayout, layout_width = "match", layout_height = "36dp", paddingLeft = "16dp", paddingRight = "16dp", { ImageView, id = "iv_image", layout_width = "36dp", layout_height = "36dp", scaleType = "centerCrop" }, { TextView, id = "tv_username", layout_marginLeft = "44dp", layout_width = "fill", paddingRight = "16dp", maxLines = "1", ellipsize = "end", textSize = "14sp", textColor = "#e86b0f", }, { TextView, id = "tv_date", layout_gravity = "bottom", layout_marginLeft = "44dp", layout_width = "fill", maxLines = "1", textSize = "11sp", textColor = "#999999", }, }, -- content { TextView, id = "tv_content", layout_width = "fill", layout_marginLeft = "16dp", layout_marginRight = "16dp", layout_marginTop = "12dp", lineSpacingMultiplier = '1.3', textSize = "14sp", textColor = "#222222", }, -- pictures { LuaNineGridView, id = "iv_nine_grid", layout_width = "match", layout_height = "200dp", gap = "4dp", maxSize = 9, visibility = "gone", layout_marginTop = "12dp", layout_marginLeft = "16dp", layout_marginRight = "16dp", }, -- video { FrameLayout, id = "layout_video", layout_width = "match", layout_height = "200dp", layout_marginTop = "12dp", layout_marginLeft = "16dp", layout_marginRight = "16dp", visibility = 8, { ImageView, id = "iv_video", layout_width = "match", layout_height = "200dp", scaleType = "centerCrop" }, { View, layout_height = "match", layout_width = "match", background = "#66000000", }, { ImageView, layout_gravity = "center", layout_width = "40dp", layout_height = "40dp", src = "#weibo-hot/ic_video_play.png", }, }, -- foot { LinearLayout, layout_width = "match", layout_height = "56dp", paddingLeft = "16dp", paddingRight = "16dp", gravity = "center_vertical", orientation = "horizontal", { ImageView, layout_width = "20dp", layout_height = "20dp", src = "#weibo-hot/ic_retweet.png", }, { TextView, id = "tv_retweet", layout_width = "70dp", paddingLeft = "4dp", textSize = "12sp", textColor = "#A6A6A6", }, { ImageView, layout_width = "20dp", layout_height = "20dp", src = "#weibo-hot/ic_comment.png" }, { TextView, id = "tv_comment", paddingLeft = "4dp", layout_width = "70dp", textSize = "12sp", textColor = "#A6A6A6", }, { ImageView, layout_width = "20dp", layout_height = "20dp", src = "#weibo-hot/ic_unlike.png" }, { TextView, id = "tv_like", paddingLeft = "4dp", layout_width = "70dp", textSize = "12sp", textColor = "#A6A6A6", }, }, { View, layout_height = "8dp", layout_width = "fill", background = "#e1e1e1", } } ================================================ FILE: lua/weibo-hot/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/29 -- A zhihu daliy app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "android.support.v4.view.ViewPager" import "android.support.design.widget.TabLayout" import "androlua.adapter.LuaFragmentPageAdapter" import "androlua.LuaHttp" import "androlua.LuaImageLoader" import "androlua.LuaFragment" import "androlua.LuaHttp" import "android.support.v7.widget.RecyclerView" import "android.support.v4.widget.SwipeRefreshLayout" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.LinearLayoutManager" import "android.view.View" import "android.support.v4.widget.Space" import "androlua.widget.ninegride.LuaNineGridView" import "androlua.widget.ninegride.LuaNineGridViewAdapter" import "androlua.widget.picture.PicturePreviewActivity" import "androlua.widget.webview.WebViewActivity" import "android.graphics.drawable.Drawable" import "java.net.URL" import "android.support.v7.widget.Toolbar" import "android.net.Uri" import "android.text.Html" import "android.text.method.LinkMovementMethod" import "androlua.LuaDrawable" import "java.lang.Thread" local JSON = require "cjson" local uihelper = require "uihelper" -- create view table -- create view table local layout = { LinearLayout, layout_width = "match", layout_height = "match", orientation = "vertical", fitsSystemWindows = true, { Toolbar, background = '#ffffff', id = 'toolbar', layout_width = "match", layout_height = "56dp", elevation = "2dp", }, { SwipeRefreshLayout, id = "refreshLayout", layout_width = "match", { RecyclerView, id = "recyclerView", layout_width = "fill", layout_height = "fill", }, }, } local item_view = require "weibo-hot/item_msg" local adapter local data = {} local page = 1 local function launchDetail(msg) if msg and msg.mblog.id then local url = 'https://m.weibo.cn/status/' .. msg.mblog.id WebViewActivity.start(activity, url, 0xFFe86b0f) return end activity.toast('没有 url 可以打开') end local function launchPicturePreview(msg, index) local urls = {} for i = 1, #msg.mblog.pics do urls[i] = msg.mblog.pics[i].large.url end local data = { uris = urls, currentIndex = index } PicturePreviewActivity.start(activity, JSON.encode(data)) end local function fetchData() local url = "https://m.weibo.cn/api/container/getIndex?containerid=102803_ctg1_9999_-_ctg1_9999&count=10&luicode=10000011&lfid=102803_ctg1_8999_-_ctg1_8999_home&page=" .. page if page < 2 then url = "https://m.weibo.cn/api/container/getIndex?containerid=102803_ctg1_9999_-_ctg1_9999&count=10&luicode=10000011&lfid=102803_ctg1_8999_-_ctg1_8999_home" end local options = { url = url } LuaHttp.request(options, function(error, code, body) local cards = JSON.decode(body).data.cards uihelper.runOnUiThread(activity, function() if page == 0 then for k, _ in pairs(data) do data[k] = nil end end local s = #data for i = 1, #cards do local item = cards[i] if item.card_group then for j = 1, #item.card_group do data[#data + 1] = item.card_group[j] end else data[#data + 1] = item end end page = page + 1 adapter.notifyItemRangeChanged(s, #data) refreshLayout.setRefreshing(false) end) end) end local imgGetter = luajava.createProxy('android.text.Html$ImageGetter', { getDrawable = function(imgUrl) return LuaDrawable.create('weibo-hot/weibo_logo.png') end -- getDrawable = function(imgUrl) -- if not imgUrl:find('^http') then imgUrl = 'https://' .. imgUrl end -- Thread(luajava.createProxy('java.lang.Runnable',{ -- run = function() -- local is = URL(imgUrl).getContent() -- local drawable = Drawable.createFromStream(is,"src") -- drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()) -- is.close() -- end -- }).start() -- local is = URL(imgUrl).getContent() -- local drawable = Drawable.createFromStream(is,"src") -- drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()) -- is.close() -- return drawable -- end }) function onCreate(savedInstanceState) activity.setLightStatusBar() activity.setContentView(loadlayout(layout)) activity.setSupportActionBar(toolbar) activity.setTitle('热门微博') toolbar.setNavigationIcon(LuaDrawable.create('weibo-hot/weibo_logo.png')) adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() return #data end, getItemViewType = function(position) return 0 end, onCreateViewHolder = function(parent, viewType) local views = {} local holder = LuaRecyclerHolder(loadlayout(item_view, views, RecyclerView)) holder.itemView.getLayoutParams().width = parent.getWidth() holder.itemView.setTag(views) holder.itemView.onClick = function(view) local position = holder.getAdapterPosition() + 1 launchDetail(data[position]) end return holder end, onBindViewHolder = function(holder, position) position = position + 1 local msg = data[position] local views = holder.itemView.getTag() views.tv_username.setText(msg.mblog.user.screen_name or 'xxxxx') views.tv_content.setText(Html.fromHtml(msg.mblog.text, imgGetter, nil)) -- views.tv_content.setMovementMethod(LinkMovementMethod.getInstance()) views.tv_date.setText(msg.mblog.created_at or '刚刚') views.tv_retweet.setText(string.format('%d', msg.mblog.reposts_count)) views.tv_comment.setText(string.format('%d', msg.mblog.comments_count)) views.tv_like.setText(string.format('%d', msg.mblog.attitudes_count)) LuaImageLoader.loadWithRadius(views.iv_image, 40, msg.mblog.user.profile_image_url) if msg.mblog.pics and #msg.mblog.pics > 0 then local pictures = msg.mblog.pics local urls = {} local len = #pictures for i = 1, len do if len == 1 then urls[i] = pictures[i].large.url local w = pictures[i].large.geo.width or 200 local h = pictures[i].large.geo.height or 200 views.iv_nine_grid.setSingleImgSize(tonumber(w), tonumber(h)) else urls[i] = pictures[i].url end end views.iv_nine_grid.setVisibility(0) views.iv_nine_grid.setAdapter(LuaNineGridViewAdapter(luajava.createProxy('androlua.widget.ninegride.LuaNineGridViewAdapter$AdapterCreator', { onDisplayImage = function(context, imageView, url) LuaImageLoader.load(imageView, url) end, onItemImageClick = function(context, imageView, index, list) launchPicturePreview(msg, index) end }))) views.iv_nine_grid.setImagesData(urls) else views.iv_nine_grid.setVisibility(8) end if msg.mblog.page_info and msg.mblog.page_info.type == 'video' then views.layout_video.setVisibility(0) LuaImageLoader.load(views.iv_video, msg.mblog.page_info.page_pic.url) else views.layout_video.setVisibility(8) end if position == #data then fetchData() end end, })) recyclerView.setLayoutManager(LinearLayoutManager(activity)) recyclerView.setAdapter(adapter) refreshLayout.setOnRefreshListener(luajava.createProxy('android.support.v4.widget.SwipeRefreshLayout$OnRefreshListener', { onRefresh = function() page = 1 fetchData() end })) refreshLayout.setRefreshing(true) fetchData() end function onCreateOptionsMenu(menu) menu.add("网页版") return true end function onOptionsItemSelected(item) local title = item.getTitle() if title == "网页版" then activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse('http://m.weibo.cn'))) end end ================================================ FILE: lua/wikibaidu/info.json ================================================ { "id": "pub.hanks.wikibaidu", "name": "百度百科", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b0859e82e896b5?w=150&h=150&f=png&s=3461", "main": "main.lua", "versionName": "1.0.1", "versionCode": 2, "desc": "中文知识性百科全书(需最新客户端)" } ================================================ FILE: lua/wikibaidu/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- qiqu -- require "import" import "android.widget.*" import "android.content.*" import "androlua.LuaWebView" import "pub.hydrogen.android.BuildConfig" -- create view table local layout = { LinearLayout, orientation = "vertical", layout_width = "fill", layout_height = "fill", statusBarColor = "#33000000", { LuaWebView, id = "webview", layout_width = "fill", layout_height = "fill", } } function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) webview.loadUrl('https://wapbaike.baidu.com/') if BuildConfig.VERSION_CODE < 15 then activity.toast("氢应用版本太低,可能无法正常使用,请升级最新版本!") end end function onBackPressed() if webview.canGoBack() then webview.goBack() return true end return false end function onDestroy() pcall(function( ) webview.release() end) end ================================================ FILE: lua/yilin/activity_detail.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaWebView" import "androlua.LuaHttp" local uihelper = require "uihelper" -- create view table local layout = { LinearLayout, layout_width = "fill", layout_height = "fill", orientation = "vertical", statusBarColor = "#66ae28", { LinearLayout, orientation = "horizontal", layout_width = "fill", layout_height = "56dp", background = "#66ae28", gravity = "center_vertical", { ImageView, id = "back", layout_width = "40dp", layout_height = "40dp", layout_marginLeft = "8dp", scaleType = "centerInside", src = "@drawable/ic_menu_back", }, { TextView, layout_height = "56dp", layout_width = "fill", paddingRight = "16dp", singleLine = true, textIsSelectable = true, ellipsize = "end", id = "tv_title", gravity = "center_vertical", paddingLeft = "8dp", textColor = "#ffffff", textSize = "14sp", }, }, { FrameLayout, layout_width = "fill", layout_height = "fill", { LuaWebView, id = "webview", layout_width = "fill", layout_height = "fill", }, { ProgressBar, layout_gravity = "center", id = "progressBar", layout_width = "40dp", layout_height = "40dp", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", } } } local css = [[ article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}html{font-family:sans-serif;-webkit-text-size-adjust:100%}body{font-family:'Helvetica Neue',Helvetica,Arial,Sans-serif;background:#fff;padding-top:0;margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{margin:.67em 0}h1,h2,h3,h4,h5,h6{font-size:16px}abbr[title]{border-bottom:1px dotted}hr{box-sizing:content-box;height:0}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap}q{quotes:\201C\201D\2018\2019}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0;vertical-align:middle;color:transparent;font-size:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}table{border-collapse:collapse;border-spacing:0;overflow:hidden}a{text-decoration:none}blockquote{border-left:3px solid #d0e5f2;font-style:normal;display:block;vertical-align:baseline;font-size:100%;margin:.5em 0;padding:0 0 0 1em}ul,ol{padding-left:20px}.content{color:#444;line-height:1.6em;font-size:16px;margin:16px}.content img{max-width:100%;display:block;margin:30px auto}.content img+img{margin-top:15px}.content img[src*="zhihu.com/equation"]{display:inline-block;margin:0 3px}.content a{color:#259}.content a:hover{text-decoration:underline} ]] local htmlTemplate = [[
    %s
    ]] local function getData(url) LuaHttp.request({ url = url }, function(error, code, body) local content = string.match(body, '
    (.-
    )%s+
    ') local data = string.format(htmlTemplate, css, content) uihelper.runOnUiThread(activity, function() print(data) webview.loadData(data, "text/html; charset=UTF-8", nil) end) end) end function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) back.onClick = function() activity.finish() end local url = activity.getIntent().getStringExtra('url') tv_title.setText(url) webview.setVisibility(0) progressBar.setVisibility(8) getData(url) end function onDestroy() if webview then webview.getParent().removeView(webview) webview.destroy() webview = nil end end ================================================ FILE: lua/yilin/fragment_yilin.lua ================================================ -- -- Created by IntelliJ IDEA. -- User: hanks -- Date: 2017/5/13 -- Time: 00:01 -- To change this template use File | Settings | File Templates. -- require "import" import "android.widget.*" import "android.content.*" import "androlua.LuaAdapter" import "androlua.LuaImageLoader" import "androlua.LuaFragment" import "androlua.LuaHttp" import "androlua.widget.webview.WebViewActivity" import "android.support.v4.widget.SwipeRefreshLayout" import "java.lang.String" local uihelper = require "uihelper" local JSON = require "cjson" local log = require "log" local pageList = {} local function trim(s) return s:gsub("^%s+", ""):gsub("%s+$", "") end local function getData(params, data, adapter, fragment, swipe_layout, reload) local function getPageList() LuaHttp.request({ url = 'http://www.92yilin.com/' }, function(error, code, body) for booklist in string.gmatch(body, "(.-)
    ") do local arr = {} for td in string.gmatch(booklist, '') do if not td:find('colspan') then local url = string.match(td, ".-") do if span:find('maglisttitle') then local url, title = string.match(span, '') data[#data + 1] = { title = title, url = pageUrl:gsub('index.html', url) } else local h2 = string.match(span, '(.-)') h2 = trim(h2) data[#data + 1] = { h2 = h2 } end end params.page = params.page + 1 adapter.notifyDataSetChanged() swipe_layout.setRefreshing(false) swipe_layout.setEnable(false) end) end) end local function launchDetail(fragment, item) local activity = fragment.getActivity() if item == nil or item.url == nil then return end local activity = fragment.getActivity() local intent = Intent(activity, LuaActivity) intent.putExtra("luaPath", 'yilin/activity_detail.lua') intent.putExtra("url", '' .. item.url) activity.startActivity(intent) end local function newInstance(id) -- create view table local layout = { SwipeRefreshLayout, layout_width = "fill", layout_height = "fill", id = "swipe_layout", { ListView, id = "listview", layout_width = "fill", layout_height = "fill", } } local item_view = { LinearLayout, layout_width = "fill", layout_height = "wrap", orientation = "vertical", { TextView, id = "tv_h1", layout_height = "40dp", layout_width = "fill", paddingLeft = "16dp", gravity = "center_vertical", textSize = "14sp", visibility = "gone", background = "#eef1f6", textColor = "#1f2d3d", }, { TextView, id = "tv_title", layout_height = "56dp", layout_width = "fill", paddingLeft = "16dp", paddingRight = "16dp", singleLine = true, ellipsize = "end", gravity = "center_vertical", textSize = "14sp", textColor = "#1f2d3d", } } local hadLoadData local isVisible local data = {} local params = { id = id, page = 1 } local ids = {} local adapter local fragment = LuaFragment.newInstance() local function lazyLoad() if not isVisible then return end if hadLoadData then return end if adapter == nil then return end hadLoadData = true getData(params, data, adapter, fragment, ids.swipe_layout) end fragment.setCreator(luajava.createProxy('androlua.LuaFragment$FragmentCreator', { onCreateView = function(inflater, container, savedInstanceState) return loadlayout(layout, ids) end, onViewCreated = function(view, savedInstanceState) adapter = LuaAdapter(luajava.createProxy("androlua.LuaAdapter$AdapterCreator", { getCount = function() return #data end, getView = function(position, convertView, parent) position = position + 1 -- lua 索引从 1开始 if convertView == nil then local views = {} -- store views convertView = loadlayout(item_view, views, ListView) convertView.getLayoutParams().width = parent.getWidth() convertView.setTag(views) end local views = convertView.getTag() local item = data[position] if item then if item.h2 then views.tv_h1.setVisibility(0) views.tv_title.setVisibility(8) views.tv_h1.setText(item.h2 or '意林') else views.tv_h1.setVisibility(8) views.tv_title.setVisibility(0) views.tv_title.setText(' · ' .. item.title) end end if position == #data then getData(params, data, adapter, fragment, ids.swipe_layout) end return convertView end })) ids.listview.setAdapter(adapter) ids.listview.setOnItemClickListener(luajava.createProxy("android.widget.AdapterView$OnItemClickListener", { onItemClick = function(adapter, view, position, id) launchDetail(fragment, data[position + 1]) end, })) ids.swipe_layout.setRefreshing(true) ids.swipe_layout.setOnRefreshListener(luajava.createProxy('android.support.v4.widget.SwipeRefreshLayout$OnRefreshListener', { onRefresh = function() getData(params, data, adapter, fragment, ids.swipe_layout, true) end })) lazyLoad() end, onUserVisible = function(visible) isVisible = visible lazyLoad() end, })) return fragment end return { newInstance = newInstance } ================================================ FILE: lua/yilin/info.json ================================================ { "id": "pub.hanks.yilin", "name": "意林", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b08862dca86462?w=150&h=150&f=png&s=3415", "main": "main.lua", "versionName": "1.1", "versionCode": 3, "desc": "《意林》杂志,鸡汤慎重服用" } ================================================ FILE: lua/yilin/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "android.support.v4.view.ViewPager" import "android.support.design.widget.TabLayout" import "androlua.adapter.LuaFragmentPageAdapter" local uihelper = require "uihelper" local JSON = require "cjson" local log = require "log" local fragmentNews = require "yilin/fragment_yilin" -- create view table local layout = { LinearLayout, layout_width = "fill", layout_height = "fill", orientation = "vertical", statusBarColor = "#66ae28", { TabLayout, id = "tab", layout_width = "fill", layout_height = "48dp", background = "#66ae28", }, { FrameLayout, layout_width = "fill", layout_height = "fill", { ViewPager, id = "viewPager", layout_width = "fill", layout_height = "fill", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", } } } local data = { titles = {}, fragments = {}, } table.insert(data.fragments, fragmentNews.newInstance(1)) table.insert(data.titles, '意林') table.insert(data.fragments, fragmentNews.newInstance(2)) table.insert(data.titles, '作文素材') table.insert(data.fragments, fragmentNews.newInstance(3)) table.insert(data.titles, '少年版') table.insert(data.fragments, fragmentNews.newInstance(4)) table.insert(data.titles, '原创版') table.insert(data.fragments, fragmentNews.newInstance(5)) table.insert(data.titles, '12+') local adapter = LuaFragmentPageAdapter(activity.getSupportFragmentManager(), luajava.createProxy("androlua.adapter.LuaFragmentPageAdapter$AdapterCreator", { getCount = function() return #data.fragments end, getItem = function(position) position = position + 1 return data.fragments[position] end, getPageTitle = function(position) position = position + 1 return data.titles[position] end })) function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) viewPager.setAdapter(adapter) viewPager.setOffscreenPageLimit(#data.fragments) viewPager.setCurrentItem(0) tab.setSelectedTabIndicatorColor(0xffffffff) tab.setTabTextColors(0x88ffffff, 0xffffffff) tab.setTabMode(TabLayout.MODE_SCROLLABLE) tab.setTabGravity(TabLayout.GRAVITY_CENTER) tab.setupWithViewPager(viewPager) end ================================================ FILE: lua/zhihu-recommend/info.json ================================================ { "id": "pub.hanks.zhihu-recommend", "name": "知乎热门", "icon": "https://user-gold-cdn.xitu.io/2019/5/30/16b08868307f7ea6?w=150&h=150&f=png&s=2168", "main": "main.lua", "versionName": "1.0.1", "versionCode": 3, "desc": "知乎热门推荐" } ================================================ FILE: lua/zhihu-recommend/main.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- douban - hot movie -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaHttp" import "androlua.LuaAdapter" import "androlua.widget.video.VideoPlayerActivity" import "androlua.LuaImageLoader" import "android.support.v7.widget.RecyclerView" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.LinearLayoutManager" import "android.support.v7.widget.Toolbar" import "android.net.Uri" import "pub.hydrogen.android.R" local uihelper = require("uihelper") local JSON = require("cjson") local log = require("log") activity.setTheme(R.style.Theme_AppCompat_NoActionBar) -- create view table local layout = { LinearLayout, orientation = "vertical", layout_width = "fill", layout_height = "fill", statusBarColor = "#0077D9", { Toolbar, background = '#0077D9', id = 'toolbar', layout_width = "match", layout_height = "56dp", }, { FrameLayout, layout_width = "fill", layout_height = "fill", { RecyclerView, background = "#f1f1f1", id = "recyclerView", layout_width = "fill", layout_height = "fill", }, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", } } } local item_view = { LinearLayout, orientation = 'vertical', layout_width = "match", { LinearLayout, orientation = 'vertical', layout_width = "match", background = "@drawable/layout_selector_tran", padding = "16dp", { LinearLayout, id = 'layout_user', gravity = 'center_vertical', { ImageView, id = "iv_avatar", layout_width = "20dp", layout_height = "20dp", scaleType = "centerCrop", }, { TextView, layout_width = "match", id = "tv_user", gravity = "center_vertical", paddingLeft = "8dp", textSize = "12sp", maxLines = 1, ellipsize = "end", textColor = "#929EA5", }, }, { TextView, id = "tv_title", layout_width = "match", paddingTop = "8dp", textSize = "16sp", textColor = "#212121", }, { TextView, id = "tv_summary", layout_width = "match", paddingTop = "8dp", lineSpacingMultiplier = 1.2, maxLines = 5, textSize = "14sp", textColor = "#343434", }, { TextView, id = "tv_info", layout_width = "match", paddingTop = "8dp", maxLines = 1, textSize = "12sp", textColor = "#919DA4", }, }, { View, layout_width = "match", layout_height = "8dp", background = '#E1E6EB', }, } local data = {} local adapter local page = 0 function trim(s) if s == nil then return '' end return s:gsub('<.->', ''):gsub('\\n', ''):gsub('^%s+', ''):gsub('%s+$', '') end local function getData() local url = string.format('https://www.zhihu.com/node/ExploreRecommendListV2') local options = { url = url, method = 'POST', formData = { "method:next", 'params:{"limit":20,"offset":' .. page * 20 .. '}' } } LuaHttp.request(options, function(error, code, body) if error or code ~= 200 then print('fetch data error') return end page = page + 1 local msg = JSON.decode(body).msg uihelper.runOnUiThread(activity, function() local s = #data for i = 1, #msg do local item = msg[i]:gsub('\\"', '"') local title, url = string.match(item, '

    (.-href="(.-)".-)

    ') local likeCount = string.match(item, '
    (.-)
    ') local username = string.match(item, 'class="author[-]link".-
    ') or '' if username then username = '<' .. username end local desc = string.match(item, '(.-)') local summary = string.match(item, '(.-)
    ') local commentCount = string.match(item, 'name="addcomment".->(.-)') local avatar = string.match(item, '') data[#data + 1] = { url = trim(url), title = trim(title), avatar = trim(avatar), username = trim(username), desc = trim(desc), summary = trim(summary), likeCount = trim(likeCount), commentCount = trim(commentCount) } end adapter.notifyItemRangeChanged(s, #data) end) end) end local function launchDetail(item) import "androlua.widget.webview.WebViewActivity" if item and item.url then local url = item.url if not url:find('^http') then url = 'https://www.zhihu.com' .. url end WebViewActivity.start(activity, url, 0xFF0077D9) return end activity.toast('没有 url 可以打开') end function onDestroy() LuaHttp.cancelAll() end function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) activity.setSupportActionBar(toolbar) activity.setTitle('热门精选') toolbar.setNavigationIcon(LuaDrawable.create('zhihu-recommend/zhihu.png')) local screenWidth = uihelper.getScreenWidth() adapter = LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = function() return #data end, getItemViewType = function(position) return 0 end, onCreateViewHolder = function(parent, viewType) local views = {} local holder holder = LuaRecyclerHolder(loadlayout(item_view, views, RecyclerView)) holder.itemView.setTag(views) holder.itemView.onClick = function(view) local position = holder.getAdapterPosition() + 1 end holder.itemView.getLayoutParams().width = screenWidth holder.itemView.onClick = function() local p = holder.getAdapterPosition() + 1 launchDetail(data[p]) end views.tv_title.setTypeface(nil, 1); return holder end, onBindViewHolder = function(holder, position) position = position + 1 local views = holder.itemView.getTag() if views == nil then return end local item = data[position] LuaImageLoader.loadWithRadius(views.iv_avatar, 20, item.avatar) if item.username == '' or item.username == '<' then views.layout_user.setVisibility(8) else views.layout_user.setVisibility(0) views.tv_user.setText(string.format('%s %s', item.username, item.desc)) end views.tv_title.setText(item.title) views.tv_summary.setText(item.summary) views.tv_info.setText(string.format('%d 赞同 %s', item.likeCount, item.commentCount)) if position == #data then getData() end end, })) recyclerView.setLayoutManager(LinearLayoutManager(activity)) recyclerView.setAdapter(adapter) getData() end function onCreateOptionsMenu(menu) menu.add("网页版") return true end function onOptionsItemSelected(item) local title = item.getTitle() if title == "网页版" then activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse('https://www.zhihu.com/explore'))) end end ================================================ FILE: lua/zhihudaliy/activity_zhihu_daliy_detail.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/26 -- A news app -- require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "androlua.LuaWebView" import "androlua.LuaHttp" local uihelper = require("uihelper") local JSON = require("cjson") -- create view table local layout = { FrameLayout, layout_width = "fill", layout_height = "fill", { LuaWebView, id = "webview", layout_width = "fill", layout_height = "fill", }, { ProgressBar, layout_gravity = "center", id = "progressBar", layout_width = "40dp", layout_height = "40dp", }, } local htmlTemplate = [[ %s %s %s ]] function onCreate(savedInstanceState) activity.setStatusBarColor(0x00000000) activity.setContentView(loadlayout(layout)) local id = activity.getIntent().getStringExtra('newsid') webview.setVisibility(0) progressBar.setVisibility(8) LuaHttp.request({ url = string.format('http://news-at.zhihu.com/api/4/news/%d', id) }, function(error, code, body) local json = JSON.decode(body) local title = json.title or '' local body = json.body or '' local image = json.image or 'https://pic1.zhimg.com/v2-456bb69183a78a7290c64ad7580fa2ec.jpg' local css = '' if json.css then for i = 1, #json.css do css = css .. string.format(' %s', p.getVersionName(), plugin.versionName) plugin.position = plugin.position - 999 else plugin.type = 'uninstall' end end end if plugin.type == 'install' then plugin.position = -plugin.position end end local function getData() local options = { url = 'https://coding.net/u/zhangyuhan/p/api_luanroid/git/raw/master/api/plugins' } LuaHttp.request(options, function(error, code, body) local localList = FileUtils.getPluginList() local json = JSON.decode(body) local list = json.data for i = 1, #list do local plugin = list[i] plugin.position = i compareWithLocal(localList, plugin) data[#data + 1] = plugin; end table.sort(data, function(l, r) return l.position < r.position end) notifyAdapterData() end) end local function downloadPlugin(plugin) plugin.type = 'downloading' notifyAdapterData() FileUtils.downloadPlugin(plugin.download, plugin.id, function(pluginDir) plugin.type = 'uninstall' notifyAdapterData() end) end function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) activity.disableDrawer() back.onClick = function() activity.finish() end tv_support.onClick = function() xpcall(function() local intentFullUrl = "intent://platformapi/startapp?saId=10000007&clientVersion=3.7.0.0718&qrcode=https%3A%2F%2Fqr.alipay.com%2Faex09002nkvmcsullzrwg2b%3F_s%3Dweb-other&_t=1472443966571#Intent;scheme=alipayqr;package=com.eg.android.AlipayGphone;end" activity.startActivity(Intent.parseUri(intentFullUrl, 1)); end, function() local url = "https://qr.alipay.com/aex09002nkvmcsullzrwg2b"; activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url))); end) end adapter = LuaAdapter(luajava.createProxy("androlua.LuaAdapter$AdapterCreator", { getCount = function() return #data end, getView = function(position, convertView, parent) position = position + 1 -- lua 索引从 1开始 if convertView == nil then local views = {} -- store views convertView = loadlayout(item_view, views, ListView) if Build.VERSION.SDK_INT < 16 then views.download.setBackgroundDrawable(gd); else views.download.setBackground(gd); end convertView.getLayoutParams().width = parent.getWidth() convertView.setTag(views) end local views = convertView.getTag() local plugin = data[position] if views == nil or plugin == nil then return end LuaImageLoader.loadWithRadius(views.icon, 40, plugin.icon) views.text.setText(plugin.name) views.desc.setText(plugin.desc) views.tv_version.setText(plugin.versionName) views.download.setText(flatType(plugin.type)) views.download.setTextColor(flatTypeColor(plugin.type)) views.download.onClick = function(view) if plugin.type == 'downloading' then return elseif plugin.type == 'update' or plugin.type == 'install' then plugin.type = 'downloading' downloadPlugin(plugin) else FileUtils.removePlugin(plugin.id) plugin.type = 'install' notifyAdapterData() end end return convertView end })) listview.setAdapter(adapter) getData() end ================================================ FILE: lua_main/activity_setting.lua ================================================ require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "android.support.v7.widget.AppCompatCheckBox" import "android.net.Uri" import "java.io.File" import "androlua.utils.DialogUtils" import "android.support.v7.widget.AppCompatSeekBar" import "androlua.common.LuaFileUtils" import "android.graphics.BitmapFactory" local Media = import "android.provider.MediaStore$Images$Media" local CompressFormat = import "android.graphics.Bitmap$CompressFormat" local DialogBuilder = import "android.app.AlertDialog$Builder" local JSON = require "cjson" local config_file = "luandroid" local sp = activity.getSharedPreferences(config_file, Context.MODE_PRIVATE) local CODE_PICK_BG, CODE_PICK_LOGO, CODE_PICK_SPLASH = 0x1, 0x2, 0x3 local config = JSON.decode(sp.getString('config', '{}')) local divider = { View, layout_width = "match", layout_height = "0.5dp", background = "#f1f1f1", } local dialog_progress = { RelativeLayout, padding = "16dp", { TextView, id = 'tv_progress', layout_alignParentRight = true, layout_centerVertical = true, textSize = "14sp", textColor = "#444444", }, { AppCompatSeekBar, id = 'progress', layout_width = "fill", layout_toLeftOf = "tv_progress" } } local function layoutTitle(text) return { TextView, layout_width = "fill", layout_height = "16dp", textColor = "#666666", background = "#fafafa", textSize = "13sp", gravity = "center_vertical", paddingLeft = "16dp", } end local function layoutText(text, id, tvId) return { LinearLayout, id = id, layout_height = "48dp", layout_width = "fill", orientation = "horizontal", gravity = "center_vertical", background = "@drawable/layout_selector_tran", { TextView, paddingLeft = "16dp", text = text, textColor = "#444444", textSize = "14sp", }, { TextView, id = tvId, singleLine = true, ellipsize = "middle", layout_width = "fill", paddingLeft = "16dp", paddingRight = "16dp", gravity = 'right', textColor = "#999999", textSize = "12sp", } } end local function layoutCheckBox(text, id, checked) return { RelativeLayout, layout_width = "match", layout_height = "48dp", paddingLeft = "16dp", { TextView, text = text, textColor = "#444444", textSize = "14sp", layout_centerVertical = true, }, { AppCompatCheckBox, id = id, layout_width = "50dp", layout_height = "50dp", layout_centerVertical = true, layout_alignParentRight = true, checked = checked, } } end local layout_content = { ScrollView, layout_width = "fill", layout_height = "fill", background = "#FFFFFF", { LinearLayout, layout_width = "fill", layout_height = "fill", orientation = "vertical", layoutTitle('界面'), layoutText('首页背景', 'layout_home_bg', 'tv_home_bg'), divider, layoutText('首页Logo', 'layout_home_logo', 'tv_home_logo'), divider, layoutText('APP启动图', 'layout_home_splash', 'tv_home_splash'), divider, layoutText('图标圆角大小', 'layout_home_radius', 'tv_home_radius'), divider, layoutText('背景不透明度', 'layout_home_alpha', 'tv_home_alpha'), divider, layoutText('恢复默认设置', 'layout_reset'), layoutTitle('其他'), { LinearLayout, id = 'layout_support', layout_height = "48dp", layout_width = "fill", orientation = "horizontal", gravity = "center_vertical", background = "@drawable/layout_selector_tran", { TextView, paddingLeft = "16dp", text = '续一秒(๑→ܫ←)', textColor = "#D86758", textSize = "14sp", }, }, divider, layoutText('应用评分/更新', 'layout_market'), divider, layoutText('推荐给好基友', 'layout_shareapp'), layoutTitle(''), } } local layout = { LinearLayout, layout_width = "match", layout_height = "match", orientation = "vertical", background = "#666666", statusBarColor = "#222222", { LinearLayout, orientation = "horizontal", layout_width = "fill", layout_height = "56dp", background = "#222222", gravity = "center_vertical", { ImageView, id = "back", layout_width = "56dp", layout_height = "56dp", src = "@drawable/ic_menu_back", background = "@drawable/layout_selector_tran", scaleType = "centerInside", }, { TextView, layout_height = "56dp", layout_width = "fill", id = "tv_title", gravity = "center_vertical", paddingLeft = "8dp", textColor = "#FFFFFF", textSize = "16sp", text = "设置", }, }, { FrameLayout, layout_width = "fill", layout_height = "fill", layout_content, { View, layout_width = "fill", layout_height = "3dp", background = "@drawable/shadow_line_top", } }, } local function layout_item_pay(id, drawable, text) return { LinearLayout, gravity = 'center_vertical', background = '@drawable/layout_selector_tran', paddingTop = '8dp', paddingLeft = '8dp', paddingBottom = '8dp', id = id, { ImageView, layout_width = '72dp', layout_height = '72dp', src = drawable, }, { TextView, layout_width = '200dp', textSize = '14sp', paddingLeft = '8dp', textColor = '#222222', text = text, }, } end local layout_pay = { LinearLayout, layout_width = 'fill', background = '#ffffff', orientation = 'vertical', gravity = 'center', layout_item_pay('iv_wechat', '@drawable/wechat', '微信捐赠(zyhan8866)'), layout_item_pay('iv_alipay', '@drawable/alipay', '支付宝捐赠'), layout_item_pay('iv_qq', '@drawable/qq', 'QQ捐赠(1161745215)'), } local function updateConfigUI(config) if config == nil then return end tv_home_bg.setText(config.home_bg or '默认') tv_home_logo.setText(config.home_logo or '默认') tv_home_splash.setText(config.home_splash or '自动(每日一张)') tv_home_radius.setText(config.home_icon_radius or '40') tv_home_alpha.setText(config.home_bg_alpha or '9') end local function saveConfig(config) sp.edit().putString("config", JSON.encode(config)).apply() updateConfigUI(config) end local function getIdentifier(type, name) -- drawable ic_back return activity.getResources().getIdentifier(name, type, activity.getPackageName()) end local function copyText(text) local clipboard = activity.getSystemService(Context.CLIPBOARD_SERVICE) local clip = ClipData.newPlainText("氢应用", text) clipboard.setPrimaryClip(clip) end function onCreate(savedInstanceState) activity.setContentView(loadlayout(layout)) activity.disableDrawer() updateConfigUI(config) back.onClick = function() activity.finish() end layout_market.onClick = function() local intent = Intent(Intent.ACTION_VIEW) intent.setData(Uri.parse('market://details?id=pub.hydrogen.android')) activity.startActivity(intent) end layout_home_bg.onClick = function() pcall(function() activity.toast('长按可恢复默认') local intent = Intent(Intent.ACTION_GET_CONTENT) intent.setType("image/*") activity.startActivityForResult(intent, CODE_PICK_BG) end) end layout_home_logo.onClick = function() pcall(function() activity.toast('长按可恢复默认') local intent = Intent(Intent.ACTION_GET_CONTENT) intent.setType("image/*") activity.startActivityForResult(intent, CODE_PICK_LOGO) end) end layout_home_splash.onClick = function() pcall(function() activity.toast('长按可恢复默认') local intent = Intent(Intent.ACTION_GET_CONTENT) intent.setType("image/*") activity.startActivityForResult(intent, CODE_PICK_SPLASH) end) end layout_home_logo.setOnLongClickListener(luajava.createProxy('android.view.View$OnLongClickListener', { onLongClick = function(view) config.home_logo = nil saveConfig(config) activity.toast('已恢复默认') return true end })) layout_home_bg.setOnLongClickListener(luajava.createProxy('android.view.View$OnLongClickListener', { onLongClick = function(view) config.home_bg = nil config.home_bg_alpha = '9' saveConfig(config) activity.toast('已恢复默认') return true end })) layout_home_splash.setOnLongClickListener(luajava.createProxy('android.view.View$OnLongClickListener', { onLongClick = function(view) config.home_splash = nil saveConfig(config) activity.toast('已恢复默认') return true end })) layout_home_radius.onClick = function() local ids = {} DialogBuilder(activity).setView(loadlayout(dialog_progress, ids, ViewGroup)).show() ids.progress.setMax(50) ids.progress.setProgress(tonumber(config.home_icon_radius) or 40) ids.tv_progress.setText(config.home_icon_radius or '40') ids.progress.setOnSeekBarChangeListener(luajava.createProxy('android.widget.SeekBar$OnSeekBarChangeListener', { onProgressChanged = function(bar, progress, fromUser) config.home_icon_radius = '' .. progress ids.tv_progress.setText(config.home_icon_radius) saveConfig(config) end })) end layout_home_alpha.onClick = function() local ids = {} DialogBuilder(activity).setView(loadlayout(dialog_progress, ids, ViewGroup)).show() ids.progress.setMax(10) ids.progress.setProgress(tonumber(config.home_bg_alpha) or 9) ids.tv_progress.setText(config.home_bg_alpha or '9') ids.progress.setOnSeekBarChangeListener(luajava.createProxy('android.widget.SeekBar$OnSeekBarChangeListener', { onProgressChanged = function(bar, progress, fromUser) config.home_bg_alpha = '' .. progress ids.tv_progress.setText(config.home_bg_alpha) saveConfig(config) end })) end layout_reset.onClick = function() DialogBuilder(activity).setTitle('重置设置').setMessage('重置到默认的设置?').setNegativeButton('取消', nil).setPositiveButton('确定', luajava.createProxy('android.content.DialogInterface$OnClickListener', { onClick = function(dialog, which) config = {} saveConfig(config) end })).show() end layout_shareapp.onClick = function() pcall(function() local intent = Intent(Intent.ACTION_SEND) intent.putExtra(Intent.EXTRA_TEXT, '震惊,所有用了这个 APP 的人都再也离不开了! http://coolapk.com/apk/pub.hydrogen.android'); intent.setType("text/plain"); activity.startActivity(Intent.createChooser(intent, '分享')) end) end layout_support.onClick = function() local ids = {} DialogBuilder(activity).setView(loadlayout(layout_pay, ids, ViewGroup)).show() ids.iv_wechat.onClick = function() copyText('zyhan8866') activity.toast('已复制"zyhan8866"') local bitmap = BitmapFactory.decodeResource(activity.getResources(), getIdentifier('drawable', 'qr_wechat')); local path = activity.getExternalFilesDir('qr').getAbsolutePath() .. '/qr_wechat.png' LuaFileUtils.bitmapToFile(bitmap, File(path), CompressFormat.PNG, 100) Media.insertImage(activity.getContentResolver(), path, "氢应用", "微信二维码"); activity.toast('二维码已保存:' .. path) pcall(function() local intent = Intent(Intent.ACTION_MAIN) local cmp = ComponentName("com.tencent.mm", "com.tencent.mm.ui.LauncherUI") intent.addCategory(Intent.CATEGORY_LAUNCHER); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setComponent(cmp); activity.startActivity(intent) end) end ids.iv_alipay.onClick = function() xpcall(function() local intentFullUrl = "intent://platformapi/startapp?saId=10000007&clientVersion=3.7.0.0718&qrcode=https%3A%2F%2Fqr.alipay.com%2Ftsx04452i1hjmquygc9be4b%3F_s%3Dweb-other&_t=1472443966571#Intent;scheme=alipayqr;package=com.eg.android.AlipayGphone;end" activity.startActivity(Intent.parseUri(intentFullUrl, 1)) end, function() local url = "https://qr.alipay.com/tsx04452i1hjmquygc9be4b" activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url))) end) end ids.iv_qq.onClick = function() local qqUrl = "mqqwpa://im/chat?chat_type=wpa&uin=1161745215&version=1" activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(qqUrl))) end end end function onActivityResult(requestCode, resultCode, data) if resultCode ~= -1 or data == nil then return end local uri = data.getData() if requestCode == CODE_PICK_BG and uri then config.home_bg = uri.toString() saveConfig(config) return end if requestCode == CODE_PICK_LOGO and uri then config.home_logo = uri.toString() saveConfig(config) return end if requestCode == CODE_PICK_SPLASH and uri then config.home_splash = uri.toString() saveConfig(config) return end end ================================================ FILE: lua_main/filehelper.lua ================================================ local filehelper = {} -- local function readfile(path) local file = io.open(path, "rb") -- r read mode and b binary mode if not file then return nil end local content = file:read "*a" -- *a or *all reads the whole file file:close() return content end -------------------------------------------- -- 功能:写入文件 -- 输入:文件名, 内容 -- 输出:生成的文件里面包含内容 local function writefile(path, content) print('writefile:' .. path, content) local wfile = io.open(path, "w") --写入文件(w覆盖) assert(wfile) --打开时验证是否出错 wfile:write(content) --写入传入的内容 wfile:close() --调用结束后记得关闭 end filehelper.readfile = readfile filehelper.writefile = writefile return filehelper ================================================ FILE: lua_main/fragment_home.lua ================================================ -- -- Created by IntelliJ IDEA. -- User: hanks -- Date: 2017/5/13 -- Time: 00:01 -- To change this template use File | Settings | File Templates. -- require "import" local FileUtils = import "androlua.common.LuaFileUtils" local DialogBuilder = import "android.app.AlertDialog$Builder" import "android.widget.*" import "android.content.*" import "androlua.LuaAdapter" import "androlua.LuaImageLoader" import "androlua.LuaFragment" import "androlua.LuaHttp" import "androlua.widget.webview.WebViewActivity" import "android.support.v4.widget.SwipeRefreshLayout" import "android.graphics.drawable.GradientDrawable" import "android.os.Build" import "android.support.v7.widget.RecyclerView" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.Toolbar" import "androlua.LuaActivity" import "androlua.LuaAdapter" import "android.support.v7.widget.GridLayoutManager" import "android.support.v7.widget.helper.ItemTouchHelper" import "pub.hanks.sample.adapter.DragTouchHelper" import "android.support.design.widget.BottomSheetBehavior" import "android.support.design.widget.CoordinatorLayout" import "android.support.v4.view.MotionEventCompat" import "android.view.MotionEvent" local Orientation = import "android.graphics.drawable.GradientDrawable$Orientation" local uihelper = require "uihelper" local JSON = require "cjson" local log = require "log" local function newInstance() -- create view table local layout = { CoordinatorLayout, layout_width = "fill", layout_height = "fill", focusable = true, focusableInTouchMode = true, { LinearLayout, layout_width = "fill", layout_height = "fill", gravity = "center_horizontal", orientation = 1, paddingLeft = "16dp", paddingRight = "16dp", { RelativeLayout, layout_height = "48dp", layout_width = "fill", layout_marginLeft = "16dp", layout_marginTop = "36dp", layout_marginRight = "8dp", { TextView, id = "tv_date", text = "23月33日 333", textSize = "14sp", layout_centerVertical = true, textColor = "#aa333333", }, { ImageView, id = "iv_setting", layout_height = "48dp", layout_width = "48dp", padding = "12dp", scaleType = "center", src = "@drawable/ic_setting", layout_alignParentRight = true, } }, { ImageView, id = "iv_logo", layout_height = "56dp", layout_width = "56dp", layout_marginTop = "24dp", visibility = "invisible", src = "@drawable/logo_no_bg", }, { RelativeLayout, layout_height = "48dp", layout_width = "fill", background = "#AAEBF0F2", layout_marginLeft = "16dp", layout_marginTop = "36dp", layout_marginRight = "16dp", { TextView, id = "tv_add_plugin", layout_height = "fill", layout_width = "fill", gravity = "center", text = "漫画 | 资讯 | 视频 | 图片", textColor = "#9DAEBF", textSize = "12sp", }, { TextView, id = "tv_updateCount", layout_height = "18dp", layout_width = "18dp", layout_margin = "12dp", layout_alignParentRight = true, layout_centerVertical = true, gravity = "center", text = "3", textColor = "#9DAEBF", textSize = "9sp", elevation = "1dp", visibility = 8, }, }, { RecyclerView, id = "recyclerView", layout_width = "fill", layout_marginTop = "32dp", clipToPadding = false, layout_marginLeft = "8dp", layout_marginRight = "8dp", overScrollMode = 2, fadingEdgeLength = 0, verticalFadingEdgeEnabled = false, horizontalFadingEdgeEnabled = false, }, }, } local sp = activity.getSharedPreferences("luandroid", Context.MODE_PRIVATE) local item_view = { RelativeLayout, layout_height = "70dp", background = "@drawable/layout_selector_tran", { ImageView, id = "icon", layout_width = "40dp", layout_height = "40dp", layout_marginTop = "6dp", layout_centerHorizontal = true, }, { TextView, id = "text", layout_below = "icon", textColor = "#444444", textSize = "9sp", gravity = "center", layout_width = "fill", layout_height = "22dp", }, { ImageView, layout_alignParentRight = true, id = "ic_del", layout_marginRight = "2dp", layout_width = "24dp", layout_height = "24dp", src = "@drawable/ic_clear", visibility = 'gone', }, } local hadLoadData local isVisible local lastId local params = { rid = rid } local data = {} local ids = {} local config = {} local isDragging = false local weeks = { "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六" } local adapter local touchHelper local gd = GradientDrawable() gd.setShape(GradientDrawable.OVAL) gd.setColor(0xFFFFFFFF) local function isDraggingIcons() return isDragging end local function setDraggingIcons(drag) isDragging = drag end local function newActivity(luaPath) local intent = Intent(activity, LuaActivity) intent.putExtra("luaPath", luaPath) activity.startActivity(intent) end local function launchPluginManager() newActivity(luajava.luadir .. '/activity_plugins.lua') end local function saveConfig(config) sp.edit().putString("config", JSON.encode(config)).apply() end local function needUpdate(localList, plugin) for i = 1, #localList do local p = localList[i - 1] if p.getId() == plugin.id and p.getVersionCode() < plugin.versionCode then return true end end return false end local function checkPluginUpdate() if config.update_inwifi == false then return end local url = 'https://coding.net/u/zhangyuhan/p/api_luanroid/git/raw/master/api/plugins' LuaHttp.request({ url = url }, function(error, code, body) local localList = FileUtils.getPluginList() local json = JSON.decode(body) local list = json.data local count = 0 for i = 1, #list do local plugin = list[i] if needUpdate(localList, plugin) then count = count + 1 end end uihelper.runOnUiThread(activity, function() if count > 0 then ids.tv_updateCount.setVisibility(0) if Build.VERSION.SDK_INT < 16 then ids.tv_updateCount.setBackgroundDrawable(gd); else ids.tv_updateCount.setBackground(gd); end ids.tv_updateCount.setText(string.format('%d', count)) else ids.tv_updateCount.setVisibility(8) end end) end) end local function getAdapter(mData, changeColor, getItemCountFunc, getTouchHelperFunc) return LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = getItemCountFunc, getItemViewType = function(position) return 0 end, onCreateViewHolder = function(parent, viewType) local views = {} local holder = LuaRecyclerHolder(loadlayout(item_view, views, RecyclerView)) holder.itemView.setTag(views) holder.itemView.getLayoutParams().width = ids.recyclerView.getWidth() / 5 - 1 holder.itemView.setOnTouchListener(luajava.createProxy('android.view.View$OnTouchListener', { onTouch = function(v, event) if isDragging and MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN then getTouchHelperFunc().startDrag(holder) end return false end })) holder.itemView.setOnLongClickListener(luajava.createProxy('android.view.View$OnLongClickListener', { onLongClick = function(v) isDragging = true adapter.notifyDataSetChanged() return true end })) holder.itemView.onClick = function() local p = holder.getAdapterPosition() + 1 local item = mData[p] newActivity(item.launchPage) end views.ic_del.onClick = function() local p = holder.getAdapterPosition() local id if p + 1 <= #data then id = data[p + 1].id table.remove(data, p + 1) adapter.notifyItemRemoved(p) end if id then FileUtils.removePlugin(id) end end return holder end, onBindViewHolder = function(holder, position) position = position + 1 local views = holder.itemView.getTag() local item = mData[position] if views == nil or item == nil then return end if isDragging then views.ic_del.setScaleX(0) views.ic_del.setScaleY(0) views.ic_del.setVisibility(0) views.ic_del.animate().scaleX(1).scaleY(1).start() else views.ic_del.setVisibility(8) end local icon = item.icon local radius = tonumber(config.home_icon_radius or '40') LuaImageLoader.loadWithRadius(views.icon, radius, icon) views.text.setText(item.text) local alpha = tonumber(config.home_bg_alpha or 9) if changeColor and alpha <= 5 then views.text.setTextColor(0xFFFFFFFF) else views.text.setTextColor(0xFF444444) end end, })) end local function getTouchHelperCallback(mData, mAdapter) return DragTouchHelper(luajava.createProxy('pub.hanks.sample.adapter.DragTouchHelper$Creator', { onMove = function(rec, holder, target) local fromPosition = holder.getAdapterPosition() + 1 local toPosition = target.getAdapterPosition() + 1 local tmp = mData[fromPosition] table.remove(mData, fromPosition) table.insert(mData, toPosition, tmp) mAdapter.notifyItemMoved(fromPosition - 1, toPosition - 1) end, isLongPressDragEnabled = function() return false end, clearView = function(rec, holder) local sortApps = {} for i = 1, #mData do sortApps[#sortApps + 1] = mData[i].id end config.sortApps = sortApps saveConfig(config) end, getDragFlags = function() return 0xF end, getSwipeFlags = function() return 0 end, })) end local function getData() for k, v in pairs(data) do data[k] = nil end config = JSON.decode(sp.getString('config', '{}')) local sortApps = config.sortApps or {} local localList = LuaFileUtils.getPluginList() for i = 1, #localList do local p = localList[i - 1] local item = { id = p.getId(), text = p.getName(), launchPage = p.getMainPath(), icon = p.getIconPath(), position = 9999 + i, } data[#data + 1] = item for j = 1, #sortApps do if sortApps[j] == item.id then item.position = j end end end -- sort table.sort(data, function(l, r) return l.position < r.position end) adapter.notifyDataSetChanged() -- save new config local newSortApps = {} for i = 1, #data do newSortApps[#newSortApps + 1] = data[i].id end config.sortApps = newSortApps saveConfig(config) end -- 获取类似 @drawable/ic_back 的资源 local function getIdentifier(type, name) -- drawable ic_back return activity.getResources().getIdentifier(name, type, activity.getPackageName()) end adapter = getAdapter(data, true, function() local size = #data if size > 20 then size = 20 end return size end, function() return touchHelper end) local fragment = LuaFragment.newInstance() fragment.setCreator(luajava.createProxy('androlua.LuaFragment$FragmentCreator', { onCreateView = function(inflater, container, savedInstanceState) return loadlayout(layout, ids) end, onResume = function() getData() -- logo if config.home_logo and config.home_logo ~= '' then LuaImageLoader.loadWithRadius(ids.iv_logo, 36, config.home_logo) else ids.iv_logo.setImageResource(getIdentifier('drawable', 'logo_no_bg')) end checkPluginUpdate() end, onViewCreated = function(view, savedInstanceState) ids.recyclerView.setLayoutManager(GridLayoutManager(activity, 5)) ids.recyclerView.setAdapter(adapter) touchHelper = ItemTouchHelper(getTouchHelperCallback(data, adapter)) touchHelper.attachToRecyclerView(ids.recyclerView) ids.tv_date.setText(os.date('%m月%d日 ') .. weeks[os.date('%w') + 1]) ids.iv_logo.onClick = function(view) newActivity(luajava.luadir .. '/activity_setting.lua') end ids.iv_setting.onClick = function(view) newActivity(luajava.luadir .. '/activity_setting.lua') end ids.tv_add_plugin.onClick = function(args) launchPluginManager() end end, })) return fragment, adapter, getData, isDraggingIcons, setDraggingIcons end return { newInstance = newInstance } ================================================ FILE: lua_main/fragment_list.lua ================================================ -- -- Created by IntelliJ IDEA. -- User: hanks -- Date: 2017/5/13 -- Time: 00:01 -- To change this template use File | Settings | File Templates. -- require "import" local FileUtils = import "androlua.common.LuaFileUtils" import "android.widget.*" import "android.content.*" import "androlua.LuaAdapter" import "androlua.LuaImageLoader" import "androlua.LuaFragment" import "androlua.LuaHttp" import "androlua.widget.webview.WebViewActivity" import "android.support.v4.widget.SwipeRefreshLayout" import "android.graphics.drawable.GradientDrawable" import "android.os.Build" import "android.support.v7.widget.RecyclerView" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.Toolbar" import "androlua.LuaActivity" import "androlua.LuaAdapter" import "android.support.v7.widget.GridLayoutManager" import "android.support.v7.widget.helper.ItemTouchHelper" import "pub.hanks.sample.adapter.DragTouchHelper" import "android.support.design.widget.BottomSheetBehavior" import "android.support.design.widget.CoordinatorLayout" import "android.support.v4.view.MotionEventCompat" import "android.view.MotionEvent" local uihelper = require "uihelper" local JSON = require "cjson" local function newInstance() -- create view table local layout = { CoordinatorLayout, layout_width = "fill", layout_height = "fill", focusable = true, focusableInTouchMode = true, { LinearLayout, layout_width = "fill", layout_height = "fill", gravity = "center_horizontal", orientation = 1, paddingLeft = "16dp", paddingRight = "16dp", { RelativeLayout, layout_height = "48dp", layout_width = "fill", layout_marginLeft = "16dp", layout_marginTop = "36dp", layout_marginRight = "8dp", { TextView, id = "tv_date", text = "23月33日 333", textSize = "14sp", layout_centerVertical = true, textColor = "#aa333333", }, { ImageView, id = "iv_setting", layout_height = "48dp", layout_width = "48dp", padding = "12dp", scaleType = "center", src = "@drawable/ic_setting", layout_alignParentRight = true, } }, { RecyclerView, id = "recyclerView", layout_width = "fill", layout_marginTop = "16dp", layout_marginLeft = "8dp", layout_marginRight = "8dp", overScrollMode = 2, fadingEdgeLength = 0, verticalFadingEdgeEnabled = false, horizontalFadingEdgeEnabled = false, }, }, } local sp = activity.getSharedPreferences("luandroid", Context.MODE_PRIVATE) local item_view = { RelativeLayout, layout_height = "70dp", background = "@drawable/layout_selector_tran", { ImageView, id = "icon", layout_width = "40dp", layout_height = "40dp", layout_marginTop = "6dp", layout_centerHorizontal = true, }, { TextView, id = "text", layout_below = "icon", textColor = "#444444", textSize = "9sp", gravity = "center", layout_width = "fill", layout_height = "22dp", }, { ImageView, layout_alignParentRight = true, id = "ic_del", layout_marginRight = "2dp", layout_width = "24dp", layout_height = "24dp", src = "@drawable/ic_clear", visibility = 'gone', }, } local hadLoadData local isVisible local lastId local data = {} local ids = {} local config = {} local isDragging = false local weeks = { "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六" } local adapter local touchHelper local gd = GradientDrawable() gd.setShape(GradientDrawable.OVAL) gd.setColor(0xFFFFFFFF) local function newActivity(luaPath) local intent = Intent(activity, LuaActivity) intent.putExtra("luaPath", luaPath) activity.startActivity(intent) end local function saveConfig(config) sp.edit().putString("config", JSON.encode(config)).apply() end local function getAdapter(mData, changeColor, getItemCountFunc, getTouchHelperFunc) return LuaRecyclerAdapter(luajava.createProxy('androlua.adapter.LuaRecyclerAdapter$AdapterCreator', { getItemCount = getItemCountFunc, getItemViewType = function(position) return 0 end, onCreateViewHolder = function(parent, viewType) local views = {} local holder = LuaRecyclerHolder(loadlayout(item_view, views, RecyclerView)) holder.itemView.setTag(views) holder.itemView.getLayoutParams().width = ids.recyclerView.getWidth() / 5 - 1 holder.itemView.setOnTouchListener(luajava.createProxy('android.view.View$OnTouchListener', { onTouch = function(v, event) if isDragging and MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN then getTouchHelperFunc().startDrag(holder) end return false end })) holder.itemView.setOnLongClickListener(luajava.createProxy('android.view.View$OnLongClickListener', { onLongClick = function(v) isDragging = true adapter.notifyDataSetChanged() return true end })) holder.itemView.onClick = function() local p = holder.getAdapterPosition() + 1 local item = mData[p] newActivity(item.launchPage) end views.ic_del.onClick = function() local p = holder.getAdapterPosition() local id if p + 1 <= #data then id = data[p + 1].id table.remove(data, p + 1) adapter.notifyItemRemoved(p) end if id then FileUtils.removePlugin(id) end end return holder end, onBindViewHolder = function(holder, position) position = position + 1 local views = holder.itemView.getTag() local item = mData[position] if views == nil or item == nil then return end if isDragging then views.ic_del.setScaleX(0) views.ic_del.setScaleY(0) views.ic_del.setVisibility(0) views.ic_del.animate().scaleX(1).scaleY(1).start() else views.ic_del.setVisibility(8) end local icon = item.icon local radius = tonumber(config.home_icon_radius or '40') LuaImageLoader.loadWithRadius(views.icon, radius, icon) views.text.setText(item.text) local alpha = tonumber(config.home_bg_alpha or 9) if changeColor and alpha <= 5 then views.text.setTextColor(0xFFFFFFFF) else views.text.setTextColor(0xFF444444) end end, })) end local function getTouchHelperCallback(mData, mAdapter) return DragTouchHelper(luajava.createProxy('pub.hanks.sample.adapter.DragTouchHelper$Creator', { onMove = function(rec, holder, target) local fromPosition = holder.getAdapterPosition() + 1 local toPosition = target.getAdapterPosition() + 1 local tmp = mData[fromPosition] table.remove(mData, fromPosition) table.insert(mData, toPosition, tmp) mAdapter.notifyItemMoved(fromPosition - 1, toPosition - 1) end, isLongPressDragEnabled = function() return false end, clearView = function(rec, holder) local sortApps = {} for i = 1, #mData do sortApps[#sortApps + 1] = mData[i].id end config.sortApps = sortApps saveConfig(config) end, getDragFlags = function() return 0xF end, getSwipeFlags = function() return 0 end, })) end local function getData() for k, v in pairs(data) do data[k] = nil end config = JSON.decode(sp.getString('config', '{}')) local sortApps = config.sortApps or {} local localList = LuaFileUtils.getPluginList() for i = 1, #localList do local p = localList[i - 1] local item = { id = p.getId(), text = p.getName(), launchPage = p.getMainPath(), icon = p.getIconPath(), position = 9999 + i, } data[#data + 1] = item for j = 1, #sortApps do if sortApps[j] == item.id then item.position = j end end end -- sort table.sort(data, function(l, r) return l.position < r.position end) adapter.notifyDataSetChanged() -- save new config local newSortApps = {} for i = 1, #data do newSortApps[#newSortApps + 1] = data[i].id end config.sortApps = newSortApps saveConfig(config) end local function isDraggingIcons() return isDragging end local function setDraggingIcons(drag) isDragging = drag end adapter = getAdapter(data, true, function() return #data end, function() return touchHelper end) local fragment = LuaFragment.newInstance() fragment.setCreator(luajava.createProxy('androlua.LuaFragment$FragmentCreator', { onCreateView = function(inflater, container, savedInstanceState) return loadlayout(layout, ids) end, onViewCreated = function(view, savedInstanceState) ids.recyclerView.setLayoutManager(GridLayoutManager(activity, 5)) ids.recyclerView.setAdapter(adapter) touchHelper = ItemTouchHelper(getTouchHelperCallback(data, adapter)) touchHelper.attachToRecyclerView(ids.recyclerView) ids.tv_date.setText(os.date('%m月%d日 ') .. weeks[os.date('%w') + 1]) ids.iv_setting.onClick = function(view) newActivity(luajava.luadir .. '/activity_setting.lua') end getData() end, })) return fragment, adapter, getData, isDraggingIcons, setDraggingIcons end return { newInstance = newInstance } ================================================ FILE: lua_main/import.lua ================================================ local context = activity or service local require = require local table = require "table" local packages = {} local loaded = {} -- store bindclass local imported = {} -- store class via import function luajava.package = packages luajava.loaded = loaded luajava.imported = imported local _G = _G local insert = table.insert local new = luajava.new local bindClass = luajava.bindClass local dexes = luajava.astable(context.getClassLoaders()) local libs = context.getLibrarys() local loaders = {} -- store fun ction to load class local function libsloader(path) local p = libs[path:match("^%a+")] if p then return assert(package.loadlib(p, "luaopen_" .. (path:gsub("%.", "_")))), p else return "\n\tno file ./libs/lib" .. path .. ".so" end end table.insert(package.searchers, libsloader) -- classname replace '_' to '&' local function massage_classname(classname) if classname:find('_') then classname = classname:gsub('_', '$') end return classname end local function import_class(classname, packagename) packagename = massage_classname(packagename) local res, class = pcall(bindClass, packagename) if res then loaded[classname] = class return class end end local function import_dex_class(classname, packagename) packagename = massage_classname(packagename) for _, dex in ipairs(dexes) do local res, class = pcall(dex.loadClass, packagename) if res then loaded[classname] = class return class end end end local pkgMT = { __index = function(T, classname) local ret, class = pcall(bindClass, rawget(T, "__name") .. classname) if ret then rawset(T, classname, class) return class else error(classname .. " is not in " .. rawget(T, "__name"), 2) end end } local function import_pacckage(packagename) local pkg = { __name = packagename } setmetatable(pkg, pkgMT) return pkg end local function import_1(classname) for i, p in ipairs(packages) do local class = import_class(classname, p .. classname) if class then return class end end end local function import_2(classname) for _, p in ipairs(packages) do local class = import_dex_class(classname, p .. classname) if class then return class end end end local function import_require(name) local s, r = pcall(require, name) if not s and not r:find("no file") then error(r, 0) end return s and r end -- append v to t local function append(t, v) for _, _v in ipairs(t) do if _v == v then return end end insert(t, v) end append(loaders, import_1) append(loaders, import_2) local globalMT = { __index = function(T, classname) for i, p in ipairs(loaders) do local class = loaded[classname] or p(classname) if class then T[classname] = class return class end end return nil end } --setmetatable(_G, globalMT) -- the implements of import fuction local function env_import(env) local _env = env setmetatable(_env, globalMT) return function(package) local j = package:find(':') if j then local dexname = package:sub(1, j - 1) local classname = package:sub(j + 1, -1) local class = context.loadDex(dexname).loadClass(classname) local classname = package:match('([^%.$]+)$') _env[classname] = class append(imported, package) return class end local i = package:find('%*$') -- android.widget.* if i then -- a wildcard; put into the package list, including the final '.' append(packages, package:sub(1, -2)) append(imported, package) return import_pacckage(package:sub(1, -2)) else -- android.widget.Button local classname = package:match('([^%.$]+)$') local class = import_require(package) or import_class(classname, package) or import_dex_class(classname, package) if class then if class ~= true then --findtable(package)=class if type(class) ~= "table" then append(imported, package) end _env[classname] = class end return class else error("cannot find " .. package, 2) end end end end function compile(name) append(dexes, context.loadDex(name)) end import = env_import(_G) append(packages, '') import 'java.lang.*' import 'java.util.*' import 'androlua.*' import "loadlayout" import "loadbitmap" import "loadmenu" function enum(e) return function() if e.hasMoreElements() then return e.nextElement() end end end function each(o) local iter = o.iterator() return function() if iter.hasNext() then return iter.next() end end end function dump(o) local t = {} local _t = {} local _n = {} local space, deep = string.rep(' ', 2), 0 local function _ToString(o, _k) if type(o) == ('number') then table.insert(t, o) elseif type(o) == ('string') then table.insert(t, string.format('%q', o)) elseif type(o) == ('table') then local mt = getmetatable(o) if mt and mt.__tostring then table.insert(t, tostring(o)) else deep = deep + 2 table.insert(t, '{') for k, v in pairs(o) do if v == _G then table.insert(t, string.format('\r\n%s%s\t=%s ;', string.rep(space, deep - 1), k, "_G")) elseif v ~= package.loaded then if tonumber(k) then k = string.format('[%s]', k) else k = string.format('[\"%s\"]', k) end table.insert(t, string.format('\r\n%s%s\t= ', string.rep(space, deep - 1), k)) if type(v) == ('table') then if _t[tostring(v)] == nil then _t[tostring(v)] = v local _k = _k .. k _t[tostring(v)] = _k _ToString(v, _k) else table.insert(t, tostring(_t[tostring(v)])) table.insert(t, ';') end else _ToString(v, _k) end end end table.insert(t, string.format('\r\n%s}', string.rep(space, deep - 1))) deep = deep - 2 end else table.insert(t, tostring(o)) end table.insert(t, " ;") return t end t = _ToString(o, '') return table.concat(t) end local NIL = {} setmetatable(NIL, { __tostring = function() return "nil" end }) local function printstack() local stacks = {} for m = 2, 16 do local dbs = {} local info = debug.getinfo(m) if info == nil then break end table.insert(stacks, dbs) dbs.info = info local func = info.func local nups = info.nups local ups = {} dbs.upvalues = ups for n = 1, nups do local n, v = debug.getupvalue(func, n) if v == nil then v = NIL end if string.byte(n) == 40 then if ups[n] == nil then ups[n] = {} end table.insert(ups[n], v) else ups[n] = v end end local lps = {} dbs.localvalues = lps lps.vararg = {} --lps.temporary={} for n = -1, -255, -1 do local k, v = debug.getlocal(m, n) if k == nil then break end if v == nil then v = NIL end table.insert(lps.vararg, v) end for n = 1, 255 do local n, v = debug.getlocal(m, n) if n == nil then break end if v == nil then v = NIL end if string.byte(n) == 40 then if lps[n] == nil then lps[n] = {} end table.insert(lps[n], v) else lps[n] = v end --table.insert(lps,string.format("%s=%s",n,v)) end end print(dump(stacks)) -- print("info="..dump(dbs)) -- print("_ENV="..dump(ups._ENV or lps._ENV)) end function getids() return luajava.ids end local function setmetamethod(t, k, v) getmetatable(t)[k] = v end local function getmetamethod(t, k, v) return getmetatable(t)[k] end local function checkPath(path) if path:find("^[^/][%w%./_%-]+$") then if not path:find("%.lua$") then path = string.format("%s/%s.lua", activity.luaDir, path) else path = string.format("%s/%s", activity.luaDir, path) end end return path end local os_mt = {} os_mt.__index = function(t, k) local _t = {} _t.__cmd = (rawget(t, "__cmd") or "") .. k .. " " setmetatable(_t, os_mt) return _t end os_mt.__call = function(t, ...) local cmd = t.__cmd .. table.concat({ ... }, " ") local p = io.popen(cmd) local s = p:read("a") p:close() return s end setmetatable(os, os_mt) local luajava_mt = { __index = function(t, k) local b, ret = xpcall(function() return bindClass((rawget(t, "__name") or "") .. k) end, function() local p = {} p.__name = (rawget(t, "__name") or "") .. k .. "." setmetatable(p, luajava_mt) return p end) rawset(t, k, ret) return ret end } setmetatable(luajava, luajava_mt) return _G ================================================ FILE: lua_main/json.lua ================================================ ----------------------------------------------------------------------------- -- JSON4Lua: JSON encoding / decoding support for the Lua language. -- json Module. -- Author: Craig Mason-Jones -- Homepage: http://github.com/craigmj/json4lua/ -- Version: aaa.0.0 -- This module is released under the MIT License (MIT). -- Please see LICENCE.txt for details. -- -- USAGE: -- This module exposes two functions: -- json.encode(o) -- Returns the table / string / boolean / number / nil / json.null value as a JSON-encoded string. -- json.decode(json_string) -- Returns a Lua object populated with the data encoded in the JSON string json_string. -- -- REQUIREMENTS: -- compat-5.aaa if using Lua 5.0 -- -- CHANGELOG -- 0.9.20 Introduction of local Lua functions for private functions (removed _ function prefix). -- Fixed Lua 5.aaa compatibility issues. -- Introduced json.null to have null values in associative arrays. -- json.encode() performance improvement (more than 50%) through table.concat rather than .. -- Introduced decode ability to ignore /**/ comments in the JSON string. -- 0.9.10 Fix to array encoding / decoding to correctly manage nil/null values in arrays. ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- -- Imports and dependencies ----------------------------------------------------------------------------- local math = require('math') local string = require("string") local table = require("table") ----------------------------------------------------------------------------- -- Module declaration ----------------------------------------------------------------------------- local json = {} -- Public namespace local json_private = {} -- Private namespace -- Public constants json.EMPTY_ARRAY = {} json.EMPTY_OBJECT = {} -- Public functions -- Private functions local decode_scanArray local decode_scanComment local decode_scanConstant local decode_scanNumber local decode_scanObject local decode_scanString local decode_scanWhitespace local encodeString local isArray local isEncodable ----------------------------------------------------------------------------- -- PUBLIC FUNCTIONS ----------------------------------------------------------------------------- --- Encodes an arbitrary Lua object / variable. -- @param v The Lua object / variable to be JSON encoded. -- @return String containing the JSON encoding in internal Lua string format (i.e. not unicode) function json.encode(v) -- Handle nil values if v == nil then return "null" end local vtype = type(v) -- Handle strings if vtype == 'string' then return '"' .. json_private.encodeString(v) .. '"' -- Need to handle encoding in string end -- Handle booleans if vtype == 'number' or vtype == 'boolean' then return tostring(v) end -- Handle tables if vtype == 'table' then local rval = {} -- Consider arrays separately local bArray, maxCount = isArray(v) if bArray then for i = 1, maxCount do table.insert(rval, json.encode(v[i])) end else -- An object, not an array for i, j in pairs(v) do if isEncodable(i) and isEncodable(j) then table.insert(rval, '"' .. json_private.encodeString(i) .. '":' .. json.encode(j)) end end end if bArray then return '[' .. table.concat(rval, ',') .. ']' else return '{' .. table.concat(rval, ',') .. '}' end end -- Handle null values if vtype == 'function' and v == json.null then return 'null' end assert(false, 'encode attempt to encode unsupported type ' .. vtype .. ':' .. tostring(v)) end --- Decodes a JSON string and returns the decoded value as a Lua data structure / value. -- @param s The string to scan. -- @param [startPos] Optional starting position where the JSON string is located. Defaults to aaa. -- @param Lua object, number The object that was scanned, as a Lua table / string / number / boolean or nil, -- and the position of the first character after -- the scanned JSON object. function json.decode(s, startPos) startPos = startPos and startPos or 1 startPos = decode_scanWhitespace(s, startPos) assert(startPos <= string.len(s), 'Unterminated JSON encoded object found at position in [' .. s .. ']') local curChar = string.sub(s, startPos, startPos) -- Object if curChar == '{' then return decode_scanObject(s, startPos) end -- Array if curChar == '[' then return decode_scanArray(s, startPos) end -- Number if string.find("+-0123456789.e", curChar, 1, true) then return decode_scanNumber(s, startPos) end -- String if curChar == [["]] or curChar == [[']] then return decode_scanString(s, startPos) end if string.sub(s, startPos, startPos + 1) == '/*' then return json.decode(s, decode_scanComment(s, startPos)) end -- Otherwise, it must be a constant return decode_scanConstant(s, startPos) end --- The null function allows one to specify a null value in an associative array (which is otherwise -- discarded if you set the value with 'nil' in Lua. Simply set t = { first=json.null } function json.null() return json.null -- so json.null() will also return null ;-) end ----------------------------------------------------------------------------- -- Internal, PRIVATE functions. -- Following a Python-like convention, I have prefixed all these 'PRIVATE' -- functions with an underscore. ----------------------------------------------------------------------------- --- Scans an array from JSON into a Lua object -- startPos begins at the start of the array. -- Returns the array and the next starting position -- @param s The string being scanned. -- @param startPos The starting position for the scan. -- @return table, int The scanned array as a table, and the position of the next character to scan. function decode_scanArray(s, startPos) local array = {} -- The return value local stringLen = string.len(s) assert(string.sub(s, startPos, startPos) == '[', 'decode_scanArray called but array does not start at position ' .. startPos .. ' in string:\n' .. s) startPos = startPos + 1 -- Infinite loop for array elements repeat startPos = decode_scanWhitespace(s, startPos) assert(startPos <= stringLen, 'JSON String ended unexpectedly scanning array.') local curChar = string.sub(s, startPos, startPos) if (curChar == ']') then return array, startPos + 1 end if (curChar == ',') then startPos = decode_scanWhitespace(s, startPos + 1) end assert(startPos <= stringLen, 'JSON String ended unexpectedly scanning array.') object, startPos = json.decode(s, startPos) table.insert(array, object) until false end --- Scans a comment and discards the comment. -- Returns the position of the next character following the comment. -- @param string s The JSON string to scan. -- @param int startPos The starting position of the comment function decode_scanComment(s, startPos) assert(string.sub(s, startPos, startPos + 1) == '/*', "decode_scanComment called but comment does not start at position " .. startPos) local endPos = string.find(s, '*/', startPos + 2) assert(endPos ~= nil, "Unterminated comment in string at " .. startPos) return endPos + 2 end --- Scans for given constants: true, false or null -- Returns the appropriate Lua type, and the position of the next character to read. -- @param s The string being scanned. -- @param startPos The position in the string at which to start scanning. -- @return object, int The object (true, false or nil) and the position at which the next character should be -- scanned. function decode_scanConstant(s, startPos) local consts = { ["true"] = true, ["false"] = false, ["null"] = nil } local constNames = { "true", "false", "null" } for i, k in pairs(constNames) do if string.sub(s, startPos, startPos + string.len(k) - 1) == k then return consts[k], startPos + string.len(k) end end assert(nil, 'Failed to scan constant from string ' .. s .. ' at starting position ' .. startPos) end --- Scans a number from the JSON encoded string. -- (in fact, also is able to scan numeric +- eqns, which is not -- in the JSON spec.) -- Returns the number, and the position of the next character -- after the number. -- @param s The string being scanned. -- @param startPos The position at which to start scanning. -- @return number, int The extracted number and the position of the next character to scan. function decode_scanNumber(s, startPos) local endPos = startPos + 1 local stringLen = string.len(s) local acceptableChars = "+-0123456789.e" while (string.find(acceptableChars, string.sub(s, endPos, endPos), 1, true) and endPos <= stringLen) do endPos = endPos + 1 end local stringValue = 'return ' .. string.sub(s, startPos, endPos - 1) local stringEval = loadstring(stringValue) assert(stringEval, 'Failed to scan number [ ' .. stringValue .. '] in JSON string at position ' .. startPos .. ' : ' .. endPos) return stringEval(), endPos end --- Scans a JSON object into a Lua object. -- startPos begins at the start of the object. -- Returns the object and the next starting position. -- @param s The string being scanned. -- @param startPos The starting position of the scan. -- @return table, int The scanned object as a table and the position of the next character to scan. function decode_scanObject(s, startPos) local object = {} local stringLen = string.len(s) local key, value assert(string.sub(s, startPos, startPos) == '{', 'decode_scanObject called but object does not start at position ' .. startPos .. ' in string:\n' .. s) startPos = startPos + 1 repeat startPos = decode_scanWhitespace(s, startPos) assert(startPos <= stringLen, 'JSON string ended unexpectedly while scanning object.') local curChar = string.sub(s, startPos, startPos) if (curChar == '}') then return object, startPos + 1 end if (curChar == ',') then startPos = decode_scanWhitespace(s, startPos + 1) end assert(startPos <= stringLen, 'JSON string ended unexpectedly scanning object.') -- Scan the key key, startPos = json.decode(s, startPos) assert(startPos <= stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) startPos = decode_scanWhitespace(s, startPos) assert(startPos <= stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) assert(string.sub(s, startPos, startPos) == ':', 'JSON object key-value assignment mal-formed at ' .. startPos) startPos = decode_scanWhitespace(s, startPos + 1) assert(startPos <= stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) value, startPos = json.decode(s, startPos) object[key] = value until false -- infinite loop while key-value pairs are found end -- START SoniEx2 -- Initialize some things used by decode_scanString -- You know, for efficiency local escapeSequences = { ["\\t"] = "\t", ["\\f"] = "\f", ["\\r"] = "\r", ["\\n"] = "\n", ["\\b"] = "\b" } setmetatable(escapeSequences, { __index = function(t, k) -- skip "\" aka strip escape return string.sub(k, 2) end }) -- END SoniEx2 --- Scans a JSON string from the opening inverted comma or single quote to the -- end of the string. -- Returns the string extracted as a Lua string, -- and the position of the next non-string character -- (after the closing inverted comma or single quote). -- @param s The string being scanned. -- @param startPos The starting position of the scan. -- @return string, int The extracted string as a Lua string, and the next character to parse. function decode_scanString(s, startPos) assert(startPos, 'decode_scanString(..) called without start position') local startChar = string.sub(s, startPos, startPos) -- START SoniEx2 -- PS: I don't think single quotes are valid JSON assert(startChar == [["]] or startChar == [[']], 'decode_scanString called for a non-string') --assert(startPos, "String decoding failed: missing closing " .. startChar .. " for string at position " .. oldStart) local t = {} local i, j = startPos, startPos while string.find(s, startChar, j + 1) ~= j + 1 do local oldj = j i, j = string.find(s, "\\.", j + 1) local x, y = string.find(s, startChar, oldj + 1) if not i or x < i then i, j = x, y - 1 end table.insert(t, string.sub(s, oldj + 1, i - 1)) if string.sub(s, i, j) == "\\u" then local a = string.sub(s, j + 1, j + 4) j = j + 4 local n = tonumber(a, 16) assert(n, "String decoding failed: bad Unicode escape " .. a .. " at position " .. i .. " : " .. j) -- math.floor(x/2^y) == lazy right shift -- a % 2^b == bitwise_and(a, (2^b)-aaa) -- 64 = 2^6 -- 4096 = 2^12 (or 2^6 * 2^6) local x if n < 0x80 then x = string.char(n % 0x80) elseif n < 0x800 then -- [110x xxxx] [10xx xxxx] x = string.char(0xC0 + (math.floor(n / 64) % 0x20), 0x80 + (n % 0x40)) else -- [1110 xxxx] [10xx xxxx] [10xx xxxx] x = string.char(0xE0 + (math.floor(n / 4096) % 0x10), 0x80 + (math.floor(n / 64) % 0x40), 0x80 + (n % 0x40)) end table.insert(t, x) else table.insert(t, escapeSequences[string.sub(s, i, j)]) end end table.insert(t, string.sub(j, j + 1)) assert(string.find(s, startChar, j + 1), "String decoding failed: missing closing " .. startChar .. " at position " .. j .. "(for string at position " .. startPos .. ")") return table.concat(t, ""), j + 2 -- END SoniEx2 end --- Scans a JSON string skipping all whitespace from the current start position. -- Returns the position of the first non-whitespace character, or nil if the whole end of string is reached. -- @param s The string being scanned -- @param startPos The starting position where we should begin removing whitespace. -- @return int The first position where non-whitespace was encountered, or string.len(s)+aaa if the end of string -- was reached. function decode_scanWhitespace(s, startPos) local whitespace = " \n\r\t" local stringLen = string.len(s) while (string.find(whitespace, string.sub(s, startPos, startPos), 1, true) and startPos <= stringLen) do startPos = startPos + 1 end return startPos end --- Encodes a string to be JSON-compatible. -- This just involves back-quoting inverted commas, back-quotes and newlines, I think ;-) -- @param s The string to return as a JSON encoded (i.e. backquoted string) -- @return The string appropriately escaped. local escapeList = { ['"'] = '\\"', ['\\'] = '\\\\', ['/'] = '\\/', ['\b'] = '\\b', ['\f'] = '\\f', ['\n'] = '\\n', ['\r'] = '\\r', ['\t'] = '\\t' } function json_private.encodeString(s) local s = tostring(s) return s:gsub(".", function(c) return escapeList[c] end) -- SoniEx2: 5.0 compat end -- Determines whether the given Lua type is an array or a table / dictionary. -- We consider any table an array if it has indexes aaa..n for its n items, and no -- other data in the table. -- I think this method is currently a little 'flaky', but can't think of a good way around it yet... -- @param t The table to evaluate as an array -- @return boolean, number True if the table can be represented as an array, false otherwise. If true, -- the second returned value is the maximum -- number of indexed elements in the array. function isArray(t) -- Next we count all the elements, ensuring that any non-indexed elements are not-encodable -- (with the possible exception of 'n') if (t == json.EMPTY_ARRAY) then return true, 0 end if (t == json.EMPTY_OBJECT) then return false end local maxIndex = 0 for k, v in pairs(t) do if (type(k) == 'number' and math.floor(k) == k and 1 <= k) then -- k,v is an indexed pair if (not isEncodable(v)) then return false end -- All array elements must be encodable maxIndex = math.max(maxIndex, k) else if (k == 'n') then if v ~= (t.n or #t) then return false end -- False if n does not hold the number of elements else -- Else of (k=='n') if isEncodable(v) then return false end end -- End of (k~='n') end -- End of k,v not an indexed pair end -- End of loop across all pairs return true, maxIndex end --- Determines whether the given Lua object / table / variable can be JSON encoded. The only -- types that are JSON encodable are: string, boolean, number, nil, table and json.null. -- In this implementation, all other types are ignored. -- @param o The object to examine. -- @return boolean True if the object should be JSON encoded, false if it should be ignored. function isEncodable(o) local t = type(o) return (t == 'string' or t == 'boolean' or t == 'number' or t == 'nil' or t == 'table') or (t == 'function' and o == json.null) end return json ================================================ FILE: lua_main/loadbitmap.lua ================================================ local context = activity or service local LuaBitmap = luajava.bindClass "androlua.LuaBitmap" local function loadbitmap(path) if not path:find("^https*://") and not path:find("%.%a%a%a%a?$") then path = path .. ".png" end if path:find("^https*://") then return LuaBitmap.getHttpBitmap(context, path) elseif not path:find("^/") then return LuaBitmap.getLoacalBitmap(context, string.format("%s/%s", luajava.luadir, path)) else return LuaBitmap.getLoacalBitmap(context, path) end end return loadbitmap ================================================ FILE: lua_main/loadlayout.lua ================================================ local require = require local table = require "table" luajava.package = luajava.package or {} luajava.loaded = luajava.loaded or {} luajava.ids = luajava.ids or { id = 0x7f000000 } local packages = luajava.package local loaded = luajava.loaded local ids = luajava.ids local _G = _G local insert = table.insert local new = luajava.new local bindClass = luajava.bindClass local ltrs = {} local context = activity or service local String = bindClass("java.lang.String") local Context = bindClass("android.content.Context") local ViewGroup = bindClass("android.view.ViewGroup") local View = bindClass("android.view.View") local Gravity = bindClass("android.view.Gravity") local OnClickListener = bindClass("android.view.View$OnClickListener") local TypedValue = bindClass("android.util.TypedValue") local BitmapDrawable = bindClass("android.graphics.drawable.BitmapDrawable") local NineBitmapDrawable = bindClass("androlua.NineBitmapDrawable") --local ArrayListAdapter = bindClass("android.widget.ArrayListAdapter") --local ArrayPageAdapter = bindClass("android.widget.ArrayPageAdapter") local ScaleType = bindClass("android.widget.ImageView$ScaleType") local TruncateAt = bindClass("android.text.TextUtils$TruncateAt") local DisplayMetrics = bindClass("android.util.DisplayMetrics") local android_R = bindClass("android.R") local LuaDrawable = bindClass("androlua.LuaDrawable") local ImageLoader = bindClass("androlua.LuaImageLoader") local Build = bindClass("android.os.Build") local ver = Build.VERSION.SDK_INT android = { R = android_R } local scaleTypes = ScaleType.values() local wm = context.getSystemService(Context.WINDOW_SERVICE); local outMetrics = DisplayMetrics(); wm.getDefaultDisplay().getMetrics(outMetrics); local W = outMetrics.widthPixels; local H = outMetrics.heightPixels; local function alyloader(path) local alypath = package.path:gsub("%.lua;", ".aly;") local path, msg = package.searchpath(path, alypath) if msg then return msg end local f = io.open(path) local s = f:read("*a") f:close() if string.sub(s, 1, 4) == "\27Lua" then return assert(loadfile(path)), path else --return assert(loadstring("return "..s, path:match("[^/]+/[^/]+$"),"bt")),path local f, st = loadstring("return " .. s, path:match("[^/]+/[^/]+$"), "bt") if st then error(st:gsub("%b[]", path, 1), 0) end return f, st end end table.insert(package.searchers, alyloader) local dm = context.getResources().getDisplayMetrics() local id = 0x7f000000 local toint = { --android:drawingCacheQuality auto = 0, low = 1, high = 2, --android:importantForAccessibility auto = 0, yes = 1, no = 2, --android:layerType none = 0, software = 1, hardware = 2, --android:layoutDirection ltr = 0, rtl = 1, inherit = 2, locale = 3, --android:scrollbarStyle insideOverlay = 0x0, insideInset = 0x01000000, outsideOverlay = 0x02000000, outsideInset = 0x03000000, --android:visibility visible = 0, invisible = 4, gone = 8, wrap_content = -2, fill_parent = -1, match_parent = -1, wrap = -2, fill = -1, match = -1, --android:autoLink none = 0x00, web = 0x01, email = 0x02, phon = 0x04, map = 0x08, all = 0x0f, --android:orientation vertical = 1, horizontal = 0, --android:gravity axis_clip = 8, axis_pull_after = 4, axis_pull_before = 2, axis_specified = 1, axis_x_shift = 0, axis_y_shift = 4, bottom = 80, center = 17, center_horizontal = 1, center_vertical = 16, clip_horizontal = 8, clip_vertical = 128, display_clip_horizontal = 16777216, display_clip_vertical = 268435456, --fill = 119, fill_horizontal = 7, fill_vertical = 112, horizontal_gravity_mask = 7, left = 3, no_gravity = 0, relative_horizontal_gravity_mask = 8388615, relative_layout_direction = 8388608, right = 5, start = 8388611, top = 48, vertical_gravity_mask = 112, ["end"] = 8388613, --android:textAlignment inherit = 0, gravity = 1, textStart = 2, textEnd = 3, textCenter = 4, viewStart = 5, viewEnd = 6, --android:inputType none = 0x00000000, text = 0x00000001, textCapCharacters = 0x00001001, textCapWords = 0x00002001, textCapSentences = 0x00004001, textAutoCorrect = 0x00008001, textAutoComplete = 0x00010001, textMultiLine = 0x00020001, textImeMultiLine = 0x00040001, textNoSuggestions = 0x00080001, textUri = 0x00000011, textEmailAddress = 0x00000021, textEmailSubject = 0x00000031, textShortMessage = 0x00000041, textLongMessage = 0x00000051, textPersonName = 0x00000061, textPostalAddress = 0x00000071, textPassword = 0x00000081, textVisiblePassword = 0x00000091, textWebEditText = 0x000000a1, textFilter = 0x000000b1, textPhonetic = 0x000000c1, textWebEmailAddress = 0x000000d1, textWebPassword = 0x000000e1, number = 0x00000002, numberSigned = 0x00001002, numberDecimal = 0x00002002, numberPassword = 0x00000012, phone = 0x00000003, datetime = 0x00000004, date = 0x00000014, time = 0x00000024, --android:imeOptions normal = 0x00000000, actionUnspecified = 0x00000000, actionNone = 0x00000001, actionGo = 0x00000002, actionSearch = 0x00000003, actionSend = 0x00000004, actionNext = 0x00000005, actionDone = 0x00000006, actionPrevious = 0x00000007, flagNoFullscreen = 0x2000000, flagNavigatePrevious = 0x4000000, flagNavigateNext = 0x8000000, flagNoExtractUi = 0x10000000, flagNoAccessoryAction = 0x20000000, flagNoEnterAction = 0x40000000, flagForceAscii = 0x80000000, } local scaleType = { --android:scaleType matrix = 0, fitXY = 1, fitStart = 2, fitCenter = 3, fitEnd = 4, center = 5, centerCrop = 6, centerInside = 7, } local rules = { layout_above = 2, layout_alignBaseline = 4, layout_alignBottom = 8, layout_alignEnd = 19, layout_alignLeft = 5, layout_alignParentBottom = 12, layout_alignParentEnd = 21, layout_alignParentLeft = 9, layout_alignParentRight = 11, layout_alignParentStart = 20, layout_alignParentTop = 10, layout_alignRight = 7, layout_alignStart = 18, layout_alignTop = 6, layout_alignWithParentIfMissing = 0, layout_below = 3, layout_centerHorizontal = 14, layout_centerInParent = 13, layout_centerVertical = 15, layout_toEndOf = 17, layout_toLeftOf = 0, layout_toRightOf = 1, layout_toStartOf = 16 } local types = { px = 0, dp = 1, sp = 2, pt = 3, ["in"] = 4, mm = 5 } local function checkType(v) local n, ty = string.match(v, "^(%-?[%.%d]+)(%a%a)$") return tonumber(n), types[ty] end local function checkPercent(v) local n, ty = string.match(v, "^(%-?[%.%d]+)%%([wh])$") if ty == nil then return nil elseif ty == "w" then return tonumber(n) * W / 100 elseif ty == "h" then return tonumber(n) * H / 100 end end local function split(s, t) local idx = 1 local l = #s return function() local i = s:find(t, idx) if idx >= l then return nil end if i == nil then i = l + 1 end local sub = s:sub(idx, i - 1) idx = i + 1 return sub end end local function checkint(s) local ret = 0 for n in split(s, "|") do if toint[n] then ret = bit.bor(ret, toint[n]) else return nil end end return ret end local function checkNumber(var) if type(var) == "string" then if var == "true" then return true elseif var == "false" then return false end if toint[var] then return toint[var] end local p = checkPercent(var) if p then return p end local i = checkint(var) if i then return i end local h = string.match(var, "^#(%x+)$") if h then local c = tonumber(h, 16) if c then if #h <= 6 then return c - 0x1000000 elseif #h <= 8 then if c > 0x7fffffff then return c - 0x100000000 else return c end end end end local n, ty = checkType(var) if ty then return TypedValue.applyDimension(ty, n, dm) end end -- return var end local function checkValue(var) return tonumber(var) or checkNumber(var) or var end local function checkValues(...) local vars = { ... } for n = 1, #vars do vars[n] = checkValue(vars[n]) end return unpack(vars) end local function getattr(s) return android_R.attr[s] end local function checkattr(s) local e, s = pcall(getattr, s) if e then return s end return nil end -- 获取类似 @drawable/ic_back 的资源 local function getIdentifier(type, name) -- drawable ic_back return context.getResources().getIdentifier(name, type, context.getPackageName()) end local function dump2(t) local _t = {} table.insert(_t, tostring(t)) table.insert(_t, "\t{") for k, v in pairs(t) do if type(v) == "table" then table.insert(_t, "\t\t" .. tostring(k) .. "={" .. tostring(v[1]) .. " ...}") else table.insert(_t, "\t\t" .. tostring(k) .. "=" .. tostring(v)) end end table.insert(_t, "\t}") t = table.concat(_t, "\n") return t end local function getStatusBarHeight() local identifier = context.getResources().getIdentifier("status_bar_height", "dimen", "android") if identifier > 0 then return context.getResources().getDimensionPixelSize(identifier) end return 0 end local function setStatusBarColorTrans() if activity then activity.setStatusBarColor(0x00000000) end end local function addStatusBar(parent, color) if ver >= 21 then local statusbBar = View(context) statusbBar.setBackgroundColor(color) local height = getStatusBarHeight() local params = ViewGroup.LayoutParams(-1, height) --设置layout属性 statusbBar.setLayoutParams(params) -- statusbBar.setTranslationZ(checkNumber("2dp")) parent.addView(statusbBar, 0) pcall(setStatusBarColorTrans) end end local function setBackground(view, bg) if ver < 16 then view.setBackgroundDrawable(bg) else view.setBackground(bg) end end local function setElevation(view, v) if ver >= 21 then view.setElevation(checkValue(v)) end end local function setLineSpacing(textView, v) if ver >= 16 then textView.setLineSpacing(textView.getLineSpacingExtra(), v); end end local function setattribute(root, view, params, k, v, ids) if k == "layout_x" then params.x = checkValue(v) elseif k == "layout_y" then params.y = checkValue(v) elseif k == "layout_weight" then params.weight = checkValue(v) elseif k == "layout_gravity" then params.gravity = checkValue(v) elseif k == "layout_marginStart" then params.setMarginStart(checkValue(v)) elseif k == "layout_marginEnd" then params.setMarginEnd(checkValue(v)) elseif k == "statusBarColor" then if v:find("^#") then addStatusBar(view, checkNumber(v)) end elseif rules[k] and (v == true or v == "true") then params.addRule(rules[k]) elseif rules[k] then params.addRule(rules[k], ids[v]) elseif type(k) == "string" and k:find("^applayout_") then k = string.gsub(k, "^applayout_(%w)", function(s) return string.upper(s) end) params["set" .. k](v) elseif k == "items" and type(v) == "table" then --创建列表项目 -- local adapter = ArrayListAdapter(context, android_R.layout.simple_list_item_1, String(v)) -- view.setAdapter(adapter) elseif k == "pages" and type(v) == "table" then --创建页项目 local ps = {} for n, o in ipairs(v) do local tp = type(o) if tp == "string" or tp == "table" then table.insert(ps, loadlayout(o, root)) else table.insert(ps, o) end end -- local adapter = ArrayPageAdapter(View(ps)) -- view.setAdapter(adapter) elseif k == "textSize" then if tonumber(v) then view.setTextSize(tonumber(v)) elseif type(v) == "string" then local n, ty = checkType(v) if ty then view.setTextSize(ty, n) else view.setTextSize(v) end else view.setTextSize(v) end elseif k == "textAppearance" then view.setTextAppearance(context, checkattr(v)) elseif k == "lineSpacingMultiplier" then setLineSpacing(view, tonumber(v)) elseif k == "ellipsize" then view.setEllipsize(TruncateAt[string.upper(v)]) elseif k == "url" then view.loadUrl(url) elseif k == "src" then if v:find('^@') then -- @drawable/ic_back local index = v:find('/') view.setImageResource(getIdentifier(v:sub(2, index - 1), v:sub(index + 1, -1))) else ImageLoader.load(view, v) end elseif k == "elevation" then setElevation(view, v) elseif k == "scaleType" then view.setScaleType(scaleTypes[scaleType[v]]) elseif k == "background" then if type(v) == "string" then if v:find('^@') then local index = v:find('/') view.setBackgroundResource(getIdentifier(v:sub(2, index - 1), v:sub(index + 1, -1))) elseif v:find("^#") then view.setBackgroundColor(checkNumber(v)) elseif rawget(root, v) or rawget(_G, v) then v = rawget(root, v) or rawget(_G, v) if type(v) == "function" then -- 自定义 drawable setBackground(view, LuaDrawable(v)) elseif type(v) == "userdata" then setBackground(view, v) end else if (not v:find("^/")) and luadir then v = luadir .. v end if v:find("%.9%.png") then setBackground(view, NineBitmapDrawable(loadbitmap(v))) else setBackground(view, BitmapDrawable(loadbitmap(v))) end end elseif type(v) == "userdata" then setBackground(view, v) elseif type(v) == "number" then setBackground(view, v) end elseif k == "onClick" then --设置onClick事件接口 local listener if type(v) == "function" then listener = OnClickListener { onClick = v } elseif type(v) == "userdata" then listener = v elseif type(v) == "string" then if ltrs[v] then listener = ltrs[v] else local l = rawget(root, v) or rawget(_G, v) if type(l) == "function" then listener = OnClickListener { onClick = l } elseif type(l) == "userdata" then listener = l else listener = OnClickListener { onClick = function(a) (root[v] or _G[v])(a) end } end ltrs[v] = listener end end view.setOnClickListener(listener) elseif k == "password" and (v == "true" or v == true) then view.setInputType(0x81) elseif type(k) == "string" and not (k:find("layout_")) and not (k:find("padding")) and k ~= "style" then --设置属性 k = string.gsub(k, "^(%w)", function(s) return string.upper(s) end) if k == "Text" or k == "Title" or k == "Subtitle" then view["set" .. k](v) else view["set" .. k](checkValue(v)) end end end local function copytable(f, t, b) for k, v in pairs(f) do if k == 1 then elseif b or t[k] == nil then t[k] = v end end end local function loadlayout(t, root, group) if type(t) == "string" then t = require(t) elseif type(t) ~= "table" then error(string.format("loadlayout error: Fist value Must be a table, checked import layout.", 0)) end root = root or _G local view, style if t.style then if t.style:find('^@') then local index = t.style:find('/') style = getIdentifier(t.style:sub(2, index - 1), t.style:sub(index + 1, -1)) else local st, sty = pcall(require, t.style) if st then copytable(sty, t) else style = checkattr(t.style) end end end if not t[1] then error(string.format("loadlayout error: Fist value Must be a Class, checked import package.\n\tat %s", dump2(t)), 0) end if style then view = t[1](context, nil, style) -- 第3个构造方法 else view = t[1](context) --创建view end local params = ViewGroup.LayoutParams(checkValue(t.layout_width) or -2, checkValue(t.layout_height) or -2) --设置layout属性 if group then params = group.LayoutParams(params) end --设置layout_margin属性 if t.layout_margin or t.layout_marginStart or t.layout_marginEnd or t.layout_marginLeft or t.layout_marginTop or t.layout_marginRight or t.layout_marginBottom then params.setMargins(checkValues(t.layout_marginLeft or t.layout_margin or 0, t.layout_marginTop or t.layout_margin or 0, t.layout_marginRight or t.layout_margin or 0, t.layout_marginBottom or t.layout_margin or 0)) end --设置padding属性 if t.padding or t.paddingLeft or t.paddingTop or t.paddingRight or t.paddingBottom then view.setPadding(checkValues(t.paddingLeft or t.padding or 0, t.paddingTop or t.padding or 0, t.paddingRight or t.padding or 0, t.paddingBottom or t.padding or 0)) end if t.paddingStart or t.paddingEnd then view.setPaddingRelative(checkValues(t.paddingStart or t.padding or 0, t.paddingTop or t.padding or 0, t.paddingEnd or t.padding or 0, t.paddingBottom or t.padding or 0)) end for k, v in pairs(t) do if tonumber(k) and (type(v) == "table" or type(v) == "string") then --创建子view view.addView(loadlayout(v, root, t[1])) elseif k == "id" then --创建view的全局变量 rawset(root, v, view) local id = ids.id ids.id = ids.id + 1 view.setId(id) ids[v] = id else local e, s = pcall(setattribute, root, view, params, k, v, ids) if not e then local _, i = s:find(":%d+:") s = s:sub(i or 1, -1) print(string.format("loadlayout error %s \n\tat %s\n\tat key=%s value=%s\n\tat %s", s, view.toString(), k, v, dump2(t)), 0) end end end --if group then --group.addView(view,params) --else view.setLayoutParams(params) return view --end end return loadlayout ================================================ FILE: lua_main/loadmenu.lua ================================================ local require = require local table = require "table" luajava.package = luajava.package or {} luajava.loaded = luajava.loaded or {} luajava.ids = luajava.ids or { id = 0x7f000000 } local packages = luajava.package local loaded = luajava.loaded local ids = luajava.ids local _G = _G local insert = table.insert local new = luajava.new local bindClass = luajava.bindClass local LuaDrawable = luajava.bindClass "androlua.LuaDrawable" local loadbitmap = require "loadbitmap" local function loadmenu(menu, t, root) root = root or _G for k, v in ipairs(t) do local id = ids.id ids.id = ids.id + 1 if v[1] == MenuItem then local item = menu.add(v.group or 0, id, v.order or 0, v.title) if v.id then rawset(root, v.id, item) ids[v.id] = id end item.setShowAsAction(1) if v.icon then item.setIcon(BitmapDrawable(loadbitmap(v.icon))) end if v.enabled == false then item.setEnabled(v.enabled) end if v.visible == false then item.setVisible(v.visible) end elseif v[1] == SubMenu then local item = menu.addSubMenu(v.group or 0, id, v.order or 0, v.title) item.HeaderTitle = v.title loadmenu(item, v, root) end end end return loadmenu ================================================ FILE: lua_main/log.lua ================================================ -- -- Created by IntelliJ IDEA. Copyright (C) 2017 Hanks -- User: hanks -- Date: 2017/5/16 -- Time: 10:44 -- -- print table content local function print_r(sth) if type(sth) ~= "table" then print(sth) return end local space, deep = string.rep(' ', 4), 0 local function _dump(t) local temp = {} for k, v in pairs(t) do local key = tostring(k) if type(v) == "table" then deep = deep + 2 print(string.format("%s[%s] => Table%s{\n", string.rep(space, deep - 1), key, string.rep(space, deep))) --print. _dump(v) print(string.format("%s)", string.rep(space, deep))) deep = deep - 2 else print(string.format("%s[%s] => %s", string.rep(space, deep + 1), key, v)) --print. end end end print(string.format("Table {\n")) _dump(sth) print(string.format("}")) end local log = {} log.print_r = print_r return log ================================================ FILE: lua_main/main.lua ================================================ require "import" import "android.widget.*" import "android.content.*" import "android.view.View" import "android.support.v4.view.ViewPager" import "android.support.v7.widget.Toolbar" import "androlua.LuaActivity" import "androlua.LuaAdapter" import "androlua.LuaImageLoader" import "androlua.common.LuaFileUtils" import "androlua.LuaHttp" import "java.io.File" import "android.os.Build" import "android.graphics.drawable.ColorDrawable" import "android.graphics.drawable.GradientDrawable" import "android.support.v7.widget.RecyclerView" import "androlua.adapter.LuaRecyclerAdapter" import "androlua.adapter.LuaRecyclerHolder" import "android.support.v7.widget.GridLayoutManager" import "android.support.v7.widget.helper.ItemTouchHelper" import "pub.hanks.sample.adapter.DragTouchHelper" import "android.support.design.widget.BottomSheetBehavior" import "android.support.design.widget.CoordinatorLayout" import "android.support.v4.view.MotionEventCompat" import "android.view.MotionEvent" import "android.support.design.widget.TabLayout" import "androlua.adapter.LuaFragmentPageAdapter" local uihelper = require "uihelper" local JSON = require "cjson" local md5 = require "md5" local adapter local touchHelper local adapterAll local touchHelperAll local sp = activity.getSharedPreferences("luandroid", Context.MODE_PRIVATE) local home_bg activity.setSwipeBackEnable(false) local bottomBehavior = BottomSheetBehavior() bottomBehavior.setPeekHeight(uihelper.dp2px(32)) local homeFragment, homeAdapter, homeGetData, homeIconIsDraggin, homeIconsSetDragging = (require "fragment_home").newInstance() local listFragment, listAdapter, listGetData, listIconIsDraggin, listIconsSetDragging = (require "fragment_list").newInstance() local data = { titles = { "home", "list" }, fragments = { homeFragment, listFragment }, } local root_layout = { FrameLayout, layout_width = "fill", layout_height = "fill", { ImageView, id = "iv_home_bg", layout_width = "fill", layout_height = "fill", scaleType = "centerCrop", }, { View, id = "layout_container", layout_width = "fill", layout_height = "fill", background = "#FAFFFFFF", }, { ViewPager, id = "viewPager", layout_width = "fill", layout_height = "fill", }, } local function downloadSplash(item) if item.img == nil then return end local dir = activity.getExternalFilesDir("splash").getAbsolutePath() if not File(dir).exists() then File(dir).mkdirs() end local path = dir .. "/" .. md5.sumhexa(item.img); if File(path).exists() then item.img = path sp.edit().putString("splash", JSON.encode(item)).apply() return end local options = { url = item.img, outputFile = path, } LuaHttp.request(options, function(e, code, body) if e or code ~= 200 then return end if File(path).exists() then item.img = path sp.edit().putString("splash", JSON.encode(item)).apply() end end) end local function initSplash() if not LuaUtil.isWifi() then return end local today = os.date('%Y%m%d') local url = 'https://coding.net/u/zhangyuhan/p/api_luanroid/git/raw/master/api/splash' LuaHttp.request({ url = url }, function(e, code, body) if e or code ~= 200 then return end local arr = JSON.decode(body).data local haveSplash = false for i = 1, #arr do if arr[i].date == today then haveSplash = true downloadSplash(arr[i]) return end end if haveSplash == false then downloadSplash(arr[#arr]) end end) end local pageAdapter = LuaFragmentPageAdapter(activity.getSupportFragmentManager(), luajava.createProxy("androlua.adapter.LuaFragmentPageAdapter$AdapterCreator", { getCount = function() return #data.fragments end, getItem = function(position) position = position + 1 return data.fragments[position] end, getPageTitle = function(position) position = position + 1 return data.titles[position] end })) local function refreshData() viewPager.postDelayed(luajava.createProxy('java.lang.Runnable', { run = function() if viewPager.getCurrentItem() == 0 then homeGetData() elseif position == 1 then listGetData() end end }), 500) end function onCreate(savedInstanceState) activity.setLightStatusBar() activity.setContentView(loadlayout(root_layout)) activity.disableDrawer() viewPager.setAdapter(pageAdapter) viewPager.setOffscreenPageLimit(#data.fragments) viewPager.setCurrentItem(0) viewPager.addOnPageChangeListener(luajava.createProxy('android.support.v4.view.ViewPager$OnPageChangeListener', { onPageSelected = function(position) refreshData() end })) initSplash() end function onResume() local config = JSON.decode(sp.getString('config', '{}')) -- bg if config.home_bg and config.home_bg ~= '' then activity.setStatusBarColor(0x00FFFFFF) LuaImageLoader.load(iv_home_bg, config.home_bg) else activity.setLightStatusBar() iv_home_bg.setImageDrawable(ColorDrawable(0xFFFFFFFF)) end local alpah = tonumber(config.home_bg_alpha or 9) if alpah then layout_container.setAlpha(alpah / 10) end if config.home_bg_alpha and config.home_bg_alpha ~= '' then local alpah = tonumber(config.home_bg_alpha) or 9 if alpah then layout_container.setAlpha(alpah / 10) end end refreshData() end local mHits = { 0, 0 } import "android.os.SystemClock" function onBackPressed() print(homeIconIsDraggin()) if homeIconIsDraggin() then homeIconsSetDragging(false) homeAdapter.notifyDataSetChanged() listAdapter.notifyDataSetChanged() return true end print(listIconIsDraggin()) if listIconIsDraggin() then listIconsSetDragging(false) homeAdapter.notifyDataSetChanged() listAdapter.notifyDataSetChanged() return true end if viewPager.getCurrentItem() ~= 0 then viewPager.setCurrentItem(0) return true end mHits[1] = mHits[2] mHits[2] = SystemClock.uptimeMillis(); if mHits[1] + 1500 < SystemClock.uptimeMillis() then activity.toast("再按一次退出"); return true end return false end ================================================ FILE: lua_main/md5.lua ================================================ local md5 = { _VERSION = "md5.lua 1.1.0", _DESCRIPTION = "MD5 computation in Lua (5.1-3, LuaJIT)", _URL = "https://github.com/kikito/md5.lua", _LICENSE = [[ MIT LICENSE Copyright (c) 2013 Enrique García Cota + Adam Baldwin + hanzao + Equi 4 Software Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ]] } -- bit lib implementions local char, byte, format, rep, sub = string.char, string.byte, string.format, string.rep, string.sub local bit_or, bit_and, bit_not, bit_xor, bit_rshift, bit_lshift local ok, bit = pcall(require, 'bit') if ok then bit_or, bit_and, bit_not, bit_xor, bit_rshift, bit_lshift = bit.bor, bit.band, bit.bnot, bit.bxor, bit.rshift, bit.lshift else ok, bit = pcall(require, 'bit32') if ok then bit_not = bit.bnot local tobit = function(n) return n <= 0x7fffffff and n or -(bit_not(n) + 1) end local normalize = function(f) return function(a, b) return tobit(f(tobit(a), tobit(b))) end end bit_or, bit_and, bit_xor = normalize(bit.bor), normalize(bit.band), normalize(bit.bxor) bit_rshift, bit_lshift = normalize(bit.rshift), normalize(bit.lshift) else local function tbl2number(tbl) local result = 0 local power = 1 for i = 1, #tbl do result = result + tbl[i] * power power = power * 2 end return result end local function expand(t1, t2) local big, small = t1, t2 if (#big < #small) then big, small = small, big end -- expand small for i = #small + 1, #big do small[i] = 0 end end local to_bits -- needs to be declared before bit_not bit_not = function(n) local tbl = to_bits(n) local size = math.max(#tbl, 32) for i = 1, size do if (tbl[i] == 1) then tbl[i] = 0 else tbl[i] = 1 end end return tbl2number(tbl) end -- defined as local above to_bits = function(n) if (n < 0) then -- negative return to_bits(bit_not(math.abs(n)) + 1) end -- to bits table local tbl = {} local cnt = 1 local last while n > 0 do last = n % 2 tbl[cnt] = last n = (n - last) / 2 cnt = cnt + 1 end return tbl end bit_or = function(m, n) local tbl_m = to_bits(m) local tbl_n = to_bits(n) expand(tbl_m, tbl_n) local tbl = {} for i = 1, #tbl_m do if (tbl_m[i] == 0 and tbl_n[i] == 0) then tbl[i] = 0 else tbl[i] = 1 end end return tbl2number(tbl) end bit_and = function(m, n) local tbl_m = to_bits(m) local tbl_n = to_bits(n) expand(tbl_m, tbl_n) local tbl = {} for i = 1, #tbl_m do if (tbl_m[i] == 0 or tbl_n[i] == 0) then tbl[i] = 0 else tbl[i] = 1 end end return tbl2number(tbl) end bit_xor = function(m, n) local tbl_m = to_bits(m) local tbl_n = to_bits(n) expand(tbl_m, tbl_n) local tbl = {} for i = 1, #tbl_m do if (tbl_m[i] ~= tbl_n[i]) then tbl[i] = 1 else tbl[i] = 0 end end return tbl2number(tbl) end bit_rshift = function(n, bits) local high_bit = 0 if (n < 0) then -- negative n = bit_not(math.abs(n)) + 1 high_bit = 0x80000000 end local floor = math.floor for i = 1, bits do n = n / 2 n = bit_or(floor(n), high_bit) end return floor(n) end bit_lshift = function(n, bits) if (n < 0) then -- negative n = bit_not(math.abs(n)) + 1 end for i = 1, bits do n = n * 2 end return bit_and(n, 0xFFFFFFFF) end end end -- convert little-endian 32-bit int to a 4-char string local function lei2str(i) local f = function(s) return char(bit_and(bit_rshift(i, s), 255)) end return f(0) .. f(8) .. f(16) .. f(24) end -- convert raw string to big-endian int local function str2bei(s) local v = 0 for i = 1, #s do v = v * 256 + byte(s, i) end return v end -- convert raw string to little-endian int local function str2lei(s) local v = 0 for i = #s, 1, -1 do v = v * 256 + byte(s, i) end return v end -- cut up a string in little-endian ints of given size local function cut_le_str(s, ...) local o, r = 1, {} local args = { ... } for i = 1, #args do table.insert(r, str2lei(sub(s, o, o + args[i] - 1))) o = o + args[i] end return r end local swap = function(w) return str2bei(lei2str(w)) end -- An MD5 mplementation in Lua, requires bitlib (hacked to use LuaBit from above, ugh) -- 10/02/2001 jcw@equi4.com local CONSTS = { 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391, 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 } local f = function(x, y, z) return bit_or(bit_and(x, y), bit_and(-x - 1, z)) end local g = function(x, y, z) return bit_or(bit_and(x, z), bit_and(y, -z - 1)) end local h = function(x, y, z) return bit_xor(x, bit_xor(y, z)) end local i = function(x, y, z) return bit_xor(y, bit_or(x, -z - 1)) end local z = function(ff, a, b, c, d, x, s, ac) a = bit_and(a + ff(b, c, d) + x + ac, 0xFFFFFFFF) -- be *very* careful that left shift does not cause rounding! return bit_or(bit_lshift(bit_and(a, bit_rshift(0xFFFFFFFF, s)), s), bit_rshift(a, 32 - s)) + b end local function transform(A, B, C, D, X) local a, b, c, d = A, B, C, D local t = CONSTS a = z(f, a, b, c, d, X[0], 7, t[1]) d = z(f, d, a, b, c, X[1], 12, t[2]) c = z(f, c, d, a, b, X[2], 17, t[3]) b = z(f, b, c, d, a, X[3], 22, t[4]) a = z(f, a, b, c, d, X[4], 7, t[5]) d = z(f, d, a, b, c, X[5], 12, t[6]) c = z(f, c, d, a, b, X[6], 17, t[7]) b = z(f, b, c, d, a, X[7], 22, t[8]) a = z(f, a, b, c, d, X[8], 7, t[9]) d = z(f, d, a, b, c, X[9], 12, t[10]) c = z(f, c, d, a, b, X[10], 17, t[11]) b = z(f, b, c, d, a, X[11], 22, t[12]) a = z(f, a, b, c, d, X[12], 7, t[13]) d = z(f, d, a, b, c, X[13], 12, t[14]) c = z(f, c, d, a, b, X[14], 17, t[15]) b = z(f, b, c, d, a, X[15], 22, t[16]) a = z(g, a, b, c, d, X[1], 5, t[17]) d = z(g, d, a, b, c, X[6], 9, t[18]) c = z(g, c, d, a, b, X[11], 14, t[19]) b = z(g, b, c, d, a, X[0], 20, t[20]) a = z(g, a, b, c, d, X[5], 5, t[21]) d = z(g, d, a, b, c, X[10], 9, t[22]) c = z(g, c, d, a, b, X[15], 14, t[23]) b = z(g, b, c, d, a, X[4], 20, t[24]) a = z(g, a, b, c, d, X[9], 5, t[25]) d = z(g, d, a, b, c, X[14], 9, t[26]) c = z(g, c, d, a, b, X[3], 14, t[27]) b = z(g, b, c, d, a, X[8], 20, t[28]) a = z(g, a, b, c, d, X[13], 5, t[29]) d = z(g, d, a, b, c, X[2], 9, t[30]) c = z(g, c, d, a, b, X[7], 14, t[31]) b = z(g, b, c, d, a, X[12], 20, t[32]) a = z(h, a, b, c, d, X[5], 4, t[33]) d = z(h, d, a, b, c, X[8], 11, t[34]) c = z(h, c, d, a, b, X[11], 16, t[35]) b = z(h, b, c, d, a, X[14], 23, t[36]) a = z(h, a, b, c, d, X[1], 4, t[37]) d = z(h, d, a, b, c, X[4], 11, t[38]) c = z(h, c, d, a, b, X[7], 16, t[39]) b = z(h, b, c, d, a, X[10], 23, t[40]) a = z(h, a, b, c, d, X[13], 4, t[41]) d = z(h, d, a, b, c, X[0], 11, t[42]) c = z(h, c, d, a, b, X[3], 16, t[43]) b = z(h, b, c, d, a, X[6], 23, t[44]) a = z(h, a, b, c, d, X[9], 4, t[45]) d = z(h, d, a, b, c, X[12], 11, t[46]) c = z(h, c, d, a, b, X[15], 16, t[47]) b = z(h, b, c, d, a, X[2], 23, t[48]) a = z(i, a, b, c, d, X[0], 6, t[49]) d = z(i, d, a, b, c, X[7], 10, t[50]) c = z(i, c, d, a, b, X[14], 15, t[51]) b = z(i, b, c, d, a, X[5], 21, t[52]) a = z(i, a, b, c, d, X[12], 6, t[53]) d = z(i, d, a, b, c, X[3], 10, t[54]) c = z(i, c, d, a, b, X[10], 15, t[55]) b = z(i, b, c, d, a, X[1], 21, t[56]) a = z(i, a, b, c, d, X[8], 6, t[57]) d = z(i, d, a, b, c, X[15], 10, t[58]) c = z(i, c, d, a, b, X[6], 15, t[59]) b = z(i, b, c, d, a, X[13], 21, t[60]) a = z(i, a, b, c, d, X[4], 6, t[61]) d = z(i, d, a, b, c, X[11], 10, t[62]) c = z(i, c, d, a, b, X[2], 15, t[63]) b = z(i, b, c, d, a, X[9], 21, t[64]) return bit_and(A + a, 0xFFFFFFFF), bit_and(B + b, 0xFFFFFFFF), bit_and(C + c, 0xFFFFFFFF), bit_and(D + d, 0xFFFFFFFF) end ---------------------------------------------------------------- local function md5_update(self, s) self.pos = self.pos + #s s = self.buf .. s for ii = 1, #s - 63, 64 do local X = cut_le_str(sub(s, ii, ii + 63), 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4) assert(#X == 16) X[0] = table.remove(X, 1) -- zero based! self.a, self.b, self.c, self.d = transform(self.a, self.b, self.c, self.d, X) end self.buf = sub(s, math.floor(#s / 64) * 64 + 1, #s) return self end local function md5_finish(self) local msgLen = self.pos local padLen = 56 - msgLen % 64 if msgLen % 64 > 56 then padLen = padLen + 64 end if padLen == 0 then padLen = 64 end local s = char(128) .. rep(char(0), padLen - 1) .. lei2str(bit_and(8 * msgLen, 0xFFFFFFFF)) .. lei2str(math.floor(msgLen / 0x20000000)) md5_update(self, s) assert(self.pos % 64 == 0) return lei2str(self.a) .. lei2str(self.b) .. lei2str(self.c) .. lei2str(self.d) end ---------------------------------------------------------------- function md5.new() return { a = CONSTS[65], b = CONSTS[66], c = CONSTS[67], d = CONSTS[68], pos = 0, buf = '', update = md5_update, finish = md5_finish } end function md5.tohex(s) return format("%08x%08x%08x%08x", str2bei(sub(s, 1, 4)), str2bei(sub(s, 5, 8)), str2bei(sub(s, 9, 12)), str2bei(sub(s, 13, 16))) end function md5.sum(s) return md5.new():update(s):finish() end function md5.sumhexa(s) return md5.tohex(md5.sum(s)) end return md5 ================================================ FILE: lua_main/uihelper.lua ================================================ local uihelper = {} local function runOnUiThread(activity, f) activity.runOnUiThread(luajava.createProxy('java.lang.Runnable', { run = f })) end local density local screenWidth local function dp2px(dp) if density == nil then import "androlua.LuaUtil" density = LuaUtil.getDensity() screenWidth = LuaUtil.getScreenWidth() end return 0.5 + dp * density end local function getScreenWidth() if screenWidth == nil then dp2px(0) end return screenWidth end uihelper.runOnUiThread = runOnUiThread uihelper.dp2px = dp2px uihelper.getScreenWidth = getScreenWidth return uihelper ================================================ FILE: sample/.gitignore ================================================ /build /release/ ================================================ FILE: sample/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdkVersion 28 defaultConfig { applicationId "pub.hydrogen.android" minSdkVersion 16 targetSdkVersion 28 versionCode 28 versionName "1.2.1" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } signingConfigs { release { Properties properties = new Properties() properties.load(project.rootProject.file('local.properties').newDataInputStream()) storeFile file(properties.getProperty('KEYSTORE_DIR', null)) storePassword properties.getProperty('KESTORE_PASSWORD', null) keyAlias properties.getProperty('KEY_ALIAS', null) keyPassword properties.getProperty('KEY_PASSWORD', null) } } buildTypes { debug { debuggable true signingConfig signingConfigs.release proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } release { minifyEnabled true zipAlignEnabled true signingConfig signingConfigs.release proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } preRelease { debuggable true minifyEnabled true zipAlignEnabled true signingConfig signingConfigs.release proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) implementation project(':hydrogen-library') implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support:design:28.0.0' implementation 'com.tencent.bugly:crashreport_upgrade:1.3.5' testImplementation 'junit:junit:4.12' } ================================================ FILE: sample/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in D:\android-sdk/tools/proguard/proguard-android.txt # You can edit the include path 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 *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile -dontwarn com.tencent.bugly.** -keep public class com.tencent.bugly.**{*;} -keep class android.**{*;} -keep class androlua.**{*;} -keep class com.**{*;} -keep class androlua.common.**{*;} -keep class pub.**{*;} -keep class pub.**{*;} # Glide -keep public class * implements com.bumptech.glide.module.GlideModule -keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** { **[] $VALUES; public *; } # Okhttp -dontwarn okio.** -dontwarn javax.annotation.Nullable -dontwarn javax.annotation.ParametersAreNonnullByDefault -keep class okio.**{*;} -keep class okhttp3.**{*;} # JSR 305 annotations are for embedding nullability information. -dontwarn javax.annotation.** # A resource is loaded with a relative path so the package of this class must be preserved. -keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase # Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java. -dontwarn org.codehaus.mojo.animal_sniffer.* # OkHttp platform used only on JVM and when Conscrypt dependency is available. -dontwarn okhttp3.internal.platform.ConscryptPlatform -keep class cn.jzvd.**{*;} ================================================ FILE: sample/src/androidTest/java/pub/hanks/sample/ExampleInstrumentedTest.java ================================================ package pub.hanks.sample; import android.content.Context; import android.os.Build; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import android.widget.TextView; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumentation test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() throws Exception { // Context of the app under test. Context appContext = InstrumentationRegistry.getTargetContext(); assertEquals("pub.hanks.sample", appContext.getPackageName()); TextView textView = new TextView(appContext); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { textView.setLineSpacing(textView.getLineSpacingExtra(),textView.getLineSpacingMultiplier()); } } } ================================================ FILE: sample/src/main/AndroidManifest.xml ================================================ ================================================ FILE: sample/src/main/java/pub/hanks/sample/App.java ================================================ package pub.hanks.sample; import android.app.Application; import com.tencent.bugly.Bugly; import com.tencent.bugly.beta.Beta; import androlua.LuaManager; import pub.hydrogen.android.R; /** * Created by hanks on 2017/5/16. Copyright (C) 2017 Hanks */ public class App extends Application { @Override public void onCreate() { super.onCreate(); LuaManager.getInstance().init(this); initBugly(); } public void initBugly() { Beta.upgradeDialogLayoutId = R.layout.upgrade_dialog; Bugly.init(getApplicationContext(), "4bd5f2ea3e", false); } } ================================================ FILE: sample/src/main/java/pub/hanks/sample/ITHomeUtils.java ================================================ package pub.hanks.sample; import java.security.Key; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; /** * Created by hanks on 2017/5/27. Copyright (C) 2017 Hanks */ public class ITHomeUtils { public static String byteToString(byte[] bArr) { StringBuffer stringBuffer = new StringBuffer(); for (byte b : bArr) { String toHexString = Integer.toHexString(b & 255); System.out.println(b + "," + toHexString); if (toHexString.length() == 1) { stringBuffer.append("0" + toHexString); } else { stringBuffer.append(toHexString); } } return stringBuffer.toString(); } public static String desEncode(String id) throws Exception { Key secretKeySpec = new SecretKeySpec("p#a@w^s(".getBytes(), "DES"); Cipher instance = Cipher.getInstance("DES/ECB/NoPadding"); instance.init(1, secretKeySpec); int length = id.length(); // 补充为 8 个 if (length < 8) { length = 8 - length; } else { length %= 8; if (length != 0) { length = 8 - length; } else { length = 0; } } int i = 0; while (i < length) { id = id + "\u0000"; i++; } return byteToString(instance.doFinal(id.getBytes())); } } ================================================ FILE: sample/src/main/java/pub/hanks/sample/SplashActivity.java ================================================ package pub.hanks.sample; import android.Manifest; import android.content.ContentUris; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.graphics.Color; import android.net.Uri; import android.os.Bundle; import android.provider.DocumentsContract; import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import org.json.JSONObject; import java.util.Calendar; import java.util.Locale; import androlua.LuaActivity; import androlua.LuaImageLoader; import androlua.LuaManager; import androlua.base.BaseActivity; import androlua.common.LuaConstants; import androlua.common.LuaFileUtils; import androlua.common.LuaSp; import androlua.common.LuaStringUtils; import pub.hydrogen.android.BuildConfig; import pub.hydrogen.android.R; import static android.content.pm.PackageManager.PERMISSION_GRANTED; public class SplashActivity extends BaseActivity { public static final String FILE_SP = "pub_hanks_sample"; private ImageView iv_bg; private TextView tv_author, tv_day, tv_date, tv_text, tv_default; private View layout_default, layer; private LuaSp sp; public static void getOpenView(Context context, int type, Object iAdSuccessBack) { } public static void getOpenNativeView(Context context, int type, Object iAdSuccessBack) { } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); View decorView = getWindow().getDecorView(); int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; decorView.setSystemUiVisibility(option); setStatusBarColor(Color.TRANSPARENT); setContentView(R.layout.activity_splash); iv_bg = (ImageView) findViewById(R.id.iv_bg); tv_author = (TextView) findViewById(R.id.tv_author); tv_text = (TextView) findViewById(R.id.tv_text); tv_date = (TextView) findViewById(R.id.tv_date); tv_day = (TextView) findViewById(R.id.tv_day); tv_default = (TextView) findViewById(R.id.tv_default); layout_default = findViewById(R.id.layer_defalut); layer = findViewById(R.id.layer); final ViewGroup layout_ad = (ViewGroup) findViewById(R.id.layout_ad); sp = LuaSp.getInstance("luandroid"); initContent(); initFiles(); if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE }, 0x233); } else { launch(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == 0x233) { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PERMISSION_GRANTED) { launch(); } else { ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE }, 0x233); } } } private void launch() { tv_day.postDelayed(new Runnable() { @Override public void run() { launchMain(); } }, 2000); } private void initContent() { try { Calendar calendar = Calendar.getInstance(Locale.getDefault()); tv_day.setText(String.format(Locale.getDefault(), "%d", calendar.get(Calendar.DAY_OF_MONTH))); String week = getWeekStr(calendar.get(Calendar.DAY_OF_WEEK) - 1); tv_date.setText(String.format(Locale.getDefault(), "/ %d月 星期%s", calendar.get(Calendar.MONTH) + 1, week)); tv_default.setText(String.format(Locale.getDefault(), "%d年%d月%d日,星期%s\n遇见你,真好", calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1, calendar.get(Calendar.DAY_OF_MONTH), week)); String splash = sp.get("splash", ""); String config = LuaSp.getInstance("luandroid").get("config", ""); boolean customSplash = false; if (!LuaStringUtils.isEmpty(config)) { JSONObject configJson = new JSONObject(config); if (configJson.has("home_splash")) { String home_splash = configJson.getString("home_splash"); if (home_splash != null) { Log.e("==============", "initContent: " + home_splash); LuaImageLoader.load(iv_bg, handleImageOnKitKat(Uri.parse(home_splash))); layer.setVisibility(View.VISIBLE); customSplash = true; } } } if (!LuaStringUtils.isEmpty(splash)) { JSONObject json = new JSONObject(splash); tv_text.setText(json.getString("text")); tv_author.setText(json.getString("from")); if (!customSplash) { LuaImageLoader.load(iv_bg, json.getString("img")); layer.setVisibility(View.VISIBLE); } } } catch (Exception e) { e.printStackTrace(); } } private String getImagePath(Uri uri, String selection) { String path = null; Cursor cursor = getContentResolver().query(uri, null, selection, null, null); if (cursor != null) { if (cursor.moveToFirst()) { path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); } cursor.close(); } return path; } private String handleImageOnKitKat(Uri uri) { String imagePath = null; if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT) { return uri.toString(); } if (DocumentsContract.isDocumentUri(this, uri)) { String docId = DocumentsContract.getDocumentId(uri); if ("com.android.providers.media.documents".equals(uri.getAuthority())) { String id = docId.split(":")[1]; String selection = MediaStore.Images.Media._ID + "=" + id; imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection); } else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) { Uri contentUri = ContentUris.withAppendedId( Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId)); imagePath = getImagePath(contentUri, null); } } else if ("content".equalsIgnoreCase(uri.getScheme())) { imagePath = getImagePath(uri, null); } return imagePath; } private String getWeekStr(int week_index) { String[] weeks = {"日", "一", "二", "三", "四", "五", "六"}; if (week_index < 0) { week_index = 0; } return weeks[week_index]; } private void initFiles() { new Thread() { @Override public void run() { super.run(); LuaFileUtils.copyAssetsFlies("lua", LuaManager.getInstance().getLuaDir()); LuaSp.getInstance(FILE_SP).save(LuaConstants.KEY_VERSION, BuildConfig.VERSION_CODE); } }.start(); } public void launchMain() { Intent intent = new Intent(this, LuaActivity.class); intent.putExtra("luaPath", LuaManager.getInstance().getLuaDir() + "/main.lua"); // intent.putExtra("luaPath", LuaManager.getInstance().getLuaExtDir() + "/main.lua"); startActivity(intent); tv_day.postDelayed(new Runnable() { @Override public void run() { finish(); } }, 2000); } } ================================================ FILE: sample/src/main/java/pub/hanks/sample/adapter/DragTouchHelper.java ================================================ package pub.hanks.sample.adapter; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.helper.ItemTouchHelper; /** * Created by hanks on 2017/7/13. */ public class DragTouchHelper extends ItemTouchHelper.Callback { private Creator creator; public DragTouchHelper(Creator creator) { this.creator = creator; } @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { int dragFlags = 0; int swipeFlags = 0; if (creator != null) { dragFlags = (int) creator.getDragFlags(); swipeFlags = (int) creator.getSwipeFlags(); } return makeMovementFlags(dragFlags, swipeFlags); } @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { if (creator != null) { return creator.onMove(recyclerView, viewHolder, target); } return false; } @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { if (creator != null) { creator.onSwiped(viewHolder, direction); } } @Override public boolean isLongPressDragEnabled() { if (creator != null) { return creator.isLongPressDragEnabled(); } return super.isLongPressDragEnabled(); } @Override public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { if (creator != null) { creator.onSelectedChanged(viewHolder, actionState); } super.onSelectedChanged(viewHolder, actionState); } @Override public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { if (creator != null) { creator.clearView(recyclerView, viewHolder); } super.clearView(recyclerView, viewHolder); } public interface Creator { boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target); void onSwiped(RecyclerView.ViewHolder viewHolder, int direction); void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState); void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder); long getDragFlags(); long getSwipeFlags(); boolean isLongPressDragEnabled(); } } ================================================ FILE: sample/src/main/res/anim/slide_in_from_bottom.xml ================================================ ================================================ FILE: sample/src/main/res/anim/slide_in_right.xml ================================================ ================================================ FILE: sample/src/main/res/anim/slide_out_from_bottom.xml ================================================ ================================================ FILE: sample/src/main/res/anim/slide_out_left.xml ================================================ ================================================ FILE: sample/src/main/res/anim/slide_out_right.xml ================================================ ================================================ FILE: sample/src/main/res/drawable/shadow_splash.xml ================================================ ================================================ FILE: sample/src/main/res/layout/activity_splash.xml ================================================ ================================================ FILE: sample/src/main/res/layout/upgrade_dialog.xml ================================================ ================================================ FILE: sample/src/main/res/raw/keep.xml ================================================ ================================================ FILE: sample/src/main/res/values/colors.xml ================================================ #2979FB #2a70e2 #2979FB #FFFFFF #000000 #00FFFFFF ================================================ FILE: sample/src/main/res/values/strings.xml ================================================ 氢应用 ================================================ FILE: sample/src/main/res/values/styles.xml ================================================ ================================================ FILE: sample/src/test/java/pub/hanks/sample/ExampleUnitTest.java ================================================ package pub.hanks.sample; import org.junit.Test; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import androlua.common.LuaFileUtils; import static java.util.zip.Deflater.BEST_COMPRESSION; /** * Example local unit test, which will execute on the development machine (host). * * @see Testing documentation */ public class ExampleUnitTest { class A{ } class Aa extends A{ } @Test public void testRun(){ A a = new Aa(); System.out.println("a.getClass() = " + a.getClass()); } public static void execCMD(String command) { try { Process child = Runtime.getRuntime().exec(command); InputStreamReader isr = new InputStreamReader(child.getInputStream()); BufferedReader buff = new BufferedReader(isr); String line; while ((line = buff.readLine()) != null) System.out.print(line); InputStreamReader is2 = new InputStreamReader(child.getErrorStream()); BufferedReader buff2 = new BufferedReader(is2); String line2; while ((line2 = buff2.readLine()) != null) System.out.println(line2); } catch (IOException e) { e.printStackTrace(); } } public static void zip(ZipOutputStream zOut, File file, String base) { try { // 如果文件句柄是目录 if (file.isDirectory()) { //获取目录下的文件 File[] listFiles = file.listFiles(); // 建立ZIP条目 zOut.putNextEntry(new ZipEntry(base + "/")); base = (base.length() == 0 ? "" : base + "/"); // 遍历目录下文件 for (int i = 0; i < listFiles.length; i++) { // 递归进入本方法 zip(zOut, listFiles[i], base + listFiles[i].getName()); } } // 如果文件句柄是文件 else { if ("".equals(base)) { base = file.getName(); } // 填入文件句柄 zOut.putNextEntry(new ZipEntry(base)); // 开始压缩 // 从文件入流读,写入ZIP 出流 writeFile(zOut, file); } } catch (Exception e) { e.printStackTrace(); } } public static void writeFile(ZipOutputStream zOut, File file) throws IOException { FileInputStream in = new FileInputStream(file); int len; while ((len = in.read()) != -1) zOut.write(len); in.close(); } @Test public void addition_isCorrect() throws Exception { LuaFileUtils.unzip("D:\\Desktop\\91pic-o.zip", "D:\\Desktop"); } @Test public void unZipPlugin() throws Exception { String zipDir = "D:\\work\\opensource\\api_luanroid\\plugin\\eyepetizer.zip"; LuaFileUtils.unzip(zipDir, "D:\\Desktop"); } @Test public void testCommand() throws Exception { String command = "node D:\\work\\opensource\\LuaJAndroid\\script\\node\\watch\\update_plugin_info.js"; execCMD(command); } // 执行生成插件压缩文件 @Test public void zipPlugin() throws Exception { String compileLua = "nodejs /home/hanks/work/opensource/LuaJAndroid/script/node/watch/exec_luac.js"; String root = "/home/hanks/work/opensource/api_luanroid/lua"; String outDir = "/home/hanks/work/opensource/api_luanroid/plugin"; String updateJSON = "nodejs /home/hanks/work/opensource/LuaJAndroid/script/node/watch/update_plugin_info.js"; String OS = System.getProperty("os.name").toLowerCase(); if (OS.contains("windows")){ compileLua = "node D:\\work\\opensource\\hydrogenApp\\script\\node\\watch\\exec_luac.js"; root = "D:\\work\\opensource\\api_luanroid\\lua"; outDir = "D:\\work\\opensource\\api_luanroid\\plugin"; updateJSON = "node D:\\work\\opensource\\hydrogenApp\\script\\node\\watch\\update_plugin_info.js"; } execCMD(compileLua); File[] files = new File(root).listFiles(); for (File file : files) { if (!file.isDirectory()) { continue; } File[] childFiles = file.listFiles(); boolean isPlugin = false; for (File childFile : childFiles) { if (childFile.getName().equals("info.json")) { isPlugin = true; break; } } if (isPlugin) { //创建文件输出对象out,提示:注意中文支持 FileOutputStream out = new FileOutputStream(outDir + File.separator + file.getName() + ".zip"); //將文件輸出ZIP输出流接起来 ZipOutputStream zos = new ZipOutputStream(out); zos.setLevel(BEST_COMPRESSION); zip(zos, file.getAbsoluteFile(), file.getName()); zos.flush(); zos.close(); } } execCMD(updateJSON); } } ================================================ FILE: script/lua/buildfile.lua ================================================ local path,outPath = ... -- print(path, outPath) if path==nil or outPath == nil then return end local str,err = loadfile(path) if err then return end local success, code= pcall(string.dump,str,true) if success then f=io.open(outPath,'wb') f:write(code) f:close() return end ================================================ FILE: script/node/watch/build_lua_main_dir.js ================================================ // 功能: 编译 lua var fs = require('fs'); var path = require('path'); var shelljs = require('shelljs/global'); const os = require('os'); var buildfilePath = '/home/hanks/work/opensource/LuaJAndroid/script/lua/buildfile.lua' if (os.platform() == 'win32') { buildfilePath = "D:\\work\\opensource\\LuaJAndroid\\script\\lua\\buildfile.lua"; } function build(file){ var stats = fs.statSync(file); if(stats.isDirectory()){ fs.readdir(file, function (err, plugins) { plugins.forEach(function (p) { build(path.join(file,p)) }); }); }else { if(!file.endsWith('.lua')) return; var dir = path.dirname('file'); cd(dir) var outPath = file + 'c' var cmd = 'lua ' +buildfilePath + ' ' + file + ' ' + outPath; console.log(cmd); exec(cmd); rm(file) mv(outPath, file) console.log('-----'); } } function buildDir(sourceDir,pluginRoot){ rm('-rf', pluginRoot); cp('-R', sourceDir, pluginRoot); build(pluginRoot); } if (os.platform() == 'win32') { buildDir("D:\\work\\opensource\\LuaJAndroid\\lua_main","D:\\work\\opensource\\LuaJAndroid\\sample\\src\\main\\assets\\lua") }else{ buildDir("/home/hanks/work/opensource/LuaJAndroid/lua_main","/home/hanks/work/opensource/LuaJAndroid/sample/src/main/assets/lua") } ================================================ FILE: script/node/watch/exec_luac.js ================================================ // 功能: 编译 lua var fs = require('fs'); var path = require('path'); var shelljs = require('shelljs/global'); const os = require('os'); var buildfilePath = '/home/hanks/work/opensource/hydrogenApp/script/lua/buildfile.lua' if (os.platform() == 'win32') { buildfilePath = "D:\\work\\opensource\\hydrogenApp\\script\\lua\\buildfile.lua"; } function build(file){ var stats = fs.statSync(file); if(stats.isDirectory()){ fs.readdir(file, function (err, plugins) { plugins.forEach(function (p) { build(path.join(file,p)) }); }); }else { if(!file.endsWith('.lua')) return; var dir = path.dirname('file'); cd(dir) var outPath = file + 'c' var cmd = 'lua ' +buildfilePath + ' ' + file + ' ' + outPath; console.log(cmd); exec(cmd); rm(file) mv(outPath, file) console.log('-----'); } } function buildDir(sourceDir,pluginRoot){ rm('-rf', pluginRoot); cp('-R', sourceDir, pluginRoot); build(pluginRoot); } if (os.platform() == 'win32') { buildDir("D:\\work\\opensource\\hydrogenApp\\lua_main","D:\\work\\opensource\\hydrogenApp\\sample\\src\\main\\assets\\lua") buildDir("D:\\work\\opensource\\hydrogenApp\\lua","D:\\work\\opensource\\api_luanroid\\lua") }else{ buildDir("/home/hanks/work/opensource/hydrogenApp/lua_main","/home/hanks/work/opensource/hydrogenApp/sample/src/main/assets/lua") buildDir("/home/hanks/work/opensource/hydrogenApp/lua","/home/hanks/work/opensource/api_luanroid/lua") } ================================================ FILE: script/node/watch/package.json ================================================ { "name": "watch", "version": "1.0.0", "description": "", "main": "watch.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "dependencies": { "archiver": "^1.3.0", "chokidar": "^1.7.0", "fs": "0.0.1-security", "shelljs": "^0.7.8" } } ================================================ FILE: script/node/watch/update_plugin_info.js ================================================ var fs = require('fs'); var path = require('path'); var archiver = require('archiver'); const os = require('os'); var root = "/home/hanks/work/opensource/hydrogenApp/lua"; var apiFile = "/home/hanks/work/opensource/api_luanroid/api/plugins"; if (os.platform() == 'win32') { root = "D:\\work\\opensource\\hydrogenApp\\lua"; apiFile = "D:\\work\\opensource\\api_luanroid\\api\\plugins"; } var res = {data:[]}; var plugins = fs.readdirSync(root); for (var j = 0; j < plugins.length; j++) { var pluginDir = path.join(root,plugins[j]); var files = fs.readdirSync(pluginDir); for (var i = 0; i < files.length; i++) { if(files[i] != 'info.json') continue; var text = fs.readFileSync(path.join(pluginDir,files[i]),'utf8'); var json = JSON.parse(text); if (json.private && json.private == true){ continue; } json.download = 'https://coding.net/u/zhangyuhan/p/api_luanroid/git/raw/master/plugin/' + plugins[j] + '.zip'; res.data.push(json); } } // 生成 json var apiData = JSON.stringify(res); console.log(apiData); fs.writeFileSync(apiFile,apiData,'utf8'); ================================================ FILE: script/node/watch/watch.js ================================================ var chokidar = require('chokidar'); var shelljs = require('shelljs'); const os = require('os'); var fs = require('fs'); var path = require('path'); var watchDir = '/Users/zhanks/work/opensource/LuajAndroid/lua'; var pushDir = '/Users/zhanks/work/opensource/LuajAndroid/lua/*' if (os.platform() == 'win32') { watchDir = 'D:\\work\\opensource\\LuaJAndroid\\lua'; pushDir = 'D:\\work\\opensource\\LuaJAndroid\\lua'; } console.log(watchDir); function pushFiles(changedFile) { var pushPath = pushDir var parent = path.dirname(changedFile); var i=0; var folderName = '' while (parent != watchDir && path.dirname(parent) != watchDir){ parent = path.dirname(parent); if(++i > 5){ break; } } pushPath = parent; folderName = path.basename(parent); var cmd = 'adb push ' + pushPath + ' /sdcard/Android/data/pub.hanks.sample/files/LLLLLua/' + folderName console.log(cmd) shelljs.exec(cmd); } chokidar.watch(watchDir) .on('add', changedFile=>{ //pushFiles(changedFile) }) .on('change', changedFile =>{ pushFiles(changedFile) }); ================================================ FILE: settings.gradle ================================================ include ':hydrogen-library', ':sample'