Jelajahi Sumber

20190426版本更新

roo00 6 tahun lalu
induk
melakukan
5d5e048e83
100 mengubah file dengan 3532 tambahan dan 1858 penghapusan
  1. 63 0
      o2android/CodingStandards.md
  2. 3 109
      o2android/README.md
  3. 3 1
      o2android/app/.gitignore
  4. TEMPAT SAMPAH
      o2android/app/assets/flutter_shared/icudtl.dat
  5. 47 47
      o2android/app/build.gradle
  6. TEMPAT SAMPAH
      o2android/app/libs/flutterpack-release.aar
  7. TEMPAT SAMPAH
      o2android/app/libs/image_picker-release.aar
  8. TEMPAT SAMPAH
      o2android/app/libs/o2_auth_sdk-release.aar
  9. TEMPAT SAMPAH
      o2android/app/libs/path_provider-release.aar
  10. TEMPAT SAMPAH
      o2android/app/libs/shared_preferences-release.aar
  11. 2 2
      o2android/app/proguard-rules.pro
  12. 26 13
      o2android/app/src/main/AndroidManifest.xml
  13. 129 0
      o2android/app/src/main/java/io/flutter/facade/Flutter.java
  14. 40 0
      o2android/app/src/main/java/io/flutter/facade/FlutterFragment.java
  15. 29 0
      o2android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java
  16. 8 5
      o2android/app/src/main/java/jiguang/chat/activity/ChatActivity.java
  17. 12 18
      o2android/app/src/main/java/jiguang/chat/controller/ChatItemController.java
  18. 16 22
      o2android/app/src/main/java/jiguang/chat/filter/EmojiFilter.java
  19. 58 68
      o2android/app/src/main/java/jiguang/chat/filter/XhsFilter.java
  20. 53 0
      o2android/app/src/main/java/jiguang/chat/pickerimage/utils/ScreenUtil.java
  21. 54 60
      o2android/app/src/main/java/jiguang/chat/utils/SimpleCommonUtils.java
  22. 8 9
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/O2App.kt
  23. 3 3
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/base/BasePresenterImpl.kt
  24. 31 21
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/bbs/view/BBSWebViewSubjectActivity.kt
  25. 1 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/calendar/vm/CreateEventViewModel.kt
  26. 3 3
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/view/CMSWebViewActivity.kt
  27. 8 7
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/meeting/apply/MeetingApplyPresenter.kt
  28. 22 9
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/meeting/main/MeetingMainFragment.kt
  29. 29 10
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/meeting/main/MeetingMainFragmentPresenter.kt
  30. 89 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/DownloadAPKFragment.kt
  31. 97 1
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/about/AboutActivity.kt
  32. 1 1
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/ai/O2AIActivity.kt
  33. 1 1
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/bind/BindPhoneActivity.kt
  34. 3 1
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/bind/SecondStepPresenter.kt
  35. 134 11
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/launch/LaunchActivity.kt
  36. 95 407
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/login/LoginActivity.kt
  37. 2 7
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/login/LoginContract.kt
  38. 29 90
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/login/LoginPresenter.kt
  39. 6 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/main/IndexFragment.kt
  40. 75 2
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/main/IndexPortalFragment.kt
  41. 11 10
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/main/SettingsFragment.kt
  42. 19 19
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/StartProcessStepTwoFragment.kt
  43. 73 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/security/AccountSecurityActivity.kt
  44. 4 4
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/skin/SkinManagerActivity.kt
  45. 9 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/PortalWebViewActivity.kt
  46. 227 25
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/TaskWebViewActivity.kt
  47. 6 1
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/TaskWebViewContract.kt
  48. 38 3
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/TaskWebViewPresenter.kt
  49. 2 1
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/component/enums/ApplicationEnum.java
  50. 45 35
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/service/DownloadAPKService.kt
  51. 129 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/flutter/FlutterConnectActivity.kt
  52. 17 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/flutter/FlutterO2Utils.kt
  53. 20 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/vo/TaskWebViewVOS.kt
  54. 13 10
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/AppUpdateUtil.kt
  55. 11 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/biometric/BioConstants.kt
  56. 149 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/biometric/BiometricPromptDialog.kt
  57. 118 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/biometric/BiometryAuthAPI23.kt
  58. 23 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/biometric/BiometryAuthAPI28.kt
  59. 96 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/biometric/BiometryManager.kt
  60. 90 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/biometric/CryptoObjectHelper.kt
  61. 16 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/biometric/IBiometryAuth.kt
  62. 272 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/widgets/BottomSheetMenu.kt
  63. 12 5
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/widgets/NestedProgressWebView.kt
  64. 8 8
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/widgets/dialog/O2AlertDialogBuilder.kt
  65. 5 0
      o2android/app/src/main/res/drawable/shape_bottom_list_button.xml
  66. 5 0
      o2android/app/src/main/res/drawable/shape_bottom_list_menu.xml
  67. 42 1
      o2android/app/src/main/res/layout/activity_account_security.xml
  68. 197 183
      o2android/app/src/main/res/layout/activity_bbs_publish_subject.xml
  69. 57 50
      o2android/app/src/main/res/layout/activity_bbs_web_view_subject.xml
  70. 16 0
      o2android/app/src/main/res/layout/activity_flutter_connect.xml
  71. 86 71
      o2android/app/src/main/res/layout/activity_login.xml
  72. 91 91
      o2android/app/src/main/res/layout/activity_skin_manager.xml
  73. 88 0
      o2android/app/src/main/res/layout/dialog_bitmetric_prompt.xml
  74. 27 0
      o2android/app/src/main/res/layout/fragment_download_progress.xml
  75. 5 3
      o2android/app/src/main/res/layout/fragment_main_settings.xml
  76. 4 4
      o2android/app/src/main/res/layout/fragment_main_todo.xml
  77. 6 1
      o2android/app/src/main/res/layout/fragment_start_process_step_two.xml
  78. 6 4
      o2android/app/src/main/res/layout/item_pop_okr_detail_work_cycle_list.xml
  79. 1 1
      o2android/app/src/main/res/layout/pop_bbs_subject_type.xml
  80. TEMPAT SAMPAH
      o2android/app/src/main/res/mipmap-xhdpi/app_mind_map.png
  81. TEMPAT SAMPAH
      o2android/app/src/main/res/mipmap-xhdpi/icon_down_arrow.png
  82. TEMPAT SAMPAH
      o2android/app/src/main/res/mipmap-xhdpi/zhiwen.png
  83. 1 0
      o2android/app/src/main/res/values/colors.xml
  84. 18 3
      o2android/app/src/main/res/values/strings.xml
  85. 1 1
      o2android/build.gradle
  86. 2 2
      o2android/gradle.properties
  87. 1 1
      o2android/settings.gradle
  88. 1 0
      o2android/test.txt
  89. 190 393
      o2ios/O2Platform.xcodeproj/project.pbxproj
  90. 6 0
      o2ios/O2Platform/Assets.xcassets/demo_alert_view/Contents.json
  91. 22 0
      o2ios/O2Platform/Assets.xcassets/demo_alert_view/icon_off_white2.imageset/Contents.json
  92. TEMPAT SAMPAH
      o2ios/O2Platform/Assets.xcassets/demo_alert_view/icon_off_white2.imageset/icon_off_white2@2x.png
  93. TEMPAT SAMPAH
      o2ios/O2Platform/Assets.xcassets/demo_alert_view/icon_off_white2.imageset/icon_off_white2@3x.png
  94. 22 0
      o2ios/O2Platform/Assets.xcassets/demo_alert_view/pic_czsm.imageset/Contents.json
  95. TEMPAT SAMPAH
      o2ios/O2Platform/Assets.xcassets/demo_alert_view/pic_czsm.imageset/pic_czsm@2x.png
  96. TEMPAT SAMPAH
      o2ios/O2Platform/Assets.xcassets/demo_alert_view/pic_czsm.imageset/pic_czsm@3x.png
  97. 22 0
      o2ios/O2Platform/Assets.xcassets/demo_alert_view/pic_tou.imageset/Contents.json
  98. TEMPAT SAMPAH
      o2ios/O2Platform/Assets.xcassets/demo_alert_view/pic_tou.imageset/pic_tou@2x.png
  99. TEMPAT SAMPAH
      o2ios/O2Platform/Assets.xcassets/demo_alert_view/pic_tou.imageset/pic_tou@3x.png
  100. 22 0
      o2ios/O2Platform/Assets.xcassets/demo_alert_view/pic_wenzi_bj.imageset/Contents.json

+ 63 - 0
o2android/CodingStandards.md

@@ -0,0 +1,63 @@
+##规范性、单一性、简洁性 3个基础原则
+
+###命名:
+    ID: 控件_模块_功能
+        控件:
+            TextView tv
+            EditText edit
+            ListView list
+            RecyclerView rv
+            GridView grid
+            LinearLayout linear
+            RelativeLayout relative
+            ScrollView scroll
+            等。。
+        范围:当前使用的activity、fragment或者业务模块的名称
+        功能:当前id所在的控件的功能
+        例子:edit_login_password ,表示登录界面的密码EditText输入框
+
+
+    文件:
+        java:驼峰命名 模块+功能+类型    如: BBSPublishActivity.java
+        xml:类型_模块_功能   如:fragment_bbs_publish.xml
+
+
+
+###存储:
+    所有文件都存储在Constants.BASE_FILE_PATH (ZONE_XBPM),里面按功能新建目录存储各种文件
+
+####测试
+这个是测试的文字
+另外一段呢。。。。
+
+这是才另外一段
+
+
+###项目结构:
+    项目采用MVP模式
+
+* api——服务端相关的代码
+* app——将界面层按照模块分配包
+   * 模块
+     * view
+     * presenter
+   * ...
+* config——Application、Activity、Fragment、Presenter等的顶级父类,常量表等
+* model——数据层,按照模块分配包
+    * vo——前端列表使用的对象
+    * persistence——持久化存储对象
+    * bo
+        * api
+        * ...
+* utils——工具集合
+* widgets——各个可复用View集合
+
+
+###主题配置
+
+
+
+
+
+
+

+ 3 - 109
o2android/README.md

@@ -1,112 +1,6 @@
-#O2OA Android
+#O2办公平台(O2OA)
 
 
-O2平台Android端应用。
+[O2办公平台(O2OA)](https://www.pgyer.com/ZhiHe_android)是一个可以自定义的私有化高效云工作平台,私有安全,创意无限
 
-[![Build Status](https://travis-ci.com/huqi1980/o2oa_client_web.svg?branch=master)](https://travis-ci.org/o2oa/o2oa)
-[![AGPL](https://img.shields.io/badge/license-AGPL-blue.svg)](https://github.com/o2oa/o2oa)
-[![code-size](https://img.shields.io/github/languages/code-size/o2oa/o2oa.svg)](https://github.com/o2oa/o2oa)
-[![last-commit](https://img.shields.io/github/last-commit/o2oa/o2oa.svg)](https://github.com/o2oa/o2oa)
----
-
-## 简介
-
-O2平台Android客户端,最低支持Android版本4.4 [**Android KitKat**]。
-
-## 导入编译
-
-请使用最新版本的`Android Studio`进行导入编译,编译的Android SDK版本是 26 [**Android O**] 。
-
-#### SDK环境安装
-
-安装Android Studio完成后,打开设置里面的SDK Manager工具。
-
-![](http://img.muliba.net/post/20190106112546.png)
-
-选择Android 8.0 ,安装SDK。然后选择SDK Tools 选项卡,
-
-![](http://img.muliba.net/post/20190106112529.png)
-
-勾选右下角的Show Package Details,然后选择Android SDK Build-Tools 下面的27.0.3版本进行安装。
-
-#### 应用配置信息修改
-
-导入项目后会生成 `local.properties` ,需要在这个文件中添加如下内容:
-
-```properties
-# 打包证书相关信息
-signingConfig.keyAlias=别名
-signingConfig.keyPassword=密码
-signingConfig.storeFilePath=证书所在路径
-signingConfig.storePassword=证书密码
-
-# 下面是一些第三方SDK的key
-# DEBUG版本的key
-JPUSH_APPKEY_DEBUG=极光推送AppKey
-PGY_APP_ID_DEBUG=蒲公英AppId
-BAIDU_APPID_DEBUG=Baidu地图AppId
-BAIDU_SECRET_DEBUG=Baidu地图Secret
-BAIDU_APPKEY_DEBUG=百度地图Appkey
-# RELEASE版本的key
-JPUSH_APPKEY_RELEASE=极光推送AppKey
-PGY_APP_ID_RELEASE=蒲公英AppId
-BAIDU_APPID_RELEASE=Baidu地图AppId
-BAIDU_SECRET_RELEASE=Baidu地图Secret
-BAIDU_APPKEY_RELEASE=百度地图Appkey
-# 腾讯Bugly AppId
-BUGLY_APPID=腾讯Bugly AppId
-
-JM_IM_USER_PASSWORD=极光IM的用户默认密码
-```
-
-
-
-## 替换应用logo图标、应用名称
-
-Logo图标分两块,第一块是App的桌面图标,第二块是App内部看到的一些O2OA的图标。
-
-### App的桌面图标
-
-这个图标需要编译打包的时候打包进去的,在 `./app/src/main/res`目录下的`mipmap`目录下:
-
-|                                                              |                                                    |
-| :----------------------------------------------------------: | :------------------------------------------------: |
-| ![http://img.muliba.net/post/20190105171759.png](http://img.muliba.net/post/20190105171759.png) | ![](http://img.muliba.net/post/20190105171908.png) |
-
-把四个目录中的`logo.png`和`logo_round.png`都替换了。
-
-### App内部的一些O2OA的图标
-
-App内看到的一些O2OA相关的logo图标,可以不编译打包进App,我们服务端可以进行动态配置。用管理员进入我们O2OA的服务端,找到系统设置->移动办公配置->样式配置,就可以修改图标了:
-
-![](http://img.muliba.net/post/20190105172349.png)
-
-
-
-### 应用名称
-
-应用桌面显示的名称也是编译打包前要修改好的,在strings资源文件中修改就行了:
-
-路径:`./app/src/main/res/values/strings.xml`
-
-![](http://img.muliba.net/post/20190105173144.png)
-
-
-
-
-
-
-
-
-
-
-
-
-
-## 官方网站:
-
-官方网站 : [http://www.o2oa.net](http://www.o2oa.net)
-
-oschina项目主页 : [https://www.oschina.net/p/o2oa](https://www.oschina.net/p/o2oa)
-
-下载地址 : [http://www.o2oa.net](http://www.o2oa.net/download.html)
+test

+ 3 - 1
o2android/app/.gitignore

@@ -1,5 +1,7 @@
 /build
-
+.project
+.classpath
+.settings/
 # VsCode
 .vscode/
 

TEMPAT SAMPAH
o2android/app/assets/flutter_shared/icudtl.dat


+ 47 - 47
o2android/app/build.gradle

@@ -10,16 +10,13 @@ ext {
     signingConfigKeyPassword = ""
     signingConfigStoreFilePath = ""
     signingConfigStorePassword = ""
-    jpushAppKeyDebug = ""
-    pgyAppIdDebug = ""
-    baiduAppIdDebug = ""
-    baiduSecretDebug = ""
-    baiduAppKeyDebug = ""
-    jpushAppKeyRelease = ""
-    pgyAppIdRelease = ""
-    baiduAppIdRelease = ""
-    baiduSecretRelease = ""
-    baiduAppKeyRelease = ""
+
+    jpushAppKey = ""
+    pgyAppId = ""
+    baiduSpeechAppId = ""
+    baiduSpeechSecret = ""
+    baiduSpeechAppKey = ""
+    baiduMapAppKey = ""
     jpushIMPassword = ""
     buglyAppId = ""
 }
@@ -34,21 +31,14 @@ def loadProperties() {
     project.signingConfigKeyPassword = properties.getProperty("signingConfig.keyPassword")
     project.signingConfigStoreFilePath = properties.getProperty("signingConfig.storeFilePath")
     project.signingConfigStorePassword = properties.getProperty("signingConfig.storePassword")
-    //debug key
-    //极光推送
-    project.jpushAppKeyDebug = properties.getProperty("JPUSH_APPKEY_DEBUG")
-    //蒲公英
-    project.pgyAppIdDebug = properties.getProperty("PGY_APP_ID_DEBUG")
-    //百度
-    project.baiduAppIdDebug = properties.getProperty("BAIDU_APPID_DEBUG")
-    project.baiduSecretDebug = properties.getProperty("BAIDU_SECRET_DEBUG")
-    project.baiduAppKeyDebug = properties.getProperty("BAIDU_APPKEY_DEBUG")
+
     //release key
-    project.jpushAppKeyRelease = properties.getProperty("JPUSH_APPKEY_RELEASE")
-    project.pgyAppIdRelease = properties.getProperty("PGY_APP_ID_RELEASE")
-    project.baiduAppIdRelease = properties.getProperty("BAIDU_APPID_RELEASE")
-    project.baiduSecretRelease = properties.getProperty("BAIDU_SECRET_RELEASE")
-    project.baiduAppKeyRelease = properties.getProperty("BAIDU_APPKEY_RELEASE")
+    project.jpushAppKey = properties.getProperty("JPUSH_APPKEY")
+    project.pgyAppId = properties.getProperty("PGY_APP_ID")
+    project.baiduSpeechAppId = properties.getProperty("BAIDU_SPEECH_APPID")
+    project.baiduSpeechSecret = properties.getProperty("BAIDU_SPEECH_SECRET")
+    project.baiduSpeechAppKey = properties.getProperty("BAIDU_SPEECH_APPKEY")
+    project.baiduMapAppKey = properties.getProperty("BAIDU_MAP_APPKEY")
 
     project.jpushIMPassword = properties.getProperty("JM_IM_USER_PASSWORD")
 
@@ -63,7 +53,7 @@ loadProperties()
 
 task printVersionName {
     def v = project.property("o2.versionName").toString()
-    println( "${v}" )
+    println("${v}")
 }
 
 
@@ -111,6 +101,11 @@ android {
         vectorDrawables.useSupportLibrary = true
 
     }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+
 
     buildTypes {
         debug {
@@ -118,28 +113,30 @@ android {
             buildConfigField "Boolean", "InnerServer", "false"
             buildConfigField "Boolean", "LOG_ENABLE", "true"
             buildConfigField "Boolean", "LOG_FILE", "true"
-            manifestPlaceholders = [JPUSH_PKGNAME: "net.zoneland.x.bpm.mobile.v1.zoneXBPM",
-                                    JPUSH_APPKEY : project.jpushAppKeyDebug,
-                                    JM_IM_USER_PASSWORD : project.jpushIMPassword,
-                                    PGY_APP_ID   : project.pgyAppIdDebug,
-                                    BAIDU_APPID  : project.baiduAppIdDebug,
-                                    BAIDU_SECRET : project.baiduSecretDebug,
-                                    BAIDU_APPKEY : project.baiduAppKeyDebug,
-                                    BUGLY_APPID  : project.buglyAppId]
+            manifestPlaceholders = [JPUSH_PKGNAME      : defaultConfig.applicationId,
+                                    JPUSH_APPKEY       : project.jpushAppKey,
+                                    JM_IM_USER_PASSWORD: project.jpushIMPassword,
+                                    PGY_APP_ID         : project.pgyAppId,
+                                    BAIDU_SPEECH_APPID : project.baiduSpeechAppId,
+                                    BAIDU_SPEECH_SECRET: project.baiduSpeechSecret,
+                                    BAIDU_SPEECH_APPKEY: project.baiduSpeechAppKey,
+                                    BAIDU_MAP_APPKEY   : project.baiduMapAppKey,
+                                    BUGLY_APPID        : project.buglyAppId]
         }
         release {
             signingConfig signingConfigs.release
             buildConfigField "Boolean", "InnerServer", "false"
             buildConfigField "Boolean", "LOG_ENABLE", "false"
             buildConfigField "Boolean", "LOG_FILE", "true"
-            manifestPlaceholders = [JPUSH_PKGNAME: "net.zoneland.x.bpm.mobile.v1.zoneXBPM",
-                                    JPUSH_APPKEY : project.jpushAppKeyRelease,
-                                    JM_IM_USER_PASSWORD : project.jpushIMPassword,
-                                    PGY_APP_ID   : project.pgyAppIdRelease,
-                                    BAIDU_APPID  : project.baiduAppIdRelease,
-                                    BAIDU_SECRET : project.baiduSecretRelease,
-                                    BAIDU_APPKEY : project.baiduAppKeyRelease,
-                                    BUGLY_APPID  : project.buglyAppId]
+            manifestPlaceholders = [JPUSH_PKGNAME      : defaultConfig.applicationId,
+                                    JPUSH_APPKEY       : project.jpushAppKey,
+                                    JM_IM_USER_PASSWORD: project.jpushIMPassword,
+                                    PGY_APP_ID         : project.pgyAppId,
+                                    BAIDU_SPEECH_APPID : project.baiduSpeechAppId,
+                                    BAIDU_SPEECH_SECRET: project.baiduSpeechSecret,
+                                    BAIDU_SPEECH_APPKEY: project.baiduSpeechAppKey,
+                                    BAIDU_MAP_APPKEY   : project.baiduMapAppKey,
+                                    BUGLY_APPID        : project.buglyAppId]
             zipAlignEnabled true     //Zipalign优化
             minifyEnabled true     //混淆
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
@@ -153,6 +150,7 @@ android {
 
         }
     }
+
     dataBinding {
         enabled true
     }
@@ -160,6 +158,7 @@ android {
         lintOptions {
             abortOnError false
         }
+
     }
 
     lintOptions {
@@ -205,19 +204,21 @@ repositories {
 dependencies {
 
     //    implementation fileTree(include: ['*.jar'], dir: 'libs')
-    implementation files('libs/o2_auth_sdk.jar')
     implementation files('libs/BaiduLBS_Android.jar')
     implementation files('libs/bdasr_V3_20180320_9066860.jar')
     implementation files('libs/com.baidu.tts_2.3.1.20170808_e39ea89.jar')
     implementation files('libs/pgyer_sdk_2.2.2.jar')
     implementation files('libs/zxing.jar')
-    implementation files('libs/picasso-2.5.2.jar')
     implementation files('libs/pinyin4j-2.5.0.jar')
     implementation files('libs/universal-image-loader-1.9.5.jar')
     implementation files('libs/tbs_sdk_thirdapp_v3.2.0.1104_43200.jar')
     implementation(name: 'material-calendarview-fancy-1.1', ext: 'aar')
-    implementation(name: 'MGLicenseManagerSDK-0.3.1', ext: 'aar')
-    implementation(name: 'MGFaceppSDK-0.5.2', ext: 'aar')
+    implementation(name: 'o2_auth_sdk-release', ext: 'aar')
+
+    implementation(name: 'flutterpack-release', ext: 'aar')
+    implementation(name: 'shared_preferences-release', ext: 'aar')
+    implementation(name: 'image_picker-release', ext: 'aar')
+    implementation(name: 'path_provider-release', ext: 'aar')
 
     //kotlin
     implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
@@ -248,6 +249,7 @@ dependencies {
     implementation 'com.facebook.shimmer:shimmer:0.1.0@aar'
     implementation 'com.borax12.materialdaterangepicker:library:1.9'
     implementation 'com.yanzhenjie:recyclerview-swipe:1.1.4'
+    implementation 'com.race604.waveloading:library:1.1.1'
 
     //http
     implementation 'com.squareup.retrofit2:retrofit:2.4.0'
@@ -273,7 +275,6 @@ dependencies {
     implementation 'com.michaelpardo:activeandroid:3.1.0-SNAPSHOT'
     implementation 'com.jakewharton:butterknife:8.4.0'
     kapt 'com.jakewharton:butterknife-compiler:8.4.0'
-    implementation 'com.github.w446108264:AndroidEmoji:1.0.0'
     implementation 'com.github.chrisbanes.photoview:library:1.2.4'
     implementation 'com.facebook.fresco:fresco:0.8.1'
     implementation 'org.greenrobot:eventbus:3.0.0'
@@ -316,4 +317,3 @@ tasks.whenTaskAdded { task ->
         task.enabled = false
     }
 }
-

TEMPAT SAMPAH
o2android/app/libs/flutterpack-release.aar


TEMPAT SAMPAH
o2android/app/libs/image_picker-release.aar


TEMPAT SAMPAH
o2android/app/libs/o2_auth_sdk-release.aar


TEMPAT SAMPAH
o2android/app/libs/path_provider-release.aar


TEMPAT SAMPAH
o2android/app/libs/shared_preferences-release.aar


+ 2 - 2
o2android/app/proguard-rules.pro

@@ -54,8 +54,8 @@
 #如果有引用v4包可以添加下面这行
 -keep public class * extends android.support.v4.app.Fragment
 
-#忽略警告
--ignorewarning
+
+-ignorewarnings
 
 #如果引用了v4或者v7包
 -dontwarn android.support.**

+ 26 - 13
o2android/app/src/main/AndroidManifest.xml

@@ -5,6 +5,7 @@
 
     <!-- baidu需要 -->
     <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+    <uses-permission android:name="android.permission.USE_FINGERPRINT" />
     <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
     <uses-permission android:name="android.permission.GET_TASKS" />
     <!-- 获取网络状态 -->
@@ -45,7 +46,7 @@
     <!-- gps -->
     <uses-feature android:name="android.hardware.location.gps" />
     <!-- android 8.0 安装未知来源apk的权限问题 -->
-    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
+    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
 
     <!-- 获取logcat日志 -->
     <application
@@ -67,6 +68,15 @@
 
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <data
+                    android:scheme="o2oa"
+                    android:host="launch"
+                    />
+            </intent-filter>
         </activity>
         <activity
             android:name=".app.o2.bind.BindPhoneActivity"
@@ -283,9 +293,10 @@
             android:screenOrientation="portrait" />
 
         <!-- ai -->
-        <activity android:name=".app.o2.ai.O2AIActivity"
+        <activity
+            android:name=".app.o2.ai.O2AIActivity"
             android:launchMode="singleTask"
-            android:screenOrientation="portrait"/>
+            android:screenOrientation="portrait" />
 
         <!-- calendar -->
         <activity
@@ -416,7 +427,7 @@
 
         <meta-data
             android:name="com.baidu.lbsapi.API_KEY"
-            android:value="${BAIDU_APPKEY}" />
+            android:value="${BAIDU_MAP_APPKEY}" />
         <!-- baidu yuyin -->
         <service
             android:name="com.baidu.speech.VoiceRecognitionService"
@@ -424,13 +435,13 @@
 
         <meta-data
             android:name="com.baidu.speech.APP_ID"
-            android:value="${BAIDU_APPID}" />
+            android:value="${BAIDU_SPEECH_APPID}" />
         <meta-data
             android:name="com.baidu.speech.API_KEY"
-            android:value="${BAIDU_APPKEY}" />
+            android:value="${BAIDU_SPEECH_APPKEY}" />
         <meta-data
             android:name="com.baidu.speech.SECRET_KEY"
-            android:value="${BAIDU_SECRET}" />
+            android:value="${BAIDU_SPEECH_SECRET}" />
 
         <!-- 删除临时文件任务 -->
         <service
@@ -447,7 +458,7 @@
             android:name=".core.service.DownloadAPKService"
             android:exported="true">
             <intent-filter>
-                <action android:name="net.zoneland.x.bpm.mobile.v1.zoneXBPM.action.UPDATE" />
+                <action android:name="${applicationId}.action.UPDATE" />
             </intent-filter>
         </service>
 
@@ -463,11 +474,12 @@
                 <action android:name="cn.jpush.android.intent.NOTIFICATION_RECEIVED" />
                 <action android:name="cn.jpush.android.intent.NOTIFICATION_OPENED" />
 
-                <category android:name="net.zoneland.x.bpm.mobile.v1.zoneXBPM" />
+                <category android:name="${applicationId}" />
             </intent-filter>
         </receiver>
 
-        <meta-data android:name="JM_IM_USER_PASSWORD"
+        <meta-data
+            android:name="JM_IM_USER_PASSWORD"
             android:value="${JM_IM_USER_PASSWORD}" />
 
         <!-- ```````````````````meta```````````````````` -->
@@ -488,7 +500,7 @@
 
         <provider
             android:name="android.support.v4.content.FileProvider"
-            android:authorities="net.zoneland.x.bpm.mobile.v1.zoneXBPM.fileProvider"
+            android:authorities="${applicationId}.fileProvider"
             android:exported="false"
             android:grantUriPermissions="true">
             <meta-data
@@ -497,11 +509,12 @@
         </provider>
         <provider
             android:name="cn.jpush.android.service.DataProvider"
-            android:authorities="net.zoneland.x.bpm.mobile.v1.zoneXBPM.DataProvider"
+            android:authorities="${applicationId}.DataProvider"
             android:exported="true"
             tools:replace="android:exported, android:authorities" />
 
-        <activity android:name=".app.tbs.FileReaderActivity"></activity>
+        <activity android:name=".app.tbs.FileReaderActivity" />
+        <activity android:name=".flutter.FlutterConnectActivity"></activity>
     </application>
 
 </manifest>

+ 129 - 0
o2android/app/src/main/java/io/flutter/facade/Flutter.java

@@ -0,0 +1,129 @@
+package io.flutter.facade;
+
+import android.app.Activity;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleObserver;
+import android.arch.lifecycle.OnLifecycleEvent;
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+
+import io.flutter.plugin.common.BasicMessageChannel;
+import io.flutter.plugin.common.StringCodec;
+import io.flutter.view.FlutterMain;
+import io.flutter.view.FlutterNativeView;
+import io.flutter.view.FlutterRunArguments;
+import io.flutter.view.FlutterView;
+import io.flutter.plugins.GeneratedPluginRegistrant;
+
+/**
+ * Main entry point for using Flutter in Android applications.
+ *
+ * <p><strong>Warning:</strong> This file is auto-generated by Flutter tooling.
+ * DO NOT EDIT.</p>
+ */
+public final class Flutter {
+  private Flutter() {
+    // to prevent instantiation
+  }
+
+  /**
+   * Initiates the Dart VM. Calling this method at an early point may help decreasing time to first
+   * frame for a subsequently created {@link FlutterView}.
+   *
+   * @param applicationContext the application's {@link Context}
+   */
+  public static void startInitialization(@NonNull Context applicationContext) {
+    FlutterMain.startInitialization(applicationContext);
+  }
+
+  /**
+   * Creates a {@link FlutterFragment} managing a {@link FlutterView}. The optional
+   * initial route string will be made available to the Dart code
+   * (via {@code window.defaultRouteName}) and may be used to determine which widget
+   * should be displayed in the view. The default initialRoute is "/".
+   *
+   * @param initialRoute an initial route {@link String}, or null
+   * @return a {@link FlutterFragment}
+   */
+  @NonNull
+  public static FlutterFragment createFragment(String initialRoute) {
+    final FlutterFragment fragment = new FlutterFragment();
+    final Bundle args = new Bundle();
+    args.putString(FlutterFragment.ARG_ROUTE, initialRoute);
+    fragment.setArguments(args);
+    return fragment;
+  }
+
+  /**
+   * Creates a {@link FlutterView} linked to the specified {@link Activity} and {@link Lifecycle}.
+   * The optional initial route string will be made available to the Dart code (via
+   * {@code window.defaultRouteName}) and may be used to determine which widget should be displayed
+   * in the view. The default initialRoute is "/".
+   *
+   * @param activity an {@link Activity}
+   * @param lifecycle a {@link Lifecycle}
+   * @param initialRoute an initial route {@link String}, or null
+   * @return a {@link FlutterView}
+   */
+  @NonNull
+  public static FlutterView createView(@NonNull final Activity activity, @NonNull final Lifecycle lifecycle, final String initialRoute) {
+    FlutterMain.startInitialization(activity.getApplicationContext());
+    FlutterMain.ensureInitializationComplete(activity.getApplicationContext(), null);
+    final FlutterNativeView nativeView = new FlutterNativeView(activity);
+    final FlutterView flutterView = new FlutterView(activity, null, nativeView) {
+      private final BasicMessageChannel<String> lifecycleMessages = new BasicMessageChannel<>(this, "flutter/lifecycle", StringCodec.INSTANCE);
+      @Override
+      public void onFirstFrame() {
+        super.onFirstFrame();
+        setAlpha(1.0f);
+      }
+
+      @Override
+      public void onPostResume() {
+        // Overriding default behavior to avoid dictating system UI via PlatformPlugin.
+        lifecycleMessages.send("AppLifecycleState.resumed");
+      }
+    };
+    if (initialRoute != null) {
+      flutterView.setInitialRoute(initialRoute);
+    }
+    lifecycle.addObserver(new LifecycleObserver() {
+      @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
+      public void onCreate() {
+        final FlutterRunArguments arguments = new FlutterRunArguments();
+        arguments.bundlePath = FlutterMain.findAppBundlePath(activity.getApplicationContext());
+        arguments.entrypoint = "main";
+        flutterView.runFromBundle(arguments);
+        GeneratedPluginRegistrant.registerWith(flutterView.getPluginRegistry());
+      }
+
+      @OnLifecycleEvent(Lifecycle.Event.ON_START)
+      public void onStart() {
+        flutterView.onStart();
+      }
+
+      @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
+      public void onResume() {
+        flutterView.onPostResume();
+      }
+
+      @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
+      public void onPause() {
+        flutterView.onPause();
+      }
+
+      @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
+      public void onStop() {
+        flutterView.onStop();
+      }
+
+      @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
+      public void onDestroy() {
+        flutterView.destroy();
+      }
+    });
+    flutterView.setAlpha(0.0f);
+    return flutterView;
+  }
+}

+ 40 - 0
o2android/app/src/main/java/io/flutter/facade/FlutterFragment.java

@@ -0,0 +1,40 @@
+package io.flutter.facade;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.Fragment;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import io.flutter.view.FlutterView;
+
+/**
+ * A {@link Fragment} managing a {@link FlutterView}.
+ *
+ * <p><strong>Warning:</strong> This file is auto-generated by Flutter tooling.
+ * DO NOT EDIT.</p>
+ */
+public class FlutterFragment extends Fragment {
+  public static final String ARG_ROUTE = "route";
+  private String mRoute = "/";
+
+  @Override
+  public void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    if (getArguments() != null) {
+      mRoute = getArguments().getString(ARG_ROUTE);
+    }
+  }
+
+  @Override
+  public void onInflate(Context context, AttributeSet attrs, Bundle savedInstanceState) {
+    super.onInflate(context, attrs, savedInstanceState);
+  }
+
+  @Override
+  public FlutterView onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+    return Flutter.createView(getActivity(), getLifecycle(), mRoute);
+  }
+}

+ 29 - 0
o2android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java

@@ -0,0 +1,29 @@
+package io.flutter.plugins;
+
+import io.flutter.plugin.common.PluginRegistry;
+import io.flutter.plugins.imagepicker.ImagePickerPlugin;
+import io.flutter.plugins.pathprovider.PathProviderPlugin;
+import io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin;
+
+/**
+ * 这个是Flutter插件引入类,flutter工程那边如果插件有变动这个类就会需要变动
+ */
+public final class GeneratedPluginRegistrant {
+  public static void registerWith(PluginRegistry registry) {
+    if (alreadyRegisteredWith(registry)) {
+      return;
+    }
+    ImagePickerPlugin.registerWith(registry.registrarFor("io.flutter.plugins.imagepicker.ImagePickerPlugin"));
+    PathProviderPlugin.registerWith(registry.registrarFor("io.flutter.plugins.pathprovider.PathProviderPlugin"));
+    SharedPreferencesPlugin.registerWith(registry.registrarFor("io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin"));
+  }
+
+  private static boolean alreadyRegisteredWith(PluginRegistry registry) {
+    final String key = GeneratedPluginRegistrant.class.getCanonicalName();
+    if (registry.hasPlugin(key)) {
+      return true;
+    }
+    registry.registrarFor(key);
+    return false;
+  }
+}

+ 8 - 5
o2android/app/src/main/java/jiguang/chat/activity/ChatActivity.java

@@ -25,7 +25,6 @@ import android.view.inputmethod.InputMethodManager;
 import android.widget.AbsListView;
 import android.widget.Toast;
 
-import com.sj.emoji.EmojiBean;
 
 import net.muliba.fancyfilepickerlibrary.PicturePicker;
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R;
@@ -453,9 +452,12 @@ public class ChatActivity extends BaseActivity implements FuncLayout.OnFuncKeyBo
                     }
                 } else {
                     String content = null;
-                    if (o instanceof EmojiBean) {
-                        content = ((EmojiBean) o).emoji;
-                    } else if (o instanceof EmoticonEntity) {
+//                    Emoji
+//                    EmojiDisplay.
+//                    if (o instanceof EmojiBean) {
+//                        content = ((EmojiBean) o).emoji;
+//                    } else
+                        if (o instanceof EmoticonEntity) {
                         content = ((EmoticonEntity) o).getContent();
                     }
 
@@ -929,7 +931,8 @@ public class ChatActivity extends BaseActivity implements FuncLayout.OnFuncKeyBo
 
     private Uri getUriFromFile(File file) {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
-            return FileProvider.getUriForFile(this, "net.zoneland.x.bpm.mobile.v1.zoneXBPM.fileProvider", file);
+            String applicationId = getApplicationContext().getPackageName();
+            return FileProvider.getUriForFile(this, applicationId+".fileProvider", file);
         }else  {
             return Uri.fromFile(file);
         }

+ 12 - 18
o2android/app/src/main/java/jiguang/chat/controller/ChatItemController.java

@@ -24,8 +24,8 @@ import android.view.animation.LinearInterpolator;
 import android.widget.ImageView;
 import android.widget.Toast;
 
+import com.bumptech.glide.Glide;
 import com.nostra13.universalimageloader.core.DisplayImageOptions;
-import com.squareup.picasso.Picasso;
 
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R;
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.person.PersonActivity;
@@ -337,15 +337,15 @@ public class ChatItemController {
                 public void onComplete(int status, String desc, File file) {
                     if (status == 0) {
                         ImageView imageView = setPictureScale(jiguang, msg, file.getPath(), holder.picture);
-                        Picasso.with(mContext).load(file).into(imageView);
-//                        Glide.with(mContext).load(file).dontAnimate().into(imageView);
+                        imageView.setTag(null);
+                        Glide.with(mContext).load(file).dontAnimate().into(imageView);
                     }
                 }
             });
         } else {
             ImageView imageView = setPictureScale(jiguang, msg, path, holder.picture);
-            Picasso.with(mContext).load(new File(path)).into(imageView);
-//            Glide.with(mContext).load(new File(path)).dontAnimate().into(imageView);
+            imageView.setTag(null);
+            Glide.with(mContext).load(new File(path)).dontAnimate().into(imageView);
         }
 
         // 接收图片
@@ -386,13 +386,7 @@ public class ChatItemController {
             }
             // 发送图片方,直接加载缩略图
         } else {
-//            try {
-//                setPictureScale(path, holder.picture);
-//                Picasso.with(mContext).load(new File(path)).into(holder.picture);
-//            } catch (NullPointerException e) {
-//                Picasso.with(mContext).load(IdHelper.getDrawable(mContext, "jmui_picture_not_found"))
-//                        .into(holder.picture);
-//            }
+
             //检查状态
             switch (msg.getStatus()) {
                 case created:
@@ -652,8 +646,8 @@ public class ChatItemController {
                 try {
                     File file = new File(path);
                     if (file.exists() && file.isFile()) {
-                        Picasso.with(mContext).load(file).into(holder.picture);
-//                        Glide.with(mContext).load(file).dontAnimate().into(holder.picture);
+                        holder.picture.setTag(null);
+                        Glide.with(mContext).load(file).dontAnimate().into(holder.picture);
                     }
                 } catch (Exception e) {
                     e.printStackTrace();
@@ -769,11 +763,11 @@ public class ChatItemController {
             String thumbPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + msg.getServerMessageId();
             String path = BitmapDecoder.extractThumbnail(videoPath, thumbPath);
             setPictureScale(null, msg, path, holder.picture);
-            Picasso.with(mContext).load(new File(path)).into(holder.picture);
-//            Glide.with(mContext).load(new File(path)).dontAnimate().into(holder.picture);
+            holder.picture.setTag(null);
+            Glide.with(mContext).load(new File(path)).dontAnimate().into(holder.picture);
         } else {
-            Picasso.with(mContext).load(R.drawable.video_not_found).into(holder.picture);
-//            Glide.with(mContext).load(R.drawable.video_not_found).dontAnimate().into(holder.picture);
+            holder.picture.setTag(null);
+            Glide.with(mContext).load(R.drawable.video_not_found).dontAnimate().into(holder.picture);
         }
 
 //        if (videoPath != null && new File(videoPath).exists()) {

+ 16 - 22
o2android/app/src/main/java/jiguang/chat/filter/EmojiFilter.java

@@ -3,13 +3,7 @@ package jiguang.chat.filter;
 import android.text.Spannable;
 import android.widget.EditText;
 
-import com.sj.emoji.EmojiDisplay;
-import com.sj.emoji.EmojiSpan;
-
-import java.util.regex.Matcher;
-
 import jiguang.chat.utils.keyboard.interfaces.EmoticonFilter;
-import jiguang.chat.utils.keyboard.utils.EmoticonsKeyboardUtils;
 
 
 public class EmojiFilter extends EmoticonFilter {
@@ -18,24 +12,24 @@ public class EmojiFilter extends EmoticonFilter {
 
     @Override
     public void filter(EditText editText, CharSequence text, int start, int lengthBefore, int lengthAfter) {
-        emojiSize = emojiSize == -1 ? EmoticonsKeyboardUtils.getFontHeight(editText) : emojiSize;
-        clearSpan(editText.getText(), start, text.toString().length());
-        Matcher m = EmojiDisplay.getMatcher(text.toString().substring(start, text.toString().length()));
-        if (m != null) {
-            while (m.find()) {
-                String emojiHex = Integer.toHexString(Character.codePointAt(m.group(), 0));
-                EmojiDisplay.emojiDisplay(editText.getContext(), editText.getText(), emojiHex, emojiSize, start + m.start(), start + m.end());
-            }
-        }
+//        emojiSize = emojiSize == -1 ? EmoticonsKeyboardUtils.getFontHeight(editText) : emojiSize;
+//        clearSpan(editText.getText(), start, text.toString().length());
+//        Matcher m = EmojiDisplay.getMatcher(text.toString().substring(start, text.toString().length()));
+//        if (m != null) {
+//            while (m.find()) {
+//                String emojiHex = Integer.toHexString(Character.codePointAt(m.group(), 0));
+//                EmojiDisplay.emojiDisplay(editText.getContext(), editText.getText(), emojiHex, emojiSize, start + m.start(), start + m.end());
+//            }
+//        }
     }
 
     private void clearSpan(Spannable spannable, int start, int end) {
-        if (start == end) {
-            return;
-        }
-        EmojiSpan[] oldSpans = spannable.getSpans(start, end, EmojiSpan.class);
-        for (int i = 0; i < oldSpans.length; i++) {
-            spannable.removeSpan(oldSpans[i]);
-        }
+//        if (start == end) {
+//            return;
+//        }
+//        EmojiSpan[] oldSpans = spannable.getSpans(start, end, EmojiSpan.class);
+//        for (int i = 0; i < oldSpans.length; i++) {
+//            spannable.removeSpan(oldSpans[i]);
+//        }
     }
 }

+ 58 - 68
o2android/app/src/main/java/jiguang/chat/filter/XhsFilter.java

@@ -1,21 +1,11 @@
 package jiguang.chat.filter;
 
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.text.Spannable;
-import android.text.TextUtils;
 import android.widget.EditText;
 
-import com.sj.emoji.EmojiDisplayListener;
-import com.sj.emoji.EmojiSpan;
-
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import jiguang.chat.utils.DefXhsEmoticons;
 import jiguang.chat.utils.keyboard.interfaces.EmoticonFilter;
-import jiguang.chat.utils.keyboard.utils.EmoticonsKeyboardUtils;
-import jiguang.chat.utils.keyboard.widget.EmoticonSpan;
 
 
 public class XhsFilter extends EmoticonFilter {
@@ -30,64 +20,64 @@ public class XhsFilter extends EmoticonFilter {
 
     @Override
     public void filter(EditText editText, CharSequence text, int start, int lengthBefore, int lengthAfter) {
-        emoticonSize = emoticonSize == -1 ? EmoticonsKeyboardUtils.getFontHeight(editText) : emoticonSize;
-        clearSpan(editText.getText(), start, text.toString().length());
-        Matcher m = getMatcher(text.toString().substring(start, text.toString().length()));
-        if (m != null) {
-            while (m.find()) {
-                String key = m.group();
-                String icon = DefXhsEmoticons.sXhsEmoticonHashMap.get(key);
-                if (!TextUtils.isEmpty(icon)) {
-                    emoticonDisplay(editText.getContext(), editText.getText(), icon, emoticonSize, start + m.start(), start + m.end());
-                }
-            }
-        }
-    }
-
-    public static Spannable spannableFilter(Context context, Spannable spannable, CharSequence text, int fontSize, EmojiDisplayListener emojiDisplayListener) {
-        Matcher m = getMatcher(text);
-        if (m != null) {
-            while (m.find()) {
-                String key = m.group();
-                String icon = DefXhsEmoticons.sXhsEmoticonHashMap.get(key);
-                if (emojiDisplayListener == null) {
-                    if (!TextUtils.isEmpty(icon)) {
-                        emoticonDisplay(context, spannable, icon, fontSize, m.start(), m.end());
-                    }
-                } else {
-                    emojiDisplayListener.onEmojiDisplay(context, spannable, icon, fontSize, m.start(), m.end());
-                }
-            }
-        }
-        return spannable;
+//        emoticonSize = emoticonSize == -1 ? EmoticonsKeyboardUtils.getFontHeight(editText) : emoticonSize;
+//        clearSpan(editText.getText(), start, text.toString().length());
+//        Matcher m = getMatcher(text.toString().substring(start, text.toString().length()));
+//        if (m != null) {
+//            while (m.find()) {
+//                String key = m.group();
+//                String icon = DefXhsEmoticons.sXhsEmoticonHashMap.get(key);
+//                if (!TextUtils.isEmpty(icon)) {
+//                    emoticonDisplay(editText.getContext(), editText.getText(), icon, emoticonSize, start + m.start(), start + m.end());
+//                }
+//            }
+//        }
     }
 
-    private void clearSpan(Spannable spannable, int start, int end) {
-        if (start == end) {
-            return;
-        }
-        EmoticonSpan[] oldSpans = spannable.getSpans(start, end, EmoticonSpan.class);
-        for (int i = 0; i < oldSpans.length; i++) {
-            spannable.removeSpan(oldSpans[i]);
-        }
-    }
-
-    public static void emoticonDisplay(Context context, Spannable spannable, String emoticonName, int fontSize, int start, int end) {
-        Drawable drawable = getDrawableFromAssets(context, emoticonName);
-        if (drawable != null) {
-            int itemHeight;
-            int itemWidth;
-            if (fontSize == WRAP_DRAWABLE) {
-                itemHeight = drawable.getIntrinsicHeight();
-                itemWidth = drawable.getIntrinsicWidth();
-            } else {
-                itemHeight = fontSize;
-                itemWidth = fontSize;
-            }
-
-            drawable.setBounds(0, 0, itemHeight, itemWidth);
-            EmojiSpan imageSpan = new EmojiSpan(drawable);
-            spannable.setSpan(imageSpan, start, end, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
-        }
-    }
+//    public static Spannable spannableFilter(Context context, Spannable spannable, CharSequence text, int fontSize, EmojiDisplayListener emojiDisplayListener) {
+//        Matcher m = getMatcher(text);
+//        if (m != null) {
+//            while (m.find()) {
+//                String key = m.group();
+//                String icon = DefXhsEmoticons.sXhsEmoticonHashMap.get(key);
+//                if (emojiDisplayListener == null) {
+//                    if (!TextUtils.isEmpty(icon)) {
+//                        emoticonDisplay(context, spannable, icon, fontSize, m.start(), m.end());
+//                    }
+//                } else {
+//                    emojiDisplayListener.onEmojiDisplay(context, spannable, icon, fontSize, m.start(), m.end());
+//                }
+//            }
+//        }
+//        return spannable;
+//    }
+//
+//    private void clearSpan(Spannable spannable, int start, int end) {
+//        if (start == end) {
+//            return;
+//        }
+//        EmoticonSpan[] oldSpans = spannable.getSpans(start, end, EmoticonSpan.class);
+//        for (int i = 0; i < oldSpans.length; i++) {
+//            spannable.removeSpan(oldSpans[i]);
+//        }
+//    }
+//
+//    public static void emoticonDisplay(Context context, Spannable spannable, String emoticonName, int fontSize, int start, int end) {
+//        Drawable drawable = getDrawableFromAssets(context, emoticonName);
+//        if (drawable != null) {
+//            int itemHeight;
+//            int itemWidth;
+//            if (fontSize == WRAP_DRAWABLE) {
+//                itemHeight = drawable.getIntrinsicHeight();
+//                itemWidth = drawable.getIntrinsicWidth();
+//            } else {
+//                itemHeight = fontSize;
+//                itemWidth = fontSize;
+//            }
+//
+//            drawable.setBounds(0, 0, itemHeight, itemWidth);
+//            EmojiSpan imageSpan = new EmojiSpan(drawable);
+//            spannable.setSpan(imageSpan, start, end, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+//        }
+//    }
 }

+ 53 - 0
o2android/app/src/main/java/jiguang/chat/pickerimage/utils/ScreenUtil.java

@@ -4,9 +4,12 @@ import android.content.Context;
 import android.content.res.Resources;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.view.Display;
+import android.view.WindowManager;
 
 
 import java.lang.reflect.Field;
+import java.lang.reflect.Method;
 
 import jiguang.chat.application.JGApplication;
 
@@ -68,6 +71,56 @@ public class ScreenUtil {
         Log.d(TAG, "screenWidth=" + screenWidth + " screenHeight=" + screenHeight + " density=" + density);
     }
 
+
+    //获取屏幕原始尺寸高度,包括虚拟功能键高度
+    public static int getDpi(Context context){
+        int dpi = 0;
+        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        Display display = windowManager.getDefaultDisplay();
+        DisplayMetrics displayMetrics = new DisplayMetrics();
+        @SuppressWarnings("rawtypes")
+        Class c;
+        try {
+            c = Class.forName("android.view.Display");
+            @SuppressWarnings("unchecked")
+            Method method = c.getMethod("getRealMetrics",DisplayMetrics.class);
+            method.invoke(display, displayMetrics);
+            dpi=displayMetrics.heightPixels;
+        }catch(Exception e){
+            e.printStackTrace();
+        }
+        return dpi;
+    }
+
+    /**
+     * 获得屏幕高度
+     *
+     * @param context
+     * @return
+     */
+    public static int getScreenHeight(Context context)
+    {
+        WindowManager wm = (WindowManager) context
+                .getSystemService(Context.WINDOW_SERVICE);
+        DisplayMetrics outMetrics = new DisplayMetrics();
+        wm.getDefaultDisplay().getMetrics(outMetrics);
+        return outMetrics.heightPixels;
+    }
+
+    /**
+     * 获取 虚拟按键的高度
+     * @param context
+     * @return
+     */
+    public static  int getBottomStatusHeight(Context context){
+        int totalHeight = getDpi(context);
+
+        int contentHeight = getScreenHeight(context);
+
+        return totalHeight  - contentHeight;
+    }
+
+
     public static int getDisplayWidth() {
         if (screenWidth == 0) {
             GetInfo(JGApplication.context);

+ 54 - 60
o2android/app/src/main/java/jiguang/chat/utils/SimpleCommonUtils.java

@@ -12,21 +12,14 @@ import android.view.ViewGroup;
 import android.widget.EditText;
 import android.widget.TextView;
 
-import com.sj.emoji.DefEmoticons;
-import com.sj.emoji.EmojiBean;
-import com.sj.emoji.EmojiDisplay;
-
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R;
 
 import java.io.IOException;
 import java.lang.reflect.Constructor;
-import java.util.ArrayList;
-import java.util.Collections;
 
 import jiguang.chat.adapter.BigEmoticonsAdapter;
 import jiguang.chat.adapter.BigEmoticonsAndTitleAdapter;
 import jiguang.chat.adapter.TextEmoticonsAdapter;
-import jiguang.chat.filter.EmojiFilter;
 import jiguang.chat.filter.XhsFilter;
 import jiguang.chat.model.Constants;
 import jiguang.chat.utils.keyboard.adpater.EmoticonsAdapter;
@@ -50,8 +43,8 @@ import jiguang.chat.view.SimpleAppsGridView;
 public class SimpleCommonUtils {
 
     public static void initEmoticonsEditText(EmoticonsEditText etContent) {
-        etContent.addEmoticonFilter(new EmojiFilter());
-        etContent.addEmoticonFilter(new XhsFilter());
+//        etContent.addEmoticonFilter(new EmojiFilter());
+//        etContent.addEmoticonFilter(new XhsFilter());
     }
 
     public static EmoticonClickListener getCommonEmoticonClickListener(final EditText editText) {
@@ -66,9 +59,10 @@ public class SimpleCommonUtils {
                     }
                     if (actionType == Constants.EMOTICON_CLICK_TEXT) {
                         String content = null;
-                        if (o instanceof EmojiBean) {
-                            content = ((EmojiBean) o).emoji;
-                        } else if (o instanceof EmoticonEntity) {
+//                        if (o instanceof EmojiBean) {
+//                            content = ((EmojiBean) o).emoji;
+//                        } else
+                            if (o instanceof EmoticonEntity) {
                             content = ((EmoticonEntity) o).getContent();
                         }
 
@@ -117,43 +111,43 @@ public class SimpleCommonUtils {
      * @param emoticonClickListener
      */
     public static void addEmojiPageSetEntity(PageSetAdapter pageSetAdapter, Context context, final EmoticonClickListener emoticonClickListener) {
-        ArrayList<EmojiBean> emojiArray = new ArrayList<>();
-        Collections.addAll(emojiArray, DefEmoticons.sEmojiArray);
-        EmoticonPageSetEntity emojiPageSetEntity
-                = new EmoticonPageSetEntity.Builder()
-                .setLine(3)
-                .setRow(7)
-                .setEmoticonList(emojiArray)
-                .setIPageViewInstantiateItem(getDefaultEmoticonPageViewInstantiateItem(new EmoticonDisplayListener<Object>() {
-                    @Override
-                    public void onBindView(int position, ViewGroup parent, EmoticonsAdapter.ViewHolder viewHolder, Object object, final boolean isDelBtn) {
-                        final EmojiBean emojiBean = (EmojiBean) object;
-                        if (emojiBean == null && !isDelBtn) {
-                            return;
-                        }
-
-                        viewHolder.ly_root.setBackgroundResource(R.drawable.bg_emoticon);
-
-                        if (isDelBtn) {
-                            viewHolder.iv_emoticon.setImageResource(R.mipmap.icon_del);
-                        } else {
-                            viewHolder.iv_emoticon.setImageResource(emojiBean.icon);
-                        }
-
-                        viewHolder.rootView.setOnClickListener(new View.OnClickListener() {
-                            @Override
-                            public void onClick(View v) {
-                                if (emoticonClickListener != null) {
-                                    emoticonClickListener.onEmoticonClick(emojiBean, Constants.EMOTICON_CLICK_TEXT, isDelBtn);
-                                }
-                            }
-                        });
-                    }
-                }))
-                .setShowDelBtn(EmoticonPageEntity.DelBtnStatus.LAST)
-                .setIconUri(ImageBase.Scheme.DRAWABLE.toUri("icon_emoji"))
-                .build();
-        pageSetAdapter.add(emojiPageSetEntity);
+//        ArrayList<EmojiBean> emojiArray = new ArrayList<>();
+//        Collections.addAll(emojiArray, DefEmoticons.sEmojiArray);
+//        EmoticonPageSetEntity emojiPageSetEntity
+//                = new EmoticonPageSetEntity.Builder()
+//                .setLine(3)
+//                .setRow(7)
+//                .setEmoticonList(emojiArray)
+//                .setIPageViewInstantiateItem(getDefaultEmoticonPageViewInstantiateItem(new EmoticonDisplayListener<Object>() {
+//                    @Override
+//                    public void onBindView(int position, ViewGroup parent, EmoticonsAdapter.ViewHolder viewHolder, Object object, final boolean isDelBtn) {
+//                        final EmojiBean emojiBean = (EmojiBean) object;
+//                        if (emojiBean == null && !isDelBtn) {
+//                            return;
+//                        }
+//
+//                        viewHolder.ly_root.setBackgroundResource(R.drawable.bg_emoticon);
+//
+//                        if (isDelBtn) {
+//                            viewHolder.iv_emoticon.setImageResource(R.mipmap.icon_del);
+//                        } else {
+//                            viewHolder.iv_emoticon.setImageResource(emojiBean.icon);
+//                        }
+//
+//                        viewHolder.rootView.setOnClickListener(new View.OnClickListener() {
+//                            @Override
+//                            public void onClick(View v) {
+//                                if (emoticonClickListener != null) {
+//                                    emoticonClickListener.onEmoticonClick(emojiBean, Constants.EMOTICON_CLICK_TEXT, isDelBtn);
+//                                }
+//                            }
+//                        });
+//                    }
+//                }))
+//                .setShowDelBtn(EmoticonPageEntity.DelBtnStatus.LAST)
+//                .setIconUri(ImageBase.Scheme.DRAWABLE.toUri("icon_emoji"))
+//                .build();
+//        pageSetAdapter.add(emojiPageSetEntity);
     }
 
 
@@ -346,17 +340,17 @@ public class SimpleCommonUtils {
         try {
             SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(content);
 
-            Spannable spannable = EmojiDisplay.spannableFilter(tv_content.getContext(),
-                    spannableStringBuilder,
-                    content,
-                    EmoticonsKeyboardUtils.getFontHeight(tv_content));
-
-            spannable = XhsFilter.spannableFilter(tv_content.getContext(),
-                    spannable,
-                    content,
-                    EmoticonsKeyboardUtils.getFontHeight(tv_content),
-                    null);
-            tv_content.setText(spannable);
+//            Spannable spannable = EmojiDisplay.spannableFilter(tv_content.getContext(),
+//                    spannableStringBuilder,
+//                    content,
+//                    EmoticonsKeyboardUtils.getFontHeight(tv_content));
+
+//            Spannable spannable = XhsFilter.spannableFilter(tv_content.getContext(),
+//                    spannableStringBuilder,
+//                    content,
+//                    EmoticonsKeyboardUtils.getFontHeight(tv_content),
+//                    null);
+            tv_content.setText(content);
         } catch (Exception e) {
             Log.e("SimpleCommonUtils","EmojiDisplay error!!!!!!!!!!!!");
         }

+ 8 - 9
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/O2App.kt

@@ -26,7 +26,6 @@ import net.muliba.changeskin.FancySkinManager
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.APIAddressHelper
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.skin.*
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.LogSingletonService
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
 import java.lang.Exception
 
 
@@ -95,11 +94,11 @@ class O2App : MultiDexApplication() {
         // qb
         QbSdk.initX5Environment(this, object : QbSdk.PreInitCallback{
             override fun onCoreInitFinished() {
-                XLog.info("qb sdk core init finish..........")
+                Log.i("O2app", "qb sdk core init finish..........")
             }
 
             override fun onViewInitFinished(p0: Boolean) {
-                XLog.info("qb sdk init $p0 ..........")
+                Log.i("O2app", "qb sdk init $p0 ..........")
             }
         })
         QbSdk.setDownloadWithoutWifi(true)
@@ -165,7 +164,7 @@ class O2App : MultiDexApplication() {
 
     fun _JMLoginInner(){
         _JMLogin { isSuccess ->
-            XLog.info("登录IM服务器result:$isSuccess *****************************************")
+            Log.i("O2app", "登录IM服务器result:$isSuccess *****************************************")
         }
     }
 
@@ -174,7 +173,7 @@ class O2App : MultiDexApplication() {
      */
     fun _JMLogin(back: (isSuccess: Boolean)->Unit) {
         if (TextUtils.isEmpty(O2SDKManager.instance().cId)) {
-            XLog.error("用户未登录。。。。。。。。。。不能连接到IM服务器")
+            Log.i("O2app", "用户未登录。。。。。。。。。。不能连接到IM服务器")
             return
         }
 
@@ -182,10 +181,10 @@ class O2App : MultiDexApplication() {
             override fun gotResult(responseCode: Int, message: String?) {
                 if (responseCode == 0) {
                     //登录成功
-                    XLog.info("登录极光IM服务器成功!!!!!!!!!!!!")
+                    Log.i("O2app", "登录极光IM服务器成功!!!!!!!!!!!!")
                     back(true)
                 } else {
-                    XLog.error("login JM IM fail, code: $responseCode, message: $message")
+                    Log.i("O2app", "login JM IM fail, code: $responseCode, message: $message")
                     //如果没有注册过 就注册一个
                     when (responseCode) {
                         800004, 800005, 800006, 801003, 898005 -> {
@@ -214,10 +213,10 @@ class O2App : MultiDexApplication() {
             override fun gotResult(responseCode: Int, message: String?) {
                 if (responseCode == 0) {
                     //注册成功
-                    XLog.info("注册极光IM服务器成功!!!!!!!!!!!!开始重新登录。。。。。。。。")
+                    Log.i("O2app", "注册极光IM服务器成功!!!!!!!!!!!!开始重新登录。。。。。。。。")
                     _JMLogin(back)
                 } else {
-                    XLog.error("register JM IM fail, code: $responseCode, message: $message")
+                    Log.i("O2app", "register JM IM fail, code: $responseCode, message: $message")
                     back(false)
                 }
             }

+ 3 - 3
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/base/BasePresenterImpl.kt

@@ -220,9 +220,9 @@ open class BasePresenterImpl<V: BaseView> : BasePresenter<V> {
             RetrofitClient.instance().organizationAssembleCustomService()
         }catch (e:Exception){
             XLog.error("", e)
-            if (context!=null){
-                XToast.toastLong(context, "公共配置服务模块异常,请联系管理员!")
-            }
+//            if (context!=null){
+//                XToast.toastLong(context, "公共配置服务模块异常,请联系管理员!")
+//            }
             null
         }
     }

+ 31 - 21
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/bbs/view/BBSWebViewSubjectActivity.kt

@@ -3,6 +3,7 @@ package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.bbs.view
 
 import android.app.Activity
 import android.content.Intent
+import android.net.http.SslError
 import android.os.Bundle
 import android.text.TextUtils
 import android.view.Gravity
@@ -10,6 +11,7 @@ import android.view.LayoutInflater
 import android.view.View
 import android.view.WindowManager
 import android.webkit.JavascriptInterface
+import android.webkit.SslErrorHandler
 import android.webkit.WebView
 import android.webkit.WebViewClient
 import android.widget.ImageView
@@ -88,9 +90,13 @@ class BBSWebViewSubjectActivity : BaseMVPActivity<BBSWebViewSubjectContract.View
         keyHeight = metric.heightPixels/3*/
         //init webview
         web_view_bbs_web_view_subject_content.addJavascriptInterface(this, "o2bbs")
-        web_view_bbs_web_view_subject_content.setWebViewClient(object : WebViewClient() {
+        web_view_bbs_web_view_subject_content.webViewClient = object : WebViewClient() {
+            override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
+                XLog.error("ssl error, $error")
+                handler?.proceed()
+            }
             override fun shouldOverrideUrlLoading(view: WebView?, url: String): Boolean {
-                XLog.debug("shouldOverrideUrlLoading:" + url)
+                XLog.debug("shouldOverrideUrlLoading:$url")
                 if (ZoneUtil.checkUrlIsInner(url)) {
                     view?.loadUrl(url)
                 } else {
@@ -98,7 +104,7 @@ class BBSWebViewSubjectActivity : BaseMVPActivity<BBSWebViewSubjectContract.View
                 }
                 return true
             }
-        })
+        }
         //发送监听
         button_bbs_subject_reply.setOnClickListener {
             publishReply()
@@ -140,24 +146,28 @@ class BBSWebViewSubjectActivity : BaseMVPActivity<BBSWebViewSubjectContract.View
         XLog.debug("bbs webView attachment : $attachment")
         val canReply  =  attachment.canReply
         val hasAttach = attachment.hasAttach
-        if (!canReply && !hasAttach) {
-            layout_bbs_subject_operation_bar.gone()
-            button_bbs_subject_attach.gone()
-        } else if (canReply && !hasAttach) {
-            button_bbs_subject_attach.setOnClickListener {
-                XToast.toastShort(this,"没有附件")
-            }
-        } else if (!canReply && hasAttach){
+//        if (!canReply && !hasAttach) {
+//            layout_bbs_subject_operation_bar.gone()
+//            button_bbs_subject_attach.gone()
+//        } else if (canReply && !hasAttach) {
+//            button_bbs_subject_attach.setOnClickListener {
+//                XToast.toastShort(this,"没有附件")
+//            }
+//        } else
+        if (!canReply){
             edit_bbs_reply_subject_content.isClickable = false
             button_bbs_subject_reply.isClickable = false
             button_bbs_subject_reply.text = "禁止评论"
         }
 
         if (hasAttach) {
+            button_bbs_subject_attach.visible()
             attachList.clear()
             attachList.addAll(attachment.attachList)
             popupWindow.listener = this
             popupWindow.setOnDismissListener { ZoneUtil.lightOn(this@BBSWebViewSubjectActivity) }
+        }else {
+            button_bbs_subject_attach.gone()
         }
     }
 
@@ -172,11 +182,11 @@ class BBSWebViewSubjectActivity : BaseMVPActivity<BBSWebViewSubjectContract.View
             XToast.toastShort(this, "请填写回复内容")
             return
         }
-        XLog.debug("content:" + content)
+        XLog.debug("content:$content")
         content = formatToHtml(content)
-        XLog.debug("content html:" + content)
+        XLog.debug("content html:$content")
         content += addAttachmentToContent()
-        XLog.debug("content html and image:" + content)
+        XLog.debug("content html and image:$content")
         val form = ReplyFormJson(
                 newReplyId,
                 content,
@@ -258,13 +268,13 @@ class BBSWebViewSubjectActivity : BaseMVPActivity<BBSWebViewSubjectContract.View
     override fun openCompletedFile(id: String?) {
         if (!TextUtils.isEmpty(id)) {
             val file = File(getAttachFileLocalPath(id!!))
-            if (file != null && file.exists()) AndroidUtils.openFileWithDefaultApp(this, file)
+            if (file.exists()) AndroidUtils.openFileWithDefaultApp(this, file)
         }
     }
 
     override fun startDownLoadFile(id: String?) {
         if (!TextUtils.isEmpty(id)){
-            taskMap.put(id!!, doAsync {
+            taskMap[id!!] = doAsync {
                 val filePath = getAttachFileLocalPath(id)
                 val file = File(filePath)
                 var downloadSuccess = false
@@ -303,13 +313,13 @@ class BBSWebViewSubjectActivity : BaseMVPActivity<BBSWebViewSubjectContract.View
                         XToast.toastShort(this@BBSWebViewSubjectActivity, "下载附件失败!")
                     }
                 }
-            })
+            }
         }
     }
 
     private fun getAttachFileLocalPath(id:String) : String{
         var path  = ""
-        attachList.filter { it.id == id }.map { path = FileExtensionHelper.getXBPMBBSAttachFolder()+File.separator+it.fileName }
+        attachList.asSequence().filter { it.id == id }.map { path = FileExtensionHelper.getXBPMBBSAttachFolder()+File.separator+it.fileName }.toList()
         return path
     }
 
@@ -373,14 +383,14 @@ class BBSWebViewSubjectActivity : BaseMVPActivity<BBSWebViewSubjectContract.View
             val deleteIcon = view.findViewById<ImageView>(R.id.image_bbs_subject_image_upload_delete_button)
             deleteIcon.visible()
             val attachmentBaseView = view.findViewById<RelativeLayout>(R.id.relative_bbs_subject_image_upload_grid_top)
-            attachmentBaseView.setOnClickListener { view ->
-                removeImageFromImageViewList(view)
+            attachmentBaseView.setOnClickListener { v ->
+                removeImageFromImageViewList(v)
                 uploadedImageMap.remove(tag)
             }
             val imageBean = uploadingImageMap[tag]
             if (imageBean != null) {
                 imageBean.fileId = fileId
-                uploadedImageMap.put(tag, imageBean)
+                uploadedImageMap[tag] = imageBean
             }
         }
         uploadingImageMap.remove(tag)

+ 1 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/calendar/vm/CreateEventViewModel.kt

@@ -425,6 +425,7 @@ class CreateEventViewModel(app: Application) : BaseO2ViewModel(app) {
             val reName = CalendarOB.remindOptions[reValue]
             if (reName != null) {
                 remindName.value = reName
+                remindValue.value = reValue
             }
         }
     }

+ 3 - 3
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/view/CMSWebViewActivity.kt

@@ -88,7 +88,7 @@ class CMSWebViewActivity : BaseMVPActivity<CMSWebViewContract.View, CMSWebViewCo
     }
 
     override fun loadAttachList(list: List<AttachmentItemVO>) {
-        if (list.size>0) {
+        if (list.isNotEmpty()) {
             fab_cms_document_attach.visible()
             attachList.clear()
             attachList.addAll(list)
@@ -171,10 +171,10 @@ class CMSWebViewActivity : BaseMVPActivity<CMSWebViewContract.View, CMSWebViewCo
         }
     }
 
-    val taskMap = HashMap<String, Future<Unit>>()
+    private val taskMap = HashMap<String, Future<Unit>>()
     private fun getAttachFileLocalPath(id: String): String {
         var path  = ""
-        attachList.filter { it.id.equals(id) }.map { path = FileExtensionHelper.getXBPMCMSAttachFolder()+File.separator+it.fileName }
+        attachList.asSequence().filter { it.id == id }.map { path = FileExtensionHelper.getXBPMCMSAttachFolder()+File.separator+it.fileName }.toList()
         return path
     }
 }

+ 8 - 7
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/meeting/apply/MeetingApplyPresenter.kt

@@ -43,22 +43,23 @@ class MeetingApplyPresenter : BasePresenterImpl<MeetingApplyContract.View>(), Me
         val json = O2SDKManager.instance().gson.toJson(info)
         XLog.debug("meeting:" + json)
         val body = RequestBody.create(MediaType.parse("text/json"), json)
+        var meetingId = ""
         getMeetingAssembleControlService(mView?.getContext())?.let { service ->
             service.saveMeeting(body)
                     .subscribeOn(Schedulers.io())
                     .flatMap { response ->
-                        val id = response.data.id
+                        meetingId = response.data.id
                         val file = File(meetingFile)
                         val requestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file)
-                        val body = MultipartBody.Part.createFormData("file", file.name, requestBody)
-                        Observable.zip(service.saveMeetingFile(body, id), Observable.just(id), { t1, t2 ->
-                            MeetingFile(t2, t1)
-                        })
+                        val fileBody = MultipartBody.Part.createFormData("file", file.name, requestBody)
+                        service.saveMeetingFile(fileBody, meetingId)
                     }
                     .observeOn(AndroidSchedulers.mainThread())
                     .subscribe({ response ->
-                        XLog.debug("save meeting success id:")
-                        mView?.saveMeetingSuccess(response.meetingId, response.idData.data.id)
+                        val fileId = response.data.id
+                        XLog.debug("save meeting success id:$meetingId")
+                        XLog.debug("save meeting file id:$fileId")
+                        mView?.saveMeetingSuccess(meetingId, fileId)
                     }, { e ->
                         XLog.error("", e)
                         mView?.doMeetingFail("申请会议失败,${e.message}")

+ 22 - 9
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/meeting/main/MeetingMainFragment.kt

@@ -10,6 +10,8 @@ import com.prolificinteractive.materialcalendarview.*
 import kotlinx.android.synthetic.main.content_meeting.*
 import net.muliba.changeskin.FancySkinManager
 import net.muliba.fancyfilepickerlibrary.ext.concat
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2App
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2SDKManager
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPViewPagerFragment
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.meeting.apply.MeetingApplyActivity
@@ -42,7 +44,7 @@ class MeetingMainFragment : BaseMVPViewPagerFragment<MeetingMainFragmentContract
     private lateinit var daySelected: String
     private lateinit var monthSelected: String
     private var identifyDialog: IdentifyChooseDialog? = null
-    private lateinit var meetingConfig: ProcessDataJson
+    private var meetingConfig: ProcessDataJson? = null
     override var mPresenter: MeetingMainFragmentContract.Presenter = MeetingMainFragmentPresenter()
     private var isWeekView = true
 
@@ -162,9 +164,8 @@ class MeetingMainFragment : BaseMVPViewPagerFragment<MeetingMainFragmentContract
         if (TextUtils.isEmpty(config)) {
             fab_meeting_create.gone()
         } else {
-            val gson = Gson()
-            meetingConfig = gson.fromJson<ProcessDataJson>(config, ProcessDataJson::class.java)
-            if (meetingConfig.mobileCreateEnable) {
+            meetingConfig = O2SDKManager.instance().gson.fromJson<ProcessDataJson>(config, ProcessDataJson::class.java)
+            if (meetingConfig?.mobileCreateEnable == true) {
                 fab_meeting_create.visible()
                 fab_meeting_create.setOnClickListener { applyMeeting() }
             } else {
@@ -178,7 +179,13 @@ class MeetingMainFragment : BaseMVPViewPagerFragment<MeetingMainFragmentContract
     override fun loadCurrentPersonIdentity(list: List<ProcessWOIdentityJson>) {
         if (list.size == 1) {
             val identifyId = list[0].distinguishedName
-            mPresenter.startProcess("", identifyId, meetingConfig.process!!.id)
+            val processId = meetingConfig?.process?.id
+            if (TextUtils.isEmpty(processId)) {
+                mPresenter.startProcess("", identifyId, processId!!)
+            }else {
+                XToast.toastShort(context, "流程id缺失")
+            }
+
         } else {
             hideLoadingDialog()
             identifyDialog = IdentifyChooseDialog(activity, list, this)
@@ -189,7 +196,12 @@ class MeetingMainFragment : BaseMVPViewPagerFragment<MeetingMainFragmentContract
     override fun positiveCallback(identifyId: String) {
         identifyDialog?.dismiss()
         showLoadingDialog()
-        mPresenter.startProcess("", identifyId, meetingConfig.process!!.id)
+        val processId = meetingConfig?.process?.id
+        if (TextUtils.isEmpty(processId)) {
+            mPresenter.startProcess("", identifyId, processId!!)
+        }else {
+            XToast.toastShort(context, "流程id缺失")
+        }
     }
 
     override fun negativeCallback() {
@@ -210,10 +222,11 @@ class MeetingMainFragment : BaseMVPViewPagerFragment<MeetingMainFragmentContract
     }
 
     private fun applyMeeting() {
-        if (meetingConfig.process == null || meetingConfig.process?.id?.isBlank() == true) {
+        val processId = meetingConfig?.process?.id
+        if (TextUtils.isEmpty(processId)) {
             activity.go<MeetingApplyActivity>()
-        } else {
-            mPresenter.loadCurrentPersonIdentityWithProcess(meetingConfig.process!!.id)
+        }else {
+            mPresenter.loadCurrentPersonIdentityWithProcess(processId!!)
         }
     }
 

+ 29 - 10
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/meeting/main/MeetingMainFragmentPresenter.kt

@@ -172,17 +172,36 @@ class MeetingMainFragmentPresenter : BasePresenterImpl<MeetingMainFragmentContra
     }
 
     override fun getMeetingConfig() {
-        mView.let {
-            getOrganizationAssembleCustomService(it?.getContext())?.getMeetingConfig()!!
-                    .subscribeOn(Schedulers.io())
-                    .observeOn(AndroidSchedulers.mainThread())
-                    .subscribe(ResponseHandler { info ->
-                        it?.setMeetingConfig(info)
-                    }, ExceptionHandler(it?.getContext()) { e ->
-                        XLog.error("", e)
-                        it?.setMeetingConfig("")
-                    })
+        val service = getOrganizationAssembleCustomService(mView?.getContext())
+        if (service != null){
+            service.getMeetingConfig()
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(ResponseHandler { info ->
+                    mView?.setMeetingConfig(info)
+                }, ExceptionHandler(mView?.getContext()) { e ->
+                    XLog.error("", e)
+                    mView?.setMeetingConfig("")
+                })
+        }else {
+            XLog.info("老公共服务器模块已经去掉了!!!")
+            val personService = getAssemblePersonalApi(mView?.getContext())
+            if (personService != null) {
+                personService.getMeetingConfig()
+                        .subscribeOn(Schedulers.io())
+                        .observeOn(AndroidSchedulers.mainThread())
+                        .subscribe(ResponseHandler { info ->
+                            mView?.setMeetingConfig(info)
+                        }, ExceptionHandler(mView?.getContext()) { e ->
+                            XLog.error("", e)
+                            mView?.setMeetingConfig("")
+                        })
+            }else {
+                XLog.error("公共服务模块不存在")
+                mView?.setMeetingConfig("")
+            }
         }
+
     }
 
     override fun loadCurrentPersonIdentityWithProcess(processId: String) {

+ 89 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/DownloadAPKFragment.kt

@@ -0,0 +1,89 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2
+
+import android.content.*
+import android.graphics.BitmapFactory
+import android.graphics.drawable.BitmapDrawable
+import android.os.Bundle
+import android.support.v4.app.DialogFragment
+import android.support.v4.content.LocalBroadcastManager
+import android.view.*
+import com.race604.drawable.wave.WaveDrawable
+import kotlinx.android.synthetic.main.fragment_download_progress.*
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.service.DownloadAPKService
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
+
+/**
+ * Created by fancyLou on 2019/1/28.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+
+class DownloadAPKFragment : DialogFragment()  {
+
+    companion object {
+        val DOWNLOAD_FRAGMENT_TAG = "DOWNLOAD_FRAGMENT_TAG"
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setStyle(android.app.DialogFragment.STYLE_NO_FRAME, R.style.customStyleDialogStyle) //NO_FRAME就是dialog无边框,0指的是默认系统Theme
+    }
+
+    override fun onStart() {
+        super.onStart()
+        val window = dialog.window
+        window.setGravity(Gravity.CENTER)
+        window.setWindowAnimations(R.style.DialogEmptyAnimation)//取消过渡动画 , 使DialogSearch的出现更加平滑
+    }
+
+    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? =
+            inflater?.inflate(R.layout.fragment_download_progress, container, false)
+
+
+    var wave : WaveDrawable? = null
+    private val localBroadcastManager: LocalBroadcastManager by lazy {
+        LocalBroadcastManager.getInstance(activity.applicationContext)
+    }
+    private var receiver : BroadcastReceiver? = null
+    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        dialog.setCancelable(false)
+        dialog.setCanceledOnTouchOutside(false)
+        dialog.setOnKeyListener(DialogInterface.OnKeyListener { _, keyCode, _ ->
+            if (keyCode == KeyEvent.KEYCODE_BACK) {
+                return@OnKeyListener true
+            }
+            false
+        })
+        val bit = BitmapFactory.decodeResource(activity.resources, R.mipmap.icon_down_arrow)
+        val draw = BitmapDrawable(activity.resources, bit)
+        wave = WaveDrawable(draw)
+        image_logo_wave.setImageDrawable(wave)
+
+        val intentfilter = IntentFilter()
+        intentfilter.addAction(DownloadAPKService.DOWNLOAD_RECIVER_ACTION_KEY)
+        receiver = object : BroadcastReceiver() {
+            override fun onReceive(context: Context?, intent: Intent?) {
+                if (DownloadAPKService.DOWNLOAD_RECIVER_ACTION_KEY == intent?.action) {
+                    val progress = intent.getIntExtra(DownloadAPKService.DOWNLOAD_PROGRESS_KEY, 0)
+                    XLog.debug("这里是progress: $progress")
+                    wave?.level = progress * 100
+                    if (progress == 100) { //结束关闭
+                        dismissAllowingStateLoss()
+                    }
+                }
+            }
+
+        }
+        localBroadcastManager.registerReceiver(receiver, intentfilter)
+
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        if (receiver!=null) {
+            localBroadcastManager.unregisterReceiver(receiver)
+        }
+    }
+}

+ 97 - 1
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/about/AboutActivity.kt

@@ -1,18 +1,33 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.about
 
+import android.content.Intent
+import android.os.Build
 import android.os.Bundle
+import android.provider.Settings
+import android.support.annotation.RequiresApi
 import android.support.v7.app.AppCompatActivity
 import android.support.v7.widget.Toolbar
 import android.text.TextUtils
 import android.widget.TextView
 import com.pgyersdk.update.PgyUpdateManager
+import com.pgyersdk.update.UpdateManagerListener
 import kotlinx.android.synthetic.main.content_about.*
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2CustomStyle
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2SDKManager
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.DownloadAPKFragment
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.service.DownloadAPKService
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.PgyUpdateBean
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.*
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialog.O2AlertIconEnum
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialog.O2DialogSupport
 
 class AboutActivity : AppCompatActivity() {
 
+    private var downloadFragment: DownloadAPKFragment? = null
+    private var versionName = ""
+    private var downloadUrl = ""
+
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(R.layout.activity_about)
@@ -40,7 +55,7 @@ class AboutActivity : AppCompatActivity() {
         }
 
         relative_about_check_version.setOnClickListener {
-            AppUpdateUtil(this).checkAppUpdate(true)
+            checkAppUpdate()
         }
     }
 
@@ -48,4 +63,85 @@ class AboutActivity : AppCompatActivity() {
         super.onPause()
         PgyUpdateManager.unregister()
     }
+
+
+    /**
+     * 检查应用是否需要更新
+     */
+
+    private fun checkAppUpdate() {
+        checkAppUpdate(callbackContinue = { result ->
+            if (result) {
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !packageManager.canRequestPackageInstalls()) {// 8.0需要判断安装未知来源的权限
+                    startInstallPermissionSettingActivity()
+                }else { // 下载安装更新
+                    if (downloadFragment == null) {
+                        downloadFragment = DownloadAPKFragment()
+                    }
+                    downloadFragment?.isCancelable = false
+                    downloadFragment?.show(supportFragmentManager, DownloadAPKFragment.DOWNLOAD_FRAGMENT_TAG)
+                    downloadServiceStart()
+                }
+            }
+        })
+    }
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+        super.onActivityResult(requestCode, resultCode, data)
+        if (resultCode == RESULT_OK && requestCode == 10086) { //  下载安装更新
+            if (downloadFragment == null) {
+                downloadFragment = DownloadAPKFragment()
+            }
+            downloadFragment?.isCancelable = false
+            downloadFragment?.show(supportFragmentManager, DownloadAPKFragment.DOWNLOAD_FRAGMENT_TAG)
+            downloadServiceStart()
+        }
+    }
+    @RequiresApi(api = Build.VERSION_CODES.O)
+    private fun startInstallPermissionSettingActivity() {
+        //注意这个是8.0新API
+        val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES)
+        startActivityForResult(intent, 10086)
+    }
+    private fun checkAppUpdate(noUpdateIsNotify: Boolean = false, callbackContinue:((flag: Boolean)->Unit)? = null) {
+        PgyUpdateManager.register(this, object : UpdateManagerListener() {
+            override fun onUpdateAvailable(p0: String?) {
+                XLog.debug("onUpdateAvailable $p0")
+                val bean = O2SDKManager.instance().gson.fromJson(p0, PgyUpdateBean::class.java)
+                versionName = bean.data.versionName
+                downloadUrl = bean.data.downloadURL
+                XLog.info("versionName:$versionName, downloadUrl:$downloadUrl")
+                if (bean != null) {
+                    val currentversionName = AndroidUtils.getAppVersionName(this@AboutActivity)
+                    if (currentversionName != versionName) {
+                        O2DialogSupport.openConfirmDialog(this@AboutActivity,"版本 $versionName 更新:"+ bean.data.releaseNote, listener = { _ ->
+                            XLog.info("notification is true..........")
+                            callbackContinue?.invoke(true)
+                        }, icon = O2AlertIconEnum.UPDATE, negativeListener = { _->
+                            callbackContinue?.invoke(false)
+                        })
+
+                    } else {
+                        callbackContinue?.invoke(false)
+                        XLog.info("versionName is same , do not show dialog! versionName:$versionName ")
+                    }
+                }else {
+                    callbackContinue?.invoke(false)
+                }
+
+            }
+
+            override fun onNoUpdateAvailable() {
+                XLog.info("没有发现新版本!")
+                XToast.toastShort(this@AboutActivity, "没有发现新版本!")
+                callbackContinue?.invoke(false)
+            }
+        })
+    }
+    private fun downloadServiceStart() {
+        val intent = Intent(this, DownloadAPKService::class.java)
+        intent.action = packageName + DownloadAPKService.DOWNLOAD_SERVICE_ACTION
+        intent.putExtra(DownloadAPKService.VERSIN_NAME_EXTRA_NAME, versionName)
+        intent.putExtra(DownloadAPKService.DOWNLOAD_URL_EXTRA_NAME, downloadUrl)
+        startService(intent)
+    }
 }

+ 1 - 1
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/ai/O2AIActivity.kt

@@ -192,7 +192,7 @@ class O2AIActivity : AppCompatActivity(), O2AIContract.View {
         // 设置合成的语调,0-9 ,默认 5
         params[SpeechSynthesizer.PARAM_PITCH] = "5"
 
-        params[SpeechSynthesizer.PARAM_MIX_MODE] = SpeechSynthesizer.MIX_MODE_DEFAULT
+        params[SpeechSynthesizer.PARAM_MIX_MODE] = SpeechSynthesizer.MIX_MODE_HIGH_SPEED_SYNTHESIZE
         // 该参数设置为TtsMode.MIX生效。即纯在线模式不生效。
         // MIX_MODE_DEFAULT 默认 ,wifi状态下使用在线,非wifi离线。在线状态下,请求超时6s自动转离线
         // MIX_MODE_HIGH_SPEED_SYNTHESIZE_WIFI wifi状态下使用在线,非wifi离线。在线状态下, 请求超时1.2s自动转离线

+ 1 - 1
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/bind/BindPhoneActivity.kt

@@ -126,8 +126,8 @@ class BindPhoneActivity: AppCompatActivity(), BindPhoneContract.View , DialogInt
 
     private fun goFinish() {
         if (goToMain) {
-            O2App.instance._JMLoginInner()
             goThenKill<MainActivity>()
+            O2App.instance._JMLoginInner()
         } else {
             goThenKill<LoginActivity>(LoginActivity.startBundleData(phone))
         }

+ 3 - 1
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/bind/SecondStepPresenter.kt

@@ -43,6 +43,7 @@ class SecondStepPresenter : BasePresenterImpl<SecondStepContract.View>(), Second
             service.bindDevice(postBody)
                     .subscribeOn(Schedulers.io())
                     .flatMap{
+                        XLog.debug("这里是绑定成功了。。。。")
                         getApiService(mView?.getContext(), url)?.getWebserverDistributeWithSource(unitData.centerHost)
                     }
                     .observeOn(AndroidSchedulers.mainThread())
@@ -50,7 +51,8 @@ class SecondStepPresenter : BasePresenterImpl<SecondStepContract.View>(), Second
                         //绑定成功写入本地存储
                         O2SDKManager.instance().bindUnit(unitData, phone, deviceId)
                         mView?.bindSuccess(data)
-                    }, Action1<Throwable> { e ->
+                    }, ExceptionHandler(mView?.getContext()) { e->
+                        e.printStackTrace()
                         XLog.error("", e)
                         mView?.bindFail()
                     })

+ 134 - 11
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/launch/LaunchActivity.kt

@@ -1,11 +1,14 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.launch
 
 import android.Manifest
+import android.app.Activity
 import android.content.Context
+import android.content.DialogInterface
 import android.content.Intent
 import android.content.IntentFilter
 import android.net.ConnectivityManager
 import android.net.Uri
+import android.os.Build
 import android.os.Bundle
 import android.os.Handler
 import android.provider.Settings
@@ -22,20 +25,24 @@ import cn.jpush.android.api.JPushInterface
 import kotlinx.android.synthetic.main.activity_launch.*
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.*
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.DownloadAPKFragment
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.bind.BindPhoneActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.login.LoginActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.main.MainActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.enums.LaunchState
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.receiver.NetworkConnectStatusReceiver
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.AppUpdateUtil
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.BitmapUtil
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XToast
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.*
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.permission.PermissionRequester
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialog.O2AlertDialogBuilder
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialog.O2AlertIconEnum
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialog.O2DialogSupport
 import org.jetbrains.anko.dip
-import java.nio.charset.Charset
+import android.support.annotation.RequiresApi
+import com.pgyersdk.update.PgyUpdateManager
+import com.pgyersdk.update.UpdateManagerListener
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.service.DownloadAPKService
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.PgyUpdateBean
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.*
 
 
 /**
@@ -144,18 +151,102 @@ class LaunchActivity : BaseMVPActivity<LaunchContract.View, LaunchContract.Prese
             XToast.toastShort(this, getString(R.string.launch_network_is_not_connected))
         }else{
             mCheckNetwork = true
+            // 是否使用蒲公英 launch()
             checkAppUpdate()
         }
     }
 
+
+    ////////////////////////////更新 start///////////////////////////////////
+
     /**
      * 检查应用是否需要更新
      */
+
+    private var downloadFragment: DownloadAPKFragment? = null
+    private var versionName = ""
+    private var downloadUrl = ""
+
     private fun checkAppUpdate() {
-        AppUpdateUtil(this).checkAppUpdate(callbackContinue = {
-            launch()
+        checkAppUpdate(callbackContinue = { result ->
+            if (result) {
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !packageManager.canRequestPackageInstalls()) {// 8.0需要判断安装未知来源的权限
+                    startInstallPermissionSettingActivity()
+                }else { // 下载安装更新
+                    if (downloadFragment == null) {
+                        downloadFragment = DownloadAPKFragment()
+                    }
+                    downloadFragment?.isCancelable = false
+                    downloadFragment?.show(supportFragmentManager, DownloadAPKFragment.DOWNLOAD_FRAGMENT_TAG)
+                    downloadServiceStart()
+                }
+            }else {
+                launch()
+            }
         })
     }
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+        super.onActivityResult(requestCode, resultCode, data)
+        if (resultCode == RESULT_OK && requestCode == 10086) { //  下载安装更新
+            if (downloadFragment == null) {
+                downloadFragment = DownloadAPKFragment()
+            }
+            downloadFragment?.isCancelable = false
+            downloadFragment?.show(supportFragmentManager, DownloadAPKFragment.DOWNLOAD_FRAGMENT_TAG)
+            downloadServiceStart()
+        }
+    }
+    @RequiresApi(api = Build.VERSION_CODES.O)
+    private fun startInstallPermissionSettingActivity() {
+        //注意这个是8.0新API
+        val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES)
+        startActivityForResult(intent, 10086)
+    }
+    private fun checkAppUpdate(noUpdateIsNotify: Boolean = false, callbackContinue:((flag: Boolean)->Unit)? = null) {
+        PgyUpdateManager.register(this, object : UpdateManagerListener() {
+            override fun onUpdateAvailable(p0: String?) {
+                XLog.debug("onUpdateAvailable $p0")
+                val bean = O2SDKManager.instance().gson.fromJson(p0, PgyUpdateBean::class.java)
+                versionName = bean.data.versionName
+                downloadUrl = bean.data.downloadURL
+                XLog.info("versionName:$versionName, downloadUrl:$downloadUrl")
+                if (bean != null) {
+                    val currentversionName = AndroidUtils.getAppVersionName(this@LaunchActivity)
+                    if (currentversionName != versionName) {
+                        O2DialogSupport.openConfirmDialog(this@LaunchActivity,"版本 $versionName 更新:"+ bean.data.releaseNote, listener = { _ ->
+                            XLog.info("notification is true..........")
+                            callbackContinue?.invoke(true)
+//                            toDownloadService(activity)
+                        }, icon = O2AlertIconEnum.UPDATE, negativeListener = {_->
+                            callbackContinue?.invoke(false)
+                        })
+
+                    } else {
+                        callbackContinue?.invoke(false)
+                        XLog.info("versionName is same , do not show dialog! versionName:$versionName ")
+                    }
+                }else {
+                    callbackContinue?.invoke(false)
+                }
+
+            }
+
+            override fun onNoUpdateAvailable() {
+                XLog.info("没有发现新版本!")
+
+                callbackContinue?.invoke(false)
+            }
+        })
+    }
+    private fun downloadServiceStart() {
+        val intent = Intent(this, DownloadAPKService::class.java)
+        intent.action = packageName + DownloadAPKService.DOWNLOAD_SERVICE_ACTION
+        intent.putExtra(DownloadAPKService.VERSIN_NAME_EXTRA_NAME, versionName)
+        intent.putExtra(DownloadAPKService.DOWNLOAD_URL_EXTRA_NAME, downloadUrl)
+        startService(intent)
+    }
+
+    /////////////////////////////////////更新 end//////////////////////////////////////
 
 
     private fun launch() {
@@ -169,8 +260,15 @@ class LaunchActivity : BaseMVPActivity<LaunchContract.View, LaunchContract.Prese
                 O2SDKManager.instance().launchInner(String(buffer, Charsets.UTF_8), launchState)
             }else {
                 XLog.error("没有获取到server.json")
-                XToast.toastShort(this, "缺少配置文件!")
-                finish()
+//                XToast.toastShort(this, "缺少配置文件!")
+                O2AlertDialogBuilder(this)
+                        .icon(O2AlertIconEnum.ALERT)
+                        .content("缺少配置文件,请联系App开发人员!")
+                        .positive("关闭")
+                        .onPositiveListener{ _ ->
+                            finish()
+                        }
+                        .show()
             }
         }else {
             if (TextUtils.isEmpty(pushToken)) {
@@ -224,8 +322,32 @@ class LaunchActivity : BaseMVPActivity<LaunchContract.View, LaunchContract.Prese
                     gotoLogin()
                 }
                 LaunchState.UnknownError -> {
-                    XToast.toastShort(this, "未知异常!")
-                    finish()
+                    if (!isFinishing) {
+                        if (BuildConfig.InnerServer) {
+                            O2AlertDialogBuilder(this)
+                                    .icon(O2AlertIconEnum.ALERT)
+                                    .content("未知异常!")
+                                    .positive("关闭")
+                                    .onPositiveListener { _ ->
+                                        finish()
+                                    }
+                                    .show()
+                        } else {
+                            O2AlertDialogBuilder(this)
+                                    .title(R.string.confirm)
+                                    .icon(O2AlertIconEnum.ALERT)
+                                    .content("未知异常!")
+                                    .positive("重新绑定")
+                                    .negative("关闭")
+                                    .onPositiveListener { _ ->
+                                        gotoBindLogin()
+                                    }
+                                    .onNegativeListener { _ ->
+                                        finish()
+                                    }
+                                    .show()
+                        }
+                    }
                 }
                 LaunchState.Success -> {
                     gotoMain()
@@ -247,6 +369,7 @@ class LaunchActivity : BaseMVPActivity<LaunchContract.View, LaunchContract.Prese
     }
 
     private fun gotoMain() {
+        O2App.instance._JMLoginInner()//im
         circleProgressBar_launch.gone()
         if (mStyleUpdate) {
             goAndClearBefore<MainActivity>()

+ 95 - 407
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/login/LoginActivity.kt

@@ -1,28 +1,15 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.login
 
-import android.Manifest
 import android.content.Context
-import android.graphics.SurfaceTexture
-import android.hardware.Camera
 import android.media.AudioManager
 import android.media.MediaPlayer
-import android.opengl.GLES20
-import android.opengl.GLSurfaceView
-import android.opengl.Matrix
 import android.os.Bundle
-import android.os.Handler
-import android.os.HandlerThread
 import android.text.InputType
 import android.text.TextUtils
 import android.view.KeyEvent
 import android.view.View
 import android.view.WindowManager
-import android.view.animation.Animation
-import android.view.animation.ScaleAnimation
 import android.view.inputmethod.InputMethodManager
-import com.facepp.demo.util.*
-import com.megvii.facepp.sdk.Facepp
-import com.megvii.licensemanager.sdk.LicenseManager
 import kotlinx.android.synthetic.main.activity_login.*
 import net.muliba.changeskin.FancySkinManager
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.*
@@ -30,16 +17,19 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.bind.BindPhoneActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.main.MainActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.AuthenticationInfoJson
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.*
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.*
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.permission.PermissionRequester
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.BitmapUtil
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.DateHelper
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XToast
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.biometric.BioConstants
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.biometric.BiometryManager
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.biometric.OnBiometryAuthCallback
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.goThenKill
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.gone
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.visible
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.CountDownButtonHelper
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialog.O2DialogSupport
 import java.io.IOException
-import java.nio.FloatBuffer
-import java.util.*
-import javax.microedition.khronos.egl.EGLConfig
-import javax.microedition.khronos.opengles.GL10
 
 
 /**
@@ -47,8 +37,7 @@ import javax.microedition.khronos.opengles.GL10
  */
 
 class LoginActivity: BaseMVPActivity<LoginContract.View, LoginContract.Presenter>(),
-        LoginContract.View, View.OnClickListener,
-        Camera.PreviewCallback, GLSurfaceView.Renderer, SurfaceTexture.OnFrameAvailableListener{
+        LoginContract.View, View.OnClickListener {
 
     override var mPresenter: LoginContract.Presenter = LoginPresenter()
 
@@ -56,7 +45,6 @@ class LoginActivity: BaseMVPActivity<LoginContract.View, LoginContract.Presenter
         super.beforeSetContentView()
         setTheme(R.style.XBPMTheme_NoActionBar)
         window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)//去掉信息栏
-        Screen.initialize(this)
     }
 
     companion object {
@@ -74,53 +62,30 @@ class LoginActivity: BaseMVPActivity<LoginContract.View, LoginContract.Presenter
                 60,
                 1)
     }
-    private val scale0 by lazy {
-        ScaleAnimation(1f,0f,1f,1f,
-            Animation.RELATIVE_TO_PARENT,0.5f,
-                Animation.RELATIVE_TO_PARENT,0.5f)
-    }
-    private val scale1 by lazy {
-        ScaleAnimation(0f,1f,1f,1f,
-                Animation.RELATIVE_TO_PARENT,0.5f,
-                Animation.RELATIVE_TO_PARENT,0.5f)
-    }
+
+    //翻转动画
+//    private val scale0 by lazy {
+//        ScaleAnimation(1f,0f,1f,1f,
+//            Animation.RELATIVE_TO_PARENT,0.5f,
+//                Animation.RELATIVE_TO_PARENT,0.5f)
+//    }
+//    private val scale1 by lazy {
+//        ScaleAnimation(0f,1f,1f,1f,
+//                Animation.RELATIVE_TO_PARENT,0.5f,
+//                Animation.RELATIVE_TO_PARENT,0.5f)
+//    }
     private var receivePhone = ""
 
     //播放声音
     private var mediaPlayer: MediaPlayer? = null
     private var playBeep: Boolean = false
-    //facepp 相关的参数
-    private var faceppLicenseIsOK: Boolean = false
-    private var is106Points: Boolean = false
-    private var isBackCamera:Boolean = false
-    private var isOneFaceTrackig:Boolean = false
-    private var trackModel: String? = null
-    private var mGlSurfaceView: GLSurfaceView? = null
-    private var mICamera: ICamera? = null
-    private var mCamera: Camera? = null
-    private val mHandlerThread = HandlerThread("facepp")
-    private var mHandler: Handler? = null
-    private var facepp: Facepp? = null
-    private var min_face_size = 200
-    private var detection_interval = 25
-    private var sensorUtil: SensorEventUtil? = null
-    private var carmeraImgData: ByteArray? = null
-    private var Angle: Int = 0
-    private var mTextureID = -1
-    private var mSurface: SurfaceTexture? = null
-    private var mCameraMatrix: CameraMatrix? = null
-    private var mPointsMatrix: PointsMatrix? = null
-    private val mMVPMatrix = FloatArray(16)
-    private val mProjMatrix = FloatArray(16)
-    private val mVMatrix = FloatArray(16)
-    private var pitch: Float = 0.toFloat()
-    private var yaw:Float = 0.toFloat()
-    private var roll:Float = 0.toFloat()
-    private var rotation = Angle
 
 
     override fun afterSetContentView(savedInstanceState: Bundle?) {
         receivePhone = intent.extras?.getString(REQUEST_PHONE_KEY) ?: ""
+        //是否开启了指纹识别登录
+        checkBioAuthLogin()
+
         setDefaultLogo()
         login_edit_username_id.setText(receivePhone)
         tv_login_copyright.text = getString(R.string.copy_right).plus(" ")
@@ -146,12 +111,12 @@ class LoginActivity: BaseMVPActivity<LoginContract.View, LoginContract.Presenter
             }
         }
 
-        facePPAuthCheck()
-        initFaceppUI()
+
 
         btn_login_submit.setOnClickListener(this)
-        btn_login_facepp.setOnClickListener(this)
-        login_facepp_back.setOnClickListener(this)
+        btn_bio_auth_login.setOnClickListener(this)
+        tv_user_fallback_btn.setOnClickListener(this)
+        tv_bioauth_btn.setOnClickListener(this)
 
 
         if (BuildConfig.InnerServer) {
@@ -160,12 +125,6 @@ class LoginActivity: BaseMVPActivity<LoginContract.View, LoginContract.Presenter
             button_login_phone_code.gone()
             tv_rebind_btn.gone()
         }else {
-            val unit = O2SDKManager.instance().prefs().getString(O2.PRE_CENTER_HOST_KEY, "")
-            if ("dev.o2oa.io" == unit || "dev.o2oa.net" == unit || "dev.o2server.io" == unit) {
-                btn_login_facepp.visible()
-            }else {
-                btn_login_facepp.gone()
-            }
             login_edit_password_id.setHint(R.string.login_code)
             login_edit_password_id.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_NORMAL
             button_login_phone_code.visible()
@@ -174,7 +133,6 @@ class LoginActivity: BaseMVPActivity<LoginContract.View, LoginContract.Presenter
             tv_rebind_btn.setOnClickListener(this)
         }
 
-        initScaleAnimation()
     }
 
 
@@ -209,19 +167,22 @@ class LoginActivity: BaseMVPActivity<LoginContract.View, LoginContract.Presenter
         initBeepSound()
     }
 
-    override fun onStop() {
-        super.onStop()
-        stopCamera()
-    }
-
     override fun onDestroy() {
         super.onDestroy()
         countDownHelper.destroy()
-        mHandler?.post { facepp?.release() }
     }
 
     override fun onClick(v: View?) {
         when(v?.id) {
+            R.id.tv_user_fallback_btn -> {
+                userFallback()
+            }
+            R.id.tv_bioauth_btn -> {
+                showBiometryAuthUI()
+            }
+            R.id.btn_bio_auth_login ->{
+                bioAuthLogin()
+            }
             R.id.btn_login_submit -> {
                 submitLogin()
             }
@@ -233,151 +194,12 @@ class LoginActivity: BaseMVPActivity<LoginContract.View, LoginContract.Presenter
                     reBindService()
                 })
             }
-            R.id.login_facepp_back -> {
-                login_facepp.startAnimation(scale0)
-            }
-            R.id.btn_login_facepp -> {
-                clickStartFaceRecognize()
-            }
         }
     }
 
-    private fun clickStartFaceRecognize() {
-        PermissionRequester(this).request(Manifest.permission.CAMERA)
-                .o2Subscribe {
-                    onNext {(granted, shouldShowRequestPermissionRationale, deniedPermissions) ->
-                       XLog.debug("granted:$granted, show:$shouldShowRequestPermissionRationale, deniedList:$deniedPermissions")
-                        if (granted) {
-                            if (faceppLicenseIsOK) {
-                                login_main.startAnimation(scale0)
-                            }else {
-                                XToast.toastShort(this@LoginActivity, "非常抱歉,今日的试用次数已经用完了!")
-                            }
-                        }else {
-                            O2DialogSupport.openAlertDialog(this@LoginActivity,
-                                    "非常抱歉,摄像头权限没有开启,马上去设置",
-                                    { AndroidUtils.gotoSettingApplication(this@LoginActivity) })
-                        }
-                    }
-                    onError { e, isNetworkError ->
-                        XLog.error("检查权限异常,$isNetworkError", e)
-                        XToast.toastShort(this@LoginActivity, "非常抱歉,无法启动摄像头!")
-                    }
-                }
-    }
-
-    override fun onDrawFrame(gl: GL10?) {
-        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT or GLES20.GL_DEPTH_BUFFER_BIT)// 清除屏幕和深度缓存
-        val mtx = FloatArray(16)
-        mSurface?.getTransformMatrix(mtx)
-        mCameraMatrix?.draw(mtx)
-        // Set the camera position (View matrix)
-        Matrix.setLookAtM(mVMatrix, 0, 0f, 0f, -3f, 0f, 0f, 0f, 0f, 1f, 0f)
-        // Calculate the projection and view transformation
-        Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0)
-        mPointsMatrix?.draw(mMVPMatrix)
-        mSurface?.updateTexImage()// 更新image,会调用onFrameAvailable方法
-    }
-
-    override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
-        XLog.debug("onSurfaceChanged.....w:$width, h:$height")
-        // 设置画面的大小
-        GLES20.glViewport(0, 0, width, height)
-        var ratio = 1f // 这样OpenGL就可以按照屏幕框来画了,不是一个正方形了
-        // this projection matrix is applied to object coordinates
-        // in the onDrawFrame() method
-        Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1f, 1f, 3f, 7f)
-    }
-
-    override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
-        XLog.debug("onSurfaceCreated...............")
-        // 黑色背景
-        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f)
-        surfaceInit()
-    }
-
-    override fun onPreviewFrame(imgData: ByteArray?, camera: Camera?) {
-        XLog.debug("onPreviewFrame...............这是获取每帧数据的地方。。")
-        //检测操作放到主线程,防止贴点延迟
-        val width = mICamera?.cameraWidth ?: 640
-        val height = mICamera?.cameraHeight ?: 480
-
-        val orientation = sensorUtil?.orientation
-        //0.4.7之前(包括)jni把所有角度的点算到竖直的坐标,所以外面画点需要再调整回来,才能与其他角度适配
-        //目前getLandmarkOrigin会获得原始的坐标,所以只需要横屏适配好其他的角度就不用适配了,因为texture和preview的角度关系是固定的
-        when (orientation) {
-            0 -> rotation = Angle
-            1 -> rotation = 0
-            2 -> rotation = 180
-            3 -> rotation = 360 - Angle
-        }
-
-        setConfig(rotation)
-
-        val faces = facepp?.detect(imgData, width, height, Facepp.IMAGEMODE_NV21)
-        if (faces != null) {
-            XLog.debug("faces size." + faces.size)
-            val pointsOpengl = ArrayList<ArrayList<*>>()
-            if (faces.isNotEmpty()) {
-                for (c in faces.indices) {
-                    if (is106Points)
-                        facepp?.getLandmarkRaw(faces[c], Facepp.FPP_GET_LANDMARK106)
-                    else
-                        facepp?.getLandmarkRaw(faces[c], Facepp.FPP_GET_LANDMARK81)
-
-                    pitch = faces[c].pitch
-                    yaw = faces[c].yaw
-                    roll = faces[c].roll
-
-
-                    //0.4.7之前(包括)jni把所有角度的点算到竖直的坐标,所以外面画点需要再调整回来,才能与其他角度适配
-                    //目前getLandmarkOrigin会获得原始的坐标,所以只需要横屏适配好其他的角度就不用适配了,因为texture和preview的角度关系是固定的
-                    val triangleVBList = ArrayList<FloatBuffer>()
-                    for (i in faces[c].points.indices) {
-                        var x = faces[c].points[i].x / width * 2 - 1
-                        if (isBackCamera)
-                            x = -x
-                        val y = faces[c].points[i].y / height * 2 - 1
-                        val pointf = floatArrayOf(y, x, 0.0f)
-                        val fb = mCameraMatrix?.floatBufferUtil(pointf)
-                        if (fb != null) {
-                            triangleVBList.add(fb)
-                        }
-                    }
-                    pointsOpengl.add(triangleVBList)
-                }
-
-                //开始在线识别
-                val data = imgData!!
-                mPresenter.searchFace(data, faces, mICamera!!, isBackCamera)
-
-            } else {
-                pitch = 0.0f
-                yaw = 0.0f
-                roll = 0.0f
-            }
-
-            /**
-             * 画人脸特征点
-             */
-            if (mPointsMatrix!=null) {
-                synchronized(mPointsMatrix!!) {
-                    mPointsMatrix!!.bottomVertexBuffer = null
-                    mPointsMatrix!!.points = pointsOpengl
-//                mPointsMatrix.faceRects = rectsOpengl
-                }
-            }
-        }
-
-    }
-
-    override fun onFrameAvailable(surfaceTexture: SurfaceTexture?) {
-        XLog.debug("onFrameAvailable。。。。。。。。。。。。。。")
-        mGlSurfaceView?.requestRender()
-    }
 
     override fun loginSuccess(data: AuthenticationInfoJson) {
-        if (login_facepp.visibility == View.VISIBLE) {
+        if (login_main_biometry.visibility == View.VISIBLE) {
             playBeepSound()
         }
         hideLoadingDialog()
@@ -442,220 +264,86 @@ class LoginActivity: BaseMVPActivity<LoginContract.View, LoginContract.Presenter
     }
 
 
-
     /**
-     * 设置logo
+     * 是否开启了指纹识别登录
      */
-    private fun setDefaultLogo() {
-        val path = O2CustomStyle.loginAvatarImagePath(this@LoginActivity)
-        if (!TextUtils.isEmpty(path)) {
-            BitmapUtil.setImageFromFile(path!!, image_login_logo)
+    private fun checkBioAuthLogin() {
+        val userId = O2SDKManager.instance().prefs().getString(BioConstants.O2_bio_auth_user_id_prefs_key, "") ?: ""
+        if (userId.isNotEmpty()) {
+            tv_bioauth_btn.visible()
+            login_form_scroll_id.gone()
+            login_main_biometry.visible()
+        }else {
+            login_form_scroll_id.visible()
+            login_main_biometry.gone()
         }
     }
+
     /**
-     * 切换动画
+     * 其他方式登录
      */
-    private fun initScaleAnimation() {
-        scale0.duration = 500
-        scale1.duration = 500
-        scale0.setAnimationListener(object : Animation.AnimationListener {
-            override fun onAnimationRepeat(animation: Animation?) {
-            }
-
-            override fun onAnimationEnd(animation: Animation?) {
-                if (login_main.visibility == View.VISIBLE) {
-                    login_main.animation = null
-                    login_main.gone()
-                    login_facepp.visible()
-                    login_facepp.startAnimation(scale1)
-                } else {
-                    login_facepp.animation = null
-                    login_facepp.gone()
-                    login_main.visible()
-                    login_main.startAnimation(scale1)
-                }
-            }
-
-            override fun onAnimationStart(animation: Animation?) {
-            }
-        })
-        scale1.setAnimationListener(object : Animation.AnimationListener {
-            override fun onAnimationRepeat(animation: Animation?) {
-            }
-
-            override fun onAnimationEnd(animation: Animation?) {
-                //判断是开启人脸识别还是关闭 这个可以在 onAnimationEnd中进行
-                if (login_main.visibility == View.VISIBLE) {
-                    stopCamera()
-                }else {
-                    startCamera()
-                }
-            }
-
-            override fun onAnimationStart(animation: Animation?) {
-            }
-        })
+    private fun userFallback() {
+        login_form_scroll_id.visible()
+        login_main_biometry.gone()
     }
 
-
-    //刷脸登录///////////////////////////////////////////////////////////
-
-
-
     /**
-     * 开启摄像头
+     * 指纹识别登录
      */
-    private fun startCamera() {
-        ConUtil.acquireWakeLock(this)
-        //设置相机 比如分辨率
-        mCamera = mICamera?.openCamera(isBackCamera, this)
-        if (mCamera != null) {
-            Angle = 360 - (mICamera?.Angle?:0)
-            if (isBackCamera)
-                Angle = (mICamera?.Angle?:0)
-
-            val params = mICamera?.layoutParam
-            mGlSurfaceView?.layoutParams = params
-
-            val width = mICamera?.cameraWidth
-            val height = mICamera?.cameraHeight
-
-            val left = 0
-            val top = 0
-
-            val errorCode = facepp?.init(this, ConUtil.getFileContent(this, R.raw.megviifacepp_0_5_2_model), if (isOneFaceTrackig) 1 else 0)
-            XLog.info("facepp init errorCode:$errorCode")
-
-            val faceppConfig = facepp?.faceppConfig
-            faceppConfig?.interval = detection_interval
-            faceppConfig?.minFaceSize = min_face_size
-            faceppConfig?.roi_left = left
-            faceppConfig?.roi_top = top
-            faceppConfig?.roi_right = width
-            faceppConfig?.roi_bottom = height
-            val array = resources.getStringArray(R.array.login_facepp_trackig_mode_array)
-            when (trackModel) {
-                array[0] -> faceppConfig?.detectionMode = Facepp.FaceppConfig.DETECTION_MODE_TRACKING_FAST
-                array[1] -> faceppConfig?.detectionMode = Facepp.FaceppConfig.DETECTION_MODE_TRACKING_ROBUST
-                array[2] -> faceppConfig?.detectionMode = Facepp.FaceppConfig.MG_FPP_DETECTIONMODE_TRACK_RECT
-            }
-            facepp?.faceppConfig = faceppConfig
-
-            val version = Facepp.getVersion()
-            XLog.debug("Facepp:version:$version")
-
-            //启动
-            mICamera?.startPreview(mSurface)// 设置预览容器
-            mICamera?.actionDetect(this)
-
-        } else {
-            XToast.toastShort(this, "打开相机失败")
-        }
+    private fun showBiometryAuthUI() {
+        login_form_scroll_id.gone()
+        login_main_biometry.visible()
     }
 
+    private val bioManager: BiometryManager by lazy { BiometryManager(this) }
     /**
-     * 关闭摄像头
+     * 指纹识别登录
      */
-    private fun stopCamera() {
-        ConUtil.releaseWakeLock()
-        mICamera?.closeCamera()
-        mCamera = null
-    }
+    private fun bioAuthLogin() {
+        if(!bioManager.isBiometricPromptEnable()){
+            XToast.toastShort(this, "指纹识别模块未启用,请检查手机是否开启")
+        }else {
+            val userId = O2SDKManager.instance().prefs().getString(BioConstants.O2_bio_auth_user_id_prefs_key, "") ?: ""
+            bioManager.authenticate(object : OnBiometryAuthCallback{
+                override fun onUseFallBack() {
+                    XLog.error("点击了《其他方式》按钮。。。。。")
+                    userFallback()
+                }
 
-    private fun surfaceInit() {
-        mTextureID = OpenGLUtil.createTextureID()
-        mSurface = SurfaceTexture(mTextureID)
-        // 这个接口就干了这么一件事,当有数据上来后会进到onFrameAvailable方法
-        mSurface?.setOnFrameAvailableListener(this)// 设置照相机有数据时进入
-        mCameraMatrix = CameraMatrix(mTextureID)
-        mPointsMatrix = PointsMatrix(false)
-        mPointsMatrix?.isShowFaceRect = false
-    }
+                override fun onSucceeded() {
+                    showLoadingDialog()
+                    mPresenter.ssoLogin(userId)
+                }
 
-    private fun setConfig(rotation: Int) {
-        val faceppConfig = facepp?.faceppConfig
-        if (faceppConfig != null && faceppConfig.rotation != rotation) {
-            faceppConfig.rotation = rotation
-            facepp!!.faceppConfig = faceppConfig
-        }
-    }
+                override fun onFailed() {
+                    XLog.error("指纹识别验证失败了。。。。。")
+                    //XToast.toastShort(this@LoginActivity, "验证失败")
+                }
 
-    /**
-     * 初始化 人脸识别相关的参数
-     */
-    private fun initFaceppUI() {
-        // 人脸识别参数设置
-        is106Points = false // 81 还 106
-        isBackCamera = false //是否后置摄像头 默认用前置
-        isOneFaceTrackig = true //单脸跟踪
-        trackModel = "Fast" //3种模式: Fast Robust Tracking_Rect
-        min_face_size = 40 // 33 --- 2147483647
-        detection_interval = 30 //毫秒
-
-        facepp = Facepp()
-        sensorUtil = SensorEventUtil(this)
-        mHandlerThread.start()
-        mHandler = Handler(mHandlerThread.looper)
-
-        mGlSurfaceView = findViewById(R.id.login_facepp_surfaceview)
-        mGlSurfaceView?.setEGLContextClientVersion(2)// 创建一个OpenGL ES 2.0
-        // context
-        mGlSurfaceView?.setRenderer(this)// 设置渲染器进入gl
-        // RENDERMODE_CONTINUOUSLY不停渲染
-        // RENDERMODE_WHEN_DIRTY懒惰渲染,需要手动调用 glSurfaceView.requestRender() 才会进行更新
-        mGlSurfaceView?.renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY// 设置渲染器模式
-        mGlSurfaceView?.setOnClickListener { autoFocus() }
-        mICamera = ICamera()
+                override fun onError(code: Int, reason: String) {
+                    XLog.error("指纹识别验证出错,code:$code , reason:$reason")
+                    //XToast.toastShort(this@LoginActivity, "验证失败")
+                }
+
+                override fun onCancel() {
+                    XLog.info("指纹识别取消了。。。。。")
+                }
+
+            })
+        }
     }
 
     /**
-     * 自动对焦
+     * 设置logo
      */
-    private fun autoFocus() {
-        if (mCamera != null && isBackCamera) {
-            mCamera?.cancelAutoFocus()
-            val parameters = mCamera?.parameters
-            parameters?.focusMode = Camera.Parameters.FOCUS_MODE_AUTO
-            mCamera?.parameters = parameters
-            mCamera?.autoFocus(null)
+    private fun setDefaultLogo() {
+        val path = O2CustomStyle.loginAvatarImagePath(this@LoginActivity)
+        if (!TextUtils.isEmpty(path)) {
+            BitmapUtil.setImageFromFile(path!!, image_login_logo)
         }
     }
 
 
-    private fun facePPAuthCheck() {
-        val licenseManager = LicenseManager(this)
-        val uuid = ConUtil.getUUIDString(this)
-        val apiName = Facepp.getApiName()
-        licenseManager.setAuthTimeBufferMillis(0)
-        licenseManager.takeLicenseFromNetwork(Util.CN_LICENSE_URL, uuid, Util.API_KEY, Util.API_SECRET, apiName,
-                "1", object : LicenseManager.TakeLicenseCallback {
-            override fun onSuccess() {
-                authState(true, 0, "")
-            }
-
-            override fun onFailed(i: Int, bytes: ByteArray?) =
-                    if (TextUtils.isEmpty(Util.API_KEY) || TextUtils.isEmpty(Util.API_SECRET)) {
-                        if (!ConUtil.isReadKey(this@LoginActivity)) {
-                            authState(false, 1001, "")
-                        } else {
-                            authState(false, 1001, "")
-                        }
-                    } else {
-                        var msg = ""
-                        if (bytes != null && bytes.isNotEmpty()) {
-                            msg = String(bytes)
-                        }
-                        authState(false, i, msg)
-                    }
-        })
-    }
-
-    private fun authState(isSuccess: Boolean, code: Int, message: String) {
-        XLog.info("Facepp  License  check $isSuccess, code:$code, message:$message")
-        faceppLicenseIsOK = isSuccess
-    }
-
-
 
     ///////////////play media////////////
     /**

+ 2 - 7
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/login/LoginContract.kt

@@ -1,11 +1,8 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.login
 
-import com.facepp.demo.util.ICamera
-import com.megvii.facepp.sdk.Facepp
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenter
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseView
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.AuthenticationInfoJson
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo.LoginHistoryVO
 
 /**
  * Created by fancy on 2017/6/8.
@@ -37,10 +34,8 @@ object LoginContract {
          */
         fun loginByPassword(userName: String, password: String)
 
-        /**
-         * 人脸识别 是否在库里
-         */
-        fun searchFace(imgData: ByteArray, faces: Array<Facepp.Face> ,mICamera: ICamera, isBackCamera: Boolean)
+
+        fun ssoLogin(userId: String)
 
 
     }

+ 29 - 90
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/login/LoginPresenter.kt

@@ -1,24 +1,15 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.login
 
-import com.facepp.demo.util.ConUtil
-import com.facepp.demo.util.ICamera
-import com.megvii.facepp.sdk.Facepp
+
 import net.muliba.accounting.app.ExceptionHandler
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2SDKManager
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenterImpl
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.APIAddressHelper
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.ResponseHandler
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.Base64ImageUtil
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.o2Subscribe
-import okhttp3.MediaType
-import okhttp3.MultipartBody
-import okhttp3.RequestBody
 import rx.android.schedulers.AndroidSchedulers
 import rx.schedulers.Schedulers
-import java.io.File
-import java.util.ArrayList
 
 /**
  * Created by fancy on 2017/6/8.
@@ -27,6 +18,7 @@ import java.util.ArrayList
 class LoginPresenter : BasePresenterImpl<LoginContract.View>(), LoginContract.Presenter {
 
 
+
     override fun getVerificationCode(value: String) {
         getAssembleAuthenticationService(mView?.getContext())?.let { service ->
             service.getPhoneCode(value)
@@ -75,90 +67,37 @@ class LoginPresenter : BasePresenterImpl<LoginContract.View>(), LoginContract.Pr
 
 
 
-    private var lock: Boolean = false
-    override fun searchFace(imgData: ByteArray, faces: Array<Facepp.Face>, mICamera: ICamera, isBackCamera: Boolean) {
-        if (lock) {
-            XLog.info("search ing ..............")
-            return
-        }
-        lock = true
-        val list = saveFaceAsFile(faces, mICamera, imgData, isBackCamera)
-        if (list.isNotEmpty()) {
-            val filePath = list[0]
-            val file = File(filePath)
-            if (file.exists()) {
-                val baseUrl = APIAddressHelper.instance().getFaceppServerUrl()
-                val faceset = O2SDKManager.instance().prefs().getString(O2.PRE_BIND_UNIT_ID_KEY, "")
-                XLog.debug("baseUrl:$baseUrl ,faceset:$faceset")
-                val requestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file)
-                val body = MultipartBody.Part.createFormData("file", file.name, requestBody)
-                val service = getFaceppService(baseUrl, mView?.getContext())
-                val ssoService = getAssembleAuthenticationService(mView?.getContext())
-                if (service!=null) {
-                    service.searchFace(body, "dev_o2oa_io")
-                            .subscribeOn(Schedulers.io())
-                            .flatMap { res->
-                                val data = res.data
-                                if (data!=null && data.results.isNotEmpty()) {
-                                    val userid = data.results[0].user_id
-                                    val enCode = Base64ImageUtil.ssoUserIdDesCode(userid)
-                                    XLog.info("识别成功,userId:$userid, 加密后:$enCode")
-                                    if (ssoService!=null) {
-                                        val jsonMap = HashMap<String, String>()
-                                        jsonMap["client"] = O2.O2_CLIENT
-                                        jsonMap["token"] = enCode
-                                        ssoService.sso(jsonMap)
-                                    }else{
-                                        throw Exception("")
-                                    }
-                                }else{
-                                    throw Exception("没有识别到这个人脸")
-                                }
-                            }
-                            .observeOn(AndroidSchedulers.mainThread())
-                            .o2Subscribe {
-                                onNext { res->
-                                    if (res != null && res.data != null) {
-                                        mView?.loginSuccess(res.data)
-                                        lock = true//不再执行searchFace了
-                                    }else {
-                                        XLog.error("没有登录成功的信息。。。。。。。")
-                                        lock = false
-                                    }
-                                }
-                                onError { e, isNetworkError ->
-                                    XLog.error("没有识别到, $isNetworkError", e)
-                                    lock = false
-                                }
 
+    override fun ssoLogin(userId: String) {
+        val ssoService = getAssembleAuthenticationService(mView?.getContext())
+        val enCode = Base64ImageUtil.ssoUserIdDesCode(userId)
+        XLog.info("识别成功,userId:$userId, 加密后:$enCode")
+        if (ssoService!=null) {
+            val jsonMap = HashMap<String, String>()
+            jsonMap["client"] = O2.O2_CLIENT
+            jsonMap["token"] = enCode
+            ssoService.sso(jsonMap)
+                    .subscribeOn(Schedulers.io())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .o2Subscribe {
+                        onNext { res->
+                            if (res != null && res.data != null) {
+                                mView?.loginSuccess(res.data)
+                            }else {
+                                XLog.error("没有登录成功的信息。。。。。。。")
+                                mView?.loginFail()
                             }
-                }else{
-                    XLog.error("人脸识别服务不存在。。。")
-                    lock = false
-                }
-            }else {
-                XLog.error("生成的人脸识别文件不存在。。。。")
-                lock = false
-            }
+                        }
+                        onError { e, isNetworkError ->
+                            XLog.error("没有识别到, $isNetworkError", e)
+                            mView?.loginFail()
+                        }
+
+                    }
         }else {
-            XLog.error("生成的人脸识别文件不存在。。。。")
-            lock = false
+            mView?.loginFail()
         }
-
     }
 
-    private fun saveFaceAsFile(faces: Array<Facepp.Face>, mICamera: ICamera, carmeraImgData: ByteArray, isBackCamera: Boolean): List<String> {
-        val imgs = ArrayList<String>()
-        for (i in faces.indices) {
-            val face = faces[i]
-            val rect = face.rect
-            val bitmap = mICamera.getBitMapWithRect(carmeraImgData, mICamera.mCamera, !isBackCamera, rect)
-            if (bitmap != null) {
-                val filePath = ConUtil.saveBitmap(mView?.getContext(), bitmap)
-                XLog.info("file path:$filePath")
-                imgs.add(filePath)
-            }
-        }
-        return imgs
-    }
+
 }

+ 6 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/main/IndexFragment.kt

@@ -13,6 +13,7 @@ import android.view.View
 import android.widget.ImageView
 import android.widget.LinearLayout
 import com.bigkoo.convenientbanner.ConvenientBanner
+import io.flutter.app.FlutterActivity
 import kotlinx.android.synthetic.main.fragment_main_todo.*
 import kotlinx.android.synthetic.main.snippet_shimmer_content.*
 import net.muliba.changeskin.FancySkinManager
@@ -38,6 +39,7 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.adapter.ConvenientBa
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.APIAddressHelper
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.enums.ApplicationEnum
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.enums.HotPictureApplicationEnum
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.flutter.FlutterConnectActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.cms.CMSDocumentInfoJson
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.HotPictureOutData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.TaskData
@@ -80,6 +82,10 @@ class IndexFragment : BaseMVPViewPagerFragment<IndexContract.View, IndexContract
                 ApplicationEnum.MEETING.key -> activity.go<MeetingMainActivity>()
                 ApplicationEnum.ATTENDANCE.key -> activity.go<AttendanceMainActivity>()
                 ApplicationEnum.CALENDAR.key -> activity.go<CalendarMainActivity>()
+                ApplicationEnum.MindMap.key -> {
+                    XLog.info("脑图")
+                    activity.go<FlutterConnectActivity>(FlutterConnectActivity.startFlutterAppWithRoute(ApplicationEnum.MindMap.key))
+                }
                 ApplicationEnum.O2AI.key -> {
                     PermissionRequester(activity)
                             .request(Manifest.permission.RECORD_AUDIO)

+ 75 - 2
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/main/IndexPortalFragment.kt

@@ -1,9 +1,14 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.main
 
 import android.Manifest
+import android.content.Intent
+import android.net.Uri
+import android.net.http.SslError
 import android.os.Bundle
 import android.text.TextUtils
 import android.webkit.JavascriptInterface
+import android.webkit.SslErrorHandler
+import android.webkit.WebView
 import android.webkit.WebViewClient
 import kotlinx.android.synthetic.main.fragment_index_portal.*
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
@@ -16,6 +21,7 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.process.ReadCompletedListAct
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.process.ReadListActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.process.TaskCompletedListActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.process.TaskListActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview.PortalWebViewActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview.TaskWebViewActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.APIAddressHelper
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.cms.CMSApplicationInfoJson
@@ -62,9 +68,15 @@ class IndexPortalFragment: BaseMVPViewPagerFragment<IndexPortalContract.View, In
         }else {
             portalUrl = APIAddressHelper.instance().getPortalWebViewUrl(portalId)
             XLog.debug("portal url : $portalUrl")
-            web_view_portal_content.addJavascriptInterface(this, "o2") //注册js对象
+            web_view_portal_content.addJavascriptInterface(this, "o2android") //注册js对象
             web_view_portal_content.webViewSetCookie(activity, portalUrl)
-            web_view_portal_content.webViewClient = WebViewClient()
+            web_view_portal_content.webViewClient = object : WebViewClient() {
+                override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
+                    XLog.error("ssl error, $error")
+                    handler?.proceed()
+                }
+
+            }
             loadWebview()
         }
 
@@ -102,6 +114,19 @@ class IndexPortalFragment: BaseMVPViewPagerFragment<IndexPortalContract.View, In
         }
     }
 
+    /**
+     * 是否含有ActionBar
+     */
+    @JavascriptInterface
+    fun actionBarLoaded(flag: String) {
+        XLog.debug("actionBarLoaded.......$flag")
+        if (!TextUtils.isEmpty(flag)) {
+            if (activity != null && activity is PortalWebViewActivity) {
+                (activity as PortalWebViewActivity).hideToolBar()
+            }
+        }
+    }
+
     /**
      * js 调用  window.o2.loadUrl(url)
      */
@@ -139,6 +164,23 @@ class IndexPortalFragment: BaseMVPViewPagerFragment<IndexPortalContract.View, In
         activity.go<CalendarMainActivity>()
     }
     @JavascriptInterface
+    fun openDingtalk(result: String) {
+        XLog.debug("open钉钉。。。。。。")
+        val intent = Intent(Intent.ACTION_VIEW)
+        val jumpUrl = "dingtalk://dingtalkclient/page/link?url="
+        intent.data = Uri.parse(jumpUrl)
+        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+        try {
+            if (null != intent.resolveActivity(context.packageManager)) {
+                context.startActivity(intent)
+            }else {
+                XLog.info("找不到。。。。")
+            }
+        } catch (e: Exception) {
+            XLog.error("", e)
+        }
+    }
+    @JavascriptInterface
     fun openScan(result: String) {
         XLog.debug("open scan ........")
         activity.runOnUiThread{
@@ -170,4 +212,35 @@ class IndexPortalFragment: BaseMVPViewPagerFragment<IndexPortalContract.View, In
         }
     }
 
+    @JavascriptInterface
+    fun closeNativeWindow(result: String) {
+        XLog.info("result:$result")
+        if (result == "true") {
+            if (activity != null) {
+                when (activity) {
+                    is PortalWebViewActivity -> {
+                        activity?.finish()
+                    }
+                    else -> {
+                        XLog.error("what Activity 。。。。。。。。。")
+                    }
+                }
+            }
+
+        }
+    }
+
+    /**
+     * 弹出窗 js调试用
+     */
+    @JavascriptInterface
+    fun openO2Alert(message: String?) {
+        if (message != null) {
+            XLog.debug("弹出窗。。message:$message")
+            activity?.runOnUiThread {
+                O2DialogSupport.openAlertDialog(activity, message)
+            }
+        }
+    }
+
 }

+ 11 - 10
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/main/SettingsFragment.kt

@@ -17,9 +17,7 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.BitmapUtil
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.HttpCacheUtil
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XToast
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.go
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.goAndClearBefore
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.o2Subscribe
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.*
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.permission.PermissionRequester
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.AndroidShareDialog
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialog.O2AlertIconEnum
@@ -49,13 +47,18 @@ class SettingsFragment : BaseMVPViewPagerFragment<SettingsContract.View, Setting
         setting_button_about_id.setOnClickListener(this)
         setting_button_remind_setting_id.setOnClickListener(this)
         setting_button_common_set_id.setOnClickListener(this)
-        setting_button_customer_service_id.setOnClickListener(this)
+        if (BuildConfig.InnerServer) {
+            id_setting_button_customer_service_split.gone()
+            setting_button_customer_service_id.gone()
+        }else {
+            id_setting_button_customer_service_split.visible()
+            setting_button_customer_service_id.visible()
+            setting_button_customer_service_id.setOnClickListener(this)
+        }
+
         setting_button_feedback_id.setOnClickListener(this)
         myInfo_logout_btn_id.setOnClickListener(this)
 
-//        setting_button_ai_bluetooth.setOnClickListener {
-//            activity.go<BlueToothClientActivity>()
-//        }
         val path = O2CustomStyle.setupAboutImagePath(activity)
         if (!TextUtils.isEmpty(path)) {
             BitmapUtil.setImageFromFile(path!!, setting_image_about_icon)
@@ -70,9 +73,6 @@ class SettingsFragment : BaseMVPViewPagerFragment<SettingsContract.View, Setting
             R.id.setting_button_skin -> activity.go<SkinManagerActivity>()
             R.id.setting_button_remind_setting_id -> activity.go<NoticeSettingActivity>()
             R.id.setting_button_common_set_id -> {
-                //test change index fragment
-//                activity.go<FileReaderActivity>()
-
                 O2DialogSupport.openConfirmDialog(activity, "确认要清除缓存吗?", {
                     HttpCacheUtil.clearCache(activity, 0)
                 }, O2AlertIconEnum.CLEAR)
@@ -99,6 +99,7 @@ class SettingsFragment : BaseMVPViewPagerFragment<SettingsContract.View, Setting
     }
 
     private fun logout() {
+        XLog.debug("acti ity: ${activity == null}")
         O2DialogSupport.openConfirmDialog(activity, "确定要退出登录吗?", {
             mPresenter.logout()
         })

+ 19 - 19
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/StartProcessStepTwoFragment.kt

@@ -44,7 +44,7 @@ class StartProcessStepTwoFragment : BaseMVPFragment<StartProcessStepTwoContract.
     var processId = ""
     var processName = ""
     var identity = ""
-    var globalLayoutListener: ViewTreeObserver.OnGlobalLayoutListener? = null
+//    var globalLayoutListener: ViewTreeObserver.OnGlobalLayoutListener? = null
     override fun initUI() {
         val startString = getString(R.string.title_activity_start_process_step_two)
         (activity as StartProcessActivity).setToolBarTitle(startString)
@@ -54,29 +54,29 @@ class StartProcessStepTwoFragment : BaseMVPFragment<StartProcessStepTwoContract.
         tv_start_process_step_two_time.text = DateHelper.nowByFormate("yyyy-MM-dd HH:mm")
         btn_start_process_step_two_positive.setOnClickListener { startProcess() }
         btn_start_process_step_two_cancel.setOnClickListener { (activity as StartProcessActivity).finish() }
-        edit_start_process_step_two_title.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
-            override fun onGlobalLayout() {
-                globalLayoutListener = this
-                val screenHeight = activity.window.decorView.rootView.height
-                val screenWidth = activity.window.decorView.rootView.width
-                val rect = Rect()
-                activity.window.decorView.getWindowVisibleDisplayFrame(rect)
-                val height = screenHeight - (rect.bottom -rect.top)
-                if (height > screenHeight/3) {
-                    emptySpaceView(true, screenWidth, height)
-                }else {
-                    emptySpaceView(false)
-                }
-            }
-        })
+//        edit_start_process_step_two_title.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
+//            override fun onGlobalLayout() {
+//                globalLayoutListener = this
+//                val screenHeight = activity.window.decorView.rootView.height
+//                val screenWidth = activity.window.decorView.rootView.width
+//                val rect = Rect()
+//                activity.window.decorView.getWindowVisibleDisplayFrame(rect)
+//                val height = screenHeight - (rect.bottom -rect.top)
+//                if (height > screenHeight/3) {
+//                    emptySpaceView(true, screenWidth, height)
+//                }else {
+//                    emptySpaceView(false)
+//                }
+//            }
+//        })
 
         mPresenter.loadCurrentPersonIdentityWithProcess(processId)
     }
 
     override fun onDestroyView() {
-        if (globalLayoutListener!=null) {
-            edit_start_process_step_two_title.viewTreeObserver.removeOnGlobalLayoutListener(globalLayoutListener)
-        }
+//        if (globalLayoutListener!=null) {
+//            edit_start_process_step_two_title.viewTreeObserver.removeOnGlobalLayoutListener(globalLayoutListener)
+//        }
         super.onDestroyView()
         XLog.debug("StartProcessStepTwoFragment onDestroyView...............")
     }

+ 73 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/security/AccountSecurityActivity.kt

@@ -7,6 +7,11 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.*
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.bind.BindPhoneActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.my.MyInfoActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XToast
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.biometric.BioConstants
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.biometric.BiometryManager
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.biometric.OnBiometryAuthCallback
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.*
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialog.O2DialogSupport
 
@@ -33,6 +38,9 @@ class AccountSecurityActivity : BaseMVPActivity<AccountSecurityContract.View, Ac
 
         val unitName = O2SDKManager.instance().prefs().getString(O2.PRE_BIND_UNIT_KEY, "")
         tv_account_security_unit_name.text = "当前绑定服务器:$unitName"
+
+        initBiometryAuthView()
+
     }
 
     override fun logoutSuccess() {
@@ -43,6 +51,71 @@ class AccountSecurityActivity : BaseMVPActivity<AccountSecurityContract.View, Ac
         goAndClearBefore<BindPhoneActivity>()
     }
 
+    private val bioManager: BiometryManager by lazy { BiometryManager(this) }
+    private fun initBiometryAuthView() {
+
+        if (bioManager.isBiometricPromptEnable()) {
+            image_btn_account_security_biometry_enable.setOnClickListener {
+                bioManager.authenticate(object : OnBiometryAuthCallback{
+                    override fun onUseFallBack() {
+                        XLog.error("点击了《其他方式》按钮。。。。。")
+                    }
+
+                    override fun onSucceeded() {
+                        XLog.debug("指纹识别验证成功")
+                        val userId = O2SDKManager.instance().prefs().getString(BioConstants.O2_bio_auth_user_id_prefs_key, "") ?: ""
+                        XToast.toastShort(this@AccountSecurityActivity, "验证成功")
+                        if (userId.isNotEmpty()) {
+                            setBioAuthResult("")
+                        }else {
+                            setBioAuthResult(O2SDKManager.instance().cId)
+                        }
+                    }
+
+                    override fun onFailed() {
+                        XLog.error("指纹识别验证失败了。。。。。")
+                        //XToast.toastShort(this@AccountSecurityActivity, "验证失败")
+                    }
+
+                    override fun onError(code: Int, reason: String) {
+                        XLog.error("指纹识别验证出错,code:$code , reason:$reason")
+                        //XToast.toastShort(this@AccountSecurityActivity, "验证失败,$reason")
+                    }
+
+                    override fun onCancel() {
+                        XLog.info("指纹识别取消了。。。。。")
+                    }
+
+                })
+            }
+            tv_account_security_biometry_name.text = "指纹识别登录"
+            val userId = O2SDKManager.instance().prefs().getString(BioConstants.O2_bio_auth_user_id_prefs_key, "") ?: ""
+            if (userId.isNotEmpty()) {
+                image_btn_account_security_biometry_enable.setImageResource(R.mipmap.icon_toggle_on_29dp)
+            }else {
+                image_btn_account_security_biometry_enable.setImageResource(R.mipmap.icon_toggle_off_29dp)
+            }
+        }else {
+            tv_account_security_biometry_name.text = "指纹识别登录不可用"
+            image_btn_account_security_biometry_enable.setImageResource(R.mipmap.icon_toggle_off_29dp)
+            image_btn_account_security_biometry_enable.setOnClickListener {
+                XToast.toastShort(this, "指纹识别不可用或未启用!")
+            }
+        }
+    }
+
+    private fun setBioAuthResult(userId: String) {
+        O2SDKManager.instance().prefs().edit{
+            putString(BioConstants.O2_bio_auth_user_id_prefs_key, userId)
+        }
+        if (userId.isNotEmpty()) {
+            image_btn_account_security_biometry_enable.setImageResource(R.mipmap.icon_toggle_on_29dp)
+        }else {
+            image_btn_account_security_biometry_enable.setImageResource(R.mipmap.icon_toggle_off_29dp)
+        }
+
+    }
+
 
     private fun changeMobile() {
         O2DialogSupport.openConfirmDialog(this, "确定要重新绑定手机号码吗,该操作会清空当前登录信息,需要重新登录?", {

+ 4 - 4
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/skin/SkinManagerActivity.kt

@@ -47,10 +47,10 @@ class SkinManagerActivity : AppCompatActivity() {
 
         }
         changeButton()
-
-        card_skin_manager_puppy2018.setOnClickListener {
-            go<SkinShowActivity>()
-        }
+//
+//        card_skin_manager_puppy2018.setOnClickListener {
+//            go<SkinShowActivity>()
+//        }
     }
 
     override fun onResume() {

+ 9 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/PortalWebViewActivity.kt

@@ -3,12 +3,16 @@ package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview
 import android.os.Bundle
 import android.text.TextUtils
 import android.view.KeyEvent
+import android.view.View
+import kotlinx.android.synthetic.main.snippet_appbarlayout_toolbar.*
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.main.IndexPortalFragment
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.APIAddressHelper
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XToast
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.gone
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.visible
 
 class PortalWebViewActivity : BaseMVPActivity<PortalWebViewContract.View, PortalWebViewContract.Presenter>(), PortalWebViewContract.View  {
     override var mPresenter: PortalWebViewContract.Presenter = PortalWebViewPresenter()
@@ -54,6 +58,7 @@ class PortalWebViewActivity : BaseMVPActivity<PortalWebViewContract.View, Portal
                 }
             }
         }
+        toolbar?.visibility = View.GONE
     }
 
     override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
@@ -70,4 +75,8 @@ class PortalWebViewActivity : BaseMVPActivity<PortalWebViewContract.View, Portal
         super.finish()
         overridePendingTransition(R.anim.activity_scale_in, R.anim.activity_scale_out)
     }
+
+    fun hideToolBar() {
+        app_bar_layout_snippet.gone()
+    }
 }

+ 227 - 25
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/TaskWebViewActivity.kt

@@ -1,34 +1,38 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview
 
 
+import android.Manifest
 import android.app.Activity
 import android.content.Intent
+import android.graphics.Bitmap
+import android.net.Uri
+import android.net.http.SslError
 import android.os.Bundle
+import android.provider.MediaStore
 import android.text.TextUtils
 import android.view.View
-import android.webkit.JavascriptInterface
-import android.webkit.WebView
-import android.webkit.WebViewClient
+import android.webkit.*
 import android.widget.EditText
 import android.widget.LinearLayout
 import android.widget.RadioButton
 import android.widget.RadioGroup
 import kotlinx.android.synthetic.main.activity_work_web_view.*
 import net.muliba.fancyfilepickerlibrary.FilePicker
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2App
+import net.muliba.fancyfilepickerlibrary.PicturePicker
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2SDKManager
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.APIAddressHelper
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.WorkControl
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ReadData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.TaskData
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.WorkControl
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.WorkOpinionData
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.AndroidUtils
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XToast
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.ZoneUtil
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo.O2UploadImageData
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.*
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.o2Subscribe
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.visible
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.permission.PermissionRequester
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.BottomSheetMenu
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialog.O2DialogSupport
 import java.io.File
 
@@ -55,6 +59,8 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
 
     val WORK_WEB_VIEW_UPLOAD_REQUEST_CODE = 1001
     val WORK_WEB_VIEW_REPLACE_REQUEST_CODE = 1002
+    val TAKE_FROM_PICTURES_CODE = 1003
+    val TAKE_FROM_CAMERA_CODE = 1004
 
     var title = ""
     var workId = ""
@@ -71,7 +77,14 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
     val routeNameList = ArrayList<String>()
 
     val downloadDocument: DownloadDocument by lazy { DownloadDocument(this) }
+    val cameraImageUri: Uri by lazy { FileUtil.getUriFromFile(this, File(FileExtensionHelper.getCameraCacheFilePath())) }
+    var imageUploadData: O2UploadImageData? = null
 
+    //web端网页文件选择支持
+    //5.0以下使用
+    private var uploadMessage: ValueCallback<Uri>? = null
+    // 5.0及以上使用
+    private var uploadMessageAboveL: ValueCallback<Array<Uri>>? = null
 
 
     override fun afterSetContentView(savedInstanceState: Bundle?) {
@@ -93,10 +106,36 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
         XLog.debug("title:$title ,  url:$url")
         setupToolBar(title, true)
 
-        web_view.addJavascriptInterface(this, "o2")
-        web_view.setWebViewClient(object : WebViewClient() {
+        web_view.addJavascriptInterface(this, "o2android")
+        web_view.webChromeClient = object : WebChromeClient() {
+
+//            override fun onProgressChanged(view: WebView, newProgress: Int) {
+//                if (newProgress == 100) {
+//                    web_view.progressBar.visibility = View.GONE
+//                } else {
+//                    if (web_view.progressBar.visibility == View.GONE)
+//                        progressBar.visibility = View.VISIBLE
+//                    progressBar.progress = newProgress
+//                }
+//                super.onProgressChanged(view, newProgress)
+//            }
+
+            // For Android >= 5.0
+            override fun onShowFileChooser(webView: WebView, filePathCallback: ValueCallback<Array<Uri>>, fileChooserParams: WebChromeClient.FileChooserParams): Boolean {
+                XLog.debug("选择文件 5。0。。。。。。。。。。。。。。。。。")
+                uploadMessageAboveL = filePathCallback
+                showPictureChooseMenu()
+                return true
+            }
+
+        }
+        web_view.webViewClient = object : WebViewClient() {
+            override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
+                XLog.error("ssl error, $error")
+                handler?.proceed()
+            }
             override fun shouldOverrideUrlLoading(view: WebView?, url: String): Boolean {
-                XLog.debug("shouldOverrideUrlLoading:" + url)
+                XLog.debug("shouldOverrideUrlLoading:$url")
                 if (ZoneUtil.checkUrlIsInner(url)) {
                     view?.loadUrl(url)
                 } else {
@@ -105,11 +144,7 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
                 return true
             }
 
-            override fun onPageFinished(view: WebView?, url: String?) {
-                super.onPageFinished(view, url)
-
-            }
-        })
+        }
 
 
         web_view.webViewSetCookie(this, url)
@@ -124,7 +159,7 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
                 WORK_WEB_VIEW_UPLOAD_REQUEST_CODE -> {
                     val result = data?.getStringExtra(FilePicker.FANCY_FILE_PICKER_SINGLE_RESULT_KEY)
                     if (!TextUtils.isEmpty(result)) {
-                        XLog.debug("uri path:" + result)
+                        XLog.debug("uri path:$result")
                         showLoadingDialog()
                         mPresenter.uploadAttachment(result!!, site, workId)
                     } else {
@@ -134,13 +169,51 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
                 WORK_WEB_VIEW_REPLACE_REQUEST_CODE -> {
                     val result = data?.getStringExtra(FilePicker.FANCY_FILE_PICKER_SINGLE_RESULT_KEY)
                     if (!TextUtils.isEmpty(result)) {
-                        XLog.debug("uri path:" + result)
+                        XLog.debug("uri path:$result")
                         showLoadingDialog()
                         mPresenter.replaceAttachment(result!!, site, attachmentId, workId)
                     } else {
                         XLog.error("FilePicker 没有返回值!")
                     }
                 }
+                TAKE_FROM_PICTURES_CODE -> {
+                    //选择照片
+                    data?.let {
+                        val result = it.extras.getString(PicturePicker.FANCY_PICTURE_PICKER_SINGLE_RESULT_KEY, "")
+                        if (!TextUtils.isEmpty(result)){
+                            XLog.debug("照片 path:$result")
+                            when {
+                                uploadMessage!=null -> {
+                                    val uri = FileUtil.getUriFromFile(this, File(result))
+                                    uploadMessage?.onReceiveValue(uri)
+                                }
+                                uploadMessageAboveL!=null -> {
+                                    val uri = FileUtil.getUriFromFile(this, File(result))
+                                    val list = ArrayList<Uri>()
+                                    list.add(uri)
+                                    uploadMessageAboveL?.onReceiveValue(list.toTypedArray())
+                                }
+                                else -> uploadImage2FileStorageStart(result)
+                            }
+                        }
+                    }
+                }
+                TAKE_FROM_CAMERA_CODE -> {
+                    //拍照
+                    XLog.debug("拍照//// ")
+                    when {
+                        uploadMessage!=null -> {
+                            uploadMessage?.onReceiveValue(cameraImageUri)
+                        }
+                        uploadMessageAboveL!=null -> {
+                            val list = ArrayList<Uri>()
+                            list.add(cameraImageUri)
+                            uploadMessageAboveL?.onReceiveValue(list.toTypedArray())
+                        }
+                        else -> uploadImage2FileStorageStart(FileExtensionHelper.getCameraCacheFilePath())
+                    }
+
+                }
             }
         }
     }
@@ -197,6 +270,7 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
      */
     @JavascriptInterface
     fun appFormLoaded(result: String) {// 获取control 动态生成操作按钮
+        XLog.debug("表单加载完成回调:$result")
         runOnUiThread {
             if (TextUtils.isEmpty(title)) {
                 web_view.evaluateJavascript("layout.appForm.businessData.work.title") { value ->
@@ -270,7 +344,7 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
      */
     @JavascriptInterface
     fun downloadAttachment(attachmentId: String) {
-        XLog.debug("download attachmentId:" + attachmentId)
+        XLog.debug("download attachmentId:$attachmentId")
         if (TextUtils.isEmpty(attachmentId)) {
             XLog.error("调用失败,附件id没有传入!")
             return
@@ -296,6 +370,37 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
         }
     }
 
+    /**
+     * 弹出窗 js调试用
+     */
+    @JavascriptInterface
+    fun openO2Alert(message: String?) {
+        if (message != null) {
+            XLog.debug("弹出窗。。message:$message")
+            runOnUiThread {
+                O2DialogSupport.openAlertDialog(this, message)
+            }
+        }
+    }
+
+    /**
+     * 图片控件
+     */
+    @JavascriptInterface
+    fun uploadImage2FileStorage(json: String?) {
+        imageUploadData = null
+        XLog.debug("打开图片上传控件, $json")
+        runOnUiThread {
+            if (json != null) {
+                imageUploadData = O2SDKManager.instance().gson.fromJson(json, O2UploadImageData::class.java)
+                showPictureChooseMenu()
+
+            }else {
+                XToast.toastShort(this, "没有传入对象")
+            }
+        }
+    }
+
 
     //MARK: - view implements
 
@@ -303,6 +408,10 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
         hideLoadingDialog()
     }
 
+    override fun saveSuccess() {
+        hideLoadingDialog()
+        XToast.toastShort(this, "保存成功!")
+    }
     override fun submitSuccess() {
         hideLoadingDialog()
         finish()
@@ -320,8 +429,8 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
     }
 
     override fun retractFail() {
-        XToast.toastShort(this, "撤回失败!")
         hideLoadingDialog()
+        XToast.toastShort(this, "撤回失败!")
     }
 
     override fun deleteSuccess() {
@@ -331,8 +440,8 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
     }
 
     override fun deleteFail() {
-        XToast.toastShort(this, "删除失败!")
         hideLoadingDialog()
+        XToast.toastShort(this, "删除失败!")
     }
 
     override fun uploadAttachmentSuccess(attachmentId: String, site: String) {
@@ -353,7 +462,7 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
 
     override fun downloadAttachmentSuccess(file: File) {
         hideLoadingDialog()
-        if (file != null && file.exists()) AndroidUtils.openFileWithDefaultApp(this, file)
+        if (file.exists()) AndroidUtils.openFileWithDefaultApp(this, file)
     }
 
     override fun invalidateArgs() {
@@ -361,11 +470,30 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
     }
 
     override fun downloadFail(message: String) {
-        XToast.toastShort(this, message)
         finishLoading()
+        XToast.toastShort(this, message)
     }
 
+    override fun upload2FileStorageFail(message: String) {
+        hideLoadingDialog()
+        XToast.toastShort(this, message)
+    }
 
+    override fun upload2FileStorageSuccess(id: String) {
+        hideLoadingDialog()
+        if (imageUploadData != null) {
+            imageUploadData!!.fileId = id
+            val callback = imageUploadData!!.callback
+            val json = O2SDKManager.instance().gson.toJson(imageUploadData)
+            val js = "$callback('$json')"
+            XLog.debug("执行js:$js")
+            web_view.evaluateJavascript(js){
+                value -> XLog.debug("replacedAttachment, onReceiveValue value=$value")
+            }
+        }else {
+            XLog.error("图片控件对象不存在。。。。。。。。")
+        }
+    }
 
     //MARK: - private function
 
@@ -471,6 +599,12 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
     private fun evaluateJavascriptGetFormData() {
         web_view.evaluateJavascript("layout.appForm.getData()") { value ->
             XLog.debug("evaluateJavascriptGetFormData, onReceiveValue save value=$value")
+            if (value == null) {
+                runOnUiThread {
+                    XToast.toastShort(getContext(), "没有获取到表单数据!")
+                }
+                return@evaluateJavascript
+            }
             formData = value
             showLoadingDialog()
             mPresenter.save(workId, value)
@@ -517,7 +651,7 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
 
     private fun validateFormBeforeSubmit(routeName: String, opinion: String, data: TaskData) {
         web_view.evaluateJavascript("layout.appForm.formValidation(\"$routeName\", \"$opinion\")") { value ->
-            XLog.debug("validateFormBeforeSubmit,value:" + value)
+            XLog.debug("validateFormBeforeSubmit,value:$value")
             if ("true" == value) {
                 data.opinion = opinion
                 data.routeName = routeName
@@ -529,6 +663,64 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
         }
     }
 
+
+
+    private fun showPictureChooseMenu() {
+        BottomSheetMenu(this)
+                .setTitle("上传照片")
+                .setItem("从相册选择", resources.getColor(R.color.z_color_text_primary)) {
+                    takeFromPictures()
+                }
+                .setItem("拍照", resources.getColor(R.color.z_color_text_primary)) {
+                    takeFromCamera()
+                }
+                .setCancelButton("取消", resources.getColor(R.color.z_color_text_hint)) {
+                    XLog.debug("取消。。。。。")
+                    if (uploadMessage!=null){
+                        uploadMessage?.onReceiveValue(null)
+                    }else if (uploadMessageAboveL!=null) {
+                        uploadMessageAboveL?.onReceiveValue(null)
+                    }
+                }
+                .show()
+    }
+
+    private fun takeFromPictures() {
+        PicturePicker()
+                .withActivity(this)
+                .chooseType(PicturePicker.CHOOSE_TYPE_SINGLE)
+                .requestCode(TAKE_FROM_PICTURES_CODE)
+                .start()
+    }
+
+    private fun takeFromCamera() {
+        PermissionRequester(this).request(Manifest.permission.CAMERA)
+                .o2Subscribe {
+                    onNext { (granted, shouldShowRequestPermissionRationale, deniedPermissions) ->
+                        XLog.info("granted:$granted , shouldShowRequest:$shouldShowRequestPermissionRationale, denied:$deniedPermissions")
+                        if (!granted) {
+                            O2DialogSupport.openAlertDialog(this@TaskWebViewActivity, "非常抱歉,相机权限没有开启,无法使用相机!")
+                        } else {
+                            openCamera()
+                        }
+                    }
+                }
+    }
+
+
+    private fun openCamera() {
+        val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
+        //return-data false 不是直接返回拍照后的照片Bitmap 因为照片太大会传输失败
+        intent.putExtra("return-data", false)
+        //改用Uri 传递
+        intent.putExtra(MediaStore.EXTRA_OUTPUT, cameraImageUri)
+        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString())
+        intent.putExtra("noFaceDetection", true)
+        startActivityForResult(intent, TAKE_FROM_CAMERA_CODE)
+    }
+
+
+
     private fun openFancyFilePicker(requestCode: Int) {
         FilePicker().withActivity(this).requestCode(requestCode)
                 .chooseType(FilePicker.CHOOSE_TYPE_SINGLE)
@@ -537,5 +729,15 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
 
 
 
+    private fun uploadImage2FileStorageStart(filePath: String) {
+        showLoadingDialog()
+        if (imageUploadData != null) {
+            mPresenter.upload2FileStorage(filePath, imageUploadData!!.referencetype, imageUploadData!!.reference)
+        }else {
+            finishLoading()
+            XToast.toastShort(this, "上传文件参数为空!!!")
+        }
+    }
+
 
 }

+ 6 - 1
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/TaskWebViewContract.kt

@@ -11,6 +11,7 @@ object TaskWebViewContract {
     interface View : BaseView {
         fun finishLoading()
         fun submitSuccess()
+        fun saveSuccess()
         fun setReadCompletedSuccess()
         fun uploadAttachmentSuccess(attachmentId:String, site:String)
         fun replaceAttachmentSuccess(attachmentId:String, site:String)
@@ -21,16 +22,20 @@ object TaskWebViewContract {
         fun retractFail()
         fun deleteSuccess()
         fun deleteFail()
+        fun upload2FileStorageFail(message: String)
+        fun upload2FileStorageSuccess(id: String)
     }
 
     interface Presenter : BasePresenter<View> {
         fun uploadAttachment(attachmentFilePath: String, site: String, workId: String)
         fun replaceAttachment(attachmentFilePath: String, site: String, attachmentId: String, workId: String)
         fun downloadAttachment(attachmentId: String, workId: String)
-        fun save(workId: String, formData: String?)
+        fun save(workId: String, formData: String)
         fun submit(data: TaskData?, workId: String, formData: String?)
         fun delete(workId: String)
         fun setReadComplete(read: ReadData?)
         fun retractWork(workId: String)
+
+        fun upload2FileStorage(filePath: String, referenceType: String , reference: String , scale: Int = 800)
     }
 }

+ 38 - 3
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/TaskWebViewPresenter.kt

@@ -12,6 +12,7 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ReadData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.TaskData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.WorkLog
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.FileExtensionHelper
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.FileUtil
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.SDCardHelper
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.o2Subscribe
@@ -30,7 +31,8 @@ import java.io.FileOutputStream
 
 class TaskWebViewPresenter : BasePresenterImpl<TaskWebViewContract.View>(), TaskWebViewContract.Presenter {
 
-    override fun save(workId: String, formData: String?) {
+
+    override fun save(workId: String, formData: String) {
         if (TextUtils.isEmpty(workId) || TextUtils.isEmpty(formData)) {
             mView?.invalidateArgs()
             XLog.error("arguments is null  workid:$workId, formData:$formData")
@@ -42,7 +44,7 @@ class TaskWebViewPresenter : BasePresenterImpl<TaskWebViewContract.View>(), Task
             service.saveTaskForm(body, workId)
                     .subscribeOn(Schedulers.io())
                     .observeOn(AndroidSchedulers.mainThread())
-                    .subscribe(ResponseHandler<IdData> { id -> mView?.finishLoading() },
+                    .subscribe(ResponseHandler<IdData> { _ -> mView?.saveSuccess() },
                             ExceptionHandler(mView?.getContext()) { e ->
                                 XLog.error("", e)
                                 mView?.finishLoading() })
@@ -142,7 +144,9 @@ class TaskWebViewPresenter : BasePresenterImpl<TaskWebViewContract.View>(), Task
                     .subscribeOn(Schedulers.io())
                     .observeOn(AndroidSchedulers.mainThread())
                     .subscribe(ResponseHandler<IdData> { id -> mView?.uploadAttachmentSuccess(id.id, site) },
-                            ExceptionHandler(mView?.getContext(), { e -> mView?.finishLoading() }))
+                            ExceptionHandler(mView?.getContext()) { e ->
+                                XLog.error("$e")
+                                mView?.finishLoading() })
         }
     }
 
@@ -257,6 +261,37 @@ class TaskWebViewPresenter : BasePresenterImpl<TaskWebViewContract.View>(), Task
     }
 
 
+    override fun upload2FileStorage(filePath: String, referenceType: String, reference: String, scale: Int) {
+        XLog.debug("上传图片,filePath:$filePath, referenceType:$referenceType, reference:$reference, scale:$scale")
+        if (filePath.isEmpty() || reference.isEmpty() || referenceType.isEmpty()) {
+            mView?.upload2FileStorageFail("传入参数不正确!")
+            return
+        }
+        val file = File(filePath)
+        if (!file.exists()) {
+            mView?.upload2FileStorageFail("文件不存在!!!")
+            return
+        }
+        val fileService = getFileAssembleControlService(mView?.getContext())
+        if (fileService!=null) {
+            val mediaType = FileUtil.getMIMEType(file)
+            val requestBody = RequestBody.create(MediaType.parse(mediaType), file)
+            val body = MultipartBody.Part.createFormData("file", file.name, requestBody)
+            fileService.uploadFile2ReferenceZone(body, referenceType, reference, scale)
+                    .subscribeOn(Schedulers.io())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .subscribe(ResponseHandler<IdData> {
+                        id -> mView?.upload2FileStorageSuccess(id.id)
+                    },
+                            ExceptionHandler(mView?.getContext()) { e ->
+                                XLog.error("$e")
+                                mView?.upload2FileStorageFail("文件上传异常") })
+        }else {
+            mView?.upload2FileStorageFail("文件模块接入异常!")
+        }
+
+    }
+
 
 
 }

+ 2 - 1
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/component/enums/ApplicationEnum.java

@@ -18,7 +18,8 @@ public enum ApplicationEnum {
     CMS("cms", "信息中心", R.mipmap.app_cms),
     ATTENDANCE("attendance", "考勤打卡", R.mipmap.app_attendance),
     O2AI("o2ai", "语音助手", R.mipmap.app_o2_ai),
-    CALENDAR("calendar", "日程安排", R.mipmap.app_calendar);
+    CALENDAR("calendar", "日程安排", R.mipmap.app_calendar),
+    MindMap("mindMap", "脑图", R.mipmap.app_mind_map);
 
 
 

+ 45 - 35
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/service/DownloadAPKService.kt

@@ -20,10 +20,8 @@ import java.net.HttpURLConnection
 import java.net.URL
 import android.app.NotificationChannel
 import android.os.Build
-
-
-
-
+import android.support.v4.content.LocalBroadcastManager
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.BuildConfig
 
 
 /**
@@ -35,20 +33,26 @@ import android.os.Build
 class DownloadAPKService : IntentService("DownloadAPKService") {
 
     companion object {
-        val DOWNLOAD_SERVICE_ACTION = "net.zoneland.x.bpm.mobile.v1.zoneXBPM.action.UPDATE"
+        val DOWNLOAD_SERVICE_ACTION = ".action.UPDATE"
         val VERSIN_NAME_EXTRA_NAME = "VERSIN_NAME_EXTRA_NAME"
         val DOWNLOAD_URL_EXTRA_NAME = "DOWNLOAD_URL_EXTRA_NAME"
+        val DOWNLOAD_RECIVER_ACTION_KEY = "DOWNLOAD_RECIVER_ACTION_KEY"
+        val DOWNLOAD_PROGRESS_KEY = "DOWNLOAD_PROGRESS_KEY"
     }
 
     private lateinit var downloadHandler: Handler
-    private val mNotificationManager: NotificationManager by lazy { getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager }
-    private var mRemoteViews: RemoteViews? = null
-    private var mNotification: Notification? = null
+//    private val mNotificationManager: NotificationManager by lazy { getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager }
+//    private var mRemoteViews: RemoteViews? = null
+//    private var mNotification: Notification? = null
 
     override fun onHandleIntent(intent: Intent?) {
         val action = intent?.action
         XLog.info("service action $action..........")
-        if (DOWNLOAD_SERVICE_ACTION == action) {
+        val applicationId = BuildConfig.APPLICATION_ID
+        XLog.info("applicationId:$applicationId")
+        val downloadAction = applicationId + DOWNLOAD_SERVICE_ACTION
+        XLog.info("downloadAction:$downloadAction")
+        if (downloadAction == action) {
             startDownload(intent)
         }
     }
@@ -129,21 +133,23 @@ class DownloadAPKService : IntentService("DownloadAPKService") {
 
     private fun createNotify() {
         try {
-            val channelID = "o2_notify_channel_down"
-            // 8.0开始要先建一个channel
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-                val channel = NotificationChannel(channelID, "下载通知", NotificationManager.IMPORTANCE_HIGH)
-                mNotificationManager.createNotificationChannel(channel)
-            }
-            val mBuilder = NotificationCompat.Builder(this, channelID)
-            mBuilder.setSmallIcon(R.mipmap.logo)
-            mBuilder.setTicker(getString(R.string.downloading_begin))
-            mRemoteViews = RemoteViews(packageName, R.layout.remote_notification_download)
-            mRemoteViews?.setProgressBar(R.id.progressBar_remote_notification_download, 100, 0, false)
-            mBuilder.setCustomContentView(mRemoteViews)
-            mNotification = mBuilder.build()
-            mNotification?.flags = Notification.FLAG_NO_CLEAR
-            mNotificationManager.notify(0, mNotification)
+//            val channelID = "o2_notify_channel_down"
+//            // 8.0开始要先建一个channel
+//            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+//                val channel = NotificationChannel(channelID, "下载通知", NotificationManager.IMPORTANCE_HIGH)
+//                mNotificationManager.createNotificationChannel(channel)
+//            }
+//            val mBuilder = NotificationCompat.Builder(this, channelID)
+//            mBuilder.setSmallIcon(R.mipmap.logo)
+//            mBuilder.setTicker(getString(R.string.downloading_begin))
+//            mRemoteViews = RemoteViews(packageName, R.layout.remote_notification_download)
+//            mRemoteViews?.setProgressBar(R.id.progressBar_remote_notification_download, 100, 0, false)
+//            mBuilder.setCustomContentView(mRemoteViews)
+//            mNotification = mBuilder.build()
+//            mNotification?.flags = Notification.FLAG_NO_CLEAR
+//            mNotificationManager.notify(0, mNotification)
+
+
         } catch (e: Exception) {
             XLog.error("创建通知异常", e)
         }
@@ -155,9 +161,13 @@ class DownloadAPKService : IntentService("DownloadAPKService") {
             val flag = msg.obj
             if (flag != null) {
                 flag as Int
-                mRemoteViews?.setProgressBar(R.id.progressBar_remote_notification_download, 100, flag, false)
-                mRemoteViews?.setTextViewText(R.id.tv_remote_notification_download_note, "正在下载 $flag %")
-                mNotificationManager.notify(0, mNotification)
+//                mRemoteViews?.setProgressBar(R.id.progressBar_remote_notification_download, 100, flag, false)
+//                mRemoteViews?.setTextViewText(R.id.tv_remote_notification_download_note, "正在下载 $flag %")
+//                mNotificationManager.notify(0, mNotification)
+                val intent = Intent(DOWNLOAD_RECIVER_ACTION_KEY)
+                intent.putExtra(DOWNLOAD_PROGRESS_KEY, flag)
+                LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent)
+                XLog.debug("send progress $flag")
             }
         } catch (e: Exception) {
             XLog.error("更新通知异常", e)
@@ -170,9 +180,9 @@ class DownloadAPKService : IntentService("DownloadAPKService") {
             val file = msg.obj
             if (file != null) {
                 file as File
-                mRemoteViews?.setProgressBar(R.id.progressBar_remote_notification_download, 100, 100, false)
-                mRemoteViews?.setTextViewText(R.id.tv_remote_notification_download_note, "下载完成!")
-                mNotification?.flags = Notification.FLAG_AUTO_CANCEL
+//                mRemoteViews?.setProgressBar(R.id.progressBar_remote_notification_download, 100, 100, false)
+//                mRemoteViews?.setTextViewText(R.id.tv_remote_notification_download_note, "下载完成!")
+//                mNotification?.flags = Notification.FLAG_AUTO_CANCEL
                 val uri = FileUtil.getUriFromFile(applicationContext, file)
                 val intent = Intent(Intent.ACTION_VIEW)
                 intent.addCategory("android.intent.category.DEFAULT")
@@ -181,14 +191,14 @@ class DownloadAPKService : IntentService("DownloadAPKService") {
                 XLog.info("文件类型:$type")
                 intent.setDataAndType(uri, type)
                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)//这个参数要添加, 不然关闭应用之后再点击通知栏会报错
-                mNotification?.contentIntent = PendingIntent.getActivity(this, 0, intent, 0)
-                mNotificationManager.notify(0, mNotification)
+//                mNotification?.contentIntent = PendingIntent.getActivity(this, 0, intent, 0)
+//                mNotificationManager.notify(0, mNotification)
                 startActivity(intent)
                 XLog.info("已经开始安装。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。")
             } else {
-                mRemoteViews?.setTextViewText(R.id.tv_remote_notification_download_note, "下载失败!")
-                mNotification?.flags = Notification.FLAG_AUTO_CANCEL
-                mNotificationManager.notify(0, mNotification)
+//                mRemoteViews?.setTextViewText(R.id.tv_remote_notification_download_note, "下载失败!")
+//                mNotification?.flags = Notification.FLAG_AUTO_CANCEL
+//                mNotificationManager.notify(0, mNotification)
             }
         } catch (e: Exception) {
             XLog.error("安装更新异常", e)

+ 129 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/flutter/FlutterConnectActivity.kt

@@ -0,0 +1,129 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.flutter
+
+import android.content.Context
+import android.os.Bundle
+import android.support.v7.app.AppCompatActivity
+import android.widget.FrameLayout
+import io.flutter.app.FlutterActivity
+import io.flutter.facade.Flutter
+import io.flutter.plugin.common.MethodChannel
+import io.flutter.plugin.common.StandardMethodCodec
+import io.flutter.plugins.GeneratedPluginRegistrant
+import io.flutter.view.FlutterView
+import net.muliba.changeskin.FancySkinManager
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2SDKManager
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.APIAddressHelper
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.APIAssemblesData
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.APIDistributeData
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.APIWebServerData
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.AuthenticationInfoJson
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.CollectUnitData
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.ImmersedStatusBarUtils
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
+import android.R.attr.path
+import android.util.AttributeSet
+import io.flutter.view.FlutterNativeView
+import android.view.WindowManager
+import io.flutter.view.FlutterMain
+
+
+/**
+ * Flutter工程连接类
+ * flutter的入口
+ */
+class FlutterConnectActivity : FlutterActivity() {
+
+
+    companion object {
+        const val ROUTE_NAME_KEY = "ROUTE_NAME_KEY"
+
+        fun startFlutterAppWithRoute(routeName: String): Bundle {
+            val bundle = Bundle()
+            bundle.putString(ROUTE_NAME_KEY, routeName)
+            return bundle
+        }
+    }
+
+    private var route = "noRoute"
+
+    override fun createFlutterView(context: Context?): FlutterView {
+        val matchParent = WindowManager.LayoutParams(-1, -1)
+        val nativeView = this.createFlutterNativeView()
+        val flutterView = FlutterView(this@FlutterConnectActivity, null as AttributeSet?, nativeView)
+        flutterView.setInitialRoute(route)
+        flutterView.layoutParams = matchParent
+        this.setContentView(flutterView)
+        return flutterView
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        route = intent.extras?.getString(ROUTE_NAME_KEY) ?: "noRoute"
+        XLog.debug("open flutter app , route name : $route")
+        FlutterMain.startInitialization(applicationContext)
+        super.onCreate(savedInstanceState)
+        GeneratedPluginRegistrant.registerWith(this)
+        // 沉浸式状态栏
+        ImmersedStatusBarUtils.setImmersedStatusBar(this)
+        MethodChannel(flutterView, FlutterO2Utils.nativeChannelName, StandardMethodCodec.INSTANCE).setMethodCallHandler { methodCall, result ->
+            when(methodCall.method) {
+                FlutterO2Utils.MethodNameO2Config -> {
+                    val themeSuffix = FancySkinManager.instance().currentSkinSuffix()
+                    XLog.debug("theme:$themeSuffix")
+                    val map = HashMap<String, String>()
+                    if (themeSuffix != "blue") {
+                        map[FlutterO2Utils.parameterNameTheme] = "red"
+                    }else {
+                        map[FlutterO2Utils.parameterNameTheme] = "blue"
+                    }
+                    //user
+                    try {
+                        val user = AuthenticationInfoJson()
+                        user.id = O2SDKManager.instance().cId
+                        user.distinguishedName = O2SDKManager.instance().distinguishedName
+                        user.token = O2SDKManager.instance().zToken
+                        user.name = O2SDKManager.instance().cName
+                        val jsonUser = O2SDKManager.instance().gson.toJson(user)
+                        map[FlutterO2Utils.parameterNameUser] = jsonUser
+                    }catch (e: Exception) {
+                        XLog.error("$e")
+                    }
+                    //unit
+                    try {
+                        val unit = CollectUnitData()
+                        unit.name = O2SDKManager.instance().prefs().getString(O2.PRE_BIND_UNIT_KEY, "")
+                        unit.centerContext = O2SDKManager.instance().prefs().getString(O2.PRE_CENTER_CONTEXT_KEY, "")
+                        unit.centerHost = O2SDKManager.instance().prefs().getString(O2.PRE_CENTER_HOST_KEY, "")
+                        unit.centerPort = O2SDKManager.instance().prefs().getInt(O2.PRE_CENTER_PORT_KEY, 80)
+                        unit.httpProtocol = O2SDKManager.instance().prefs().getString(O2.PRE_CENTER_HTTP_PROTOCOL_KEY, "http")
+                        map[FlutterO2Utils.parameterNameUnit] = O2SDKManager.instance().gson.toJson(unit)
+                    }catch (e: Exception) {
+                        XLog.error("$e")
+                    }
+                    //centerServer
+                    try {
+                        val oldDataJson = O2SDKManager.instance().prefs().getString(O2.PRE_ASSEMBLESJSON_KEY, "")
+                        val oldWebDataJson = O2SDKManager.instance().prefs().getString(O2.PRE_WEBSERVERJSON_KEY, "")
+                        val data = O2SDKManager.instance().gson.fromJson<APIAssemblesData>(oldDataJson, APIAssemblesData::class.java)
+                        val webData = O2SDKManager.instance().gson.fromJson<APIWebServerData>(oldWebDataJson, APIWebServerData::class.java)
+                        val dis = APIDistributeData()
+                        dis.webServer = webData
+                        dis.assembles = data
+                        map[FlutterO2Utils.parameterNameCenterServer] = O2SDKManager.instance().gson.toJson(dis)
+                    }catch (e: Exception) {
+                        XLog.error("$e")
+                    }
+
+                    result.success(map)
+                }
+                else -> {
+                    XLog.error("没有实现当前方法, method:${methodCall.method}")
+                    result.error("没有实现当前方法", "", "")
+                }
+            }
+        }
+    }
+
+
+
+}

+ 17 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/flutter/FlutterO2Utils.kt

@@ -0,0 +1,17 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.flutter
+
+/**
+ * Created by fancyLou on 2019/2/18.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+
+object FlutterO2Utils {
+
+    const val nativeChannelName: String = "net.o2oa.flutter/native_get"
+    const val MethodNameO2Config: String = "o2Config"
+    const val parameterNameTheme : String = "o2Theme"
+    const val parameterNameUser : String = "o2UserInfo"
+    const val parameterNameUnit : String = "o2UnitInfo"
+    const val parameterNameCenterServer : String = "o2CenterServerInfo"
+}

+ 20 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/vo/TaskWebViewVOS.kt

@@ -0,0 +1,20 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo
+
+/**
+ * Created by fancyLou on 2019/4/19.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+
+/**
+ * 网页上图片控件 传入的对象
+ * {"mwfId":"imageclipper","callback":"o2.imageClipperCallback","referencetype":"processPlatformJob","reference":"c3d73214-ade8-47fe-9462-68493342ddfa"}
+ */
+data class O2UploadImageData(
+        var mwfId: String = "",//控件id
+        var callback: String = "",//回调函数
+        var referencetype: String = "",//上传文件服务器的业务类型名称
+        var reference: String = "",//关联业务id
+        var fileId: String = ""//上传返回的文件id
+
+)

+ 13 - 10
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/AppUpdateUtil.kt

@@ -10,6 +10,7 @@ import com.pgyersdk.update.UpdateManagerListener
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2App
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2SDKManager
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.DownloadAPKFragment
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.service.DownloadAPKService
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.PgyUpdateBean
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialog.O2AlertIconEnum
@@ -30,7 +31,7 @@ class AppUpdateUtil(activity: Activity) {
     private var versionName = ""
     private var downloadUrl = ""
 
-    fun checkAppUpdate(noUpdateIsNotify: Boolean = false, callbackContinue:(()->Unit)? = null) {
+    fun checkAppUpdate(noUpdateIsNotify: Boolean = false, callbackContinue:((flag: Boolean)->Unit)? = null) {
         PgyUpdateManager.register(weakReference.get(), object : UpdateManagerListener() {
             override fun onUpdateAvailable(p0: String?) {
                 XLog.debug("onUpdateAvailable $p0")
@@ -44,17 +45,18 @@ class AppUpdateUtil(activity: Activity) {
                     if (currentversionName != versionName) {
                         O2DialogSupport.openConfirmDialog(activity, bean.data.releaseNote, listener = { _ ->
                             XLog.info("notification is true..........")
-                            toDownloadService(activity, callbackContinue)
+                            callbackContinue?.invoke(true)
+//                            toDownloadService(activity)
                         }, icon = O2AlertIconEnum.UPDATE, negativeListener = {_->
-                            callbackContinue?.invoke()
+                            callbackContinue?.invoke(false)
                         })
 
                     } else {
-                        callbackContinue?.invoke()
+                        callbackContinue?.invoke(false)
                         XLog.info("versionName is same , do not show dialog! versionName:$versionName ")
                     }
                 }else {
-                    callbackContinue?.invoke()
+                    callbackContinue?.invoke(false)
                 }
 
             }
@@ -67,7 +69,7 @@ class AppUpdateUtil(activity: Activity) {
                         XToast.toastShort(activity, "没有发现新版本!")
                     }
                 }
-                callbackContinue?.invoke()
+                callbackContinue?.invoke(false)
             }
         })
     }
@@ -98,24 +100,25 @@ class AppUpdateUtil(activity: Activity) {
 
     }
 
-    private fun toDownloadService(activity: Activity, callbackContinue:(()->Unit)? = null) {
+    private fun toDownloadService(activity: Activity) {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !activity.packageManager.canRequestPackageInstalls()) {// 8.0需要判断安装未知来源的权限
             O2DialogSupport.openAlertDialog(activity, "非常抱歉,'安装未知应用' 权限未开通, 马上去设置", { _->
                 AndroidUtils.gotoSettingInstalls(activity)
             })
         } else {
             downloadServiceStart(activity)
-            callbackContinue?.invoke()
+//            callbackContinue?.invoke()
         }
 
 
     }
 
-    private fun downloadServiceStart(activity: Activity) {
+    fun downloadServiceStart(activity: Activity) {
         val intent = Intent(activity, DownloadAPKService::class.java)
-        intent.action = DownloadAPKService.DOWNLOAD_SERVICE_ACTION
+        intent.action = activity.packageName + DownloadAPKService.DOWNLOAD_SERVICE_ACTION
         intent.putExtra(DownloadAPKService.VERSIN_NAME_EXTRA_NAME, versionName)
         intent.putExtra(DownloadAPKService.DOWNLOAD_URL_EXTRA_NAME, downloadUrl)
         activity.startService(intent)
+
     }
 }

+ 11 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/biometric/BioConstants.kt

@@ -0,0 +1,11 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.biometric
+
+/**
+ * Created by fancyLou on 2019/3/18.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+
+object BioConstants {
+    const val O2_bio_auth_user_id_prefs_key = "O2_bio_auth_user_id_prefs_key"
+}

+ 149 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/biometric/BiometricPromptDialog.kt

@@ -0,0 +1,149 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.biometric
+
+import android.support.v4.content.ContextCompat
+import android.content.DialogInterface
+import android.os.Bundle
+import android.app.Activity
+import android.app.Dialog
+import android.app.DialogFragment
+import android.content.Context
+import android.support.annotation.Nullable
+import android.view.*
+import android.widget.RelativeLayout
+import android.widget.TextView
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
+
+
+/**
+ * Created by fancyLou on 2019/3/17.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+class BiometricPromptDialog : DialogFragment() {
+
+    companion object {
+        val STATE_NORMAL = 1
+        val STATE_FAILED = 2
+        val STATE_ERROR = 3
+        val STATE_SUCCEED = 4
+        fun newInstance(): BiometricPromptDialog = BiometricPromptDialog()
+    }
+
+
+    private var mStateTv: TextView? = null
+    private var mUsePasswordBtn: TextView? = null
+    private var mCancelBtn: TextView? = null
+    private var mActivity: Activity? = null
+    private var mDialogActionCallback: OnBiometricPromptDialogActionCallback? = null
+
+    interface OnBiometricPromptDialogActionCallback {
+        fun onDialogDismiss()
+        fun onUsePassword()
+        fun onCancel()
+    }
+
+
+
+    fun setOnBiometricPromptDialogActionCallback(callback: OnBiometricPromptDialogActionCallback) {
+        mDialogActionCallback = callback
+    }
+
+    override fun onActivityCreated(savedInstanceState: Bundle?) {
+        super.onActivityCreated(savedInstanceState)
+        setupWindow(dialog.window)
+    }
+
+    @Nullable
+    override fun onCreateView(inflater: LayoutInflater, @Nullable container: ViewGroup?, @Nullable savedInstanceState: Bundle?): View? {
+        val view = inflater.inflate(R.layout.dialog_bitmetric_prompt, container)
+
+        val rootView: RelativeLayout = view.findViewById(R.id.root_view)
+        rootView.isClickable = false
+
+        mStateTv = view.findViewById(R.id.state_tv)
+        mUsePasswordBtn = view.findViewById(R.id.use_password_btn)
+        mCancelBtn = view.findViewById(R.id.cancel_btn)
+
+        mUsePasswordBtn!!.visibility = View.GONE
+        mUsePasswordBtn!!.setOnClickListener {
+            if (mDialogActionCallback != null) {
+                mDialogActionCallback!!.onUsePassword()
+            }
+
+            dismiss()
+        }
+        mCancelBtn!!.setOnClickListener {
+            if (mDialogActionCallback != null) {
+                mDialogActionCallback!!.onCancel()
+            }
+            dismiss()
+        }
+        return view
+    }
+
+    override fun onAttach(context: Context) {
+        super.onAttach(context)
+        mActivity = context as Activity
+    }
+
+    override fun onAttach(activity: Activity?) {
+        super.onAttach(activity)
+        mActivity = activity
+    }
+
+    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+        val dialog = super.onCreateDialog(savedInstanceState)
+        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
+        if (dialog.window != null) {
+            dialog.window.setBackgroundDrawableResource(R.color.bg_biometry_dialog)
+        }
+        return dialog
+    }
+
+    override fun onDismiss(dialog: DialogInterface?) {
+        super.onDismiss(dialog)
+
+        if (mDialogActionCallback != null) {
+            mDialogActionCallback!!.onDialogDismiss()
+        }
+    }
+
+    private fun setupWindow(window: Window?) {
+        if (window != null) {
+            val lp = window.getAttributes()
+            lp.gravity = Gravity.CENTER
+            lp.dimAmount = 0f
+            lp.flags = lp.flags or WindowManager.LayoutParams.FLAG_DIM_BEHIND
+            window.attributes = lp
+            window.setBackgroundDrawableResource(R.color.bg_biometry_dialog)
+            window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
+        }
+    }
+
+    fun setState(state: Int) {
+        when (state) {
+            STATE_NORMAL -> {
+                mStateTv!!.setTextColor(ContextCompat.getColor(mActivity!!, R.color.text_primary))
+                mStateTv!!.text = mActivity!!.getString(R.string.biometric_dialog_state_normal)
+                mCancelBtn!!.visibility = View.VISIBLE
+            }
+            STATE_FAILED -> {
+                mStateTv!!.setTextColor(ContextCompat.getColor(mActivity!!, R.color.z_color_primary))
+                mStateTv!!.text = mActivity!!.getString(R.string.biometric_dialog_state_failed)
+                mCancelBtn!!.visibility = View.VISIBLE
+            }
+            STATE_ERROR -> {
+                mStateTv!!.setTextColor(ContextCompat.getColor(mActivity!!, R.color.z_color_primary))
+                mStateTv!!.text = mActivity!!.getString(R.string.biometric_dialog_state_error)
+                mCancelBtn!!.visibility = View.GONE
+            }
+            STATE_SUCCEED -> {
+                mStateTv!!.setTextColor(ContextCompat.getColor(mActivity!!, R.color.z_color_accent))
+                mStateTv!!.text = mActivity!!.getString(R.string.biometric_dialog_state_succeeded)
+                mCancelBtn!!.visibility = View.VISIBLE
+
+                mStateTv!!.postDelayed({ dismiss() }, 500)
+            }
+        }
+    }
+}

+ 118 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/biometric/BiometryAuthAPI23.kt

@@ -0,0 +1,118 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.biometric
+
+import android.app.Activity
+import android.os.Build
+import android.os.CancellationSignal
+import android.support.annotation.RequiresApi
+import android.hardware.fingerprint.FingerprintManager
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
+
+
+/**
+ * Created by fancyLou on 2019/3/17.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+
+@RequiresApi(Build.VERSION_CODES.M)
+class BiometryAuthAPI23(val mActivity: Activity) : IBiometryAuth {
+
+    private val mFingerprintManager: FingerprintManager? by lazy {
+        mActivity.getSystemService(FingerprintManager::class.java)
+    }
+    private val mFmAuthCallback = FingerprintAuthCallback()
+
+    private var mDialog: BiometricPromptDialog? = null
+    private var mCancellationSignal: CancellationSignal? = null
+    private var mOnBiometryAuthCallback: OnBiometryAuthCallback? = null
+
+
+    override fun authenticate(cancel: CancellationSignal, callback: OnBiometryAuthCallback) {
+        mOnBiometryAuthCallback = callback
+        mDialog = BiometricPromptDialog.newInstance()
+        mDialog?.setOnBiometricPromptDialogActionCallback(object : BiometricPromptDialog.OnBiometricPromptDialogActionCallback {
+            override fun onDialogDismiss() {
+                //当dialog消失的时候,包括点击userPassword、点击cancel、和识别成功之后
+                if (mCancellationSignal != null && !mCancellationSignal!!.isCanceled) {
+                    mCancellationSignal!!.cancel()
+                }
+            }
+
+            override fun onUsePassword() {
+                //一些情况下,用户还可以选择使用密码
+                if (mOnBiometryAuthCallback != null) {
+                    mOnBiometryAuthCallback?.onUseFallBack()
+                }
+            }
+
+            override fun onCancel() {
+                //点击cancel键
+                if (mOnBiometryAuthCallback != null) {
+                    mOnBiometryAuthCallback?.onCancel()
+                }
+            }
+        })
+        mDialog?.show(mActivity.fragmentManager, "BiometryAuthAPI23")
+        mCancellationSignal = cancel
+        if (mCancellationSignal == null) {
+            mCancellationSignal = CancellationSignal()
+        }
+        mCancellationSignal?.setOnCancelListener { mDialog?.dismiss() }
+
+        try {
+            val cryptoObjectHelper = CryptoObjectHelper()
+            mFingerprintManager?.authenticate(
+                    cryptoObjectHelper.buildCryptoObject(), mCancellationSignal,
+                    0, mFmAuthCallback, null)
+        } catch (e: Exception) {
+            e.printStackTrace()
+        }
+
+    }
+
+
+    fun isHardwareDetected(): Boolean {
+        return mFingerprintManager?.isHardwareDetected == true
+    }
+
+    fun hasEnrolledFingerprints(): Boolean {
+        return  mFingerprintManager?.hasEnrolledFingerprints() == true
+    }
+
+
+
+    inner class FingerprintAuthCallback : FingerprintManager.AuthenticationCallback() {
+        override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
+            super.onAuthenticationError(errorCode, errString)
+            XLog.error( "onAuthenticationError() called with: errorCode = [$errorCode], errString = [$errString]")
+            mDialog?.setState(BiometricPromptDialog.STATE_ERROR)
+            mOnBiometryAuthCallback?.onError(errorCode, errString.toString())
+        }
+
+        override fun onAuthenticationFailed() {
+            super.onAuthenticationFailed()
+            XLog.error( "onAuthenticationFailed() called")
+            mDialog?.setState(BiometricPromptDialog.STATE_FAILED)
+            mOnBiometryAuthCallback?.onFailed()
+        }
+
+        override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence) {
+            super.onAuthenticationHelp(helpCode, helpString)
+            XLog.debug( "onAuthenticationHelp() called with: helpCode = [$helpCode], helpString = [$helpString]")
+            mDialog?.setState(BiometricPromptDialog.STATE_FAILED)
+            mOnBiometryAuthCallback?.onFailed()
+
+        }
+
+        override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult) {
+            super.onAuthenticationSucceeded(result)
+            XLog.info(  "onAuthenticationSucceeded: ")
+            mDialog?.setState(BiometricPromptDialog.STATE_SUCCEED)
+            mOnBiometryAuthCallback?.onSucceeded()
+
+        }
+    }
+
+
+
+}

+ 23 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/biometric/BiometryAuthAPI28.kt

@@ -0,0 +1,23 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.biometric
+
+import android.app.Activity
+import android.os.Build
+import android.os.CancellationSignal
+import android.support.annotation.RequiresApi
+
+/**
+ * Created by fancyLou on 2019/3/17.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+
+
+//@RequiresApi(Build.VERSION_CODES.P)
+class BiometryAuthAPI28(mActivity: Activity): IBiometryAuth {
+
+
+    override fun authenticate(cancel: CancellationSignal, callback: OnBiometryAuthCallback) {
+
+    }
+
+}

+ 96 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/biometric/BiometryManager.kt

@@ -0,0 +1,96 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.biometric
+
+import android.app.Activity
+import android.os.Build
+import android.os.CancellationSignal
+import android.app.KeyguardManager
+import android.content.Context
+
+
+
+
+/**
+ * Created by fancyLou on 2019/3/17.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+
+class BiometryManager {
+
+    private var mActivity: Activity
+    private var mImpl : IBiometryAuth? = null
+
+    constructor(activity: Activity) {
+        mActivity = activity
+        if (isAboveApi23()) {
+            mImpl =  BiometryAuthAPI23(activity)
+        }
+    }
+
+    fun authenticate(callback: OnBiometryAuthCallback) {
+        mImpl?.authenticate(CancellationSignal(), callback)
+    }
+
+    fun authenticate(cancel: CancellationSignal,
+                     callback: OnBiometryAuthCallback) {
+        mImpl?.authenticate(cancel, callback)
+    }
+
+    fun isBiometricPromptEnable(): Boolean {
+        return (isAboveApi23()
+                && isHardwareDetected()
+                && hasEnrolledFingerprints()
+                && isKeyguardSecure())
+    }
+
+    private fun hasEnrolledFingerprints(): Boolean {
+        return if (isAboveApi23()) {
+            (mImpl as BiometryAuthAPI23).hasEnrolledFingerprints()
+        } else {
+            false
+        }
+    }
+
+    private fun isHardwareDetected(): Boolean {
+        return if (isAboveApi23()) {
+            (mImpl as BiometryAuthAPI23).isHardwareDetected()
+        } else {
+            false
+        }
+    }
+
+
+    private fun isKeyguardSecure(): Boolean {
+        val keyguardManager = mActivity.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
+        return keyguardManager.isKeyguardSecure
+
+    }
+    private fun isAboveApi23(): Boolean {
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
+    }
+
+
+
+
+}
+
+
+
+
+
+
+
+
+interface OnBiometryAuthCallback {
+
+    fun onUseFallBack()
+
+    fun onSucceeded()
+
+    fun onFailed()
+
+    fun onError(code: Int, reason: String)
+
+    fun onCancel()
+
+}

+ 90 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/biometric/CryptoObjectHelper.kt

@@ -0,0 +1,90 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.biometric
+
+import android.os.Build
+import android.support.annotation.RequiresApi
+import android.security.keystore.KeyProperties
+import android.security.keystore.KeyGenParameterSpec
+import android.security.keystore.KeyPermanentlyInvalidatedException
+import android.hardware.fingerprint.FingerprintManager
+import java.security.Key
+import java.security.KeyStore
+import javax.crypto.Cipher
+import javax.crypto.KeyGenerator
+
+
+/**
+ * Created by fancyLou on 2019/3/17.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+@RequiresApi(Build.VERSION_CODES.M)
+class CryptoObjectHelper {
+
+    // This can be key name you want. Should be unique for the app.
+    val KEY_NAME = "net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.biometric.CryptoObjectHelper"
+
+    // We always use this keystore on Android.
+    val KEYSTORE_NAME = "AndroidKeyStore"
+
+    // Should be no need to change these values.
+    val KEY_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES
+    val BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC
+    val ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7
+    val TRANSFORMATION = KEY_ALGORITHM + "/" +
+            BLOCK_MODE + "/" +
+            ENCRYPTION_PADDING
+
+
+    var _keystore: KeyStore
+
+    init {
+        _keystore = KeyStore.getInstance(KEYSTORE_NAME)
+        _keystore.load(null)
+    }
+
+    @Throws(Exception::class)
+    fun buildCryptoObject(): FingerprintManager.CryptoObject {
+        val cipher = createCipher(true)
+        return FingerprintManager.CryptoObject(cipher)
+    }
+
+    @Throws(Exception::class)
+    fun createCipher(retry: Boolean): Cipher {
+        val key = getKey()
+        val cipher = Cipher.getInstance(TRANSFORMATION)
+        try {
+            cipher.init(Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE, key)
+        } catch (e: KeyPermanentlyInvalidatedException) {
+            _keystore.deleteEntry(KEY_NAME)
+            if (retry) {
+                createCipher(false)
+            } else {
+                throw Exception("Could not create the cipher for fingerprint authentication.", e)
+            }
+        }
+
+        return cipher
+    }
+
+    @Throws(Exception::class)
+    fun getKey(): Key {
+
+        if (!_keystore.isKeyEntry(KEY_NAME)) {
+            createKey()
+        }
+
+        return _keystore.getKey(KEY_NAME, null)
+    }
+
+    @Throws(Exception::class)
+    fun createKey() {
+        val keyGen = KeyGenerator.getInstance(KEY_ALGORITHM, KEYSTORE_NAME)
+        val keyGenSpec = KeyGenParameterSpec.Builder(KEY_NAME, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
+                .setBlockModes(BLOCK_MODE)
+                .setEncryptionPaddings(ENCRYPTION_PADDING)
+                .setUserAuthenticationRequired(true)
+                .build()
+        keyGen.init(keyGenSpec)
+        keyGen.generateKey()
+    }
+}

+ 16 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/biometric/IBiometryAuth.kt

@@ -0,0 +1,16 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.biometric
+
+import android.os.CancellationSignal
+
+
+
+/**
+ * Created by fancyLou on 2019/3/17.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+
+interface IBiometryAuth {
+    fun authenticate(cancel: CancellationSignal,
+                     callback: OnBiometryAuthCallback)
+}

+ 272 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/widgets/BottomSheetMenu.kt

@@ -0,0 +1,272 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets
+
+import android.animation.Animator
+import android.animation.ValueAnimator
+import android.app.Activity
+import android.graphics.Color
+import android.graphics.Typeface
+import android.util.TypedValue
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.view.animation.DecelerateInterpolator
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.borax12.materialdaterangepicker.Utils.dpToPx
+import jiguang.chat.pickerimage.utils.ScreenUtil.getBottomStatusHeight
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
+
+/**
+ * Created by fancyLou on 2019/4/19.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+class BottomSheetMenu(private val activity: Activity) {
+    private val shadowMax = 0xa0
+    private var majorTitle: TextView? = null
+    private var title: TextView? = null
+    private val viewGroup: FrameLayout
+    private val contentGroup: LinearLayout
+    private val menuList: LinearLayout
+    private val commonMargin = dpToPx(10f, activity.resources)
+    private val itemMargin = dpToPx(16f, activity.resources)
+    private var contentLayoutHeight = 0
+
+    var isShow = false
+
+    init {
+        viewGroup = initViewGroup()
+        contentGroup = initContentGroup()
+        menuList = initMenuList()
+        viewGroup.addView(contentGroup)
+        contentGroup.addView(menuList)
+    }
+
+    fun show() {
+        if (!isShow) {
+            (activity.window.decorView as ViewGroup).addView(viewGroup)
+            contentGroup.apply {
+                post {
+                    contentLayoutHeight = measuredHeight
+                    translationY = contentLayoutHeight.toFloat()
+                    visibility = View.VISIBLE
+                    startAnimator(true)
+                }
+            }
+            isShow = true
+        }
+    }
+
+    fun dismiss() {
+        if (isShow) {
+            startAnimator(false, object : Animator.AnimatorListener {
+                override fun onAnimationRepeat(animation: Animator?) {
+
+                }
+
+                override fun onAnimationCancel(animation: Animator?) {
+
+                }
+
+                override fun onAnimationStart(animation: Animator?) {
+
+                }
+
+                override fun onAnimationEnd(animation: Animator?) {
+                    (activity.window.decorView as ViewGroup).removeView(viewGroup)
+                }
+            })
+            isShow = false
+        }
+    }
+
+    /**
+     * 设置主标题
+     */
+    fun setMajorTitle(text: String): BottomSheetMenu {
+        majorTitle = TextView(activity).apply {
+            layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
+                    LinearLayout.LayoutParams.WRAP_CONTENT).apply {
+                val margin = dpToPx(20f, activity.resources)
+                if (title == null) {
+                    setMargins(0, margin, 0, margin)
+                } else {
+                    setMargins(0, margin, 0, 0)
+                }
+            }
+            setTextSize(TypedValue.COMPLEX_UNIT_SP, 14f)
+            setTextColor(Color.parseColor("#8f8f8f"))
+            typeface = Typeface.DEFAULT_BOLD
+            setText(text)
+        }
+        menuList.addView(majorTitle, 0)
+        return this
+    }
+
+    /**
+     * 设置副标题
+     */
+    fun setTitle(text: String): BottomSheetMenu {
+        title = TextView(activity).apply {
+            layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
+                    LinearLayout.LayoutParams.WRAP_CONTENT).apply {
+                val margin = dpToPx(20f, activity.resources)
+                if (majorTitle == null) {
+                    setMargins(0, margin, 0, margin)
+                } else {
+                    setMargins(0, 0, 0, margin)
+                }
+            }
+            setTextSize(TypedValue.COMPLEX_UNIT_SP, 14f)
+            setTextColor(Color.parseColor("#8f8f8f"))
+            setText(text)
+        }
+        if (majorTitle == null) {
+            menuList.addView(title, 0)
+        } else {
+            menuList.addView(title, 1)
+        }
+        return this
+    }
+
+    /**
+     * 设置子项
+     */
+    fun setItem(text: String, textColor: Int = 0, itemClickListener: () -> Unit): BottomSheetMenu {
+        val textView = TextView(activity)
+        textView.apply {
+            layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
+                    LinearLayout.LayoutParams.WRAP_CONTENT).apply {
+                setPadding(0, itemMargin, 0, itemMargin)
+            }
+            gravity = Gravity.CENTER
+            setBackgroundResource(R.drawable.shape_bottom_list_menu)
+            setTextSize(TypedValue.COMPLEX_UNIT_SP, 20f)
+            if (textColor == 0) {
+                setTextColor(Color.parseColor("#FF3B30"))
+            } else {
+                try {
+                    setTextColor(textColor)
+                } catch (e: Throwable) {
+                    e.printStackTrace()
+                }
+            }
+            setText(text)
+            setOnClickListener {
+                itemClickListener.invoke()
+                dismiss()
+            }
+        }
+        val lineView = View(activity).apply {
+            layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 1)
+            setBackgroundColor(Color.parseColor("#dcdbdf"))
+        }
+        menuList.addView(lineView)
+        menuList.addView(textView)
+        return this
+    }
+
+    /**
+     * 设置按钮
+     */
+    fun setCancelButton(text: String, textColor: Int = 0,  cancelClickListener: () -> Unit): BottomSheetMenu {
+        val cancelView = TextView(activity).apply {
+            layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
+                    LinearLayout.LayoutParams.WRAP_CONTENT).apply {
+                setMargins(commonMargin, 0, commonMargin, commonMargin)
+            }
+            setPadding(0, itemMargin, 0, itemMargin)
+            background = resources.getDrawable(R.drawable.shape_bottom_list_menu)
+            if (textColor == 0) {
+                setTextColor(Color.parseColor("#FF3B30"))
+            } else {
+                try {
+                    setTextColor(textColor)
+                } catch (e: Throwable) {
+                    e.printStackTrace()
+                }
+            }
+            gravity = Gravity.CENTER
+            setTextSize(TypedValue.COMPLEX_UNIT_SP, 20f)
+            setText(text)
+            typeface = Typeface.DEFAULT_BOLD
+            setOnClickListener {
+                cancelClickListener.invoke()
+                dismiss()
+            }
+        }
+        contentGroup.addView(cancelView)
+        return this
+    }
+
+    /**
+     * 初始化容器
+     * 背景阴影容器
+     */
+    private fun initViewGroup() = FrameLayout(activity).apply {
+        layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT)
+        setOnClickListener {
+            dismiss()
+        }
+    }
+
+    /**
+     * 初始化内容容器
+     * 滑动动画载体
+     */
+    private fun initContentGroup() = LinearLayout(activity).apply {
+        layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
+                FrameLayout.LayoutParams.WRAP_CONTENT).apply {
+            gravity = Gravity.BOTTOM
+            bottomMargin = getBottomStatusHeight(activity)
+            visibility = View.INVISIBLE
+        }
+        orientation = LinearLayout.VERTICAL
+    }
+
+    /**
+     * 初始化菜单列表
+     */
+    private fun initMenuList() = LinearLayout(activity).apply {
+        layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
+                LinearLayout.LayoutParams.WRAP_CONTENT).apply {
+            setMargins(commonMargin, 0, commonMargin, commonMargin)
+        }
+        gravity = Gravity.CENTER_HORIZONTAL
+        orientation = LinearLayout.VERTICAL
+        background = activity.resources.getDrawable(R.drawable.shape_bottom_list_menu)
+    }
+
+    private fun startAnimator(enterType: Boolean, listener: Animator.AnimatorListener? = null) {
+        viewGroup.post {
+            ValueAnimator().apply {
+                if (enterType) {
+                    setFloatValues(contentLayoutHeight.toFloat(), 0f)
+                } else {
+                    setFloatValues(0f, contentLayoutHeight.toFloat())
+                }
+                duration = 300
+                interpolator = DecelerateInterpolator()
+                addUpdateListener {
+                    val value = it.animatedValue as Float
+                    contentGroup.translationY = value
+                    setShadow()
+                }
+                listener?.let {
+                    addListener(it)
+                }
+                start()
+            }
+        }
+    }
+
+    private fun setShadow() {
+        val ratio = contentGroup.translationY / contentLayoutHeight
+        val shadow = (shadowMax * (1 - ratio)).toInt()
+        if (shadow >= 16) {
+            viewGroup.setBackgroundColor(Color.parseColor("#${shadow.toString(16)}000000"))
+        }
+    }
+
+}

+ 12 - 5
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/widgets/NestedProgressWebView.kt

@@ -1,5 +1,6 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets
 
+import android.annotation.SuppressLint
 import android.content.Context
 import android.os.Build
 import android.support.v4.content.ContextCompat
@@ -11,10 +12,7 @@ import android.util.AttributeSet
 import android.view.ActionMode
 import android.view.MotionEvent
 import android.view.View
-import android.webkit.CookieManager
-import android.webkit.JavascriptInterface
-import android.webkit.WebChromeClient
-import android.webkit.WebView
+import android.webkit.*
 import android.widget.ProgressBar
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2App
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2SDKManager
@@ -36,7 +34,7 @@ class NestedProgressWebView : WebView, NestedScrollingChild {
     private val mScrollConsumed = IntArray(2)
     private var mNestedOffsetY: Int = 0
     private val mChildHelper: NestedScrollingChildHelper = NestedScrollingChildHelper(this)
-    private lateinit var progressBar: ProgressBar
+    protected lateinit var progressBar: ProgressBar
     private val mActionList = ArrayList<String>()
     private var mActionMode: ActionMode? = null
     private var mLinkJsInterfaceName:String = "fancyActionJsInterface"
@@ -68,13 +66,22 @@ class NestedProgressWebView : WebView, NestedScrollingChild {
         webChromeClient = ProgressWebChromeClient()
     }
 
+    @SuppressLint("SetJavaScriptEnabled")
     private fun initSettings() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            settings.safeBrowsingEnabled = false
+        }
+
         settings.javaScriptEnabled = true
         settings.allowFileAccess = true
         settings.setAppCacheEnabled(true)
         settings.builtInZoomControls = false
         settings.setSupportMultipleWindows(true)
         settings.javaScriptCanOpenWindowsAutomatically = true
+        //5.0以上开启混合模式加载
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
+        }
     }
 
     fun addActionList(list: List<String>) {

+ 8 - 8
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/widgets/dialog/O2AlertDialogBuilder.kt

@@ -15,6 +15,7 @@ import android.widget.LinearLayout
 import android.widget.TextView
 import net.muliba.changeskin.FancySkinManager
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.gone
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.visible
 import org.jetbrains.anko.dip
@@ -26,15 +27,14 @@ import org.jetbrains.annotations.NotNull
  */
 
 
-class O2AlertDialogBuilder(val context: Context)  {
+class O2AlertDialogBuilder(val mContext: Context)  {
 
-    private val mContext: Context = context
-    private var dialogTitle: CharSequence = context.getText(R.string.hint)
+    private var dialogTitle: CharSequence = mContext.getText(R.string.hint)
     private var dialogContent: CharSequence = "" //提示内容 和 dialogCustomView互相排斥
     private var dialogCustomView: View? = null //自定义view 和 dialogContent互相排斥
     private var dialogIcon: O2AlertIconEnum = O2AlertIconEnum.ALERT
     private var negativeText: CharSequence? = null
-    private var positiveText: CharSequence = context.getText(R.string.positive)
+    private var positiveText: CharSequence = mContext.getText(R.string.positive)
 
     //event
     private var _onPositiveListener: ((dialog: O2Dialog) -> Unit)? = null
@@ -61,7 +61,7 @@ class O2AlertDialogBuilder(val context: Context)  {
         if (titleRes == 0) {
             return this
         }
-        dialogTitle = context.getString(titleRes)
+        dialogTitle = mContext.getString(titleRes)
         return this
     }
 
@@ -80,7 +80,7 @@ class O2AlertDialogBuilder(val context: Context)  {
         if (dialogCustomView!=null) {
             throw IllegalStateException("You cannot set content When you already have CustomView !")
         }
-        dialogContent = context.getString(contentRes)
+        dialogContent = mContext.getString(contentRes)
         return this
     }
 
@@ -130,7 +130,7 @@ class O2AlertDialogBuilder(val context: Context)  {
         if (positiveRes == 0) {
             return this
         }
-        positiveText = context.getText(positiveRes)
+        positiveText = mContext.getText(positiveRes)
         return this
     }
 
@@ -151,7 +151,7 @@ class O2AlertDialogBuilder(val context: Context)  {
         if (negativeRes == 0) {
             return this
         }
-        negativeText = context.getText(negativeRes)
+        negativeText = mContext.getText(negativeRes)
         return this
     }
 

+ 5 - 0
o2android/app/src/main/res/drawable/shape_bottom_list_button.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="15dp"/>
+    <solid android:color="#ffffff"/>
+</shape>

+ 5 - 0
o2android/app/src/main/res/drawable/shape_bottom_list_menu.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="15dp"/>
+    <solid android:color="#f1f1f1"/>
+</shape>

+ 42 - 1
o2android/app/src/main/res/layout/activity_account_security.xml

@@ -81,12 +81,13 @@
                         android:textColor="@color/z_color_text_primary_dark"/>
                 </RelativeLayout>
             </LinearLayout>
+
             <View
                 android:layout_width="match_parent"
                 android:layout_height="1dp"
                 android:layout_marginLeft="@dimen/spacing_normal"
                 android:layout_marginRight="@dimen/spacing_normal"
-                android:background="@color/z_color_split_line_ddd"></View>
+                android:background="@color/z_color_split_line_ddd" />
             <LinearLayout
                 android:orientation="vertical"
                 android:layout_width="match_parent"
@@ -132,5 +133,45 @@
 
         </LinearLayout>
 
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/spacing_small"
+        android:background="@android:color/white">
+        <LinearLayout
+            android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <RelativeLayout
+                android:id="@+id/rl_account_security_biometry_btn"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:layout_marginTop="@dimen/spacing_small"
+                android:layout_marginBottom="@dimen/spacing_small"
+                android:layout_marginLeft="@dimen/spacing_normal"
+                android:layout_marginRight="@dimen/spacing_normal">
+                <TextView
+                    android:id="@+id/tv_account_security_biometry_name"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="指纹识别"
+                    android:layout_alignParentStart="true"
+                    android:textSize="15sp"
+                    android:textColor="@color/z_color_text_hint"
+                    android:layout_centerVertical="true" />
+                <ImageButton
+                    android:id="@+id/image_btn_account_security_biometry_enable"
+                    android:layout_width="48dp"
+                    android:layout_height="29dp"
+                    android:layout_alignParentEnd="true"
+                    android:layout_centerVertical="true"
+                    android:background="@null"
+                    android:src="@mipmap/icon_toggle_off_29dp"/>
+            </RelativeLayout>
+        </LinearLayout>
+    </LinearLayout>
+
     </LinearLayout>
 </RelativeLayout>

+ 197 - 183
o2android/app/src/main/res/layout/activity_bbs_publish_subject.xml

@@ -1,225 +1,240 @@
 <?xml version="1.0" encoding="utf-8"?>
-<android.support.design.widget.CoordinatorLayout
+<FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/activity_bbs_publish_subject"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@android:color/white"
-    android:fitsSystemWindows="true"
-    tools:context="net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.bbs.publish.BBSPublishSubjectActivity">
-
-    <android.support.design.widget.AppBarLayout
-        android:id="@+id/app_bar_layout_snippet"
-        android:layout_height="wrap_content"
+    tools:context="net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.bbs.publish.BBSPublishSubjectActivity"
+    >
+    <android.support.design.widget.CoordinatorLayout
+        android:id="@+id/activity_bbs_publish_subject"
         android:layout_width="match_parent"
-        android:theme="@style/XBPMTheme.AppBarOverlay">
-        <android.support.v7.widget.Toolbar
-            android:id="@+id/toolbar_snippet_top_bar"
-            android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@android:color/white">
+        <android.support.design.widget.AppBarLayout
+            android:id="@+id/app_bar_layout_snippet"
             android:layout_height="wrap_content"
-            android:background="@color/z_color_primary"
-            app:popupTheme="@style/XBPMTheme.PopupOverlay"
-            app:layout_scrollFlags="scroll|snap|enterAlways">
-            <TextView
-                android:id="@+id/tv_snippet_top_title"
-                android:layout_width="wrap_content"
+            android:layout_width="match_parent"
+            android:theme="@style/XBPMTheme.AppBarOverlay">
+            <android.support.v7.widget.Toolbar
+                android:id="@+id/toolbar_snippet_top_bar"
+                android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:layout_gravity="center"
-                android:ellipsize="marquee"
-                android:textAppearance="?android:attr/textAppearanceMedium"
-                android:text=""
-                android:singleLine="true" />
-        </android.support.v7.widget.Toolbar>
-    </android.support.design.widget.AppBarLayout>
-
-    <ScrollView
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_marginTop="?attr/actionBarSize">
-
-        <LinearLayout
+                android:background="@color/z_color_primary"
+                app:popupTheme="@style/XBPMTheme.PopupOverlay"
+                app:layout_scrollFlags="scroll|snap|enterAlways">
+                <TextView
+                    android:id="@+id/tv_snippet_top_title"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center"
+                    android:ellipsize="marquee"
+                    android:textAppearance="?android:attr/textAppearanceMedium"
+                    android:text=""
+                    android:singleLine="true" />
+            </android.support.v7.widget.Toolbar>
+        </android.support.design.widget.AppBarLayout>
+
+        <ScrollView
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="vertical">
+            android:layout_height="match_parent"
+            android:layout_marginTop="?attr/actionBarSize">
 
             <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:layout_marginBottom="@dimen/spacing_small"
-                android:layout_marginLeft="@dimen/spacing_normal"
-                android:layout_marginRight="@dimen/spacing_normal"
-                android:layout_marginTop="@dimen/spacing_small"
-                android:orientation="horizontal">
-
-                <TextView
-                    android:layout_width="0dp"
-                    android:layout_height="wrap_content"
-                    android:layout_weight="1"
-                    android:text="@string/bbs_publish_section_label"
-                    android:textColor="@color/z_color_text_primary"
-                    android:textSize="15sp" />
+                android:orientation="vertical">
 
-                <TextView
-                    android:id="@+id/tv_bbs_publish_subject_section"
-                    android:layout_width="0dp"
+                <LinearLayout
+                    android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:layout_weight="3"
-                    android:textColor="@color/z_color_text_hint"
-                    android:textSize="15sp"
-                    tools:text="版块1" />
+                    android:layout_marginBottom="@dimen/spacing_small"
+                    android:layout_marginLeft="@dimen/spacing_normal"
+                    android:layout_marginRight="@dimen/spacing_normal"
+                    android:layout_marginTop="@dimen/spacing_small"
+                    android:orientation="horizontal">
 
-            </LinearLayout>
+                    <TextView
+                        android:layout_width="0dp"
+                        android:layout_height="wrap_content"
+                        android:layout_weight="1"
+                        android:text="@string/bbs_publish_section_label"
+                        android:textColor="@color/z_color_text_primary"
+                        android:textSize="15sp" />
 
-            <View
-                android:layout_width="match_parent"
-                android:layout_height="1px"
-                android:layout_marginLeft="@dimen/spacing_normal"
-                android:layout_marginRight="@dimen/spacing_normal"
-                android:background="@color/z_color_subtitle_font" />
+                    <TextView
+                        android:id="@+id/tv_bbs_publish_subject_section"
+                        android:layout_width="0dp"
+                        android:layout_height="wrap_content"
+                        android:layout_weight="3"
+                        android:textColor="@color/z_color_text_hint"
+                        android:textSize="15sp"
+                        tools:text="版块1" />
 
-            <LinearLayout
-                android:id="@+id/layout_bbs_publish_subject_type_choose_button"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginBottom="@dimen/spacing_small"
-                android:layout_marginLeft="@dimen/spacing_normal"
-                android:layout_marginRight="@dimen/spacing_normal"
-                android:layout_marginTop="@dimen/spacing_small"
-                android:gravity="center_vertical"
-                android:orientation="horizontal">
+                </LinearLayout>
 
-                <TextView
-                    android:layout_width="0dp"
-                    android:layout_height="wrap_content"
-                    android:layout_weight="1"
-                    android:text="@string/bbs_publish_subject_type_label"
-                    android:textColor="@color/z_color_text_primary"
-                    android:textSize="15sp" />
+                <View
+                    android:layout_width="match_parent"
+                    android:layout_height="1px"
+                    android:layout_marginLeft="@dimen/spacing_normal"
+                    android:layout_marginRight="@dimen/spacing_normal"
+                    android:background="@color/z_color_subtitle_font" />
 
-                <RelativeLayout
-                    android:layout_width="0dp"
+                <LinearLayout
+                    android:id="@+id/layout_bbs_publish_subject_type_choose_button"
+                    android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:layout_weight="3"
+                    android:layout_marginBottom="@dimen/spacing_small"
+                    android:layout_marginLeft="@dimen/spacing_normal"
+                    android:layout_marginRight="@dimen/spacing_normal"
+                    android:layout_marginTop="@dimen/spacing_small"
+                    android:gravity="center_vertical"
                     android:orientation="horizontal">
 
                     <TextView
-                        android:id="@+id/tv_bbs_publish_subject_type"
-                        android:layout_width="wrap_content"
+                        android:layout_width="0dp"
                         android:layout_height="wrap_content"
-                        android:layout_centerVertical="true"
-                        android:layout_alignParentLeft="true"
-                        android:text="@string/bbs_publish_subject_type"
-                        android:textColor="@color/z_color_text_hint"
+                        android:layout_weight="1"
+                        android:text="@string/bbs_publish_subject_type_label"
+                        android:textColor="@color/z_color_text_primary"
                         android:textSize="15sp" />
 
-                    <ImageView
-                        android:layout_width="24dp"
-                        android:layout_height="24dp"
-                        android:layout_alignParentRight="true"
-                        android:src="@mipmap/icon_select_more_gray" />
-                </RelativeLayout>
+                    <RelativeLayout
+                        android:layout_width="0dp"
+                        android:layout_height="wrap_content"
+                        android:layout_weight="3"
+                        android:orientation="horizontal">
+
+                        <TextView
+                            android:id="@+id/tv_bbs_publish_subject_type"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_centerVertical="true"
+                            android:layout_alignParentStart="true"
+                            android:text="@string/bbs_publish_subject_type"
+                            android:textColor="@color/z_color_text_hint"
+                            android:textSize="15sp" />
+
+                        <ImageView
+                            android:layout_width="24dp"
+                            android:layout_height="24dp"
+                            android:layout_alignParentEnd="true"
+                            android:src="@mipmap/icon_select_more_gray" />
+                    </RelativeLayout>
+
+                </LinearLayout>
+
+                <View
+                    android:layout_width="match_parent"
+                    android:layout_height="1px"
+                    android:layout_marginLeft="@dimen/spacing_normal"
+                    android:layout_marginRight="@dimen/spacing_normal"
+                    android:background="@color/z_color_subtitle_font" />
 
-            </LinearLayout>
+                <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginBottom="@dimen/spacing_small"
+                    android:layout_marginLeft="@dimen/spacing_normal"
+                    android:layout_marginRight="@dimen/spacing_normal"
+                    android:layout_marginTop="@dimen/spacing_small"
+                    android:orientation="horizontal">
 
-            <View
-                android:layout_width="match_parent"
-                android:layout_height="1px"
-                android:layout_marginLeft="@dimen/spacing_normal"
-                android:layout_marginRight="@dimen/spacing_normal"
-                android:background="@color/z_color_subtitle_font" />
+                    <TextView
+                        android:layout_width="0dp"
+                        android:layout_height="wrap_content"
+                        android:layout_weight="1"
+                        android:text="@string/bbs_publish_subject_title_label"
+                        android:textColor="@color/z_color_text_primary"
+                        android:textSize="15sp" />
 
-            <LinearLayout
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginBottom="@dimen/spacing_small"
-                android:layout_marginLeft="@dimen/spacing_normal"
-                android:layout_marginRight="@dimen/spacing_normal"
-                android:layout_marginTop="@dimen/spacing_small"
-                android:orientation="horizontal">
+                    <EditText
+                        android:id="@+id/edit_bbs_publish_subject_title"
+                        android:layout_width="0dp"
+                        android:layout_height="wrap_content"
+                        android:layout_weight="3"
+                        android:background="@null"
+                        android:textColor="@color/z_color_text_hint"
+                        android:textSize="15sp"
+                        android:inputType="text"
+                        android:maxLines="1"
+                        android:imeOptions="actionNext"
+                        android:nextFocusForward="@+id/edit_bbs_publish_subject_summary"
+                        tools:text="标题111111" />
+                </LinearLayout>
+
+                <View
+                    android:layout_width="match_parent"
+                    android:layout_height="1px"
+                    android:layout_marginLeft="@dimen/spacing_normal"
+                    android:layout_marginRight="@dimen/spacing_normal"
+                    android:background="@color/z_color_subtitle_font" />
 
-                <TextView
-                    android:layout_width="0dp"
-                    android:layout_height="wrap_content"
-                    android:layout_weight="1"
-                    android:text="@string/bbs_publish_subject_title_label"
-                    android:textColor="@color/z_color_text_primary"
-                    android:textSize="15sp" />
-
-                <EditText
-                    android:id="@+id/edit_bbs_publish_subject_title"
-                    android:layout_width="0dp"
+                <LinearLayout
+                    android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:layout_weight="3"
-                    android:background="@null"
-                    android:textColor="@color/z_color_text_hint"
-                    android:textSize="15sp"
-                    tools:text="标题111111" />
-            </LinearLayout>
+                    android:layout_marginBottom="@dimen/spacing_small"
+                    android:layout_marginLeft="@dimen/spacing_normal"
+                    android:layout_marginRight="@dimen/spacing_normal"
+                    android:layout_marginTop="@dimen/spacing_small"
+                    android:orientation="horizontal">
 
-            <View
-                android:layout_width="match_parent"
-                android:layout_height="1px"
-                android:layout_marginLeft="@dimen/spacing_normal"
-                android:layout_marginRight="@dimen/spacing_normal"
-                android:background="@color/z_color_subtitle_font" />
+                    <TextView
+                        android:layout_width="0dp"
+                        android:layout_height="wrap_content"
+                        android:layout_weight="1"
+                        android:text="@string/bbs_publish_subject_summary_label"
+                        android:textColor="@color/z_color_text_primary"
+                        android:textSize="15sp" />
 
-            <LinearLayout
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginBottom="@dimen/spacing_small"
-                android:layout_marginLeft="@dimen/spacing_normal"
-                android:layout_marginRight="@dimen/spacing_normal"
-                android:layout_marginTop="@dimen/spacing_small"
-                android:orientation="horizontal">
+                    <EditText
+                        android:id="@+id/edit_bbs_publish_subject_summary"
+                        android:layout_width="0dp"
+                        android:layout_height="wrap_content"
+                        android:layout_weight="3"
+                        android:background="@null"
+                        android:textColor="@color/z_color_text_hint"
+                        android:textSize="15sp"
+                        android:inputType="text"
+                        android:maxLines="1"
+                        android:imeOptions="actionNext"
+                        android:nextFocusForward="@+id/edit_bbs_publish_subject_content"
+                        tools:text="摘要111111" />
+                </LinearLayout>
+
+                <View
+                    android:layout_width="match_parent"
+                    android:layout_height="@dimen/spacing_meeting"
+                    android:background="@color/z_color_bottom_bar_background" />
 
-                <TextView
-                    android:layout_width="0dp"
-                    android:layout_height="wrap_content"
-                    android:layout_weight="1"
-                    android:text="@string/bbs_publish_subject_summary_label"
-                    android:textColor="@color/z_color_text_primary"
-                    android:textSize="15sp" />
-
-                <EditText
-                    android:id="@+id/edit_bbs_publish_subject_summary"
-                    android:layout_width="0dp"
+                <android.support.design.widget.TextInputLayout
+                    android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:layout_weight="3"
-                    android:background="@null"
-                    android:textColor="@color/z_color_text_hint"
-                    android:textSize="15sp"
-                    tools:text="摘要111111" />
+                    android:layout_marginBottom="@dimen/spacing_small"
+                    android:layout_marginLeft="@dimen/spacing_normal"
+                    android:layout_marginRight="@dimen/spacing_normal"
+                    android:layout_marginTop="@dimen/spacing_small">
+
+                    <EditText
+                        android:id="@+id/edit_bbs_publish_subject_content"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:background="@null"
+                        android:gravity="start"
+                        android:hint="@string/bbs_publish_subject_content_label"
+                        android:inputType="text"
+                        android:imeOptions="actionDone"
+                        android:minLines="15"
+                        android:textColor="@color/z_color_text_hint"
+                        android:textSize="15sp" />
+                </android.support.design.widget.TextInputLayout>
             </LinearLayout>
+        </ScrollView>
 
-            <View
-                android:layout_width="match_parent"
-                android:layout_height="@dimen/spacing_meeting"
-                android:background="@color/z_color_bottom_bar_background" />
-
-            <android.support.design.widget.TextInputLayout
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginBottom="@dimen/spacing_small"
-                android:layout_marginLeft="@dimen/spacing_normal"
-                android:layout_marginRight="@dimen/spacing_normal"
-                android:layout_marginTop="@dimen/spacing_small">
 
-                <EditText
-                    android:id="@+id/edit_bbs_publish_subject_content"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:background="@null"
-                    android:gravity="start"
-                    android:hint="@string/bbs_publish_subject_content_label"
-                    android:minLines="15"
-                    android:textColor="@color/z_color_text_hint"
-                    android:textSize="15sp" />
-            </android.support.design.widget.TextInputLayout>
-        </LinearLayout>
-    </ScrollView>
 
+    </android.support.design.widget.CoordinatorLayout>
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -267,5 +282,4 @@
             </LinearLayout>
         </HorizontalScrollView>
     </LinearLayout>
-
-</android.support.design.widget.CoordinatorLayout>
+</FrameLayout>

+ 57 - 50
o2android/app/src/main/res/layout/activity_bbs_web_view_subject.xml

@@ -1,54 +1,71 @@
 <?xml version="1.0" encoding="utf-8"?>
-<android.support.design.widget.CoordinatorLayout
+<FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/activity_bbs_web_view_subject"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@color/z_color_background"
-    android:fitsSystemWindows="true"
-    tools:context="net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.bbs.view.BBSWebViewSubjectActivity">
-
-    <android.support.design.widget.AppBarLayout
-        android:id="@+id/app_bar_layout_snippet"
-        android:layout_height="wrap_content"
-        android:layout_width="match_parent"
-        android:theme="@style/XBPMTheme.AppBarOverlay">
-        <android.support.v7.widget.Toolbar
-            android:id="@+id/toolbar_snippet_top_bar"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:background="@color/z_color_primary"
-            app:popupTheme="@style/XBPMTheme.PopupOverlay"
-            app:layout_scrollFlags="scroll|snap|enterAlways">
-            <TextView
-                android:id="@+id/tv_snippet_top_title"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center"
-                android:ellipsize="marquee"
-                android:textAppearance="?android:attr/textAppearanceMedium"
-                android:text=""
-                android:singleLine="true" />
-        </android.support.v7.widget.Toolbar>
-    </android.support.design.widget.AppBarLayout>
-
-    <net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.NestedProgressWebView
-        android:id="@+id/web_view_bbs_web_view_subject_content"
+    tools:context="net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.bbs.view.BBSWebViewSubjectActivity"
+    >
+    <android.support.design.widget.CoordinatorLayout
+        android:id="@+id/activity_bbs_web_view_subject"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_marginBottom="105dp"
-        android:background="@android:color/white"
-        app:layout_behavior="@string/appbar_scrolling_view_behavior" >
-    </net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.NestedProgressWebView>
+        android:background="@color/z_color_background"
+        android:fitsSystemWindows="true">
 
+        <android.support.design.widget.AppBarLayout
+            android:id="@+id/app_bar_layout_snippet"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:theme="@style/XBPMTheme.AppBarOverlay">
+            <android.support.v7.widget.Toolbar
+                android:id="@+id/toolbar_snippet_top_bar"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:background="@color/z_color_primary"
+                app:popupTheme="@style/XBPMTheme.PopupOverlay"
+                app:layout_scrollFlags="scroll|snap|enterAlways">
+                <TextView
+                    android:id="@+id/tv_snippet_top_title"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center"
+                    android:ellipsize="marquee"
+                    android:textAppearance="?android:attr/textAppearanceMedium"
+                    android:text=""
+                    android:singleLine="true" />
+            </android.support.v7.widget.Toolbar>
+        </android.support.design.widget.AppBarLayout>
+
+        <net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.NestedProgressWebView
+            android:id="@+id/web_view_bbs_web_view_subject_content"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_marginBottom="105dp"
+            android:background="@android:color/white"
+            app:layout_behavior="@string/appbar_scrolling_view_behavior" >
+        </net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.NestedProgressWebView>
+
+    </android.support.design.widget.CoordinatorLayout>
+    <android.support.design.widget.FloatingActionButton
+        android:id="@+id/button_bbs_subject_attach"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@mipmap/icon_attach_white_24dp"
+        app:backgroundTintMode="src_over"
+        app:backgroundTint="@color/z_color_primary"
+        android:layout_gravity="bottom|end"
+        android:layout_marginBottom="120dp"
+        android:layout_marginEnd="@dimen/spacing_normal"
+        android:visibility="gone"
+        />
     <android.support.constraint.ConstraintLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:background="@android:color/transparent"
-        android:layout_gravity="bottom">
-
+        android:layout_gravity="bottom"
+        android:layout_marginBottom="0dp">
         <LinearLayout
             android:id="@+id/layout_bbs_subject_operation_bar"
             android:layout_width="match_parent"
@@ -112,23 +129,13 @@
                     android:text="发送"
                     android:textColor="@color/white_translucent"
                     android:layout_margin="@dimen/spacing_normal"
-
                     android:background="@drawable/button_submit_comment"/>
 
             </LinearLayout>
         </LinearLayout>
 
-        <android.support.design.widget.FloatingActionButton
-            android:id="@+id/button_bbs_subject_attach"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:src="@mipmap/icon_attach_white_24dp"
-            android:backgroundTintMode="src_over"
-            android:backgroundTint="@color/z_color_primary"
-            android:layout_margin="@dimen/spacing_normal"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintBottom_toTopOf="@id/layout_bbs_subject_operation_bar"/>
+
 
     </android.support.constraint.ConstraintLayout>
+</FrameLayout>
 
-</android.support.design.widget.CoordinatorLayout>

+ 16 - 0
o2android/app/src/main/res/layout/activity_flutter_connect.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".flutter.FlutterConnectActivity">
+    <FrameLayout
+        android:id="@+id/flutter_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent" />
+</android.support.constraint.ConstraintLayout>

+ 86 - 71
o2android/app/src/main/res/layout/activity_login.xml

@@ -19,6 +19,7 @@
             android:id="@+id/login_form_scroll_id"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
+            android:visibility="visible"
             android:paddingBottom="@dimen/activity_vertical_margin"
             android:paddingLeft="@dimen/login_horizontal_margin"
             android:paddingRight="@dimen/login_horizontal_margin"
@@ -163,32 +164,48 @@
                         style="@style/Widget.AppCompat.Button.Borderless"
                         />
 
-                    <Button
-                        android:id="@+id/btn_login_facepp"
-                        android:layout_width="match_parent"
-                        android:layout_height="44dp"
-                        android:layout_marginTop="14dp"
-                        android:layout_gravity="center"
-                        android:background="@drawable/button_background_44dp_border"
-                        android:text="@string/activity_login_face"
-                        android:textColor="@color/z_color_text_primary"
-                        android:textSize="16sp"
-                        style="@style/Widget.AppCompat.Button.Borderless"
-                        android:visibility="gone"
-                        />
+                    <!--<Button-->
+                        <!--android:id="@+id/btn_login_facepp"-->
+                        <!--android:layout_width="match_parent"-->
+                        <!--android:layout_height="44dp"-->
+                        <!--android:layout_marginTop="14dp"-->
+                        <!--android:layout_gravity="center"-->
+                        <!--android:background="@drawable/button_background_44dp_border"-->
+                        <!--android:text="@string/activity_login_face"-->
+                        <!--android:textColor="@color/z_color_text_primary"-->
+                        <!--android:textSize="16sp"-->
+                        <!--style="@style/Widget.AppCompat.Button.Borderless"-->
+                        <!--android:visibility="gone"-->
+                        <!--/>-->
 
 
-                    <TextView
-                        android:id="@+id/tv_rebind_btn"
-                        android:layout_width="wrap_content"
+
+                    <RelativeLayout
+                        android:layout_width="match_parent"
                         android:layout_height="wrap_content"
-                        android:text="@string/login_button_rebind"
-                        android:textColor="@color/z_color_text_hint"
-                        android:textSize="13sp"
-                        android:layout_marginTop="10dp"
-                        android:layout_gravity="end"
-                        android:layout_marginEnd="15dp"
-                        />
+                        android:layout_marginTop="10dp">
+                        <TextView
+                            android:id="@+id/tv_bioauth_btn"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="@string/activity_login_fingerprint"
+                            android:textColor="@color/z_color_text_hint"
+                            android:textSize="13sp"
+                            android:layout_alignParentStart="true"
+                            android:layout_marginStart="15dp"
+                            android:visibility="gone"
+                            />
+                        <TextView
+                            android:id="@+id/tv_rebind_btn"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="@string/login_button_rebind"
+                            android:textColor="@color/z_color_text_hint"
+                            android:textSize="13sp"
+                            android:layout_alignParentEnd="true"
+                            android:layout_marginEnd="15dp"
+                            />
+                    </RelativeLayout>
 
                 </LinearLayout>
 
@@ -197,6 +214,52 @@
         </ScrollView>
 
 
+        <RelativeLayout
+            android:id="@+id/login_main_biometry"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:visibility="gone"
+            android:focusable="true"
+            android:focusableInTouchMode="true"
+            android:background="@mipmap/login_background_2"
+            android:paddingLeft="@dimen/login_horizontal_margin"
+            android:paddingRight="@dimen/login_horizontal_margin">
+            <Button
+                android:id="@+id/btn_bio_auth_login"
+                android:layout_width="match_parent"
+                android:layout_height="44dp"
+                android:layout_centerInParent="true"
+                android:background="@drawable/button_background_44dp"
+                android:text="@string/activity_login_fingerprint_click"
+                android:textColor="@android:color/white"
+                android:textSize="18sp"
+                style="@style/Widget.AppCompat.Button.Borderless"
+                />
+            <net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.CircleImageView
+                android:id="@+id/image_bio_auth_icon"
+                android:layout_width="120dp"
+                android:layout_height="120dp"
+                android:layout_marginBottom="20dp"
+                android:layout_centerHorizontal="true"
+                android:layout_above="@+id/btn_bio_auth_login"
+                android:src="@mipmap/zhiwen"
+                />
+            <TextView
+                android:id="@+id/tv_user_fallback_btn"
+                android:layout_below="@+id/btn_bio_auth_login"
+                android:layout_alignParentEnd="true"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/login_button_user_fallback"
+                android:textColor="@color/z_color_text_hint"
+                android:textSize="13sp"
+                android:layout_marginTop="15dp"
+                android:layout_marginEnd="15dp"
+                />
+
+
+
+        </RelativeLayout>
         <TextView
             android:id="@+id/tv_login_copyright"
             android:layout_width="match_parent"
@@ -211,54 +274,6 @@
 
     </RelativeLayout>
 
-    <RelativeLayout
-        android:id="@+id/login_facepp"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:focusable="true"
-        android:focusableInTouchMode="true"
-        android:visibility="gone">
-
-        <android.opengl.GLSurfaceView
-            android:id="@+id/login_facepp_surfaceview"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"/>
-
-
-        <ImageView
-            android:id="@+id/login_facepp_back"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:src="@mipmap/icon_menu_window_close"
-            android:layout_alignParentStart="true"
-            android:layout_marginTop="14dp"
-            android:layout_marginStart="14dp"
-            />
-
-        <TextView
-            android:id="@+id/login_facepp_title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_alignParentTop="true"
-            android:layout_marginTop="14dp"
-            android:layout_centerHorizontal="true"
-            android:textColor="#ffffffff"
-            android:textSize="18sp"
-            android:text="人脸识别登录"/>
-
-        <TextView
-            android:id="@+id/login_facepp_debugPrinttext"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_alignParentBottom="true"
-            android:layout_marginBottom="48dp"
-            android:layout_centerHorizontal="true"
-            android:textColor="#ffffffff"
-            android:textSize="18sp"
-            tools:text="1111111"/>
-
-
-    </RelativeLayout>
 </LinearLayout>
 
 

+ 91 - 91
o2android/app/src/main/res/layout/activity_skin_manager.xml

@@ -101,97 +101,97 @@
         </android.support.constraint.ConstraintLayout>
 
 
-        <LinearLayout
-            android:id="@+id/linear_skin_manager_online"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="@dimen/spacing_small"
-            android:background="@android:color/transparent"
-            android:paddingBottom="@dimen/spacing_normal"
-            android:paddingStart="@dimen/spacing_normal"
-            android:paddingTop="@dimen/spacing_normal"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toBottomOf="@+id/constraint_skin_manager_default">
-
-            <TextView
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:text="@string/skin_manager_online"
-                android:textColor="@color/z_color_text_primary"
-                android:textSize="@dimen/font_large" />
-        </LinearLayout>
-
-        <android.support.constraint.ConstraintLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="@dimen/spacing_small"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toBottomOf="@+id/linear_skin_manager_online">
-
-            <android.support.v7.widget.CardView
-                android:id="@+id/card_skin_manager_puppy2018"
-                android:layout_width="165dp"
-                android:layout_height="235dp"
-                android:layout_marginBottom="@dimen/spacing_normal"
-                android:layout_marginLeft="@dimen/spacing_small"
-                app:cardBackgroundColor="@android:color/white"
-                app:cardCornerRadius="10dp"
-                app:layout_constraintBottom_toBottomOf="parent"
-                app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintTop_toTopOf="parent">
-
-                <ImageView
-                    android:layout_width="match_parent"
-                    android:layout_height="170dp"
-                    android:layout_gravity="top"
-                    android:src="@mipmap/puppy_outside" />
-
-                <TextView
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="bottom|center_horizontal"
-                    android:layout_marginBottom="@dimen/spacing_large"
-                    android:gravity="center"
-                    android:text="Puppy2018"
-                    android:textColor="@color/z_color_text_primary_dark"
-                    android:textSize="@dimen/font_large" />
-                <ImageView
-                    android:layout_width="42dp"
-                    android:layout_height="42dp"
-                    android:layout_gravity="right|bottom"
-                    android:src="@mipmap/right_bottom_corner_new"/>
-            </android.support.v7.widget.CardView>
-
-            <android.support.v7.widget.CardView
-                android:layout_width="165dp"
-                android:layout_height="235dp"
-                app:cardBackgroundColor="@android:color/white"
-                app:cardCornerRadius="10dp"
-                android:layout_marginBottom="@dimen/spacing_normal"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintTop_toTopOf="parent"
-                app:layout_constraintBottom_toBottomOf="parent"
-                android:layout_marginRight="@dimen/spacing_small">
-
-                <ImageView
-                    android:layout_width="match_parent"
-                    android:layout_height="170dp"
-                    android:layout_gravity="top"
-                    android:src="@mipmap/new_skin_preparing" />
-
-                <TextView
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="bottom|center_horizontal"
-                    android:layout_marginBottom="@dimen/spacing_large"
-                    android:gravity="center"
-                    android:text="小叶正在赶制中....."
-                    android:textColor="@color/z_color_text_primary_dark"
-                    android:textSize="@dimen/font_large" />
-            </android.support.v7.widget.CardView>
-        </android.support.constraint.ConstraintLayout>
+        <!--<LinearLayout-->
+            <!--android:id="@+id/linear_skin_manager_online"-->
+            <!--android:layout_width="match_parent"-->
+            <!--android:layout_height="wrap_content"-->
+            <!--android:layout_marginTop="@dimen/spacing_small"-->
+            <!--android:background="@android:color/transparent"-->
+            <!--android:paddingBottom="@dimen/spacing_normal"-->
+            <!--android:paddingStart="@dimen/spacing_normal"-->
+            <!--android:paddingTop="@dimen/spacing_normal"-->
+            <!--app:layout_constraintEnd_toEndOf="parent"-->
+            <!--app:layout_constraintStart_toStartOf="parent"-->
+            <!--app:layout_constraintTop_toBottomOf="@+id/constraint_skin_manager_default">-->
+
+            <!--<TextView-->
+                <!--android:layout_width="match_parent"-->
+                <!--android:layout_height="wrap_content"-->
+                <!--android:text="@string/skin_manager_online"-->
+                <!--android:textColor="@color/z_color_text_primary"-->
+                <!--android:textSize="@dimen/font_large" />-->
+        <!--</LinearLayout>-->
+
+        <!--<android.support.constraint.ConstraintLayout-->
+            <!--android:layout_width="match_parent"-->
+            <!--android:layout_height="wrap_content"-->
+            <!--android:layout_marginTop="@dimen/spacing_small"-->
+            <!--app:layout_constraintEnd_toEndOf="parent"-->
+            <!--app:layout_constraintStart_toStartOf="parent"-->
+            <!--app:layout_constraintTop_toBottomOf="@+id/linear_skin_manager_online">-->
+
+            <!--<android.support.v7.widget.CardView-->
+                <!--android:id="@+id/card_skin_manager_puppy2018"-->
+                <!--android:layout_width="165dp"-->
+                <!--android:layout_height="235dp"-->
+                <!--android:layout_marginBottom="@dimen/spacing_normal"-->
+                <!--android:layout_marginLeft="@dimen/spacing_small"-->
+                <!--app:cardBackgroundColor="@android:color/white"-->
+                <!--app:cardCornerRadius="10dp"-->
+                <!--app:layout_constraintBottom_toBottomOf="parent"-->
+                <!--app:layout_constraintStart_toStartOf="parent"-->
+                <!--app:layout_constraintTop_toTopOf="parent">-->
+
+                <!--<ImageView-->
+                    <!--android:layout_width="match_parent"-->
+                    <!--android:layout_height="170dp"-->
+                    <!--android:layout_gravity="top"-->
+                    <!--android:src="@mipmap/puppy_outside" />-->
+
+                <!--<TextView-->
+                    <!--android:layout_width="wrap_content"-->
+                    <!--android:layout_height="wrap_content"-->
+                    <!--android:layout_gravity="bottom|center_horizontal"-->
+                    <!--android:layout_marginBottom="@dimen/spacing_large"-->
+                    <!--android:gravity="center"-->
+                    <!--android:text="Puppy2018"-->
+                    <!--android:textColor="@color/z_color_text_primary_dark"-->
+                    <!--android:textSize="@dimen/font_large" />-->
+                <!--<ImageView-->
+                    <!--android:layout_width="42dp"-->
+                    <!--android:layout_height="42dp"-->
+                    <!--android:layout_gravity="right|bottom"-->
+                    <!--android:src="@mipmap/right_bottom_corner_new"/>-->
+            <!--</android.support.v7.widget.CardView>-->
+
+            <!--<android.support.v7.widget.CardView-->
+                <!--android:layout_width="165dp"-->
+                <!--android:layout_height="235dp"-->
+                <!--app:cardBackgroundColor="@android:color/white"-->
+                <!--app:cardCornerRadius="10dp"-->
+                <!--android:layout_marginBottom="@dimen/spacing_normal"-->
+                <!--app:layout_constraintEnd_toEndOf="parent"-->
+                <!--app:layout_constraintTop_toTopOf="parent"-->
+                <!--app:layout_constraintBottom_toBottomOf="parent"-->
+                <!--android:layout_marginRight="@dimen/spacing_small">-->
+
+                <!--<ImageView-->
+                    <!--android:layout_width="match_parent"-->
+                    <!--android:layout_height="170dp"-->
+                    <!--android:layout_gravity="top"-->
+                    <!--android:src="@mipmap/new_skin_preparing" />-->
+
+                <!--<TextView-->
+                    <!--android:layout_width="wrap_content"-->
+                    <!--android:layout_height="wrap_content"-->
+                    <!--android:layout_gravity="bottom|center_horizontal"-->
+                    <!--android:layout_marginBottom="@dimen/spacing_large"-->
+                    <!--android:gravity="center"-->
+                    <!--android:text="小叶正在赶制中....."-->
+                    <!--android:textColor="@color/z_color_text_primary_dark"-->
+                    <!--android:textSize="@dimen/font_large" />-->
+            <!--</android.support.v7.widget.CardView>-->
+        <!--</android.support.constraint.ConstraintLayout>-->
 
     </android.support.constraint.ConstraintLayout>
 </android.support.v4.widget.NestedScrollView>

+ 88 - 0
o2android/app/src/main/res/layout/dialog_bitmetric_prompt.xml

@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/root_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <LinearLayout
+        android:layout_width="280dp"
+        android:layout_height="wrap_content"
+        android:layout_centerInParent="true"
+        android:background="@android:color/white"
+        android:orientation="vertical">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="24dp"
+            android:layout_marginTop="20dp"
+            android:text="@string/biometric_dialog_title"
+            android:textColor="@android:color/black"
+            android:textSize="20dp" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="24dp"
+            android:layout_marginRight="24dp"
+            android:layout_marginTop="12dp"
+            android:text="@string/biometric_dialog_subtitle"
+            android:textColor="#75000000"
+            android:textSize="16dp" />
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="24dp"
+            android:layout_marginRight="24dp"
+            android:layout_marginTop="24dp"
+            android:orientation="horizontal">
+
+            <ImageView
+                android:layout_width="54dp"
+                android:layout_height="54dp"
+                android:src="@mipmap/zhiwen" />
+
+            <TextView
+                android:id="@+id/state_tv"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:layout_marginLeft="8dp"
+                android:text="@string/biometric_dialog_state_normal"
+                android:textColor="#CDCED0"
+                android:textSize="16dp"
+                android:typeface="normal" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="52dp"
+            android:layout_gravity="right"
+            android:layout_marginRight="24dp"
+            android:layout_marginTop="30dp"
+            android:orientation="horizontal">
+
+            <TextView
+                android:id="@+id/use_password_btn"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:gravity="center"
+                android:text="@string/biometric_dialog_use_password"
+                android:textColor="#419BF9"
+                android:textSize="14dp" />
+
+            <TextView
+                android:id="@+id/cancel_btn"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:layout_marginLeft="14dp"
+                android:gravity="center"
+                android:text="@string/biometric_dialog_cancel"
+                android:textColor="#419BF9"
+                android:textSize="14dp" />
+
+        </LinearLayout>
+    </LinearLayout>
+
+</RelativeLayout>

+ 27 - 0
o2android/app/src/main/res/layout/fragment_download_progress.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <ImageView
+        android:id="@+id/image_logo_wave"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@mipmap/icon_down_arrow"
+        />
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/image_logo_wave"
+        android:layout_marginTop="@dimen/spacing_normal"
+        android:textColor="@color/white"
+        android:textSize="@dimen/font_large"
+        android:text="@string/skin_show_downloading"/>
+</android.support.constraint.ConstraintLayout>

+ 5 - 3
o2android/app/src/main/res/layout/fragment_main_settings.xml

@@ -207,9 +207,10 @@
                     </RelativeLayout>
 
                     <View
+                        android:id="@+id/id_setting_button_customer_service_split"
                         android:layout_width="match_parent"
                         android:layout_height="1dp"
-                        android:background="@color/z_color_split_line_ddd"></View>
+                        android:background="@color/z_color_split_line_ddd" />
                     <!--分享-->
                     <RelativeLayout
                         android:id="@+id/setting_button_customer_service_id"
@@ -223,6 +224,7 @@
                             android:layout_centerVertical="true"
                             android:src="@mipmap/icon_setting_share_22dp"
                             android:layout_alignParentLeft="true"/>
+
                         <TextView
                             android:layout_width="wrap_content"
                             android:layout_height="wrap_content"
@@ -231,7 +233,7 @@
                             android:layout_marginLeft="10dp"
                             android:text="分享下载"
                             android:textSize="15sp"
-                            android:textColor="@color/z_color_text_primary_dark"></TextView>
+                            android:textColor="@color/z_color_text_primary_dark" />
                         <ImageView
                             android:layout_width="22dp"
                             android:layout_height="22dp"
@@ -245,7 +247,7 @@
                         android:layout_width="match_parent"
                         android:layout_height="1dp"
                         android:background="@color/z_color_split_line_ddd"
-                        android:padding="0dp"></View>
+                        android:padding="0dp" />
                     <!--反馈-->
                     <RelativeLayout
                         android:id="@+id/setting_button_feedback_id"

+ 4 - 4
o2android/app/src/main/res/layout/fragment_main_todo.xml

@@ -188,8 +188,8 @@
             android:paddingBottom="10dp">
             <ImageView
                 android:id="@+id/image_todo_fragment_scan_code"
-                android:layout_width="22dp"
-                android:layout_height="22dp"
+                android:layout_width="24dp"
+                android:layout_height="24dp"
                 android:src="@mipmap/menu_scan_qrcode"
                 android:layout_alignParentStart="true"
                 android:layout_centerVertical="true"
@@ -210,8 +210,8 @@
 
             <ImageView
                 android:id="@+id/tv_todo_fragment_publish"
-                android:layout_width="22dp"
-                android:layout_height="22dp"
+                android:layout_width="24dp"
+                android:layout_height="24dp"
                 android:src="@mipmap/menu_index_add"
                 android:layout_alignParentEnd="true"
                 android:layout_centerVertical="true"

+ 6 - 1
o2android/app/src/main/res/layout/fragment_start_process_step_two.xml

@@ -156,9 +156,12 @@
                             android:layout_height="wrap_content"
                             android:layout_weight="2"
                             android:hint="@string/title_please_enter_start_process_step_two"
-                            android:maxLines="1"
                             android:textColor="@color/z_color_text_primary"
                             android:textColorHint="@color/z_color_text_hint"
+                            android:inputType="text"
+                            android:singleLine="true"
+                            android:imeOptions="actionNext"
+                            android:nextFocusForward="@+id/btn_start_process_step_two_positive"
                             android:textSize="14sp"
                             android:background="@null"/>
                     </LinearLayout>
@@ -188,6 +191,8 @@
                     android:text="@string/submit"
                     android:textColor="@android:color/white"
                     android:textSize="18sp"
+                    android:focusable="true"
+                    android:focusableInTouchMode="true"
                     />
 
                 <Button

+ 6 - 4
o2android/app/src/main/res/layout/item_pop_okr_detail_work_cycle_list.xml

@@ -1,13 +1,15 @@
 <?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     android:orientation="horizontal"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:padding="@dimen/spacing_small">
+    android:layout_height="wrap_content">
     <TextView
         android:id="@+id/tv_item_pop_okr_detail_work_cycle_title"
-        android:layout_width="match_parent"
+        android:layout_width="wrap_content"
         android:textSize="@dimen/font_normal"
-        android:layout_height="wrap_content"/>
+        android:layout_height="wrap_content"
+        android:layout_margin="@dimen/spacing_normal"
+        tools:text="测试文字"/>
 
 </LinearLayout>

+ 1 - 1
o2android/app/src/main/res/layout/pop_bbs_subject_type.xml

@@ -19,5 +19,5 @@
         android:id="@+id/recycler_pop_bbs_subject_type_list"
         android:layout_width="match_parent"
         android:layout_height="0dp"
-        android:layout_weight="1"></android.support.v7.widget.RecyclerView>
+        android:layout_weight="1" />
 </LinearLayout>

TEMPAT SAMPAH
o2android/app/src/main/res/mipmap-xhdpi/app_mind_map.png


TEMPAT SAMPAH
o2android/app/src/main/res/mipmap-xhdpi/icon_down_arrow.png


TEMPAT SAMPAH
o2android/app/src/main/res/mipmap-xhdpi/zhiwen.png


+ 1 - 0
o2android/app/src/main/res/values/colors.xml

@@ -89,6 +89,7 @@
     <color name="overlay_work_place">#1c3288dd</color>
     <color name="overlay_work_place_border">#3288dd</color>
 
+    <color name="bg_biometry_dialog">#99000000</color>
 
     <!--im-->
     <color name="item_click_bg_color">#E9EDED</color>

+ 18 - 3
o2android/app/src/main/res/values/strings.xml

@@ -1,7 +1,7 @@
 <resources>
-    <string name="app_name">O2</string>
-    <string name="app_about">关于O2</string>
-    <string name="app_name_about">O2</string>
+    <string name="app_name">O2OA</string>
+    <string name="app_about">关于O2OA</string>
+    <string name="app_name_about">O2OA</string>
     <string name="copy_right">&#169;</string>
     <string name="reserved">All right reserved.</string>
     <string name="version">版本:</string>
@@ -113,6 +113,8 @@
     <string name="login_code">验证码</string>
     <string name="activity_login_password">密码</string>
     <string name="activity_login_submit">登    录</string>
+    <string name="activity_login_fingerprint">指纹识别登录</string>
+    <string name="activity_login_fingerprint_click">点击开始认证</string>
     <string name="activity_login_face">人脸识别</string>
     <string name="activity_logout">退出登录</string>
     <string name="login_button_next">下一步</string>
@@ -120,6 +122,7 @@
     <string name="login_button_code">发送验证码</string>
     <string name="login_button_code_disabled">重新发送</string>
     <string name="login_button_rebind">重新绑定</string>
+    <string name="login_button_user_fallback">其他方式登录</string>
     <string name="activity_login_save">保存</string>
     <string name="activity_login_remember_login">记住我</string>
     <string-array name="login_facepp_trackig_mode_array">
@@ -678,6 +681,18 @@
     <string name="skin_show_downloading">下载中...</string>
     <string name="skin_show_installing">安装中...</string>
 
+
+    <!-- 指纹识别-->
+    <string name="biometric_dialog_cancel">取消</string>
+    <string name="biometric_dialog_title">验证</string>
+    <string name="biometric_dialog_subtitle">正在指纹识别中...</string>
+    <string name="biometric_dialog_state_normal">指纹识别</string>
+    <string name="biometric_dialog_state_failed">识别失败,请重试</string>
+    <string name="biometric_dialog_state_error">验证失败,请使用其他方式验证</string>
+    <string name="biometric_dialog_state_error_enable_process">验证识别,没有启用指纹识别</string>
+    <string name="biometric_dialog_state_succeeded">验证成功</string>
+    <string name="biometric_dialog_use_password">其他方式</string>
+
     <!--custom style -->
     <string name="custom_style_install_label">正在从服务器端装载UI资源</string>
 

+ 1 - 1
o2android/build.gradle

@@ -30,9 +30,9 @@ buildscript {
 
 allprojects {
     repositories {
+        google()
         jcenter()
         mavenCentral()
-        google()
         maven { url "https://jitpack.io" }
         maven {
             url "https://oss.sonatype.org/content/repositories/snapshots/"

+ 2 - 2
o2android/gradle.properties

@@ -20,8 +20,8 @@ org.gradle.parallel=true
 
 
 # o2
-o2.versionName=4.8.0
-o2.versionCode=80
+o2.versionName=4.9.0
+o2.versionCode=90
 
 
 # sjgj

+ 1 - 1
o2android/settings.gradle

@@ -1 +1 @@
-include ':app'
+include ':app', ':o2_auth_sdk'

+ 1 - 0
o2android/test.txt

@@ -0,0 +1 @@
+test

File diff ditekan karena terlalu besar
+ 190 - 393
o2ios/O2Platform.xcodeproj/project.pbxproj


+ 6 - 0
o2ios/O2Platform/Assets.xcassets/demo_alert_view/Contents.json

@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}

+ 22 - 0
o2ios/O2Platform/Assets.xcassets/demo_alert_view/icon_off_white2.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_off_white2@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_off_white2@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}

TEMPAT SAMPAH
o2ios/O2Platform/Assets.xcassets/demo_alert_view/icon_off_white2.imageset/icon_off_white2@2x.png


TEMPAT SAMPAH
o2ios/O2Platform/Assets.xcassets/demo_alert_view/icon_off_white2.imageset/icon_off_white2@3x.png


+ 22 - 0
o2ios/O2Platform/Assets.xcassets/demo_alert_view/pic_czsm.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "pic_czsm@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "pic_czsm@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}

TEMPAT SAMPAH
o2ios/O2Platform/Assets.xcassets/demo_alert_view/pic_czsm.imageset/pic_czsm@2x.png


TEMPAT SAMPAH
o2ios/O2Platform/Assets.xcassets/demo_alert_view/pic_czsm.imageset/pic_czsm@3x.png


+ 22 - 0
o2ios/O2Platform/Assets.xcassets/demo_alert_view/pic_tou.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "pic_tou@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "pic_tou@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}

TEMPAT SAMPAH
o2ios/O2Platform/Assets.xcassets/demo_alert_view/pic_tou.imageset/pic_tou@2x.png


TEMPAT SAMPAH
o2ios/O2Platform/Assets.xcassets/demo_alert_view/pic_tou.imageset/pic_tou@3x.png


+ 22 - 0
o2ios/O2Platform/Assets.xcassets/demo_alert_view/pic_wenzi_bj.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "pic_wenzi_bj@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "pic_wenzi_bj@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini